diff --git a/Android.mk b/Android.mk index ef1cdff1..80c61b08 100644 --- a/Android.mk +++ b/Android.mk @@ -61,6 +61,7 @@ SPVTOOLS_SRC_FILES := \ source/val/validate_instruction.cpp \ source/val/validate_memory.cpp \ source/val/validate_memory_semantics.cpp \ + source/val/validate_mesh_shading.cpp \ source/val/validate_misc.cpp \ source/val/validate_mode_setting.cpp \ source/val/validate_layout.cpp \ @@ -68,6 +69,8 @@ SPVTOOLS_SRC_FILES := \ source/val/validate_logicals.cpp \ source/val/validate_non_uniform.cpp \ source/val/validate_primitives.cpp \ + source/val/validate_ray_query.cpp \ + source/val/validate_ray_tracing.cpp \ source/val/validate_scopes.cpp \ source/val/validate_small_type_uses.cpp \ source/val/validate_type.cpp @@ -88,8 +91,11 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/composite.cpp \ source/opt/const_folding_rules.cpp \ source/opt/constants.cpp \ + source/opt/control_dependence.cpp \ + source/opt/convert_to_sampled_image_pass.cpp \ source/opt/convert_to_half_pass.cpp \ source/opt/copy_prop_arrays.cpp \ + source/opt/dataflow.cpp \ source/opt/dead_branch_elim_pass.cpp \ source/opt/dead_insert_elim_pass.cpp \ source/opt/dead_variable_elimination.cpp \ @@ -97,13 +103,16 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/debug_info_manager.cpp \ source/opt/def_use_manager.cpp \ source/opt/desc_sroa.cpp \ + source/opt/desc_sroa_util.cpp \ source/opt/dominator_analysis.cpp \ source/opt/dominator_tree.cpp \ source/opt/eliminate_dead_constant_pass.cpp \ source/opt/eliminate_dead_functions_pass.cpp \ source/opt/eliminate_dead_functions_util.cpp \ + source/opt/eliminate_dead_input_components_pass.cpp \ source/opt/eliminate_dead_members_pass.cpp \ source/opt/feature_manager.cpp \ + source/opt/fix_func_call_arguments.cpp \ source/opt/fix_storage_class.cpp \ source/opt/flatten_decoration_pass.cpp \ source/opt/fold.cpp \ @@ -122,6 +131,7 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/instruction.cpp \ source/opt/instruction_list.cpp \ source/opt/instrument_pass.cpp \ + source/opt/interface_var_sroa.cpp \ source/opt/interp_fixup_pass.cpp \ source/opt/ir_context.cpp \ source/opt/ir_loader.cpp \ @@ -152,17 +162,21 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/redundancy_elimination.cpp \ source/opt/register_pressure.cpp \ source/opt/relax_float_ops_pass.cpp \ + source/opt/remove_dontinline_pass.cpp \ source/opt/remove_duplicates_pass.cpp \ + source/opt/remove_unused_interface_variables_pass.cpp \ + source/opt/replace_desc_array_access_using_var_index.cpp \ source/opt/replace_invalid_opc.cpp \ source/opt/scalar_analysis.cpp \ source/opt/scalar_analysis_simplification.cpp \ source/opt/scalar_replacement_pass.cpp \ source/opt/set_spec_constant_default_value_pass.cpp \ source/opt/simplification_pass.cpp \ + source/opt/spread_volatile_semantics.cpp \ source/opt/ssa_rewrite_pass.cpp \ source/opt/strength_reduction_pass.cpp \ source/opt/strip_debug_info_pass.cpp \ - source/opt/strip_reflect_info_pass.cpp \ + source/opt/strip_nonsemantic_info_pass.cpp \ source/opt/struct_cfg_analysis.cpp \ source/opt/type_manager.cpp \ source/opt/types.cpp \ @@ -180,6 +194,7 @@ SPV_GLSL_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.glsl.st SPV_OPENCL_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.opencl.std.100.grammar.json SPV_DEBUGINFO_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.debuginfo.grammar.json SPV_CLDEBUGINFO100_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json +SPV_VKDEBUGINFO100_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json define gen_spvtools_grammar_tables $(call generate-file-dir,$(1)/core.insts-unified1.inc) @@ -211,6 +226,7 @@ $(LOCAL_PATH)/source/ext_inst.cpp: \ $(1)/opencl.std.insts.inc \ $(1)/debuginfo.insts.inc \ $(1)/opencl.debuginfo.100.insts.inc \ + $(1)/nonsemantic.shader.debuginfo.100.insts.inc \ $(1)/spv-amd-gcn-shader.insts.inc \ $(1)/spv-amd-shader-ballot.insts.inc \ $(1)/spv-amd-shader-explicit-vertex-parameter.insts.inc \ @@ -240,6 +256,7 @@ endef # We generate language-specific headers for DebugInfo and OpenCL.DebugInfo.100 $(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),DebugInfo,$(SPV_DEBUGINFO_GRAMMAR))) $(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),OpenCLDebugInfo100,$(SPV_CLDEBUGINFO100_GRAMMAR))) +$(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),NonSemanticShaderDebugInfo100,$(SPV_VKDEBUGINFO100_GRAMMAR))) define gen_spvtools_vendor_tables @@ -257,6 +274,7 @@ endef # Vendor and debug extended instruction sets, with grammars from SPIRV-Tools source tree. $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),debuginfo,"")) $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),opencl.debuginfo.100,"CLDEBUG100_")) +$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),nonsemantic.shader.debuginfo.100,"SHDEBUG100_")) $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-gcn-shader,"")) $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-shader-ballot,"")) $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-shader-explicit-vertex-parameter,"")) @@ -289,7 +307,7 @@ $(1)/build-version.inc: \ $(LOCAL_PATH)/utils/update_build_version.py \ $(LOCAL_PATH)/CHANGES @$(HOST_PYTHON) $(LOCAL_PATH)/utils/update_build_version.py \ - $(LOCAL_PATH) $(1)/build-version.inc + $(LOCAL_PATH)/CHANGES $(1)/build-version.inc @echo "[$(TARGET_ARCH_ABI)] Generate : build-version.inc <= CHANGES" $(LOCAL_PATH)/source/software_version.cpp: $(1)/build-version.inc endef diff --git a/BUILD.bazel b/BUILD.bazel index 52290cfb..35dfd665 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -3,6 +3,7 @@ load( "COMMON_COPTS", "DEBUGINFO_GRAMMAR_JSON_FILE", "CLDEBUGINFO100_GRAMMAR_JSON_FILE", + "SHDEBUGINFO100_GRAMMAR_JSON_FILE", "TEST_COPTS", "base_test", "generate_core_tables", @@ -12,6 +13,7 @@ load( "generate_opencl_tables", "generate_vendor_tables", "link_test", + "lint_test", "opt_test", "reduce_test", "util_test", @@ -59,12 +61,16 @@ generate_vendor_tables("debuginfo") generate_vendor_tables("opencl.debuginfo.100", "CLDEBUG100_") +generate_vendor_tables("nonsemantic.shader.debuginfo.100", "SHDEBUG100_") + generate_vendor_tables("nonsemantic.clspvreflection") generate_extinst_lang_headers("DebugInfo", DEBUGINFO_GRAMMAR_JSON_FILE) generate_extinst_lang_headers("OpenCLDebugInfo100", CLDEBUGINFO100_GRAMMAR_JSON_FILE) +generate_extinst_lang_headers("NonSemanticShaderDebugInfo100", SHDEBUGINFO100_GRAMMAR_JSON_FILE) + py_binary( name = "generate_registry_tables", srcs = ["utils/generate_registry_tables.py"], @@ -75,7 +81,8 @@ genrule( srcs = ["@spirv_headers//:spirv_xml_registry"], outs = ["generators.inc"], cmd = "$(location generate_registry_tables) --xml=$(location @spirv_headers//:spirv_xml_registry) --generator-output=$(location generators.inc)", - tools = [":generate_registry_tables"], + cmd_bat = "$(location //:generate_registry_tables) --xml=$(location @spirv_headers//:spirv_xml_registry) --generator-output=$(location generators.inc)", + exec_tools = [":generate_registry_tables"], ) py_binary( @@ -87,8 +94,9 @@ genrule( name = "gen_build_version", srcs = ["CHANGES"], outs = ["build-version.inc"], - cmd = "SOURCE_DATE_EPOCH=0 $(location update_build_version) $$(dirname $(location CHANGES)) $(location build-version.inc)", - tools = [":update_build_version"], + cmd = "SOURCE_DATE_EPOCH=0 $(location update_build_version) $(location CHANGES) $(location build-version.inc)", + cmd_bat = "set SOURCE_DATE_EPOCH=0 && $(location //:update_build_version) $(location CHANGES) $(location build-version.inc)", + exec_tools = [":update_build_version"], ) # Libraries @@ -101,12 +109,14 @@ cc_library( ":gen_enum_string_mapping", ":gen_extinst_lang_headers_DebugInfo", ":gen_extinst_lang_headers_OpenCLDebugInfo100", + ":gen_extinst_lang_headers_NonSemanticShaderDebugInfo100", ":gen_glsl_tables_unified1", ":gen_opencl_tables_unified1", ":gen_registry_tables", ":gen_vendor_tables_debuginfo", ":gen_vendor_tables_nonsemantic_clspvreflection", ":gen_vendor_tables_opencl_debuginfo_100", + ":gen_vendor_tables_nonsemantic_shader_debuginfo_100", ":gen_vendor_tables_spv_amd_gcn_shader", ":gen_vendor_tables_spv_amd_shader_ballot", ":gen_vendor_tables_spv_amd_shader_explicit_vertex_parameter", @@ -225,6 +235,19 @@ cc_library( ], ) +cc_library( + name = "spirv_tools_lint", + srcs = glob(["source/lint/*.cpp", "source/lint/*.h"]), + hdrs = ["include/spirv-tools/linter.hpp"], + copts = COMMON_COPTS, + linkstatic = 1, + visibility = ["//visibility:public"], + deps = [ + ":spirv_tools", + ":spirv_tools_opt", + ], +) + cc_library( name = "tools_util", srcs = glob(["tools/util/*.cpp"]), @@ -322,6 +345,21 @@ cc_binary( ], ) +cc_binary( + name = "spirv-lint", + srcs = [ + "tools/io.h", + "tools/lint/lint.cpp", + ], + copts = COMMON_COPTS, + visibility = ["//visibility:public"], + deps = [ + ":spirv_tools", + ":spirv_tools_lint", + ":tools_util", + ], +) + cc_binary( name = "spirv-cfg", srcs = [ @@ -465,6 +503,13 @@ base_test( ["test/link/*.cpp"], )] +[lint_test( + name = f[10:-4], # strip test/lint/, .cpp + srcs = [f], +) for f in glob( + ["test/lint/*.cpp"], +)] + [opt_test( name = f[9:-4], # strip test/opt/, .cpp srcs = [f], diff --git a/BUILD.gn b/BUILD.gn index ff16f63d..d2b2dff9 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -28,16 +28,16 @@ config("spv_headers_public_config") { source_set("spv_headers") { sources = [ - "include/spirv/1.2/GLSL.std.450.h", - "include/spirv/1.2/OpenCL.std.h", - "include/spirv/1.2/spirv.h", - "include/spirv/1.2/spirv.hpp", - "include/spirv/unified1/GLSL.std.450.h", - "include/spirv/unified1/NonSemanticClspvReflection.h", - "include/spirv/unified1/NonSemanticDebugPrintf.h", - "include/spirv/unified1/OpenCL.std.h", - "include/spirv/unified1/spirv.h", - "include/spirv/unified1/spirv.hpp", + #"include/spirv/1.2/GLSL.std.450.h", + #"include/spirv/1.2/OpenCL.std.h", + #"include/spirv/1.2/spirv.h", + #"include/spirv/1.2/spirv.hpp", + #"include/spirv/unified1/GLSL.std.450.h", + #"include/spirv/unified1/NonSemanticClspvReflection.h", + #"include/spirv/unified1/NonSemanticDebugPrintf.h", + #"include/spirv/unified1/OpenCL.std.h", + #"include/spirv/unified1/spirv.h", + #"include/spirv/unified1/spirv.hpp", ] public_configs = [ ":spv_headers_public_config" ] @@ -132,6 +132,9 @@ ohos_source_set("deqp_spirvtool_source") { "//third_party/spirv-tools/source/val/validate_small_type_uses.cpp", "//third_party/spirv-tools/source/val/validate_type.cpp", "//third_party/spirv-tools/source/val/validation_state.cpp", + "//third_party/spirv-tools/source/val/validate_mesh_shading.cpp", + "//third_party/spirv-tools/source/val/validate_ray_query.cpp", + "//third_party/spirv-tools/source/val/validate_ray_tracing.cpp", ] include_dirs = deqp_common_include_dirs diff --git a/CHANGES b/CHANGES index 2b2da982..56a2d52f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,175 @@ Revision history for SPIRV-Tools +v2022.4 2022-10-12 + - General + - Support Narrow Types in BitCast Folding Rule (#4941) + - spirv-diff: Allow no SpecId (#4904) + - build: cmake: Add support for GNU/Hurd (#4895) + - Implement tool changes for SPV_EXT_mesh_shader. (#4915) + - Validator + - spirv-val: Add SPV_ARM_core_builtins validation (#4958) + - spirv-val: Add an option to use friendly names or not (#4951) + - spirv-val: Consistently quote ids in messages (#4950) + - spirv-val: Add initial SPV_EXT_mesh_shader validation (#4924) + - spirv-val: Make it legal to use arrays of ray queries (#4938) + - spirv-val: Better message for using OpTypeBool in input/output (#4901) + - spirv-val: Add SPV_KHR_ray_tracing storage class (#4868) + - Optimizer + - spirv-opt: Fix stacked CompositeExtract constant folds (#4932) + - Improve time to build dominators (#4916) + - Fix ADCE to mark scope and inlined_at of line instructions as live. (#4910) + - Improve algorithm to reorder blocks in a function (#4911) + - Add structs to eliminate dead input components (#4894) + - spirv-opt: fix copy-propagate-arrays index opti on structs. (#4891) + - Fix ADCE to not eliminate top level DebugInfo instructions (#4889) + - Fix array copy propagation (#4890) + +v2022.3 2022-08-08 + - General + - Add SPV_KHR_fragment_shader_barycentric support (#4805) + - Add support for SPV_KHR_subgroup_rotate (#4786) + - use exec_tools instead of tools for better RBE compatibility (#4837) + - Write binary files to stdout in binary on windows. (#4834) + - Allow spirv-opt print-all to show pretty IDs (#4888) + - Validator + - spirv-val: Add PerVertexKHR (#4807) + - spirv-opt : Add FixFuncCallArgumentsPass (#4775) + - spirv-val: Add CullMaskKHR support (#4792) + - Require ColMajor or RowMajor for matrices (#4878) + - spirv-val: Add SPV_KHR_ray_query (#4848) + - spirv-val: Add SPV_KHR_ray_tracing instructions (#4871) + - Implement SPV_NV_bindless_texture related changes (#4847) + - spirv-val: Add OpConvertUToAccelerationStructureKHR (#4838) + - spirv-val: Add support for SPV_AMD_shader_early_and_late_fragment_tests (#4812) + - Optimizer + - Fold multiply and subtraction into FMA with negation (#4808) + - Add more folding for composite instructions (#4802) + - spirv-opt: add pass for interface variable scalar replacement (#4779) + - Don't try to unroll loop with step count 0. (#4769) + - spirv-opt: SPV_NV_bindless_texture related changes (#4870) + - Linker + - linker: Recalculate interface variables (#4784) + +v2022.2 2022-04-07 + - General + - Add OpModuleProcessed to debug opcode (#4694) + - Optimizer + - Complete handling of RayQueryKHR type (#4690) + - Have scalar replacement use undef instead of null (#4691) + - Optimize Instruction::Instruction (#4705) + - Handle propagation of arrays with decorations (#4717) + - spirv-opt: Add OpExecutionModeId support (#4719) + - Optimize Type::HashValue (#4707) + - Optimize DefUseManager allocations (#4709) + - Add pass to remove DontInline function control (#4747) + - Better handling of 0xFFFFFFFF when folding vector shuffle (#4743) + - Reset the id bound on the module in compact ids (#4744) + - spirv-opt: (WIP) Eliminate Dead Input Component Pass (#4720) + - Support SPV_KHR_uniform_group_instructions (#4734) + - Handle shaders without execution model in spread-volatile-semantics (#4766) + - Validator + - Fix handling of Nontemporal image operand (#4692) + - [spirv-val] Allow 0 Component Count for DebugTypeArray for Shader (#4706) + - spirv-val: Validate DebugTypeMatrix (#4732) + - spirv-val: Label Vulkan VUID 04734 (#4739) + - spirv-val: Label VUID 06491 (#4745) + - spirv-val: Disallow array of push constants (#4742) + - spirv-val: Label Vulkan RuntimeArray VUID (#4749) + - spirv-val: Add Vulkan Image VUID 06214 (#4750) + - spirv-val: Add Vulkan Dref not allowed 3D dim VUID (#4751) + - spirv-val: Label and add test for PSB Aligned (#4756) + - spirv-val: Add Vulkan 32-bit bit op Base (#4758) + - spirv-val: Add more Vulkan VUID labels (#4764) + - Diff + - Introduce spirv-diff (#4611) + - Stabilize the output of spirv-diff (#4698) + - spirv-diff: Handle OpSpecConstant array sizes (#4700) + - spirv-diff: Match OpSpecConstantComposite correctly (#4704) + - spirv-diff: Use GetSingleWord*Operand (#4768) + - spirv-diff: Basic support for OpTypeForwardPointer (#4761) + - spirv-diff: Fix OpTypeFunction matching w.r.t operand count (#4771) + +v2022.1 2022-01-26 + - General + - Add SPIR-V 1.6 support to wasm build (#4674) + - Improvements to disassembly within PassManager (#4677) + - Basic support for SPIR-V 1.6 (#4663) + - reflect debug (#4662) + - Fix endianness of string literals (#4622) + - Optimizer + - spirv-opt: add pass to Spread Volatile semantics (#4667) + - Fix constant propagation and folding of FClamp instructions (#4651) + - Manually fold floating point division by zero (#4637) + - Allow ADCE to remove dead inputs (#4629) + - Linker + - Linker improvements (#4679) + * test/linker: Code factorisation and small tweaks + * linker: Do not fail when going over limits + - Validator + - val: interface struct with builtins must be Block (#4665) + - Fuzzer + - Avoid id bound errors during opt fuzzing (#4658) + - Avoid uninitialised read when parsing hex float (#4646) + +v2021.4 2021-11-11 + - General + - Add a WebAssembly build (#3752) + - Make cxx exceptions controllable (#4591) + - Validator + - Improve decoration validation (#4490) + - Optimizer + - Add spirv-opt pass to replace descriptor accesses based on variable indices (#4574) + - Do not fold snegate feeding sdiv (#4600) + - Handle overflowing id in merge return (#4606) + - Fuzzer + - Add libFuzzer target for spirv-fuzz (#4434) + - Linter + +v2021.3 2021-08-24 + - General + - Initial support for SPV_KHR_integer_dot_product (#4327) + - Add non-semantic vulkan extended instruction set (#4362) + - Add common enum for debug info instructions from either opencl or vulkan (#4377) + - Validator + - Add validation for SPV_EXT_shader_atomic_float16_add (#4325) + - Disallow loading a runtime-sized array (#4473) + - spirv-val: Validate vulkan debug info similarly to opencl debug info (#4466) + - Optimizer + - spirv-opt: support SPV_EXT_shader_image_int64 (#4379) + - spirv-opt: Add dataflow analysis framework (#4402) + - Add control dependence analysis to opt (#4380) + - Add spirv-opt convert-to-sampled-image pass (#4340) + - spirv-opt: Add handling of vulkan debug info to DebugInfoManager (#4423) + - Fuzz + - spirv-fuzz: support AtomicLoad (#4330) + - spirv-fuzz: Support AtomicStore (#4440) + - spirv-fuzz: TransformationWrapVectorSynonym that rewrites scalar operations using vectors (#4376) + - spirv-fuzz: Add minimal SPIR-V example to test shaders (#4415) + - spirv-fuzz: support building using gn (#4365) + - Linter + - Add new target for spirv-lint (#4446) + - spirv-lint: add basic CLI argument handling (#4478) + - Add divergence analysis to linter (#4465) + +v2021.2 2021-06-18 + - General + - Support SPV_KHR_subgroup_uniform_control_flow (#4318) + - Support Intel extensions for fixed point and hls-float (#4321) + - Fix crash when optimizing shaders with DebugPrintf (#4280) + + - Validator + - Support Vulkan Storage Class for Execution Model (#4212) + + - Optimizer + - Handle SPV_KHR_vulkan_memory_model in dead-code elimination (#4320) + - Support folding OpBitcast with numeric constants (#4247) + + - Fuzz + - Add tests for MaybeGet* functions in fuzzerutil (#4284) + - Fix OutlineFunction in presence of unreachable blocks (#4308) + - Fix def-use update in PermutePhiOperands (#4309) + - Swap positions of two functions in a module (#4236) + v2021.1 2021-04-19 - General - Support SPV_KHR_linkonce_odr, SPV_KHR_expect_assume (#4161) @@ -15,10 +185,6 @@ v2021.1 2021-04-19 - Do not add too many dead blocks (#4217) - Add WGSL compatibility flag to context (#4193) - Add persistent state to the fuzzer (#4137) - - Debug Info - - Instrumentation - - Linker - - Reduce v2020.7 2021-02-16 - General diff --git a/CMakeLists.txt b/CMakeLists.txt index 55f84e6d..1b8fe92f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ include(GNUInstallDirs) set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_CXX_STANDARD 11) +option(ENABLE_RTTI "Enables RTTI" OFF) option(SPIRV_ALLOW_TIMERS "Allow timers via clock_gettime on supported platforms" ON) if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") @@ -48,13 +49,19 @@ elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") add_definitions(-DSPIRV_MAC) elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "iOS") add_definitions(-DSPIRV_IOS) +elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "tvOS") + add_definitions(-DSPIRV_TVOS) elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Android") add_definitions(-DSPIRV_ANDROID) set(SPIRV_TIMER_ENABLED ${SPIRV_ALLOW_TIMERS}) elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") add_definitions(-DSPIRV_FREEBSD) +elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "OpenBSD") + add_definitions(-DSPIRV_OPENBSD) elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Fuchsia") add_definitions(-DSPIRV_FUCHSIA) +elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "GNU") + add_definitions(-DSPIRV_GNU) else() message(FATAL_ERROR "Your platform '${CMAKE_SYSTEM_NAME}' is not supported!") endif() @@ -81,6 +88,10 @@ endif(SPIRV_BUILD_COMPRESSION) option(SPIRV_BUILD_FUZZER "Build spirv-fuzz" OFF) +set(SPIRV_LIB_FUZZING_ENGINE_LINK_OPTIONS "" CACHE STRING "Used by OSS-Fuzz to control, via link options, which fuzzing engine should be used") + +option(SPIRV_BUILD_LIBFUZZER_TARGETS "Build libFuzzer targets" OFF) + option(SPIRV_WERROR "Enable error on warning" ON) if(("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR (("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") AND (NOT CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC"))) set(COMPILER_IS_LIKE_GNU TRUE) @@ -109,7 +120,7 @@ if(${COMPILER_IS_LIKE_GNU}) set(SPIRV_WARNINGS ${SPIRV_WARNINGS} -Werror) endif() elseif(MSVC) - set(SPIRV_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS /wd4800) + set(SPIRV_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS /wd4800 /wd4819) if(${SPIRV_WERROR}) set(SPIRV_WARNINGS ${SPIRV_WARNINGS} /WX) @@ -157,6 +168,7 @@ endif() # Note this target provides no API stability guarantees. # # Ideally, all of these will go away - see https://github.com/KhronosGroup/SPIRV-Tools/issues/3909. +option(ENABLE_EXCEPTIONS_ON_MSVC "Build SPIRV-TOOLS with c++ exceptions enabled in MSVC" ON) option(SPIRV_TOOLS_BUILD_STATIC "Build ${SPIRV_TOOLS}-static target. ${SPIRV_TOOLS} will alias to ${SPIRV_TOOLS}-static or ${SPIRV_TOOLS}-shared based on BUILD_SHARED_LIBS" ON) if(SPIRV_TOOLS_BUILD_STATIC) set(SPIRV_TOOLS_FULL_VISIBILITY ${SPIRV_TOOLS}-static) @@ -176,11 +188,14 @@ function(spvtools_default_compile_options TARGET) target_compile_options(${TARGET} PRIVATE ${SPIRV_WARNINGS}) if (${COMPILER_IS_LIKE_GNU}) - target_compile_options(${TARGET} PRIVATE - -std=c++11 -fno-exceptions -fno-rtti) + target_compile_options(${TARGET} PRIVATE -std=c++11 -fno-exceptions) target_compile_options(${TARGET} PRIVATE -Wall -Wextra -Wno-long-long -Wshadow -Wundef -Wconversion -Wno-sign-conversion) + + if(NOT ENABLE_RTTI) + add_compile_options(-fno-rtti) + endif() # For good call stacks in profiles, keep the frame pointers. if(NOT "${SPIRV_PERF}" STREQUAL "") target_compile_options(${TARGET} PRIVATE -fno-omit-frame-pointer) @@ -205,7 +220,9 @@ function(spvtools_default_compile_options TARGET) if (MSVC) # Specify /EHs for exception handling. This makes using SPIRV-Tools as # dependencies in other projects easier. - target_compile_options(${TARGET} PRIVATE /EHs) + if(ENABLE_EXCEPTIONS_ON_MSVC) + target_compile_options(${TARGET} PRIVATE /EHs) + endif() endif() # For MinGW cross compile, statically link to the C++ runtime. @@ -286,7 +303,7 @@ endif() # Turn off if they take too long. option(SPIRV_CHECK_CONTEXT "In a debug build, check if the IR context is in a valid state." ON) if (${SPIRV_CHECK_CONTEXT}) - add_definitions(-DSPIRV_CHECK_CONTEXT) + add_compile_options($<$:-DSPIRV_CHECK_CONTEXT>) endif() # Precompiled header macro. Parameters are source file list and filename for pch cpp file. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b46ae31e..1eb8b689 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -98,13 +98,6 @@ should pay particular attention to: scenarios? The respective SPIR-V dialects are slightly different. * Changes are made to a container while iterating through it. You have to be careful that iterators are not invalidated or that elements are not skipped. -* C++11 and VS2013. We generally assume that we have a C++11 compliant - compiler. However, on Windows, we still support Visual Studio 2013, which is - not fully C++11 compliant. See - [here](https://msdn.microsoft.com/en-us/library/hh567368.aspx). In - particular, note that it does not provide default move-constructors or - move-assignments for classes. In general, r-value references do not work the - way you might assume they do. * For SPIR-V transforms: The module is changed, but the analyses are not updated. For example, a new instruction is added, but the def-use manager is not updated. Later on, it is possible that the def-use manager will be used, diff --git a/DEPS b/DEPS index 65aebf5a..3a5f6145 100644 --- a/DEPS +++ b/DEPS @@ -3,10 +3,10 @@ use_relative_paths = True vars = { 'github': 'https://github.com', - 'effcee_revision': '2ec8f8738118cc483b67c04a759fee53496c5659', - 'googletest_revision': 'b7d472f1225c5a64943821d8483fecb469d3f382', - 're2_revision': 'f8e389f3acdc2517562924239e2a188037393683', - 'spirv_headers_revision': 'bcf55210f13a4fa3c3d0963b509ff1070e434c79', + 'effcee_revision': '35912e1b7778ec2ddcff7e7188177761539e59e0', + 'googletest_revision': 'd9bb8412d60b993365abb53f00b6dad9b2c01b62', + 're2_revision': 'd2836d1b1c34c4e330a85a1006201db474bf2c8a', + 'spirv_headers_revision': '85a1ed200d50660786c1a88d9166e871123cce39', } deps = { diff --git a/README.md b/README.md index 3637305b..e951e37f 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,6 @@ headers, and XML registry. ## Downloads -[![Build status](https://ci.appveyor.com/api/projects/status/gpue87cesrx3pi0d/branch/master?svg=true)](https://ci.appveyor.com/project/Khronoswebmaster/spirv-tools/branch/master) Linux[![Linux Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_linux_clang_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_release.html) MacOS[![MacOS Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_macos_clang_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_release.html) Windows[![Windows Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_windows_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_release.html) @@ -45,6 +44,20 @@ following versions are ordered from oldest to newest: Use the `--version` option on each command line tool to see the software version. An API call reports the software version as a C-style string. +## Releases + +Some versions of SPIRV-Tools are tagged as stable releases (see +[tags](https://github.com/KhronosGroup/SPIRV-Tools/tags) on github). +These versions undergo extra testing. +Releases are not directly related to releases (or versions) of +[SPIRV-Headers][spirv-headers]. +Releases of SPIRV-Tools are tested against the version of SPIRV-Headers listed +in the [DEPS](DEPS) file. +The release generally uses the most recent compatible version of SPIRV-Headers +available at the time of release. +No version of SPIRV-Headers other than the one listed in the DEPS file is +guaranteed to work with the SPIRV-Tools release. + ## Supported features ### Assembler, binary parser, and disassembler @@ -199,6 +212,24 @@ issue](https://github.com/KhronosGroup/SPIRV-Tools/issues]) with "Fuzzer:" as the start of its title. +### Diff + +*Note:* The diff tool is still under development. + +The diff tool takes two SPIR-V files, either in binary or text format and +produces a diff-style comparison between the two. The instructions between the +src and dst modules are matched as best as the tool can, and output is produced +(in src id-space) that shows which instructions are removed in src, added in dst +or modified between them. The order of instructions are not retained. + +Matching instructions between two SPIR-V modules is not trivial, and thus a +number of heuristics are applied in this tool. In particular, without debug +information, match functions is nontrivial as they can be reordered. As such, +this tool is primarily useful to produce the diff of two SPIR-V modules derived +from the same source, for example before and after a modification to the shader, +before and after a transformation, or SPIR-V produced from different tools. + + ### Extras * [Utility filters](#utility-filters) @@ -242,6 +273,34 @@ Contributions via merge request are welcome. Changes should: We intend to maintain a linear history on the GitHub `master` branch. +### Getting the source + +Example of getting sources, assuming SPIRV-Tools is configured as a standalone project: + + git clone https://github.com/KhronosGroup/SPIRV-Tools.git spirv-tools + cd spirv-tools + + # Check out sources for dependencies, at versions known to work together, + # as listed in the DEPS file. + python3 utils/git-sync-deps + +For some kinds of development, you may need the latest sources from the third-party projects: + + git clone https://github.com/KhronosGroup/SPIRV-Headers.git spirv-tools/external/spirv-headers + git clone https://github.com/google/googletest.git spirv-tools/external/googletest + git clone https://github.com/google/effcee.git spirv-tools/external/effcee + git clone https://github.com/google/re2.git spirv-tools/external/re2 + +#### Dependency on Effcee + +Some tests depend on the [Effcee][effcee] library for stateful matching. +Effcee itself depends on [RE2][re2]. + +* If SPIRV-Tools is configured as part of a larger project that already uses + Effcee, then that project should include Effcee before SPIRV-Tools. +* Otherwise, SPIRV-Tools expects Effcee sources to appear in `external/effcee` + and RE2 sources to appear in `external/re2`. + ### Source code organization * `example`: demo code of using SPIRV-Tools APIs @@ -260,14 +319,6 @@ We intend to maintain a linear history on the GitHub `master` branch. * `test/`: Tests, using the [googletest][googletest] framework * `tools/`: Command line executables -Example of getting sources, assuming SPIRV-Tools is configured as a standalone project: - - git clone https://github.com/KhronosGroup/SPIRV-Tools.git spirv-tools - git clone https://github.com/KhronosGroup/SPIRV-Headers.git spirv-tools/external/spirv-headers - git clone https://github.com/google/googletest.git spirv-tools/external/googletest - git clone https://github.com/google/effcee.git spirv-tools/external/effcee - git clone https://github.com/google/re2.git spirv-tools/external/re2 - ### Tests The project contains a number of tests, used to drive development @@ -281,46 +332,12 @@ tests: `googletest` source into the `/external/googletest` directory before configuring and building the project. -*Note*: You must use a version of googletest that includes -[a fix][googletest-pull-612] for [googletest issue 610][googletest-issue-610]. -The fix is included on the googletest master branch any time after 2015-11-10. -In particular, googletest must be newer than version 1.7.0. - -### Dependency on Effcee - -Some tests depend on the [Effcee][effcee] library for stateful matching. -Effcee itself depends on [RE2][re2]. - -* If SPIRV-Tools is configured as part of a larger project that already uses - Effcee, then that project should include Effcee before SPIRV-Tools. -* Otherwise, SPIRV-Tools expects Effcee sources to appear in `external/effcee` - and RE2 sources to appear in `external/re2`. - - ## Build -Instead of building manually, you can also download the binaries for your -platform directly from the [master-tot release][master-tot-release] on GitHub. -Those binaries are automatically uploaded by the buildbots after successful -testing and they always reflect the current top of the tree of the master -branch. +*Note*: Prebuilt binaries are available from the [downloads](docs/downloads.md) page. -In order to build the code, you first need to sync the external repositories -that it depends on. Assume that `` is the root directory of the -checked out code: - -```sh -cd -git clone https://github.com/KhronosGroup/SPIRV-Headers.git external/spirv-headers -git clone https://github.com/google/effcee.git external/effcee -git clone https://github.com/google/re2.git external/re2 -git clone https://github.com/google/googletest.git external/googletest # optional - -``` - -*Note*: -The script `utils/git-sync-deps` can be used to checkout and/or update the -contents of the repos under `external/` instead of manually maintaining them. +First [get the sources](#getting-the-source). +Then build using CMake, Bazel, Android ndk-build, or the Emscripten SDK. ### Build using CMake You can build the project using [CMake][cmake]: @@ -348,7 +365,7 @@ option, like so: ```sh # In (the SPIRV-Tools repo root): -git clone --depth=1 --branch v3.13.0 https://github.com/protocolbuffers/protobuf external/protobuf +git clone --depth=1 --branch v3.13.0.1 https://github.com/protocolbuffers/protobuf external/protobuf # In your build directory: cmake [-G ] -DSPIRV_BUILD_FUZZER=ON @@ -365,6 +382,30 @@ You can also use [Bazel](https://bazel.build/) to build the project. cd bazel build :all ``` +### Build a node.js package using Emscripten + +The SPIRV-Tools core library can be built to a WebAssembly [node.js](https://nodejs.org) +module. The resulting `SpirvTools` WebAssembly module only exports methods to +assemble and disassemble SPIR-V modules. + +First, make sure you have the [Emscripten SDK](https://emscripten.org). +Then: + +```sh +cd +./source/wasm/build.sh +``` + +The resulting node package, with JavaScript and TypeScript bindings, is +written to `/out/web`. + +Note: This builds the package locally. It does *not* publish it to [npm](https://npmjs.org). + +To test the result: + +```sh +node ./test/wasm/test.js +``` ### Tools you'll need @@ -376,17 +417,19 @@ targets, you need to install CMake Version 2.8.12 or later. - [Python 3](http://www.python.org/): for utility scripts and running the test suite. - [Bazel](https://bazel.build/) (optional): if building the source with Bazel, -you need to install Bazel Version 0.29.1 on your machine. Other versions may +you need to install Bazel Version 5.0.0 on your machine. Other versions may also work, but are not verified. +- [Emscripten SDK](https://emscripten.org) (optional): if building the + WebAssembly module. SPIRV-Tools is regularly tested with the following compilers: On Linux -- GCC version 4.8.5 -- Clang version 3.8 +- GCC version 9.3 +- Clang version 10.0 On MacOS -- AppleClang 10.0 +- AppleClang 11.0 On Windows - Visual Studio 2015 @@ -421,7 +464,7 @@ via setting `SPIRV_TOOLS_EXTRA_DEFINITIONS`. For example, by setting it to `/D_ITERATOR_DEBUG_LEVEL=0` on Windows, you can disable checked iterators and iterator debugging. -### Android +### Android ndk-build SPIR-V Tools supports building static libraries `libSPIRV-Tools.a` and `libSPIRV-Tools-opt.a` for Android: @@ -442,11 +485,12 @@ $ANDROID_NDK/ndk-build -C ../android_test \ ``` ### Updating DEPS -Occasionally the entries in DEPS will need to be updated. This is done on demand -when there is a request to do this, often due to downstream breakages. There is -a script `utils/roll_deps.sh` provided, which will generate a patch with the -updated DEPS values. This will still need to be tested in your checkout to -confirm that there are no integration issues that need to be resolved. + +Occasionally the entries in [DEPS](DEPS) will need to be updated. This is done on +demand when there is a request to do this, often due to downstream breakages. +To update `DEPS`, run `utils/roll_deps.sh` and confirm that tests pass. +The script requires Chromium's +[`depot_tools`](https://chromium.googlesource.com/chromium/tools/depot_tools). ## Library @@ -598,6 +642,15 @@ This is experimental. * `spirv-cfg` - the control flow graph dumper * `/tools/cfg` +### Diff tool + +*Warning:* This functionality is under development, and is incomplete. + +The diff tool produces a diff-style comparison between two SPIR-V modules. + +* `spirv-diff` - the standalone diff tool + * ``/tools/diff` + ### Utility filters * `spirv-lesspipe.sh` - Automatically disassembles `.spv` binary files for the @@ -643,8 +696,32 @@ This is experimental. ### Tests -Tests are only built when googletest is found. Use `ctest` to run all the -tests. +Tests are only built when googletest is found. + +#### Running test with CMake + +Use `ctest -j ` to run all the tests. To run tests using all threads: +```shell +ctest -j$(nproc) +``` + +To run a single test target, use `ctest [-j ] -R `. For example, +you can run all `opt` tests with: +```shell +ctest -R 'spirv-tools-test_opt' +``` + +#### Running test with Bazel + +Use `bazel test :all` to run all tests. This will run tests in parallel by default. + +To run a single test target, specify `:my_test_target` instead of `:all`. Test target +names get printed when you run `bazel test :all`. For example, you can run +`opt_def_use_test` with: +```shell +bazel test :opt_def_use_test +``` + ## Future Work @@ -705,4 +782,3 @@ limitations under the License. [CMake]: https://cmake.org/ [cpp-style-guide]: https://google.github.io/styleguide/cppguide.html [clang-sanitizers]: http://clang.llvm.org/docs/UsersManual.html#controlling-code-generation -[master-tot-release]: https://github.com/KhronosGroup/SPIRV-Tools/releases/tag/master-tot diff --git a/build_defs.bzl b/build_defs.bzl index 30af3bd6..71891372 100644 --- a/build_defs.bzl +++ b/build_defs.bzl @@ -41,6 +41,7 @@ TEST_COPTS = COMMON_COPTS + select({ DEBUGINFO_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_debuginfo_grammar_unified1" CLDEBUGINFO100_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_opencl_debuginfo_100_grammar_unified1" +SHDEBUGINFO100_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_nonsemantic_shader_debuginfo_100_grammar_unified1" def generate_core_tables(version = None): if not version: @@ -67,7 +68,15 @@ def generate_core_tables(version = None): "--core-insts-output=$(location {3}) " + "--operand-kinds-output=$(location {4})" ).format(*fmtargs), - tools = [":generate_grammar_tables"], + cmd_bat = ( + "$(location :generate_grammar_tables) " + + "--spirv-core-grammar=$(location {0}) " + + "--extinst-debuginfo-grammar=$(location {1}) " + + "--extinst-cldebuginfo100-grammar=$(location {2}) " + + "--core-insts-output=$(location {3}) " + + "--operand-kinds-output=$(location {4})" + ).format(*fmtargs), + exec_tools = [":generate_grammar_tables"], visibility = ["//visibility:private"], ) @@ -96,7 +105,15 @@ def generate_enum_string_mapping(version = None): "--extension-enum-output=$(location {3}) " + "--enum-string-mapping-output=$(location {4})" ).format(*fmtargs), - tools = [":generate_grammar_tables"], + cmd_bat = ( + "$(location :generate_grammar_tables) " + + "--spirv-core-grammar=$(location {0}) " + + "--extinst-debuginfo-grammar=$(location {1}) " + + "--extinst-cldebuginfo100-grammar=$(location {2}) " + + "--extension-enum-output=$(location {3}) " + + "--enum-string-mapping-output=$(location {4})" + ).format(*fmtargs), + exec_tools = [":generate_grammar_tables"], visibility = ["//visibility:private"], ) @@ -117,7 +134,12 @@ def generate_opencl_tables(version = None): "--extinst-opencl-grammar=$(location {0}) " + "--opencl-insts-output=$(location {1})" ).format(*fmtargs), - tools = [":generate_grammar_tables"], + cmd_bat = ( + "$(location :generate_grammar_tables) " + + "--extinst-opencl-grammar=$(location {0}) " + + "--opencl-insts-output=$(location {1})" + ).format(*fmtargs), + exec_tools = [":generate_grammar_tables"], visibility = ["//visibility:private"], ) @@ -138,7 +160,12 @@ def generate_glsl_tables(version = None): "--extinst-glsl-grammar=$(location {0}) " + "--glsl-insts-output=$(location {1})" ).format(*fmtargs), - tools = [":generate_grammar_tables"], + cmd_bat = ( + "$(location :generate_grammar_tables) " + + "--extinst-glsl-grammar=$(location {0}) " + + "--glsl-insts-output=$(location {1})" + ).format(*fmtargs), + exec_tools = [":generate_grammar_tables"], visibility = ["//visibility:private"], ) @@ -160,7 +187,13 @@ def generate_vendor_tables(extension, operand_kind_prefix = ""): "--vendor-insts-output=$(location {1}) " + "--vendor-operand-kind-prefix={2}" ).format(*fmtargs), - tools = [":generate_grammar_tables"], + cmd_bat = ( + "$(location :generate_grammar_tables) " + + "--extinst-vendor-grammar=$(location {0}) " + + "--vendor-insts-output=$(location {1}) " + + "--vendor-operand-kind-prefix={2}" + ).format(*fmtargs), + exec_tools = [":generate_grammar_tables"], visibility = ["//visibility:private"], ) @@ -178,7 +211,12 @@ def generate_extinst_lang_headers(name, grammar = None): "--extinst-grammar=$< " + "--extinst-output-path=$(location {0})" ).format(*fmtargs), - tools = [":generate_language_headers"], + cmd_bat = ( + "$(location :generate_language_headers) " + + "--extinst-grammar=$< " + + "--extinst-output-path=$(location {0})" + ).format(*fmtargs), + exec_tools = [":generate_language_headers"], visibility = ["//visibility:private"], ) @@ -201,6 +239,23 @@ def base_test(name, srcs, deps = []): ] + deps, ) +def lint_test(name, srcs, deps = []): + if name[-5:] != "_test": + name = name + "_test" + native.cc_test( + name = "lint_" + name, + srcs = srcs, + compatible_with = [], + copts = TEST_COPTS, + size = "large", + deps = [ + ":spirv_tools_lint", + "@com_google_googletest//:gtest_main", + "@com_google_googletest//:gtest", + "@com_google_effcee//:effcee", + ] + deps, + ) + def link_test(name, srcs, deps = []): if name[-5:] != "_test": name = name + "_test" diff --git a/docs/downloads.md b/docs/downloads.md index 9c7d8567..168937a7 100644 --- a/docs/downloads.md +++ b/docs/downloads.md @@ -1,14 +1,28 @@ # Downloads -Download the latest builds. -## Release +## Latest builds + +Download the latest builds of the [master](https://github.com/KhronosGroup/SPIRV-Tools/tree/master) branch. + +### Release build | Windows | Linux | MacOS | | --- | --- | --- | | [MSVC 2017](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_release.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_release.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_release.html) | | | [gcc](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_gcc_release.html) | | -## Debug +### Debug build | Windows | Linux | MacOS | | --- | --- | --- | | [MSVC 2017](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_debug.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_debug.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_debug.html) | | | [gcc](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_gcc_debug.html) | | + + +## Vulkan SDK + +SPIRV-Tools is published as part of the [LunarG Vulkan SDK](https://www.lunarg.com/vulkan-sdk/). +The Vulkan SDK is updated approximately every six weeks. + +## Android NDK + +SPIRV-Tools host executables, and library sources are published as +part of the [Android NDK](https://developer.android.com/ndk/downloads). diff --git a/include/spirv-tools/instrument.hpp b/include/spirv-tools/instrument.hpp index 05a0472e..a19491fd 100644 --- a/include/spirv-tools/instrument.hpp +++ b/include/spirv-tools/instrument.hpp @@ -178,6 +178,7 @@ static const int kInstErrorBuffOOBUniform = 4; static const int kInstErrorBuffOOBStorage = 5; static const int kInstErrorBuffOOBUniformTexel = 6; static const int kInstErrorBuffOOBStorageTexel = 7; +static const int kInstErrorMax = kInstErrorBuffOOBStorageTexel; // Direct Input Buffer Offsets // diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h index 344c3c01..b549efba 100644 --- a/include/spirv-tools/libspirv.h +++ b/include/spirv-tools/libspirv.h @@ -113,6 +113,9 @@ typedef enum spv_endianness_t { // Sometimes we also need to be able to express the fact that an operand // is a member of an optional tuple of values. In that case the first member // would be optional, and the subsequent members would be required. +// +// NOTE: Although we don't promise binary compatibility, as a courtesy, please +// add new enum values at the end. typedef enum spv_operand_type_t { // A sentinel value. SPV_OPERAND_TYPE_NONE = 0, @@ -167,12 +170,8 @@ typedef enum spv_operand_type_t { SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS, // SPIR-V Sec 3.29 SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO, // SPIR-V Sec 3.30 SPV_OPERAND_TYPE_CAPABILITY, // SPIR-V Sec 3.31 - SPV_OPERAND_TYPE_RAY_FLAGS, // SPIR-V Sec 3.RF - SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION, // SPIR-V Sec 3.RQIntersection - SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE, // SPIR-V Sec - // 3.RQCommitted - SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE, // SPIR-V Sec - // 3.RQCandidate + + // NOTE: New concrete enum values should be added at the end. // Set 5: Operands that are a single word bitmask. // Sometimes a set bit indicates the instruction requires still more operands. @@ -184,7 +183,10 @@ typedef enum spv_operand_type_t { SPV_OPERAND_TYPE_MEMORY_ACCESS, // SPIR-V Sec 3.26 SPV_OPERAND_TYPE_FRAGMENT_SHADING_RATE, // SPIR-V Sec 3.FSR -// The remaining operand types are only used internally by the assembler. +// NOTE: New concrete enum values should be added at the end. + +// The "optional" and "variable" operand types are only used internally by +// the assembler and the binary parser. // There are two categories: // Optional : expands to 0 or 1 operand, like ? in regular expressions. // Variable : expands to 0, 1 or many operands or pairs of operands. @@ -269,6 +271,14 @@ typedef enum spv_operand_type_t { // A value enum from https://github.com/KhronosGroup/SPIRV-Headers/pull/177 SPV_OPERAND_TYPE_OVERFLOW_MODES, + // Concrete operand types for the provisional Vulkan ray tracing feature. + SPV_OPERAND_TYPE_RAY_FLAGS, // SPIR-V Sec 3.RF + SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION, // SPIR-V Sec 3.RQIntersection + SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE, // SPIR-V Sec + // 3.RQCommitted + SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE, // SPIR-V Sec + // 3.RQCandidate + // Concrete operand types for integer dot product. // Packed vector format SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT, // SPIR-V Sec 3.x @@ -299,6 +309,7 @@ typedef enum spv_ext_inst_type_t { SPV_EXT_INST_TYPE_DEBUGINFO, SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION, + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100, // Multiple distinct extended instruction set types could return this // value, if they are prefixed with NonSemantic. and are otherwise @@ -471,6 +482,7 @@ SPIRV_TOOLS_EXPORT const char* spvSoftwareVersionDetailsString(void); // SPV_ENV_VULKAN_1_1 -> SPIR-V 1.3 // SPV_ENV_VULKAN_1_1_SPIRV_1_4 -> SPIR-V 1.4 // SPV_ENV_VULKAN_1_2 -> SPIR-V 1.5 +// SPV_ENV_VULKAN_1_3 -> SPIR-V 1.6 // Consult the description of API entry points for specific rules. typedef enum { SPV_ENV_UNIVERSAL_1_0, // SPIR-V 1.0 latest revision, no other restrictions. @@ -505,6 +517,11 @@ typedef enum { SPV_ENV_UNIVERSAL_1_5, // SPIR-V 1.5 latest revision, no other restrictions. SPV_ENV_VULKAN_1_2, // Vulkan 1.2 latest revision. + + SPV_ENV_UNIVERSAL_1_6, // SPIR-V 1.6 latest revision, no other restrictions. + SPV_ENV_VULKAN_1_3, // Vulkan 1.3 latest revision. + + SPV_ENV_MAX // Keep this as the last enum value. } spv_target_env; // SPIR-V Validator can be parameterized with the following Universal Limits. @@ -543,7 +560,7 @@ SPIRV_TOOLS_EXPORT bool spvParseVulkanEnv(uint32_t vulkan_ver, // Creates a context object for most of the SPIRV-Tools API. // Returns null if env is invalid. // -// See specific API calls for how the target environment is interpeted +// See specific API calls for how the target environment is interpreted // (particularly assembly and validation). SPIRV_TOOLS_EXPORT spv_context spvContextCreate(spv_target_env env); @@ -595,7 +612,7 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetRelaxLogicalPointer( // set that option. // 2) Pointers that are pass as parameters to function calls do not have to // match the storage class of the formal parameter. -// 3) Pointers that are actaul parameters on function calls do not have to point +// 3) Pointers that are actual parameters on function calls do not have to point // to the same type pointed as the formal parameter. The types just need to // logically match. // 4) GLSLstd450 Interpolate* instructions can have a load of an interpolant @@ -621,7 +638,7 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetUniformBufferStandardLayout( // Records whether the validator should use "scalar" block layout rules. // Scalar layout rules are more permissive than relaxed block layout. // -// See Vulkan extnesion VK_EXT_scalar_block_layout. The scalar alignment is +// See Vulkan extension VK_EXT_scalar_block_layout. The scalar alignment is // defined as follows: // - scalar alignment of a scalar is the scalar size // - scalar alignment of a vector is the scalar alignment of its component @@ -653,6 +670,10 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetSkipBlockLayout( SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetAllowLocalSizeId( spv_validator_options options, bool val); +// Whether friendly names should be used in validation error messages. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetFriendlyNames( + spv_validator_options options, bool val); + // Creates an optimizer options object with default options. Returns a valid // options object. The object remains valid until it is passed into // |spvOptimizerOptionsDestroy|. diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp index 0c31a182..408e3ebb 100644 --- a/include/spirv-tools/libspirv.hpp +++ b/include/spirv-tools/libspirv.hpp @@ -36,7 +36,7 @@ class Context { public: // Constructs a context targeting the given environment |env|. // - // See specific API calls for how the target environment is interpeted + // See specific API calls for how the target environment is interpreted // (particularly assembly and validation). // // The constructed instance will have an empty message consumer, which just @@ -115,6 +115,12 @@ class ValidatorOptions { spvValidatorOptionsSetSkipBlockLayout(options_, val); } + // Enables LocalSizeId decorations where the environment would not otherwise + // allow them. + void SetAllowLocalSizeId(bool val) { + spvValidatorOptionsSetAllowLocalSizeId(options_, val); + } + // Records whether or not the validator should relax the rules on pointer // usage in logical addressing mode. // @@ -133,7 +139,7 @@ class ValidatorOptions { // set that option. // 2) Pointers that are pass as parameters to function calls do not have to // match the storage class of the formal parameter. - // 3) Pointers that are actaul parameters on function calls do not have to + // 3) Pointers that are actual parameters on function calls do not have to // point to the same type pointed as the formal parameter. The types just // need to logically match. // 4) GLSLstd450 Interpolate* instructions can have a load of an interpolant @@ -142,6 +148,11 @@ class ValidatorOptions { spvValidatorOptionsSetBeforeHlslLegalization(options_, val); } + // Whether friendly names should be used in validation error messages. + void SetFriendlyNames(bool val) { + spvValidatorOptionsSetFriendlyNames(options_, val); + } + private: spv_validator_options options_; }; diff --git a/include/spirv-tools/linter.hpp b/include/spirv-tools/linter.hpp index 57d1b4e9..52ed5a46 100644 --- a/include/spirv-tools/linter.hpp +++ b/include/spirv-tools/linter.hpp @@ -35,7 +35,7 @@ class Linter { void SetMessageConsumer(MessageConsumer consumer); // Returns a reference to the registered message consumer. - const MessageConsumer& consumer() const; + const MessageConsumer& Consumer() const; bool Run(const uint32_t* binary, size_t binary_size); diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index e8b5b698..94973560 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "libspirv.hpp" @@ -27,7 +28,8 @@ namespace spvtools { namespace opt { class Pass; -} +struct DescriptorSetAndBinding; +} // namespace opt // C++ interface for SPIR-V optimization functionalities. It wraps the context // (including target environment and the corresponding SPIR-V grammar) and @@ -41,7 +43,7 @@ class Optimizer { // consumed by the RegisterPass() method. Tokens are one-time objects that // only support move; copying is not allowed. struct PassToken { - struct Impl; // Opaque struct for holding inernal data. + struct Impl; // Opaque struct for holding internal data. PassToken(std::unique_ptr); @@ -225,16 +227,17 @@ Optimizer::PassToken CreateNullPass(); // Creates a strip-debug-info pass. // A strip-debug-info pass removes all debug instructions (as documented in -// Section 3.32.2 of the SPIR-V spec) of the SPIR-V module to be optimized. +// Section 3.42.2 of the SPIR-V spec) of the SPIR-V module to be optimized. Optimizer::PassToken CreateStripDebugInfoPass(); -// Creates a strip-reflect-info pass. -// A strip-reflect-info pass removes all reflections instructions. -// For now, this is limited to removing decorations defined in -// SPV_GOOGLE_hlsl_functionality1. The coverage may expand in -// the future. +// [Deprecated] This will create a strip-nonsemantic-info pass. See below. Optimizer::PassToken CreateStripReflectInfoPass(); +// Creates a strip-nonsemantic-info pass. +// A strip-nonsemantic-info pass removes all reflections and explicitly +// non-semantic instructions. +Optimizer::PassToken CreateStripNonSemanticInfoPass(); + // Creates an eliminate-dead-functions pass. // An eliminate-dead-functions pass will remove all functions that are not in // the call trees rooted at entry points and exported functions. These @@ -293,11 +296,11 @@ Optimizer::PassToken CreateFreezeSpecConstantValuePass(); // and can be changed in future. A spec constant is foldable if all of its // value(s) can be determined from the module. E.g., an integer spec constant // defined with OpSpecConstantOp instruction can be folded if its value won't -// change later. This pass will replace the original OpSpecContantOp instruction -// with an OpConstant instruction. When folding composite spec constants, -// new instructions may be inserted to define the components of the composite -// constant first, then the original spec constants will be replaced by -// OpConstantComposite instructions. +// change later. This pass will replace the original OpSpecConstantOp +// instruction with an OpConstant instruction. When folding composite spec +// constants, new instructions may be inserted to define the components of the +// composite constant first, then the original spec constants will be replaced +// by OpConstantComposite instructions. // // There are some operations not supported yet: // OpSConvert, OpFConvert, OpQuantizeToF16 and @@ -323,7 +326,7 @@ Optimizer::PassToken CreateUnifyConstantPass(); // Creates a eliminate-dead-constant pass. // A eliminate-dead-constant pass removes dead constants, including normal -// contants defined by OpConstant, OpConstantComposite, OpConstantTrue, or +// constants defined by OpConstant, OpConstantComposite, OpConstantTrue, or // OpConstantFalse and spec constants defined by OpSpecConstant, // OpSpecConstantComposite, OpSpecConstantTrue, OpSpecConstantFalse or // OpSpecConstantOp. @@ -387,7 +390,7 @@ Optimizer::PassToken CreateInlineOpaquePass(); // Only modules with relaxed logical addressing (see opt/instruction.h) are // currently processed. // -// This pass is most effective if preceeded by Inlining and +// This pass is most effective if preceded by Inlining and // LocalAccessChainConvert. This pass will reduce the work needed to be done // by LocalSingleStoreElim and LocalMultiStoreElim. // @@ -405,7 +408,7 @@ Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass(); // Note that some branches and blocks may be left to avoid creating invalid // control flow. Improving this is left to future work. // -// This pass is most effective when preceeded by passes which eliminate +// This pass is most effective when preceded by passes which eliminate // local loads and stores, effectively propagating constant values where // possible. Optimizer::PassToken CreateDeadBranchElimPass(); @@ -422,7 +425,7 @@ Optimizer::PassToken CreateDeadBranchElimPass(); // are currently processed. Currently modules with any extensions enabled are // not processed. This is left for future work. // -// This pass is most effective if preceeded by Inlining and +// This pass is most effective if preceded by Inlining and // LocalAccessChainConvert. LocalSingleStoreElim and LocalSingleBlockElim // will reduce the work that this pass has to do. Optimizer::PassToken CreateLocalMultiStoreElimPass(); @@ -512,7 +515,20 @@ Optimizer::PassToken CreateDeadInsertElimPass(); // Conversion, which tends to cause cycles of dead code to be left after // Store/Load elimination passes are completed. These cycles cannot be // eliminated with standard dead code elimination. +// +// If |preserve_interface| is true, all non-io variables in the entry point +// interface are considered live and are not eliminated. This mode is needed +// by GPU-Assisted validation instrumentation, where a change in the interface +// is not allowed. Optimizer::PassToken CreateAggressiveDCEPass(); +Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface); + +// Creates a remove-unused-interface-variables pass. +// Removes variables referenced on the |OpEntryPoint| instruction that are not +// referenced in the entry point function or any function in its call tree. Note +// that this could cause the shader interface to no longer match other shader +// stages. +Optimizer::PassToken CreateRemoveUnusedInterfaceVariablesPass(); // Creates an empty pass. // This is deprecated and will be removed. @@ -614,7 +630,7 @@ Optimizer::PassToken CreateRedundancyEliminationPass(); Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit = 100); // Create a private to local pass. -// This pass looks for variables delcared in the private storage class that are +// This pass looks for variables declared in the private storage class that are // used in only one function. Those variables are moved to the function storage // class in the function that they are used. Optimizer::PassToken CreatePrivateToLocalPass(); @@ -692,8 +708,11 @@ Optimizer::PassToken CreateVectorDCEPass(); // Create a pass to reduce the size of loads. // This pass looks for loads of structures where only a few of its members are // used. It replaces the loads feeding an OpExtract with an OpAccessChain and -// a load of the specific elements. -Optimizer::PassToken CreateReduceLoadSizePass(); +// a load of the specific elements. The parameter is a threshold to determine +// whether we have to replace the load or not. If the ratio of the used +// components of the load is less than the threshold, we replace the load. +Optimizer::PassToken CreateReduceLoadSizePass( + double load_replacement_threshold = 0.9); // Create a pass to combine chained access chains. // This pass looks for access chains fed by other access chains and combines @@ -816,6 +835,26 @@ Optimizer::PassToken CreateFixStorageClassPass(); // inclusive. Optimizer::PassToken CreateGraphicsRobustAccessPass(); +// Create a pass to spread Volatile semantics to variables with SMIDNV, +// WarpIDNV, SubgroupSize, SubgroupLocalInvocationId, SubgroupEqMask, +// SubgroupGeMask, SubgroupGtMask, SubgroupLeMask, or SubgroupLtMask BuiltIn +// decorations or OpLoad for them when the shader model is the ray generation, +// closest hit, miss, intersection, or callable. This pass can be used for +// VUID-StandaloneSpirv-VulkanMemoryModel-04678 and +// VUID-StandaloneSpirv-VulkanMemoryModel-04679 (See "Standalone SPIR-V +// Validation" section of Vulkan spec "Appendix A: Vulkan Environment for +// SPIR-V"). When the SPIR-V version is 1.6 or above, the pass also spreads +// the Volatile semantics to a variable with HelperInvocation BuiltIn decoration +// in the fragement shader. +Optimizer::PassToken CreateSpreadVolatileSemanticsPass(); + +// Create a pass to replace a descriptor access using variable index. +// This pass replaces every access using a variable index to array variable +// |desc| that has a DescriptorSet and Binding decorations with a constant +// element of the array. In order to replace the access using a variable index +// with the constant element, it uses a switch statement. +Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass(); + // Create descriptor scalar replacement pass. // This pass replaces every array variable |desc| that has a DescriptorSet and // Binding decorations with a new variable for each element of the array. @@ -847,6 +886,37 @@ Optimizer::PassToken CreateAmdExtToKhrPass(); // propagated into their final positions. Optimizer::PassToken CreateInterpolateFixupPass(); +// Removes unused components from composite input variables. Current +// implementation just removes trailing unused components from input arrays. +// The pass performs best after maximizing dead code removal. A subsequent dead +// code elimination pass would be beneficial in removing newly unused component +// types. +Optimizer::PassToken CreateEliminateDeadInputComponentsPass(); + +// Creates a convert-to-sampled-image pass to convert images and/or +// samplers with given pairs of descriptor set and binding to sampled image. +// If a pair of an image and a sampler have the same pair of descriptor set and +// binding that is one of the given pairs, they will be converted to a sampled +// image. In addition, if only an image has the descriptor set and binding that +// is one of the given pairs, it will be converted to a sampled image as well. +Optimizer::PassToken CreateConvertToSampledImagePass( + const std::vector& + descriptor_set_binding_pairs); + +// Create an interface-variable-scalar-replacement pass that replaces array or +// matrix interface variables with a series of scalar or vector interface +// variables. For example, it replaces `float3 foo[2]` with `float3 foo0, foo1`. +Optimizer::PassToken CreateInterfaceVariableScalarReplacementPass(); + +// Creates a remove-dont-inline pass to remove the |DontInline| function control +// from every function in the module. This is useful if you want the inliner to +// inline these functions some reason. +Optimizer::PassToken CreateRemoveDontInlinePass(); +// Create a fix-func-call-param pass to fix non memory argument for the function +// call, as spirv-validation requires function parameters to be an memory +// object, currently the pass would remove accesschain pointer argument passed +// to the function +Optimizer::PassToken CreateFixFuncCallArgumentsPass(); } // namespace spvtools #endif // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_ diff --git a/kokoro/linux-clang-ubsan/build.sh b/kokoro/linux-clang-ubsan/build.sh new file mode 100755 index 00000000..b5941e34 --- /dev/null +++ b/kokoro/linux-clang-ubsan/build.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Copyright (c) 2021 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. +# +# Linux Build Script. + +# Fail on any error. +set -e +# Display commands being run. +set -x + +SCRIPT_DIR=`dirname "$BASH_SOURCE"` +source $SCRIPT_DIR/../scripts/linux/build.sh UBSAN clang cmake diff --git a/kokoro/linux-clang-ubsan/continuous.cfg b/kokoro/linux-clang-ubsan/continuous.cfg new file mode 100644 index 00000000..cb5535e1 --- /dev/null +++ b/kokoro/linux-clang-ubsan/continuous.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2021 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. + +# Continuous build configuration. +build_file: "SPIRV-Tools/kokoro/linux-clang-ubsan/build.sh" diff --git a/kokoro/linux-clang-ubsan/presubmit.cfg b/kokoro/linux-clang-ubsan/presubmit.cfg new file mode 100644 index 00000000..029c74a5 --- /dev/null +++ b/kokoro/linux-clang-ubsan/presubmit.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2021 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. + +# Presubmit build configuration. +build_file: "SPIRV-Tools/kokoro/linux-clang-ubsan/build.sh" diff --git a/kokoro/macos-clang-release-bazel/build.sh b/kokoro/macos-clang-release-bazel/build.sh index d2a516f5..c62611ab 100644 --- a/kokoro/macos-clang-release-bazel/build.sh +++ b/kokoro/macos-clang-release-bazel/build.sh @@ -31,14 +31,14 @@ cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31 git clone --depth=1 https://github.com/google/effcee external/effcee git clone --depth=1 https://github.com/google/re2 external/re2 -# Get bazel 0.29.1. -gsutil cp gs://bazel/0.29.1/release/bazel-0.29.1-darwin-x86_64 . -chmod +x bazel-0.29.1-darwin-x86_64 +# Get bazel 5.0.0 +gsutil cp gs://bazel/5.0.0/release/bazel-5.0.0-darwin-x86_64 . +chmod +x bazel-5.0.0-darwin-x86_64 echo $(date): Build everything... -./bazel-0.29.1-darwin-x86_64 build :all +./bazel-5.0.0-darwin-x86_64 build :all echo $(date): Build completed. echo $(date): Starting bazel test... -./bazel-0.29.1-darwin-x86_64 test :all +./bazel-5.0.0-darwin-x86_64 test :all echo $(date): Bazel test completed. diff --git a/kokoro/scripts/linux/build-docker.sh b/kokoro/scripts/linux/build-docker.sh index ba216987..80043b8a 100755 --- a/kokoro/scripts/linux/build-docker.sh +++ b/kokoro/scripts/linux/build-docker.sh @@ -51,14 +51,14 @@ clone_if_missing https://github.com/google/googletest external/googlete pushd external/googletest; git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7; popd clone_if_missing https://github.com/google/effcee external/effcee --depth=1 clone_if_missing https://github.com/google/re2 external/re2 --depth=1 -clone_if_missing https://github.com/protocolbuffers/protobuf external/protobuf --branch v3.13.0 +clone_if_missing https://github.com/protocolbuffers/protobuf external/protobuf --branch v3.13.0.1 if [ $TOOL = "cmake" ]; then using cmake-3.17.2 using ninja-1.10.0 # Possible configurations are: - # ASAN, COVERAGE, RELEASE, DEBUG, DEBUG_EXCEPTION, RELEASE_MINGW + # ASAN, UBSAN, COVERAGE, RELEASE, DEBUG, DEBUG_EXCEPTION, RELEASE_MINGW BUILD_TYPE="Debug" if [ $CONFIG = "RELEASE" ] || [ $CONFIG = "RELEASE_MINGW" ]; then BUILD_TYPE="RelWithDebInfo" @@ -69,6 +69,13 @@ if [ $TOOL = "cmake" ]; then if [ $CONFIG = "ASAN" ]; then ADDITIONAL_CMAKE_FLAGS="-DSPIRV_USE_SANITIZER=address,bounds,null" [ $COMPILER = "clang" ] || { echo "$CONFIG requires clang"; exit 1; } + elif [ $CONFIG = "UBSAN" ]; then + # UBSan requires RTTI, and by default UBSan does not exit when errors are + # encountered - additional compiler options are required to force this. + # The -DSPIRV_USE_SANITIZER=undefined option instructs SPIR-V Tools to be + # built with UBSan enabled. + ADDITIONAL_CMAKE_FLAGS="-DSPIRV_USE_SANITIZER=undefined -DENABLE_RTTI=ON -DCMAKE_C_FLAGS=-fno-sanitize-recover=all -DCMAKE_CXX_FLAGS=-fno-sanitize-recover=all" + [ $COMPILER = "clang" ] || { echo "$CONFIG requires clang"; exit 1; } elif [ $CONFIG = "COVERAGE" ]; then ADDITIONAL_CMAKE_FLAGS="-DENABLE_CODE_COVERAGE=ON" SKIP_TESTS="True" @@ -79,6 +86,10 @@ if [ $TOOL = "cmake" ]; then SKIP_TESTS="True" fi + if [ $COMPILER = "clang" ]; then + ADDITIONAL_CMAKE_FLAGS="$ADDITIONAL_CMAKE_FLAGS -DSPIRV_BUILD_LIBFUZZER_TARGETS=ON" + fi + clean_dir "$ROOT_DIR/build" cd "$ROOT_DIR/build" @@ -184,7 +195,7 @@ elif [ $TOOL = "android-ndk-build" ]; then echo $(date): ndk-build completed. elif [ $TOOL = "bazel" ]; then - using bazel-3.1.0 + using bazel-5.0.0 echo $(date): Build everything... bazel build :all diff --git a/kokoro/scripts/linux/build.sh b/kokoro/scripts/linux/build.sh index 4731ebdc..85d4b61a 100644 --- a/kokoro/scripts/linux/build.sh +++ b/kokoro/scripts/linux/build.sh @@ -26,7 +26,9 @@ COMPILER=$2 TOOL=$3 BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT} +# "--privileged" is required to run ptrace in the asan builds. docker run --rm -i \ + --privileged \ --volume "${ROOT_DIR}:${ROOT_DIR}" \ --volume "${KOKORO_ARTIFACTS_DIR}:${KOKORO_ARTIFACTS_DIR}" \ --workdir "${ROOT_DIR}" \ diff --git a/kokoro/scripts/macos/build.sh b/kokoro/scripts/macos/build.sh index 44c9a412..46128238 100644 --- a/kokoro/scripts/macos/build.sh +++ b/kokoro/scripts/macos/build.sh @@ -36,7 +36,7 @@ git clone https://github.com/google/googletest external/googletest cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. git clone --depth=1 https://github.com/google/effcee external/effcee git clone --depth=1 https://github.com/google/re2 external/re2 -git clone --depth=1 --branch v3.13.0 https://github.com/protocolbuffers/protobuf external/protobuf +git clone --depth=1 --branch v3.13.0.1 https://github.com/protocolbuffers/protobuf external/protobuf mkdir build && cd $SRC/build diff --git a/kokoro/scripts/windows/build.bat b/kokoro/scripts/windows/build.bat index a4ce792a..8c9d6892 100644 --- a/kokoro/scripts/windows/build.bat +++ b/kokoro/scripts/windows/build.bat @@ -22,7 +22,7 @@ set BUILD_TYPE=%1 set VS_VERSION=%2 :: Force usage of python 3.6 -set PATH=C:\python36;"C:\Program Files\CMake\bin";%PATH% +set PATH=C:\python36;"C:\Program Files\cmake-3.23.1-windows-x86_64\bin";%PATH% cd %SRC% git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers @@ -30,7 +30,7 @@ git clone https://github.com/google/googletest external/googletest cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. git clone --depth=1 https://github.com/google/effcee external/effcee git clone --depth=1 https://github.com/google/re2 external/re2 -git clone --depth=1 --branch v3.13.0 https://github.com/protocolbuffers/protobuf external/protobuf +git clone --depth=1 --branch v3.13.0.1 https://github.com/protocolbuffers/protobuf external/protobuf :: ######################################### :: set up msvc build env @@ -41,9 +41,6 @@ if %VS_VERSION% == 2017 ( ) else if %VS_VERSION% == 2015 ( call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 echo "Using VS 2015..." -) else if %VS_VERSION% == 2013 ( - call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x64 - echo "Using VS 2013..." ) cd %SRC% @@ -62,15 +59,8 @@ if "%KOKORO_GITHUB_COMMIT%." == "." ( set CMAKE_FLAGS=-DCMAKE_INSTALL_PREFIX=%KOKORO_ARTIFACTS_DIR%\install -GNinja -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DRE2_BUILD_TESTING=OFF -DCMAKE_C_COMPILER=cl.exe -DCMAKE_CXX_COMPILER=cl.exe -:: Skip building tests for VS2013 -if %VS_VERSION% == 2013 ( - set CMAKE_FLAGS=%CMAKE_FLAGS% -DSPIRV_SKIP_TESTS=ON -) - -:: Skip building spirv-fuzz for VS2013; it relies on protobufs which VS2013 cannot handle. -if %VS_VERSION% NEQ 2013 ( - set CMAKE_FLAGS=%CMAKE_FLAGS% -DSPIRV_BUILD_FUZZER=ON -) +:: Build spirv-fuzz +set CMAKE_FLAGS=%CMAKE_FLAGS% -DSPIRV_BUILD_FUZZER=ON cmake %CMAKE_FLAGS% .. @@ -85,13 +75,11 @@ echo "Build Completed %DATE% %TIME%" setlocal ENABLEDELAYEDEXPANSION :: ################################################ -:: Run the tests (We no longer run tests on VS2013) +:: Run the tests :: ################################################ echo "Running Tests... %DATE% %TIME%" -if %VS_VERSION% NEQ 2013 ( - ctest -C %BUILD_TYPE% --output-on-failure --timeout 300 - if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL! -) +ctest -C %BUILD_TYPE% --output-on-failure --timeout 300 +if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL! echo "Tests Completed %DATE% %TIME%" :: ################################################ @@ -106,4 +94,3 @@ rm -rf %SRC%\build rm -rf %SRC%\external exit /b 0 - diff --git a/kokoro/windows-msvc-2015-release-bazel/build.bat b/kokoro/windows-msvc-2015-release-bazel/build.bat index 2f721af4..de20b0aa 100644 --- a/kokoro/windows-msvc-2015-release-bazel/build.bat +++ b/kokoro/windows-msvc-2015-release-bazel/build.bat @@ -30,14 +30,13 @@ git clone --depth=1 https://github.com/google/effcee external/effce git clone --depth=1 https://github.com/google/re2 external/re2 :: REM Install Bazel. -wget -q https://github.com/bazelbuild/bazel/releases/download/0.29.1/bazel-0.29.1-windows-x86_64.zip -unzip -q bazel-0.29.1-windows-x86_64.zip +wget -q https://github.com/bazelbuild/bazel/releases/download/5.0.0/bazel-5.0.0-windows-x86_64.zip +unzip -q bazel-5.0.0-windows-x86_64.zip :: Set up MSVC call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 set BAZEL_VS=C:\Program Files (x86)\Microsoft Visual Studio 14.0 set BAZEL_VC=C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC -set BAZEL_SH=c:\tools\msys64\usr\bin\bash.exe set BAZEL_PYTHON=c:\tools\python2\python.exe :: ######################################### diff --git a/read.me b/read.me new file mode 100644 index 00000000..e69de29b diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 8247f6f8..668579ac 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -17,10 +17,10 @@ set(VIMSYNTAX_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_vim_sy set(XML_REGISTRY_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_registry_tables.py") set(LANG_HEADER_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_language_headers.py") -# For now, assume the DebugInfo grammar file is in the current directory. -# It might migrate to SPIRV-Headers. +# Pull in grammar files that have migrated to SPIRV-Headers set(DEBUGINFO_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.debuginfo.grammar.json") set(CLDEBUGINFO100_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json") +set(VKDEBUGINFO100_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json") # macro() definitions are used in the following because we need to append .inc # file paths into some global lists (*_CPP_DEPENDS). And those global lists are @@ -113,6 +113,9 @@ endmacro(spvtools_opencl_tables) macro(spvtools_vendor_tables VENDOR_TABLE SHORT_NAME OPERAND_KIND_PREFIX) set(INSTS_FILE "${spirv-tools_BINARY_DIR}/${VENDOR_TABLE}.insts.inc") set(GRAMMAR_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.${VENDOR_TABLE}.grammar.json") + if(NOT EXISTS ${GRAMMAR_FILE}) + set(GRAMMAR_FILE "${spirv-tools_SOURCE_DIR}/source/extinst.${VENDOR_TABLE}.grammar.json") + endif() add_custom_command(OUTPUT ${INSTS_FILE} COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT} --extinst-vendor-grammar=${GRAMMAR_FILE} @@ -148,9 +151,11 @@ spvtools_vendor_tables("spv-amd-gcn-shader" "spv-amd-gs" "") spvtools_vendor_tables("spv-amd-shader-ballot" "spv-amd-sb" "") spvtools_vendor_tables("debuginfo" "debuginfo" "") spvtools_vendor_tables("opencl.debuginfo.100" "cldi100" "CLDEBUG100_") +spvtools_vendor_tables("nonsemantic.shader.debuginfo.100" "shdi100" "SHDEBUG100_") spvtools_vendor_tables("nonsemantic.clspvreflection" "clspvreflection" "") spvtools_extinst_lang_headers("DebugInfo" ${DEBUGINFO_GRAMMAR_JSON_FILE}) spvtools_extinst_lang_headers("OpenCLDebugInfo100" ${CLDEBUGINFO100_GRAMMAR_JSON_FILE}) +spvtools_extinst_lang_headers("NonSemanticShaderDebugInfo100" ${VKDEBUGINFO100_GRAMMAR_JSON_FILE}) spvtools_vimsyntax("unified1" "1.0") add_custom_target(spirv-tools-vimsyntax DEPENDS ${VIMSYNTAX_FILE}) @@ -192,7 +197,7 @@ set(SPIRV_TOOLS_CHANGES_FILE add_custom_command(OUTPUT ${SPIRV_TOOLS_BUILD_VERSION_INC} COMMAND ${PYTHON_EXECUTABLE} ${SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR} - ${spirv-tools_SOURCE_DIR} ${SPIRV_TOOLS_BUILD_VERSION_INC} + ${SPIRV_TOOLS_CHANGES_FILE} ${SPIRV_TOOLS_BUILD_VERSION_INC} DEPENDS ${SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR} ${SPIRV_TOOLS_CHANGES_FILE} COMMENT "Update build-version.inc in the SPIRV-Tools build directory (if necessary).") @@ -211,12 +216,15 @@ add_subdirectory(opt) add_subdirectory(reduce) add_subdirectory(fuzz) add_subdirectory(link) +add_subdirectory(lint) +add_subdirectory(diff) set(SPIRV_SOURCES ${spirv-tools_SOURCE_DIR}/include/spirv-tools/libspirv.h ${CMAKE_CURRENT_SOURCE_DIR}/util/bitutils.h ${CMAKE_CURRENT_SOURCE_DIR}/util/bit_vector.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/hash_combine.h ${CMAKE_CURRENT_SOURCE_DIR}/util/hex_float.h ${CMAKE_CURRENT_SOURCE_DIR}/util/make_unique.h ${CMAKE_CURRENT_SOURCE_DIR}/util/parse_number.h @@ -226,6 +234,7 @@ set(SPIRV_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/assembly_grammar.h ${CMAKE_CURRENT_SOURCE_DIR}/binary.h ${CMAKE_CURRENT_SOURCE_DIR}/cfa.h + ${CMAKE_CURRENT_SOURCE_DIR}/common_debug_info.h ${CMAKE_CURRENT_SOURCE_DIR}/diagnostic.h ${CMAKE_CURRENT_SOURCE_DIR}/disassemble.h ${CMAKE_CURRENT_SOURCE_DIR}/enum_set.h @@ -309,10 +318,13 @@ set(SPIRV_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_logicals.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_memory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_memory_semantics.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_mesh_shading.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_misc.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_mode_setting.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_non_uniform.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_primitives.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_ray_query.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_ray_tracing.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_scopes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_small_type_uses.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_type.cpp @@ -397,11 +409,17 @@ if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") find_library(LIBRT rt) if(LIBRT) foreach(target ${SPIRV_TOOLS_TARGETS}) - target_link_libraries(${target} ${LIBRT}) + target_link_libraries(${target} rt) endforeach() endif() endif() +if (ANDROID) + foreach(target ${SPIRV_TOOLS_TARGETS}) + target_link_libraries(${target} PRIVATE android log) + endforeach() +endif() + if(ENABLE_SPIRV_TOOLS_INSTALL) install(TARGETS ${SPIRV_TOOLS_TARGETS} EXPORT ${SPIRV_TOOLS}Targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} @@ -415,8 +433,10 @@ if(ENABLE_SPIRV_TOOLS_INSTALL) # Special config file for root library compared to other libs. file(WRITE ${CMAKE_BINARY_DIR}/${SPIRV_TOOLS}Config.cmake "include(\${CMAKE_CURRENT_LIST_DIR}/${SPIRV_TOOLS}Target.cmake)\n" - "set(${SPIRV_TOOLS}_LIBRARIES ${SPIRV_TOOLS})\n" - "get_target_property(${SPIRV_TOOLS}_INCLUDE_DIRS ${SPIRV_TOOLS} INTERFACE_INCLUDE_DIRECTORIES)\n") + "if(TARGET ${SPIRV_TOOLS})\n" + " set(${SPIRV_TOOLS}_LIBRARIES ${SPIRV_TOOLS})\n" + " get_target_property(${SPIRV_TOOLS}_INCLUDE_DIRS ${SPIRV_TOOLS} INTERFACE_INCLUDE_DIRECTORIES)\n" + "endif()\n") install(FILES ${CMAKE_BINARY_DIR}/${SPIRV_TOOLS}Config.cmake DESTINATION ${PACKAGE_DIR}) endif(ENABLE_SPIRV_TOOLS_INSTALL) diff --git a/source/assembly_grammar.cpp b/source/assembly_grammar.cpp index 79f18eee..4f5942ab 100644 --- a/source/assembly_grammar.cpp +++ b/source/assembly_grammar.cpp @@ -62,9 +62,9 @@ spv_result_t spvTextParseMaskOperand(spv_target_env env, end = std::find(begin, text_end, separator); spv_operand_desc entry = nullptr; - if (spvOperandTableNameLookup(env, operandTable, type, begin, end - begin, - &entry)) { - return SPV_ERROR_INVALID_TEXT; + if (auto error = spvOperandTableNameLookup(env, operandTable, type, begin, + end - begin, &entry)) { + return error; } value |= entry->value; diff --git a/source/binary.cpp b/source/binary.cpp index 7448721b..24d32f8c 100644 --- a/source/binary.cpp +++ b/source/binary.cpp @@ -33,6 +33,7 @@ #include "source/operand.h" #include "source/spirv_constant.h" #include "source/spirv_endian.h" +#include "source/util/string_utils.h" spv_result_t spvBinaryHeaderGet(const spv_const_binary binary, const spv_endianness_t endian, @@ -62,6 +63,15 @@ spv_result_t spvBinaryHeaderGet(const spv_const_binary binary, return SPV_SUCCESS; } +std::string spvDecodeLiteralStringOperand(const spv_parsed_instruction_t& inst, + const uint16_t operand_index) { + assert(operand_index < inst.num_operands); + const spv_parsed_operand_t& operand = inst.operands[operand_index]; + + return spvtools::utils::MakeString(inst.words + operand.offset, + operand.num_words); +} + namespace { // A SPIR-V binary parser. A parser instance communicates detailed parse @@ -205,7 +215,7 @@ class Parser { size_t word_index; // The current position in words. size_t instruction_count; // The count of processed instructions spv_endianness_t endian; // The endianness of the binary. - // Is the SPIR-V binary in a different endiannes from the host native + // Is the SPIR-V binary in a different endianness from the host native // endianness? bool requires_endian_conversion; @@ -290,7 +300,7 @@ spv_result_t Parser::parseInstruction() { const uint32_t first_word = peek(); // If the module's endianness is different from the host native endianness, - // then converted_words contains the the endian-translated words in the + // then converted_words contains the endian-translated words in the // instruction. _.endian_converted_words.clear(); _.endian_converted_words.push_back(first_word); @@ -507,7 +517,8 @@ spv_result_t Parser::parseOperand(size_t inst_offset, case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: { assert(SpvOpSpecConstantOp == opcode); - if (grammar_.lookupSpecConstantOpcode(SpvOp(word))) { + if (word > static_cast(SpvOp::SpvOpMax) || + grammar_.lookupSpecConstantOpcode(SpvOp(word))) { return diagnostic() << "Invalid " << spvOperandTypeStr(type) << ": " << word; } @@ -576,27 +587,18 @@ spv_result_t Parser::parseOperand(size_t inst_offset, case SPV_OPERAND_TYPE_LITERAL_STRING: case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: { - convert_operand_endianness = false; - const char* string = - reinterpret_cast(_.words + _.word_index); - // Compute the length of the string, but make sure we don't run off the - // end of the input. - const size_t remaining_input_bytes = - sizeof(uint32_t) * (_.num_words - _.word_index); - const size_t string_num_content_bytes = - spv_strnlen_s(string, remaining_input_bytes); - // If there was no terminating null byte, then that's an end-of-input - // error. - if (string_num_content_bytes == remaining_input_bytes) + const size_t max_words = _.num_words - _.word_index; + std::string string = + spvtools::utils::MakeString(_.words + _.word_index, max_words, false); + + if (string.length() == max_words * 4) return exhaustedInputDiagnostic(inst_offset, opcode, type); - // Account for null in the word length, so add 1 for null, then add 3 to - // make sure we round up. The following is equivalent to: - // (string_num_content_bytes + 1 + 3) / 4 - const size_t string_num_words = string_num_content_bytes / 4 + 1; + // Make sure we can record the word count without overflow. // // This error can't currently be triggered because of validity // checks elsewhere. + const size_t string_num_words = string.length() / 4 + 1; if (string_num_words > std::numeric_limits::max()) { return diagnostic() << "Literal string is longer than " << std::numeric_limits::max() @@ -610,7 +612,7 @@ spv_result_t Parser::parseOperand(size_t inst_offset, // There is only one string literal argument to OpExtInstImport, // so it's sufficient to guard this just on the opcode. const spv_ext_inst_type_t ext_inst_type = - spvExtInstImportTypeGet(string); + spvExtInstImportTypeGet(string.c_str()); if (SPV_EXT_INST_TYPE_NONE == ext_inst_type) { return diagnostic() << "Invalid extended instruction import '" << string << "'"; @@ -657,12 +659,18 @@ spv_result_t Parser::parseOperand(size_t inst_offset, case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION: case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: case SPV_OPERAND_TYPE_FPDENORM_MODE: - case SPV_OPERAND_TYPE_FPOPERATION_MODE: { + case SPV_OPERAND_TYPE_FPOPERATION_MODE: + case SPV_OPERAND_TYPE_QUANTIZATION_MODES: + case SPV_OPERAND_TYPE_OVERFLOW_MODES: + case SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT: + case SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT: { // A single word that is a plain enum value. // Map an optional operand type to its corresponding concrete type. if (type == SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER) parsed_operand.type = SPV_OPERAND_TYPE_ACCESS_QUALIFIER; + if (type == SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT) + parsed_operand.type = SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT; spv_operand_desc entry; if (grammar_.lookupOperand(type, word, &entry)) { diff --git a/source/binary.h b/source/binary.h index 66d24c7e..eb3beaca 100644 --- a/source/binary.h +++ b/source/binary.h @@ -15,6 +15,8 @@ #ifndef SOURCE_BINARY_H_ #define SOURCE_BINARY_H_ +#include + #include "source/spirv_definition.h" #include "spirv-tools/libspirv.h" @@ -33,4 +35,9 @@ spv_result_t spvBinaryHeaderGet(const spv_const_binary binary, // replacement for C11's strnlen_s which might not exist in all environments. size_t spv_strnlen_s(const char* str, size_t strsz); +// Decode the string literal operand with index operand_index from instruction +// inst. +std::string spvDecodeLiteralStringOperand(const spv_parsed_instruction_t& inst, + const uint16_t operand_index); + #endif // SOURCE_BINARY_H_ diff --git a/source/cfa.h b/source/cfa.h index 0d09014c..2743ab40 100644 --- a/source/cfa.h +++ b/source/cfa.h @@ -42,7 +42,7 @@ class CFA { /// Returns true if a block with @p id is found in the @p work_list vector /// - /// @param[in] work_list Set of blocks visited in the the depth first + /// @param[in] work_list Set of blocks visited in the depth first /// traversal /// of the CFG /// @param[in] id The ID of the block being checked @@ -52,12 +52,37 @@ class CFA { uint32_t id); public: + /// @brief Depth first traversal starting from the \p entry BasicBlock + /// + /// This function performs a depth first traversal from the \p entry + /// BasicBlock and calls the pre/postorder functions when it needs to process + /// the node in pre order, post order. + /// + /// @param[in] entry The root BasicBlock of a CFG + /// @param[in] successor_func A function which will return a pointer to the + /// successor nodes + /// @param[in] preorder A function that will be called for every block in a + /// CFG following preorder traversal semantics + /// @param[in] postorder A function that will be called for every block in a + /// CFG following postorder traversal semantics + /// @param[in] terminal A function that will be called to determine if the + /// search should stop at the given node. + /// NOTE: The @p successor_func and predecessor_func each return a pointer to + /// a collection such that iterators to that collection remain valid for the + /// lifetime of the algorithm. + static void DepthFirstTraversal(const BB* entry, + get_blocks_func successor_func, + std::function preorder, + std::function postorder, + std::function terminal); + /// @brief Depth first traversal starting from the \p entry BasicBlock /// /// This function performs a depth first traversal from the \p entry /// BasicBlock and calls the pre/postorder functions when it needs to process /// the node in pre order, post order. It also calls the backedge function - /// when a back edge is encountered. + /// when a back edge is encountered. The backedge function can be empty. The + /// runtime of the algorithm is improved if backedge is empty. /// /// @param[in] entry The root BasicBlock of a CFG /// @param[in] successor_func A function which will return a pointer to the @@ -67,16 +92,18 @@ class CFA { /// @param[in] postorder A function that will be called for every block in a /// CFG following postorder traversal semantics /// @param[in] backedge A function that will be called when a backedge is - /// encountered during a traversal + /// encountered during a traversal. + /// @param[in] terminal A function that will be called to determine if the + /// search should stop at the given node. /// NOTE: The @p successor_func and predecessor_func each return a pointer to - /// a - /// collection such that iterators to that collection remain valid for the + /// a collection such that iterators to that collection remain valid for the /// lifetime of the algorithm. static void DepthFirstTraversal( const BB* entry, get_blocks_func successor_func, std::function preorder, std::function postorder, - std::function backedge); + std::function backedge, + std::function terminal); /// @brief Calculates dominator edges for a set of blocks /// @@ -133,12 +160,28 @@ bool CFA::FindInWorkList(const std::vector& work_list, return false; } +template +void CFA::DepthFirstTraversal(const BB* entry, + get_blocks_func successor_func, + std::function preorder, + std::function postorder, + std::function terminal) { + DepthFirstTraversal(entry, successor_func, preorder, postorder, + /* backedge = */ {}, terminal); +} + template void CFA::DepthFirstTraversal( const BB* entry, get_blocks_func successor_func, std::function preorder, std::function postorder, - std::function backedge) { + std::function backedge, + std::function terminal) { + assert(successor_func && "The successor function cannot be empty."); + assert(preorder && "The preorder function cannot be empty."); + assert(postorder && "The postorder function cannot be empty."); + assert(terminal && "The terminal function cannot be empty."); + std::unordered_set processed; /// NOTE: work_list is the sequence of nodes from the root node to the node @@ -152,13 +195,13 @@ void CFA::DepthFirstTraversal( while (!work_list.empty()) { block_info& top = work_list.back(); - if (top.iter == end(*successor_func(top.block))) { + if (terminal(top.block) || top.iter == end(*successor_func(top.block))) { postorder(top.block); work_list.pop_back(); } else { BB* child = *top.iter; top.iter++; - if (FindInWorkList(work_list, child->id())) { + if (backedge && FindInWorkList(work_list, child->id())) { backedge(top.block, child); } if (processed.count(child->id()) == 0) { @@ -265,12 +308,12 @@ std::vector CFA::TraversalRoots(const std::vector& blocks, auto mark_visited = [&visited](const BB* b) { visited.insert(b); }; auto ignore_block = [](const BB*) {}; - auto ignore_blocks = [](const BB*, const BB*) {}; + auto no_terminal_blocks = [](const BB*) { return false; }; auto traverse_from_root = [&mark_visited, &succ_func, &ignore_block, - &ignore_blocks](const BB* entry) { + &no_terminal_blocks](const BB* entry) { DepthFirstTraversal(entry, succ_func, mark_visited, ignore_block, - ignore_blocks); + no_terminal_blocks); }; std::vector result; diff --git a/source/common_debug_info.h b/source/common_debug_info.h new file mode 100644 index 00000000..ffa5d340 --- /dev/null +++ b/source/common_debug_info.h @@ -0,0 +1,64 @@ +// Copyright (c) 2021 The Khronos Group Inc. +// Copyright (c) 2021 Valve Corporation +// Copyright (c) 2021 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_COMMON_DEBUG_INFO_HEADER_H_ +#define SOURCE_COMMON_DEBUG_INFO_HEADER_H_ + +// This enum defines the known common set of instructions that are the same +// between OpenCL.DebugInfo.100 and NonSemantic.Shader.DebugInfo.100. +// Note that NonSemantic.Shader.* instructions can still have slightly +// different encoding, as it does not use literals anywhere and only constants. +enum CommonDebugInfoInstructions { + CommonDebugInfoDebugInfoNone = 0, + CommonDebugInfoDebugCompilationUnit = 1, + CommonDebugInfoDebugTypeBasic = 2, + CommonDebugInfoDebugTypePointer = 3, + CommonDebugInfoDebugTypeQualifier = 4, + CommonDebugInfoDebugTypeArray = 5, + CommonDebugInfoDebugTypeVector = 6, + CommonDebugInfoDebugTypedef = 7, + CommonDebugInfoDebugTypeFunction = 8, + CommonDebugInfoDebugTypeEnum = 9, + CommonDebugInfoDebugTypeComposite = 10, + CommonDebugInfoDebugTypeMember = 11, + CommonDebugInfoDebugTypeInheritance = 12, + CommonDebugInfoDebugTypePtrToMember = 13, + CommonDebugInfoDebugTypeTemplate = 14, + CommonDebugInfoDebugTypeTemplateParameter = 15, + CommonDebugInfoDebugTypeTemplateTemplateParameter = 16, + CommonDebugInfoDebugTypeTemplateParameterPack = 17, + CommonDebugInfoDebugGlobalVariable = 18, + CommonDebugInfoDebugFunctionDeclaration = 19, + CommonDebugInfoDebugFunction = 20, + CommonDebugInfoDebugLexicalBlock = 21, + CommonDebugInfoDebugLexicalBlockDiscriminator = 22, + CommonDebugInfoDebugScope = 23, + CommonDebugInfoDebugNoScope = 24, + CommonDebugInfoDebugInlinedAt = 25, + CommonDebugInfoDebugLocalVariable = 26, + CommonDebugInfoDebugInlinedVariable = 27, + CommonDebugInfoDebugDeclare = 28, + CommonDebugInfoDebugValue = 29, + CommonDebugInfoDebugOperation = 30, + CommonDebugInfoDebugExpression = 31, + CommonDebugInfoDebugMacroDef = 32, + CommonDebugInfoDebugMacroUndef = 33, + CommonDebugInfoDebugImportedEntity = 34, + CommonDebugInfoDebugSource = 35, + CommonDebugInfoInstructionsMax = 0x7ffffff +}; + +#endif // SOURCE_COMMON_DEBUG_INFO_HEADER_H_ diff --git a/source/diagnostic.cpp b/source/diagnostic.cpp index edc27c8f..f3aa2594 100644 --- a/source/diagnostic.cpp +++ b/source/diagnostic.cpp @@ -37,7 +37,7 @@ spv_diagnostic spvDiagnosticCreate(const spv_position position, diagnostic->position = *position; diagnostic->isTextSource = false; memset(diagnostic->error, 0, length); - strncpy(diagnostic->error, message, length); + strcpy(diagnostic->error, message); return diagnostic; } diff --git a/source/diff/CMakeLists.txt b/source/diff/CMakeLists.txt new file mode 100644 index 00000000..1328699a --- /dev/null +++ b/source/diff/CMakeLists.txt @@ -0,0 +1,54 @@ +# Copyright (c) 2022 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. +set(SPIRV_TOOLS_DIFF_SOURCES + diff.h + lcs.h + + diff.cpp +) + +add_library(SPIRV-Tools-diff ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_TOOLS_DIFF_SOURCES}) + +spvtools_default_compile_options(SPIRV-Tools-diff) +target_include_directories(SPIRV-Tools-diff + PUBLIC + $ + $ + $ + PRIVATE ${spirv-tools_BINARY_DIR} +) +# We need the assembling and disassembling functionalities in the main library. +target_link_libraries(SPIRV-Tools-diff + PUBLIC ${SPIRV_TOOLS_FULL_VISIBILITY}) +# We need the internals of spirv-opt. +target_link_libraries(SPIRV-Tools-diff + PUBLIC SPIRV-Tools-opt) + +set_property(TARGET SPIRV-Tools-diff PROPERTY FOLDER "SPIRV-Tools libraries") +spvtools_check_symbol_exports(SPIRV-Tools-diff) + +if(ENABLE_SPIRV_TOOLS_INSTALL) + install(TARGETS SPIRV-Tools-diff EXPORT SPIRV-Tools-diffTargets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + export(EXPORT SPIRV-Tools-diffTargets FILE SPIRV-Tools-diffTargets.cmake) + + spvtools_config_package_dir(SPIRV-Tools-diff PACKAGE_DIR) + install(EXPORT SPIRV-Tools-diffTargets FILE SPIRV-Tools-diffTargets.cmake + DESTINATION ${PACKAGE_DIR}) + + spvtools_generate_config_file(SPIRV-Tools-diff) + install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-diffConfig.cmake DESTINATION ${PACKAGE_DIR}) +endif(ENABLE_SPIRV_TOOLS_INSTALL) diff --git a/source/diff/diff.cpp b/source/diff/diff.cpp new file mode 100644 index 00000000..7ed41de5 --- /dev/null +++ b/source/diff/diff.cpp @@ -0,0 +1,2869 @@ +// Copyright (c) 2022 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/diff/diff.h" + +#include "source/diff/lcs.h" +#include "source/disassemble.h" +#include "source/ext_inst.h" +#include "source/latest_version_spirv_header.h" +#include "source/print.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace diff { + +namespace { + +// A map from an id to the instruction that defines it. +using IdToInstructionMap = std::vector; +// A map from an id to the instructions that decorate it, or name it, etc. +using IdToInfoMap = std::vector>; +// A map from an instruction to another, used for instructions without id. +using InstructionToInstructionMap = + std::unordered_map; +// A flat list of instructions in a function for easier iteration. +using InstructionList = std::vector; +// A map from a function to its list of instructions. +using FunctionInstMap = std::map; +// A list of ids with some similar property, for example functions with the same +// name. +using IdGroup = std::vector; +// A map of names to ids with the same name. This is an ordered map so +// different implementations produce identical results. +using IdGroupMapByName = std::map; +using IdGroupMapByTypeId = std::map; +using IdGroupMapByOp = std::map; +using IdGroupMapByStorageClass = std::map; + +// A set of potential id mappings that haven't been resolved yet. Any id in src +// may map in any id in dst. Note that ids are added in the same order as they +// appear in src and dst to facilitate matching dependent instructions. For +// example, this guarantees that when matching OpTypeVector, the basic type of +// the vector is already (potentially) matched. +struct PotentialIdMap { + std::vector src_ids; + std::vector dst_ids; +}; + +void CompactIds(std::vector& ids) { + size_t write_index = 0; + for (size_t i = 0; i < ids.size(); ++i) { + if (ids[i] != 0) { + ids[write_index++] = ids[i]; + } + } + ids.resize(write_index); +} + +// A mapping between src and dst ids. +class IdMap { + public: + IdMap(size_t id_bound) { id_map_.resize(id_bound, 0); } + + void MapIds(uint32_t from, uint32_t to) { + assert(from != 0); + assert(to != 0); + assert(from < id_map_.size()); + assert(id_map_[from] == 0); + + id_map_[from] = to; + } + + uint32_t MappedId(uint32_t from) const { + assert(from != 0); + return from < id_map_.size() ? id_map_[from] : 0; + } + const opt::Instruction* MappedInst(const opt::Instruction* from_inst) const { + assert(from_inst != nullptr); + assert(!from_inst->HasResultId()); + + auto mapped = inst_map_.find(from_inst); + if (mapped == inst_map_.end()) { + return nullptr; + } + return mapped->second; + } + + bool IsMapped(uint32_t from) const { + assert(from != 0); + return from < id_map_.size() && id_map_[from] != 0; + } + + // Map any ids in src and dst that have not been mapped to new ids in dst and + // src respectively. + void MapUnmatchedIds(IdMap& other_way); + + // Some instructions don't have result ids. Those are mapped by pointer. + void MapInsts(const opt::Instruction* from_inst, + const opt::Instruction* to_inst) { + assert(from_inst != nullptr); + assert(to_inst != nullptr); + assert(inst_map_.find(from_inst) == inst_map_.end()); + + inst_map_[from_inst] = to_inst; + } + + uint32_t IdBound() const { return static_cast(id_map_.size()); } + + private: + // Given an id, returns the corresponding id in the other module, or 0 if not + // matched yet. + std::vector id_map_; + + // Same for instructions that don't have an id. + InstructionToInstructionMap inst_map_; +}; + +// Two way mapping of ids. +class SrcDstIdMap { + public: + SrcDstIdMap(size_t src_id_bound, size_t dst_id_bound) + : src_to_dst_(src_id_bound), dst_to_src_(dst_id_bound) {} + + void MapIds(uint32_t src, uint32_t dst) { + src_to_dst_.MapIds(src, dst); + dst_to_src_.MapIds(dst, src); + } + + uint32_t MappedDstId(uint32_t src) { + uint32_t dst = src_to_dst_.MappedId(src); + assert(dst == 0 || dst_to_src_.MappedId(dst) == src); + return dst; + } + uint32_t MappedSrcId(uint32_t dst) { + uint32_t src = dst_to_src_.MappedId(dst); + assert(src == 0 || src_to_dst_.MappedId(src) == dst); + return src; + } + + bool IsSrcMapped(uint32_t src) { return src_to_dst_.IsMapped(src); } + bool IsDstMapped(uint32_t dst) { return dst_to_src_.IsMapped(dst); } + + // Map any ids in src and dst that have not been mapped to new ids in dst and + // src respectively. + void MapUnmatchedIds(); + + // Some instructions don't have result ids. Those are mapped by pointer. + void MapInsts(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + assert(src_inst->HasResultId() == dst_inst->HasResultId()); + if (src_inst->HasResultId()) { + MapIds(src_inst->result_id(), dst_inst->result_id()); + } else { + src_to_dst_.MapInsts(src_inst, dst_inst); + dst_to_src_.MapInsts(dst_inst, src_inst); + } + } + + const IdMap& SrcToDstMap() const { return src_to_dst_; } + const IdMap& DstToSrcMap() const { return dst_to_src_; } + + private: + IdMap src_to_dst_; + IdMap dst_to_src_; +}; + +struct IdInstructions { + IdInstructions(const opt::Module* module) + : inst_map_(module->IdBound(), nullptr), + name_map_(module->IdBound()), + decoration_map_(module->IdBound()), + forward_pointer_map_(module->IdBound()) { + // Map ids from all sections to instructions that define them. + MapIdsToInstruction(module->ext_inst_imports()); + MapIdsToInstruction(module->types_values()); + for (const opt::Function& function : *module) { + function.ForEachInst( + [this](const opt::Instruction* inst) { + if (inst->HasResultId()) { + MapIdToInstruction(inst->result_id(), inst); + } + }, + true, true); + } + + // Gather decorations applied to ids that could be useful in matching them + // between src and dst modules. + MapIdsToInfos(module->debugs2()); + MapIdsToInfos(module->annotations()); + MapIdsToInfos(module->types_values()); + } + + void MapIdToInstruction(uint32_t id, const opt::Instruction* inst); + + void MapIdsToInstruction( + opt::IteratorRange section); + void MapIdsToInfos( + opt::IteratorRange section); + + IdToInstructionMap inst_map_; + IdToInfoMap name_map_; + IdToInfoMap decoration_map_; + IdToInstructionMap forward_pointer_map_; +}; + +class Differ { + public: + Differ(opt::IRContext* src, opt::IRContext* dst, std::ostream& out, + Options options) + : src_context_(src), + dst_context_(dst), + src_(src->module()), + dst_(dst->module()), + options_(options), + out_(out), + src_id_to_(src_), + dst_id_to_(dst_), + id_map_(src_->IdBound(), dst_->IdBound()) { + // Cache function bodies in canonicalization order. + GetFunctionBodies(src_context_, &src_funcs_, &src_func_insts_); + GetFunctionBodies(dst_context_, &dst_funcs_, &dst_func_insts_); + } + + // Match ids or instructions of different sections. + void MatchCapabilities(); + void MatchExtensions(); + void MatchExtInstImportIds(); + void MatchMemoryModel(); + void MatchEntryPointIds(); + void MatchExecutionModes(); + void MatchTypeForwardPointers(); + void MatchTypeIds(); + void MatchConstants(); + void MatchVariableIds(); + void MatchFunctions(); + + // Debug info and annotations are matched only after ids are matched. + void MatchDebugs1(); + void MatchDebugs2(); + void MatchDebugs3(); + void MatchExtInstDebugInfo(); + void MatchAnnotations(); + + // Output the diff. + spv_result_t Output(); + + void DumpIdMap() { + if (!options_.dump_id_map) { + return; + } + + out_ << " Src -> Dst\n"; + for (uint32_t src_id = 1; src_id < src_->IdBound(); ++src_id) { + uint32_t dst_id = id_map_.MappedDstId(src_id); + if (src_id_to_.inst_map_[src_id] != nullptr && dst_id != 0) + out_ << std::setw(4) << src_id << " -> " << std::setw(4) << dst_id + << " [" << spvOpcodeString(src_id_to_.inst_map_[src_id]->opcode()) + << "]\n"; + } + } + + private: + // Helper functions that match ids between src and dst + void PoolPotentialIds( + opt::IteratorRange section, + std::vector& ids, bool is_src, + std::function filter, + std::function get_id); + void MatchIds( + PotentialIdMap& potential, + std::function + match); + // Helper functions that match id-less instructions between src and dst. + void MatchPreambleInstructions( + opt::IteratorRange src_insts, + opt::IteratorRange dst_insts); + InstructionList SortPreambleInstructions( + const opt::Module* module, + opt::IteratorRange insts); + int ComparePreambleInstructions(const opt::Instruction* a, + const opt::Instruction* b, + const opt::Module* src_inst_module, + const opt::Module* dst_inst_module); + // Helper functions that match debug and annotation instructions of already + // matched ids. + void MatchDebugAndAnnotationInstructions( + opt::IteratorRange src_insts, + opt::IteratorRange dst_insts); + + // Get various properties from an id. These Helper functions are passed to + // `GroupIds` and `GroupIdsAndMatch` below (as the `get_group` argument). + uint32_t GroupIdsHelperGetTypeId(const IdInstructions& id_to, uint32_t id); + SpvStorageClass GroupIdsHelperGetTypePointerStorageClass( + const IdInstructions& id_to, uint32_t id); + SpvOp GroupIdsHelperGetTypePointerTypeOp(const IdInstructions& id_to, + uint32_t id); + + // Given a list of ids, groups them based on some value. The `get_group` + // function extracts a piece of information corresponding to each id, and the + // ids are bucketed based on that (and output in `groups`). This is useful to + // attempt to match ids between src and dst only when said property is + // identical. + template + void GroupIds(const IdGroup& ids, bool is_src, std::map* groups, + T (Differ::*get_group)(const IdInstructions&, uint32_t)); + + // Calls GroupIds to bucket ids in src and dst based on a property returned by + // `get_group`. This function then calls `match_group` for each bucket (i.e. + // "group") with identical values for said property. + // + // For example, say src and dst ids have the following properties + // correspondingly: + // + // - src ids' properties: {id0: A, id1: A, id2: B, id3: C, id4: B} + // - dst ids' properties: {id0': B, id1': C, id2': B, id3': D, id4': B} + // + // Then `match_group` is called 2 times: + // + // - Once with: ([id2, id4], [id0', id2', id4']) corresponding to B + // - Once with: ([id3], [id2']) corresponding to C + // + // Ids corresponding to A and D cannot match based on this property. + template + void GroupIdsAndMatch( + const IdGroup& src_ids, const IdGroup& dst_ids, T invalid_group_key, + T (Differ::*get_group)(const IdInstructions&, uint32_t), + std::function + match_group); + + // Helper functions that determine if two instructions match + bool DoIdsMatch(uint32_t src_id, uint32_t dst_id); + bool DoesOperandMatch(const opt::Operand& src_operand, + const opt::Operand& dst_operand); + bool DoOperandsMatch(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, + uint32_t in_operand_index_start, + uint32_t in_operand_count); + bool DoInstructionsMatch(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst); + bool DoIdsMatchFuzzy(uint32_t src_id, uint32_t dst_id); + bool DoesOperandMatchFuzzy(const opt::Operand& src_operand, + const opt::Operand& dst_operand); + bool DoInstructionsMatchFuzzy(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst); + bool AreIdenticalUintConstants(uint32_t src_id, uint32_t dst_id); + bool DoDebugAndAnnotationInstructionsMatch(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst); + bool AreVariablesMatchable(uint32_t src_id, uint32_t dst_id, + uint32_t flexibility); + bool MatchOpTypeStruct(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, + uint32_t flexibility); + bool MatchOpConstant(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, uint32_t flexibility); + bool MatchOpSpecConstant(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst); + bool MatchOpVariable(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, uint32_t flexibility); + bool MatchPerVertexType(uint32_t src_type_id, uint32_t dst_type_id); + bool MatchPerVertexVariable(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst); + + // Helper functions for matching OpTypeForwardPointer + void MatchTypeForwardPointersByName(const IdGroup& src, const IdGroup& dst); + void MatchTypeForwardPointersByTypeOp(const IdGroup& src, const IdGroup& dst); + + // Helper functions for function matching. + using FunctionMap = std::map; + + InstructionList GetFunctionBody(opt::IRContext* context, + opt::Function& function); + InstructionList GetFunctionHeader(const opt::Function& function); + void GetFunctionBodies(opt::IRContext* context, FunctionMap* functions, + FunctionInstMap* function_insts); + void GetFunctionHeaderInstructions(const opt::Module* module, + FunctionInstMap* function_insts); + void BestEffortMatchFunctions(const IdGroup& src_func_ids, + const IdGroup& dst_func_ids, + const FunctionInstMap& src_func_insts, + const FunctionInstMap& dst_func_insts); + + // Calculates the diff of two function bodies. Note that the matched + // instructions themselves may not be identical; output of exact matches + // should produce the exact instruction while inexact matches should produce a + // diff as well. + // + // Returns the similarity of the two bodies = 2*N_match / (N_src + N_dst) + void MatchFunctionParamIds(const opt::Function* src_func, + const opt::Function* dst_func); + float MatchFunctionBodies(const InstructionList& src_body, + const InstructionList& dst_body, + DiffMatch* src_match_result, + DiffMatch* dst_match_result); + void MatchIdsInFunctionBodies(const InstructionList& src_body, + const InstructionList& dst_body, + const DiffMatch& src_match_result, + const DiffMatch& dst_match_result, + uint32_t flexibility); + void MatchVariablesUsedByMatchedInstructions(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, + uint32_t flexibility); + + // Helper functions to retrieve information pertaining to an id + const opt::Instruction* GetInst(const IdInstructions& id_to, uint32_t id); + uint32_t GetConstantUint(const IdInstructions& id_to, uint32_t constant_id); + SpvExecutionModel GetExecutionModel(const opt::Module* module, + uint32_t entry_point_id); + bool HasName(const IdInstructions& id_to, uint32_t id); + // Get the OpName associated with an id + std::string GetName(const IdInstructions& id_to, uint32_t id, bool* has_name); + // Get the OpName associated with an id, with argument types stripped for + // functions. Some tools don't encode function argument types in the OpName + // string, and this improves diff between SPIR-V from those tools and others. + std::string GetSanitizedName(const IdInstructions& id_to, uint32_t id); + uint32_t GetVarTypeId(const IdInstructions& id_to, uint32_t var_id, + SpvStorageClass* storage_class); + bool GetDecorationValue(const IdInstructions& id_to, uint32_t id, + SpvDecoration decoration, uint32_t* decoration_value); + const opt::Instruction* GetForwardPointerInst(const IdInstructions& id_to, + uint32_t id); + bool IsIntType(const IdInstructions& id_to, uint32_t type_id); + bool IsFloatType(const IdInstructions& id_to, uint32_t type_id); + bool IsConstantUint(const IdInstructions& id_to, uint32_t id); + bool IsVariable(const IdInstructions& id_to, uint32_t pointer_id); + bool IsOp(const IdInstructions& id_to, uint32_t id, SpvOp opcode); + bool IsPerVertexType(const IdInstructions& id_to, uint32_t type_id); + bool IsPerVertexVariable(const IdInstructions& id_to, uint32_t type_id); + SpvStorageClass GetPerVertexStorageClass(const opt::Module* module, + uint32_t type_id); + spv_ext_inst_type_t GetExtInstType(const IdInstructions& id_to, + uint32_t set_id); + spv_number_kind_t GetNumberKind(const IdInstructions& id_to, + const opt::Instruction& inst, + uint32_t operand_index, + uint32_t* number_bit_width); + spv_number_kind_t GetTypeNumberKind(const IdInstructions& id_to, uint32_t id, + uint32_t* number_bit_width); + + // Helper functions to output a diff line + const opt::Instruction* MappedDstInst(const opt::Instruction* src_inst); + const opt::Instruction* MappedSrcInst(const opt::Instruction* dst_inst); + const opt::Instruction* MappedInstImpl(const opt::Instruction* inst, + const IdMap& to_other, + const IdInstructions& other_id_to); + void OutputLine(std::function are_lines_identical, + std::function output_src_line, + std::function output_dst_line); + template + void OutputSection( + const InstList& src_insts, const InstList& dst_insts, + std::function + write_inst); + void ToParsedInstruction(const opt::Instruction& inst, + const IdInstructions& id_to, + const opt::Instruction& original_inst, + spv_parsed_instruction_t* parsed_inst, + std::vector& parsed_operands, + std::vector& inst_binary); + opt::Instruction ToMappedSrcIds(const opt::Instruction& dst_inst); + + void OutputRed() { + if (options_.color_output) out_ << spvtools::clr::red{true}; + } + void OutputGreen() { + if (options_.color_output) out_ << spvtools::clr::green{true}; + } + void OutputResetColor() { + if (options_.color_output) out_ << spvtools::clr::reset{true}; + } + + opt::IRContext* src_context_; + opt::IRContext* dst_context_; + const opt::Module* src_; + const opt::Module* dst_; + Options options_; + std::ostream& out_; + + // Helpers to look up instructions based on id. + IdInstructions src_id_to_; + IdInstructions dst_id_to_; + + // The ids that have been matched between src and dst so far. + SrcDstIdMap id_map_; + + // List of instructions in function bodies after canonicalization. Cached + // here to avoid duplicate work. More importantly, some maps use + // opt::Instruction pointers so they need to be unique. + FunctionInstMap src_func_insts_; + FunctionInstMap dst_func_insts_; + FunctionMap src_funcs_; + FunctionMap dst_funcs_; +}; + +void IdMap::MapUnmatchedIds(IdMap& other_way) { + const uint32_t src_id_bound = static_cast(id_map_.size()); + const uint32_t dst_id_bound = static_cast(other_way.id_map_.size()); + + uint32_t next_src_id = src_id_bound; + uint32_t next_dst_id = dst_id_bound; + + for (uint32_t src_id = 1; src_id < src_id_bound; ++src_id) { + if (!IsMapped(src_id)) { + MapIds(src_id, next_dst_id); + + other_way.id_map_.push_back(0); + other_way.MapIds(next_dst_id++, src_id); + } + } + + for (uint32_t dst_id = 1; dst_id < dst_id_bound; ++dst_id) { + if (!other_way.IsMapped(dst_id)) { + id_map_.push_back(0); + MapIds(next_src_id, dst_id); + + other_way.MapIds(dst_id, next_src_id++); + } + } +} + +void SrcDstIdMap::MapUnmatchedIds() { + src_to_dst_.MapUnmatchedIds(dst_to_src_); +} + +void IdInstructions::MapIdToInstruction(uint32_t id, + const opt::Instruction* inst) { + assert(id != 0); + assert(id < inst_map_.size()); + assert(inst_map_[id] == nullptr); + + inst_map_[id] = inst; +} + +void IdInstructions::MapIdsToInstruction( + opt::IteratorRange section) { + for (const opt::Instruction& inst : section) { + uint32_t result_id = inst.result_id(); + if (result_id == 0) { + continue; + } + + MapIdToInstruction(result_id, &inst); + } +} + +void IdInstructions::MapIdsToInfos( + opt::IteratorRange section) { + for (const opt::Instruction& inst : section) { + IdToInfoMap* info_map = nullptr; + uint32_t id_operand = 0; + + switch (inst.opcode()) { + case SpvOpName: + info_map = &name_map_; + break; + case SpvOpMemberName: + info_map = &name_map_; + break; + case SpvOpDecorate: + info_map = &decoration_map_; + break; + case SpvOpMemberDecorate: + info_map = &decoration_map_; + break; + case SpvOpTypeForwardPointer: { + uint32_t id = inst.GetSingleWordOperand(0); + assert(id != 0); + + assert(id < forward_pointer_map_.size()); + forward_pointer_map_[id] = &inst; + continue; + } + default: + // Currently unsupported instruction, don't attempt to use it for + // matching. + break; + } + + if (info_map == nullptr) { + continue; + } + + uint32_t id = inst.GetOperand(id_operand).AsId(); + assert(id != 0); + + assert(id < info_map->size()); + assert(std::find((*info_map)[id].begin(), (*info_map)[id].end(), &inst) == + (*info_map)[id].end()); + + (*info_map)[id].push_back(&inst); + } +} + +void Differ::PoolPotentialIds( + opt::IteratorRange section, + std::vector& ids, bool is_src, + std::function filter, + std::function get_id) { + for (const opt::Instruction& inst : section) { + if (!filter(inst)) { + continue; + } + + uint32_t result_id = get_id(inst); + assert(result_id != 0); + + assert(std::find(ids.begin(), ids.end(), result_id) == ids.end()); + + // Don't include ids that are already matched, for example through + // OpTypeForwardPointer. + const bool is_matched = is_src ? id_map_.IsSrcMapped(result_id) + : id_map_.IsDstMapped(result_id); + if (is_matched) { + continue; + } + + ids.push_back(result_id); + } +} + +void Differ::MatchIds( + PotentialIdMap& potential, + std::function + match) { + for (size_t src_index = 0; src_index < potential.src_ids.size(); + ++src_index) { + for (size_t dst_index = 0; dst_index < potential.dst_ids.size(); + ++dst_index) { + const uint32_t src_id = potential.src_ids[src_index]; + const uint32_t dst_id = potential.dst_ids[dst_index]; + + if (dst_id == 0) { + // Already matched. + continue; + } + + const opt::Instruction* src_inst = src_id_to_.inst_map_[src_id]; + const opt::Instruction* dst_inst = dst_id_to_.inst_map_[dst_id]; + + if (match(src_inst, dst_inst)) { + id_map_.MapIds(src_id, dst_id); + + // Remove the ids from the potential list. + potential.src_ids[src_index] = 0; + potential.dst_ids[dst_index] = 0; + + // Find a match for the next src id. + break; + } + } + } + + // Remove matched ids to make the next iteration faster. + CompactIds(potential.src_ids); + CompactIds(potential.dst_ids); +} + +void Differ::MatchPreambleInstructions( + opt::IteratorRange src_insts, + opt::IteratorRange dst_insts) { + // First, pool all instructions from each section and sort them. + InstructionList sorted_src_insts = SortPreambleInstructions(src_, src_insts); + InstructionList sorted_dst_insts = SortPreambleInstructions(dst_, dst_insts); + + // Then walk and match them. + size_t src_cur = 0; + size_t dst_cur = 0; + + while (src_cur < sorted_src_insts.size() && + dst_cur < sorted_dst_insts.size()) { + const opt::Instruction* src_inst = sorted_src_insts[src_cur]; + const opt::Instruction* dst_inst = sorted_dst_insts[dst_cur]; + + int compare = ComparePreambleInstructions(src_inst, dst_inst, src_, dst_); + if (compare == 0) { + id_map_.MapInsts(src_inst, dst_inst); + } + if (compare <= 0) { + ++src_cur; + } + if (compare >= 0) { + ++dst_cur; + } + } +} + +InstructionList Differ::SortPreambleInstructions( + const opt::Module* module, + opt::IteratorRange insts) { + InstructionList sorted; + for (const opt::Instruction& inst : insts) { + sorted.push_back(&inst); + } + std::sort( + sorted.begin(), sorted.end(), + [this, module](const opt::Instruction* a, const opt::Instruction* b) { + return ComparePreambleInstructions(a, b, module, module) < 0; + }); + return sorted; +} + +int Differ::ComparePreambleInstructions(const opt::Instruction* a, + const opt::Instruction* b, + const opt::Module* src_inst_module, + const opt::Module* dst_inst_module) { + assert(a->opcode() == b->opcode()); + assert(!a->HasResultId()); + assert(!a->HasResultType()); + + const uint32_t a_operand_count = a->NumOperands(); + const uint32_t b_operand_count = b->NumOperands(); + + if (a_operand_count < b_operand_count) { + return -1; + } + if (a_operand_count > b_operand_count) { + return 1; + } + + // Instead of comparing OpExecutionMode entry point ids as ids, compare them + // through their corresponding execution model. This simplifies traversing + // the sorted list of instructions between src and dst modules. + if (a->opcode() == SpvOpExecutionMode) { + const SpvExecutionModel src_model = + GetExecutionModel(src_inst_module, a->GetSingleWordOperand(0)); + const SpvExecutionModel dst_model = + GetExecutionModel(dst_inst_module, b->GetSingleWordOperand(0)); + + if (src_model < dst_model) { + return -1; + } + if (src_model > dst_model) { + return 1; + } + } + + // Match every operand of the instruction. + for (uint32_t operand_index = 0; operand_index < a_operand_count; + ++operand_index) { + const opt::Operand& a_operand = a->GetOperand(operand_index); + const opt::Operand& b_operand = b->GetOperand(operand_index); + + if (a_operand.type < b_operand.type) { + return -1; + } + if (a_operand.type > b_operand.type) { + return 1; + } + + switch (a_operand.type) { + case SPV_OPERAND_TYPE_ID: + // Don't compare ids, there can't be multiple instances of the + // OpExecutionMode with different ids of the same execution model. + break; + case SPV_OPERAND_TYPE_TYPE_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + case SPV_OPERAND_TYPE_SCOPE_ID: + assert(false && "Unreachable"); + break; + case SPV_OPERAND_TYPE_LITERAL_STRING: { + int str_compare = + strcmp(a_operand.AsString().c_str(), b_operand.AsString().c_str()); + if (str_compare != 0) { + return str_compare; + } + break; + } + default: + // Expect literal values to match. + assert(a_operand.words.size() == 1); + assert(b_operand.words.size() == 1); + + if (a_operand.words[0] < b_operand.words[0]) { + return -1; + } + if (a_operand.words[0] > b_operand.words[0]) { + return 1; + } + break; + } + } + + return 0; +} + +void Differ::MatchDebugAndAnnotationInstructions( + opt::IteratorRange src_insts, + opt::IteratorRange dst_insts) { + for (const opt::Instruction& src_inst : src_insts) { + for (const opt::Instruction& dst_inst : dst_insts) { + if (MappedSrcInst(&dst_inst) != nullptr) { + continue; + } + + // Map instructions as soon as they match. Debug and annotation + // instructions are matched such that there can't be multiple matches. + if (DoDebugAndAnnotationInstructionsMatch(&src_inst, &dst_inst)) { + id_map_.MapInsts(&src_inst, &dst_inst); + break; + } + } + } +} + +uint32_t Differ::GroupIdsHelperGetTypeId(const IdInstructions& id_to, + uint32_t id) { + return GetInst(id_to, id)->type_id(); +} + +SpvStorageClass Differ::GroupIdsHelperGetTypePointerStorageClass( + const IdInstructions& id_to, uint32_t id) { + const opt::Instruction* inst = GetInst(id_to, id); + assert(inst && inst->opcode() == SpvOpTypePointer); + return SpvStorageClass(inst->GetSingleWordInOperand(0)); +} + +SpvOp Differ::GroupIdsHelperGetTypePointerTypeOp(const IdInstructions& id_to, + uint32_t id) { + const opt::Instruction* inst = GetInst(id_to, id); + assert(inst && inst->opcode() == SpvOpTypePointer); + + const uint32_t type_id = inst->GetSingleWordInOperand(1); + const opt::Instruction* type_inst = GetInst(id_to, type_id); + assert(type_inst); + + return type_inst->opcode(); +} + +template +void Differ::GroupIds(const IdGroup& ids, bool is_src, + std::map* groups, + T (Differ::*get_group)(const IdInstructions&, uint32_t)) { + assert(groups->empty()); + + const IdInstructions& id_to = is_src ? src_id_to_ : dst_id_to_; + + for (const uint32_t id : ids) { + // Don't include ids that are already matched, for example through + // OpEntryPoint. + const bool is_matched = + is_src ? id_map_.IsSrcMapped(id) : id_map_.IsDstMapped(id); + if (is_matched) { + continue; + } + + T group = (this->*get_group)(id_to, id); + (*groups)[group].push_back(id); + } +} + +template +void Differ::GroupIdsAndMatch( + const IdGroup& src_ids, const IdGroup& dst_ids, T invalid_group_key, + T (Differ::*get_group)(const IdInstructions&, uint32_t), + std::function + match_group) { + // Group the ids based on a key (get_group) + std::map src_groups; + std::map dst_groups; + + GroupIds(src_ids, true, &src_groups, get_group); + GroupIds(dst_ids, false, &dst_groups, get_group); + + // Iterate over the groups, and match those with identical keys + for (const auto& iter : src_groups) { + const T& key = iter.first; + const IdGroup& src_group = iter.second; + + if (key == invalid_group_key) { + continue; + } + + const IdGroup& dst_group = dst_groups[key]; + + // Let the caller match the groups as appropriate. + match_group(src_group, dst_group); + } +} + +bool Differ::DoIdsMatch(uint32_t src_id, uint32_t dst_id) { + assert(dst_id != 0); + return id_map_.MappedDstId(src_id) == dst_id; +} + +bool Differ::DoesOperandMatch(const opt::Operand& src_operand, + const opt::Operand& dst_operand) { + assert(src_operand.type == dst_operand.type); + + switch (src_operand.type) { + case SPV_OPERAND_TYPE_ID: + case SPV_OPERAND_TYPE_TYPE_ID: + case SPV_OPERAND_TYPE_RESULT_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + case SPV_OPERAND_TYPE_SCOPE_ID: + // Match ids only if they are already matched in the id map. + return DoIdsMatch(src_operand.AsId(), dst_operand.AsId()); + case SPV_OPERAND_TYPE_LITERAL_STRING: + return src_operand.AsString() == dst_operand.AsString(); + default: + // Otherwise expect them to match exactly. + assert(src_operand.type != SPV_OPERAND_TYPE_LITERAL_STRING); + if (src_operand.words.size() != dst_operand.words.size()) { + return false; + } + for (size_t i = 0; i < src_operand.words.size(); ++i) { + if (src_operand.words[i] != dst_operand.words[i]) { + return false; + } + } + return true; + } +} + +bool Differ::DoOperandsMatch(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, + uint32_t in_operand_index_start, + uint32_t in_operand_count) { + // Caller should have returned early for instructions with different opcode. + assert(src_inst->opcode() == dst_inst->opcode()); + + bool match = true; + for (uint32_t i = 0; i < in_operand_count; ++i) { + const uint32_t in_operand_index = in_operand_index_start + i; + + const opt::Operand& src_operand = src_inst->GetInOperand(in_operand_index); + const opt::Operand& dst_operand = dst_inst->GetInOperand(in_operand_index); + + match = match && DoesOperandMatch(src_operand, dst_operand); + } + + return match; +} + +bool Differ::DoInstructionsMatch(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + // Check whether the two instructions are identical, that is the instructions + // themselves are matched, every id is matched, and every other value is + // identical. + if (MappedDstInst(src_inst) != dst_inst) { + return false; + } + + assert(src_inst->opcode() == dst_inst->opcode()); + if (src_inst->NumOperands() != dst_inst->NumOperands()) { + return false; + } + + for (uint32_t operand_index = 0; operand_index < src_inst->NumOperands(); + ++operand_index) { + const opt::Operand& src_operand = src_inst->GetOperand(operand_index); + const opt::Operand& dst_operand = dst_inst->GetOperand(operand_index); + + if (!DoesOperandMatch(src_operand, dst_operand)) { + return false; + } + } + + return true; +} + +bool Differ::DoIdsMatchFuzzy(uint32_t src_id, uint32_t dst_id) { + assert(dst_id != 0); + const uint32_t mapped_dst_id = id_map_.MappedDstId(src_id); + + // Consider unmatched ids as a match. In function bodies, no result id is + // matched yet and thus they are excluded from instruction matching when used + // as parameters in subsequent instructions. + if (mapped_dst_id == 0 || mapped_dst_id == dst_id) { + return true; + } + + // Int and Uint constants are interchangeable, match them in that case. + if (AreIdenticalUintConstants(src_id, dst_id)) { + return true; + } + + return false; +} + +bool Differ::DoesOperandMatchFuzzy(const opt::Operand& src_operand, + const opt::Operand& dst_operand) { + if (src_operand.type != dst_operand.type) { + return false; + } + + assert(src_operand.type != SPV_OPERAND_TYPE_RESULT_ID); + assert(dst_operand.type != SPV_OPERAND_TYPE_RESULT_ID); + + switch (src_operand.type) { + case SPV_OPERAND_TYPE_ID: + case SPV_OPERAND_TYPE_TYPE_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + case SPV_OPERAND_TYPE_SCOPE_ID: + // Match id operands only if they are already matched in the id map. + return DoIdsMatchFuzzy(src_operand.AsId(), dst_operand.AsId()); + default: + // Otherwise allow everything to match. + return true; + } +} + +bool Differ::DoInstructionsMatchFuzzy(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + // Similar to DoOperandsMatch, but only checks that ids that have already been + // matched are identical. Ids that are unknown are allowed to match, as well + // as any non-id operand. + if (src_inst->opcode() != dst_inst->opcode()) { + return false; + } + // For external instructions, make sure the set and opcode of the external + // instruction matches too. + if (src_inst->opcode() == SpvOpExtInst) { + if (!DoOperandsMatch(src_inst, dst_inst, 0, 2)) { + return false; + } + } + + assert(src_inst->HasResultType() == dst_inst->HasResultType()); + if (src_inst->HasResultType() && + !DoIdsMatchFuzzy(src_inst->type_id(), dst_inst->type_id())) { + return false; + } + + // TODO: allow some instructions to match with different instruction lengths, + // for example OpImage* with additional operands. + if (src_inst->NumInOperandWords() != dst_inst->NumInOperandWords()) { + return false; + } + + bool match = true; + for (uint32_t in_operand_index = 0; + in_operand_index < src_inst->NumInOperandWords(); ++in_operand_index) { + const opt::Operand& src_operand = src_inst->GetInOperand(in_operand_index); + const opt::Operand& dst_operand = dst_inst->GetInOperand(in_operand_index); + + match = match && DoesOperandMatchFuzzy(src_operand, dst_operand); + } + + return match; +} + +bool Differ::AreIdenticalUintConstants(uint32_t src_id, uint32_t dst_id) { + return IsConstantUint(src_id_to_, src_id) && + IsConstantUint(dst_id_to_, dst_id) && + GetConstantUint(src_id_to_, src_id) == + GetConstantUint(dst_id_to_, dst_id); +} + +bool Differ::DoDebugAndAnnotationInstructionsMatch( + const opt::Instruction* src_inst, const opt::Instruction* dst_inst) { + if (src_inst->opcode() != dst_inst->opcode()) { + return false; + } + + switch (src_inst->opcode()) { + case SpvOpString: + case SpvOpSourceExtension: + case SpvOpModuleProcessed: + return DoesOperandMatch(src_inst->GetOperand(0), dst_inst->GetOperand(0)); + case SpvOpSource: + return DoOperandsMatch(src_inst, dst_inst, 0, 2); + case SpvOpSourceContinued: + return true; + case SpvOpName: + return DoOperandsMatch(src_inst, dst_inst, 0, 1); + case SpvOpMemberName: + return DoOperandsMatch(src_inst, dst_inst, 0, 2); + case SpvOpDecorate: + return DoOperandsMatch(src_inst, dst_inst, 0, 2); + case SpvOpMemberDecorate: + return DoOperandsMatch(src_inst, dst_inst, 0, 3); + case SpvOpExtInst: + case SpvOpDecorationGroup: + case SpvOpGroupDecorate: + case SpvOpGroupMemberDecorate: + return false; + default: + return false; + } +} + +bool Differ::AreVariablesMatchable(uint32_t src_id, uint32_t dst_id, + uint32_t flexibility) { + // Variables must match by their built-in decorations. + uint32_t src_built_in_decoration = 0, dst_built_in_decoration = 0; + const bool src_is_built_in = GetDecorationValue( + src_id_to_, src_id, SpvDecorationBuiltIn, &src_built_in_decoration); + const bool dst_is_built_in = GetDecorationValue( + dst_id_to_, dst_id, SpvDecorationBuiltIn, &dst_built_in_decoration); + + if (src_is_built_in != dst_is_built_in) { + return false; + } + if (src_is_built_in && src_built_in_decoration != dst_built_in_decoration) { + return false; + } + + // Check their types and storage classes. + SpvStorageClass src_storage_class, dst_storage_class; + const uint32_t src_type_id = + GetVarTypeId(src_id_to_, src_id, &src_storage_class); + const uint32_t dst_type_id = + GetVarTypeId(dst_id_to_, dst_id, &dst_storage_class); + + if (!DoIdsMatch(src_type_id, dst_type_id)) { + return false; + } + switch (flexibility) { + case 0: + if (src_storage_class != dst_storage_class) { + return false; + } + break; + case 1: + if (src_storage_class != dst_storage_class) { + // Allow one of the two to be Private while the other is Input or + // Output, this allows matching in/out variables that have been turned + // global as part of linking two stages (as done in ANGLE). + const bool src_is_io = src_storage_class == SpvStorageClassInput || + src_storage_class == SpvStorageClassOutput; + const bool dst_is_io = dst_storage_class == SpvStorageClassInput || + dst_storage_class == SpvStorageClassOutput; + const bool src_is_private = src_storage_class == SpvStorageClassPrivate; + const bool dst_is_private = dst_storage_class == SpvStorageClassPrivate; + + if (!((src_is_io && dst_is_private) || (src_is_private && dst_is_io))) { + return false; + } + } + break; + default: + assert(false && "Unreachable"); + return false; + } + + // TODO: Is there any other way to check compatiblity of the variables? It's + // easy to tell when the variables definitely don't match, but there's little + // information that can be used for a definite match. + return true; +} + +bool Differ::MatchOpTypeStruct(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, + uint32_t flexibility) { + const uint32_t src_type_id = src_inst->result_id(); + const uint32_t dst_type_id = dst_inst->result_id(); + + bool src_has_name = false, dst_has_name = false; + std::string src_name = GetName(src_id_to_, src_type_id, &src_has_name); + std::string dst_name = GetName(dst_id_to_, dst_type_id, &dst_has_name); + + // If debug info is present, always match the structs by name. + if (src_has_name && dst_has_name) { + if (src_name != dst_name) { + return false; + } + + // For gl_PerVertex, find the type pointer of this type (array) and make + // sure the storage classes of src and dst match; geometry and tessellation + // shaders have two instances of gl_PerVertex. + if (src_name == "gl_PerVertex") { + return MatchPerVertexType(src_type_id, dst_type_id); + } + + return true; + } + + // If debug info is not present, match the structs by their type. + + // For gl_PerVertex, find the type pointer of this type (array) and match by + // storage class. The gl_PerVertex struct is itself found by the BuiltIn + // decorations applied to its members. + const bool src_is_per_vertex = IsPerVertexType(src_id_to_, src_type_id); + const bool dst_is_per_vertex = IsPerVertexType(dst_id_to_, dst_type_id); + if (src_is_per_vertex != dst_is_per_vertex) { + return false; + } + + if (src_is_per_vertex) { + return MatchPerVertexType(src_type_id, dst_type_id); + } + + switch (flexibility) { + case 0: + if (src_inst->NumInOperandWords() != dst_inst->NumInOperandWords()) { + return false; + } + return DoOperandsMatch(src_inst, dst_inst, 0, + src_inst->NumInOperandWords()); + case 1: + // TODO: match by taking a diff of the fields, and see if there's a >75% + // match. Need to then make sure OpMemberName, OpMemberDecorate, + // OpAccessChain etc are aware of the struct field matching. + return false; + default: + assert(false && "Unreachable"); + return false; + } +} + +bool Differ::MatchOpConstant(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, + uint32_t flexibility) { + // The constants' type must match. In flexibility == 1, match constants of + // int and uint, as they are generally interchangeable. + switch (flexibility) { + case 0: + if (!DoesOperandMatch(src_inst->GetOperand(0), dst_inst->GetOperand(0))) { + return false; + } + break; + case 1: + if (!IsIntType(src_id_to_, src_inst->type_id()) || + !IsIntType(dst_id_to_, dst_inst->type_id())) { + return false; + } + break; + default: + assert(false && "Unreachable"); + return false; + } + + const opt::Operand& src_value_operand = src_inst->GetOperand(2); + const opt::Operand& dst_value_operand = dst_inst->GetOperand(2); + + const uint64_t src_value = src_value_operand.AsLiteralUint64(); + const uint64_t dst_value = dst_value_operand.AsLiteralUint64(); + + // If values are identical, it's a match. + if (src_value == dst_value) { + return true; + } + + // Otherwise, only allow flexibility for float types. + if (IsFloatType(src_id_to_, src_inst->type_id()) && flexibility == 1) { + // Tolerance is: + // + // - For float: allow 4 bits of mantissa as error + // - For double: allow 6 bits of mantissa as error + // + // TODO: the above values are arbitrary and a placeholder; investigate the + // amount of error resulting from using `printf("%f", f)` and `printf("%lf", + // d)` and having glslang parse them. + const uint64_t tolerance = src_value_operand.words.size() == 1 ? 16 : 64; + return src_value - dst_value < tolerance || + dst_value - src_value < tolerance; + } + + return false; +} + +bool Differ::MatchOpSpecConstant(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + const uint32_t src_id = src_inst->result_id(); + const uint32_t dst_id = dst_inst->result_id(); + + bool src_has_name = false, dst_has_name = false; + std::string src_name = GetName(src_id_to_, src_id, &src_has_name); + std::string dst_name = GetName(dst_id_to_, dst_id, &dst_has_name); + + // If debug info is present, always match the spec consts by name. + if (src_has_name && dst_has_name) { + return src_name == dst_name; + } + + // Otherwise, match them by SpecId. + uint32_t src_spec_id, dst_spec_id; + + if (GetDecorationValue(src_id_to_, src_id, SpvDecorationSpecId, + &src_spec_id) && + GetDecorationValue(dst_id_to_, dst_id, SpvDecorationSpecId, + &dst_spec_id)) { + return src_spec_id == dst_spec_id; + } + + // There is no SpecId decoration, while not practical, still valid. + // SpecConstantOp don't have SpecId and can be matched by operands + if (src_inst->opcode() == SpvOpSpecConstantOp) { + if (src_inst->NumInOperandWords() == dst_inst->NumInOperandWords()) { + return DoOperandsMatch(src_inst, dst_inst, 0, + src_inst->NumInOperandWords()); + } + } + + return false; +} + +bool Differ::MatchOpVariable(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, + uint32_t flexibility) { + const uint32_t src_id = src_inst->result_id(); + const uint32_t dst_id = dst_inst->result_id(); + + const bool src_is_pervertex = IsPerVertexVariable(src_id_to_, src_id); + const bool dst_is_pervertex = IsPerVertexVariable(dst_id_to_, dst_id); + + // For gl_PerVertex, make sure the input and output instances are matched + // correctly. + if (src_is_pervertex != dst_is_pervertex) { + return false; + } + if (src_is_pervertex) { + return MatchPerVertexVariable(src_inst, dst_inst); + } + + bool src_has_name = false, dst_has_name = false; + std::string src_name = GetName(src_id_to_, src_id, &src_has_name); + std::string dst_name = GetName(dst_id_to_, dst_id, &dst_has_name); + + // If debug info is present, always match the variables by name. + if (src_has_name && dst_has_name) { + return src_name == dst_name; + } + + // If debug info is not present, see if the variables can be matched by their + // built-in decorations. + uint32_t src_built_in_decoration; + const bool src_is_built_in = GetDecorationValue( + src_id_to_, src_id, SpvDecorationBuiltIn, &src_built_in_decoration); + + if (src_is_built_in && AreVariablesMatchable(src_id, dst_id, flexibility)) { + return true; + } + + SpvStorageClass src_storage_class, dst_storage_class; + GetVarTypeId(src_id_to_, src_id, &src_storage_class); + GetVarTypeId(dst_id_to_, dst_id, &dst_storage_class); + + if (src_storage_class != dst_storage_class) { + return false; + } + + // If variables are decorated with set/binding, match by the value of those + // decorations. + if (!options_.ignore_set_binding) { + uint32_t src_set = 0, dst_set = 0; + uint32_t src_binding = 0, dst_binding = 0; + + const bool src_has_set = GetDecorationValue( + src_id_to_, src_id, SpvDecorationDescriptorSet, &src_set); + const bool dst_has_set = GetDecorationValue( + dst_id_to_, dst_id, SpvDecorationDescriptorSet, &dst_set); + const bool src_has_binding = + GetDecorationValue(src_id_to_, src_id, SpvDecorationBinding, &src_set); + const bool dst_has_binding = + GetDecorationValue(dst_id_to_, dst_id, SpvDecorationBinding, &dst_set); + + if (src_has_set && dst_has_set && src_has_binding && dst_has_binding) { + return src_set == dst_set && src_binding == dst_binding; + } + } + + // If variables are decorated with location, match by the value of that + // decoration. + if (!options_.ignore_location) { + uint32_t src_location, dst_location; + + const bool src_has_location = GetDecorationValue( + src_id_to_, src_id, SpvDecorationLocation, &src_location); + const bool dst_has_location = GetDecorationValue( + dst_id_to_, dst_id, SpvDecorationLocation, &dst_location); + + if (src_has_location && dst_has_location) { + return src_location == dst_location; + } + } + + // Currently, there's no other way to match variables. + return false; +} + +bool Differ::MatchPerVertexType(uint32_t src_type_id, uint32_t dst_type_id) { + // For gl_PerVertex, find the type pointer of this type (array) and make sure + // the storage classes of src and dst match; geometry and tessellation shaders + // have two instances of gl_PerVertex. + SpvStorageClass src_storage_class = + GetPerVertexStorageClass(src_, src_type_id); + SpvStorageClass dst_storage_class = + GetPerVertexStorageClass(dst_, dst_type_id); + + assert(src_storage_class == SpvStorageClassInput || + src_storage_class == SpvStorageClassOutput); + assert(dst_storage_class == SpvStorageClassInput || + dst_storage_class == SpvStorageClassOutput); + + return src_storage_class == dst_storage_class; +} + +bool Differ::MatchPerVertexVariable(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + SpvStorageClass src_storage_class = + SpvStorageClass(src_inst->GetSingleWordInOperand(0)); + SpvStorageClass dst_storage_class = + SpvStorageClass(dst_inst->GetSingleWordInOperand(0)); + + return src_storage_class == dst_storage_class; +} + +void Differ::MatchTypeForwardPointersByName(const IdGroup& src, + const IdGroup& dst) { + // Given two sets of compatible groups of OpTypeForwardPointer instructions, + // attempts to match them by name. + + // Group them by debug info and loop over them. + GroupIdsAndMatch( + src, dst, "", &Differ::GetSanitizedName, + [this](const IdGroup& src_group, const IdGroup& dst_group) { + + // Match only if there's a unique forward declaration with this debug + // name. + if (src_group.size() == 1 && dst_group.size() == 1) { + id_map_.MapIds(src_group[0], dst_group[0]); + } + }); +} + +void Differ::MatchTypeForwardPointersByTypeOp(const IdGroup& src, + const IdGroup& dst) { + // Given two sets of compatible groups of OpTypeForwardPointer instructions, + // attempts to match them by type op. Must be called after + // MatchTypeForwardPointersByName to match as many as possible by debug info. + + // Remove ids that are matched with debug info in + // MatchTypeForwardPointersByName. + IdGroup src_unmatched_ids; + IdGroup dst_unmatched_ids; + + std::copy_if(src.begin(), src.end(), std::back_inserter(src_unmatched_ids), + [this](uint32_t id) { return !id_map_.IsSrcMapped(id); }); + std::copy_if(dst.begin(), dst.end(), std::back_inserter(dst_unmatched_ids), + [this](uint32_t id) { return !id_map_.IsDstMapped(id); }); + + // Match only if there's a unique forward declaration with this + // storage class and type opcode. If both have debug info, they + // must not have been matchable. + if (src_unmatched_ids.size() == 1 && dst_unmatched_ids.size() == 1) { + uint32_t src_id = src_unmatched_ids[0]; + uint32_t dst_id = dst_unmatched_ids[0]; + if (!HasName(src_id_to_, src_id) || !HasName(dst_id_to_, dst_id)) { + id_map_.MapIds(src_id, dst_id); + } + } +} + +InstructionList Differ::GetFunctionBody(opt::IRContext* context, + opt::Function& function) { + // Canonicalize the blocks of the function to produce better diff, for example + // to not produce any diff if the src and dst have the same switch/case blocks + // but with the cases simply reordered. + std::list order; + context->cfg()->ComputeStructuredOrder(&function, &*function.begin(), &order); + + // Go over the instructions of the function and add the instructions to a flat + // list to simplify future iterations. + InstructionList body; + for (opt::BasicBlock* block : order) { + block->ForEachInst( + [&body](const opt::Instruction* inst) { body.push_back(inst); }, true); + } + body.push_back(function.EndInst()); + + return body; +} + +InstructionList Differ::GetFunctionHeader(const opt::Function& function) { + // Go over the instructions of the function and add the header instructions to + // a flat list to simplify diff generation. + InstructionList body; + function.WhileEachInst( + [&body](const opt::Instruction* inst) { + if (inst->opcode() == SpvOpLabel) { + return false; + } + body.push_back(inst); + return true; + }, + true, true); + + return body; +} + +void Differ::GetFunctionBodies(opt::IRContext* context, FunctionMap* functions, + FunctionInstMap* function_insts) { + for (opt::Function& function : *context->module()) { + uint32_t id = function.result_id(); + assert(functions->find(id) == functions->end()); + assert(function_insts->find(id) == function_insts->end()); + + (*functions)[id] = &function; + + InstructionList body = GetFunctionBody(context, function); + (*function_insts)[id] = std::move(body); + } +} + +void Differ::GetFunctionHeaderInstructions(const opt::Module* module, + FunctionInstMap* function_insts) { + for (opt::Function& function : *module) { + InstructionList body = GetFunctionHeader(function); + (*function_insts)[function.result_id()] = std::move(body); + } +} + +void Differ::BestEffortMatchFunctions(const IdGroup& src_func_ids, + const IdGroup& dst_func_ids, + const FunctionInstMap& src_func_insts, + const FunctionInstMap& dst_func_insts) { + struct MatchResult { + uint32_t src_id; + uint32_t dst_id; + DiffMatch src_match; + DiffMatch dst_match; + float match_rate; + bool operator<(const MatchResult& other) const { + return match_rate > other.match_rate; + } + }; + std::vector all_match_results; + + for (const uint32_t src_func_id : src_func_ids) { + if (id_map_.IsSrcMapped(src_func_id)) { + continue; + } + const std::string src_name = GetSanitizedName(src_id_to_, src_func_id); + + for (const uint32_t dst_func_id : dst_func_ids) { + if (id_map_.IsDstMapped(dst_func_id)) { + continue; + } + + // Don't match functions that are named, but the names are different. + const std::string dst_name = GetSanitizedName(dst_id_to_, dst_func_id); + if (src_name != "" && dst_name != "" && src_name != dst_name) { + continue; + } + + DiffMatch src_match_result, dst_match_result; + float match_rate = MatchFunctionBodies( + src_func_insts.at(src_func_id), dst_func_insts.at(dst_func_id), + &src_match_result, &dst_match_result); + + // Only consider the functions a match if there's at least 60% match. + // This is an arbitrary limit that should be tuned. + constexpr float pass_match_rate = 0.6f; + if (match_rate >= pass_match_rate) { + all_match_results.emplace_back( + MatchResult{src_func_id, dst_func_id, std::move(src_match_result), + std::move(dst_match_result), match_rate}); + } + } + } + + std::sort(all_match_results.begin(), all_match_results.end()); + + for (const MatchResult& match_result : all_match_results) { + if (id_map_.IsSrcMapped(match_result.src_id) || + id_map_.IsDstMapped(match_result.dst_id)) { + continue; + } + + id_map_.MapIds(match_result.src_id, match_result.dst_id); + + MatchIdsInFunctionBodies(src_func_insts.at(match_result.src_id), + dst_func_insts.at(match_result.dst_id), + match_result.src_match, match_result.dst_match, 0); + } +} + +void Differ::MatchFunctionParamIds(const opt::Function* src_func, + const opt::Function* dst_func) { + IdGroup src_params; + IdGroup dst_params; + src_func->ForEachParam( + [&src_params](const opt::Instruction* param) { + src_params.push_back(param->result_id()); + }, + false); + dst_func->ForEachParam( + [&dst_params](const opt::Instruction* param) { + dst_params.push_back(param->result_id()); + }, + false); + + GroupIdsAndMatch( + src_params, dst_params, "", &Differ::GetSanitizedName, + [this](const IdGroup& src_group, const IdGroup& dst_group) { + + // There shouldn't be two parameters with the same name, so the ids + // should match. There is nothing restricting the SPIR-V however to have + // two parameters with the same name, so be resilient against that. + if (src_group.size() == 1 && dst_group.size() == 1) { + id_map_.MapIds(src_group[0], dst_group[0]); + } + }); + + // Then match the parameters by their type. If there are multiple of them, + // match them by their order. + GroupIdsAndMatch( + src_params, dst_params, 0, &Differ::GroupIdsHelperGetTypeId, + [this](const IdGroup& src_group_by_type_id, + const IdGroup& dst_group_by_type_id) { + + const size_t shared_param_count = + std::min(src_group_by_type_id.size(), dst_group_by_type_id.size()); + + for (size_t param_index = 0; param_index < shared_param_count; + ++param_index) { + id_map_.MapIds(src_group_by_type_id[0], dst_group_by_type_id[0]); + } + }); +} + +float Differ::MatchFunctionBodies(const InstructionList& src_body, + const InstructionList& dst_body, + DiffMatch* src_match_result, + DiffMatch* dst_match_result) { + LongestCommonSubsequence> lcs(src_body, + dst_body); + + uint32_t best_match_length = lcs.Get( + [this](const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + return DoInstructionsMatchFuzzy(src_inst, dst_inst); + }, + src_match_result, dst_match_result); + + // TODO: take the gaps in between matches and match those again with a relaxed + // instruction-and-type-only comparison. This can produce a better diff for + // example if an array index is changed, causing the OpAccessChain id to not + // match and subsequently every operation that's derived from that id. + // Usually this mismatch cascades until the next OpStore which doesn't produce + // an id. + + return static_cast(best_match_length) * 2.0f / + static_cast(src_body.size() + dst_body.size()); +} + +void Differ::MatchIdsInFunctionBodies(const InstructionList& src_body, + const InstructionList& dst_body, + const DiffMatch& src_match_result, + const DiffMatch& dst_match_result, + uint32_t flexibility) { + size_t src_cur = 0; + size_t dst_cur = 0; + + while (src_cur < src_body.size() && dst_cur < dst_body.size()) { + if (src_match_result[src_cur] && dst_match_result[dst_cur]) { + // Match instructions the src and dst instructions. + // + // TODO: count the matchings between variables discovered this way and + // choose the "best match" after all functions have been diffed and all + // instructions analyzed. + const opt::Instruction* src_inst = src_body[src_cur++]; + const opt::Instruction* dst_inst = dst_body[dst_cur++]; + + // Record the matching between the instructions. This is done only once + // (hence flexibility == 0). Calls with non-zero flexibility values will + // only deal with matching other ids based on the operands. + if (flexibility == 0) { + id_map_.MapInsts(src_inst, dst_inst); + } + + // Match any unmatched variables referenced by the instructions. + MatchVariablesUsedByMatchedInstructions(src_inst, dst_inst, flexibility); + continue; + } + if (!src_match_result[src_cur]) { + ++src_cur; + } + if (!dst_match_result[dst_cur]) { + ++dst_cur; + } + } +} + +void Differ::MatchVariablesUsedByMatchedInstructions( + const opt::Instruction* src_inst, const opt::Instruction* dst_inst, + uint32_t flexibility) { + // For OpAccessChain, OpLoad and OpStore instructions that reference unmatched + // variables, match them as a best effort. + assert(src_inst->opcode() == dst_inst->opcode()); + switch (src_inst->opcode()) { + default: + // TODO: match functions based on OpFunctionCall? + break; + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpPtrAccessChain: + case SpvOpInBoundsPtrAccessChain: + case SpvOpLoad: + case SpvOpStore: + const uint32_t src_pointer_id = src_inst->GetSingleWordInOperand(0); + const uint32_t dst_pointer_id = dst_inst->GetSingleWordInOperand(0); + if (IsVariable(src_id_to_, src_pointer_id) && + IsVariable(dst_id_to_, dst_pointer_id) && + !id_map_.IsSrcMapped(src_pointer_id) && + !id_map_.IsDstMapped(dst_pointer_id) && + AreVariablesMatchable(src_pointer_id, dst_pointer_id, flexibility)) { + id_map_.MapIds(src_pointer_id, dst_pointer_id); + } + break; + } +} + +const opt::Instruction* Differ::GetInst(const IdInstructions& id_to, + uint32_t id) { + assert(id != 0); + assert(id < id_to.inst_map_.size()); + + const opt::Instruction* inst = id_to.inst_map_[id]; + assert(inst != nullptr); + + return inst; +} + +uint32_t Differ::GetConstantUint(const IdInstructions& id_to, + uint32_t constant_id) { + const opt::Instruction* constant_inst = GetInst(id_to, constant_id); + assert(constant_inst->opcode() == SpvOpConstant); + assert(GetInst(id_to, constant_inst->type_id())->opcode() == SpvOpTypeInt); + + return constant_inst->GetSingleWordInOperand(0); +} + +SpvExecutionModel Differ::GetExecutionModel(const opt::Module* module, + uint32_t entry_point_id) { + for (const opt::Instruction& inst : module->entry_points()) { + assert(inst.opcode() == SpvOpEntryPoint); + if (inst.GetSingleWordOperand(1) == entry_point_id) { + return SpvExecutionModel(inst.GetSingleWordOperand(0)); + } + } + + assert(false && "Unreachable"); + return SpvExecutionModel(0xFFF); +} + +bool Differ::HasName(const IdInstructions& id_to, uint32_t id) { + assert(id != 0); + assert(id < id_to.name_map_.size()); + + for (const opt::Instruction* inst : id_to.name_map_[id]) { + if (inst->opcode() == SpvOpName) { + return true; + } + } + + return false; +} + +std::string Differ::GetName(const IdInstructions& id_to, uint32_t id, + bool* has_name) { + assert(id != 0); + assert(id < id_to.name_map_.size()); + + for (const opt::Instruction* inst : id_to.name_map_[id]) { + if (inst->opcode() == SpvOpName) { + *has_name = true; + return inst->GetOperand(1).AsString(); + } + } + + *has_name = false; + return ""; +} + +std::string Differ::GetSanitizedName(const IdInstructions& id_to, uint32_t id) { + bool has_name = false; + std::string name = GetName(id_to, id, &has_name); + + if (!has_name) { + return ""; + } + + // Remove args from the name, in case this is a function name + return name.substr(0, name.find('(')); +} + +uint32_t Differ::GetVarTypeId(const IdInstructions& id_to, uint32_t var_id, + SpvStorageClass* storage_class) { + const opt::Instruction* var_inst = GetInst(id_to, var_id); + assert(var_inst->opcode() == SpvOpVariable); + + *storage_class = SpvStorageClass(var_inst->GetSingleWordInOperand(0)); + + // Get the type pointer from the variable. + const uint32_t type_pointer_id = var_inst->type_id(); + const opt::Instruction* type_pointer_inst = GetInst(id_to, type_pointer_id); + + // Get the type from the type pointer. + return type_pointer_inst->GetSingleWordInOperand(1); +} + +bool Differ::GetDecorationValue(const IdInstructions& id_to, uint32_t id, + SpvDecoration decoration, + uint32_t* decoration_value) { + assert(id != 0); + assert(id < id_to.decoration_map_.size()); + + for (const opt::Instruction* inst : id_to.decoration_map_[id]) { + if (inst->opcode() == SpvOpDecorate && + inst->GetSingleWordOperand(0) == id && + inst->GetSingleWordOperand(1) == decoration) { + *decoration_value = inst->GetSingleWordOperand(2); + return true; + } + } + + return false; +} + +const opt::Instruction* Differ::GetForwardPointerInst( + const IdInstructions& id_to, uint32_t id) { + assert(id != 0); + assert(id < id_to.forward_pointer_map_.size()); + return id_to.forward_pointer_map_[id]; +} + +bool Differ::IsIntType(const IdInstructions& id_to, uint32_t type_id) { + return IsOp(id_to, type_id, SpvOpTypeInt); +} + +bool Differ::IsFloatType(const IdInstructions& id_to, uint32_t type_id) { + return IsOp(id_to, type_id, SpvOpTypeFloat); +} + +bool Differ::IsConstantUint(const IdInstructions& id_to, uint32_t id) { + const opt::Instruction* constant_inst = GetInst(id_to, id); + if (constant_inst->opcode() != SpvOpConstant) { + return false; + } + + const opt::Instruction* type_inst = GetInst(id_to, constant_inst->type_id()); + return type_inst->opcode() == SpvOpTypeInt; +} + +bool Differ::IsVariable(const IdInstructions& id_to, uint32_t pointer_id) { + return IsOp(id_to, pointer_id, SpvOpVariable); +} + +bool Differ::IsOp(const IdInstructions& id_to, uint32_t id, SpvOp op) { + return GetInst(id_to, id)->opcode() == op; +} + +bool Differ::IsPerVertexType(const IdInstructions& id_to, uint32_t type_id) { + assert(type_id != 0); + assert(type_id < id_to.decoration_map_.size()); + + for (const opt::Instruction* inst : id_to.decoration_map_[type_id]) { + if (inst->opcode() == SpvOpMemberDecorate && + inst->GetSingleWordOperand(0) == type_id && + inst->GetSingleWordOperand(2) == SpvDecorationBuiltIn) { + SpvBuiltIn built_in = SpvBuiltIn(inst->GetSingleWordOperand(3)); + + // Only gl_PerVertex can have, and it can only have, the following + // built-in decorations. + return built_in == SpvBuiltInPosition || + built_in == SpvBuiltInPointSize || + built_in == SpvBuiltInClipDistance || + built_in == SpvBuiltInCullDistance; + } + } + + return false; +} + +bool Differ::IsPerVertexVariable(const IdInstructions& id_to, uint32_t var_id) { + // Get the type from the type pointer. + SpvStorageClass storage_class; + uint32_t type_id = GetVarTypeId(id_to, var_id, &storage_class); + const opt::Instruction* type_inst = GetInst(id_to, type_id); + + // If array, get the element type. + if (type_inst->opcode() == SpvOpTypeArray) { + type_id = type_inst->GetSingleWordInOperand(0); + } + + // Now check if the type is gl_PerVertex. + return IsPerVertexType(id_to, type_id); +} + +SpvStorageClass Differ::GetPerVertexStorageClass(const opt::Module* module, + uint32_t type_id) { + for (const opt::Instruction& inst : module->types_values()) { + switch (inst.opcode()) { + case SpvOpTypeArray: + // The gl_PerVertex instance could be an array, look for a variable of + // the array type instead. + if (inst.GetSingleWordInOperand(0) == type_id) { + type_id = inst.result_id(); + } + break; + case SpvOpTypePointer: + // Find the storage class of the pointer to this type. + if (inst.GetSingleWordInOperand(1) == type_id) { + return SpvStorageClass(inst.GetSingleWordInOperand(0)); + } + break; + default: + break; + } + } + + // gl_PerVertex is declared, but is unused. Return either of Input or Output + // classes just so it matches one in the other module. This should be highly + // unlikely, perhaps except for ancient GS-used-to-emulate-CS scenarios. + return SpvStorageClassOutput; +} + +spv_ext_inst_type_t Differ::GetExtInstType(const IdInstructions& id_to, + uint32_t set_id) { + const opt::Instruction* set_inst = GetInst(id_to, set_id); + return spvExtInstImportTypeGet(set_inst->GetInOperand(0).AsString().c_str()); +} + +spv_number_kind_t Differ::GetNumberKind(const IdInstructions& id_to, + const opt::Instruction& inst, + uint32_t operand_index, + uint32_t* number_bit_width) { + const opt::Operand& operand = inst.GetOperand(operand_index); + *number_bit_width = 0; + + // A very limited version of Parser::parseOperand. + switch (operand.type) { + case SPV_OPERAND_TYPE_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER: + // Always unsigned integers. + *number_bit_width = 32; + return SPV_NUMBER_UNSIGNED_INT; + case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: + case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER: + switch (inst.opcode()) { + case SpvOpSwitch: + case SpvOpConstant: + case SpvOpSpecConstant: + // Same kind of number as the selector (OpSwitch) or the type + // (Op*Constant). + return GetTypeNumberKind(id_to, inst.GetSingleWordOperand(0), + number_bit_width); + default: + assert(false && "Unreachable"); + break; + } + break; + default: + break; + } + + return SPV_NUMBER_NONE; +} + +spv_number_kind_t Differ::GetTypeNumberKind(const IdInstructions& id_to, + uint32_t id, + uint32_t* number_bit_width) { + const opt::Instruction* type_inst = GetInst(id_to, id); + if (!spvOpcodeIsScalarType(type_inst->opcode())) { + type_inst = GetInst(id_to, type_inst->type_id()); + } + + switch (type_inst->opcode()) { + case SpvOpTypeInt: + *number_bit_width = type_inst->GetSingleWordOperand(1); + return type_inst->GetSingleWordOperand(2) == 0 ? SPV_NUMBER_UNSIGNED_INT + : SPV_NUMBER_SIGNED_INT; + break; + case SpvOpTypeFloat: + *number_bit_width = type_inst->GetSingleWordOperand(1); + return SPV_NUMBER_FLOATING; + default: + assert(false && "Unreachable"); + return SPV_NUMBER_NONE; + } +} + +void Differ::MatchCapabilities() { + MatchPreambleInstructions(src_->capabilities(), dst_->capabilities()); +} + +void Differ::MatchExtensions() { + MatchPreambleInstructions(src_->extensions(), dst_->extensions()); +} + +void Differ::MatchExtInstImportIds() { + // Bunch all of this section's ids as potential matches. + PotentialIdMap potential_id_map; + auto get_result_id = [](const opt::Instruction& inst) { + return inst.result_id(); + }; + auto accept_all = [](const opt::Instruction&) { return true; }; + + PoolPotentialIds(src_->ext_inst_imports(), potential_id_map.src_ids, true, + accept_all, get_result_id); + PoolPotentialIds(dst_->ext_inst_imports(), potential_id_map.dst_ids, false, + accept_all, get_result_id); + + // Then match the ids. + MatchIds(potential_id_map, [](const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + // Match OpExtInstImport by exact name, which is operand 1 + const opt::Operand& src_name = src_inst->GetOperand(1); + const opt::Operand& dst_name = dst_inst->GetOperand(1); + + return src_name.AsString() == dst_name.AsString(); + }); +} +void Differ::MatchMemoryModel() { + // Always match the memory model instructions, there is always a single one of + // it. + id_map_.MapInsts(src_->GetMemoryModel(), dst_->GetMemoryModel()); +} + +void Differ::MatchEntryPointIds() { + // Match OpEntryPoint ids (at index 1) by ExecutionModel (at index 0) and + // possibly name (at index 2). OpEntryPoint doesn't produce a result id, so + // this function doesn't use the helpers the other functions use. + + // Map from execution model to OpEntryPoint instructions of that model. + using ExecutionModelMap = + std::unordered_map>; + ExecutionModelMap src_entry_points_map; + ExecutionModelMap dst_entry_points_map; + std::set all_execution_models; + + for (const opt::Instruction& src_inst : src_->entry_points()) { + uint32_t execution_model = src_inst.GetSingleWordOperand(0); + src_entry_points_map[execution_model].push_back(&src_inst); + all_execution_models.insert(execution_model); + } + for (const opt::Instruction& dst_inst : dst_->entry_points()) { + uint32_t execution_model = dst_inst.GetSingleWordOperand(0); + dst_entry_points_map[execution_model].push_back(&dst_inst); + all_execution_models.insert(execution_model); + } + + // Go through each model and match the ids. + for (const uint32_t execution_model : all_execution_models) { + auto& src_insts = src_entry_points_map[execution_model]; + auto& dst_insts = dst_entry_points_map[execution_model]; + + // If there is only one entry point in src and dst with that model, match + // them unconditionally. + if (src_insts.size() == 1 && dst_insts.size() == 1) { + uint32_t src_id = src_insts[0]->GetSingleWordOperand(1); + uint32_t dst_id = dst_insts[0]->GetSingleWordOperand(1); + id_map_.MapIds(src_id, dst_id); + id_map_.MapInsts(src_insts[0], dst_insts[0]); + continue; + } + + // Otherwise match them by name. + bool matched = false; + for (const opt::Instruction* src_inst : src_insts) { + for (const opt::Instruction* dst_inst : dst_insts) { + const opt::Operand& src_name = src_inst->GetOperand(2); + const opt::Operand& dst_name = dst_inst->GetOperand(2); + + if (src_name.AsString() == dst_name.AsString()) { + uint32_t src_id = src_inst->GetSingleWordOperand(1); + uint32_t dst_id = dst_inst->GetSingleWordOperand(1); + id_map_.MapIds(src_id, dst_id); + id_map_.MapInsts(src_inst, dst_inst); + matched = true; + break; + } + } + if (matched) { + break; + } + } + } +} + +void Differ::MatchExecutionModes() { + MatchPreambleInstructions(src_->execution_modes(), dst_->execution_modes()); +} + +void Differ::MatchTypeForwardPointers() { + // Bunch all of type forward pointers as potential matches. + PotentialIdMap potential_id_map; + auto get_pointer_type_id = [](const opt::Instruction& inst) { + return inst.GetSingleWordOperand(0); + }; + auto accept_type_forward_pointer_ops = [](const opt::Instruction& inst) { + return inst.opcode() == SpvOpTypeForwardPointer; + }; + + PoolPotentialIds(src_->types_values(), potential_id_map.src_ids, true, + accept_type_forward_pointer_ops, get_pointer_type_id); + PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids, false, + accept_type_forward_pointer_ops, get_pointer_type_id); + + // Matching types with cyclical references (i.e. in the style of linked lists) + // can get very complex. Currently, the diff tool matches types bottom up, so + // on every instruction it expects to know if its operands are already matched + // or not. With cyclical references, it cannot know that. Type matching may + // need significant modifications to be able to support this use case. + // + // Currently, forwarded types are only matched by storage class and debug + // info, with minimal matching of the type being forwarded: + // + // - Group by class + // - Group by OpType being pointed to + // - Group by debug info + // - If same name and unique, match + // - If leftover is unique, match + + // Group forwarded pointers by storage class first and loop over them. + GroupIdsAndMatch( + potential_id_map.src_ids, potential_id_map.dst_ids, SpvStorageClassMax, + &Differ::GroupIdsHelperGetTypePointerStorageClass, + [this](const IdGroup& src_group_by_storage_class, + const IdGroup& dst_group_by_storage_class) { + + // Group them further by the type they are pointing to and loop over + // them. + GroupIdsAndMatch( + src_group_by_storage_class, dst_group_by_storage_class, SpvOpMax, + &Differ::GroupIdsHelperGetTypePointerTypeOp, + [this](const IdGroup& src_group_by_type_op, + const IdGroup& dst_group_by_type_op) { + + // Group them even further by debug info, if possible and match by + // debug name. + MatchTypeForwardPointersByName(src_group_by_type_op, + dst_group_by_type_op); + + // Match the leftovers only if they lack debug info and there is + // only one instance of them. + MatchTypeForwardPointersByTypeOp(src_group_by_type_op, + dst_group_by_type_op); + }); + }); + + // Match the instructions that forward declare the same type themselves + for (uint32_t src_id : potential_id_map.src_ids) { + uint32_t dst_id = id_map_.MappedDstId(src_id); + if (dst_id == 0) continue; + + const opt::Instruction* src_forward_inst = + GetForwardPointerInst(src_id_to_, src_id); + const opt::Instruction* dst_forward_inst = + GetForwardPointerInst(dst_id_to_, dst_id); + + assert(src_forward_inst); + assert(dst_forward_inst); + + id_map_.MapInsts(src_forward_inst, dst_forward_inst); + } +} + +void Differ::MatchTypeIds() { + // Bunch all of type ids as potential matches. + PotentialIdMap potential_id_map; + auto get_result_id = [](const opt::Instruction& inst) { + return inst.result_id(); + }; + auto accept_type_ops = [](const opt::Instruction& inst) { + return spvOpcodeGeneratesType(inst.opcode()); + }; + + PoolPotentialIds(src_->types_values(), potential_id_map.src_ids, true, + accept_type_ops, get_result_id); + PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids, false, + accept_type_ops, get_result_id); + + // Then match the ids. Start with exact matches, then match the leftover with + // gradually loosening degrees of strictness. For example, in the absence of + // debug info, two block types will be matched if they differ only in a few of + // the fields. + for (uint32_t flexibility = 0; flexibility < 2; ++flexibility) { + MatchIds(potential_id_map, [this, flexibility]( + const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + const SpvOp src_op = src_inst->opcode(); + const SpvOp dst_op = dst_inst->opcode(); + + // Don't match if the opcode is not the same. + if (src_op != dst_op) { + return false; + } + + switch (src_op) { + case SpvOpTypeVoid: + case SpvOpTypeBool: + case SpvOpTypeSampler: + // void, bool and sampler are unique, match them. + return true; + case SpvOpTypeInt: + case SpvOpTypeFloat: + case SpvOpTypeVector: + case SpvOpTypeMatrix: + case SpvOpTypeSampledImage: + case SpvOpTypeRuntimeArray: + case SpvOpTypePointer: + // Match these instructions when all operands match. + assert(src_inst->NumInOperandWords() == + dst_inst->NumInOperandWords()); + return DoOperandsMatch(src_inst, dst_inst, 0, + src_inst->NumInOperandWords()); + + case SpvOpTypeFunction: + case SpvOpTypeImage: + // Match function types only if they have the same number of operands, + // and they all match. + // Match image types similarly, expecting the optional final parameter + // to match (if provided in both) + if (src_inst->NumInOperandWords() != dst_inst->NumInOperandWords()) { + return false; + } + return DoOperandsMatch(src_inst, dst_inst, 0, + src_inst->NumInOperandWords()); + + case SpvOpTypeArray: + // Match arrays only if the element type and length match. The length + // is an id of a constant, so the actual constant it's defining is + // compared instead. + if (!DoOperandsMatch(src_inst, dst_inst, 0, 1)) { + return false; + } + + if (AreIdenticalUintConstants(src_inst->GetSingleWordInOperand(1), + dst_inst->GetSingleWordInOperand(1))) { + return true; + } + + // If size is not OpConstant, expect the ids to match exactly (for + // example if a spec contant is used). + return DoOperandsMatch(src_inst, dst_inst, 1, 1); + + case SpvOpTypeStruct: + return MatchOpTypeStruct(src_inst, dst_inst, flexibility); + + default: + return false; + } + }); + } +} + +void Differ::MatchConstants() { + // Bunch all of constant ids as potential matches. + PotentialIdMap potential_id_map; + auto get_result_id = [](const opt::Instruction& inst) { + return inst.result_id(); + }; + auto accept_type_ops = [](const opt::Instruction& inst) { + return spvOpcodeIsConstant(inst.opcode()); + }; + + PoolPotentialIds(src_->types_values(), potential_id_map.src_ids, true, + accept_type_ops, get_result_id); + PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids, false, + accept_type_ops, get_result_id); + + // Then match the ids. Constants are matched exactly, except for float types + // that are first matched exactly, then leftovers are matched with a small + // error. + for (uint32_t flexibility = 0; flexibility < 2; ++flexibility) { + MatchIds(potential_id_map, [this, flexibility]( + const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + const SpvOp src_op = src_inst->opcode(); + const SpvOp dst_op = dst_inst->opcode(); + + // Don't match if the opcode is not the same. + if (src_op != dst_op) { + return false; + } + + switch (src_op) { + case SpvOpConstantTrue: + case SpvOpConstantFalse: + // true and false are unique, match them. + return true; + case SpvOpConstant: + return MatchOpConstant(src_inst, dst_inst, flexibility); + case SpvOpConstantComposite: + case SpvOpSpecConstantComposite: + // Composite constants must match in type and value. + // + // TODO: match OpConstantNull with OpConstantComposite with all zeros + // at flexibility == 1 + // TODO: match constants from structs that have been flexibly-matched. + if (src_inst->NumInOperandWords() != dst_inst->NumInOperandWords()) { + return false; + } + return DoesOperandMatch(src_inst->GetOperand(0), + dst_inst->GetOperand(0)) && + DoOperandsMatch(src_inst, dst_inst, 0, + src_inst->NumInOperandWords()); + case SpvOpConstantSampler: + // Match sampler constants exactly. + // TODO: Allow flexibility in parameters to better diff shaders where + // the sampler param has changed. + assert(src_inst->NumInOperandWords() == + dst_inst->NumInOperandWords()); + return DoOperandsMatch(src_inst, dst_inst, 0, + src_inst->NumInOperandWords()); + case SpvOpConstantNull: + // Match null constants as long as the type matches. + return DoesOperandMatch(src_inst->GetOperand(0), + dst_inst->GetOperand(0)); + + case SpvOpSpecConstantTrue: + case SpvOpSpecConstantFalse: + case SpvOpSpecConstant: + case SpvOpSpecConstantOp: + // Match spec constants by name if available, then by the SpecId + // decoration. + return MatchOpSpecConstant(src_inst, dst_inst); + + default: + return false; + } + }); + } +} + +void Differ::MatchVariableIds() { + // Bunch all of variable ids as potential matches. + PotentialIdMap potential_id_map; + auto get_result_id = [](const opt::Instruction& inst) { + return inst.result_id(); + }; + auto accept_type_ops = [](const opt::Instruction& inst) { + return inst.opcode() == SpvOpVariable; + }; + + PoolPotentialIds(src_->types_values(), potential_id_map.src_ids, true, + accept_type_ops, get_result_id); + PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids, false, + accept_type_ops, get_result_id); + + // Then match the ids. Start with exact matches, then match the leftover with + // gradually loosening degrees of strictness. For example, in the absence of + // debug info, two otherwise identical variables will be matched if one of + // them has a Private storage class and the other doesn't. + for (uint32_t flexibility = 0; flexibility < 2; ++flexibility) { + MatchIds(potential_id_map, + [this, flexibility](const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + assert(src_inst->opcode() == SpvOpVariable); + assert(dst_inst->opcode() == SpvOpVariable); + + return MatchOpVariable(src_inst, dst_inst, flexibility); + }); + } +} + +void Differ::MatchFunctions() { + IdGroup src_func_ids; + IdGroup dst_func_ids; + + for (const auto& func : src_funcs_) { + src_func_ids.push_back(func.first); + } + for (const auto& func : dst_funcs_) { + dst_func_ids.push_back(func.first); + } + + // Base the matching of functions on debug info when available. + GroupIdsAndMatch( + src_func_ids, dst_func_ids, "", &Differ::GetSanitizedName, + [this](const IdGroup& src_group, const IdGroup& dst_group) { + + // If there is a single function with this name in src and dst, it's a + // definite match. + if (src_group.size() == 1 && dst_group.size() == 1) { + id_map_.MapIds(src_group[0], dst_group[0]); + return; + } + + // If there are multiple functions with the same name, group them by + // type, and match only if the types match (and are unique). + GroupIdsAndMatch(src_group, dst_group, 0, + &Differ::GroupIdsHelperGetTypeId, + [this](const IdGroup& src_group_by_type_id, + const IdGroup& dst_group_by_type_id) { + + if (src_group_by_type_id.size() == 1 && + dst_group_by_type_id.size() == 1) { + id_map_.MapIds(src_group_by_type_id[0], + dst_group_by_type_id[0]); + } + }); + }); + + // Any functions that are left are pooled together and matched as if unnamed, + // with the only exception that two functions with mismatching names are not + // matched. + // + // Before that however, the diff of the functions that are matched are taken + // and processed, so that more of the global variables can be matched before + // attempting to match the rest of the functions. They can contribute to the + // precision of the diff of those functions. + for (const uint32_t src_func_id : src_func_ids) { + const uint32_t dst_func_id = id_map_.MappedDstId(src_func_id); + if (dst_func_id == 0) { + continue; + } + + // Since these functions are definite matches, match their parameters for a + // better diff. + MatchFunctionParamIds(src_funcs_[src_func_id], dst_funcs_[dst_func_id]); + + // Take the diff of the two functions. + DiffMatch src_match_result, dst_match_result; + MatchFunctionBodies(src_func_insts_[src_func_id], + dst_func_insts_[dst_func_id], &src_match_result, + &dst_match_result); + + // Match ids between the two function bodies; which can also result in + // global variables getting matched. + MatchIdsInFunctionBodies(src_func_insts_[src_func_id], + dst_func_insts_[dst_func_id], src_match_result, + dst_match_result, 0); + } + + // Best effort match functions with matching type. + GroupIdsAndMatch( + src_func_ids, dst_func_ids, 0, &Differ::GroupIdsHelperGetTypeId, + [this](const IdGroup& src_group_by_type_id, + const IdGroup& dst_group_by_type_id) { + + BestEffortMatchFunctions(src_group_by_type_id, dst_group_by_type_id, + src_func_insts_, dst_func_insts_); + }); + + // Any function that's left, best effort match them. + BestEffortMatchFunctions(src_func_ids, dst_func_ids, src_func_insts_, + dst_func_insts_); +} + +void Differ::MatchDebugs1() { + // This section in cludes: OpString, OpSourceExtension, OpSource, + // OpSourceContinued + MatchDebugAndAnnotationInstructions(src_->debugs1(), dst_->debugs1()); +} + +void Differ::MatchDebugs2() { + // This section includes: OpName, OpMemberName + MatchDebugAndAnnotationInstructions(src_->debugs2(), dst_->debugs2()); +} + +void Differ::MatchDebugs3() { + // This section includes: OpModuleProcessed + MatchDebugAndAnnotationInstructions(src_->debugs3(), dst_->debugs3()); +} + +void Differ::MatchExtInstDebugInfo() { + // This section includes OpExtInst for DebugInfo extension + MatchDebugAndAnnotationInstructions(src_->ext_inst_debuginfo(), + dst_->ext_inst_debuginfo()); +} + +void Differ::MatchAnnotations() { + // This section includes OpDecorate and family. + MatchDebugAndAnnotationInstructions(src_->annotations(), dst_->annotations()); +} + +const opt::Instruction* Differ::MappedDstInst( + const opt::Instruction* src_inst) { + return MappedInstImpl(src_inst, id_map_.SrcToDstMap(), dst_id_to_); +} + +const opt::Instruction* Differ::MappedSrcInst( + const opt::Instruction* dst_inst) { + return MappedInstImpl(dst_inst, id_map_.DstToSrcMap(), src_id_to_); +} + +const opt::Instruction* Differ::MappedInstImpl( + const opt::Instruction* inst, const IdMap& to_other, + const IdInstructions& other_id_to) { + if (inst->HasResultId()) { + if (to_other.IsMapped(inst->result_id())) { + const uint32_t other_result_id = to_other.MappedId(inst->result_id()); + + assert(other_result_id < other_id_to.inst_map_.size()); + return other_id_to.inst_map_[other_result_id]; + } + + return nullptr; + } + + return to_other.MappedInst(inst); +} + +void Differ::OutputLine(std::function are_lines_identical, + std::function output_src_line, + std::function output_dst_line) { + if (are_lines_identical()) { + out_ << " "; + output_src_line(); + } else { + OutputRed(); + out_ << "-"; + output_src_line(); + + OutputGreen(); + out_ << "+"; + output_dst_line(); + + OutputResetColor(); + } +} + +const opt::Instruction* IterInst(opt::Module::const_inst_iterator& iter) { + return &*iter; +} + +const opt::Instruction* IterInst(InstructionList::const_iterator& iter) { + return *iter; +} + +template +void Differ::OutputSection( + const InstList& src_insts, const InstList& dst_insts, + std::function + write_inst) { + auto src_iter = src_insts.begin(); + auto dst_iter = dst_insts.begin(); + + // - While src_inst doesn't have a match, output it with - + // - While dst_inst doesn't have a match, output it with + + // - Now src_inst and dst_inst both have matches; might not match each other! + // * If section is unordered, just process src_inst and its match (dst_inst + // or not), + // dst_inst will eventually be processed when its match is seen. + // * If section is ordered, also just process src_inst and its match. Its + // match must + // necessarily be dst_inst. + while (src_iter != src_insts.end() || dst_iter != dst_insts.end()) { + OutputRed(); + while (src_iter != src_insts.end() && + MappedDstInst(IterInst(src_iter)) == nullptr) { + out_ << "-"; + write_inst(*IterInst(src_iter), src_id_to_, *IterInst(src_iter)); + ++src_iter; + } + OutputGreen(); + while (dst_iter != dst_insts.end() && + MappedSrcInst(IterInst(dst_iter)) == nullptr) { + out_ << "+"; + write_inst(ToMappedSrcIds(*IterInst(dst_iter)), dst_id_to_, + *IterInst(dst_iter)); + ++dst_iter; + } + OutputResetColor(); + + if (src_iter != src_insts.end() && dst_iter != dst_insts.end()) { + const opt::Instruction* src_inst = IterInst(src_iter); + const opt::Instruction* matched_dst_inst = MappedDstInst(src_inst); + + assert(matched_dst_inst != nullptr); + assert(MappedSrcInst(IterInst(dst_iter)) != nullptr); + + OutputLine( + [this, src_inst, matched_dst_inst]() { + return DoInstructionsMatch(src_inst, matched_dst_inst); + }, + [this, src_inst, &write_inst]() { + write_inst(*src_inst, src_id_to_, *src_inst); + }, + [this, matched_dst_inst, &write_inst]() { + write_inst(ToMappedSrcIds(*matched_dst_inst), dst_id_to_, + *matched_dst_inst); + }); + + ++src_iter; + ++dst_iter; + } + } +} + +void Differ::ToParsedInstruction( + const opt::Instruction& inst, const IdInstructions& id_to, + const opt::Instruction& original_inst, + spv_parsed_instruction_t* parsed_inst, + std::vector& parsed_operands, + std::vector& inst_binary) { + inst.ToBinaryWithoutAttachedDebugInsts(&inst_binary); + parsed_operands.resize(inst.NumOperands()); + + parsed_inst->words = inst_binary.data(); + parsed_inst->num_words = static_cast(inst_binary.size()); + parsed_inst->opcode = static_cast(inst.opcode()); + parsed_inst->ext_inst_type = + inst.opcode() == SpvOpExtInst + ? GetExtInstType(id_to, original_inst.GetSingleWordInOperand(0)) + : SPV_EXT_INST_TYPE_NONE; + parsed_inst->type_id = + inst.HasResultType() ? inst.GetSingleWordOperand(0) : 0; + parsed_inst->result_id = inst.HasResultId() ? inst.result_id() : 0; + parsed_inst->operands = parsed_operands.data(); + parsed_inst->num_operands = static_cast(parsed_operands.size()); + + // Word 0 is always op and num_words, so operands start at offset 1. + uint32_t offset = 1; + for (uint16_t operand_index = 0; operand_index < parsed_inst->num_operands; + ++operand_index) { + const opt::Operand& operand = inst.GetOperand(operand_index); + spv_parsed_operand_t& parsed_operand = parsed_operands[operand_index]; + + parsed_operand.offset = static_cast(offset); + parsed_operand.num_words = static_cast(operand.words.size()); + parsed_operand.type = operand.type; + parsed_operand.number_kind = GetNumberKind( + id_to, original_inst, operand_index, &parsed_operand.number_bit_width); + + offset += parsed_operand.num_words; + } +} + +opt::Instruction Differ::ToMappedSrcIds(const opt::Instruction& dst_inst) { + // Create an identical instruction to dst_inst, except ids are changed to the + // mapped one. + opt::Instruction mapped_inst = dst_inst; + + for (uint32_t operand_index = 0; operand_index < mapped_inst.NumOperands(); + ++operand_index) { + opt::Operand& operand = mapped_inst.GetOperand(operand_index); + + if (spvIsIdType(operand.type)) { + assert(id_map_.IsDstMapped(operand.AsId())); + operand.words[0] = id_map_.MappedSrcId(operand.AsId()); + } + } + + return mapped_inst; +} + +spv_result_t Differ::Output() { + id_map_.MapUnmatchedIds(); + src_id_to_.inst_map_.resize(id_map_.SrcToDstMap().IdBound(), nullptr); + dst_id_to_.inst_map_.resize(id_map_.DstToSrcMap().IdBound(), nullptr); + + const spv_target_env target_env = SPV_ENV_UNIVERSAL_1_6; + spv_opcode_table opcode_table; + spv_operand_table operand_table; + spv_ext_inst_table ext_inst_table; + spv_result_t result; + + result = spvOpcodeTableGet(&opcode_table, target_env); + if (result != SPV_SUCCESS) return result; + + result = spvOperandTableGet(&operand_table, target_env); + if (result != SPV_SUCCESS) return result; + + result = spvExtInstTableGet(&ext_inst_table, target_env); + if (result != SPV_SUCCESS) return result; + + spv_context_t context{ + target_env, + opcode_table, + operand_table, + ext_inst_table, + }; + + const AssemblyGrammar grammar(&context); + if (!grammar.isValid()) return SPV_ERROR_INVALID_TABLE; + + uint32_t disassembly_options = SPV_BINARY_TO_TEXT_OPTION_PRINT; + if (options_.indent) { + disassembly_options |= SPV_BINARY_TO_TEXT_OPTION_INDENT; + } + + NameMapper name_mapper = GetTrivialNameMapper(); + disassemble::InstructionDisassembler dis(grammar, out_, disassembly_options, + name_mapper); + + if (!options_.no_header) { + // Output the header + // TODO: when using diff with text, the assembler overrides the version and + // generator, so these aren't reflected correctly in the output. Could + // potentially extract this info from the header comment. + OutputLine([]() { return true; }, [&dis]() { dis.EmitHeaderSpirv(); }, + []() { assert(false && "Unreachable"); }); + OutputLine([this]() { return src_->version() == dst_->version(); }, + [this, &dis]() { dis.EmitHeaderVersion(src_->version()); }, + [this, &dis]() { dis.EmitHeaderVersion(dst_->version()); }); + OutputLine([this]() { return src_->generator() == dst_->generator(); }, + [this, &dis]() { dis.EmitHeaderGenerator(src_->generator()); }, + [this, &dis]() { dis.EmitHeaderGenerator(dst_->generator()); }); + OutputLine( + [this]() { return src_->IdBound() == id_map_.SrcToDstMap().IdBound(); }, + [this, &dis]() { dis.EmitHeaderIdBound(src_->IdBound()); }, + [this, &dis]() { + dis.EmitHeaderIdBound(id_map_.SrcToDstMap().IdBound()); + }); + OutputLine([this]() { return src_->schema() == dst_->schema(); }, + [this, &dis]() { dis.EmitHeaderSchema(src_->schema()); }, + [this, &dis]() { dis.EmitHeaderSchema(dst_->schema()); }); + } + + // For each section, iterate both modules and output the disassembly. + auto write_inst = [this, &dis](const opt::Instruction& inst, + const IdInstructions& id_to, + const opt::Instruction& original_inst) { + spv_parsed_instruction_t parsed_inst; + std::vector parsed_operands; + std::vector inst_binary; + + ToParsedInstruction(inst, id_to, original_inst, &parsed_inst, + parsed_operands, inst_binary); + + dis.EmitInstruction(parsed_inst, 0); + }; + + OutputSection(src_->capabilities(), dst_->capabilities(), write_inst); + OutputSection(src_->extensions(), dst_->extensions(), write_inst); + OutputSection(src_->ext_inst_imports(), dst_->ext_inst_imports(), write_inst); + + // There is only one memory model. + OutputLine( + [this]() { + return DoInstructionsMatch(src_->GetMemoryModel(), + dst_->GetMemoryModel()); + }, + [this, &write_inst]() { + write_inst(*src_->GetMemoryModel(), src_id_to_, + *src_->GetMemoryModel()); + }, + [this, &write_inst]() { + write_inst(*dst_->GetMemoryModel(), dst_id_to_, + *dst_->GetMemoryModel()); + }); + + OutputSection(src_->entry_points(), dst_->entry_points(), write_inst); + OutputSection(src_->execution_modes(), dst_->execution_modes(), write_inst); + OutputSection(src_->debugs1(), dst_->debugs1(), write_inst); + OutputSection(src_->debugs2(), dst_->debugs2(), write_inst); + OutputSection(src_->debugs3(), dst_->debugs3(), write_inst); + OutputSection(src_->ext_inst_debuginfo(), dst_->ext_inst_debuginfo(), + write_inst); + OutputSection(src_->annotations(), dst_->annotations(), write_inst); + OutputSection(src_->types_values(), dst_->types_values(), write_inst); + + // Get the body of all the functions. + FunctionInstMap src_func_header_insts; + FunctionInstMap dst_func_header_insts; + + GetFunctionHeaderInstructions(src_, &src_func_header_insts); + GetFunctionHeaderInstructions(dst_, &dst_func_header_insts); + + for (const auto& src_func : src_func_insts_) { + const uint32_t src_func_id = src_func.first; + const InstructionList& src_insts = src_func.second; + const InstructionList& src_header_insts = + src_func_header_insts[src_func_id]; + + const uint32_t dst_func_id = id_map_.MappedDstId(src_func_id); + if (dst_func_insts_.find(dst_func_id) == dst_func_insts_.end()) { + OutputSection(src_header_insts, InstructionList(), write_inst); + OutputSection(src_insts, InstructionList(), write_inst); + continue; + } + + const InstructionList& dst_insts = dst_func_insts_[dst_func_id]; + const InstructionList& dst_header_insts = + dst_func_header_insts[dst_func_id]; + OutputSection(src_header_insts, dst_header_insts, write_inst); + OutputSection(src_insts, dst_insts, write_inst); + } + + for (const auto& dst_func : dst_func_insts_) { + const uint32_t dst_func_id = dst_func.first; + const InstructionList& dst_insts = dst_func.second; + const InstructionList& dst_header_insts = + dst_func_header_insts[dst_func_id]; + + const uint32_t src_func_id = id_map_.MappedSrcId(dst_func_id); + if (src_func_insts_.find(src_func_id) == src_func_insts_.end()) { + OutputSection(InstructionList(), dst_header_insts, write_inst); + OutputSection(InstructionList(), dst_insts, write_inst); + } + } + + out_ << std::flush; + + return SPV_SUCCESS; +} + +} // anonymous namespace + +spv_result_t Diff(opt::IRContext* src, opt::IRContext* dst, std::ostream& out, + Options options) { + // High level algorithm: + // + // - Some sections of SPIR-V don't deal with ids; instructions in those + // sections are matched identically. For example OpCapability instructions. + // - Some sections produce ids, and they can be trivially matched by their + // parameters. For example OpExtInstImport instructions. + // - Some sections annotate ids. These are matched at the end, after the ids + // themselves are matched. For example OpName or OpDecorate instructions. + // - Some sections produce ids that depend on other ids and they can be + // recursively matched. For example OpType* instructions. + // - Some sections produce ids that are not trivially matched. For these ids, + // the debug info is used when possible, or a best guess (such as through + // decorations) is used. For example OpVariable instructions. + // - Matching functions is done with multiple attempts: + // * Functions with identical debug names are matched if there are no + // overloads. + // * Otherwise, functions with identical debug names and types are matched. + // * The rest of the functions are best-effort matched, first in groups of + // identical type, then any with any. + // * The best-effort matching takes the diff of every pair of functions in + // a group and selects the top matches that also meet a similarity + // index. + // * Once a pair of functions are matched, the fuzzy diff of the + // instructions is used to match the instructions in the function body. + // The fuzzy diff makes sure that sufficiently similar instructions are + // matched and that yet-to-be-matched result ids don't result in a larger + // diff. + // + // Once the instructions are matched between the src and dst SPIR-V, the src + // is traversed and its disassembly is output. In the process, any unmatched + // instruction is prefixed with -, and any unmatched instruction in dst in the + // same section is output prefixed with +. To avoid confusion, the + // instructions in dst are output with matching ids in src so the output + // assembly is consistent. + + Differ differ(src, dst, out, options); + + // First, match instructions between the different non-annotation sections of + // the SPIR-V. + differ.MatchCapabilities(); + differ.MatchExtensions(); + differ.MatchExtInstImportIds(); + differ.MatchMemoryModel(); + differ.MatchEntryPointIds(); + differ.MatchExecutionModes(); + differ.MatchTypeForwardPointers(); + differ.MatchTypeIds(); + differ.MatchConstants(); + differ.MatchVariableIds(); + differ.MatchFunctions(); + + // Match instructions that annotate previously-matched ids. + differ.MatchDebugs1(); + differ.MatchDebugs2(); + differ.MatchDebugs3(); + differ.MatchExtInstDebugInfo(); + differ.MatchAnnotations(); + + // Show the disassembly with the diff. + // + // TODO: Based on an option, output either based on src or dst, i.e. the diff + // can show the ids and instruction/function order either from src or dst. + spv_result_t result = differ.Output(); + + differ.DumpIdMap(); + + return result; +} + +} // namespace diff +} // namespace spvtools diff --git a/source/diff/diff.h b/source/diff/diff.h new file mode 100644 index 00000000..932de9ee --- /dev/null +++ b/source/diff/diff.h @@ -0,0 +1,48 @@ +// Copyright (c) 2022 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_DIFF_DIFF_H_ +#define SOURCE_DIFF_DIFF_H_ + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace diff { + +struct Options { + bool ignore_set_binding = false; + bool ignore_location = false; + bool indent = false; + bool no_header = false; + bool color_output = false; + bool dump_id_map = false; +}; + +// Given two SPIR-V modules, this function outputs the textual diff of their +// assembly in `out`. The diff is *semantic*, so that the ordering of certain +// instructions wouldn't matter. +// +// The output is a disassembly of src, with diff(1)-style + and - lines that +// show how the src is changed into dst. To make this disassembly +// self-consistent, the ids that are output are all in the space of the src +// module; e.g. any + lines (showing instructions from the dst module) have +// their ids mapped to the matched instruction in the src module (or a new id +// allocated in the src module if unmatched). +spv_result_t Diff(opt::IRContext* src, opt::IRContext* dst, std::ostream& out, + Options options); + +} // namespace diff +} // namespace spvtools + +#endif // SOURCE_DIFF_DIFF_H_ diff --git a/source/diff/lcs.h b/source/diff/lcs.h new file mode 100644 index 00000000..6c00e864 --- /dev/null +++ b/source/diff/lcs.h @@ -0,0 +1,224 @@ +// Copyright (c) 2022 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_DIFF_LCS_H_ +#define SOURCE_DIFF_LCS_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace spvtools { +namespace diff { + +// The result of a diff. +using DiffMatch = std::vector; + +// Helper class to find the longest common subsequence between two function +// bodies. +template +class LongestCommonSubsequence { + public: + LongestCommonSubsequence(const Sequence& src, const Sequence& dst) + : src_(src), + dst_(dst), + table_(src.size(), std::vector(dst.size())) {} + + // Given two sequences, it creates a matching between them. The elements are + // simply marked as matched in src and dst, with any unmatched element in src + // implying a removal and any unmatched element in dst implying an addition. + // + // Returns the length of the longest common subsequence. + template + uint32_t Get(std::function match, + DiffMatch* src_match_result, DiffMatch* dst_match_result); + + private: + struct DiffMatchIndex { + uint32_t src_offset; + uint32_t dst_offset; + }; + + template + void CalculateLCS(std::function match); + void RetrieveMatch(DiffMatch* src_match_result, DiffMatch* dst_match_result); + bool IsInBound(DiffMatchIndex index) { + return index.src_offset < src_.size() && index.dst_offset < dst_.size(); + } + bool IsCalculated(DiffMatchIndex index) { + assert(IsInBound(index)); + return table_[index.src_offset][index.dst_offset].valid; + } + bool IsCalculatedOrOutOfBound(DiffMatchIndex index) { + return !IsInBound(index) || IsCalculated(index); + } + uint32_t GetMemoizedLength(DiffMatchIndex index) { + if (!IsInBound(index)) { + return 0; + } + assert(IsCalculated(index)); + return table_[index.src_offset][index.dst_offset].best_match_length; + } + bool IsMatched(DiffMatchIndex index) { + assert(IsCalculated(index)); + return table_[index.src_offset][index.dst_offset].matched; + } + void MarkMatched(DiffMatchIndex index, uint32_t best_match_length, + bool matched) { + assert(IsInBound(index)); + DiffMatchEntry& entry = table_[index.src_offset][index.dst_offset]; + assert(!entry.valid); + + entry.best_match_length = best_match_length & 0x3FFFFFFF; + assert(entry.best_match_length == best_match_length); + entry.matched = matched; + entry.valid = true; + } + + const Sequence& src_; + const Sequence& dst_; + + struct DiffMatchEntry { + DiffMatchEntry() : best_match_length(0), matched(false), valid(false) {} + + uint32_t best_match_length : 30; + // Whether src[i] and dst[j] matched. This is an optimization to avoid + // calling the `match` function again when walking the LCS table. + uint32_t matched : 1; + // Use for the recursive algorithm to know if the contents of this entry are + // valid. + uint32_t valid : 1; + }; + + std::vector> table_; +}; + +template +template +uint32_t LongestCommonSubsequence::Get( + std::function match, + DiffMatch* src_match_result, DiffMatch* dst_match_result) { + CalculateLCS(match); + RetrieveMatch(src_match_result, dst_match_result); + return GetMemoizedLength({0, 0}); +} + +template +template +void LongestCommonSubsequence::CalculateLCS( + std::function match) { + // The LCS algorithm is simple. Given sequences s and d, with a:b depicting a + // range in python syntax: + // + // lcs(s[i:], d[j:]) = + // lcs(s[i+1:], d[j+1:]) + 1 if s[i] == d[j] + // max(lcs(s[i+1:], d[j:]), lcs(s[i:], d[j+1:])) o.w. + // + // Once the LCS table is filled according to the above, it can be walked and + // the best match retrieved. + // + // This is a recursive function with memoization, which avoids filling table + // entries where unnecessary. This makes the best case O(N) instead of + // O(N^2). The implemention uses a std::stack to avoid stack overflow on long + // sequences. + + if (src_.empty() || dst_.empty()) { + return; + } + + std::stack to_calculate; + to_calculate.push({0, 0}); + + while (!to_calculate.empty()) { + DiffMatchIndex current = to_calculate.top(); + to_calculate.pop(); + assert(IsInBound(current)); + + // If already calculated through another path, ignore it. + if (IsCalculated(current)) { + continue; + } + + if (match(src_[current.src_offset], dst_[current.dst_offset])) { + // If the current elements match, advance both indices and calculate the + // LCS if not already. Visit `current` again afterwards, so its + // corresponding entry will be updated. + DiffMatchIndex next = {current.src_offset + 1, current.dst_offset + 1}; + if (IsCalculatedOrOutOfBound(next)) { + MarkMatched(current, GetMemoizedLength(next) + 1, true); + } else { + to_calculate.push(current); + to_calculate.push(next); + } + continue; + } + + // We've reached a pair of elements that don't match. Calculate the LCS for + // both cases of either being left unmatched and take the max. Visit + // `current` again afterwards, so its corresponding entry will be updated. + DiffMatchIndex next_src = {current.src_offset + 1, current.dst_offset}; + DiffMatchIndex next_dst = {current.src_offset, current.dst_offset + 1}; + + if (IsCalculatedOrOutOfBound(next_src) && + IsCalculatedOrOutOfBound(next_dst)) { + uint32_t best_match_length = + std::max(GetMemoizedLength(next_src), GetMemoizedLength(next_dst)); + MarkMatched(current, best_match_length, false); + continue; + } + + to_calculate.push(current); + if (!IsCalculatedOrOutOfBound(next_src)) { + to_calculate.push(next_src); + } + if (!IsCalculatedOrOutOfBound(next_dst)) { + to_calculate.push(next_dst); + } + } +} + +template +void LongestCommonSubsequence::RetrieveMatch( + DiffMatch* src_match_result, DiffMatch* dst_match_result) { + src_match_result->clear(); + dst_match_result->clear(); + + src_match_result->resize(src_.size(), false); + dst_match_result->resize(dst_.size(), false); + + DiffMatchIndex current = {0, 0}; + while (IsInBound(current)) { + if (IsMatched(current)) { + (*src_match_result)[current.src_offset++] = true; + (*dst_match_result)[current.dst_offset++] = true; + continue; + } + + if (GetMemoizedLength({current.src_offset + 1, current.dst_offset}) >= + GetMemoizedLength({current.src_offset, current.dst_offset + 1})) { + ++current.src_offset; + } else { + ++current.dst_offset; + } + } +} + +} // namespace diff +} // namespace spvtools + +#endif // SOURCE_DIFF_LCS_H_ diff --git a/source/disassemble.cpp b/source/disassemble.cpp index 966a59c7..1d61b9f9 100644 --- a/source/disassemble.cpp +++ b/source/disassemble.cpp @@ -17,6 +17,8 @@ // This file contains a disassembler: It converts a SPIR-V binary // to text. +#include "source/disassemble.h" + #include #include #include @@ -28,9 +30,7 @@ #include "source/assembly_grammar.h" #include "source/binary.h" #include "source/diagnostic.h" -#include "source/disassemble.h" #include "source/ext_inst.h" -#include "source/name_mapper.h" #include "source/opcode.h" #include "source/parsed_operand.h" #include "source/print.h" @@ -40,29 +40,21 @@ #include "source/util/make_unique.h" #include "spirv-tools/libspirv.h" +namespace spvtools { namespace { // A Disassembler instance converts a SPIR-V binary to its assembly // representation. class Disassembler { public: - Disassembler(const spvtools::AssemblyGrammar& grammar, uint32_t options, - spvtools::NameMapper name_mapper) - : grammar_(grammar), - print_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options)), - color_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COLOR, options)), - indent_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_INDENT, options) - ? kStandardIndent - : 0), - comment_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COMMENT, options)), + Disassembler(const AssemblyGrammar& grammar, uint32_t options, + NameMapper name_mapper) + : print_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options)), text_(), out_(print_ ? out_stream() : out_stream(text_)), - stream_(out_.get()), + instruction_disassembler_(grammar, out_.get(), options, name_mapper), header_(!spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER, options)), - show_byte_offset_(spvIsInBitfield( - SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET, options)), - byte_offset_(0), - name_mapper_(std::move(name_mapper)) {} + byte_offset_(0) {} // Emits the assembly header for the module, and sets up internal state // so subsequent callbacks can handle the cases where the entire module @@ -78,56 +70,13 @@ class Disassembler { spv_result_t SaveTextResult(spv_text* text_result) const; private: - enum { kStandardIndent = 15 }; - - using out_stream = spvtools::out_stream; - - // Emits an operand for the given instruction, where the instruction - // is at offset words from the start of the binary. - void EmitOperand(const spv_parsed_instruction_t& inst, - const uint16_t operand_index); - - // Emits a mask expression for the given mask word of the specified type. - void EmitMaskOperand(const spv_operand_type_t type, const uint32_t word); - - // Resets the output color, if color is turned on. - void ResetColor() { - if (color_) out_.get() << spvtools::clr::reset{print_}; - } - // Sets the output to grey, if color is turned on. - void SetGrey() { - if (color_) out_.get() << spvtools::clr::grey{print_}; - } - // Sets the output to blue, if color is turned on. - void SetBlue() { - if (color_) out_.get() << spvtools::clr::blue{print_}; - } - // Sets the output to yellow, if color is turned on. - void SetYellow() { - if (color_) out_.get() << spvtools::clr::yellow{print_}; - } - // Sets the output to red, if color is turned on. - void SetRed() { - if (color_) out_.get() << spvtools::clr::red{print_}; - } - // Sets the output to green, if color is turned on. - void SetGreen() { - if (color_) out_.get() << spvtools::clr::green{print_}; - } - - const spvtools::AssemblyGrammar& grammar_; const bool print_; // Should we also print to the standard output stream? - const bool color_; // Should we print in colour? - const int indent_; // How much to indent. 0 means don't indent - const int comment_; // Should we comment the source spv_endianness_t endian_; // The detected endianness of the binary. std::stringstream text_; // Captures the text, if not printing. out_stream out_; // The Output stream. Either to text_ or standard output. - std::ostream& stream_; // The output std::stream. - const bool header_; // Should we output header as the leading comment? - const bool show_byte_offset_; // Should we print byte offset, in hex? - size_t byte_offset_; // The number of bytes processed so far. - spvtools::NameMapper name_mapper_; + disassemble::InstructionDisassembler instruction_disassembler_; + const bool header_; // Should we output header as the leading comment? + size_t byte_offset_; // The number of bytes processed so far. bool inserted_decoration_space_ = false; bool inserted_debug_space_ = false; bool inserted_type_space_ = false; @@ -139,21 +88,11 @@ spv_result_t Disassembler::HandleHeader(spv_endianness_t endian, endian_ = endian; if (header_) { - const char* generator_tool = - spvGeneratorStr(SPV_GENERATOR_TOOL_PART(generator)); - stream_ << "; SPIR-V\n" - << "; Version: " << SPV_SPIRV_VERSION_MAJOR_PART(version) << "." - << SPV_SPIRV_VERSION_MINOR_PART(version) << "\n" - << "; Generator: " << generator_tool; - // For unknown tools, print the numeric tool value. - if (0 == strcmp("Unknown", generator_tool)) { - stream_ << "(" << SPV_GENERATOR_TOOL_PART(generator) << ")"; - } - // Print the miscellaneous part of the generator word on the same - // line as the tool name. - stream_ << "; " << SPV_GENERATOR_MISC_PART(generator) << "\n" - << "; Bound: " << id_bound << "\n" - << "; Schema: " << schema << "\n"; + instruction_disassembler_.EmitHeaderSpirv(); + instruction_disassembler_.EmitHeaderVersion(version); + instruction_disassembler_.EmitHeaderGenerator(generator); + instruction_disassembler_.EmitHeaderIdBound(id_bound); + instruction_disassembler_.EmitHeaderSchema(schema); } byte_offset_ = SPV_INDEX_INSTRUCTION * sizeof(uint32_t); @@ -163,220 +102,17 @@ spv_result_t Disassembler::HandleHeader(spv_endianness_t endian, spv_result_t Disassembler::HandleInstruction( const spv_parsed_instruction_t& inst) { - auto opcode = static_cast(inst.opcode); - if (comment_ && opcode == SpvOpFunction) { - stream_ << std::endl; - stream_ << std::string(indent_, ' '); - stream_ << "; Function " << name_mapper_(inst.result_id) << std::endl; - } - if (comment_ && !inserted_decoration_space_ && - spvOpcodeIsDecoration(opcode)) { - inserted_decoration_space_ = true; - stream_ << std::endl; - stream_ << std::string(indent_, ' '); - stream_ << "; Annotations" << std::endl; - } - if (comment_ && !inserted_debug_space_ && spvOpcodeIsDebug(opcode)) { - inserted_debug_space_ = true; - stream_ << std::endl; - stream_ << std::string(indent_, ' '); - stream_ << "; Debug Information" << std::endl; - } - if (comment_ && !inserted_type_space_ && spvOpcodeGeneratesType(opcode)) { - inserted_type_space_ = true; - stream_ << std::endl; - stream_ << std::string(indent_, ' '); - stream_ << "; Types, variables and constants" << std::endl; - } + instruction_disassembler_.EmitSectionComment(inst, inserted_decoration_space_, + inserted_debug_space_, + inserted_type_space_); - if (inst.result_id) { - SetBlue(); - const std::string id_name = name_mapper_(inst.result_id); - if (indent_) - stream_ << std::setw(std::max(0, indent_ - 3 - int(id_name.size()))); - stream_ << "%" << id_name; - ResetColor(); - stream_ << " = "; - } else { - stream_ << std::string(indent_, ' '); - } - - stream_ << "Op" << spvOpcodeString(opcode); - - for (uint16_t i = 0; i < inst.num_operands; i++) { - const spv_operand_type_t type = inst.operands[i].type; - assert(type != SPV_OPERAND_TYPE_NONE); - if (type == SPV_OPERAND_TYPE_RESULT_ID) continue; - stream_ << " "; - EmitOperand(inst, i); - } - - if (comment_ && opcode == SpvOpName) { - const spv_parsed_operand_t& operand = inst.operands[0]; - const uint32_t word = inst.words[operand.offset]; - stream_ << " ; id %" << word; - } - - if (show_byte_offset_) { - SetGrey(); - auto saved_flags = stream_.flags(); - auto saved_fill = stream_.fill(); - stream_ << " ; 0x" << std::setw(8) << std::hex << std::setfill('0') - << byte_offset_; - stream_.flags(saved_flags); - stream_.fill(saved_fill); - ResetColor(); - } + instruction_disassembler_.EmitInstruction(inst, byte_offset_); byte_offset_ += inst.num_words * sizeof(uint32_t); - stream_ << "\n"; return SPV_SUCCESS; } -void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst, - const uint16_t operand_index) { - assert(operand_index < inst.num_operands); - const spv_parsed_operand_t& operand = inst.operands[operand_index]; - const uint32_t word = inst.words[operand.offset]; - switch (operand.type) { - case SPV_OPERAND_TYPE_RESULT_ID: - assert(false && " is not supposed to be handled here"); - SetBlue(); - stream_ << "%" << name_mapper_(word); - break; - case SPV_OPERAND_TYPE_ID: - case SPV_OPERAND_TYPE_TYPE_ID: - case SPV_OPERAND_TYPE_SCOPE_ID: - case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: - SetYellow(); - stream_ << "%" << name_mapper_(word); - break; - case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: { - spv_ext_inst_desc ext_inst; - SetRed(); - if (grammar_.lookupExtInst(inst.ext_inst_type, word, &ext_inst) == - SPV_SUCCESS) { - stream_ << ext_inst->name; - } else { - if (!spvExtInstIsNonSemantic(inst.ext_inst_type)) { - assert(false && "should have caught this earlier"); - } else { - // for non-semantic instruction sets we can just print the number - stream_ << word; - } - } - } break; - case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: { - spv_opcode_desc opcode_desc; - if (grammar_.lookupOpcode(SpvOp(word), &opcode_desc)) - assert(false && "should have caught this earlier"); - SetRed(); - stream_ << opcode_desc->name; - } break; - case SPV_OPERAND_TYPE_LITERAL_INTEGER: - case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: { - SetRed(); - spvtools::EmitNumericLiteral(&stream_, inst, operand); - ResetColor(); - } break; - case SPV_OPERAND_TYPE_LITERAL_STRING: { - stream_ << "\""; - SetGreen(); - // Strings are always little-endian, and null-terminated. - // Write out the characters, escaping as needed, and without copying - // the entire string. - auto c_str = reinterpret_cast(inst.words + operand.offset); - for (auto p = c_str; *p; ++p) { - if (*p == '"' || *p == '\\') stream_ << '\\'; - stream_ << *p; - } - ResetColor(); - stream_ << '"'; - } break; - case SPV_OPERAND_TYPE_CAPABILITY: - case SPV_OPERAND_TYPE_SOURCE_LANGUAGE: - case SPV_OPERAND_TYPE_EXECUTION_MODEL: - case SPV_OPERAND_TYPE_ADDRESSING_MODEL: - case SPV_OPERAND_TYPE_MEMORY_MODEL: - case SPV_OPERAND_TYPE_EXECUTION_MODE: - case SPV_OPERAND_TYPE_STORAGE_CLASS: - case SPV_OPERAND_TYPE_DIMENSIONALITY: - case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE: - case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE: - case SPV_OPERAND_TYPE_SAMPLER_IMAGE_FORMAT: - case SPV_OPERAND_TYPE_FP_ROUNDING_MODE: - case SPV_OPERAND_TYPE_LINKAGE_TYPE: - case SPV_OPERAND_TYPE_ACCESS_QUALIFIER: - case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE: - case SPV_OPERAND_TYPE_DECORATION: - case SPV_OPERAND_TYPE_BUILT_IN: - case SPV_OPERAND_TYPE_GROUP_OPERATION: - case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS: - case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: - case SPV_OPERAND_TYPE_RAY_FLAGS: - case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION: - case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE: - case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE: - case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: - case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE: - case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER: - case SPV_OPERAND_TYPE_DEBUG_OPERATION: - case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: - case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE: - case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER: - case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION: - case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: - case SPV_OPERAND_TYPE_FPDENORM_MODE: - case SPV_OPERAND_TYPE_FPOPERATION_MODE: { - spv_operand_desc entry; - if (grammar_.lookupOperand(operand.type, word, &entry)) - assert(false && "should have caught this earlier"); - stream_ << entry->name; - } break; - case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE: - case SPV_OPERAND_TYPE_FUNCTION_CONTROL: - case SPV_OPERAND_TYPE_LOOP_CONTROL: - case SPV_OPERAND_TYPE_IMAGE: - case SPV_OPERAND_TYPE_MEMORY_ACCESS: - case SPV_OPERAND_TYPE_SELECTION_CONTROL: - case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: - case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: - EmitMaskOperand(operand.type, word); - break; - default: - assert(false && "unhandled or invalid case"); - } - ResetColor(); -} - -void Disassembler::EmitMaskOperand(const spv_operand_type_t type, - const uint32_t word) { - // Scan the mask from least significant bit to most significant bit. For each - // set bit, emit the name of that bit. Separate multiple names with '|'. - uint32_t remaining_word = word; - uint32_t mask; - int num_emitted = 0; - for (mask = 1; remaining_word; mask <<= 1) { - if (remaining_word & mask) { - remaining_word ^= mask; - spv_operand_desc entry; - if (grammar_.lookupOperand(type, mask, &entry)) - assert(false && "should have caught this earlier"); - if (num_emitted) stream_ << "|"; - stream_ << entry->name; - num_emitted++; - } - } - if (!num_emitted) { - // An operand value of 0 was provided, so represent it by the name - // of the 0 value. In many cases, that's "None". - spv_operand_desc entry; - if (SPV_SUCCESS == grammar_.lookupOperand(type, 0, &entry)) - stream_ << entry->name; - } -} - spv_result_t Disassembler::SaveTextResult(spv_text* text_result) const { if (!print_) { size_t length = text_.str().size(); @@ -458,8 +194,342 @@ spv_result_t DisassembleTargetInstruction( return SPV_SUCCESS; } +constexpr int kStandardIndent = 15; } // namespace +namespace disassemble { +InstructionDisassembler::InstructionDisassembler(const AssemblyGrammar& grammar, + std::ostream& stream, + uint32_t options, + NameMapper name_mapper) + : grammar_(grammar), + stream_(stream), + print_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options)), + color_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COLOR, options)), + indent_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_INDENT, options) + ? kStandardIndent + : 0), + comment_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COMMENT, options)), + show_byte_offset_( + spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET, options)), + name_mapper_(std::move(name_mapper)) {} + +void InstructionDisassembler::EmitHeaderSpirv() { stream_ << "; SPIR-V\n"; } + +void InstructionDisassembler::EmitHeaderVersion(uint32_t version) { + stream_ << "; Version: " << SPV_SPIRV_VERSION_MAJOR_PART(version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(version) << "\n"; +} + +void InstructionDisassembler::EmitHeaderGenerator(uint32_t generator) { + const char* generator_tool = + spvGeneratorStr(SPV_GENERATOR_TOOL_PART(generator)); + stream_ << "; Generator: " << generator_tool; + // For unknown tools, print the numeric tool value. + if (0 == strcmp("Unknown", generator_tool)) { + stream_ << "(" << SPV_GENERATOR_TOOL_PART(generator) << ")"; + } + // Print the miscellaneous part of the generator word on the same + // line as the tool name. + stream_ << "; " << SPV_GENERATOR_MISC_PART(generator) << "\n"; +} + +void InstructionDisassembler::EmitHeaderIdBound(uint32_t id_bound) { + stream_ << "; Bound: " << id_bound << "\n"; +} + +void InstructionDisassembler::EmitHeaderSchema(uint32_t schema) { + stream_ << "; Schema: " << schema << "\n"; +} + +void InstructionDisassembler::EmitInstruction( + const spv_parsed_instruction_t& inst, size_t inst_byte_offset) { + auto opcode = static_cast(inst.opcode); + + if (inst.result_id) { + SetBlue(); + const std::string id_name = name_mapper_(inst.result_id); + if (indent_) + stream_ << std::setw(std::max(0, indent_ - 3 - int(id_name.size()))); + stream_ << "%" << id_name; + ResetColor(); + stream_ << " = "; + } else { + stream_ << std::string(indent_, ' '); + } + + stream_ << "Op" << spvOpcodeString(opcode); + + for (uint16_t i = 0; i < inst.num_operands; i++) { + const spv_operand_type_t type = inst.operands[i].type; + assert(type != SPV_OPERAND_TYPE_NONE); + if (type == SPV_OPERAND_TYPE_RESULT_ID) continue; + stream_ << " "; + EmitOperand(inst, i); + } + + if (comment_ && opcode == SpvOpName) { + const spv_parsed_operand_t& operand = inst.operands[0]; + const uint32_t word = inst.words[operand.offset]; + stream_ << " ; id %" << word; + } + + if (show_byte_offset_) { + SetGrey(); + auto saved_flags = stream_.flags(); + auto saved_fill = stream_.fill(); + stream_ << " ; 0x" << std::setw(8) << std::hex << std::setfill('0') + << inst_byte_offset; + stream_.flags(saved_flags); + stream_.fill(saved_fill); + ResetColor(); + } + stream_ << "\n"; +} + +void InstructionDisassembler::EmitSectionComment( + const spv_parsed_instruction_t& inst, bool& inserted_decoration_space, + bool& inserted_debug_space, bool& inserted_type_space) { + auto opcode = static_cast(inst.opcode); + if (comment_ && opcode == SpvOpFunction) { + stream_ << std::endl; + stream_ << std::string(indent_, ' '); + stream_ << "; Function " << name_mapper_(inst.result_id) << std::endl; + } + if (comment_ && !inserted_decoration_space && spvOpcodeIsDecoration(opcode)) { + inserted_decoration_space = true; + stream_ << std::endl; + stream_ << std::string(indent_, ' '); + stream_ << "; Annotations" << std::endl; + } + if (comment_ && !inserted_debug_space && spvOpcodeIsDebug(opcode)) { + inserted_debug_space = true; + stream_ << std::endl; + stream_ << std::string(indent_, ' '); + stream_ << "; Debug Information" << std::endl; + } + if (comment_ && !inserted_type_space && spvOpcodeGeneratesType(opcode)) { + inserted_type_space = true; + stream_ << std::endl; + stream_ << std::string(indent_, ' '); + stream_ << "; Types, variables and constants" << std::endl; + } +} + +void InstructionDisassembler::EmitOperand(const spv_parsed_instruction_t& inst, + const uint16_t operand_index) { + assert(operand_index < inst.num_operands); + const spv_parsed_operand_t& operand = inst.operands[operand_index]; + const uint32_t word = inst.words[operand.offset]; + switch (operand.type) { + case SPV_OPERAND_TYPE_RESULT_ID: + assert(false && " is not supposed to be handled here"); + SetBlue(); + stream_ << "%" << name_mapper_(word); + break; + case SPV_OPERAND_TYPE_ID: + case SPV_OPERAND_TYPE_TYPE_ID: + case SPV_OPERAND_TYPE_SCOPE_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + SetYellow(); + stream_ << "%" << name_mapper_(word); + break; + case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: { + spv_ext_inst_desc ext_inst; + SetRed(); + if (grammar_.lookupExtInst(inst.ext_inst_type, word, &ext_inst) == + SPV_SUCCESS) { + stream_ << ext_inst->name; + } else { + if (!spvExtInstIsNonSemantic(inst.ext_inst_type)) { + assert(false && "should have caught this earlier"); + } else { + // for non-semantic instruction sets we can just print the number + stream_ << word; + } + } + } break; + case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: { + spv_opcode_desc opcode_desc; + if (grammar_.lookupOpcode(SpvOp(word), &opcode_desc)) + assert(false && "should have caught this earlier"); + SetRed(); + stream_ << opcode_desc->name; + } break; + case SPV_OPERAND_TYPE_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: { + SetRed(); + EmitNumericLiteral(&stream_, inst, operand); + ResetColor(); + } break; + case SPV_OPERAND_TYPE_LITERAL_STRING: { + stream_ << "\""; + SetGreen(); + + std::string str = spvDecodeLiteralStringOperand(inst, operand_index); + for (char const& c : str) { + if (c == '"' || c == '\\') stream_ << '\\'; + stream_ << c; + } + ResetColor(); + stream_ << '"'; + } break; + case SPV_OPERAND_TYPE_CAPABILITY: + case SPV_OPERAND_TYPE_SOURCE_LANGUAGE: + case SPV_OPERAND_TYPE_EXECUTION_MODEL: + case SPV_OPERAND_TYPE_ADDRESSING_MODEL: + case SPV_OPERAND_TYPE_MEMORY_MODEL: + case SPV_OPERAND_TYPE_EXECUTION_MODE: + case SPV_OPERAND_TYPE_STORAGE_CLASS: + case SPV_OPERAND_TYPE_DIMENSIONALITY: + case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE: + case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE: + case SPV_OPERAND_TYPE_SAMPLER_IMAGE_FORMAT: + case SPV_OPERAND_TYPE_FP_ROUNDING_MODE: + case SPV_OPERAND_TYPE_LINKAGE_TYPE: + case SPV_OPERAND_TYPE_ACCESS_QUALIFIER: + case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE: + case SPV_OPERAND_TYPE_DECORATION: + case SPV_OPERAND_TYPE_BUILT_IN: + case SPV_OPERAND_TYPE_GROUP_OPERATION: + case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS: + case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: + case SPV_OPERAND_TYPE_RAY_FLAGS: + case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION: + case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE: + case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE: + case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE: + case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER: + case SPV_OPERAND_TYPE_DEBUG_OPERATION: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: + case SPV_OPERAND_TYPE_FPDENORM_MODE: + case SPV_OPERAND_TYPE_FPOPERATION_MODE: + case SPV_OPERAND_TYPE_QUANTIZATION_MODES: + case SPV_OPERAND_TYPE_OVERFLOW_MODES: { + spv_operand_desc entry; + if (grammar_.lookupOperand(operand.type, word, &entry)) + assert(false && "should have caught this earlier"); + stream_ << entry->name; + } break; + case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE: + case SPV_OPERAND_TYPE_FUNCTION_CONTROL: + case SPV_OPERAND_TYPE_LOOP_CONTROL: + case SPV_OPERAND_TYPE_IMAGE: + case SPV_OPERAND_TYPE_MEMORY_ACCESS: + case SPV_OPERAND_TYPE_SELECTION_CONTROL: + case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: + EmitMaskOperand(operand.type, word); + break; + default: + if (spvOperandIsConcreteMask(operand.type)) { + EmitMaskOperand(operand.type, word); + } else if (spvOperandIsConcrete(operand.type)) { + spv_operand_desc entry; + if (grammar_.lookupOperand(operand.type, word, &entry)) + assert(false && "should have caught this earlier"); + stream_ << entry->name; + } else { + assert(false && "unhandled or invalid case"); + } + break; + } + ResetColor(); +} + +void InstructionDisassembler::EmitMaskOperand(const spv_operand_type_t type, + const uint32_t word) { + // Scan the mask from least significant bit to most significant bit. For each + // set bit, emit the name of that bit. Separate multiple names with '|'. + uint32_t remaining_word = word; + uint32_t mask; + int num_emitted = 0; + for (mask = 1; remaining_word; mask <<= 1) { + if (remaining_word & mask) { + remaining_word ^= mask; + spv_operand_desc entry; + if (grammar_.lookupOperand(type, mask, &entry)) + assert(false && "should have caught this earlier"); + if (num_emitted) stream_ << "|"; + stream_ << entry->name; + num_emitted++; + } + } + if (!num_emitted) { + // An operand value of 0 was provided, so represent it by the name + // of the 0 value. In many cases, that's "None". + spv_operand_desc entry; + if (SPV_SUCCESS == grammar_.lookupOperand(type, 0, &entry)) + stream_ << entry->name; + } +} + +void InstructionDisassembler::ResetColor() { + if (color_) stream_ << spvtools::clr::reset{print_}; +} +void InstructionDisassembler::SetGrey() { + if (color_) stream_ << spvtools::clr::grey{print_}; +} +void InstructionDisassembler::SetBlue() { + if (color_) stream_ << spvtools::clr::blue{print_}; +} +void InstructionDisassembler::SetYellow() { + if (color_) stream_ << spvtools::clr::yellow{print_}; +} +void InstructionDisassembler::SetRed() { + if (color_) stream_ << spvtools::clr::red{print_}; +} +void InstructionDisassembler::SetGreen() { + if (color_) stream_ << spvtools::clr::green{print_}; +} +} // namespace disassemble + +std::string spvInstructionBinaryToText(const spv_target_env env, + const uint32_t* instCode, + const size_t instWordCount, + const uint32_t* code, + const size_t wordCount, + const uint32_t options) { + spv_context context = spvContextCreate(env); + const AssemblyGrammar grammar(context); + if (!grammar.isValid()) { + spvContextDestroy(context); + return ""; + } + + // Generate friendly names for Ids if requested. + std::unique_ptr friendly_mapper; + NameMapper name_mapper = GetTrivialNameMapper(); + if (options & SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) { + friendly_mapper = MakeUnique(context, code, wordCount); + name_mapper = friendly_mapper->GetNameMapper(); + } + + // Now disassemble! + Disassembler disassembler(grammar, options, name_mapper); + WrappedDisassembler wrapped(&disassembler, instCode, instWordCount); + spvBinaryParse(context, &wrapped, code, wordCount, DisassembleTargetHeader, + DisassembleTargetInstruction, nullptr); + + spv_text text = nullptr; + std::string output; + if (disassembler.SaveTextResult(&text) == SPV_SUCCESS) { + output.assign(text->str, text->str + text->length); + // Drop trailing newline characters. + while (!output.empty() && output.back() == '\n') output.pop_back(); + } + spvTextDestroy(text); + spvContextDestroy(context); + + return output; +} +} // namespace spvtools + spv_result_t spvBinaryToText(const spv_const_context context, const uint32_t* code, const size_t wordCount, const uint32_t options, spv_text* pText, @@ -483,53 +553,13 @@ spv_result_t spvBinaryToText(const spv_const_context context, } // Now disassemble! - Disassembler disassembler(grammar, options, name_mapper); - if (auto error = spvBinaryParse(&hijack_context, &disassembler, code, - wordCount, DisassembleHeader, - DisassembleInstruction, pDiagnostic)) { + spvtools::Disassembler disassembler(grammar, options, name_mapper); + if (auto error = + spvBinaryParse(&hijack_context, &disassembler, code, wordCount, + spvtools::DisassembleHeader, + spvtools::DisassembleInstruction, pDiagnostic)) { return error; } return disassembler.SaveTextResult(pText); } - -std::string spvtools::spvInstructionBinaryToText(const spv_target_env env, - const uint32_t* instCode, - const size_t instWordCount, - const uint32_t* code, - const size_t wordCount, - const uint32_t options) { - spv_context context = spvContextCreate(env); - const spvtools::AssemblyGrammar grammar(context); - if (!grammar.isValid()) { - spvContextDestroy(context); - return ""; - } - - // Generate friendly names for Ids if requested. - std::unique_ptr friendly_mapper; - spvtools::NameMapper name_mapper = spvtools::GetTrivialNameMapper(); - if (options & SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) { - friendly_mapper = spvtools::MakeUnique( - context, code, wordCount); - name_mapper = friendly_mapper->GetNameMapper(); - } - - // Now disassemble! - Disassembler disassembler(grammar, options, name_mapper); - WrappedDisassembler wrapped(&disassembler, instCode, instWordCount); - spvBinaryParse(context, &wrapped, code, wordCount, DisassembleTargetHeader, - DisassembleTargetInstruction, nullptr); - - spv_text text = nullptr; - std::string output; - if (disassembler.SaveTextResult(&text) == SPV_SUCCESS) { - output.assign(text->str, text->str + text->length); - // Drop trailing newline characters. - while (!output.empty() && output.back() == '\n') output.pop_back(); - } - spvTextDestroy(text); - spvContextDestroy(context); - - return output; -} diff --git a/source/disassemble.h b/source/disassemble.h index ac357427..b520a1ea 100644 --- a/source/disassemble.h +++ b/source/disassemble.h @@ -15,17 +15,20 @@ #ifndef SOURCE_DISASSEMBLE_H_ #define SOURCE_DISASSEMBLE_H_ +#include #include +#include "source/name_mapper.h" #include "spirv-tools/libspirv.h" namespace spvtools { // Decodes the given SPIR-V instruction binary representation to its assembly // text. The context is inferred from the provided module binary. The options -// parameter is a bit field of spv_binary_to_text_options_t. Decoded text will -// be stored into *text. Any error will be written into *diagnostic if -// diagnostic is non-null. +// parameter is a bit field of spv_binary_to_text_options_t (note: the option +// SPV_BINARY_TO_TEXT_OPTION_PRINT will be ignored). Decoded text will be +// stored into *text. Any error will be written into *diagnostic if diagnostic +// is non-null. std::string spvInstructionBinaryToText(const spv_target_env env, const uint32_t* inst_binary, const size_t inst_word_count, @@ -33,6 +36,63 @@ std::string spvInstructionBinaryToText(const spv_target_env env, const size_t word_count, const uint32_t options); +class AssemblyGrammar; +namespace disassemble { + +// Shared code with other tools (than the disassembler) that might need to +// output disassembly. An InstructionDisassembler instance converts SPIR-V +// binary for an instruction to its assembly representation. +class InstructionDisassembler { + public: + InstructionDisassembler(const AssemblyGrammar& grammar, std::ostream& stream, + uint32_t options, NameMapper name_mapper); + + // Emits the assembly header for the module. + void EmitHeaderSpirv(); + void EmitHeaderVersion(uint32_t version); + void EmitHeaderGenerator(uint32_t generator); + void EmitHeaderIdBound(uint32_t id_bound); + void EmitHeaderSchema(uint32_t schema); + + // Emits the assembly text for the given instruction. + void EmitInstruction(const spv_parsed_instruction_t& inst, + size_t inst_byte_offset); + + // Emits a comment between different sections of the module. + void EmitSectionComment(const spv_parsed_instruction_t& inst, + bool& inserted_decoration_space, + bool& inserted_debug_space, + bool& inserted_type_space); + + // Resets the output color, if color is turned on. + void ResetColor(); + // Set the output color, if color is turned on. + void SetGrey(); + void SetBlue(); + void SetYellow(); + void SetRed(); + void SetGreen(); + + private: + // Emits an operand for the given instruction, where the instruction + // is at offset words from the start of the binary. + void EmitOperand(const spv_parsed_instruction_t& inst, + const uint16_t operand_index); + + // Emits a mask expression for the given mask word of the specified type. + void EmitMaskOperand(const spv_operand_type_t type, const uint32_t word); + + const spvtools::AssemblyGrammar& grammar_; + std::ostream& stream_; + const bool print_; // Should we also print to the standard output stream? + const bool color_; // Should we print in colour? + const int indent_; // How much to indent. 0 means don't indent + const int comment_; // Should we comment the source + const bool show_byte_offset_; // Should we print byte offset, in hex? + spvtools::NameMapper name_mapper_; +}; + +} // namespace disassemble } // namespace spvtools #endif // SOURCE_DISASSEMBLE_H_ diff --git a/source/ext_inst.cpp b/source/ext_inst.cpp index 795cb0f3..4e279545 100644 --- a/source/ext_inst.cpp +++ b/source/ext_inst.cpp @@ -29,6 +29,7 @@ #include "debuginfo.insts.inc" #include "glsl.std.450.insts.inc" #include "nonsemantic.clspvreflection.insts.inc" +#include "nonsemantic.shader.debuginfo.100.insts.inc" #include "opencl.debuginfo.100.insts.inc" #include "opencl.std.insts.inc" @@ -55,6 +56,9 @@ static const spv_ext_inst_group_t kGroups_1_0[] = { debuginfo_entries}, {SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, ARRAY_SIZE(opencl_debuginfo_100_entries), opencl_debuginfo_100_entries}, + {SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100, + ARRAY_SIZE(nonsemantic_shader_debuginfo_100_entries), + nonsemantic_shader_debuginfo_100_entries}, {SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION, ARRAY_SIZE(nonsemantic_clspvreflection_entries), nonsemantic_clspvreflection_entries}, @@ -92,6 +96,8 @@ spv_result_t spvExtInstTableGet(spv_ext_inst_table* pExtInstTable, case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: + case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: *pExtInstTable = &kTable_1_0; return SPV_SUCCESS; default: @@ -126,6 +132,9 @@ spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) { if (!strcmp("OpenCL.DebugInfo.100", name)) { return SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100; } + if (!strcmp("NonSemantic.Shader.DebugInfo.100", name)) { + return SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100; + } if (!strncmp("NonSemantic.ClspvReflection.", name, 28)) { return SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION; } @@ -139,6 +148,7 @@ spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) { bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type) { if (type == SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN || + type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100 || type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) { return true; } @@ -147,6 +157,7 @@ bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type) { bool spvExtInstIsDebugInfo(const spv_ext_inst_type_t type) { if (type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || + type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100 || type == SPV_EXT_INST_TYPE_DEBUGINFO) { return true; } diff --git a/source/ext_inst.h b/source/ext_inst.h index aff6e308..4027f4c3 100644 --- a/source/ext_inst.h +++ b/source/ext_inst.h @@ -27,7 +27,7 @@ bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type); // Returns true if the extended instruction set is debug info bool spvExtInstIsDebugInfo(const spv_ext_inst_type_t type); -// Finds the named extented instruction of the given type in the given extended +// Finds the named extended instruction of the given type in the given extended // instruction table. On success, returns SPV_SUCCESS and writes a handle of // the instruction entry into *entry. spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table, @@ -35,7 +35,7 @@ spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table, const char* name, spv_ext_inst_desc* entry); -// Finds the extented instruction of the given type in the given extended +// Finds the extended instruction of the given type in the given extended // instruction table by value. On success, returns SPV_SUCCESS and writes a // handle of the instruction entry into *entry. spv_result_t spvExtInstTableValueLookup(const spv_ext_inst_table table, diff --git a/source/extensions.cpp b/source/extensions.cpp index a94db273..049a3ad1 100644 --- a/source/extensions.cpp +++ b/source/extensions.cpp @@ -18,6 +18,7 @@ #include #include +#include "source/binary.h" #include "source/enum_string_mapping.h" namespace spvtools { @@ -30,8 +31,9 @@ std::string GetExtensionString(const spv_parsed_instruction_t* inst) { const auto& operand = inst->operands[0]; assert(operand.type == SPV_OPERAND_TYPE_LITERAL_STRING); assert(inst->num_words > operand.offset); + (void)operand; /* No unused variables in release builds. */ - return reinterpret_cast(inst->words + operand.offset); + return spvDecodeLiteralStringOperand(*inst, 0); } std::string ExtensionSetToString(const ExtensionSet& extensions) { diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt index 804fcf07..dd674dd0 100644 --- a/source/fuzz/CMakeLists.txt +++ b/source/fuzz/CMakeLists.txt @@ -101,6 +101,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_outline_functions.h fuzzer_pass_permute_blocks.h fuzzer_pass_permute_function_parameters.h + fuzzer_pass_permute_function_variables.h fuzzer_pass_permute_instructions.h fuzzer_pass_permute_phi_operands.h fuzzer_pass_propagate_instructions_down.h @@ -120,8 +121,10 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_split_blocks.h fuzzer_pass_swap_commutable_operands.h fuzzer_pass_swap_conditional_branch_operands.h + fuzzer_pass_swap_functions.h fuzzer_pass_toggle_access_chain_instruction.h fuzzer_pass_wrap_regions_in_selections.h + fuzzer_pass_wrap_vector_synonym.h fuzzer_util.h id_use_descriptor.h instruction_descriptor.h @@ -222,10 +225,13 @@ if(SPIRV_BUILD_FUZZER) transformation_store.h transformation_swap_commutable_operands.h transformation_swap_conditional_branch_operands.h + transformation_swap_function_variables.h + transformation_swap_two_functions.h transformation_toggle_access_chain_instruction.h transformation_vector_shuffle.h transformation_wrap_early_terminator_in_function.h transformation_wrap_region_in_selection.h + transformation_wrap_vector_synonym.h uniform_buffer_element_descriptor.h ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h @@ -292,6 +298,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_outline_functions.cpp fuzzer_pass_permute_blocks.cpp fuzzer_pass_permute_function_parameters.cpp + fuzzer_pass_permute_function_variables.cpp fuzzer_pass_permute_instructions.cpp fuzzer_pass_permute_phi_operands.cpp fuzzer_pass_propagate_instructions_down.cpp @@ -311,8 +318,10 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_split_blocks.cpp fuzzer_pass_swap_commutable_operands.cpp fuzzer_pass_swap_conditional_branch_operands.cpp + fuzzer_pass_swap_functions.cpp fuzzer_pass_toggle_access_chain_instruction.cpp fuzzer_pass_wrap_regions_in_selections.cpp + fuzzer_pass_wrap_vector_synonym.cpp fuzzer_util.cpp id_use_descriptor.cpp instruction_descriptor.cpp @@ -411,10 +420,13 @@ if(SPIRV_BUILD_FUZZER) transformation_store.cpp transformation_swap_commutable_operands.cpp transformation_swap_conditional_branch_operands.cpp + transformation_swap_function_variables.cpp + transformation_swap_two_functions.cpp transformation_toggle_access_chain_instruction.cpp transformation_vector_shuffle.cpp transformation_wrap_early_terminator_in_function.cpp transformation_wrap_region_in_selection.cpp + transformation_wrap_vector_synonym.cpp uniform_buffer_element_descriptor.cpp ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc ) diff --git a/source/fuzz/available_instructions.cpp b/source/fuzz/available_instructions.cpp index e25ed900..0db8b207 100644 --- a/source/fuzz/available_instructions.cpp +++ b/source/fuzz/available_instructions.cpp @@ -44,7 +44,7 @@ AvailableInstructions::AvailableInstructions( // Consider every reachable block in the function. auto dominator_analysis = ir_context->GetDominatorAnalysis(&function); for (auto& block : function) { - if (!fuzzerutil::BlockIsReachableInItsFunction(ir_context, &block)) { + if (!ir_context->IsReachable(block)) { // The block is not reachable. continue; } diff --git a/source/fuzz/fact_manager/fact_manager.h b/source/fuzz/fact_manager/fact_manager.h index 5cf5b18b..ce28ae4c 100644 --- a/source/fuzz/fact_manager/fact_manager.h +++ b/source/fuzz/fact_manager/fact_manager.h @@ -163,7 +163,7 @@ class FactManager { std::vector GetSynonymsForDataDescriptor( const protobufs::DataDescriptor& data_descriptor) const; - // Returns true if and ony if |data_descriptor1| and |data_descriptor2| are + // Returns true if and only if |data_descriptor1| and |data_descriptor2| are // known to be synonymous. bool IsSynonymous(const protobufs::DataDescriptor& data_descriptor1, const protobufs::DataDescriptor& data_descriptor2) const; @@ -174,7 +174,7 @@ class FactManager { //============================== // Querying facts about dead blocks - // Returns true if and ony if |block_id| is the id of a block known to be + // Returns true if and only if |block_id| is the id of a block known to be // dynamically unreachable. bool BlockIsDead(uint32_t block_id) const; @@ -184,7 +184,7 @@ class FactManager { //============================== // Querying facts about livesafe function - // Returns true if and ony if |function_id| is the id of a function known + // Returns true if and only if |function_id| is the id of a function known // to be livesafe. bool FunctionIsLivesafe(uint32_t function_id) const; @@ -194,7 +194,7 @@ class FactManager { //============================== // Querying facts about irrelevant values - // Returns true if and ony if the value of the pointee associated with + // Returns true if and only if the value of the pointee associated with // |pointer_id| is irrelevant. bool PointeeValueIsIrrelevant(uint32_t pointer_id) const; diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp index 9d1af752..9838e64f 100644 --- a/source/fuzz/fuzzer.cpp +++ b/source/fuzz/fuzzer.cpp @@ -67,6 +67,7 @@ #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_function_variables.h" #include "source/fuzz/fuzzer_pass_permute_instructions.h" #include "source/fuzz/fuzzer_pass_permute_phi_operands.h" #include "source/fuzz/fuzzer_pass_propagate_instructions_down.h" @@ -86,8 +87,10 @@ #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_swap_functions.h" #include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h" #include "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h" +#include "source/fuzz/fuzzer_pass_wrap_vector_synonym.h" #include "source/fuzz/pass_management/repeated_pass_manager.h" #include "source/fuzz/pass_management/repeated_pass_recommender_standard.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" @@ -107,7 +110,8 @@ Fuzzer::Fuzzer(std::unique_ptr ir_context, bool enable_all_passes, RepeatedPassStrategy repeated_pass_strategy, bool validate_after_each_fuzzer_pass, - spv_validator_options validator_options) + spv_validator_options validator_options, + bool ignore_inapplicable_transformations /* = true */) : consumer_(std::move(consumer)), enable_all_passes_(enable_all_passes), validate_after_each_fuzzer_pass_(validate_after_each_fuzzer_pass), @@ -121,7 +125,9 @@ Fuzzer::Fuzzer(std::unique_ptr ir_context, pass_instances_(), repeated_pass_recommender_(nullptr), repeated_pass_manager_(nullptr), - final_passes_() { + final_passes_(), + ignore_inapplicable_transformations_( + ignore_inapplicable_transformations) { assert(ir_context_ && "IRContext is not initialized"); assert(fuzzer_context_ && "FuzzerContext is not initialized"); assert(transformation_context_ && "TransformationContext is not initialized"); @@ -212,6 +218,7 @@ Fuzzer::Fuzzer(std::unique_ptr ir_context, MaybeAddRepeatedPass( &pass_instances_); MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); // There is a theoretical possibility that no pass instances were created // until now; loop again if so. } while (pass_instances_.GetPasses().empty()); @@ -236,8 +243,10 @@ Fuzzer::Fuzzer(std::unique_ptr ir_context, &final_passes_); } MaybeAddFinalPass(&final_passes_); + MaybeAddFinalPass(&final_passes_); MaybeAddFinalPass(&final_passes_); MaybeAddFinalPass(&final_passes_); + MaybeAddFinalPass(&final_passes_); MaybeAddFinalPass(&final_passes_); } @@ -251,7 +260,8 @@ void Fuzzer::MaybeAddRepeatedPass(uint32_t percentage_chance_of_adding_pass, fuzzer_context_->ChoosePercentage(percentage_chance_of_adding_pass)) { pass_instances->SetPass(MakeUnique( ir_context_.get(), transformation_context_.get(), fuzzer_context_.get(), - &transformation_sequence_out_, std::forward(extra_args)...)); + &transformation_sequence_out_, ignore_inapplicable_transformations_, + std::forward(extra_args)...)); } } @@ -261,7 +271,8 @@ void Fuzzer::MaybeAddFinalPass(std::vector>* passes, if (enable_all_passes_ || fuzzer_context_->ChooseEven()) { passes->push_back(MakeUnique( ir_context_.get(), transformation_context_.get(), fuzzer_context_.get(), - &transformation_sequence_out_, std::forward(extra_args)...)); + &transformation_sequence_out_, ignore_inapplicable_transformations_, + std::forward(extra_args)...)); } } diff --git a/source/fuzz/fuzzer.h b/source/fuzz/fuzzer.h index 1e7d6860..4c38977f 100644 --- a/source/fuzz/fuzzer.h +++ b/source/fuzz/fuzzer.h @@ -62,7 +62,8 @@ class Fuzzer { const std::vector& donor_suppliers, bool enable_all_passes, RepeatedPassStrategy repeated_pass_strategy, bool validate_after_each_fuzzer_pass, - spv_validator_options validator_options); + spv_validator_options validator_options, + bool ignore_inapplicable_transformations = true); // Disables copy/move constructor/assignment operations. Fuzzer(const Fuzzer&) = delete; @@ -187,6 +188,12 @@ class Fuzzer { // Some passes that it does not make sense to apply repeatedly, as they do not // unlock other passes. std::vector> final_passes_; + + // When set, this flag causes inapplicable transformations that should be + // applicable by construction to be ignored. This is useful when the fuzzer + // is being deployed at scale to test a SPIR-V processing tool, and where it + // is desirable to ignore bugs in the fuzzer itself. + const bool ignore_inapplicable_transformations_; }; } // namespace fuzz diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp index ef93e719..7e34cc33 100644 --- a/source/fuzz/fuzzer_context.cpp +++ b/source/fuzz/fuzzer_context.cpp @@ -21,7 +21,7 @@ namespace fuzz { namespace { -// An offset between the the module's id bound and the minimum fresh id. +// An offset between the module's id bound and the minimum fresh id. // // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541): consider // the case where the maximum id bound is reached. @@ -42,6 +42,8 @@ const std::pair kChanceOfAddingAnotherPassToPassLoop = {50, const std::pair kChanceOfAddingAnotherStructField = {20, 90}; const std::pair kChanceOfAddingArrayOrStructType = {20, 90}; +const std::pair KChanceOfAddingAtomicLoad = {30, 90}; +const std::pair KChanceOfAddingAtomicStore = {20, 90}; const std::pair kChanceOfAddingBitInstructionSynonym = {5, 20}; const std::pair @@ -120,6 +122,8 @@ const std::pair kChanceOfMovingBlockDown = {20, 50}; const std::pair kChanceOfMutatingPointer = {20, 90}; const std::pair kChanceOfObfuscatingConstant = {10, 90}; const std::pair kChanceOfOutliningFunction = {10, 90}; +const std::pair kChanceOfPermutingFunctionVariables = {30, + 90}; const std::pair kChanceOfPermutingInstructions = {20, 70}; const std::pair kChanceOfPermutingParameters = {30, 90}; const std::pair kChanceOfPermutingPhiOperands = {30, 90}; @@ -151,12 +155,16 @@ const std::pair kChanceOfReplacingParametersWithGlobals = { const std::pair kChanceOfReplacingParametersWithStruct = { 20, 40}; const std::pair kChanceOfSplittingBlock = {40, 95}; +const std::pair + kChanceOfSwappingAnotherPairOfFunctionVariables = {30, 90}; const std::pair kChanceOfSwappingConditionalBranchOperands = {10, 70}; +const std::pair kChanceOfSwappingFunctions = {10, 90}; const std::pair kChanceOfTogglingAccessChainInstruction = { 20, 90}; const std::pair kChanceOfWrappingRegionInSelection = {70, 90}; +const std::pair kChanceOfWrappingVectorSynonym = {10, 90}; // Default limits for various quantities that are chosen during fuzzing. // Keep them in alphabetical order. @@ -210,6 +218,10 @@ FuzzerContext::FuzzerContext(std::unique_ptr random_generator, ChooseBetweenMinAndMax(kChanceOfAddingAnotherStructField); chance_of_adding_array_or_struct_type_ = ChooseBetweenMinAndMax(kChanceOfAddingArrayOrStructType); + chance_of_adding_atomic_load_ = + ChooseBetweenMinAndMax(KChanceOfAddingAtomicLoad); + chance_of_adding_atomic_store_ = + ChooseBetweenMinAndMax(KChanceOfAddingAtomicStore); chance_of_adding_bit_instruction_synonym_ = ChooseBetweenMinAndMax(kChanceOfAddingBitInstructionSynonym); chance_of_adding_both_branches_when_replacing_opselect_ = @@ -315,6 +327,8 @@ FuzzerContext::FuzzerContext(std::unique_ptr random_generator, ChooseBetweenMinAndMax(kChanceOfObfuscatingConstant); chance_of_outlining_function_ = ChooseBetweenMinAndMax(kChanceOfOutliningFunction); + chance_of_permuting_function_variables_ = + ChooseBetweenMinAndMax(kChanceOfPermutingFunctionVariables); chance_of_permuting_instructions_ = ChooseBetweenMinAndMax(kChanceOfPermutingInstructions); chance_of_permuting_parameters_ = @@ -352,12 +366,18 @@ FuzzerContext::FuzzerContext(std::unique_ptr random_generator, chance_of_replacing_parameters_with_struct_ = ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithStruct); chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock); + chance_of_swapping_another_pair_of_function_variables_ = + ChooseBetweenMinAndMax(kChanceOfSwappingAnotherPairOfFunctionVariables); chance_of_swapping_conditional_branch_operands_ = ChooseBetweenMinAndMax(kChanceOfSwappingConditionalBranchOperands); + chance_of_swapping_functions_ = + ChooseBetweenMinAndMax(kChanceOfSwappingFunctions); chance_of_toggling_access_chain_instruction_ = ChooseBetweenMinAndMax(kChanceOfTogglingAccessChainInstruction); chance_of_wrapping_region_in_selection_ = ChooseBetweenMinAndMax(kChanceOfWrappingRegionInSelection); + chance_of_wrapping_vector_synonym_ = + ChooseBetweenMinAndMax(kChanceOfWrappingVectorSynonym); } FuzzerContext::~FuzzerContext() = default; diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h index 40fd8783..77a5d400 100644 --- a/source/fuzz/fuzzer_context.h +++ b/source/fuzz/fuzzer_context.h @@ -142,6 +142,12 @@ class FuzzerContext { uint32_t GetChanceOfAddingArrayOrStructType() const { return chance_of_adding_array_or_struct_type_; } + uint32_t GetChanceOfAddingAtomicLoad() const { + return chance_of_adding_atomic_load_; + } + uint32_t GetChanceOfAddingAtomicStore() const { + return chance_of_adding_atomic_store_; + } uint32_t GetChanceOfAddingBitInstructionSynonym() const { return chance_of_adding_bit_instruction_synonym_; } @@ -303,6 +309,9 @@ class FuzzerContext { uint32_t GetChanceOfOutliningFunction() const { return chance_of_outlining_function_; } + uint32_t GetChanceOfPermutingFunctionVariables() const { + return chance_of_permuting_function_variables_; + } uint32_t GetChanceOfPermutingInstructions() const { return chance_of_permuting_instructions_; } @@ -360,9 +369,17 @@ class FuzzerContext { uint32_t GetChanceOfSplittingBlock() const { return chance_of_splitting_block_; } + uint32_t GetChanceOfSwappingAnotherPairOfFunctionVariables() const { + return chance_of_swapping_another_pair_of_function_variables_; + } uint32_t GetChanceOfSwappingConditionalBranchOperands() const { return chance_of_swapping_conditional_branch_operands_; } + + uint32_t GetChanceOfSwappingFunctions() const { + return chance_of_swapping_functions_; + } + uint32_t GetChanceOfTogglingAccessChainInstruction() const { return chance_of_toggling_access_chain_instruction_; } @@ -370,6 +387,10 @@ class FuzzerContext { return chance_of_wrapping_region_in_selection_; } + uint32_t GetChanceOfWrappingVectorSynonym() const { + return chance_of_wrapping_vector_synonym_; + } + // Other functions to control transformations. Keep them in alphabetical // order. uint32_t GetMaximumEquivalenceClassSizeForDataSynonymFactClosure() const { @@ -414,6 +435,9 @@ class FuzzerContext { uint32_t GetRandomIndexForCompositeInsert(uint32_t number_of_components) { return random_generator_->RandomUint32(number_of_components); } + uint32_t GetRandomIndexForWrappingVector(uint32_t vector_width) { + return random_generator_->RandomUint32(vector_width); + } int64_t GetRandomValueForStepConstantInLoop() { return random_generator_->RandomUint64(UINT64_MAX); } @@ -451,6 +475,9 @@ class FuzzerContext { // Ensure that the number of unused components is non-zero. return random_generator_->RandomUint32(max_unused_component_count) + 1; } + uint32_t GetWidthOfWrappingVector() { + return 2 + random_generator_->RandomUint32(3); + } bool GoDeeperInConstantObfuscation(uint32_t depth) { return go_deeper_in_constant_obfuscation_(depth, random_generator_.get()); } @@ -471,6 +498,8 @@ class FuzzerContext { uint32_t chance_of_adding_another_pass_to_pass_loop_; uint32_t chance_of_adding_another_struct_field_; uint32_t chance_of_adding_array_or_struct_type_; + uint32_t chance_of_adding_atomic_load_; + uint32_t chance_of_adding_atomic_store_; uint32_t chance_of_adding_bit_instruction_synonym_; uint32_t chance_of_adding_both_branches_when_replacing_opselect_; uint32_t chance_of_adding_composite_extract_; @@ -526,6 +555,7 @@ class FuzzerContext { uint32_t chance_of_mutating_pointer_; uint32_t chance_of_obfuscating_constant_; uint32_t chance_of_outlining_function_; + uint32_t chance_of_permuting_function_variables_; uint32_t chance_of_permuting_instructions_; uint32_t chance_of_permuting_parameters_; uint32_t chance_of_permuting_phi_operands_; @@ -545,9 +575,12 @@ class FuzzerContext { 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_another_pair_of_function_variables_; uint32_t chance_of_swapping_conditional_branch_operands_; + uint32_t chance_of_swapping_functions_; uint32_t chance_of_toggling_access_chain_instruction_; uint32_t chance_of_wrapping_region_in_selection_; + uint32_t chance_of_wrapping_vector_synonym_; // Limits associated with various quantities for which random values are // chosen during fuzzing. diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp index 486f18fa..6a879851 100644 --- a/source/fuzz/fuzzer_pass.cpp +++ b/source/fuzz/fuzzer_pass.cpp @@ -43,11 +43,14 @@ namespace fuzz { FuzzerPass::FuzzerPass(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : ir_context_(ir_context), transformation_context_(transformation_context), fuzzer_context_(fuzzer_context), - transformations_(transformations) {} + transformations_(transformations), + ignore_inapplicable_transformations_( + ignore_inapplicable_transformations) {} FuzzerPass::~FuzzerPass() = default; @@ -111,10 +114,8 @@ void FuzzerPass::ForEachInstructionWithInstructionDescriptor( // module. std::vector reachable_blocks; - const auto* dominator_analysis = - GetIRContext()->GetDominatorAnalysis(function); for (auto& block : *function) { - if (dominator_analysis->IsReachable(&block)) { + if (GetIRContext()->IsReachable(block)) { reachable_blocks.push_back(&block); } } @@ -184,6 +185,49 @@ void FuzzerPass::ForEachInstructionWithInstructionDescriptor( } } +void FuzzerPass::ApplyTransformation(const Transformation& transformation) { + if (ignore_inapplicable_transformations_) { + // If an applicable-by-construction transformation turns out to be + // inapplicable, this is a bug in the fuzzer. However, when deploying the + // fuzzer at scale for finding bugs in SPIR-V processing tools it is + // desirable to silently ignore such bugs. This code path caters for that + // scenario. + if (!transformation.IsApplicable(GetIRContext(), + *GetTransformationContext())) { + return; + } + } else { + // This code path caters for debugging bugs in the fuzzer, where an + // applicable-by-construction transformation turns out to be inapplicable. + assert(transformation.IsApplicable(GetIRContext(), + *GetTransformationContext()) && + "Transformation should be applicable by construction."); + } + transformation.Apply(GetIRContext(), GetTransformationContext()); + auto transformation_message = transformation.ToMessage(); + assert(transformation_message.transformation_case() != + protobufs::Transformation::TRANSFORMATION_NOT_SET && + "Bad transformation."); + *GetTransformations()->add_transformation() = + std::move(transformation_message); +} + +bool FuzzerPass::MaybeApplyTransformation( + const Transformation& transformation) { + if (transformation.IsApplicable(GetIRContext(), + *GetTransformationContext())) { + transformation.Apply(GetIRContext(), GetTransformationContext()); + auto transformation_message = transformation.ToMessage(); + assert(transformation_message.transformation_case() != + protobufs::Transformation::TRANSFORMATION_NOT_SET && + "Bad transformation."); + *GetTransformations()->add_transformation() = + std::move(transformation_message); + return true; + } + return false; +} + uint32_t FuzzerPass::FindOrCreateBoolType() { if (auto existing_id = fuzzerutil::MaybeGetBoolType(GetIRContext())) { return existing_id; @@ -217,7 +261,7 @@ uint32_t FuzzerPass::FindOrCreateFloatType(uint32_t width) { uint32_t FuzzerPass::FindOrCreateFunctionType( uint32_t return_type_id, const std::vector& argument_id) { - // FindFunctionType has a sigle argument for OpTypeFunction operands + // FindFunctionType has a single argument for OpTypeFunction operands // so we will have to copy them all in this vector std::vector type_ids(argument_id.size() + 1); type_ids[0] = return_type_id; diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h index da3f2390..2655b540 100644 --- a/source/fuzz/fuzzer_pass.h +++ b/source/fuzz/fuzzer_pass.h @@ -33,7 +33,8 @@ class FuzzerPass { FuzzerPass(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); virtual ~FuzzerPass(); @@ -106,37 +107,13 @@ class FuzzerPass { // A generic helper for applying a transformation that should be applicable // by construction, and adding it to the sequence of applied transformations. - void ApplyTransformation(const Transformation& transformation) { - assert(transformation.IsApplicable(GetIRContext(), - *GetTransformationContext()) && - "Transformation should be applicable by construction."); - transformation.Apply(GetIRContext(), GetTransformationContext()); - protobufs::Transformation transformation_message = - transformation.ToMessage(); - assert(transformation_message.transformation_case() != - protobufs::Transformation::TRANSFORMATION_NOT_SET && - "Bad transformation."); - *GetTransformations()->add_transformation() = transformation_message; - } + void ApplyTransformation(const Transformation& transformation); // 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()); - protobufs::Transformation transformation_message = - transformation.ToMessage(); - assert(transformation_message.transformation_case() != - protobufs::Transformation::TRANSFORMATION_NOT_SET && - "Bad transformation."); - *GetTransformations()->add_transformation() = transformation_message; - return true; - } - return false; - } + bool MaybeApplyTransformation(const Transformation& transformation); // Returns the id of an OpTypeBool instruction. If such an instruction does // not exist, a transformation is applied to add it. @@ -345,6 +322,10 @@ class FuzzerPass { TransformationContext* transformation_context_; FuzzerContext* fuzzer_context_; protobufs::TransformationSequence* transformations_; + // If set, then transformations that should be applicable by construction are + // still tested for applicability, and ignored if they turn out to be + // inapplicable. Otherwise, applicability by construction is asserted. + const bool ignore_inapplicable_transformations_; }; } // namespace fuzz diff --git a/source/fuzz/fuzzer_pass_add_access_chains.cpp b/source/fuzz/fuzzer_pass_add_access_chains.cpp index c498642c..39f193d9 100644 --- a/source/fuzz/fuzzer_pass_add_access_chains.cpp +++ b/source/fuzz/fuzzer_pass_add_access_chains.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassAddAccessChains::FuzzerPassAddAccessChains( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddAccessChains::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_add_access_chains.h b/source/fuzz/fuzzer_pass_add_access_chains.h index e80c2c6c..5e209cd2 100644 --- a/source/fuzz/fuzzer_pass_add_access_chains.h +++ b/source/fuzz/fuzzer_pass_add_access_chains.h @@ -28,7 +28,8 @@ class FuzzerPassAddAccessChains : public FuzzerPass { FuzzerPassAddAccessChains(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp index b2edc194..1b0d7b10 100644 --- a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp +++ b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassAddBitInstructionSynonyms::FuzzerPassAddBitInstructionSynonyms( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddBitInstructionSynonyms::Apply() { for (auto& function : *GetIRContext()->module()) { @@ -45,22 +46,11 @@ void FuzzerPassAddBitInstructionSynonyms::Apply() { continue; } - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557): - // Right now we only support certain operations. When this issue is - // addressed the following conditional can use the function - // |spvOpcodeIsBit|. - if (instruction.opcode() != SpvOpBitwiseOr && - instruction.opcode() != SpvOpBitwiseXor && - instruction.opcode() != SpvOpBitwiseAnd && - instruction.opcode() != SpvOpNot) { - continue; - } - - // Right now, only integer operands are supported. - if (GetIRContext() - ->get_type_mgr() - ->GetType(instruction.type_id()) - ->AsVector()) { + // Make sure fuzzer never applies a transformation to a bitwise + // instruction with differently signed operands, only integer operands + // are supported and bitwise operations are supported only. + if (!TransformationAddBitInstructionSynonym::IsInstructionSupported( + GetIRContext(), &instruction)) { continue; } diff --git a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h index 28f95779..38d81aa7 100644 --- a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h +++ b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h @@ -28,7 +28,8 @@ class FuzzerPassAddBitInstructionSynonyms : public FuzzerPass { FuzzerPassAddBitInstructionSynonyms( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_composite_extract.cpp b/source/fuzz/fuzzer_pass_add_composite_extract.cpp index 19f99024..dbbec0ca 100644 --- a/source/fuzz/fuzzer_pass_add_composite_extract.cpp +++ b/source/fuzz/fuzzer_pass_add_composite_extract.cpp @@ -26,9 +26,10 @@ namespace fuzz { FuzzerPassAddCompositeExtract::FuzzerPassAddCompositeExtract( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddCompositeExtract::Apply() { std::vector composite_synonyms; diff --git a/source/fuzz/fuzzer_pass_add_composite_extract.h b/source/fuzz/fuzzer_pass_add_composite_extract.h index 32ac190a..e7ed18af 100644 --- a/source/fuzz/fuzzer_pass_add_composite_extract.h +++ b/source/fuzz/fuzzer_pass_add_composite_extract.h @@ -27,7 +27,8 @@ class FuzzerPassAddCompositeExtract : public FuzzerPass { FuzzerPassAddCompositeExtract( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_composite_inserts.cpp b/source/fuzz/fuzzer_pass_add_composite_inserts.cpp index cf314d30..2ac12de4 100644 --- a/source/fuzz/fuzzer_pass_add_composite_inserts.cpp +++ b/source/fuzz/fuzzer_pass_add_composite_inserts.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassAddCompositeInserts::FuzzerPassAddCompositeInserts( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddCompositeInserts::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_add_composite_inserts.h b/source/fuzz/fuzzer_pass_add_composite_inserts.h index 4d511f6c..d9f42d58 100644 --- a/source/fuzz/fuzzer_pass_add_composite_inserts.h +++ b/source/fuzz/fuzzer_pass_add_composite_inserts.h @@ -27,7 +27,8 @@ class FuzzerPassAddCompositeInserts : public FuzzerPass { FuzzerPassAddCompositeInserts( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_add_composite_types.cpp b/source/fuzz/fuzzer_pass_add_composite_types.cpp index 3dfbd690..af36ad06 100644 --- a/source/fuzz/fuzzer_pass_add_composite_types.cpp +++ b/source/fuzz/fuzzer_pass_add_composite_types.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassAddCompositeTypes::FuzzerPassAddCompositeTypes( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddCompositeTypes::Apply() { MaybeAddMissingVectorTypes(); diff --git a/source/fuzz/fuzzer_pass_add_composite_types.h b/source/fuzz/fuzzer_pass_add_composite_types.h index 89d48f8a..f16c79e3 100644 --- a/source/fuzz/fuzzer_pass_add_composite_types.h +++ b/source/fuzz/fuzzer_pass_add_composite_types.h @@ -27,7 +27,8 @@ class FuzzerPassAddCompositeTypes : public FuzzerPass { FuzzerPassAddCompositeTypes( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_add_copy_memory.cpp b/source/fuzz/fuzzer_pass_add_copy_memory.cpp index b654927f..6551f49f 100644 --- a/source/fuzz/fuzzer_pass_add_copy_memory.cpp +++ b/source/fuzz/fuzzer_pass_add_copy_memory.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassAddCopyMemory::FuzzerPassAddCopyMemory( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddCopyMemory::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_add_copy_memory.h b/source/fuzz/fuzzer_pass_add_copy_memory.h index 0f7db0c7..e258752c 100644 --- a/source/fuzz/fuzzer_pass_add_copy_memory.h +++ b/source/fuzz/fuzzer_pass_add_copy_memory.h @@ -27,7 +27,8 @@ class FuzzerPassAddCopyMemory : public FuzzerPass { FuzzerPassAddCopyMemory(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_dead_blocks.cpp b/source/fuzz/fuzzer_pass_add_dead_blocks.cpp index 8c166a20..82d53ebb 100644 --- a/source/fuzz/fuzzer_pass_add_dead_blocks.cpp +++ b/source/fuzz/fuzzer_pass_add_dead_blocks.cpp @@ -31,9 +31,10 @@ const size_t kMaxTransformationsInOnePass = 100U; FuzzerPassAddDeadBlocks::FuzzerPassAddDeadBlocks( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddDeadBlocks::Apply() { // We iterate over all blocks in the module collecting up those at which we diff --git a/source/fuzz/fuzzer_pass_add_dead_blocks.h b/source/fuzz/fuzzer_pass_add_dead_blocks.h index a87c05c3..4567e87d 100644 --- a/source/fuzz/fuzzer_pass_add_dead_blocks.h +++ b/source/fuzz/fuzzer_pass_add_dead_blocks.h @@ -27,7 +27,8 @@ class FuzzerPassAddDeadBlocks : public FuzzerPass { FuzzerPassAddDeadBlocks(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_dead_breaks.cpp b/source/fuzz/fuzzer_pass_add_dead_breaks.cpp index 0c18da90..c3664c87 100644 --- a/source/fuzz/fuzzer_pass_add_dead_breaks.cpp +++ b/source/fuzz/fuzzer_pass_add_dead_breaks.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassAddDeadBreaks::FuzzerPassAddDeadBreaks( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddDeadBreaks::Apply() { // We first collect up lots of possibly-applicable transformations. diff --git a/source/fuzz/fuzzer_pass_add_dead_breaks.h b/source/fuzz/fuzzer_pass_add_dead_breaks.h index d1086fc4..361c3464 100644 --- a/source/fuzz/fuzzer_pass_add_dead_breaks.h +++ b/source/fuzz/fuzzer_pass_add_dead_breaks.h @@ -26,7 +26,8 @@ class FuzzerPassAddDeadBreaks : public FuzzerPass { FuzzerPassAddDeadBreaks(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_dead_continues.cpp b/source/fuzz/fuzzer_pass_add_dead_continues.cpp index 1ab40b73..38814811 100644 --- a/source/fuzz/fuzzer_pass_add_dead_continues.cpp +++ b/source/fuzz/fuzzer_pass_add_dead_continues.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassAddDeadContinues::FuzzerPassAddDeadContinues( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddDeadContinues::Apply() { // Consider every block in every function. diff --git a/source/fuzz/fuzzer_pass_add_dead_continues.h b/source/fuzz/fuzzer_pass_add_dead_continues.h index bf0009e4..4f1bd601 100644 --- a/source/fuzz/fuzzer_pass_add_dead_continues.h +++ b/source/fuzz/fuzzer_pass_add_dead_continues.h @@ -23,10 +23,11 @@ namespace fuzz { // A fuzzer pass for adding dead continue edges to the module. class FuzzerPassAddDeadContinues : public FuzzerPass { public: - FuzzerPassAddDeadContinues( - opt::IRContext* ir_context, TransformationContext* transformation_context, - FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + FuzzerPassAddDeadContinues(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_equation_instructions.cpp b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp index 15554b7a..4bbded8e 100644 --- a/source/fuzz/fuzzer_pass_add_equation_instructions.cpp +++ b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp @@ -45,9 +45,10 @@ bool IsBitWidthSupported(opt::IRContext* ir_context, uint32_t bit_width) { FuzzerPassAddEquationInstructions::FuzzerPassAddEquationInstructions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddEquationInstructions::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_add_equation_instructions.h b/source/fuzz/fuzzer_pass_add_equation_instructions.h index dbec5bac..dc9a27b3 100644 --- a/source/fuzz/fuzzer_pass_add_equation_instructions.h +++ b/source/fuzz/fuzzer_pass_add_equation_instructions.h @@ -29,7 +29,8 @@ class FuzzerPassAddEquationInstructions : public FuzzerPass { FuzzerPassAddEquationInstructions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_add_function_calls.cpp b/source/fuzz/fuzzer_pass_add_function_calls.cpp index 2240696b..033f4a27 100644 --- a/source/fuzz/fuzzer_pass_add_function_calls.cpp +++ b/source/fuzz/fuzzer_pass_add_function_calls.cpp @@ -26,9 +26,10 @@ namespace fuzz { FuzzerPassAddFunctionCalls::FuzzerPassAddFunctionCalls( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddFunctionCalls::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_add_function_calls.h b/source/fuzz/fuzzer_pass_add_function_calls.h index 081510c1..80b03d7b 100644 --- a/source/fuzz/fuzzer_pass_add_function_calls.h +++ b/source/fuzz/fuzzer_pass_add_function_calls.h @@ -24,10 +24,11 @@ namespace fuzz { // anywhere, and (b) any functions, from dead blocks. class FuzzerPassAddFunctionCalls : public FuzzerPass { public: - FuzzerPassAddFunctionCalls( - opt::IRContext* ir_context, TransformationContext* transformation_context, - FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + FuzzerPassAddFunctionCalls(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_add_global_variables.cpp b/source/fuzz/fuzzer_pass_add_global_variables.cpp index 06797413..061f44d0 100644 --- a/source/fuzz/fuzzer_pass_add_global_variables.cpp +++ b/source/fuzz/fuzzer_pass_add_global_variables.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassAddGlobalVariables::FuzzerPassAddGlobalVariables( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddGlobalVariables::Apply() { SpvStorageClass variable_storage_class = SpvStorageClassPrivate; @@ -47,6 +48,10 @@ void FuzzerPassAddGlobalVariables::Apply() { // These are the basic types that are available to this fuzzer pass. auto& basic_types = basic_type_ids_and_pointers.first; + if (basic_types.empty()) { + // There are no basic types, so there is nothing this fuzzer pass can do. + return; + } // These are the pointers to those basic types that are *initially* available // to the fuzzer pass. The fuzzer pass might add pointer types in cases where diff --git a/source/fuzz/fuzzer_pass_add_global_variables.h b/source/fuzz/fuzzer_pass_add_global_variables.h index 3745c5c3..1496646e 100644 --- a/source/fuzz/fuzzer_pass_add_global_variables.h +++ b/source/fuzz/fuzzer_pass_add_global_variables.h @@ -27,7 +27,8 @@ class FuzzerPassAddGlobalVariables : public FuzzerPass { FuzzerPassAddGlobalVariables( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp index c10db720..19661d10 100644 --- a/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp +++ b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp @@ -27,9 +27,10 @@ FuzzerPassAddImageSampleUnusedComponents:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddImageSampleUnusedComponents::Apply() { // SPIR-V module to help understand the transformation. diff --git a/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h index f5dea8bf..1a278931 100644 --- a/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h +++ b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h @@ -28,7 +28,8 @@ class FuzzerPassAddImageSampleUnusedComponents : public FuzzerPass { FuzzerPassAddImageSampleUnusedComponents( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_loads.cpp b/source/fuzz/fuzzer_pass_add_loads.cpp index 2e50da26..ab91543b 100644 --- a/source/fuzz/fuzzer_pass_add_loads.cpp +++ b/source/fuzz/fuzzer_pass_add_loads.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassAddLoads::FuzzerPassAddLoads( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddLoads::Apply() { ForEachInstructionWithInstructionDescriptor( @@ -38,18 +39,22 @@ void FuzzerPassAddLoads::Apply() { "The opcode of the instruction we might insert before must be " "the same as the opcode in the descriptor for the instruction"); - // Check whether it is legitimate to insert a load before this - // instruction. - if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, inst_it)) { - return; - } - // Randomly decide whether to try inserting a load here. if (!GetFuzzerContext()->ChoosePercentage( GetFuzzerContext()->GetChanceOfAddingLoad())) { return; } + // Check whether it is legitimate to insert a load or atomic load before + // this instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, inst_it)) { + return; + } + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpAtomicLoad, + inst_it)) { + return; + } + std::vector relevant_instructions = FindAvailableInstructions( function, block, inst_it, @@ -79,13 +84,53 @@ void FuzzerPassAddLoads::Apply() { return; } - // Choose a pointer at random, and create and apply a loading - // transformation based on it. - ApplyTransformation(TransformationLoad( - GetFuzzerContext()->GetFreshId(), + auto chosen_instruction = relevant_instructions[GetFuzzerContext()->RandomIndex( - relevant_instructions)] - ->result_id(), + relevant_instructions)]; + + bool is_atomic_load = false; + uint32_t memory_scope_id = 0; + uint32_t memory_semantics_id = 0; + + auto storage_class = static_cast( + GetIRContext() + ->get_def_use_mgr() + ->GetDef(chosen_instruction->type_id()) + ->GetSingleWordInOperand(0)); + + switch (storage_class) { + case SpvStorageClassStorageBuffer: + case SpvStorageClassPhysicalStorageBuffer: + case SpvStorageClassWorkgroup: + case SpvStorageClassCrossWorkgroup: + case SpvStorageClassAtomicCounter: + case SpvStorageClassImage: + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingAtomicLoad())) { + is_atomic_load = true; + + memory_scope_id = FindOrCreateConstant( + {SpvScopeInvocation}, + FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()), + false); + + memory_semantics_id = FindOrCreateConstant( + {static_cast( + fuzzerutil::GetMemorySemanticsForStorageClass( + storage_class))}, + FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()), + false); + } + break; + + default: + break; + } + + // Create and apply the transformation. + ApplyTransformation(TransformationLoad( + GetFuzzerContext()->GetFreshId(), chosen_instruction->result_id(), + is_atomic_load, memory_scope_id, memory_semantics_id, instruction_descriptor)); }); } diff --git a/source/fuzz/fuzzer_pass_add_loads.h b/source/fuzz/fuzzer_pass_add_loads.h index 3913c624..14460e7f 100644 --- a/source/fuzz/fuzzer_pass_add_loads.h +++ b/source/fuzz/fuzzer_pass_add_loads.h @@ -26,7 +26,8 @@ class FuzzerPassAddLoads : public FuzzerPass { FuzzerPassAddLoads(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_local_variables.cpp b/source/fuzz/fuzzer_pass_add_local_variables.cpp index a50b0b07..a4e739f8 100644 --- a/source/fuzz/fuzzer_pass_add_local_variables.cpp +++ b/source/fuzz/fuzzer_pass_add_local_variables.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassAddLocalVariables::FuzzerPassAddLocalVariables( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddLocalVariables::Apply() { auto basic_type_ids_and_pointers = @@ -34,6 +35,10 @@ void FuzzerPassAddLocalVariables::Apply() { // These are the basic types that are available to this fuzzer pass. auto& basic_types = basic_type_ids_and_pointers.first; + if (basic_types.empty()) { + // The pass cannot do anything if there are no basic types. + return; + } // These are the pointers to those basic types that are *initially* available // to the fuzzer pass. The fuzzer pass might add pointer types in cases where diff --git a/source/fuzz/fuzzer_pass_add_local_variables.h b/source/fuzz/fuzzer_pass_add_local_variables.h index d73dae29..c969ba06 100644 --- a/source/fuzz/fuzzer_pass_add_local_variables.h +++ b/source/fuzz/fuzzer_pass_add_local_variables.h @@ -27,7 +27,8 @@ class FuzzerPassAddLocalVariables : public FuzzerPass { FuzzerPassAddLocalVariables( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp b/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp index 1cfed866..e8c9e964 100644 --- a/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp +++ b/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassAddLoopPreheaders::FuzzerPassAddLoopPreheaders( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddLoopPreheaders::Apply() { for (auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_add_loop_preheaders.h b/source/fuzz/fuzzer_pass_add_loop_preheaders.h index 8ac2dac8..2a13dbac 100644 --- a/source/fuzz/fuzzer_pass_add_loop_preheaders.h +++ b/source/fuzz/fuzzer_pass_add_loop_preheaders.h @@ -30,7 +30,8 @@ class FuzzerPassAddLoopPreheaders : public FuzzerPass { FuzzerPassAddLoopPreheaders( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp b/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp index 69e0697f..1a65ef61 100644 --- a/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp +++ b/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp @@ -29,9 +29,10 @@ FuzzerPassAddLoopsToCreateIntConstantSynonyms:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddLoopsToCreateIntConstantSynonyms::Apply() { std::vector constants; diff --git a/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h b/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h index 2eacef5f..14e17542 100644 --- a/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h +++ b/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h @@ -28,7 +28,8 @@ class FuzzerPassAddLoopsToCreateIntConstantSynonyms : public FuzzerPass { FuzzerPassAddLoopsToCreateIntConstantSynonyms( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp index d0753100..8ae6e90a 100644 --- a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp +++ b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp @@ -22,9 +22,10 @@ namespace fuzz { FuzzerPassAddNoContractionDecorations::FuzzerPassAddNoContractionDecorations( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddNoContractionDecorations::Apply() { // Consider every instruction in every block in every function. diff --git a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h index 74212d87..7aeb26de 100644 --- a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h +++ b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h @@ -26,7 +26,8 @@ class FuzzerPassAddNoContractionDecorations : public FuzzerPass { FuzzerPassAddNoContractionDecorations( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp b/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp index be6e7ea1..73b6b0ac 100644 --- a/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp +++ b/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassAddOpPhiSynonyms::FuzzerPassAddOpPhiSynonyms( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddOpPhiSynonyms::Apply() { // Get a list of synonymous ids with the same type that can be used in the diff --git a/source/fuzz/fuzzer_pass_add_opphi_synonyms.h b/source/fuzz/fuzzer_pass_add_opphi_synonyms.h index 9077118e..c45c9a8a 100644 --- a/source/fuzz/fuzzer_pass_add_opphi_synonyms.h +++ b/source/fuzz/fuzzer_pass_add_opphi_synonyms.h @@ -25,10 +25,11 @@ namespace fuzz { // synonymous with the others. class FuzzerPassAddOpPhiSynonyms : public FuzzerPass { public: - FuzzerPassAddOpPhiSynonyms( - opt::IRContext* ir_context, TransformationContext* transformation_context, - FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + FuzzerPassAddOpPhiSynonyms(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_add_parameters.cpp b/source/fuzz/fuzzer_pass_add_parameters.cpp index 784653d8..1cb6a79d 100644 --- a/source/fuzz/fuzzer_pass_add_parameters.cpp +++ b/source/fuzz/fuzzer_pass_add_parameters.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassAddParameters::FuzzerPassAddParameters( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddParameters::Apply() { // Compute type candidates for the new parameter. diff --git a/source/fuzz/fuzzer_pass_add_parameters.h b/source/fuzz/fuzzer_pass_add_parameters.h index 47dde390..c79f0e09 100644 --- a/source/fuzz/fuzzer_pass_add_parameters.h +++ b/source/fuzz/fuzzer_pass_add_parameters.h @@ -30,7 +30,8 @@ class FuzzerPassAddParameters : public FuzzerPass { FuzzerPassAddParameters(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp b/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp index 58c6d1b2..b8c2b559 100644 --- a/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp +++ b/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp @@ -22,9 +22,10 @@ namespace fuzz { FuzzerPassAddRelaxedDecorations::FuzzerPassAddRelaxedDecorations( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddRelaxedDecorations::Apply() { // Consider every instruction in every block in every function. diff --git a/source/fuzz/fuzzer_pass_add_relaxed_decorations.h b/source/fuzz/fuzzer_pass_add_relaxed_decorations.h index 723c4a0a..51eb5942 100644 --- a/source/fuzz/fuzzer_pass_add_relaxed_decorations.h +++ b/source/fuzz/fuzzer_pass_add_relaxed_decorations.h @@ -26,7 +26,8 @@ class FuzzerPassAddRelaxedDecorations : public FuzzerPass { FuzzerPassAddRelaxedDecorations( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_stores.cpp b/source/fuzz/fuzzer_pass_add_stores.cpp index f89428d3..606e4a63 100644 --- a/source/fuzz/fuzzer_pass_add_stores.cpp +++ b/source/fuzz/fuzzer_pass_add_stores.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassAddStores::FuzzerPassAddStores( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddStores::Apply() { ForEachInstructionWithInstructionDescriptor( @@ -38,16 +39,20 @@ void FuzzerPassAddStores::Apply() { "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 inserting a store here. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingStore())) { + return; + } + // Check whether it is legitimate to insert a store before this // instruction. if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, inst_it)) { return; } - - // Randomly decide whether to try inserting a store here. - if (!GetFuzzerContext()->ChoosePercentage( - GetFuzzerContext()->GetChanceOfAddingStore())) { + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpAtomicStore, + inst_it)) { return; } @@ -116,10 +121,49 @@ void FuzzerPassAddStores::Apply() { return; } - // Choose a value at random, and create and apply a storing - // transformation based on it and the pointer. + bool is_atomic_store = false; + uint32_t memory_scope_id = 0; + uint32_t memory_semantics_id = 0; + + auto storage_class = + static_cast(GetIRContext() + ->get_def_use_mgr() + ->GetDef(pointer->type_id()) + ->GetSingleWordInOperand(0)); + + switch (storage_class) { + case SpvStorageClassStorageBuffer: + case SpvStorageClassPhysicalStorageBuffer: + case SpvStorageClassWorkgroup: + case SpvStorageClassCrossWorkgroup: + case SpvStorageClassAtomicCounter: + case SpvStorageClassImage: + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingAtomicStore())) { + is_atomic_store = true; + + memory_scope_id = FindOrCreateConstant( + {SpvScopeInvocation}, + FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()), + false); + + memory_semantics_id = FindOrCreateConstant( + {static_cast( + fuzzerutil::GetMemorySemanticsForStorageClass( + storage_class))}, + FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()), + false); + } + break; + + default: + break; + } + + // Create and apply the transformation. ApplyTransformation(TransformationStore( - pointer->result_id(), + pointer->result_id(), is_atomic_store, memory_scope_id, + memory_semantics_id, relevant_values[GetFuzzerContext()->RandomIndex(relevant_values)] ->result_id(), instruction_descriptor)); diff --git a/source/fuzz/fuzzer_pass_add_stores.h b/source/fuzz/fuzzer_pass_add_stores.h index 9519c385..b18dde1f 100644 --- a/source/fuzz/fuzzer_pass_add_stores.h +++ b/source/fuzz/fuzzer_pass_add_stores.h @@ -28,7 +28,8 @@ class FuzzerPassAddStores : public FuzzerPass { FuzzerPassAddStores(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_synonyms.cpp b/source/fuzz/fuzzer_pass_add_synonyms.cpp index 2a85e52c..1d188deb 100644 --- a/source/fuzz/fuzzer_pass_add_synonyms.cpp +++ b/source/fuzz/fuzzer_pass_add_synonyms.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassAddSynonyms::FuzzerPassAddSynonyms( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddSynonyms::Apply() { ForEachInstructionWithInstructionDescriptor( @@ -79,6 +80,8 @@ void FuzzerPassAddSynonyms::Apply() { case protobufs::TransformationAddSynonym::ADD_ZERO: case protobufs::TransformationAddSynonym::SUB_ZERO: case protobufs::TransformationAddSynonym::LOGICAL_OR: + case protobufs::TransformationAddSynonym::BITWISE_OR: + case protobufs::TransformationAddSynonym::BITWISE_XOR: // Create a zero constant to be used as an operand of the synonymous // instruction. FindOrCreateZeroConstant(existing_synonym->type_id(), false); diff --git a/source/fuzz/fuzzer_pass_add_synonyms.h b/source/fuzz/fuzzer_pass_add_synonyms.h index ccf4a886..b0c174bc 100644 --- a/source/fuzz/fuzzer_pass_add_synonyms.h +++ b/source/fuzz/fuzzer_pass_add_synonyms.h @@ -27,7 +27,8 @@ class FuzzerPassAddSynonyms : public FuzzerPass { FuzzerPassAddSynonyms(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp index e2eaaa0a..a29d1d34 100644 --- a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp +++ b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassAddVectorShuffleInstructions::FuzzerPassAddVectorShuffleInstructions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddVectorShuffleInstructions::Apply() { ForEachInstructionWithInstructionDescriptor( @@ -75,7 +76,7 @@ void FuzzerPassAddVectorShuffleInstructions::Apply() { ->IdIsIrrelevant(instruction->result_id()) && !fuzzerutil::CanMakeSynonymOf(ir_context, *GetTransformationContext(), - instruction)) { + *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. diff --git a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h index c5af374c..64d6608a 100644 --- a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h +++ b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h @@ -26,7 +26,8 @@ class FuzzerPassAddVectorShuffleInstructions : public FuzzerPass { FuzzerPassAddVectorShuffleInstructions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp b/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp index 3c4f3803..94428f72 100644 --- a/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp +++ b/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassAdjustBranchWeights::FuzzerPassAdjustBranchWeights( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAdjustBranchWeights::Apply() { // For all OpBranchConditional instructions, diff --git a/source/fuzz/fuzzer_pass_adjust_branch_weights.h b/source/fuzz/fuzzer_pass_adjust_branch_weights.h index de2f33d3..ae1ea345 100644 --- a/source/fuzz/fuzzer_pass_adjust_branch_weights.h +++ b/source/fuzz/fuzzer_pass_adjust_branch_weights.h @@ -28,7 +28,8 @@ class FuzzerPassAdjustBranchWeights : public FuzzerPass { FuzzerPassAdjustBranchWeights( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_adjust_function_controls.cpp b/source/fuzz/fuzzer_pass_adjust_function_controls.cpp index b38bd212..1c2bc8cf 100644 --- a/source/fuzz/fuzzer_pass_adjust_function_controls.cpp +++ b/source/fuzz/fuzzer_pass_adjust_function_controls.cpp @@ -22,9 +22,10 @@ namespace fuzz { FuzzerPassAdjustFunctionControls::FuzzerPassAdjustFunctionControls( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAdjustFunctionControls::Apply() { // Consider every function in the module. diff --git a/source/fuzz/fuzzer_pass_adjust_function_controls.h b/source/fuzz/fuzzer_pass_adjust_function_controls.h index 5ef32a18..7a8c4927 100644 --- a/source/fuzz/fuzzer_pass_adjust_function_controls.h +++ b/source/fuzz/fuzzer_pass_adjust_function_controls.h @@ -26,7 +26,8 @@ class FuzzerPassAdjustFunctionControls : public FuzzerPass { FuzzerPassAdjustFunctionControls( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp b/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp index 0f7cf037..fe855cad 100644 --- a/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp +++ b/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp @@ -22,9 +22,10 @@ namespace fuzz { FuzzerPassAdjustLoopControls::FuzzerPassAdjustLoopControls( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAdjustLoopControls::Apply() { // Consider every merge instruction in the module (via looking through all diff --git a/source/fuzz/fuzzer_pass_adjust_loop_controls.h b/source/fuzz/fuzzer_pass_adjust_loop_controls.h index 4ca670b7..25e83047 100644 --- a/source/fuzz/fuzzer_pass_adjust_loop_controls.h +++ b/source/fuzz/fuzzer_pass_adjust_loop_controls.h @@ -26,7 +26,8 @@ class FuzzerPassAdjustLoopControls : public FuzzerPass { FuzzerPassAdjustLoopControls( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp index 778d43ff..d2ff40e6 100644 --- a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp +++ b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassAdjustMemoryOperandsMasks::FuzzerPassAdjustMemoryOperandsMasks( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAdjustMemoryOperandsMasks::Apply() { // Consider every block in every function. diff --git a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h index a068b8dd..3197f91b 100644 --- a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h +++ b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h @@ -27,7 +27,8 @@ class FuzzerPassAdjustMemoryOperandsMasks : public FuzzerPass { FuzzerPassAdjustMemoryOperandsMasks( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp b/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp index d9b4e293..7d8e6b57 100644 --- a/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp +++ b/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp @@ -22,9 +22,10 @@ namespace fuzz { FuzzerPassAdjustSelectionControls::FuzzerPassAdjustSelectionControls( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAdjustSelectionControls::Apply() { // Consider every merge instruction in the module (via looking through all diff --git a/source/fuzz/fuzzer_pass_adjust_selection_controls.h b/source/fuzz/fuzzer_pass_adjust_selection_controls.h index 6931f942..ac55de79 100644 --- a/source/fuzz/fuzzer_pass_adjust_selection_controls.h +++ b/source/fuzz/fuzzer_pass_adjust_selection_controls.h @@ -26,7 +26,8 @@ class FuzzerPassAdjustSelectionControls : public FuzzerPass { FuzzerPassAdjustSelectionControls( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp index 71208313..5c3b86b9 100644 --- a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp +++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp @@ -28,9 +28,10 @@ namespace fuzz { FuzzerPassApplyIdSynonyms::FuzzerPassApplyIdSynonyms( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassApplyIdSynonyms::Apply() { // Compute a closure of data synonym facts, to enrich the pool of synonyms @@ -197,7 +198,7 @@ bool FuzzerPassApplyIdSynonyms::DataDescriptorsHaveCompatibleTypes( GetIRContext(), base_object_type_id_2, dd2.index()); assert(type_id_1 && type_id_2 && "Data descriptors have invalid types"); - return TransformationReplaceIdWithSynonym::TypesAreCompatible( + return fuzzerutil::TypesAreCompatible( GetIRContext(), opcode, use_in_operand_index, type_id_1, type_id_2); } diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.h b/source/fuzz/fuzzer_pass_apply_id_synonyms.h index b402b509..3da9c5d6 100644 --- a/source/fuzz/fuzzer_pass_apply_id_synonyms.h +++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.h @@ -28,7 +28,8 @@ class FuzzerPassApplyIdSynonyms : public FuzzerPass { FuzzerPassApplyIdSynonyms(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_construct_composites.cpp b/source/fuzz/fuzzer_pass_construct_composites.cpp index 1a174cf1..ff022fcc 100644 --- a/source/fuzz/fuzzer_pass_construct_composites.cpp +++ b/source/fuzz/fuzzer_pass_construct_composites.cpp @@ -26,9 +26,10 @@ namespace fuzz { FuzzerPassConstructComposites::FuzzerPassConstructComposites( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassConstructComposites::Apply() { // Gather up the ids of all composite types, but skip block-/buffer @@ -61,7 +62,7 @@ void FuzzerPassConstructComposites::Apply() { return GetTransformationContext()->GetFactManager()->IdIsIrrelevant( inst->result_id()) || fuzzerutil::CanMakeSynonymOf(ir_context, - *GetTransformationContext(), inst); + *GetTransformationContext(), *inst); }); ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_construct_composites.h b/source/fuzz/fuzzer_pass_construct_composites.h index 333ac931..81413980 100644 --- a/source/fuzz/fuzzer_pass_construct_composites.h +++ b/source/fuzz/fuzzer_pass_construct_composites.h @@ -29,7 +29,8 @@ class FuzzerPassConstructComposites : public FuzzerPass { FuzzerPassConstructComposites( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_copy_objects.cpp b/source/fuzz/fuzzer_pass_copy_objects.cpp index 09b5368a..80cc2a57 100644 --- a/source/fuzz/fuzzer_pass_copy_objects.cpp +++ b/source/fuzz/fuzzer_pass_copy_objects.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassCopyObjects::FuzzerPassCopyObjects( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassCopyObjects::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_copy_objects.h b/source/fuzz/fuzzer_pass_copy_objects.h index 461cd979..2fb5a53f 100644 --- a/source/fuzz/fuzzer_pass_copy_objects.h +++ b/source/fuzz/fuzzer_pass_copy_objects.h @@ -26,7 +26,8 @@ class FuzzerPassCopyObjects : public FuzzerPass { FuzzerPassCopyObjects(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_donate_modules.cpp b/source/fuzz/fuzzer_pass_donate_modules.cpp index 2f2ed50a..29ede58b 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.cpp +++ b/source/fuzz/fuzzer_pass_donate_modules.cpp @@ -45,10 +45,11 @@ FuzzerPassDonateModules::FuzzerPassDonateModules( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations, - const std::vector& donor_suppliers) + bool ignore_inapplicable_transformations, + std::vector donor_suppliers) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations), - donor_suppliers_(donor_suppliers) {} + transformations, ignore_inapplicable_transformations), + donor_suppliers_(std::move(donor_suppliers)) {} void FuzzerPassDonateModules::Apply() { // If there are no donor suppliers, this fuzzer pass is a no-op. @@ -478,7 +479,7 @@ void FuzzerPassDonateModules::HandleTypeOrValue( "should have been donated."); // It is OK to have duplicate constant composite definitions, so add - // this to the module using remapped versions of all consituent ids and + // this to the module using remapped versions of all constituent ids and // the result type. new_result_id = GetFuzzerContext()->GetFreshId(); std::vector constituent_ids; diff --git a/source/fuzz/fuzzer_pass_donate_modules.h b/source/fuzz/fuzzer_pass_donate_modules.h index 1581a8a4..924dd358 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.h +++ b/source/fuzz/fuzzer_pass_donate_modules.h @@ -31,7 +31,8 @@ class FuzzerPassDonateModules : public FuzzerPass { opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations, - const std::vector& donor_suppliers); + bool ignore_inapplicable_transformations, + std::vector donor_suppliers); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp b/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp index e08d65b1..3a9a7e66 100644 --- a/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp +++ b/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp @@ -25,9 +25,10 @@ FuzzerPassDuplicateRegionsWithSelections:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassDuplicateRegionsWithSelections::Apply() { // Iterate over all of the functions in the module. diff --git a/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h b/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h index 7cb1197d..74343a66 100644 --- a/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h +++ b/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h @@ -29,7 +29,8 @@ class FuzzerPassDuplicateRegionsWithSelections : public FuzzerPass { FuzzerPassDuplicateRegionsWithSelections( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp b/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp index e25dcbc3..5bf0461d 100644 --- a/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp +++ b/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassExpandVectorReductions::FuzzerPassExpandVectorReductions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassExpandVectorReductions::Apply() { for (auto& function : *GetIRContext()->module()) { @@ -46,7 +47,7 @@ void FuzzerPassExpandVectorReductions::Apply() { // It must be able to make a synonym of |instruction|. if (!fuzzerutil::CanMakeSynonymOf( - GetIRContext(), *GetTransformationContext(), &instruction)) { + GetIRContext(), *GetTransformationContext(), instruction)) { continue; } diff --git a/source/fuzz/fuzzer_pass_expand_vector_reductions.h b/source/fuzz/fuzzer_pass_expand_vector_reductions.h index ed0225df..c0673e12 100644 --- a/source/fuzz/fuzzer_pass_expand_vector_reductions.h +++ b/source/fuzz/fuzzer_pass_expand_vector_reductions.h @@ -28,7 +28,8 @@ class FuzzerPassExpandVectorReductions : public FuzzerPass { FuzzerPassExpandVectorReductions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp b/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp index 84da7a74..70fa6a12 100644 --- a/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp +++ b/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp @@ -26,9 +26,10 @@ namespace fuzz { FuzzerPassFlattenConditionalBranches::FuzzerPassFlattenConditionalBranches( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassFlattenConditionalBranches::Apply() { for (auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_flatten_conditional_branches.h b/source/fuzz/fuzzer_pass_flatten_conditional_branches.h index e7d7dc36..abfbc3c1 100644 --- a/source/fuzz/fuzzer_pass_flatten_conditional_branches.h +++ b/source/fuzz/fuzzer_pass_flatten_conditional_branches.h @@ -25,7 +25,8 @@ class FuzzerPassFlattenConditionalBranches : public FuzzerPass { FuzzerPassFlattenConditionalBranches( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_inline_functions.cpp b/source/fuzz/fuzzer_pass_inline_functions.cpp index 405afd8b..4024096f 100644 --- a/source/fuzz/fuzzer_pass_inline_functions.cpp +++ b/source/fuzz/fuzzer_pass_inline_functions.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassInlineFunctions::FuzzerPassInlineFunctions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassInlineFunctions::Apply() { // |function_call_instructions| are the instructions that will be inlined. diff --git a/source/fuzz/fuzzer_pass_inline_functions.h b/source/fuzz/fuzzer_pass_inline_functions.h index a6ed8cad..c4e0b831 100644 --- a/source/fuzz/fuzzer_pass_inline_functions.h +++ b/source/fuzz/fuzzer_pass_inline_functions.h @@ -28,7 +28,8 @@ class FuzzerPassInlineFunctions : public FuzzerPass { FuzzerPassInlineFunctions(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp index 675cae9a..d8780ff7 100644 --- a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp +++ b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp @@ -27,9 +27,10 @@ FuzzerPassInterchangeSignednessOfIntegerOperands:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassInterchangeSignednessOfIntegerOperands::Apply() { assert(!GetFuzzerContext()->IsWgslCompatible() && diff --git a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h index 11b8fb6e..6a10e897 100644 --- a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h +++ b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h @@ -32,7 +32,8 @@ class FuzzerPassInterchangeSignednessOfIntegerOperands : public FuzzerPass { FuzzerPassInterchangeSignednessOfIntegerOperands( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp index 5d0a2223..2c16cd5b 100644 --- a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp +++ b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassInterchangeZeroLikeConstants::FuzzerPassInterchangeZeroLikeConstants( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} uint32_t FuzzerPassInterchangeZeroLikeConstants::FindOrCreateToggledConstant( opt::Instruction* declaration) { diff --git a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h index 012f03d3..bb4a4dc7 100644 --- a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h +++ b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h @@ -33,7 +33,8 @@ class FuzzerPassInterchangeZeroLikeConstants : public FuzzerPass { FuzzerPassInterchangeZeroLikeConstants( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp b/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp index 542748e9..130f7502 100644 --- a/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp +++ b/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassInvertComparisonOperators::FuzzerPassInvertComparisonOperators( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassInvertComparisonOperators::Apply() { GetIRContext()->module()->ForEachInst([this](const opt::Instruction* inst) { diff --git a/source/fuzz/fuzzer_pass_invert_comparison_operators.h b/source/fuzz/fuzzer_pass_invert_comparison_operators.h index 5f015c28..d0d09edb 100644 --- a/source/fuzz/fuzzer_pass_invert_comparison_operators.h +++ b/source/fuzz/fuzzer_pass_invert_comparison_operators.h @@ -27,7 +27,8 @@ class FuzzerPassInvertComparisonOperators : public FuzzerPass { FuzzerPassInvertComparisonOperators( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp b/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp index 7bf07a49..b755d235 100644 --- a/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp +++ b/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassMakeVectorOperationsDynamic::FuzzerPassMakeVectorOperationsDynamic( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassMakeVectorOperationsDynamic::Apply() { for (auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h b/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h index da27825a..b6a46c78 100644 --- a/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h +++ b/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h @@ -27,7 +27,8 @@ class FuzzerPassMakeVectorOperationsDynamic : public FuzzerPass { FuzzerPassMakeVectorOperationsDynamic( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_merge_blocks.cpp b/source/fuzz/fuzzer_pass_merge_blocks.cpp index a8ec784c..70200629 100644 --- a/source/fuzz/fuzzer_pass_merge_blocks.cpp +++ b/source/fuzz/fuzzer_pass_merge_blocks.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassMergeBlocks::FuzzerPassMergeBlocks( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassMergeBlocks::Apply() { // First we populate a sequence of transformations that we might consider diff --git a/source/fuzz/fuzzer_pass_merge_blocks.h b/source/fuzz/fuzzer_pass_merge_blocks.h index 66cf4c64..46e257f1 100644 --- a/source/fuzz/fuzzer_pass_merge_blocks.h +++ b/source/fuzz/fuzzer_pass_merge_blocks.h @@ -26,7 +26,8 @@ class FuzzerPassMergeBlocks : public FuzzerPass { FuzzerPassMergeBlocks(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_merge_function_returns.cpp b/source/fuzz/fuzzer_pass_merge_function_returns.cpp index 70fa8200..220f707b 100644 --- a/source/fuzz/fuzzer_pass_merge_function_returns.cpp +++ b/source/fuzz/fuzzer_pass_merge_function_returns.cpp @@ -26,9 +26,10 @@ namespace fuzz { FuzzerPassMergeFunctionReturns::FuzzerPassMergeFunctionReturns( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassMergeFunctionReturns::Apply() { // The pass might add new functions to the module (due to wrapping early @@ -172,8 +173,9 @@ void FuzzerPassMergeFunctionReturns::Apply() { } // Get the ids needed by the transformation. - uint32_t outer_header_id = GetFuzzerContext()->GetFreshId(); - uint32_t outer_return_id = GetFuzzerContext()->GetFreshId(); + const uint32_t outer_header_id = GetFuzzerContext()->GetFreshId(); + const uint32_t unreachable_continue_id = GetFuzzerContext()->GetFreshId(); + const uint32_t outer_return_id = GetFuzzerContext()->GetFreshId(); bool function_is_void = GetIRContext()->get_type_mgr()->GetType(function->type_id())->AsVoid(); @@ -209,8 +211,8 @@ void FuzzerPassMergeFunctionReturns::Apply() { // Apply the transformation if it is applicable (it could be inapplicable if // adding new predecessors to merge blocks breaks dominance rules). MaybeApplyTransformation(TransformationMergeFunctionReturns( - function->result_id(), outer_header_id, outer_return_id, return_val_id, - returnable_val_id, merge_blocks_info)); + function->result_id(), outer_header_id, unreachable_continue_id, + outer_return_id, return_val_id, returnable_val_id, merge_blocks_info)); } } diff --git a/source/fuzz/fuzzer_pass_merge_function_returns.h b/source/fuzz/fuzzer_pass_merge_function_returns.h index ddd2c9dc..a799b8be 100644 --- a/source/fuzz/fuzzer_pass_merge_function_returns.h +++ b/source/fuzz/fuzzer_pass_merge_function_returns.h @@ -31,7 +31,8 @@ class FuzzerPassMergeFunctionReturns : public FuzzerPass { FuzzerPassMergeFunctionReturns( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_mutate_pointers.cpp b/source/fuzz/fuzzer_pass_mutate_pointers.cpp index f021a978..bbe05409 100644 --- a/source/fuzz/fuzzer_pass_mutate_pointers.cpp +++ b/source/fuzz/fuzzer_pass_mutate_pointers.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassMutatePointers::FuzzerPassMutatePointers( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassMutatePointers::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_mutate_pointers.h b/source/fuzz/fuzzer_pass_mutate_pointers.h index 5ef6a2ab..45c6a7cc 100644 --- a/source/fuzz/fuzzer_pass_mutate_pointers.h +++ b/source/fuzz/fuzzer_pass_mutate_pointers.h @@ -26,7 +26,8 @@ class FuzzerPassMutatePointers : public FuzzerPass { FuzzerPassMutatePointers(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp index 32318e89..f60c1b4c 100644 --- a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp +++ b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp @@ -30,9 +30,10 @@ namespace fuzz { FuzzerPassObfuscateConstants::FuzzerPassObfuscateConstants( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaConstantPair( uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, diff --git a/source/fuzz/fuzzer_pass_obfuscate_constants.h b/source/fuzz/fuzzer_pass_obfuscate_constants.h index 82b1092c..30e64d28 100644 --- a/source/fuzz/fuzzer_pass_obfuscate_constants.h +++ b/source/fuzz/fuzzer_pass_obfuscate_constants.h @@ -30,7 +30,8 @@ class FuzzerPassObfuscateConstants : public FuzzerPass { FuzzerPassObfuscateConstants( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_outline_functions.cpp b/source/fuzz/fuzzer_pass_outline_functions.cpp index bfde61f7..b90c12dc 100644 --- a/source/fuzz/fuzzer_pass_outline_functions.cpp +++ b/source/fuzz/fuzzer_pass_outline_functions.cpp @@ -27,9 +27,10 @@ namespace fuzz { FuzzerPassOutlineFunctions::FuzzerPassOutlineFunctions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassOutlineFunctions::Apply() { std::vector original_functions; diff --git a/source/fuzz/fuzzer_pass_outline_functions.h b/source/fuzz/fuzzer_pass_outline_functions.h index 45e52ff4..d80dc4a7 100644 --- a/source/fuzz/fuzzer_pass_outline_functions.h +++ b/source/fuzz/fuzzer_pass_outline_functions.h @@ -24,10 +24,11 @@ namespace fuzz { // flow graph into their own functions. class FuzzerPassOutlineFunctions : public FuzzerPass { public: - FuzzerPassOutlineFunctions( - opt::IRContext* ir_context, TransformationContext* transformation_context, - FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + FuzzerPassOutlineFunctions(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_permute_blocks.cpp b/source/fuzz/fuzzer_pass_permute_blocks.cpp index 769c49f0..e55fae32 100644 --- a/source/fuzz/fuzzer_pass_permute_blocks.cpp +++ b/source/fuzz/fuzzer_pass_permute_blocks.cpp @@ -22,9 +22,10 @@ namespace fuzz { FuzzerPassPermuteBlocks::FuzzerPassPermuteBlocks( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassPermuteBlocks::Apply() { // For now we do something very simple: we randomly decide whether to move a diff --git a/source/fuzz/fuzzer_pass_permute_blocks.h b/source/fuzz/fuzzer_pass_permute_blocks.h index e40178e6..39326c2b 100644 --- a/source/fuzz/fuzzer_pass_permute_blocks.h +++ b/source/fuzz/fuzzer_pass_permute_blocks.h @@ -27,7 +27,8 @@ class FuzzerPassPermuteBlocks : public FuzzerPass { FuzzerPassPermuteBlocks(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_permute_function_parameters.cpp b/source/fuzz/fuzzer_pass_permute_function_parameters.cpp index 9a61bea0..a8035b90 100644 --- a/source/fuzz/fuzzer_pass_permute_function_parameters.cpp +++ b/source/fuzz/fuzzer_pass_permute_function_parameters.cpp @@ -28,9 +28,10 @@ namespace fuzz { FuzzerPassPermuteFunctionParameters::FuzzerPassPermuteFunctionParameters( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassPermuteFunctionParameters::Apply() { for (const auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_permute_function_parameters.h b/source/fuzz/fuzzer_pass_permute_function_parameters.h index a4bf2ca5..c5b6ad43 100644 --- a/source/fuzz/fuzzer_pass_permute_function_parameters.h +++ b/source/fuzz/fuzzer_pass_permute_function_parameters.h @@ -32,7 +32,8 @@ class FuzzerPassPermuteFunctionParameters : public FuzzerPass { FuzzerPassPermuteFunctionParameters( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_permute_function_variables.cpp b/source/fuzz/fuzzer_pass_permute_function_variables.cpp new file mode 100644 index 00000000..f8b9b450 --- /dev/null +++ b/source/fuzz/fuzzer_pass_permute_function_variables.cpp @@ -0,0 +1,75 @@ +// Copyright (c) 2021 Mostafa Ashraf +// +// 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_permute_function_variables.h" + +#include +#include +#include + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_swap_function_variables.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassPermuteFunctionVariables::FuzzerPassPermuteFunctionVariables( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) { +} // Here we call parent constructor. + +void FuzzerPassPermuteFunctionVariables::Apply() { + // Permuting OpVariable instructions in each function. + for (auto& function : *GetIRContext()->module()) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfPermutingFunctionVariables())) { + continue; + } + + auto first_block = function.entry().get(); + + std::vector variables; + for (auto& instruction : *first_block) { + if (instruction.opcode() == SpvOpVariable) { + variables.push_back(&instruction); + } + } + if (variables.size() <= 1) { + continue; + } + do { + uint32_t instruction_1_index = GetFuzzerContext()->RandomIndex(variables); + uint32_t instruction_2_index = GetFuzzerContext()->RandomIndex(variables); + + if (instruction_1_index != instruction_2_index) { + ApplyTransformation(TransformationSwapFunctionVariables( + variables[instruction_1_index]->result_id(), + variables[instruction_2_index]->result_id())); + } + + } while (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfSwappingAnotherPairOfFunctionVariables()) && + variables.size() > 2); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/fuzzer_pass_permute_function_variables.h b/source/fuzz/fuzzer_pass_permute_function_variables.h new file mode 100644 index 00000000..2ebc15f2 --- /dev/null +++ b/source/fuzz/fuzzer_pass_permute_function_variables.h @@ -0,0 +1,38 @@ +// Copyright (c) 2021 Mostafa Ashraf +// +// 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_FUNCTION_VARIABLES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_VARIABLES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// This fuzzer pass permutes variables in functions in the module. +class FuzzerPassPermuteFunctionVariables : public FuzzerPass { + public: + FuzzerPassPermuteFunctionVariables( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_VARIABLES_H_ diff --git a/source/fuzz/fuzzer_pass_permute_instructions.cpp b/source/fuzz/fuzzer_pass_permute_instructions.cpp index f17e0187..3ef76be4 100644 --- a/source/fuzz/fuzzer_pass_permute_instructions.cpp +++ b/source/fuzz/fuzzer_pass_permute_instructions.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassPermuteInstructions::FuzzerPassPermuteInstructions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassPermuteInstructions::Apply() { // We are iterating over all instructions in all basic blocks. diff --git a/source/fuzz/fuzzer_pass_permute_instructions.h b/source/fuzz/fuzzer_pass_permute_instructions.h index 027101dd..b7ccbccb 100644 --- a/source/fuzz/fuzzer_pass_permute_instructions.h +++ b/source/fuzz/fuzzer_pass_permute_instructions.h @@ -27,7 +27,8 @@ class FuzzerPassPermuteInstructions : public FuzzerPass { FuzzerPassPermuteInstructions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_permute_phi_operands.cpp b/source/fuzz/fuzzer_pass_permute_phi_operands.cpp index f2cc5231..5fac9816 100644 --- a/source/fuzz/fuzzer_pass_permute_phi_operands.cpp +++ b/source/fuzz/fuzzer_pass_permute_phi_operands.cpp @@ -28,9 +28,10 @@ namespace fuzz { FuzzerPassPermutePhiOperands::FuzzerPassPermutePhiOperands( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassPermutePhiOperands::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_permute_phi_operands.h b/source/fuzz/fuzzer_pass_permute_phi_operands.h index 79999562..30a9d4f5 100644 --- a/source/fuzz/fuzzer_pass_permute_phi_operands.h +++ b/source/fuzz/fuzzer_pass_permute_phi_operands.h @@ -27,7 +27,8 @@ class FuzzerPassPermutePhiOperands : public FuzzerPass { FuzzerPassPermutePhiOperands( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp b/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp index af27a5da..4c46dcd3 100644 --- a/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp +++ b/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp @@ -23,16 +23,16 @@ namespace fuzz { FuzzerPassPropagateInstructionsDown::FuzzerPassPropagateInstructionsDown( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassPropagateInstructionsDown::Apply() { for (const auto& function : *GetIRContext()->module()) { std::vector reachable_blocks; for (const auto& block : function) { - if (GetIRContext()->GetDominatorAnalysis(&function)->IsReachable( - &block)) { + if (GetIRContext()->IsReachable(block)) { reachable_blocks.push_back(&block); } } diff --git a/source/fuzz/fuzzer_pass_propagate_instructions_down.h b/source/fuzz/fuzzer_pass_propagate_instructions_down.h index a2a0aac8..18f01654 100644 --- a/source/fuzz/fuzzer_pass_propagate_instructions_down.h +++ b/source/fuzz/fuzzer_pass_propagate_instructions_down.h @@ -26,7 +26,8 @@ class FuzzerPassPropagateInstructionsDown : public FuzzerPass { FuzzerPassPropagateInstructionsDown( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp b/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp index 8cd7437b..5e45da83 100644 --- a/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp +++ b/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassPropagateInstructionsUp::FuzzerPassPropagateInstructionsUp( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassPropagateInstructionsUp::Apply() { for (const auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_propagate_instructions_up.h b/source/fuzz/fuzzer_pass_propagate_instructions_up.h index b89be48d..0cb8396b 100644 --- a/source/fuzz/fuzzer_pass_propagate_instructions_up.h +++ b/source/fuzz/fuzzer_pass_propagate_instructions_up.h @@ -27,7 +27,8 @@ class FuzzerPassPropagateInstructionsUp : public FuzzerPass { FuzzerPassPropagateInstructionsUp( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp b/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp index 54e589c9..a6c07b4b 100644 --- a/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp +++ b/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassPushIdsThroughVariables::FuzzerPassPushIdsThroughVariables( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassPushIdsThroughVariables::Apply() { ForEachInstructionWithInstructionDescriptor( @@ -47,7 +48,7 @@ void FuzzerPassPushIdsThroughVariables::Apply() { // The block containing the instruction we are going to insert before // must be reachable. - if (!fuzzerutil::BlockIsReachableInItsFunction(GetIRContext(), block)) { + if (!GetIRContext()->IsReachable(*block)) { return; } @@ -69,6 +70,12 @@ void FuzzerPassPushIdsThroughVariables::Apply() { auto basic_type_ids_and_pointers = GetAvailableBasicTypesAndPointers(variable_storage_class); auto& basic_types = basic_type_ids_and_pointers.first; + + // There must be at least some basic types. + if (basic_types.empty()) { + return; + } + uint32_t basic_type_id = basic_types[GetFuzzerContext()->RandomIndex(basic_types)]; @@ -96,7 +103,7 @@ void FuzzerPassPushIdsThroughVariables::Apply() { ->IdIsIrrelevant(instruction->result_id()) && !fuzzerutil::CanMakeSynonymOf(ir_context, *GetTransformationContext(), - instruction)) { + *instruction)) { return false; } diff --git a/source/fuzz/fuzzer_pass_push_ids_through_variables.h b/source/fuzz/fuzzer_pass_push_ids_through_variables.h index 53008ee2..71b69492 100644 --- a/source/fuzz/fuzzer_pass_push_ids_through_variables.h +++ b/source/fuzz/fuzzer_pass_push_ids_through_variables.h @@ -28,7 +28,8 @@ class FuzzerPassPushIdsThroughVariables : public FuzzerPass { FuzzerPassPushIdsThroughVariables( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp index 8a83d3bc..467e6134 100644 --- a/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp +++ b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp @@ -29,9 +29,10 @@ FuzzerPassReplaceAddsSubsMulsWithCarryingExtended:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::Apply() { std::vector instructions_for_transformation; diff --git a/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h index 0e29a6c6..268655f9 100644 --- a/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h +++ b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h @@ -29,7 +29,8 @@ class FuzzerPassReplaceAddsSubsMulsWithCarryingExtended : public FuzzerPass { FuzzerPassReplaceAddsSubsMulsWithCarryingExtended( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp b/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp index a516f3d4..995657cc 100644 --- a/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp +++ b/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp @@ -28,9 +28,10 @@ FuzzerPassReplaceBranchesFromDeadBlocksWithExits:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceBranchesFromDeadBlocksWithExits::Apply() { // OpKill can only be used as a terminator in a function that is guaranteed diff --git a/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h b/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h index ab7e00e5..cdbb66f2 100644 --- a/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h +++ b/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h @@ -28,7 +28,8 @@ class FuzzerPassReplaceBranchesFromDeadBlocksWithExits : public FuzzerPass { FuzzerPassReplaceBranchesFromDeadBlocksWithExits( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp index f17339a9..af1aacee 100644 --- a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp +++ b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp @@ -26,9 +26,10 @@ FuzzerPassReplaceCopyMemoriesWithLoadsStores:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceCopyMemoriesWithLoadsStores::Apply() { GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) { diff --git a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h index cffe1cb9..7d954abe 100644 --- a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h +++ b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h @@ -28,7 +28,8 @@ class FuzzerPassReplaceCopyMemoriesWithLoadsStores : public FuzzerPass { FuzzerPassReplaceCopyMemoriesWithLoadsStores( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp b/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp index 24f2255b..d0992a3f 100644 --- a/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp +++ b/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp @@ -26,9 +26,10 @@ FuzzerPassReplaceCopyObjectsWithStoresLoads:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceCopyObjectsWithStoresLoads::Apply() { GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) { diff --git a/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h b/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h index e7b11ce1..2ffc00b6 100644 --- a/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h +++ b/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h @@ -28,7 +28,8 @@ class FuzzerPassReplaceCopyObjectsWithStoresLoads : public FuzzerPass { FuzzerPassReplaceCopyObjectsWithStoresLoads( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp index 7e9d7baa..4d55ae84 100644 --- a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp +++ b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp @@ -27,9 +27,10 @@ namespace fuzz { FuzzerPassReplaceIrrelevantIds::FuzzerPassReplaceIrrelevantIds( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceIrrelevantIds::Apply() { // Keep track of the irrelevant ids. This includes all the ids that are diff --git a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h index 1dc6b5d3..80f8eb86 100644 --- a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h +++ b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h @@ -28,7 +28,8 @@ class FuzzerPassReplaceIrrelevantIds : public FuzzerPass { FuzzerPassReplaceIrrelevantIds( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp b/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp index 0890c2fe..445dbfe1 100644 --- a/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp +++ b/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp @@ -26,9 +26,10 @@ FuzzerPassReplaceLinearAlgebraInstructions:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceLinearAlgebraInstructions::Apply() { // For each instruction, checks whether it is a linear algebra instruction. In diff --git a/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h b/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h index 5d2f2042..5734bf1f 100644 --- a/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h +++ b/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h @@ -27,7 +27,8 @@ class FuzzerPassReplaceLinearAlgebraInstructions : public FuzzerPass { FuzzerPassReplaceLinearAlgebraInstructions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp b/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp index f2cf80fa..38ac048b 100644 --- a/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp +++ b/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp @@ -27,9 +27,10 @@ FuzzerPassReplaceLoadsStoresWithCopyMemories:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceLoadsStoresWithCopyMemories::Apply() { // We look for matching pairs of instructions OpLoad and diff --git a/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h b/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h index f30fc2b7..f6209fc0 100644 --- a/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h +++ b/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h @@ -28,7 +28,8 @@ class FuzzerPassReplaceLoadsStoresWithCopyMemories : public FuzzerPass { FuzzerPassReplaceLoadsStoresWithCopyMemories( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp index b0a3d57c..ea90a7ac 100644 --- a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp +++ b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp @@ -24,9 +24,10 @@ FuzzerPassReplaceOpPhiIdsFromDeadPredecessors:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceOpPhiIdsFromDeadPredecessors::Apply() { // Keep a vector of the transformations to apply. diff --git a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h index a2bc1886..b01e242f 100644 --- a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h +++ b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h @@ -27,7 +27,8 @@ class FuzzerPassReplaceOpPhiIdsFromDeadPredecessors : public FuzzerPass { FuzzerPassReplaceOpPhiIdsFromDeadPredecessors( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp b/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp index 10bb90ad..72ed0936 100644 --- a/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp +++ b/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp @@ -27,9 +27,10 @@ FuzzerPassReplaceOpSelectsWithConditionalBranches:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceOpSelectsWithConditionalBranches::Apply() { // Keep track of the instructions that we want to replace. We need to collect diff --git a/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h b/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h index ec743890..174962ee 100644 --- a/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h +++ b/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h @@ -27,7 +27,8 @@ class FuzzerPassReplaceOpSelectsWithConditionalBranches : public FuzzerPass { FuzzerPassReplaceOpSelectsWithConditionalBranches( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp b/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp index 5c256bb0..7fb7b0d2 100644 --- a/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp +++ b/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp @@ -27,9 +27,10 @@ namespace fuzz { FuzzerPassReplaceParameterWithGlobal::FuzzerPassReplaceParameterWithGlobal( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceParameterWithGlobal::Apply() { for (const auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_replace_parameter_with_global.h b/source/fuzz/fuzzer_pass_replace_parameter_with_global.h index 2ae49469..4eb50866 100644 --- a/source/fuzz/fuzzer_pass_replace_parameter_with_global.h +++ b/source/fuzz/fuzzer_pass_replace_parameter_with_global.h @@ -27,7 +27,8 @@ class FuzzerPassReplaceParameterWithGlobal : public FuzzerPass { FuzzerPassReplaceParameterWithGlobal( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp b/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp index c045e19f..f029316e 100644 --- a/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp +++ b/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp @@ -27,9 +27,10 @@ namespace fuzz { FuzzerPassReplaceParamsWithStruct::FuzzerPassReplaceParamsWithStruct( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceParamsWithStruct::Apply() { for (const auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_replace_params_with_struct.h b/source/fuzz/fuzzer_pass_replace_params_with_struct.h index f17f5207..3af7367a 100644 --- a/source/fuzz/fuzzer_pass_replace_params_with_struct.h +++ b/source/fuzz/fuzzer_pass_replace_params_with_struct.h @@ -27,7 +27,8 @@ class FuzzerPassReplaceParamsWithStruct : public FuzzerPass { FuzzerPassReplaceParamsWithStruct( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_split_blocks.cpp b/source/fuzz/fuzzer_pass_split_blocks.cpp index 7b493559..40a4151d 100644 --- a/source/fuzz/fuzzer_pass_split_blocks.cpp +++ b/source/fuzz/fuzzer_pass_split_blocks.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassSplitBlocks::FuzzerPassSplitBlocks( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassSplitBlocks::Apply() { // Gather up pointers to all the blocks in the module. We are then able to diff --git a/source/fuzz/fuzzer_pass_split_blocks.h b/source/fuzz/fuzzer_pass_split_blocks.h index 58f10ddb..b1b94240 100644 --- a/source/fuzz/fuzzer_pass_split_blocks.h +++ b/source/fuzz/fuzzer_pass_split_blocks.h @@ -27,7 +27,8 @@ class FuzzerPassSplitBlocks : public FuzzerPass { FuzzerPassSplitBlocks(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp b/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp index 27fadd17..dce65f08 100644 --- a/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp +++ b/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassSwapCommutableOperands::FuzzerPassSwapCommutableOperands( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassSwapCommutableOperands::Apply() { auto context = GetIRContext(); diff --git a/source/fuzz/fuzzer_pass_swap_commutable_operands.h b/source/fuzz/fuzzer_pass_swap_commutable_operands.h index 93de1728..13d8fb6e 100644 --- a/source/fuzz/fuzzer_pass_swap_commutable_operands.h +++ b/source/fuzz/fuzzer_pass_swap_commutable_operands.h @@ -28,7 +28,8 @@ class FuzzerPassSwapCommutableOperands : public FuzzerPass { FuzzerPassSwapCommutableOperands( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp index b145b3bc..f8bf111d 100644 --- a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp +++ b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp @@ -27,9 +27,10 @@ FuzzerPassSwapBranchConditionalOperands:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassSwapBranchConditionalOperands::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h index 0137f38b..7f71f9b1 100644 --- a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h +++ b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h @@ -27,7 +27,8 @@ class FuzzerPassSwapBranchConditionalOperands : public FuzzerPass { FuzzerPassSwapBranchConditionalOperands( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_swap_functions.cpp b/source/fuzz/fuzzer_pass_swap_functions.cpp new file mode 100644 index 00000000..8eeec850 --- /dev/null +++ b/source/fuzz/fuzzer_pass_swap_functions.cpp @@ -0,0 +1,53 @@ +// Copyright (c) 2021 Shiyu Liu +// +// 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_functions.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/transformation_swap_two_functions.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassSwapFunctions::FuzzerPassSwapFunctions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassSwapFunctions::Apply() { + // Collect all function ids in a module. + std::vector function_ids; + for (auto& function : *GetIRContext()->module()) { + function_ids.emplace_back(function.result_id()); + } + + // Iterate through every combination of id i & j where i!=j. + for (size_t i = 0; i < function_ids.size(); ++i) { + for (size_t j = i + 1; j < function_ids.size(); ++j) { + // Perform function swap randomly. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfSwappingFunctions())) { + continue; + } + TransformationSwapTwoFunctions transformation(function_ids[i], + function_ids[j]); + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/fuzzer_pass_swap_functions.h b/source/fuzz/fuzzer_pass_swap_functions.h new file mode 100644 index 00000000..7af527f0 --- /dev/null +++ b/source/fuzz/fuzzer_pass_swap_functions.h @@ -0,0 +1,38 @@ +// Copyright (c) 2021 Shiyu Liu +// +// 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_FUNCTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_SWAP_FUNCTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly swap functions within a module. +class FuzzerPassSwapFunctions : public FuzzerPass { + public: + FuzzerPassSwapFunctions(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_SWAP_FUNCTIONS_H_ diff --git a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp index e5afd9ee..ac2b1565 100644 --- a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp +++ b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassToggleAccessChainInstruction::FuzzerPassToggleAccessChainInstruction( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassToggleAccessChainInstruction::Apply() { auto context = GetIRContext(); diff --git a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h index ff2f5d45..f0b6166b 100644 --- a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h +++ b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h @@ -27,7 +27,8 @@ class FuzzerPassToggleAccessChainInstruction : public FuzzerPass { FuzzerPassToggleAccessChainInstruction( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp b/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp index 66bbcd81..3a3b12c1 100644 --- a/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp +++ b/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassWrapRegionsInSelections::FuzzerPassWrapRegionsInSelections( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassWrapRegionsInSelections::Apply() { for (auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h b/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h index 822c308f..fc3d7df9 100644 --- a/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h +++ b/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h @@ -27,7 +27,8 @@ class FuzzerPassWrapRegionsInSelections : public FuzzerPass { FuzzerPassWrapRegionsInSelections( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp b/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp new file mode 100644 index 00000000..35adcfec --- /dev/null +++ b/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp @@ -0,0 +1,144 @@ +// Copyright (c) 2021 Shiyu Liu +// +// 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_wrap_vector_synonym.h" +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_composite_construct.h" +#include "source/fuzz/transformation_wrap_vector_synonym.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassWrapVectorSynonym::FuzzerPassWrapVectorSynonym( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassWrapVectorSynonym::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/, + opt::BasicBlock::iterator instruction_iterator, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + + // Randomly decide whether to wrap it to a vector operation. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfWrappingVectorSynonym())) { + return; + } + + // The transformation is not applicable if the instruction has missing + // result id, type id, or is not supported type. + if (!TransformationWrapVectorSynonym::IsInstructionSupported( + GetIRContext(), *instruction_iterator)) { + return; + } + + // It must be valid to insert an OpCompositeConstruct instruction + // before |instruction_iterator|. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpCompositeConstruct, instruction_iterator)) { + return; + } + + // Get the scalar operands from the original instruction. + opt::Instruction* operand1 = GetIRContext()->get_def_use_mgr()->GetDef( + instruction_iterator->GetSingleWordInOperand(0)); + opt::Instruction* operand2 = GetIRContext()->get_def_use_mgr()->GetDef( + instruction_iterator->GetSingleWordInOperand(1)); + + // We need to be able to make a synonym of the scalar operation's result + // id, as well as the operand ids (for example, they cannot be + // irrelevant). + if (!fuzzerutil::CanMakeSynonymOf(GetIRContext(), + *GetTransformationContext(), + *instruction_iterator)) { + return; + } + if (!fuzzerutil::CanMakeSynonymOf( + GetIRContext(), *GetTransformationContext(), *operand1)) { + return; + } + if (!fuzzerutil::CanMakeSynonymOf( + GetIRContext(), *GetTransformationContext(), *operand2)) { + return; + } + + // Get a random vector size from 2 to 4. + uint32_t vector_size = GetFuzzerContext()->GetWidthOfWrappingVector(); + + // Randomly choose a position that target ids should be placed at. + // The position is in range [0, n - 1], where n is the size of the + // vector. + uint32_t position = + GetFuzzerContext()->GetRandomIndexForWrappingVector(vector_size); + + // Stores the ids of scalar constants. + std::vector vec1_components; + std::vector vec2_components; + + // Populate components based on vector type and size. + for (uint32_t i = 0; i < vector_size; ++i) { + if (i == position) { + vec1_components.emplace_back(operand1->result_id()); + vec2_components.emplace_back(operand2->result_id()); + } else { + vec1_components.emplace_back( + FindOrCreateZeroConstant(operand1->type_id(), true)); + vec2_components.emplace_back( + FindOrCreateZeroConstant(operand2->type_id(), true)); + } + } + + // Add two OpCompositeConstruct to the module with result id returned. + // The added vectors may have different types, for instance if the + // scalar instruction operates on integers with differing sign. + + // Add the first OpCompositeConstruct that wraps the id of the first + // operand. + uint32_t result_id1 = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationCompositeConstruct( + FindOrCreateVectorType(operand1->type_id(), vector_size), + vec1_components, instruction_descriptor, result_id1)); + + // Add the second OpCompositeConstruct that wraps the id of the second + // operand. + uint32_t result_id2 = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationCompositeConstruct( + FindOrCreateVectorType(operand2->type_id(), vector_size), + vec2_components, instruction_descriptor, result_id2)); + + // The result of the vector instruction that + // TransformationWrapVectorSynonym will create should be a vector of the + // right size, with the scalar instruction's result type as its element + // type. This can be distinct from the types of the operands, if the + // scalar instruction adds two signed integers and stores the result in + // an unsigned id, for example. A transformation is applied to add the + // right type to the module. + FindOrCreateVectorType(instruction_iterator->type_id(), vector_size); + + // Apply transformation to do vector operation and add synonym between + // the result vector id and the id of the original instruction. + ApplyTransformation(TransformationWrapVectorSynonym( + instruction_iterator->result_id(), result_id1, result_id2, + GetFuzzerContext()->GetFreshId(), position)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/fuzzer_pass_wrap_vector_synonym.h b/source/fuzz/fuzzer_pass_wrap_vector_synonym.h new file mode 100644 index 00000000..51458383 --- /dev/null +++ b/source/fuzz/fuzzer_pass_wrap_vector_synonym.h @@ -0,0 +1,38 @@ +// Copyright (c) 2021 Shiyu Liu +// +// 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_WRAP_VECTOR_SYNONYM_H_ +#define SOURCE_FUZZ_FUZZER_PASS_WRAP_VECTOR_SYNONYM_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly wrap a scalar operation into a vector operation. +class FuzzerPassWrapVectorSynonym : public FuzzerPass { + public: + FuzzerPassWrapVectorSynonym( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_WRAP_VECTOR_SYNONYM_H_ diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index aa0d30e2..1d368a9f 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -252,11 +252,11 @@ bool BlockIsBackEdge(opt::IRContext* context, uint32_t block_id, return false; } - // |block_id| must be reachable and be dominated by |loop_header|. + // |block| 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); + return context->IsReachable(*block) && + dominator_analysis->Dominates(loop_header, block); } bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id, @@ -284,13 +284,6 @@ opt::BasicBlock::iterator GetIteratorForInstruction( return block->end(); } -bool BlockIsReachableInItsFunction(opt::IRContext* context, - opt::BasicBlock* bb) { - auto enclosing_function = bb->GetParent(); - return context->GetDominatorAnalysis(enclosing_function) - ->Dominates(enclosing_function->entry().get(), bb); -} - bool CanInsertOpcodeBeforeInstruction( SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block) { if (instruction_in_block->PreviousNode() && @@ -312,34 +305,34 @@ bool CanInsertOpcodeBeforeInstruction( bool CanMakeSynonymOf(opt::IRContext* ir_context, const TransformationContext& transformation_context, - opt::Instruction* inst) { - if (inst->opcode() == SpvOpSampledImage) { + const 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 // instructions that are used for making synonyms. return false; } - if (!inst->HasResultId()) { + if (!inst.HasResultId()) { // We can only make a synonym of an instruction that generates an id. return false; } if (transformation_context.GetFactManager()->IdIsIrrelevant( - inst->result_id())) { + inst.result_id())) { // An irrelevant id can't be a synonym of anything. return false; } - if (!inst->type_id()) { + if (!inst.type_id()) { // We can only make a synonym of an instruction that has a type. return false; } - auto type_inst = ir_context->get_def_use_mgr()->GetDef(inst->type_id()); + auto type_inst = ir_context->get_def_use_mgr()->GetDef(inst.type_id()); if (type_inst->opcode() == SpvOpTypeVoid) { // We only make synonyms of instructions that define objects, and an object // cannot have void type. return false; } if (type_inst->opcode() == SpvOpTypePointer) { - switch (inst->opcode()) { + switch (inst.opcode()) { case SpvOpConstantNull: case SpvOpUndef: // We disallow making synonyms of null or undefined pointers. This is @@ -355,7 +348,7 @@ bool CanMakeSynonymOf(opt::IRContext* ir_context, // not decorated analogously, using the original object vs. its synonymous // form may not be equivalent. return ir_context->get_decoration_mgr() - ->GetDecorationsFor(inst->result_id(), true) + ->GetDecorationsFor(inst.result_id(), true) .empty(); } @@ -462,6 +455,30 @@ uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst, } } +SpvMemorySemanticsMask GetMemorySemanticsForStorageClass( + SpvStorageClass storage_class) { + switch (storage_class) { + case SpvStorageClassWorkgroup: + return SpvMemorySemanticsWorkgroupMemoryMask; + + case SpvStorageClassStorageBuffer: + case SpvStorageClassPhysicalStorageBuffer: + return SpvMemorySemanticsUniformMemoryMask; + + case SpvStorageClassCrossWorkgroup: + return SpvMemorySemanticsCrossWorkgroupMemoryMask; + + case SpvStorageClassAtomicCounter: + return SpvMemorySemanticsAtomicCounterMemoryMask; + + case SpvStorageClassImage: + return SpvMemorySemanticsImageMemoryMask; + + default: + return SpvMemorySemanticsMaskNone; + } +} + bool IsValid(const opt::IRContext* context, spv_validator_options validator_options, MessageConsumer consumer) { @@ -509,8 +526,12 @@ bool IsValidAndWellFormed(const opt::IRContext* ir_context, // this is a useful aid to debugging. std::unordered_map unique_ids; bool found_duplicate = false; - ir_context->module()->ForEachInst([&consumer, &found_duplicate, + ir_context->module()->ForEachInst([&consumer, &found_duplicate, ir_context, &unique_ids](opt::Instruction* inst) { + (void)ir_context; // Only used in an assertion; keep release-mode compilers + // happy. + assert(inst->context() == ir_context && + "Instruction has wrong IR context."); if (unique_ids.count(inst->unique_id()) != 0) { consumer(SPV_MSG_INFO, nullptr, {}, "Two instructions have the same unique id (set a breakpoint to " @@ -660,13 +681,12 @@ bool IdIsAvailableAtUse(opt::IRContext* context, // It is not OK for a definition to use itself. return false; } - auto dominator_analysis = context->GetDominatorAnalysis(enclosing_function); - if (!dominator_analysis->IsReachable( - context->get_instr_block(use_instruction)) || - !dominator_analysis->IsReachable(context->get_instr_block(id))) { + if (!context->IsReachable(*context->get_instr_block(use_instruction)) || + !context->IsReachable(*context->get_instr_block(id))) { // Skip unreachable blocks. return false; } + auto dominator_analysis = context->GetDominatorAnalysis(enclosing_function); if (use_instruction->opcode() == SpvOpPhi) { // In the case where the use is an operand to OpPhi, it is actually the // *parent* block associated with the operand that must be dominated by @@ -704,8 +724,8 @@ bool IdIsAvailableBeforeInstruction(opt::IRContext* context, } const auto* dominator_analysis = context->GetDominatorAnalysis(function_enclosing_instruction); - if (dominator_analysis->IsReachable(context->get_instr_block(instruction)) && - dominator_analysis->IsReachable(context->get_instr_block(id)) && + if (context->IsReachable(*context->get_instr_block(instruction)) && + context->IsReachable(*context->get_instr_block(id)) && dominator_analysis->Dominates(id_definition, instruction)) { // The id's definition dominates the instruction, and both the definition // and the instruction are in reachable blocks, thus the id is available at @@ -715,8 +735,7 @@ bool IdIsAvailableBeforeInstruction(opt::IRContext* context, if (id_definition->opcode() == SpvOpVariable && function_enclosing_instruction == context->get_instr_block(id)->GetParent()) { - assert(!dominator_analysis->IsReachable( - context->get_instr_block(instruction)) && + assert(!context->IsReachable(*context->get_instr_block(instruction)) && "If the instruction were in a reachable block we should already " "have returned true."); // The id is a variable and it is in the same function as |instruction|. @@ -796,11 +815,38 @@ uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst, 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() || - type.AsStruct() || type.AsPointer() || type.AsEvent() || - type.AsDeviceEvent() || type.AsReserveId() || type.AsQueue(); +bool IsNullConstantSupported(opt::IRContext* ir_context, + const opt::Instruction& type_inst) { + switch (type_inst.opcode()) { + case SpvOpTypeArray: + case SpvOpTypeBool: + case SpvOpTypeDeviceEvent: + case SpvOpTypeEvent: + case SpvOpTypeFloat: + case SpvOpTypeInt: + case SpvOpTypeMatrix: + case SpvOpTypeQueue: + case SpvOpTypeReserveId: + case SpvOpTypeVector: + case SpvOpTypeStruct: + return true; + case SpvOpTypePointer: + // Null pointers are allowed if the VariablePointers capability is + // enabled, or if the VariablePointersStorageBuffer capability is enabled + // and the pointer type has StorageBuffer as its storage class. + if (ir_context->get_feature_mgr()->HasCapability( + SpvCapabilityVariablePointers)) { + return true; + } + if (ir_context->get_feature_mgr()->HasCapability( + SpvCapabilityVariablePointersStorageBuffer)) { + return type_inst.GetSingleWordInOperand(0) == + SpvStorageClassStorageBuffer; + } + return false; + default: + return false; + } } bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces( @@ -1625,6 +1671,44 @@ bool IdUseCanBeReplaced(opt::IRContext* ir_context, return false; } + if (ir_context->get_feature_mgr()->HasCapability(SpvCapabilityShader)) { + // With the Shader capability, memory scope and memory semantics operands + // are required to be constants, so they cannot be replaced arbitrarily. + switch (use_instruction->opcode()) { + case SpvOpAtomicLoad: + case SpvOpAtomicStore: + case SpvOpAtomicExchange: + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicIAdd: + case SpvOpAtomicISub: + case SpvOpAtomicSMin: + case SpvOpAtomicUMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMax: + case SpvOpAtomicAnd: + case SpvOpAtomicOr: + case SpvOpAtomicXor: + if (use_in_operand_index == 1 || use_in_operand_index == 2) { + return false; + } + break; + case SpvOpAtomicCompareExchange: + if (use_in_operand_index == 1 || use_in_operand_index == 2 || + use_in_operand_index == 3) { + return false; + } + break; + case SpvOpAtomicCompareExchangeWeak: + case SpvOpAtomicFlagTestAndSet: + case SpvOpAtomicFlagClear: + case SpvOpAtomicFAddEXT: + assert(false && "Not allowed with the Shader capability."); + default: + break; + } + } + return true; } @@ -1883,7 +1967,7 @@ bool NewTerminatorPreservesDominationRules(opt::IRContext* ir_context, // all its dependencies satisfy domination rules (i.e. all id operands // dominate that instruction). for (const auto& block : *mutated_block->GetParent()) { - if (!dominator_analysis.IsReachable(&block)) { + if (!ir_context->IsReachable(block)) { // If some block is not reachable then we don't need to worry about the // preservation of domination rules for its instructions. continue; @@ -1925,6 +2009,102 @@ bool NewTerminatorPreservesDominationRules(opt::IRContext* ir_context, return true; } +opt::Module::iterator GetFunctionIterator(opt::IRContext* ir_context, + uint32_t function_id) { + return std::find_if(ir_context->module()->begin(), + ir_context->module()->end(), + [function_id](const opt::Function& f) { + return f.result_id() == function_id; + }); +} + +// 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 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 SpvOpAtomicStore: + case SpvOpAtomicExchange: + case SpvOpAtomicIAdd: + case SpvOpAtomicISub: + case SpvOpAtomicSMin: + case SpvOpAtomicUMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMax: + case SpvOpAtomicAnd: + case SpvOpAtomicOr: + case SpvOpAtomicXor: + case SpvOpAtomicFAddEXT: // Capability AtomicFloat32AddEXT, + // AtomicFloat64AddEXT. + assert(use_in_operand_index != 0 && + "Signedness check should not occur on a pointer operand."); + return use_in_operand_index == 1 || use_in_operand_index == 2; + + case SpvOpAtomicCompareExchange: + case SpvOpAtomicCompareExchangeWeak: // Capability Kernel. + assert(use_in_operand_index != 0 && + "Signedness check should not occur on a pointer operand."); + return use_in_operand_index >= 1 && use_in_operand_index <= 3; + + case SpvOpAtomicLoad: + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicFlagTestAndSet: // Capability Kernel. + case SpvOpAtomicFlagClear: // Capability Kernel. + assert(use_in_operand_index != 0 && + "Signedness check should not occur on a pointer operand."); + return use_in_operand_index >= 1; + + 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; + } +} + +bool TypesAreCompatible(opt::IRContext* ir_context, SpvOp opcode, + uint32_t use_in_operand_index, uint32_t type_id_1, + uint32_t type_id_2) { + assert(ir_context->get_type_mgr()->GetType(type_id_1) && + ir_context->get_type_mgr()->GetType(type_id_2) && + "Type ids are invalid"); + + return type_id_1 == type_id_2 || + (IsAgnosticToSignednessOfOperand(opcode, use_in_operand_index) && + fuzzerutil::TypesAreEqualUpToSign(ir_context, type_id_1, type_id_2)); +} + } // namespace fuzzerutil } // namespace fuzz } // namespace spvtools diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h index fcbf1c65..54aa14a2 100644 --- a/source/fuzz/fuzzer_util.h +++ b/source/fuzz/fuzzer_util.h @@ -24,6 +24,7 @@ #include "source/opt/basic_block.h" #include "source/opt/instruction.h" #include "source/opt/ir_context.h" +#include "source/opt/module.h" #include "spirv-tools/libspirv.hpp" namespace spvtools { @@ -107,11 +108,6 @@ bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id, opt::BasicBlock::iterator GetIteratorForInstruction( opt::BasicBlock* block, const opt::Instruction* inst); -// Returns true if and only if there is a path to |bb| from the entry block of -// the function that contains |bb|. -bool BlockIsReachableInItsFunction(opt::IRContext* context, - opt::BasicBlock* bb); - // Determines whether it is OK to insert an instruction with opcode |opcode| // before |instruction_in_block|. bool CanInsertOpcodeBeforeInstruction( @@ -122,7 +118,7 @@ bool CanInsertOpcodeBeforeInstruction( // does not participate in IdIsIrrelevant fact. bool CanMakeSynonymOf(opt::IRContext* ir_context, const TransformationContext& transformation_context, - opt::Instruction* inst); + const opt::Instruction& inst); // Determines whether the given type is a composite; that is: an array, matrix, // struct or vector. @@ -173,6 +169,10 @@ uint32_t GetArraySize(const opt::Instruction& array_type_instruction, uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst, opt::IRContext* ir_context); +// Returns memory semantics mask for specific storage class. +SpvMemorySemanticsMask GetMemorySemanticsForStorageClass( + SpvStorageClass storage_class); + // Returns true if and only if |context| is valid, according to the validator // instantiated with |validator_options|. |consumer| is used for error // reporting. @@ -277,8 +277,10 @@ 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); +// to have an OpConstantNull value. This may depend on the capabilities declared +// in |context|. +bool IsNullConstantSupported(opt::IRContext* context, + const opt::Instruction& 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 @@ -596,6 +598,27 @@ bool NewTerminatorPreservesDominationRules(opt::IRContext* ir_context, uint32_t block_id, opt::Instruction new_terminator); +// Return the iterator that points to the function with the corresponding +// function id. If the function is not found, return the pointer pointing to +// module()->end(). +opt::Module::iterator GetFunctionIterator(opt::IRContext* ir_context, + uint32_t function_id); + +// 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. +bool IsAgnosticToSignednessOfOperand(SpvOp opcode, + uint32_t use_in_operand_index); + +// Returns true if |type_id_1| and |type_id_2| represent compatible types +// given the context of the instruction with |opcode| (i.e. we can replace +// an operand of |opcode| of the first type with an id of the second type +// and vice-versa). +bool TypesAreCompatible(opt::IRContext* ir_context, SpvOp opcode, + uint32_t use_in_operand_index, uint32_t type_id_1, + uint32_t type_id_2); + } // namespace fuzzerutil } // namespace fuzz } // namespace spvtools diff --git a/source/fuzz/pass_management/repeated_pass_instances.h b/source/fuzz/pass_management/repeated_pass_instances.h index 80ac0875..da61fdab 100644 --- a/source/fuzz/pass_management/repeated_pass_instances.h +++ b/source/fuzz/pass_management/repeated_pass_instances.h @@ -73,6 +73,7 @@ #include "source/fuzz/fuzzer_pass_split_blocks.h" #include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h" #include "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h" +#include "source/fuzz/fuzzer_pass_wrap_vector_synonym.h" namespace spvtools { namespace fuzz { @@ -168,6 +169,7 @@ class RepeatedPassInstances { REPEATED_PASS_INSTANCE(SplitBlocks); REPEATED_PASS_INSTANCE(SwapBranchConditionalOperands); REPEATED_PASS_INSTANCE(WrapRegionsInSelections); + REPEATED_PASS_INSTANCE(WrapVectorSynonym); #undef REPEATED_PASS_INSTANCE public: diff --git a/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp b/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp index a933848d..6c61c0d4 100644 --- a/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp +++ b/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp @@ -351,6 +351,12 @@ RepeatedPassRecommenderStandard::GetFuturePassRecommendations( pass_instances_->GetReplaceIrrelevantIds(), pass_instances_->GetFlattenConditionalBranches()}); } + if (&pass == pass_instances_->GetWrapVectorSynonym()) { + // This transformation introduces synonym facts and irrelevant ids. + return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms(), + pass_instances_->GetReplaceIrrelevantIds()}); + } + assert(false && "Unreachable: every fuzzer pass should be dealt with."); return {}; } diff --git a/source/fuzz/protobufs/spirvfuzz_protobufs.h b/source/fuzz/protobufs/spirvfuzz_protobufs.h index eb8cb145..46c21881 100644 --- a/source/fuzz/protobufs/spirvfuzz_protobufs.h +++ b/source/fuzz/protobufs/spirvfuzz_protobufs.h @@ -23,8 +23,11 @@ #if defined(__clang__) #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-parameter" +#pragma clang diagnostic ignored "-Wunknown-warning-option" // Must come first +#pragma clang diagnostic ignored "-Wreserved-identifier" #pragma clang diagnostic ignored "-Wshadow" +#pragma clang diagnostic ignored "-Wsuggest-destructor-override" +#pragma clang diagnostic ignored "-Wunused-parameter" #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto index 770a2dd3..e71b6a3f 100644 --- a/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -557,6 +557,9 @@ message Transformation { TransformationWrapEarlyTerminatorInFunction wrap_early_terminator_in_function = 83; TransformationMergeFunctionReturns merge_function_returns = 84; TransformationExpandVectorReduction expand_vector_reduction = 85; + TransformationSwapFunctionVariables swap_function_variables = 86; + TransformationSwapTwoFunctions swap_two_functions = 87; + TransformationWrapVectorSynonym wrap_vector_synonym = 88; // Add additional option using the next available number. } } @@ -1134,6 +1137,14 @@ message TransformationAddSynonym { // New synonym is derived by applying OpLogicalAnd to |result_id| with the second // operand being 'true'. LOGICAL_AND = 5; + + // New synonym is derived by applying OpBitwiseOr to |result_id| with the second + // operand being 0 taken with the same bit length as |result_id| + BITWISE_OR = 6; + + // New synonym is derived by applying OpBitwiseXor to |result_id| with the second + // operand being 0 taken with the same bit length as |result_id| + BITWISE_XOR = 7; } // Type of the synonym to create. See SynonymType for more details. @@ -1558,17 +1569,26 @@ message TransformationInvertComparisonOperator { message TransformationLoad { - // Transformation that adds an OpLoad instruction from a pointer into an id. + // Transformation that adds an OpLoad or OpAtomicLoad instruction from a pointer into an id. - // The result of the load instruction + // The result of the load instruction. uint32 fresh_id = 1; - // The pointer to be loaded from + // The pointer to be loaded from. uint32 pointer_id = 2; + // True if and only if the load should be atomic. + bool is_atomic = 3; + + // The memory scope for the atomic load. Ignored unless |is_atomic| is true. + uint32 memory_scope_id = 4; + + // The memory semantics for the atomic load. Ignored unless |is_atomic| is true. + uint32 memory_semantics_id = 5; + // A descriptor for an instruction in a block before which the new OpLoad - // instruction should be inserted - InstructionDescriptor instruction_to_insert_before = 3; + // instruction should be inserted. + InstructionDescriptor instruction_to_insert_before = 6; } @@ -1632,6 +1652,9 @@ message TransformationMergeFunctionReturns { // A fresh id for the header of the new outer loop. uint32 outer_header_id = 2; + // A fresh id for an unreachable continue construct for the new outer loop. + uint32 unreachable_continue_id = 7; + // A fresh id for the new return block of the function, // i.e. the merge block of the new outer loop. uint32 outer_return_id = 3; @@ -1938,10 +1961,10 @@ message TransformationReplaceBooleanConstantWithConstantBinary { // A descriptor for the boolean constant id we would like to replace IdUseDescriptor id_use_descriptor = 1; - // Id for the constant to be used on the LHS of the comparision + // Id for the constant to be used on the LHS of the comparison uint32 lhs_id = 2; - // Id for the constant to be used on the RHS of the comparision + // Id for the constant to be used on the RHS of the comparison uint32 rhs_id = 3; // Opcode for binary operator @@ -2222,17 +2245,26 @@ message TransformationSplitBlock { message TransformationStore { - // Transformation that adds an OpStore instruction of an id to a pointer. + // Transformation that adds an OpStore or OpAtomicStore instruction of an id to a pointer. - // The pointer to be stored to + // The pointer to be stored to. uint32 pointer_id = 1; - // The value to be stored - uint32 value_id = 2; + // True if and only if the load should be atomic. + bool is_atomic = 2; + + // The memory scope for the atomic load. Ignored unless |is_atomic| is true. + uint32 memory_scope_id = 3; + + // The memory semantics for the atomic load. Ignored unless |is_atomic| is true. + uint32 memory_semantics_id = 4; + + // The value to be stored. + uint32 value_id = 5; // A descriptor for an instruction in a block before which the new OpStore - // instruction should be inserted - InstructionDescriptor instruction_to_insert_before = 3; + // instruction should be inserted. + InstructionDescriptor instruction_to_insert_before = 6; } @@ -2260,6 +2292,24 @@ message TransformationSwapConditionalBranchOperands { } +message TransformationSwapFunctionVariables { + // A transformation that swaps function variables + + // Result id of the first variable. + uint32 result_id1 = 1; + // Result id of the second variable. + uint32 result_id2 = 2; + +} + +message TransformationSwapTwoFunctions { + // A transformation that swaps the position of two functions within the same module. + + // the IDs for the two functions that are swapped. + uint32 function_id1 = 1; + uint32 function_id2 = 2; +} + message TransformationToggleAccessChainInstruction { // A transformation that toggles an access chain instruction. @@ -2340,3 +2390,39 @@ message TransformationWrapRegionInSelection { bool branch_condition = 3; } + +message TransformationWrapVectorSynonym { + // A transformation that wraps an arithmetic operation into a vector operation + // and get the result of the original operation from the corresponding index. + // For instance, for this transformation, an scalar operation between two scalars: + // define op ∈ {+, -, *} + // c = a op b + // + // requires the availability of two vectors: + // + // va = vector(..., a, ...) + // vb = vector(..., b, ...) + // + // where a and b are in the same position i in each of their corresponding vector + // and a is synonymous with va[i] and b is synonymous with vb[i]. + // + // The transformation then add an instruction vc = va op vb where c is synonymous + // with vc[i]. + + // The result if of the original scalar operation instruction. + uint32 instruction_id = 1; + + // The result id for the first vector that contains the first value of the scalar operation. + uint32 vector_operand1 = 2; + + // The result id for the second vector that contains the second value of the scalar operation. + uint32 vector_operand2 = 3; + + // A fresh id for the resulted vector from the addition of the first and second vector. + uint32 fresh_id = 4; + + // The position in the vector where the value of original instruction is located. Must be in + // the corresponding vector range. + uint32 scalar_position = 5; + +} diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp index ebbc3937..70a302b7 100644 --- a/source/fuzz/transformation.cpp +++ b/source/fuzz/transformation.cpp @@ -98,10 +98,13 @@ #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_swap_function_variables.h" +#include "source/fuzz/transformation_swap_two_functions.h" #include "source/fuzz/transformation_toggle_access_chain_instruction.h" #include "source/fuzz/transformation_vector_shuffle.h" #include "source/fuzz/transformation_wrap_early_terminator_in_function.h" #include "source/fuzz/transformation_wrap_region_in_selection.h" +#include "source/fuzz/transformation_wrap_vector_synonym.h" #include "source/util/make_unique.h" namespace spvtools { @@ -361,6 +364,12 @@ std::unique_ptr Transformation::FromMessage( kSwapConditionalBranchOperands: return MakeUnique( message.swap_conditional_branch_operands()); + case protobufs::Transformation::TransformationCase::kSwapFunctionVariables: + return MakeUnique( + message.swap_function_variables()); + case protobufs::Transformation::TransformationCase::kSwapTwoFunctions: + return MakeUnique( + message.swap_two_functions()); case protobufs::Transformation::TransformationCase:: kToggleAccessChainInstruction: return MakeUnique( @@ -374,6 +383,9 @@ std::unique_ptr Transformation::FromMessage( case protobufs::Transformation::TransformationCase::kWrapRegionInSelection: return MakeUnique( message.wrap_region_in_selection()); + case protobufs::Transformation::TransformationCase::kWrapVectorSynonym: + return MakeUnique( + message.wrap_vector_synonym()); case protobufs::Transformation::TRANSFORMATION_NOT_SET: assert(false && "An unset transformation was encountered."); return nullptr; diff --git a/source/fuzz/transformation_access_chain.cpp b/source/fuzz/transformation_access_chain.cpp index 97d4e041..3fe9e656 100644 --- a/source/fuzz/transformation_access_chain.cpp +++ b/source/fuzz/transformation_access_chain.cpp @@ -122,7 +122,7 @@ bool TransformationAccessChain::IsApplicable( bool successful; std::tie(successful, index_value) = - GetIndexValue(ir_context, index_id, subobject_type_id); + GetStructIndexValue(ir_context, index_id, subobject_type_id); if (!successful) { return false; @@ -247,7 +247,7 @@ void TransformationAccessChain::Apply( // It is a struct: we need to retrieve the integer value. index_value = - GetIndexValue(ir_context, index_id, subobject_type_id).second; + GetStructIndexValue(ir_context, index_id, subobject_type_id).second; new_index_id = index_id; @@ -363,9 +363,12 @@ protobufs::Transformation TransformationAccessChain::ToMessage() const { return result; } -std::pair TransformationAccessChain::GetIndexValue( +std::pair TransformationAccessChain::GetStructIndexValue( opt::IRContext* ir_context, uint32_t index_id, uint32_t object_type_id) const { + assert(ir_context->get_def_use_mgr()->GetDef(object_type_id)->opcode() == + SpvOpTypeStruct && + "Precondition: the type must be a struct type."); if (!ValidIndexToComposite(ir_context, index_id, object_type_id)) { return {false, 0}; } @@ -374,10 +377,9 @@ std::pair TransformationAccessChain::GetIndexValue( 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}; - } + // Ensure that the index given must represent a constant. + assert(spvOpcodeIsConstant(index_instruction->opcode()) && + "A non-constant index should already have been rejected."); // The index must be in bounds. uint32_t value = index_instruction->GetSingleWordInOperand(0); diff --git a/source/fuzz/transformation_access_chain.h b/source/fuzz/transformation_access_chain.h index 5582de39..4e4fd2b6 100644 --- a/source/fuzz/transformation_access_chain.h +++ b/source/fuzz/transformation_access_chain.h @@ -83,13 +83,13 @@ class TransformationAccessChain : public Transformation { private: // 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 + // - |object_type_id| must be a struct 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 GetIndexValue(opt::IRContext* ir_context, - uint32_t index_id, - uint32_t object_type_id) const; + std::pair GetStructIndexValue(opt::IRContext* ir_context, + 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 diff --git a/source/fuzz/transformation_add_bit_instruction_synonym.cpp b/source/fuzz/transformation_add_bit_instruction_synonym.cpp index d232e13a..636c0a38 100644 --- a/source/fuzz/transformation_add_bit_instruction_synonym.cpp +++ b/source/fuzz/transformation_add_bit_instruction_synonym.cpp @@ -39,20 +39,9 @@ bool TransformationAddBitInstructionSynonym::IsApplicable( auto instruction = ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id()); - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557): - // Right now we only support certain operations. When this issue is addressed - // the following conditional can use the function |spvOpcodeIsBit|. - // |instruction| must be defined and must be a supported bit instruction. - if (!instruction || (instruction->opcode() != SpvOpBitwiseOr && - instruction->opcode() != SpvOpBitwiseXor && - instruction->opcode() != SpvOpBitwiseAnd && - instruction->opcode() != SpvOpNot)) { - return false; - } - - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3792): - // Right now, only integer operands are supported. - if (ir_context->get_type_mgr()->GetType(instruction->type_id())->AsVector()) { + // Checks on: only integer operands are supported, instructions are bitwise + // operations only. Signedness of the operands must be the same. + if (!IsInstructionSupported(ir_context, instruction)) { return false; } @@ -111,6 +100,65 @@ void TransformationAddBitInstructionSynonym::Apply( } } +bool TransformationAddBitInstructionSynonym::IsInstructionSupported( + opt::IRContext* ir_context, opt::Instruction* instruction) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557): + // Right now we only support certain operations. When this issue is addressed + // the following conditional can use the function |spvOpcodeIsBit|. + // |instruction| must be defined and must be a supported bit instruction. + if (!instruction || (instruction->opcode() != SpvOpBitwiseOr && + instruction->opcode() != SpvOpBitwiseXor && + instruction->opcode() != SpvOpBitwiseAnd && + instruction->opcode() != SpvOpNot)) { + return false; + } + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3792): + // Right now, only integer operands are supported. + if (ir_context->get_type_mgr()->GetType(instruction->type_id())->AsVector()) { + return false; + } + + if (instruction->opcode() == SpvOpNot) { + auto operand = instruction->GetInOperand(0).words[0]; + auto operand_inst = ir_context->get_def_use_mgr()->GetDef(operand); + auto operand_type = + ir_context->get_type_mgr()->GetType(operand_inst->type_id()); + auto operand_sign = operand_type->AsInteger()->IsSigned(); + + auto type_id_sign = ir_context->get_type_mgr() + ->GetType(instruction->type_id()) + ->AsInteger() + ->IsSigned(); + + return operand_sign == type_id_sign; + + } else { + // Other BitWise operations that takes two operands. + auto first_operand = instruction->GetInOperand(0).words[0]; + auto first_operand_inst = + ir_context->get_def_use_mgr()->GetDef(first_operand); + auto first_operand_type = + ir_context->get_type_mgr()->GetType(first_operand_inst->type_id()); + auto first_operand_sign = first_operand_type->AsInteger()->IsSigned(); + + auto second_operand = instruction->GetInOperand(1).words[0]; + auto second_operand_inst = + ir_context->get_def_use_mgr()->GetDef(second_operand); + auto second_operand_type = + ir_context->get_type_mgr()->GetType(second_operand_inst->type_id()); + auto second_operand_sign = second_operand_type->AsInteger()->IsSigned(); + + auto type_id_sign = ir_context->get_type_mgr() + ->GetType(instruction->type_id()) + ->AsInteger() + ->IsSigned(); + + return first_operand_sign == second_operand_sign && + first_operand_sign == type_id_sign; + } +} + protobufs::Transformation TransformationAddBitInstructionSynonym::ToMessage() const { protobufs::Transformation result; diff --git a/source/fuzz/transformation_add_bit_instruction_synonym.h b/source/fuzz/transformation_add_bit_instruction_synonym.h index ad9d32d9..40b947a2 100644 --- a/source/fuzz/transformation_add_bit_instruction_synonym.h +++ b/source/fuzz/transformation_add_bit_instruction_synonym.h @@ -128,6 +128,14 @@ class TransformationAddBitInstructionSynonym : public Transformation { static uint32_t GetRequiredFreshIdCount(opt::IRContext* ir_context, opt::Instruction* bit_instruction); + // Returns true if: + // - A |bit_instruction| is one of OpBitwiseOr, OpBitwiseAnd, OpBitwiseXor or + // OpNot. + // - |bit_instruction|'s operands are scalars. + // - The operands have the same signedness. + static bool IsInstructionSupported(opt::IRContext* ir_context, + opt::Instruction* instruction); + private: protobufs::TransformationAddBitInstructionSynonym message_; diff --git a/source/fuzz/transformation_add_constant_composite.cpp b/source/fuzz/transformation_add_constant_composite.cpp index e6cd5a96..89007ab6 100644 --- a/source/fuzz/transformation_add_constant_composite.cpp +++ b/source/fuzz/transformation_add_constant_composite.cpp @@ -75,7 +75,7 @@ bool TransformationAddConstantComposite::IsApplicable( // We do not create constants of structs decorated with Block nor // BufferBlock. The SPIR-V spec does not explicitly disallow this, but it // seems like a strange thing to do, so we disallow it to avoid triggering - // low priorty edge case issues related to it. + // low priority edge case issues related to it. if (fuzzerutil::HasBlockOrBufferBlockDecoration( ir_context, composite_type_instruction->result_id())) { return false; diff --git a/source/fuzz/transformation_add_constant_null.cpp b/source/fuzz/transformation_add_constant_null.cpp index 32544e6d..c0f73670 100644 --- a/source/fuzz/transformation_add_constant_null.cpp +++ b/source/fuzz/transformation_add_constant_null.cpp @@ -35,14 +35,14 @@ bool TransformationAddConstantNull::IsApplicable( if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { return false; } - auto type = context->get_type_mgr()->GetType(message_.type_id()); + auto type = context->get_def_use_mgr()->GetDef(message_.type_id()); // The type must exist. if (!type) { return false; } // The type must be one of the types for which null constants are allowed, // according to the SPIR-V spec. - return fuzzerutil::IsNullConstantSupported(*type); + return fuzzerutil::IsNullConstantSupported(context, *type); } void TransformationAddConstantNull::Apply( diff --git a/source/fuzz/transformation_add_dead_block.cpp b/source/fuzz/transformation_add_dead_block.cpp index 82e8cd8f..df700ce5 100644 --- a/source/fuzz/transformation_add_dead_block.cpp +++ b/source/fuzz/transformation_add_dead_block.cpp @@ -79,9 +79,7 @@ bool TransformationAddDeadBlock::IsApplicable( } // |existing_block| must be reachable. - opt::DominatorAnalysis* dominator_analysis = - ir_context->GetDominatorAnalysis(existing_block->GetParent()); - if (!dominator_analysis->IsReachable(existing_block->id())) { + if (!ir_context->IsReachable(*existing_block)) { return false; } @@ -94,6 +92,8 @@ bool TransformationAddDeadBlock::IsApplicable( // the selection construct, its header |existing_block| will not dominate the // merge block |successor_block_id|, which is invalid. Thus, |existing_block| // must dominate |successor_block_id|. + opt::DominatorAnalysis* dominator_analysis = + ir_context->GetDominatorAnalysis(existing_block->GetParent()); if (!dominator_analysis->Dominates(existing_block->id(), successor_block_id)) { return false; diff --git a/source/fuzz/transformation_add_dead_break.cpp b/source/fuzz/transformation_add_dead_break.cpp index ad46ce7f..32080ca4 100644 --- a/source/fuzz/transformation_add_dead_break.cpp +++ b/source/fuzz/transformation_add_dead_break.cpp @@ -134,7 +134,7 @@ bool TransformationAddDeadBreak::IsApplicable( return false; } - if (!fuzzerutil::BlockIsReachableInItsFunction(ir_context, bb_to)) { + if (!ir_context->IsReachable(*bb_to)) { // If the target of the break is unreachable, we conservatively do not // allow adding a dead break, to avoid the compilations that arise due to // the lack of sensible dominance information for unreachable blocks. diff --git a/source/fuzz/transformation_add_dead_continue.cpp b/source/fuzz/transformation_add_dead_continue.cpp index be6294e8..f2b9ab3f 100644 --- a/source/fuzz/transformation_add_dead_continue.cpp +++ b/source/fuzz/transformation_add_dead_continue.cpp @@ -83,8 +83,7 @@ bool TransformationAddDeadContinue::IsApplicable( auto continue_block = ir_context->cfg()->block(loop_header)->ContinueBlockId(); - if (!fuzzerutil::BlockIsReachableInItsFunction( - ir_context, ir_context->cfg()->block(continue_block))) { + if (!ir_context->IsReachable(*ir_context->cfg()->block(continue_block))) { // If the loop's continue block is unreachable, we conservatively do not // allow adding a dead continue, to avoid the compilations that arise due to // the lack of sensible dominance information for unreachable blocks. diff --git a/source/fuzz/transformation_add_global_undef.cpp b/source/fuzz/transformation_add_global_undef.cpp index eb390ea0..ec0574a4 100644 --- a/source/fuzz/transformation_add_global_undef.cpp +++ b/source/fuzz/transformation_add_global_undef.cpp @@ -15,6 +15,7 @@ #include "source/fuzz/transformation_add_global_undef.h" #include "source/fuzz/fuzzer_util.h" +#include "source/opt/reflect.h" namespace spvtools { namespace fuzz { @@ -35,9 +36,11 @@ bool TransformationAddGlobalUndef::IsApplicable( if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } - auto type = ir_context->get_type_mgr()->GetType(message_.type_id()); - // The type must exist, and must not be a function type. - return type && !type->AsFunction(); + auto type = ir_context->get_def_use_mgr()->GetDef(message_.type_id()); + // The type must exist, and must not be a function or pointer type. + return type != nullptr && opt::IsTypeInst(type->opcode()) && + type->opcode() != SpvOpTypeFunction && + type->opcode() != SpvOpTypePointer; } void TransformationAddGlobalUndef::Apply( diff --git a/source/fuzz/transformation_add_global_undef.h b/source/fuzz/transformation_add_global_undef.h index 37542c3f..fff1ad9d 100644 --- a/source/fuzz/transformation_add_global_undef.h +++ b/source/fuzz/transformation_add_global_undef.h @@ -31,7 +31,7 @@ class TransformationAddGlobalUndef : public Transformation { TransformationAddGlobalUndef(uint32_t fresh_id, uint32_t type_id); // - |message_.fresh_id| must be fresh - // - |message_.type_id| must be the id of a non-function type + // - |message_.type_id| must be the id of a non-function, non-pointer type bool IsApplicable( opt::IRContext* ir_context, const TransformationContext& transformation_context) const override; diff --git a/source/fuzz/transformation_add_synonym.cpp b/source/fuzz/transformation_add_synonym.cpp index 40768e2e..69269e5e 100644 --- a/source/fuzz/transformation_add_synonym.cpp +++ b/source/fuzz/transformation_add_synonym.cpp @@ -151,7 +151,8 @@ bool TransformationAddSynonym::IsInstructionValid( return false; } - if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, inst)) { + if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, + *inst)) { return false; } @@ -170,6 +171,18 @@ bool TransformationAddSynonym::IsInstructionValid( return type->AsInteger() || type->AsFloat(); } + case protobufs::TransformationAddSynonym::BITWISE_OR: + case protobufs::TransformationAddSynonym::BITWISE_XOR: { + // The instruction must be either an integer or a vector of integers. + 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(); + } + + return type->AsInteger(); + } case protobufs::TransformationAddSynonym::COPY_OBJECT: // All checks for OpCopyObject are handled by // fuzzerutil::CanMakeSynonymOf. @@ -195,68 +208,56 @@ TransformationAddSynonym::MakeSynonymousInstruction( auto synonym_type_id = fuzzerutil::GetTypeId(ir_context, message_.result_id()); assert(synonym_type_id && "Synonym has invalid type id"); + auto opcode = SpvOpNop; + const auto* synonym_type = + ir_context->get_type_mgr()->GetType(synonym_type_id); + assert(synonym_type && "Synonym has invalid type"); + + auto is_integral = (synonym_type->AsVector() && + synonym_type->AsVector()->element_type()->AsInteger()) || + synonym_type->AsInteger(); switch (message_.synonym_type()) { case protobufs::TransformationAddSynonym::SUB_ZERO: + opcode = is_integral ? SpvOpISub : SpvOpFSub; + break; 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"); + opcode = is_integral ? SpvOpIMul : SpvOpFMul; + break; + case protobufs::TransformationAddSynonym::ADD_ZERO: + opcode = is_integral ? SpvOpIAdd : SpvOpFAdd; + break; + case protobufs::TransformationAddSynonym::LOGICAL_OR: + opcode = SpvOpLogicalOr; + break; + case protobufs::TransformationAddSynonym::LOGICAL_AND: + opcode = SpvOpLogicalAnd; + break; + case protobufs::TransformationAddSynonym::BITWISE_OR: + opcode = SpvOpBitwiseOr; + break; + case protobufs::TransformationAddSynonym::BITWISE_XOR: + opcode = SpvOpBitwiseXor; + break; - // 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( - 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( 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( - 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; } + + return MakeUnique( + 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)}}}); } uint32_t TransformationAddSynonym::MaybeGetConstantId( @@ -273,6 +274,8 @@ uint32_t TransformationAddSynonym::MaybeGetConstantId( case protobufs::TransformationAddSynonym::ADD_ZERO: case protobufs::TransformationAddSynonym::SUB_ZERO: case protobufs::TransformationAddSynonym::LOGICAL_OR: + case protobufs::TransformationAddSynonym::BITWISE_OR: + case protobufs::TransformationAddSynonym::BITWISE_XOR: return fuzzerutil::MaybeGetZeroConstant( ir_context, transformation_context, synonym_type_id, false); case protobufs::TransformationAddSynonym::MUL_ONE: @@ -319,6 +322,8 @@ bool TransformationAddSynonym::IsAdditionalConstantRequired( case protobufs::TransformationAddSynonym::LOGICAL_OR: case protobufs::TransformationAddSynonym::MUL_ONE: case protobufs::TransformationAddSynonym::LOGICAL_AND: + case protobufs::TransformationAddSynonym::BITWISE_OR: + case protobufs::TransformationAddSynonym::BITWISE_XOR: return true; default: return false; diff --git a/source/fuzz/transformation_composite_construct.cpp b/source/fuzz/transformation_composite_construct.cpp index 0cd2308b..2d8e5991 100644 --- a/source/fuzz/transformation_composite_construct.cpp +++ b/source/fuzz/transformation_composite_construct.cpp @@ -297,7 +297,7 @@ void TransformationCompositeConstruct::AddDataSynonymFacts( composite_type->AsVector() && component_type->AsVector(); if (!fuzzerutil::CanMakeSynonymOf( ir_context, *transformation_context, - ir_context->get_def_use_mgr()->GetDef(component))) { + *ir_context->get_def_use_mgr()->GetDef(component))) { // We can't make a synonym of this component, so we skip on to the next // component. In the case where we're packing a vector into a vector we // have to skip as many components of the resulting vectors as there are diff --git a/source/fuzz/transformation_composite_extract.cpp b/source/fuzz/transformation_composite_extract.cpp index 647cd74e..0fbd4e1b 100644 --- a/source/fuzz/transformation_composite_extract.cpp +++ b/source/fuzz/transformation_composite_extract.cpp @@ -125,7 +125,7 @@ void TransformationCompositeExtract::AddDataSynonymFacts( // or if the result id into which we are extracting is irrelevant. if (!fuzzerutil::CanMakeSynonymOf( ir_context, *transformation_context, - ir_context->get_def_use_mgr()->GetDef(message_.composite_id())) || + *ir_context->get_def_use_mgr()->GetDef(message_.composite_id())) || transformation_context->GetFactManager()->IdIsIrrelevant( message_.fresh_id())) { return; diff --git a/source/fuzz/transformation_composite_insert.cpp b/source/fuzz/transformation_composite_insert.cpp index 14c3a1b0..60fa5628 100644 --- a/source/fuzz/transformation_composite_insert.cpp +++ b/source/fuzz/transformation_composite_insert.cpp @@ -14,7 +14,6 @@ #include "transformation_composite_insert.h" -#include "source/fuzz/fuzzer_pass_add_composite_inserts.h" #include "source/fuzz/fuzzer_util.h" #include "source/fuzz/instruction_descriptor.h" @@ -220,9 +219,9 @@ void TransformationCompositeInsert::AddDataSynonymFacts( continue; } current_index.push_back(i); - if (fuzzerutil::CanMakeSynonymOf( - ir_context, *transformation_context, - ir_context->get_def_use_mgr()->GetDef(message_.composite_id()))) { + if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context, + *ir_context->get_def_use_mgr()->GetDef( + message_.composite_id()))) { transformation_context->GetFactManager()->AddFactDataSynonym( MakeDataDescriptor(message_.fresh_id(), current_index), MakeDataDescriptor(message_.composite_id(), current_index)); @@ -236,7 +235,7 @@ void TransformationCompositeInsert::AddDataSynonymFacts( // synonymous with the result of the insert instruction at the given index. if (fuzzerutil::CanMakeSynonymOf( ir_context, *transformation_context, - ir_context->get_def_use_mgr()->GetDef(message_.object_id()))) { + *ir_context->get_def_use_mgr()->GetDef(message_.object_id()))) { transformation_context->GetFactManager()->AddFactDataSynonym( MakeDataDescriptor(message_.object_id(), {}), MakeDataDescriptor(message_.fresh_id(), index)); diff --git a/source/fuzz/transformation_duplicate_region_with_selection.cpp b/source/fuzz/transformation_duplicate_region_with_selection.cpp index dee1207f..db88610f 100644 --- a/source/fuzz/transformation_duplicate_region_with_selection.cpp +++ b/source/fuzz/transformation_duplicate_region_with_selection.cpp @@ -309,9 +309,9 @@ void TransformationDuplicateRegionWithSelection::Apply( // Construct the merge block. std::unique_ptr merge_block = - MakeUnique(MakeUnique(opt::Instruction( + MakeUnique(MakeUnique( ir_context, SpvOpLabel, 0, message_.merge_label_fresh_id(), - opt::Instruction::OperandList()))); + opt::Instruction::OperandList())); // Get the maps from the protobuf. std::map original_label_to_duplicate_label = @@ -325,7 +325,7 @@ void TransformationDuplicateRegionWithSelection::Apply( std::map original_id_to_phi_id = fuzzerutil::RepeatedUInt32PairToMap(message_.original_id_to_phi_id()); - // Use oveflow ids to fill in any required ids that are missing from these + // Use overflow ids to fill in any required ids that are missing from these // maps. for (auto block : region_blocks) { if (original_label_to_duplicate_label.count(block->id()) == 0) { diff --git a/source/fuzz/transformation_expand_vector_reduction.cpp b/source/fuzz/transformation_expand_vector_reduction.cpp index 99387066..bafcf929 100644 --- a/source/fuzz/transformation_expand_vector_reduction.cpp +++ b/source/fuzz/transformation_expand_vector_reduction.cpp @@ -129,7 +129,7 @@ void TransformationExpandVectorReduction::Apply( // If it's possible to make a synonym of |instruction|, then add the fact that // the last |logical_instruction| is a synonym of |instruction|. if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context, - instruction)) { + *instruction)) { transformation_context->GetFactManager()->AddFactDataSynonym( MakeDataDescriptor(logical_instruction.result_id(), {}), MakeDataDescriptor(instruction->result_id(), {})); diff --git a/source/fuzz/transformation_flatten_conditional_branch.cpp b/source/fuzz/transformation_flatten_conditional_branch.cpp index b8c6de0c..127e7628 100644 --- a/source/fuzz/transformation_flatten_conditional_branch.cpp +++ b/source/fuzz/transformation_flatten_conditional_branch.cpp @@ -441,17 +441,17 @@ bool TransformationFlattenConditionalBranch:: header->terminator()->opcode() == SpvOpBranchConditional && "|header| must be the header of a conditional."); + // |header| must be reachable. + if (!ir_context->IsReachable(*header)) { + return false; + } + auto enclosing_function = header->GetParent(); auto dominator_analysis = ir_context->GetDominatorAnalysis(enclosing_function); auto postdominator_analysis = ir_context->GetPostDominatorAnalysis(enclosing_function); - // |header| must be reachable. - if (!dominator_analysis->IsReachable(header)) { - return false; - } - // Check that the header and the merge block describe a single-entry, // single-exit region. if (!dominator_analysis->Dominates(header->id(), merge_block_id) || diff --git a/source/fuzz/transformation_load.cpp b/source/fuzz/transformation_load.cpp index e22f8dd2..bf48d996 100644 --- a/source/fuzz/transformation_load.cpp +++ b/source/fuzz/transformation_load.cpp @@ -24,10 +24,15 @@ TransformationLoad::TransformationLoad(protobufs::TransformationLoad message) : message_(std::move(message)) {} TransformationLoad::TransformationLoad( - uint32_t fresh_id, uint32_t pointer_id, + uint32_t fresh_id, uint32_t pointer_id, bool is_atomic, + uint32_t memory_scope, uint32_t memory_semantics, const protobufs::InstructionDescriptor& instruction_to_insert_before) { message_.set_fresh_id(fresh_id); message_.set_pointer_id(pointer_id); + message_.set_is_atomic(is_atomic); + message_.set_memory_scope_id(memory_scope); + message_.set_memory_semantics_id(memory_semantics); + *message_.mutable_instruction_to_insert_before() = instruction_to_insert_before; } @@ -68,11 +73,98 @@ bool TransformationLoad::IsApplicable( if (!insert_before) { return false; } - // ... and it must be legitimate to insert a store before it. - if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, insert_before)) { + // ... and it must be legitimate to insert a load before it. + if (!message_.is_atomic() && + !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, insert_before)) { return false; } + if (message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpAtomicLoad, insert_before)) { + return false; + } + + if (message_.is_atomic()) { + // Check the exists of memory scope and memory semantics ids. + auto memory_scope_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.memory_scope_id()); + auto memory_semantics_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.memory_semantics_id()); + + if (!memory_scope_instruction) { + return false; + } + if (!memory_semantics_instruction) { + return false; + } + // The memory scope and memory semantics instructions must have the + // 'OpConstant' opcode. + if (memory_scope_instruction->opcode() != SpvOpConstant) { + return false; + } + if (memory_semantics_instruction->opcode() != SpvOpConstant) { + return false; + } + // The memory scope and memory semantics need to be available before + // |insert_before|. + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before, message_.memory_scope_id())) { + return false; + } + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before, message_.memory_semantics_id())) { + return false; + } + // The memory scope and memory semantics instructions must have an Integer + // operand type with signedness does not matters. + if (ir_context->get_def_use_mgr() + ->GetDef(memory_scope_instruction->type_id()) + ->opcode() != SpvOpTypeInt) { + return false; + } + if (ir_context->get_def_use_mgr() + ->GetDef(memory_semantics_instruction->type_id()) + ->opcode() != SpvOpTypeInt) { + return false; + } + + // The size of the integer for memory scope and memory semantics + // instructions must be equal to 32 bits. + auto memory_scope_int_width = + ir_context->get_def_use_mgr() + ->GetDef(memory_scope_instruction->type_id()) + ->GetSingleWordInOperand(0); + auto memory_semantics_int_width = + ir_context->get_def_use_mgr() + ->GetDef(memory_semantics_instruction->type_id()) + ->GetSingleWordInOperand(0); + + if (memory_scope_int_width != 32) { + return false; + } + if (memory_semantics_int_width != 32) { + return false; + } + + // The memory scope constant value must be that of SpvScopeInvocation. + auto memory_scope_const_value = + memory_scope_instruction->GetSingleWordInOperand(0); + if (memory_scope_const_value != SpvScopeInvocation) { + return false; + } + + // The memory semantics constant value must match the storage class of the + // pointer being loaded from. + auto memory_semantics_const_value = static_cast( + memory_semantics_instruction->GetSingleWordInOperand(0)); + if (memory_semantics_const_value != + fuzzerutil::GetMemorySemanticsForStorageClass( + static_cast( + pointer_type->GetSingleWordInOperand(0)))) { + return false; + } + } + // The pointer needs to be available at the insertion point. return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, message_.pointer_id()); @@ -80,22 +172,46 @@ bool TransformationLoad::IsApplicable( void TransformationLoad::Apply(opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType( - ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id())); - fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - auto insert_before = - FindInstruction(message_.instruction_to_insert_before(), ir_context); - auto new_instruction = MakeUnique( - ir_context, SpvOpLoad, result_type, message_.fresh_id(), - opt::Instruction::OperandList( - {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}})); - auto new_instruction_ptr = new_instruction.get(); - insert_before->InsertBefore(std::move(new_instruction)); - // Inform the def-use manager about the new instruction and record its basic - // block. - ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); - ir_context->set_instr_block(new_instruction_ptr, - ir_context->get_instr_block(insert_before)); + if (message_.is_atomic()) { + // OpAtomicLoad instruction. + uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id())); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto new_instruction = MakeUnique( + ir_context, SpvOpAtomicLoad, result_type, message_.fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, + {SPV_OPERAND_TYPE_SCOPE_ID, {message_.memory_scope_id()}}, + {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, + {message_.memory_semantics_id()}}})); + auto new_instruction_ptr = new_instruction.get(); + insert_before->InsertBefore(std::move(new_instruction)); + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, + ir_context->get_instr_block(insert_before)); + } else { + // OpLoad instruction. + uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id())); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto new_instruction = MakeUnique( + ir_context, SpvOpLoad, result_type, message_.fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}})); + auto new_instruction_ptr = new_instruction.get(); + insert_before->InsertBefore(std::move(new_instruction)); + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, + ir_context->get_instr_block(insert_before)); + } } protobufs::Transformation TransformationLoad::ToMessage() const { diff --git a/source/fuzz/transformation_load.h b/source/fuzz/transformation_load.h index d10b0073..57b4a535 100644 --- a/source/fuzz/transformation_load.h +++ b/source/fuzz/transformation_load.h @@ -28,11 +28,20 @@ class TransformationLoad : public Transformation { explicit TransformationLoad(protobufs::TransformationLoad message); TransformationLoad( - uint32_t fresh_id, uint32_t pointer_id, + uint32_t fresh_id, uint32_t pointer_id, bool is_atomic, + uint32_t memory_scope, uint32_t memory_semantics, const protobufs::InstructionDescriptor& instruction_to_insert_before); // - |message_.fresh_id| must be fresh // - |message_.pointer_id| must be the id of a pointer + // - |message_.is_atomic| must be true if want to work with OpAtomicLoad + // - If |is_atomic| is true then |message_memory_scope_id| must be the id of + // an OpConstant 32 bit integer instruction with the value + // SpvScopeInvocation. + // - If |is_atomic| is true then |message_.memory_semantics_id| must be the id + // of an OpConstant 32 bit integer instruction with the values + // SpvMemorySemanticsWorkgroupMemoryMask or + // SpvMemorySemanticsUniformMemoryMask. // - The pointer must not be OpConstantNull or OpUndef // - |message_.instruction_to_insert_before| must identify an instruction // before which it is valid to insert an OpLoad, and where diff --git a/source/fuzz/transformation_merge_blocks.cpp b/source/fuzz/transformation_merge_blocks.cpp index 22236795..dbf782e2 100644 --- a/source/fuzz/transformation_merge_blocks.cpp +++ b/source/fuzz/transformation_merge_blocks.cpp @@ -43,6 +43,9 @@ bool TransformationMergeBlocks::IsApplicable( } auto first_block = ir_context->cfg()->block(predecessors.at(0)); + if (!ir_context->IsReachable(*first_block)) { + return false; + } return opt::blockmergeutil::CanMergeWithSuccessor(ir_context, first_block); } diff --git a/source/fuzz/transformation_merge_blocks.h b/source/fuzz/transformation_merge_blocks.h index f6306c5a..f3462fae 100644 --- a/source/fuzz/transformation_merge_blocks.h +++ b/source/fuzz/transformation_merge_blocks.h @@ -31,6 +31,7 @@ class TransformationMergeBlocks : public Transformation { TransformationMergeBlocks(uint32_t block_id); // - |message_.block_id| must be the id of a block, b + // - b must be statically reachable in the control flow graph of its function // - b must have a single predecessor, a // - b must be the sole successor of a // - Replacing a with the merge of a and b (and removing b) must lead to a diff --git a/source/fuzz/transformation_merge_function_returns.cpp b/source/fuzz/transformation_merge_function_returns.cpp index 1aefedc0..022e1b6d 100644 --- a/source/fuzz/transformation_merge_function_returns.cpp +++ b/source/fuzz/transformation_merge_function_returns.cpp @@ -25,11 +25,13 @@ TransformationMergeFunctionReturns::TransformationMergeFunctionReturns( : message_(std::move(message)) {} TransformationMergeFunctionReturns::TransformationMergeFunctionReturns( - uint32_t function_id, uint32_t outer_header_id, uint32_t outer_return_id, + uint32_t function_id, uint32_t outer_header_id, + uint32_t unreachable_continue_id, uint32_t outer_return_id, uint32_t return_val_id, uint32_t any_returnable_val_id, const std::vector& returns_merging_info) { message_.set_function_id(function_id); message_.set_outer_header_id(outer_header_id); + message_.set_unreachable_continue_id(unreachable_continue_id); message_.set_outer_return_id(outer_return_id); message_.set_return_val_id(return_val_id); message_.set_any_returnable_val_id(any_returnable_val_id); @@ -66,7 +68,9 @@ bool TransformationMergeFunctionReturns::IsApplicable( // Check that the fresh ids provided are fresh and distinct. std::set used_fresh_ids; - for (uint32_t id : {message_.outer_header_id(), message_.outer_return_id()}) { + for (uint32_t id : + {message_.outer_header_id(), message_.unreachable_continue_id(), + message_.outer_return_id()}) { if (!id || !CheckIdIsFreshAndNotUsedByThisTransformation(id, ir_context, &used_fresh_ids)) { return false; @@ -499,25 +503,20 @@ void TransformationMergeFunctionReturns::Apply( fuzzerutil::UpdateModuleIdBound(ir_context, message_.outer_header_id()); - // Add the instruction: OpLoopMerge %outer_return_id %outer_header_id None - // The header is the continue block of the outer loop. + // Add the instruction: + // OpLoopMerge %outer_return_id %unreachable_continue_id None outer_loop_header->AddInstruction(MakeUnique( ir_context, SpvOpLoopMerge, 0, 0, opt::Instruction::OperandList{ {SPV_OPERAND_TYPE_ID, {message_.outer_return_id()}}, - {SPV_OPERAND_TYPE_ID, {message_.outer_header_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.unreachable_continue_id()}}, {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}})); - // Add conditional branch: - // OpBranchConditional %true %block_after_entry %outer_header_id - // This will always branch to %block_after_entry, but it also creates a back - // edge for the loop (which is never traversed). + // Add unconditional branch to %block_after_entry. outer_loop_header->AddInstruction(MakeUnique( - ir_context, SpvOpBranchConditional, 0, 0, + ir_context, SpvOpBranch, 0, 0, opt::Instruction::OperandList{ - {SPV_OPERAND_TYPE_ID, {constant_true}}, - {SPV_OPERAND_TYPE_ID, {block_after_entry}}, - {SPV_OPERAND_TYPE_ID, {message_.outer_header_id()}}})); + {SPV_OPERAND_TYPE_ID, {block_after_entry}}})); // Insert the header right after the entry block. function->InsertBasicBlockAfter(std::move(outer_loop_header), @@ -581,6 +580,24 @@ void TransformationMergeFunctionReturns::Apply( // Insert the new return block at the end of the function. function->AddBasicBlock(std::move(outer_return_block)); + // Create the unreachable continue block associated with the enclosing loop. + auto unreachable_continue_block = + MakeUnique(MakeUnique( + ir_context, SpvOpLabel, 0, message_.unreachable_continue_id(), + opt::Instruction::OperandList())); + + fuzzerutil::UpdateModuleIdBound(ir_context, + message_.unreachable_continue_id()); + + // Insert an branch back to the loop header, to create a back edge. + unreachable_continue_block->AddInstruction(MakeUnique( + ir_context, SpvOpBranch, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.outer_header_id()}}})); + + // Insert the unreachable continue block at the end of the function. + function->AddBasicBlock(std::move(unreachable_continue_block)); + // All analyses must be invalidated because the structure of the module was // changed. ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); @@ -590,13 +607,14 @@ std::unordered_set TransformationMergeFunctionReturns::GetFreshIds() const { std::unordered_set result; result.emplace(message_.outer_header_id()); + result.emplace(message_.unreachable_continue_id()); result.emplace(message_.outer_return_id()); // |message_.return_val_info| can be 0 if the function is void. if (message_.return_val_id()) { result.emplace(message_.return_val_id()); } - for (auto merging_info : message_.return_merging_info()) { + for (const auto& merging_info : message_.return_merging_info()) { result.emplace(merging_info.is_returning_id()); // |maybe_return_val_id| can be 0 if the function is void. if (merging_info.maybe_return_val_id()) { diff --git a/source/fuzz/transformation_merge_function_returns.h b/source/fuzz/transformation_merge_function_returns.h index 8f0937c2..b3208acf 100644 --- a/source/fuzz/transformation_merge_function_returns.h +++ b/source/fuzz/transformation_merge_function_returns.h @@ -25,7 +25,8 @@ class TransformationMergeFunctionReturns : public Transformation { protobufs::TransformationMergeFunctionReturns message); TransformationMergeFunctionReturns( - uint32_t function_id, uint32_t outer_header_id, uint32_t outer_return_id, + uint32_t function_id, uint32_t outer_header_id, + uint32_t unreachable_continue_id, uint32_t outer_return_id, uint32_t return_val_id, uint32_t any_returnable_val_id, const std::vector& returns_merging_info); @@ -40,7 +41,7 @@ class TransformationMergeFunctionReturns : public Transformation { // statements, this id will be ignored. // - Merge blocks of reachable loops that contain return statements only // consist of OpLabel, OpPhi or OpBranch instructions. - // - The model contains OpConstantTrue and OpConstantFalse instructions. + // - The module contains OpConstantTrue and OpConstantFalse instructions. // - For all merge blocks of reachable loops that contain return statements, // either: // - a mapping is provided in |message_.return_merging_info|, all of the diff --git a/source/fuzz/transformation_outline_function.cpp b/source/fuzz/transformation_outline_function.cpp index 6ee91225..3140fa6b 100644 --- a/source/fuzz/transformation_outline_function.cpp +++ b/source/fuzz/transformation_outline_function.cpp @@ -175,6 +175,18 @@ bool TransformationOutlineFunction::IsApplicable( // This is achieved by going through every block in the function that contains // the region. for (auto& block : *entry_block->GetParent()) { + if (region_set.count(&block) != 0) { + // The block is in the region. Check that it does not have any unreachable + // predecessors. If it does, then we do not regard the region as single- + // entry-single-exit and hence do not outline it. + for (auto pred : ir_context->cfg()->preds(block.id())) { + if (!ir_context->IsReachable(*ir_context->cfg()->block(pred))) { + // The predecessor is unreachable. + return false; + } + } + } + if (&block == exit_block) { // It is OK (and typically expected) for the exit block of the region to // have successors outside the region. diff --git a/source/fuzz/transformation_permute_phi_operands.cpp b/source/fuzz/transformation_permute_phi_operands.cpp index 60563312..7ee7a82f 100644 --- a/source/fuzz/transformation_permute_phi_operands.cpp +++ b/source/fuzz/transformation_permute_phi_operands.cpp @@ -81,8 +81,7 @@ void TransformationPermutePhiOperands::Apply( inst->SetInOperands(std::move(permuted_operands)); // Update the def-use manager. - ir_context->get_def_use_mgr()->ClearInst(inst); - ir_context->get_def_use_mgr()->AnalyzeInstDefUse(inst); + ir_context->UpdateDefUse(inst); } protobufs::Transformation TransformationPermutePhiOperands::ToMessage() const { diff --git a/source/fuzz/transformation_propagate_instruction_down.cpp b/source/fuzz/transformation_propagate_instruction_down.cpp index 7713562e..c3b7c4d9 100644 --- a/source/fuzz/transformation_propagate_instruction_down.cpp +++ b/source/fuzz/transformation_propagate_instruction_down.cpp @@ -386,11 +386,8 @@ bool TransformationPropagateInstructionDown::IsApplicableToBlock( return false; } - const auto* dominator_analysis = - ir_context->GetDominatorAnalysis(block->GetParent()); - // |block| must be reachable. - if (!dominator_analysis->IsReachable(block)) { + if (!ir_context->IsReachable(*block)) { return false; } @@ -430,6 +427,9 @@ bool TransformationPropagateInstructionDown::IsApplicableToBlock( auto phi_block_id = GetOpPhiBlockId(ir_context, block_id, *inst_to_propagate, successor_ids); + const auto* dominator_analysis = + ir_context->GetDominatorAnalysis(block->GetParent()); + // Make sure we can adjust all users of the propagated instruction. return ir_context->get_def_use_mgr()->WhileEachUse( inst_to_propagate, @@ -537,7 +537,7 @@ uint32_t TransformationPropagateInstructionDown::GetOpPhiBlockId( // Check that |merge_block_id| is reachable in the CFG and |block_id| // dominates |merge_block_id|. - if (!dominator_analysis->IsReachable(merge_block_id) || + if (!ir_context->IsReachable(*ir_context->cfg()->block(merge_block_id)) || !dominator_analysis->Dominates(block_id, merge_block_id)) { return 0; } diff --git a/source/fuzz/transformation_push_id_through_variable.cpp b/source/fuzz/transformation_push_id_through_variable.cpp index 0df1da6b..55a57a15 100644 --- a/source/fuzz/transformation_push_id_through_variable.cpp +++ b/source/fuzz/transformation_push_id_through_variable.cpp @@ -61,7 +61,7 @@ bool TransformationPushIdThroughVariable::IsApplicable( // 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)) { + if (!ir_context->IsReachable(*basic_block)) { return false; } @@ -155,7 +155,7 @@ void TransformationPushIdThroughVariable::Apply( // We should be able to create a synonym of |value_id| if it's not irrelevant. if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context, - value_instruction) && + *value_instruction) && !transformation_context->GetFactManager()->IdIsIrrelevant( message_.value_synonym_id())) { // Adds the fact that |message_.value_synonym_id| diff --git a/source/fuzz/transformation_replace_id_with_synonym.cpp b/source/fuzz/transformation_replace_id_with_synonym.cpp index 92ce751d..8d21d233 100644 --- a/source/fuzz/transformation_replace_id_with_synonym.cpp +++ b/source/fuzz/transformation_replace_id_with_synonym.cpp @@ -65,9 +65,10 @@ bool TransformationReplaceIdWithSynonym::IsApplicable( // 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 (!TypesAreCompatible(ir_context, use_instruction->opcode(), - message_.id_use_descriptor().in_operand_index(), - type_id_of_interest, type_id_synonym)) { + if (!fuzzerutil::TypesAreCompatible( + ir_context, use_instruction->opcode(), + message_.id_use_descriptor().in_operand_index(), type_id_of_interest, + type_id_synonym)) { return false; } @@ -109,59 +110,6 @@ protobufs::Transformation TransformationReplaceIdWithSynonym::ToMessage() return result; } -// 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; - } -} - -bool TransformationReplaceIdWithSynonym::TypesAreCompatible( - opt::IRContext* ir_context, SpvOp opcode, uint32_t use_in_operand_index, - uint32_t type_id_1, uint32_t type_id_2) { - assert(ir_context->get_type_mgr()->GetType(type_id_1) && - ir_context->get_type_mgr()->GetType(type_id_2) && - "Type ids are invalid"); - - return type_id_1 == type_id_2 || - (IsAgnosticToSignednessOfOperand(opcode, use_in_operand_index) && - fuzzerutil::TypesAreEqualUpToSign(ir_context, type_id_1, type_id_2)); -} - std::unordered_set TransformationReplaceIdWithSynonym::GetFreshIds() const { return std::unordered_set(); diff --git a/source/fuzz/transformation_replace_id_with_synonym.h b/source/fuzz/transformation_replace_id_with_synonym.h index 1ac636b4..66f8e439 100644 --- a/source/fuzz/transformation_replace_id_with_synonym.h +++ b/source/fuzz/transformation_replace_id_with_synonym.h @@ -32,7 +32,7 @@ class TransformationReplaceIdWithSynonym : public Transformation { protobufs::IdUseDescriptor id_use_descriptor, uint32_t synonymous_id); // - The fact manager must know that the id identified by - // |message_.id_use_descriptor| is synonomous with |message_.synonymous_id|. + // |message_.id_use_descriptor| is synonymous with |message_.synonymous_id|. // - Replacing the id in |message_.id_use_descriptor| by // |message_.synonymous_id| must respect SPIR-V's rules about uses being // dominated by their definitions. @@ -52,22 +52,7 @@ class TransformationReplaceIdWithSynonym : public Transformation { protobufs::Transformation ToMessage() const override; - // Returns true if |type_id_1| and |type_id_2| represent compatible types - // given the context of the instruction with |opcode| (i.e. we can replace - // an operand of |opcode| of the first type with an id of the second type - // and vice-versa). - static bool TypesAreCompatible(opt::IRContext* ir_context, SpvOp opcode, - uint32_t use_in_operand_index, - uint32_t type_id_1, uint32_t type_id_2); - private: - // 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); - protobufs::TransformationReplaceIdWithSynonym message_; }; diff --git a/source/fuzz/transformation_store.cpp b/source/fuzz/transformation_store.cpp index f8c6d013..c00cd345 100644 --- a/source/fuzz/transformation_store.cpp +++ b/source/fuzz/transformation_store.cpp @@ -24,9 +24,13 @@ TransformationStore::TransformationStore(protobufs::TransformationStore message) : message_(std::move(message)) {} TransformationStore::TransformationStore( - uint32_t pointer_id, uint32_t value_id, + uint32_t pointer_id, bool is_atomic, uint32_t memory_scope, + uint32_t memory_semantics, uint32_t value_id, const protobufs::InstructionDescriptor& instruction_to_insert_before) { message_.set_pointer_id(pointer_id); + message_.set_is_atomic(is_atomic); + message_.set_memory_scope_id(memory_scope); + message_.set_memory_semantics_id(memory_semantics); message_.set_value_id(value_id); *message_.mutable_instruction_to_insert_before() = instruction_to_insert_before; @@ -70,8 +74,12 @@ bool TransformationStore::IsApplicable( return false; } // ... and it must be legitimate to insert a store before it. - if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, - insert_before)) { + if (!message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpStore, insert_before)) { + return false; + } + if (message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpAtomicStore, insert_before)) { return false; } @@ -102,6 +110,87 @@ bool TransformationStore::IsApplicable( return false; } + if (message_.is_atomic()) { + // Check the exists of memory scope and memory semantics ids. + auto memory_scope_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.memory_scope_id()); + auto memory_semantics_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.memory_semantics_id()); + + if (!memory_scope_instruction) { + return false; + } + if (!memory_semantics_instruction) { + return false; + } + // The memory scope and memory semantics instructions must have the + // 'OpConstant' opcode. + if (memory_scope_instruction->opcode() != SpvOpConstant) { + return false; + } + if (memory_semantics_instruction->opcode() != SpvOpConstant) { + return false; + } + // The memory scope and memory semantics need to be available before + // |insert_before|. + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before, message_.memory_scope_id())) { + return false; + } + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before, message_.memory_semantics_id())) { + return false; + } + // The memory scope and memory semantics instructions must have an Integer + // operand type with signedness does not matters. + if (ir_context->get_def_use_mgr() + ->GetDef(memory_scope_instruction->type_id()) + ->opcode() != SpvOpTypeInt) { + return false; + } + if (ir_context->get_def_use_mgr() + ->GetDef(memory_semantics_instruction->type_id()) + ->opcode() != SpvOpTypeInt) { + return false; + } + + // The size of the integer for memory scope and memory semantics + // instructions must be equal to 32 bits. + auto memory_scope_int_width = + ir_context->get_def_use_mgr() + ->GetDef(memory_scope_instruction->type_id()) + ->GetSingleWordInOperand(0); + auto memory_semantics_int_width = + ir_context->get_def_use_mgr() + ->GetDef(memory_semantics_instruction->type_id()) + ->GetSingleWordInOperand(0); + + if (memory_scope_int_width != 32) { + return false; + } + if (memory_semantics_int_width != 32) { + return false; + } + + // The memory scope constant value must be that of SpvScopeInvocation. + auto memory_scope_const_value = + memory_scope_instruction->GetSingleWordInOperand(0); + if (memory_scope_const_value != SpvScopeInvocation) { + return false; + } + + // The memory semantics constant value must match the storage class of the + // pointer being loaded from. + auto memory_semantics_const_value = static_cast( + memory_semantics_instruction->GetSingleWordInOperand(0)); + if (memory_semantics_const_value != + fuzzerutil::GetMemorySemanticsForStorageClass( + static_cast( + pointer_type->GetSingleWordInOperand(0)))) { + return false; + } + } + // The value needs to be available at the insertion point. return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, message_.value_id()); @@ -109,20 +198,43 @@ bool TransformationStore::IsApplicable( void TransformationStore::Apply(opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - auto insert_before = - FindInstruction(message_.instruction_to_insert_before(), ir_context); - auto new_instruction = MakeUnique( - ir_context, SpvOpStore, 0, 0, - opt::Instruction::OperandList( - {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, - {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})); - auto new_instruction_ptr = new_instruction.get(); - insert_before->InsertBefore(std::move(new_instruction)); - // Inform the def-use manager about the new instruction and record its basic - // block. - ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); - ir_context->set_instr_block(new_instruction_ptr, - ir_context->get_instr_block(insert_before)); + if (message_.is_atomic()) { + // OpAtomicStore instruction. + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto new_instruction = MakeUnique( + ir_context, SpvOpAtomicStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, + {SPV_OPERAND_TYPE_SCOPE_ID, {message_.memory_scope_id()}}, + {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, + {message_.memory_semantics_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})); + auto new_instruction_ptr = new_instruction.get(); + insert_before->InsertBefore(std::move(new_instruction)); + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, + ir_context->get_instr_block(insert_before)); + + } else { + // OpStore instruction. + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto new_instruction = MakeUnique( + ir_context, SpvOpStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})); + auto new_instruction_ptr = new_instruction.get(); + insert_before->InsertBefore(std::move(new_instruction)); + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, + ir_context->get_instr_block(insert_before)); + } } protobufs::Transformation TransformationStore::ToMessage() const { diff --git a/source/fuzz/transformation_store.h b/source/fuzz/transformation_store.h index 18ba1d7d..638713bb 100644 --- a/source/fuzz/transformation_store.h +++ b/source/fuzz/transformation_store.h @@ -28,12 +28,21 @@ class TransformationStore : public Transformation { explicit TransformationStore(protobufs::TransformationStore message); TransformationStore( - uint32_t pointer_id, uint32_t value_id, + uint32_t pointer_id, bool is_atomic, uint32_t memory_scope, + uint32_t memory_semantics, uint32_t value_id, const protobufs::InstructionDescriptor& instruction_to_insert_before); // - |message_.pointer_id| must be the id of a pointer // - The pointer type must not have read-only storage class // - The pointer must not be OpConstantNull or OpUndef + // - |message_.is_atomic| must be true if want to work with OpAtomicStore. + // - If |is_atomic| is true then |message_memory_scope_id| must be the id of + // an OpConstant 32 bit integer instruction with the value + // SpvScopeInvocation. + // - If |is_atomic| is true then |message_.memory_semantics_id| must be the id + // of an OpConstant 32 bit integer instruction with the values + // SpvMemorySemanticsWorkgroupMemoryMask or + // SpvMemorySemanticsUniformMemoryMask. // - |message_.value_id| must be an instruction result id that has the same // type as the pointee type of |message_.pointer_id| // - |message_.instruction_to_insert_before| must identify an instruction diff --git a/source/fuzz/transformation_swap_function_variables.cpp b/source/fuzz/transformation_swap_function_variables.cpp new file mode 100644 index 00000000..aec32fe1 --- /dev/null +++ b/source/fuzz/transformation_swap_function_variables.cpp @@ -0,0 +1,93 @@ +// Copyright (c) 2021 Mostafa Ashraf +// +// 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_function_variables.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationSwapFunctionVariables::TransformationSwapFunctionVariables( + protobufs::TransformationSwapFunctionVariables message) + : message_(std::move(message)) {} + +TransformationSwapFunctionVariables::TransformationSwapFunctionVariables( + uint32_t result_id1, uint32_t result_id2) { + message_.set_result_id1(result_id1); + message_.set_result_id2(result_id2); +} + +bool TransformationSwapFunctionVariables::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + uint32_t result_id1 = message_.result_id1(); + uint32_t result_id2 = message_.result_id2(); + + assert((result_id1 != result_id2) && "Two results ids are equal"); + + // The result ids used in the message must refer to instructions. + auto instruction1 = ir_context->get_def_use_mgr()->GetDef(result_id1); + auto instruction2 = ir_context->get_def_use_mgr()->GetDef(result_id2); + if (instruction1 == nullptr || instruction2 == nullptr) { + return false; + } + // Both instructions must be variables. + if (instruction1->opcode() != SpvOpVariable || + instruction2->opcode() != SpvOpVariable) { + return false; + } + + // Both variable instructions must be in some basic block (as they are + // function-local variables), and they must be in the same block (as they need + // to be variables of the same function). + auto* block_1 = ir_context->get_instr_block(result_id1); + auto* block_2 = ir_context->get_instr_block(result_id2); + if (block_1 == nullptr || block_2 == nullptr) { + return false; + } + + return block_1 == block_2; +} + +void TransformationSwapFunctionVariables::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // The result ids used in the message must refer to instructions. + auto instruction1 = + ir_context->get_def_use_mgr()->GetDef(message_.result_id1()); + auto instruction2 = + ir_context->get_def_use_mgr()->GetDef(message_.result_id2()); + + std::unique_ptr temp_instruction = + MakeUnique(); + + temp_instruction->InsertBefore(instruction1); + instruction1->InsertAfter(instruction2); + instruction2->InsertAfter(temp_instruction.get()); + temp_instruction->RemoveFromList(); +} + +protobufs::Transformation TransformationSwapFunctionVariables::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_swap_function_variables() = message_; + return result; +} + +std::unordered_set TransformationSwapFunctionVariables::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/transformation_swap_function_variables.h b/source/fuzz/transformation_swap_function_variables.h new file mode 100644 index 00000000..eb383f4f --- /dev/null +++ b/source/fuzz/transformation_swap_function_variables.h @@ -0,0 +1,57 @@ +// Copyright (c) 2021 Mostafa Ashraf +// +// 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_FUNCTION_VARIABLES_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SWAP_FUNCTION_VARIABLES_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 { + +// A transformation that swaps two variable declaration instructions that appear +// in the same function. +class TransformationSwapFunctionVariables : public Transformation { + public: + explicit TransformationSwapFunctionVariables( + protobufs::TransformationSwapFunctionVariables message); + + TransformationSwapFunctionVariables(uint32_t result_id1, uint32_t result_id2); + + // - |message_.result_id1| and |message_.result_id2| must be the ids of + // distinct OpVariable instructions appearing in the same function. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Swaps two OpVariable instructions with result ids |message_.result_id1| + // and |message_.result_id2|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + protobufs::Transformation ToMessage() const override; + + std::unordered_set GetFreshIds() const override; + + private: + protobufs::TransformationSwapFunctionVariables message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SWAP_FUNCTION_VARIABLES_H_ diff --git a/source/fuzz/transformation_swap_two_functions.cpp b/source/fuzz/transformation_swap_two_functions.cpp new file mode 100644 index 00000000..85d9e79c --- /dev/null +++ b/source/fuzz/transformation_swap_two_functions.cpp @@ -0,0 +1,72 @@ +// Copyright (c) 2021 Shiyu Liu +// +// 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_two_functions.h" + +#include "source/opt/function.h" +#include "source/opt/module.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationSwapTwoFunctions::TransformationSwapTwoFunctions( + protobufs::TransformationSwapTwoFunctions message) + : message_(std::move(message)) {} + +TransformationSwapTwoFunctions::TransformationSwapTwoFunctions(uint32_t id1, + uint32_t id2) { + assert(id1 != id2 && "The two function ids cannot be the same."); + message_.set_function_id1(id1); + message_.set_function_id2(id2); +} + +bool TransformationSwapTwoFunctions::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + auto func1_ptr = ir_context->GetFunction(message_.function_id1()); + auto func2_ptr = ir_context->GetFunction(message_.function_id2()); + return func1_ptr != nullptr && func2_ptr != nullptr; +} + +void TransformationSwapTwoFunctions::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + opt::Module::iterator func1_it = + fuzzerutil::GetFunctionIterator(ir_context, message_.function_id1()); + opt::Module::iterator func2_it = + fuzzerutil::GetFunctionIterator(ir_context, message_.function_id2()); + + assert(func1_it != ir_context->module()->end() && + "Could not find function 1."); + assert(func2_it != ir_context->module()->end() && + "Could not find function 2."); + + // Two function pointers are all set, swap the two functions within the + // module. + std::iter_swap(func1_it.Get(), func2_it.Get()); +} + +protobufs::Transformation TransformationSwapTwoFunctions::ToMessage() const { + protobufs::Transformation result; + *result.mutable_swap_two_functions() = message_; + return result; +} + +std::unordered_set TransformationSwapTwoFunctions::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/transformation_swap_two_functions.h b/source/fuzz/transformation_swap_two_functions.h new file mode 100644 index 00000000..1b6d6e80 --- /dev/null +++ b/source/fuzz/transformation_swap_two_functions.h @@ -0,0 +1,56 @@ +// Copyright (c) 2021 Shiyu Liu +// +// 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_TWO_FUNCTIONS_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SWAP_TWO_FUNCTIONS_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 TransformationSwapTwoFunctions : public Transformation { + public: + explicit TransformationSwapTwoFunctions( + protobufs::TransformationSwapTwoFunctions message); + + TransformationSwapTwoFunctions(uint32_t function_id1, uint32_t function_id2); + + // |function_id1| and |function_id1| should all be existing ids. + // Swap function operation is only permitted if: + // - both ids must be ids of functions. + // - both ids can be found in the module. + // - function_id1 and function_id2 are not the same. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // OpFunction with |function_id1| and |function_id1| are swapped. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationSwapTwoFunctions message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SWAP_TWO_FUNCTIONS_H_ diff --git a/source/fuzz/transformation_vector_shuffle.cpp b/source/fuzz/transformation_vector_shuffle.cpp index ac0e3ccb..742a2c80 100644 --- a/source/fuzz/transformation_vector_shuffle.cpp +++ b/source/fuzz/transformation_vector_shuffle.cpp @@ -204,7 +204,7 @@ void TransformationVectorShuffle::AddDataSynonymFacts( // Check that the first vector can participate in data synonym facts. if (!fuzzerutil::CanMakeSynonymOf( ir_context, *transformation_context, - ir_context->get_def_use_mgr()->GetDef(message_.vector1()))) { + *ir_context->get_def_use_mgr()->GetDef(message_.vector1()))) { continue; } descriptor_for_source_component = @@ -213,7 +213,7 @@ void TransformationVectorShuffle::AddDataSynonymFacts( // Check that the second vector can participate in data synonym facts. if (!fuzzerutil::CanMakeSynonymOf( ir_context, *transformation_context, - ir_context->get_def_use_mgr()->GetDef(message_.vector2()))) { + *ir_context->get_def_use_mgr()->GetDef(message_.vector2()))) { continue; } auto index_into_vector_2 = diff --git a/source/fuzz/transformation_wrap_vector_synonym.cpp b/source/fuzz/transformation_wrap_vector_synonym.cpp new file mode 100644 index 00000000..490bcd78 --- /dev/null +++ b/source/fuzz/transformation_wrap_vector_synonym.cpp @@ -0,0 +1,200 @@ +// Copyright (c) 2021 Shiyu Liu +// +// 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_wrap_vector_synonym.h" + +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/opt/instruction.h" + +namespace spvtools { +namespace fuzz { + +TransformationWrapVectorSynonym::TransformationWrapVectorSynonym( + protobufs::TransformationWrapVectorSynonym message) + : message_(std::move(message)) {} + +TransformationWrapVectorSynonym::TransformationWrapVectorSynonym( + uint32_t instruction_id, uint32_t vector_operand1, uint32_t vector_operand2, + uint32_t fresh_id, uint32_t pos) { + message_.set_instruction_id(instruction_id); + message_.set_vector_operand1(vector_operand1); + message_.set_vector_operand2(vector_operand2); + message_.set_fresh_id(fresh_id); + message_.set_scalar_position(pos); +} + +bool TransformationWrapVectorSynonym::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // |fresh_id| must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + const opt::Instruction* instruction = + ir_context->get_def_use_mgr()->GetDef(message_.instruction_id()); + + // |instruction_id| must refer to an existing instruction. + if (instruction == nullptr) { + return false; + } + + if (!IsInstructionSupported(ir_context, *instruction)) { + return false; + } + + // It must be possible to make a synonym of the result id of the scalar + // operation + if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, + *instruction)) { + return false; + } + + // |vector_operand1| and |vector_operand2| must exist. + auto vec1 = ir_context->get_def_use_mgr()->GetDef(message_.vector_operand1()); + auto vec2 = ir_context->get_def_use_mgr()->GetDef(message_.vector_operand2()); + + if (vec1 == nullptr || vec2 == nullptr) { + return false; + } + + // The 2 vectors must have compatible vector types. + auto vec1_type_id = vec1->type_id(); + auto vec2_type_id = vec2->type_id(); + + for (auto operand_index : {0, 1}) { + if (!fuzzerutil::TypesAreCompatible(ir_context, instruction->opcode(), + operand_index, vec1_type_id, + vec2_type_id)) { + return false; + } + } + + auto vec1_type = ir_context->get_def_use_mgr()->GetDef(vec1_type_id); + if (vec1_type->opcode() != SpvOpTypeVector) { + return false; + } + + // A suitable vector for the result type of the new vector instruction must + // exist in the module. This is a vector of the right length, whose element + // type matches the result type of the scalar instruction. + uint32_t vector_size = vec1_type->GetSingleWordInOperand(1); + if (!fuzzerutil::MaybeGetVectorType(ir_context, instruction->type_id(), + vector_size)) { + return false; + } + + // |scalar_position| needs to be a non-negative integer less than the vector + // length. + // OpTypeVector instruction has the component count at index 2. + if (message_.scalar_position() >= ir_context->get_def_use_mgr() + ->GetDef(vec1_type_id) + ->GetSingleWordInOperand(1)) { + return false; + } + + if (!transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(message_.vector_operand1(), + {message_.scalar_position()}), + MakeDataDescriptor(instruction->GetSingleWordInOperand(0), {}))) { + return false; + } + + if (!transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(message_.vector_operand2(), + {message_.scalar_position()}), + MakeDataDescriptor(instruction->GetSingleWordInOperand(1), {}))) { + return false; + } + + return true; +} + +void TransformationWrapVectorSynonym::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Create an instruction descriptor for the original instruction. + auto instruction = + ir_context->get_def_use_mgr()->GetDef(message_.instruction_id()); + auto destination_block = ir_context->get_instr_block(instruction); + + // Populate input operand list with two vectors for vector operation. + opt::Instruction::OperandList in_operands; + in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.vector_operand1()}}); + in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.vector_operand2()}}); + + // Make a new arithmetic instruction: %fresh_id = OpXX %type_id %result_id1 + // %result_id2. + auto vector_operand_type = ir_context->get_def_use_mgr()->GetDef( + fuzzerutil::GetTypeId(ir_context, message_.vector_operand1())); + uint32_t vector_size = vector_operand_type->GetSingleWordInOperand(1); + auto vec_type_id = fuzzerutil::MaybeGetVectorType( + ir_context, instruction->type_id(), vector_size); + auto new_instruction = MakeUnique( + ir_context, instruction->opcode(), vec_type_id, message_.fresh_id(), + std::move(in_operands)); + auto new_instruction_ptr = new_instruction.get(); + instruction->InsertBefore(std::move(new_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, destination_block); + + // Add |fresh_id| to id bound. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Add synonyms between |fresh_id| and |instruction_id|. + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.fresh_id(), {message_.scalar_position()}), + MakeDataDescriptor(message_.instruction_id(), {})); +} + +protobufs::Transformation TransformationWrapVectorSynonym::ToMessage() const { + protobufs::Transformation result; + *result.mutable_wrap_vector_synonym() = message_; + return result; +} + +std::unordered_set TransformationWrapVectorSynonym::GetFreshIds() + const { + return std::unordered_set{message_.fresh_id()}; +} + +bool TransformationWrapVectorSynonym::IsInstructionSupported( + opt::IRContext* ir_context, const opt::Instruction& instruction) { + if (!instruction.result_id() || !instruction.type_id()) { + return false; + } + auto type_instruction = + ir_context->get_def_use_mgr()->GetDef(instruction.type_id()); + + if ((type_instruction->opcode() != SpvOpTypeInt && + type_instruction->opcode() != SpvOpTypeFloat)) { + return false; + } + + switch (instruction.opcode()) { + case SpvOpIAdd: + case SpvOpISub: + case SpvOpIMul: + case SpvOpFAdd: + case SpvOpFSub: + case SpvOpFMul: + return true; + default: + return false; + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/transformation_wrap_vector_synonym.h b/source/fuzz/transformation_wrap_vector_synonym.h new file mode 100644 index 00000000..94437fe5 --- /dev/null +++ b/source/fuzz/transformation_wrap_vector_synonym.h @@ -0,0 +1,80 @@ +// Copyright (c) 2021 Shiyu Liu +// +// 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_WRAP_VECTOR_SYNONYM_H_ +#define SOURCE_FUZZ_TRANSFORMATION_WRAP_VECTOR_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 TransformationWrapVectorSynonym : public Transformation { + public: + explicit TransformationWrapVectorSynonym( + protobufs::TransformationWrapVectorSynonym message); + + TransformationWrapVectorSynonym(uint32_t instruction_id, + uint32_t vector_operand1, + uint32_t vector_operand2, uint32_t fresh_id, + uint32_t pos); + // - |instruction_id| must be the id of a supported arithmetic operation + // and must be relevant. + // - |vector_operand1| and |vector_operand2| represents the result ids of the + // two vector operands. + // - |fresh_id| is an unused id that will be used as a result id of the + // created instruction. + // - |vector_operand1| and |vector_operand2| must have compatible vector types + // that are supported by this transformation. + // - |pos| is an index of the operands of |instruction_id| in the + // |vector_operand1| and |vector_operand2|. It must be less than the size + // of those vector operands. + // - A vector type with the same width as the types of the vector operands, + // and element type matching the type of |instruction_id|, must exist in the + // module. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds a new instruction before the |instruction_id| with |fresh_id| + // result id and |instruction_id|'s opcode. The added instruction has + // two operands: |vector_operand1| and |vector_operand2| and its type + // id is equal to the type ids of those operands. A new fact is added + // to the fact manager specifying that |fresh_id[pos]| is synonymous + // to |instruction_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + protobufs::Transformation ToMessage() const override; + + // Checks whether the instruction given is supported by the transformation. + // A valid instruction must: + // - has both result id and type id. + // - is a supported scalar operation instruction. + // - has a supported type that is either int or float. + static bool IsInstructionSupported(opt::IRContext* ir_context, + const opt::Instruction& instruction); + + private: + protobufs::TransformationWrapVectorSynonym message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_WRAP_VECTOR_SYNONYM_H_ diff --git a/source/libspirv.cpp b/source/libspirv.cpp index 0bc09350..be76caaa 100644 --- a/source/libspirv.cpp +++ b/source/libspirv.cpp @@ -99,7 +99,9 @@ bool SpirvTools::Disassemble(const uint32_t* binary, const size_t binary_size, spv_text spvtext = nullptr; spv_result_t status = spvBinaryToText(impl_->context, binary, binary_size, options, &spvtext, nullptr); - if (status == SPV_SUCCESS) { + if (status == SPV_SUCCESS && + (options & SPV_BINARY_TO_TEXT_OPTION_PRINT) == 0) { + assert(spvtext); text->assign(spvtext->str, spvtext->str + spvtext->length); } spvTextDestroy(spvtext); diff --git a/source/link/BUILD.gn b/source/link/BUILD.gn index 7199601f..84a5d2b4 100644 --- a/source/link/BUILD.gn +++ b/source/link/BUILD.gn @@ -34,6 +34,7 @@ ohos_source_set("deqp_spirvtool_link_source") { "//third_party/spirv-tools", "//third_party/vk-gl-cts/build/external/spirv-tools/spirv-tools", "//third_party/spirv-headers/include", + "//third_party/spirv-headers/include/spirv/unified1", "//third_party/spirv-tools/include", ] diff --git a/source/link/CMakeLists.txt b/source/link/CMakeLists.txt index c8dd2f71..a452a107 100644 --- a/source/link/CMakeLists.txt +++ b/source/link/CMakeLists.txt @@ -23,7 +23,7 @@ target_include_directories(SPIRV-Tools-link $ PRIVATE ${spirv-tools_BINARY_DIR} ) -# We need the IR functionnalities from the optimizer +# We need the IR functionalities from the optimizer target_link_libraries(SPIRV-Tools-link PUBLIC SPIRV-Tools-opt) diff --git a/source/link/linker.cpp b/source/link/linker.cpp index 8da4a98d..3b388cc6 100644 --- a/source/link/linker.cpp +++ b/source/link/linker.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -33,9 +34,12 @@ #include "source/opt/ir_loader.h" #include "source/opt/pass_manager.h" #include "source/opt/remove_duplicates_pass.h" +#include "source/opt/remove_unused_interface_variables_pass.h" #include "source/opt/type_manager.h" +#include "source/spirv_constant.h" #include "source/spirv_target_env.h" #include "source/util/make_unique.h" +#include "source/util/string_utils.h" #include "spirv-tools/libspirv.hpp" namespace spvtools { @@ -85,10 +89,6 @@ spv_result_t ShiftIdsInModules(const MessageConsumer& consumer, // // |header| should not be null, |modules| should not be empty and pointers // should be non-null. |max_id_bound| should be strictly greater than 0. -// -// TODO(pierremoreau): What to do when binaries use different versions of -// SPIR-V? For now, use the max of all versions found in -// the input modules. spv_result_t GenerateHeader(const MessageConsumer& consumer, const std::vector& modules, uint32_t max_id_bound, opt::ModuleHeader* header); @@ -129,7 +129,7 @@ spv_result_t CheckImportExportCompatibility(const MessageConsumer& consumer, // Remove linkage specific instructions, such as prototypes of imported // functions, declarations of imported variables, import (and export if -// necessary) linkage attribtes. +// necessary) linkage attributes. // // |linked_context| and |decoration_manager| should not be null, and the // 'RemoveDuplicatePass' should be run first. @@ -147,6 +147,15 @@ spv_result_t RemoveLinkageSpecificInstructions( spv_result_t VerifyIds(const MessageConsumer& consumer, opt::IRContext* linked_context); +// Verify that the universal limits are not crossed, and warn the user +// otherwise. +// +// TODO(pierremoreau): +// - Verify against the limits of the environment (e.g. Vulkan limits if +// consuming vulkan1.x) +spv_result_t VerifyLimits(const MessageConsumer& consumer, + const opt::IRContext& linked_context); + spv_result_t ShiftIdsInModules(const MessageConsumer& consumer, std::vector* modules, uint32_t* max_id_bound) { @@ -162,29 +171,31 @@ spv_result_t ShiftIdsInModules(const MessageConsumer& consumer, return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) << "|max_id_bound| of ShiftIdsInModules should not be null."; - uint32_t id_bound = modules->front()->IdBound() - 1u; + const size_t id_bound = + std::accumulate(modules->begin(), modules->end(), static_cast(1), + [](const size_t& accumulation, opt::Module* module) { + return accumulation + module->IdBound() - 1u; + }); + if (id_bound > std::numeric_limits::max()) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "Too many IDs (" << id_bound + << "): combining all modules would overflow the 32-bit word of the " + "SPIR-V header."; + + *max_id_bound = static_cast(id_bound); + + uint32_t id_offset = modules->front()->IdBound() - 1u; for (auto module_iter = modules->begin() + 1; module_iter != modules->end(); ++module_iter) { Module* module = *module_iter; - module->ForEachInst([&id_bound](Instruction* insn) { - insn->ForEachId([&id_bound](uint32_t* id) { *id += id_bound; }); + module->ForEachInst([&id_offset](Instruction* insn) { + insn->ForEachId([&id_offset](uint32_t* id) { *id += id_offset; }); }); - id_bound += module->IdBound() - 1u; - if (id_bound > 0x3FFFFF) - return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_ID) - << "The limit of IDs, 4194303, was exceeded:" - << " " << id_bound << " is the current ID bound."; + id_offset += module->IdBound() - 1u; // Invalidate the DefUseManager module->context()->InvalidateAnalyses(opt::IRContext::kAnalysisDefUse); } - ++id_bound; - if (id_bound > 0x3FFFFF) - return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_ID) - << "The limit of IDs, 4194303, was exceeded:" - << " " << id_bound << " is the current ID bound."; - - *max_id_bound = id_bound; return SPV_SUCCESS; } @@ -201,15 +212,25 @@ spv_result_t GenerateHeader(const MessageConsumer& consumer, return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) << "|max_id_bound| of GenerateHeader should not be null."; - uint32_t version = 0u; - for (const auto& module : modules) - version = std::max(version, module->version()); + const uint32_t linked_version = modules.front()->version(); + for (std::size_t i = 1; i < modules.size(); ++i) { + const uint32_t module_version = modules[i]->version(); + if (module_version != linked_version) + return DiagnosticStream({0, 0, 1}, consumer, "", SPV_ERROR_INTERNAL) + << "Conflicting SPIR-V versions: " + << SPV_SPIRV_VERSION_MAJOR_PART(linked_version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(linked_version) + << " (input modules 1 through " << i << ") vs " + << SPV_SPIRV_VERSION_MAJOR_PART(module_version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(module_version) + << " (input module " << (i + 1) << ")."; + } header->magic_number = SpvMagicNumber; - header->version = version; - header->generator = 17u; + header->version = linked_version; + header->generator = SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_LINKER, 0); header->bound = max_id_bound; - header->reserved = 0u; + header->schema = 0u; return SPV_SUCCESS; } @@ -242,55 +263,65 @@ spv_result_t MergeModules(const MessageConsumer& consumer, linked_module->AddExtInstImport( std::unique_ptr(inst.Clone(linked_context))); - do { - const Instruction* memory_model_inst = input_modules[0]->GetMemoryModel(); - if (memory_model_inst == nullptr) break; + const Instruction* linked_memory_model_inst = + input_modules.front()->GetMemoryModel(); + if (linked_memory_model_inst == nullptr) { + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Input module 1 is lacking an OpMemoryModel instruction."; + } + const uint32_t linked_addressing_model = + linked_memory_model_inst->GetSingleWordOperand(0u); + const uint32_t linked_memory_model = + linked_memory_model_inst->GetSingleWordOperand(1u); - uint32_t addressing_model = memory_model_inst->GetSingleWordOperand(0u); - uint32_t memory_model = memory_model_inst->GetSingleWordOperand(1u); - for (const auto& module : input_modules) { - memory_model_inst = module->GetMemoryModel(); - if (memory_model_inst == nullptr) continue; + for (std::size_t i = 1; i < input_modules.size(); ++i) { + const Module* module = input_modules[i]; + const Instruction* memory_model_inst = module->GetMemoryModel(); + if (memory_model_inst == nullptr) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Input module " << (i + 1) + << " is lacking an OpMemoryModel instruction."; - if (addressing_model != memory_model_inst->GetSingleWordOperand(0u)) { - spv_operand_desc initial_desc = nullptr, current_desc = nullptr; - grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL, - addressing_model, &initial_desc); - grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL, - memory_model_inst->GetSingleWordOperand(0u), - ¤t_desc); - return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) - << "Conflicting addressing models: " << initial_desc->name - << " vs " << current_desc->name << "."; - } - if (memory_model != memory_model_inst->GetSingleWordOperand(1u)) { - spv_operand_desc initial_desc = nullptr, current_desc = nullptr; - grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, memory_model, - &initial_desc); - grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, - memory_model_inst->GetSingleWordOperand(1u), - ¤t_desc); - return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) - << "Conflicting memory models: " << initial_desc->name << " vs " - << current_desc->name << "."; - } + const uint32_t module_addressing_model = + memory_model_inst->GetSingleWordOperand(0u); + if (module_addressing_model != linked_addressing_model) { + spv_operand_desc linked_desc = nullptr, module_desc = nullptr; + grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL, + linked_addressing_model, &linked_desc); + grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL, + module_addressing_model, &module_desc); + return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) + << "Conflicting addressing models: " << linked_desc->name + << " (input modules 1 through " << i << ") vs " + << module_desc->name << " (input module " << (i + 1) << ")."; } - if (memory_model_inst != nullptr) - linked_module->SetMemoryModel(std::unique_ptr( - memory_model_inst->Clone(linked_context))); - } while (false); + const uint32_t module_memory_model = + memory_model_inst->GetSingleWordOperand(1u); + if (module_memory_model != linked_memory_model) { + spv_operand_desc linked_desc = nullptr, module_desc = nullptr; + grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, linked_memory_model, + &linked_desc); + grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, module_memory_model, + &module_desc); + return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) + << "Conflicting memory models: " << linked_desc->name + << " (input modules 1 through " << i << ") vs " + << module_desc->name << " (input module " << (i + 1) << ")."; + } + } + linked_module->SetMemoryModel(std::unique_ptr( + linked_memory_model_inst->Clone(linked_context))); - std::vector> entry_points; + std::vector> entry_points; for (const auto& module : input_modules) for (const auto& inst : module->entry_points()) { const uint32_t model = inst.GetSingleWordInOperand(0); - const char* const name = - reinterpret_cast(inst.GetInOperand(2).words.data()); + const std::string name = inst.GetInOperand(2).AsString(); const auto i = std::find_if( entry_points.begin(), entry_points.end(), - [model, name](const std::pair& v) { - return v.first == model && strcmp(name, v.second) == 0; + [model, name](const std::pair& v) { + return v.first == model && v.second == name; }); if (i != entry_points.end()) { spv_operand_desc desc = nullptr; @@ -331,13 +362,10 @@ spv_result_t MergeModules(const MessageConsumer& consumer, // If the generated module uses SPIR-V 1.1 or higher, add an // OpModuleProcessed instruction about the linking step. - if (linked_module->version() >= 0x10100) { + if (linked_module->version() >= SPV_SPIRV_VERSION_WORD(1, 1)) { const std::string processed_string("Linked by SPIR-V Tools Linker"); - const auto num_chars = processed_string.size(); - // Compute num words, accommodate the terminating null character. - const auto num_words = (num_chars + 1 + 3) / 4; - std::vector processed_words(num_words, 0u); - std::memcpy(processed_words.data(), processed_string.data(), num_chars); + std::vector processed_words = + spvtools::utils::MakeVector(processed_string); linked_module->AddDebug3Inst(std::unique_ptr( new Instruction(linked_context, SpvOpModuleProcessed, 0u, 0u, {{SPV_OPERAND_TYPE_LITERAL_STRING, processed_words}}))); @@ -351,18 +379,12 @@ spv_result_t MergeModules(const MessageConsumer& consumer, // TODO(pierremoreau): Since the modules have not been validate, should we // expect SpvStorageClassFunction variables outside // functions? - uint32_t num_global_values = 0u; for (const auto& module : input_modules) { for (const auto& inst : module->types_values()) { linked_module->AddType( std::unique_ptr(inst.Clone(linked_context))); - num_global_values += inst.opcode() == SpvOpVariable; } } - if (num_global_values > 0xFFFF) - return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) - << "The limit of global values, 65535, was exceeded;" - << " " << num_global_values << " global values were found."; // Process functions and their basic blocks for (const auto& module : input_modules) { @@ -413,8 +435,7 @@ spv_result_t GetImportExportPairs(const MessageConsumer& consumer, const uint32_t type = decoration.GetSingleWordInOperand(3u); LinkageSymbolInfo symbol_info; - symbol_info.name = - reinterpret_cast(decoration.GetInOperand(2u).words.data()); + symbol_info.name = decoration.GetInOperand(2u).AsString(); symbol_info.id = id; symbol_info.type_id = 0u; @@ -635,6 +656,34 @@ spv_result_t VerifyIds(const MessageConsumer& consumer, return SPV_SUCCESS; } +spv_result_t VerifyLimits(const MessageConsumer& consumer, + const opt::IRContext& linked_context) { + spv_position_t position = {}; + + const uint32_t max_id_bound = linked_context.module()->id_bound(); + if (max_id_bound >= SPV_LIMIT_RESULT_ID_BOUND) + DiagnosticStream({0u, 0u, 4u}, consumer, "", SPV_WARNING) + << "The minimum limit of IDs, " << (SPV_LIMIT_RESULT_ID_BOUND - 1) + << ", was exceeded:" + << " " << max_id_bound << " is the current ID bound.\n" + << "The resulting module might not be supported by all " + "implementations."; + + size_t num_global_values = 0u; + for (const auto& inst : linked_context.module()->types_values()) { + num_global_values += inst.opcode() == SpvOpVariable; + } + if (num_global_values >= SPV_LIMIT_GLOBAL_VARIABLES_MAX) + DiagnosticStream(position, consumer, "", SPV_WARNING) + << "The minimum limit of global values, " + << (SPV_LIMIT_GLOBAL_VARIABLES_MAX - 1) << ", was exceeded;" + << " " << num_global_values << " global values were found.\n" + << "The resulting module might not be supported by all " + "implementations."; + + return SPV_SUCCESS; +} + } // namespace spv_result_t Link(const Context& context, @@ -759,7 +808,16 @@ spv_result_t Link(const Context& context, const uint32_t* const* binaries, pass_res = manager.Run(&linked_context); if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA; - // Phase 11: Output the module + // Phase 11: Recompute EntryPoint variables + manager.AddPass(); + pass_res = manager.Run(&linked_context); + if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA; + + // Phase 12: Warn if SPIR-V limits were exceeded + res = VerifyLimits(consumer, linked_context); + if (res != SPV_SUCCESS) return res; + + // Phase 13: Output the module linked_context.module()->ToBinary(linked_binary, true); return SPV_SUCCESS; diff --git a/source/lint/BUILD.gn b/source/lint/BUILD.gn index c9addffc..5b3e9d56 100644 --- a/source/lint/BUILD.gn +++ b/source/lint/BUILD.gn @@ -30,6 +30,7 @@ ohos_source_set("deqp_spirvtool_lint_source") { sources = [ "//third_party/spirv-tools/source/lint/divergence_analysis.cpp", "//third_party/spirv-tools/source/lint/linter.cpp", + "//third_party/spirv-tools/source/lint/lint_divergent_derivatives.cpp", ] include_dirs = deqp_common_include_dirs @@ -37,6 +38,7 @@ ohos_source_set("deqp_spirvtool_lint_source") { "//third_party/spirv-tools", "//third_party/vk-gl-cts/build/external/spirv-tools/spirv-tools", "//third_party/spirv-headers/include", + "//third_party/spirv-headers/include/spirv/unified1", "//third_party/spirv-tools/include", ] diff --git a/source/lint/CMakeLists.txt b/source/lint/CMakeLists.txt index f9cae28a..1feae3f9 100644 --- a/source/lint/CMakeLists.txt +++ b/source/lint/CMakeLists.txt @@ -13,9 +13,11 @@ # limitations under the License. set(SPIRV_TOOLS_LINT_SOURCES divergence_analysis.h + lints.h linter.cpp divergence_analysis.cpp + lint_divergent_derivatives.cpp ) if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) diff --git a/source/lint/linter.cpp b/source/lint/linter.cpp index 0f847953..e4ed04ea 100644 --- a/source/lint/linter.cpp +++ b/source/lint/linter.cpp @@ -14,6 +14,13 @@ #include "spirv-tools/linter.hpp" +#include "source/lint/lints.h" +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" +#include "spirv-tools/libspirv.h" +#include "spirv-tools/libspirv.hpp" +#include "spirv/unified1/spirv.h" + namespace spvtools { struct Linter::Impl { @@ -32,20 +39,22 @@ Linter::Linter(spv_target_env env) : impl_(new Impl(env)) {} Linter::~Linter() {} void Linter::SetMessageConsumer(MessageConsumer consumer) { - impl_->message_consumer = consumer; + impl_->message_consumer = std::move(consumer); } -const MessageConsumer& Linter::consumer() const { +const MessageConsumer& Linter::Consumer() const { return impl_->message_consumer; } bool Linter::Run(const uint32_t* binary, size_t binary_size) { - (void)binary; - (void)binary_size; + std::unique_ptr context = + BuildModule(SPV_ENV_VULKAN_1_2, Consumer(), binary, binary_size); + if (context == nullptr) return false; - consumer()(SPV_MSG_INFO, "", {0, 0, 0}, "Hello, world!"); + bool result = true; + result &= lint::lints::CheckDivergentDerivatives(context.get()); - return true; + return result; } } // namespace spvtools diff --git a/source/name_mapper.cpp b/source/name_mapper.cpp index eb08f8fe..3b31d33a 100644 --- a/source/name_mapper.cpp +++ b/source/name_mapper.cpp @@ -22,10 +22,10 @@ #include #include -#include "spirv-tools/libspirv.h" - +#include "source/binary.h" #include "source/latest_version_spirv_header.h" #include "source/parsed_operand.h" +#include "spirv-tools/libspirv.h" namespace spvtools { namespace { @@ -172,7 +172,7 @@ spv_result_t FriendlyNameMapper::ParseInstruction( const auto result_id = inst.result_id; switch (inst.opcode) { case SpvOpName: - SaveName(inst.words[1], reinterpret_cast(inst.words + 2)); + SaveName(inst.words[1], spvDecodeLiteralStringOperand(inst, 1)); break; case SpvOpDecorate: // Decorations come after OpName. So OpName will take precedence over @@ -274,9 +274,8 @@ spv_result_t FriendlyNameMapper::ParseInstruction( SaveName(result_id, "Queue"); break; case SpvOpTypeOpaque: - SaveName(result_id, - std::string("Opaque_") + - Sanitize(reinterpret_cast(inst.words + 2))); + SaveName(result_id, std::string("Opaque_") + + Sanitize(spvDecodeLiteralStringOperand(inst, 1))); break; case SpvOpTypePipeStorage: SaveName(result_id, "PipeStorage"); diff --git a/source/opcode.cpp b/source/opcode.cpp index c96cde8d..3f927290 100644 --- a/source/opcode.cpp +++ b/source/opcode.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 The Khronos Group Inc. +// Copyright (c) 2015-2022 The Khronos Group Inc. // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights // reserved. // @@ -40,12 +40,12 @@ struct OpcodeDescPtrLen { static const spv_opcode_table_t kOpcodeTable = {ARRAY_SIZE(kOpcodeTableEntries), kOpcodeTableEntries}; -// Represents a vendor tool entry in the SPIR-V XML Regsitry. +// Represents a vendor tool entry in the SPIR-V XML Registry. struct VendorTool { uint32_t value; const char* vendor; const char* tool; // Might be empty string. - const char* vendor_tool; // Combiantion of vendor and tool. + const char* vendor_tool; // Combination of vendor and tool. }; const VendorTool vendor_tools[] = { @@ -453,6 +453,7 @@ bool spvOpcodeIsAbort(SpvOp opcode) { case SpvOpTerminateInvocation: case SpvOpTerminateRayKHR: case SpvOpIgnoreIntersectionKHR: + case SpvOpEmitMeshTasksEXT: return true; default: return false; @@ -467,11 +468,6 @@ bool spvOpcodeIsBlockTerminator(SpvOp opcode) { return spvOpcodeIsBranch(opcode) || spvOpcodeIsReturnOrAbort(opcode); } -bool spvOpcodeTerminatesExecution(SpvOp opcode) { - return opcode == SpvOpKill || opcode == SpvOpTerminateInvocation || - opcode == SpvOpTerminateRayKHR || opcode == SpvOpIgnoreIntersectionKHR; -} - bool spvOpcodeIsBaseOpaqueType(SpvOp opcode) { switch (opcode) { case SpvOpTypeImage: @@ -528,6 +524,7 @@ bool spvOpcodeIsNonUniformGroupOperation(SpvOp opcode) { case SpvOpGroupNonUniformLogicalXor: case SpvOpGroupNonUniformQuadBroadcast: case SpvOpGroupNonUniformQuadSwap: + case SpvOpGroupNonUniformRotateKHR: return true; default: return false; @@ -631,6 +628,7 @@ bool spvOpcodeIsDebug(SpvOp opcode) { case SpvOpString: case SpvOpLine: case SpvOpNoLine: + case SpvOpModuleProcessed: return true; default: return false; diff --git a/source/opcode.h b/source/opcode.h index c8525a25..77a0bed2 100644 --- a/source/opcode.h +++ b/source/opcode.h @@ -110,18 +110,16 @@ bool spvOpcodeIsBranch(SpvOp opcode); // Returns true if the given opcode is a return instruction. bool spvOpcodeIsReturn(SpvOp opcode); -// Returns true if the given opcode aborts execution. +// Returns true if the given opcode aborts execution. To abort means that after +// executing that instruction, no other instructions will be executed regardless +// of the context in which the instruction appears. Note that `OpUnreachable` +// is considered an abort even if its behaviour is undefined. bool spvOpcodeIsAbort(SpvOp opcode); // Returns true if the given opcode is a return instruction or it aborts // execution. bool spvOpcodeIsReturnOrAbort(SpvOp opcode); -// Returns true if the given opcode is a kill instruction or it terminates -// execution. Note that branches, returns, and unreachables do not terminate -// execution. -bool spvOpcodeTerminatesExecution(SpvOp opcode); - // Returns true if the given opcode is a basic block terminator. bool spvOpcodeIsBlockTerminator(SpvOp opcode); diff --git a/source/operand.cpp b/source/operand.cpp index 5a69fb24..0c255a35 100644 --- a/source/operand.cpp +++ b/source/operand.cpp @@ -72,12 +72,16 @@ spv_result_t spvOperandTableNameLookup(spv_target_env env, // Note that the second rule assumes the extension enabling this operand // is indeed requested in the SPIR-V code; checking that should be // validator's work. - if (((version >= entry.minVersion && version <= entry.lastVersion) || - entry.numExtensions > 0u || entry.numCapabilities > 0u) && - nameLength == strlen(entry.name) && + if (nameLength == strlen(entry.name) && !strncmp(entry.name, name, nameLength)) { - *pEntry = &entry; - return SPV_SUCCESS; + if ((version >= entry.minVersion && version <= entry.lastVersion) || + entry.numExtensions > 0u || entry.numCapabilities > 0u) { + *pEntry = &entry; + return SPV_SUCCESS; + } else { + // if there is no extension/capability then the version is wrong + return SPV_ERROR_WRONG_VERSION; + } } } } @@ -229,6 +233,9 @@ const char* spvOperandTypeStr(spv_operand_type_t type) { return "ray query committed intersection type"; case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE: return "ray query candidate intersection type"; + case SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT: + case SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT: + return "packed vector format"; case SPV_OPERAND_TYPE_IMAGE: case SPV_OPERAND_TYPE_OPTIONAL_IMAGE: return "image"; @@ -269,6 +276,10 @@ const char* spvOperandTypeStr(spv_operand_type_t type) { return "FP denorm mode"; case SPV_OPERAND_TYPE_FPOPERATION_MODE: return "FP operation mode"; + case SPV_OPERAND_TYPE_QUANTIZATION_MODES: + return "quantization mode"; + case SPV_OPERAND_TYPE_OVERFLOW_MODES: + return "overflow mode"; case SPV_OPERAND_TYPE_NONE: return "NONE"; @@ -355,6 +366,9 @@ bool spvOperandIsConcrete(spv_operand_type_t type) { case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: case SPV_OPERAND_TYPE_FPDENORM_MODE: case SPV_OPERAND_TYPE_FPOPERATION_MODE: + case SPV_OPERAND_TYPE_QUANTIZATION_MODES: + case SPV_OPERAND_TYPE_OVERFLOW_MODES: + case SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT: return true; default: break; @@ -390,6 +404,7 @@ bool spvOperandIsOptional(spv_operand_type_t type) { case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER: case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: case SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER: + case SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT: case SPV_OPERAND_TYPE_OPTIONAL_CIV: return true; default: @@ -567,6 +582,12 @@ std::function spvOperandCanBeForwardDeclaredFunction( std::function spvDbgInfoExtOperandCanBeForwardDeclaredFunction( spv_ext_inst_type_t ext_type, uint32_t key) { + // The Vulkan debug info extended instruction set is non-semantic so allows no + // forward references ever + if (ext_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + return [](unsigned) { return false; }; + } + // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/532): Forward // references for debug info instructions are still in discussion. We must // update the following lines of code when we conclude the spec. diff --git a/source/opt/BUILD.gn b/source/opt/BUILD.gn index 38d70e6c..7c0804b5 100644 --- a/source/opt/BUILD.gn +++ b/source/opt/BUILD.gn @@ -44,7 +44,6 @@ ohos_source_set("deqp_spirvtool_opt_source") { "//third_party/spirv-tools/source/opt/constants.cpp", "//third_party/spirv-tools/source/opt/control_dependence.cpp", - #"//third_party/spirv-tools/source/opt/convert_to_sampled_image_pass.cpp", "//third_party/spirv-tools/source/opt/convert_to_half_pass.cpp", "//third_party/spirv-tools/source/opt/copy_prop_arrays.cpp", "//third_party/spirv-tools/source/opt/dataflow.cpp", @@ -112,7 +111,6 @@ ohos_source_set("deqp_spirvtool_opt_source") { "//third_party/spirv-tools/source/opt/relax_float_ops_pass.cpp", "//third_party/spirv-tools/source/opt/remove_duplicates_pass.cpp", - #"//third_party/spirv-tools/source/opt/remove_unused_interface_variables_pass.cpp", "//third_party/spirv-tools/source/opt/replace_invalid_opc.cpp", "//third_party/spirv-tools/source/opt/scalar_analysis.cpp", "//third_party/spirv-tools/source/opt/scalar_analysis_simplification.cpp", @@ -122,7 +120,7 @@ ohos_source_set("deqp_spirvtool_opt_source") { "//third_party/spirv-tools/source/opt/ssa_rewrite_pass.cpp", "//third_party/spirv-tools/source/opt/strength_reduction_pass.cpp", "//third_party/spirv-tools/source/opt/strip_debug_info_pass.cpp", - "//third_party/spirv-tools/source/opt/strip_reflect_info_pass.cpp", + "//third_party/spirv-tools/source/opt/strip_nonsemantic_info_pass.cpp", "//third_party/spirv-tools/source/opt/struct_cfg_analysis.cpp", "//third_party/spirv-tools/source/opt/type_manager.cpp", "//third_party/spirv-tools/source/opt/types.cpp", @@ -132,6 +130,17 @@ ohos_source_set("deqp_spirvtool_opt_source") { "//third_party/spirv-tools/source/opt/vector_dce.cpp", "//third_party/spirv-tools/source/opt/workaround1209.cpp", "//third_party/spirv-tools/source/opt/wrap_opkill.cpp", + "//third_party/spirv-tools/source/opt/fix_func_call_arguments.cpp", + + "//third_party/spirv-tools/source/opt/convert_to_sampled_image_pass.cpp", + "//third_party/spirv-tools/source/opt/desc_sroa_util.cpp", + "//third_party/spirv-tools/source/opt/eliminate_dead_input_components_pass.cpp", + "//third_party/spirv-tools/source/opt/interface_var_sroa.cpp", + "//third_party/spirv-tools/source/opt/remove_dontinline_pass.cpp", + "//third_party/spirv-tools/source/opt/remove_unused_interface_variables_pass.cpp", + "//third_party/spirv-tools/source/opt/replace_desc_array_access_using_var_index.cpp", + "//third_party/spirv-tools/source/opt/spread_volatile_semantics.cpp", + ] include_dirs = deqp_common_include_dirs @@ -139,6 +148,7 @@ ohos_source_set("deqp_spirvtool_opt_source") { "//third_party/spirv-tools", "//third_party/vk-gl-cts/build/external/spirv-tools/spirv-tools", "//third_party/spirv-headers/include", + "//third_party/spirv-headers/include/spirv/unified1", "//third_party/spirv-tools/include", ] diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt index 88d56589..75fe4c0f 100644 --- a/source/opt/CMakeLists.txt +++ b/source/opt/CMakeLists.txt @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. set(SPIRV_TOOLS_OPT_SOURCES + fix_func_call_arguments.h aggressive_dead_code_elim_pass.h amd_ext_to_khr.h basic_block.h @@ -27,8 +28,11 @@ set(SPIRV_TOOLS_OPT_SOURCES composite.h const_folding_rules.h constants.h + control_dependence.h + convert_to_sampled_image_pass.h convert_to_half_pass.h copy_prop_arrays.h + dataflow.h dead_branch_elim_pass.h dead_insert_elim_pass.h dead_variable_elimination.h @@ -36,11 +40,13 @@ set(SPIRV_TOOLS_OPT_SOURCES debug_info_manager.h def_use_manager.h desc_sroa.h + desc_sroa_util.h dominator_analysis.h dominator_tree.h eliminate_dead_constant_pass.h eliminate_dead_functions_pass.h eliminate_dead_functions_util.h + eliminate_dead_input_components_pass.h eliminate_dead_members_pass.h empty_pass.h feature_manager.h @@ -62,6 +68,7 @@ set(SPIRV_TOOLS_OPT_SOURCES instruction.h instruction_list.h instrument_pass.h + interface_var_sroa.h interp_fixup_pass.h ir_builder.h ir_context.h @@ -95,17 +102,21 @@ set(SPIRV_TOOLS_OPT_SOURCES reflect.h register_pressure.h relax_float_ops_pass.h + remove_dontinline_pass.h remove_duplicates_pass.h + remove_unused_interface_variables_pass.h + replace_desc_array_access_using_var_index.h replace_invalid_opc.h scalar_analysis.h scalar_analysis_nodes.h scalar_replacement_pass.h set_spec_constant_default_value_pass.h simplification_pass.h + spread_volatile_semantics.h ssa_rewrite_pass.h strength_reduction_pass.h strip_debug_info_pass.h - strip_reflect_info_pass.h + strip_nonsemantic_info_pass.h struct_cfg_analysis.h tree_iterator.h type_manager.h @@ -117,6 +128,7 @@ set(SPIRV_TOOLS_OPT_SOURCES workaround1209.h wrap_opkill.h + fix_func_call_arguments.cpp aggressive_dead_code_elim_pass.cpp amd_ext_to_khr.cpp basic_block.cpp @@ -132,8 +144,11 @@ set(SPIRV_TOOLS_OPT_SOURCES composite.cpp const_folding_rules.cpp constants.cpp + control_dependence.cpp + convert_to_sampled_image_pass.cpp convert_to_half_pass.cpp copy_prop_arrays.cpp + dataflow.cpp dead_branch_elim_pass.cpp dead_insert_elim_pass.cpp dead_variable_elimination.cpp @@ -141,11 +156,13 @@ set(SPIRV_TOOLS_OPT_SOURCES debug_info_manager.cpp def_use_manager.cpp desc_sroa.cpp + desc_sroa_util.cpp dominator_analysis.cpp dominator_tree.cpp eliminate_dead_constant_pass.cpp eliminate_dead_functions_pass.cpp eliminate_dead_functions_util.cpp + eliminate_dead_input_components_pass.cpp eliminate_dead_members_pass.cpp feature_manager.cpp fix_storage_class.cpp @@ -166,6 +183,7 @@ set(SPIRV_TOOLS_OPT_SOURCES instruction.cpp instruction_list.cpp instrument_pass.cpp + interface_var_sroa.cpp interp_fixup_pass.cpp ir_context.cpp ir_loader.cpp @@ -196,17 +214,21 @@ set(SPIRV_TOOLS_OPT_SOURCES redundancy_elimination.cpp register_pressure.cpp relax_float_ops_pass.cpp + remove_dontinline_pass.cpp remove_duplicates_pass.cpp + remove_unused_interface_variables_pass.cpp + replace_desc_array_access_using_var_index.cpp replace_invalid_opc.cpp scalar_analysis.cpp scalar_analysis_simplification.cpp scalar_replacement_pass.cpp set_spec_constant_default_value_pass.cpp simplification_pass.cpp + spread_volatile_semantics.cpp ssa_rewrite_pass.cpp strength_reduction_pass.cpp strip_debug_info_pass.cpp - strip_reflect_info_pass.cpp + strip_nonsemantic_info_pass.cpp struct_cfg_analysis.cpp type_manager.cpp types.cpp diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index 81b2232f..7fa5c8a9 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2017 The Khronos Group Inc. // Copyright (c) 2017 Valve Corporation // Copyright (c) 2017 LunarG Inc. -// Copyright (c) 2018 Google LLC +// Copyright (c) 2018-2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,9 +23,11 @@ #include "source/cfa.h" #include "source/latest_version_glsl_std_450_header.h" #include "source/opt/eliminate_dead_functions_util.h" +#include "source/opt/ir_builder.h" #include "source/opt/iterator.h" #include "source/opt/reflect.h" #include "source/spirv_constant.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -35,10 +37,10 @@ namespace { const uint32_t kTypePointerStorageClassInIdx = 0; const uint32_t kEntryPointFunctionIdInIdx = 1; const uint32_t kSelectionMergeMergeBlockIdInIdx = 0; -const uint32_t kLoopMergeMergeBlockIdInIdx = 0; const uint32_t kLoopMergeContinueBlockIdInIdx = 1; const uint32_t kCopyMemoryTargetAddrInIdx = 0; const uint32_t kCopyMemorySourceAddrInIdx = 1; +const uint32_t kLoadSourceAddrInIdx = 0; const uint32_t kDebugDeclareOperandVariableIndex = 5; const uint32_t kGlobalVariableVariableIndex = 12; @@ -96,16 +98,21 @@ bool AggressiveDCEPass::IsVarOfStorage(uint32_t varId, uint32_t storageClass) { storageClass; } -bool AggressiveDCEPass::IsLocalVar(uint32_t varId) { +bool AggressiveDCEPass::IsLocalVar(uint32_t varId, Function* func) { if (IsVarOfStorage(varId, SpvStorageClassFunction)) { return true; } - if (!private_like_local_) { + + if (!IsVarOfStorage(varId, SpvStorageClassPrivate) && + !IsVarOfStorage(varId, SpvStorageClassWorkgroup)) { return false; } - return IsVarOfStorage(varId, SpvStorageClassPrivate) || - IsVarOfStorage(varId, SpvStorageClassWorkgroup); + // For a variable in the Private or WorkGroup storage class, the variable will + // get a new instance for every call to an entry point. If the entry point + // does not have a call, then no other function can read or write to that + // instance of the variable. + return IsEntryPointWithNoCalls(func); } void AggressiveDCEPass::AddStores(Function* func, uint32_t ptrId) { @@ -140,20 +147,21 @@ void AggressiveDCEPass::AddStores(Function* func, uint32_t ptrId) { bool AggressiveDCEPass::AllExtensionsSupported() const { // If any extension not in allowlist, return false for (auto& ei : get_module()->extensions()) { - const char* extName = - reinterpret_cast(&ei.GetInOperand(0).words[0]); + const std::string extName = ei.GetInOperand(0).AsString(); if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) return false; } - return true; -} - -bool AggressiveDCEPass::IsDead(Instruction* inst) { - if (IsLive(inst)) return false; - if ((inst->IsBranch() || inst->opcode() == SpvOpUnreachable) && - !IsStructuredHeader(context()->get_instr_block(inst), nullptr, nullptr, - nullptr)) - return false; + // Only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise + // around unknown extended instruction sets even if they are non-semantic + for (auto& inst : context()->module()->ext_inst_imports()) { + assert(inst.opcode() == SpvOpExtInstImport && + "Expecting an import of an extension's instruction set."); + const std::string extension_name = inst.GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, "NonSemantic.") && + extension_name != "NonSemantic.Shader.DebugInfo.100") { + return false; + } + } return true; } @@ -173,12 +181,12 @@ bool AggressiveDCEPass::IsTargetDead(Instruction* inst) { }); return dead; } - return IsDead(tInst); + return !IsLive(tInst); } void AggressiveDCEPass::ProcessLoad(Function* func, uint32_t varId) { // Only process locals - if (!IsLocalVar(varId)) return; + if (!IsLocalVar(varId, func)) return; // Return if already processed if (live_local_vars_.find(varId) != live_local_vars_.end()) return; // Mark all stores to varId as live @@ -187,66 +195,6 @@ void AggressiveDCEPass::ProcessLoad(Function* func, uint32_t varId) { live_local_vars_.insert(varId); } -bool AggressiveDCEPass::IsStructuredHeader(BasicBlock* bp, - Instruction** mergeInst, - Instruction** branchInst, - uint32_t* mergeBlockId) { - if (!bp) return false; - Instruction* mi = bp->GetMergeInst(); - if (mi == nullptr) return false; - Instruction* bri = &*bp->tail(); - if (branchInst != nullptr) *branchInst = bri; - if (mergeInst != nullptr) *mergeInst = mi; - if (mergeBlockId != nullptr) *mergeBlockId = mi->GetSingleWordInOperand(0); - return true; -} - -void AggressiveDCEPass::ComputeBlock2HeaderMaps( - std::list& structuredOrder) { - block2headerBranch_.clear(); - header2nextHeaderBranch_.clear(); - branch2merge_.clear(); - structured_order_index_.clear(); - std::stack currentHeaderBranch; - currentHeaderBranch.push(nullptr); - uint32_t currentMergeBlockId = 0; - uint32_t index = 0; - for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); - ++bi, ++index) { - structured_order_index_[*bi] = index; - // If this block is the merge block of the current control construct, - // we are leaving the current construct so we must update state - if ((*bi)->id() == currentMergeBlockId) { - currentHeaderBranch.pop(); - Instruction* chb = currentHeaderBranch.top(); - if (chb != nullptr) - currentMergeBlockId = branch2merge_[chb]->GetSingleWordInOperand(0); - } - Instruction* mergeInst; - Instruction* branchInst; - uint32_t mergeBlockId; - bool is_header = - IsStructuredHeader(*bi, &mergeInst, &branchInst, &mergeBlockId); - // Map header block to next enclosing header. - if (is_header) header2nextHeaderBranch_[*bi] = currentHeaderBranch.top(); - // If this is a loop header, update state first so the block will map to - // itself. - if (is_header && mergeInst->opcode() == SpvOpLoopMerge) { - currentHeaderBranch.push(branchInst); - branch2merge_[branchInst] = mergeInst; - currentMergeBlockId = mergeBlockId; - } - // Map the block to the current construct. - block2headerBranch_[*bi] = currentHeaderBranch.top(); - // If this is an if header, update state so following blocks map to the if. - if (is_header && mergeInst->opcode() == SpvOpSelectionMerge) { - currentHeaderBranch.push(branchInst); - branch2merge_[branchInst] = mergeInst; - currentMergeBlockId = mergeBlockId; - } - } -} - void AggressiveDCEPass::AddBranch(uint32_t labelId, BasicBlock* bp) { std::unique_ptr newBranch( new Instruction(context(), SpvOpBranch, 0, 0, @@ -262,23 +210,18 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist( mergeInst->opcode() == SpvOpLoopMerge); BasicBlock* header = context()->get_instr_block(mergeInst); - uint32_t headerIndex = structured_order_index_[header]; const uint32_t mergeId = mergeInst->GetSingleWordInOperand(0); - BasicBlock* merge = context()->get_instr_block(mergeId); - uint32_t mergeIndex = structured_order_index_[merge]; - get_def_use_mgr()->ForEachUser( - mergeId, [headerIndex, mergeIndex, this](Instruction* user) { - if (!user->IsBranch()) return; - BasicBlock* block = context()->get_instr_block(user); - uint32_t index = structured_order_index_[block]; - if (headerIndex < index && index < mergeIndex) { - // This is a break from the loop. - AddToWorklist(user); - // Add branch's merge if there is one. - Instruction* userMerge = branch2merge_[user]; - if (userMerge != nullptr) AddToWorklist(userMerge); - } - }); + get_def_use_mgr()->ForEachUser(mergeId, [header, this](Instruction* user) { + if (!user->IsBranch()) return; + BasicBlock* block = context()->get_instr_block(user); + if (BlockIsInConstruct(header, block)) { + // This is a break from the loop. + AddToWorklist(user); + // Add branch's merge if there is one. + Instruction* userMerge = GetMergeInstruction(user); + if (userMerge != nullptr) AddToWorklist(userMerge); + } + }); if (mergeInst->opcode() != SpvOpLoopMerge) { return; @@ -292,7 +235,7 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist( if (op == SpvOpBranchConditional || op == SpvOpSwitch) { // A conditional branch or switch can only be a continue if it does not // have a merge instruction or its merge block is not the continue block. - Instruction* hdrMerge = branch2merge_[user]; + Instruction* hdrMerge = GetMergeInstruction(user); if (hdrMerge != nullptr && hdrMerge->opcode() == SpvOpSelectionMerge) { uint32_t hdrMergeId = hdrMerge->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx); @@ -304,9 +247,9 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist( // An unconditional branch can only be a continue if it is not // branching to its own merge block. BasicBlock* blk = context()->get_instr_block(user); - Instruction* hdrBranch = block2headerBranch_[blk]; + Instruction* hdrBranch = GetHeaderBranch(blk); if (hdrBranch == nullptr) return; - Instruction* hdrMerge = branch2merge_[hdrBranch]; + Instruction* hdrMerge = GetMergeInstruction(hdrBranch); if (hdrMerge->opcode() == SpvOpLoopMerge) return; uint32_t hdrMergeId = hdrMerge->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx); @@ -319,254 +262,36 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist( } bool AggressiveDCEPass::AggressiveDCE(Function* func) { - // Mark function parameters as live. - AddToWorklist(&func->DefInst()); - func->ForEachParam( - [this](const Instruction* param) { - AddToWorklist(const_cast(param)); - }, - false); - - // Compute map from block to controlling conditional branch - std::list structuredOrder; - cfg()->ComputeStructuredOrder(func, &*func->begin(), &structuredOrder); - ComputeBlock2HeaderMaps(structuredOrder); - bool modified = false; - // Add instructions with external side effects to worklist. Also add branches - // EXCEPT those immediately contained in an "if" selection construct or a loop - // or continue construct. - // TODO(greg-lunarg): Handle Frexp, Modf more optimally - call_in_func_ = false; - func_is_entry_point_ = false; - private_stores_.clear(); + std::list structured_order; + cfg()->ComputeStructuredOrder(func, &*func->begin(), &structured_order); 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. - std::stack assume_branches_live; - std::stack currentMergeBlockId; - // Push sentinel values on stack for when outside of any control flow. - assume_branches_live.push(true); - currentMergeBlockId.push(0); - for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) { - // If exiting if or loop, update stacks - if ((*bi)->id() == currentMergeBlockId.top()) { - assume_branches_live.pop(); - currentMergeBlockId.pop(); - } - for (auto ii = (*bi)->begin(); ii != (*bi)->end(); ++ii) { - SpvOp op = ii->opcode(); - switch (op) { - case SpvOpStore: { - uint32_t varId; - (void)GetPtr(&*ii, &varId); - // Mark stores as live if their variable is not function scope - // and is not private scope. Remember private stores for possible - // later inclusion. We cannot call IsLocalVar at this point because - // private_like_local_ has not been set yet. - if (IsVarOfStorage(varId, SpvStorageClassPrivate) || - IsVarOfStorage(varId, SpvStorageClassWorkgroup)) - private_stores_.push_back(&*ii); - else if (!IsVarOfStorage(varId, SpvStorageClassFunction)) - AddToWorklist(&*ii); - } break; - case SpvOpCopyMemory: - case SpvOpCopyMemorySized: { - uint32_t varId; - (void)GetPtr(ii->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx), - &varId); - if (IsVarOfStorage(varId, SpvStorageClassPrivate) || - IsVarOfStorage(varId, SpvStorageClassWorkgroup)) - private_stores_.push_back(&*ii); - else if (!IsVarOfStorage(varId, SpvStorageClassFunction)) - AddToWorklist(&*ii); - } break; - case SpvOpLoopMerge: { - assume_branches_live.push(false); - currentMergeBlockId.push( - ii->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx)); - } break; - case SpvOpSelectionMerge: { - assume_branches_live.push(false); - currentMergeBlockId.push( - ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx)); - } break; - case SpvOpSwitch: - case SpvOpBranch: - case SpvOpBranchConditional: - case SpvOpUnreachable: { - if (assume_branches_live.top()) { - AddToWorklist(&*ii); - } - } break; - default: { - // Function calls, atomics, function params, function returns, etc. - // TODO(greg-lunarg): function calls live only if write to non-local - if (!ii->IsOpcodeSafeToDelete()) { - AddToWorklist(&*ii); - } - // Remember function calls - if (op == SpvOpFunctionCall) call_in_func_ = true; - } break; - } - } - } - // See if current function is an entry point - for (auto& ei : get_module()->entry_points()) { - if (ei.GetSingleWordInOperand(kEntryPointFunctionIdInIdx) == - func->result_id()) { - func_is_entry_point_ = true; - break; - } - } - // If the current function is an entry point and has no function calls, - // we can optimize private variables as locals - private_like_local_ = func_is_entry_point_ && !call_in_func_; - // If privates are not like local, add their stores to worklist - if (!private_like_local_) - for (auto& ps : private_stores_) AddToWorklist(ps); - // Perform closure on live instruction set. - while (!worklist_.empty()) { - Instruction* liveInst = worklist_.front(); - // Add all operand instructions if not already live - liveInst->ForEachInId([&liveInst, this](const uint32_t* iid) { - Instruction* inInst = get_def_use_mgr()->GetDef(*iid); - // Do not add label if an operand of a branch. This is not needed - // as part of live code discovery and can create false live code, - // for example, the branch to a header of a loop. - if (inInst->opcode() == SpvOpLabel && liveInst->IsBranch()) return; - AddToWorklist(inInst); - }); - if (liveInst->type_id() != 0) { - AddToWorklist(get_def_use_mgr()->GetDef(liveInst->type_id())); - } - // If in a structured if or loop construct, add the controlling - // conditional branch and its merge. - BasicBlock* blk = context()->get_instr_block(liveInst); - Instruction* branchInst = block2headerBranch_[blk]; - if (branchInst != nullptr) { - AddToWorklist(branchInst); - Instruction* mergeInst = branch2merge_[branchInst]; - AddToWorklist(mergeInst); - } - // If the block is a header, add the next outermost controlling - // conditional branch and its merge. - Instruction* nextBranchInst = header2nextHeaderBranch_[blk]; - if (nextBranchInst != nullptr) { - AddToWorklist(nextBranchInst); - Instruction* mergeInst = branch2merge_[nextBranchInst]; - AddToWorklist(mergeInst); - } - // If local load, add all variable's stores if variable not already live - if (liveInst->opcode() == SpvOpLoad || liveInst->IsAtomicWithLoad()) { - uint32_t varId; - (void)GetPtr(liveInst, &varId); - if (varId != 0) { - ProcessLoad(func, varId); - } - // Process memory copies like loads - } else if (liveInst->opcode() == SpvOpCopyMemory || - liveInst->opcode() == SpvOpCopyMemorySized) { - uint32_t varId; - (void)GetPtr(liveInst->GetSingleWordInOperand(kCopyMemorySourceAddrInIdx), - &varId); - if (varId != 0) { - ProcessLoad(func, varId); - } - // If DebugDeclare, process as load of variable - } else if (liveInst->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugDeclare) { - uint32_t varId = - liveInst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); - ProcessLoad(func, varId); - // If DebugValue with Deref, process as load of variable - } else if (liveInst->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugValue) { - uint32_t varId = context() - ->get_debug_info_mgr() - ->GetVariableIdOfDebugValueUsedForDeclare(liveInst); - if (varId != 0) ProcessLoad(func, varId); - // If merge, add other branches that are part of its control structure - } else if (liveInst->opcode() == SpvOpLoopMerge || - liveInst->opcode() == SpvOpSelectionMerge) { - AddBreaksAndContinuesToWorklist(liveInst); - // If function call, treat as if it loads from all pointer arguments - } else if (liveInst->opcode() == SpvOpFunctionCall) { - liveInst->ForEachInId([this, func](const uint32_t* iid) { - // Skip non-ptr args - if (!IsPtr(*iid)) return; - uint32_t varId; - (void)GetPtr(*iid, &varId); - ProcessLoad(func, varId); - }); - // If function parameter, treat as if it's result id is loaded from - } else if (liveInst->opcode() == SpvOpFunctionParameter) { - 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(func, varId); - } - } + InitializeWorkList(func, structured_order); + ProcessWorkList(func); + return KillDeadInstructions(func, structured_order); +} - // Add OpDecorateId instructions that apply to this instruction to the work - // list. We use the decoration manager to look through the group - // decorations to get to the OpDecorate* instructions themselves. - auto decorations = - get_decoration_mgr()->GetDecorationsFor(liveInst->result_id(), false); - for (Instruction* dec : decorations) { - // We only care about OpDecorateId instructions because the are the only - // decorations that will reference an id that will have to be kept live - // because of that use. - if (dec->opcode() != SpvOpDecorateId) { - continue; - } - if (dec->GetSingleWordInOperand(1) == - SpvDecorationHlslCounterBufferGOOGLE) { - // These decorations should not force the use id to be live. It will be - // removed if either the target or the in operand are dead. - continue; - } - AddToWorklist(dec); - } - - // Add DebugScope and DebugInlinedAt for |liveInst| to the work list. - if (liveInst->GetDebugScope().GetLexicalScope() != kNoDebugScope) { - auto* scope = get_def_use_mgr()->GetDef( - liveInst->GetDebugScope().GetLexicalScope()); - AddToWorklist(scope); - } - if (liveInst->GetDebugInlinedAt() != kNoInlinedAt) { - auto* inlined_at = - get_def_use_mgr()->GetDef(liveInst->GetDebugInlinedAt()); - AddToWorklist(inlined_at); - } - worklist_.pop(); - } - - // Kill dead instructions and remember dead blocks - for (auto bi = structuredOrder.begin(); bi != structuredOrder.end();) { - uint32_t mergeBlockId = 0; - (*bi)->ForEachInst([this, &modified, &mergeBlockId](Instruction* inst) { - if (!IsDead(inst)) return; +bool AggressiveDCEPass::KillDeadInstructions( + const Function* func, std::list& structured_order) { + bool modified = false; + for (auto bi = structured_order.begin(); bi != structured_order.end();) { + uint32_t merge_block_id = 0; + (*bi)->ForEachInst([this, &modified, &merge_block_id](Instruction* inst) { + if (IsLive(inst)) return; if (inst->opcode() == SpvOpLabel) return; // If dead instruction is selection merge, remember merge block // for new branch at end of block if (inst->opcode() == SpvOpSelectionMerge || inst->opcode() == SpvOpLoopMerge) - mergeBlockId = inst->GetSingleWordInOperand(0); + merge_block_id = inst->GetSingleWordInOperand(0); to_kill_.push_back(inst); modified = true; }); // If a structured if or loop was deleted, add a branch to its merge // block, and traverse to the merge block and continue processing there. // We know the block still exists because the label is not deleted. - if (mergeBlockId != 0) { - AddBranch(mergeBlockId, *bi); - for (++bi; (*bi)->id() != mergeBlockId; ++bi) { + if (merge_block_id != 0) { + AddBranch(merge_block_id, *bi); + for (++bi; (*bi)->id() != merge_block_id; ++bi) { } auto merge_terminator = (*bi)->terminator(); @@ -589,13 +314,254 @@ bool AggressiveDCEPass::AggressiveDCE(Function* func) { live_insts_.Set(merge_terminator->unique_id()); } } else { + Instruction* inst = (*bi)->terminator(); + if (!IsLive(inst)) { + // If the terminator is not live, this block has no live instructions, + // and it will be unreachable. + AddUnreachable(*bi); + } ++bi; } } - return modified; } +void AggressiveDCEPass::ProcessWorkList(Function* func) { + while (!worklist_.empty()) { + Instruction* live_inst = worklist_.front(); + worklist_.pop(); + AddOperandsToWorkList(live_inst); + MarkBlockAsLive(live_inst); + MarkLoadedVariablesAsLive(func, live_inst); + AddDecorationsToWorkList(live_inst); + AddDebugInstructionsToWorkList(live_inst); + } +} + +void AggressiveDCEPass::AddDebugScopeToWorkList(const Instruction* inst) { + auto scope = inst->GetDebugScope(); + auto lex_scope_id = scope.GetLexicalScope(); + if (lex_scope_id != kNoDebugScope) + AddToWorklist(get_def_use_mgr()->GetDef(lex_scope_id)); + auto inlined_at_id = scope.GetInlinedAt(); + if (inlined_at_id != kNoInlinedAt) + AddToWorklist(get_def_use_mgr()->GetDef(inlined_at_id)); +} + +void AggressiveDCEPass::AddDebugInstructionsToWorkList( + const Instruction* inst) { + for (auto& line_inst : inst->dbg_line_insts()) { + if (line_inst.IsDebugLineInst()) { + AddOperandsToWorkList(&line_inst); + } + AddDebugScopeToWorkList(&line_inst); + } + AddDebugScopeToWorkList(inst); +} + +void AggressiveDCEPass::AddDecorationsToWorkList(const Instruction* inst) { + // Add OpDecorateId instructions that apply to this instruction to the work + // list. We use the decoration manager to look through the group + // decorations to get to the OpDecorate* instructions themselves. + auto decorations = + get_decoration_mgr()->GetDecorationsFor(inst->result_id(), false); + for (Instruction* dec : decorations) { + // We only care about OpDecorateId instructions because the are the only + // decorations that will reference an id that will have to be kept live + // because of that use. + if (dec->opcode() != SpvOpDecorateId) { + continue; + } + if (dec->GetSingleWordInOperand(1) == + SpvDecorationHlslCounterBufferGOOGLE) { + // These decorations should not force the use id to be live. It will be + // removed if either the target or the in operand are dead. + continue; + } + AddToWorklist(dec); + } +} + +void AggressiveDCEPass::MarkLoadedVariablesAsLive(Function* func, + Instruction* inst) { + std::vector live_variables = GetLoadedVariables(inst); + for (uint32_t var_id : live_variables) { + ProcessLoad(func, var_id); + } +} + +std::vector AggressiveDCEPass::GetLoadedVariables(Instruction* inst) { + if (inst->opcode() == SpvOpFunctionCall) { + return GetLoadedVariablesFromFunctionCall(inst); + } + uint32_t var_id = GetLoadedVariableFromNonFunctionCalls(inst); + if (var_id == 0) { + return {}; + } + return {var_id}; +} + +uint32_t AggressiveDCEPass::GetLoadedVariableFromNonFunctionCalls( + Instruction* inst) { + std::vector live_variables; + if (inst->IsAtomicWithLoad()) { + return GetVariableId(inst->GetSingleWordInOperand(kLoadSourceAddrInIdx)); + } + + switch (inst->opcode()) { + case SpvOpLoad: + case SpvOpImageTexelPointer: + return GetVariableId(inst->GetSingleWordInOperand(kLoadSourceAddrInIdx)); + case SpvOpCopyMemory: + case SpvOpCopyMemorySized: + return GetVariableId( + inst->GetSingleWordInOperand(kCopyMemorySourceAddrInIdx)); + default: + break; + } + + switch (inst->GetCommonDebugOpcode()) { + case CommonDebugInfoDebugDeclare: + return inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); + case CommonDebugInfoDebugValue: { + analysis::DebugInfoManager* debug_info_mgr = + context()->get_debug_info_mgr(); + return debug_info_mgr->GetVariableIdOfDebugValueUsedForDeclare(inst); + } + default: + break; + } + return 0; +} + +std::vector AggressiveDCEPass::GetLoadedVariablesFromFunctionCall( + const Instruction* inst) { + assert(inst->opcode() == SpvOpFunctionCall); + std::vector live_variables; + inst->ForEachInId([this, &live_variables](const uint32_t* operand_id) { + if (!IsPtr(*operand_id)) return; + uint32_t var_id = GetVariableId(*operand_id); + live_variables.push_back(var_id); + }); + return live_variables; +} + +uint32_t AggressiveDCEPass::GetVariableId(uint32_t ptr_id) { + assert(IsPtr(ptr_id) && + "Cannot get the variable when input is not a pointer."); + uint32_t varId = 0; + (void)GetPtr(ptr_id, &varId); + return varId; +} + +void AggressiveDCEPass::MarkBlockAsLive(Instruction* inst) { + BasicBlock* basic_block = context()->get_instr_block(inst); + if (basic_block == nullptr) { + return; + } + + // If we intend to keep this instruction, we need the block label and + // block terminator to have a valid block for the instruction. + AddToWorklist(basic_block->GetLabelInst()); + + // We need to mark the successors blocks that follow as live. If this is + // header of the merge construct, the construct may be folded, but we will + // definitely need the merge label. If it is not a construct, the terminator + // must be live, and the successor blocks will be marked as live when + // processing the terminator. + uint32_t merge_id = basic_block->MergeBlockIdIfAny(); + if (merge_id == 0) { + AddToWorklist(basic_block->terminator()); + } else { + AddToWorklist(context()->get_def_use_mgr()->GetDef(merge_id)); + } + + // Mark the structured control flow constructs that contains this block as + // live. If |inst| is an instruction in the loop header, then it is part of + // the loop, so the loop construct must be live. We exclude the label because + // it does not matter how many times it is executed. This could be extended + // to more instructions, but we will need it for now. + if (inst->opcode() != SpvOpLabel) + MarkLoopConstructAsLiveIfLoopHeader(basic_block); + + Instruction* next_branch_inst = GetBranchForNextHeader(basic_block); + if (next_branch_inst != nullptr) { + AddToWorklist(next_branch_inst); + Instruction* mergeInst = GetMergeInstruction(next_branch_inst); + AddToWorklist(mergeInst); + } + + if (inst->opcode() == SpvOpLoopMerge || + inst->opcode() == SpvOpSelectionMerge) { + AddBreaksAndContinuesToWorklist(inst); + } +} +void AggressiveDCEPass::MarkLoopConstructAsLiveIfLoopHeader( + BasicBlock* basic_block) { + // If this is the header for a loop, then loop structure needs to keep as well + // because the loop header is also part of the loop. + Instruction* merge_inst = basic_block->GetLoopMergeInst(); + if (merge_inst != nullptr) { + AddToWorklist(basic_block->terminator()); + AddToWorklist(merge_inst); + } +} + +void AggressiveDCEPass::AddOperandsToWorkList(const Instruction* inst) { + inst->ForEachInId([this](const uint32_t* iid) { + Instruction* inInst = get_def_use_mgr()->GetDef(*iid); + AddToWorklist(inInst); + }); + if (inst->type_id() != 0) { + AddToWorklist(get_def_use_mgr()->GetDef(inst->type_id())); + } +} + +void AggressiveDCEPass::InitializeWorkList( + Function* func, std::list& structured_order) { + AddToWorklist(&func->DefInst()); + MarkFunctionParameterAsLive(func); + MarkFirstBlockAsLive(func); + + // Add instructions with external side effects to the worklist. Also add + // branches that are not attached to a structured construct. + // TODO(s-perron): The handling of branch seems to be adhoc. This needs to be + // cleaned up. + for (auto& bi : structured_order) { + for (auto ii = bi->begin(); ii != bi->end(); ++ii) { + SpvOp op = ii->opcode(); + if (ii->IsBranch()) { + continue; + } + switch (op) { + case SpvOpStore: { + uint32_t var_id = 0; + (void)GetPtr(&*ii, &var_id); + if (!IsLocalVar(var_id, func)) AddToWorklist(&*ii); + } break; + case SpvOpCopyMemory: + case SpvOpCopyMemorySized: { + uint32_t var_id = 0; + uint32_t target_addr_id = + ii->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx); + (void)GetPtr(target_addr_id, &var_id); + if (!IsLocalVar(var_id, func)) AddToWorklist(&*ii); + } break; + case SpvOpLoopMerge: + case SpvOpSelectionMerge: + case SpvOpUnreachable: + break; + default: { + // Function calls, atomics, function params, function returns, etc. + if (!ii->IsOpcodeSafeToDelete()) { + AddToWorklist(&*ii); + } + } break; + } + } + } +} + void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() { // Keep all execution modes. for (auto& exec : get_module()->execution_modes()) { @@ -603,11 +569,7 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() { } // Keep all entry points. for (auto& entry : get_module()->entry_points()) { - if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { - // In SPIR-V 1.4 and later, entry points must list all global variables - // used. DCE can still remove non-input/output variables and update the - // interface list. Mark the entry point as live and inputs and outputs as - // live, but defer decisions all other interfaces. + if (!preserve_interface_) { live_insts_.Set(entry.unique_id()); // The actual function is live always. AddToWorklist( @@ -615,8 +577,9 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() { for (uint32_t i = 3; i < entry.NumInOperands(); ++i) { auto* var = get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i)); auto storage_class = var->GetSingleWordInOperand(0u); - if (storage_class == SpvStorageClassInput || - storage_class == SpvStorageClassOutput) { + // Vulkan support outputs without an associated input, but not inputs + // without an associated output. + if (storage_class == SpvStorageClassOutput) { AddToWorklist(var); } } @@ -650,16 +613,35 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() { } // For each DebugInfo GlobalVariable keep all operands except the Variable. - // Later, if the variable is dead, we will set the operand to DebugInfoNone. + // Later, if the variable is killed with KillInst(), we will set the operand + // to DebugInfoNone. Create and save DebugInfoNone now for this possible + // later use. This is slightly unoptimal, but it avoids generating it during + // instruction killing when the module is not consistent. + bool debug_global_seen = false; for (auto& dbg : get_module()->ext_inst_debuginfo()) { - if (dbg.GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugGlobalVariable) + if (dbg.GetCommonDebugOpcode() != CommonDebugInfoDebugGlobalVariable) continue; + debug_global_seen = true; dbg.ForEachInId([this](const uint32_t* iid) { - Instruction* inInst = get_def_use_mgr()->GetDef(*iid); - if (inInst->opcode() == SpvOpVariable) return; - AddToWorklist(inInst); + Instruction* in_inst = get_def_use_mgr()->GetDef(*iid); + if (in_inst->opcode() == SpvOpVariable) return; + AddToWorklist(in_inst); }); } + if (debug_global_seen) { + auto dbg_none = context()->get_debug_info_mgr()->GetDebugInfoNone(); + AddToWorklist(dbg_none); + } + + // Add top level DebugInfo to worklist + for (auto& dbg : get_module()->ext_inst_debuginfo()) { + auto op = dbg.GetShader100DebugOpcode(); + if (op == NonSemanticShaderDebugInfo100DebugCompilationUnit || + op == NonSemanticShaderDebugInfo100DebugEntryPoint || + op == NonSemanticShaderDebugInfo100DebugSourceContinued) { + AddToWorklist(&dbg); + } + } } Pass::Status AggressiveDCEPass::ProcessImpl() { @@ -689,9 +671,14 @@ Pass::Status AggressiveDCEPass::ProcessImpl() { InitializeModuleScopeLiveInstructions(); - // Process all entry point functions. - ProcessFunction pfn = [this](Function* fp) { return AggressiveDCE(fp); }; - modified |= context()->ProcessEntryPointCallTree(pfn); + // Run |AggressiveDCE| on the remaining functions. The order does not matter, + // since |AggressiveDCE| is intra-procedural. This can mean that function + // will become dead if all function call to them are removed. These dead + // function will still be in the module after this pass. We expect this to be + // rare. + for (Function& fp : *context()->module()) { + modified |= AggressiveDCE(&fp); + } // If the decoration manager is kept live then the context will try to keep it // up to date. ADCE deals with group decorations by changing the operands in @@ -717,22 +704,22 @@ Pass::Status AggressiveDCEPass::ProcessImpl() { } // Cleanup all CFG including all unreachable blocks. - ProcessFunction cleanup = [this](Function* f) { return CFGCleanup(f); }; - modified |= context()->ProcessEntryPointCallTree(cleanup); + for (Function& fp : *context()->module()) { + modified |= CFGCleanup(&fp); + } return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } bool AggressiveDCEPass::EliminateDeadFunctions() { // Identify live functions first. Those that are not live - // are dead. ADCE is disabled for non-shaders so we do not check for exported - // functions here. + // are dead. std::unordered_set live_function_set; ProcessFunction mark_live = [&live_function_set](Function* fp) { live_function_set.insert(fp); return false; }; - context()->ProcessEntryPointCallTree(mark_live); + context()->ProcessReachableCallTree(mark_live); bool modified = false; for (auto funcIter = get_module()->begin(); @@ -798,7 +785,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() { uint32_t counter_buffer_id = annotation->GetSingleWordInOperand(2); Instruction* counter_buffer_inst = get_def_use_mgr()->GetDef(counter_buffer_id); - if (IsDead(counter_buffer_inst)) { + if (!IsLive(counter_buffer_inst)) { context()->KillInst(annotation); modified = true; } @@ -813,7 +800,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() { for (uint32_t i = 1; i < annotation->NumOperands();) { Instruction* opInst = get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i)); - if (IsDead(opInst)) { + if (!IsLive(opInst)) { // Don't increment |i|. annotation->RemoveOperand(i); modified = true; @@ -840,7 +827,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() { for (uint32_t i = 1; i < annotation->NumOperands();) { Instruction* opInst = get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i)); - if (IsDead(opInst)) { + if (!IsLive(opInst)) { // Don't increment |i|. annotation->RemoveOperand(i + 1); annotation->RemoveOperand(i); @@ -874,14 +861,13 @@ bool AggressiveDCEPass::ProcessGlobalValues() { } for (auto& dbg : get_module()->ext_inst_debuginfo()) { - if (!IsDead(&dbg)) continue; + if (IsLive(&dbg)) continue; // Save GlobalVariable if its variable is live, otherwise null out variable // index - if (dbg.GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugGlobalVariable) { + if (dbg.GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) { auto var_id = dbg.GetSingleWordOperand(kGlobalVariableVariableIndex); Instruction* var_inst = get_def_use_mgr()->GetDef(var_id); - if (!IsDead(var_inst)) continue; + if (IsLive(var_inst)) continue; context()->ForgetUses(&dbg); dbg.SetOperand( kGlobalVariableVariableIndex, @@ -896,7 +882,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() { // Since ADCE is disabled for non-shaders, we don't check for export linkage // attributes here. for (auto& val : get_module()->types_values()) { - if (IsDead(&val)) { + if (!IsLive(&val)) { // Save forwarded pointer if pointer is live since closure does not mark // this live as it does not have a result id. This is a little too // conservative since it is not known if the structure type that needed @@ -904,14 +890,14 @@ bool AggressiveDCEPass::ProcessGlobalValues() { if (val.opcode() == SpvOpTypeForwardPointer) { uint32_t ptr_ty_id = val.GetSingleWordInOperand(0); Instruction* ptr_ty_inst = get_def_use_mgr()->GetDef(ptr_ty_id); - if (!IsDead(ptr_ty_inst)) continue; + if (IsLive(ptr_ty_inst)) continue; } to_kill_.push_back(&val); modified = true; } } - if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { + if (!preserve_interface_) { // Remove the dead interface variables from the entry point interface list. for (auto& entry : get_module()->entry_points()) { std::vector new_operands; @@ -922,7 +908,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() { } else { auto* var = get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i)); - if (!IsDead(var)) { + if (IsLive(var)) { new_operands.push_back(entry.GetInOperand(i)); } } @@ -937,8 +923,6 @@ bool AggressiveDCEPass::ProcessGlobalValues() { return modified; } -AggressiveDCEPass::AggressiveDCEPass() = default; - Pass::Status AggressiveDCEPass::Process() { // Initialize extensions allowlist InitExtensions(); @@ -996,8 +980,124 @@ void AggressiveDCEPass::InitExtensions() { "SPV_EXT_physical_storage_buffer", "SPV_KHR_terminate_invocation", "SPV_KHR_shader_clock", + "SPV_KHR_vulkan_memory_model", + "SPV_KHR_subgroup_uniform_control_flow", + "SPV_KHR_integer_dot_product", + "SPV_EXT_shader_image_int64", + "SPV_KHR_non_semantic_info", + "SPV_KHR_uniform_group_instructions", + "SPV_KHR_fragment_shader_barycentric", }); } +Instruction* AggressiveDCEPass::GetHeaderBranch(BasicBlock* blk) { + if (blk == nullptr) { + return nullptr; + } + BasicBlock* header_block = GetHeaderBlock(blk); + if (header_block == nullptr) { + return nullptr; + } + return header_block->terminator(); +} + +BasicBlock* AggressiveDCEPass::GetHeaderBlock(BasicBlock* blk) const { + if (blk == nullptr) { + return nullptr; + } + + BasicBlock* header_block = nullptr; + if (blk->IsLoopHeader()) { + header_block = blk; + } else { + uint32_t header = + context()->GetStructuredCFGAnalysis()->ContainingConstruct(blk->id()); + header_block = context()->get_instr_block(header); + } + return header_block; +} + +Instruction* AggressiveDCEPass::GetMergeInstruction(Instruction* inst) { + BasicBlock* bb = context()->get_instr_block(inst); + if (bb == nullptr) { + return nullptr; + } + return bb->GetMergeInst(); +} + +Instruction* AggressiveDCEPass::GetBranchForNextHeader(BasicBlock* blk) { + if (blk == nullptr) { + return nullptr; + } + + if (blk->IsLoopHeader()) { + uint32_t header = + context()->GetStructuredCFGAnalysis()->ContainingConstruct(blk->id()); + blk = context()->get_instr_block(header); + } + return GetHeaderBranch(blk); +} + +void AggressiveDCEPass::MarkFunctionParameterAsLive(const Function* func) { + func->ForEachParam( + [this](const Instruction* param) { + AddToWorklist(const_cast(param)); + }, + false); +} + +bool AggressiveDCEPass::BlockIsInConstruct(BasicBlock* header_block, + BasicBlock* bb) { + if (bb == nullptr || header_block == nullptr) { + return false; + } + + uint32_t current_header = bb->id(); + while (current_header != 0) { + if (current_header == header_block->id()) return true; + current_header = context()->GetStructuredCFGAnalysis()->ContainingConstruct( + current_header); + } + return false; +} + +bool AggressiveDCEPass::IsEntryPointWithNoCalls(Function* func) { + auto cached_result = entry_point_with_no_calls_cache_.find(func->result_id()); + if (cached_result != entry_point_with_no_calls_cache_.end()) { + return cached_result->second; + } + bool result = IsEntryPoint(func) && !HasCall(func); + entry_point_with_no_calls_cache_[func->result_id()] = result; + return result; +} + +bool AggressiveDCEPass::IsEntryPoint(Function* func) { + for (const Instruction& entry_point : get_module()->entry_points()) { + uint32_t entry_point_id = + entry_point.GetSingleWordInOperand(kEntryPointFunctionIdInIdx); + if (entry_point_id == func->result_id()) { + return true; + } + } + return false; +} + +bool AggressiveDCEPass::HasCall(Function* func) { + return !func->WhileEachInst( + [](Instruction* inst) { return inst->opcode() != SpvOpFunctionCall; }); +} + +void AggressiveDCEPass::MarkFirstBlockAsLive(Function* func) { + BasicBlock* first_block = &*func->begin(); + MarkBlockAsLive(first_block->GetLabelInst()); +} + +void AggressiveDCEPass::AddUnreachable(BasicBlock*& block) { + InstructionBuilder builder( + context(), block, + IRContext::kAnalysisInstrToBlockMapping | IRContext::kAnalysisDefUse); + builder.AddUnreachable(); +} + } // namespace opt } // namespace spvtools diff --git a/source/opt/aggressive_dead_code_elim_pass.h b/source/opt/aggressive_dead_code_elim_pass.h index f02e729f..c1291dc4 100644 --- a/source/opt/aggressive_dead_code_elim_pass.h +++ b/source/opt/aggressive_dead_code_elim_pass.h @@ -44,7 +44,9 @@ class AggressiveDCEPass : public MemPass { using GetBlocksFunction = std::function*(const BasicBlock*)>; - AggressiveDCEPass(); + AggressiveDCEPass(bool preserve_interface = false) + : preserve_interface_(preserve_interface) {} + const char* name() const override { return "eliminate-dead-code-aggressive"; } Status Process() override; @@ -55,23 +57,26 @@ class AggressiveDCEPass : public MemPass { } private: + // Preserve entry point interface if true. All variables in interface + // will be marked live and will not be eliminated. This mode is needed by + // GPU-Assisted Validation instrumentation where a change in the interface + // is not allowed. + bool preserve_interface_; + // Return true if |varId| is a variable of |storageClass|. |varId| must either // be 0 or the result of an instruction. bool IsVarOfStorage(uint32_t varId, uint32_t storageClass); - // Return true if |varId| is variable of function storage class or is - // private variable and privates can be optimized like locals (see - // privates_like_local_). - bool IsLocalVar(uint32_t varId); + // Return true if the instance of the variable |varId| can only be access in + // |func|. For example, a function scope variable, or a private variable + // where |func| is an entry point with no function calls. + bool IsLocalVar(uint32_t varId, Function* func); // Return true if |inst| is marked live. bool IsLive(const Instruction* inst) const { return live_insts_.Get(inst->unique_id()); } - // Returns true if |inst| is dead. - bool IsDead(Instruction* inst); - // Adds entry points, execution modes and workgroup size decorations to the // worklist for processing with the first function. void InitializeModuleScopeLiveInstructions(); @@ -101,18 +106,6 @@ class AggressiveDCEPass : public MemPass { // If |varId| is local, mark all stores of varId as live. 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 - // merge block if they are not nullptr. Any of |mergeInst|, |branchInst| or - // |mergeBlockId| may be a null pointer. Returns false if |bp| is a null - // pointer. - bool IsStructuredHeader(BasicBlock* bp, Instruction** mergeInst, - Instruction** branchInst, uint32_t* mergeBlockId); - - // Initialize block2headerBranch_, header2nextHeaderBranch_, and - // branch2merge_ using |structuredOrder| to order blocks. - void ComputeBlock2HeaderMaps(std::list& structuredOrder); - // Add branch to |labelId| to end of block |bp|. void AddBranch(uint32_t labelId, BasicBlock* bp); @@ -140,14 +133,103 @@ class AggressiveDCEPass : public MemPass { Pass::Status ProcessImpl(); - // True if current function has a call instruction contained in it - bool call_in_func_; + // Adds instructions which must be kept because of they have side-effects + // that ADCE cannot model to the work list. + void InitializeWorkList(Function* func, + std::list& structured_order); - // True if current function is an entry point - bool func_is_entry_point_; + // Process each instruction in the work list by marking any instruction that + // that it depends on as live, and adding it to the work list. The work list + // will be empty at the end. + void ProcessWorkList(Function* func); - // True if current function is entry point and has no function calls. - bool private_like_local_; + // Kills any instructions in |func| that have not been marked as live. + bool KillDeadInstructions(const Function* func, + std::list& structured_order); + + // Adds the instructions that define the operands of |inst| to the work list. + void AddOperandsToWorkList(const Instruction* inst); + + // Marks all of the labels and branch that inst requires as live. + void MarkBlockAsLive(Instruction* inst); + + // Marks any variables from which |inst| may require data as live. + void MarkLoadedVariablesAsLive(Function* func, Instruction* inst); + + // Returns the id of the variable that |ptr_id| point to. |ptr_id| must be a + // value whose type is a pointer. + uint32_t GetVariableId(uint32_t ptr_id); + + // Returns all of the ids for the variables from which |inst| will load data. + std::vector GetLoadedVariables(Instruction* inst); + + // Returns all of the ids for the variables from which |inst| will load data. + // The opcode of |inst| must be OpFunctionCall. + std::vector GetLoadedVariablesFromFunctionCall( + const Instruction* inst); + + // Returns the id of the variable from which |inst| will load data. |inst| + // must not be an OpFunctionCall. Returns 0 if no data is read or the + // variable cannot be determined. Note that in logical addressing mode the + // latter is not possible for function and private storage class because there + // cannot be variable pointers pointing to those storage classes. + uint32_t GetLoadedVariableFromNonFunctionCalls(Instruction* inst); + + // Adds all decorations of |inst| to the work list. + void AddDecorationsToWorkList(const Instruction* inst); + + // Adds DebugScope instruction associated with |inst| to the work list. + void AddDebugScopeToWorkList(const Instruction* inst); + + // Adds all debug instruction associated with |inst| to the work list. + void AddDebugInstructionsToWorkList(const Instruction* inst); + + // Marks all of the OpFunctionParameter instructions in |func| as live. + void MarkFunctionParameterAsLive(const Function* func); + + // Returns the terminator instruction in the header for the innermost + // construct that contains |blk|. Returns nullptr if no such header exists. + Instruction* GetHeaderBranch(BasicBlock* blk); + + // Returns the header for the innermost construct that contains |blk|. A loop + // header will be its own header. Returns nullptr if no such header exists. + BasicBlock* GetHeaderBlock(BasicBlock* blk) const; + + // Returns the same as |GetHeaderBlock| except if |blk| is a loop header it + // will return the header of the next enclosing construct. Returns nullptr if + // no such header exists. + Instruction* GetBranchForNextHeader(BasicBlock* blk); + + // Returns the merge instruction in the same basic block as |inst|. Returns + // nullptr if one does not exist. + Instruction* GetMergeInstruction(Instruction* inst); + + // Returns true if |bb| is in the construct with header |header_block|. + bool BlockIsInConstruct(BasicBlock* header_block, BasicBlock* bb); + + // Returns true if |func| is an entry point that does not have any function + // calls. + bool IsEntryPointWithNoCalls(Function* func); + + // Returns true if |func| is an entry point. + bool IsEntryPoint(Function* func); + + // Returns true if |func| contains a function call. + bool HasCall(Function* func); + + // Marks the first block, which is the entry block, in |func| as live. + void MarkFirstBlockAsLive(Function* func); + + // Adds an OpUnreachable instruction at the end of |block|. + void AddUnreachable(BasicBlock*& block); + + // Marks the OpLoopMerge and the terminator in |basic_block| as live if + // |basic_block| is a loop header. + void MarkLoopConstructAsLiveIfLoopHeader(BasicBlock* basic_block); + + // The cached results for |IsEntryPointWithNoCalls|. It maps the function's + // result id to the return value. + std::unordered_map entry_point_with_no_calls_cache_; // Live Instruction Worklist. An instruction is added to this list // if it might have a side effect, either directly or indirectly. @@ -156,27 +238,6 @@ class AggressiveDCEPass : public MemPass { // building up the live instructions set |live_insts_|. std::queue worklist_; - // Map from block to the branch instruction in the header of the most - // immediate controlling structured if or loop. A loop header block points - // to its own branch instruction. An if-selection block points to the branch - // of an enclosing construct's header, if one exists. - std::unordered_map block2headerBranch_; - - // Map from header block to the branch instruction in the header of the - // structured construct enclosing it. - // The liveness algorithm is designed to iteratively mark as live all - // structured constructs enclosing a live instruction. - std::unordered_map header2nextHeaderBranch_; - - // Maps basic block to their index in the structured order traversal. - std::unordered_map structured_order_index_; - - // Map from branch to its associated merge instruction, if any - std::unordered_map branch2merge_; - - // Store instructions to variables of private storage - std::vector private_stores_; - // Live Instructions utils::BitVector live_insts_; diff --git a/source/opt/amd_ext_to_khr.cpp b/source/opt/amd_ext_to_khr.cpp index ccedc0bc..dd9bafda 100644 --- a/source/opt/amd_ext_to_khr.cpp +++ b/source/opt/amd_ext_to_khr.cpp @@ -584,9 +584,9 @@ bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst, } // Get the constants that will be used. - uint32_t f0_const_id = const_mgr->GetFloatConst(0.0); - uint32_t f2_const_id = const_mgr->GetFloatConst(2.0); - uint32_t f0_5_const_id = const_mgr->GetFloatConst(0.5); + uint32_t f0_const_id = const_mgr->GetFloatConstId(0.0); + uint32_t f2_const_id = const_mgr->GetFloatConstId(2.0); + uint32_t f0_5_const_id = const_mgr->GetFloatConstId(0.5); const analysis::Constant* vec_const = const_mgr->GetConstant(v2_float_type, {f0_5_const_id, f0_5_const_id}); uint32_t vec_const_id = @@ -731,12 +731,12 @@ bool ReplaceCubeFaceIndex(IRContext* ctx, Instruction* inst, } // Get the constants that will be used. - uint32_t f0_const_id = const_mgr->GetFloatConst(0.0); - uint32_t f1_const_id = const_mgr->GetFloatConst(1.0); - uint32_t f2_const_id = const_mgr->GetFloatConst(2.0); - uint32_t f3_const_id = const_mgr->GetFloatConst(3.0); - uint32_t f4_const_id = const_mgr->GetFloatConst(4.0); - uint32_t f5_const_id = const_mgr->GetFloatConst(5.0); + uint32_t f0_const_id = const_mgr->GetFloatConstId(0.0); + uint32_t f1_const_id = const_mgr->GetFloatConstId(1.0); + uint32_t f2_const_id = const_mgr->GetFloatConstId(2.0); + uint32_t f3_const_id = const_mgr->GetFloatConstId(3.0); + uint32_t f4_const_id = const_mgr->GetFloatConstId(4.0); + uint32_t f5_const_id = const_mgr->GetFloatConstId(5.0); // Extract the input values. Instruction* x = ir_builder.AddCompositeExtract(float_type_id, input_id, {0}); @@ -935,8 +935,7 @@ Pass::Status AmdExtensionToKhrPass::Process() { std::vector to_be_killed; for (Instruction& inst : context()->module()->extensions()) { if (inst.opcode() == SpvOpExtension) { - if (ext_to_remove.count(reinterpret_cast( - &(inst.GetInOperand(0).words[0]))) != 0) { + if (ext_to_remove.count(inst.GetInOperand(0).AsString()) != 0) { to_be_killed.push_back(&inst); } } @@ -944,8 +943,7 @@ Pass::Status AmdExtensionToKhrPass::Process() { for (Instruction& inst : context()->ext_inst_imports()) { if (inst.opcode() == SpvOpExtInstImport) { - if (ext_to_remove.count(reinterpret_cast( - &(inst.GetInOperand(0).words[0]))) != 0) { + if (ext_to_remove.count(inst.GetInOperand(0).AsString()) != 0) { to_be_killed.push_back(&inst); } } diff --git a/source/opt/amd_ext_to_khr.h b/source/opt/amd_ext_to_khr.h index fd3dab4e..6a39d953 100644 --- a/source/opt/amd_ext_to_khr.h +++ b/source/opt/amd_ext_to_khr.h @@ -23,7 +23,7 @@ namespace spvtools { namespace opt { // Replaces the extensions VK_AMD_shader_ballot, VK_AMD_gcn_shader, and -// VK_AMD_shader_trinary_minmax with equivalant code using core instructions and +// VK_AMD_shader_trinary_minmax with equivalent code using core instructions and // capabilities. class AmdExtensionToKhrPass : public Pass { public: diff --git a/source/opt/basic_block.h b/source/opt/basic_block.h index 6741a50f..dd3b2e28 100644 --- a/source/opt/basic_block.h +++ b/source/opt/basic_block.h @@ -83,7 +83,7 @@ class BasicBlock { const Instruction* GetMergeInst() const; Instruction* GetMergeInst(); - // Returns the OpLoopMerge instruciton in this basic block, if it exists. + // Returns the OpLoopMerge instruction in this basic block, if it exists. // Otherwise return null. May be used whenever tail() can be used. const Instruction* GetLoopMergeInst() const; Instruction* GetLoopMergeInst(); diff --git a/source/opt/block_merge_pass.cpp b/source/opt/block_merge_pass.cpp index c7315baf..ef7f31fe 100644 --- a/source/opt/block_merge_pass.cpp +++ b/source/opt/block_merge_pass.cpp @@ -28,7 +28,9 @@ namespace opt { bool BlockMergePass::MergeBlocks(Function* func) { bool modified = false; for (auto bi = func->begin(); bi != func->end();) { - if (blockmergeutil::CanMergeWithSuccessor(context(), &*bi)) { + // Don't bother trying to merge unreachable blocks. + if (context()->IsReachable(*bi) && + blockmergeutil::CanMergeWithSuccessor(context(), &*bi)) { blockmergeutil::MergeWithSuccessor(context(), func, bi); // Reprocess block. modified = true; @@ -42,7 +44,7 @@ bool BlockMergePass::MergeBlocks(Function* func) { Pass::Status BlockMergePass::Process() { // Process all entry point functions. ProcessFunction pfn = [this](Function* fp) { return MergeBlocks(fp); }; - bool modified = context()->ProcessEntryPointCallTree(pfn); + bool modified = context()->ProcessReachableCallTree(pfn); return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } diff --git a/source/opt/block_merge_util.cpp b/source/opt/block_merge_util.cpp index 14b5d364..8ae8020a 100644 --- a/source/opt/block_merge_util.cpp +++ b/source/opt/block_merge_util.cpp @@ -103,11 +103,6 @@ bool CanMergeWithSuccessor(IRContext* context, BasicBlock* block) { return false; } - // Don't bother trying to merge unreachable blocks. - if (auto dominators = context->GetDominatorAnalysis(block->GetParent())) { - if (!dominators->IsReachable(block)) return false; - } - Instruction* merge_inst = block->GetMergeInst(); const bool pred_is_header = IsHeader(block); if (pred_is_header && lab_id != merge_inst->GetSingleWordInOperand(0u)) { @@ -176,10 +171,17 @@ void MergeWithSuccessor(IRContext* context, Function* func, // and OpBranchConditional. auto terminator = bi->terminator(); auto& vec = terminator->dbg_line_insts(); - auto& new_vec = merge_inst->dbg_line_insts(); - new_vec.insert(new_vec.end(), vec.begin(), vec.end()); - terminator->clear_dbg_line_insts(); - + if (vec.size() > 0) { + merge_inst->ClearDbgLineInsts(); + auto& new_vec = merge_inst->dbg_line_insts(); + new_vec.insert(new_vec.end(), vec.begin(), vec.end()); + terminator->ClearDbgLineInsts(); + for (auto& l_inst : new_vec) + context->get_def_use_mgr()->AnalyzeInstDefUse(&l_inst); + } + // Clear debug scope of terminator to avoid DebugScope + // emitted between terminator and merge. + terminator->SetDebugScope(DebugScope(kNoDebugScope, kNoInlinedAt)); // Move the merge instruction to just before the terminator. merge_inst->InsertBefore(terminator); } diff --git a/source/opt/ccp_pass.cpp b/source/opt/ccp_pass.cpp index d84f13f5..5f855027 100644 --- a/source/opt/ccp_pass.cpp +++ b/source/opt/ccp_pass.cpp @@ -102,6 +102,34 @@ SSAPropagator::PropStatus CCPPass::VisitPhi(Instruction* phi) { return SSAPropagator::kInteresting; } +uint32_t CCPPass::ComputeLatticeMeet(Instruction* instr, uint32_t val2) { + // Given two values val1 and val2, the meet operation in the constant + // lattice uses the following rules: + // + // meet(val1, UNDEFINED) = val1 + // meet(val1, VARYING) = VARYING + // meet(val1, val2) = val1 if val1 == val2 + // meet(val1, val2) = VARYING if val1 != val2 + // + // When two different values meet, the result is always varying because CCP + // does not allow lateral transitions in the lattice. This prevents + // infinite cycles during propagation. + auto val1_it = values_.find(instr->result_id()); + if (val1_it == values_.end()) { + return val2; + } + + uint32_t val1 = val1_it->second; + if (IsVaryingValue(val1)) { + return val1; + } else if (IsVaryingValue(val2)) { + return val2; + } else if (val1 != val2) { + return kVaryingSSAId; + } + return val2; +} + SSAPropagator::PropStatus CCPPass::VisitAssignment(Instruction* instr) { assert(instr->result_id() != 0 && "Expecting an instruction that produces a result"); @@ -115,8 +143,10 @@ SSAPropagator::PropStatus CCPPass::VisitAssignment(Instruction* instr) { if (IsVaryingValue(it->second)) { return MarkInstructionVarying(instr); } else { - values_[instr->result_id()] = it->second; - return SSAPropagator::kInteresting; + uint32_t new_val = ComputeLatticeMeet(instr, it->second); + values_[instr->result_id()] = new_val; + return IsVaryingValue(new_val) ? SSAPropagator::kVarying + : SSAPropagator::kInteresting; } } return SSAPropagator::kNotInteresting; @@ -142,9 +172,13 @@ SSAPropagator::PropStatus CCPPass::VisitAssignment(Instruction* instr) { if (folded_inst != nullptr) { // We do not want to change the body of the function by adding new // instructions. When folding we can only generate new constants. - assert(folded_inst->IsConstant() && "CCP is only interested in constant."); - values_[instr->result_id()] = folded_inst->result_id(); - return SSAPropagator::kInteresting; + assert((folded_inst->IsConstant() || + IsSpecConstantInst(folded_inst->opcode())) && + "CCP is only interested in constant values."); + uint32_t new_val = ComputeLatticeMeet(instr, folded_inst->result_id()); + values_[instr->result_id()] = new_val; + return IsVaryingValue(new_val) ? SSAPropagator::kVarying + : SSAPropagator::kInteresting; } // Conservatively mark this instruction as varying if any input id is varying. @@ -291,6 +325,10 @@ bool CCPPass::ReplaceValues() { } bool CCPPass::PropagateConstants(Function* fp) { + if (fp->IsDeclaration()) { + return false; + } + // Mark function parameters as varying. fp->ForEachParam([this](const Instruction* inst) { values_[inst->result_id()] = kVaryingSSAId; diff --git a/source/opt/ccp_pass.h b/source/opt/ccp_pass.h index fb20c780..77ea9f80 100644 --- a/source/opt/ccp_pass.h +++ b/source/opt/ccp_pass.h @@ -92,6 +92,22 @@ class CCPPass : public MemPass { // generated during propagation. analysis::ConstantManager* const_mgr_; + // Returns a new value for |instr| by computing the meet operation between + // its existing value and |val2|. + // + // Given two values val1 and val2, the meet operation in the constant + // lattice uses the following rules: + // + // meet(val1, UNDEFINED) = val1 + // meet(val1, VARYING) = VARYING + // meet(val1, val2) = val1 if val1 == val2 + // meet(val1, val2) = VARYING if val1 != val2 + // + // When two different values meet, the result is always varying because CCP + // does not allow lateral transitions in the lattice. This prevents + // infinite cycles during propagation. + uint32_t ComputeLatticeMeet(Instruction* instr, uint32_t val2); + // Constant value table. Each entry in this map // represents the compile-time constant value for |id| as declared by // |const_decl_id|. Each |const_decl_id| in this table is an OpConstant diff --git a/source/opt/cfg.cpp b/source/opt/cfg.cpp index ac0fcc36..a0248d54 100644 --- a/source/opt/cfg.cpp +++ b/source/opt/cfg.cpp @@ -74,6 +74,12 @@ void CFG::RemoveNonExistingEdges(uint32_t blk_id) { void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root, std::list* order) { + ComputeStructuredOrder(func, root, nullptr, order); +} + +void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root, + BasicBlock* end, + std::list* order) { assert(module_->context()->get_feature_mgr()->HasCapability( SpvCapabilityShader) && "This only works on structured control flow"); @@ -81,7 +87,8 @@ void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root, // Compute structured successors and do DFS. ComputeStructuredSuccessors(func); auto ignore_block = [](cbb_ptr) {}; - auto ignore_edge = [](cbb_ptr, cbb_ptr) {}; + auto terminal = [end](cbb_ptr bb) { return bb == end; }; + auto get_structured_successors = [this](const BasicBlock* b) { return &(block2structured_succs_[b]); }; @@ -92,7 +99,7 @@ void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root, order->push_front(const_cast(b)); }; CFA::DepthFirstTraversal(root, get_structured_successors, - ignore_block, post_order, ignore_edge); + ignore_block, post_order, terminal); } void CFG::ForEachBlockInPostOrder(BasicBlock* bb, @@ -205,7 +212,7 @@ BasicBlock* CFG::SplitLoopHeader(BasicBlock* bb) { // Find the back edge BasicBlock* latch_block = nullptr; Function::iterator latch_block_iter = header_it; - while (++latch_block_iter != fn->end()) { + for (; latch_block_iter != fn->end(); ++latch_block_iter) { // If blocks are in the proper order, then the only branch that appears // after the header is the latch. if (std::find(pred.begin(), pred.end(), latch_block_iter->id()) != @@ -237,6 +244,15 @@ BasicBlock* CFG::SplitLoopHeader(BasicBlock* bb) { context->set_instr_block(inst, new_header); }); + // If |bb| was the latch block, the branch back to the header is not in + // |new_header|. + if (latch_block == bb) { + if (new_header->ContinueBlockId() == bb->id()) { + new_header->GetLoopMergeInst()->SetInOperand(1, {new_header_id}); + } + latch_block = new_header; + } + // Adjust the OpPhi instructions as needed. bb->ForEachPhiInst([latch_block, bb, new_header, context](Instruction* phi) { std::vector preheader_phi_ops; diff --git a/source/opt/cfg.h b/source/opt/cfg.h index f2806822..fa4fef2d 100644 --- a/source/opt/cfg.h +++ b/source/opt/cfg.h @@ -30,7 +30,7 @@ class CFG { public: explicit CFG(Module* module); - // Return the list of predecesors for basic block with label |blkid|. + // Return the list of predecessors for basic block with label |blkid|. // TODO(dnovillo): Move this to BasicBlock. const std::vector& preds(uint32_t blk_id) const { assert(label2preds_.count(blk_id)); @@ -66,6 +66,14 @@ class CFG { void ComputeStructuredOrder(Function* func, BasicBlock* root, std::list* order); + // Compute structured block order into |order| for |func| starting at |root| + // and ending at |end|. This order has the property that dominators come + // before all blocks they dominate, merge blocks come after all blocks that + // are in the control constructs of their header, and continue blocks come + // after all the blocks in the body of their loop. + void ComputeStructuredOrder(Function* func, BasicBlock* root, BasicBlock* end, + std::list* order); + // Applies |f| to all blocks that can be reach from |bb| in post order. void ForEachBlockInPostOrder(BasicBlock* bb, const std::function& f); diff --git a/source/opt/combine_access_chains.cpp b/source/opt/combine_access_chains.cpp index facfc24b..142897a2 100644 --- a/source/opt/combine_access_chains.cpp +++ b/source/opt/combine_access_chains.cpp @@ -34,6 +34,10 @@ Pass::Status CombineAccessChains::Process() { } bool CombineAccessChains::ProcessFunction(Function& function) { + if (function.IsDeclaration()) { + return false; + } + bool modified = false; cfg()->ForEachBlockInReversePostOrder( diff --git a/source/opt/compact_ids_pass.cpp b/source/opt/compact_ids_pass.cpp index 67091531..5a2a54b1 100644 --- a/source/opt/compact_ids_pass.cpp +++ b/source/opt/compact_ids_pass.cpp @@ -44,6 +44,11 @@ Pass::Status CompactIdsPass::Process() { bool modified = false; std::unordered_map result_id_mapping; + // Disable automatic DebugInfo analysis for the life of the CompactIds pass. + // The DebugInfo manager requires the SPIR-V to be valid to run, but this is + // not true at all times in CompactIds as it remaps all ids. + context()->InvalidateAnalyses(IRContext::kAnalysisDebugInfo); + context()->module()->ForEachInst( [&result_id_mapping, &modified](Instruction* inst) { auto operand = inst->begin(); @@ -86,9 +91,13 @@ Pass::Status CompactIdsPass::Process() { }, true); - if (modified) + if (context()->module()->id_bound() != result_id_mapping.size() + 1) { + modified = true; context()->module()->SetIdBound( static_cast(result_id_mapping.size() + 1)); + // There are ids in the feature manager that could now be invalid + context()->ResetFeatureManager(); + } return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } diff --git a/source/opt/const_folding_rules.cpp b/source/opt/const_folding_rules.cpp index d262a7ec..0ad755c9 100644 --- a/source/opt/const_folding_rules.cpp +++ b/source/opt/const_folding_rules.cpp @@ -22,6 +22,45 @@ namespace { const uint32_t kExtractCompositeIdInIdx = 0; +// Returns a constants with the value NaN of the given type. Only works for +// 32-bit and 64-bit float point types. Returns |nullptr| if an error occurs. +const analysis::Constant* GetNan(const analysis::Type* type, + analysis::ConstantManager* const_mgr) { + const analysis::Float* float_type = type->AsFloat(); + if (float_type == nullptr) { + return nullptr; + } + + switch (float_type->width()) { + case 32: + return const_mgr->GetFloatConst(std::numeric_limits::quiet_NaN()); + case 64: + return const_mgr->GetDoubleConst( + std::numeric_limits::quiet_NaN()); + default: + return nullptr; + } +} + +// Returns a constants with the value INF of the given type. Only works for +// 32-bit and 64-bit float point types. Returns |nullptr| if an error occurs. +const analysis::Constant* GetInf(const analysis::Type* type, + analysis::ConstantManager* const_mgr) { + const analysis::Float* float_type = type->AsFloat(); + if (float_type == nullptr) { + return nullptr; + } + + switch (float_type->width()) { + case 32: + return const_mgr->GetFloatConst(std::numeric_limits::infinity()); + case 64: + return const_mgr->GetDoubleConst(std::numeric_limits::infinity()); + default: + return nullptr; + } +} + // Returns true if |type| is Float or a vector of Float. bool HasFloatingPoint(const analysis::Type* type) { if (type->AsFloat()) { @@ -33,6 +72,23 @@ bool HasFloatingPoint(const analysis::Type* type) { return false; } +// Returns a constants with the value |-val| of the given type. Only works for +// 32-bit and 64-bit float point types. Returns |nullptr| if an error occurs. +const analysis::Constant* NegateFPConst(const analysis::Type* result_type, + const analysis::Constant* val, + analysis::ConstantManager* const_mgr) { + const analysis::Float* float_type = result_type->AsFloat(); + assert(float_type != nullptr); + if (float_type->width() == 32) { + float fa = val->GetFloat(); + return const_mgr->GetFloatConst(-fa); + } else if (float_type->width() == 64) { + double da = val->GetDouble(); + return const_mgr->GetDoubleConst(-da); + } + return nullptr; +} + // Folds an OpcompositeExtract where input is a composite constant. ConstantFoldingRule FoldExtractWithConstants() { return [](IRContext* context, Instruction* inst, @@ -195,6 +251,193 @@ ConstantFoldingRule FoldVectorTimesScalar() { }; } +ConstantFoldingRule FoldVectorTimesMatrix() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + assert(inst->opcode() == SpvOpVectorTimesMatrix); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + + if (!inst->IsFloatingPointFoldingAllowed()) { + if (HasFloatingPoint(type_mgr->GetType(inst->type_id()))) { + return nullptr; + } + } + + const analysis::Constant* c1 = constants[0]; + const analysis::Constant* c2 = constants[1]; + + if (c1 == nullptr || c2 == nullptr) { + return nullptr; + } + + // Check result type. + const analysis::Type* result_type = type_mgr->GetType(inst->type_id()); + const analysis::Vector* vector_type = result_type->AsVector(); + assert(vector_type != nullptr); + const analysis::Type* element_type = vector_type->element_type(); + assert(element_type != nullptr); + const analysis::Float* float_type = element_type->AsFloat(); + assert(float_type != nullptr); + + // Check types of c1 and c2. + assert(c1->type()->AsVector() == vector_type); + assert(c1->type()->AsVector()->element_type() == element_type && + c2->type()->AsMatrix()->element_type() == vector_type); + + // Get a float vector that is the result of vector-times-matrix. + std::vector c1_components = + c1->GetVectorComponents(const_mgr); + std::vector c2_components = + c2->AsMatrixConstant()->GetComponents(); + uint32_t resultVectorSize = result_type->AsVector()->element_count(); + + std::vector ids; + + if ((c1 && c1->IsZero()) || (c2 && c2->IsZero())) { + std::vector words(float_type->width() / 32, 0); + for (uint32_t i = 0; i < resultVectorSize; ++i) { + const analysis::Constant* new_elem = + const_mgr->GetConstant(float_type, words); + ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } + + if (float_type->width() == 32) { + for (uint32_t i = 0; i < resultVectorSize; ++i) { + float result_scalar = 0.0f; + const analysis::VectorConstant* c2_vec = + c2_components[i]->AsVectorConstant(); + for (uint32_t j = 0; j < c2_vec->GetComponents().size(); ++j) { + float c1_scalar = c1_components[j]->GetFloat(); + float c2_scalar = c2_vec->GetComponents()[j]->GetFloat(); + result_scalar += c1_scalar * c2_scalar; + } + utils::FloatProxy result(result_scalar); + std::vector words = result.GetWords(); + const analysis::Constant* new_elem = + const_mgr->GetConstant(float_type, words); + ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } else if (float_type->width() == 64) { + for (uint32_t i = 0; i < c2_components.size(); ++i) { + double result_scalar = 0.0; + const analysis::VectorConstant* c2_vec = + c2_components[i]->AsVectorConstant(); + for (uint32_t j = 0; j < c2_vec->GetComponents().size(); ++j) { + double c1_scalar = c1_components[j]->GetDouble(); + double c2_scalar = c2_vec->GetComponents()[j]->GetDouble(); + result_scalar += c1_scalar * c2_scalar; + } + utils::FloatProxy result(result_scalar); + std::vector words = result.GetWords(); + const analysis::Constant* new_elem = + const_mgr->GetConstant(float_type, words); + ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } + return nullptr; + }; +} + +ConstantFoldingRule FoldMatrixTimesVector() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + assert(inst->opcode() == SpvOpMatrixTimesVector); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + + if (!inst->IsFloatingPointFoldingAllowed()) { + if (HasFloatingPoint(type_mgr->GetType(inst->type_id()))) { + return nullptr; + } + } + + const analysis::Constant* c1 = constants[0]; + const analysis::Constant* c2 = constants[1]; + + if (c1 == nullptr || c2 == nullptr) { + return nullptr; + } + + // Check result type. + const analysis::Type* result_type = type_mgr->GetType(inst->type_id()); + const analysis::Vector* vector_type = result_type->AsVector(); + assert(vector_type != nullptr); + const analysis::Type* element_type = vector_type->element_type(); + assert(element_type != nullptr); + const analysis::Float* float_type = element_type->AsFloat(); + assert(float_type != nullptr); + + // Check types of c1 and c2. + assert(c1->type()->AsMatrix()->element_type() == vector_type); + assert(c2->type()->AsVector()->element_type() == element_type); + + // Get a float vector that is the result of matrix-times-vector. + std::vector c1_components = + c1->AsMatrixConstant()->GetComponents(); + std::vector c2_components = + c2->GetVectorComponents(const_mgr); + uint32_t resultVectorSize = result_type->AsVector()->element_count(); + + std::vector ids; + + if ((c1 && c1->IsZero()) || (c2 && c2->IsZero())) { + std::vector words(float_type->width() / 32, 0); + for (uint32_t i = 0; i < resultVectorSize; ++i) { + const analysis::Constant* new_elem = + const_mgr->GetConstant(float_type, words); + ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } + + if (float_type->width() == 32) { + for (uint32_t i = 0; i < resultVectorSize; ++i) { + float result_scalar = 0.0f; + for (uint32_t j = 0; j < c1_components.size(); ++j) { + float c1_scalar = c1_components[j] + ->AsVectorConstant() + ->GetComponents()[i] + ->GetFloat(); + float c2_scalar = c2_components[j]->GetFloat(); + result_scalar += c1_scalar * c2_scalar; + } + utils::FloatProxy result(result_scalar); + std::vector words = result.GetWords(); + const analysis::Constant* new_elem = + const_mgr->GetConstant(float_type, words); + ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } else if (float_type->width() == 64) { + for (uint32_t i = 0; i < resultVectorSize; ++i) { + double result_scalar = 0.0; + for (uint32_t j = 0; j < c1_components.size(); ++j) { + double c1_scalar = c1_components[j] + ->AsVectorConstant() + ->GetComponents()[i] + ->GetDouble(); + double c2_scalar = c2_components[j]->GetDouble(); + result_scalar += c1_scalar * c2_scalar; + } + utils::FloatProxy result(result_scalar); + std::vector words = result.GetWords(); + const analysis::Constant* new_elem = + const_mgr->GetConstant(float_type, words); + ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } + return nullptr; + }; +} + ConstantFoldingRule FoldCompositeWithConstants() { // Folds an OpCompositeConstruct where all of the inputs are constants to a // constant. A new constant is created if necessary. @@ -492,7 +735,60 @@ ConstantFoldingRule FoldQuantizeToF16() { ConstantFoldingRule FoldFSub() { return FoldFPBinaryOp(FOLD_FPARITH_OP(-)); } ConstantFoldingRule FoldFAdd() { return FoldFPBinaryOp(FOLD_FPARITH_OP(+)); } ConstantFoldingRule FoldFMul() { return FoldFPBinaryOp(FOLD_FPARITH_OP(*)); } -ConstantFoldingRule FoldFDiv() { return FoldFPBinaryOp(FOLD_FPARITH_OP(/)); } + +// Returns the constant that results from evaluating |numerator| / 0.0. Returns +// |nullptr| if the result could not be evaluated. +const analysis::Constant* FoldFPScalarDivideByZero( + const analysis::Type* result_type, const analysis::Constant* numerator, + analysis::ConstantManager* const_mgr) { + if (numerator == nullptr) { + return nullptr; + } + + if (numerator->IsZero()) { + return GetNan(result_type, const_mgr); + } + + const analysis::Constant* result = GetInf(result_type, const_mgr); + if (result == nullptr) { + return nullptr; + } + + if (numerator->AsFloatConstant()->GetValueAsDouble() < 0.0) { + result = NegateFPConst(result_type, result, const_mgr); + } + return result; +} + +// Returns the result of folding |numerator| / |denominator|. Returns |nullptr| +// if it cannot be folded. +const analysis::Constant* FoldScalarFPDivide( + const analysis::Type* result_type, const analysis::Constant* numerator, + const analysis::Constant* denominator, + analysis::ConstantManager* const_mgr) { + if (denominator == nullptr) { + return nullptr; + } + + if (denominator->IsZero()) { + return FoldFPScalarDivideByZero(result_type, numerator, const_mgr); + } + + const analysis::FloatConstant* denominator_float = + denominator->AsFloatConstant(); + if (denominator_float && denominator->GetValueAsDouble() == -0.0) { + const analysis::Constant* result = + FoldFPScalarDivideByZero(result_type, numerator, const_mgr); + if (result != nullptr) + result = NegateFPConst(result_type, result, const_mgr); + return result; + } else { + return FOLD_FPARITH_OP(/)(result_type, numerator, denominator, const_mgr); + } +} + +// Returns the constant folding rule to fold |OpFDiv| with two constants. +ConstantFoldingRule FoldFDiv() { return FoldFPBinaryOp(FoldScalarFPDivide); } bool CompareFloatingPoint(bool op_result, bool op_unordered, bool need_ordered) { @@ -655,20 +951,7 @@ UnaryScalarFoldingRule FoldFNegateOp() { analysis::ConstantManager* const_mgr) -> const analysis::Constant* { assert(result_type != nullptr && a != nullptr); assert(result_type == a->type()); - const analysis::Float* float_type = result_type->AsFloat(); - assert(float_type != nullptr); - if (float_type->width() == 32) { - float fa = a->GetFloat(); - utils::FloatProxy result(-fa); - std::vector words = result.GetWords(); - return const_mgr->GetConstant(result_type, words); - } else if (float_type->width() == 64) { - double da = a->GetDouble(); - utils::FloatProxy result(-da); - std::vector words = result.GetWords(); - return const_mgr->GetConstant(result_type, words); - } - return nullptr; + return NegateFPConst(result_type, a, const_mgr); }; } @@ -900,17 +1183,6 @@ ConstantFoldingRule FoldFMix() { }; } -template -IntType FoldIClamp(IntType x, IntType min_val, IntType max_val) { - if (x < min_val) { - x = min_val; - } - if (x > max_val) { - x = max_val; - } - return x; -} - const analysis::Constant* FoldMin(const analysis::Type* result_type, const analysis::Constant* a, const analysis::Constant* b, @@ -1002,7 +1274,7 @@ const analysis::Constant* FoldClamp1( "Expecting a GLSLstd450 extended instruction."); // Make sure all Clamp operands are constants. - for (uint32_t i = 1; i < 3; i++) { + for (uint32_t i = 1; i < 4; i++) { if (constants[i] == nullptr) { return nullptr; } @@ -1017,7 +1289,7 @@ const analysis::Constant* FoldClamp1( context); } -// Fold a clamp instruction when |x >= min_val|. +// Fold a clamp instruction when |x <= min_val|. const analysis::Constant* FoldClamp2( IRContext* context, Instruction* inst, const std::vector& constants) { @@ -1192,6 +1464,8 @@ void ConstantFoldingRules::AddFoldingRules() { rules_[SpvOpVectorShuffle].push_back(FoldVectorShuffleWithConstants()); rules_[SpvOpVectorTimesScalar].push_back(FoldVectorTimesScalar()); + rules_[SpvOpVectorTimesMatrix].push_back(FoldVectorTimesMatrix()); + rules_[SpvOpMatrixTimesVector].push_back(FoldMatrixTimesVector()); rules_[SpvOpFNegate].push_back(FoldFNegate()); rules_[SpvOpQuantizeToF16].push_back(FoldQuantizeToF16()); @@ -1250,7 +1524,7 @@ void ConstantFoldingRules::AddFoldingRules() { FoldFPUnaryOp(FoldFTranscendentalUnary(std::log))); #ifdef __ANDROID__ - // Android NDK r15c tageting ABI 15 doesn't have full support for C++11 + // Android NDK r15c targeting ABI 15 doesn't have full support for C++11 // (no std::exp2/log2). ::exp2 is available from C99 but ::log2 isn't // available up until ABI 18 so we use a shim auto log2_shim = [](double v) -> double { return log(v) / log(2.0); }; diff --git a/source/opt/constants.cpp b/source/opt/constants.cpp index cf24295d..bcff08c1 100644 --- a/source/opt/constants.cpp +++ b/source/opt/constants.cpp @@ -158,6 +158,7 @@ Type* ConstantManager::GetType(const Instruction* inst) const { std::vector ConstantManager::GetOperandConstants( const Instruction* inst) const { std::vector constants; + constants.reserve(inst->NumInOperands()); for (uint32_t i = 0; i < inst->NumInOperands(); i++) { const Operand* operand = &inst->GetInOperand(i); if (operand->type != SPV_OPERAND_TYPE_ID) { @@ -217,7 +218,8 @@ Instruction* ConstantManager::BuildInstructionAndAddToModule( auto* new_inst_ptr = new_inst.get(); *pos = pos->InsertBefore(std::move(new_inst)); ++(*pos); - context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inst_ptr); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inst_ptr); MapConstantToInst(new_const, new_inst_ptr); return new_inst_ptr; } @@ -389,19 +391,72 @@ const Constant* ConstantManager::GetConstant( return cst ? RegisterConstant(std::move(cst)) : nullptr; } -uint32_t ConstantManager::GetFloatConst(float val) { +const Constant* ConstantManager::GetNumericVectorConstantWithWords( + const Vector* type, const std::vector& literal_words) { + const auto* element_type = type->element_type(); + uint32_t words_per_element = 0; + if (const auto* float_type = element_type->AsFloat()) + words_per_element = float_type->width() / 32; + else if (const auto* int_type = element_type->AsInteger()) + words_per_element = int_type->width() / 32; + + if (words_per_element != 1 && words_per_element != 2) return nullptr; + + if (words_per_element * type->element_count() != + static_cast(literal_words.size())) { + return nullptr; + } + + std::vector element_ids; + for (uint32_t i = 0; i < type->element_count(); ++i) { + auto first_word = literal_words.begin() + (words_per_element * i); + std::vector const_data(first_word, + first_word + words_per_element); + const analysis::Constant* element_constant = + GetConstant(element_type, const_data); + auto element_id = GetDefiningInstruction(element_constant)->result_id(); + element_ids.push_back(element_id); + } + + return GetConstant(type, element_ids); +} + +uint32_t ConstantManager::GetFloatConstId(float val) { + const Constant* c = GetFloatConst(val); + return GetDefiningInstruction(c)->result_id(); +} + +const Constant* ConstantManager::GetFloatConst(float val) { Type* float_type = context()->get_type_mgr()->GetFloatType(); utils::FloatProxy v(val); const Constant* c = GetConstant(float_type, v.GetWords()); + return c; +} + +uint32_t ConstantManager::GetDoubleConstId(double val) { + const Constant* c = GetDoubleConst(val); return GetDefiningInstruction(c)->result_id(); } +const Constant* ConstantManager::GetDoubleConst(double val) { + Type* float_type = context()->get_type_mgr()->GetDoubleType(); + utils::FloatProxy v(val); + const Constant* c = GetConstant(float_type, v.GetWords()); + return c; +} + uint32_t ConstantManager::GetSIntConst(int32_t val) { Type* sint_type = context()->get_type_mgr()->GetSIntType(); const Constant* c = GetConstant(sint_type, {static_cast(val)}); return GetDefiningInstruction(c)->result_id(); } +uint32_t ConstantManager::GetUIntConst(uint32_t val) { + Type* uint_type = context()->get_type_mgr()->GetUIntType(); + const Constant* c = GetConstant(uint_type, {val}); + return GetDefiningInstruction(c)->result_id(); +} + std::vector Constant::GetVectorComponents( analysis::ConstantManager* const_mgr) const { std::vector components; diff --git a/source/opt/constants.h b/source/opt/constants.h index 5bd0ae32..588ca3e7 100644 --- a/source/opt/constants.h +++ b/source/opt/constants.h @@ -163,6 +163,21 @@ class ScalarConstant : public Constant { return is_zero; } + uint32_t GetU32BitValue() const { + // Relies on unsigned values smaller than 32-bit being zero extended. See + // section 2.2.1 of the SPIR-V spec. + assert(words().size() == 1); + return words()[0]; + } + + uint64_t GetU64BitValue() const { + // Relies on unsigned values smaller than 64-bit being zero extended. See + // section 2.2.1 of the SPIR-V spec. + assert(words().size() == 2); + return static_cast(words()[1]) << 32 | + static_cast(words()[0]); + } + protected: ScalarConstant(const Type* ty, const std::vector& w) : Constant(ty), words_(w) {} @@ -189,13 +204,6 @@ class IntConstant : public ScalarConstant { return words()[0]; } - uint32_t GetU32BitValue() const { - // Relies on unsigned values smaller than 32-bit being zero extended. See - // section 2.2.1 of the SPIR-V spec. - assert(words().size() == 1); - return words()[0]; - } - int64_t GetS64BitValue() const { // Relies on unsigned values smaller than 64-bit being sign extended. See // section 2.2.1 of the SPIR-V spec. @@ -204,14 +212,6 @@ class IntConstant : public ScalarConstant { static_cast(words()[0]); } - uint64_t GetU64BitValue() const { - // Relies on unsigned values smaller than 64-bit being zero extended. See - // section 2.2.1 of the SPIR-V spec. - assert(words().size() == 2); - return static_cast(words()[1]) << 32 | - static_cast(words()[0]); - } - // Make a copy of this IntConstant instance. std::unique_ptr CopyIntConstant() const { return MakeUnique(type_->AsInteger(), words_); @@ -506,10 +506,11 @@ class ConstantManager { IRContext* context() const { return ctx_; } // Gets or creates a unique Constant instance of type |type| and a vector of - // constant defining words |words|. If a Constant instance existed already in - // the constant pool, it returns a pointer to it. Otherwise, it creates one - // using CreateConstant. If a new Constant instance cannot be created, it - // returns nullptr. + // constant defining words or ids for elements of Vector type + // |literal_words_or_ids|. If a Constant instance existed already in the + // constant pool, it returns a pointer to it. Otherwise, it creates one using + // CreateConstant. If a new Constant instance cannot be created, it returns + // nullptr. const Constant* GetConstant( const Type* type, const std::vector& literal_words_or_ids); @@ -519,6 +520,14 @@ class ConstantManager { literal_words_or_ids.end())); } + // Gets or creates a unique Constant instance of Vector type |type| with + // numeric elements and a vector of constant defining words |literal_words|. + // If a Constant instance existed already in the constant pool, it returns a + // pointer to it. Otherwise, it creates one using CreateConstant. If a new + // Constant instance cannot be created, it returns nullptr. + const Constant* GetNumericVectorConstantWithWords( + const Vector* type, const std::vector& literal_words); + // Gets or creates a Constant instance to hold the constant value of the given // instruction. It returns a pointer to a Constant instance or nullptr if it // could not create the constant. @@ -532,7 +541,7 @@ class ConstantManager { // instruction at the end of the current module's types section. // // |type_id| is an optional argument for disambiguating equivalent types. If - // |type_id| is specified, the contant returned will have that type id. + // |type_id| is specified, the constant returned will have that type id. Instruction* GetDefiningInstruction(const Constant* c, uint32_t type_id = 0, Module::inst_iterator* pos = nullptr); @@ -628,11 +637,23 @@ class ConstantManager { } // Returns the id of a 32-bit floating point constant with value |val|. - uint32_t GetFloatConst(float val); + uint32_t GetFloatConstId(float val); + + // Returns a 32-bit float constant with the given value. + const Constant* GetFloatConst(float val); + + // Returns the id of a 64-bit floating point constant with value |val|. + uint32_t GetDoubleConstId(double val); + + // Returns a 64-bit float constant with the given value. + const Constant* GetDoubleConst(double val); // Returns the id of a 32-bit signed integer constant with value |val|. uint32_t GetSIntConst(int32_t val); + // Returns the id of a 32-bit unsigned integer constant with value |val|. + uint32_t GetUIntConst(uint32_t val); + private: // Creates a Constant instance with the given type and a vector of constant // defining words. Returns a unique pointer to the created Constant instance diff --git a/source/opt/convert_to_half_pass.cpp b/source/opt/convert_to_half_pass.cpp index 6b3b540a..4086e31a 100644 --- a/source/opt/convert_to_half_pass.cpp +++ b/source/opt/convert_to_half_pass.cpp @@ -177,18 +177,21 @@ bool ConvertToHalfPass::GenHalfArith(Instruction* inst) { return modified; } -bool ConvertToHalfPass::ProcessPhi(Instruction* inst) { - // Add float16 converts of any float32 operands and change type - // of phi to float16 equivalent. Operand converts need to be added to - // preceeding blocks. +bool ConvertToHalfPass::ProcessPhi(Instruction* inst, uint32_t from_width, + uint32_t to_width) { + // Add converts of any float operands to to_width if they are of from_width. + // If converting to 16, change type of phi to float16 equivalent and remember + // result id. Converts need to be added to preceding blocks. uint32_t ocnt = 0; uint32_t* prev_idp; - inst->ForEachInId([&ocnt, &prev_idp, this](uint32_t* idp) { + bool modified = false; + inst->ForEachInId([&ocnt, &prev_idp, &from_width, &to_width, &modified, + this](uint32_t* idp) { if (ocnt % 2 == 0) { prev_idp = idp; } else { Instruction* val_inst = get_def_use_mgr()->GetDef(*prev_idp); - if (IsFloat(val_inst, 32)) { + if (IsFloat(val_inst, from_width)) { BasicBlock* bp = context()->get_instr_block(*idp); auto insert_before = bp->tail(); if (insert_before != bp->begin()) { @@ -197,15 +200,19 @@ bool ConvertToHalfPass::ProcessPhi(Instruction* inst) { insert_before->opcode() != SpvOpLoopMerge) ++insert_before; } - GenConvert(prev_idp, 16, &*insert_before); + GenConvert(prev_idp, to_width, &*insert_before); + modified = true; } } ++ocnt; }); - inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16)); - get_def_use_mgr()->AnalyzeInstUse(inst); - converted_ids_.insert(inst->result_id()); - return true; + if (to_width == 16u) { + inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16u)); + converted_ids_.insert(inst->result_id()); + modified = true; + } + if (modified) get_def_use_mgr()->AnalyzeInstUse(inst); + return modified; } bool ConvertToHalfPass::ProcessConvert(Instruction* inst) { @@ -242,9 +249,10 @@ bool ConvertToHalfPass::ProcessImageRef(Instruction* inst) { } bool ConvertToHalfPass::ProcessDefault(Instruction* inst) { - bool modified = false; // If non-relaxed instruction has changed operands, need to convert // them back to float32 + if (inst->opcode() == SpvOpPhi) return ProcessPhi(inst, 16u, 32u); + bool modified = false; inst->ForEachInId([&inst, &modified, this](uint32_t* idp) { if (converted_ids_.count(*idp) == 0) return; uint32_t old_id = *idp; @@ -262,7 +270,7 @@ bool ConvertToHalfPass::GenHalfInst(Instruction* inst) { if (IsArithmetic(inst) && inst_relaxed) modified = GenHalfArith(inst); else if (inst->opcode() == SpvOpPhi && inst_relaxed) - modified = ProcessPhi(inst); + modified = ProcessPhi(inst, 32u, 16u); else if (inst->opcode() == SpvOpFConvert) modified = ProcessConvert(inst); else if (image_ops_.count(inst->opcode()) != 0) @@ -340,7 +348,7 @@ Pass::Status ConvertToHalfPass::ProcessImpl() { Pass::ProcessFunction pfn = [this](Function* fp) { return ProcessFunction(fp); }; - bool modified = context()->ProcessEntryPointCallTree(pfn); + bool modified = context()->ProcessReachableCallTree(pfn); // If modified, make sure module has Float16 capability if (modified) context()->AddCapability(SpvCapabilityFloat16); // Remove all RelaxedPrecision decorations from instructions and globals diff --git a/source/opt/convert_to_half_pass.h b/source/opt/convert_to_half_pass.h index b647dd4a..c6e84d1b 100644 --- a/source/opt/convert_to_half_pass.h +++ b/source/opt/convert_to_half_pass.h @@ -93,7 +93,7 @@ class ConvertToHalfPass : public Pass { bool GenHalfArith(Instruction* inst); // Gen code for relaxed phi |inst| - bool ProcessPhi(Instruction* inst); + bool ProcessPhi(Instruction* inst, uint32_t from_width, uint32_t to_width); // Gen code for relaxed convert |inst| bool ProcessConvert(Instruction* inst); diff --git a/source/opt/convert_to_sampled_image_pass.cpp b/source/opt/convert_to_sampled_image_pass.cpp new file mode 100644 index 00000000..e84d3578 --- /dev/null +++ b/source/opt/convert_to_sampled_image_pass.cpp @@ -0,0 +1,437 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/convert_to_sampled_image_pass.h" + +#include +#include +#include + +#include "source/opt/ir_builder.h" +#include "source/util/make_unique.h" +#include "source/util/parse_number.h" + +namespace spvtools { +namespace opt { + +using VectorOfDescriptorSetAndBindingPairs = + std::vector; +using DescriptorSetBindingToInstruction = + ConvertToSampledImagePass::DescriptorSetBindingToInstruction; + +namespace { + +using utils::ParseNumber; + +// Returns true if the given char is ':', '\0' or considered as blank space +// (i.e.: '\n', '\r', '\v', '\t', '\f' and ' '). +bool IsSeparator(char ch) { + return std::strchr(":\0", ch) || std::isspace(ch) != 0; +} + +// Reads characters starting from |str| until it meets a separator. Parses a +// number from the characters and stores it into |number|. Returns the pointer +// to the separator if it succeeds. Otherwise, returns nullptr. +const char* ParseNumberUntilSeparator(const char* str, uint32_t* number) { + const char* number_begin = str; + while (!IsSeparator(*str)) str++; + const char* number_end = str; + std::string number_in_str(number_begin, number_end - number_begin); + if (!utils::ParseNumber(number_in_str.c_str(), number)) { + // The descriptor set is not a valid uint32 number. + return nullptr; + } + return str; +} + +// Returns id of the image type used for the sampled image type of +// |sampled_image|. +uint32_t GetImageTypeOfSampledImage(analysis::TypeManager* type_mgr, + Instruction* sampled_image) { + auto* sampled_image_type = + type_mgr->GetType(sampled_image->type_id())->AsSampledImage(); + return type_mgr->GetTypeInstruction(sampled_image_type->image_type()); +} + +// Finds the instruction whose id is |inst_id|. Follows the operand of +// OpCopyObject recursively if the opcode of the instruction is OpCopyObject +// and returns the first instruction that does not have OpCopyObject as opcode. +Instruction* GetNonCopyObjectDef(analysis::DefUseManager* def_use_mgr, + uint32_t inst_id) { + Instruction* inst = def_use_mgr->GetDef(inst_id); + while (inst->opcode() == SpvOpCopyObject) { + inst_id = inst->GetSingleWordInOperand(0u); + inst = def_use_mgr->GetDef(inst_id); + } + return inst; +} + +} // namespace + +bool ConvertToSampledImagePass::GetDescriptorSetBinding( + const Instruction& inst, + DescriptorSetAndBinding* descriptor_set_binding) const { + auto* decoration_manager = context()->get_decoration_mgr(); + bool found_descriptor_set_to_convert = false; + bool found_binding_to_convert = false; + for (auto decorate : + decoration_manager->GetDecorationsFor(inst.result_id(), false)) { + uint32_t decoration = decorate->GetSingleWordInOperand(1u); + if (decoration == SpvDecorationDescriptorSet) { + if (found_descriptor_set_to_convert) { + assert(false && "A resource has two OpDecorate for the descriptor set"); + return false; + } + descriptor_set_binding->descriptor_set = + decorate->GetSingleWordInOperand(2u); + found_descriptor_set_to_convert = true; + } else if (decoration == SpvDecorationBinding) { + if (found_binding_to_convert) { + assert(false && "A resource has two OpDecorate for the binding"); + return false; + } + descriptor_set_binding->binding = decorate->GetSingleWordInOperand(2u); + found_binding_to_convert = true; + } + } + return found_descriptor_set_to_convert && found_binding_to_convert; +} + +bool ConvertToSampledImagePass::ShouldResourceBeConverted( + const DescriptorSetAndBinding& descriptor_set_binding) const { + return descriptor_set_binding_pairs_.find(descriptor_set_binding) != + descriptor_set_binding_pairs_.end(); +} + +const analysis::Type* ConvertToSampledImagePass::GetVariableType( + const Instruction& variable) const { + if (variable.opcode() != SpvOpVariable) return nullptr; + auto* type = context()->get_type_mgr()->GetType(variable.type_id()); + auto* pointer_type = type->AsPointer(); + if (!pointer_type) return nullptr; + + return pointer_type->pointee_type(); +} + +SpvStorageClass ConvertToSampledImagePass::GetStorageClass( + const Instruction& variable) const { + assert(variable.opcode() == SpvOpVariable); + auto* type = context()->get_type_mgr()->GetType(variable.type_id()); + auto* pointer_type = type->AsPointer(); + if (!pointer_type) return SpvStorageClassMax; + + return pointer_type->storage_class(); +} + +bool ConvertToSampledImagePass::CollectResourcesToConvert( + DescriptorSetBindingToInstruction* descriptor_set_binding_pair_to_sampler, + DescriptorSetBindingToInstruction* descriptor_set_binding_pair_to_image) + const { + for (auto& inst : context()->types_values()) { + const auto* variable_type = GetVariableType(inst); + if (variable_type == nullptr) continue; + + DescriptorSetAndBinding descriptor_set_binding; + if (!GetDescriptorSetBinding(inst, &descriptor_set_binding)) continue; + + if (!ShouldResourceBeConverted(descriptor_set_binding)) { + continue; + } + + if (variable_type->AsImage()) { + if (!descriptor_set_binding_pair_to_image + ->insert({descriptor_set_binding, &inst}) + .second) { + return false; + } + } else if (variable_type->AsSampler()) { + if (!descriptor_set_binding_pair_to_sampler + ->insert({descriptor_set_binding, &inst}) + .second) { + return false; + } + } + } + return true; +} + +Pass::Status ConvertToSampledImagePass::Process() { + Status status = Status::SuccessWithoutChange; + + DescriptorSetBindingToInstruction descriptor_set_binding_pair_to_sampler, + descriptor_set_binding_pair_to_image; + if (!CollectResourcesToConvert(&descriptor_set_binding_pair_to_sampler, + &descriptor_set_binding_pair_to_image)) { + return Status::Failure; + } + + for (auto& image : descriptor_set_binding_pair_to_image) { + status = CombineStatus( + status, UpdateImageVariableToSampledImage(image.second, image.first)); + if (status == Status::Failure) { + return status; + } + } + + for (const auto& sampler : descriptor_set_binding_pair_to_sampler) { + // Converting only a Sampler to Sampled Image is not allowed. It must have a + // corresponding image to combine the sampler with. + auto image_itr = descriptor_set_binding_pair_to_image.find(sampler.first); + if (image_itr == descriptor_set_binding_pair_to_image.end() || + image_itr->second == nullptr) { + return Status::Failure; + } + + status = CombineStatus( + status, CheckUsesOfSamplerVariable(sampler.second, image_itr->second)); + if (status == Status::Failure) { + return status; + } + } + + return status; +} + +void ConvertToSampledImagePass::FindUses(const Instruction* inst, + std::vector* uses, + uint32_t user_opcode) const { + auto* def_use_mgr = context()->get_def_use_mgr(); + def_use_mgr->ForEachUser(inst, [uses, user_opcode, this](Instruction* user) { + if (user->opcode() == user_opcode) { + uses->push_back(user); + } else if (user->opcode() == SpvOpCopyObject) { + FindUses(user, uses, user_opcode); + } + }); +} + +void ConvertToSampledImagePass::FindUsesOfImage( + const Instruction* image, std::vector* uses) const { + auto* def_use_mgr = context()->get_def_use_mgr(); + def_use_mgr->ForEachUser(image, [uses, this](Instruction* user) { + switch (user->opcode()) { + case SpvOpImageFetch: + case SpvOpImageRead: + case SpvOpImageWrite: + case SpvOpImageQueryFormat: + case SpvOpImageQueryOrder: + case SpvOpImageQuerySizeLod: + case SpvOpImageQuerySize: + case SpvOpImageQueryLevels: + case SpvOpImageQuerySamples: + case SpvOpImageSparseFetch: + uses->push_back(user); + default: + break; + } + if (user->opcode() == SpvOpCopyObject) { + FindUsesOfImage(user, uses); + } + }); +} + +Instruction* ConvertToSampledImagePass::CreateImageExtraction( + Instruction* sampled_image) { + InstructionBuilder builder( + context(), sampled_image->NextNode(), + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + return builder.AddUnaryOp( + GetImageTypeOfSampledImage(context()->get_type_mgr(), sampled_image), + SpvOpImage, sampled_image->result_id()); +} + +uint32_t ConvertToSampledImagePass::GetSampledImageTypeForImage( + Instruction* image_variable) { + const auto* variable_type = GetVariableType(*image_variable); + if (variable_type == nullptr) return 0; + const auto* image_type = variable_type->AsImage(); + if (image_type == nullptr) return 0; + + analysis::Image image_type_for_sampled_image(*image_type); + analysis::SampledImage sampled_image_type(&image_type_for_sampled_image); + return context()->get_type_mgr()->GetTypeInstruction(&sampled_image_type); +} + +Instruction* ConvertToSampledImagePass::UpdateImageUses( + Instruction* sampled_image_load) { + std::vector uses_of_load; + FindUsesOfImage(sampled_image_load, &uses_of_load); + if (uses_of_load.empty()) return nullptr; + + auto* extracted_image = CreateImageExtraction(sampled_image_load); + for (auto* user : uses_of_load) { + user->SetInOperand(0, {extracted_image->result_id()}); + context()->get_def_use_mgr()->AnalyzeInstUse(user); + } + return extracted_image; +} + +bool ConvertToSampledImagePass:: + IsSamplerOfSampledImageDecoratedByDescriptorSetBinding( + Instruction* sampled_image_inst, + const DescriptorSetAndBinding& descriptor_set_binding) { + auto* def_use_mgr = context()->get_def_use_mgr(); + uint32_t sampler_id = sampled_image_inst->GetSingleWordInOperand(1u); + auto* sampler_load = def_use_mgr->GetDef(sampler_id); + if (sampler_load->opcode() != SpvOpLoad) return false; + auto* sampler = def_use_mgr->GetDef(sampler_load->GetSingleWordInOperand(0u)); + DescriptorSetAndBinding sampler_descriptor_set_binding; + return GetDescriptorSetBinding(*sampler, &sampler_descriptor_set_binding) && + sampler_descriptor_set_binding == descriptor_set_binding; +} + +void ConvertToSampledImagePass::UpdateSampledImageUses( + Instruction* image_load, Instruction* image_extraction, + const DescriptorSetAndBinding& image_descriptor_set_binding) { + std::vector sampled_image_users; + FindUses(image_load, &sampled_image_users, SpvOpSampledImage); + + auto* def_use_mgr = context()->get_def_use_mgr(); + for (auto* sampled_image_inst : sampled_image_users) { + if (IsSamplerOfSampledImageDecoratedByDescriptorSetBinding( + sampled_image_inst, image_descriptor_set_binding)) { + context()->ReplaceAllUsesWith(sampled_image_inst->result_id(), + image_load->result_id()); + def_use_mgr->AnalyzeInstUse(image_load); + context()->KillInst(sampled_image_inst); + } else { + if (!image_extraction) + image_extraction = CreateImageExtraction(image_load); + sampled_image_inst->SetInOperand(0, {image_extraction->result_id()}); + def_use_mgr->AnalyzeInstUse(sampled_image_inst); + } + } +} + +void ConvertToSampledImagePass::MoveInstructionNextToType(Instruction* inst, + uint32_t type_id) { + auto* type_inst = context()->get_def_use_mgr()->GetDef(type_id); + inst->SetResultType(type_id); + inst->RemoveFromList(); + inst->InsertAfter(type_inst); +} + +bool ConvertToSampledImagePass::ConvertImageVariableToSampledImage( + Instruction* image_variable, uint32_t sampled_image_type_id) { + auto* sampled_image_type = + context()->get_type_mgr()->GetType(sampled_image_type_id); + if (sampled_image_type == nullptr) return false; + auto storage_class = GetStorageClass(*image_variable); + if (storage_class == SpvStorageClassMax) return false; + analysis::Pointer sampled_image_pointer(sampled_image_type, storage_class); + + // Make sure |image_variable| is behind its type i.e., avoid the forward + // reference. + uint32_t type_id = + context()->get_type_mgr()->GetTypeInstruction(&sampled_image_pointer); + MoveInstructionNextToType(image_variable, type_id); + return true; +} + +Pass::Status ConvertToSampledImagePass::UpdateImageVariableToSampledImage( + Instruction* image_variable, + const DescriptorSetAndBinding& descriptor_set_binding) { + std::vector image_variable_loads; + FindUses(image_variable, &image_variable_loads, SpvOpLoad); + if (image_variable_loads.empty()) return Status::SuccessWithoutChange; + + const uint32_t sampled_image_type_id = + GetSampledImageTypeForImage(image_variable); + if (!sampled_image_type_id) return Status::Failure; + + for (auto* load : image_variable_loads) { + load->SetResultType(sampled_image_type_id); + auto* image_extraction = UpdateImageUses(load); + UpdateSampledImageUses(load, image_extraction, descriptor_set_binding); + } + + return ConvertImageVariableToSampledImage(image_variable, + sampled_image_type_id) + ? Status::SuccessWithChange + : Status::Failure; +} + +bool ConvertToSampledImagePass::DoesSampledImageReferenceImage( + Instruction* sampled_image_inst, Instruction* image_variable) { + if (sampled_image_inst->opcode() != SpvOpSampledImage) return false; + auto* def_use_mgr = context()->get_def_use_mgr(); + auto* image_load = GetNonCopyObjectDef( + def_use_mgr, sampled_image_inst->GetSingleWordInOperand(0u)); + if (image_load->opcode() != SpvOpLoad) return false; + auto* image = + GetNonCopyObjectDef(def_use_mgr, image_load->GetSingleWordInOperand(0u)); + return image->opcode() == SpvOpVariable && + image->result_id() == image_variable->result_id(); +} + +Pass::Status ConvertToSampledImagePass::CheckUsesOfSamplerVariable( + const Instruction* sampler_variable, + Instruction* image_to_be_combined_with) { + if (image_to_be_combined_with == nullptr) return Status::Failure; + + std::vector sampler_variable_loads; + FindUses(sampler_variable, &sampler_variable_loads, SpvOpLoad); + for (auto* load : sampler_variable_loads) { + std::vector sampled_image_users; + FindUses(load, &sampled_image_users, SpvOpSampledImage); + for (auto* sampled_image_inst : sampled_image_users) { + if (!DoesSampledImageReferenceImage(sampled_image_inst, + image_to_be_combined_with)) { + return Status::Failure; + } + } + } + return Status::SuccessWithoutChange; +} + +std::unique_ptr +ConvertToSampledImagePass::ParseDescriptorSetBindingPairsString( + const char* str) { + if (!str) return nullptr; + + auto descriptor_set_binding_pairs = + MakeUnique(); + + while (std::isspace(*str)) str++; // skip leading spaces. + + // The parsing loop, break when points to the end. + while (*str) { + // Parse the descriptor set. + uint32_t descriptor_set = 0; + str = ParseNumberUntilSeparator(str, &descriptor_set); + if (str == nullptr) return nullptr; + + // Find the ':', spaces between the descriptor set and the ':' are not + // allowed. + if (*str++ != ':') { + // ':' not found + return nullptr; + } + + // Parse the binding. + uint32_t binding = 0; + str = ParseNumberUntilSeparator(str, &binding); + if (str == nullptr) return nullptr; + + descriptor_set_binding_pairs->push_back({descriptor_set, binding}); + + // Skip trailing spaces. + while (std::isspace(*str)) str++; + } + + return descriptor_set_binding_pairs; +} + +} // namespace opt +} // namespace spvtools diff --git a/source/opt/convert_to_sampled_image_pass.h b/source/opt/convert_to_sampled_image_pass.h new file mode 100644 index 00000000..d3938af7 --- /dev/null +++ b/source/opt/convert_to_sampled_image_pass.h @@ -0,0 +1,207 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_CONVERT_TO_SAMPLED_IMAGE_PASS_H_ +#define SOURCE_OPT_CONVERT_TO_SAMPLED_IMAGE_PASS_H_ + +#include +#include +#include + +#include "source/opt/pass.h" +#include "source/opt/types.h" + +namespace spvtools { +namespace opt { + +// A struct for a pair of descriptor set and binding. +struct DescriptorSetAndBinding { + uint32_t descriptor_set; + uint32_t binding; + + bool operator==(const DescriptorSetAndBinding& descriptor_set_binding) const { + return descriptor_set_binding.descriptor_set == descriptor_set && + descriptor_set_binding.binding == binding; + } +}; + +// See optimizer.hpp for documentation. +class ConvertToSampledImagePass : public Pass { + public: + // Hashing functor for the pair of descriptor set and binding. + struct DescriptorSetAndBindingHash { + size_t operator()( + const DescriptorSetAndBinding& descriptor_set_binding) const { + return std::hash()(descriptor_set_binding.descriptor_set) ^ + std::hash()(descriptor_set_binding.binding); + } + }; + + using SetOfDescriptorSetAndBindingPairs = + std::unordered_set; + using DescriptorSetBindingToInstruction = + std::unordered_map; + + explicit ConvertToSampledImagePass( + const std::vector& descriptor_set_binding_pairs) + : descriptor_set_binding_pairs_(descriptor_set_binding_pairs.begin(), + descriptor_set_binding_pairs.end()) {} + + const char* name() const override { return "convert-to-sampled-image"; } + Status Process() override; + + // Parses the given null-terminated C string to get a vector of descriptor set + // and binding pairs. Returns a unique pointer to the vector of descriptor set + // and binding pairs built from the given |str| on success. Returns a nullptr + // if the given string is not valid for building the vector of pairs. + // A valid string for building the vector of pairs should follow the rule + // below: + // + // ": : ..." + // Example: + // "3:5 2:1 0:4" + // + // Entries are separated with blank spaces (i.e.:' ', '\n', '\r', '\t', + // '\f', '\v'). Each entry corresponds to a descriptor set and binding pair. + // Multiple spaces between, before or after entries are allowed. However, + // spaces are not allowed within a descriptor set or binding. + // + // In each entry, the descriptor set and binding are separated by ':'. + // Missing ':' in any entry is invalid. And it is invalid to have blank + // spaces in between the descriptor set and ':' or ':' and the binding. + // + // : the descriptor set. + // The text must represent a valid uint32_t number. + // + // : the binding. + // The text must represent a valid uint32_t number. + static std::unique_ptr> + ParseDescriptorSetBindingPairsString(const char* str); + + private: + // Collects resources to convert to sampled image and saves them in + // |descriptor_set_binding_pair_to_sampler| if the resource is a sampler and + // saves them in |descriptor_set_binding_pair_to_image| if the resource is an + // image. Returns false if two samplers or two images have the same descriptor + // set and binding. Otherwise, returns true. + bool CollectResourcesToConvert( + DescriptorSetBindingToInstruction* descriptor_set_binding_pair_to_sampler, + DescriptorSetBindingToInstruction* descriptor_set_binding_pair_to_image) + const; + + // Finds an OpDecorate with DescriptorSet decorating |inst| and another + // OpDecorate with Binding decorating |inst|. Stores the descriptor set and + // binding in |descriptor_set_binding|. Returns whether it successfully finds + // the descriptor set and binding or not. + bool GetDescriptorSetBinding( + const Instruction& inst, + DescriptorSetAndBinding* descriptor_set_binding) const; + + // Returns whether |descriptor_set_binding| is a pair of a descriptor set + // and a binding that we have to convert resources with it to a sampled image + // or not. + bool ShouldResourceBeConverted( + const DescriptorSetAndBinding& descriptor_set_binding) const; + + // Returns the pointee type of the type of variable |variable|. If |variable| + // is not an OpVariable instruction, just returns nullptr. + const analysis::Type* GetVariableType(const Instruction& variable) const; + + // Returns the storage class of |variable|. + SpvStorageClass GetStorageClass(const Instruction& variable) const; + + // Finds |inst|'s users whose opcode is |user_opcode| or users of OpCopyObject + // instructions of |inst| whose opcode is |user_opcode| and puts them in + // |uses|. + void FindUses(const Instruction* inst, std::vector* uses, + uint32_t user_opcode) const; + + // Finds OpImage* instructions using |image| or OpCopyObject instructions that + // copy |image| and puts them in |uses|. + void FindUsesOfImage(const Instruction* image, + std::vector* uses) const; + + // Creates an OpImage instruction that extracts the image from the sampled + // image |sampled_image|. + Instruction* CreateImageExtraction(Instruction* sampled_image); + + // Converts |image_variable| whose type is an image pointer to sampled image + // type. Updates users of |image_variable| accordingly. If some instructions + // e.g., OpImageRead use |image_variable| as an Image operand, creates an + // image extracted from the sampled image using OpImage and replace the Image + // operands of the users with the extracted image. If some OpSampledImage + // instructions use |image_variable| and sampler whose descriptor set and + // binding are the same with |image_variable|, just combines |image_variable| + // and the sampler to a sampled image. + Pass::Status UpdateImageVariableToSampledImage( + Instruction* image_variable, + const DescriptorSetAndBinding& descriptor_set_binding); + + // Returns the id of type sampled image type whose image type is the one of + // |image_variable|. + uint32_t GetSampledImageTypeForImage(Instruction* image_variable); + + // Moves |inst| next to the OpType* instruction with |type_id|. + void MoveInstructionNextToType(Instruction* inst, uint32_t type_id); + + // Converts |image_variable| whose type is an image pointer to sampled image + // with the type id |sampled_image_type_id|. Returns whether it successfully + // converts the type of |image_variable| or not. + bool ConvertImageVariableToSampledImage(Instruction* image_variable, + uint32_t sampled_image_type_id); + + // Replaces |sampled_image_load| instruction used by OpImage* with the image + // extracted from |sampled_image_load|. Returns the extracted image or nullptr + // if it does not have uses. + Instruction* UpdateImageUses(Instruction* sampled_image_load); + + // Returns true if the sampler of |sampled_image_inst| is decorated by a + // descriptor set and a binding |descriptor_set_binding|. + bool IsSamplerOfSampledImageDecoratedByDescriptorSetBinding( + Instruction* sampled_image_inst, + const DescriptorSetAndBinding& descriptor_set_binding); + + // Replaces OpSampledImage instructions using |image_load| with |image_load| + // if the sampler of the OpSampledImage instruction has descriptor set and + // binding |image_descriptor_set_binding|. Otherwise, replaces |image_load| + // with |image_extraction|. + void UpdateSampledImageUses( + Instruction* image_load, Instruction* image_extraction, + const DescriptorSetAndBinding& image_descriptor_set_binding); + + // Checks the uses of |sampler_variable|. When a sampler is used by + // OpSampledImage instruction, the corresponding image must be + // |image_to_be_combined_with| that should be already converted to a sampled + // image by UpdateImageVariableToSampledImage() method. + Pass::Status CheckUsesOfSamplerVariable( + const Instruction* sampler_variable, + Instruction* image_to_be_combined_with); + + // Returns true if Image operand of |sampled_image_inst| is the image of + // |image_variable|. + bool DoesSampledImageReferenceImage(Instruction* sampled_image_inst, + Instruction* image_variable); + + // A set of pairs of descriptor set and binding. If an image and/or a sampler + // have a pair of descriptor set and binding that is an element of + // |descriptor_set_binding_pairs_|, they/it will be converted to a sampled + // image by this pass. + const SetOfDescriptorSetAndBindingPairs descriptor_set_binding_pairs_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_CONVERT_TO_SAMPLED_IMAGE_PASS_H_ diff --git a/source/opt/copy_prop_arrays.cpp b/source/opt/copy_prop_arrays.cpp index 67a97b6a..0b235629 100644 --- a/source/opt/copy_prop_arrays.cpp +++ b/source/opt/copy_prop_arrays.cpp @@ -29,10 +29,10 @@ const uint32_t kCompositeExtractObjectInOperand = 0; 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; +bool IsDebugDeclareOrValue(Instruction* di) { + auto dbg_opcode = di->GetCommonDebugOpcode(); + return dbg_opcode == CommonDebugInfoDebugDeclare || + dbg_opcode == CommonDebugInfoDebugValue; } } // namespace @@ -40,6 +40,10 @@ bool IsOpenCL100DebugDeclareOrValue(Instruction* di) { Pass::Status CopyPropagateArrays::Process() { bool modified = false; for (Function& function : *get_module()) { + if (function.IsDeclaration()) { + continue; + } + BasicBlock* entry_bb = &*function.begin(); for (auto var_inst = entry_bb->begin(); var_inst->opcode() == SpvOpVariable; @@ -147,9 +151,17 @@ Instruction* CopyPropagateArrays::BuildNewAccessChain( return source->GetVariable(); } + source->BuildConstants(); + std::vector access_ids(source->AccessChain().size()); + std::transform( + source->AccessChain().cbegin(), source->AccessChain().cend(), + access_ids.begin(), [](const AccessChainEntry& entry) { + assert(entry.is_result_id && "Constants needs to be built first."); + return entry.result_id; + }); + return builder.AddAccessChain(source->GetPointerTypeId(this), - source->GetVariable()->result_id(), - source->AccessChain()); + source->GetVariable()->result_id(), access_ids); } bool CopyPropagateArrays::HasNoStores(Instruction* ptr_inst) { @@ -164,6 +176,8 @@ bool CopyPropagateArrays::HasNoStores(Instruction* ptr_inst) { return false; } else if (use->opcode() == SpvOpImageTexelPointer) { return true; + } else if (use->opcode() == SpvOpEntryPoint) { + return true; } // Some other instruction. Be conservative. return false; @@ -194,7 +208,7 @@ bool CopyPropagateArrays::HasValidReferencesOnly(Instruction* ptr_inst, return ptr_inst->opcode() == SpvOpVariable && store_inst->GetSingleWordInOperand(kStorePointerInOperand) == ptr_inst->result_id(); - } else if (IsOpenCL100DebugDeclareOrValue(use)) { + } else if (IsDebugDeclareOrValue(use)) { return true; } // Some other instruction. Be conservative. @@ -264,30 +278,20 @@ std::unique_ptr CopyPropagateArrays::BuildMemoryObjectFromExtract(Instruction* extract_inst) { assert(extract_inst->opcode() == SpvOpCompositeExtract && "Expecting an OpCompositeExtract instruction."); - analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); - std::unique_ptr result = GetSourceObjectIfAny( extract_inst->GetSingleWordInOperand(kCompositeExtractObjectInOperand)); - if (result) { - analysis::Integer int_type(32, false); - const analysis::Type* uint32_type = - context()->get_type_mgr()->GetRegisteredType(&int_type); - - std::vector components; - // Convert the indices in the extract instruction to a series of ids that - // can be used by the |OpAccessChain| instruction. - for (uint32_t i = 1; i < extract_inst->NumInOperands(); ++i) { - uint32_t index = extract_inst->GetSingleWordInOperand(i); - const analysis::Constant* index_const = - const_mgr->GetConstant(uint32_type, {index}); - components.push_back( - const_mgr->GetDefiningInstruction(index_const)->result_id()); - } - result->GetMember(components); - return result; + if (!result) { + return nullptr; } - return nullptr; + + // Copy the indices of the extract instruction to |OpAccessChain| indices. + std::vector components; + for (uint32_t i = 1; i < extract_inst->NumInOperands(); ++i) { + components.push_back({false, {extract_inst->GetSingleWordInOperand(i)}}); + } + result->PushIndirection(components); + return result; } std::unique_ptr @@ -311,19 +315,12 @@ CopyPropagateArrays::BuildMemoryObjectFromCompositeConstruct( return nullptr; } - analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); - const analysis::Constant* last_access = - const_mgr->FindDeclaredConstant(memory_object->AccessChain().back()); - if (!last_access || !last_access->type()->AsInteger()) { + AccessChainEntry last_access = memory_object->AccessChain().back(); + if (!IsAccessChainIndexValidAndEqualTo(last_access, 0)) { return nullptr; } - if (last_access->GetU32() != 0) { - return nullptr; - } - - memory_object->GetParent(); - + memory_object->PopIndirection(); if (memory_object->GetNumberOfMembers() != conststruct_inst->NumInOperands()) { return nullptr; @@ -345,13 +342,8 @@ CopyPropagateArrays::BuildMemoryObjectFromCompositeConstruct( return nullptr; } - last_access = - const_mgr->FindDeclaredConstant(member_object->AccessChain().back()); - if (!last_access || !last_access->type()->AsInteger()) { - return nullptr; - } - - if (last_access->GetU32() != i) { + last_access = member_object->AccessChain().back(); + if (!IsAccessChainIndexValidAndEqualTo(last_access, i)) { return nullptr; } } @@ -405,17 +397,12 @@ CopyPropagateArrays::BuildMemoryObjectFromInsert(Instruction* insert_inst) { return nullptr; } - const analysis::Constant* last_access = - const_mgr->FindDeclaredConstant(memory_object->AccessChain().back()); - if (!last_access || !last_access->type()->AsInteger()) { + AccessChainEntry last_access = memory_object->AccessChain().back(); + if (!IsAccessChainIndexValidAndEqualTo(last_access, number_of_elements - 1)) { return nullptr; } - if (last_access->GetU32() != number_of_elements - 1) { - return nullptr; - } - - memory_object->GetParent(); + memory_object->PopIndirection(); Instruction* current_insert = def_use_mgr->GetDef(insert_inst->GetSingleWordInOperand(1)); @@ -452,14 +439,9 @@ CopyPropagateArrays::BuildMemoryObjectFromInsert(Instruction* insert_inst) { return nullptr; } - const analysis::Constant* current_last_access = - const_mgr->FindDeclaredConstant( - current_memory_object->AccessChain().back()); - if (!current_last_access || !current_last_access->type()->AsInteger()) { - return nullptr; - } - - if (current_last_access->GetU32() != i - 1) { + AccessChainEntry current_last_access = + current_memory_object->AccessChain().back(); + if (!IsAccessChainIndexValidAndEqualTo(current_last_access, i - 1)) { return nullptr; } current_insert = @@ -469,6 +451,21 @@ CopyPropagateArrays::BuildMemoryObjectFromInsert(Instruction* insert_inst) { return memory_object; } +bool CopyPropagateArrays::IsAccessChainIndexValidAndEqualTo( + const AccessChainEntry& entry, uint32_t value) const { + if (!entry.is_result_id) { + return entry.immediate == value; + } + + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + const analysis::Constant* constant = + const_mgr->FindDeclaredConstant(entry.result_id); + if (!constant || !constant->type()->AsInteger()) { + return false; + } + return constant->GetU32() == value; +} + bool CopyPropagateArrays::IsPointerToArrayType(uint32_t type_id) { analysis::TypeManager* type_mgr = context()->get_type_mgr(); analysis::Pointer* pointer_type = type_mgr->GetType(type_id)->AsPointer(); @@ -500,7 +497,7 @@ bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst, const_mgr, type](Instruction* use, uint32_t) { - if (IsOpenCL100DebugDeclareOrValue(use)) return true; + if (IsDebugDeclareOrValue(use)) return true; switch (use->opcode()) { case SpvOpLoad: { @@ -526,6 +523,12 @@ bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst, // Variable index means the type is a type where every element // is the same type. Use element 0 to get the type. access_chain.push_back(0); + + // We are trying to access a struct with variable indices. + // This cannot happen. + if (pointee_type->kind() == analysis::Type::kStruct) { + return false; + } } } @@ -592,9 +595,9 @@ void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst, Instruction* use = pair.first; uint32_t index = pair.second; - if (use->IsOpenCL100DebugInstr()) { - switch (use->GetOpenCL100DebugOpcode()) { - case OpenCLDebugInfo100DebugDeclare: { + if (use->IsCommonDebugInstr()) { + switch (use->GetCommonDebugOpcode()) { + case CommonDebugInfoDebugDeclare: { if (new_ptr_inst->opcode() == SpvOpVariable || new_ptr_inst->opcode() == SpvOpFunctionParameter) { context()->ForgetUses(use); @@ -608,9 +611,8 @@ void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst, context()->ForgetUses(use); // Change DebugDeclare to DebugValue. - use->SetOperand( - index - 2, - {static_cast(OpenCLDebugInfo100DebugValue)}); + use->SetOperand(index - 2, + {static_cast(CommonDebugInfoDebugValue)}); use->SetOperand(index, {new_ptr_inst->result_id()}); // Add Deref operation. @@ -625,7 +627,7 @@ void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst, } break; } - case OpenCLDebugInfo100DebugValue: + case CommonDebugInfoDebugValue: context()->ForgetUses(use); use->SetOperand(index, {new_ptr_inst->result_id()}); context()->AnalyzeUses(use); @@ -742,11 +744,11 @@ void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst, context()->AnalyzeUses(use); } break; + case SpvOpDecorate: + // We treat an OpImageTexelPointer as a load. The result type should + // always have the Image storage class, and should not need to be + // updated. case SpvOpImageTexelPointer: - // We treat an OpImageTexelPointer as a load. The result type should - // always have the Image storage class, and should not need to be - // updated. - // Replace the actual use. context()->ForgetUses(use); use->SetOperand(index, {new_ptr_inst->result_id()}); @@ -782,8 +784,8 @@ uint32_t CopyPropagateArrays::GetMemberTypeId( return id; } -void CopyPropagateArrays::MemoryObject::GetMember( - const std::vector& access_chain) { +void CopyPropagateArrays::MemoryObject::PushIndirection( + const std::vector& access_chain) { access_chain_.insert(access_chain_.end(), access_chain.begin(), access_chain.end()); } @@ -818,23 +820,29 @@ uint32_t CopyPropagateArrays::MemoryObject::GetNumberOfMembers() { template CopyPropagateArrays::MemoryObject::MemoryObject(Instruction* var_inst, iterator begin, iterator end) - : variable_inst_(var_inst), access_chain_(begin, end) {} + : variable_inst_(var_inst) { + std::transform(begin, end, std::back_inserter(access_chain_), + [](uint32_t id) { + return AccessChainEntry{true, {id}}; + }); +} std::vector CopyPropagateArrays::MemoryObject::GetAccessIds() const { analysis::ConstantManager* const_mgr = variable_inst_->context()->get_constant_mgr(); - std::vector access_indices; - for (uint32_t id : AccessChain()) { - const analysis::Constant* element_index_const = - const_mgr->FindDeclaredConstant(id); - if (!element_index_const) { - access_indices.push_back(0); - } else { - access_indices.push_back(element_index_const->GetU32()); - } - } - return access_indices; + std::vector indices(AccessChain().size()); + std::transform(AccessChain().cbegin(), AccessChain().cend(), indices.begin(), + [&const_mgr](const AccessChainEntry& entry) { + if (entry.is_result_id) { + const analysis::Constant* constant = + const_mgr->FindDeclaredConstant(entry.result_id); + return constant == nullptr ? 0 : constant->GetU32(); + } + + return entry.immediate; + }); + return indices; } bool CopyPropagateArrays::MemoryObject::Contains( @@ -855,5 +863,24 @@ bool CopyPropagateArrays::MemoryObject::Contains( return true; } +void CopyPropagateArrays::MemoryObject::BuildConstants() { + for (auto& entry : access_chain_) { + if (entry.is_result_id) { + continue; + } + + auto context = variable_inst_->context(); + analysis::Integer int_type(32, false); + const analysis::Type* uint32_type = + context->get_type_mgr()->GetRegisteredType(&int_type); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Constant* index_const = + const_mgr->GetConstant(uint32_type, {entry.immediate}); + entry.result_id = + const_mgr->GetDefiningInstruction(index_const)->result_id(); + entry.is_result_id = true; + } +} + } // namespace opt } // namespace spvtools diff --git a/source/opt/copy_prop_arrays.h b/source/opt/copy_prop_arrays.h index f4314a74..9e7641f6 100644 --- a/source/opt/copy_prop_arrays.h +++ b/source/opt/copy_prop_arrays.h @@ -35,7 +35,7 @@ namespace opt { // // The hard part is keeping all of the types correct. We do not want to // have to do too large a search to update everything, which may not be -// possible, do we give up if we see any instruction that might be hard to +// possible, so we give up if we see any instruction that might be hard to // update. class CopyPropagateArrays : public MemPass { @@ -52,6 +52,22 @@ class CopyPropagateArrays : public MemPass { } private: + // Represents one index in the OpAccessChain instruction. It can be either + // an instruction's result_id (OpConstant by ex), or a immediate value. + // Immediate values are used to prepare the final access chain without + // creating OpConstant instructions until done. + struct AccessChainEntry { + bool is_result_id; + union { + uint32_t result_id; + uint32_t immediate; + }; + + bool operator!=(const AccessChainEntry& other) const { + return other.is_result_id != is_result_id || other.result_id != result_id; + } + }; + // The class used to identify a particular memory object. This memory object // will be owned by a particular variable, meaning that the memory is part of // that variable. It could be the entire variable or a member of the @@ -70,12 +86,12 @@ class CopyPropagateArrays : public MemPass { // (starting from the current member). The elements in |access_chain| are // interpreted the same as the indices in the |OpAccessChain| // instruction. - void GetMember(const std::vector& access_chain); + void PushIndirection(const std::vector& access_chain); // Change |this| to now represent the first enclosing object to which it // belongs. (Remove the last element off the access_chain). It is invalid // to call this function if |this| does not represent a member of its owner. - void GetParent() { + void PopIndirection() { assert(IsMember()); access_chain_.pop_back(); } @@ -95,7 +111,13 @@ class CopyPropagateArrays : public MemPass { // member that |this| represents starting from the owning variable. These // values are to be interpreted the same way the indices are in an // |OpAccessChain| instruction. - const std::vector& AccessChain() const { return access_chain_; } + const std::vector& AccessChain() const { + return access_chain_; + } + + // Converts all immediate values in the AccessChain their OpConstant + // equivalent. + void BuildConstants(); // Returns the type id of the pointer type that can be used to point to this // memory object. @@ -137,7 +159,7 @@ class CopyPropagateArrays : public MemPass { // The access chain to reach the particular member the memory object // represents. It should be interpreted the same way the indices in an // |OpAccessChain| are interpreted. - std::vector access_chain_; + std::vector access_chain_; std::vector GetAccessIds() const; }; @@ -192,10 +214,14 @@ class CopyPropagateArrays : public MemPass { std::unique_ptr BuildMemoryObjectFromInsert( Instruction* insert_inst); + // Return true if the given entry can represent the given value. + bool IsAccessChainIndexValidAndEqualTo(const AccessChainEntry& entry, + uint32_t value) const; + // Return true if |type_id| is a pointer type whose pointee type is an array. bool IsPointerToArrayType(uint32_t type_id); - // Returns true of there are not stores using |ptr_inst| or something derived + // Returns true if there are not stores using |ptr_inst| or something derived // from it. bool HasNoStores(Instruction* ptr_inst); diff --git a/source/opt/dead_branch_elim_pass.cpp b/source/opt/dead_branch_elim_pass.cpp index 0054f576..d99b7f78 100644 --- a/source/opt/dead_branch_elim_pass.cpp +++ b/source/opt/dead_branch_elim_pass.cpp @@ -207,7 +207,7 @@ bool DeadBranchElimPass::SimplifyBranch(BasicBlock* block, Instruction::OperandList new_operands; new_operands.push_back(terminator->GetInOperand(0)); new_operands.push_back({SPV_OPERAND_TYPE_ID, {live_lab_id}}); - terminator->SetInOperands(move(new_operands)); + terminator->SetInOperands(std::move(new_operands)); context()->UpdateDefUse(terminator); } else { // Check if the merge instruction is still needed because of a @@ -346,6 +346,7 @@ bool DeadBranchElimPass::FixPhiNodesInLiveBlocks( if (operands.size() == 4) { // First input data operands is at index 2. uint32_t replId = operands[2u].words[0]; + context()->KillNamesAndDecorates(inst->result_id()); context()->ReplaceAllUsesWith(inst->result_id(), replId); iter = context()->KillInst(&*inst); } else { @@ -419,6 +420,10 @@ bool DeadBranchElimPass::EraseDeadBlocks( } bool DeadBranchElimPass::EliminateDeadBranches(Function* func) { + if (func->IsDeclaration()) { + return false; + } + bool modified = false; std::unordered_set live_blocks; modified |= MarkLiveBlocks(func, &live_blocks); @@ -454,17 +459,8 @@ void DeadBranchElimPass::FixBlockOrder() { }; // Reorders blocks according to structured order. - ProcessFunction reorder_structured = [this](Function* function) { - std::list order; - context()->cfg()->ComputeStructuredOrder(function, &*function->begin(), - &order); - std::vector blocks; - for (auto block : order) { - blocks.push_back(block); - } - for (uint32_t i = 1; i < blocks.size(); ++i) { - function->MoveBasicBlockToAfter(blocks[i]->id(), blocks[i - 1]); - } + ProcessFunction reorder_structured = [](Function* function) { + function->ReorderBasicBlocksInStructuredOrder(); return true; }; diff --git a/source/opt/dead_branch_elim_pass.h b/source/opt/dead_branch_elim_pass.h index 7841bc47..198bad2d 100644 --- a/source/opt/dead_branch_elim_pass.h +++ b/source/opt/dead_branch_elim_pass.h @@ -98,7 +98,7 @@ class DeadBranchElimPass : public MemPass { // Fix phis in reachable blocks so that only live (or unremovable) incoming // edges are present. If the block now only has a single live incoming edge, // remove the phi and replace its uses with its data input. If the single - // remaining incoming edge is from the phi itself, the the phi is in an + // remaining incoming edge is from the phi itself, the phi is in an // unreachable single block loop. Either the block is dead and will be // removed, or it's reachable from an unreachable continue target. In the // latter case that continue target block will be collapsed into a block that @@ -158,7 +158,7 @@ class DeadBranchElimPass : public MemPass { uint32_t cont_id, uint32_t header_id, uint32_t merge_id, std::unordered_set* blocks_with_back_edges); - // Returns true if there is a brach to the merge node of the selection + // Returns true if there is a branch to the merge node of the selection // construct |switch_header_id| that is inside a nested selection construct or // in the header of the nested selection construct. bool SwitchHasNestedBreak(uint32_t switch_header_id); diff --git a/source/opt/dead_insert_elim_pass.cpp b/source/opt/dead_insert_elim_pass.cpp index 46f4f124..d877f0f9 100644 --- a/source/opt/dead_insert_elim_pass.cpp +++ b/source/opt/dead_insert_elim_pass.cpp @@ -196,7 +196,7 @@ bool DeadInsertElimPass::EliminateDeadInsertsOnePass(Function* func) { } const uint32_t id = ii->result_id(); get_def_use_mgr()->ForEachUser(id, [&ii, this](Instruction* user) { - if (user->IsOpenCL100DebugInstr()) return; + if (user->IsCommonDebugInstr()) return; switch (user->opcode()) { case SpvOpCompositeInsert: case SpvOpPhi: @@ -256,7 +256,7 @@ Pass::Status DeadInsertElimPass::Process() { ProcessFunction pfn = [this](Function* fp) { return EliminateDeadInserts(fp); }; - bool modified = context()->ProcessEntryPointCallTree(pfn); + bool modified = context()->ProcessReachableCallTree(pfn); return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } diff --git a/source/opt/debug_info_manager.cpp b/source/opt/debug_info_manager.cpp index e782641f..3585186f 100644 --- a/source/opt/debug_info_manager.cpp +++ b/source/opt/debug_info_manager.cpp @@ -1,4 +1,5 @@ -// Copyright (c) 2020 Google LLC +// Copyright (c) 2020-2022 Google LLC +// Copyright (c) 2022 LunarG Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,12 +19,16 @@ #include "source/opt/ir_context.h" -// Constants for OpenCL.DebugInfo.100 extension instructions. +// Constants for OpenCL.DebugInfo.100 & NonSemantic.Shader.DebugInfo.100 +// extension instructions. static const uint32_t kOpLineOperandLineIndex = 1; static const uint32_t kLineOperandIndexDebugFunction = 7; static const uint32_t kLineOperandIndexDebugLexicalBlock = 5; +static const uint32_t kLineOperandIndexDebugLine = 5; static const uint32_t kDebugFunctionOperandFunctionIndex = 13; +static const uint32_t kDebugFunctionDefinitionOperandDebugFunctionIndex = 4; +static const uint32_t kDebugFunctionDefinitionOperandOpFunctionIndex = 5; static const uint32_t kDebugFunctionOperandParentIndex = 9; static const uint32_t kDebugTypeCompositeOperandParentIndex = 9; static const uint32_t kDebugLexicalBlockOperandParentIndex = 7; @@ -46,8 +51,8 @@ namespace { void SetInlinedOperand(Instruction* dbg_inlined_at, uint32_t inlined_operand) { assert(dbg_inlined_at); - assert(dbg_inlined_at->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugInlinedAt); + assert(dbg_inlined_at->GetCommonDebugOpcode() == + CommonDebugInfoDebugInlinedAt); if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex) { dbg_inlined_at->AddOperand( {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inlined_operand}}); @@ -59,8 +64,8 @@ void SetInlinedOperand(Instruction* dbg_inlined_at, uint32_t inlined_operand) { uint32_t GetInlinedOperand(Instruction* dbg_inlined_at) { assert(dbg_inlined_at); - assert(dbg_inlined_at->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugInlinedAt); + assert(dbg_inlined_at->GetCommonDebugOpcode() == + CommonDebugInfoDebugInlinedAt); if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex) return kNoInlinedAt; return dbg_inlined_at->GetSingleWordOperand( @@ -68,8 +73,7 @@ uint32_t GetInlinedOperand(Instruction* dbg_inlined_at) { } bool IsEmptyDebugExpression(Instruction* instr) { - return instr->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugExpression && + return (instr->GetCommonDebugOpcode() == CommonDebugInfoDebugExpression) && instr->NumOperands() == kDebugExpressOperandOperationIndex; } @@ -79,43 +83,63 @@ DebugInfoManager::DebugInfoManager(IRContext* c) : context_(c) { AnalyzeDebugInsts(*c->module()); } +uint32_t DebugInfoManager::GetDbgSetImportId() { + uint32_t setId = + context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo(); + if (setId == 0) { + setId = + context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo(); + } + return setId; +} + Instruction* DebugInfoManager::GetDbgInst(uint32_t id) { auto dbg_inst_it = id_to_dbg_inst_.find(id); return dbg_inst_it == id_to_dbg_inst_.end() ? nullptr : dbg_inst_it->second; } void DebugInfoManager::RegisterDbgInst(Instruction* inst) { - assert( - inst->NumInOperands() != 0 && - context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() == - inst->GetInOperand(0).words[0] && - "Given instruction is not a debug instruction"); + assert(inst->NumInOperands() != 0 && + (GetDbgSetImportId() == inst->GetInOperand(0).words[0]) && + "Given instruction is not a debug instruction"); id_to_dbg_inst_[inst->result_id()] = inst; } void DebugInfoManager::RegisterDbgFunction(Instruction* inst) { - assert(inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction && - "inst is not a DebugFunction"); - auto fn_id = inst->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex); - // Do not register function that has been optimized away - auto fn_inst = GetDbgInst(fn_id); - if (fn_inst != nullptr) { - assert(GetDbgInst(fn_id)->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugInfoNone); - return; + if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) { + auto fn_id = inst->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex); + // Do not register function that has been optimized away. + auto fn_inst = GetDbgInst(fn_id); + if (fn_inst != nullptr) { + assert(GetDbgInst(fn_id)->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugInfoNone); + return; + } + assert( + fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() && + "Register DebugFunction for a function that already has DebugFunction"); + fn_id_to_dbg_fn_[fn_id] = inst; + } else if (inst->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) { + auto fn_id = inst->GetSingleWordOperand( + kDebugFunctionDefinitionOperandOpFunctionIndex); + auto fn_inst = GetDbgInst(inst->GetSingleWordOperand( + kDebugFunctionDefinitionOperandDebugFunctionIndex)); + assert(fn_inst && fn_inst->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunction); + assert(fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() && + "Register DebugFunctionDefinition for a function that already has " + "DebugFunctionDefinition"); + fn_id_to_dbg_fn_[fn_id] = fn_inst; + } else { + assert(false && "inst is not a DebugFunction"); } - assert( - fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() && - "Register DebugFunction for a function that already has DebugFunction"); - 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); + assert(dbg_declare->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare || + dbg_declare->GetCommonDebugOpcode() == CommonDebugInfoDebugValue); 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}; @@ -124,29 +148,57 @@ void DebugInfoManager::RegisterDbgDeclare(uint32_t var_id, } } +// Create new constant directly into global value area, bypassing the +// Constant manager. This is used when the DefUse or Constant managers +// are invalid and cannot be regenerated due to the module being in an +// inconsistent state e.g. in the middle of significant modification +// such as inlining. Invalidate Constant and DefUse managers if used. +uint32_t AddNewConstInGlobals(IRContext* context, uint32_t const_value) { + uint32_t id = context->TakeNextId(); + std::unique_ptr new_const(new Instruction( + context, SpvOpConstant, context->get_type_mgr()->GetUIntTypeId(), id, + { + {spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, + {const_value}}, + })); + context->module()->AddGlobalValue(std::move(new_const)); + context->InvalidateAnalyses(IRContext::kAnalysisConstants); + context->InvalidateAnalyses(IRContext::kAnalysisDefUse); + return id; +} + uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line, const DebugScope& scope) { - if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() == - 0) - return kNoInlinedAt; + uint32_t setId = GetDbgSetImportId(); + + if (setId == 0) return kNoInlinedAt; + + spv_operand_type_t line_number_type = + spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER; + + // In NonSemantic.Shader.DebugInfo.100, all constants are IDs of OpConstant, + // not literals. + if (setId == + context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) + line_number_type = spv_operand_type_t::SPV_OPERAND_TYPE_ID; uint32_t line_number = 0; if (line == nullptr) { auto* lexical_scope_inst = GetDbgInst(scope.GetLexicalScope()); if (lexical_scope_inst == nullptr) return kNoInlinedAt; - OpenCLDebugInfo100Instructions debug_opcode = - lexical_scope_inst->GetOpenCL100DebugOpcode(); + CommonDebugInfoInstructions debug_opcode = + lexical_scope_inst->GetCommonDebugOpcode(); switch (debug_opcode) { - case OpenCLDebugInfo100DebugFunction: + case CommonDebugInfoDebugFunction: line_number = lexical_scope_inst->GetSingleWordOperand( kLineOperandIndexDebugFunction); break; - case OpenCLDebugInfo100DebugLexicalBlock: + case CommonDebugInfoDebugLexicalBlock: line_number = lexical_scope_inst->GetSingleWordOperand( kLineOperandIndexDebugLexicalBlock); break; - case OpenCLDebugInfo100DebugTypeComposite: - case OpenCLDebugInfo100DebugCompilationUnit: + case CommonDebugInfoDebugTypeComposite: + case CommonDebugInfoDebugCompilationUnit: assert(false && "DebugTypeComposite and DebugCompilationUnit are lexical " "scopes, but we inline functions into a function or a block " @@ -160,7 +212,31 @@ uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line, break; } } else { - line_number = line->GetSingleWordOperand(kOpLineOperandLineIndex); + if (line->opcode() == SpvOpLine) { + line_number = line->GetSingleWordOperand(kOpLineOperandLineIndex); + } else if (line->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugLine) { + line_number = line->GetSingleWordOperand(kLineOperandIndexDebugLine); + } else { + assert(false && + "Unreachable. A line instruction must be OpLine or DebugLine"); + } + + // If we need the line number as an ID, generate that constant now. + // If Constant or DefUse managers are invalid, generate constant + // directly into the global value section of the module; do not + // use Constant manager which may attempt to invoke building of the + // DefUse manager which cannot be done during inlining. The extra + // constants that may be generated here is likely not significant + // and will likely be cleaned up in later passes. + if (line_number_type == spv_operand_type_t::SPV_OPERAND_TYPE_ID && + line->opcode() == SpvOpLine) { + if (!context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse) || + !context()->AreAnalysesValid(IRContext::Analysis::kAnalysisConstants)) + line_number = AddNewConstInGlobals(context(), line_number); + else + line_number = context()->get_constant_mgr()->GetUIntConst(line_number); + } } uint32_t result_id = context()->TakeNextId(); @@ -168,13 +244,10 @@ uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line, 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_ID, {setId}}, {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, - {static_cast(OpenCLDebugInfo100DebugInlinedAt)}}, - {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {line_number}}, + {static_cast(CommonDebugInfoDebugInlinedAt)}}, + {line_number_type, {line_number}}, {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {scope.GetLexicalScope()}}, })); // |scope| already has DebugInlinedAt. We put the existing DebugInlinedAt @@ -257,19 +330,34 @@ Instruction* DebugInfoManager::GetDebugOperationWithDeref() { if (deref_operation_ != nullptr) return deref_operation_; uint32_t result_id = context()->TakeNextId(); - std::unique_ptr 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(OpenCLDebugInfo100DebugOperation)}}, - {SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION, - {static_cast(OpenCLDebugInfo100Deref)}}, - })); + std::unique_ptr deref_operation; + + if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()) { + deref_operation = std::unique_ptr(new Instruction( + context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), + result_id, + { + {SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast(OpenCLDebugInfo100DebugOperation)}}, + {SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION, + {static_cast(OpenCLDebugInfo100Deref)}}, + })); + } else { + uint32_t deref_id = context()->get_constant_mgr()->GetUIntConst( + NonSemanticShaderDebugInfo100Deref); + + deref_operation = std::unique_ptr( + new Instruction(context(), SpvOpExtInst, + context()->get_type_mgr()->GetVoidTypeId(), result_id, + { + {SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast( + NonSemanticShaderDebugInfo100DebugOperation)}}, + {SPV_OPERAND_TYPE_ID, {deref_id}}, + })); + } // Add to the front of |ext_inst_debuginfo_|. deref_operation_ = @@ -283,8 +371,7 @@ Instruction* DebugInfoManager::GetDebugOperationWithDeref() { } Instruction* DebugInfoManager::DerefDebugExpression(Instruction* dbg_expr) { - assert(dbg_expr->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugExpression); + assert(dbg_expr->GetCommonDebugOpcode() == CommonDebugInfoDebugExpression); std::unique_ptr deref_expr(dbg_expr->Clone(context())); deref_expr->SetResultId(context()->TakeNextId()); deref_expr->InsertOperand( @@ -306,12 +393,9 @@ Instruction* DebugInfoManager::GetDebugInfoNone() { 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_ID, {GetDbgSetImportId()}}, {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, - {static_cast(OpenCLDebugInfo100DebugInfoNone)}}, + {static_cast(CommonDebugInfoDebugInfoNone)}}, })); // Add to the front of |ext_inst_debuginfo_|. @@ -333,12 +417,9 @@ Instruction* DebugInfoManager::GetEmptyDebugExpression() { 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_ID, {GetDbgSetImportId()}}, {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, - {static_cast(OpenCLDebugInfo100DebugExpression)}}, + {static_cast(CommonDebugInfoDebugExpression)}}, })); // Add to the front of |ext_inst_debuginfo_|. @@ -355,8 +436,7 @@ Instruction* DebugInfoManager::GetEmptyDebugExpression() { Instruction* DebugInfoManager::GetDebugInlinedAt(uint32_t dbg_inlined_at_id) { auto* inlined_at = GetDbgInst(dbg_inlined_at_id); if (inlined_at == nullptr) return nullptr; - if (inlined_at->GetOpenCL100DebugOpcode() != - OpenCLDebugInfo100DebugInlinedAt) { + if (inlined_at->GetCommonDebugOpcode() != CommonDebugInfoDebugInlinedAt) { return nullptr; } return inlined_at; @@ -403,23 +483,23 @@ bool DebugInfoManager::KillDebugDeclares(uint32_t variable_id) { 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(); + CommonDebugInfoInstructions debug_opcode = + dbg_scope_itr->second->GetCommonDebugOpcode(); uint32_t parent_scope = kNoDebugScope; switch (debug_opcode) { - case OpenCLDebugInfo100DebugFunction: + case CommonDebugInfoDebugFunction: parent_scope = dbg_scope_itr->second->GetSingleWordOperand( kDebugFunctionOperandParentIndex); break; - case OpenCLDebugInfo100DebugLexicalBlock: + case CommonDebugInfoDebugLexicalBlock: parent_scope = dbg_scope_itr->second->GetSingleWordOperand( kDebugLexicalBlockOperandParentIndex); break; - case OpenCLDebugInfo100DebugTypeComposite: + case CommonDebugInfoDebugTypeComposite: parent_scope = dbg_scope_itr->second->GetSingleWordOperand( kDebugTypeCompositeOperandParentIndex); break; - case OpenCLDebugInfo100DebugCompilationUnit: + case CommonDebugInfoDebugCompilationUnit: // DebugCompilationUnit does not have a parent scope. break; default: @@ -477,10 +557,10 @@ bool DebugInfoManager::IsDeclareVisibleToInstr(Instruction* dbg_declare, return false; } -bool DebugInfoManager::AddDebugValueIfVarDeclIsVisible( - Instruction* scope_and_line, uint32_t variable_id, uint32_t value_id, - Instruction* insert_pos, - std::unordered_set* invisible_decls) { +bool DebugInfoManager::AddDebugValueForVariable(Instruction* scope_and_line, + uint32_t variable_id, + uint32_t value_id, + Instruction* insert_pos) { assert(scope_and_line != nullptr); auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id); @@ -488,11 +568,6 @@ bool DebugInfoManager::AddDebugValueIfVarDeclIsVisible( bool modified = false; for (auto* dbg_decl_or_val : dbg_decl_itr->second) { - if (!IsDeclareVisibleToInstr(dbg_decl_or_val, scope_and_line)) { - if (invisible_decls) invisible_decls->insert(dbg_decl_or_val); - continue; - } - // Avoid inserting the new DebugValue between OpPhi or OpVariable // instructions. Instruction* insert_before = insert_pos->NextNode(); @@ -513,8 +588,7 @@ Instruction* DebugInfoManager::AddDebugValueForDecl( std::unique_ptr dbg_val(dbg_decl->Clone(context())); dbg_val->SetResultId(context()->TakeNextId()); - dbg_val->SetInOperand(kExtInstInstructionInIdx, - {OpenCLDebugInfo100DebugValue}); + dbg_val->SetInOperand(kExtInstInstructionInIdx, {CommonDebugInfoDebugValue}); dbg_val->SetOperand(kDebugDeclareOperandVariableIndex, {value_id}); dbg_val->SetOperand(kDebugValueOperandExpressionIndex, {GetEmptyDebugExpression()->result_id()}); @@ -532,9 +606,20 @@ Instruction* DebugInfoManager::AddDebugValueForDecl( return added_dbg_val; } +uint32_t DebugInfoManager::GetVulkanDebugOperation(Instruction* inst) { + assert(inst->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugOperation && + "inst must be Vulkan DebugOperation"); + return context() + ->get_constant_mgr() + ->GetConstantFromInst(context()->get_def_use_mgr()->GetDef( + inst->GetSingleWordOperand(kDebugOperationOperandOperationIndex))) + ->GetU32(); +} + uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare( Instruction* inst) { - if (inst->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugValue) return 0; + if (inst->GetCommonDebugOpcode() != CommonDebugInfoDebugValue) return 0; auto* expr = GetDbgInst(inst->GetSingleWordOperand(kDebugValueOperandExpressionIndex)); @@ -544,9 +629,19 @@ uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare( auto* operation = GetDbgInst( expr->GetSingleWordOperand(kDebugExpressOperandOperationIndex)); if (operation == nullptr) return 0; - if (operation->GetSingleWordOperand(kDebugOperationOperandOperationIndex) != - OpenCLDebugInfo100Deref) { - return 0; + + // OpenCL.DebugInfo.100 contains a literal for the operation, Vulkan uses an + // OpConstant. + if (inst->IsOpenCL100DebugInstr()) { + if (operation->GetSingleWordOperand(kDebugOperationOperandOperationIndex) != + OpenCLDebugInfo100Deref) { + return 0; + } + } else { + uint32_t operation_const = GetVulkanDebugOperation(operation); + if (operation_const != NonSemanticShaderDebugInfo100Deref) { + return 0; + } } uint32_t var_id = @@ -567,8 +662,8 @@ uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare( } bool DebugInfoManager::IsDebugDeclare(Instruction* instr) { - if (!instr->IsOpenCL100DebugInstr()) return false; - return instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare || + if (!instr->IsCommonDebugInstr()) return false; + return instr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare || GetVariableIdOfDebugValueUsedForDeclare(instr) != 0; } @@ -615,14 +710,13 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) { users.insert(inst); } - if (!inst->IsOpenCL100DebugInstr()) return; + if (!inst->IsCommonDebugInstr()) return; RegisterDbgInst(inst); - if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) { - assert(GetDebugFunction(inst->GetSingleWordOperand( - kDebugFunctionOperandFunctionIndex)) == nullptr && - "Two DebugFunction instruction exists for a single OpFunction."); + if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction || + inst->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) { RegisterDbgFunction(inst); } @@ -633,8 +727,17 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) { deref_operation_ = inst; } + if (deref_operation_ == nullptr && + inst->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugOperation) { + uint32_t operation_const = GetVulkanDebugOperation(inst); + if (operation_const == NonSemanticShaderDebugInfo100Deref) { + deref_operation_ = inst; + } + } + if (debug_info_none_inst_ == nullptr && - inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) { + inst->GetCommonDebugOpcode() == CommonDebugInfoDebugInfoNone) { debug_info_none_inst_ = inst; } @@ -642,7 +745,7 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) { empty_debug_expr_inst_ = inst; } - if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) { + if (inst->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { uint32_t var_id = inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); RegisterDbgDeclare(var_id, inst); @@ -655,8 +758,8 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) { void DebugInfoManager::ConvertDebugGlobalToLocalVariable( Instruction* dbg_global_var, Instruction* local_var) { - if (dbg_global_var->GetOpenCL100DebugOpcode() != - OpenCLDebugInfo100DebugGlobalVariable) { + if (dbg_global_var->GetCommonDebugOpcode() != + CommonDebugInfoDebugGlobalVariable) { return; } assert(local_var->opcode() == SpvOpVariable || @@ -664,7 +767,7 @@ void DebugInfoManager::ConvertDebugGlobalToLocalVariable( // Convert |dbg_global_var| to DebugLocalVariable dbg_global_var->SetInOperand(kExtInstInstructionInIdx, - {OpenCLDebugInfo100DebugLocalVariable}); + {CommonDebugInfoDebugLocalVariable}); auto flags = dbg_global_var->GetSingleWordOperand( kDebugGlobalVariableOperandFlagsIndex); for (uint32_t i = dbg_global_var->NumInOperands() - 1; @@ -680,20 +783,20 @@ void DebugInfoManager::ConvertDebugGlobalToLocalVariable( 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_ID, {GetDbgSetImportId()}}, {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, - {static_cast(OpenCLDebugInfo100DebugDeclare)}}, + {static_cast(CommonDebugInfoDebugDeclare)}}, {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)); + // Must insert after all OpVariables in block + Instruction* insert_before = local_var; + while (insert_before->opcode() == SpvOpVariable) + insert_before = insert_before->NextNode(); + auto* added_dbg_decl = insert_before->InsertBefore(std::move(new_dbg_decl)); if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_decl); if (context()->AreAnalysesValid( @@ -713,7 +816,7 @@ void DebugInfoManager::AnalyzeDebugInsts(Module& module) { // list. if (empty_debug_expr_inst_ != nullptr && empty_debug_expr_inst_->PreviousNode() != nullptr && - empty_debug_expr_inst_->PreviousNode()->IsOpenCL100DebugInstr()) { + empty_debug_expr_inst_->PreviousNode()->IsCommonDebugInstr()) { empty_debug_expr_inst_->InsertBefore( &*context()->module()->ext_inst_debuginfo_begin()); } @@ -722,7 +825,7 @@ void DebugInfoManager::AnalyzeDebugInsts(Module& module) { // list. if (debug_info_none_inst_ != nullptr && debug_info_none_inst_->PreviousNode() != nullptr && - debug_info_none_inst_->PreviousNode()->IsOpenCL100DebugInstr()) { + debug_info_none_inst_->PreviousNode()->IsCommonDebugInstr()) { debug_info_none_inst_->InsertBefore( &*context()->module()->ext_inst_debuginfo_begin()); } @@ -740,7 +843,7 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) { inlinedat_id_to_users_itr->second.erase(instr); } - if (instr == nullptr || !instr->IsOpenCL100DebugInstr()) { + if (instr == nullptr || !instr->IsCommonDebugInstr()) { return; } @@ -751,9 +854,15 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) { instr->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex); fn_id_to_dbg_fn_.erase(fn_id); } + if (instr->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) { + auto fn_id = instr->GetSingleWordOperand( + kDebugFunctionDefinitionOperandOpFunctionIndex); + fn_id_to_dbg_fn_.erase(fn_id); + } - if (instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare || - instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) { + if (instr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare || + instr->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) { auto var_or_value_id = instr->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_or_value_id); @@ -767,6 +876,8 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) { for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin(); dbg_instr_itr != context()->module()->ext_inst_debuginfo_end(); ++dbg_instr_itr) { + // OpenCL.DebugInfo.100 contains the operation as a literal operand, in + // Vulkan it's referenced as an OpConstant. if (instr != &*dbg_instr_itr && dbg_instr_itr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugOperation && @@ -775,6 +886,14 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) { OpenCLDebugInfo100Deref) { deref_operation_ = &*dbg_instr_itr; break; + } else if (instr != &*dbg_instr_itr && + dbg_instr_itr->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugOperation) { + uint32_t operation_const = GetVulkanDebugOperation(&*dbg_instr_itr); + if (operation_const == NonSemanticShaderDebugInfo100Deref) { + deref_operation_ = &*dbg_instr_itr; + break; + } } } } @@ -784,9 +903,8 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) { 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) { + if (instr != &*dbg_instr_itr && dbg_instr_itr->GetCommonDebugOpcode() == + CommonDebugInfoDebugInfoNone) { debug_info_none_inst_ = &*dbg_instr_itr; break; } diff --git a/source/opt/debug_info_manager.h b/source/opt/debug_info_manager.h index 776e9baa..abb7b9a0 100644 --- a/source/opt/debug_info_manager.h +++ b/source/opt/debug_info_manager.h @@ -15,6 +15,7 @@ #ifndef SOURCE_OPT_DEBUG_INFO_MANAGER_H_ #define SOURCE_OPT_DEBUG_INFO_MANAGER_H_ +#include #include #include @@ -67,8 +68,8 @@ class DebugInlinedAtContext { std::unordered_map callee_inlined_at2chain_; }; -// A class for analyzing, managing, and creating OpenCL.DebugInfo.100 extension -// instructions. +// A class for analyzing, managing, and creating OpenCL.DebugInfo.100 and +// NonSemantic.Shader.DebugInfo.100 extension instructions. class DebugInfoManager { public: // Constructs a debug information manager from the given |context|. @@ -85,7 +86,7 @@ class DebugInfoManager { return !(lhs == rhs); } - // Analyzes OpenCL.DebugInfo.100 instruction |dbg_inst|. + // Analyzes DebugInfo instruction |dbg_inst|. void AnalyzeDebugInst(Instruction* dbg_inst); // Creates new DebugInlinedAt and returns its id. Its line operand is the @@ -144,12 +145,10 @@ class DebugInfoManager { // 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|. - // Returns whether a DebugValue is added or not. |invisible_decls| returns - // DebugDeclares invisible to |scope_and_line|. - bool AddDebugValueIfVarDeclIsVisible( - Instruction* scope_and_line, uint32_t variable_id, uint32_t value_id, - Instruction* insert_pos, - std::unordered_set* invisible_decls); + // Returns whether a DebugValue is added or not. + bool AddDebugValueForVariable(Instruction* scope_and_line, + uint32_t variable_id, uint32_t value_id, + Instruction* insert_pos); // Creates a DebugValue for DebugDeclare |dbg_decl| and inserts it before // |insert_before|. The new DebugValue has the same line and scope as @@ -164,6 +163,9 @@ class DebugInfoManager { // Erases |instr| from data structures of this class. void ClearDebugInfo(Instruction* instr); + // Return the opcode for the Vulkan DebugOperation inst + uint32_t GetVulkanDebugOperation(Instruction* inst); + // 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. @@ -190,10 +192,13 @@ class DebugInfoManager { private: IRContext* context() { return context_; } - // Analyzes OpenCL.DebugInfo.100 instructions in the given |module| and + // Analyzes DebugInfo instructions in the given |module| and // populates data structures in this class. void AnalyzeDebugInsts(Module& module); + // Get the DebugInfo ExtInstImport Id, or 0 if no DebugInfo is available. + uint32_t GetDbgSetImportId(); + // Returns the debug instruction whose id is |id|. Returns |nullptr| if one // does not exists. Instruction* GetDbgInst(uint32_t id); @@ -230,7 +235,7 @@ class DebugInfoManager { IRContext* context_; - // Mapping from ids of OpenCL.DebugInfo.100 extension instructions + // Mapping from ids of DebugInfo extension instructions. // to their Instruction instances. std::unordered_map id_to_dbg_inst_; @@ -238,9 +243,18 @@ class DebugInfoManager { // operand is the function. std::unordered_map fn_id_to_dbg_fn_; + // Orders Instruction* for use in associative containers (i.e. less than + // ordering). Unique Id is used. + typedef Instruction* InstPtr; + struct InstPtrLess { + bool operator()(const InstPtr& lhs, const InstPtr& rhs) const { + return lhs->unique_id() < rhs->unique_id(); + } + }; + // Mapping from variable or value ids to DebugDeclare or DebugValue // instructions whose operand is the variable or value. - std::unordered_map> + std::unordered_map> var_id_to_dbg_decl_; // Mapping from DebugScope ids to users. diff --git a/source/opt/decoration_manager.cpp b/source/opt/decoration_manager.cpp index 4bf026ef..2146c359 100644 --- a/source/opt/decoration_manager.cpp +++ b/source/opt/decoration_manager.cpp @@ -490,6 +490,14 @@ void DecorationManager::ForEachDecoration( }); } +bool DecorationManager::HasDecoration(uint32_t id, uint32_t decoration) { + bool has_decoration = false; + ForEachDecoration(id, decoration, [&has_decoration](const Instruction&) { + has_decoration = true; + }); + return has_decoration; +} + bool DecorationManager::FindDecoration( uint32_t id, uint32_t decoration, std::function f) { diff --git a/source/opt/decoration_manager.h b/source/opt/decoration_manager.h index b753e6be..fe78f2ce 100644 --- a/source/opt/decoration_manager.h +++ b/source/opt/decoration_manager.h @@ -90,6 +90,10 @@ class DecorationManager { bool AreDecorationsTheSame(const Instruction* inst1, const Instruction* inst2, bool ignore_target) const; + // Returns whether a decoration instruction for |id| with decoration + // |decoration| exists or not. + bool HasDecoration(uint32_t id, uint32_t decoration); + // |f| is run on each decoration instruction for |id| with decoration // |decoration|. Processed are all decorations which target |id| either // directly or indirectly by Decoration Groups. diff --git a/source/opt/def_use_manager.cpp b/source/opt/def_use_manager.cpp index 0ec98cae..d54fdb65 100644 --- a/source/opt/def_use_manager.cpp +++ b/source/opt/def_use_manager.cpp @@ -14,11 +14,6 @@ #include "source/opt/def_use_manager.h" -#include - -#include "source/opt/log.h" -#include "source/opt/reflect.h" - namespace spvtools { namespace opt { namespace analysis { @@ -59,7 +54,7 @@ void DefUseManager::AnalyzeInstUse(Instruction* inst) { uint32_t use_id = inst->GetSingleWordOperand(i); Instruction* def = GetDef(use_id); assert(def && "Definition is not registered."); - id_to_users_.insert(UserEntry(def, inst)); + id_to_users_.insert(UserEntry{def, inst}); used_ids->push_back(use_id); } break; default: @@ -71,6 +66,9 @@ void DefUseManager::AnalyzeInstUse(Instruction* inst) { void DefUseManager::AnalyzeInstDefUse(Instruction* inst) { AnalyzeInstDef(inst); AnalyzeInstUse(inst); + // Analyze lines last otherwise they will be cleared when inst is + // cleared by preceding two calls + for (auto& l_inst : inst->dbg_line_insts()) AnalyzeInstDefUse(&l_inst); } void DefUseManager::UpdateDefUse(Instruction* inst) { @@ -99,13 +97,13 @@ const Instruction* DefUseManager::GetDef(uint32_t id) const { DefUseManager::IdToUsersMap::const_iterator DefUseManager::UsersBegin( const Instruction* def) const { return id_to_users_.lower_bound( - UserEntry(const_cast(def), nullptr)); + UserEntry{const_cast(def), nullptr}); } bool DefUseManager::UsersNotEnd(const IdToUsersMap::const_iterator& iter, const IdToUsersMap::const_iterator& cached_end, const Instruction* inst) const { - return (iter != cached_end && iter->first == inst); + return (iter != cached_end && iter->def == inst); } bool DefUseManager::UsersNotEnd(const IdToUsersMap::const_iterator& iter, @@ -122,7 +120,7 @@ bool DefUseManager::WhileEachUser( auto end = id_to_users_.end(); for (auto iter = UsersBegin(def); UsersNotEnd(iter, end, def); ++iter) { - if (!f(iter->second)) return false; + if (!f(iter->user)) return false; } return true; } @@ -155,7 +153,7 @@ bool DefUseManager::WhileEachUse( auto end = id_to_users_.end(); for (auto iter = UsersBegin(def); UsersNotEnd(iter, end, def); ++iter) { - Instruction* user = iter->second; + Instruction* user = iter->user; for (uint32_t idx = 0; idx != user->NumOperands(); ++idx) { const Operand& op = user->GetOperand(idx); if (op.type != SPV_OPERAND_TYPE_RESULT_ID && spvIsIdType(op.type)) { @@ -224,9 +222,11 @@ void DefUseManager::AnalyzeDefUse(Module* module) { if (!module) return; // Analyze all the defs before any uses to catch forward references. module->ForEachInst( - std::bind(&DefUseManager::AnalyzeInstDef, this, std::placeholders::_1)); + std::bind(&DefUseManager::AnalyzeInstDef, this, std::placeholders::_1), + true); module->ForEachInst( - std::bind(&DefUseManager::AnalyzeInstUse, this, std::placeholders::_1)); + std::bind(&DefUseManager::AnalyzeInstUse, this, std::placeholders::_1), + true); } void DefUseManager::ClearInst(Instruction* inst) { @@ -253,45 +253,59 @@ void DefUseManager::EraseUseRecordsOfOperandIds(const Instruction* inst) { if (iter != inst_to_used_ids_.end()) { for (auto use_id : iter->second) { id_to_users_.erase( - UserEntry(GetDef(use_id), const_cast(inst))); + UserEntry{GetDef(use_id), const_cast(inst)}); } - inst_to_used_ids_.erase(inst); + inst_to_used_ids_.erase(iter); } } -bool operator==(const DefUseManager& lhs, const DefUseManager& rhs) { +bool CompareAndPrintDifferences(const DefUseManager& lhs, + const DefUseManager& rhs) { + bool same = true; + if (lhs.id_to_def_ != rhs.id_to_def_) { - return false; + for (auto p : lhs.id_to_def_) { + if (rhs.id_to_def_.find(p.first) == rhs.id_to_def_.end()) { + printf("Diff in id_to_def: missing value in rhs\n"); + } + } + for (auto p : rhs.id_to_def_) { + if (lhs.id_to_def_.find(p.first) == lhs.id_to_def_.end()) { + printf("Diff in id_to_def: missing value in lhs\n"); + } + } + same = false; } if (lhs.id_to_users_ != rhs.id_to_users_) { for (auto p : lhs.id_to_users_) { if (rhs.id_to_users_.count(p) == 0) { - return false; + printf("Diff in id_to_users: missing value in rhs\n"); } } for (auto p : rhs.id_to_users_) { if (lhs.id_to_users_.count(p) == 0) { - return false; + printf("Diff in id_to_users: missing value in lhs\n"); } } - return false; + same = false; } if (lhs.inst_to_used_ids_ != rhs.inst_to_used_ids_) { for (auto p : lhs.inst_to_used_ids_) { if (rhs.inst_to_used_ids_.count(p.first) == 0) { - return false; + printf("Diff in inst_to_used_ids: missing value in rhs\n"); } } for (auto p : rhs.inst_to_used_ids_) { if (lhs.inst_to_used_ids_.count(p.first) == 0) { - return false; + printf("Diff in inst_to_used_ids: missing value in lhs\n"); } } - return false; + same = false; } - return true; + + return same; } } // namespace analysis diff --git a/source/opt/def_use_manager.h b/source/opt/def_use_manager.h index 0499e82b..a8dbbc60 100644 --- a/source/opt/def_use_manager.h +++ b/source/opt/def_use_manager.h @@ -15,10 +15,8 @@ #ifndef SOURCE_OPT_DEF_USE_MANAGER_H_ #define SOURCE_OPT_DEF_USE_MANAGER_H_ -#include #include #include -#include #include #include "source/opt/instruction.h" @@ -51,15 +49,17 @@ inline bool operator<(const Use& lhs, const Use& rhs) { return lhs.operand_index < rhs.operand_index; } -// Definition and user pair. -// -// The first element of the pair is the definition. -// The second element of the pair is the user. -// // Definition should never be null. User can be null, however, such an entry // should be used only for searching (e.g. all users of a particular definition) // and never stored in a container. -using UserEntry = std::pair; +struct UserEntry { + Instruction* def; + Instruction* user; +}; + +inline bool operator==(const UserEntry& lhs, const UserEntry& rhs) { + return lhs.def == rhs.def && lhs.user == rhs.user; +} // Orders UserEntry for use in associative containers (i.e. less than ordering). // @@ -72,24 +72,24 @@ using UserEntry = std::pair; // definition (i.e. using {def, nullptr}). struct UserEntryLess { bool operator()(const UserEntry& lhs, const UserEntry& rhs) const { - // If lhs.first and rhs.first are both null, fall through to checking the + // If lhs.def and rhs.def are both null, fall through to checking the // second entries. - if (!lhs.first && rhs.first) return true; - if (lhs.first && !rhs.first) return false; + if (!lhs.def && rhs.def) return true; + if (lhs.def && !rhs.def) return false; // If neither definition is null, then compare unique ids. - if (lhs.first && rhs.first) { - if (lhs.first->unique_id() < rhs.first->unique_id()) return true; - if (rhs.first->unique_id() < lhs.first->unique_id()) return false; + if (lhs.def && rhs.def) { + if (lhs.def->unique_id() < rhs.def->unique_id()) return true; + if (rhs.def->unique_id() < lhs.def->unique_id()) return false; } // Return false on equality. - if (!lhs.second && !rhs.second) return false; - if (!lhs.second) return true; - if (!rhs.second) return false; + if (!lhs.user && !rhs.user) return false; + if (!lhs.user) return true; + if (!rhs.user) return false; // If neither user is null then compare unique ids. - return lhs.second->unique_id() < rhs.second->unique_id(); + return lhs.user->unique_id() < rhs.user->unique_id(); } }; @@ -97,7 +97,6 @@ struct UserEntryLess { class DefUseManager { public: using IdToDefMap = std::unordered_map; - using IdToUsersMap = std::set; // Constructs a def-use manager from the given |module|. All internal messages // will be communicated to the outside via the given message |consumer|. This @@ -191,14 +190,12 @@ class DefUseManager { // Returns the annotation instrunctions which are a direct use of the given // |id|. This means when the decorations are applied through decoration // group(s), this function will just return the OpGroupDecorate - // instrcution(s) which refer to the given id as an operand. The OpDecorate + // instruction(s) which refer to the given id as an operand. The OpDecorate // instructions which decorate the decoration group will not be returned. std::vector GetAnnotations(uint32_t id) const; // Returns the map from ids to their def instructions. const IdToDefMap& id_to_defs() const { return id_to_def_; } - // Returns the map from instructions to their users. - const IdToUsersMap& id_to_users() const { return id_to_users_; } // Clear the internal def-use record of the given instruction |inst|. This // method will update the use information of the operand ids of |inst|. The @@ -210,16 +207,15 @@ class DefUseManager { // Erases the records that a given instruction uses its operand ids. void EraseUseRecordsOfOperandIds(const Instruction* inst); - friend bool operator==(const DefUseManager&, const DefUseManager&); - friend bool operator!=(const DefUseManager& lhs, const DefUseManager& rhs) { - return !(lhs == rhs); - } + friend bool CompareAndPrintDifferences(const DefUseManager&, + const DefUseManager&); - // If |inst| has not already been analysed, then analyses its defintion and + // If |inst| has not already been analysed, then analyses its definition and // uses. void UpdateDefUse(Instruction* inst); private: + using IdToUsersMap = std::set; using InstToUsedIdsMap = std::unordered_map>; diff --git a/source/opt/desc_sroa.cpp b/source/opt/desc_sroa.cpp index 5e950069..b130ca80 100644 --- a/source/opt/desc_sroa.cpp +++ b/source/opt/desc_sroa.cpp @@ -14,10 +14,19 @@ #include "source/opt/desc_sroa.h" +#include "source/opt/desc_sroa_util.h" #include "source/util/string_utils.h" namespace spvtools { namespace opt { +namespace { + +bool IsDecorationBinding(Instruction* inst) { + if (inst->opcode() != SpvOpDecorate) return false; + return inst->GetSingleWordInOperand(1u) == SpvDecorationBinding; +} + +} // namespace Pass::Status DescriptorScalarReplacement::Process() { bool modified = false; @@ -25,7 +34,7 @@ Pass::Status DescriptorScalarReplacement::Process() { std::vector vars_to_kill; for (Instruction& var : context()->types_values()) { - if (IsCandidate(&var)) { + if (descsroautil::IsDescriptorArray(context(), &var)) { modified = true; if (!ReplaceCandidate(&var)) { return Status::Failure; @@ -41,72 +50,6 @@ Pass::Status DescriptorScalarReplacement::Process() { return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); } -bool DescriptorScalarReplacement::IsCandidate(Instruction* var) { - if (var->opcode() != SpvOpVariable) { - return false; - } - - uint32_t ptr_type_id = var->type_id(); - Instruction* ptr_type_inst = - context()->get_def_use_mgr()->GetDef(ptr_type_id); - if (ptr_type_inst->opcode() != SpvOpTypePointer) { - return false; - } - - 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 && - 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. - if (IsTypeOfStructuredBuffer(var_type_inst)) { - return false; - } - - bool has_desc_set_decoration = false; - context()->get_decoration_mgr()->ForEachDecoration( - var->result_id(), SpvDecorationDescriptorSet, - [&has_desc_set_decoration](const Instruction&) { - has_desc_set_decoration = true; - }); - if (!has_desc_set_decoration) { - return false; - } - - bool has_binding_decoration = false; - context()->get_decoration_mgr()->ForEachDecoration( - var->result_id(), SpvDecorationBinding, - [&has_binding_decoration](const Instruction&) { - has_binding_decoration = true; - }); - if (!has_binding_decoration) { - return false; - } - - return true; -} - -bool DescriptorScalarReplacement::IsTypeOfStructuredBuffer( - const Instruction* type) const { - if (type->opcode() != SpvOpTypeStruct) { - return false; - } - - // All buffers have offset decorations for members of their structure types. - // This is how we distinguish it from a structure of descriptors. - bool has_offset_decoration = false; - context()->get_decoration_mgr()->ForEachDecoration( - type->result_id(), SpvDecorationOffset, - [&has_offset_decoration](const Instruction&) { - has_offset_decoration = true; - }); - return has_offset_decoration; -} - bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) { std::vector access_chain_work_list; std::vector load_work_list; @@ -162,21 +105,20 @@ bool DescriptorScalarReplacement::ReplaceAccessChain(Instruction* var, return false; } - uint32_t idx_id = use->GetSingleWordInOperand(1); - const analysis::Constant* idx_const = - context()->get_constant_mgr()->FindDeclaredConstant(idx_id); - if (idx_const == nullptr) { + const analysis::Constant* const_index = + descsroautil::GetAccessChainIndexAsConst(context(), use); + if (const_index == nullptr) { context()->EmitErrorMessage("Variable cannot be replaced: invalid index", use); return false; } - uint32_t idx = idx_const->GetU32(); + uint32_t idx = const_index->GetU32(); uint32_t replacement_var = GetReplacementVariable(var, idx); if (use->NumInOperands() == 2) { // We are not indexing into the replacement variable. We can replaces the - // access chain with the replacement varibale itself. + // access chain with the replacement variable itself. context()->ReplaceAllUsesWith(use->result_id(), replacement_var); context()->KillInst(use); return true; @@ -193,8 +135,8 @@ bool DescriptorScalarReplacement::ReplaceAccessChain(Instruction* var, // Use the replacement variable as the base address. new_operands.push_back({SPV_OPERAND_TYPE_ID, {replacement_var}}); - // Drop the first index because it is consumed by the replacment, and copy the - // rest. + // Drop the first index because it is consumed by the replacement, and copy + // the rest. for (uint32_t i = 4; i < use->NumOperands(); i++) { new_operands.emplace_back(use->GetOperand(i)); } @@ -208,39 +150,12 @@ uint32_t DescriptorScalarReplacement::GetReplacementVariable(Instruction* var, uint32_t idx) { auto replacement_vars = replacement_variables_.find(var); if (replacement_vars == replacement_variables_.end()) { - 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 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."); - - // 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(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(num_members, 0)}) - .first; - } + uint32_t number_of_elements = + descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var); + replacement_vars = + replacement_variables_ + .insert({var, std::vector(number_of_elements, 0)}) + .first; } if (replacement_vars->second[idx] == 0) { @@ -250,6 +165,75 @@ uint32_t DescriptorScalarReplacement::GetReplacementVariable(Instruction* var, return replacement_vars->second[idx]; } +void DescriptorScalarReplacement::CopyDecorationsForNewVariable( + Instruction* old_var, uint32_t index, uint32_t new_var_id, + uint32_t new_var_ptr_type_id, const bool is_old_var_array, + const bool is_old_var_struct, Instruction* old_var_type) { + // Handle OpDecorate and OpDecorateString instructions. + for (auto old_decoration : + get_decoration_mgr()->GetDecorationsFor(old_var->result_id(), true)) { + uint32_t new_binding = 0; + if (IsDecorationBinding(old_decoration)) { + new_binding = GetNewBindingForElement( + old_decoration->GetSingleWordInOperand(2), index, new_var_ptr_type_id, + is_old_var_array, is_old_var_struct, old_var_type); + } + CreateNewDecorationForNewVariable(old_decoration, new_var_id, new_binding); + } + + // Handle OpMemberDecorate instructions. + for (auto old_decoration : get_decoration_mgr()->GetDecorationsFor( + old_var_type->result_id(), true)) { + assert(old_decoration->opcode() == SpvOpMemberDecorate); + if (old_decoration->GetSingleWordInOperand(1u) != index) continue; + CreateNewDecorationForMemberDecorate(old_decoration, new_var_id); + } +} + +uint32_t DescriptorScalarReplacement::GetNewBindingForElement( + uint32_t old_binding, uint32_t index, uint32_t new_var_ptr_type_id, + const bool is_old_var_array, const bool is_old_var_struct, + Instruction* old_var_type) { + if (is_old_var_array) { + return old_binding + index * GetNumBindingsUsedByType(new_var_ptr_type_id); + } + if (is_old_var_struct) { + // The binding offset that should be added is the sum of binding + // numbers used by previous members of the current struct. + uint32_t new_binding = old_binding; + for (uint32_t i = 0; i < index; ++i) { + new_binding += + GetNumBindingsUsedByType(old_var_type->GetSingleWordInOperand(i)); + } + return new_binding; + } + return old_binding; +} + +void DescriptorScalarReplacement::CreateNewDecorationForNewVariable( + Instruction* old_decoration, uint32_t new_var_id, uint32_t new_binding) { + assert(old_decoration->opcode() == SpvOpDecorate || + old_decoration->opcode() == SpvOpDecorateString); + std::unique_ptr new_decoration(old_decoration->Clone(context())); + new_decoration->SetInOperand(0, {new_var_id}); + + if (IsDecorationBinding(new_decoration.get())) { + new_decoration->SetInOperand(2, {new_binding}); + } + context()->AddAnnotationInst(std::move(new_decoration)); +} + +void DescriptorScalarReplacement::CreateNewDecorationForMemberDecorate( + Instruction* old_member_decoration, uint32_t new_var_id) { + std::vector operands( + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {new_var_id}}}); + auto new_decorate_operand_begin = old_member_decoration->begin() + 2u; + auto new_decorate_operand_end = old_member_decoration->end(); + operands.insert(operands.end(), new_decorate_operand_begin, + new_decorate_operand_end); + get_decoration_mgr()->AddDecoration(SpvOpDecorate, std::move(operands)); +} + uint32_t DescriptorScalarReplacement::CreateReplacementVariable( Instruction* var, uint32_t idx) { // The storage class for the new variable is the same as the original. @@ -285,33 +269,8 @@ uint32_t DescriptorScalarReplacement::CreateReplacementVariable( {static_cast(storage_class)}}})); context()->AddGlobalValue(std::move(variable)); - // Copy all of the decorations to the new variable. The only difference is - // the Binding decoration needs to be adjusted. - for (auto old_decoration : - get_decoration_mgr()->GetDecorationsFor(var->result_id(), true)) { - assert(old_decoration->opcode() == SpvOpDecorate); - std::unique_ptr new_decoration( - old_decoration->Clone(context())); - new_decoration->SetInOperand(0, {id}); - - uint32_t decoration = new_decoration->GetSingleWordInOperand(1u); - if (decoration == SpvDecorationBinding) { - 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)); - } + CopyDecorationsForNewVariable(var, idx, id, ptr_element_type_id, is_array, + is_struct, pointee_type_inst); // Create a new OpName for the replacement variable. std::vector> names_to_add; @@ -377,7 +336,7 @@ uint32_t DescriptorScalarReplacement::GetNumBindingsUsedByType( // The number of bindings consumed by a structure is the sum of the bindings // used by its members. if (type_inst->opcode() == SpvOpTypeStruct && - !IsTypeOfStructuredBuffer(type_inst)) { + !descsroautil::IsTypeOfStructuredBuffer(context(), type_inst)) { uint32_t sum = 0; for (uint32_t i = 0; i < type_inst->NumInOperands(); i++) sum += GetNumBindingsUsedByType(type_inst->GetSingleWordInOperand(i)); diff --git a/source/opt/desc_sroa.h b/source/opt/desc_sroa.h index cd72fd30..6a24fd87 100644 --- a/source/opt/desc_sroa.h +++ b/source/opt/desc_sroa.h @@ -46,10 +46,6 @@ class DescriptorScalarReplacement : public Pass { } private: - // Returns true if |var| is an OpVariable instruction that represents a - // descriptor array. These are the variables that we want to replace. - bool IsCandidate(Instruction* var); - // Replaces all references to |var| by new variables, one for each element of // the array |var|. The binding for the new variables corresponding to // element i will be the binding of |var| plus i. Returns true if successful. @@ -93,14 +89,50 @@ class DescriptorScalarReplacement : public Pass { // bindings used by its members. uint32_t GetNumBindingsUsedByType(uint32_t type_id); - // Returns true if |type| is a type that could be used for a structured buffer - // as opposed to a type that would be used for a structure of resource - // descriptors. - bool IsTypeOfStructuredBuffer(const Instruction* type) const; + // Copy all of the decorations of variable |old_var| and make them as + // decorations for the new variable whose id is |new_var_id|. The new variable + // is supposed to replace |index|th element of |old_var|. + // |new_var_ptr_type_id| is the id of the pointer to the type of the new + // variable. |is_old_var_array| is true if |old_var| has an array type. + // |is_old_var_struct| is true if |old_var| has a structure type. + // |old_var_type| is the pointee type of |old_var|. + void CopyDecorationsForNewVariable(Instruction* old_var, uint32_t index, + uint32_t new_var_id, + uint32_t new_var_ptr_type_id, + const bool is_old_var_array, + const bool is_old_var_struct, + Instruction* old_var_type); + + // Get the new binding number for a new variable that will be replaced with an + // |index|th element of an old variable. The old variable has |old_binding| + // as its binding number. |ptr_elem_type_id| the id of the pointer to the + // element type. |is_old_var_array| is true if the old variable has an array + // type. |is_old_var_struct| is true if the old variable has a structure type. + // |old_var_type| is the pointee type of the old variable. + uint32_t GetNewBindingForElement(uint32_t old_binding, uint32_t index, + uint32_t ptr_elem_type_id, + const bool is_old_var_array, + const bool is_old_var_struct, + Instruction* old_var_type); + + // Create a new OpDecorate(String) instruction by cloning |old_decoration|. + // The new OpDecorate(String) instruction will be used for a variable whose id + // is |new_var_ptr_type_id|. If |old_decoration| is a decoration for a + // binding, the new OpDecorate(String) instruction will have |new_binding| as + // its binding. + void CreateNewDecorationForNewVariable(Instruction* old_decoration, + uint32_t new_var_id, + uint32_t new_binding); + + // Create a new OpDecorate instruction whose operand is the same as an + // OpMemberDecorate instruction |old_member_decoration| except Target operand. + // The Target operand of the new OpDecorate instruction will be |new_var_id|. + void CreateNewDecorationForMemberDecorate(Instruction* old_decoration, + uint32_t new_var_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 + // a variable that will be used in the place of the ith element of the // array |var|. If the entry is |0|, then the variable has not been // created yet. std::map> replacement_variables_; diff --git a/source/opt/desc_sroa_util.cpp b/source/opt/desc_sroa_util.cpp new file mode 100644 index 00000000..1954e2cc --- /dev/null +++ b/source/opt/desc_sroa_util.cpp @@ -0,0 +1,117 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/desc_sroa_util.h" + +namespace spvtools { +namespace opt { +namespace { + +const uint32_t kOpAccessChainInOperandIndexes = 1; + +// Returns the length of array type |type|. +uint32_t GetLengthOfArrayType(IRContext* context, Instruction* type) { + assert(type->opcode() == SpvOpTypeArray && "type must be array"); + uint32_t length_id = type->GetSingleWordInOperand(1); + const analysis::Constant* length_const = + context->get_constant_mgr()->FindDeclaredConstant(length_id); + assert(length_const != nullptr); + return length_const->GetU32(); +} + +} // namespace + +namespace descsroautil { + +bool IsDescriptorArray(IRContext* context, Instruction* var) { + if (var->opcode() != SpvOpVariable) { + return false; + } + + uint32_t ptr_type_id = var->type_id(); + Instruction* ptr_type_inst = context->get_def_use_mgr()->GetDef(ptr_type_id); + if (ptr_type_inst->opcode() != SpvOpTypePointer) { + return false; + } + + 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 && + 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. + if (IsTypeOfStructuredBuffer(context, var_type_inst)) { + return false; + } + + if (!context->get_decoration_mgr()->HasDecoration( + var->result_id(), SpvDecorationDescriptorSet)) { + return false; + } + + return context->get_decoration_mgr()->HasDecoration(var->result_id(), + SpvDecorationBinding); +} + +bool IsTypeOfStructuredBuffer(IRContext* context, const Instruction* type) { + if (type->opcode() != SpvOpTypeStruct) { + return false; + } + + // All buffers have offset decorations for members of their structure types. + // This is how we distinguish it from a structure of descriptors. + return context->get_decoration_mgr()->HasDecoration(type->result_id(), + SpvDecorationOffset); +} + +const analysis::Constant* GetAccessChainIndexAsConst( + IRContext* context, Instruction* access_chain) { + if (access_chain->NumInOperands() <= 1) { + return nullptr; + } + uint32_t idx_id = GetFirstIndexOfAccessChain(access_chain); + const analysis::Constant* idx_const = + context->get_constant_mgr()->FindDeclaredConstant(idx_id); + return idx_const; +} + +uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain) { + assert(access_chain->NumInOperands() > 1 && + "OpAccessChain does not have Indexes operand"); + return access_chain->GetSingleWordInOperand(kOpAccessChainInOperandIndexes); +} + +uint32_t GetNumberOfElementsForArrayOrStruct(IRContext* context, + Instruction* var) { + uint32_t ptr_type_id = var->type_id(); + Instruction* ptr_type_inst = context->get_def_use_mgr()->GetDef(ptr_type_id); + assert(ptr_type_inst->opcode() == SpvOpTypePointer && + "Variable should be a pointer to an array or structure."); + uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1); + Instruction* pointee_type_inst = + context->get_def_use_mgr()->GetDef(pointee_type_id); + if (pointee_type_inst->opcode() == SpvOpTypeArray) { + return GetLengthOfArrayType(context, pointee_type_inst); + } + assert(pointee_type_inst->opcode() == SpvOpTypeStruct && + "Variable should be a pointer to an array or structure."); + return pointee_type_inst->NumInOperands(); +} + +} // namespace descsroautil +} // namespace opt +} // namespace spvtools diff --git a/source/opt/desc_sroa_util.h b/source/opt/desc_sroa_util.h new file mode 100644 index 00000000..2f45c0c2 --- /dev/null +++ b/source/opt/desc_sroa_util.h @@ -0,0 +1,54 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DESC_SROA_UTIL_H_ +#define SOURCE_OPT_DESC_SROA_UTIL_H_ + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +// Provides functions for the descriptor array SROA. +namespace descsroautil { + +// Returns true if |var| is an OpVariable instruction that represents a +// descriptor array. +bool IsDescriptorArray(IRContext* context, Instruction* var); + +// Returns true if |type| is a type that could be used for a structured buffer +// as opposed to a type that would be used for a structure of resource +// descriptors. +bool IsTypeOfStructuredBuffer(IRContext* context, const Instruction* type); + +// Returns the first index of the OpAccessChain instruction |access_chain| as +// a constant. Returns nullptr if it is not a constant. +const analysis::Constant* GetAccessChainIndexAsConst(IRContext* context, + Instruction* access_chain); + +// Returns the number of elements of an OpVariable instruction |var| whose type +// must be a pointer to an array or a struct. +uint32_t GetNumberOfElementsForArrayOrStruct(IRContext* context, + Instruction* var); + +// Returns the first Indexes operand id of the OpAccessChain or +// OpInBoundsAccessChain instruction |access_chain|. The access chain must have +// at least 1 index. +uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain); + +} // namespace descsroautil +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DESC_SROA_UTIL_H_ diff --git a/source/opt/dominator_tree.cpp b/source/opt/dominator_tree.cpp index 55287f44..2680be2a 100644 --- a/source/opt/dominator_tree.cpp +++ b/source/opt/dominator_tree.cpp @@ -48,7 +48,7 @@ namespace { // BBType - BasicBlock type. Will either be BasicBlock or DominatorTreeNode // SuccessorLambda - Lamdba matching the signature of 'const // std::vector*(const BBType *A)'. Will return a vector of the nodes -// succeding BasicBlock A. +// succeeding BasicBlock A. // PostLambda - Lamdba matching the signature of 'void (const BBType*)' will be // called on each node traversed AFTER their children. // PreLambda - Lamdba matching the signature of 'void (const BBType*)' will be @@ -57,9 +57,9 @@ template static void DepthFirstSearch(const BBType* bb, SuccessorLambda successors, PreLambda pre, PostLambda post) { - // Ignore backedge operation. - auto nop_backedge = [](const BBType*, const BBType*) {}; - CFA::DepthFirstTraversal(bb, successors, pre, post, nop_backedge); + auto no_terminal_blocks = [](const BBType*) { return false; }; + CFA::DepthFirstTraversal(bb, successors, pre, post, + no_terminal_blocks); } // Wrapper around CFA::DepthFirstTraversal to provide an interface to perform @@ -69,7 +69,7 @@ static void DepthFirstSearch(const BBType* bb, SuccessorLambda successors, // BBType - BasicBlock type. Will either be BasicBlock or DominatorTreeNode // SuccessorLambda - Lamdba matching the signature of 'const // std::vector*(const BBType *A)'. Will return a vector of the nodes -// succeding BasicBlock A. +// succeeding BasicBlock A. // PostLambda - Lamdba matching the signature of 'void (const BBType*)' will be // called on each node traversed after their children. template @@ -103,7 +103,8 @@ class BasicBlockSuccessorHelper { using Function = typename GetFunctionClass::FunctionType; using BasicBlockListTy = std::vector; - using BasicBlockMapTy = std::map; + using BasicBlockMapTy = + std::unordered_map; public: // For compliance with the dominance tree computation, entry nodes are @@ -158,19 +159,7 @@ BasicBlockSuccessorHelper::BasicBlockSuccessorHelper( template void BasicBlockSuccessorHelper::CreateSuccessorMap( Function& f, const BasicBlock* placeholder_start_node) { - std::map id_to_BB_map; - auto GetSuccessorBasicBlock = [&f, &id_to_BB_map](uint32_t successor_id) { - BasicBlock*& Succ = id_to_BB_map[successor_id]; - if (!Succ) { - for (BasicBlock& BBIt : f) { - if (successor_id == BBIt.id()) { - Succ = &BBIt; - break; - } - } - } - return Succ; - }; + IRContext* context = f.DefInst().context(); if (invert_graph_) { // For the post dominator tree, we see the inverted graph. @@ -184,9 +173,8 @@ void BasicBlockSuccessorHelper::CreateSuccessorMap( BasicBlockListTy& pred_list = predecessors_[&bb]; const auto& const_bb = bb; const_bb.ForEachSuccessorLabel( - [this, &pred_list, &bb, - &GetSuccessorBasicBlock](const uint32_t successor_id) { - BasicBlock* succ = GetSuccessorBasicBlock(successor_id); + [this, &pred_list, &bb, context](const uint32_t successor_id) { + BasicBlock* succ = context->get_instr_block(successor_id); // Inverted graph: our successors in the CFG // are our predecessors in the inverted graph. this->successors_[succ].push_back(&bb); @@ -207,7 +195,7 @@ void BasicBlockSuccessorHelper::CreateSuccessorMap( const auto& const_bb = bb; const_bb.ForEachSuccessorLabel([&](const uint32_t successor_id) { - BasicBlock* succ = GetSuccessorBasicBlock(successor_id); + BasicBlock* succ = context->get_instr_block(successor_id); succ_list.push_back(succ); predecessors_[succ].push_back(&bb); }); diff --git a/source/opt/dominator_tree.h b/source/opt/dominator_tree.h index 0024bc50..1674b228 100644 --- a/source/opt/dominator_tree.h +++ b/source/opt/dominator_tree.h @@ -278,7 +278,7 @@ class DominatorTree { private: // Wrapper function which gets the list of pairs of each BasicBlocks to its - // immediately dominating BasicBlock and stores the result in the the edges + // immediately dominating BasicBlock and stores the result in the edges // parameter. // // The |edges| vector will contain the dominator tree as pairs of nodes. diff --git a/source/opt/eliminate_dead_functions_util.cpp b/source/opt/eliminate_dead_functions_util.cpp index 6b5234bb..1379120f 100644 --- a/source/opt/eliminate_dead_functions_util.cpp +++ b/source/opt/eliminate_dead_functions_util.cpp @@ -23,9 +23,11 @@ Module::iterator EliminateFunction(IRContext* context, Module::iterator* func_iter) { bool first_func = *func_iter == context->module()->begin(); bool seen_func_end = false; + std::unordered_set to_kill; (*func_iter) ->ForEachInst( - [context, first_func, func_iter, &seen_func_end](Instruction* inst) { + [context, first_func, func_iter, &seen_func_end, + &to_kill](Instruction* inst) { if (inst->opcode() == SpvOpFunctionEnd) { seen_func_end = true; } @@ -33,6 +35,7 @@ Module::iterator EliminateFunction(IRContext* context, // global values if this is the first function. if (seen_func_end && inst->opcode() == SpvOpExtInst) { assert(inst->IsNonSemanticInstruction()); + if (to_kill.find(inst) != to_kill.end()) return; std::unique_ptr clone(inst->Clone(context)); context->ForgetUses(inst); context->AnalyzeDefUse(clone.get()); @@ -44,12 +47,17 @@ Module::iterator EliminateFunction(IRContext* context, prev_func_iter->AddNonSemanticInstruction(std::move(clone)); } inst->ToNop(); - } else { - context->KillNonSemanticInfo(inst); + } else if (to_kill.find(inst) == to_kill.end()) { + context->CollectNonSemanticTree(inst, &to_kill); context->KillInst(inst); } }, true, true); + + for (auto* dead : to_kill) { + context->KillInst(dead); + } + return func_iter->Erase(); } diff --git a/source/opt/eliminate_dead_input_components_pass.cpp b/source/opt/eliminate_dead_input_components_pass.cpp new file mode 100644 index 00000000..aa2776bb --- /dev/null +++ b/source/opt/eliminate_dead_input_components_pass.cpp @@ -0,0 +1,189 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// Copyright (c) 2022 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/eliminate_dead_input_components_pass.h" + +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "source/util/bit_vector.h" + +namespace { + +const uint32_t kAccessChainBaseInIdx = 0; +const uint32_t kAccessChainIndex0InIdx = 1; +const uint32_t kConstantValueInIdx = 0; +const uint32_t kVariableStorageClassInIdx = 0; + +} // namespace + +namespace spvtools { +namespace opt { + +Pass::Status EliminateDeadInputComponentsPass::Process() { + // Current functionality assumes shader capability + if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader)) + return Status::SuccessWithoutChange; + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + bool modified = false; + std::vector> arrays_to_change; + for (auto& var : context()->types_values()) { + if (var.opcode() != SpvOpVariable) { + continue; + } + analysis::Type* var_type = type_mgr->GetType(var.type_id()); + analysis::Pointer* ptr_type = var_type->AsPointer(); + if (ptr_type == nullptr) { + continue; + } + if (ptr_type->storage_class() != SpvStorageClassInput) { + continue; + } + const analysis::Array* arr_type = ptr_type->pointee_type()->AsArray(); + if (arr_type != nullptr) { + unsigned arr_len_id = arr_type->LengthId(); + Instruction* arr_len_inst = def_use_mgr->GetDef(arr_len_id); + if (arr_len_inst->opcode() != SpvOpConstant) { + continue; + } + // SPIR-V requires array size is >= 1, so this works for signed or + // unsigned size + unsigned original_max = + arr_len_inst->GetSingleWordInOperand(kConstantValueInIdx) - 1; + unsigned max_idx = FindMaxIndex(var, original_max); + if (max_idx != original_max) { + ChangeArrayLength(var, max_idx + 1); + modified = true; + } + continue; + } + const analysis::Struct* struct_type = ptr_type->pointee_type()->AsStruct(); + if (struct_type == nullptr) continue; + const auto elt_types = struct_type->element_types(); + unsigned original_max = static_cast(elt_types.size()) - 1; + unsigned max_idx = FindMaxIndex(var, original_max); + if (max_idx != original_max) { + ChangeStructLength(var, max_idx + 1); + modified = true; + } + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +unsigned EliminateDeadInputComponentsPass::FindMaxIndex(Instruction& var, + unsigned original_max) { + unsigned max = 0; + bool seen_non_const_ac = false; + assert(var.opcode() == SpvOpVariable && "must be variable"); + context()->get_def_use_mgr()->WhileEachUser( + var.result_id(), [&max, &seen_non_const_ac, var, this](Instruction* use) { + auto use_opcode = use->opcode(); + if (use_opcode == SpvOpLoad || use_opcode == SpvOpCopyMemory || + use_opcode == SpvOpCopyMemorySized || + use_opcode == SpvOpCopyObject) { + seen_non_const_ac = true; + return false; + } + if (use->opcode() != SpvOpAccessChain && + use->opcode() != SpvOpInBoundsAccessChain) { + return true; + } + // OpAccessChain with no indices currently not optimized + if (use->NumInOperands() == 1) { + seen_non_const_ac = true; + return false; + } + unsigned base_id = use->GetSingleWordInOperand(kAccessChainBaseInIdx); + USE_ASSERT(base_id == var.result_id() && "unexpected base"); + unsigned idx_id = use->GetSingleWordInOperand(kAccessChainIndex0InIdx); + Instruction* idx_inst = context()->get_def_use_mgr()->GetDef(idx_id); + if (idx_inst->opcode() != SpvOpConstant) { + seen_non_const_ac = true; + return false; + } + unsigned value = idx_inst->GetSingleWordInOperand(kConstantValueInIdx); + if (value > max) max = value; + return true; + }); + return seen_non_const_ac ? original_max : max; +} + +void EliminateDeadInputComponentsPass::ChangeArrayLength(Instruction& arr_var, + unsigned length) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + analysis::Pointer* ptr_type = + type_mgr->GetType(arr_var.type_id())->AsPointer(); + const analysis::Array* arr_ty = ptr_type->pointee_type()->AsArray(); + assert(arr_ty && "expecting array type"); + uint32_t length_id = const_mgr->GetUIntConst(length); + analysis::Array new_arr_ty(arr_ty->element_type(), + arr_ty->GetConstantLengthInfo(length_id, length)); + analysis::Type* reg_new_arr_ty = type_mgr->GetRegisteredType(&new_arr_ty); + analysis::Pointer new_ptr_ty(reg_new_arr_ty, SpvStorageClassInput); + analysis::Type* reg_new_ptr_ty = type_mgr->GetRegisteredType(&new_ptr_ty); + uint32_t new_ptr_ty_id = type_mgr->GetTypeInstruction(reg_new_ptr_ty); + arr_var.SetResultType(new_ptr_ty_id); + def_use_mgr->AnalyzeInstUse(&arr_var); + // Move arr_var after its new type to preserve order + USE_ASSERT(arr_var.GetSingleWordInOperand(kVariableStorageClassInIdx) != + SpvStorageClassFunction && + "cannot move Function variable"); + Instruction* new_ptr_ty_inst = def_use_mgr->GetDef(new_ptr_ty_id); + arr_var.RemoveFromList(); + arr_var.InsertAfter(new_ptr_ty_inst); +} + +void EliminateDeadInputComponentsPass::ChangeStructLength( + Instruction& struct_var, unsigned length) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Pointer* ptr_type = + type_mgr->GetType(struct_var.type_id())->AsPointer(); + const analysis::Struct* struct_ty = ptr_type->pointee_type()->AsStruct(); + assert(struct_ty && "expecting struct type"); + const auto orig_elt_types = struct_ty->element_types(); + std::vector new_elt_types; + for (unsigned u = 0; u < length; ++u) + new_elt_types.push_back(orig_elt_types[u]); + analysis::Struct new_struct_ty(new_elt_types); + analysis::Type* reg_new_struct_ty = + type_mgr->GetRegisteredType(&new_struct_ty); + uint32_t new_struct_ty_id = type_mgr->GetTypeInstruction(reg_new_struct_ty); + uint32_t old_struct_ty_id = type_mgr->GetTypeInstruction(struct_ty); + analysis::DecorationManager* deco_mgr = context()->get_decoration_mgr(); + deco_mgr->CloneDecorations(old_struct_ty_id, new_struct_ty_id); + analysis::Pointer new_ptr_ty(reg_new_struct_ty, SpvStorageClassInput); + analysis::Type* reg_new_ptr_ty = type_mgr->GetRegisteredType(&new_ptr_ty); + uint32_t new_ptr_ty_id = type_mgr->GetTypeInstruction(reg_new_ptr_ty); + struct_var.SetResultType(new_ptr_ty_id); + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + def_use_mgr->AnalyzeInstUse(&struct_var); + // Move struct_var after its new type to preserve order + USE_ASSERT(struct_var.GetSingleWordInOperand(kVariableStorageClassInIdx) != + SpvStorageClassFunction && + "cannot move Function variable"); + Instruction* new_ptr_ty_inst = def_use_mgr->GetDef(new_ptr_ty_id); + struct_var.RemoveFromList(); + struct_var.InsertAfter(new_ptr_ty_inst); +} + +} // namespace opt +} // namespace spvtools diff --git a/source/opt/eliminate_dead_input_components_pass.h b/source/opt/eliminate_dead_input_components_pass.h new file mode 100644 index 00000000..a3a133c2 --- /dev/null +++ b/source/opt/eliminate_dead_input_components_pass.h @@ -0,0 +1,65 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// Copyright (c) 2022 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_ELIMINATE_DEAD_INPUT_COMPONENTS_H_ +#define SOURCE_OPT_ELIMINATE_DEAD_INPUT_COMPONENTS_H_ + +#include + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class EliminateDeadInputComponentsPass : public Pass { + public: + explicit EliminateDeadInputComponentsPass() {} + + const char* name() const override { + return "eliminate-dead-input-components"; + } + + Status Process() override; + + // Return the mask of preserved Analyses. + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG | + IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Find the max constant used to index the variable declared by |var| + // through OpAccessChain or OpInBoundsAccessChain. If any non-constant + // indices or non-Op*AccessChain use of |var|, return |original_max|. + unsigned FindMaxIndex(Instruction& var, unsigned original_max); + + // Change the length of the array |inst| to |length| + void ChangeArrayLength(Instruction& inst, unsigned length); + + // Change the length of the struct |struct_var| to |length| + void ChangeStructLength(Instruction& struct_var, unsigned length); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_ELIMINATE_DEAD_INPUT_COMPONENTS_H_ diff --git a/source/opt/eliminate_dead_members_pass.cpp b/source/opt/eliminate_dead_members_pass.cpp index 173df620..52aca525 100644 --- a/source/opt/eliminate_dead_members_pass.cpp +++ b/source/opt/eliminate_dead_members_pass.cpp @@ -20,6 +20,7 @@ namespace { const uint32_t kRemovedMember = 0xFFFFFFFF; const uint32_t kSpecConstOpOpcodeIdx = 0; +constexpr uint32_t kArrayElementTypeIdx = 0; } // namespace namespace spvtools { @@ -37,7 +38,7 @@ Pass::Status EliminateDeadMembersPass::Process() { } void EliminateDeadMembersPass::FindLiveMembers() { - // Until we have implemented the rewritting of OpSpecConsantOp instructions, + // Until we have implemented the rewriting of OpSpecConsantOp instructions, // we have to mark them as fully used just to be safe. for (auto& inst : get_module()->types_values()) { if (inst.opcode() == SpvOpSpecConstantOp) { @@ -64,6 +65,10 @@ void EliminateDeadMembersPass::FindLiveMembers() { MarkPointeeTypeAsFullUsed(inst.type_id()); break; default: + // Ignore structured buffers as layout(offset) qualifiers cannot be + // applied to structure fields + if (inst.IsVulkanStorageBufferVariable()) + MarkPointeeTypeAsFullUsed(inst.type_id()); break; } } @@ -136,18 +141,22 @@ void EliminateDeadMembersPass::MarkMembersAsLiveForStore( void EliminateDeadMembersPass::MarkTypeAsFullyUsed(uint32_t type_id) { Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); assert(type_inst != nullptr); - if (type_inst->opcode() != SpvOpTypeStruct) { - return; - } - // Mark every member of the current struct as used. - for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) { - used_members_[type_id].insert(i); - } - - // Mark any sub struct as fully used. - for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) { - MarkTypeAsFullyUsed(type_inst->GetSingleWordInOperand(i)); + switch (type_inst->opcode()) { + case SpvOpTypeStruct: + // Mark every member and its type as fully used. + for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) { + used_members_[type_id].insert(i); + MarkTypeAsFullyUsed(type_inst->GetSingleWordInOperand(i)); + } + break; + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + MarkTypeAsFullyUsed( + type_inst->GetSingleWordInOperand(kArrayElementTypeIdx)); + break; + default: + break; } } @@ -561,7 +570,7 @@ bool EliminateDeadMembersPass::UpdateCompsiteExtract(Instruction* inst) { Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); switch (type_inst->opcode()) { case SpvOpTypeStruct: - // The type will have already been rewriten, so use the new member + // The type will have already been rewritten, so use the new member // index. type_id = type_inst->GetSingleWordInOperand(new_member_idx); break; diff --git a/source/opt/feature_manager.cpp b/source/opt/feature_manager.cpp index ad70c1e4..a5902716 100644 --- a/source/opt/feature_manager.cpp +++ b/source/opt/feature_manager.cpp @@ -39,8 +39,7 @@ void FeatureManager::AddExtension(Instruction* ext) { assert(ext->opcode() == SpvOpExtension && "Expecting an extension instruction."); - const std::string name = - reinterpret_cast(ext->GetInOperand(0u).words.data()); + const std::string name = ext->GetInOperand(0u).AsString(); Extension extension; if (GetExtensionFromString(name.c_str(), &extension)) { extensions_.Add(extension); @@ -80,6 +79,8 @@ void FeatureManager::AddExtInstImportIds(Module* module) { extinst_importid_GLSLstd450_ = module->GetExtInstImportId("GLSL.std.450"); extinst_importid_OpenCL100DebugInfo_ = module->GetExtInstImportId("OpenCL.DebugInfo.100"); + extinst_importid_Shader100DebugInfo_ = + module->GetExtInstImportId("NonSemantic.Shader.DebugInfo.100"); } bool operator==(const FeatureManager& a, const FeatureManager& b) { @@ -107,6 +108,11 @@ bool operator==(const FeatureManager& a, const FeatureManager& b) { return false; } + if (a.extinst_importid_Shader100DebugInfo_ != + b.extinst_importid_Shader100DebugInfo_) { + return false; + } + return true; } } // namespace opt diff --git a/source/opt/feature_manager.h b/source/opt/feature_manager.h index 66d1cbac..68c8e9a2 100644 --- a/source/opt/feature_manager.h +++ b/source/opt/feature_manager.h @@ -55,6 +55,10 @@ class FeatureManager { return extinst_importid_OpenCL100DebugInfo_; } + uint32_t GetExtInstImportId_Shader100DebugInfo() const { + return extinst_importid_Shader100DebugInfo_; + } + friend bool operator==(const FeatureManager& a, const FeatureManager& b); friend bool operator!=(const FeatureManager& a, const FeatureManager& b) { return !(a == b); @@ -92,6 +96,10 @@ class FeatureManager { // Common OpenCL100DebugInfo external instruction import ids, cached // for performance. uint32_t extinst_importid_OpenCL100DebugInfo_ = 0; + + // Common NonSemanticShader100DebugInfo external instruction import ids, + // cached for performance. + uint32_t extinst_importid_Shader100DebugInfo_ = 0; }; } // namespace opt diff --git a/source/opt/fix_func_call_arguments.cpp b/source/opt/fix_func_call_arguments.cpp new file mode 100644 index 00000000..d140fb4b --- /dev/null +++ b/source/opt/fix_func_call_arguments.cpp @@ -0,0 +1,90 @@ +// Copyright (c) 2022 Advanced Micro Devices, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fix_func_call_arguments.h" + +#include "ir_builder.h" + +using namespace spvtools; +using namespace opt; + +bool FixFuncCallArgumentsPass::ModuleHasASingleFunction() { + auto funcsNum = get_module()->end() - get_module()->begin(); + return funcsNum == 1; +} + +Pass::Status FixFuncCallArgumentsPass::Process() { + bool modified = false; + if (ModuleHasASingleFunction()) return Status::SuccessWithoutChange; + for (auto& func : *get_module()) { + func.ForEachInst([this, &modified](Instruction* inst) { + if (inst->opcode() == SpvOpFunctionCall) { + modified |= FixFuncCallArguments(inst); + } + }); + } + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +bool FixFuncCallArgumentsPass::FixFuncCallArguments( + Instruction* func_call_inst) { + bool modified = false; + for (uint32_t i = 0; i < func_call_inst->NumInOperands(); ++i) { + Operand& op = func_call_inst->GetInOperand(i); + if (op.type != SPV_OPERAND_TYPE_ID) continue; + Instruction* operand_inst = get_def_use_mgr()->GetDef(op.AsId()); + if (operand_inst->opcode() == SpvOpAccessChain) { + uint32_t var_id = + ReplaceAccessChainFuncCallArguments(func_call_inst, operand_inst); + func_call_inst->SetInOperand(i, {var_id}); + modified = true; + } + } + if (modified) { + context()->UpdateDefUse(func_call_inst); + } + return modified; +} + +uint32_t FixFuncCallArgumentsPass::ReplaceAccessChainFuncCallArguments( + Instruction* func_call_inst, Instruction* operand_inst) { + InstructionBuilder builder( + context(), func_call_inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + Instruction* next_insert_point = func_call_inst->NextNode(); + // Get Variable insertion point + Function* func = context()->get_instr_block(func_call_inst)->GetParent(); + Instruction* variable_insertion_point = &*(func->begin()->begin()); + Instruction* op_ptr_type = get_def_use_mgr()->GetDef(operand_inst->type_id()); + Instruction* op_type = + get_def_use_mgr()->GetDef(op_ptr_type->GetSingleWordInOperand(1)); + uint32_t varType = context()->get_type_mgr()->FindPointerToType( + op_type->result_id(), SpvStorageClassFunction); + // Create new variable + builder.SetInsertPoint(variable_insertion_point); + Instruction* var = builder.AddVariable(varType, SpvStorageClassFunction); + // Load access chain to the new variable before function call + builder.SetInsertPoint(func_call_inst); + + uint32_t operand_id = operand_inst->result_id(); + Instruction* load = builder.AddLoad(op_type->result_id(), operand_id); + builder.AddStore(var->result_id(), load->result_id()); + // Load return value to the acesschain after function call + builder.SetInsertPoint(next_insert_point); + load = builder.AddLoad(op_type->result_id(), var->result_id()); + builder.AddStore(operand_id, load->result_id()); + + return var->result_id(); +} diff --git a/source/opt/fix_func_call_arguments.h b/source/opt/fix_func_call_arguments.h new file mode 100644 index 00000000..15781b8c --- /dev/null +++ b/source/opt/fix_func_call_arguments.h @@ -0,0 +1,47 @@ +// Copyright (c) 2022 Advanced Micro Devices, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _VAR_FUNC_CALL_PASS_H +#define _VAR_FUNC_CALL_PASS_H + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { +class FixFuncCallArgumentsPass : public Pass { + public: + FixFuncCallArgumentsPass() {} + const char* name() const override { return "fix-for-funcall-param"; } + Status Process() override; + // Returns true if the module has one one function. + bool ModuleHasASingleFunction(); + // Copies from the memory pointed to by |operand_inst| to a new function scope + // variable created before |func_call_inst|, and + // copies the value of the new variable back to the memory pointed to by + // |operand_inst| after |funct_call_inst| Returns the id of + // the new variable. + uint32_t ReplaceAccessChainFuncCallArguments(Instruction* func_call_inst, + Instruction* operand_inst); + + // Fix function call |func_call_inst| non memory object arguments + bool FixFuncCallArguments(Instruction* func_call_inst); + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisTypes; + } +}; +} // namespace opt +} // namespace spvtools + +#endif // _VAR_FUNC_CALL_PASS_H \ No newline at end of file diff --git a/source/opt/fold.cpp b/source/opt/fold.cpp index 6550fb4f..315741ad 100644 --- a/source/opt/fold.cpp +++ b/source/opt/fold.cpp @@ -540,7 +540,7 @@ std::vector InstructionFolder::FoldVectors( // in 32-bit words here. The reason of not using FoldScalars() here // is that we do not create temporary null constants as components // when the vector operand is a NullConstant because Constant creation - // may need extra checks for the validity and that is not manageed in + // may need extra checks for the validity and that is not managed in // here. if (const analysis::ScalarConstant* scalar_component = vector_operand->GetComponents().at(d)->AsScalarConstant()) { @@ -627,8 +627,7 @@ Instruction* InstructionFolder::FoldInstructionToConstant( Instruction* inst, std::function id_map) const { analysis::ConstantManager* const_mgr = context_->get_constant_mgr(); - if (!inst->IsFoldableByFoldScalar() && - !GetConstantFoldingRules().HasFoldingRule(inst)) { + if (!inst->IsFoldableByFoldScalar() && !HasConstFoldingRule(inst)) { return nullptr; } // Collect the values of the constant parameters. diff --git a/source/opt/fold_spec_constant_op_and_composite_pass.cpp b/source/opt/fold_spec_constant_op_and_composite_pass.cpp index 8ab717ea..7a518701 100644 --- a/source/opt/fold_spec_constant_op_and_composite_pass.cpp +++ b/source/opt/fold_spec_constant_op_and_composite_pass.cpp @@ -28,6 +28,7 @@ namespace opt { Pass::Status FoldSpecConstantOpAndCompositePass::Process() { bool modified = false; + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); // Traverse through all the constant defining instructions. For Normal // Constants whose values are determined and do not depend on OpUndef // instructions, records their values in two internal maps: id_to_const_val_ @@ -62,8 +63,8 @@ Pass::Status FoldSpecConstantOpAndCompositePass::Process() { // used in OpSpecConstant{Composite|Op} instructions. // TODO(qining): If the constant or its type has decoration, we may need // to skip it. - if (context()->get_constant_mgr()->GetType(inst) && - !context()->get_constant_mgr()->GetType(inst)->decoration_empty()) + if (const_mgr->GetType(inst) && + !const_mgr->GetType(inst)->decoration_empty()) continue; switch (SpvOp opcode = inst->opcode()) { // Records the values of Normal Constants. @@ -80,15 +81,14 @@ Pass::Status FoldSpecConstantOpAndCompositePass::Process() { // Constant will be turned in to a Normal Constant. In that case, a // Constant instance should also be created successfully and recorded // in the id_to_const_val_ and const_val_to_id_ mapps. - if (auto const_value = - context()->get_constant_mgr()->GetConstantFromInst(inst)) { + if (auto const_value = const_mgr->GetConstantFromInst(inst)) { // Need to replace the OpSpecConstantComposite instruction with a // corresponding OpConstantComposite instruction. if (opcode == SpvOp::SpvOpSpecConstantComposite) { inst->SetOpcode(SpvOp::SpvOpConstantComposite); modified = true; } - context()->get_constant_mgr()->MapConstantToInst(const_value, inst); + const_mgr->MapConstantToInst(const_value, inst); } break; } @@ -115,7 +115,7 @@ bool FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp( Instruction* folded_inst = nullptr; assert(inst->GetInOperand(0).type == SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER && - "The first in-operand of OpSpecContantOp instruction must be of " + "The first in-operand of OpSpecConstantOp instruction must be of " "SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER type"); switch (static_cast(inst->GetSingleWordInOperand(0))) { @@ -144,17 +144,9 @@ bool FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp( return true; } -uint32_t FoldSpecConstantOpAndCompositePass::GetTypeComponent( - uint32_t typeId, uint32_t element) const { - Instruction* type = context()->get_def_use_mgr()->GetDef(typeId); - uint32_t subtype = type->GetTypeComponent(element); - assert(subtype != 0); - - return subtype; -} - Instruction* FoldSpecConstantOpAndCompositePass::FoldWithInstructionFolder( Module::inst_iterator* inst_iter_ptr) { + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); // If one of operands to the instruction is not a // constant, then we cannot fold this spec constant. for (uint32_t i = 1; i < (*inst_iter_ptr)->NumInOperands(); i++) { @@ -164,7 +156,7 @@ Instruction* FoldSpecConstantOpAndCompositePass::FoldWithInstructionFolder( continue; } uint32_t id = operand.words[0]; - if (context()->get_constant_mgr()->FindDeclaredConstant(id) == nullptr) { + if (const_mgr->FindDeclaredConstant(id) == nullptr) { return nullptr; } } @@ -211,89 +203,10 @@ Instruction* FoldSpecConstantOpAndCompositePass::FoldWithInstructionFolder( new_const_inst->InsertAfter(insert_pos); get_def_use_mgr()->AnalyzeInstDefUse(new_const_inst); } + const_mgr->MapInst(new_const_inst); return new_const_inst; } -Instruction* FoldSpecConstantOpAndCompositePass::DoVectorShuffle( - Module::inst_iterator* pos) { - Instruction* inst = &**pos; - analysis::Vector* result_vec_type = - context()->get_constant_mgr()->GetType(inst)->AsVector(); - assert(inst->NumInOperands() - 1 > 2 && - "OpSpecConstantOp DoVectorShuffle instruction requires more than 2 " - "operands (2 vector ids and at least one literal operand"); - assert(result_vec_type && - "The result of VectorShuffle must be of type vector"); - - // A temporary null constants that can be used as the components of the result - // vector. This is needed when any one of the vector operands are null - // constant. - const analysis::Constant* null_component_constants = nullptr; - - // Get a concatenated vector of scalar constants. The vector should be built - // with the components from the first and the second operand of VectorShuffle. - std::vector concatenated_components; - // Note that for OpSpecConstantOp, the second in-operand is the first id - // operand. The first in-operand is the spec opcode. - for (uint32_t i : {1, 2}) { - assert(inst->GetInOperand(i).type == SPV_OPERAND_TYPE_ID && - "The vector operand must have a SPV_OPERAND_TYPE_ID type"); - uint32_t operand_id = inst->GetSingleWordInOperand(i); - auto operand_const = - context()->get_constant_mgr()->FindDeclaredConstant(operand_id); - if (!operand_const) return nullptr; - const analysis::Type* operand_type = operand_const->type(); - assert(operand_type->AsVector() && - "The first two operand of VectorShuffle must be of vector type"); - if (auto vec_const = operand_const->AsVectorConstant()) { - // case 1: current operand is a non-null vector constant. - concatenated_components.insert(concatenated_components.end(), - vec_const->GetComponents().begin(), - vec_const->GetComponents().end()); - } else if (operand_const->AsNullConstant()) { - // case 2: current operand is a null vector constant. Create a temporary - // null scalar constant as the component. - if (!null_component_constants) { - const analysis::Type* component_type = - operand_type->AsVector()->element_type(); - null_component_constants = - context()->get_constant_mgr()->GetConstant(component_type, {}); - } - // Append the null scalar consts to the concatenated components - // vector. - concatenated_components.insert(concatenated_components.end(), - operand_type->AsVector()->element_count(), - null_component_constants); - } else { - // no other valid cases - return nullptr; - } - } - // Create null component constants if there are any. The component constants - // must be added to the module before the dependee composite constants to - // satisfy SSA def-use dominance. - if (null_component_constants) { - context()->get_constant_mgr()->BuildInstructionAndAddToModule( - null_component_constants, pos); - } - // Create the new vector constant with the selected components. - std::vector selected_components; - for (uint32_t i = 3; i < inst->NumInOperands(); i++) { - assert(inst->GetInOperand(i).type == SPV_OPERAND_TYPE_LITERAL_INTEGER && - "The literal operand must of type SPV_OPERAND_TYPE_LITERAL_INTEGER"); - uint32_t literal = inst->GetSingleWordInOperand(i); - assert(literal < concatenated_components.size() && - "Literal index out of bound of the concatenated vector"); - selected_components.push_back(concatenated_components[literal]); - } - auto new_vec_const = MakeUnique( - result_vec_type, selected_components); - auto reg_vec_const = - context()->get_constant_mgr()->RegisterConstant(std::move(new_vec_const)); - return context()->get_constant_mgr()->BuildInstructionAndAddToModule( - reg_vec_const, pos); -} - namespace { // A helper function to check the type for component wise operations. Returns // true if the type: @@ -374,8 +287,8 @@ utils::SmallVector EncodeIntegerAsWords(const analysis::Type& type, Instruction* FoldSpecConstantOpAndCompositePass::DoComponentWiseOperation( Module::inst_iterator* pos) { const Instruction* inst = &**pos; - const analysis::Type* result_type = - context()->get_constant_mgr()->GetType(inst); + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + const analysis::Type* result_type = const_mgr->GetType(inst); SpvOp spec_opcode = static_cast(inst->GetSingleWordInOperand(0)); // Check and collect operands. std::vector operands; @@ -400,10 +313,9 @@ Instruction* FoldSpecConstantOpAndCompositePass::DoComponentWiseOperation( // Scalar operation const uint32_t result_val = context()->get_instruction_folder().FoldScalars(spec_opcode, operands); - auto result_const = context()->get_constant_mgr()->GetConstant( + auto result_const = const_mgr->GetConstant( result_type, EncodeIntegerAsWords(*result_type, result_val)); - return context()->get_constant_mgr()->BuildInstructionAndAddToModule( - result_const, pos); + return const_mgr->BuildInstructionAndAddToModule(result_const, pos); } else if (result_type->AsVector()) { // Vector operation const analysis::Type* element_type = @@ -414,11 +326,10 @@ Instruction* FoldSpecConstantOpAndCompositePass::DoComponentWiseOperation( operands); std::vector result_vector_components; for (const uint32_t r : result_vec) { - if (auto rc = context()->get_constant_mgr()->GetConstant( + if (auto rc = const_mgr->GetConstant( element_type, EncodeIntegerAsWords(*element_type, r))) { result_vector_components.push_back(rc); - if (!context()->get_constant_mgr()->BuildInstructionAndAddToModule( - rc, pos)) { + if (!const_mgr->BuildInstructionAndAddToModule(rc, pos)) { assert(false && "Failed to build and insert constant declaring instruction " "for the given vector component constant"); @@ -429,10 +340,8 @@ Instruction* FoldSpecConstantOpAndCompositePass::DoComponentWiseOperation( } auto new_vec_const = MakeUnique( result_type->AsVector(), result_vector_components); - auto reg_vec_const = context()->get_constant_mgr()->RegisterConstant( - std::move(new_vec_const)); - return context()->get_constant_mgr()->BuildInstructionAndAddToModule( - reg_vec_const, pos); + auto reg_vec_const = const_mgr->RegisterConstant(std::move(new_vec_const)); + return const_mgr->BuildInstructionAndAddToModule(reg_vec_const, pos); } else { // Cannot process invalid component wise operation. The result of component // wise operation must be of integer or bool scalar or vector of diff --git a/source/opt/fold_spec_constant_op_and_composite_pass.h b/source/opt/fold_spec_constant_op_and_composite_pass.h index 361d3cac..9a8fb403 100644 --- a/source/opt/fold_spec_constant_op_and_composite_pass.h +++ b/source/opt/fold_spec_constant_op_and_composite_pass.h @@ -58,22 +58,11 @@ class FoldSpecConstantOpAndCompositePass : public Pass { // |inst_iter_ptr| using the instruction folder. Instruction* FoldWithInstructionFolder(Module::inst_iterator* inst_iter_ptr); - // Try to fold the OpSpecConstantOp VectorShuffle instruction pointed by the - // given instruction iterator to a normal constant defining instruction. - // Returns the pointer to the new constant defining instruction if succeeded. - // Otherwise return nullptr. - Instruction* DoVectorShuffle(Module::inst_iterator* inst_iter_ptr); - // Try to fold the OpSpecConstantOp instruction // pointed by the given instruction iterator to a normal constant defining // instruction. Returns the pointer to the new constant defining instruction // if succeeded, otherwise return nullptr. Instruction* DoComponentWiseOperation(Module::inst_iterator* inst_iter_ptr); - - // Returns the |element|'th subtype of |type|. - // - // |type| must be a composite type. - uint32_t GetTypeComponent(uint32_t type, uint32_t element) const; }; } // namespace opt diff --git a/source/opt/folding_rules.cpp b/source/opt/folding_rules.cpp index 1e7c424b..3d803add 100644 --- a/source/opt/folding_rules.cpp +++ b/source/opt/folding_rules.cpp @@ -14,6 +14,7 @@ #include "source/opt/folding_rules.h" +#include #include #include #include @@ -124,6 +125,69 @@ Instruction* NonConstInput(IRContext* context, const analysis::Constant* c, inst->GetSingleWordInOperand(in_op)); } +std::vector ExtractInts(uint64_t val) { + std::vector words; + words.push_back(static_cast(val)); + words.push_back(static_cast(val >> 32)); + return words; +} + +std::vector GetWordsFromScalarIntConstant( + const analysis::IntConstant* c) { + assert(c != nullptr); + uint32_t width = c->type()->AsInteger()->width(); + assert(width == 8 || width == 16 || width == 32 || width == 64); + if (width == 64) { + uint64_t uval = static_cast(c->GetU64()); + return ExtractInts(uval); + } + // Section 2.2.1 of the SPIR-V spec guarantees that all integer types + // smaller than 32-bits are automatically zero or sign extended to 32-bits. + return {c->GetU32BitValue()}; +} + +std::vector GetWordsFromScalarFloatConstant( + const analysis::FloatConstant* c) { + assert(c != nullptr); + uint32_t width = c->type()->AsFloat()->width(); + assert(width == 16 || width == 32 || width == 64); + if (width == 64) { + utils::FloatProxy result(c->GetDouble()); + return result.GetWords(); + } + // Section 2.2.1 of the SPIR-V spec guarantees that all floating-point types + // smaller than 32-bits are automatically zero extended to 32-bits. + return {c->GetU32BitValue()}; +} + +std::vector GetWordsFromNumericScalarOrVectorConstant( + analysis::ConstantManager* const_mgr, const analysis::Constant* c) { + if (const auto* float_constant = c->AsFloatConstant()) { + return GetWordsFromScalarFloatConstant(float_constant); + } else if (const auto* int_constant = c->AsIntConstant()) { + return GetWordsFromScalarIntConstant(int_constant); + } else if (const auto* vec_constant = c->AsVectorConstant()) { + std::vector words; + for (const auto* comp : vec_constant->GetComponents()) { + auto comp_in_words = + GetWordsFromNumericScalarOrVectorConstant(const_mgr, comp); + words.insert(words.end(), comp_in_words.begin(), comp_in_words.end()); + } + return words; + } + return {}; +} + +const analysis::Constant* ConvertWordsToNumericScalarOrVectorConstant( + analysis::ConstantManager* const_mgr, const std::vector& words, + const analysis::Type* type) { + if (type->AsInteger() || type->AsFloat()) + return const_mgr->GetConstant(type, words); + if (const auto* vec_type = type->AsVector()) + return const_mgr->GetNumericVectorConstantWithWords(vec_type, words); + return nullptr; +} + // Returns the negation of |c|. |c| must be a 32 or 64 bit floating point // constant. uint32_t NegateFloatingPointConstant(analysis::ConstantManager* const_mgr, @@ -146,13 +210,6 @@ uint32_t NegateFloatingPointConstant(analysis::ConstantManager* const_mgr, return const_mgr->GetDefiningInstruction(negated_const)->result_id(); } -std::vector ExtractInts(uint64_t val) { - std::vector words; - words.push_back(static_cast(val)); - words.push_back(static_cast(val >> 32)); - return words; -} - // Negates the integer constant |c|. Returns the id of the defining instruction. uint32_t NegateIntegerConstant(analysis::ConstantManager* const_mgr, const analysis::Constant* c) { @@ -223,6 +280,11 @@ uint32_t Reciprocal(analysis::ConstantManager* const_mgr, uint32_t width = c->type()->AsFloat()->width(); assert(width == 32 || width == 64); std::vector words; + + if (c->IsZero()) { + return 0; + } + if (width == 64) { spvtools::utils::FloatProxy result(1.0 / c->GetDouble()); if (!IsValidResult(result.getAsFloat())) return 0; @@ -470,7 +532,8 @@ uint32_t PerformFloatingPointOperation(analysis::ConstantManager* const_mgr, float fval = val.getAsFloat(); \ if (!IsValidResult(fval)) return 0; \ words = val.GetWords(); \ - } static_assert(true, "require extra semicolon") + } \ + static_assert(true, "require extra semicolon") switch (opcode) { case SpvOpFMul: FOLD_OP(*); @@ -505,24 +568,19 @@ uint32_t PerformIntegerOperation(analysis::ConstantManager* const_mgr, uint32_t width = type->AsInteger()->width(); assert(width == 32 || width == 64); std::vector words; -#define FOLD_OP(op) \ - if (width == 64) { \ - if (type->IsSigned()) { \ - int64_t val = input1->GetS64() op input2->GetS64(); \ - words = ExtractInts(static_cast(val)); \ - } else { \ - uint64_t val = input1->GetU64() op input2->GetU64(); \ - words = ExtractInts(val); \ - } \ - } else { \ - if (type->IsSigned()) { \ - int32_t val = input1->GetS32() op input2->GetS32(); \ - words.push_back(static_cast(val)); \ - } else { \ - uint32_t val = input1->GetU32() op input2->GetU32(); \ - words.push_back(val); \ - } \ - } static_assert(true, "require extra semicalon") + // Regardless of the sign of the constant, folding is performed on an unsigned + // interpretation of the constant data. This avoids signed integer overflow + // while folding, and works because sign is irrelevant for the IAdd, ISub and + // IMul instructions. +#define FOLD_OP(op) \ + if (width == 64) { \ + uint64_t val = input1->GetU64() op input2->GetU64(); \ + words = ExtractInts(val); \ + } else { \ + uint32_t val = input1->GetU32() op input2->GetU32(); \ + words.push_back(val); \ + } \ + static_assert(true, "require extra semicolon") switch (opcode) { case SpvOpIMul: FOLD_OP(*); @@ -914,30 +972,21 @@ FoldingRule MergeDivMulArithmetic() { // Fold divides of a constant and a negation. // Cases: // (-x) / 2 = x / -2 -// 2 / (-x) = 2 / -x +// 2 / (-x) = -2 / x FoldingRule MergeDivNegateArithmetic() { return [](IRContext* context, Instruction* inst, const std::vector& constants) { - assert(inst->opcode() == SpvOpFDiv || inst->opcode() == SpvOpSDiv || - inst->opcode() == SpvOpUDiv); + assert(inst->opcode() == SpvOpFDiv); analysis::ConstantManager* const_mgr = context->get_constant_mgr(); - const analysis::Type* type = - context->get_type_mgr()->GetType(inst->type_id()); - bool uses_float = HasFloatingPoint(type); - if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; - - uint32_t width = ElementWidth(type); - if (width != 32 && width != 64) return false; + if (!inst->IsFloatingPointFoldingAllowed()) return false; const analysis::Constant* const_input1 = ConstInput(constants); if (!const_input1) return false; Instruction* other_inst = NonConstInput(context, constants[0], inst); - if (uses_float && !other_inst->IsFloatingPointFoldingAllowed()) - return false; + if (!other_inst->IsFloatingPointFoldingAllowed()) return false; bool first_is_variable = constants[0] == nullptr; - if (other_inst->opcode() == SpvOpFNegate || - other_inst->opcode() == SpvOpSNegate) { + if (other_inst->opcode() == SpvOpFNegate) { uint32_t neg_id = NegateConstant(const_mgr, const_input1); if (first_is_variable) { @@ -1389,6 +1438,132 @@ FoldingRule FactorAddMuls() { }; } +// Replaces |inst| inplace with an FMA instruction |(x*y)+a|. +void ReplaceWithFma(Instruction* inst, uint32_t x, uint32_t y, uint32_t a) { + uint32_t ext = + inst->context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + + if (ext == 0) { + inst->context()->AddExtInstImport("GLSL.std.450"); + ext = inst->context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + assert(ext != 0 && + "Could not add the GLSL.std.450 extended instruction set"); + } + + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {ext}}); + operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {GLSLstd450Fma}}); + operands.push_back({SPV_OPERAND_TYPE_ID, {x}}); + operands.push_back({SPV_OPERAND_TYPE_ID, {y}}); + operands.push_back({SPV_OPERAND_TYPE_ID, {a}}); + + inst->SetOpcode(SpvOpExtInst); + inst->SetInOperands(std::move(operands)); +} + +// Folds a multiple and add into an Fma. +// +// Cases: +// (x * y) + a = Fma x y a +// a + (x * y) = Fma x y a +bool MergeMulAddArithmetic(IRContext* context, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == SpvOpFAdd); + + if (!inst->IsFloatingPointFoldingAllowed()) { + return false; + } + + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + for (int i = 0; i < 2; i++) { + uint32_t op_id = inst->GetSingleWordInOperand(i); + Instruction* op_inst = def_use_mgr->GetDef(op_id); + + if (op_inst->opcode() != SpvOpFMul) { + continue; + } + + if (!op_inst->IsFloatingPointFoldingAllowed()) { + continue; + } + + uint32_t x = op_inst->GetSingleWordInOperand(0); + uint32_t y = op_inst->GetSingleWordInOperand(1); + uint32_t a = inst->GetSingleWordInOperand((i + 1) % 2); + ReplaceWithFma(inst, x, y, a); + return true; + } + return false; +} + +// Replaces |sub| inplace with an FMA instruction |(x*y)+a| where |a| first gets +// negated if |negate_addition| is true, otherwise |x| gets negated. +void ReplaceWithFmaAndNegate(Instruction* sub, uint32_t x, uint32_t y, + uint32_t a, bool negate_addition) { + uint32_t ext = + sub->context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + + if (ext == 0) { + sub->context()->AddExtInstImport("GLSL.std.450"); + ext = sub->context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + assert(ext != 0 && + "Could not add the GLSL.std.450 extended instruction set"); + } + + InstructionBuilder ir_builder( + sub->context(), sub, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + Instruction* neg = ir_builder.AddUnaryOp(sub->type_id(), SpvOpFNegate, + negate_addition ? a : x); + uint32_t neg_op = neg->result_id(); // -a : -x + + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {ext}}); + operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {GLSLstd450Fma}}); + operands.push_back({SPV_OPERAND_TYPE_ID, {negate_addition ? x : neg_op}}); + operands.push_back({SPV_OPERAND_TYPE_ID, {y}}); + operands.push_back({SPV_OPERAND_TYPE_ID, {negate_addition ? neg_op : a}}); + + sub->SetOpcode(SpvOpExtInst); + sub->SetInOperands(std::move(operands)); +} + +// Folds a multiply and subtract into an Fma and negation. +// +// Cases: +// (x * y) - a = Fma x y -a +// a - (x * y) = Fma -x y a +bool MergeMulSubArithmetic(IRContext* context, Instruction* sub, + const std::vector&) { + assert(sub->opcode() == SpvOpFSub); + + if (!sub->IsFloatingPointFoldingAllowed()) { + return false; + } + + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + for (int i = 0; i < 2; i++) { + uint32_t op_id = sub->GetSingleWordInOperand(i); + Instruction* mul = def_use_mgr->GetDef(op_id); + + if (mul->opcode() != SpvOpFMul) { + continue; + } + + if (!mul->IsFloatingPointFoldingAllowed()) { + continue; + } + + uint32_t x = mul->GetSingleWordInOperand(0); + uint32_t y = mul->GetSingleWordInOperand(1); + uint32_t a = sub->GetSingleWordInOperand((i + 1) % 2); + ReplaceWithFmaAndNegate(sub, x, y, a, i == 0); + return true; + } + return false; +} + FoldingRule IntMultipleBy1() { return [](IRContext*, Instruction* inst, const std::vector& constants) { @@ -1415,90 +1590,172 @@ FoldingRule IntMultipleBy1() { }; } -FoldingRule CompositeConstructFeedingExtract() { - return [](IRContext* context, Instruction* inst, - const std::vector&) { - // If the input to an OpCompositeExtract is an OpCompositeConstruct, - // then we can simply use the appropriate element in the construction. - assert(inst->opcode() == SpvOpCompositeExtract && - "Wrong opcode. Should be OpCompositeExtract."); - analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); - analysis::TypeManager* type_mgr = context->get_type_mgr(); +// Returns the number of elements that the |index|th in operand in |inst| +// contributes to the result of |inst|. |inst| must be an +// OpCompositeConstructInstruction. +uint32_t GetNumOfElementsContributedByOperand(IRContext* context, + const Instruction* inst, + uint32_t index) { + assert(inst->opcode() == SpvOpCompositeConstruct); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); - // If there are no index operands, then this rule cannot do anything. - if (inst->NumInOperands() <= 1) { - return false; - } + analysis::Vector* result_type = + type_mgr->GetType(inst->type_id())->AsVector(); + if (result_type == nullptr) { + // If the result of the OpCompositeConstruct is not a vector then every + // operands corresponds to a single element in the result. + return 1; + } - uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); - Instruction* cinst = def_use_mgr->GetDef(cid); + // If the result type is a vector then the operands are either scalars or + // vectors. If it is a scalar, then it corresponds to a single element. If it + // is a vector, then each element in the vector will be an element in the + // result. + uint32_t id = inst->GetSingleWordInOperand(index); + Instruction* def = def_use_mgr->GetDef(id); + analysis::Vector* type = type_mgr->GetType(def->type_id())->AsVector(); + if (type == nullptr) { + return 1; + } + return type->element_count(); +} - if (cinst->opcode() != SpvOpCompositeConstruct) { - return false; - } +// Returns the in-operands for an OpCompositeExtract instruction that are needed +// to extract the |result_index|th element in the result of |inst| without using +// the result of |inst|. Returns the empty vector if |result_index| is +// out-of-bounds. |inst| must be an |OpCompositeConstruct| instruction. +std::vector GetExtractOperandsForElementOfCompositeConstruct( + IRContext* context, const Instruction* inst, uint32_t result_index) { + assert(inst->opcode() == SpvOpCompositeConstruct); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); - std::vector operands; - analysis::Type* composite_type = type_mgr->GetType(cinst->type_id()); - if (composite_type->AsVector() == nullptr) { - // Get the element being extracted from the OpCompositeConstruct - // Since it is not a vector, it is simple to extract the single element. - uint32_t element_index = inst->GetSingleWordInOperand(1); - uint32_t element_id = cinst->GetSingleWordInOperand(element_index); - operands.push_back({SPV_OPERAND_TYPE_ID, {element_id}}); + analysis::Type* result_type = type_mgr->GetType(inst->type_id()); + if (result_type->AsVector() == nullptr) { + uint32_t id = inst->GetSingleWordInOperand(result_index); + return {Operand(SPV_OPERAND_TYPE_ID, {id})}; + } - // Add the remaining indices for extraction. - for (uint32_t i = 2; i < inst->NumInOperands(); ++i) { - operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, - {inst->GetSingleWordInOperand(i)}}); - } - - } else { - // With vectors we have to handle the case where it is concatenating - // vectors. - assert(inst->NumInOperands() == 2 && - "Expecting a vector of scalar values."); - - uint32_t element_index = inst->GetSingleWordInOperand(1); - for (uint32_t construct_index = 0; - construct_index < cinst->NumInOperands(); ++construct_index) { - uint32_t element_id = cinst->GetSingleWordInOperand(construct_index); - Instruction* element_def = def_use_mgr->GetDef(element_id); - analysis::Vector* element_type = - type_mgr->GetType(element_def->type_id())->AsVector(); - if (element_type) { - uint32_t vector_size = element_type->element_count(); - if (vector_size <= element_index) { - // The element we want comes after this vector. - element_index -= vector_size; - } else { - // We want an element of this vector. - operands.push_back({SPV_OPERAND_TYPE_ID, {element_id}}); - operands.push_back( - {SPV_OPERAND_TYPE_LITERAL_INTEGER, {element_index}}); - break; - } - } else { - if (element_index == 0) { - // This is a scalar, and we this is the element we are extracting. - operands.push_back({SPV_OPERAND_TYPE_ID, {element_id}}); - break; - } else { - // Skip over this scalar value. - --element_index; - } - } + // If the result type is a vector, then vector operands are concatenated. + uint32_t total_element_count = 0; + for (uint32_t idx = 0; idx < inst->NumInOperands(); ++idx) { + uint32_t element_count = + GetNumOfElementsContributedByOperand(context, inst, idx); + total_element_count += element_count; + if (result_index < total_element_count) { + std::vector operands; + uint32_t id = inst->GetSingleWordInOperand(idx); + Instruction* operand_def = def_use_mgr->GetDef(id); + analysis::Type* operand_type = type_mgr->GetType(operand_def->type_id()); + + operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); + if (operand_type->AsVector()) { + uint32_t start_index_of_id = total_element_count - element_count; + uint32_t index_into_id = result_index - start_index_of_id; + operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {index_into_id}}); } + return operands; } + } + return {}; +} +bool CompositeConstructFeedingExtract( + IRContext* context, Instruction* inst, + const std::vector&) { + // If the input to an OpCompositeExtract is an OpCompositeConstruct, + // then we can simply use the appropriate element in the construction. + assert(inst->opcode() == SpvOpCompositeExtract && + "Wrong opcode. Should be OpCompositeExtract."); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + + // If there are no index operands, then this rule cannot do anything. + if (inst->NumInOperands() <= 1) { + return false; + } + + uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); + Instruction* cinst = def_use_mgr->GetDef(cid); + + if (cinst->opcode() != SpvOpCompositeConstruct) { + return false; + } + + uint32_t index_into_result = inst->GetSingleWordInOperand(1); + std::vector operands = + GetExtractOperandsForElementOfCompositeConstruct(context, cinst, + index_into_result); + + if (operands.empty()) { + return false; + } + + // Add the remaining indices for extraction. + for (uint32_t i = 2; i < inst->NumInOperands(); ++i) { + operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {inst->GetSingleWordInOperand(i)}}); + } + + if (operands.size() == 1) { // If there were no extra indices, then we have the final object. No need - // to extract even more. - if (operands.size() == 1) { - inst->SetOpcode(SpvOpCopyObject); - } + // to extract any more. + inst->SetOpcode(SpvOpCopyObject); + } - inst->SetInOperands(std::move(operands)); - return true; - }; + inst->SetInOperands(std::move(operands)); + return true; +} + +// Walks the indexes chain from |start| to |end| of an OpCompositeInsert or +// OpCompositeExtract instruction, and returns the type of the final element +// being accessed. +const analysis::Type* GetElementType(uint32_t type_id, + Instruction::iterator start, + Instruction::iterator end, + const analysis::TypeManager* type_mgr) { + const analysis::Type* type = type_mgr->GetType(type_id); + for (auto index : make_range(std::move(start), std::move(end))) { + assert(index.type == SPV_OPERAND_TYPE_LITERAL_INTEGER && + index.words.size() == 1); + if (auto* array_type = type->AsArray()) { + type = array_type->element_type(); + } else if (auto* matrix_type = type->AsMatrix()) { + type = matrix_type->element_type(); + } else if (auto* struct_type = type->AsStruct()) { + type = struct_type->element_types()[index.words[0]]; + } else { + type = nullptr; + } + } + return type; +} + +// Returns true of |inst_1| and |inst_2| have the same indexes that will be used +// to index into a composite object, excluding the last index. The two +// instructions must have the same opcode, and be either OpCompositeExtract or +// OpCompositeInsert instructions. +bool HaveSameIndexesExceptForLast(Instruction* inst_1, Instruction* inst_2) { + assert(inst_1->opcode() == inst_2->opcode() && + "Expecting the opcodes to be the same."); + assert((inst_1->opcode() == SpvOpCompositeInsert || + inst_1->opcode() == SpvOpCompositeExtract) && + "Instructions must be OpCompositeInsert or OpCompositeExtract."); + + if (inst_1->NumInOperands() != inst_2->NumInOperands()) { + return false; + } + + uint32_t first_index_position = + (inst_1->opcode() == SpvOpCompositeInsert ? 2 : 1); + for (uint32_t i = first_index_position; i < inst_1->NumInOperands() - 1; + i++) { + if (inst_1->GetSingleWordInOperand(i) != + inst_2->GetSingleWordInOperand(i)) { + return false; + } + } + return true; } // If the OpCompositeConstruct is simply putting back together elements that @@ -1523,19 +1780,24 @@ bool CompositeExtractFeedingConstruct( // - extractions // - extracting the same position they are inserting // - all extract from the same id. + Instruction* first_element_inst = nullptr; for (uint32_t i = 0; i < inst->NumInOperands(); ++i) { const uint32_t element_id = inst->GetSingleWordInOperand(i); Instruction* element_inst = def_use_mgr->GetDef(element_id); + if (first_element_inst == nullptr) { + first_element_inst = element_inst; + } if (element_inst->opcode() != SpvOpCompositeExtract) { return false; } - if (element_inst->NumInOperands() != 2) { + if (!HaveSameIndexesExceptForLast(element_inst, first_element_inst)) { return false; } - if (element_inst->GetSingleWordInOperand(1) != i) { + if (element_inst->GetSingleWordInOperand(element_inst->NumInOperands() - + 1) != i) { return false; } @@ -1551,13 +1813,31 @@ bool CompositeExtractFeedingConstruct( // The last check it to see that the object being extracted from is the // correct type. Instruction* original_inst = def_use_mgr->GetDef(original_id); - if (original_inst->type_id() != inst->type_id()) { + analysis::TypeManager* type_mgr = context->get_type_mgr(); + const analysis::Type* original_type = + GetElementType(original_inst->type_id(), first_element_inst->begin() + 3, + first_element_inst->end() - 1, type_mgr); + + if (original_type == nullptr) { return false; } - // Simplify by using the original object. - inst->SetOpcode(SpvOpCopyObject); - inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {original_id}}}); + if (inst->type_id() != type_mgr->GetId(original_type)) { + return false; + } + + if (first_element_inst->NumInOperands() == 2) { + // Simplify by using the original object. + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {original_id}}}); + return true; + } + + // Copies the original id and all indexes except for the last to the new + // extract instruction. + inst->SetOpcode(SpvOpCompositeExtract); + inst->SetInOperands(std::vector(first_element_inst->begin() + 2, + first_element_inst->end() - 1)); return true; } @@ -1761,6 +2041,139 @@ FoldingRule FMixFeedingExtract() { }; } +// Returns the number of elements in the composite type |type|. Returns 0 if +// |type| is a scalar value. +uint32_t GetNumberOfElements(const analysis::Type* type) { + if (auto* vector_type = type->AsVector()) { + return vector_type->element_count(); + } + if (auto* matrix_type = type->AsMatrix()) { + return matrix_type->element_count(); + } + if (auto* struct_type = type->AsStruct()) { + return static_cast(struct_type->element_types().size()); + } + if (auto* array_type = type->AsArray()) { + return array_type->length_info().words[0]; + } + return 0; +} + +// Returns a map with the set of values that were inserted into an object by +// the chain of OpCompositeInsertInstruction starting with |inst|. +// The map will map the index to the value inserted at that index. +std::map GetInsertedValues(Instruction* inst) { + analysis::DefUseManager* def_use_mgr = inst->context()->get_def_use_mgr(); + std::map values_inserted; + Instruction* current_inst = inst; + while (current_inst->opcode() == SpvOpCompositeInsert) { + if (current_inst->NumInOperands() > inst->NumInOperands()) { + // This is the catch the case + // %2 = OpCompositeInsert %m2x2int %v2int_1_0 %m2x2int_undef 0 + // %3 = OpCompositeInsert %m2x2int %int_4 %2 0 0 + // %4 = OpCompositeInsert %m2x2int %v2int_2_3 %3 1 + // In this case we cannot do a single construct to get the matrix. + uint32_t partially_inserted_element_index = + current_inst->GetSingleWordInOperand(inst->NumInOperands() - 1); + if (values_inserted.count(partially_inserted_element_index) == 0) + return {}; + } + if (HaveSameIndexesExceptForLast(inst, current_inst)) { + values_inserted.insert( + {current_inst->GetSingleWordInOperand(current_inst->NumInOperands() - + 1), + current_inst->GetSingleWordInOperand(kInsertObjectIdInIdx)}); + } + current_inst = def_use_mgr->GetDef( + current_inst->GetSingleWordInOperand(kInsertCompositeIdInIdx)); + } + return values_inserted; +} + +// Returns true of there is an entry in |values_inserted| for every element of +// |Type|. +bool DoInsertedValuesCoverEntireObject( + const analysis::Type* type, std::map& values_inserted) { + uint32_t container_size = GetNumberOfElements(type); + if (container_size != values_inserted.size()) { + return false; + } + + if (values_inserted.rbegin()->first >= container_size) { + return false; + } + return true; +} + +// Returns the type of the element that immediately contains the element being +// inserted by the OpCompositeInsert instruction |inst|. +const analysis::Type* GetContainerType(Instruction* inst) { + assert(inst->opcode() == SpvOpCompositeInsert); + analysis::TypeManager* type_mgr = inst->context()->get_type_mgr(); + return GetElementType(inst->type_id(), inst->begin() + 4, inst->end() - 1, + type_mgr); +} + +// Returns an OpCompositeConstruct instruction that build an object with +// |type_id| out of the values in |values_inserted|. Each value will be +// placed at the index corresponding to the value. The new instruction will +// be placed before |insert_before|. +Instruction* BuildCompositeConstruct( + uint32_t type_id, const std::map& values_inserted, + Instruction* insert_before) { + InstructionBuilder ir_builder( + insert_before->context(), insert_before, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + std::vector ids_in_order; + for (auto it : values_inserted) { + ids_in_order.push_back(it.second); + } + Instruction* construct = + ir_builder.AddCompositeConstruct(type_id, ids_in_order); + return construct; +} + +// Replaces the OpCompositeInsert |inst| that inserts |construct| into the same +// object as |inst| with final index removed. If the resulting +// OpCompositeInsert instruction would have no remaining indexes, the +// instruction is replaced with an OpCopyObject instead. +void InsertConstructedObject(Instruction* inst, const Instruction* construct) { + if (inst->NumInOperands() == 3) { + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {construct->result_id()}}}); + } else { + inst->SetInOperand(kInsertObjectIdInIdx, {construct->result_id()}); + inst->RemoveOperand(inst->NumOperands() - 1); + } +} + +// Replaces a series of |OpCompositeInsert| instruction that cover the entire +// object with an |OpCompositeConstruct|. +bool CompositeInsertToCompositeConstruct( + IRContext* context, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == SpvOpCompositeInsert && + "Wrong opcode. Should be OpCompositeInsert."); + if (inst->NumInOperands() < 3) return false; + + std::map values_inserted = GetInsertedValues(inst); + const analysis::Type* container_type = GetContainerType(inst); + if (container_type == nullptr) { + return false; + } + + if (!DoInsertedValuesCoverEntireObject(container_type, values_inserted)) { + return false; + } + + analysis::TypeManager* type_mgr = context->get_type_mgr(); + Instruction* construct = BuildCompositeConstruct( + type_mgr->GetId(container_type), values_inserted, inst); + InsertConstructedObject(inst, construct); + return true; +} + FoldingRule RedundantPhi() { // An OpPhi instruction where all values are the same or the result of the phi // itself, can be replaced by the value itself. @@ -1796,6 +2209,35 @@ FoldingRule RedundantPhi() { }; } +FoldingRule BitCastScalarOrVector() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpBitcast && constants.size() == 1); + if (constants[0] == nullptr) return false; + + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + if (HasFloatingPoint(type) && !inst->IsFloatingPointFoldingAllowed()) + return false; + + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + std::vector words = + GetWordsFromNumericScalarOrVectorConstant(const_mgr, constants[0]); + if (words.size() == 0) return false; + + const analysis::Constant* bitcasted_constant = + ConvertWordsToNumericScalarOrVectorConstant(const_mgr, words, type); + if (!bitcasted_constant) return false; + + auto new_feeder_id = + const_mgr->GetDefiningInstruction(bitcasted_constant, inst->type_id()) + ->result_id(); + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {new_feeder_id}}}); + return true; + }; +} + FoldingRule RedundantSelect() { // An OpSelect instruction where both values are the same or the condition is // constant can be replaced by one of the values @@ -2267,7 +2709,7 @@ FoldingRule VectorShuffleFeedingShuffle() { // fold. return false; } - } else { + } else if (component_index != undef_literal) { if (new_feeder_id == 0) { // First time through, save the id of the operand the element comes // from. @@ -2281,7 +2723,7 @@ FoldingRule VectorShuffleFeedingShuffle() { component_index -= feeder_op0_length; } - if (!feeder_is_op0) { + if (!feeder_is_op0 && component_index != undef_literal) { component_index += op0_length; } } @@ -2309,7 +2751,8 @@ FoldingRule VectorShuffleFeedingShuffle() { if (adjustment != 0) { for (uint32_t i = 2; i < new_operands.size(); i++) { - if (inst->GetSingleWordInOperand(i) >= op0_length) { + uint32_t operand = inst->GetSingleWordInOperand(i); + if (operand >= op0_length && operand != undef_literal) { new_operands[i].words[0] -= adjustment; } } @@ -2423,13 +2866,17 @@ void FoldingRules::AddFoldingRules() { // Note that the order in which rules are added to the list matters. If a rule // applies to the instruction, the rest of the rules will not be attempted. // Take that into consideration. + rules_[SpvOpBitcast].push_back(BitCastScalarOrVector()); + rules_[SpvOpCompositeConstruct].push_back(CompositeExtractFeedingConstruct); rules_[SpvOpCompositeExtract].push_back(InsertFeedingExtract()); - rules_[SpvOpCompositeExtract].push_back(CompositeConstructFeedingExtract()); + rules_[SpvOpCompositeExtract].push_back(CompositeConstructFeedingExtract); rules_[SpvOpCompositeExtract].push_back(VectorShuffleFeedingExtract()); rules_[SpvOpCompositeExtract].push_back(FMixFeedingExtract()); + rules_[SpvOpCompositeInsert].push_back(CompositeInsertToCompositeConstruct); + rules_[SpvOpDot].push_back(DotProductDoingExtract()); rules_[SpvOpEntryPoint].push_back(RemoveRedundantOperands()); @@ -2440,6 +2887,7 @@ void FoldingRules::AddFoldingRules() { rules_[SpvOpFAdd].push_back(MergeAddSubArithmetic()); rules_[SpvOpFAdd].push_back(MergeGenericAddSubArithmetic()); rules_[SpvOpFAdd].push_back(FactorAddMuls()); + rules_[SpvOpFAdd].push_back(MergeMulAddArithmetic); rules_[SpvOpFDiv].push_back(RedundantFDiv()); rules_[SpvOpFDiv].push_back(ReciprocalFDiv()); @@ -2460,6 +2908,7 @@ void FoldingRules::AddFoldingRules() { rules_[SpvOpFSub].push_back(MergeSubNegateArithmetic()); rules_[SpvOpFSub].push_back(MergeSubAddArithmetic()); rules_[SpvOpFSub].push_back(MergeSubSubArithmetic()); + rules_[SpvOpFSub].push_back(MergeMulSubArithmetic); rules_[SpvOpIAdd].push_back(RedundantIAdd()); rules_[SpvOpIAdd].push_back(MergeAddNegateArithmetic()); @@ -2478,8 +2927,6 @@ void FoldingRules::AddFoldingRules() { rules_[SpvOpPhi].push_back(RedundantPhi()); - rules_[SpvOpSDiv].push_back(MergeDivNegateArithmetic()); - rules_[SpvOpSNegate].push_back(MergeNegateArithmetic()); rules_[SpvOpSNegate].push_back(MergeNegateMulDivArithmetic()); rules_[SpvOpSNegate].push_back(MergeNegateAddSubArithmetic()); @@ -2488,8 +2935,6 @@ void FoldingRules::AddFoldingRules() { rules_[SpvOpStore].push_back(StoringUndef()); - rules_[SpvOpUDiv].push_back(MergeDivNegateArithmetic()); - rules_[SpvOpVectorShuffle].push_back(VectorShuffleFeedingShuffle()); rules_[SpvOpImageSampleImplicitLod].push_back(UpdateImageOperands()); diff --git a/source/opt/function.cpp b/source/opt/function.cpp index 38c66951..bb51df3f 100644 --- a/source/opt/function.cpp +++ b/source/opt/function.cpp @@ -270,5 +270,13 @@ std::string Function::PrettyPrint(uint32_t options) const { }); return str.str(); } + +void Function::ReorderBasicBlocksInStructuredOrder() { + std::list order; + IRContext* context = this->def_inst_->context(); + context->cfg()->ComputeStructuredOrder(this, blocks_[0].get(), &order); + ReorderBasicBlocks(order.begin(), order.end()); +} + } // namespace opt } // namespace spvtools diff --git a/source/opt/function.h b/source/opt/function.h index 9e1c7274..146cbe34 100644 --- a/source/opt/function.h +++ b/source/opt/function.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -177,7 +178,22 @@ class Function { // debuggers. void Dump() const; + // Returns true is a function declaration and not a function definition. + bool IsDeclaration() { return begin() == end(); } + + // Reorders the basic blocks in the function to match the structured order. + void ReorderBasicBlocksInStructuredOrder(); + private: + // Reorders the basic blocks in the function to match the order given by the + // range |{begin,end}|. The range must contain every basic block in the + // function, and no extras. + template + void ReorderBasicBlocks(It begin, It end); + + template + bool ContainsAllBlocksInTheFunction(It begin, It end); + // The OpFunction instruction that begins the definition of this function. std::unique_ptr def_inst_; // All parameters to this function. @@ -259,6 +275,34 @@ inline void Function::AddNonSemanticInstruction( non_semantic_.emplace_back(std::move(non_semantic)); } +template +void Function::ReorderBasicBlocks(It begin, It end) { + // Asserts to make sure every node in the function is in new_order. + assert(ContainsAllBlocksInTheFunction(begin, end)); + + // We have a pointer to all the elements in order, so we can release all + // pointers in |block_|, and then create the new unique pointers from |{begin, + // end}|. + std::for_each(blocks_.begin(), blocks_.end(), + [](std::unique_ptr& bb) { bb.release(); }); + std::transform(begin, end, blocks_.begin(), [](BasicBlock* bb) { + return std::unique_ptr(bb); + }); +} + +template +bool Function::ContainsAllBlocksInTheFunction(It begin, It end) { + std::unordered_multiset range(begin, end); + if (range.size() != blocks_.size()) { + return false; + } + + for (auto& bb : blocks_) { + if (range.count(bb.get()) == 0) return false; + } + return true; +} + } // namespace opt } // namespace spvtools diff --git a/source/opt/graphics_robust_access_pass.cpp b/source/opt/graphics_robust_access_pass.cpp index 46483e48..4652d72d 100644 --- a/source/opt/graphics_robust_access_pass.cpp +++ b/source/opt/graphics_robust_access_pass.cpp @@ -13,7 +13,7 @@ // limitations under the License. // This pass injects code in a graphics shader to implement guarantees -// satisfying Vulkan's robustBufferAcces rules. Robust access rules permit +// satisfying Vulkan's robustBufferAccess rules. Robust access rules permit // an out-of-bounds access to be redirected to an access of the same type // (load, store, etc.) but within the same root object. // @@ -74,7 +74,7 @@ // Pointers are always (correctly) typed and so the address and number of // consecutive locations are fully determined by the pointer. // -// - A pointer value orginates as one of few cases: +// - A pointer value originates as one of few cases: // // - OpVariable for an interface object or an array of them: image, // buffer (UBO or SSBO), sampler, sampled-image, push-constant, input @@ -272,10 +272,11 @@ void GraphicsRobustAccessPass::ClampIndicesForAccessChain( // Replaces one of the OpAccessChain index operands with a new value. // Updates def-use analysis. - auto replace_index = [&inst, def_use_mgr](uint32_t operand_index, - Instruction* new_value) { + auto replace_index = [this, &inst, def_use_mgr](uint32_t operand_index, + Instruction* new_value) { inst.SetOperand(operand_index, {new_value->result_id()}); def_use_mgr->AnalyzeInstUse(&inst); + module_status_.modified = true; return SPV_SUCCESS; }; @@ -558,21 +559,17 @@ uint32_t GraphicsRobustAccessPass::GetGlslInsts() { if (module_status_.glsl_insts_id == 0) { // This string serves double-duty as raw data for a string and for a vector // of 32-bit words - const char glsl[] = "GLSL.std.450\0\0\0\0"; - const size_t glsl_str_byte_len = 16; + const char glsl[] = "GLSL.std.450"; // Use an existing import if we can. for (auto& inst : context()->module()->ext_inst_imports()) { - const auto& name_words = inst.GetInOperand(0).words; - if (0 == std::strncmp(reinterpret_cast(name_words.data()), - glsl, glsl_str_byte_len)) { + if (inst.GetInOperand(0).AsString() == glsl) { module_status_.glsl_insts_id = inst.result_id(); } } if (module_status_.glsl_insts_id == 0) { // Make a new import instruction. module_status_.glsl_insts_id = TakeNextId(); - std::vector words(glsl_str_byte_len / sizeof(uint32_t)); - std::memcpy(words.data(), glsl, glsl_str_byte_len); + std::vector words = spvtools::utils::MakeVector(glsl); auto import_inst = MakeUnique( context(), SpvOpExtInstImport, 0, module_status_.glsl_insts_id, std::initializer_list{ @@ -961,7 +958,7 @@ spv_result_t GraphicsRobustAccessPass::ClampCoordinateForImageTexelPointer( constant_mgr->GetDefiningInstruction(component_0)->result_id(); // If the image is a cube array, then the last component of the queried - // size is the layer count. In the query, we have to accomodate folding + // size is the layer count. In the query, we have to accommodate folding // in the face index ranging from 0 through 5. The inclusive upper bound // on the third coordinate therefore is multiplied by 6. auto* query_size_including_faces = query_size; diff --git a/source/opt/graphics_robust_access_pass.h b/source/opt/graphics_robust_access_pass.h index 6fc692c1..8f4c9dc7 100644 --- a/source/opt/graphics_robust_access_pass.h +++ b/source/opt/graphics_robust_access_pass.h @@ -111,7 +111,7 @@ class GraphicsRobustAccessPass : public Pass { Instruction* max, Instruction* where); // Returns a new instruction which evaluates to the length the runtime array - // referenced by the access chain at the specfied index. The instruction is + // referenced by the access chain at the specified index. The instruction is // inserted before the access chain instruction. Returns a null pointer in // some cases if assumptions are violated (rather than asserting out). opt::Instruction* MakeRuntimeArrayLengthInst(Instruction* access_chain, diff --git a/source/opt/if_conversion.cpp b/source/opt/if_conversion.cpp index 4284069a..1232796e 100644 --- a/source/opt/if_conversion.cpp +++ b/source/opt/if_conversion.cpp @@ -129,6 +129,7 @@ Pass::Status IfConversion::Process() { Instruction* select = builder.AddSelect(phi->type_id(), condition, true_value->result_id(), false_value->result_id()); + context()->get_def_use_mgr()->AnalyzeInstDefUse(select); select->UpdateDebugInfoFrom(phi); context()->ReplaceAllUsesWith(phi->result_id(), select->result_id()); to_kill.push_back(phi); @@ -159,6 +160,11 @@ bool IfConversion::CheckBlock(BasicBlock* block, DominatorAnalysis* dominators, BasicBlock* inc1 = context()->get_instr_block(preds[1]); if (dominators->Dominates(block, inc1)) return false; + if (inc0 == inc1) { + // If the predecessor blocks are the same, then there is only 1 value for + // the OpPhi. Other transformation should be able to simplify that. + return false; + } // All phis will have the same common dominator, so cache the result // for this block. If there is no common dominator, then we cannot transform // any phi in this basic block. @@ -168,6 +174,8 @@ bool IfConversion::CheckBlock(BasicBlock* block, DominatorAnalysis* dominators, if (branch->opcode() != SpvOpBranchConditional) return false; auto merge = (*common)->GetMergeInst(); if (!merge || merge->opcode() != SpvOpSelectionMerge) return false; + if (merge->GetSingleWordInOperand(1) == SpvSelectionControlDontFlattenMask) + return false; if ((*common)->MergeBlockIdIfAny() != block->id()) return false; return true; diff --git a/source/opt/inline_exhaustive_pass.cpp b/source/opt/inline_exhaustive_pass.cpp index f24f744d..bef45017 100644 --- a/source/opt/inline_exhaustive_pass.cpp +++ b/source/opt/inline_exhaustive_pass.cpp @@ -65,7 +65,7 @@ Pass::Status InlineExhaustivePass::ProcessImpl() { status = CombineStatus(status, InlineExhaustive(fp)); return false; }; - context()->ProcessEntryPointCallTree(pfn); + context()->ProcessReachableCallTree(pfn); return status; } diff --git a/source/opt/inline_opaque_pass.cpp b/source/opt/inline_opaque_pass.cpp index 6ccaf908..fe9c6799 100644 --- a/source/opt/inline_opaque_pass.cpp +++ b/source/opt/inline_opaque_pass.cpp @@ -105,7 +105,7 @@ Pass::Status InlineOpaquePass::ProcessImpl() { status = CombineStatus(status, InlineOpaque(fp)); return false; }; - context()->ProcessEntryPointCallTree(pfn); + context()->ProcessReachableCallTree(pfn); return status; } diff --git a/source/opt/inline_pass.cpp b/source/opt/inline_pass.cpp index 8159ebf7..e14516f7 100644 --- a/source/opt/inline_pass.cpp +++ b/source/opt/inline_pass.cpp @@ -92,7 +92,7 @@ void InlinePass::AddStore(uint32_t ptr_id, uint32_t val_id, {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}}, {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {val_id}}})); if (line_inst != nullptr) { - newStore->dbg_line_insts().push_back(*line_inst); + newStore->AddDebugLine(line_inst); } newStore->SetDebugScope(dbg_scope); (*block_ptr)->AddInstruction(std::move(newStore)); @@ -106,7 +106,7 @@ void InlinePass::AddLoad(uint32_t type_id, uint32_t resultId, uint32_t ptr_id, new Instruction(context(), SpvOpLoad, type_id, resultId, {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}}})); if (line_inst != nullptr) { - newLoad->dbg_line_insts().push_back(*line_inst); + newLoad->AddDebugLine(line_inst); } newLoad->SetDebugScope(dbg_scope); (*block_ptr)->AddInstruction(std::move(newLoad)); @@ -158,8 +158,8 @@ bool InlinePass::CloneAndMapLocals( auto callee_block_itr = calleeFn->begin(); auto callee_var_itr = callee_block_itr->begin(); while (callee_var_itr->opcode() == SpvOp::SpvOpVariable || - callee_var_itr->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugDeclare) { + callee_var_itr->GetCommonDebugOpcode() == + CommonDebugInfoDebugDeclare) { if (callee_var_itr->opcode() != SpvOp::SpvOpVariable) { ++callee_var_itr; continue; @@ -300,8 +300,7 @@ InstructionList::iterator InlinePass::AddStoresForVariableInitializers( UptrVectorIterator callee_first_block_itr) { auto callee_itr = callee_first_block_itr->begin(); while (callee_itr->opcode() == SpvOp::SpvOpVariable || - callee_itr->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugDeclare) { + callee_itr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { if (callee_itr->opcode() == SpvOp::SpvOpVariable && callee_itr->NumInOperands() == 2) { assert(callee2caller.count(callee_itr->result_id()) && @@ -315,8 +314,7 @@ InstructionList::iterator InlinePass::AddStoresForVariableInitializers( context()->get_debug_info_mgr()->BuildDebugScope( callee_itr->GetDebugScope(), inlined_at_ctx)); } - if (callee_itr->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugDeclare) { + if (callee_itr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { InlineSingleInstruction( callee2caller, new_blk_ptr->get(), &*callee_itr, context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( @@ -405,6 +403,14 @@ bool InlinePass::InlineEntryBlock( callee2caller, inlined_at_ctx, new_blk_ptr, callee_first_block); while (callee_inst_itr != callee_first_block->end()) { + // Don't inline function definition links, the calling function is not a + // definition. + if (callee_inst_itr->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) { + ++callee_inst_itr; + continue; + } + if (!InlineSingleInstruction( callee2caller, new_blk_ptr->get(), &*callee_inst_itr, context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( @@ -435,6 +441,11 @@ std::unique_ptr InlinePass::InlineBasicBlocks( auto tail_inst_itr = callee_block_itr->end(); for (auto inst_itr = callee_block_itr->begin(); inst_itr != tail_inst_itr; ++inst_itr) { + // Don't inline function definition links, the calling function is not a + // definition + if (inst_itr->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) + continue; if (!InlineSingleInstruction( callee2caller, new_blk_ptr.get(), &*inst_itr, context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( @@ -497,6 +508,37 @@ void InlinePass::MoveLoopMergeInstToFirstBlock( delete &*loop_merge_itr; } +void InlinePass::UpdateSingleBlockLoopContinueTarget( + uint32_t new_id, std::vector>* new_blocks) { + auto& header = new_blocks->front(); + auto* merge_inst = header->GetLoopMergeInst(); + + // The back-edge block is split at the branch to create a new back-edge + // block. The old block is modified to branch to the new block. The loop + // merge instruction is updated to declare the new block as the continue + // target. This has the effect of changing the loop from being a large + // continue construct and an empty loop construct to being a loop with a loop + // construct and a trivial continue construct. This change is made to satisfy + // structural dominance. + + // Add the new basic block. + std::unique_ptr new_block = + MakeUnique(NewLabel(new_id)); + auto& old_backedge = new_blocks->back(); + auto old_branch = old_backedge->tail(); + + // Move the old back edge into the new block. + std::unique_ptr br(&*old_branch); + new_block->AddInstruction(std::move(br)); + + // Add a branch to the new block from the old back-edge block. + AddBranch(new_id, &old_backedge); + new_blocks->push_back(std::move(new_block)); + + // Update the loop's continue target to the new block. + merge_inst->SetInOperand(1u, {new_id}); +} + bool InlinePass::GenInlineCode( std::vector>* new_blocks, std::vector>* new_vars, @@ -628,9 +670,19 @@ bool InlinePass::GenInlineCode( // Finalize inline code. new_blocks->push_back(std::move(new_blk_ptr)); - if (caller_is_loop_header && (new_blocks->size() > 1)) + if (caller_is_loop_header && (new_blocks->size() > 1)) { MoveLoopMergeInstToFirstBlock(new_blocks); + // If the loop was a single basic block previously, update it's structure. + auto& header = new_blocks->front(); + auto* merge_inst = header->GetLoopMergeInst(); + if (merge_inst->GetSingleWordInOperand(1u) == header->id()) { + auto new_id = context()->TakeNextId(); + if (new_id == 0) return false; + UpdateSingleBlockLoopContinueTarget(new_id, new_blocks); + } + } + // Update block map given replacement blocks. for (auto& blk : *new_blocks) { id2block_[blk->id()] = &*blk; @@ -742,22 +794,25 @@ bool InlinePass::IsInlinableFunction(Function* func) { return false; } - // Do not inline functions with an OpKill if they are called from a continue - // construct. If it is inlined into a continue construct it will generate - // invalid code. + // Do not inline functions with an abort instruction if they are called from a + // continue construct. If it is inlined into a continue construct the backedge + // will no longer post-dominate the continue target, which is invalid. An + // `OpUnreachable` is acceptable because it will not change post-dominance if + // it is statically unreachable. bool func_is_called_from_continue = funcs_called_from_continue_.count(func->result_id()) != 0; - if (func_is_called_from_continue && ContainsKillOrTerminateInvocation(func)) { + if (func_is_called_from_continue && ContainsAbortOtherThanUnreachable(func)) { return false; } return true; } -bool InlinePass::ContainsKillOrTerminateInvocation(Function* func) const { +bool InlinePass::ContainsAbortOtherThanUnreachable(Function* func) const { return !func->WhileEachInst([](Instruction* inst) { - return !spvOpcodeTerminatesExecution(inst->opcode()); + return inst->opcode() == SpvOpUnreachable || + !spvOpcodeIsAbort(inst->opcode()); }); } diff --git a/source/opt/inline_pass.h b/source/opt/inline_pass.h index 9a5429ba..d29c1e07 100644 --- a/source/opt/inline_pass.h +++ b/source/opt/inline_pass.h @@ -139,9 +139,9 @@ class InlinePass : public Pass { // Return true if |func| is a function that can be inlined. bool IsInlinableFunction(Function* func); - // Returns true if |func| contains an OpKill or OpTerminateInvocation - // instruction. - bool ContainsKillOrTerminateInvocation(Function* func) const; + // Returns true if |func| contains an abort instruction that is not an + // `OpUnreachable` instruction. + bool ContainsAbortOtherThanUnreachable(Function* func) const; // Update phis in succeeding blocks to point to new last block void UpdateSucceedingPhis( @@ -235,6 +235,12 @@ class InlinePass : public Pass { // Move the OpLoopMerge from the last block back to the first. void MoveLoopMergeInstToFirstBlock( std::vector>* new_blocks); + + // Update the structure of single block loops so that the inlined code ends + // up in the loop construct and a new continue target is added to satisfy + // structural dominance. + void UpdateSingleBlockLoopContinueTarget( + uint32_t new_id, std::vector>* new_blocks); }; } // namespace opt diff --git a/source/opt/inst_bindless_check_pass.cpp b/source/opt/inst_bindless_check_pass.cpp index 5607239a..c2c5d6cb 100644 --- a/source/opt/inst_bindless_check_pass.cpp +++ b/source/opt/inst_bindless_check_pass.cpp @@ -39,13 +39,6 @@ static const int kSpvTypeImageMS = 4; static const int kSpvTypeImageSampled = 5; } // anonymous namespace -// Avoid unused variable warning/error on Linux -#ifndef NDEBUG -#define USE_ASSERT(x) assert(x) -#else -#define USE_ASSERT(x) ((void)(x)) -#endif - namespace spvtools { namespace opt { diff --git a/source/opt/inst_bindless_check_pass.h b/source/opt/inst_bindless_check_pass.h index cd961805..e6e6ef4f 100644 --- a/source/opt/inst_bindless_check_pass.h +++ b/source/opt/inst_bindless_check_pass.h @@ -147,11 +147,11 @@ class InstBindlessCheckPass : public InstrumentPass { uint32_t GenLastByteIdx(RefAnalysis* ref, InstructionBuilder* builder); // Clone original image computation starting at |image_id| into |builder|. - // This may generate more than one instruction if neccessary. + // This may generate more than one instruction if necessary. uint32_t CloneOriginalImage(uint32_t image_id, InstructionBuilder* builder); // Clone original original reference encapsulated by |ref| into |builder|. - // This may generate more than one instruction if neccessary. + // This may generate more than one instruction if necessary. uint32_t CloneOriginalReference(RefAnalysis* ref, InstructionBuilder* builder); diff --git a/source/opt/inst_buff_addr_check_pass.cpp b/source/opt/inst_buff_addr_check_pass.cpp index 06acc7ea..3318f88f 100644 --- a/source/opt/inst_buff_addr_check_pass.cpp +++ b/source/opt/inst_buff_addr_check_pass.cpp @@ -130,13 +130,48 @@ void InstBuffAddrCheckPass::GenCheckCode( context()->KillInst(ref_inst); } +uint32_t InstBuffAddrCheckPass::GetTypeAlignment(uint32_t type_id) { + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + switch (type_inst->opcode()) { + case SpvOpTypeFloat: + case SpvOpTypeInt: + case SpvOpTypeVector: + return GetTypeLength(type_id); + case SpvOpTypeMatrix: + return GetTypeAlignment(type_inst->GetSingleWordInOperand(0)); + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + return GetTypeAlignment(type_inst->GetSingleWordInOperand(0)); + case SpvOpTypeStruct: { + uint32_t max = 0; + type_inst->ForEachInId([&max, this](const uint32_t* iid) { + uint32_t alignment = GetTypeAlignment(*iid); + max = (alignment > max) ? alignment : max; + }); + return max; + } + case SpvOpTypePointer: + assert(type_inst->GetSingleWordInOperand(0) == + SpvStorageClassPhysicalStorageBufferEXT && + "unexpected pointer type"); + return 8u; + default: + assert(false && "unexpected type"); + return 0; + } +} + uint32_t InstBuffAddrCheckPass::GetTypeLength(uint32_t type_id) { Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); switch (type_inst->opcode()) { case SpvOpTypeFloat: case SpvOpTypeInt: return type_inst->GetSingleWordInOperand(0) / 8u; - case SpvOpTypeVector: + case SpvOpTypeVector: { + uint32_t raw_cnt = type_inst->GetSingleWordInOperand(1); + uint32_t adj_cnt = (raw_cnt == 3u) ? 4u : raw_cnt; + return adj_cnt * GetTypeLength(type_inst->GetSingleWordInOperand(0)); + } case SpvOpTypeMatrix: return type_inst->GetSingleWordInOperand(1) * GetTypeLength(type_inst->GetSingleWordInOperand(0)); @@ -145,8 +180,29 @@ uint32_t InstBuffAddrCheckPass::GetTypeLength(uint32_t type_id) { SpvStorageClassPhysicalStorageBufferEXT && "unexpected pointer type"); return 8u; + case SpvOpTypeArray: { + uint32_t const_id = type_inst->GetSingleWordInOperand(1); + Instruction* const_inst = get_def_use_mgr()->GetDef(const_id); + uint32_t cnt = const_inst->GetSingleWordInOperand(0); + return cnt * GetTypeLength(type_inst->GetSingleWordInOperand(0)); + } + case SpvOpTypeStruct: { + uint32_t len = 0; + type_inst->ForEachInId([&len, this](const uint32_t* iid) { + // Align struct length + uint32_t alignment = GetTypeAlignment(*iid); + uint32_t mod = len % alignment; + uint32_t diff = (mod != 0) ? alignment - mod : 0; + len += diff; + // Increment struct length by component length + uint32_t comp_len = GetTypeLength(*iid); + len += comp_len; + }); + return len; + } + case SpvOpTypeRuntimeArray: default: - assert(false && "unexpected buffer reference type"); + assert(false && "unexpected type"); return 0; } } @@ -337,6 +393,8 @@ uint32_t InstBuffAddrCheckPass::GetSearchAndTestFuncId() { get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst); input_func->SetFunctionEnd(std::move(func_end_inst)); context()->AddFunction(std::move(input_func)); + context()->AddDebug2Inst( + NewGlobalName(search_test_func_id_, "search_and_test")); } return search_test_func_id_; } diff --git a/source/opt/inst_buff_addr_check_pass.h b/source/opt/inst_buff_addr_check_pass.h index ec7bb684..fb43c397 100644 --- a/source/opt/inst_buff_addr_check_pass.h +++ b/source/opt/inst_buff_addr_check_pass.h @@ -28,7 +28,9 @@ namespace opt { // external design of this class may change as the layer evolves. class InstBuffAddrCheckPass : public InstrumentPass { public: - // Preferred interface + // For test harness only + InstBuffAddrCheckPass() : InstrumentPass(7, 23, kInstValidationIdBuffAddr) {} + // For all other interfaces InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id) : InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr) {} @@ -37,11 +39,15 @@ class InstBuffAddrCheckPass : public InstrumentPass { // See optimizer.hpp for pass user documentation. Status Process() override; - const char* name() const override { return "inst-bindless-check-pass"; } + const char* name() const override { return "inst-buff-addr-check-pass"; } private: - // Return byte length of type |type_id|. Must be int, float, vector, matrix - // or physical pointer. + // Return byte alignment of type |type_id|. Must be int, float, vector, + // matrix, struct, array or physical pointer. Uses std430 alignment. + uint32_t GetTypeAlignment(uint32_t type_id); + + // Return byte length of type |type_id|. Must be int, float, vector, matrix, + // struct, array or physical pointer. Uses std430 alignment and sizes. uint32_t GetTypeLength(uint32_t type_id); // Add |type_id| param to |input_func| and add id to |param_vec|. diff --git a/source/opt/inst_debug_printf_pass.cpp b/source/opt/inst_debug_printf_pass.cpp index c0e6bc3f..4218138f 100644 --- a/source/opt/inst_debug_printf_pass.cpp +++ b/source/opt/inst_debug_printf_pass.cpp @@ -16,6 +16,7 @@ #include "inst_debug_printf_pass.h" +#include "source/util/string_utils.h" #include "spirv/unified1/NonSemanticDebugPrintf.h" namespace spvtools { @@ -231,10 +232,8 @@ Pass::Status InstDebugPrintfPass::ProcessImpl() { bool non_sem_set_seen = false; for (auto c_itr = context()->module()->ext_inst_import_begin(); c_itr != context()->module()->ext_inst_import_end(); ++c_itr) { - const char* set_name = - reinterpret_cast(&c_itr->GetInOperand(0).words[0]); - const char* non_sem_str = "NonSemantic."; - if (!strncmp(set_name, non_sem_str, strlen(non_sem_str))) { + const std::string set_name = c_itr->GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(set_name, "NonSemantic.")) { non_sem_set_seen = true; break; } @@ -242,9 +241,8 @@ Pass::Status InstDebugPrintfPass::ProcessImpl() { if (!non_sem_set_seen) { for (auto c_itr = context()->module()->extension_begin(); c_itr != context()->module()->extension_end(); ++c_itr) { - const char* ext_name = - reinterpret_cast(&c_itr->GetInOperand(0).words[0]); - if (!strcmp(ext_name, "SPV_KHR_non_semantic_info")) { + const std::string ext_name = c_itr->GetInOperand(0).AsString(); + if (ext_name == "SPV_KHR_non_semantic_info") { context()->KillInst(&*c_itr); break; } diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp index 1054a203..e775a992 100644 --- a/source/opt/instruction.cpp +++ b/source/opt/instruction.cpp @@ -30,9 +30,11 @@ namespace { const uint32_t kTypeImageDimIndex = 1; const uint32_t kLoadBaseIndex = 0; const uint32_t kPointerTypeStorageClassIndex = 0; +const uint32_t kVariableStorageClassIndex = 0; const uint32_t kTypeImageSampledIndex = 5; -// Constants for OpenCL.DebugInfo.100 extension instructions. +// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100 +// extension instructions. const uint32_t kExtInstSetIdInIdx = 0; const uint32_t kExtInstInstructionInIdx = 1; const uint32_t kDebugScopeNumWords = 7; @@ -64,27 +66,28 @@ Instruction::Instruction(IRContext* c, SpvOp op) Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst, std::vector&& dbg_line) - : context_(c), + : utils::IntrusiveNodeBase(), + context_(c), opcode_(static_cast(inst.opcode)), has_type_id_(inst.type_id != 0), has_result_id_(inst.result_id != 0), unique_id_(c->TakeNextUniqueId()), dbg_line_insts_(std::move(dbg_line)), dbg_scope_(kNoDebugScope, kNoInlinedAt) { - assert((!IsDebugLineInst(opcode_) || dbg_line.empty()) && - "Op(No)Line attaching to Op(No)Line found"); for (uint32_t i = 0; i < inst.num_operands; ++i) { const auto& current_payload = inst.operands[i]; - std::vector words( - inst.words + current_payload.offset, + operands_.emplace_back( + current_payload.type, inst.words + current_payload.offset, inst.words + current_payload.offset + current_payload.num_words); - operands_.emplace_back(current_payload.type, std::move(words)); } + assert((!IsLineInst() || dbg_line.empty()) && + "Op(No)Line attaching to Op(No)Line found"); } Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst, const DebugScope& dbg_scope) - : context_(c), + : utils::IntrusiveNodeBase(), + context_(c), opcode_(static_cast(inst.opcode)), has_type_id_(inst.type_id != 0), has_result_id_(inst.result_id != 0), @@ -92,10 +95,9 @@ Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst, dbg_scope_(dbg_scope) { for (uint32_t i = 0; i < inst.num_operands; ++i) { const auto& current_payload = inst.operands[i]; - std::vector words( - inst.words + current_payload.offset, + operands_.emplace_back( + current_payload.type, inst.words + current_payload.offset, inst.words + current_payload.offset + current_payload.num_words); - operands_.emplace_back(current_payload.type, std::move(words)); } } @@ -122,6 +124,7 @@ Instruction::Instruction(IRContext* c, SpvOp op, uint32_t ty_id, Instruction::Instruction(Instruction&& that) : utils::IntrusiveNodeBase(), + context_(that.context_), opcode_(that.opcode_), has_type_id_(that.has_type_id_), has_result_id_(that.has_result_id_), @@ -135,6 +138,7 @@ Instruction::Instruction(Instruction&& that) } Instruction& Instruction::operator=(Instruction&& that) { + context_ = that.context_; opcode_ = that.opcode_; has_type_id_ = that.has_type_id_; has_result_id_ = that.has_result_id_; @@ -153,6 +157,10 @@ Instruction* Instruction::Clone(IRContext* c) const { clone->unique_id_ = c->TakeNextUniqueId(); clone->operands_ = operands_; clone->dbg_line_insts_ = dbg_line_insts_; + for (auto& i : clone->dbg_line_insts_) { + i.unique_id_ = c->TakeNextUniqueId(); + if (i.IsDebugLineInst()) i.SetResultId(c->TakeNextId()); + } clone->dbg_scope_ = dbg_scope_; return clone; } @@ -394,6 +402,21 @@ bool Instruction::IsVulkanStorageBuffer() const { return false; } +bool Instruction::IsVulkanStorageBufferVariable() const { + if (opcode() != SpvOpVariable) { + return false; + } + + uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex); + if (storage_class == SpvStorageClassStorageBuffer || + storage_class == SpvStorageClassUniform) { + Instruction* var_type = context()->get_def_use_mgr()->GetDef(type_id()); + return var_type != nullptr && var_type->IsVulkanStorageBuffer(); + } + + return false; +} + bool Instruction::IsVulkanUniformBuffer() const { if (opcode() != SpvOpTypePointer) { return false; @@ -481,32 +504,12 @@ bool Instruction::IsReadOnlyPointerKernel() const { return storage_class == SpvStorageClassUniformConstant; } -uint32_t Instruction::GetTypeComponent(uint32_t element) const { - uint32_t subtype = 0; - switch (opcode()) { - case SpvOpTypeStruct: - subtype = GetSingleWordInOperand(element); - break; - case SpvOpTypeArray: - case SpvOpTypeRuntimeArray: - case SpvOpTypeVector: - case SpvOpTypeMatrix: - // These types all have uniform subtypes. - subtype = GetSingleWordInOperand(0u); - break; - default: - break; - } - - return subtype; -} - void Instruction::UpdateLexicalScope(uint32_t scope) { dbg_scope_.SetLexicalScope(scope); for (auto& i : dbg_line_insts_) { i.dbg_scope_.SetLexicalScope(scope); } - if (!IsDebugLineInst(opcode()) && + if (!IsLineInst() && context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) { context()->get_debug_info_mgr()->AnalyzeDebugInst(this); } @@ -517,24 +520,61 @@ void Instruction::UpdateDebugInlinedAt(uint32_t new_inlined_at) { for (auto& i : dbg_line_insts_) { i.dbg_scope_.SetInlinedAt(new_inlined_at); } - if (!IsDebugLineInst(opcode()) && + if (!IsLineInst() && context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) { context()->get_debug_info_mgr()->AnalyzeDebugInst(this); } } +void Instruction::ClearDbgLineInsts() { + if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) { + auto def_use_mgr = context()->get_def_use_mgr(); + for (auto& l_inst : dbg_line_insts_) def_use_mgr->ClearInst(&l_inst); + } + clear_dbg_line_insts(); +} + void Instruction::UpdateDebugInfoFrom(const Instruction* from) { if (from == nullptr) return; - clear_dbg_line_insts(); + ClearDbgLineInsts(); if (!from->dbg_line_insts().empty()) - dbg_line_insts().push_back(from->dbg_line_insts().back()); + AddDebugLine(&from->dbg_line_insts().back()); SetDebugScope(from->GetDebugScope()); - if (!IsDebugLineInst(opcode()) && + if (!IsLineInst() && context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) { context()->get_debug_info_mgr()->AnalyzeDebugInst(this); } } +void Instruction::AddDebugLine(const Instruction* inst) { + dbg_line_insts_.push_back(*inst); + dbg_line_insts_.back().unique_id_ = context()->TakeNextUniqueId(); + if (inst->IsDebugLineInst()) + dbg_line_insts_.back().SetResultId(context_->TakeNextId()); + if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(&dbg_line_insts_.back()); +} + +bool Instruction::IsDebugLineInst() const { + NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode(); + return ((ext_opt == NonSemanticShaderDebugInfo100DebugLine) || + (ext_opt == NonSemanticShaderDebugInfo100DebugNoLine)); +} + +bool Instruction::IsLineInst() const { return IsLine() || IsNoLine(); } + +bool Instruction::IsLine() const { + if (opcode() == SpvOpLine) return true; + NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode(); + return ext_opt == NonSemanticShaderDebugInfo100DebugLine; +} + +bool Instruction::IsNoLine() const { + if (opcode() == SpvOpNoLine) return true; + NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode(); + return ext_opt == NonSemanticShaderDebugInfo100DebugNoLine; +} + Instruction* Instruction::InsertBefore(std::unique_ptr&& inst) { inst.get()->InsertBefore(this); return inst.release(); @@ -618,6 +658,53 @@ OpenCLDebugInfo100Instructions Instruction::GetOpenCL100DebugOpcode() const { GetSingleWordInOperand(kExtInstInstructionInIdx)); } +NonSemanticShaderDebugInfo100Instructions Instruction::GetShader100DebugOpcode() + const { + if (opcode() != SpvOpExtInst) { + return NonSemanticShaderDebugInfo100InstructionsMax; + } + + if (!context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) { + return NonSemanticShaderDebugInfo100InstructionsMax; + } + + if (GetSingleWordInOperand(kExtInstSetIdInIdx) != + context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) { + return NonSemanticShaderDebugInfo100InstructionsMax; + } + + uint32_t opcode = GetSingleWordInOperand(kExtInstInstructionInIdx); + if (opcode >= NonSemanticShaderDebugInfo100InstructionsMax) { + return NonSemanticShaderDebugInfo100InstructionsMax; + } + + return NonSemanticShaderDebugInfo100Instructions(opcode); +} + +CommonDebugInfoInstructions Instruction::GetCommonDebugOpcode() const { + if (opcode() != SpvOpExtInst) { + return CommonDebugInfoInstructionsMax; + } + + const uint32_t opencl_set_id = + context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo(); + const uint32_t shader_set_id = + context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo(); + + if (!opencl_set_id && !shader_set_id) { + return CommonDebugInfoInstructionsMax; + } + + const uint32_t used_set_id = GetSingleWordInOperand(kExtInstSetIdInIdx); + + if (used_set_id != opencl_set_id && used_set_id != shader_set_id) { + return CommonDebugInfoInstructionsMax; + } + + return CommonDebugInfoInstructions( + GetSingleWordInOperand(kExtInstInstructionInIdx)); +} + bool Instruction::IsValidBaseImage() const { uint32_t tid = type_id(); if (tid == 0) { @@ -940,10 +1027,10 @@ void DebugScope::ToBinary(uint32_t type_id, uint32_t result_id, uint32_t ext_set, std::vector* binary) const { uint32_t num_words = kDebugScopeNumWords; - OpenCLDebugInfo100Instructions dbg_opcode = OpenCLDebugInfo100DebugScope; + CommonDebugInfoInstructions dbg_opcode = CommonDebugInfoDebugScope; if (GetLexicalScope() == kNoDebugScope) { num_words = kDebugNoScopeNumWords; - dbg_opcode = OpenCLDebugInfo100DebugNoScope; + dbg_opcode = CommonDebugInfoDebugNoScope; } else if (GetInlinedAt() == kNoInlinedAt) { num_words = kDebugScopeNumWordsWithoutInlinedAt; } diff --git a/source/opt/instruction.h b/source/opt/instruction.h index 3e557dd7..e79c6289 100644 --- a/source/opt/instruction.h +++ b/source/opt/instruction.h @@ -22,7 +22,10 @@ #include #include +#include "NonSemanticShaderDebugInfo100.h" #include "OpenCLDebugInfo100.h" +#include "source/binary.h" +#include "source/common_debug_info.h" #include "source/latest_version_glsl_std_450_header.h" #include "source/latest_version_spirv_header.h" #include "source/opcode.h" @@ -30,6 +33,7 @@ #include "source/opt/reflect.h" #include "source/util/ilist_node.h" #include "source/util/small_vector.h" +#include "source/util/string_utils.h" #include "spirv-tools/libspirv.h" const uint32_t kNoDebugScope = 0; @@ -80,21 +84,32 @@ struct Operand { Operand(spv_operand_type_t t, const OperandData& w) : type(t), words(w) {} + template + Operand(spv_operand_type_t t, InputIt firstOperandData, + InputIt lastOperandData) + : type(t), words(firstOperandData, lastOperandData) {} + spv_operand_type_t type; // Type of this logical operand. OperandData words; // Binary segments of this logical operand. - // Returns a string operand as a C-style string. - const char* AsCString() const { - assert(type == SPV_OPERAND_TYPE_LITERAL_STRING); - return reinterpret_cast(words.data()); + uint32_t AsId() const { + assert(spvIsIdType(type)); + assert(words.size() == 1); + return words[0]; } // Returns a string operand as a std::string. - std::string AsString() const { return AsCString(); } + std::string AsString() const { + assert(type == SPV_OPERAND_TYPE_LITERAL_STRING); + return spvtools::utils::MakeString(words); + } // Returns a literal integer operand as a uint64_t uint64_t AsLiteralUint64() const { - assert(type == SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER); + assert(type == SPV_OPERAND_TYPE_LITERAL_INTEGER || + type == SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER || + type == SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER || + type == SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER); assert(1 <= words.size()); assert(words.size() <= 2); uint64_t result = 0; @@ -121,7 +136,7 @@ inline bool operator!=(const Operand& o1, const Operand& o2) { } // This structure is used to represent a DebugScope instruction from -// the OpenCL.100.DebugInfo extened instruction set. Note that we can +// the OpenCL.100.DebugInfo extended instruction set. Note that we can // ignore the result id of DebugScope instruction because it is not // used for anything. We do not keep it to reduce the size of // structure. @@ -250,11 +265,6 @@ class Instruction : public utils::IntrusiveNodeBase { // Clear line-related debug instructions attached to this instruction. void clear_dbg_line_insts() { dbg_line_insts_.clear(); } - // Set line-related debug instructions. - void set_dbg_line_insts(const std::vector& lines) { - dbg_line_insts_ = lines; - } - // Same semantics as in the base class except the list the InstructionList // containing |pos| will now assume ownership of |this|. // inline void MoveBefore(Instruction* pos); @@ -298,14 +308,28 @@ class Instruction : public utils::IntrusiveNodeBase { inline void SetInOperands(OperandList&& new_operands); // Sets the result type id. inline void SetResultType(uint32_t ty_id); + inline bool HasResultType() const { return has_type_id_; } // Sets the result id inline void SetResultId(uint32_t res_id); inline bool HasResultId() const { return has_result_id_; } // Sets DebugScope. inline void SetDebugScope(const DebugScope& scope); inline const DebugScope& GetDebugScope() const { return dbg_scope_; } + // Add debug line inst. Renew result id if Debug[No]Line + void AddDebugLine(const Instruction* inst); // Updates DebugInlinedAt of DebugScope and OpLine. void UpdateDebugInlinedAt(uint32_t new_inlined_at); + // Clear line-related debug instructions attached to this instruction + // along with def-use entries. + void ClearDbgLineInsts(); + // Return true if Shader100:Debug[No]Line + bool IsDebugLineInst() const; + // Return true if Op[No]Line or Shader100:Debug[No]Line + bool IsLineInst() const; + // Return true if OpLine or Shader100:DebugLine + bool IsLine() const; + // Return true if OpNoLine or Shader100:DebugNoLine + bool IsNoLine() const; inline uint32_t GetDebugInlinedAt() const { return dbg_scope_.GetInlinedAt(); } @@ -454,6 +478,10 @@ class Instruction : public utils::IntrusiveNodeBase { // storage buffer. bool IsVulkanStorageBuffer() const; + // Returns true if the instruction defines a variable in StorageBuffer or + // Uniform storage class with a pointer type that points to a storage buffer. + bool IsVulkanStorageBufferVariable() const; + // Returns true if the instruction defines a pointer type that points to a // uniform buffer. bool IsVulkanUniformBuffer() const; @@ -476,10 +504,6 @@ class Instruction : public utils::IntrusiveNodeBase { // Returns true if this instruction exits this function or aborts execution. bool IsReturnOrAbort() const { return spvOpcodeIsReturnOrAbort(opcode()); } - // Returns the id for the |element|'th subtype. If the |this| is not a - // composite type, this function returns 0. - uint32_t GetTypeComponent(uint32_t element) const; - // Returns true if this instruction is a basic block terminator. bool IsBlockTerminator() const { return spvOpcodeIsBlockTerminator(opcode()); @@ -550,11 +574,30 @@ class Instruction : public utils::IntrusiveNodeBase { // OpenCLDebugInfo100InstructionsMax. OpenCLDebugInfo100Instructions GetOpenCL100DebugOpcode() const; + // Returns debug opcode of an NonSemantic.Shader.DebugInfo.100 instruction. If + // it is not an NonSemantic.Shader.DebugInfo.100 instruction, just return + // NonSemanticShaderDebugInfo100InstructionsMax. + NonSemanticShaderDebugInfo100Instructions GetShader100DebugOpcode() const; + + // Returns debug opcode of an OpenCL.100.DebugInfo or + // NonSemantic.Shader.DebugInfo.100 instruction. Since these overlap, we + // return the OpenCLDebugInfo code + CommonDebugInfoInstructions GetCommonDebugOpcode() const; + // Returns true if it is an OpenCL.DebugInfo.100 instruction. bool IsOpenCL100DebugInstr() const { return GetOpenCL100DebugOpcode() != OpenCLDebugInfo100InstructionsMax; } + // Returns true if it is an NonSemantic.Shader.DebugInfo.100 instruction. + bool IsShader100DebugInstr() const { + return GetShader100DebugOpcode() != + NonSemanticShaderDebugInfo100InstructionsMax; + } + bool IsCommonDebugInstr() const { + return GetCommonDebugOpcode() != CommonDebugInfoInstructionsMax; + } + // Returns true if this instructions a non-semantic instruction. bool IsNonSemanticInstruction() const; @@ -588,9 +631,9 @@ class Instruction : public utils::IntrusiveNodeBase { uint32_t unique_id_; // Unique instruction id // All logical operands, including result type id and result id. OperandList operands_; - // Opline and OpNoLine instructions preceding this instruction. Note that for - // Instructions representing OpLine or OpNonLine itself, this field should be - // empty. + // Op[No]Line or Debug[No]Line instructions preceding this instruction. Note + // that for Instructions representing Op[No]Line or Debug[No]Line themselves, + // this field should be empty. std::vector dbg_line_insts_; // DebugScope that wraps this instruction. diff --git a/source/opt/instrument_pass.cpp b/source/opt/instrument_pass.cpp index ed34fb02..d143d595 100644 --- a/source/opt/instrument_pass.cpp +++ b/source/opt/instrument_pass.cpp @@ -88,6 +88,51 @@ std::unique_ptr InstrumentPass::NewLabel(uint32_t label_id) { return newLabel; } +std::unique_ptr InstrumentPass::NewName( + uint32_t id, const std::string& name_str) { + std::unique_ptr new_name(new Instruction( + context(), SpvOpName, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {id}}, + {SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}})); + + return new_name; +} + +std::unique_ptr InstrumentPass::NewGlobalName( + uint32_t id, const std::string& name_str) { + std::string prefixed_name; + switch (validation_id_) { + case kInstValidationIdBindless: + prefixed_name = "inst_bindless_"; + break; + case kInstValidationIdBuffAddr: + prefixed_name = "inst_buff_addr_"; + break; + case kInstValidationIdDebugPrintf: + prefixed_name = "inst_printf_"; + break; + default: + assert(false); // add new instrumentation pass here + prefixed_name = "inst_pass_"; + break; + } + prefixed_name += name_str; + return NewName(id, prefixed_name); +} + +std::unique_ptr InstrumentPass::NewMemberName( + uint32_t id, uint32_t member_index, const std::string& name_str) { + std::unique_ptr new_name(new Instruction( + context(), SpvOpMemberName, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {id}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {member_index}}, + {SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}})); + + return new_name; +} + uint32_t InstrumentPass::Gen32BitCvtCode(uint32_t val_id, InstructionBuilder* builder) { // Convert integer value to 32-bit if necessary @@ -200,7 +245,9 @@ void InstrumentPass::GenStageStreamWriteCode(uint32_t stage_idx, } break; case SpvExecutionModelGLCompute: case SpvExecutionModelTaskNV: - case SpvExecutionModelMeshNV: { + case SpvExecutionModelMeshNV: + case SpvExecutionModelTaskEXT: + case SpvExecutionModelMeshEXT: { // Load and store GlobalInvocationId. uint32_t load_id = GenVarLoad( context()->GetBuiltinInputVarId(SpvBuiltInGlobalInvocationId), @@ -525,6 +572,10 @@ uint32_t InstrumentPass::GetOutputBufferId() { {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {SpvStorageClassStorageBuffer}}})); context()->AddGlobalValue(std::move(newVarOp)); + context()->AddDebug2Inst(NewGlobalName(obufTyId, "OutputBuffer")); + context()->AddDebug2Inst(NewMemberName(obufTyId, 0, "written_count")); + context()->AddDebug2Inst(NewMemberName(obufTyId, 1, "data")); + context()->AddDebug2Inst(NewGlobalName(output_buffer_id_, "output_buffer")); deco_mgr->AddDecorationVal(output_buffer_id_, SpvDecorationDescriptorSet, desc_set_); deco_mgr->AddDecorationVal(output_buffer_id_, SpvDecorationBinding, @@ -569,6 +620,9 @@ uint32_t InstrumentPass::GetInputBufferId() { {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {SpvStorageClassStorageBuffer}}})); context()->AddGlobalValue(std::move(newVarOp)); + context()->AddDebug2Inst(NewGlobalName(ibufTyId, "InputBuffer")); + context()->AddDebug2Inst(NewMemberName(ibufTyId, 0, "data")); + context()->AddDebug2Inst(NewGlobalName(input_buffer_id_, "input_buffer")); deco_mgr->AddDecorationVal(input_buffer_id_, SpvDecorationDescriptorSet, desc_set_); deco_mgr->AddDecorationVal(input_buffer_id_, SpvDecorationBinding, @@ -783,6 +837,12 @@ uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx, get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst); output_func->SetFunctionEnd(std::move(func_end_inst)); context()->AddFunction(std::move(output_func)); + + std::string name("stream_write_"); + name += std::to_string(param_cnt); + + context()->AddDebug2Inst( + NewGlobalName(param2output_func_id_[param_cnt], name)); } return param2output_func_id_[param_cnt]; } @@ -863,6 +923,11 @@ uint32_t InstrumentPass::GetDirectReadFunctionId(uint32_t param_cnt) { get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst); input_func->SetFunctionEnd(std::move(func_end_inst)); context()->AddFunction(std::move(input_func)); + + std::string name("direct_read_"); + name += std::to_string(param_cnt); + context()->AddDebug2Inst(NewGlobalName(func_id, name)); + param2input_func_id_[param_cnt] = func_id; return func_id; } @@ -1001,7 +1066,8 @@ bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) { stage != SpvExecutionModelAnyHitNV && stage != SpvExecutionModelClosestHitNV && stage != SpvExecutionModelMissNV && - stage != SpvExecutionModelCallableNV) { + stage != SpvExecutionModelCallableNV && + stage != SpvExecutionModelTaskEXT && stage != SpvExecutionModelMeshEXT) { if (consumer()) { std::string message = "Stage not supported by instrumentation"; consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str()); diff --git a/source/opt/instrument_pass.h b/source/opt/instrument_pass.h index 12b939d4..215b0263 100644 --- a/source/opt/instrument_pass.h +++ b/source/opt/instrument_pass.h @@ -50,7 +50,7 @@ // A validation pass may read or write multiple buffers. All such buffers // are located in a single debug descriptor set whose index is passed at the // creation of the instrumentation pass. The bindings of the buffers used by -// a validation pass are permanantly assigned and fixed and documented by +// a validation pass are permanently assigned and fixed and documented by // the kDebugOutput* static consts. namespace spvtools { @@ -179,8 +179,8 @@ class InstrumentPass : public Pass { // the error. Every stage will write a fixed number of words. Vertex shaders // will write the Vertex and Instance ID. Fragment shaders will write // FragCoord.xy. Compute shaders will write the GlobalInvocation ID. - // The tesselation eval shader will write the Primitive ID and TessCoords.uv. - // The tesselation control shader and geometry shader will write the + // The tessellation eval shader will write the Primitive ID and TessCoords.uv. + // The tessellation control shader and geometry shader will write the // Primitive ID and Invocation ID. // // The Validation Error Code specifies the exact error which has occurred. @@ -224,6 +224,19 @@ class InstrumentPass : public Pass { // Return new label. std::unique_ptr NewLabel(uint32_t label_id); + // Set the name function parameter or local variable + std::unique_ptr NewName(uint32_t id, + const std::string& name_str); + + // Set the name for a function or global variable, names will be + // prefixed to identify which instrumentation pass generated them. + std::unique_ptr NewGlobalName(uint32_t id, + const std::string& name_str); + + // Set the name for a structure member + std::unique_ptr NewMemberName(uint32_t id, uint32_t member_index, + const std::string& name_str); + // Return id for 32-bit unsigned type uint32_t GetUintId(); diff --git a/source/opt/interface_var_sroa.cpp b/source/opt/interface_var_sroa.cpp new file mode 100644 index 00000000..1b2cb363 --- /dev/null +++ b/source/opt/interface_var_sroa.cpp @@ -0,0 +1,968 @@ +// Copyright (c) 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/interface_var_sroa.h" + +#include + +#include "source/opt/decoration_manager.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/function.h" +#include "source/opt/log.h" +#include "source/opt/type_manager.h" +#include "source/util/make_unique.h" + +const static uint32_t kOpDecorateDecorationInOperandIndex = 1; +const static uint32_t kOpDecorateLiteralInOperandIndex = 2; +const static uint32_t kOpEntryPointInOperandInterface = 3; +const static uint32_t kOpVariableStorageClassInOperandIndex = 0; +const static uint32_t kOpTypeArrayElemTypeInOperandIndex = 0; +const static uint32_t kOpTypeArrayLengthInOperandIndex = 1; +const static uint32_t kOpTypeMatrixColCountInOperandIndex = 1; +const static uint32_t kOpTypeMatrixColTypeInOperandIndex = 0; +const static uint32_t kOpTypePtrTypeInOperandIndex = 1; +const static uint32_t kOpConstantValueInOperandIndex = 0; + +namespace spvtools { +namespace opt { +namespace { + +// Get the length of the OpTypeArray |array_type|. +uint32_t GetArrayLength(analysis::DefUseManager* def_use_mgr, + Instruction* array_type) { + assert(array_type->opcode() == SpvOpTypeArray); + uint32_t const_int_id = + array_type->GetSingleWordInOperand(kOpTypeArrayLengthInOperandIndex); + Instruction* array_length_inst = def_use_mgr->GetDef(const_int_id); + assert(array_length_inst->opcode() == SpvOpConstant); + return array_length_inst->GetSingleWordInOperand( + kOpConstantValueInOperandIndex); +} + +// Get the element type instruction of the OpTypeArray |array_type|. +Instruction* GetArrayElementType(analysis::DefUseManager* def_use_mgr, + Instruction* array_type) { + assert(array_type->opcode() == SpvOpTypeArray); + uint32_t elem_type_id = + array_type->GetSingleWordInOperand(kOpTypeArrayElemTypeInOperandIndex); + return def_use_mgr->GetDef(elem_type_id); +} + +// Get the column type instruction of the OpTypeMatrix |matrix_type|. +Instruction* GetMatrixColumnType(analysis::DefUseManager* def_use_mgr, + Instruction* matrix_type) { + assert(matrix_type->opcode() == SpvOpTypeMatrix); + uint32_t column_type_id = + matrix_type->GetSingleWordInOperand(kOpTypeMatrixColTypeInOperandIndex); + return def_use_mgr->GetDef(column_type_id); +} + +// Traverses the component type of OpTypeArray or OpTypeMatrix. Repeats it +// |depth_to_component| times recursively and returns the component type. +// |type_id| is the result id of the OpTypeArray or OpTypeMatrix instruction. +uint32_t GetComponentTypeOfArrayMatrix(analysis::DefUseManager* def_use_mgr, + uint32_t type_id, + uint32_t depth_to_component) { + if (depth_to_component == 0) return type_id; + + Instruction* type_inst = def_use_mgr->GetDef(type_id); + if (type_inst->opcode() == SpvOpTypeArray) { + uint32_t elem_type_id = + type_inst->GetSingleWordInOperand(kOpTypeArrayElemTypeInOperandIndex); + return GetComponentTypeOfArrayMatrix(def_use_mgr, elem_type_id, + depth_to_component - 1); + } + + assert(type_inst->opcode() == SpvOpTypeMatrix); + uint32_t column_type_id = + type_inst->GetSingleWordInOperand(kOpTypeMatrixColTypeInOperandIndex); + return GetComponentTypeOfArrayMatrix(def_use_mgr, column_type_id, + depth_to_component - 1); +} + +// Creates an OpDecorate instruction whose Target is |var_id| and Decoration is +// |decoration|. Adds |literal| as an extra operand of the instruction. +void CreateDecoration(analysis::DecorationManager* decoration_mgr, + uint32_t var_id, SpvDecoration decoration, + uint32_t literal) { + std::vector operands({ + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {var_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_DECORATION, + {static_cast(decoration)}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {literal}}, + }); + decoration_mgr->AddDecoration(SpvOpDecorate, std::move(operands)); +} + +// Replaces load instructions with composite construct instructions in all the +// users of the loads. |loads_to_composites| is the mapping from each load to +// its corresponding OpCompositeConstruct. +void ReplaceLoadWithCompositeConstruct( + IRContext* context, + const std::unordered_map& loads_to_composites) { + for (const auto& load_and_composite : loads_to_composites) { + Instruction* load = load_and_composite.first; + Instruction* composite_construct = load_and_composite.second; + + std::vector users; + context->get_def_use_mgr()->ForEachUse( + load, [&users, composite_construct](Instruction* user, uint32_t index) { + user->GetOperand(index).words[0] = composite_construct->result_id(); + users.push_back(user); + }); + + for (Instruction* user : users) + context->get_def_use_mgr()->AnalyzeInstUse(user); + } +} + +// Returns the storage class of the instruction |var|. +SpvStorageClass GetStorageClass(Instruction* var) { + return static_cast( + var->GetSingleWordInOperand(kOpVariableStorageClassInOperandIndex)); +} + +} // namespace + +bool InterfaceVariableScalarReplacement::HasExtraArrayness( + Instruction& entry_point, Instruction* var) { + SpvExecutionModel execution_model = + static_cast(entry_point.GetSingleWordInOperand(0)); + if (execution_model != SpvExecutionModelTessellationEvaluation && + execution_model != SpvExecutionModelTessellationControl) { + return false; + } + if (!context()->get_decoration_mgr()->HasDecoration(var->result_id(), + SpvDecorationPatch)) { + if (execution_model == SpvExecutionModelTessellationControl) return true; + return GetStorageClass(var) != SpvStorageClassOutput; + } + return false; +} + +bool InterfaceVariableScalarReplacement:: + CheckExtraArraynessConflictBetweenEntries(Instruction* interface_var, + bool has_extra_arrayness) { + if (has_extra_arrayness) { + return !ReportErrorIfHasNoExtraArraynessForOtherEntry(interface_var); + } + return !ReportErrorIfHasExtraArraynessForOtherEntry(interface_var); +} + +bool InterfaceVariableScalarReplacement::GetVariableLocation( + Instruction* var, uint32_t* location) { + return !context()->get_decoration_mgr()->WhileEachDecoration( + var->result_id(), SpvDecorationLocation, + [location](const Instruction& inst) { + *location = + inst.GetSingleWordInOperand(kOpDecorateLiteralInOperandIndex); + return false; + }); +} + +bool InterfaceVariableScalarReplacement::GetVariableComponent( + Instruction* var, uint32_t* component) { + return !context()->get_decoration_mgr()->WhileEachDecoration( + var->result_id(), SpvDecorationComponent, + [component](const Instruction& inst) { + *component = + inst.GetSingleWordInOperand(kOpDecorateLiteralInOperandIndex); + return false; + }); +} + +std::vector +InterfaceVariableScalarReplacement::CollectInterfaceVariables( + Instruction& entry_point) { + std::vector interface_vars; + for (uint32_t i = kOpEntryPointInOperandInterface; + i < entry_point.NumInOperands(); ++i) { + Instruction* interface_var = context()->get_def_use_mgr()->GetDef( + entry_point.GetSingleWordInOperand(i)); + assert(interface_var->opcode() == SpvOpVariable); + + SpvStorageClass storage_class = GetStorageClass(interface_var); + if (storage_class != SpvStorageClassInput && + storage_class != SpvStorageClassOutput) { + continue; + } + + interface_vars.push_back(interface_var); + } + return interface_vars; +} + +void InterfaceVariableScalarReplacement::KillInstructionAndUsers( + Instruction* inst) { + if (inst->opcode() == SpvOpEntryPoint) { + return; + } + if (inst->opcode() != SpvOpAccessChain) { + context()->KillInst(inst); + return; + } + std::vector users; + context()->get_def_use_mgr()->ForEachUser( + inst, [&users](Instruction* user) { users.push_back(user); }); + for (auto user : users) { + context()->KillInst(user); + } + context()->KillInst(inst); +} + +void InterfaceVariableScalarReplacement::KillInstructionsAndUsers( + const std::vector& insts) { + for (Instruction* inst : insts) { + KillInstructionAndUsers(inst); + } +} + +void InterfaceVariableScalarReplacement::KillLocationAndComponentDecorations( + uint32_t var_id) { + context()->get_decoration_mgr()->RemoveDecorationsFrom( + var_id, [](const Instruction& inst) { + uint32_t decoration = + inst.GetSingleWordInOperand(kOpDecorateDecorationInOperandIndex); + return decoration == SpvDecorationLocation || + decoration == SpvDecorationComponent; + }); +} + +bool InterfaceVariableScalarReplacement::ReplaceInterfaceVariableWithScalars( + Instruction* interface_var, Instruction* interface_var_type, + uint32_t location, uint32_t component, uint32_t extra_array_length) { + NestedCompositeComponents scalar_interface_vars = + CreateScalarInterfaceVarsForReplacement(interface_var_type, + GetStorageClass(interface_var), + extra_array_length); + + AddLocationAndComponentDecorations(scalar_interface_vars, &location, + component); + KillLocationAndComponentDecorations(interface_var->result_id()); + + if (!ReplaceInterfaceVarWith(interface_var, extra_array_length, + scalar_interface_vars)) { + return false; + } + + context()->KillInst(interface_var); + return true; +} + +bool InterfaceVariableScalarReplacement::ReplaceInterfaceVarWith( + Instruction* interface_var, uint32_t extra_array_length, + const NestedCompositeComponents& scalar_interface_vars) { + std::vector users; + context()->get_def_use_mgr()->ForEachUser( + interface_var, [&users](Instruction* user) { users.push_back(user); }); + + std::vector interface_var_component_indices; + std::unordered_map loads_to_composites; + std::unordered_map + loads_for_access_chain_to_composites; + if (extra_array_length != 0) { + // Note that the extra arrayness is the first dimension of the array + // interface variable. + for (uint32_t index = 0; index < extra_array_length; ++index) { + std::unordered_map loads_to_component_values; + if (!ReplaceComponentsOfInterfaceVarWith( + interface_var, users, scalar_interface_vars, + interface_var_component_indices, &index, + &loads_to_component_values, + &loads_for_access_chain_to_composites)) { + return false; + } + AddComponentsToCompositesForLoads(loads_to_component_values, + &loads_to_composites, 0); + } + } else if (!ReplaceComponentsOfInterfaceVarWith( + interface_var, users, scalar_interface_vars, + interface_var_component_indices, nullptr, &loads_to_composites, + &loads_for_access_chain_to_composites)) { + return false; + } + + ReplaceLoadWithCompositeConstruct(context(), loads_to_composites); + ReplaceLoadWithCompositeConstruct(context(), + loads_for_access_chain_to_composites); + + KillInstructionsAndUsers(users); + return true; +} + +void InterfaceVariableScalarReplacement::AddLocationAndComponentDecorations( + const NestedCompositeComponents& vars, uint32_t* location, + uint32_t component) { + if (!vars.HasMultipleComponents()) { + uint32_t var_id = vars.GetComponentVariable()->result_id(); + CreateDecoration(context()->get_decoration_mgr(), var_id, + SpvDecorationLocation, *location); + CreateDecoration(context()->get_decoration_mgr(), var_id, + SpvDecorationComponent, component); + ++(*location); + return; + } + for (const auto& var : vars.GetComponents()) { + AddLocationAndComponentDecorations(var, location, component); + } +} + +bool InterfaceVariableScalarReplacement::ReplaceComponentsOfInterfaceVarWith( + Instruction* interface_var, + const std::vector& interface_var_users, + const NestedCompositeComponents& scalar_interface_vars, + std::vector& interface_var_component_indices, + const uint32_t* extra_array_index, + std::unordered_map* loads_to_composites, + std::unordered_map* + loads_for_access_chain_to_composites) { + if (!scalar_interface_vars.HasMultipleComponents()) { + for (Instruction* interface_var_user : interface_var_users) { + if (!ReplaceComponentOfInterfaceVarWith( + interface_var, interface_var_user, + scalar_interface_vars.GetComponentVariable(), + interface_var_component_indices, extra_array_index, + loads_to_composites, loads_for_access_chain_to_composites)) { + return false; + } + } + return true; + } + return ReplaceMultipleComponentsOfInterfaceVarWith( + interface_var, interface_var_users, scalar_interface_vars.GetComponents(), + interface_var_component_indices, extra_array_index, loads_to_composites, + loads_for_access_chain_to_composites); +} + +bool InterfaceVariableScalarReplacement:: + ReplaceMultipleComponentsOfInterfaceVarWith( + Instruction* interface_var, + const std::vector& interface_var_users, + const std::vector& components, + std::vector& interface_var_component_indices, + const uint32_t* extra_array_index, + std::unordered_map* loads_to_composites, + std::unordered_map* + loads_for_access_chain_to_composites) { + for (uint32_t i = 0; i < components.size(); ++i) { + interface_var_component_indices.push_back(i); + std::unordered_map loads_to_component_values; + std::unordered_map + loads_for_access_chain_to_component_values; + if (!ReplaceComponentsOfInterfaceVarWith( + interface_var, interface_var_users, components[i], + interface_var_component_indices, extra_array_index, + &loads_to_component_values, + &loads_for_access_chain_to_component_values)) { + return false; + } + interface_var_component_indices.pop_back(); + + uint32_t depth_to_component = + static_cast(interface_var_component_indices.size()); + AddComponentsToCompositesForLoads( + loads_for_access_chain_to_component_values, + loads_for_access_chain_to_composites, depth_to_component); + if (extra_array_index) ++depth_to_component; + AddComponentsToCompositesForLoads(loads_to_component_values, + loads_to_composites, depth_to_component); + } + return true; +} + +bool InterfaceVariableScalarReplacement::ReplaceComponentOfInterfaceVarWith( + Instruction* interface_var, Instruction* interface_var_user, + Instruction* scalar_var, + const std::vector& interface_var_component_indices, + const uint32_t* extra_array_index, + std::unordered_map* loads_to_component_values, + std::unordered_map* + loads_for_access_chain_to_component_values) { + SpvOp opcode = interface_var_user->opcode(); + if (opcode == SpvOpStore) { + uint32_t value_id = interface_var_user->GetSingleWordInOperand(1); + StoreComponentOfValueToScalarVar(value_id, interface_var_component_indices, + scalar_var, extra_array_index, + interface_var_user); + return true; + } + if (opcode == SpvOpLoad) { + Instruction* scalar_load = + LoadScalarVar(scalar_var, extra_array_index, interface_var_user); + loads_to_component_values->insert({interface_var_user, scalar_load}); + return true; + } + + // Copy OpName and annotation instructions only once. Therefore, we create + // them only for the first element of the extra array. + if (extra_array_index && *extra_array_index != 0) return true; + + if (opcode == SpvOpDecorateId || opcode == SpvOpDecorateString || + opcode == SpvOpDecorate) { + CloneAnnotationForVariable(interface_var_user, scalar_var->result_id()); + return true; + } + + if (opcode == SpvOpName) { + std::unique_ptr new_inst(interface_var_user->Clone(context())); + new_inst->SetInOperand(0, {scalar_var->result_id()}); + context()->AddDebug2Inst(std::move(new_inst)); + return true; + } + + if (opcode == SpvOpEntryPoint) { + return ReplaceInterfaceVarInEntryPoint(interface_var, interface_var_user, + scalar_var->result_id()); + } + + if (opcode == SpvOpAccessChain) { + ReplaceAccessChainWith(interface_var_user, interface_var_component_indices, + scalar_var, + loads_for_access_chain_to_component_values); + return true; + } + + std::string message("Unhandled instruction"); + message += "\n " + interface_var_user->PrettyPrint( + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + message += + "\nfor interface variable scalar replacement\n " + + interface_var->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + context()->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); + return false; +} + +void InterfaceVariableScalarReplacement::UseBaseAccessChainForAccessChain( + Instruction* access_chain, Instruction* base_access_chain) { + assert(base_access_chain->opcode() == SpvOpAccessChain && + access_chain->opcode() == SpvOpAccessChain && + access_chain->GetSingleWordInOperand(0) == + base_access_chain->result_id()); + Instruction::OperandList new_operands; + for (uint32_t i = 0; i < base_access_chain->NumInOperands(); ++i) { + new_operands.emplace_back(base_access_chain->GetInOperand(i)); + } + for (uint32_t i = 1; i < access_chain->NumInOperands(); ++i) { + new_operands.emplace_back(access_chain->GetInOperand(i)); + } + access_chain->SetInOperands(std::move(new_operands)); +} + +Instruction* InterfaceVariableScalarReplacement::CreateAccessChainToVar( + uint32_t var_type_id, Instruction* var, + const std::vector& index_ids, Instruction* insert_before, + uint32_t* component_type_id) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + *component_type_id = GetComponentTypeOfArrayMatrix( + def_use_mgr, var_type_id, static_cast(index_ids.size())); + + uint32_t ptr_type_id = + GetPointerType(*component_type_id, GetStorageClass(var)); + + std::unique_ptr new_access_chain( + new Instruction(context(), SpvOpAccessChain, ptr_type_id, TakeNextId(), + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {var->result_id()}}})); + for (uint32_t index_id : index_ids) { + new_access_chain->AddOperand({SPV_OPERAND_TYPE_ID, {index_id}}); + } + + Instruction* inst = new_access_chain.get(); + def_use_mgr->AnalyzeInstDefUse(inst); + insert_before->InsertBefore(std::move(new_access_chain)); + return inst; +} + +Instruction* InterfaceVariableScalarReplacement::CreateAccessChainWithIndex( + uint32_t component_type_id, Instruction* var, uint32_t index, + Instruction* insert_before) { + uint32_t ptr_type_id = + GetPointerType(component_type_id, GetStorageClass(var)); + uint32_t index_id = context()->get_constant_mgr()->GetUIntConst(index); + std::unique_ptr new_access_chain( + new Instruction(context(), SpvOpAccessChain, ptr_type_id, TakeNextId(), + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {var->result_id()}}, + {SPV_OPERAND_TYPE_ID, {index_id}}, + })); + Instruction* inst = new_access_chain.get(); + context()->get_def_use_mgr()->AnalyzeInstDefUse(inst); + insert_before->InsertBefore(std::move(new_access_chain)); + return inst; +} + +void InterfaceVariableScalarReplacement::ReplaceAccessChainWith( + Instruction* access_chain, + const std::vector& interface_var_component_indices, + Instruction* scalar_var, + std::unordered_map* loads_to_component_values) { + std::vector indexes; + for (uint32_t i = 1; i < access_chain->NumInOperands(); ++i) { + indexes.push_back(access_chain->GetSingleWordInOperand(i)); + } + + // Note that we have a strong assumption that |access_chain| has only a single + // index that is for the extra arrayness. + context()->get_def_use_mgr()->ForEachUser( + access_chain, + [this, access_chain, &indexes, &interface_var_component_indices, + scalar_var, loads_to_component_values](Instruction* user) { + switch (user->opcode()) { + case SpvOpAccessChain: { + UseBaseAccessChainForAccessChain(user, access_chain); + ReplaceAccessChainWith(user, interface_var_component_indices, + scalar_var, loads_to_component_values); + return; + } + case SpvOpStore: { + uint32_t value_id = user->GetSingleWordInOperand(1); + StoreComponentOfValueToAccessChainToScalarVar( + value_id, interface_var_component_indices, scalar_var, indexes, + user); + return; + } + case SpvOpLoad: { + Instruction* value = + LoadAccessChainToVar(scalar_var, indexes, user); + loads_to_component_values->insert({user, value}); + return; + } + default: + break; + } + }); +} + +void InterfaceVariableScalarReplacement::CloneAnnotationForVariable( + Instruction* annotation_inst, uint32_t var_id) { + assert(annotation_inst->opcode() == SpvOpDecorate || + annotation_inst->opcode() == SpvOpDecorateId || + annotation_inst->opcode() == SpvOpDecorateString); + std::unique_ptr new_inst(annotation_inst->Clone(context())); + new_inst->SetInOperand(0, {var_id}); + context()->AddAnnotationInst(std::move(new_inst)); +} + +bool InterfaceVariableScalarReplacement::ReplaceInterfaceVarInEntryPoint( + Instruction* interface_var, Instruction* entry_point, + uint32_t scalar_var_id) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + uint32_t interface_var_id = interface_var->result_id(); + if (interface_vars_removed_from_entry_point_operands_.find( + interface_var_id) != + interface_vars_removed_from_entry_point_operands_.end()) { + entry_point->AddOperand({SPV_OPERAND_TYPE_ID, {scalar_var_id}}); + def_use_mgr->AnalyzeInstUse(entry_point); + return true; + } + + bool success = !entry_point->WhileEachInId( + [&interface_var_id, &scalar_var_id](uint32_t* id) { + if (*id == interface_var_id) { + *id = scalar_var_id; + return false; + } + return true; + }); + if (!success) { + std::string message( + "interface variable is not an operand of the entry point"); + message += "\n " + interface_var->PrettyPrint( + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + message += "\n " + entry_point->PrettyPrint( + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + context()->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); + return false; + } + + def_use_mgr->AnalyzeInstUse(entry_point); + interface_vars_removed_from_entry_point_operands_.insert(interface_var_id); + return true; +} + +uint32_t InterfaceVariableScalarReplacement::GetPointeeTypeIdOfVar( + Instruction* var) { + assert(var->opcode() == SpvOpVariable); + + uint32_t ptr_type_id = var->type_id(); + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + Instruction* ptr_type_inst = def_use_mgr->GetDef(ptr_type_id); + + assert(ptr_type_inst->opcode() == SpvOpTypePointer && + "Variable must have a pointer type."); + return ptr_type_inst->GetSingleWordInOperand(kOpTypePtrTypeInOperandIndex); +} + +void InterfaceVariableScalarReplacement::StoreComponentOfValueToScalarVar( + uint32_t value_id, const std::vector& component_indices, + Instruction* scalar_var, const uint32_t* extra_array_index, + Instruction* insert_before) { + uint32_t component_type_id = GetPointeeTypeIdOfVar(scalar_var); + Instruction* ptr = scalar_var; + if (extra_array_index) { + auto* ty_mgr = context()->get_type_mgr(); + analysis::Array* array_type = ty_mgr->GetType(component_type_id)->AsArray(); + assert(array_type != nullptr); + component_type_id = ty_mgr->GetTypeInstruction(array_type->element_type()); + ptr = CreateAccessChainWithIndex(component_type_id, scalar_var, + *extra_array_index, insert_before); + } + + StoreComponentOfValueTo(component_type_id, value_id, component_indices, ptr, + extra_array_index, insert_before); +} + +Instruction* InterfaceVariableScalarReplacement::LoadScalarVar( + Instruction* scalar_var, const uint32_t* extra_array_index, + Instruction* insert_before) { + uint32_t component_type_id = GetPointeeTypeIdOfVar(scalar_var); + Instruction* ptr = scalar_var; + if (extra_array_index) { + auto* ty_mgr = context()->get_type_mgr(); + analysis::Array* array_type = ty_mgr->GetType(component_type_id)->AsArray(); + assert(array_type != nullptr); + component_type_id = ty_mgr->GetTypeInstruction(array_type->element_type()); + ptr = CreateAccessChainWithIndex(component_type_id, scalar_var, + *extra_array_index, insert_before); + } + + return CreateLoad(component_type_id, ptr, insert_before); +} + +Instruction* InterfaceVariableScalarReplacement::CreateLoad( + uint32_t type_id, Instruction* ptr, Instruction* insert_before) { + std::unique_ptr load( + new Instruction(context(), SpvOpLoad, type_id, TakeNextId(), + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {ptr->result_id()}}})); + Instruction* load_inst = load.get(); + context()->get_def_use_mgr()->AnalyzeInstDefUse(load_inst); + insert_before->InsertBefore(std::move(load)); + return load_inst; +} + +void InterfaceVariableScalarReplacement::StoreComponentOfValueTo( + uint32_t component_type_id, uint32_t value_id, + const std::vector& component_indices, Instruction* ptr, + const uint32_t* extra_array_index, Instruction* insert_before) { + std::unique_ptr composite_extract(CreateCompositeExtract( + component_type_id, value_id, component_indices, extra_array_index)); + + std::unique_ptr new_store( + new Instruction(context(), SpvOpStore)); + new_store->AddOperand({SPV_OPERAND_TYPE_ID, {ptr->result_id()}}); + new_store->AddOperand( + {SPV_OPERAND_TYPE_ID, {composite_extract->result_id()}}); + + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + def_use_mgr->AnalyzeInstDefUse(composite_extract.get()); + def_use_mgr->AnalyzeInstDefUse(new_store.get()); + + insert_before->InsertBefore(std::move(composite_extract)); + insert_before->InsertBefore(std::move(new_store)); +} + +Instruction* InterfaceVariableScalarReplacement::CreateCompositeExtract( + uint32_t type_id, uint32_t composite_id, + const std::vector& indexes, const uint32_t* extra_first_index) { + uint32_t component_id = TakeNextId(); + Instruction* composite_extract = new Instruction( + context(), SpvOpCompositeExtract, type_id, component_id, + std::initializer_list{{SPV_OPERAND_TYPE_ID, {composite_id}}}); + if (extra_first_index) { + composite_extract->AddOperand( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {*extra_first_index}}); + } + for (uint32_t index : indexes) { + composite_extract->AddOperand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}}); + } + return composite_extract; +} + +void InterfaceVariableScalarReplacement:: + StoreComponentOfValueToAccessChainToScalarVar( + uint32_t value_id, const std::vector& component_indices, + Instruction* scalar_var, + const std::vector& access_chain_indices, + Instruction* insert_before) { + uint32_t component_type_id = GetPointeeTypeIdOfVar(scalar_var); + Instruction* ptr = scalar_var; + if (!access_chain_indices.empty()) { + ptr = CreateAccessChainToVar(component_type_id, scalar_var, + access_chain_indices, insert_before, + &component_type_id); + } + + StoreComponentOfValueTo(component_type_id, value_id, component_indices, ptr, + nullptr, insert_before); +} + +Instruction* InterfaceVariableScalarReplacement::LoadAccessChainToVar( + Instruction* var, const std::vector& indexes, + Instruction* insert_before) { + uint32_t component_type_id = GetPointeeTypeIdOfVar(var); + Instruction* ptr = var; + if (!indexes.empty()) { + ptr = CreateAccessChainToVar(component_type_id, var, indexes, insert_before, + &component_type_id); + } + + return CreateLoad(component_type_id, ptr, insert_before); +} + +Instruction* +InterfaceVariableScalarReplacement::CreateCompositeConstructForComponentOfLoad( + Instruction* load, uint32_t depth_to_component) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + uint32_t type_id = load->type_id(); + if (depth_to_component != 0) { + type_id = GetComponentTypeOfArrayMatrix(def_use_mgr, load->type_id(), + depth_to_component); + } + uint32_t new_id = context()->TakeNextId(); + std::unique_ptr new_composite_construct( + new Instruction(context(), SpvOpCompositeConstruct, type_id, new_id, {})); + Instruction* composite_construct = new_composite_construct.get(); + def_use_mgr->AnalyzeInstDefUse(composite_construct); + + // Insert |new_composite_construct| after |load|. When there are multiple + // recursive composite construct instructions for a load, we have to place the + // composite construct with a lower depth later because it constructs the + // composite that contains other composites with lower depths. + auto* insert_before = load->NextNode(); + while (true) { + auto itr = + composite_ids_to_component_depths.find(insert_before->result_id()); + if (itr == composite_ids_to_component_depths.end()) break; + if (itr->second <= depth_to_component) break; + insert_before = insert_before->NextNode(); + } + insert_before->InsertBefore(std::move(new_composite_construct)); + composite_ids_to_component_depths.insert({new_id, depth_to_component}); + return composite_construct; +} + +void InterfaceVariableScalarReplacement::AddComponentsToCompositesForLoads( + const std::unordered_map& + loads_to_component_values, + std::unordered_map* loads_to_composites, + uint32_t depth_to_component) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + for (auto& load_and_component_vale : loads_to_component_values) { + Instruction* load = load_and_component_vale.first; + Instruction* component_value = load_and_component_vale.second; + Instruction* composite_construct = nullptr; + auto itr = loads_to_composites->find(load); + if (itr == loads_to_composites->end()) { + composite_construct = + CreateCompositeConstructForComponentOfLoad(load, depth_to_component); + loads_to_composites->insert({load, composite_construct}); + } else { + composite_construct = itr->second; + } + composite_construct->AddOperand( + {SPV_OPERAND_TYPE_ID, {component_value->result_id()}}); + def_use_mgr->AnalyzeInstDefUse(composite_construct); + } +} + +uint32_t InterfaceVariableScalarReplacement::GetArrayType( + uint32_t elem_type_id, uint32_t array_length) { + analysis::Type* elem_type = context()->get_type_mgr()->GetType(elem_type_id); + uint32_t array_length_id = + context()->get_constant_mgr()->GetUIntConst(array_length); + analysis::Array array_type( + elem_type, + analysis::Array::LengthInfo{array_length_id, {0, array_length}}); + return context()->get_type_mgr()->GetTypeInstruction(&array_type); +} + +uint32_t InterfaceVariableScalarReplacement::GetPointerType( + uint32_t type_id, SpvStorageClass storage_class) { + analysis::Type* type = context()->get_type_mgr()->GetType(type_id); + analysis::Pointer ptr_type(type, storage_class); + return context()->get_type_mgr()->GetTypeInstruction(&ptr_type); +} + +InterfaceVariableScalarReplacement::NestedCompositeComponents +InterfaceVariableScalarReplacement::CreateScalarInterfaceVarsForArray( + Instruction* interface_var_type, SpvStorageClass storage_class, + uint32_t extra_array_length) { + assert(interface_var_type->opcode() == SpvOpTypeArray); + + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + uint32_t array_length = GetArrayLength(def_use_mgr, interface_var_type); + Instruction* elem_type = GetArrayElementType(def_use_mgr, interface_var_type); + + NestedCompositeComponents scalar_vars; + while (array_length > 0) { + NestedCompositeComponents scalar_vars_for_element = + CreateScalarInterfaceVarsForReplacement(elem_type, storage_class, + extra_array_length); + scalar_vars.AddComponent(scalar_vars_for_element); + --array_length; + } + return scalar_vars; +} + +InterfaceVariableScalarReplacement::NestedCompositeComponents +InterfaceVariableScalarReplacement::CreateScalarInterfaceVarsForMatrix( + Instruction* interface_var_type, SpvStorageClass storage_class, + uint32_t extra_array_length) { + assert(interface_var_type->opcode() == SpvOpTypeMatrix); + + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + uint32_t column_count = interface_var_type->GetSingleWordInOperand( + kOpTypeMatrixColCountInOperandIndex); + Instruction* column_type = + GetMatrixColumnType(def_use_mgr, interface_var_type); + + NestedCompositeComponents scalar_vars; + while (column_count > 0) { + NestedCompositeComponents scalar_vars_for_column = + CreateScalarInterfaceVarsForReplacement(column_type, storage_class, + extra_array_length); + scalar_vars.AddComponent(scalar_vars_for_column); + --column_count; + } + return scalar_vars; +} + +InterfaceVariableScalarReplacement::NestedCompositeComponents +InterfaceVariableScalarReplacement::CreateScalarInterfaceVarsForReplacement( + Instruction* interface_var_type, SpvStorageClass storage_class, + uint32_t extra_array_length) { + // Handle array case. + if (interface_var_type->opcode() == SpvOpTypeArray) { + return CreateScalarInterfaceVarsForArray(interface_var_type, storage_class, + extra_array_length); + } + + // Handle matrix case. + if (interface_var_type->opcode() == SpvOpTypeMatrix) { + return CreateScalarInterfaceVarsForMatrix(interface_var_type, storage_class, + extra_array_length); + } + + // Handle scalar or vector case. + NestedCompositeComponents scalar_var; + uint32_t type_id = interface_var_type->result_id(); + if (extra_array_length != 0) { + type_id = GetArrayType(type_id, extra_array_length); + } + uint32_t ptr_type_id = + context()->get_type_mgr()->FindPointerToType(type_id, storage_class); + uint32_t id = TakeNextId(); + std::unique_ptr variable( + new Instruction(context(), SpvOpVariable, ptr_type_id, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_STORAGE_CLASS, + {static_cast(storage_class)}}})); + scalar_var.SetSingleComponentVariable(variable.get()); + context()->AddGlobalValue(std::move(variable)); + return scalar_var; +} + +Instruction* InterfaceVariableScalarReplacement::GetTypeOfVariable( + Instruction* var) { + uint32_t pointee_type_id = GetPointeeTypeIdOfVar(var); + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + return def_use_mgr->GetDef(pointee_type_id); +} + +Pass::Status InterfaceVariableScalarReplacement::Process() { + Pass::Status status = Status::SuccessWithoutChange; + for (Instruction& entry_point : get_module()->entry_points()) { + status = + CombineStatus(status, ReplaceInterfaceVarsWithScalars(entry_point)); + } + return status; +} + +bool InterfaceVariableScalarReplacement:: + ReportErrorIfHasExtraArraynessForOtherEntry(Instruction* var) { + if (vars_with_extra_arrayness.find(var) == vars_with_extra_arrayness.end()) + return false; + + std::string message( + "A variable is arrayed for an entry point but it is not " + "arrayed for another entry point"); + message += + "\n " + var->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + context()->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); + return true; +} + +bool InterfaceVariableScalarReplacement:: + ReportErrorIfHasNoExtraArraynessForOtherEntry(Instruction* var) { + if (vars_without_extra_arrayness.find(var) == + vars_without_extra_arrayness.end()) + return false; + + std::string message( + "A variable is not arrayed for an entry point but it is " + "arrayed for another entry point"); + message += + "\n " + var->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + context()->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); + return true; +} + +Pass::Status +InterfaceVariableScalarReplacement::ReplaceInterfaceVarsWithScalars( + Instruction& entry_point) { + std::vector interface_vars = + CollectInterfaceVariables(entry_point); + + Pass::Status status = Status::SuccessWithoutChange; + for (Instruction* interface_var : interface_vars) { + uint32_t location, component; + if (!GetVariableLocation(interface_var, &location)) continue; + if (!GetVariableComponent(interface_var, &component)) component = 0; + + Instruction* interface_var_type = GetTypeOfVariable(interface_var); + uint32_t extra_array_length = 0; + if (HasExtraArrayness(entry_point, interface_var)) { + extra_array_length = + GetArrayLength(context()->get_def_use_mgr(), interface_var_type); + interface_var_type = + GetArrayElementType(context()->get_def_use_mgr(), interface_var_type); + vars_with_extra_arrayness.insert(interface_var); + } else { + vars_without_extra_arrayness.insert(interface_var); + } + + if (!CheckExtraArraynessConflictBetweenEntries(interface_var, + extra_array_length != 0)) { + return Pass::Status::Failure; + } + + if (interface_var_type->opcode() != SpvOpTypeArray && + interface_var_type->opcode() != SpvOpTypeMatrix) { + continue; + } + + if (!ReplaceInterfaceVariableWithScalars(interface_var, interface_var_type, + location, component, + extra_array_length)) { + return Pass::Status::Failure; + } + status = Pass::Status::SuccessWithChange; + } + + return status; +} + +} // namespace opt +} // namespace spvtools diff --git a/source/opt/interface_var_sroa.h b/source/opt/interface_var_sroa.h new file mode 100644 index 00000000..23baad0a --- /dev/null +++ b/source/opt/interface_var_sroa.h @@ -0,0 +1,401 @@ +// Copyright (c) 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_INTERFACE_VAR_SROA_H_ +#define SOURCE_OPT_INTERFACE_VAR_SROA_H_ + +#include + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +// +// Note that the current implementation of this pass covers only store, load, +// access chain instructions for the interface variables. Supporting other types +// of instructions is a future work. +class InterfaceVariableScalarReplacement : public Pass { + public: + InterfaceVariableScalarReplacement() {} + + const char* name() const override { + return "interface-variable-scalar-replacement"; + } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDecorations | IRContext::kAnalysisDefUse | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // A struct containing components of a composite variable. If the composite + // consists of multiple or recursive components, |component_variable| is + // nullptr and |nested_composite_components| keeps the components. If it has a + // single component, |nested_composite_components| is empty and + // |component_variable| is the component. Note that each element of + // |nested_composite_components| has the NestedCompositeComponents struct as + // its type that can recursively keep the components. + struct NestedCompositeComponents { + NestedCompositeComponents() : component_variable(nullptr) {} + + bool HasMultipleComponents() const { + return !nested_composite_components.empty(); + } + + const std::vector& GetComponents() const { + return nested_composite_components; + } + + void AddComponent(const NestedCompositeComponents& component) { + nested_composite_components.push_back(component); + } + + Instruction* GetComponentVariable() const { return component_variable; } + + void SetSingleComponentVariable(Instruction* var) { + component_variable = var; + } + + private: + std::vector nested_composite_components; + Instruction* component_variable; + }; + + // Collects all interface variables used by the |entry_point|. + std::vector CollectInterfaceVariables(Instruction& entry_point); + + // Returns whether |var| has the extra arrayness for the entry point + // |entry_point| or not. + bool HasExtraArrayness(Instruction& entry_point, Instruction* var); + + // Finds a Location BuiltIn decoration of |var| and returns it via + // |location|. Returns true whether the location exists or not. + bool GetVariableLocation(Instruction* var, uint32_t* location); + + // Finds a Component BuiltIn decoration of |var| and returns it via + // |component|. Returns true whether the component exists or not. + bool GetVariableComponent(Instruction* var, uint32_t* component); + + // Returns the interface variable instruction whose result id is + // |interface_var_id|. + Instruction* GetInterfaceVariable(uint32_t interface_var_id); + + // Returns the type of |var| as an instruction. + Instruction* GetTypeOfVariable(Instruction* var); + + // Replaces an interface variable |interface_var| whose type is + // |interface_var_type| with scalars and returns whether it succeeds or not. + // |location| is the value of Location Decoration for |interface_var|. + // |component| is the value of Component Decoration for |interface_var|. + // If |extra_array_length| is 0, it means |interface_var| has a Patch + // decoration. Otherwise, |extra_array_length| denotes the length of the extra + // array of |interface_var|. + bool ReplaceInterfaceVariableWithScalars(Instruction* interface_var, + Instruction* interface_var_type, + uint32_t location, + uint32_t component, + uint32_t extra_array_length); + + // Creates scalar variables with the storage classe |storage_class| to replace + // an interface variable whose type is |interface_var_type|. If + // |extra_array_length| is not zero, adds the extra arrayness to the created + // scalar variables. + NestedCompositeComponents CreateScalarInterfaceVarsForReplacement( + Instruction* interface_var_type, SpvStorageClass storage_class, + uint32_t extra_array_length); + + // Creates scalar variables with the storage classe |storage_class| to replace + // the interface variable whose type is OpTypeArray |interface_var_type| with. + // If |extra_array_length| is not zero, adds the extra arrayness to all the + // scalar variables. + NestedCompositeComponents CreateScalarInterfaceVarsForArray( + Instruction* interface_var_type, SpvStorageClass storage_class, + uint32_t extra_array_length); + + // Creates scalar variables with the storage classe |storage_class| to replace + // the interface variable whose type is OpTypeMatrix |interface_var_type| + // with. If |extra_array_length| is not zero, adds the extra arrayness to all + // the scalar variables. + NestedCompositeComponents CreateScalarInterfaceVarsForMatrix( + Instruction* interface_var_type, SpvStorageClass storage_class, + uint32_t extra_array_length); + + // Recursively adds Location and Component decorations to variables in + // |vars| with |location| and |component|. Increases |location| by one after + // it actually adds Location and Component decorations for a variable. + void AddLocationAndComponentDecorations(const NestedCompositeComponents& vars, + uint32_t* location, + uint32_t component); + + // Replaces the interface variable |interface_var| with + // |scalar_interface_vars| and returns whether it succeeds or not. + // |extra_arrayness| is the extra arrayness of the interface variable. + // |scalar_interface_vars| contains the nested variables to replace the + // interface variable with. + bool ReplaceInterfaceVarWith( + Instruction* interface_var, uint32_t extra_arrayness, + const NestedCompositeComponents& scalar_interface_vars); + + // Replaces |interface_var| in the operands of instructions + // |interface_var_users| with |scalar_interface_vars|. This is a recursive + // method and |interface_var_component_indices| is used to specify which + // recursive component of |interface_var| is replaced. Returns composite + // construct instructions to be replaced with load instructions of + // |interface_var_users| via |loads_to_composites|. Returns composite + // construct instructions to be replaced with load instructions of access + // chain instructions in |interface_var_users| via + // |loads_for_access_chain_to_composites|. + bool ReplaceComponentsOfInterfaceVarWith( + Instruction* interface_var, + const std::vector& interface_var_users, + const NestedCompositeComponents& scalar_interface_vars, + std::vector& interface_var_component_indices, + const uint32_t* extra_array_index, + std::unordered_map* loads_to_composites, + std::unordered_map* + loads_for_access_chain_to_composites); + + // Replaces |interface_var| in the operands of instructions + // |interface_var_users| with |components| that is a vector of components for + // the interface variable |interface_var|. This is a recursive method and + // |interface_var_component_indices| is used to specify which recursive + // component of |interface_var| is replaced. Returns composite construct + // instructions to be replaced with load instructions of |interface_var_users| + // via |loads_to_composites|. Returns composite construct instructions to be + // replaced with load instructions of access chain instructions in + // |interface_var_users| via |loads_for_access_chain_to_composites|. + bool ReplaceMultipleComponentsOfInterfaceVarWith( + Instruction* interface_var, + const std::vector& interface_var_users, + const std::vector& components, + std::vector& interface_var_component_indices, + const uint32_t* extra_array_index, + std::unordered_map* loads_to_composites, + std::unordered_map* + loads_for_access_chain_to_composites); + + // Replaces a component of |interface_var| that is used as an operand of + // instruction |interface_var_user| with |scalar_var|. + // |interface_var_component_indices| is a vector of recursive indices for + // which recursive component of |interface_var| is replaced. If + // |interface_var_user| is a load, returns the component value via + // |loads_to_component_values|. If |interface_var_user| is an access chain, + // returns the component value for loads of |interface_var_user| via + // |loads_for_access_chain_to_component_values|. + bool ReplaceComponentOfInterfaceVarWith( + Instruction* interface_var, Instruction* interface_var_user, + Instruction* scalar_var, + const std::vector& interface_var_component_indices, + const uint32_t* extra_array_index, + std::unordered_map* loads_to_component_values, + std::unordered_map* + loads_for_access_chain_to_component_values); + + // Creates instructions to load |scalar_var| and inserts them before + // |insert_before|. If |extra_array_index| is not null, they load + // |extra_array_index| th component of |scalar_var| instead of |scalar_var| + // itself. + Instruction* LoadScalarVar(Instruction* scalar_var, + const uint32_t* extra_array_index, + Instruction* insert_before); + + // Creates instructions to load an access chain to |var| and inserts them + // before |insert_before|. |Indexes| will be Indexes operand of the access + // chain. + Instruction* LoadAccessChainToVar(Instruction* var, + const std::vector& indexes, + Instruction* insert_before); + + // Creates instructions to store a component of an aggregate whose id is + // |value_id| to an access chain to |scalar_var| and inserts the created + // instructions before |insert_before|. To get the component, recursively + // traverses the aggregate with |component_indices| as indexes. + // Numbers in |access_chain_indices| are the Indexes operand of the access + // chain to |scalar_var| + void StoreComponentOfValueToAccessChainToScalarVar( + uint32_t value_id, const std::vector& component_indices, + Instruction* scalar_var, + const std::vector& access_chain_indices, + Instruction* insert_before); + + // Creates instructions to store a component of an aggregate whose id is + // |value_id| to |scalar_var| and inserts the created instructions before + // |insert_before|. To get the component, recursively traverses the aggregate + // using |extra_array_index| and |component_indices| as indexes. + void StoreComponentOfValueToScalarVar( + uint32_t value_id, const std::vector& component_indices, + Instruction* scalar_var, const uint32_t* extra_array_index, + Instruction* insert_before); + + // Creates instructions to store a component of an aggregate whose id is + // |value_id| to |ptr| and inserts the created instructions before + // |insert_before|. To get the component, recursively traverses the aggregate + // using |extra_array_index| and |component_indices| as indexes. + // |component_type_id| is the id of the type instruction of the component. + void StoreComponentOfValueTo(uint32_t component_type_id, uint32_t value_id, + const std::vector& component_indices, + Instruction* ptr, + const uint32_t* extra_array_index, + Instruction* insert_before); + + // Creates new OpCompositeExtract with |type_id| for Result Type, + // |composite_id| for Composite operand, and |indexes| for Indexes operands. + // If |extra_first_index| is not nullptr, uses it as the first Indexes + // operand. + Instruction* CreateCompositeExtract(uint32_t type_id, uint32_t composite_id, + const std::vector& indexes, + const uint32_t* extra_first_index); + + // Creates a new OpLoad whose Result Type is |type_id| and Pointer operand is + // |ptr|. Inserts the new instruction before |insert_before|. + Instruction* CreateLoad(uint32_t type_id, Instruction* ptr, + Instruction* insert_before); + + // Clones an annotation instruction |annotation_inst| and sets the target + // operand of the new annotation instruction as |var_id|. + void CloneAnnotationForVariable(Instruction* annotation_inst, + uint32_t var_id); + + // Replaces the interface variable |interface_var| in the operands of the + // entry point |entry_point| with |scalar_var_id|. If it cannot find + // |interface_var| from the operands of the entry point |entry_point|, adds + // |scalar_var_id| as an operand of the entry point |entry_point|. + bool ReplaceInterfaceVarInEntryPoint(Instruction* interface_var, + Instruction* entry_point, + uint32_t scalar_var_id); + + // Creates an access chain instruction whose Base operand is |var| and Indexes + // operand is |index|. |component_type_id| is the id of the type instruction + // that is the type of component. Inserts the new access chain before + // |insert_before|. + Instruction* CreateAccessChainWithIndex(uint32_t component_type_id, + Instruction* var, uint32_t index, + Instruction* insert_before); + + // Returns the pointee type of the type of variable |var|. + uint32_t GetPointeeTypeIdOfVar(Instruction* var); + + // Replaces the access chain |access_chain| and its users with a new access + // chain that points |scalar_var| as the Base operand having + // |interface_var_component_indices| as Indexes operands and users of the new + // access chain. When some of the users are load instructions, returns the + // original load instruction to the new instruction that loads a component of + // the original load value via |loads_to_component_values|. + void ReplaceAccessChainWith( + Instruction* access_chain, + const std::vector& interface_var_component_indices, + Instruction* scalar_var, + std::unordered_map* + loads_to_component_values); + + // Assuming that |access_chain| is an access chain instruction whose Base + // operand is |base_access_chain|, replaces the operands of |access_chain| + // with operands of |base_access_chain| and Indexes operands of + // |access_chain|. + void UseBaseAccessChainForAccessChain(Instruction* access_chain, + Instruction* base_access_chain); + + // Creates composite construct instructions for load instructions that are the + // keys of |loads_to_component_values| if no such composite construct + // instructions exist. Adds a component of the composite as an operand of the + // created composite construct instruction. Each value of + // |loads_to_component_values| is the component. Returns the created composite + // construct instructions using |loads_to_composites|. |depth_to_component| is + // the number of recursive access steps to get the component from the + // composite. + void AddComponentsToCompositesForLoads( + const std::unordered_map& + loads_to_component_values, + std::unordered_map* loads_to_composites, + uint32_t depth_to_component); + + // Creates a composite construct instruction for a component of the value of + // instruction |load| in |depth_to_component| th recursive depth and inserts + // it after |load|. + Instruction* CreateCompositeConstructForComponentOfLoad( + Instruction* load, uint32_t depth_to_component); + + // Creates a new access chain instruction that points to variable |var| whose + // type is the instruction with |var_type_id| and inserts it before + // |insert_before|. The new access chain will have |index_ids| for Indexes + // operands. Returns the type id of the component that is pointed by the new + // access chain via |component_type_id|. + Instruction* CreateAccessChainToVar(uint32_t var_type_id, Instruction* var, + const std::vector& index_ids, + Instruction* insert_before, + uint32_t* component_type_id); + + // Returns the result id of OpTypeArray instrunction whose Element Type + // operand is |elem_type_id| and Length operand is |array_length|. + uint32_t GetArrayType(uint32_t elem_type_id, uint32_t array_length); + + // Returns the result id of OpTypePointer instrunction whose Type + // operand is |type_id| and Storage Class operand is |storage_class|. + uint32_t GetPointerType(uint32_t type_id, SpvStorageClass storage_class); + + // Kills an instrunction |inst| and its users. + void KillInstructionAndUsers(Instruction* inst); + + // Kills a vector of instrunctions |insts| and their users. + void KillInstructionsAndUsers(const std::vector& insts); + + // Kills all OpDecorate instructions for Location and Component of the + // variable whose id is |var_id|. + void KillLocationAndComponentDecorations(uint32_t var_id); + + // If |var| has the extra arrayness for an entry point, reports an error and + // returns true. Otherwise, returns false. + bool ReportErrorIfHasExtraArraynessForOtherEntry(Instruction* var); + + // If |var| does not have the extra arrayness for an entry point, reports an + // error and returns true. Otherwise, returns false. + bool ReportErrorIfHasNoExtraArraynessForOtherEntry(Instruction* var); + + // If |interface_var| has the extra arrayness for an entry point but it does + // not have one for another entry point, reports an error and returns false. + // Otherwise, returns true. |has_extra_arrayness| denotes whether it has an + // extra arrayness for an entry point or not. + bool CheckExtraArraynessConflictBetweenEntries(Instruction* interface_var, + bool has_extra_arrayness); + + // Conducts the scalar replacement for the interface variables used by the + // |entry_point|. + Pass::Status ReplaceInterfaceVarsWithScalars(Instruction& entry_point); + + // A set of interface variable ids that were already removed from operands of + // the entry point. + std::unordered_set + interface_vars_removed_from_entry_point_operands_; + + // A mapping from ids of new composite construct instructions that load + // instructions are replaced with to the recursive depth of the component of + // load that the new component construct instruction is used for. + std::unordered_map composite_ids_to_component_depths; + + // A set of interface variables with the extra arrayness for any of the entry + // points. + std::unordered_set vars_with_extra_arrayness; + + // A set of interface variables without the extra arrayness for any of the + // entry points. + std::unordered_set vars_without_extra_arrayness; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_INTERFACE_VAR_SROA_H_ diff --git a/source/opt/interp_fixup_pass.cpp b/source/opt/interp_fixup_pass.cpp index ad29e6a7..e8cdd99f 100644 --- a/source/opt/interp_fixup_pass.cpp +++ b/source/opt/interp_fixup_pass.cpp @@ -31,13 +31,6 @@ namespace { // Input Operand Indices static const int kSpvVariableStorageClassInIdx = 0; -// Avoid unused variable warning/error on Linux -#ifndef NDEBUG -#define USE_ASSERT(x) assert(x) -#else -#define USE_ASSERT(x) ((void)(x)) -#endif - // Folding rule function which attempts to replace |op(OpLoad(a),...)| // by |op(a,...)|, where |op| is one of the GLSLstd450 InterpolateAt* // instructions. Returns true if replaced, false otherwise. diff --git a/source/opt/ir_builder.h b/source/opt/ir_builder.h index fe5feff5..9d4fa8fe 100644 --- a/source/opt/ir_builder.h +++ b/source/opt/ir_builder.h @@ -359,8 +359,9 @@ class InstructionBuilder { return AddInstruction(std::move(select)); } - // Adds a signed int32 constant to the binary. - // The |value| parameter is the constant value to be added. + // Returns a pointer to the definition of a signed 32-bit integer constant + // with the given value. Returns |nullptr| if the constant does not exist and + // cannot be created. Instruction* GetSintConstant(int32_t value) { return GetIntConstant(value, true); } @@ -381,21 +382,24 @@ class InstructionBuilder { GetContext()->TakeNextId(), ops)); return AddInstruction(std::move(construct)); } - // Adds an unsigned int32 constant to the binary. - // The |value| parameter is the constant value to be added. + + // Returns a pointer to the definition of an unsigned 32-bit integer constant + // with the given value. Returns |nullptr| if the constant does not exist and + // cannot be created. Instruction* GetUintConstant(uint32_t value) { return GetIntConstant(value, false); } uint32_t GetUintConstantId(uint32_t value) { Instruction* uint_inst = GetUintConstant(value); - return uint_inst->result_id(); + return (uint_inst != nullptr ? uint_inst->result_id() : 0); } // Adds either a signed or unsigned 32 bit integer constant to the binary - // depedning on the |sign|. If |sign| is true then the value is added as a + // depending on the |sign|. If |sign| is true then the value is added as a // signed constant otherwise as an unsigned constant. If |sign| is false the - // value must not be a negative number. + // value must not be a negative number. Returns false if the constant does + // not exists and could be be created. template Instruction* GetIntConstant(T value, bool sign) { // Assert that we are not trying to store a negative number in an unsigned @@ -411,6 +415,10 @@ class InstructionBuilder { uint32_t type_id = GetContext()->get_type_mgr()->GetTypeInstruction(&int_type); + if (type_id == 0) { + return nullptr; + } + // Get the memory managed type so that it is safe to be stored by // GetConstant. analysis::Type* rebuilt_type = @@ -479,6 +487,15 @@ class InstructionBuilder { return AddInstruction(std::move(new_inst)); } + Instruction* AddVariable(uint32_t type_id, uint32_t storage_class) { + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {storage_class}}); + std::unique_ptr new_inst( + new Instruction(GetContext(), SpvOpVariable, type_id, + GetContext()->TakeNextId(), operands)); + return AddInstruction(std::move(new_inst)); + } + Instruction* AddStore(uint32_t ptr_id, uint32_t obj_id) { std::vector operands; operands.push_back({SPV_OPERAND_TYPE_ID, {ptr_id}}); diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp index 82107b5c..c9c3f1b5 100644 --- a/source/opt/ir_context.cpp +++ b/source/opt/ir_context.cpp @@ -30,7 +30,8 @@ static const int kSpvDecorateBuiltinInIdx = 2; static const int kEntryPointInterfaceInIdx = 3; static const int kEntryPointFunctionIdInIdx = 1; -// Constants for OpenCL.DebugInfo.100 extension instructions. +// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100 +// extension instructions. static const uint32_t kDebugFunctionOperandFunctionIndex = 13; static const uint32_t kDebugGlobalVariableOperandVariableIndex = 11; @@ -40,6 +41,8 @@ namespace spvtools { namespace opt { void IRContext::BuildInvalidAnalyses(IRContext::Analysis set) { + set = Analysis(set & ~valid_analyses_); + if (set & kAnalysisDefUse) { BuildDefUseManager(); } @@ -105,7 +108,7 @@ void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) { analyses_to_invalidate |= kAnalysisDebugInfo; } - // The dominator analysis hold the psuedo entry and exit nodes from the CFG. + // The dominator analysis hold the pseudo entry and exit nodes from the CFG. // Also if the CFG change the dominators many changed as well, so the // dominator analysis should be invalidated as well. if (analyses_to_invalidate & kAnalysisCFG) { @@ -170,7 +173,9 @@ Instruction* IRContext::KillInst(Instruction* inst) { KillOperandFromDebugInstructions(inst); if (AreAnalysesValid(kAnalysisDefUse)) { - get_def_use_mgr()->ClearInst(inst); + analysis::DefUseManager* def_use_mgr = get_def_use_mgr(); + def_use_mgr->ClearInst(inst); + for (auto& l_inst : inst->dbg_line_insts()) def_use_mgr->ClearInst(&l_inst); } if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) { instr_to_block_.erase(inst); @@ -214,10 +219,12 @@ Instruction* IRContext::KillInst(Instruction* inst) { return next_instruction; } -void IRContext::KillNonSemanticInfo(Instruction* inst) { +void IRContext::CollectNonSemanticTree( + Instruction* inst, std::unordered_set* to_kill) { if (!inst->HasResultId()) return; + // Debug[No]Line result id is not used, so we are done + if (inst->IsDebugLineInst()) return; std::vector work_list; - std::vector to_kill; std::unordered_set seen; work_list.push_back(inst); @@ -225,17 +232,13 @@ void IRContext::KillNonSemanticInfo(Instruction* inst) { auto* i = work_list.back(); work_list.pop_back(); get_def_use_mgr()->ForEachUser( - i, [&work_list, &to_kill, &seen](Instruction* user) { + i, [&work_list, to_kill, &seen](Instruction* user) { if (user->IsNonSemanticInstruction() && seen.insert(user).second) { work_list.push_back(user); - to_kill.push_back(user); + to_kill->insert(user); } }); } - - for (auto* dead : to_kill) { - KillInst(dead); - } } bool IRContext::KillDef(uint32_t id) { @@ -316,7 +319,7 @@ bool IRContext::IsConsistent() { #else if (AreAnalysesValid(kAnalysisDefUse)) { analysis::DefUseManager new_def_use(module()); - if (*get_def_use_mgr() != new_def_use) { + if (!CompareAndPrintDifferences(*get_def_use_mgr(), new_def_use)) { return false; } } @@ -441,8 +444,7 @@ void IRContext::KillOperandFromDebugInstructions(Instruction* inst) { if (opcode == SpvOpVariable || IsConstantInst(opcode)) { for (auto it = module()->ext_inst_debuginfo_begin(); it != module()->ext_inst_debuginfo_end(); ++it) { - if (it->GetOpenCL100DebugOpcode() != - OpenCLDebugInfo100DebugGlobalVariable) + if (it->GetCommonDebugOpcode() != CommonDebugInfoDebugGlobalVariable) continue; auto& operand = it->GetOperand(kDebugGlobalVariableOperandVariableIndex); if (operand.words[0] == id) { @@ -623,9 +625,8 @@ void IRContext::AddCombinatorsForCapability(uint32_t capability) { void IRContext::AddCombinatorsForExtension(Instruction* extension) { assert(extension->opcode() == SpvOpExtInstImport && "Expecting an import of an extension's instruction set."); - const char* extension_name = - reinterpret_cast(&extension->GetInOperand(0).words[0]); - if (!strcmp(extension_name, "GLSL.std.450")) { + const std::string extension_name = extension->GetInOperand(0).AsString(); + if (extension_name == "GLSL.std.450") { combinator_ops_[extension->result_id()] = {GLSLstd450Round, GLSLstd450RoundEven, GLSLstd450Trunc, @@ -925,6 +926,19 @@ bool IRContext::ProcessCallTreeFromRoots(ProcessFunction& pfn, return modified; } +void IRContext::CollectCallTreeFromRoots(unsigned entryId, + std::unordered_set* funcs) { + std::queue roots; + roots.push(entryId); + while (!roots.empty()) { + const uint32_t fi = roots.front(); + roots.pop(); + funcs->insert(fi); + Function* fn = GetFunction(fi); + AddCalls(fn, &roots); + } +} + void IRContext::EmitErrorMessage(std::string message, Instruction* inst) { if (!consumer()) { return; @@ -934,7 +948,7 @@ void IRContext::EmitErrorMessage(std::string message, Instruction* inst) { while (line_inst != nullptr) { // Stop at the beginning of the basic block. if (!line_inst->dbg_line_insts().empty()) { line_inst = &line_inst->dbg_line_insts().back(); - if (line_inst->opcode() == SpvOpNoLine) { + if (line_inst->IsNoLine()) { line_inst = nullptr; } break; @@ -944,11 +958,11 @@ void IRContext::EmitErrorMessage(std::string message, Instruction* inst) { uint32_t line_number = 0; uint32_t col_number = 0; - char* source = nullptr; + std::string source; if (line_inst != nullptr) { Instruction* file_name = get_def_use_mgr()->GetDef(line_inst->GetSingleWordInOperand(0)); - source = reinterpret_cast(&file_name->GetInOperand(0).words[0]); + source = file_name->GetInOperand(0).AsString(); // Get the line number and column number. line_number = line_inst->GetSingleWordInOperand(1); @@ -957,7 +971,7 @@ void IRContext::EmitErrorMessage(std::string message, Instruction* inst) { message += "\n " + inst->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); - consumer()(SPV_MSG_ERROR, source, {line_number, col_number, 0}, + consumer()(SPV_MSG_ERROR, source.c_str(), {line_number, col_number, 0}, message.c_str()); } @@ -1038,5 +1052,11 @@ bool IRContext::CheckCFG() { return true; } + +bool IRContext::IsReachable(const opt::BasicBlock& bb) { + auto enclosing_function = bb.GetParent(); + return GetDominatorAnalysis(enclosing_function) + ->Dominates(enclosing_function->entry().get(), &bb); +} } // namespace opt } // namespace spvtools diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h index 5aa25acd..2f27942b 100644 --- a/source/opt/ir_context.h +++ b/source/opt/ir_context.h @@ -43,6 +43,7 @@ #include "source/opt/type_manager.h" #include "source/opt/value_number_table.h" #include "source/util/make_unique.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -98,6 +99,7 @@ class IRContext { module_(new Module()), consumer_(std::move(c)), def_use_mgr_(nullptr), + feature_mgr_(nullptr), valid_analyses_(kAnalysisNone), constant_mgr_(nullptr), type_mgr_(nullptr), @@ -116,6 +118,7 @@ class IRContext { module_(std::move(m)), consumer_(std::move(c)), def_use_mgr_(nullptr), + feature_mgr_(nullptr), valid_analyses_(kAnalysisNone), type_mgr_(nullptr), id_to_name_(nullptr), @@ -190,8 +193,8 @@ class IRContext { inline IteratorRange debugs3() const; // Iterators for debug info instructions (excluding OpLine & OpNoLine) - // contained in this module. These are OpExtInst for OpenCL.DebugInfo.100 - // or DebugInfo extension placed between section 9 and 10. + // contained in this module. These are OpExtInst for DebugInfo extension + // placed between section 9 and 10. inline Module::inst_iterator ext_inst_debuginfo_begin(); inline Module::inst_iterator ext_inst_debuginfo_end(); inline IteratorRange ext_inst_debuginfo(); @@ -299,7 +302,7 @@ class IRContext { } } - // Returns a pointer the decoration manager. If the decoration manger is + // Returns a pointer the decoration manager. If the decoration manager is // invalid, it is rebuilt first. analysis::DecorationManager* get_decoration_mgr() { if (!AreAnalysesValid(kAnalysisDecorations)) { @@ -382,7 +385,7 @@ class IRContext { // Deletes the instruction defining the given |id|. Returns true on // success, false if the given |id| is not defined at all. This method also - // erases the name, decorations, and defintion of |id|. + // erases the name, decorations, and definition of |id|. // // Pointers and iterators pointing to the deleted instructions become invalid. // However other pointers and iterators are still valid. @@ -403,8 +406,14 @@ class IRContext { // instruction exists. Instruction* KillInst(Instruction* inst); - // Removes the non-semantic instruction tree that uses |inst|'s result id. - void KillNonSemanticInfo(Instruction* inst); + // Collects the non-semantic instruction tree that uses |inst|'s result id + // to be killed later. + void CollectNonSemanticTree(Instruction* inst, + std::unordered_set* to_kill); + + // Collect function reachable from |entryId|, returns |funcs| + void CollectCallTreeFromRoots(unsigned entryId, + std::unordered_set* funcs); // Returns true if all of the given analyses are valid. bool AreAnalysesValid(Analysis set) { return (set & valid_analyses_) == set; } @@ -514,6 +523,18 @@ class IRContext { std::string message = "ID overflow. Try running compact-ids."; consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); } +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + // If TakeNextId returns 0, it is very likely that execution will + // subsequently fail. Such failures are false alarms from a fuzzing point + // of view: they are due to the fact that too many ids were used, rather + // than being due to an actual bug. Thus, during a fuzzing build, it is + // preferable to bail out when ID overflow occurs. + // + // A zero exit code is returned here because a non-zero code would cause + // ClusterFuzz/OSS-Fuzz to regard the termination as a crash, and spurious + // crash reports is what this guard aims to avoid. + exit(0); +#endif } return next_id; } @@ -596,10 +617,14 @@ class IRContext { bool ProcessCallTreeFromRoots(ProcessFunction& pfn, std::queue* roots); - // Emmits a error message to the message consumer indicating the error + // Emits a error message to the message consumer indicating the error // described by |message| occurred in |inst|. void EmitErrorMessage(std::string message, Instruction* inst); + // Returns true if and only if there is a path to |bb| from the entry block of + // the function that contains |bb|. + bool IsReachable(const opt::BasicBlock& bb); + private: // Builds the def-use manager from scratch, even if it was already valid. void BuildDefUseManager() { @@ -763,6 +788,8 @@ class IRContext { // The instruction decoration manager for |module_|. std::unique_ptr decoration_mgr_; + + // The feature manager for |module_|. std::unique_ptr feature_mgr_; // A map from instructions to the basic block they belong to. This mapping is @@ -779,7 +806,7 @@ class IRContext { // iterators to traverse instructions. std::unordered_map id_to_func_; - // A bitset indicating which analyes are currently valid. + // A bitset indicating which analyzes are currently valid. Analysis valid_analyses_; // Opcodes of shader capability core executable instructions @@ -844,8 +871,7 @@ inline IRContext::Analysis operator|(IRContext::Analysis lhs, inline IRContext::Analysis& operator|=(IRContext::Analysis& lhs, IRContext::Analysis rhs) { - lhs = static_cast(static_cast(lhs) | - static_cast(rhs)); + lhs = lhs | rhs; return lhs; } @@ -1010,11 +1036,7 @@ void IRContext::AddCapability(std::unique_ptr&& c) { } void IRContext::AddExtension(const std::string& ext_name) { - const auto num_chars = ext_name.size(); - // Compute num words, accommodate the terminating null character. - const auto num_words = (num_chars + 1 + 3) / 4; - std::vector ext_words(num_words, 0u); - std::memcpy(ext_words.data(), ext_name.data(), num_chars); + std::vector ext_words = spvtools::utils::MakeVector(ext_name); AddExtension(std::unique_ptr( new Instruction(this, SpvOpExtension, 0u, 0u, {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}}))); @@ -1031,11 +1053,7 @@ void IRContext::AddExtension(std::unique_ptr&& e) { } void IRContext::AddExtInstImport(const std::string& name) { - const auto num_chars = name.size(); - // Compute num words, accommodate the terminating null character. - const auto num_words = (num_chars + 1 + 3) / 4; - std::vector ext_words(num_words, 0u); - std::memcpy(ext_words.data(), name.data(), num_chars); + std::vector ext_words = spvtools::utils::MakeVector(name); AddExtInstImport(std::unique_ptr( new Instruction(this, SpvOpExtInstImport, 0u, TakeNextId(), {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}}))); @@ -1076,6 +1094,9 @@ void IRContext::AddDebug2Inst(std::unique_ptr&& d) { id_to_name_->insert({d->GetSingleWordInOperand(0), d.get()}); } } + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->AnalyzeInstDefUse(d.get()); + } module()->AddDebug2Inst(std::move(d)); } diff --git a/source/opt/ir_loader.cpp b/source/opt/ir_loader.cpp index e443ebb5..734ad554 100644 --- a/source/opt/ir_loader.cpp +++ b/source/opt/ir_loader.cpp @@ -19,6 +19,7 @@ #include "DebugInfo.h" #include "OpenCLDebugInfo100.h" #include "source/ext_inst.h" +#include "source/opt/ir_context.h" #include "source/opt/log.h" #include "source/opt/reflect.h" #include "source/util/make_unique.h" @@ -37,26 +38,40 @@ IrLoader::IrLoader(const MessageConsumer& consumer, Module* m) inst_index_(0), last_dbg_scope_(kNoDebugScope, kNoInlinedAt) {} +bool IsLineInst(const spv_parsed_instruction_t* inst) { + const auto opcode = static_cast(inst->opcode); + if (IsOpLineInst(opcode)) return true; + if (opcode != SpvOpExtInst) return false; + if (inst->ext_inst_type != SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) + return false; + const uint32_t ext_inst_index = inst->words[kExtInstSetIndex]; + const NonSemanticShaderDebugInfo100Instructions ext_inst_key = + NonSemanticShaderDebugInfo100Instructions(ext_inst_index); + return ext_inst_key == NonSemanticShaderDebugInfo100DebugLine || + ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine; +} + bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { ++inst_index_; - const auto opcode = static_cast(inst->opcode); - if (IsDebugLineInst(opcode)) { + if (IsLineInst(inst)) { module()->SetContainsDebugInfo(); last_line_inst_.reset(); - dbg_line_info_.push_back( - Instruction(module()->context(), *inst, last_dbg_scope_)); + dbg_line_info_.emplace_back(module()->context(), *inst, last_dbg_scope_); return true; } // If it is a DebugScope or DebugNoScope of debug extension, we do not // create a new instruction, but simply keep the information in // struct DebugScope. + const auto opcode = static_cast(inst->opcode); if (opcode == SpvOpExtInst && spvExtInstIsDebugInfo(inst->ext_inst_type)) { const uint32_t ext_inst_index = inst->words[kExtInstSetIndex]; - if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { - const OpenCLDebugInfo100Instructions ext_inst_key = - OpenCLDebugInfo100Instructions(ext_inst_index); - if (ext_inst_key == OpenCLDebugInfo100DebugScope) { + if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || + inst->ext_inst_type == + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + const CommonDebugInfoInstructions ext_inst_key = + CommonDebugInfoInstructions(ext_inst_index); + if (ext_inst_key == CommonDebugInfoDebugScope) { uint32_t inlined_at = 0; if (inst->num_words > kInlinedAtIndex) inlined_at = inst->words[kInlinedAtIndex]; @@ -65,7 +80,7 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { module()->SetContainsDebugInfo(); return true; } - if (ext_inst_key == OpenCLDebugInfo100DebugNoScope) { + if (ext_inst_key == CommonDebugInfoDebugNoScope) { last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); module()->SetContainsDebugInfo(); return true; @@ -94,14 +109,20 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { new Instruction(module()->context(), *inst, std::move(dbg_line_info_))); if (!spv_inst->dbg_line_insts().empty()) { if (extra_line_tracking_ && - (spv_inst->dbg_line_insts().back().opcode() != SpvOpNoLine)) { + (!spv_inst->dbg_line_insts().back().IsNoLine())) { last_line_inst_ = std::unique_ptr( spv_inst->dbg_line_insts().back().Clone(module()->context())); + if (last_line_inst_->IsDebugLineInst()) + last_line_inst_->SetResultId(module()->context()->TakeNextId()); } dbg_line_info_.clear(); } else if (last_line_inst_ != nullptr) { last_line_inst_->SetDebugScope(last_dbg_scope_); spv_inst->dbg_line_insts().push_back(*last_line_inst_); + last_line_inst_ = std::unique_ptr( + spv_inst->dbg_line_insts().back().Clone(module()->context())); + if (last_line_inst_->IsDebugLineInst()) + last_line_inst_->SetResultId(module()->context()->TakeNextId()); } const char* src = source_.c_str(); @@ -166,9 +187,12 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { module_->AddExtInstImport(std::move(spv_inst)); } else if (opcode == SpvOpMemoryModel) { module_->SetMemoryModel(std::move(spv_inst)); + } else if (opcode == SpvOpSamplerImageAddressingModeNV) { + module_->SetSampledImageAddressMode(std::move(spv_inst)); } else if (opcode == SpvOpEntryPoint) { module_->AddEntryPoint(std::move(spv_inst)); - } else if (opcode == SpvOpExecutionMode) { + } else if (opcode == SpvOpExecutionMode || + opcode == SpvOpExecutionModeId) { module_->AddExecutionMode(std::move(spv_inst)); } else if (IsDebug1Inst(opcode)) { module_->AddDebug1Inst(std::move(spv_inst)); @@ -230,6 +254,35 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { block_->AddInstruction(std::move(spv_inst)); break; } + default: { + Errorf(consumer_, src, loc, + "Debug info extension instruction other than DebugScope, " + "DebugNoScope, DebugFunctionDefinition, DebugDeclare, and " + "DebugValue found inside function", + opcode); + return false; + } + } + } else if (inst->ext_inst_type == + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + const NonSemanticShaderDebugInfo100Instructions ext_inst_key = + NonSemanticShaderDebugInfo100Instructions(ext_inst_index); + switch (ext_inst_key) { + case NonSemanticShaderDebugInfo100DebugDeclare: + case NonSemanticShaderDebugInfo100DebugValue: + case NonSemanticShaderDebugInfo100DebugScope: + case NonSemanticShaderDebugInfo100DebugNoScope: + case NonSemanticShaderDebugInfo100DebugFunctionDefinition: { + if (block_ == nullptr) { // Inside function but outside blocks + Errorf(consumer_, src, loc, + "Debug info extension instruction found inside function " + "but outside block", + opcode); + } else { + block_->AddInstruction(std::move(spv_inst)); + } + break; + } default: { Errorf(consumer_, src, loc, "Debug info extension instruction other than DebugScope, " diff --git a/source/opt/iterator.h b/source/opt/iterator.h index 2280582d..847e1bbd 100644 --- a/source/opt/iterator.h +++ b/source/opt/iterator.h @@ -142,7 +142,7 @@ inline IteratorRange make_range(const IteratorType& begin, template inline IteratorRange make_range(IteratorType&& begin, IteratorType&& end) { - return {std::move(begin), std::move(end)}; + return {std::forward(begin), std::forward(end)}; } // Returns a (begin, end) iterator pair for the given container. diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp index 205cd7a3..d11682f3 100644 --- a/source/opt/local_access_chain_convert_pass.cpp +++ b/source/opt/local_access_chain_convert_pass.cpp @@ -19,6 +19,7 @@ #include "ir_builder.h" #include "ir_context.h" #include "iterator.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -27,8 +28,6 @@ namespace { const uint32_t kStoreValIdInIdx = 1; const uint32_t kAccessChainPtrIdInIdx = 0; -const uint32_t kConstantValueInIdx = 0; -const uint32_t kTypeIntWidthInIdx = 0; } // anonymous namespace @@ -66,7 +65,19 @@ void LocalAccessChainConvertPass::AppendConstantOperands( ptrInst->ForEachInId([&iidIdx, &in_opnds, this](const uint32_t* iid) { if (iidIdx > 0) { const Instruction* cInst = get_def_use_mgr()->GetDef(*iid); - uint32_t val = cInst->GetSingleWordInOperand(kConstantValueInIdx); + const auto* constant_value = + context()->get_constant_mgr()->GetConstantFromInst(cInst); + assert(constant_value != nullptr && + "Expecting the index to be a constant."); + + // We take the sign extended value because OpAccessChain interprets the + // index as signed. + int64_t long_value = constant_value->GetSignExtendedValue(); + assert(long_value <= UINT32_MAX && long_value >= 0 && + "The index value is too large for a composite insert or extract " + "instruction."); + + uint32_t val = static_cast(long_value); in_opnds->push_back( {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {val}}); } @@ -168,13 +179,18 @@ bool LocalAccessChainConvertPass::GenAccessChainStoreReplacement( return true; } -bool LocalAccessChainConvertPass::IsConstantIndexAccessChain( +bool LocalAccessChainConvertPass::Is32BitConstantIndexAccessChain( const Instruction* acp) const { uint32_t inIdx = 0; return acp->WhileEachInId([&inIdx, this](const uint32_t* tid) { if (inIdx > 0) { Instruction* opInst = get_def_use_mgr()->GetDef(*tid); if (opInst->opcode() != SpvOpConstant) return false; + const auto* index = + context()->get_constant_mgr()->GetConstantFromInst(opInst); + int64_t index_value = index->GetSignExtendedValue(); + if (index_value > UINT32_MAX) return false; + if (index_value < 0) return false; } ++inIdx; return true; @@ -184,8 +200,8 @@ bool LocalAccessChainConvertPass::IsConstantIndexAccessChain( bool LocalAccessChainConvertPass::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) { - if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue || - user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) { + if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugValue || + user->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { return true; } SpvOp op = user->opcode(); @@ -223,14 +239,21 @@ void LocalAccessChainConvertPass::FindTargetVars(Function* func) { } // Rule out variables with nested access chains // TODO(): Convert nested access chains - if (IsNonPtrAccessChain(op) && ptrInst->GetSingleWordInOperand( + bool is_non_ptr_access_chain = IsNonPtrAccessChain(op); + if (is_non_ptr_access_chain && ptrInst->GetSingleWordInOperand( kAccessChainPtrIdInIdx) != varId) { seen_non_target_vars_.insert(varId); seen_target_vars_.erase(varId); break; } // Rule out variables accessed with non-constant indices - if (!IsConstantIndexAccessChain(ptrInst)) { + if (!Is32BitConstantIndexAccessChain(ptrInst)) { + seen_non_target_vars_.insert(varId); + seen_target_vars_.erase(varId); + break; + } + + if (is_non_ptr_access_chain && AnyIndexIsOutOfBounds(ptrInst)) { seen_non_target_vars_.insert(varId); seen_target_vars_.erase(varId); break; @@ -328,21 +351,26 @@ bool LocalAccessChainConvertPass::AllExtensionsSupported() const { return false; // If any extension not in allowlist, return false for (auto& ei : get_module()->extensions()) { - const char* extName = - reinterpret_cast(&ei.GetInOperand(0).words[0]); + const std::string extName = ei.GetInOperand(0).AsString(); if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) return false; } + // only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise + // around unknown extended + // instruction sets even if they are non-semantic + for (auto& inst : context()->module()->ext_inst_imports()) { + assert(inst.opcode() == SpvOpExtInstImport && + "Expecting an import of an extension's instruction set."); + const std::string extension_name = inst.GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, "NonSemantic.") && + extension_name != "NonSemantic.Shader.DebugInfo.100") { + return false; + } + } return true; } Pass::Status LocalAccessChainConvertPass::ProcessImpl() { - // If non-32-bit integer type in module, terminate processing - // TODO(): Handle non-32-bit integer constants in access chains - for (const Instruction& inst : get_module()->types_values()) - if (inst.opcode() == SpvOpTypeInt && - inst.GetSingleWordInOperand(kTypeIntWidthInIdx) != 32) - return Status::SuccessWithoutChange; // Do not process if module contains OpGroupDecorate. Additional // support required in KillNamesAndDecorates(). // TODO(greg-lunarg): Add support for OpGroupDecorate @@ -418,8 +446,51 @@ void LocalAccessChainConvertPass::InitExtensions() { "SPV_KHR_ray_query", "SPV_EXT_fragment_invocation_density", "SPV_KHR_terminate_invocation", + "SPV_KHR_subgroup_uniform_control_flow", + "SPV_KHR_integer_dot_product", + "SPV_EXT_shader_image_int64", + "SPV_KHR_non_semantic_info", + "SPV_KHR_uniform_group_instructions", + "SPV_KHR_fragment_shader_barycentric", }); } +bool LocalAccessChainConvertPass::AnyIndexIsOutOfBounds( + const Instruction* access_chain_inst) { + assert(IsNonPtrAccessChain(access_chain_inst->opcode())); + + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + auto constants = const_mgr->GetOperandConstants(access_chain_inst); + uint32_t base_pointer_id = access_chain_inst->GetSingleWordInOperand(0); + Instruction* base_pointer = get_def_use_mgr()->GetDef(base_pointer_id); + const analysis::Pointer* base_pointer_type = + type_mgr->GetType(base_pointer->type_id())->AsPointer(); + assert(base_pointer_type != nullptr && + "The base of the access chain is not a pointer."); + const analysis::Type* current_type = base_pointer_type->pointee_type(); + for (uint32_t i = 1; i < access_chain_inst->NumInOperands(); ++i) { + if (IsIndexOutOfBounds(constants[i], current_type)) { + return true; + } + + uint32_t index = + (constants[i] + ? static_cast(constants[i]->GetZeroExtendedValue()) + : 0); + current_type = type_mgr->GetMemberType(current_type, {index}); + } + + return false; +} + +bool LocalAccessChainConvertPass::IsIndexOutOfBounds( + const analysis::Constant* index, const analysis::Type* type) const { + if (index == nullptr) { + return false; + } + return index->GetZeroExtendedValue() >= type->NumberOfComponents(); +} + } // namespace opt } // namespace spvtools diff --git a/source/opt/local_access_chain_convert_pass.h b/source/opt/local_access_chain_convert_pass.h index 552062e5..c3731b1c 100644 --- a/source/opt/local_access_chain_convert_pass.h +++ b/source/opt/local_access_chain_convert_pass.h @@ -81,7 +81,7 @@ class LocalAccessChainConvertPass : public MemPass { std::vector* in_opnds); // Create a load/insert/store equivalent to a store of - // |valId| through (constant index) access chaing |ptrInst|. + // |valId| through (constant index) access chain |ptrInst|. // Append to |newInsts|. Returns true if successful. bool GenAccessChainStoreReplacement( const Instruction* ptrInst, uint32_t valId, @@ -94,8 +94,9 @@ class LocalAccessChainConvertPass : public MemPass { bool ReplaceAccessChainLoad(const Instruction* address_inst, Instruction* original_load); - // Return true if all indices of access chain |acp| are OpConstant integers - bool IsConstantIndexAccessChain(const Instruction* acp) const; + // Return true if all indices of the access chain |acp| are OpConstant + // integers whose signed values can be represented as unsigned 32-bit values. + bool Is32BitConstantIndexAccessChain(const Instruction* acp) const; // Identify all function scope variables of target type which are // accessed only with loads, stores and access chains with constant @@ -110,6 +111,17 @@ class LocalAccessChainConvertPass : public MemPass { // Returns a status to indicate success or failure, and change or no change. Status ConvertLocalAccessChains(Function* func); + // Returns true one of the indexes in the |access_chain_inst| is definitly out + // of bounds. If the size of the type or the value of the index is unknown, + // then it will be considered in-bounds. + bool AnyIndexIsOutOfBounds(const Instruction* access_chain_inst); + + // Returns true if getting element |index| from |type| would be out-of-bounds. + // If |index| is nullptr or the size of the type are unknown, then it will be + // considered in-bounds. + bool IsIndexOutOfBounds(const analysis::Constant* index, + const analysis::Type* type) const; + // Initialize extensions allowlist void InitExtensions(); diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp index 5f35ee1a..a58e8e4c 100644 --- a/source/opt/local_single_block_elim_pass.cpp +++ b/source/opt/local_single_block_elim_pass.cpp @@ -19,6 +19,7 @@ #include #include "source/opt/iterator.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -31,9 +32,9 @@ const uint32_t kStoreValIdInIdx = 1; 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) { + auto dbg_op = user->GetCommonDebugOpcode(); + if (dbg_op == CommonDebugInfoDebugDeclare || + dbg_op == CommonDebugInfoDebugValue) { return true; } SpvOp op = user->opcode(); @@ -183,11 +184,22 @@ void LocalSingleBlockLoadStoreElimPass::Initialize() { bool LocalSingleBlockLoadStoreElimPass::AllExtensionsSupported() const { // If any extension not in allowlist, return false for (auto& ei : get_module()->extensions()) { - const char* extName = - reinterpret_cast(&ei.GetInOperand(0).words[0]); + const std::string extName = ei.GetInOperand(0).AsString(); if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) return false; } + // only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise + // around unknown extended + // instruction sets even if they are non-semantic + for (auto& inst : context()->module()->ext_inst_imports()) { + assert(inst.opcode() == SpvOpExtInstImport && + "Expecting an import of an extension's instruction set."); + const std::string extension_name = inst.GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, "NonSemantic.") && + extension_name != "NonSemantic.Shader.DebugInfo.100") { + return false; + } + } return true; } @@ -209,7 +221,7 @@ Pass::Status LocalSingleBlockLoadStoreElimPass::ProcessImpl() { return LocalSingleBlockLoadStoreElim(fp); }; - bool modified = context()->ProcessEntryPointCallTree(pfn); + bool modified = context()->ProcessReachableCallTree(pfn); return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } @@ -270,6 +282,12 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() { "SPV_EXT_fragment_invocation_density", "SPV_EXT_physical_storage_buffer", "SPV_KHR_terminate_invocation", + "SPV_KHR_subgroup_uniform_control_flow", + "SPV_KHR_integer_dot_product", + "SPV_EXT_shader_image_int64", + "SPV_KHR_non_semantic_info", + "SPV_KHR_uniform_group_instructions", + "SPV_KHR_fragment_shader_barycentric", }); } diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp index d322a2fb..81648c7b 100644 --- a/source/opt/local_single_store_elim_pass.cpp +++ b/source/opt/local_single_store_elim_pass.cpp @@ -19,6 +19,7 @@ #include "source/cfa.h" #include "source/latest_version_glsl_std_450_header.h" #include "source/opt/iterator.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -48,11 +49,22 @@ bool LocalSingleStoreElimPass::LocalSingleStoreElim(Function* func) { bool LocalSingleStoreElimPass::AllExtensionsSupported() const { // If any extension not in allowlist, return false for (auto& ei : get_module()->extensions()) { - const char* extName = - reinterpret_cast(&ei.GetInOperand(0).words[0]); + const std::string extName = ei.GetInOperand(0).AsString(); if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) return false; } + // only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise + // around unknown extended + // instruction sets even if they are non-semantic + for (auto& inst : context()->module()->ext_inst_imports()) { + assert(inst.opcode() == SpvOpExtInstImport && + "Expecting an import of an extension's instruction set."); + const std::string extension_name = inst.GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, "NonSemantic.") && + extension_name != "NonSemantic.Shader.DebugInfo.100") { + return false; + } + } return true; } @@ -67,7 +79,7 @@ Pass::Status LocalSingleStoreElimPass::ProcessImpl() { ProcessFunction pfn = [this](Function* fp) { return LocalSingleStoreElim(fp); }; - bool modified = context()->ProcessEntryPointCallTree(pfn); + bool modified = context()->ProcessReachableCallTree(pfn); return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } @@ -123,6 +135,12 @@ void LocalSingleStoreElimPass::InitExtensionAllowList() { "SPV_EXT_fragment_invocation_density", "SPV_EXT_physical_storage_buffer", "SPV_KHR_terminate_invocation", + "SPV_KHR_subgroup_uniform_control_flow", + "SPV_KHR_integer_dot_product", + "SPV_EXT_shader_image_int64", + "SPV_KHR_non_semantic_info", + "SPV_KHR_uniform_group_instructions", + "SPV_KHR_fragment_shader_barycentric", }); } bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) { @@ -157,29 +175,9 @@ bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) { bool LocalSingleStoreElimPass::RewriteDebugDeclares(Instruction* store_inst, uint32_t var_id) { - std::unordered_set invisible_decls; uint32_t value_id = store_inst->GetSingleWordInOperand(1); - bool modified = - context()->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible( - store_inst, var_id, value_id, store_inst, &invisible_decls); - - // For cases like the argument passing for an inlined function, the value - // assignment is out of DebugDeclare's scope, but we have to preserve the - // value assignment information using DebugValue. Generally, we need - // ssa-rewrite analysis to decide a proper value assignment but at this point - // we confirm that |var_id| has a single store. We can safely add DebugValue. - if (!invisible_decls.empty()) { - BasicBlock* store_block = context()->get_instr_block(store_inst); - DominatorAnalysis* dominator_analysis = - context()->GetDominatorAnalysis(store_block->GetParent()); - for (auto* decl : invisible_decls) { - if (dominator_analysis->Dominates(store_inst, decl)) { - context()->get_debug_info_mgr()->AddDebugValueForDecl(decl, value_id, - decl, store_inst); - modified = true; - } - } - } + bool modified = context()->get_debug_info_mgr()->AddDebugValueForVariable( + store_inst, var_id, value_id, store_inst); modified |= context()->get_debug_info_mgr()->KillDebugDeclares(var_id); return modified; } @@ -221,9 +219,9 @@ Instruction* LocalSingleStoreElimPass::FindSingleStoreAndCheckUses( case SpvOpCopyObject: break; case SpvOpExtInst: { - auto dbg_op = user->GetOpenCL100DebugOpcode(); - if (dbg_op == OpenCLDebugInfo100DebugDeclare || - dbg_op == OpenCLDebugInfo100DebugValue) { + auto dbg_op = user->GetCommonDebugOpcode(); + if (dbg_op == CommonDebugInfoDebugDeclare || + dbg_op == CommonDebugInfoDebugValue) { break; } return nullptr; @@ -290,9 +288,9 @@ bool LocalSingleStoreElimPass::RewriteLoads( bool modified = false; for (Instruction* use : uses) { if (use->opcode() == SpvOpStore) continue; - auto dbg_op = use->GetOpenCL100DebugOpcode(); - if (dbg_op == OpenCLDebugInfo100DebugDeclare || - dbg_op == OpenCLDebugInfo100DebugValue) + auto dbg_op = use->GetCommonDebugOpcode(); + if (dbg_op == CommonDebugInfoDebugDeclare || + dbg_op == CommonDebugInfoDebugValue) continue; if (use->opcode() == SpvOpLoad && dominator_analysis->Dominates(store_inst, use)) { diff --git a/source/opt/loop_descriptor.cpp b/source/opt/loop_descriptor.cpp index b5b56309..13982d18 100644 --- a/source/opt/loop_descriptor.cpp +++ b/source/opt/loop_descriptor.cpp @@ -497,7 +497,8 @@ void Loop::ComputeLoopStructuredOrder( // continue blocks that must be copied to retain the structured order. // The structured order will include these. std::list order; - cfg.ComputeStructuredOrder(loop_header_->GetParent(), loop_header_, &order); + cfg.ComputeStructuredOrder(loop_header_->GetParent(), loop_header_, + loop_merge_, &order); for (BasicBlock* bb : order) { if (bb == GetMergeBlock()) { break; @@ -719,7 +720,7 @@ bool Loop::FindNumberOfIterations(const Instruction* induction, step_value = -step_value; } - // Find the inital value of the loop and make sure it is a constant integer. + // Find the initial value of the loop and make sure it is a constant integer. int64_t init_value = 0; if (!GetInductionInitValue(induction, &init_value)) return false; @@ -751,9 +752,13 @@ bool Loop::FindNumberOfIterations(const Instruction* induction, // We retrieve the number of iterations using the following formula, diff / // |step_value| where diff is calculated differently according to the // |condition| and uses the |condition_value| and |init_value|. If diff / -// |step_value| is NOT cleanly divisable then we add one to the sum. +// |step_value| is NOT cleanly divisible then we add one to the sum. int64_t Loop::GetIterations(SpvOp condition, int64_t condition_value, int64_t init_value, int64_t step_value) const { + if (step_value == 0) { + return 0; + } + int64_t diff = 0; switch (condition) { @@ -795,7 +800,7 @@ int64_t Loop::GetIterations(SpvOp condition, int64_t condition_value, // If the condition is not met to begin with the loop will never iterate. if (!(init_value >= condition_value)) return 0; - // We subract one to make it the same as SpvOpGreaterThan as it is + // We subtract one to make it the same as SpvOpGreaterThan as it is // functionally equivalent. diff = init_value - (condition_value - 1); diff --git a/source/opt/loop_descriptor.h b/source/opt/loop_descriptor.h index 4b4f8bc7..df012274 100644 --- a/source/opt/loop_descriptor.h +++ b/source/opt/loop_descriptor.h @@ -395,10 +395,11 @@ class Loop { // Sets |merge| as the loop merge block. No checks are performed here. inline void SetMergeBlockImpl(BasicBlock* merge) { loop_merge_ = merge; } - // Each differnt loop |condition| affects how we calculate the number of + // Each different loop |condition| affects how we calculate the number of // iterations using the |condition_value|, |init_value|, and |step_values| of // the induction variable. This method will return the number of iterations in - // a loop with those values for a given |condition|. + // a loop with those values for a given |condition|. Returns 0 if the number + // of iterations could not be computed. int64_t GetIterations(SpvOp condition, int64_t condition_value, int64_t init_value, int64_t step_value) const; diff --git a/source/opt/loop_fission.cpp b/source/opt/loop_fission.cpp index 0678113c..b4df8c62 100644 --- a/source/opt/loop_fission.cpp +++ b/source/opt/loop_fission.cpp @@ -29,7 +29,7 @@ // 2 - For each loop in the list, group each instruction into a set of related // instructions by traversing each instructions users and operands recursively. // We stop if we encounter an instruction we have seen before or an instruction -// which we don't consider relevent (i.e OpLoopMerge). We then group these +// which we don't consider relevant (i.e OpLoopMerge). We then group these // groups into two different sets, one for the first loop and one for the // second. // @@ -453,7 +453,7 @@ Pass::Status LoopFissionPass::Process() { for (Function& f : *context()->module()) { // We collect all the inner most loops in the function and run the loop // splitting util on each. The reason we do this is to allow us to iterate - // over each, as creating new loops will invalidate the the loop iterator. + // over each, as creating new loops will invalidate the loop iterator. std::vector inner_most_loops{}; LoopDescriptor& loop_descriptor = *context()->GetLoopDescriptor(&f); for (Loop& loop : loop_descriptor) { diff --git a/source/opt/loop_fission.h b/source/opt/loop_fission.h index e7a59c18..9bc12c0f 100644 --- a/source/opt/loop_fission.h +++ b/source/opt/loop_fission.h @@ -33,7 +33,7 @@ namespace opt { class LoopFissionPass : public Pass { public: - // Fuction used to determine if a given loop should be split. Takes register + // Function used to determine if a given loop should be split. Takes register // pressure region for that loop as a parameter and returns true if the loop // should be split. using FissionCriteriaFunction = diff --git a/source/opt/loop_fusion.cpp b/source/opt/loop_fusion.cpp index 07d171a0..f3aab283 100644 --- a/source/opt/loop_fusion.cpp +++ b/source/opt/loop_fusion.cpp @@ -165,7 +165,7 @@ bool LoopFusion::AreCompatible() { // Check adjacency, |loop_0_| should come just before |loop_1_|. // There is always at least one block between loops, even if it's empty. - // We'll check at most 2 preceeding blocks. + // We'll check at most 2 preceding blocks. auto pre_header_1 = loop_1_->GetPreHeaderBlock(); @@ -712,7 +712,7 @@ void LoopFusion::Fuse() { ld->RemoveLoop(loop_1_); - // Kill unnessecary instructions and remove all empty blocks. + // Kill unnecessary instructions and remove all empty blocks. for (auto inst : instr_to_delete) { context_->KillInst(inst); } diff --git a/source/opt/loop_fusion.h b/source/opt/loop_fusion.h index d61d6783..769da5f1 100644 --- a/source/opt/loop_fusion.h +++ b/source/opt/loop_fusion.h @@ -40,7 +40,7 @@ class LoopFusion { // That means: // * they both have one induction variable // * they have the same upper and lower bounds - // - same inital value + // - same initial value // - same condition // * they have the same update step // * they are adjacent, with |loop_0| appearing before |loop_1| diff --git a/source/opt/loop_fusion_pass.h b/source/opt/loop_fusion_pass.h index 3a0be600..9d5b7ccd 100644 --- a/source/opt/loop_fusion_pass.h +++ b/source/opt/loop_fusion_pass.h @@ -33,7 +33,7 @@ class LoopFusionPass : public Pass { // Processes the given |module|. Returns Status::Failure if errors occur when // processing. Returns the corresponding Status::Success if processing is - // succesful to indicate whether changes have been made to the modue. + // successful to indicate whether changes have been made to the module. Status Process() override; private: diff --git a/source/opt/loop_peeling.h b/source/opt/loop_peeling.h index 413f896f..2a55fe44 100644 --- a/source/opt/loop_peeling.h +++ b/source/opt/loop_peeling.h @@ -261,7 +261,7 @@ class LoopPeelingPass : public Pass { // Processes the given |module|. Returns Status::Failure if errors occur when // processing. Returns the corresponding Status::Success if processing is - // succesful to indicate whether changes have been made to the modue. + // successful to indicate whether changes have been made to the module. Pass::Status Process() override; private: diff --git a/source/opt/loop_unroller.cpp b/source/opt/loop_unroller.cpp index 6cdced46..6f4e6f41 100644 --- a/source/opt/loop_unroller.cpp +++ b/source/opt/loop_unroller.cpp @@ -163,7 +163,7 @@ struct LoopUnrollState { }; // This class implements the actual unrolling. It uses a LoopUnrollState to -// maintain the state of the unrolling inbetween steps. +// maintain the state of the unrolling in between steps. class LoopUnrollerUtilsImpl { public: using BasicBlockListTy = std::vector>; @@ -209,7 +209,7 @@ class LoopUnrollerUtilsImpl { // Add all blocks_to_add_ to function_ at the |insert_point|. void AddBlocksToFunction(const BasicBlock* insert_point); - // Duplicates the |old_loop|, cloning each body and remaping the ids without + // Duplicates the |old_loop|, cloning each body and remapping the ids without // removing instructions or changing relative structure. Result will be stored // in |new_loop|. void DuplicateLoop(Loop* old_loop, Loop* new_loop); @@ -241,7 +241,7 @@ class LoopUnrollerUtilsImpl { // Remap all the in |basic_block| to new IDs and keep the mapping of new ids // to old // ids. |loop| is used to identify special loop blocks (header, continue, - // ect). + // etc). void AssignNewResultIds(BasicBlock* basic_block); // Using the map built by AssignNewResultIds, replace the uses in |inst| @@ -320,7 +320,7 @@ class LoopUnrollerUtilsImpl { // and then be remapped at the end. std::vector loop_phi_instructions_; - // The number of loop iterations that the loop would preform pre-unroll. + // The number of loop iterations that the loop would perform pre-unroll. size_t number_of_loop_iterations_; // The amount that the loop steps each iteration. @@ -384,6 +384,7 @@ void LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop, std::unique_ptr new_label{new Instruction( context_, SpvOp::SpvOpLabel, 0, context_->TakeNextId(), {})}; std::unique_ptr new_exit_bb{new BasicBlock(std::move(new_label))}; + new_exit_bb->SetParent(&function_); // Save the id of the block before we move it. uint32_t new_merge_id = new_exit_bb->id(); @@ -760,7 +761,7 @@ void LoopUnrollerUtilsImpl::FoldConditionBlock(BasicBlock* condition_block, IRContext::Analysis::kAnalysisInstrToBlockMapping); Instruction* new_branch = builder.AddBranch(new_target); - new_branch->set_dbg_line_insts(lines); + if (!lines.empty()) new_branch->AddDebugLine(&lines.back()); new_branch->SetDebugScope(scope); } @@ -797,6 +798,9 @@ void LoopUnrollerUtilsImpl::CloseUnrolledLoop(Loop* loop) { for (BasicBlock* block : loop_blocks_inorder_) { RemapOperands(block); } + for (auto& block_itr : blocks_to_add_) { + RemapOperands(block_itr.get()); + } // Rewrite the last phis, since they may still reference the original phi. for (Instruction* last_phi : state_.previous_phis_) { @@ -836,7 +840,7 @@ void LoopUnrollerUtilsImpl::DuplicateLoop(Loop* old_loop, Loop* new_loop) { new_loop->SetMergeBlock(new_merge); } -// Whenever the utility copies a block it stores it in a tempory buffer, this +// Whenever the utility copies a block it stores it in a temporary buffer, this // function adds the buffer into the Function. The blocks will be inserted // after the block |insert_point|. void LoopUnrollerUtilsImpl::AddBlocksToFunction( @@ -870,6 +874,10 @@ void LoopUnrollerUtilsImpl::AssignNewResultIds(BasicBlock* basic_block) { def_use_mgr->AnalyzeInstDefUse(basic_block->GetLabelInst()); for (Instruction& inst : *basic_block) { + // Do def/use analysis on new lines + for (auto& line : inst.dbg_line_insts()) + def_use_mgr->AnalyzeInstDefUse(&line); + uint32_t old_id = inst.result_id(); // Ignore stores etc. @@ -989,6 +997,20 @@ bool LoopUtils::CanPerformUnroll() { if (!loop_->FindNumberOfIterations(induction, &*condition->ctail(), nullptr)) return false; +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + // ClusterFuzz/OSS-Fuzz is likely to yield examples with very high loop + // iteration counts. This can cause timeouts and memouts during fuzzing that + // are not classed as bugs. To avoid this noise, loop unrolling is not applied + // to loops with large iteration counts when fuzzing. + const size_t kFuzzerIterationLimit = 100; + size_t num_iterations; + loop_->FindNumberOfIterations(induction, &*condition->ctail(), + &num_iterations); + if (num_iterations > kFuzzerIterationLimit) { + return false; + } +#endif + // Make sure the latch block is a unconditional branch to the header // block. const Instruction& branch = *loop_->GetLatchBlock()->ctail(); @@ -1095,6 +1117,10 @@ void LoopUtils::Finalize() { Pass::Status LoopUnroller::Process() { bool changed = false; for (Function& f : *context()->module()) { + if (f.IsDeclaration()) { + continue; + } + LoopDescriptor* LD = context()->GetLoopDescriptor(&f); for (Loop& loop : *LD) { LoopUtils loop_utils{context(), &loop}; diff --git a/source/opt/loop_unswitch_pass.cpp b/source/opt/loop_unswitch_pass.cpp index d805ecf3..1ee7e5e2 100644 --- a/source/opt/loop_unswitch_pass.cpp +++ b/source/opt/loop_unswitch_pass.cpp @@ -118,7 +118,7 @@ class LoopUnswitch { // Find a value that can be used to select the default path. // If none are possible, then it will just use 0. The value does not matter - // because this path will never be taken becaues the new switch outside of + // because this path will never be taken because the new switch outside of // the loop cannot select this path either. std::vector existing_values; for (uint32_t i = 2; i < switch_inst->NumInOperands(); i += 2) { diff --git a/source/opt/loop_unswitch_pass.h b/source/opt/loop_unswitch_pass.h index 3ecdd611..4f7295d4 100644 --- a/source/opt/loop_unswitch_pass.h +++ b/source/opt/loop_unswitch_pass.h @@ -30,7 +30,7 @@ class LoopUnswitchPass : public Pass { // Processes the given |module|. Returns Status::Failure if errors occur when // processing. Returns the corresponding Status::Success if processing is - // succesful to indicate whether changes have been made to the modue. + // successful to indicate whether changes have been made to the module. Pass::Status Process() override; private: diff --git a/source/opt/loop_utils.h b/source/opt/loop_utils.h index a4e61900..70060fc4 100644 --- a/source/opt/loop_utils.h +++ b/source/opt/loop_utils.h @@ -123,7 +123,7 @@ class LoopUtils { // Clone the |loop_| and make the new loop branch to the second loop on exit. Loop* CloneAndAttachLoopToHeader(LoopCloningResult* cloning_result); - // Perfom a partial unroll of |loop| by given |factor|. This will copy the + // Perform a partial unroll of |loop| by given |factor|. This will copy the // body of the loop |factor| times. So a |factor| of one would give a new loop // with the original body plus one unrolled copy body. bool PartiallyUnroll(size_t factor); @@ -139,7 +139,7 @@ class LoopUtils { // 1. That the loop is in structured order. // 2. That the continue block is a branch to the header. // 3. That the only phi used in the loop is the induction variable. - // TODO(stephen@codeplay.com): This is a temporary mesure, after the loop is + // TODO(stephen@codeplay.com): This is a temporary measure, after the loop is // converted into LCSAA form and has a single entry and exit we can rewrite // the other phis. // 4. That this is an inner most loop, or that loops contained within this diff --git a/source/opt/mem_pass.cpp b/source/opt/mem_pass.cpp index 57387984..ca4889b7 100644 --- a/source/opt/mem_pass.cpp +++ b/source/opt/mem_pass.cpp @@ -20,7 +20,6 @@ #include #include -#include "OpenCLDebugInfo100.h" #include "source/cfa.h" #include "source/opt/basic_block.h" #include "source/opt/dominator_analysis.h" @@ -87,8 +86,8 @@ bool MemPass::IsPtr(uint32_t ptrId) { } const SpvOp op = ptrInst->opcode(); if (op == SpvOpVariable || IsNonPtrAccessChain(op)) return true; - if (op != SpvOpFunctionParameter) return false; const uint32_t varTypeId = ptrInst->type_id(); + if (varTypeId == 0) return false; const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId); return varTypeInst->opcode() == SpvOpTypePointer; } @@ -226,9 +225,9 @@ MemPass::MemPass() {} 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) { + auto dbg_op = user->GetCommonDebugOpcode(); + if (dbg_op == CommonDebugInfoDebugDeclare || + dbg_op == CommonDebugInfoDebugValue) { return true; } SpvOp op = user->opcode(); diff --git a/source/opt/merge_return_pass.cpp b/source/opt/merge_return_pass.cpp index f1601049..7710deae 100644 --- a/source/opt/merge_return_pass.cpp +++ b/source/opt/merge_return_pass.cpp @@ -111,7 +111,9 @@ bool MergeReturnPass::ProcessStructured( } RecordImmediateDominators(function); - AddSingleCaseSwitchAroundFunction(); + if (!AddSingleCaseSwitchAroundFunction()) { + return false; + } std::list order; cfg()->ComputeStructuredOrder(function, &*function->begin(), &order); @@ -429,6 +431,7 @@ bool MergeReturnPass::BreakFromConstruct( std::list* order, Instruction* break_merge_inst) { // Make sure the CFG is build here. If we don't then it becomes very hard // to know which new blocks need to be updated. + context()->InvalidateAnalyses(IRContext::kAnalysisCFG); context()->BuildInvalidAnalyses(IRContext::kAnalysisCFG); // When predicating, be aware of whether this block is a header block, a @@ -770,7 +773,7 @@ void MergeReturnPass::InsertAfterElement(BasicBlock* element, list->insert(pos, new_element); } -void MergeReturnPass::AddSingleCaseSwitchAroundFunction() { +bool MergeReturnPass::AddSingleCaseSwitchAroundFunction() { CreateReturnBlock(); CreateReturn(final_return_block_); @@ -778,7 +781,10 @@ void MergeReturnPass::AddSingleCaseSwitchAroundFunction() { cfg()->RegisterBlock(final_return_block_); } - CreateSingleCaseSwitch(final_return_block_); + if (!CreateSingleCaseSwitch(final_return_block_)) { + return false; + } + return true; } BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) { @@ -813,7 +819,7 @@ BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) { return new_block; } -void MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) { +bool MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) { // Insert the switch before any code is run. We have to split the entry // block to make sure the OpVariable instructions remain in the entry block. BasicBlock* start_block = &*function_->begin(); @@ -830,13 +836,17 @@ void MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) { context(), start_block, IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); - builder.AddSwitch(builder.GetUintConstantId(0u), old_block->id(), {}, - merge_target->id()); + uint32_t const_zero_id = builder.GetUintConstantId(0u); + if (const_zero_id == 0) { + return false; + } + builder.AddSwitch(const_zero_id, old_block->id(), {}, merge_target->id()); if (context()->AreAnalysesValid(IRContext::kAnalysisCFG)) { cfg()->RegisterBlock(old_block); cfg()->AddEdges(start_block); } + return true; } bool MergeReturnPass::HasNontrivialUnreachableBlocks(Function* function) { diff --git a/source/opt/merge_return_pass.h b/source/opt/merge_return_pass.h index 06a3e7b5..d15db2f6 100644 --- a/source/opt/merge_return_pass.h +++ b/source/opt/merge_return_pass.h @@ -118,8 +118,6 @@ class MergeReturnPass : public MemPass { StructuredControlState(Instruction* break_merge, Instruction* merge) : break_merge_(break_merge), current_merge_(merge) {} - StructuredControlState(const StructuredControlState&) = default; - bool InBreakable() const { return break_merge_; } bool InStructuredFlow() const { return CurrentMergeId() != 0; } @@ -247,7 +245,7 @@ class MergeReturnPass : public MemPass { // Add new phi nodes for any id that no longer dominate all of it uses. A phi // node is added to a block |bb| for an id if the id is defined between the - // original immediate dominator of |bb| and its new immidiate dominator. It + // original immediate dominator of |bb| and its new immediate dominator. It // is assumed that at this point there are no unreachable blocks in the // control flow graph. void AddNewPhiNodes(); @@ -273,11 +271,11 @@ class MergeReturnPass : public MemPass { void InsertAfterElement(BasicBlock* element, BasicBlock* new_element, std::list* list); - // Creates a single case switch around all of the exectuable code of the + // Creates a single case switch around all of the executable code of the // current function where the switch and case value are both zero and the // default is the merge block. Returns after the switch is executed. Sets // |final_return_block_|. - void AddSingleCaseSwitchAroundFunction(); + bool AddSingleCaseSwitchAroundFunction(); // Creates a new basic block that branches to |header_label_id|. Returns the // new basic block. The block will be the second last basic block in the @@ -286,7 +284,7 @@ class MergeReturnPass : public MemPass { // Creates a one case switch around the executable code of the function with // |merge_target| as the merge node. - void CreateSingleCaseSwitch(BasicBlock* merge_target); + bool CreateSingleCaseSwitch(BasicBlock* merge_target); // Returns true if |function| has an unreachable block that is not a continue // target that simply branches back to the header, or a merge block containing diff --git a/source/opt/module.cpp b/source/opt/module.cpp index 0c886010..c98af8f5 100644 --- a/source/opt/module.cpp +++ b/source/opt/module.cpp @@ -90,6 +90,8 @@ void Module::ForEachInst(const std::function& f, DELEGATE(extensions_); DELEGATE(ext_inst_imports_); if (memory_model_) memory_model_->ForEachInst(f, run_on_debug_line_insts); + if (sampled_image_address_mode_) + sampled_image_address_mode_->ForEachInst(f, run_on_debug_line_insts); DELEGATE(entry_points_); DELEGATE(execution_modes_); DELEGATE(debugs1_); @@ -114,6 +116,9 @@ void Module::ForEachInst(const std::function& f, if (memory_model_) static_cast(memory_model_.get()) ->ForEachInst(f, run_on_debug_line_insts); + if (sampled_image_address_mode_) + static_cast(sampled_image_address_mode_.get()) + ->ForEachInst(f, run_on_debug_line_insts); for (auto& i : entry_points_) DELEGATE(i); for (auto& i : execution_modes_) DELEGATE(i); for (auto& i : debugs1_) DELEGATE(i); @@ -139,26 +144,27 @@ void Module::ToBinary(std::vector* binary, bool skip_nop) const { // TODO(antiagainst): should we change the generator number? binary->push_back(header_.generator); binary->push_back(header_.bound); - binary->push_back(header_.reserved); + binary->push_back(header_.schema); size_t bound_idx = binary->size() - 2; DebugScope last_scope(kNoDebugScope, kNoInlinedAt); const Instruction* last_line_inst = nullptr; bool between_merge_and_branch = false; + bool between_label_and_phi_var = false; auto write_inst = [binary, skip_nop, &last_scope, &last_line_inst, - &between_merge_and_branch, this](const Instruction* i) { + &between_merge_and_branch, &between_label_and_phi_var, + this](const Instruction* i) { // Skip emitting line instructions between merge and branch instructions. auto opcode = i->opcode(); - if (between_merge_and_branch && - (opcode == SpvOpLine || opcode == SpvOpNoLine)) { + if (between_merge_and_branch && i->IsLineInst()) { return; } between_merge_and_branch = false; if (last_line_inst != nullptr) { - // If the current instruction is OpLine and it is the same with - // the last line instruction that is still effective (can be applied + // If the current instruction is OpLine or DebugLine and it is the same + // as the last line instruction that is still effective (can be applied // to the next instruction), we skip writing the current instruction. - if (opcode == SpvOpLine) { + if (i->IsLine()) { uint32_t operand_index = 0; if (last_line_inst->WhileEachInOperand( [&operand_index, i](const uint32_t* word) { @@ -167,39 +173,67 @@ void Module::ToBinary(std::vector* binary, bool skip_nop) const { })) { return; } - } else if (opcode != SpvOpNoLine && i->dbg_line_insts().empty()) { + } else if (!i->IsNoLine() && i->dbg_line_insts().empty()) { // If the current instruction does not have the line information, // the last line information is not effective any more. Emit OpNoLine - // to specify it. - binary->push_back((1 << 16) | static_cast(SpvOpNoLine)); + // or DebugNoLine to specify it. + uint32_t shader_set_id = context() + ->get_feature_mgr() + ->GetExtInstImportId_Shader100DebugInfo(); + if (shader_set_id != 0) { + binary->push_back((5 << 16) | static_cast(SpvOpExtInst)); + binary->push_back(context()->get_type_mgr()->GetVoidTypeId()); + binary->push_back(context()->TakeNextId()); + binary->push_back(shader_set_id); + binary->push_back(NonSemanticShaderDebugInfo100DebugNoLine); + } else { + binary->push_back((1 << 16) | static_cast(SpvOpNoLine)); + } last_line_inst = nullptr; } } + + if (opcode == SpvOpLabel) { + between_label_and_phi_var = true; + } else if (opcode != SpvOpVariable && opcode != SpvOpPhi && + !spvtools::opt::IsOpLineInst(opcode)) { + between_label_and_phi_var = false; + } + if (!(skip_nop && i->IsNop())) { const auto& scope = i->GetDebugScope(); if (scope != last_scope) { - // Emit DebugScope |scope| to |binary|. - auto dbg_inst = ext_inst_debuginfo_.begin(); - scope.ToBinary(dbg_inst->type_id(), context()->TakeNextId(), - dbg_inst->GetSingleWordOperand(2), binary); + // Can only emit nonsemantic instructions after all phi instructions + // in a block so don't emit scope instructions before phi instructions + // for NonSemantic.Shader.DebugInfo.100. + if (!between_label_and_phi_var || + context() + ->get_feature_mgr() + ->GetExtInstImportId_OpenCL100DebugInfo()) { + // Emit DebugScope |scope| to |binary|. + auto dbg_inst = ext_inst_debuginfo_.begin(); + scope.ToBinary(dbg_inst->type_id(), context()->TakeNextId(), + dbg_inst->GetSingleWordOperand(2), binary); + } last_scope = scope; } i->ToBinaryWithoutAttachedDebugInsts(binary); } // Update the last line instruction. - if (spvOpcodeIsBlockTerminator(opcode) || opcode == SpvOpNoLine) { + if (spvOpcodeIsBlockTerminator(opcode) || i->IsNoLine()) { last_line_inst = nullptr; } else if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) { between_merge_and_branch = true; last_line_inst = nullptr; - } else if (opcode == SpvOpLine) { + } else if (i->IsLine()) { last_line_inst = i; } }; ForEachInst(write_inst, true); - // We create new instructions for DebugScope. The bound must be updated. + // We create new instructions for DebugScope and DebugNoLine. The bound must + // be updated. binary->data()[bound_idx] = header_.bound; } @@ -231,9 +265,7 @@ bool Module::HasExplicitCapability(uint32_t cap) { uint32_t Module::GetExtInstImportId(const char* extstr) { for (auto& ei : ext_inst_imports_) - if (!strcmp(extstr, - reinterpret_cast(&(ei.GetInOperand(0).words[0])))) - return ei.result_id(); + if (!ei.GetInOperand(0).AsString().compare(extstr)) return ei.result_id(); return 0; } diff --git a/source/opt/module.h b/source/opt/module.h index d609b603..7a6be460 100644 --- a/source/opt/module.h +++ b/source/opt/module.h @@ -36,7 +36,7 @@ struct ModuleHeader { uint32_t version; uint32_t generator; uint32_t bound; - uint32_t reserved; + uint32_t schema; }; // A SPIR-V module. It contains all the information for a SPIR-V module and @@ -61,7 +61,7 @@ class Module { } // Returns the Id bound. - uint32_t IdBound() { return header_.bound; } + uint32_t IdBound() const { return header_.bound; } // Returns the current Id bound and increases it to the next available value. // If the id bound has already reached its maximum value, then 0 is returned. @@ -83,6 +83,9 @@ class Module { // Set the memory model for this module. inline void SetMemoryModel(std::unique_ptr m); + // Set the sampled image addressing mode for this module. + inline void SetSampledImageAddressMode(std::unique_ptr m); + // Appends an entry point instruction to this module. inline void AddEntryPoint(std::unique_ptr e); @@ -103,8 +106,8 @@ class Module { // This is due to decision by the SPIR Working Group, pending publication. inline void AddDebug3Inst(std::unique_ptr d); - // Appends a debug info extension (OpenCL.DebugInfo.100 or DebugInfo) - // instruction to this module. + // Appends a debug info extension (OpenCL.DebugInfo.100, + // NonSemantic.Shader.DebugInfo.100, or DebugInfo) instruction to this module. inline void AddExtInstDebugInfo(std::unique_ptr d); // Appends an annotation instruction to this module. @@ -141,6 +144,8 @@ class Module { inline uint32_t id_bound() const { return header_.bound; } inline uint32_t version() const { return header_.version; } + inline uint32_t generator() const { return header_.generator; } + inline uint32_t schema() const { return header_.schema; } inline void set_version(uint32_t v) { header_.version = v; } @@ -156,12 +161,20 @@ class Module { inline IteratorRange ext_inst_imports(); inline IteratorRange ext_inst_imports() const; - // Return the memory model instruction contained inthis module. + // Return the memory model instruction contained in this module. inline Instruction* GetMemoryModel() { return memory_model_.get(); } inline const Instruction* GetMemoryModel() const { return memory_model_.get(); } + // Return the sampled image address mode instruction contained in this module. + inline Instruction* GetSampledImageAddressMode() { + return sampled_image_address_mode_.get(); + } + inline const Instruction* GetSampledImageAddressMode() const { + return sampled_image_address_mode_.get(); + } + // There are several kinds of debug instructions, according to where they can // appear in the logical layout of a module: // - Section 7a: OpString, OpSourceExtension, OpSource, OpSourceContinued @@ -192,8 +205,8 @@ class Module { inline IteratorRange debugs3() const; // Iterators for debug info instructions (excluding OpLine & OpNoLine) - // contained in this module. These are OpExtInst for OpenCL.DebugInfo.100 - // or DebugInfo extension placed between section 9 and 10. + // contained in this module. These are OpExtInst for DebugInfo extension + // placed between section 9 and 10. inline inst_iterator ext_inst_debuginfo_begin(); inline inst_iterator ext_inst_debuginfo_end(); inline IteratorRange ext_inst_debuginfo(); @@ -286,6 +299,8 @@ class Module { InstructionList ext_inst_imports_; // A module only has one memory model instruction. std::unique_ptr memory_model_; + // A module can only have one optional sampled image addressing mode + std::unique_ptr sampled_image_address_mode_; InstructionList entry_points_; InstructionList execution_modes_; InstructionList debugs1_; @@ -324,6 +339,10 @@ inline void Module::SetMemoryModel(std::unique_ptr m) { memory_model_ = std::move(m); } +inline void Module::SetSampledImageAddressMode(std::unique_ptr m) { + sampled_image_address_mode_ = std::move(m); +} + inline void Module::AddEntryPoint(std::unique_ptr e) { entry_points_.push_back(std::move(e)); } diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index a5d10c3d..381589b5 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -288,6 +288,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { RegisterPass(CreateStripDebugInfoPass()); } else if (pass_name == "strip-reflect") { RegisterPass(CreateStripReflectInfoPass()); + } else if (pass_name == "strip-nonsemantic") { + RegisterPass(CreateStripNonSemanticInfoPass()); } else if (pass_name == "set-spec-const-default-value") { if (pass_args.size() > 0) { auto spec_ids_vals = @@ -320,6 +322,10 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { RegisterPass(CreateCombineAccessChainsPass()); } else if (pass_name == "convert-local-access-chains") { RegisterPass(CreateLocalAccessChainConvertPass()); + } else if (pass_name == "replace-desc-array-access-using-var-index") { + RegisterPass(CreateReplaceDescArrayAccessUsingVarIndexPass()); + } else if (pass_name == "spread-volatile-semantics") { + RegisterPass(CreateSpreadVolatileSemanticsPass()); } else if (pass_name == "descriptor-scalar-replacement") { RegisterPass(CreateDescriptorScalarReplacementPass()); } else if (pass_name == "eliminate-dead-code-aggressive") { @@ -385,7 +391,23 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { } else if (pass_name == "loop-invariant-code-motion") { RegisterPass(CreateLoopInvariantCodeMotionPass()); } else if (pass_name == "reduce-load-size") { - RegisterPass(CreateReduceLoadSizePass()); + if (pass_args.size() == 0) { + RegisterPass(CreateReduceLoadSizePass()); + } else { + double load_replacement_threshold = 0.9; + if (pass_args.find_first_not_of(".0123456789") == std::string::npos) { + load_replacement_threshold = atof(pass_args.c_str()); + } + + if (load_replacement_threshold >= 0) { + RegisterPass(CreateReduceLoadSizePass(load_replacement_threshold)); + } else { + Error(consumer(), nullptr, {}, + "--reduce-load-size must have no arguments or a non-negative " + "double argument"); + return false; + } + } } else if (pass_name == "redundancy-elimination") { RegisterPass(CreateRedundancyEliminationPass()); } else if (pass_name == "private-to-local") { @@ -401,22 +423,22 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { RegisterPass(CreateSimplificationPass()); RegisterPass(CreateDeadBranchElimPass()); RegisterPass(CreateBlockMergePass()); - RegisterPass(CreateAggressiveDCEPass()); + RegisterPass(CreateAggressiveDCEPass(true)); } else if (pass_name == "inst-desc-idx-check") { RegisterPass(CreateInstBindlessCheckPass(7, 23, true, true)); RegisterPass(CreateSimplificationPass()); RegisterPass(CreateDeadBranchElimPass()); RegisterPass(CreateBlockMergePass()); - RegisterPass(CreateAggressiveDCEPass()); + RegisterPass(CreateAggressiveDCEPass(true)); } else if (pass_name == "inst-buff-oob-check") { RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false, true, true)); RegisterPass(CreateSimplificationPass()); RegisterPass(CreateDeadBranchElimPass()); RegisterPass(CreateBlockMergePass()); - RegisterPass(CreateAggressiveDCEPass()); + RegisterPass(CreateAggressiveDCEPass(true)); } else if (pass_name == "inst-buff-addr-check") { RegisterPass(CreateInstBuffAddrCheckPass(7, 23)); - RegisterPass(CreateAggressiveDCEPass()); + RegisterPass(CreateAggressiveDCEPass(true)); } else if (pass_name == "convert-relaxed-to-half") { RegisterPass(CreateConvertRelaxedToHalfPass()); } else if (pass_name == "relax-float-ops") { @@ -489,6 +511,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { RegisterSizePasses(); } else if (pass_name == "legalize-hlsl") { RegisterLegalizationPasses(); + } else if (pass_name == "remove-unused-interface-variables") { + RegisterPass(CreateRemoveUnusedInterfaceVariablesPass()); } else if (pass_name == "graphics-robust-access") { RegisterPass(CreateGraphicsRobustAccessPass()); } else if (pass_name == "wrap-opkill") { @@ -497,6 +521,32 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { RegisterPass(CreateAmdExtToKhrPass()); } else if (pass_name == "interpolate-fixup") { RegisterPass(CreateInterpolateFixupPass()); + } else if (pass_name == "remove-dont-inline") { + RegisterPass(CreateRemoveDontInlinePass()); + } else if (pass_name == "eliminate-dead-input-components") { + RegisterPass(CreateEliminateDeadInputComponentsPass()); + } else if (pass_name == "fix-func-call-param") { + RegisterPass(CreateFixFuncCallArgumentsPass()); + } else if (pass_name == "convert-to-sampled-image") { + if (pass_args.size() > 0) { + auto descriptor_set_binding_pairs = + opt::ConvertToSampledImagePass::ParseDescriptorSetBindingPairsString( + pass_args.c_str()); + if (!descriptor_set_binding_pairs) { + Errorf(consumer(), nullptr, {}, + "Invalid argument for --convert-to-sampled-image: %s", + pass_args.c_str()); + return false; + } + RegisterPass(CreateConvertToSampledImagePass( + std::move(*descriptor_set_binding_pairs))); + } else { + Errorf(consumer(), nullptr, {}, + "Invalid pairs of descriptor set and binding '%s'. Expected a " + "string of : pairs.", + pass_args.c_str()); + return false; + } } else { Errorf(consumer(), nullptr, {}, "Unknown flag '--%s'. Use --help for a list of valid flags", @@ -573,10 +623,16 @@ bool Optimizer::Run(const uint32_t* original_binary, assert(optimized_binary_with_nop.size() == original_binary_size && "Binary size unexpectedly changed despite the optimizer saying " "there was no change"); - assert(memcmp(optimized_binary_with_nop.data(), original_binary, - original_binary_size) == 0 && - "Binary content unexpectedly changed despite the optimizer saying " - "there was no change"); + + // Compare the magic number to make sure the binaries were encoded in the + // endianness. If not, the contents of the binaries will be different, so + // do not check the contents. + if (optimized_binary_with_nop[0] == original_binary[0]) { + assert(memcmp(optimized_binary_with_nop.data(), original_binary, + original_binary_size) == 0 && + "Binary content unexpectedly changed despite the optimizer saying " + "there was no change"); + } } #endif // !NDEBUG @@ -613,8 +669,12 @@ Optimizer::PassToken CreateStripDebugInfoPass() { } Optimizer::PassToken CreateStripReflectInfoPass() { + return CreateStripNonSemanticInfoPass(); +} + +Optimizer::PassToken CreateStripNonSemanticInfoPass() { return MakeUnique( - MakeUnique()); + MakeUnique()); } Optimizer::PassToken CreateEliminateDeadFunctionsPass() { @@ -726,7 +786,17 @@ Optimizer::PassToken CreateLocalMultiStoreElimPass() { Optimizer::PassToken CreateAggressiveDCEPass() { return MakeUnique( - MakeUnique()); + MakeUnique(false)); +} + +Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface) { + return MakeUnique( + MakeUnique(preserve_interface)); +} + +Optimizer::PassToken CreateRemoveUnusedInterfaceVariablesPass() { + return MakeUnique( + MakeUnique()); } Optimizer::PassToken CreatePropagateLineInfoPass() { @@ -852,9 +922,10 @@ Optimizer::PassToken CreateVectorDCEPass() { return MakeUnique(MakeUnique()); } -Optimizer::PassToken CreateReduceLoadSizePass() { +Optimizer::PassToken CreateReduceLoadSizePass( + double load_replacement_threshold) { return MakeUnique( - MakeUnique()); + MakeUnique(load_replacement_threshold)); } Optimizer::PassToken CreateCombineAccessChainsPass() { @@ -914,6 +985,16 @@ Optimizer::PassToken CreateGraphicsRobustAccessPass() { MakeUnique()); } +Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateSpreadVolatileSemanticsPass() { + return MakeUnique( + MakeUnique()); +} + Optimizer::PassToken CreateDescriptorScalarReplacementPass() { return MakeUnique( MakeUnique()); @@ -933,4 +1014,30 @@ Optimizer::PassToken CreateInterpolateFixupPass() { MakeUnique()); } +Optimizer::PassToken CreateEliminateDeadInputComponentsPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateConvertToSampledImagePass( + const std::vector& + descriptor_set_binding_pairs) { + return MakeUnique( + MakeUnique(descriptor_set_binding_pairs)); +} + +Optimizer::PassToken CreateInterfaceVariableScalarReplacementPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateRemoveDontInlinePass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateFixFuncCallArgumentsPass() { + return MakeUnique( + MakeUnique()); +} } // namespace spvtools diff --git a/source/opt/pass.cpp b/source/opt/pass.cpp index 09b78af9..017aad10 100644 --- a/source/opt/pass.cpp +++ b/source/opt/pass.cpp @@ -43,8 +43,8 @@ Pass::Status Pass::Run(IRContext* ctx) { if (status == Status::SuccessWithChange) { ctx->InvalidateAnalysesExceptFor(GetPreservedAnalyses()); } - assert((status == Status::Failure || ctx->IsConsistent()) && - "An analysis in the context is out of date."); + if (!(status == Status::Failure || ctx->IsConsistent())) + assert(false && "An analysis in the context is out of date."); return status; } diff --git a/source/opt/pass.h b/source/opt/pass.h index a8c9c4b4..b2303e23 100644 --- a/source/opt/pass.h +++ b/source/opt/pass.h @@ -28,6 +28,13 @@ #include "spirv-tools/libspirv.hpp" #include "types.h" +// Avoid unused variable warning/error on Linux +#ifndef NDEBUG +#define USE_ASSERT(x) assert(x) +#else +#define USE_ASSERT(x) ((void)(x)) +#endif + namespace spvtools { namespace opt { @@ -129,7 +136,7 @@ class Pass { // Processes the given |module|. Returns Status::Failure if errors occur when // processing. Returns the corresponding Status::Success if processing is - // succesful to indicate whether changes are made to the module. + // successful to indicate whether changes are made to the module. virtual Status Process() = 0; // Return the next available SSA id and increment it. diff --git a/source/opt/pass_manager.cpp b/source/opt/pass_manager.cpp index be53d344..d3c47e7f 100644 --- a/source/opt/pass_manager.cpp +++ b/source/opt/pass_manager.cpp @@ -35,10 +35,18 @@ Pass::Status PassManager::Run(IRContext* context) { if (print_all_stream_) { std::vector binary; context->module()->ToBinary(&binary, false); - SpirvTools t(SPV_ENV_UNIVERSAL_1_2); + SpirvTools t(target_env_); + t.SetMessageConsumer(consumer()); std::string disassembly; - t.Disassemble(binary, &disassembly, 0); - *print_all_stream_ << preamble << (pass ? pass->name() : "") << "\n" + std::string pass_name = (pass ? pass->name() : ""); + if (!t.Disassemble(binary, &disassembly)) { + std::string msg = "Disassembly failed before pass "; + msg += pass_name + "\n"; + spv_position_t null_pos{0, 0, 0}; + consumer()(SPV_MSG_WARNING, "", null_pos, msg.c_str()); + return; + } + *print_all_stream_ << preamble << pass_name << "\n" << disassembly << std::endl; } }; diff --git a/source/opt/pass_manager.h b/source/opt/pass_manager.h index 9686dddc..11961a33 100644 --- a/source/opt/pass_manager.h +++ b/source/opt/pass_manager.h @@ -54,7 +54,7 @@ class PassManager { // Adds an externally constructed pass. void AddPass(std::unique_ptr pass); // Uses the argument |args| to construct a pass instance of type |T|, and adds - // the pass instance to this pass manger. The pass added will use this pass + // the pass instance to this pass manager. The pass added will use this pass // manager's message consumer. template void AddPass(Args&&... args); @@ -70,7 +70,7 @@ class PassManager { // Runs all passes on the given |module|. Returns Status::Failure if errors // occur when processing using one of the registered passes. All passes // registered after the error-reporting pass will be skipped. Returns the - // corresponding Status::Success if processing is succesful to indicate + // corresponding Status::Success if processing is successful to indicate // whether changes are made to the module. // // After running all the passes, they are removed from the list. diff --git a/source/opt/passes.h b/source/opt/passes.h index bfb34af7..21354c77 100644 --- a/source/opt/passes.h +++ b/source/opt/passes.h @@ -26,6 +26,7 @@ #include "source/opt/combine_access_chains.h" #include "source/opt/compact_ids_pass.h" #include "source/opt/convert_to_half_pass.h" +#include "source/opt/convert_to_sampled_image_pass.h" #include "source/opt/copy_prop_arrays.h" #include "source/opt/dead_branch_elim_pass.h" #include "source/opt/dead_insert_elim_pass.h" @@ -33,8 +34,10 @@ #include "source/opt/desc_sroa.h" #include "source/opt/eliminate_dead_constant_pass.h" #include "source/opt/eliminate_dead_functions_pass.h" +#include "source/opt/eliminate_dead_input_components_pass.h" #include "source/opt/eliminate_dead_members_pass.h" #include "source/opt/empty_pass.h" +#include "source/opt/fix_func_call_arguments.h" #include "source/opt/fix_storage_class.h" #include "source/opt/flatten_decoration_pass.h" #include "source/opt/fold_spec_constant_op_and_composite_pass.h" @@ -46,6 +49,7 @@ #include "source/opt/inst_bindless_check_pass.h" #include "source/opt/inst_buff_addr_check_pass.h" #include "source/opt/inst_debug_printf_pass.h" +#include "source/opt/interface_var_sroa.h" #include "source/opt/interp_fixup_pass.h" #include "source/opt/licm_pass.h" #include "source/opt/local_access_chain_convert_pass.h" @@ -63,15 +67,19 @@ #include "source/opt/reduce_load_size.h" #include "source/opt/redundancy_elimination.h" #include "source/opt/relax_float_ops_pass.h" +#include "source/opt/remove_dontinline_pass.h" #include "source/opt/remove_duplicates_pass.h" +#include "source/opt/remove_unused_interface_variables_pass.h" +#include "source/opt/replace_desc_array_access_using_var_index.h" #include "source/opt/replace_invalid_opc.h" #include "source/opt/scalar_replacement_pass.h" #include "source/opt/set_spec_constant_default_value_pass.h" #include "source/opt/simplification_pass.h" +#include "source/opt/spread_volatile_semantics.h" #include "source/opt/ssa_rewrite_pass.h" #include "source/opt/strength_reduction_pass.h" #include "source/opt/strip_debug_info_pass.h" -#include "source/opt/strip_reflect_info_pass.h" +#include "source/opt/strip_nonsemantic_info_pass.h" #include "source/opt/unify_const_pass.h" #include "source/opt/upgrade_memory_model.h" #include "source/opt/vector_dce.h" diff --git a/source/opt/private_to_local_pass.cpp b/source/opt/private_to_local_pass.cpp index dd6cbbde..80fb4c53 100644 --- a/source/opt/private_to_local_pass.cpp +++ b/source/opt/private_to_local_pass.cpp @@ -135,7 +135,7 @@ bool PrivateToLocalPass::MoveVariable(Instruction* variable, // Place the variable at the start of the first basic block. context()->AnalyzeUses(variable); context()->set_instr_block(variable, &*function->begin()); - function->begin()->begin()->InsertBefore(move(var)); + function->begin()->begin()->InsertBefore(std::move(var)); // Update uses where the type may have changed. return UpdateUses(variable); @@ -157,8 +157,7 @@ uint32_t PrivateToLocalPass::GetNewType(uint32_t old_type_id) { 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) { + if (inst->GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) { return true; } switch (inst->opcode()) { @@ -183,8 +182,7 @@ 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) { + if (inst->GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) { context()->get_debug_info_mgr()->ConvertDebugGlobalToLocalVariable(inst, user); return true; diff --git a/source/opt/private_to_local_pass.h b/source/opt/private_to_local_pass.h index c6127d67..e96a965e 100644 --- a/source/opt/private_to_local_pass.h +++ b/source/opt/private_to_local_pass.h @@ -44,7 +44,7 @@ class PrivateToLocalPass : public Pass { // class of |function|. Returns false if the variable could not be moved. bool MoveVariable(Instruction* variable, Function* function); - // |inst| is an instruction declaring a varible. If that variable is + // |inst| is an instruction declaring a variable. If that variable is // referenced in a single function and all of uses are valid as defined by // |IsValidUse|, then that function is returned. Otherwise, the return // value is |nullptr|. diff --git a/source/opt/reduce_load_size.cpp b/source/opt/reduce_load_size.cpp index c58adf47..56491b2f 100644 --- a/source/opt/reduce_load_size.cpp +++ b/source/opt/reduce_load_size.cpp @@ -27,7 +27,6 @@ namespace { const uint32_t kExtractCompositeIdInIdx = 0; const uint32_t kVariableStorageClassInIdx = 0; const uint32_t kLoadPointerInIdx = 0; -const double kThreshold = 0.9; } // namespace @@ -139,7 +138,7 @@ bool ReduceLoadSize::ShouldReplaceExtract(Instruction* inst) { all_elements_used = !def_use_mgr->WhileEachUser(op_inst, [&elements_used](Instruction* use) { - if (use->IsOpenCL100DebugInstr()) return true; + if (use->IsCommonDebugInstr()) return true; if (use->opcode() != SpvOpCompositeExtract || use->NumInOperands() == 1) { return false; @@ -151,6 +150,8 @@ bool ReduceLoadSize::ShouldReplaceExtract(Instruction* inst) { bool should_replace = false; if (all_elements_used) { should_replace = false; + } else if (1.0 <= replacement_threshold_) { + should_replace = true; } else { analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); analysis::TypeManager* type_mgr = context()->get_type_mgr(); @@ -160,8 +161,15 @@ bool ReduceLoadSize::ShouldReplaceExtract(Instruction* inst) { case analysis::Type::kArray: { const analysis::Constant* size_const = const_mgr->FindDeclaredConstant(load_type->AsArray()->LengthId()); - assert(size_const->AsIntConstant()); - total_size = size_const->GetU32(); + + if (size_const) { + assert(size_const->AsIntConstant()); + total_size = size_const->GetU32(); + } else { + // The size is spec constant, so it is unknown at this time. Assume + // it is very large. + total_size = UINT32_MAX; + } } break; case analysis::Type::kStruct: total_size = static_cast( @@ -172,7 +180,7 @@ bool ReduceLoadSize::ShouldReplaceExtract(Instruction* inst) { } double percent_used = static_cast(elements_used.size()) / static_cast(total_size); - should_replace = (percent_used < kThreshold); + should_replace = (percent_used < replacement_threshold_); } should_replace_cache_[op_inst->result_id()] = should_replace; diff --git a/source/opt/reduce_load_size.h b/source/opt/reduce_load_size.h index ccac49be..b3238453 100644 --- a/source/opt/reduce_load_size.h +++ b/source/opt/reduce_load_size.h @@ -27,6 +27,9 @@ namespace opt { // See optimizer.hpp for documentation. class ReduceLoadSize : public Pass { public: + explicit ReduceLoadSize(double replacement_threshold) + : replacement_threshold_(replacement_threshold) {} + const char* name() const override { return "reduce-load-size"; } Status Process() override; @@ -54,6 +57,11 @@ class ReduceLoadSize : public Pass { // on the load feeding |inst|. bool ShouldReplaceExtract(Instruction* inst); + // Threshold to determine whether we have to replace the load or not. If the + // ratio of the used components of the load is less than the threshold, we + // replace the load. + double replacement_threshold_; + // Maps the result id of an OpLoad instruction to the result of whether or // not the OpCompositeExtract that use the id should be replaced. std::unordered_map should_replace_cache_; diff --git a/source/opt/redundancy_elimination.cpp b/source/opt/redundancy_elimination.cpp index 362e54dc..398225bb 100644 --- a/source/opt/redundancy_elimination.cpp +++ b/source/opt/redundancy_elimination.cpp @@ -24,6 +24,10 @@ Pass::Status RedundancyEliminationPass::Process() { ValueNumberTable vnTable(context()); for (auto& func : *get_module()) { + if (func.IsDeclaration()) { + continue; + } + // Build the dominator tree for this function. It is how the code is // traversed. DominatorTree& dom_tree = diff --git a/source/opt/redundancy_elimination.h b/source/opt/redundancy_elimination.h index 91809b5d..40451f40 100644 --- a/source/opt/redundancy_elimination.h +++ b/source/opt/redundancy_elimination.h @@ -41,7 +41,7 @@ class RedundancyEliminationPass : public LocalRedundancyEliminationPass { // in the function containing |bb|. // // |value_to_ids| is a map from value number to ids. If {vn, id} is in - // |value_to_ids| then vn is the value number of id, and the defintion of id + // |value_to_ids| then vn is the value number of id, and the definition of id // dominates |bb|. // // Returns true if at least one instruction is deleted. diff --git a/source/opt/reflect.h b/source/opt/reflect.h index c7d46df5..c2ffb0be 100644 --- a/source/opt/reflect.h +++ b/source/opt/reflect.h @@ -34,7 +34,7 @@ inline bool IsDebug2Inst(SpvOp opcode) { inline bool IsDebug3Inst(SpvOp opcode) { return opcode == SpvOpModuleProcessed; } -inline bool IsDebugLineInst(SpvOp opcode) { +inline bool IsOpLineInst(SpvOp opcode) { return opcode == SpvOpLine || opcode == SpvOpNoLine; } inline bool IsAnnotationInst(SpvOp opcode) { @@ -51,7 +51,8 @@ inline bool IsTypeInst(SpvOp opcode) { opcode == SpvOpTypeCooperativeMatrixNV; } inline bool IsConstantInst(SpvOp opcode) { - return opcode >= SpvOpConstantTrue && opcode <= SpvOpSpecConstantOp; + return (opcode >= SpvOpConstantTrue && opcode <= SpvOpSpecConstantOp) || + opcode == SpvOpConstantFunctionPointerINTEL; } inline bool IsCompileTimeConstantInst(SpvOp opcode) { return opcode >= SpvOpConstantTrue && opcode <= SpvOpConstantNull; diff --git a/source/opt/register_pressure.cpp b/source/opt/register_pressure.cpp index 5750c6d4..1ad33738 100644 --- a/source/opt/register_pressure.cpp +++ b/source/opt/register_pressure.cpp @@ -378,7 +378,7 @@ void RegisterLiveness::SimulateFusion( // The loop fusion is injecting the l1 before the l2, the latch of l1 will be // connected to the header of l2. // To compute the register usage, we inject the loop live-in (union of l1 and - // l2 live-in header blocks) into the the live in/out of each basic block of + // l2 live-in header blocks) into the live in/out of each basic block of // l1 to get the peak register usage. We then repeat the operation to for l2 // basic blocks but in this case we inject the live-out of the latch of l1. auto live_loop = MakeFilterIteratorRange( diff --git a/source/opt/relax_float_ops_pass.cpp b/source/opt/relax_float_ops_pass.cpp index 73f16ddf..3fcf8795 100644 --- a/source/opt/relax_float_ops_pass.cpp +++ b/source/opt/relax_float_ops_pass.cpp @@ -76,7 +76,7 @@ Pass::Status RelaxFloatOpsPass::ProcessImpl() { Pass::ProcessFunction pfn = [this](Function* fp) { return ProcessFunction(fp); }; - bool modified = context()->ProcessEntryPointCallTree(pfn); + bool modified = context()->ProcessReachableCallTree(pfn); return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } diff --git a/source/opt/remove_dontinline_pass.cpp b/source/opt/remove_dontinline_pass.cpp new file mode 100644 index 00000000..4dd1cd4f --- /dev/null +++ b/source/opt/remove_dontinline_pass.cpp @@ -0,0 +1,49 @@ +// Copyright (c) 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/remove_dontinline_pass.h" + +namespace spvtools { +namespace opt { + +Pass::Status RemoveDontInline::Process() { + bool modified = false; + modified = ClearDontInlineFunctionControl(); + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +bool RemoveDontInline::ClearDontInlineFunctionControl() { + bool modified = false; + for (auto& func : *get_module()) { + ClearDontInlineFunctionControl(&func); + } + return modified; +} + +bool RemoveDontInline::ClearDontInlineFunctionControl(Function* function) { + constexpr uint32_t kFunctionControlInOperandIdx = 0; + Instruction* function_inst = &function->DefInst(); + uint32_t function_control = + function_inst->GetSingleWordInOperand(kFunctionControlInOperandIdx); + + if ((function_control & SpvFunctionControlDontInlineMask) == 0) { + return false; + } + function_control &= ~SpvFunctionControlDontInlineMask; + function_inst->SetInOperand(kFunctionControlInOperandIdx, {function_control}); + return true; +} + +} // namespace opt +} // namespace spvtools diff --git a/source/opt/remove_dontinline_pass.h b/source/opt/remove_dontinline_pass.h new file mode 100644 index 00000000..16243199 --- /dev/null +++ b/source/opt/remove_dontinline_pass.h @@ -0,0 +1,42 @@ +// Copyright (c) 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_REMOVE_DONTINLINE_PASS_H_ +#define SOURCE_OPT_REMOVE_DONTINLINE_PASS_H_ + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class RemoveDontInline : public Pass { + public: + const char* name() const override { return "remove-dont-inline"; } + Status Process() override; + + private: + // Clears the DontInline function control from every function in the module. + // Returns true of a change was made. + bool ClearDontInlineFunctionControl(); + + // Clears the DontInline function control from |function|. + // Returns true of a change was made. + bool ClearDontInlineFunctionControl(Function* function); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_REMOVE_DONTINLINE_PASS_H_ diff --git a/source/opt/remove_duplicates_pass.cpp b/source/opt/remove_duplicates_pass.cpp index 0e65cc8d..1ed8e2a0 100644 --- a/source/opt/remove_duplicates_pass.cpp +++ b/source/opt/remove_duplicates_pass.cpp @@ -72,9 +72,8 @@ bool RemoveDuplicatesPass::RemoveDuplicatesExtInstImports() const { std::unordered_map ext_inst_imports; for (auto* i = &*context()->ext_inst_import_begin(); i;) { - auto res = ext_inst_imports.emplace( - reinterpret_cast(i->GetInOperand(0u).words.data()), - i->result_id()); + auto res = ext_inst_imports.emplace(i->GetInOperand(0u).AsString(), + i->result_id()); if (res.second) { // Never seen before, keep it. i = i->NextNode(); diff --git a/source/opt/remove_unused_interface_variables_pass.cpp b/source/opt/remove_unused_interface_variables_pass.cpp new file mode 100644 index 00000000..31e87bd4 --- /dev/null +++ b/source/opt/remove_unused_interface_variables_pass.cpp @@ -0,0 +1,93 @@ +// Copyright (c) 2021 ZHOU He +// +// 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 "remove_unused_interface_variables_pass.h" +#include "source/spirv_constant.h" +namespace spvtools { +namespace opt { + +class RemoveUnusedInterfaceVariablesContext { + RemoveUnusedInterfaceVariablesPass& parent_; + Instruction& entry_; + std::unordered_set used_variables_; + IRContext::ProcessFunction pfn_ = + std::bind(&RemoveUnusedInterfaceVariablesContext::processFunction, this, + std::placeholders::_1); + + bool processFunction(Function* func) { + for (const auto& basic_block : *func) + for (const auto& instruction : basic_block) + instruction.ForEachInId([&](const uint32_t* id) { + if (used_variables_.count(*id)) return; + auto* var = parent_.get_def_use_mgr()->GetDef(*id); + if (!var || var->opcode() != SpvOpVariable) return; + auto storage_class = var->GetSingleWordInOperand(0); + if (storage_class != SpvStorageClassFunction && + (parent_.get_module()->version() >= + SPV_SPIRV_VERSION_WORD(1, 4) || + storage_class == SpvStorageClassInput || + storage_class == SpvStorageClassOutput)) + used_variables_.insert(*id); + }); + return false; + } + + public: + RemoveUnusedInterfaceVariablesContext( + RemoveUnusedInterfaceVariablesPass& parent, Instruction& entry) + : parent_(parent), entry_(entry) {} + + void CollectUsedVariables() { + std::queue roots; + roots.push(entry_.GetSingleWordInOperand(1)); + parent_.context()->ProcessCallTreeFromRoots(pfn_, &roots); + } + + bool ShouldModify() { + std::unordered_set old_variables; + for (int i = entry_.NumInOperands() - 1; i >= 3; --i) { + auto variable = entry_.GetInOperand(i).words[0]; + if (!used_variables_.count(variable)) return true; // It is unused. + if (old_variables.count(variable)) return true; // It is duplicate. + old_variables.insert(variable); + } + if (old_variables.size() != used_variables_.size()) // Missing IDs. + return true; + return false; + } + + void Modify() { + for (int i = entry_.NumInOperands() - 1; i >= 3; --i) + entry_.RemoveInOperand(i); + for (auto id : used_variables_) { + entry_.AddOperand(Operand(SPV_OPERAND_TYPE_ID, {id})); + } + } +}; + +RemoveUnusedInterfaceVariablesPass::Status +RemoveUnusedInterfaceVariablesPass::Process() { + bool modified = false; + for (auto& entry : get_module()->entry_points()) { + RemoveUnusedInterfaceVariablesContext context(*this, entry); + context.CollectUsedVariables(); + if (context.ShouldModify()) { + context.Modify(); + modified = true; + } + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} +} // namespace opt +} // namespace spvtools \ No newline at end of file diff --git a/source/opt/remove_unused_interface_variables_pass.h b/source/opt/remove_unused_interface_variables_pass.h new file mode 100644 index 00000000..7f11187c --- /dev/null +++ b/source/opt/remove_unused_interface_variables_pass.h @@ -0,0 +1,26 @@ +// Copyright (c) 2021 ZHOU He +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/pass.h" +namespace spvtools { +namespace opt { + +class RemoveUnusedInterfaceVariablesPass : public Pass { + const char* name() const override { + return "remove-unused-interface-variables-pass"; + } + Status Process() override; +}; +} // namespace opt +} // namespace spvtools \ No newline at end of file diff --git a/source/opt/replace_desc_array_access_using_var_index.cpp b/source/opt/replace_desc_array_access_using_var_index.cpp new file mode 100644 index 00000000..e97593ef --- /dev/null +++ b/source/opt/replace_desc_array_access_using_var_index.cpp @@ -0,0 +1,429 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/replace_desc_array_access_using_var_index.h" + +#include "source/opt/desc_sroa_util.h" +#include "source/opt/ir_builder.h" +#include "source/util/string_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +const uint32_t kOpAccessChainInOperandIndexes = 1; +const uint32_t kOpTypePointerInOperandType = 1; +const uint32_t kOpTypeArrayInOperandType = 0; +const uint32_t kOpTypeStructInOperandMember = 0; +IRContext::Analysis kAnalysisDefUseAndInstrToBlockMapping = + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping; + +uint32_t GetValueWithKeyExistenceCheck( + uint32_t key, const std::unordered_map& map) { + auto itr = map.find(key); + assert(itr != map.end() && "Key does not exist"); + return itr->second; +} + +} // namespace + +Pass::Status ReplaceDescArrayAccessUsingVarIndex::Process() { + Status status = Status::SuccessWithoutChange; + for (Instruction& var : context()->types_values()) { + if (descsroautil::IsDescriptorArray(context(), &var)) { + if (ReplaceVariableAccessesWithConstantElements(&var)) + status = Status::SuccessWithChange; + } + } + return status; +} + +bool ReplaceDescArrayAccessUsingVarIndex:: + ReplaceVariableAccessesWithConstantElements(Instruction* var) const { + std::vector work_list; + get_def_use_mgr()->ForEachUser(var, [&work_list](Instruction* use) { + switch (use->opcode()) { + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + work_list.push_back(use); + break; + default: + break; + } + }); + + bool updated = false; + for (Instruction* access_chain : work_list) { + if (descsroautil::GetAccessChainIndexAsConst(context(), access_chain) == + nullptr) { + ReplaceAccessChain(var, access_chain); + updated = true; + } + } + // Note that we do not consider OpLoad and OpCompositeExtract because + // OpCompositeExtract always has constant literals for indices. + return updated; +} + +void ReplaceDescArrayAccessUsingVarIndex::ReplaceAccessChain( + Instruction* var, Instruction* access_chain) const { + uint32_t number_of_elements = + descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var); + assert(number_of_elements != 0 && "Number of element is 0"); + if (number_of_elements == 1) { + UseConstIndexForAccessChain(access_chain, 0); + get_def_use_mgr()->AnalyzeInstUse(access_chain); + return; + } + ReplaceUsersOfAccessChain(access_chain, number_of_elements); +} + +void ReplaceDescArrayAccessUsingVarIndex::ReplaceUsersOfAccessChain( + Instruction* access_chain, uint32_t number_of_elements) const { + std::vector final_users; + CollectRecursiveUsersWithConcreteType(access_chain, &final_users); + for (auto* inst : final_users) { + std::deque insts_to_be_cloned = + CollectRequiredImageAndAccessInsts(inst); + ReplaceNonUniformAccessWithSwitchCase( + inst, access_chain, number_of_elements, insts_to_be_cloned); + } +} + +void ReplaceDescArrayAccessUsingVarIndex::CollectRecursiveUsersWithConcreteType( + Instruction* access_chain, std::vector* final_users) const { + std::queue work_list; + work_list.push(access_chain); + while (!work_list.empty()) { + auto* inst_from_work_list = work_list.front(); + work_list.pop(); + get_def_use_mgr()->ForEachUser( + inst_from_work_list, [this, final_users, &work_list](Instruction* use) { + // TODO: Support Boolean type as well. + if (!use->HasResultId() || IsConcreteType(use->type_id())) { + final_users->push_back(use); + } else { + work_list.push(use); + } + }); + } +} + +std::deque +ReplaceDescArrayAccessUsingVarIndex::CollectRequiredImageAndAccessInsts( + Instruction* user) const { + std::unordered_set seen_inst_ids; + std::queue work_list; + + auto decision_to_include_operand = [this, &seen_inst_ids, + &work_list](uint32_t* idp) { + if (!seen_inst_ids.insert(*idp).second) return; + Instruction* operand = get_def_use_mgr()->GetDef(*idp); + if (context()->get_instr_block(operand) != nullptr && + (HasImageOrImagePtrType(operand) || + operand->opcode() == SpvOpAccessChain || + operand->opcode() == SpvOpInBoundsAccessChain)) { + work_list.push(operand); + } + }; + + std::deque required_insts; + required_insts.push_front(user); + user->ForEachInId(decision_to_include_operand); + while (!work_list.empty()) { + auto* inst_from_work_list = work_list.front(); + work_list.pop(); + required_insts.push_front(inst_from_work_list); + inst_from_work_list->ForEachInId(decision_to_include_operand); + } + return required_insts; +} + +bool ReplaceDescArrayAccessUsingVarIndex::HasImageOrImagePtrType( + const Instruction* inst) const { + assert(inst != nullptr && inst->type_id() != 0 && "Invalid instruction"); + return IsImageOrImagePtrType(get_def_use_mgr()->GetDef(inst->type_id())); +} + +bool ReplaceDescArrayAccessUsingVarIndex::IsImageOrImagePtrType( + const Instruction* type_inst) const { + if (type_inst->opcode() == SpvOpTypeImage || + type_inst->opcode() == SpvOpTypeSampler || + type_inst->opcode() == SpvOpTypeSampledImage) { + return true; + } + if (type_inst->opcode() == SpvOpTypePointer) { + Instruction* pointee_type_inst = get_def_use_mgr()->GetDef( + type_inst->GetSingleWordInOperand(kOpTypePointerInOperandType)); + return IsImageOrImagePtrType(pointee_type_inst); + } + if (type_inst->opcode() == SpvOpTypeArray) { + Instruction* element_type_inst = get_def_use_mgr()->GetDef( + type_inst->GetSingleWordInOperand(kOpTypeArrayInOperandType)); + return IsImageOrImagePtrType(element_type_inst); + } + if (type_inst->opcode() != SpvOpTypeStruct) return false; + for (uint32_t in_operand_idx = kOpTypeStructInOperandMember; + in_operand_idx < type_inst->NumInOperands(); ++in_operand_idx) { + Instruction* member_type_inst = get_def_use_mgr()->GetDef( + type_inst->GetSingleWordInOperand(kOpTypeStructInOperandMember)); + if (IsImageOrImagePtrType(member_type_inst)) return true; + } + return false; +} + +bool ReplaceDescArrayAccessUsingVarIndex::IsConcreteType( + uint32_t type_id) const { + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + if (type_inst->opcode() == SpvOpTypeInt || + type_inst->opcode() == SpvOpTypeFloat) { + return true; + } + if (type_inst->opcode() == SpvOpTypeVector || + type_inst->opcode() == SpvOpTypeMatrix || + type_inst->opcode() == SpvOpTypeArray) { + return IsConcreteType(type_inst->GetSingleWordInOperand(0)); + } + if (type_inst->opcode() == SpvOpTypeStruct) { + for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) { + if (!IsConcreteType(type_inst->GetSingleWordInOperand(i))) return false; + } + return true; + } + return false; +} + +BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateCaseBlock( + Instruction* access_chain, uint32_t element_index, + const std::deque& insts_to_be_cloned, + uint32_t branch_target_id, + std::unordered_map* old_ids_to_new_ids) const { + auto* case_block = CreateNewBlock(); + AddConstElementAccessToCaseBlock(case_block, access_chain, element_index, + old_ids_to_new_ids); + CloneInstsToBlock(case_block, access_chain, insts_to_be_cloned, + old_ids_to_new_ids); + AddBranchToBlock(case_block, branch_target_id); + UseNewIdsInBlock(case_block, *old_ids_to_new_ids); + return case_block; +} + +void ReplaceDescArrayAccessUsingVarIndex::CloneInstsToBlock( + BasicBlock* block, Instruction* inst_to_skip_cloning, + const std::deque& insts_to_be_cloned, + std::unordered_map* old_ids_to_new_ids) const { + for (auto* inst_to_be_cloned : insts_to_be_cloned) { + if (inst_to_be_cloned == inst_to_skip_cloning) continue; + std::unique_ptr clone(inst_to_be_cloned->Clone(context())); + if (inst_to_be_cloned->HasResultId()) { + uint32_t new_id = context()->TakeNextId(); + clone->SetResultId(new_id); + (*old_ids_to_new_ids)[inst_to_be_cloned->result_id()] = new_id; + } + get_def_use_mgr()->AnalyzeInstDefUse(clone.get()); + context()->set_instr_block(clone.get(), block); + block->AddInstruction(std::move(clone)); + } +} + +void ReplaceDescArrayAccessUsingVarIndex::UseNewIdsInBlock( + BasicBlock* block, + const std::unordered_map& old_ids_to_new_ids) const { + for (auto block_itr = block->begin(); block_itr != block->end(); + ++block_itr) { + (&*block_itr)->ForEachInId([&old_ids_to_new_ids](uint32_t* idp) { + auto old_ids_to_new_ids_itr = old_ids_to_new_ids.find(*idp); + if (old_ids_to_new_ids_itr == old_ids_to_new_ids.end()) return; + *idp = old_ids_to_new_ids_itr->second; + }); + get_def_use_mgr()->AnalyzeInstUse(&*block_itr); + } +} + +void ReplaceDescArrayAccessUsingVarIndex::ReplaceNonUniformAccessWithSwitchCase( + Instruction* access_chain_final_user, Instruction* access_chain, + uint32_t number_of_elements, + const std::deque& insts_to_be_cloned) const { + auto* block = context()->get_instr_block(access_chain_final_user); + // If the instruction does not belong to a block (i.e. in the case of + // OpDecorate), no replacement is needed. + if (!block) return; + + // Create merge block and add terminator + auto* merge_block = SeparateInstructionsIntoNewBlock( + block, access_chain_final_user->NextNode()); + + auto* function = block->GetParent(); + + // Add case blocks + std::vector phi_operands; + std::vector case_block_ids; + for (uint32_t idx = 0; idx < number_of_elements; ++idx) { + std::unordered_map old_ids_to_new_ids_for_cloned_insts; + std::unique_ptr case_block(CreateCaseBlock( + access_chain, idx, insts_to_be_cloned, merge_block->id(), + &old_ids_to_new_ids_for_cloned_insts)); + case_block_ids.push_back(case_block->id()); + function->InsertBasicBlockBefore(std::move(case_block), merge_block); + + // Keep the operand for OpPhi + if (!access_chain_final_user->HasResultId()) continue; + uint32_t phi_operand = + GetValueWithKeyExistenceCheck(access_chain_final_user->result_id(), + old_ids_to_new_ids_for_cloned_insts); + phi_operands.push_back(phi_operand); + } + + // Create default block + std::unique_ptr default_block( + CreateDefaultBlock(access_chain_final_user->HasResultId(), &phi_operands, + merge_block->id())); + uint32_t default_block_id = default_block->id(); + function->InsertBasicBlockBefore(std::move(default_block), merge_block); + + // Create OpSwitch + uint32_t access_chain_index_var_id = + descsroautil::GetFirstIndexOfAccessChain(access_chain); + AddSwitchForAccessChain(block, access_chain_index_var_id, default_block_id, + merge_block->id(), case_block_ids); + + // Create phi instructions + if (!phi_operands.empty()) { + uint32_t phi_id = CreatePhiInstruction(merge_block, phi_operands, + case_block_ids, default_block_id); + context()->ReplaceAllUsesWith(access_chain_final_user->result_id(), phi_id); + } + + // Replace OpPhi incoming block operand that uses |block| with |merge_block| + ReplacePhiIncomingBlock(block->id(), merge_block->id()); +} + +BasicBlock* +ReplaceDescArrayAccessUsingVarIndex::SeparateInstructionsIntoNewBlock( + BasicBlock* block, Instruction* separation_begin_inst) const { + auto separation_begin = block->begin(); + while (separation_begin != block->end() && + &*separation_begin != separation_begin_inst) { + ++separation_begin; + } + return block->SplitBasicBlock(context(), context()->TakeNextId(), + separation_begin); +} + +BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateNewBlock() const { + auto* new_block = new BasicBlock(std::unique_ptr( + new Instruction(context(), SpvOpLabel, 0, context()->TakeNextId(), {}))); + get_def_use_mgr()->AnalyzeInstDefUse(new_block->GetLabelInst()); + context()->set_instr_block(new_block->GetLabelInst(), new_block); + return new_block; +} + +void ReplaceDescArrayAccessUsingVarIndex::UseConstIndexForAccessChain( + Instruction* access_chain, uint32_t const_element_idx) const { + uint32_t const_element_idx_id = + context()->get_constant_mgr()->GetUIntConst(const_element_idx); + access_chain->SetInOperand(kOpAccessChainInOperandIndexes, + {const_element_idx_id}); +} + +void ReplaceDescArrayAccessUsingVarIndex::AddConstElementAccessToCaseBlock( + BasicBlock* case_block, Instruction* access_chain, + uint32_t const_element_idx, + std::unordered_map* old_ids_to_new_ids) const { + std::unique_ptr access_clone(access_chain->Clone(context())); + UseConstIndexForAccessChain(access_clone.get(), const_element_idx); + + uint32_t new_access_id = context()->TakeNextId(); + (*old_ids_to_new_ids)[access_clone->result_id()] = new_access_id; + access_clone->SetResultId(new_access_id); + get_def_use_mgr()->AnalyzeInstDefUse(access_clone.get()); + + context()->set_instr_block(access_clone.get(), case_block); + case_block->AddInstruction(std::move(access_clone)); +} + +void ReplaceDescArrayAccessUsingVarIndex::AddBranchToBlock( + BasicBlock* parent_block, uint32_t branch_destination) const { + InstructionBuilder builder{context(), parent_block, + kAnalysisDefUseAndInstrToBlockMapping}; + builder.AddBranch(branch_destination); +} + +BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateDefaultBlock( + bool null_const_for_phi_is_needed, std::vector* phi_operands, + uint32_t merge_block_id) const { + auto* default_block = CreateNewBlock(); + AddBranchToBlock(default_block, merge_block_id); + if (!null_const_for_phi_is_needed) return default_block; + + // Create null value for OpPhi + Instruction* inst = context()->get_def_use_mgr()->GetDef((*phi_operands)[0]); + auto* null_const_inst = GetConstNull(inst->type_id()); + phi_operands->push_back(null_const_inst->result_id()); + return default_block; +} + +Instruction* ReplaceDescArrayAccessUsingVarIndex::GetConstNull( + uint32_t type_id) const { + assert(type_id != 0 && "Result type is expected"); + auto* type = context()->get_type_mgr()->GetType(type_id); + auto* null_const = context()->get_constant_mgr()->GetConstant(type, {}); + return context()->get_constant_mgr()->GetDefiningInstruction(null_const); +} + +void ReplaceDescArrayAccessUsingVarIndex::AddSwitchForAccessChain( + BasicBlock* parent_block, uint32_t access_chain_index_var_id, + uint32_t default_id, uint32_t merge_id, + const std::vector& case_block_ids) const { + InstructionBuilder builder{context(), parent_block, + kAnalysisDefUseAndInstrToBlockMapping}; + std::vector> cases; + for (uint32_t i = 0; i < static_cast(case_block_ids.size()); ++i) { + cases.emplace_back(Operand::OperandData{i}, case_block_ids[i]); + } + builder.AddSwitch(access_chain_index_var_id, default_id, cases, merge_id); +} + +uint32_t ReplaceDescArrayAccessUsingVarIndex::CreatePhiInstruction( + BasicBlock* parent_block, const std::vector& phi_operands, + const std::vector& case_block_ids, + uint32_t default_block_id) const { + std::vector incomings; + assert(case_block_ids.size() + 1 == phi_operands.size() && + "Number of Phi operands must be exactly 1 bigger than the one of case " + "blocks"); + for (size_t i = 0; i < case_block_ids.size(); ++i) { + incomings.push_back(phi_operands[i]); + incomings.push_back(case_block_ids[i]); + } + incomings.push_back(phi_operands.back()); + incomings.push_back(default_block_id); + + InstructionBuilder builder{context(), &*parent_block->begin(), + kAnalysisDefUseAndInstrToBlockMapping}; + uint32_t phi_result_type_id = + context()->get_def_use_mgr()->GetDef(phi_operands[0])->type_id(); + auto* phi = builder.AddPhi(phi_result_type_id, incomings); + return phi->result_id(); +} + +void ReplaceDescArrayAccessUsingVarIndex::ReplacePhiIncomingBlock( + uint32_t old_incoming_block_id, uint32_t new_incoming_block_id) const { + context()->ReplaceAllUsesWithPredicate( + old_incoming_block_id, new_incoming_block_id, + [](Instruction* use) { return use->opcode() == SpvOpPhi; }); +} + +} // namespace opt +} // namespace spvtools diff --git a/source/opt/replace_desc_array_access_using_var_index.h b/source/opt/replace_desc_array_access_using_var_index.h new file mode 100644 index 00000000..51817c15 --- /dev/null +++ b/source/opt/replace_desc_array_access_using_var_index.h @@ -0,0 +1,205 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_ +#define SOURCE_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/opt/function.h" +#include "source/opt/pass.h" +#include "source/opt/type_manager.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class ReplaceDescArrayAccessUsingVarIndex : public Pass { + public: + ReplaceDescArrayAccessUsingVarIndex() {} + + const char* name() const override { + return "replace-desc-array-access-using-var-index"; + } + + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Replaces all accesses to |var| using variable indices with constant + // elements of the array |var|. Creates switch-case statements to determine + // the value of the variable index for all the possible cases. Returns + // whether replacement is done or not. + bool ReplaceVariableAccessesWithConstantElements(Instruction* var) const; + + // Replaces the OpAccessChain or OpInBoundsAccessChain instruction |use| that + // uses the descriptor variable |var| with the OpAccessChain or + // OpInBoundsAccessChain instruction with a constant Indexes operand. + void ReplaceAccessChain(Instruction* var, Instruction* use) const; + + // Updates the first Indexes operand of the OpAccessChain or + // OpInBoundsAccessChain instruction |access_chain| to let it use a constant + // index |const_element_idx|. + void UseConstIndexForAccessChain(Instruction* access_chain, + uint32_t const_element_idx) const; + + // Replaces users of the OpAccessChain or OpInBoundsAccessChain instruction + // |access_chain| that accesses an array descriptor variable using variable + // indices with constant elements. |number_of_elements| is the number + // of array elements. + void ReplaceUsersOfAccessChain(Instruction* access_chain, + uint32_t number_of_elements) const; + + // Puts all the recursive users of |access_chain| with concrete result types + // or the ones without result it in |final_users|. + void CollectRecursiveUsersWithConcreteType( + Instruction* access_chain, std::vector* final_users) const; + + // Recursively collects the operands of |user| (and operands of the operands) + // whose result types are images/samplers (or pointers/arrays/ structs of + // them) and access chains instructions and returns them. The returned + // collection includes |user|. + std::deque CollectRequiredImageAndAccessInsts( + Instruction* user) const; + + // Returns whether result type of |inst| is an image/sampler/pointer of image + // or sampler or not. + bool HasImageOrImagePtrType(const Instruction* inst) const; + + // Returns whether |type_inst| is an image/sampler or pointer/array/struct of + // image or sampler or not. + bool IsImageOrImagePtrType(const Instruction* type_inst) const; + + // Returns whether the type with |type_id| is a concrete type or not. + bool IsConcreteType(uint32_t type_id) const; + + // Replaces the non-uniform access to a descriptor variable + // |access_chain_final_user| with OpSwitch instruction and case blocks. Each + // case block will contain a clone of |access_chain| and clones of + // |non_uniform_accesses_to_clone| that are recursively used by + // |access_chain_final_user|. The clone of |access_chain| (or + // OpInBoundsAccessChain) will have a constant index for its first index. The + // OpSwitch instruction will have the cases for the variable index of + // |access_chain| from 0 to |number_of_elements| - 1. + void ReplaceNonUniformAccessWithSwitchCase( + Instruction* access_chain_final_user, Instruction* access_chain, + uint32_t number_of_elements, + const std::deque& non_uniform_accesses_to_clone) const; + + // Creates and returns a new basic block that contains all instructions of + // |block| after |separation_begin_inst|. The new basic block is added to the + // function in this method. + BasicBlock* SeparateInstructionsIntoNewBlock( + BasicBlock* block, Instruction* separation_begin_inst) const; + + // Creates and returns a new block. + BasicBlock* CreateNewBlock() const; + + // Returns the first operand id of the OpAccessChain or OpInBoundsAccessChain + // instruction |access_chain|. + uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain) const; + + // Adds a clone of the OpAccessChain or OpInBoundsAccessChain instruction + // |access_chain| to |case_block|. The clone of |access_chain| will use + // |const_element_idx| for its first index. |old_ids_to_new_ids| keeps the + // mapping from the result id of |access_chain| to the result of its clone. + void AddConstElementAccessToCaseBlock( + BasicBlock* case_block, Instruction* access_chain, + uint32_t const_element_idx, + std::unordered_map* old_ids_to_new_ids) const; + + // Clones all instructions in |insts_to_be_cloned| and put them to |block|. + // |old_ids_to_new_ids| keeps the mapping from the result id of each + // instruction of |insts_to_be_cloned| to the result of their clones. + void CloneInstsToBlock( + BasicBlock* block, Instruction* inst_to_skip_cloning, + const std::deque& insts_to_be_cloned, + std::unordered_map* old_ids_to_new_ids) const; + + // Adds OpBranch to |branch_destination| at the end of |parent_block|. + void AddBranchToBlock(BasicBlock* parent_block, + uint32_t branch_destination) const; + + // Replaces in-operands of all instructions in the basic block |block| using + // |old_ids_to_new_ids|. It conducts the replacement only if the in-operand + // id is a key of |old_ids_to_new_ids|. + void UseNewIdsInBlock( + BasicBlock* block, + const std::unordered_map& old_ids_to_new_ids) const; + + // Creates a case block for |element_index| case. It adds clones of + // |insts_to_be_cloned| and a clone of |access_chain| with |element_index| as + // its first index. The termination instruction of the created case block will + // be a branch to |branch_target_id|. Puts old ids to new ids map for the + // cloned instructions in |old_ids_to_new_ids|. + BasicBlock* CreateCaseBlock( + Instruction* access_chain, uint32_t element_index, + const std::deque& insts_to_be_cloned, + uint32_t branch_target_id, + std::unordered_map* old_ids_to_new_ids) const; + + // Creates a default block for switch-case statement that has only a single + // instruction OpBranch whose target is a basic block with |merge_block_id|. + // If |null_const_for_phi_is_needed| is true, gets or creates a default null + // constant value for a phi instruction whose operands are |phi_operands| and + // puts it in |phi_operands|. + BasicBlock* CreateDefaultBlock(bool null_const_for_phi_is_needed, + std::vector* phi_operands, + uint32_t merge_block_id) const; + + // Creates and adds an OpSwitch used for the selection of OpAccessChain whose + // first Indexes operand is |access_chain_index_var_id|. The OpSwitch will be + // added at the end of |parent_block|. It will jump to |default_id| for the + // default case and jumps to one of case blocks whose ids are |case_block_ids| + // if |access_chain_index_var_id| matches the case number. |merge_id| is the + // merge block id. + void AddSwitchForAccessChain( + BasicBlock* parent_block, uint32_t access_chain_index_var_id, + uint32_t default_id, uint32_t merge_id, + const std::vector& case_block_ids) const; + + // Creates a phi instruction with |phi_operands| as values and + // |case_block_ids| and |default_block_id| as incoming blocks. The size of + // |phi_operands| must be exactly 1 larger than the size of |case_block_ids|. + // The last element of |phi_operands| will be used for |default_block_id|. It + // adds the phi instruction to the beginning of |parent_block|. + uint32_t CreatePhiInstruction(BasicBlock* parent_block, + const std::vector& phi_operands, + const std::vector& case_block_ids, + uint32_t default_block_id) const; + + // Replaces the incoming block operand of OpPhi instructions with + // |new_incoming_block_id| if the incoming block operand is + // |old_incoming_block_id|. + void ReplacePhiIncomingBlock(uint32_t old_incoming_block_id, + uint32_t new_incoming_block_id) const; + + // Create an OpConstantNull instruction whose result type id is |type_id|. + Instruction* GetConstNull(uint32_t type_id) const; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_ diff --git a/source/opt/replace_invalid_opc.cpp b/source/opt/replace_invalid_opc.cpp index 38b7539b..1dcd06f5 100644 --- a/source/opt/replace_invalid_opc.cpp +++ b/source/opt/replace_invalid_opc.cpp @@ -71,10 +71,10 @@ bool ReplaceInvalidOpcodePass::RewriteFunction(Function* function, function->ForEachInst( [model, &modified, &last_line_dbg_inst, this](Instruction* inst) { // Track the debug information so we can have a meaningful message. - if (inst->opcode() == SpvOpLabel || inst->opcode() == SpvOpNoLine) { + if (inst->opcode() == SpvOpLabel || inst->IsNoLine()) { last_line_dbg_inst = nullptr; return; - } else if (inst->opcode() == SpvOpLine) { + } else if (inst->IsLine()) { last_line_dbg_inst = inst; return; } @@ -100,10 +100,19 @@ bool ReplaceInvalidOpcodePass::RewriteFunction(Function* function, ReplaceInstruction(inst, nullptr, 0, 0); } else { // Get the name of the source file. - Instruction* file_name = context()->get_def_use_mgr()->GetDef( - last_line_dbg_inst->GetSingleWordInOperand(0)); - const char* source = reinterpret_cast( - &file_name->GetInOperand(0).words[0]); + uint32_t file_name_id = 0; + if (last_line_dbg_inst->opcode() == SpvOpLine) { + file_name_id = last_line_dbg_inst->GetSingleWordInOperand(0); + } else { // Shader100::DebugLine + uint32_t debug_source_id = + last_line_dbg_inst->GetSingleWordInOperand(2); + Instruction* debug_source_inst = + context()->get_def_use_mgr()->GetDef(debug_source_id); + file_name_id = debug_source_inst->GetSingleWordInOperand(2); + } + Instruction* file_name = + context()->get_def_use_mgr()->GetDef(file_name_id); + const std::string source = file_name->GetInOperand(0).AsString(); // Get the line number and column number. uint32_t line_number = @@ -111,7 +120,7 @@ bool ReplaceInvalidOpcodePass::RewriteFunction(Function* function, uint32_t col_number = last_line_dbg_inst->GetSingleWordInOperand(2); // Replace the instruction. - ReplaceInstruction(inst, source, line_number, col_number); + ReplaceInstruction(inst, source.c_str(), line_number, col_number); } } }, diff --git a/source/opt/scalar_analysis.cpp b/source/opt/scalar_analysis.cpp index 38555e64..2b0a824c 100644 --- a/source/opt/scalar_analysis.cpp +++ b/source/opt/scalar_analysis.cpp @@ -581,7 +581,7 @@ static void PushToString(T id, std::u32string* str) { // Implements the hashing of SENodes. size_t SENodeHash::operator()(const SENode* node) const { - // Concatinate the terms into a string which we can hash. + // Concatenate the terms into a string which we can hash. std::u32string hash_string{}; // Hashing the type as a string is safer than hashing the enum as the enum is diff --git a/source/opt/scalar_analysis_nodes.h b/source/opt/scalar_analysis_nodes.h index b0e3fefd..91ce446f 100644 --- a/source/opt/scalar_analysis_nodes.h +++ b/source/opt/scalar_analysis_nodes.h @@ -167,7 +167,7 @@ class SENode { const ChildContainerType& GetChildren() const { return children_; } ChildContainerType& GetChildren() { return children_; } - // Return true if this node is a cant compute node. + // Return true if this node is a can't compute node. bool IsCantCompute() const { return GetType() == CanNotCompute; } // Implements a casting method for each type. diff --git a/source/opt/scalar_analysis_simplification.cpp b/source/opt/scalar_analysis_simplification.cpp index 52f2d6ad..3c1ecc08 100644 --- a/source/opt/scalar_analysis_simplification.cpp +++ b/source/opt/scalar_analysis_simplification.cpp @@ -88,7 +88,7 @@ class SENodeSimplifyImpl { private: // Recursively descend through the graph to build up the accumulator objects - // which are used to flatten the graph. |child| is the node currenty being + // which are used to flatten the graph. |child| is the node currently being // traversed and the |negation| flag is used to signify that this operation // was preceded by a unary negative operation and as such the result should be // negated. @@ -134,7 +134,7 @@ class SENodeSimplifyImpl { // offset. SENode* EliminateZeroCoefficientRecurrents(SENode* node); - // A reference the the analysis which requested the simplification. + // A reference the analysis which requested the simplification. ScalarEvolutionAnalysis& analysis_; // The node being simplified. diff --git a/source/opt/scalar_replacement_pass.cpp b/source/opt/scalar_replacement_pass.cpp index 4d47bdd8..e27c828b 100644 --- a/source/opt/scalar_replacement_pass.cpp +++ b/source/opt/scalar_replacement_pass.cpp @@ -24,9 +24,11 @@ #include "source/opt/reflect.h" #include "source/opt/types.h" #include "source/util/make_unique.h" +#include "types.h" static const uint32_t kDebugValueOperandValueIndex = 5; static const uint32_t kDebugValueOperandExpressionIndex = 6; +static const uint32_t kDebugDeclareOperandVariableIndex = 5; namespace spvtools { namespace opt { @@ -34,6 +36,10 @@ namespace opt { Pass::Status ScalarReplacementPass::Process() { Status status = Status::SuccessWithoutChange; for (auto& f : *get_module()) { + if (f.IsDeclaration()) { + continue; + } + Status functionStatus = ProcessFunction(&f); if (functionStatus == Status::Failure) return functionStatus; @@ -83,14 +89,14 @@ Pass::Status ScalarReplacementPass::ReplaceVariable( std::vector dead; bool replaced_all_uses = get_def_use_mgr()->WhileEachUser( inst, [this, &replacements, &dead](Instruction* user) { - if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) { + if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { if (ReplaceWholeDebugDeclare(user, replacements)) { dead.push_back(user); return true; } return false; } - if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) { + if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) { if (ReplaceWholeDebugValue(user, replacements)) { dead.push_back(user); return true; @@ -172,10 +178,14 @@ bool ScalarReplacementPass::ReplaceWholeDebugDeclare( // Add DebugValue instruction with Indexes operand and Deref operation. int32_t idx = 0; for (const auto* var : replacements) { + Instruction* insert_before = var->NextNode(); + while (insert_before->opcode() == SpvOpVariable) + insert_before = insert_before->NextNode(); + assert(insert_before != nullptr && "unexpected end of list"); Instruction* added_dbg_value = context()->get_debug_info_mgr()->AddDebugValueForDecl( dbg_decl, /*value_id=*/var->result_id(), - /*insert_before=*/var->NextNode(), /*scope_and_line=*/dbg_decl); + /*insert_before=*/insert_before, /*scope_and_line=*/dbg_decl); if (added_dbg_value == nullptr) return false; added_dbg_value->AddOperand( @@ -386,7 +396,7 @@ bool ScalarReplacementPass::CreateReplacementVariables( if (!components_used || components_used->count(elem)) { CreateVariable(*id, inst, elem, replacements); } else { - replacements->push_back(CreateNullConstant(*id)); + replacements->push_back(GetUndef(*id)); } elem++; }); @@ -397,8 +407,8 @@ bool ScalarReplacementPass::CreateReplacementVariables( CreateVariable(type->GetSingleWordInOperand(0u), inst, i, replacements); } else { - replacements->push_back( - CreateNullConstant(type->GetSingleWordInOperand(0u))); + uint32_t element_type_id = type->GetSingleWordInOperand(0); + replacements->push_back(GetUndef(element_type_id)); } } break; @@ -420,6 +430,10 @@ bool ScalarReplacementPass::CreateReplacementVariables( replacements->end(); } +Instruction* ScalarReplacementPass::GetUndef(uint32_t type_id) { + return get_def_use_mgr()->GetDef(Type2Undef(type_id)); +} + void ScalarReplacementPass::TransferAnnotations( const Instruction* source, std::vector* replacements) { // Only transfer invariant and restrict decorations on the variable. There are @@ -504,7 +518,7 @@ void ScalarReplacementPass::CreateVariable( } } - // Update the OpenCL.DebugInfo.100 debug information. + // Update the DebugInfo debug information. inst->UpdateDebugInfoFrom(varInst); replacements->push_back(inst); @@ -791,8 +805,8 @@ bool ScalarReplacementPass::CheckUses(const Instruction* inst, get_def_use_mgr()->ForEachUse(inst, [this, max_legal_index, stats, &ok]( const Instruction* user, uint32_t index) { - if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare || - user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) { + if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare || + user->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) { // TODO: include num_partial_accesses if it uses Fragment operation or // DebugValue has Indexes operand. stats->num_full_accesses++; @@ -864,6 +878,11 @@ bool ScalarReplacementPass::CheckUsesRelaxed(const Instruction* inst) const { case SpvOpImageTexelPointer: if (!CheckImageTexelPointer(index)) ok = false; break; + case SpvOpExtInst: + if (user->GetCommonDebugOpcode() != CommonDebugInfoDebugDeclare || + !CheckDebugDeclare(index)) + ok = false; + break; default: ok = false; break; @@ -894,6 +913,12 @@ bool ScalarReplacementPass::CheckStore(const Instruction* inst, return false; return true; } + +bool ScalarReplacementPass::CheckDebugDeclare(uint32_t index) const { + if (index != kDebugDeclareOperandVariableIndex) return false; + return true; +} + bool ScalarReplacementPass::IsLargerThanSizeLimit(uint64_t length) const { if (max_num_elements_ == 0) { return false; @@ -961,20 +986,6 @@ ScalarReplacementPass::GetUsedComponents(Instruction* inst) { return result; } -Instruction* ScalarReplacementPass::CreateNullConstant(uint32_t type_id) { - analysis::TypeManager* type_mgr = context()->get_type_mgr(); - analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); - - const analysis::Type* type = type_mgr->GetType(type_id); - const analysis::Constant* null_const = const_mgr->GetConstant(type, {}); - Instruction* null_inst = - const_mgr->GetDefiningInstruction(null_const, type_id); - if (null_inst != nullptr) { - context()->UpdateDefUse(null_inst); - } - return null_inst; -} - uint64_t ScalarReplacementPass::GetMaxLegalIndex( const Instruction* var_inst) const { assert(var_inst->opcode() == SpvOpVariable && diff --git a/source/opt/scalar_replacement_pass.h b/source/opt/scalar_replacement_pass.h index 9e9f0739..6a66dfb8 100644 --- a/source/opt/scalar_replacement_pass.h +++ b/source/opt/scalar_replacement_pass.h @@ -15,6 +15,7 @@ #ifndef SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_ #define SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_ +#include #include #include #include @@ -23,23 +24,34 @@ #include #include "source/opt/function.h" -#include "source/opt/pass.h" +#include "source/opt/mem_pass.h" #include "source/opt/type_manager.h" namespace spvtools { namespace opt { // Documented in optimizer.hpp -class ScalarReplacementPass : public Pass { +class ScalarReplacementPass : public MemPass { private: static const uint32_t kDefaultLimit = 100; public: ScalarReplacementPass(uint32_t limit = kDefaultLimit) : max_num_elements_(limit) { - name_[0] = '\0'; - strcat(name_, "scalar-replacement="); - sprintf(&name_[strlen(name_)], "%d", max_num_elements_); + const auto num_to_write = snprintf( + name_, sizeof(name_), "scalar-replacement=%u", max_num_elements_); + assert(size_t(num_to_write) < sizeof(name_)); + (void)num_to_write; // Mark as unused + +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + // ClusterFuzz/OSS-Fuzz is likely to yield examples with very large arrays. + // This can cause timeouts and memouts during fuzzing that + // are not classed as bugs. To avoid this noise, we set the + // max_num_elements_ to a smaller value for fuzzing. + max_num_elements_ = + (max_num_elements_ > 0 && max_num_elements_ < 100 ? max_num_elements_ + : 100); +#endif } const char* name() const override { return name_; } @@ -142,6 +154,9 @@ class ScalarReplacementPass : public Pass { // of |inst| and the store is not to volatile memory. bool CheckStore(const Instruction* inst, uint32_t index) const; + // Returns true if the DebugDeclare can be scalarized at |index|. + bool CheckDebugDeclare(uint32_t index) const; + // Returns true if |index| is the pointer operand of an OpImageTexelPointer // instruction. bool CheckImageTexelPointer(uint32_t index) const; @@ -231,10 +246,8 @@ class ScalarReplacementPass : public Pass { std::unique_ptr> GetUsedComponents( Instruction* inst); - // Returns an instruction defining a null constant with type |type_id|. If - // one already exists, it is returned. Otherwise a new one is created. - // Returns |nullptr| if the new constant could not be created. - Instruction* CreateNullConstant(uint32_t type_id); + // Returns an instruction defining an undefined value type |type_id|. + Instruction* GetUndef(uint32_t type_id); // Maps storage type to a pointer type enclosing that type. std::unordered_map pointee_to_pointer_; @@ -252,7 +265,10 @@ class ScalarReplacementPass : public Pass { // Limit on the number of members in an object that will be replaced. // 0 means there is no limit. uint32_t max_num_elements_; - char name_[55]; + // This has to be big enough to fit "scalar-replacement=" followed by a + // uint32_t number written in decimal (so 10 digits), and then a + // terminating nul. + char name_[30]; }; } // namespace opt diff --git a/source/opt/set_spec_constant_default_value_pass.cpp b/source/opt/set_spec_constant_default_value_pass.cpp index 4c8d116f..4def2b09 100644 --- a/source/opt/set_spec_constant_default_value_pass.cpp +++ b/source/opt/set_spec_constant_default_value_pass.cpp @@ -85,6 +85,10 @@ std::vector ParseDefaultValueStr(const char* text, // with 0x1, which represents a 'true'. // If all words in the bit pattern are zero, returns a bit pattern with 0x0, // which represents a 'false'. +// For integer and floating point types narrower than 32 bits, the upper bits +// in the input bit pattern are ignored. Instead the upper bits are set +// according to SPIR-V literal requirements: sign extend a signed integer, and +// otherwise set the upper bits to zero. std::vector ParseDefaultValueBitPattern( const std::vector& input_bit_pattern, const analysis::Type* type) { @@ -98,12 +102,33 @@ std::vector ParseDefaultValueBitPattern( } return result; } else if (const auto* IT = type->AsInteger()) { - if (IT->width() == input_bit_pattern.size() * sizeof(uint32_t) * 8) { - return std::vector(input_bit_pattern); + const auto width = IT->width(); + assert(width > 0); + const auto adjusted_width = std::max(32u, width); + if (adjusted_width == input_bit_pattern.size() * sizeof(uint32_t) * 8) { + result = std::vector(input_bit_pattern); + if (width < 32) { + const uint32_t high_active_bit = (1u << width) >> 1; + if (IT->IsSigned() && (high_active_bit & result[0])) { + // Sign extend. This overwrites the sign bit again, but that's ok. + result[0] = result[0] | ~(high_active_bit - 1); + } else { + // Upper bits must be zero. + result[0] = result[0] & ((1u << width) - 1); + } + } + return result; } } else if (const auto* FT = type->AsFloat()) { - if (FT->width() == input_bit_pattern.size() * sizeof(uint32_t) * 8) { - return std::vector(input_bit_pattern); + const auto width = FT->width(); + const auto adjusted_width = std::max(32u, width); + if (adjusted_width == input_bit_pattern.size() * sizeof(uint32_t) * 8) { + result = std::vector(input_bit_pattern); + if (width < 32) { + // Upper bits must be zero. + result[0] = result[0] & ((1u << width) - 1); + } + return result; } } result.clear(); diff --git a/source/opt/simplification_pass.cpp b/source/opt/simplification_pass.cpp index 319ceecf..43ec15f8 100644 --- a/source/opt/simplification_pass.cpp +++ b/source/opt/simplification_pass.cpp @@ -45,6 +45,10 @@ void SimplificationPass::AddNewOperands( } bool SimplificationPass::SimplifyFunction(Function* function) { + if (function->IsDeclaration()) { + return false; + } + bool modified = false; // Phase 1: Traverse all instructions in dominance order. // The second phase will only be on the instructions whose inputs have changed diff --git a/source/opt/spread_volatile_semantics.cpp b/source/opt/spread_volatile_semantics.cpp new file mode 100644 index 00000000..b61fd0f4 --- /dev/null +++ b/source/opt/spread_volatile_semantics.cpp @@ -0,0 +1,298 @@ +// Copyright (c) 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/spread_volatile_semantics.h" + +#include "source/opt/decoration_manager.h" +#include "source/opt/ir_builder.h" +#include "source/spirv_constant.h" + +namespace spvtools { +namespace opt { +namespace { + +const uint32_t kOpDecorateInOperandBuiltinDecoration = 2u; +const uint32_t kOpLoadInOperandMemoryOperands = 1u; +const uint32_t kOpEntryPointInOperandEntryPoint = 1u; +const uint32_t kOpEntryPointInOperandInterface = 3u; + +bool HasBuiltinDecoration(analysis::DecorationManager* decoration_manager, + uint32_t var_id, uint32_t built_in) { + return decoration_manager->FindDecoration( + var_id, SpvDecorationBuiltIn, [built_in](const Instruction& inst) { + return built_in == inst.GetSingleWordInOperand( + kOpDecorateInOperandBuiltinDecoration); + }); +} + +bool IsBuiltInForRayTracingVolatileSemantics(uint32_t built_in) { + switch (built_in) { + case SpvBuiltInSMIDNV: + case SpvBuiltInWarpIDNV: + case SpvBuiltInSubgroupSize: + case SpvBuiltInSubgroupLocalInvocationId: + case SpvBuiltInSubgroupEqMask: + case SpvBuiltInSubgroupGeMask: + case SpvBuiltInSubgroupGtMask: + case SpvBuiltInSubgroupLeMask: + case SpvBuiltInSubgroupLtMask: + return true; + default: + return false; + } +} + +bool HasBuiltinForRayTracingVolatileSemantics( + analysis::DecorationManager* decoration_manager, uint32_t var_id) { + return decoration_manager->FindDecoration( + var_id, SpvDecorationBuiltIn, [](const Instruction& inst) { + uint32_t built_in = + inst.GetSingleWordInOperand(kOpDecorateInOperandBuiltinDecoration); + return IsBuiltInForRayTracingVolatileSemantics(built_in); + }); +} + +bool HasVolatileDecoration(analysis::DecorationManager* decoration_manager, + uint32_t var_id) { + return decoration_manager->HasDecoration(var_id, SpvDecorationVolatile); +} + +} // namespace + +Pass::Status SpreadVolatileSemantics::Process() { + if (HasNoExecutionModel()) { + return Status::SuccessWithoutChange; + } + const bool is_vk_memory_model_enabled = + context()->get_feature_mgr()->HasCapability( + SpvCapabilityVulkanMemoryModel); + CollectTargetsForVolatileSemantics(is_vk_memory_model_enabled); + + // If VulkanMemoryModel capability is not enabled, we have to set Volatile + // decoration for interface variables instead of setting Volatile for load + // instructions. If an interface (or pointers to it) is used by two load + // instructions in two entry points and one must be volatile while another + // is not, we have to report an error for the conflict. + if (!is_vk_memory_model_enabled && + HasInterfaceInConflictOfVolatileSemantics()) { + return Status::Failure; + } + + return SpreadVolatileSemanticsToVariables(is_vk_memory_model_enabled); +} + +Pass::Status SpreadVolatileSemantics::SpreadVolatileSemanticsToVariables( + const bool is_vk_memory_model_enabled) { + Status status = Status::SuccessWithoutChange; + for (Instruction& var : context()->types_values()) { + auto entry_function_ids = + EntryFunctionsToSpreadVolatileSemanticsForVar(var.result_id()); + if (entry_function_ids.empty()) { + continue; + } + + if (is_vk_memory_model_enabled) { + SetVolatileForLoadsInEntries(&var, entry_function_ids); + } else { + DecorateVarWithVolatile(&var); + } + status = Status::SuccessWithChange; + } + return status; +} + +bool SpreadVolatileSemantics::IsTargetUsedByNonVolatileLoadInEntryPoint( + uint32_t var_id, Instruction* entry_point) { + uint32_t entry_function_id = + entry_point->GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint); + std::unordered_set funcs; + context()->CollectCallTreeFromRoots(entry_function_id, &funcs); + return !VisitLoadsOfPointersToVariableInEntries( + var_id, + [](Instruction* load) { + // If it has a load without volatile memory operand, finish traversal + // and return false. + if (load->NumInOperands() <= kOpLoadInOperandMemoryOperands) { + return false; + } + uint32_t memory_operands = + load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands); + return (memory_operands & SpvMemoryAccessVolatileMask) != 0; + }, + funcs); +} + +bool SpreadVolatileSemantics::HasInterfaceInConflictOfVolatileSemantics() { + for (Instruction& entry_point : get_module()->entry_points()) { + SpvExecutionModel execution_model = + static_cast(entry_point.GetSingleWordInOperand(0)); + for (uint32_t operand_index = kOpEntryPointInOperandInterface; + operand_index < entry_point.NumInOperands(); ++operand_index) { + uint32_t var_id = entry_point.GetSingleWordInOperand(operand_index); + if (!EntryFunctionsToSpreadVolatileSemanticsForVar(var_id).empty() && + !IsTargetForVolatileSemantics(var_id, execution_model) && + IsTargetUsedByNonVolatileLoadInEntryPoint(var_id, &entry_point)) { + Instruction* inst = context()->get_def_use_mgr()->GetDef(var_id); + context()->EmitErrorMessage( + "Variable is a target for Volatile semantics for an entry point, " + "but it is not for another entry point", + inst); + return true; + } + } + } + return false; +} + +void SpreadVolatileSemantics::MarkVolatileSemanticsForVariable( + uint32_t var_id, Instruction* entry_point) { + uint32_t entry_function_id = + entry_point->GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint); + auto itr = var_ids_to_entry_fn_for_volatile_semantics_.find(var_id); + if (itr == var_ids_to_entry_fn_for_volatile_semantics_.end()) { + var_ids_to_entry_fn_for_volatile_semantics_[var_id] = {entry_function_id}; + return; + } + itr->second.insert(entry_function_id); +} + +void SpreadVolatileSemantics::CollectTargetsForVolatileSemantics( + const bool is_vk_memory_model_enabled) { + for (Instruction& entry_point : get_module()->entry_points()) { + SpvExecutionModel execution_model = + static_cast(entry_point.GetSingleWordInOperand(0)); + for (uint32_t operand_index = kOpEntryPointInOperandInterface; + operand_index < entry_point.NumInOperands(); ++operand_index) { + uint32_t var_id = entry_point.GetSingleWordInOperand(operand_index); + if (!IsTargetForVolatileSemantics(var_id, execution_model)) { + continue; + } + if (is_vk_memory_model_enabled || + IsTargetUsedByNonVolatileLoadInEntryPoint(var_id, &entry_point)) { + MarkVolatileSemanticsForVariable(var_id, &entry_point); + } + } + } +} + +void SpreadVolatileSemantics::DecorateVarWithVolatile(Instruction* var) { + analysis::DecorationManager* decoration_manager = + context()->get_decoration_mgr(); + uint32_t var_id = var->result_id(); + if (HasVolatileDecoration(decoration_manager, var_id)) { + return; + } + get_decoration_mgr()->AddDecoration( + SpvOpDecorate, {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {var_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {SpvDecorationVolatile}}}); +} + +bool SpreadVolatileSemantics::VisitLoadsOfPointersToVariableInEntries( + uint32_t var_id, const std::function& handle_load, + const std::unordered_set& function_ids) { + std::vector worklist({var_id}); + auto* def_use_mgr = context()->get_def_use_mgr(); + while (!worklist.empty()) { + uint32_t ptr_id = worklist.back(); + worklist.pop_back(); + bool finish_traversal = !def_use_mgr->WhileEachUser( + ptr_id, [this, &worklist, &ptr_id, handle_load, + &function_ids](Instruction* user) { + BasicBlock* block = context()->get_instr_block(user); + if (block == nullptr || + function_ids.find(block->GetParent()->result_id()) == + function_ids.end()) { + return true; + } + + if (user->opcode() == SpvOpAccessChain || + user->opcode() == SpvOpInBoundsAccessChain || + user->opcode() == SpvOpPtrAccessChain || + user->opcode() == SpvOpInBoundsPtrAccessChain || + user->opcode() == SpvOpCopyObject) { + if (ptr_id == user->GetSingleWordInOperand(0)) + worklist.push_back(user->result_id()); + return true; + } + + if (user->opcode() != SpvOpLoad) { + return true; + } + + return handle_load(user); + }); + if (finish_traversal) return false; + } + return true; +} + +void SpreadVolatileSemantics::SetVolatileForLoadsInEntries( + Instruction* var, const std::unordered_set& entry_function_ids) { + // Set Volatile memory operand for all load instructions if they do not have + // it. + for (auto entry_id : entry_function_ids) { + std::unordered_set funcs; + context()->CollectCallTreeFromRoots(entry_id, &funcs); + VisitLoadsOfPointersToVariableInEntries( + var->result_id(), + [](Instruction* load) { + if (load->NumInOperands() <= kOpLoadInOperandMemoryOperands) { + load->AddOperand({SPV_OPERAND_TYPE_MEMORY_ACCESS, + {SpvMemoryAccessVolatileMask}}); + return true; + } + uint32_t memory_operands = + load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands); + memory_operands |= SpvMemoryAccessVolatileMask; + load->SetInOperand(kOpLoadInOperandMemoryOperands, {memory_operands}); + return true; + }, + funcs); + } +} + +bool SpreadVolatileSemantics::IsTargetForVolatileSemantics( + uint32_t var_id, SpvExecutionModel execution_model) { + analysis::DecorationManager* decoration_manager = + context()->get_decoration_mgr(); + if (execution_model == SpvExecutionModelFragment) { + return get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 6) && + HasBuiltinDecoration(decoration_manager, var_id, + SpvBuiltInHelperInvocation); + } + + if (execution_model == SpvExecutionModelIntersectionKHR || + execution_model == SpvExecutionModelIntersectionNV) { + if (HasBuiltinDecoration(decoration_manager, var_id, + SpvBuiltInRayTmaxKHR)) { + return true; + } + } + + switch (execution_model) { + case SpvExecutionModelRayGenerationKHR: + case SpvExecutionModelClosestHitKHR: + case SpvExecutionModelMissKHR: + case SpvExecutionModelCallableKHR: + case SpvExecutionModelIntersectionKHR: + return HasBuiltinForRayTracingVolatileSemantics(decoration_manager, + var_id); + default: + return false; + } +} + +} // namespace opt +} // namespace spvtools diff --git a/source/opt/spread_volatile_semantics.h b/source/opt/spread_volatile_semantics.h new file mode 100644 index 00000000..014858d3 --- /dev/null +++ b/source/opt/spread_volatile_semantics.h @@ -0,0 +1,116 @@ +// Copyright (c) 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_SPREAD_VOLATILE_SEMANTICS_H_ +#define SOURCE_OPT_SPREAD_VOLATILE_SEMANTICS_H_ + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class SpreadVolatileSemantics : public Pass { + public: + SpreadVolatileSemantics() {} + + const char* name() const override { return "spread-volatile-semantics"; } + + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisDecorations | + IRContext::kAnalysisInstrToBlockMapping; + } + + private: + // Returns true if it does not have an execution model. Linkage shaders do not + // have an execution model. + bool HasNoExecutionModel() { + return get_module()->entry_points().empty() && + context()->get_feature_mgr()->HasCapability(SpvCapabilityLinkage); + } + + // Iterates interface variables and spreads the Volatile semantics if it has + // load instructions for the Volatile semantics. + Pass::Status SpreadVolatileSemanticsToVariables( + const bool is_vk_memory_model_enabled); + + // Returns whether |var_id| is the result id of a target builtin variable for + // the volatile semantics for |execution_model| based on the Vulkan spec + // VUID-StandaloneSpirv-VulkanMemoryModel-04678 or + // VUID-StandaloneSpirv-VulkanMemoryModel-04679. + bool IsTargetForVolatileSemantics(uint32_t var_id, + SpvExecutionModel execution_model); + + // Collects interface variables that need the volatile semantics. + // |is_vk_memory_model_enabled| is true if VulkanMemoryModel capability is + // enabled. + void CollectTargetsForVolatileSemantics( + const bool is_vk_memory_model_enabled); + + // Reports an error if an interface variable is used by two entry points and + // it needs the Volatile decoration for one but not for another. Returns true + // if the error must be reported. + bool HasInterfaceInConflictOfVolatileSemantics(); + + // Returns whether the variable whose result is |var_id| is used by a + // non-volatile load or a pointer to it is used by a non-volatile load in + // |entry_point| or not. + bool IsTargetUsedByNonVolatileLoadInEntryPoint(uint32_t var_id, + Instruction* entry_point); + + // Visits load instructions of pointers to variable whose result id is + // |var_id| if the load instructions are in reachable functions from entry + // points. |handle_load| is a function to do some actions for the load + // instructions. Finishes the traversal and returns false if |handle_load| + // returns false for a load instruction. Otherwise, returns true after running + // |handle_load| for all the load instructions. + bool VisitLoadsOfPointersToVariableInEntries( + uint32_t var_id, const std::function& handle_load, + const std::unordered_set& function_ids); + + // Sets Memory Operands of OpLoad instructions that load |var| or pointers + // of |var| as Volatile if the function id of the OpLoad instruction is + // included in |entry_function_ids|. + void SetVolatileForLoadsInEntries( + Instruction* var, const std::unordered_set& entry_function_ids); + + // Adds OpDecorate Volatile for |var| if it does not exist. + void DecorateVarWithVolatile(Instruction* var); + + // Returns a set of entry function ids to spread the volatile semantics for + // the variable with the result id |var_id|. + std::unordered_set EntryFunctionsToSpreadVolatileSemanticsForVar( + uint32_t var_id) { + auto itr = var_ids_to_entry_fn_for_volatile_semantics_.find(var_id); + if (itr == var_ids_to_entry_fn_for_volatile_semantics_.end()) return {}; + return itr->second; + } + + // Specifies that we have to spread the volatile semantics for the + // variable with the result id |var_id| for the entry point |entry_point|. + void MarkVolatileSemanticsForVariable(uint32_t var_id, + Instruction* entry_point); + + // Result ids of variables to entry function ids for the volatile semantics + // spread. + std::unordered_map> + var_ids_to_entry_fn_for_volatile_semantics_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_SPREAD_VOLATILE_SEMANTICS_H_ diff --git a/source/opt/ssa_rewrite_pass.cpp b/source/opt/ssa_rewrite_pass.cpp index 81770d77..22d81104 100644 --- a/source/opt/ssa_rewrite_pass.cpp +++ b/source/opt/ssa_rewrite_pass.cpp @@ -67,7 +67,6 @@ namespace opt { namespace { const uint32_t kStoreValIdInIdx = 1; const uint32_t kVariableInitIdInIdx = 1; -const uint32_t kDebugDeclareOperandVariableIdx = 5; } // namespace std::string SSARewriter::PhiCandidate::PrettyPrint(const CFG* cfg) const { @@ -315,8 +314,8 @@ void SSARewriter::ProcessStore(Instruction* inst, BasicBlock* bb) { } if (pass_->IsTargetVar(var_id)) { WriteVariable(var_id, bb, val_id); - pass_->context()->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible( - inst, var_id, val_id, inst, &decls_invisible_to_value_assignment_); + pass_->context()->get_debug_info_mgr()->AddDebugValueForVariable( + inst, var_id, val_id, inst); #if SSA_REWRITE_DEBUGGING_LEVEL > 1 std::cerr << "\tFound store '%" << var_id << " = %" << val_id << "': " @@ -559,9 +558,9 @@ bool SSARewriter::ApplyReplacements() { // Add DebugValue for the new OpPhi instruction. insert_it->SetDebugScope(local_var->GetDebugScope()); - pass_->context()->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible( + pass_->context()->get_debug_info_mgr()->AddDebugValueForVariable( &*insert_it, phi_candidate->var_id(), phi_candidate->result_id(), - &*insert_it, &decls_invisible_to_value_assignment_); + &*insert_it); modified = true; } @@ -650,62 +649,6 @@ void SSARewriter::FinalizePhiCandidates() { } } -Pass::Status SSARewriter::AddDebugValuesForInvisibleDebugDecls(Function* fp) { - // For the cases the value assignment is invisible to DebugDeclare e.g., - // the argument passing for an inlined function. - // - // Before inlining foo(int x): - // a = 3; - // foo(3); - // After inlining: - // a = 3; - // foo and x disappeared but we want to specify "DebugValue: %x = %int_3". - // - // We want to specify the value for the variable using |defs_at_block_[bb]|, - // where |bb| is the basic block contains the decl. - DominatorAnalysis* dom_tree = pass_->context()->GetDominatorAnalysis(fp); - Pass::Status status = Pass::Status::SuccessWithoutChange; - for (auto* decl : decls_invisible_to_value_assignment_) { - uint32_t var_id = - decl->GetSingleWordOperand(kDebugDeclareOperandVariableIdx); - auto* var = pass_->get_def_use_mgr()->GetDef(var_id); - if (var->opcode() == SpvOpFunctionParameter) continue; - - auto* bb = pass_->context()->get_instr_block(decl); - uint32_t value_id = GetValueAtBlock(var_id, bb); - Instruction* value = nullptr; - if (value_id) value = pass_->get_def_use_mgr()->GetDef(value_id); - - // If |value| is defined before the function body, it dominates |decl|. - // If |value| dominates |decl|, we can set it as DebugValue. - if (value && (pass_->context()->get_instr_block(value) == nullptr || - dom_tree->Dominates(value, decl))) { - if (pass_->context()->get_debug_info_mgr()->AddDebugValueForDecl( - decl, value->result_id(), decl, value) == nullptr) { - return Pass::Status::Failure; - } - } else { - // If |value| in the same basic block does not dominate |decl|, we can - // assign the value in the immediate dominator. - value_id = GetValueAtBlock(var_id, dom_tree->ImmediateDominator(bb)); - if (value_id) value = pass_->get_def_use_mgr()->GetDef(value_id); - if (value_id && - pass_->context()->get_debug_info_mgr()->AddDebugValueForDecl( - decl, value_id, decl, value) == nullptr) { - return Pass::Status::Failure; - } - } - - // DebugDeclares of target variables will be removed by - // SSARewritePass::Process(). - if (!pass_->IsTargetVar(var_id)) { - pass_->context()->get_debug_info_mgr()->KillDebugDeclares(var_id); - } - status = Pass::Status::SuccessWithChange; - } - return status; -} - Pass::Status SSARewriter::RewriteFunctionIntoSSA(Function* fp) { #if SSA_REWRITE_DEBUGGING_LEVEL > 0 std::cerr << "Function before SSA rewrite:\n" @@ -735,12 +678,6 @@ Pass::Status SSARewriter::RewriteFunctionIntoSSA(Function* fp) { // Finally, apply all the replacements in the IR. bool modified = ApplyReplacements(); - auto status = AddDebugValuesForInvisibleDebugDecls(fp); - if (status == Pass::Status::SuccessWithChange || - status == Pass::Status::Failure) { - return status; - } - #if SSA_REWRITE_DEBUGGING_LEVEL > 0 std::cerr << "\n\n\nFunction after SSA rewrite:\n" << fp->PrettyPrint(0) << "\n"; @@ -753,6 +690,9 @@ Pass::Status SSARewriter::RewriteFunctionIntoSSA(Function* fp) { Pass::Status SSARewritePass::Process() { Status status = Status::SuccessWithoutChange; for (auto& fn : *get_module()) { + if (fn.IsDeclaration()) { + continue; + } status = CombineStatus(status, SSARewriter(this).RewriteFunctionIntoSSA(&fn)); // Kill DebugDeclares for target variables. diff --git a/source/opt/ssa_rewrite_pass.h b/source/opt/ssa_rewrite_pass.h index 1f4cd24c..2470f85f 100644 --- a/source/opt/ssa_rewrite_pass.h +++ b/source/opt/ssa_rewrite_pass.h @@ -253,11 +253,6 @@ class SSARewriter { // candidates. void FinalizePhiCandidates(); - // Adds DebugValues for DebugDeclares in - // |decls_invisible_to_value_assignment_|. Returns whether the function was - // modified or not, and whether or not the conversion was successful. - Pass::Status AddDebugValuesForInvisibleDebugDecls(Function* fp); - // Prints the table of Phi candidates to std::cerr. void PrintPhiCandidates() const; @@ -295,10 +290,6 @@ class SSARewriter { // Memory pass requesting the SSA rewriter. MemPass* pass_; - - // Set of DebugDeclare instructions that are not added as DebugValue because - // they are invisible to the store or phi instructions. - std::unordered_set decls_invisible_to_value_assignment_; }; class SSARewritePass : public MemPass { diff --git a/source/opt/strength_reduction_pass.h b/source/opt/strength_reduction_pass.h index 8dfeb307..1cbbbcc6 100644 --- a/source/opt/strength_reduction_pass.h +++ b/source/opt/strength_reduction_pass.h @@ -34,7 +34,7 @@ class StrengthReductionPass : public Pass { // Returns true if something changed. bool ReplaceMultiplyByPowerOf2(BasicBlock::iterator*); - // Scan the types and constants in the module looking for the the integer + // Scan the types and constants in the module looking for the integer // types that we are // interested in. The shift operation needs a small unsigned integer. We // need to find diff --git a/source/opt/strip_debug_info_pass.cpp b/source/opt/strip_debug_info_pass.cpp index c86ce578..6a0ebf24 100644 --- a/source/opt/strip_debug_info_pass.cpp +++ b/source/opt/strip_debug_info_pass.cpp @@ -14,6 +14,7 @@ #include "source/opt/strip_debug_info_pass.h" #include "source/opt/ir_context.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -21,9 +22,8 @@ namespace opt { Pass::Status StripDebugInfoPass::Process() { bool uses_non_semantic_info = false; for (auto& inst : context()->module()->extensions()) { - const char* ext_name = - reinterpret_cast(&inst.GetInOperand(0).words[0]); - if (0 == std::strcmp(ext_name, "SPV_KHR_non_semantic_info")) { + const std::string ext_name = inst.GetInOperand(0).AsString(); + if (ext_name == "SPV_KHR_non_semantic_info") { uses_non_semantic_info = true; } } @@ -46,9 +46,10 @@ Pass::Status StripDebugInfoPass::Process() { if (use->opcode() == SpvOpExtInst) { auto ext_inst_set = def_use->GetDef(use->GetSingleWordInOperand(0u)); - const char* extension_name = reinterpret_cast( - &ext_inst_set->GetInOperand(0).words[0]); - if (0 == std::strncmp(extension_name, "NonSemantic.", 12)) { + const std::string extension_name = + ext_inst_set->GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, + "NonSemantic.")) { // found a non-semantic use, return false as we cannot // remove this OpString return false; diff --git a/source/opt/strip_nonsemantic_info_pass.cpp b/source/opt/strip_nonsemantic_info_pass.cpp new file mode 100644 index 00000000..cd1fbb63 --- /dev/null +++ b/source/opt/strip_nonsemantic_info_pass.cpp @@ -0,0 +1,115 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/strip_nonsemantic_info_pass.h" + +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" +#include "source/util/string_utils.h" + +namespace spvtools { +namespace opt { + +Pass::Status StripNonSemanticInfoPass::Process() { + bool modified = false; + + std::vector to_remove; + + bool other_uses_for_decorate_string = false; + for (auto& inst : context()->module()->annotations()) { + switch (inst.opcode()) { + case SpvOpDecorateStringGOOGLE: + if (inst.GetSingleWordInOperand(1) == SpvDecorationHlslSemanticGOOGLE || + inst.GetSingleWordInOperand(1) == SpvDecorationUserTypeGOOGLE) { + to_remove.push_back(&inst); + } else { + other_uses_for_decorate_string = true; + } + break; + + case SpvOpMemberDecorateStringGOOGLE: + if (inst.GetSingleWordInOperand(2) == SpvDecorationHlslSemanticGOOGLE || + inst.GetSingleWordInOperand(2) == SpvDecorationUserTypeGOOGLE) { + to_remove.push_back(&inst); + } else { + other_uses_for_decorate_string = true; + } + break; + + case SpvOpDecorateId: + if (inst.GetSingleWordInOperand(1) == + SpvDecorationHlslCounterBufferGOOGLE) { + to_remove.push_back(&inst); + } + break; + + default: + break; + } + } + + for (auto& inst : context()->module()->extensions()) { + const std::string ext_name = inst.GetInOperand(0).AsString(); + if (ext_name == "SPV_GOOGLE_hlsl_functionality1") { + to_remove.push_back(&inst); + } else if (ext_name == "SPV_GOOGLE_user_type") { + to_remove.push_back(&inst); + } else if (!other_uses_for_decorate_string && + ext_name == "SPV_GOOGLE_decorate_string") { + to_remove.push_back(&inst); + } else if (ext_name == "SPV_KHR_non_semantic_info") { + to_remove.push_back(&inst); + } + } + + // remove any extended inst imports that are non semantic + std::unordered_set non_semantic_sets; + for (auto& inst : context()->module()->ext_inst_imports()) { + assert(inst.opcode() == SpvOpExtInstImport && + "Expecting an import of an extension's instruction set."); + const std::string extension_name = inst.GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, "NonSemantic.")) { + non_semantic_sets.insert(inst.result_id()); + to_remove.push_back(&inst); + } + } + + // if we removed some non-semantic sets, then iterate over the instructions in + // the module to remove any OpExtInst that referenced those sets + if (!non_semantic_sets.empty()) { + context()->module()->ForEachInst( + [&non_semantic_sets, &to_remove](Instruction* inst) { + if (inst->opcode() == SpvOpExtInst) { + if (non_semantic_sets.find(inst->GetSingleWordInOperand(0)) != + non_semantic_sets.end()) { + to_remove.push_back(inst); + } + } + }, + true); + } + + for (auto* inst : to_remove) { + modified = true; + context()->KillInst(inst); + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/source/opt/strip_nonsemantic_info_pass.h b/source/opt/strip_nonsemantic_info_pass.h new file mode 100644 index 00000000..ff4e2e1d --- /dev/null +++ b/source/opt/strip_nonsemantic_info_pass.h @@ -0,0 +1,44 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_STRIP_NONSEMANTIC_INFO_PASS_H_ +#define SOURCE_OPT_STRIP_NONSEMANTIC_INFO_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class StripNonSemanticInfoPass : public Pass { + public: + const char* name() const override { return "strip-nonsemantic"; } + Status Process() override; + + // Return the mask of preserved Analyses. + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG | + IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_STRIP_NONSEMANTIC_INFO_PASS_H_ diff --git a/source/opt/type_manager.cpp b/source/opt/type_manager.cpp index 7935ad33..a0006f55 100644 --- a/source/opt/type_manager.cpp +++ b/source/opt/type_manager.cpp @@ -23,6 +23,7 @@ #include "source/opt/log.h" #include "source/opt/reflect.h" #include "source/util/make_unique.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -234,6 +235,7 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) { DefineParameterlessCase(PipeStorage); DefineParameterlessCase(NamedBarrier); DefineParameterlessCase(AccelerationStructureNV); + DefineParameterlessCase(RayQueryKHR); #undef DefineParameterlessCase case Type::kInteger: typeInst = MakeUnique( @@ -349,11 +351,8 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) { } case Type::kOpaque: { const Opaque* opaque = type->AsOpaque(); - size_t size = opaque->name().size(); // Convert to null-terminated packed UTF-8 string. - std::vector words(size / 4 + 1, 0); - char* dst = reinterpret_cast(words.data()); - strncpy(dst, opaque->name().c_str(), size); + std::vector words = spvtools::utils::MakeVector(opaque->name()); typeInst = MakeUnique( context(), SpvOpTypeOpaque, 0, id, std::initializer_list{ @@ -529,6 +528,7 @@ Type* TypeManager::RebuildType(const Type& type) { DefineNoSubtypeCase(PipeStorage); DefineNoSubtypeCase(NamedBarrier); DefineNoSubtypeCase(AccelerationStructureNV); + DefineNoSubtypeCase(RayQueryKHR); #undef DefineNoSubtypeCase case Type::kVector: { const Vector* vec_ty = type.AsVector(); @@ -781,8 +781,7 @@ Type* TypeManager::RecordIfTypeDefinition(const Instruction& inst) { } } break; case SpvOpTypeOpaque: { - const uint32_t* data = inst.GetInOperand(0).words.data(); - type = new Opaque(reinterpret_cast(data)); + type = new Opaque(inst.GetInOperand(0).AsString()); } break; case SpvOpTypePointer: { uint32_t pointee_type_id = inst.GetSingleWordInOperand(1); diff --git a/source/opt/type_manager.h b/source/opt/type_manager.h index ce9d83d4..72e37f48 100644 --- a/source/opt/type_manager.h +++ b/source/opt/type_manager.h @@ -160,6 +160,13 @@ class TypeManager { uint32_t GetFloatTypeId() { return GetTypeInstruction(GetFloatType()); } + Type* GetDoubleType() { + Float float_type(64); + return GetRegisteredType(&float_type); + } + + uint32_t GetDoubleTypeId() { return GetTypeInstruction(GetDoubleType()); } + Type* GetUIntVectorType(uint32_t size) { Vector vec_type(GetUIntType(), size); return GetRegisteredType(&vec_type); diff --git a/source/opt/types.cpp b/source/opt/types.cpp index b1eb3a50..056acebb 100644 --- a/source/opt/types.cpp +++ b/source/opt/types.cpp @@ -16,11 +16,13 @@ #include #include +#include #include #include #include #include +#include "source/util/hash_combine.h" #include "source/util/make_unique.h" #include "spirv/unified1/spirv.h" @@ -28,6 +30,7 @@ namespace spvtools { namespace opt { namespace analysis { +using spvtools::utils::hash_combine; using U32VecVec = std::vector>; namespace { @@ -182,23 +185,26 @@ bool Type::operator==(const Type& other) const { } } -void Type::GetHashWords(std::vector* words, - std::unordered_set* seen) const { - if (!seen->insert(this).second) { - return; +size_t Type::ComputeHashValue(size_t hash, SeenTypes* seen) const { + // Linear search through a dense, cache coherent vector is faster than O(log + // n) search in a complex data structure (eg std::set) for the generally small + // number of nodes. It also skips the overhead of an new/delete per Type + // (when inserting/removing from a set). + if (std::find(seen->begin(), seen->end(), this) != seen->end()) { + return hash; } - words->push_back(kind_); + seen->push_back(this); + + hash = hash_combine(hash, uint32_t(kind_)); for (const auto& d : decorations_) { - for (auto w : d) { - words->push_back(w); - } + hash = hash_combine(hash, d); } switch (kind_) { -#define DeclareKindCase(type) \ - case k##type: \ - As##type()->GetExtraHashWords(words, seen); \ +#define DeclareKindCase(type) \ + case k##type: \ + hash = As##type()->ComputeExtraStateHash(hash, seen); \ break DeclareKindCase(Void); DeclareKindCase(Bool); @@ -232,18 +238,42 @@ void Type::GetHashWords(std::vector* words, break; } - seen->erase(this); + seen->pop_back(); + return hash; } size_t Type::HashValue() const { - std::u32string h; - std::vector words; - GetHashWords(&words); - for (auto w : words) { - h.push_back(w); - } + SeenTypes seen; + return ComputeHashValue(0, &seen); +} - return std::hash()(h); +uint64_t Type::NumberOfComponents() const { + switch (kind()) { + case kVector: + return AsVector()->element_count(); + case kMatrix: + return AsMatrix()->element_count(); + case kArray: { + Array::LengthInfo length_info = AsArray()->length_info(); + if (length_info.words[0] != Array::LengthInfo::kConstant) { + return UINT64_MAX; + } + assert(length_info.words.size() <= 3 && + "The size of the array could not fit size_t."); + uint64_t length = 0; + length |= length_info.words[1]; + if (length_info.words.size() > 2) { + length |= static_cast(length_info.words[2]) << 32; + } + return length; + } + case kRuntimeArray: + return UINT64_MAX; + case kStruct: + return AsStruct()->element_types().size(); + default: + return 0; + } } bool Integer::IsSameImpl(const Type* that, IsSameCache*) const { @@ -258,10 +288,8 @@ std::string Integer::str() const { return oss.str(); } -void Integer::GetExtraHashWords(std::vector* words, - std::unordered_set*) const { - words->push_back(width_); - words->push_back(signed_); +size_t Integer::ComputeExtraStateHash(size_t hash, SeenTypes*) const { + return hash_combine(hash, width_, signed_); } bool Float::IsSameImpl(const Type* that, IsSameCache*) const { @@ -275,9 +303,8 @@ std::string Float::str() const { return oss.str(); } -void Float::GetExtraHashWords(std::vector* words, - std::unordered_set*) const { - words->push_back(width_); +size_t Float::ComputeExtraStateHash(size_t hash, SeenTypes*) const { + return hash_combine(hash, width_); } Vector::Vector(const Type* type, uint32_t count) @@ -299,10 +326,11 @@ std::string Vector::str() const { return oss.str(); } -void Vector::GetExtraHashWords(std::vector* words, - std::unordered_set* seen) const { - element_type_->GetHashWords(words, seen); - words->push_back(count_); +size_t Vector::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + // prefer form that doesn't require push/pop from stack: add state and + // make tail call. + hash = hash_combine(hash, count_); + return element_type_->ComputeHashValue(hash, seen); } Matrix::Matrix(const Type* type, uint32_t count) @@ -324,10 +352,9 @@ std::string Matrix::str() const { return oss.str(); } -void Matrix::GetExtraHashWords(std::vector* words, - std::unordered_set* seen) const { - element_type_->GetHashWords(words, seen); - words->push_back(count_); +size_t Matrix::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + hash = hash_combine(hash, count_); + return element_type_->ComputeHashValue(hash, seen); } Image::Image(Type* type, SpvDim dimen, uint32_t d, bool array, bool multisample, @@ -362,16 +389,10 @@ std::string Image::str() const { return oss.str(); } -void Image::GetExtraHashWords(std::vector* words, - std::unordered_set* seen) const { - sampled_type_->GetHashWords(words, seen); - words->push_back(dim_); - words->push_back(depth_); - words->push_back(arrayed_); - words->push_back(ms_); - words->push_back(sampled_); - words->push_back(format_); - words->push_back(access_qualifier_); +size_t Image::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + hash = hash_combine(hash, uint32_t(dim_), depth_, arrayed_, ms_, sampled_, + uint32_t(format_), uint32_t(access_qualifier_)); + return sampled_type_->ComputeHashValue(hash, seen); } bool SampledImage::IsSameImpl(const Type* that, IsSameCache* seen) const { @@ -387,9 +408,8 @@ std::string SampledImage::str() const { return oss.str(); } -void SampledImage::GetExtraHashWords( - std::vector* words, std::unordered_set* seen) const { - image_type_->GetHashWords(words, seen); +size_t SampledImage::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + return image_type_->ComputeHashValue(hash, seen); } Array::Array(const Type* type, const Array::LengthInfo& length_info_arg) @@ -422,16 +442,19 @@ std::string Array::str() const { return oss.str(); } -void Array::GetExtraHashWords(std::vector* words, - std::unordered_set* seen) const { - element_type_->GetHashWords(words, seen); - // This should mirror the logic in IsSameImpl - words->insert(words->end(), length_info_.words.begin(), - length_info_.words.end()); +size_t Array::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + hash = hash_combine(hash, length_info_.words); + return element_type_->ComputeHashValue(hash, seen); } void Array::ReplaceElementType(const Type* type) { element_type_ = type; } +Array::LengthInfo Array::GetConstantLengthInfo(uint32_t const_id, + uint32_t length) const { + std::vector extra_words{LengthInfo::Case::kConstant, length}; + return {const_id, extra_words}; +} + RuntimeArray::RuntimeArray(const Type* type) : Type(kRuntimeArray), element_type_(type) { assert(!type->AsVoid()); @@ -450,9 +473,8 @@ std::string RuntimeArray::str() const { return oss.str(); } -void RuntimeArray::GetExtraHashWords( - std::vector* words, std::unordered_set* seen) const { - element_type_->GetHashWords(words, seen); +size_t RuntimeArray::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + return element_type_->ComputeHashValue(hash, seen); } void RuntimeArray::ReplaceElementType(const Type* type) { @@ -509,19 +531,14 @@ std::string Struct::str() const { return oss.str(); } -void Struct::GetExtraHashWords(std::vector* words, - std::unordered_set* seen) const { +size_t Struct::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { for (auto* t : element_types_) { - t->GetHashWords(words, seen); + hash = t->ComputeHashValue(hash, seen); } for (const auto& pair : element_decorations_) { - words->push_back(pair.first); - for (const auto& d : pair.second) { - for (auto w : d) { - words->push_back(w); - } - } + hash = hash_combine(hash, pair.first, pair.second); } + return hash; } bool Opaque::IsSameImpl(const Type* that, IsSameCache*) const { @@ -536,11 +553,8 @@ std::string Opaque::str() const { return oss.str(); } -void Opaque::GetExtraHashWords(std::vector* words, - std::unordered_set*) const { - for (auto c : name_) { - words->push_back(static_cast(c)); - } +size_t Opaque::ComputeExtraStateHash(size_t hash, SeenTypes*) const { + return hash_combine(hash, name_); } Pointer::Pointer(const Type* type, SpvStorageClass sc) @@ -569,10 +583,9 @@ std::string Pointer::str() const { return os.str(); } -void Pointer::GetExtraHashWords(std::vector* words, - std::unordered_set* seen) const { - pointee_type_->GetHashWords(words, seen); - words->push_back(storage_class_); +size_t Pointer::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + hash = hash_combine(hash, uint32_t(storage_class_)); + return pointee_type_->ComputeHashValue(hash, seen); } void Pointer::SetPointeeType(const Type* type) { pointee_type_ = type; } @@ -606,12 +619,11 @@ std::string Function::str() const { return oss.str(); } -void Function::GetExtraHashWords(std::vector* words, - std::unordered_set* seen) const { - return_type_->GetHashWords(words, seen); +size_t Function::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { for (const auto* t : param_types_) { - t->GetHashWords(words, seen); + hash = t->ComputeHashValue(hash, seen); } + return return_type_->ComputeHashValue(hash, seen); } void Function::SetReturnType(const Type* type) { return_type_ = type; } @@ -628,9 +640,8 @@ std::string Pipe::str() const { return oss.str(); } -void Pipe::GetExtraHashWords(std::vector* words, - std::unordered_set*) const { - words->push_back(access_qualifier_); +size_t Pipe::ComputeExtraStateHash(size_t hash, SeenTypes*) const { + return hash_combine(hash, uint32_t(access_qualifier_)); } bool ForwardPointer::IsSameImpl(const Type* that, IsSameCache*) const { @@ -653,11 +664,11 @@ std::string ForwardPointer::str() const { return oss.str(); } -void ForwardPointer::GetExtraHashWords( - std::vector* words, std::unordered_set* seen) const { - words->push_back(target_id_); - words->push_back(storage_class_); - if (pointer_) pointer_->GetHashWords(words, seen); +size_t ForwardPointer::ComputeExtraStateHash(size_t hash, + SeenTypes* seen) const { + hash = hash_combine(hash, target_id_, uint32_t(storage_class_)); + if (pointer_) hash = pointer_->ComputeHashValue(hash, seen); + return hash; } CooperativeMatrixNV::CooperativeMatrixNV(const Type* type, const uint32_t scope, @@ -681,12 +692,10 @@ std::string CooperativeMatrixNV::str() const { return oss.str(); } -void CooperativeMatrixNV::GetExtraHashWords( - std::vector* words, std::unordered_set* pSet) const { - component_type_->GetHashWords(words, pSet); - words->push_back(scope_id_); - words->push_back(rows_id_); - words->push_back(columns_id_); +size_t CooperativeMatrixNV::ComputeExtraStateHash(size_t hash, + SeenTypes* seen) const { + hash = hash_combine(hash, scope_id_, rows_id_, columns_id_); + return component_type_->ComputeHashValue(hash, seen); } bool CooperativeMatrixNV::IsSameImpl(const Type* that, diff --git a/source/opt/types.h b/source/opt/types.h index 9ecd41a6..a92669e9 100644 --- a/source/opt/types.h +++ b/source/opt/types.h @@ -28,6 +28,7 @@ #include "source/latest_version_spirv_header.h" #include "source/opt/instruction.h" +#include "source/util/small_vector.h" #include "spirv-tools/libspirv.h" namespace spvtools { @@ -67,6 +68,8 @@ class Type { public: typedef std::set> IsSameCache; + using SeenTypes = spvtools::utils::SmallVector; + // Available subtypes. // // When adding a new derived class of Type, please add an entry to the enum. @@ -96,7 +99,8 @@ class Type { kNamedBarrier, kAccelerationStructureNV, kCooperativeMatrixNV, - kRayQueryKHR + kRayQueryKHR, + kLast }; Type(Kind k) : kind_(k) {} @@ -154,21 +158,11 @@ class Type { // Returns the hash value of this type. size_t HashValue() const; - // Adds the necessary words to compute a hash value of this type to |words|. - void GetHashWords(std::vector* words) const { - std::unordered_set seen; - GetHashWords(words, &seen); - } + size_t ComputeHashValue(size_t hash, SeenTypes* seen) const; - // Adds the necessary words to compute a hash value of this type to |words|. - void GetHashWords(std::vector* words, - std::unordered_set* seen) const; - - // Adds necessary extra words for a subtype to calculate a hash value into - // |words|. - virtual void GetExtraHashWords( - std::vector* words, - std::unordered_set* pSet) const = 0; + // Returns the number of components in a composite type. Returns 0 for a + // non-composite type. + uint64_t NumberOfComponents() const; // A bunch of methods for casting this type to a given type. Returns this if the // cast can be done, nullptr otherwise. @@ -204,6 +198,10 @@ class Type { DeclareCastMethod(RayQueryKHR) #undef DeclareCastMethod +protected: + // Add any type-specific state to |hash| and returns new hash. + virtual size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const = 0; + protected: // Decorations attached to this type. Each decoration is encoded as a vector // of uint32_t numbers. The first uint32_t number is the decoration value, @@ -232,8 +230,7 @@ class Integer : public Type { uint32_t width() const { return width_; } bool IsSigned() const { return signed_; } - void GetExtraHashWords(std::vector* words, - std::unordered_set* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; @@ -253,8 +250,7 @@ class Float : public Type { const Float* AsFloat() const override { return this; } uint32_t width() const { return width_; } - void GetExtraHashWords(std::vector* words, - std::unordered_set* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; @@ -274,8 +270,7 @@ class Vector : public Type { Vector* AsVector() override { return this; } const Vector* AsVector() const override { return this; } - void GetExtraHashWords(std::vector* words, - std::unordered_set* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; @@ -296,8 +291,7 @@ class Matrix : public Type { Matrix* AsMatrix() override { return this; } const Matrix* AsMatrix() const override { return this; } - void GetExtraHashWords(std::vector* words, - std::unordered_set* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; @@ -327,8 +321,7 @@ class Image : public Type { SpvImageFormat format() const { return format_; } SpvAccessQualifier access_qualifier() const { return access_qualifier_; } - void GetExtraHashWords(std::vector* words, - std::unordered_set* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; @@ -355,8 +348,7 @@ class SampledImage : public Type { const Type* image_type() const { return image_type_; } - void GetExtraHashWords(std::vector* words, - std::unordered_set* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; @@ -399,10 +391,10 @@ class Array : public Type { Array* AsArray() override { return this; } const Array* AsArray() const override { return this; } - void GetExtraHashWords(std::vector* words, - std::unordered_set* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; void ReplaceElementType(const Type* element_type); + LengthInfo GetConstantLengthInfo(uint32_t const_id, uint32_t length) const; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; @@ -422,8 +414,7 @@ class RuntimeArray : public Type { RuntimeArray* AsRuntimeArray() override { return this; } const RuntimeArray* AsRuntimeArray() const override { return this; } - void GetExtraHashWords(std::vector* words, - std::unordered_set* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; void ReplaceElementType(const Type* element_type); @@ -459,8 +450,7 @@ class Struct : public Type { Struct* AsStruct() override { return this; } const Struct* AsStruct() const override { return this; } - void GetExtraHashWords(std::vector* words, - std::unordered_set* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; @@ -491,8 +481,7 @@ class Opaque : public Type { const std::string& name() const { return name_; } - void GetExtraHashWords(std::vector* words, - std::unordered_set* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; @@ -512,8 +501,7 @@ class Pointer : public Type { Pointer* AsPointer() override { return this; } const Pointer* AsPointer() const override { return this; } - void GetExtraHashWords(std::vector* words, - std::unordered_set* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; void SetPointeeType(const Type* type); @@ -539,8 +527,7 @@ class Function : public Type { const std::vector& param_types() const { return param_types_; } std::vector& param_types() { return param_types_; } - void GetExtraHashWords(std::vector* words, - std::unordered_set*) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; void SetReturnType(const Type* type); @@ -564,8 +551,7 @@ class Pipe : public Type { SpvAccessQualifier access_qualifier() const { return access_qualifier_; } - void GetExtraHashWords(std::vector* words, - std::unordered_set* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; @@ -592,8 +578,7 @@ class ForwardPointer : public Type { ForwardPointer* AsForwardPointer() override { return this; } const ForwardPointer* AsForwardPointer() const override { return this; } - void GetExtraHashWords(std::vector* words, - std::unordered_set* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; @@ -616,8 +601,7 @@ class CooperativeMatrixNV : public Type { return this; } - void GetExtraHashWords(std::vector*, - std::unordered_set*) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; const Type* component_type() const { return component_type_; } uint32_t scope_id() const { return scope_id_; } @@ -633,24 +617,25 @@ class CooperativeMatrixNV : public Type { const uint32_t columns_id_; }; -#define DefineParameterlessType(type, name) \ - class type : public Type { \ - public: \ - type() : Type(k##type) {} \ - type(const type&) = default; \ - \ - std::string str() const override { return #name; } \ - \ - type* As##type() override { return this; } \ - const type* As##type() const override { return this; } \ - \ - void GetExtraHashWords(std::vector*, \ - std::unordered_set*) const override {} \ - \ - private: \ - bool IsSameImpl(const Type* that, IsSameCache*) const override { \ - return that->As##type() && HasSameDecorations(that); \ - } \ +#define DefineParameterlessType(type, name) \ + class type : public Type { \ + public: \ + type() : Type(k##type) {} \ + type(const type&) = default; \ + \ + std::string str() const override { return #name; } \ + \ + type* As##type() override { return this; } \ + const type* As##type() const override { return this; } \ + \ + size_t ComputeExtraStateHash(size_t hash, SeenTypes*) const override { \ + return hash; \ + } \ + \ + private: \ + bool IsSameImpl(const Type* that, IsSameCache*) const override { \ + return that->As##type() && HasSameDecorations(that); \ + } \ } DefineParameterlessType(Void, void); DefineParameterlessType(Bool, bool); diff --git a/source/opt/unify_const_pass.cpp b/source/opt/unify_const_pass.cpp index 227fd61d..6bfa11a5 100644 --- a/source/opt/unify_const_pass.cpp +++ b/source/opt/unify_const_pass.cpp @@ -151,7 +151,7 @@ Pass::Status UnifyConstantPass::Process() { // 'SpecId' decoration and all of them should be treated as unique. // 'SpecId' is not applicable to SpecConstants defined with // OpSpecConstant{Op|Composite}, their values are not necessary to be - // unique. When all the operands/compoents are the same between two + // unique. When all the operands/components are the same between two // OpSpecConstant{Op|Composite} results, their result values must be the // same so are unifiable. case SpvOp::SpvOpSpecConstantOp: diff --git a/source/opt/upgrade_memory_model.cpp b/source/opt/upgrade_memory_model.cpp index ab252059..9d6a5bce 100644 --- a/source/opt/upgrade_memory_model.cpp +++ b/source/opt/upgrade_memory_model.cpp @@ -20,6 +20,7 @@ #include "source/opt/ir_context.h" #include "source/spirv_constant.h" #include "source/util/make_unique.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -58,9 +59,7 @@ void UpgradeMemoryModel::UpgradeMemoryModelInstruction() { std::initializer_list{ {SPV_OPERAND_TYPE_CAPABILITY, {SpvCapabilityVulkanMemoryModelKHR}}})); const std::string extension = "SPV_KHR_vulkan_memory_model"; - std::vector words(extension.size() / 4 + 1, 0); - char* dst = reinterpret_cast(words.data()); - strncpy(dst, extension.c_str(), extension.size()); + std::vector words = spvtools::utils::MakeVector(extension); context()->AddExtension( MakeUnique(context(), SpvOpExtension, 0, 0, std::initializer_list{ @@ -85,8 +84,7 @@ void UpgradeMemoryModel::UpgradeInstructions() { if (ext_inst == GLSLstd450Modf || ext_inst == GLSLstd450Frexp) { auto import = get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0u)); - if (reinterpret_cast(import->GetInOperand(0u).words.data()) == - std::string("GLSL.std.450")) { + if (import->GetInOperand(0u).AsString() == "GLSL.std.450") { UpgradeExtInst(inst); } } diff --git a/source/opt/value_number_table.cpp b/source/opt/value_number_table.cpp index 32d6de94..5271e3fe 100644 --- a/source/opt/value_number_table.cpp +++ b/source/opt/value_number_table.cpp @@ -50,7 +50,7 @@ uint32_t ValueNumberTable::AssignValueNumber(Instruction* inst) { // OpSampledImage and OpImage must remain in the same basic block in which // they are used, because of this we will assign each one it own value number. if (!context()->IsCombinatorInstruction(inst) && - !inst->IsOpenCL100DebugInstr()) { + !inst->IsCommonDebugInstr()) { value = TakeNextValueNumber(); id_to_value_[inst->result_id()] = value; return value; diff --git a/source/opt/vector_dce.cpp b/source/opt/vector_dce.cpp index 14a55c1e..28d94a07 100644 --- a/source/opt/vector_dce.cpp +++ b/source/opt/vector_dce.cpp @@ -52,7 +52,7 @@ void VectorDCE::FindLiveComponents(Function* function, // components are live because of arbitrary nesting of structs. function->ForEachInst( [&work_list, this, live_components](Instruction* current_inst) { - if (current_inst->IsOpenCL100DebugInstr()) { + if (current_inst->IsCommonDebugInstr()) { return; } if (!HasVectorOrScalarResult(current_inst) || @@ -110,7 +110,11 @@ void VectorDCE::MarkExtractUseAsLive(const Instruction* current_inst, if (current_inst->NumInOperands() < 2) { new_item.components = live_elements; } else { - new_item.components.Set(current_inst->GetSingleWordInOperand(1)); + uint32_t element_index = current_inst->GetSingleWordInOperand(1); + uint32_t item_size = GetVectorComponentCount(operand_inst->type_id()); + if (element_index < item_size) { + new_item.components.Set(element_index); + } } AddItemToWorkListIfNeeded(new_item, live_components, work_list); } @@ -176,10 +180,10 @@ void VectorDCE::MarkVectorShuffleUsesAsLive( second_operand.instruction = def_use_mgr->GetDef(current_item.instruction->GetSingleWordInOperand(1)); - analysis::TypeManager* type_mgr = context()->get_type_mgr(); - analysis::Vector* first_type = - type_mgr->GetType(first_operand.instruction->type_id())->AsVector(); - uint32_t size_of_first_operand = first_type->element_count(); + uint32_t size_of_first_operand = + GetVectorComponentCount(first_operand.instruction->type_id()); + uint32_t size_of_second_operand = + GetVectorComponentCount(second_operand.instruction->type_id()); for (uint32_t in_op = 2; in_op < current_item.instruction->NumInOperands(); ++in_op) { @@ -187,7 +191,7 @@ void VectorDCE::MarkVectorShuffleUsesAsLive( if (current_item.components.Get(in_op - 2)) { if (index < size_of_first_operand) { first_operand.components.Set(index); - } else { + } else if (index - size_of_first_operand < size_of_second_operand) { second_operand.components.Set(index - size_of_first_operand); } } @@ -202,7 +206,6 @@ void VectorDCE::MarkCompositeContructUsesAsLive( VectorDCE::LiveComponentMap* live_components, std::vector* work_list) { analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); - analysis::TypeManager* type_mgr = context()->get_type_mgr(); uint32_t current_component = 0; Instruction* current_inst = work_item.instruction; @@ -223,8 +226,7 @@ void VectorDCE::MarkCompositeContructUsesAsLive( assert(HasVectorResult(op_inst)); WorkListItem new_work_item; new_work_item.instruction = op_inst; - uint32_t op_vector_size = - type_mgr->GetType(op_inst->type_id())->AsVector()->element_count(); + uint32_t op_vector_size = GetVectorComponentCount(op_inst->type_id()); for (uint32_t op_vector_idx = 0; op_vector_idx < op_vector_size; op_vector_idx++, current_component++) { @@ -297,6 +299,18 @@ bool VectorDCE::HasScalarResult(const Instruction* inst) const { } } +uint32_t VectorDCE::GetVectorComponentCount(uint32_t type_id) { + assert(type_id != 0 && + "Trying to get the vector element count, but the type id is 0"); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + const analysis::Type* type = type_mgr->GetType(type_id); + const analysis::Vector* vector_type = type->AsVector(); + assert( + vector_type && + "Trying to get the vector element count, but the type is not a vector"); + return vector_type->element_count(); +} + bool VectorDCE::RewriteInstructions( Function* function, const VectorDCE::LiveComponentMap& live_components) { bool modified = false; @@ -394,7 +408,7 @@ void VectorDCE::MarkDebugValueUsesAsDead( Instruction* composite, std::vector* dead_dbg_value) { context()->get_def_use_mgr()->ForEachUser( composite, [&dead_dbg_value](Instruction* use) { - if (use->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) + if (use->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) dead_dbg_value->push_back(use); }); } diff --git a/source/opt/vector_dce.h b/source/opt/vector_dce.h index 0df9aee1..a55bda69 100644 --- a/source/opt/vector_dce.h +++ b/source/opt/vector_dce.h @@ -73,7 +73,7 @@ class VectorDCE : public MemPass { bool RewriteInstructions(Function* function, const LiveComponentMap& live_components); - // Makrs all DebugValue instructions that use |composite| for their values as + // Makes 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* dead_dbg_value); @@ -94,12 +94,15 @@ class VectorDCE : public MemPass { // Returns true if the result of |inst| is a vector or a scalar. bool HasVectorOrScalarResult(const Instruction* inst) const; - // Returns true if the result of |inst| is a scalar. + // Returns true if the result of |inst| is a vector. bool HasVectorResult(const Instruction* inst) const; - // Returns true if the result of |inst| is a vector. + // Returns true if the result of |inst| is a scalar. bool HasScalarResult(const Instruction* inst) const; + // Returns the number of elements in the vector type with id |type_id|. + uint32_t GetVectorComponentCount(uint32_t type_id); + // Adds |work_item| to |work_list| if it is not already live according to // |live_components|. |live_components| is updated to indicate that // |work_item| is now live. diff --git a/source/parsed_operand.cpp b/source/parsed_operand.cpp index 7ad369cd..5f8e94db 100644 --- a/source/parsed_operand.cpp +++ b/source/parsed_operand.cpp @@ -24,7 +24,9 @@ namespace spvtools { void EmitNumericLiteral(std::ostream* out, const spv_parsed_instruction_t& inst, const spv_parsed_operand_t& operand) { if (operand.type != SPV_OPERAND_TYPE_LITERAL_INTEGER && - operand.type != SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER) + operand.type != SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER && + operand.type != SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER && + operand.type != SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER) return; if (operand.num_words < 1) return; // TODO(dneto): Support more than 64-bits at a time. diff --git a/source/print.cpp b/source/print.cpp index 128587ae..6c94e2b7 100644 --- a/source/print.cpp +++ b/source/print.cpp @@ -15,8 +15,9 @@ #include "source/print.h" #if defined(SPIRV_ANDROID) || defined(SPIRV_LINUX) || defined(SPIRV_MAC) || \ - defined(SPIRV_IOS) || defined(SPIRV_FREEBSD) || \ - defined(SPIRV_EMSCRIPTEN) || defined(SPIRV_FUCHSIA) + defined(SPIRV_IOS) || defined(SPIRV_TVOS) || defined(SPIRV_FREEBSD) || \ + defined(SPIRV_OPENBSD) || defined(SPIRV_EMSCRIPTEN) || \ + defined(SPIRV_FUCHSIA) || defined(SPIRV_GNU) namespace spvtools { clr::reset::operator const char*() { return "\x1b[0m"; } diff --git a/source/reduce/BUILD.gn b/source/reduce/BUILD.gn index 2b18c937..d2c79812 100644 --- a/source/reduce/BUILD.gn +++ b/source/reduce/BUILD.gn @@ -30,8 +30,6 @@ ohos_source_set("deqp_spirvtool_reduce_source") { sources = [ "//third_party/spirv-tools/source/reduce/change_operand_reduction_opportunity.cpp", "//third_party/spirv-tools/source/reduce/change_operand_to_undef_reduction_opportunity.cpp", - "//third_party/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp", - "//third_party/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp", "//third_party/spirv-tools/source/reduce/merge_blocks_reduction_opportunity.cpp", "//third_party/spirv-tools/source/reduce/merge_blocks_reduction_opportunity_finder.cpp", "//third_party/spirv-tools/source/reduce/operand_to_const_reduction_opportunity_finder.cpp", @@ -54,9 +52,10 @@ ohos_source_set("deqp_spirvtool_reduce_source") { "//third_party/spirv-tools/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp", "//third_party/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.cpp", "//third_party/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp", - - #"//third_party/spirv-tools/source/reduce/structured_construct_to_block_reduction_opportunity.cpp", - #"//third_party/spirv-tools/source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp", + "//third_party/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp", + "//third_party/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp", + "//third_party/spirv-tools/source/reduce/structured_construct_to_block_reduction_opportunity.cpp", + "//third_party/spirv-tools/source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp", "//third_party/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp", "//third_party/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp", ] diff --git a/source/reduce/CMakeLists.txt b/source/reduce/CMakeLists.txt index a3291c77..6fd8409f 100644 --- a/source/reduce/CMakeLists.txt +++ b/source/reduce/CMakeLists.txt @@ -14,6 +14,8 @@ set(SPIRV_TOOLS_REDUCE_SOURCES change_operand_reduction_opportunity.h change_operand_to_undef_reduction_opportunity.h + conditional_branch_to_simple_conditional_branch_opportunity_finder.h + conditional_branch_to_simple_conditional_branch_reduction_opportunity.h merge_blocks_reduction_opportunity.h merge_blocks_reduction_opportunity_finder.h operand_to_const_reduction_opportunity_finder.h @@ -34,15 +36,17 @@ set(SPIRV_TOOLS_REDUCE_SOURCES remove_struct_member_reduction_opportunity.h remove_unused_instruction_reduction_opportunity_finder.h remove_unused_struct_member_reduction_opportunity_finder.h - structured_loop_to_selection_reduction_opportunity.h - structured_loop_to_selection_reduction_opportunity_finder.h - conditional_branch_to_simple_conditional_branch_opportunity_finder.h - conditional_branch_to_simple_conditional_branch_reduction_opportunity.h simple_conditional_branch_to_branch_opportunity_finder.h simple_conditional_branch_to_branch_reduction_opportunity.h + structured_construct_to_block_reduction_opportunity.h + structured_construct_to_block_reduction_opportunity_finder.h + structured_loop_to_selection_reduction_opportunity.h + structured_loop_to_selection_reduction_opportunity_finder.h change_operand_reduction_opportunity.cpp change_operand_to_undef_reduction_opportunity.cpp + conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp + conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp merge_blocks_reduction_opportunity.cpp merge_blocks_reduction_opportunity_finder.cpp operand_to_const_reduction_opportunity_finder.cpp @@ -63,12 +67,12 @@ set(SPIRV_TOOLS_REDUCE_SOURCES remove_struct_member_reduction_opportunity.cpp remove_unused_instruction_reduction_opportunity_finder.cpp remove_unused_struct_member_reduction_opportunity_finder.cpp - structured_loop_to_selection_reduction_opportunity.cpp - structured_loop_to_selection_reduction_opportunity_finder.cpp - conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp - conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp simple_conditional_branch_to_branch_opportunity_finder.cpp simple_conditional_branch_to_branch_reduction_opportunity.cpp + structured_construct_to_block_reduction_opportunity.cpp + structured_construct_to_block_reduction_opportunity_finder.cpp + structured_loop_to_selection_reduction_opportunity.cpp + structured_loop_to_selection_reduction_opportunity_finder.cpp ) if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) diff --git a/source/reduce/change_operand_reduction_opportunity.cpp b/source/reduce/change_operand_reduction_opportunity.cpp index c3f6fd7b..18e340bf 100644 --- a/source/reduce/change_operand_reduction_opportunity.cpp +++ b/source/reduce/change_operand_reduction_opportunity.cpp @@ -14,6 +14,8 @@ #include "source/reduce/change_operand_reduction_opportunity.h" +#include "source/opt/ir_context.h" + namespace spvtools { namespace reduce { @@ -26,6 +28,7 @@ bool ChangeOperandReductionOpportunity::PreconditionHolds() { void ChangeOperandReductionOpportunity::Apply() { inst_->SetOperand(operand_index_, {new_id_}); + inst_->context()->get_def_use_mgr()->UpdateDefUse(inst_); } } // namespace reduce diff --git a/source/reduce/change_operand_to_undef_reduction_opportunity.cpp b/source/reduce/change_operand_to_undef_reduction_opportunity.cpp index 8e33da66..7cc06a03 100644 --- a/source/reduce/change_operand_to_undef_reduction_opportunity.cpp +++ b/source/reduce/change_operand_to_undef_reduction_opportunity.cpp @@ -35,6 +35,7 @@ void ChangeOperandToUndefReductionOpportunity::Apply() { assert(operand_type_id); auto undef_id = FindOrCreateGlobalUndef(context_, operand_type_id); inst_->SetOperand(operand_index_, {undef_id}); + context_->InvalidateAnalyses(opt::IRContext::kAnalysisDefUse); } } // namespace reduce diff --git a/source/reduce/reducer.cpp b/source/reduce/reducer.cpp index 16bb94fe..b752f415 100644 --- a/source/reduce/reducer.cpp +++ b/source/reduce/reducer.cpp @@ -28,6 +28,7 @@ #include "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h" #include "source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h" #include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h" +#include "source/reduce/structured_construct_to_block_reduction_opportunity_finder.h" #include "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h" #include "source/spirv_reducer_options.h" @@ -112,6 +113,8 @@ void Reducer::AddDefaultReductionPasses() { spvtools::MakeUnique()); AddReductionPass( spvtools::MakeUnique()); + AddReductionPass(spvtools::MakeUnique< + StructuredConstructToBlockReductionOpportunityFinder>()); AddReductionPass(spvtools::MakeUnique< StructuredLoopToSelectionReductionOpportunityFinder>()); AddReductionPass( @@ -141,7 +144,7 @@ void Reducer::AddReductionPass( std::unique_ptr finder) { passes_.push_back( spvtools::MakeUnique(target_env_, std::move(finder))); -} +} void Reducer::AddCleanupReductionPass( std::unique_ptr finder) { diff --git a/source/reduce/remove_block_reduction_opportunity.cpp b/source/reduce/remove_block_reduction_opportunity.cpp index aa481059..55e95769 100644 --- a/source/reduce/remove_block_reduction_opportunity.cpp +++ b/source/reduce/remove_block_reduction_opportunity.cpp @@ -20,12 +20,11 @@ namespace spvtools { namespace reduce { RemoveBlockReductionOpportunity::RemoveBlockReductionOpportunity( - opt::Function* function, opt::BasicBlock* block) - : function_(function), block_(block) { + opt::IRContext* context, opt::Function* function, opt::BasicBlock* block) + : context_(context), function_(function), block_(block) { // precondition: assert(block_->begin() != block_->end() && - block_->begin()->context()->get_def_use_mgr()->NumUsers( - block_->id()) == 0 && + context_->get_def_use_mgr()->NumUsers(block_->id()) == 0 && "RemoveBlockReductionOpportunity block must have 0 references"); } @@ -38,10 +37,8 @@ void RemoveBlockReductionOpportunity::Apply() { // We need an iterator pointing to the block, hence the loop. for (auto bi = function_->begin(); bi != function_->end(); ++bi) { if (bi->id() == block_->id()) { - bi->KillAllInsts(true); bi.Erase(); - // Block removal changes the function, but we don't use analyses, so no - // need to invalidate them. + context_->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); return; } } diff --git a/source/reduce/remove_block_reduction_opportunity.h b/source/reduce/remove_block_reduction_opportunity.h index 4b358abc..03fede52 100644 --- a/source/reduce/remove_block_reduction_opportunity.h +++ b/source/reduce/remove_block_reduction_opportunity.h @@ -27,7 +27,8 @@ namespace reduce { class RemoveBlockReductionOpportunity : public ReductionOpportunity { public: // Creates the opportunity to remove |block| in |function| in |context|. - RemoveBlockReductionOpportunity(opt::Function* function, + RemoveBlockReductionOpportunity(opt::IRContext* context, + opt::Function* function, opt::BasicBlock* block); bool PreconditionHolds() override; @@ -36,6 +37,7 @@ class RemoveBlockReductionOpportunity : public ReductionOpportunity { void Apply() override; private: + opt::IRContext* context_; opt::Function* function_; opt::BasicBlock* block_; }; diff --git a/source/reduce/remove_block_reduction_opportunity_finder.cpp b/source/reduce/remove_block_reduction_opportunity_finder.cpp index 27a4570c..3b13728a 100644 --- a/source/reduce/remove_block_reduction_opportunity_finder.cpp +++ b/source/reduce/remove_block_reduction_opportunity_finder.cpp @@ -32,8 +32,8 @@ RemoveBlockReductionOpportunityFinder::GetAvailableOpportunities( for (auto* function : GetTargetFunctions(context, target_function)) { for (auto bi = function->begin(); bi != function->end(); ++bi) { if (IsBlockValidOpportunity(context, function, &bi)) { - result.push_back( - MakeUnique(function, &*bi)); + result.push_back(MakeUnique( + context, function, &*bi)); } } } diff --git a/source/reduce/remove_function_reduction_opportunity.cpp b/source/reduce/remove_function_reduction_opportunity.cpp index ecad6707..4b85058b 100644 --- a/source/reduce/remove_function_reduction_opportunity.cpp +++ b/source/reduce/remove_function_reduction_opportunity.cpp @@ -29,8 +29,8 @@ void RemoveFunctionReductionOpportunity::Apply() { for (opt::Module::iterator function_it = context_->module()->begin(); function_it != context_->module()->end(); ++function_it) { if (&*function_it == function_) { - opt::eliminatedeadfunctionsutil::EliminateFunction(context_, - &function_it); + function_it.Erase(); + context_->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); return; } } diff --git a/source/reduce/remove_struct_member_reduction_opportunity.cpp b/source/reduce/remove_struct_member_reduction_opportunity.cpp index 787c629b..e72ed351 100644 --- a/source/reduce/remove_struct_member_reduction_opportunity.cpp +++ b/source/reduce/remove_struct_member_reduction_opportunity.cpp @@ -129,6 +129,8 @@ void RemoveStructMemberReductionOpportunity::Apply() { // Remove the member from the struct type. struct_type_->RemoveInOperand(member_index_); + + context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); } void RemoveStructMemberReductionOpportunity::AdjustAccessedIndices( @@ -151,7 +153,7 @@ void RemoveStructMemberReductionOpportunity::AdjustAccessedIndices( next_type = type_inst->GetSingleWordInOperand(0); break; case SpvOpTypeStruct: { - // Struct types are special becuase (a) we may need to adjust the index + // Struct types are special because (a) we may need to adjust the index // being used, if the struct type is the one from which we are removing // a member, and (b) the type encountered by following the current index // is dependent on the value of the index. diff --git a/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp b/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp index e72be625..cd0c4e4d 100644 --- a/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp +++ b/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp @@ -136,9 +136,9 @@ RemoveUnusedStructMemberReductionOpportunityFinder::GetAvailableOpportunities( } } - // We now know those struct indices that are unsed, and we make a reduction + // We now know those struct indices that are unused, and we make a reduction // opportunity for each of them. By mapping each relevant member index to the - // structs in which it is unsed, we will group all opportunities to remove + // structs in which it is unused, we will group all opportunities to remove // member k of a struct (for some k) together. This reduces the likelihood // that opportunities to remove members from the same struct will be adjacent, // which is good because such opportunities mutually disable one another. diff --git a/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp b/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp index ca17f9eb..a2be0c4d 100644 --- a/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp +++ b/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp @@ -51,6 +51,8 @@ void SimpleConditionalBranchToBranchReductionOpportunity::Apply() { {{SPV_OPERAND_TYPE_ID, {conditional_branch_instruction_->GetSingleWordInOperand( kTrueBranchOperandIndex)}}}); + conditional_branch_instruction_->context()->InvalidateAnalysesExceptFor( + opt::IRContext::kAnalysisNone); } } // namespace reduce diff --git a/source/reduce/structured_construct_to_block_reduction_opportunity.cpp b/source/reduce/structured_construct_to_block_reduction_opportunity.cpp new file mode 100644 index 00000000..ed738411 --- /dev/null +++ b/source/reduce/structured_construct_to_block_reduction_opportunity.cpp @@ -0,0 +1,67 @@ +// Copyright (c) 2021 Alastair F. Donaldson +// +// 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/reduce/structured_construct_to_block_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +bool StructuredConstructToBlockReductionOpportunity::PreconditionHolds() { + return context_->get_def_use_mgr()->GetDef(construct_header_) != nullptr; +} + +void StructuredConstructToBlockReductionOpportunity::Apply() { + auto header_block = context_->cfg()->block(construct_header_); + auto merge_block = context_->cfg()->block(header_block->MergeBlockId()); + + auto* enclosing_function = header_block->GetParent(); + + // A region of blocks is defined in terms of dominators and post-dominators, + // so we compute these for the enclosing function. + auto* dominators = context_->GetDominatorAnalysis(enclosing_function); + auto* postdominators = context_->GetPostDominatorAnalysis(enclosing_function); + + // For each block in the function, determine whether it is inside the region. + // If it is, delete it. + for (auto block_it = enclosing_function->begin(); + block_it != enclosing_function->end();) { + if (header_block != &*block_it && merge_block != &*block_it && + dominators->Dominates(header_block, &*block_it) && + postdominators->Dominates(merge_block, &*block_it)) { + block_it = block_it.Erase(); + } else { + ++block_it; + } + } + // Having removed some blocks from the module it is necessary to invalidate + // analyses, since the remaining patch-up work depends on various analyses + // which will otherwise reference blocks that have been deleted. + context_->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + // We demote the header of the region to a regular block by deleting its merge + // instruction. + context_->KillInst(header_block->GetMergeInst()); + + // The terminator for the header block is changed to be an unconditional + // branch to the merge block. + header_block->terminator()->SetOpcode(SpvOpBranch); + header_block->terminator()->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {merge_block->id()}}}); + + // This is an intrusive change, so we invalidate all analyses. + context_->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +} // namespace reduce +} // namespace spvtools diff --git a/source/reduce/structured_construct_to_block_reduction_opportunity.h b/source/reduce/structured_construct_to_block_reduction_opportunity.h new file mode 100644 index 00000000..f461a2f0 --- /dev/null +++ b/source/reduce/structured_construct_to_block_reduction_opportunity.h @@ -0,0 +1,49 @@ +// Copyright (c) 2021 Alastair F. Donaldson +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/ir_context.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to replace a skeletal structured control flow construct with a +// single block. +class StructuredConstructToBlockReductionOpportunity + : public ReductionOpportunity { + public: + // Constructs an opportunity from a header block id. + StructuredConstructToBlockReductionOpportunity(opt::IRContext* context, + uint32_t construct_header) + : context_(context), construct_header_(construct_header) {} + + // Returns true if and only if |construct_header_| exists in the module - + // another opportunity may have removed it. + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + opt::IRContext* context_; + uint32_t construct_header_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_H_ diff --git a/source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp b/source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp new file mode 100644 index 00000000..29fbe551 --- /dev/null +++ b/source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp @@ -0,0 +1,185 @@ +// Copyright (c) 2021 Alastair F. Donaldson +// +// 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/reduce/structured_construct_to_block_reduction_opportunity_finder.h" + +#include + +#include "source/reduce/structured_construct_to_block_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +std::vector> +StructuredConstructToBlockReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + std::vector> result; + + // Consider every function in the module. + for (auto* function : GetTargetFunctions(context, target_function)) { + // For every header block in the function, there is potentially a region of + // blocks that could be collapsed. + std::unordered_map> + regions; + + // Regions are identified using dominators and postdominators, so we compute + // those for the function. + auto* dominators = context->GetDominatorAnalysis(function); + auto* postdominators = context->GetPostDominatorAnalysis(function); + + // Consider every block in the function. + for (auto& block : *function) { + // If a block has an unreachable predecessor then folding away a region in + // which that block is contained gets complicated, so we ignore regions + // that contain such blocks. We note whether this block suffers from this + // problem. + bool has_unreachable_predecessor = + HasUnreachablePredecessor(block, context); + + // Look through all the regions we have identified so far to see whether + // this block is part of a region, or spoils a region (by having an + // unreachable predecessor). + for (auto entry = regions.begin(); entry != regions.end();) { + // |block| is in this region if it is dominated by the header, + // post-dominated by the merge, and different from the merge. + assert(&block != entry->first && + "The block should not be the region's header because we only " + "make a region when we encounter its header."); + if (entry->first->MergeBlockId() != block.id() && + dominators->Dominates(entry->first, &block) && + postdominators->Dominates( + entry->first->GetMergeInst()->GetSingleWordInOperand(0), + block.id())) { + if (has_unreachable_predecessor) { + // The block would be in this region, but it has an unreachable + // predecessor. This spoils the region, so we remove it. + entry = regions.erase(entry); + continue; + } else { + // Add the block to the region. + entry->second.insert(&block); + } + } + ++entry; + } + if (block.MergeBlockIdIfAny() == 0) { + // The block isn't a header, so it doesn't constitute a new region. + continue; + } + if (!context->IsReachable(block)) { + // The block isn't reachable, so it doesn't constitute a new region. + continue; + } + auto* merge_block = context->cfg()->block( + block.GetMergeInst()->GetSingleWordInOperand(0)); + if (!context->IsReachable(*merge_block)) { + // The block's merge is unreachable, so it doesn't constitute a new + // region. + continue; + } + assert(dominators->Dominates(&block, merge_block) && + "The merge block is reachable, so the header must dominate it"); + if (!postdominators->Dominates(merge_block, &block)) { + // The block is not post-dominated by its merge. This happens for + // instance when there is a break from a conditional, or an early exit. + // This also means that we don't add a region. + continue; + } + // We have a reachable header block with a reachable merge that + // postdominates the header: this means we have a new region. + regions.emplace(&block, std::unordered_set()); + } + + // Now that we have found all the regions and blocks within them, we check + // whether any region defines an id that is used outside the region. If this + // is *not* the case, then we have an opportunity to collapse the region + // down to its header block and merge block. + for (auto& entry : regions) { + if (DefinitionsRestrictedToRegion(*entry.first, entry.second, context)) { + result.emplace_back( + MakeUnique( + context, entry.first->id())); + } + } + } + return result; +} + +bool StructuredConstructToBlockReductionOpportunityFinder:: + DefinitionsRestrictedToRegion( + const opt::BasicBlock& header, + const std::unordered_set& region, + opt::IRContext* context) { + // Consider every block in the region. + for (auto& block : region) { + // Consider every instruction in the block - this includes the label + // instruction + if (!block->WhileEachInst( + [context, &header, ®ion](opt::Instruction* inst) -> bool { + if (inst->result_id() == 0) { + // The instruction does not generate a result id, thus it cannot + // be referred to outside the region - this is fine. + return true; + } + // Consider every use of the instruction's result id. + if (!context->get_def_use_mgr()->WhileEachUse( + inst->result_id(), + [context, &header, ®ion](opt::Instruction* user, + uint32_t) -> bool { + auto user_block = context->get_instr_block(user); + if (user == header.GetMergeInst() || + user == header.terminator()) { + // We are going to delete the header's merge + // instruction and rewrite its terminator, so it does + // not matter if the user is one of these + // instructions. + return true; + } + if (user_block == nullptr || + region.count(user_block) == 0) { + // The user is either a global instruction, or an + // instruction in a block outside the region. Removing + // the region would invalidate this user. + return false; + } + return true; + })) { + return false; + } + return true; + })) { + return false; + } + } + return true; +} + +bool StructuredConstructToBlockReductionOpportunityFinder:: + HasUnreachablePredecessor(const opt::BasicBlock& block, + opt::IRContext* context) { + for (auto pred : context->cfg()->preds(block.id())) { + if (!context->IsReachable(*context->cfg()->block(pred))) { + return true; + } + } + return false; +} + +std::string StructuredConstructToBlockReductionOpportunityFinder::GetName() + const { + return "StructuredConstructToBlockReductionOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/source/reduce/structured_construct_to_block_reduction_opportunity_finder.h b/source/reduce/structured_construct_to_block_reduction_opportunity_finder.h new file mode 100644 index 00000000..28bbc17c --- /dev/null +++ b/source/reduce/structured_construct_to_block_reduction_opportunity_finder.h @@ -0,0 +1,57 @@ +// Copyright (c) 2021 Alastair F. Donaldson +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_FINDER_H +#define SOURCE_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_FINDER_H + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder for opportunities to replace a skeletal structured control flow +// construct - that is, a construct that does not define anything that's used +// outside the construct - into its header block. +class StructuredConstructToBlockReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + StructuredConstructToBlockReductionOpportunityFinder() = default; + + ~StructuredConstructToBlockReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + private: + // Returns true if and only if all instructions defined in |region| are used + // only inside |region|, with the exception that they may be used by the merge + // or terminator instruction of |header|, which must be the header block for + // the region. + static bool DefinitionsRestrictedToRegion( + const opt::BasicBlock& header, + const std::unordered_set& region, + opt::IRContext* context); + + // Returns true if and only if |block| has at least one predecessor that is + // unreachable in the control flow graph of its function. + static bool HasUnreachablePredecessor(const opt::BasicBlock& block, + opt::IRContext* context); +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_FINDER_H diff --git a/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp b/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp index 0c004439..850af456 100644 --- a/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp +++ b/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp @@ -27,16 +27,14 @@ const uint32_t kMergeNodeIndex = 0; bool StructuredLoopToSelectionReductionOpportunity::PreconditionHolds() { // Is the loop header reachable? - return loop_construct_header_->GetLabel() - ->context() - ->GetDominatorAnalysis(enclosing_function_) - ->IsReachable(loop_construct_header_); + return loop_construct_header_->GetLabel()->context()->IsReachable( + *loop_construct_header_); } void StructuredLoopToSelectionReductionOpportunity::Apply() { // Force computation of dominator analysis, CFG and structured CFG analysis // before we start to mess with edges in the function. - context_->GetDominatorAnalysis(enclosing_function_); + context_->GetDominatorAnalysis(loop_construct_header_->GetParent()); context_->cfg(); context_->GetStructuredCFGAnalysis(); @@ -78,8 +76,7 @@ void StructuredLoopToSelectionReductionOpportunity::RedirectToClosestMergeBlock( } already_seen.insert(pred); - if (!context_->GetDominatorAnalysis(enclosing_function_) - ->IsReachable(pred)) { + if (!context_->IsReachable(*context_->cfg()->block(pred))) { // We do not care about unreachable predecessors (and dominance // information, and thus the notion of structured control flow, makes // little sense for unreachable blocks). @@ -216,7 +213,7 @@ void StructuredLoopToSelectionReductionOpportunity::ChangeLoopToSelection() { void StructuredLoopToSelectionReductionOpportunity::FixNonDominatedIdUses() { // Consider each instruction in the function. - for (auto& block : *enclosing_function_) { + for (auto& block : *loop_construct_header_->GetParent()) { for (auto& def : block) { if (def.opcode() == SpvOpVariable) { // Variables are defined at the start of the function, and can be @@ -243,7 +240,7 @@ void StructuredLoopToSelectionReductionOpportunity::FixNonDominatedIdUses() { case SpvStorageClassFunction: use->SetOperand( index, {FindOrCreateFunctionVariable( - context_, enclosing_function_, + context_, loop_construct_header_->GetParent(), context_->get_type_mgr()->GetId(pointer_type))}); break; default: @@ -276,11 +273,11 @@ bool StructuredLoopToSelectionReductionOpportunity:: if (use->opcode() == SpvOpPhi) { // A use in a phi doesn't need to be dominated by its definition, but the // associated parent block does need to be dominated by the definition. - return context_->GetDominatorAnalysis(enclosing_function_) + return context_->GetDominatorAnalysis(loop_construct_header_->GetParent()) ->Dominates(def_block.id(), use->GetSingleWordOperand(use_index + 1)); } // In non-phi cases, a use needs to be dominated by its definition. - return context_->GetDominatorAnalysis(enclosing_function_) + return context_->GetDominatorAnalysis(loop_construct_header_->GetParent()) ->Dominates(def, use); } diff --git a/source/reduce/structured_loop_to_selection_reduction_opportunity.h b/source/reduce/structured_loop_to_selection_reduction_opportunity.h index 4c576196..0e3c840a 100644 --- a/source/reduce/structured_loop_to_selection_reduction_opportunity.h +++ b/source/reduce/structured_loop_to_selection_reduction_opportunity.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef SOURCE_REDUCE_CUT_LOOP_REDUCTION_OPPORTUNITY_H_ -#define SOURCE_REDUCE_CUT_LOOP_REDUCTION_OPPORTUNITY_H_ +#ifndef SOURCE_REDUCE_STRUCTURED_LOOP_TO_SELECTION_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_STRUCTURED_LOOP_TO_SELECTION_REDUCTION_OPPORTUNITY_H_ #include "source/opt/def_use_manager.h" #include "source/opt/dominator_analysis.h" @@ -30,11 +30,8 @@ class StructuredLoopToSelectionReductionOpportunity // Constructs an opportunity from a loop header block and the function that // encloses it. explicit StructuredLoopToSelectionReductionOpportunity( - opt::IRContext* context, opt::BasicBlock* loop_construct_header, - opt::Function* enclosing_function) - : context_(context), - loop_construct_header_(loop_construct_header), - enclosing_function_(enclosing_function) {} + opt::IRContext* context, opt::BasicBlock* loop_construct_header) + : context_(context), loop_construct_header_(loop_construct_header) {} // Returns true if the loop header is reachable. A structured loop might // become unreachable as a result of turning another structured loop into @@ -88,10 +85,9 @@ class StructuredLoopToSelectionReductionOpportunity opt::IRContext* context_; opt::BasicBlock* loop_construct_header_; - opt::Function* enclosing_function_; }; } // namespace reduce } // namespace spvtools -#endif // SOURCE_REDUCE_CUT_LOOP_REDUCTION_OPPORTUNITY_H_ +#endif // SOURCE_REDUCE_STRUCTURED_LOOP_TO_SELECTION_REDUCTION_OPPORTUNITY_H_ diff --git a/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp b/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp index fdf3ab04..3fe61280 100644 --- a/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp +++ b/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp @@ -86,8 +86,8 @@ StructuredLoopToSelectionReductionOpportunityFinder::GetAvailableOpportunities( // We can turn this structured loop into a selection, so add the // opportunity to do so. result.push_back( - MakeUnique( - context, &block, function)); + MakeUnique(context, + &block)); } } return result; diff --git a/source/spirv_constant.h b/source/spirv_constant.h index 39771ccb..8636806c 100644 --- a/source/spirv_constant.h +++ b/source/spirv_constant.h @@ -84,6 +84,7 @@ typedef enum spv_generator_t { SPV_GENERATOR_KHRONOS_LLVM_TRANSLATOR = 6, SPV_GENERATOR_KHRONOS_ASSEMBLER = 7, SPV_GENERATOR_KHRONOS_GLSLANG = 8, + SPV_GENERATOR_KHRONOS_LINKER = 17, SPV_GENERATOR_NUM_ENTRIES, SPV_FORCE_16_BIT_ENUM(spv_generator_t) } spv_generator_t; diff --git a/source/spirv_definition.h b/source/spirv_definition.h index 63a4ef0d..5dbd6ab2 100644 --- a/source/spirv_definition.h +++ b/source/spirv_definition.h @@ -27,7 +27,7 @@ typedef struct spv_header_t { uint32_t generator; uint32_t bound; uint32_t schema; // NOTE: Reserved - const uint32_t* instructions; // NOTE: Unfixed pointer to instruciton stream + const uint32_t* instructions; // NOTE: Unfixed pointer to instruction stream } spv_header_t; #endif // SOURCE_SPIRV_DEFINITION_H_ diff --git a/source/spirv_endian.h b/source/spirv_endian.h index c2540bec..b4927f31 100644 --- a/source/spirv_endian.h +++ b/source/spirv_endian.h @@ -31,7 +31,7 @@ uint64_t spvFixDoubleWord(const uint32_t low, const uint32_t high, spv_result_t spvBinaryEndianness(const spv_const_binary binary, spv_endianness_t* endian); -// Returns true if the given endianness matches the host's native endiannes. +// Returns true if the given endianness matches the host's native endianness. bool spvIsHostEndian(spv_endianness_t endian); #endif // SOURCE_SPIRV_ENDIAN_H_ diff --git a/source/spirv_target_env.cpp b/source/spirv_target_env.cpp index f20ebb4f..9a038174 100644 --- a/source/spirv_target_env.cpp +++ b/source/spirv_target_env.cpp @@ -62,7 +62,7 @@ const char* spvTargetEnvDescription(spv_target_env env) { case SPV_ENV_VULKAN_1_1: return "SPIR-V 1.3 (under Vulkan 1.1 semantics)"; case SPV_ENV_WEBGPU_0: - assert(false); + assert(false && "Deprecated target environment value."); break; case SPV_ENV_UNIVERSAL_1_4: return "SPIR-V 1.4"; @@ -72,6 +72,13 @@ const char* spvTargetEnvDescription(spv_target_env env) { return "SPIR-V 1.5"; case SPV_ENV_VULKAN_1_2: return "SPIR-V 1.5 (under Vulkan 1.2 semantics)"; + case SPV_ENV_UNIVERSAL_1_6: + return "SPIR-V 1.6"; + case SPV_ENV_VULKAN_1_3: + return "SPIR-V 1.6 (under Vulkan 1.3 semantics)"; + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); + break; } return ""; } @@ -102,7 +109,7 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) { case SPV_ENV_VULKAN_1_1: return SPV_SPIRV_VERSION_WORD(1, 3); case SPV_ENV_WEBGPU_0: - assert(false); + assert(false && "Deprecated target environment value."); break; case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_VULKAN_1_1_SPIRV_1_4: @@ -110,6 +117,12 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) { case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: return SPV_SPIRV_VERSION_WORD(1, 5); + case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: + return SPV_SPIRV_VERSION_WORD(1, 6); + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); + break; } return SPV_SPIRV_VERSION_WORD(0, 0); } @@ -119,12 +132,14 @@ static const std::pair spvTargetEnvNameMap[] = { {"vulkan1.0", SPV_ENV_VULKAN_1_0}, {"vulkan1.1", SPV_ENV_VULKAN_1_1}, {"vulkan1.2", SPV_ENV_VULKAN_1_2}, + {"vulkan1.3", SPV_ENV_VULKAN_1_3}, {"spv1.0", SPV_ENV_UNIVERSAL_1_0}, {"spv1.1", SPV_ENV_UNIVERSAL_1_1}, {"spv1.2", SPV_ENV_UNIVERSAL_1_2}, {"spv1.3", SPV_ENV_UNIVERSAL_1_3}, {"spv1.4", SPV_ENV_UNIVERSAL_1_4}, {"spv1.5", SPV_ENV_UNIVERSAL_1_5}, + {"spv1.6", SPV_ENV_UNIVERSAL_1_6}, {"opencl1.2embedded", SPV_ENV_OPENCL_EMBEDDED_1_2}, {"opencl1.2", SPV_ENV_OPENCL_1_2}, {"opencl2.0embedded", SPV_ENV_OPENCL_EMBEDDED_2_0}, @@ -171,7 +186,8 @@ static const VulkanEnv ordered_vulkan_envs[] = { {SPV_ENV_VULKAN_1_0, VULKAN_VER(1, 0), SPIRV_VER(1, 0)}, {SPV_ENV_VULKAN_1_1, VULKAN_VER(1, 1), SPIRV_VER(1, 3)}, {SPV_ENV_VULKAN_1_1_SPIRV_1_4, VULKAN_VER(1, 1), SPIRV_VER(1, 4)}, - {SPV_ENV_VULKAN_1_2, VULKAN_VER(1, 2), SPIRV_VER(1, 5)}}; + {SPV_ENV_VULKAN_1_2, VULKAN_VER(1, 2), SPIRV_VER(1, 5)}, + {SPV_ENV_VULKAN_1_3, VULKAN_VER(1, 3), SPIRV_VER(1, 6)}}; bool spvParseVulkanEnv(uint32_t vulkan_ver, uint32_t spirv_ver, spv_target_env* env) { @@ -205,14 +221,19 @@ bool spvIsVulkanEnv(spv_target_env env) { case SPV_ENV_UNIVERSAL_1_3: case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_UNIVERSAL_1_6: return false; case SPV_ENV_VULKAN_1_0: case SPV_ENV_VULKAN_1_1: case SPV_ENV_VULKAN_1_1_SPIRV_1_4: case SPV_ENV_VULKAN_1_2: + case SPV_ENV_VULKAN_1_3: return true; case SPV_ENV_WEBGPU_0: - assert(false); + assert(false && "Deprecated target environment value."); + break; + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); break; } return false; @@ -235,6 +256,8 @@ bool spvIsOpenCLEnv(spv_target_env env) { case SPV_ENV_VULKAN_1_1_SPIRV_1_4: case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: + case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: return false; case SPV_ENV_OPENCL_1_2: case SPV_ENV_OPENCL_EMBEDDED_1_2: @@ -246,7 +269,10 @@ bool spvIsOpenCLEnv(spv_target_env env) { case SPV_ENV_OPENCL_2_2: return true; case SPV_ENV_WEBGPU_0: - assert(false); + assert(false && "Deprecated target environment value."); + break; + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); break; } return false; @@ -272,6 +298,8 @@ bool spvIsOpenGLEnv(spv_target_env env) { case SPV_ENV_VULKAN_1_1_SPIRV_1_4: case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: + case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: return false; case SPV_ENV_OPENGL_4_0: case SPV_ENV_OPENGL_4_1: @@ -280,7 +308,45 @@ bool spvIsOpenGLEnv(spv_target_env env) { case SPV_ENV_OPENGL_4_5: return true; case SPV_ENV_WEBGPU_0: - assert(false); + assert(false && "Deprecated target environment value."); + break; + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); + break; + } + return false; +} + +bool spvIsValidEnv(spv_target_env env) { + switch (env) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_OPENCL_1_2: + case SPV_ENV_OPENCL_EMBEDDED_1_2: + case SPV_ENV_OPENCL_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_1: + case SPV_ENV_OPENCL_EMBEDDED_2_2: + case SPV_ENV_OPENCL_2_1: + case SPV_ENV_OPENCL_2_2: + case SPV_ENV_UNIVERSAL_1_4: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_VULKAN_1_2: + case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: + case SPV_ENV_OPENGL_4_0: + case SPV_ENV_OPENGL_4_1: + case SPV_ENV_OPENGL_4_2: + case SPV_ENV_OPENGL_4_3: + case SPV_ENV_OPENGL_4_5: + return true; + case SPV_ENV_WEBGPU_0: + case SPV_ENV_MAX: break; } return false; @@ -307,20 +373,25 @@ std::string spvLogStringForEnv(spv_target_env env) { } case SPV_ENV_VULKAN_1_0: case SPV_ENV_VULKAN_1_1: - case SPV_ENV_VULKAN_1_1_SPIRV_1_4: { - case SPV_ENV_VULKAN_1_2: - return "Vulkan"; + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_VULKAN_1_2: + case SPV_ENV_VULKAN_1_3: { + return "Vulkan"; } case SPV_ENV_UNIVERSAL_1_0: case SPV_ENV_UNIVERSAL_1_1: case SPV_ENV_UNIVERSAL_1_2: case SPV_ENV_UNIVERSAL_1_3: case SPV_ENV_UNIVERSAL_1_4: - case SPV_ENV_UNIVERSAL_1_5: { + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_UNIVERSAL_1_6: { return "Universal"; } case SPV_ENV_WEBGPU_0: - assert(false); + assert(false && "Deprecated target environment value."); + break; + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); break; } return "Unknown"; diff --git a/source/spirv_target_env.h b/source/spirv_target_env.h index a804d615..f3b0c2f6 100644 --- a/source/spirv_target_env.h +++ b/source/spirv_target_env.h @@ -28,6 +28,9 @@ bool spvIsOpenCLEnv(spv_target_env env); // Returns true if |env| is an OPENGL environment, false otherwise. bool spvIsOpenGLEnv(spv_target_env env); +// Returns true if |env| is an implemented/valid environment, false otherwise. +bool spvIsValidEnv(spv_target_env env); + // Returns the version number for the given SPIR-V target environment. uint32_t spvVersionForTargetEnv(spv_target_env env); @@ -37,7 +40,7 @@ std::string spvLogStringForEnv(spv_target_env env); // Returns a formatted list of all SPIR-V target environment names that // can be parsed by spvParseTargetEnv. -// |pad| is the number of space characters that the begining of each line +// |pad| is the number of space characters that the beginning of each line // except the first one will be padded with. // |wrap| is the max length of lines the user desires. Word-wrapping will // occur to satisfy this limit. diff --git a/source/spirv_validator_options.cpp b/source/spirv_validator_options.cpp index e5b1eece..b72a6446 100644 --- a/source/spirv_validator_options.cpp +++ b/source/spirv_validator_options.cpp @@ -125,3 +125,8 @@ void spvValidatorOptionsSetAllowLocalSizeId(spv_validator_options options, bool val) { options->allow_localsizeid = val; } + +void spvValidatorOptionsSetFriendlyNames(spv_validator_options options, + bool val) { + options->use_friendly_names = val; +} diff --git a/source/spirv_validator_options.h b/source/spirv_validator_options.h index ff283cab..01450480 100644 --- a/source/spirv_validator_options.h +++ b/source/spirv_validator_options.h @@ -47,8 +47,9 @@ struct spv_validator_options_t { scalar_block_layout(false), workgroup_scalar_block_layout(false), skip_block_layout(false), - allow_localsizeid(false), - before_hlsl_legalization(false) {} + allow_localsizeid(false), + before_hlsl_legalization(false), + use_friendly_names(true) {} validator_universal_limits_t universal_limits_; bool relax_struct_store; @@ -60,6 +61,7 @@ struct spv_validator_options_t { bool skip_block_layout; bool allow_localsizeid; bool before_hlsl_legalization; + bool use_friendly_names; }; #endif // SOURCE_SPIRV_VALIDATOR_OPTIONS_H_ diff --git a/source/table.cpp b/source/table.cpp index d4a2d7e9..822cefeb 100644 --- a/source/table.cpp +++ b/source/table.cpp @@ -41,6 +41,8 @@ spv_context spvContextCreate(spv_target_env env) { case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: + case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: break; default: return nullptr; diff --git a/source/text.cpp b/source/text.cpp index 88a8e8ff..90f69c52 100644 --- a/source/text.cpp +++ b/source/text.cpp @@ -403,9 +403,10 @@ spv_result_t spvTextEncodeOperand(const spvtools::AssemblyGrammar& grammar, case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: { uint32_t value; - if (grammar.parseMaskOperand(type, textValue, &value)) { - return context->diagnostic() << "Invalid " << spvOperandTypeStr(type) - << " operand '" << textValue << "'."; + if (auto error = grammar.parseMaskOperand(type, textValue, &value)) { + return context->diagnostic(error) + << "Invalid " << spvOperandTypeStr(type) << " operand '" + << textValue << "'."; } if (auto error = context->binaryEncodeU32(value, pInst)) return error; // Prepare to parse the operands for this logical operand. @@ -622,7 +623,8 @@ spv_result_t spvTextEncodeOpcode(const spvtools::AssemblyGrammar& grammar, break; } else { return context->diagnostic() - << "Expected operand, found end of stream."; + << "Expected operand for " << opcodeName + << " instruction, but found the end of the stream."; } } assert(error == SPV_SUCCESS && "Somebody added another way to fail"); @@ -632,7 +634,8 @@ spv_result_t spvTextEncodeOpcode(const spvtools::AssemblyGrammar& grammar, break; } else { return context->diagnostic() - << "Expected operand, found next instruction instead."; + << "Expected operand for " << opcodeName + << " instruction, but found the next instruction instead."; } } @@ -666,7 +669,7 @@ spv_result_t spvTextEncodeOpcode(const spvtools::AssemblyGrammar& grammar, if (pInst->words.size() > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) { return context->diagnostic() - << "Instruction too long: " << pInst->words.size() + << opcodeName << " Instruction too long: " << pInst->words.size() << " words, but the limit is " << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX; } @@ -715,6 +718,12 @@ spv_result_t GetNumericIds(const spvtools::AssemblyGrammar& grammar, while (context.hasText()) { spv_instruction_t inst; + // Operand parsing sometimes involves knowing the opcode of the instruction + // being parsed. A malformed input might feature such an operand *before* + // the opcode is known. To guard against accessing an uninitialized opcode, + // the instruction's opcode is initialized to a default value. + inst.opcode = SpvOpMax; + if (spvTextEncodeOpcode(grammar, &context, &inst)) { return SPV_ERROR_INVALID_TEXT; } @@ -763,8 +772,8 @@ spv_result_t spvTextToBinaryInternal(const spvtools::AssemblyGrammar& grammar, instructions.push_back({}); spv_instruction_t& inst = instructions.back(); - if (spvTextEncodeOpcode(grammar, &context, &inst)) { - return SPV_ERROR_INVALID_TEXT; + if (auto error = spvTextEncodeOpcode(grammar, &context, &inst)) { + return error; } if (context.advance()) break; diff --git a/source/text_handler.cpp b/source/text_handler.cpp index c31f34a6..15c1741f 100644 --- a/source/text_handler.cpp +++ b/source/text_handler.cpp @@ -29,6 +29,7 @@ #include "source/util/bitutils.h" #include "source/util/hex_float.h" #include "source/util/parse_number.h" +#include "source/util/string_utils.h" namespace spvtools { namespace { @@ -61,28 +62,29 @@ spv_result_t advanceLine(spv_text text, spv_position position) { // parameters, its the users responsibility to ensure these are non null. spv_result_t advance(spv_text text, spv_position position) { // NOTE: Consume white space, otherwise don't advance. - if (position->index >= text->length) return SPV_END_OF_STREAM; - switch (text->str[position->index]) { - case '\0': - return SPV_END_OF_STREAM; - case ';': - if (spv_result_t error = advanceLine(text, position)) return error; - return advance(text, position); - case ' ': - case '\t': - case '\r': - position->column++; - position->index++; - return advance(text, position); - case '\n': - position->column = 0; - position->line++; - position->index++; - return advance(text, position); - default: - break; + while (true) { + if (position->index >= text->length) return SPV_END_OF_STREAM; + switch (text->str[position->index]) { + case '\0': + return SPV_END_OF_STREAM; + case ';': + if (spv_result_t error = advanceLine(text, position)) return error; + continue; + case ' ': + case '\t': + case '\r': + position->column++; + position->index++; + continue; + case '\n': + position->column = 0; + position->line++; + position->index++; + continue; + default: + return SPV_SUCCESS; + } } - return SPV_SUCCESS; } // Fetches the next word from the given text stream starting from the given @@ -120,7 +122,8 @@ spv_result_t getWord(spv_text text, spv_position position, std::string* word) { case '\n': case '\r': if (escaping || quoting) break; - // Fall through. + word->assign(text->str + start_index, text->str + position->index); + return SPV_SUCCESS; case '\0': { // NOTE: End of word found! word->assign(text->str + start_index, text->str + position->index); return SPV_SUCCESS; @@ -306,14 +309,8 @@ spv_result_t AssemblyContext::binaryEncodeString(const char* value, << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX << " words."; } - pInst->words.resize(newWordCount); - - // Make sure all the bytes in the last word are 0, in case we only - // write a partial word at the end. - pInst->words.back() = 0; - - char* dest = (char*)&pInst->words[oldWordCount]; - strncpy(dest, value, length + 1); + pInst->words.reserve(newWordCount); + spvtools::utils::AppendToVector(value, &pInst->words); return SPV_SUCCESS; } diff --git a/source/util/bit_vector.h b/source/util/bit_vector.h index 3e189cb1..826d62f0 100644 --- a/source/util/bit_vector.h +++ b/source/util/bit_vector.h @@ -32,7 +32,7 @@ class BitVector { enum { kInitialNumBits = 1024 }; public: - // Creates a bit vector contianing 0s. + // Creates a bit vector containing 0s. BitVector(uint32_t reserved_size = kInitialNumBits) : bits_((reserved_size - 1) / kBitContainerSize + 1, 0) {} diff --git a/source/util/hash_combine.h b/source/util/hash_combine.h new file mode 100644 index 00000000..1a2dbc33 --- /dev/null +++ b/source/util/hash_combine.h @@ -0,0 +1,53 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_HASH_COMBINE_H_ +#define SOURCE_UTIL_HASH_COMBINE_H_ + +#include +#include +#include + +namespace spvtools { +namespace utils { + +// Helpers for incrementally computing hashes. +// For reference, see +// http://open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3876.pdf + +template +inline size_t hash_combine(std::size_t seed, const T& val) { + return seed ^ (std::hash()(val) + 0x9e3779b9 + (seed << 6) + (seed >> 2)); +} + +template +inline size_t hash_combine(std::size_t hash, const std::vector& vals) { + for (const T& val : vals) { + hash = hash_combine(hash, val); + } + return hash; +} + +inline size_t hash_combine(std::size_t hash) { return hash; } + +template +inline size_t hash_combine(std::size_t hash, const T& val, + const Types&... args) { + return hash_combine(hash_combine(hash, val), args...); +} + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_HASH_COMBINE_H_ diff --git a/source/util/hex_float.h b/source/util/hex_float.h index cfc40fa6..06e3c575 100644 --- a/source/util/hex_float.h +++ b/source/util/hex_float.h @@ -199,7 +199,7 @@ bool operator==(const FloatProxy& first, const FloatProxy& second) { // Reads a FloatProxy value as a normal float from a stream. template std::istream& operator>>(std::istream& is, FloatProxy& value) { - T float_val; + T float_val = static_cast(0.0); is >> float_val; value = FloatProxy(float_val); return is; @@ -209,9 +209,10 @@ std::istream& operator>>(std::istream& is, FloatProxy& value) { // be the default for any non-specialized type. template struct HexFloatTraits { - // Integer type that can store this hex-float. + // Integer type that can store the bit representation of this hex-float. using uint_type = void; - // Signed integer type that can store this hex-float. + // Signed integer type that can store the bit representation of this + // hex-float. using int_type = void; // The numerical type that this HexFloat represents. using underlying_type = void; @@ -958,9 +959,15 @@ std::istream& operator>>(std::istream& is, HexFloat& value) { // This "looks" like a hex-float so treat it as one. bool seen_p = false; bool seen_dot = false; + + // The mantissa bits, without the most significant 1 bit, and with the + // the most recently read bits in the least significant positions. + uint_type fraction = 0; + // The number of mantissa bits that have been read, including the leading 1 + // bit that is not written into 'fraction'. uint_type fraction_index = 0; - uint_type fraction = 0; + // TODO(dneto): handle overflow and underflow int_type exponent = HF::exponent_bias; // Strip off leading zeros so we don't have to special-case them later. @@ -968,11 +975,13 @@ std::istream& operator>>(std::istream& is, HexFloat& value) { is.get(); } - bool is_denorm = - true; // Assume denorm "representation" until we hear otherwise. - // NB: This does not mean the value is actually denorm, - // it just means that it was written 0. + // Does the mantissa, as written, have non-zero digits to the left of + // the decimal point. Assume no until proven otherwise. + bool has_integer_part = false; bool bits_written = false; // Stays false until we write a bit. + + // Scan the mantissa hex digits until we see a '.' or the 'p' that + // starts the exponent. while (!seen_p && !seen_dot) { // Handle characters that are left of the fractional part. if (next_char == '.') { @@ -980,9 +989,8 @@ std::istream& operator>>(std::istream& is, HexFloat& value) { } else if (next_char == 'p') { seen_p = true; } else if (::isxdigit(next_char)) { - // We know this is not denormalized since we have stripped all leading - // zeroes and we are not a ".". - is_denorm = false; + // We have stripped all leading zeroes and we have not yet seen a ".". + has_integer_part = true; int number = get_nibble_from_character(next_char); for (int i = 0; i < 4; ++i, number <<= 1) { uint_type write_bit = (number & 0x8) ? 0x1 : 0x0; @@ -993,8 +1001,12 @@ std::istream& operator>>(std::istream& is, HexFloat& value) { fraction | static_cast( write_bit << (HF::top_bit_left_shift - fraction_index++))); + // TODO(dneto): Avoid overflow. Testing would require + // parameterization. exponent = static_cast(exponent + 1); } + // Since this updated after setting fraction bits, this effectively + // drops the leading 1 bit. bits_written |= write_bit != 0; } } else { @@ -1005,6 +1017,9 @@ std::istream& operator>>(std::istream& is, HexFloat& value) { is.get(); next_char = is.peek(); } + + // Finished reading the part preceding any '.' or 'p'. + bits_written = false; while (seen_dot && !seen_p) { // Handle only fractional parts now. @@ -1015,10 +1030,12 @@ std::istream& operator>>(std::istream& is, HexFloat& value) { for (int i = 0; i < 4; ++i, number <<= 1) { uint_type write_bit = (number & 0x8) ? 0x01 : 0x00; bits_written |= write_bit != 0; - if (is_denorm && !bits_written) { + if ((!has_integer_part) && !bits_written) { // Handle modifying the exponent here this way we can handle // an arbitrary number of hex values without overflowing our // integer. + // TODO(dneto): Handle underflow. Testing would require extra + // parameterization. exponent = static_cast(exponent - 1); } else { fraction = static_cast( @@ -1037,34 +1054,79 @@ std::istream& operator>>(std::istream& is, HexFloat& value) { next_char = is.peek(); } - bool seen_sign = false; + // Finished reading the part preceding 'p'. + // In hex floats syntax, the binary exponent is required. + + bool seen_exponent_sign = false; int8_t exponent_sign = 1; + bool seen_written_exponent_digits = false; + // The magnitude of the exponent, as written, or the sentinel value to signal + // overflow. int_type written_exponent = 0; + // A sentinel value signalling overflow of the magnitude of the written + // exponent. We'll assume that -written_exponent_overflow is valid for the + // type. Later we may add 1 or subtract 1 from the adjusted exponent, so leave + // room for an extra 1. + const int_type written_exponent_overflow = + std::numeric_limits::max() - 1; while (true) { - if ((next_char == '-' || next_char == '+')) { - if (seen_sign) { + if (!seen_written_exponent_digits && + (next_char == '-' || next_char == '+')) { + if (seen_exponent_sign) { is.setstate(std::ios::failbit); return is; } - seen_sign = true; + seen_exponent_sign = true; exponent_sign = (next_char == '-') ? -1 : 1; } else if (::isdigit(next_char)) { + seen_written_exponent_digits = true; // Hex-floats express their exponent as decimal. - written_exponent = static_cast(written_exponent * 10); - written_exponent = - static_cast(written_exponent + (next_char - '0')); + int_type digit = + static_cast(static_cast(next_char) - '0'); + if (written_exponent >= (written_exponent_overflow - digit) / 10) { + // The exponent is very big. Saturate rather than overflow the exponent. + // signed integer, which would be undefined behaviour. + written_exponent = written_exponent_overflow; + } else { + written_exponent = static_cast( + static_cast(written_exponent * 10) + digit); + } } else { break; } is.get(); next_char = is.peek(); } + if (!seen_written_exponent_digits) { + // Binary exponent had no digits. + is.setstate(std::ios::failbit); + return is; + } written_exponent = static_cast(written_exponent * exponent_sign); - exponent = static_cast(exponent + written_exponent); + // Now fold in the exponent bias into the written exponent, updating exponent. + // But avoid undefined behaviour that would result from overflowing int_type. + if (written_exponent >= 0 && exponent >= 0) { + // Saturate up to written_exponent_overflow. + if (written_exponent_overflow - exponent > written_exponent) { + exponent = static_cast(written_exponent + exponent); + } else { + exponent = written_exponent_overflow; + } + } else if (written_exponent < 0 && exponent < 0) { + // Saturate down to -written_exponent_overflow. + if (written_exponent_overflow + exponent > -written_exponent) { + exponent = static_cast(written_exponent + exponent); + } else { + exponent = static_cast(-written_exponent_overflow); + } + } else { + // They're of opposing sign, so it's safe to add. + exponent = static_cast(written_exponent + exponent); + } - bool is_zero = is_denorm && (fraction == 0); - if (is_denorm && !is_zero) { + bool is_zero = (!has_integer_part) && (fraction == 0); + if ((!has_integer_part) && !is_zero) { fraction = static_cast(fraction << 1); exponent = static_cast(exponent - 1); } else if (is_zero) { @@ -1081,7 +1143,7 @@ std::istream& operator>>(std::istream& is, HexFloat& value) { const int_type max_exponent = SetBits::get; - // Handle actual denorm numbers + // Handle denorm numbers while (exponent < 0 && !is_zero) { fraction = static_cast(fraction >> 1); exponent = static_cast(exponent + 1); diff --git a/source/util/ilist.h b/source/util/ilist.h index 9837b09b..42d5e62b 100644 --- a/source/util/ilist.h +++ b/source/util/ilist.h @@ -59,7 +59,7 @@ class IntrusiveList { // Moves the contents of the given list to the list being constructed. IntrusiveList(IntrusiveList&&); - // Destorys the list. Note that the elements of the list will not be deleted, + // Destroys the list. Note that the elements of the list will not be deleted, // but they will be removed from the list. virtual ~IntrusiveList(); @@ -348,6 +348,7 @@ void IntrusiveList::Check(NodeType* start) { p = p->next_node_; } while (p != start); assert(sentinel_count == 1 && "List should have exactly 1 sentinel node."); + (void)sentinel_count; p = start; do { diff --git a/source/util/parse_number.h b/source/util/parse_number.h index 729aac54..d0f2a09a 100644 --- a/source/util/parse_number.h +++ b/source/util/parse_number.h @@ -220,7 +220,7 @@ EncodeNumberStatus ParseAndEncodeIntegerNumber( std::function emit, std::string* error_msg); // Parses a floating point value of a given |type| from the given |text| and -// encodes the number by the given |emit| funciton. On success, returns +// encodes the number by the given |emit| function. On success, returns // EncodeNumberStatus::kSuccess and the parsed number will be consumed by the // given |emit| function word by word (least significant word first). On // failure, this function returns the error code of the encoding status and diff --git a/source/util/small_vector.h b/source/util/small_vector.h index f2c1147b..648a3482 100644 --- a/source/util/small_vector.h +++ b/source/util/small_vector.h @@ -64,6 +64,11 @@ class SmallVector { } } + template + SmallVector(InputIt first, InputIt last) : SmallVector() { + insert(end(), first, last); + } + SmallVector(std::vector&& vec) : SmallVector() { if (vec.size() > small_size) { large_data_ = MakeUnique>(std::move(vec)); @@ -175,9 +180,12 @@ class SmallVector { return true; } +// Avoid infinite recursion from rewritten operators in C++20 +#if __cplusplus <= 201703L friend bool operator==(const std::vector& lhs, const SmallVector& rhs) { return rhs == lhs; } +#endif friend bool operator!=(const SmallVector& lhs, const std::vector& rhs) { return !(lhs == rhs); @@ -325,6 +333,15 @@ class SmallVector { ++size_; } + void pop_back() { + if (large_data_) { + large_data_->pop_back(); + } else { + --size_; + small_data_[size_].~T(); + } + } + template iterator insert(iterator pos, InputIt first, InputIt last) { size_t element_idx = (pos - begin()); @@ -363,7 +380,7 @@ class SmallVector { } } - // Upate the size. + // Update the size. size_ += num_of_new_elements; return pos; } @@ -449,7 +466,7 @@ class SmallVector { T* small_data_; // The actual data used to store the array elements. It must never be used - // directly, but must only be accesed through |small_data_|. + // directly, but must only be accessed through |small_data_|. typename std::aligned_storage::value>::type buffer[small_size]; diff --git a/source/util/string_utils.h b/source/util/string_utils.h index 4282aa94..03e20b3d 100644 --- a/source/util/string_utils.h +++ b/source/util/string_utils.h @@ -16,6 +16,8 @@ #define SOURCE_UTIL_STRING_UTILS_H_ #include + +#include #include #include #include @@ -44,9 +46,10 @@ std::string CardinalToOrdinal(size_t cardinal); // string will be empty. std::pair SplitFlagArgs(const std::string& flag); -// Encodes a string as a sequence of words, using the SPIR-V encoding. -inline std::vector MakeVector(std::string input) { - std::vector result; +// Encodes a string as a sequence of words, using the SPIR-V encoding, appending +// to an existing vector. +inline void AppendToVector(const std::string& input, + std::vector* result) { uint32_t word = 0; size_t num_bytes = input.size(); // SPIR-V strings are null-terminated. The byte_index == num_bytes @@ -56,24 +59,36 @@ inline std::vector MakeVector(std::string input) { (byte_index < num_bytes ? uint8_t(input[byte_index]) : uint8_t(0)); word |= (new_byte << (8 * (byte_index % sizeof(uint32_t)))); if (3 == (byte_index % sizeof(uint32_t))) { - result.push_back(word); + result->push_back(word); word = 0; } } // Emit a trailing partial word. if ((num_bytes + 1) % sizeof(uint32_t)) { - result.push_back(word); + result->push_back(word); } +} + +// Encodes a string as a sequence of words, using the SPIR-V encoding. +inline std::vector MakeVector(const std::string& input) { + std::vector result; + AppendToVector(input, &result); return result; } -// Decode a string from a sequence of words, using the SPIR-V encoding. -template -inline std::string MakeString(const VectorType& words) { +// Decode a string from a sequence of words between first and last, using the +// SPIR-V encoding. Assert that a terminating 0-byte was found (unless +// assert_found_terminating_null is passed as false). +template +inline std::string MakeString(InputIt first, InputIt last, + bool assert_found_terminating_null = true) { std::string result; + constexpr size_t kCharsPerWord = sizeof(*first); + static_assert(kCharsPerWord == 4, "expect 4-byte word"); - for (uint32_t word : words) { - for (int byte_index = 0; byte_index < 4; byte_index++) { + for (InputIt pos = first; pos != last; ++pos) { + uint32_t word = *pos; + for (size_t byte_index = 0; byte_index < kCharsPerWord; byte_index++) { uint32_t extracted_word = (word >> (8 * byte_index)) & 0xFF; char c = static_cast(extracted_word); if (c == 0) { @@ -82,9 +97,33 @@ inline std::string MakeString(const VectorType& words) { result += c; } } - assert(false && "Did not find terminating null for the string."); + assert(!assert_found_terminating_null && + "Did not find terminating null for the string."); + (void)assert_found_terminating_null; /* No unused parameters in release + builds. */ return result; -} // namespace utils +} + +// Decode a string from a sequence of words in a vector, using the SPIR-V +// encoding. +template +inline std::string MakeString(const VectorType& words, + bool assert_found_terminating_null = true) { + return MakeString(words.cbegin(), words.cend(), + assert_found_terminating_null); +} + +// Decode a string from array words, consuming up to count words, using the +// SPIR-V encoding. +inline std::string MakeString(const uint32_t* words, size_t num_words, + bool assert_found_terminating_null = true) { + return MakeString(words, words + num_words, assert_found_terminating_null); +} + +// Check if str starts with prefix (only included since C++20) +inline bool starts_with(const std::string& str, const char* prefix) { + return 0 == str.compare(0, std::strlen(prefix), prefix); +} } // namespace utils } // namespace spvtools diff --git a/source/util/timer.h b/source/util/timer.h index fc4b747b..08083119 100644 --- a/source/util/timer.h +++ b/source/util/timer.h @@ -206,16 +206,16 @@ class Timer { // Variable to save the result of clock_gettime(CLOCK_PROCESS_CPUTIME_ID) when // Timer::Stop() is called. It is used as the last status of CPU time. The - // resouce usage is measured by subtracting |cpu_before_| from it. + // resource usage is measured by subtracting |cpu_before_| from it. timespec cpu_after_; // Variable to save the result of clock_gettime(CLOCK_MONOTONIC) when // Timer::Stop() is called. It is used as the last status of WALL time. The - // resouce usage is measured by subtracting |wall_before_| from it. + // resource usage is measured by subtracting |wall_before_| from it. timespec wall_after_; // Variable to save the result of getrusage() when Timer::Stop() is called. It - // is used as the last status of USR time, SYS time, and RSS. Those resouce + // is used as the last status of USR time, SYS time, and RSS. Those resource // usages are measured by subtracting |usage_before_| from it. rusage usage_after_; diff --git a/source/val/basic_block.cpp b/source/val/basic_block.cpp index b2a8793d..da05db3a 100644 --- a/source/val/basic_block.cpp +++ b/source/val/basic_block.cpp @@ -24,11 +24,13 @@ namespace val { BasicBlock::BasicBlock(uint32_t label_id) : id_(label_id), immediate_dominator_(nullptr), - immediate_post_dominator_(nullptr), + immediate_structural_dominator_(nullptr), + immediate_structural_post_dominator_(nullptr), predecessors_(), successors_(), type_(0), reachable_(false), + structurally_reachable_(false), label_(nullptr), terminator_(nullptr) {} @@ -36,21 +38,32 @@ void BasicBlock::SetImmediateDominator(BasicBlock* dom_block) { immediate_dominator_ = dom_block; } -void BasicBlock::SetImmediatePostDominator(BasicBlock* pdom_block) { - immediate_post_dominator_ = pdom_block; +void BasicBlock::SetImmediateStructuralDominator(BasicBlock* dom_block) { + immediate_structural_dominator_ = dom_block; +} + +void BasicBlock::SetImmediateStructuralPostDominator(BasicBlock* pdom_block) { + immediate_structural_post_dominator_ = pdom_block; } const BasicBlock* BasicBlock::immediate_dominator() const { return immediate_dominator_; } -const BasicBlock* BasicBlock::immediate_post_dominator() const { - return immediate_post_dominator_; +const BasicBlock* BasicBlock::immediate_structural_dominator() const { + return immediate_structural_dominator_; +} + +const BasicBlock* BasicBlock::immediate_structural_post_dominator() const { + return immediate_structural_post_dominator_; } BasicBlock* BasicBlock::immediate_dominator() { return immediate_dominator_; } -BasicBlock* BasicBlock::immediate_post_dominator() { - return immediate_post_dominator_; +BasicBlock* BasicBlock::immediate_structural_dominator() { + return immediate_structural_dominator_; +} +BasicBlock* BasicBlock::immediate_structural_post_dominator() { + return immediate_structural_post_dominator_; } void BasicBlock::RegisterSuccessors( @@ -58,6 +71,10 @@ void BasicBlock::RegisterSuccessors( for (auto& block : next_blocks) { block->predecessors_.push_back(this); successors_.push_back(block); + + // Register structural successors/predecessors too. + block->structural_predecessors_.push_back(this); + structural_successors_.push_back(block); } } @@ -67,10 +84,16 @@ bool BasicBlock::dominates(const BasicBlock& other) const { std::find(other.dom_begin(), other.dom_end(), this)); } -bool BasicBlock::postdominates(const BasicBlock& other) const { - return (this == &other) || - !(other.pdom_end() == - std::find(other.pdom_begin(), other.pdom_end(), this)); +bool BasicBlock::structurally_dominates(const BasicBlock& other) const { + return (this == &other) || !(other.structural_dom_end() == + std::find(other.structural_dom_begin(), + other.structural_dom_end(), this)); +} + +bool BasicBlock::structurally_postdominates(const BasicBlock& other) const { + return (this == &other) || !(other.structural_pdom_end() == + std::find(other.structural_pdom_begin(), + other.structural_pdom_end(), this)); } BasicBlock::DominatorIterator::DominatorIterator() : current_(nullptr) {} @@ -107,21 +130,43 @@ BasicBlock::DominatorIterator BasicBlock::dom_end() { return DominatorIterator(); } -const BasicBlock::DominatorIterator BasicBlock::pdom_begin() const { - return DominatorIterator( - this, [](const BasicBlock* b) { return b->immediate_post_dominator(); }); +const BasicBlock::DominatorIterator BasicBlock::structural_dom_begin() const { + return DominatorIterator(this, [](const BasicBlock* b) { + return b->immediate_structural_dominator(); + }); } -BasicBlock::DominatorIterator BasicBlock::pdom_begin() { - return DominatorIterator( - this, [](const BasicBlock* b) { return b->immediate_post_dominator(); }); +BasicBlock::DominatorIterator BasicBlock::structural_dom_begin() { + return DominatorIterator(this, [](const BasicBlock* b) { + return b->immediate_structural_dominator(); + }); } -const BasicBlock::DominatorIterator BasicBlock::pdom_end() const { +const BasicBlock::DominatorIterator BasicBlock::structural_dom_end() const { return DominatorIterator(); } -BasicBlock::DominatorIterator BasicBlock::pdom_end() { +BasicBlock::DominatorIterator BasicBlock::structural_dom_end() { + return DominatorIterator(); +} + +const BasicBlock::DominatorIterator BasicBlock::structural_pdom_begin() const { + return DominatorIterator(this, [](const BasicBlock* b) { + return b->immediate_structural_post_dominator(); + }); +} + +BasicBlock::DominatorIterator BasicBlock::structural_pdom_begin() { + return DominatorIterator(this, [](const BasicBlock* b) { + return b->immediate_structural_post_dominator(); + }); +} + +const BasicBlock::DominatorIterator BasicBlock::structural_pdom_end() const { + return DominatorIterator(); +} + +BasicBlock::DominatorIterator BasicBlock::structural_pdom_end() { return DominatorIterator(); } diff --git a/source/val/basic_block.h b/source/val/basic_block.h index 5af4b9e4..be5657ea 100644 --- a/source/val/basic_block.h +++ b/source/val/basic_block.h @@ -64,9 +64,32 @@ class BasicBlock { /// Returns the successors of the BasicBlock std::vector* successors() { return &successors_; } - /// Returns true if the block is reachable in the CFG + /// Returns the structural successors of the BasicBlock + std::vector* structural_predecessors() { + return &structural_predecessors_; + } + + /// Returns the structural predecessors of the BasicBlock + const std::vector* structural_predecessors() const { + return &structural_predecessors_; + } + + /// Returns the structural successors of the BasicBlock + std::vector* structural_successors() { + return &structural_successors_; + } + + /// Returns the structural predecessors of the BasicBlock + const std::vector* structural_successors() const { + return &structural_successors_; + } + + /// Returns true if the block is reachable in the CFG. bool reachable() const { return reachable_; } + /// Returns true if the block is structurally reachable in the CFG. + bool structurally_reachable() const { return structurally_reachable_; } + /// Returns true if BasicBlock is of the given type bool is_type(BlockType type) const { if (type == kBlockTypeUndefined) return type_.none(); @@ -76,6 +99,11 @@ class BasicBlock { /// Sets the reachability of the basic block in the CFG void set_reachable(bool reachability) { reachable_ = reachability; } + /// Sets the structural reachability of the basic block in the CFG + void set_structurally_reachable(bool reachability) { + structurally_reachable_ = reachability; + } + /// Sets the type of the BasicBlock void set_type(BlockType type) { if (type == kBlockTypeUndefined) @@ -84,27 +112,38 @@ class BasicBlock { type_.set(type); } - /// Sets the immedate dominator of this basic block + /// Sets the immediate dominator of this basic block /// /// @param[in] dom_block The dominator block void SetImmediateDominator(BasicBlock* dom_block); - /// Sets the immedate post dominator of this basic block + /// Sets the immediate dominator of this basic block + /// + /// @param[in] dom_block The dominator block + void SetImmediateStructuralDominator(BasicBlock* dom_block); + + /// Sets the immediate post dominator of this basic block /// /// @param[in] pdom_block The post dominator block - void SetImmediatePostDominator(BasicBlock* pdom_block); + void SetImmediateStructuralPostDominator(BasicBlock* pdom_block); - /// Returns the immedate dominator of this basic block + /// Returns the immediate dominator of this basic block BasicBlock* immediate_dominator(); - /// Returns the immedate dominator of this basic block + /// Returns the immediate dominator of this basic block const BasicBlock* immediate_dominator() const; - /// Returns the immedate post dominator of this basic block - BasicBlock* immediate_post_dominator(); + /// Returns the immediate dominator of this basic block + BasicBlock* immediate_structural_dominator(); - /// Returns the immedate post dominator of this basic block - const BasicBlock* immediate_post_dominator() const; + /// Returns the immediate dominator of this basic block + const BasicBlock* immediate_structural_dominator() const; + + /// Returns the immediate post dominator of this basic block + BasicBlock* immediate_structural_post_dominator(); + + /// Returns the immediate post dominator of this basic block + const BasicBlock* immediate_structural_post_dominator() const; /// Returns the label instruction for the block, or nullptr if not set. const Instruction* label() const { return label_; } @@ -132,9 +171,18 @@ class BasicBlock { /// Assumes dominators have been computed. bool dominates(const BasicBlock& other) const; - /// Returns true if this block postdominates the other block. - /// Assumes dominators have been computed. - bool postdominates(const BasicBlock& other) const; + /// Returns true if this block structurally dominates the other block. + /// Assumes structural dominators have been computed. + bool structurally_dominates(const BasicBlock& other) const; + + /// Returns true if this block structurally postdominates the other block. + /// Assumes structural dominators have been computed. + bool structurally_postdominates(const BasicBlock& other) const; + + void RegisterStructuralSuccessor(BasicBlock* block) { + block->structural_predecessors_.push_back(this); + structural_successors_.push_back(block); + } /// @brief A BasicBlock dominator iterator class /// @@ -191,18 +239,32 @@ class BasicBlock { /// block DominatorIterator dom_end(); + /// Returns a dominator iterator which points to the current block + const DominatorIterator structural_dom_begin() const; + + /// Returns a dominator iterator which points to the current block + DominatorIterator structural_dom_begin(); + + /// Returns a dominator iterator which points to one element past the first + /// block + const DominatorIterator structural_dom_end() const; + + /// Returns a dominator iterator which points to one element past the first + /// block + DominatorIterator structural_dom_end(); + /// Returns a post dominator iterator which points to the current block - const DominatorIterator pdom_begin() const; + const DominatorIterator structural_pdom_begin() const; /// Returns a post dominator iterator which points to the current block - DominatorIterator pdom_begin(); + DominatorIterator structural_pdom_begin(); /// Returns a post dominator iterator which points to one element past the /// last block - const DominatorIterator pdom_end() const; + const DominatorIterator structural_pdom_end() const; /// Returns a post dominator iterator which points to one element past the /// last block - DominatorIterator pdom_end(); + DominatorIterator structural_pdom_end(); private: /// Id of the BasicBlock @@ -211,8 +273,11 @@ class BasicBlock { /// Pointer to the immediate dominator of the BasicBlock BasicBlock* immediate_dominator_; - /// Pointer to the immediate dominator of the BasicBlock - BasicBlock* immediate_post_dominator_; + /// Pointer to the immediate structural dominator of the BasicBlock + BasicBlock* immediate_structural_dominator_; + + /// Pointer to the immediate structural post dominator of the BasicBlock + BasicBlock* immediate_structural_post_dominator_; /// The set of predecessors of the BasicBlock std::vector predecessors_; @@ -226,11 +291,17 @@ class BasicBlock { /// True if the block is reachable in the CFG bool reachable_; + /// True if the block is structurally reachable in the CFG + bool structurally_reachable_; + /// label of this block, if any. const Instruction* label_; /// Terminator of this block. const Instruction* terminator_; + + std::vector structural_predecessors_; + std::vector structural_successors_; }; /// @brief Returns true if the iterators point to the same element or if both diff --git a/source/val/construct.cpp b/source/val/construct.cpp index 733856cb..52e61d55 100644 --- a/source/val/construct.cpp +++ b/source/val/construct.cpp @@ -70,55 +70,45 @@ BasicBlock* Construct::exit_block() { return exit_block_; } void Construct::set_exit(BasicBlock* block) { exit_block_ = block; } -Construct::ConstructBlockSet Construct::blocks(Function* function) const { - auto header = entry_block(); - auto merge = exit_block(); - assert(header); - int header_depth = function->GetBlockDepth(const_cast(header)); - ConstructBlockSet construct_blocks; - std::unordered_set corresponding_headers; - for (auto& other : corresponding_constructs()) { - corresponding_headers.insert(other->entry_block()); +Construct::ConstructBlockSet Construct::blocks(Function* /*function*/) const { + const auto header = entry_block(); + const auto exit = exit_block(); + const bool is_continue = type() == ConstructType::kContinue; + const bool is_loop = type() == ConstructType::kLoop; + const BasicBlock* continue_header = nullptr; + if (is_loop) { + // The only corresponding construct for a loop is the continue. + continue_header = (*corresponding_constructs().begin())->entry_block(); } std::vector stack; stack.push_back(const_cast(header)); + ConstructBlockSet construct_blocks; while (!stack.empty()) { - BasicBlock* block = stack.back(); + auto* block = stack.back(); stack.pop_back(); - if (merge == block && ExitBlockIsMergeBlock()) { - // Merge block is not part of the construct. - continue; - } + if (header->structurally_dominates(*block)) { + bool include = false; + if (is_continue && exit->structurally_postdominates(*block)) { + // Continue construct include blocks dominated by the continue target + // and post-dominated by the back-edge block. + include = true; + } else if (!exit->structurally_dominates(*block)) { + // Selection and loop constructs include blocks dominated by the header + // and not dominated by the merge. + include = true; + if (is_loop && continue_header->structurally_dominates(*block)) { + // Loop constructs have an additional constraint that they do not + // include blocks dominated by the continue construct. Since all + // blocks in the continue construct are dominated by the continue + // target, we just test for dominance by continue target. + include = false; + } + } + if (include) { + if (!construct_blocks.insert(block).second) continue; - if (corresponding_headers.count(block)) { - // Entered a corresponding construct. - continue; - } - - int block_depth = function->GetBlockDepth(block); - if (block_depth < header_depth) { - // Broke to outer construct. - continue; - } - - // In a loop, the continue target is at a depth of the loop construct + 1. - // A selection construct nested directly within the loop construct is also - // at the same depth. It is valid, however, to branch directly to the - // continue target from within the selection construct. - if (block != header && block_depth == header_depth && - type() == ConstructType::kSelection && - block->is_type(kBlockTypeContinue)) { - // Continued to outer construct. - continue; - } - - if (!construct_blocks.insert(block).second) continue; - - if (merge != block) { - for (auto succ : *block->successors()) { - // All blocks in the construct must be dominated by the header. - if (header->dominates(*succ)) { + for (auto succ : *block->structural_successors()) { stack.push_back(succ); } } @@ -176,10 +166,12 @@ bool Construct::IsStructuredExit(ValidationState_t& _, BasicBlock* dest) const { for (auto& use : block->label()->uses()) { if ((use.first->opcode() == SpvOpLoopMerge || use.first->opcode() == SpvOpSelectionMerge) && - use.second == 1) + use.second == 1 && + use.first->block()->structurally_dominates(*block)) { return use.first->block(); + } } - return block->immediate_dominator(); + return block->immediate_structural_dominator(); }; bool seen_switch = false; @@ -195,7 +187,7 @@ bool Construct::IsStructuredExit(ValidationState_t& _, BasicBlock* dest) const { terminator->opcode() == SpvOpSwitch)) { auto merge_target = merge_inst->GetOperandAs(0u); auto merge_block = merge_inst->function()->GetBlock(merge_target).first; - if (merge_block->dominates(*header)) { + if (merge_block->structurally_dominates(*header)) { block = NextBlock(block); continue; } diff --git a/source/val/decoration.h b/source/val/decoration.h index ed3320f8..4f53f207 100644 --- a/source/val/decoration.h +++ b/source/val/decoration.h @@ -69,6 +69,15 @@ class Decoration { std::vector& params() { return params_; } const std::vector& params() const { return params_; } + inline bool operator<(const Decoration& rhs) const { + // Note: Sort by struct_member_index_ first, then type, so look up can be + // efficient using lower_bound() and upper_bound(). + if (struct_member_index_ < rhs.struct_member_index_) return true; + if (rhs.struct_member_index_ < struct_member_index_) return false; + if (dec_type_ < rhs.dec_type_) return true; + if (rhs.dec_type_ < dec_type_) return false; + return params_ < rhs.params_; + } inline bool operator==(const Decoration& rhs) const { return (dec_type_ == rhs.dec_type_ && params_ == rhs.params_ && struct_member_index_ == rhs.struct_member_index_); diff --git a/source/val/function.cpp b/source/val/function.cpp index 249c8664..fc7ccd06 100644 --- a/source/val/function.cpp +++ b/source/val/function.cpp @@ -57,7 +57,7 @@ spv_result_t Function::RegisterFunctionParameter(uint32_t parameter_id, uint32_t type_id) { assert(current_block_ == nullptr && "RegisterFunctionParameter can only be called when parsing the binary " - "ouside of a block"); + "outside of a block"); // TODO(umar): Validate function parameter type order and count // TODO(umar): Use these variables to validate parameter type (void)parameter_id; @@ -73,6 +73,8 @@ spv_result_t Function::RegisterLoopMerge(uint32_t merge_id, BasicBlock& continue_target_block = blocks_.at(continue_id); assert(current_block_ && "RegisterLoopMerge must be called when called within a block"); + current_block_->RegisterStructuralSuccessor(&merge_block); + current_block_->RegisterStructuralSuccessor(&continue_target_block); current_block_->set_type(kBlockTypeLoop); merge_block.set_type(kBlockTypeMerge); @@ -101,6 +103,7 @@ spv_result_t Function::RegisterSelectionMerge(uint32_t merge_id) { current_block_->set_type(kBlockTypeSelection); merge_block.set_type(kBlockTypeMerge); merge_block_header_[&merge_block] = current_block_; + current_block_->RegisterStructuralSuccessor(&merge_block); AddConstruct({ConstructType::kSelection, current_block(), &merge_block}); @@ -130,7 +133,7 @@ spv_result_t Function::RegisterBlock(uint32_t block_id, bool is_definition) { undefined_blocks_.erase(block_id); current_block_ = &inserted_block->second; ordered_blocks_.push_back(current_block_); - } else if (success) { // Block doesn't exsist but this is not a definition + } else if (success) { // Block doesn't exist but this is not a definition undefined_blocks_.insert(block_id); } @@ -251,16 +254,6 @@ Function::GetBlocksFunction Function::AugmentedCFGSuccessorsFunction() const { }; } -Function::GetBlocksFunction -Function::AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge() const { - return [this](const BasicBlock* block) { - auto where = loop_header_successors_plus_continue_target_map_.find(block); - return where == loop_header_successors_plus_continue_target_map_.end() - ? AugmentedCFGSuccessorsFunction()(block) - : &(*where).second; - }; -} - Function::GetBlocksFunction Function::AugmentedCFGPredecessorsFunction() const { return [this](const BasicBlock* block) { auto where = augmented_predecessors_map_.find(block); @@ -269,11 +262,35 @@ Function::GetBlocksFunction Function::AugmentedCFGPredecessorsFunction() const { }; } +Function::GetBlocksFunction Function::AugmentedStructuralCFGSuccessorsFunction() + const { + return [this](const BasicBlock* block) { + auto where = augmented_successors_map_.find(block); + return where == augmented_successors_map_.end() + ? block->structural_successors() + : &(*where).second; + }; +} + +Function::GetBlocksFunction +Function::AugmentedStructuralCFGPredecessorsFunction() const { + return [this](const BasicBlock* block) { + auto where = augmented_predecessors_map_.find(block); + return where == augmented_predecessors_map_.end() + ? block->structural_predecessors() + : &(*where).second; + }; +} + void Function::ComputeAugmentedCFG() { // Compute the successors of the pseudo-entry block, and // the predecessors of the pseudo exit block. - auto succ_func = [](const BasicBlock* b) { return b->successors(); }; - auto pred_func = [](const BasicBlock* b) { return b->predecessors(); }; + auto succ_func = [](const BasicBlock* b) { + return b->structural_successors(); + }; + auto pred_func = [](const BasicBlock* b) { + return b->structural_predecessors(); + }; CFA::ComputeAugmentedCFG( ordered_blocks_, &pseudo_entry_block_, &pseudo_exit_block_, &augmented_successors_map_, &augmented_predecessors_map_, succ_func, @@ -308,6 +325,9 @@ int Function::GetBlockDepth(BasicBlock* bb) { if (block_depth_.find(bb) != block_depth_.end()) { return block_depth_[bb]; } + // Avoid recursion. Something is wrong if the same block is encountered + // multiple times. + block_depth_[bb] = 0; BasicBlock* bb_dom = bb->immediate_dominator(); if (!bb_dom || bb == bb_dom) { diff --git a/source/val/function.h b/source/val/function.h index 400bb634..126b1dc7 100644 --- a/source/val/function.h +++ b/source/val/function.h @@ -73,8 +73,8 @@ class Function { /// Registers a variable in the current block /// - /// @param[in] type_id The type ID of the varaible - /// @param[in] id The ID of the varaible + /// @param[in] type_id The type ID of the variable + /// @param[in] id The ID of the variable /// @param[in] storage The storage of the variable /// @param[in] init_id The initializer ID of the variable /// @@ -184,12 +184,12 @@ class Function { std::function*(const BasicBlock*)>; /// Returns the block successors function for the augmented CFG. GetBlocksFunction AugmentedCFGSuccessorsFunction() const; - /// Like AugmentedCFGSuccessorsFunction, but also includes a forward edge from - /// a loop header block to its continue target, if they are different blocks. - GetBlocksFunction - AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge() const; /// Returns the block predecessors function for the augmented CFG. GetBlocksFunction AugmentedCFGPredecessorsFunction() const; + /// Returns the block structural successors function for the augmented CFG. + GetBlocksFunction AugmentedStructuralCFGSuccessorsFunction() const; + /// Returns the block structural predecessors function for the augmented CFG. + GetBlocksFunction AugmentedStructuralCFGPredecessorsFunction() const; /// Returns the control flow nesting depth of the given basic block. /// This function only works when you have structured control flow. @@ -197,10 +197,10 @@ class Function { /// been identified and dominators have been computed. int GetBlockDepth(BasicBlock* bb); - /// Prints a GraphViz digraph of the CFG of the current funciton + /// Prints a GraphViz digraph of the CFG of the current function void PrintDotGraph() const; - /// Prints a directed graph of the CFG of the current funciton + /// Prints a directed graph of the CFG of the current function void PrintBlocks() const; /// Registers execution model limitation such as "Feature X is only available @@ -285,7 +285,7 @@ class Function { /// The type of the return value uint32_t result_type_id_; - /// The control fo the funciton + /// The control fo the function SpvFunctionControlMask function_control_; /// The type of declaration of each function diff --git a/source/val/instruction.cpp b/source/val/instruction.cpp index b9155898..f16fcd73 100644 --- a/source/val/instruction.cpp +++ b/source/val/instruction.cpp @@ -16,6 +16,9 @@ #include +#include "source/binary.h" +#include "source/util/string_utils.h" + namespace spvtools { namespace val { @@ -41,5 +44,12 @@ bool operator==(const Instruction& lhs, uint32_t rhs) { return lhs.id() == rhs; } +template <> +std::string Instruction::GetOperandAs(size_t index) const { + const spv_parsed_operand_t& o = operands_.at(index); + assert(o.offset + o.num_words <= inst_.num_words); + return spvtools::utils::MakeString(words_.data() + o.offset, o.num_words); +} + } // namespace val } // namespace spvtools diff --git a/source/val/instruction.h b/source/val/instruction.h index 617cb066..6d1f9f4f 100644 --- a/source/val/instruction.h +++ b/source/val/instruction.h @@ -133,6 +133,9 @@ bool operator<(const Instruction& lhs, uint32_t rhs); bool operator==(const Instruction& lhs, const Instruction& rhs); bool operator==(const Instruction& lhs, uint32_t rhs); +template <> +std::string Instruction::GetOperandAs(size_t index) const; + } // namespace val } // namespace spvtools diff --git a/source/val/validate.cpp b/source/val/validate.cpp index 45b6a463..efb9225e 100644 --- a/source/val/validate.cpp +++ b/source/val/validate.cpp @@ -202,13 +202,15 @@ spv_result_t ValidateBinaryUsingContextAndValidationState( /* diagnostic = */ nullptr); // Parse the module and perform inline validation checks. These checks do - // not require the the knowledge of the whole module. + // not require the knowledge of the whole module. if (auto error = spvBinaryParse(&context, vstate, words, num_words, /*parsed_header =*/nullptr, ProcessInstruction, pDiagnostic)) { return error; } + bool has_mask_task_nv = false; + bool has_mask_task_ext = false; std::vector visited_entry_points; for (auto& instruction : vstate->ordered_instructions()) { { @@ -219,9 +221,7 @@ spv_result_t ValidateBinaryUsingContextAndValidationState( if (inst->opcode() == SpvOpEntryPoint) { const auto entry_point = inst->GetOperandAs(1); const auto execution_model = inst->GetOperandAs(0); - const char* str = reinterpret_cast( - inst->words().data() + inst->operand(2).offset); - const std::string desc_name(str); + const std::string desc_name = inst->GetOperandAs(2); ValidationState_t::EntryPointDescription desc; desc.name = desc_name; @@ -237,9 +237,8 @@ spv_result_t ValidateBinaryUsingContextAndValidationState( for (const Instruction* check_inst : visited_entry_points) { const auto check_execution_model = check_inst->GetOperandAs(0); - const char* check_str = reinterpret_cast( - check_inst->words().data() + inst->operand(2).offset); - const std::string check_name(check_str); + const std::string check_name = + check_inst->GetOperandAs(2); if (desc_name == check_name && execution_model == check_execution_model) { @@ -250,6 +249,11 @@ spv_result_t ValidateBinaryUsingContextAndValidationState( } } visited_entry_points.push_back(inst); + + has_mask_task_nv |= (execution_model == SpvExecutionModelTaskNV || + execution_model == SpvExecutionModelMeshNV); + has_mask_task_ext |= (execution_model == SpvExecutionModelTaskEXT || + execution_model == SpvExecutionModelMeshEXT); } if (inst->opcode() == SpvOpFunctionCall) { if (!vstate->in_function_body()) { @@ -296,6 +300,17 @@ spv_result_t ValidateBinaryUsingContextAndValidationState( return vstate->diag(SPV_ERROR_INVALID_LAYOUT, nullptr) << "Missing OpFunctionEnd at end of module."; + if (vstate->HasCapability(SpvCapabilityBindlessTextureNV) && + !vstate->has_samplerimage_variable_address_mode_specified()) + return vstate->diag(SPV_ERROR_INVALID_LAYOUT, nullptr) + << "Missing required OpSamplerImageAddressingModeNV instruction."; + + if (has_mask_task_ext && has_mask_task_nv) + return vstate->diag(SPV_ERROR_INVALID_LAYOUT, nullptr) + << vstate->VkErrorID(7102) + << "Module can't mix MeshEXT/TaskEXT with MeshNV/TaskNV Execution " + "Model."; + // Catch undefined forward references before performing further checks. if (auto error = ValidateForwardDecls(*vstate)) return error; @@ -348,10 +363,13 @@ spv_result_t ValidateBinaryUsingContextAndValidationState( if (auto error = NonUniformPass(*vstate, &instruction)) return error; if (auto error = LiteralsPass(*vstate, &instruction)) return error; + if (auto error = RayQueryPass(*vstate, &instruction)) return error; + if (auto error = RayTracingPass(*vstate, &instruction)) return error; + if (auto error = MeshShadingPass(*vstate, &instruction)) return error; } // Validate the preconditions involving adjacent instructions. e.g. SpvOpPhi - // must only be preceeded by SpvOpLabel, SpvOpPhi, or SpvOpLine. + // must only be preceded by SpvOpLabel, SpvOpPhi, or SpvOpLine. if (auto error = ValidateAdjacency(*vstate)) return error; if (auto error = ValidateEntryPoints(*vstate)) return error; diff --git a/source/val/validate.h b/source/val/validate.h index 3fc183de..4b953ba3 100644 --- a/source/val/validate.h +++ b/source/val/validate.h @@ -70,7 +70,7 @@ spv_result_t CheckIdDefinitionDominateUse(ValidationState_t& _); /// /// This function will iterate over all instructions and check for any required /// predecessor and/or successor instructions. e.g. SpvOpPhi must only be -/// preceeded by SpvOpLabel, SpvOpPhi, or SpvOpLine. +/// preceded by SpvOpLabel, SpvOpPhi, or SpvOpLine. /// /// @param[in] _ the validation state of the module /// @@ -197,6 +197,15 @@ spv_result_t FunctionPass(ValidationState_t& _, const Instruction* inst); /// Validates correctness of miscellaneous instructions. spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst); +/// Validates correctness of ray query instructions. +spv_result_t RayQueryPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of ray tracing instructions. +spv_result_t RayTracingPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of mesh shading instructions. +spv_result_t MeshShadingPass(ValidationState_t& _, const Instruction* inst); + /// Calculates the reachability of basic blocks. void ReachabilityPass(ValidationState_t& _); diff --git a/source/val/validate_adjacency.cpp b/source/val/validate_adjacency.cpp index 64655b0d..8e6c373e 100644 --- a/source/val/validate_adjacency.cpp +++ b/source/val/validate_adjacency.cpp @@ -60,7 +60,10 @@ spv_result_t ValidateAdjacency(ValidationState_t& _) { // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/533): We need // to discuss the location of DebugScope, DebugNoScope, DebugDeclare, // and DebugValue. - if (!spvExtInstIsDebugInfo(inst.ext_inst_type())) { + // NOTE: This does not apply to the non-semantic vulkan debug info. + if (!spvExtInstIsDebugInfo(inst.ext_inst_type()) || + inst.ext_inst_type() == + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { adjacency_status = PHI_AND_VAR_INVALID; } break; diff --git a/source/val/validate_annotation.cpp b/source/val/validate_annotation.cpp index 85d2b751..21f999b0 100644 --- a/source/val/validate_annotation.cpp +++ b/source/val/validate_annotation.cpp @@ -22,142 +22,10 @@ namespace spvtools { namespace val { namespace { -std::string LogStringForDecoration(uint32_t decoration) { - switch (decoration) { - case SpvDecorationRelaxedPrecision: - return "RelaxedPrecision"; - case SpvDecorationSpecId: - return "SpecId"; - case SpvDecorationBlock: - return "Block"; - case SpvDecorationBufferBlock: - return "BufferBlock"; - case SpvDecorationRowMajor: - return "RowMajor"; - case SpvDecorationColMajor: - return "ColMajor"; - case SpvDecorationArrayStride: - return "ArrayStride"; - case SpvDecorationMatrixStride: - return "MatrixStride"; - case SpvDecorationGLSLShared: - return "GLSLShared"; - case SpvDecorationGLSLPacked: - return "GLSLPacked"; - case SpvDecorationCPacked: - return "CPacked"; - case SpvDecorationBuiltIn: - return "BuiltIn"; - case SpvDecorationNoPerspective: - return "NoPerspective"; - case SpvDecorationFlat: - return "Flat"; - case SpvDecorationPatch: - return "Patch"; - case SpvDecorationCentroid: - return "Centroid"; - case SpvDecorationSample: - return "Sample"; - case SpvDecorationInvariant: - return "Invariant"; - case SpvDecorationRestrict: - return "Restrict"; - case SpvDecorationAliased: - return "Aliased"; - case SpvDecorationVolatile: - return "Volatile"; - case SpvDecorationConstant: - return "Constant"; - case SpvDecorationCoherent: - return "Coherent"; - case SpvDecorationNonWritable: - return "NonWritable"; - case SpvDecorationNonReadable: - return "NonReadable"; - case SpvDecorationUniform: - return "Uniform"; - case SpvDecorationSaturatedConversion: - return "SaturatedConversion"; - case SpvDecorationStream: - return "Stream"; - case SpvDecorationLocation: - return "Location"; - case SpvDecorationComponent: - return "Component"; - case SpvDecorationIndex: - return "Index"; - case SpvDecorationBinding: - return "Binding"; - case SpvDecorationDescriptorSet: - return "DescriptorSet"; - case SpvDecorationOffset: - return "Offset"; - case SpvDecorationXfbBuffer: - return "XfbBuffer"; - case SpvDecorationXfbStride: - return "XfbStride"; - case SpvDecorationFuncParamAttr: - return "FuncParamAttr"; - case SpvDecorationFPRoundingMode: - return "FPRoundingMode"; - case SpvDecorationFPFastMathMode: - return "FPFastMathMode"; - case SpvDecorationLinkageAttributes: - return "LinkageAttributes"; - case SpvDecorationNoContraction: - return "NoContraction"; - case SpvDecorationInputAttachmentIndex: - return "InputAttachmentIndex"; - case SpvDecorationAlignment: - return "Alignment"; - case SpvDecorationMaxByteOffset: - return "MaxByteOffset"; - case SpvDecorationAlignmentId: - return "AlignmentId"; - case SpvDecorationMaxByteOffsetId: - return "MaxByteOffsetId"; - case SpvDecorationNoSignedWrap: - return "NoSignedWrap"; - case SpvDecorationNoUnsignedWrap: - return "NoUnsignedWrap"; - case SpvDecorationExplicitInterpAMD: - return "ExplicitInterpAMD"; - case SpvDecorationOverrideCoverageNV: - return "OverrideCoverageNV"; - case SpvDecorationPassthroughNV: - return "PassthroughNV"; - case SpvDecorationViewportRelativeNV: - return "ViewportRelativeNV"; - case SpvDecorationSecondaryViewportRelativeNV: - return "SecondaryViewportRelativeNV"; - case SpvDecorationPerPrimitiveNV: - return "PerPrimitiveNV"; - case SpvDecorationPerViewNV: - return "PerViewNV"; - case SpvDecorationPerTaskNV: - return "PerTaskNV"; - case SpvDecorationPerVertexNV: - return "PerVertexNV"; - case SpvDecorationNonUniformEXT: - return "NonUniformEXT"; - case SpvDecorationRestrictPointerEXT: - return "RestrictPointerEXT"; - case SpvDecorationAliasedPointerEXT: - return "AliasedPointerEXT"; - case SpvDecorationHlslCounterBufferGOOGLE: - return "HlslCounterBufferGOOGLE"; - case SpvDecorationHlslSemanticGOOGLE: - return "HlslSemanticGOOGLE"; - default: - break; - } - return "Unknown"; -} - // Returns true if the decoration takes ID parameters. // TODO(dneto): This can be generated from the grammar. -bool DecorationTakesIdParameters(uint32_t type) { - switch (static_cast(type)) { +bool DecorationTakesIdParameters(SpvDecoration type) { + switch (type) { case SpvDecorationUniformId: case SpvDecorationAlignmentId: case SpvDecorationMaxByteOffsetId: @@ -169,17 +37,221 @@ bool DecorationTakesIdParameters(uint32_t type) { return false; } -spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) { - const auto decoration = inst->GetOperandAs(1); - if (decoration == SpvDecorationSpecId) { - const auto target_id = inst->GetOperandAs(0); - const auto target = _.FindDef(target_id); - if (!target || !spvOpcodeIsScalarSpecConstant(target->opcode())) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpDecorate SpecId decoration target '" - << _.getIdName(target_id) - << "' is not a scalar specialization constant."; +bool IsMemberDecorationOnly(SpvDecoration dec) { + switch (dec) { + case SpvDecorationRowMajor: + case SpvDecorationColMajor: + case SpvDecorationMatrixStride: + // SPIR-V spec bug? Offset is generated on variables when dealing with + // transform feedback. + // case SpvDecorationOffset: + return true; + default: + break; + } + return false; +} + +bool IsNotMemberDecoration(SpvDecoration dec) { + switch (dec) { + case SpvDecorationSpecId: + case SpvDecorationBlock: + case SpvDecorationBufferBlock: + case SpvDecorationArrayStride: + case SpvDecorationGLSLShared: + case SpvDecorationGLSLPacked: + case SpvDecorationCPacked: + // TODO: https://github.com/KhronosGroup/glslang/issues/703: + // glslang applies Restrict to structure members. + // case SpvDecorationRestrict: + case SpvDecorationAliased: + case SpvDecorationConstant: + case SpvDecorationUniform: + case SpvDecorationUniformId: + case SpvDecorationSaturatedConversion: + case SpvDecorationIndex: + case SpvDecorationBinding: + case SpvDecorationDescriptorSet: + case SpvDecorationFuncParamAttr: + case SpvDecorationFPRoundingMode: + case SpvDecorationFPFastMathMode: + case SpvDecorationLinkageAttributes: + case SpvDecorationNoContraction: + case SpvDecorationInputAttachmentIndex: + case SpvDecorationAlignment: + case SpvDecorationMaxByteOffset: + case SpvDecorationAlignmentId: + case SpvDecorationMaxByteOffsetId: + case SpvDecorationNoSignedWrap: + case SpvDecorationNoUnsignedWrap: + case SpvDecorationNonUniform: + case SpvDecorationRestrictPointer: + case SpvDecorationAliasedPointer: + case SpvDecorationCounterBuffer: + return true; + default: + break; + } + return false; +} + +spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec, + const Instruction* inst, + const Instruction* target) { + auto fail = [&_, dec, inst, target](uint32_t vuid) -> DiagnosticStream { + DiagnosticStream ds = std::move( + _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(vuid) << _.SpvDecorationString(dec) + << " decoration on target " << _.getIdName(target->id()) << " "); + return ds; + }; + switch (dec) { + case SpvDecorationSpecId: + if (!spvOpcodeIsScalarSpecConstant(target->opcode())) { + return fail(0) << "must be a scalar specialization constant"; + } + break; + case SpvDecorationBlock: + case SpvDecorationBufferBlock: + case SpvDecorationGLSLShared: + case SpvDecorationGLSLPacked: + case SpvDecorationCPacked: + if (target->opcode() != SpvOpTypeStruct) { + return fail(0) << "must be a structure type"; + } + break; + case SpvDecorationArrayStride: + if (target->opcode() != SpvOpTypeArray && + target->opcode() != SpvOpTypeRuntimeArray && + target->opcode() != SpvOpTypePointer) { + return fail(0) << "must be an array or pointer type"; + } + break; + case SpvDecorationBuiltIn: + if (target->opcode() != SpvOpVariable && + !spvOpcodeIsConstant(target->opcode())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "BuiltIns can only target variables, structure members or " + "constants"; + } + if (_.HasCapability(SpvCapabilityShader) && + inst->GetOperandAs(2) == SpvBuiltInWorkgroupSize) { + if (!spvOpcodeIsConstant(target->opcode())) { + return fail(0) << "must be a constant for WorkgroupSize"; + } + } else if (target->opcode() != SpvOpVariable) { + return fail(0) << "must be a variable"; + } + break; + case SpvDecorationNoPerspective: + case SpvDecorationFlat: + case SpvDecorationPatch: + case SpvDecorationCentroid: + case SpvDecorationSample: + case SpvDecorationRestrict: + case SpvDecorationAliased: + case SpvDecorationVolatile: + case SpvDecorationCoherent: + case SpvDecorationNonWritable: + case SpvDecorationNonReadable: + case SpvDecorationXfbBuffer: + case SpvDecorationXfbStride: + case SpvDecorationComponent: + case SpvDecorationStream: + case SpvDecorationRestrictPointer: + case SpvDecorationAliasedPointer: + if (target->opcode() != SpvOpVariable && + target->opcode() != SpvOpFunctionParameter) { + return fail(0) << "must be a memory object declaration"; + } + if (_.GetIdOpcode(target->type_id()) != SpvOpTypePointer) { + return fail(0) << "must be a pointer type"; + } + break; + case SpvDecorationInvariant: + case SpvDecorationConstant: + case SpvDecorationLocation: + case SpvDecorationIndex: + case SpvDecorationBinding: + case SpvDecorationDescriptorSet: + case SpvDecorationInputAttachmentIndex: + if (target->opcode() != SpvOpVariable) { + return fail(0) << "must be a variable"; + } + break; + default: + break; + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + // The following were all checked as pointer types above. + SpvStorageClass sc = SpvStorageClassUniform; + const auto type = _.FindDef(target->type_id()); + if (type && type->operands().size() > 2) { + sc = type->GetOperandAs(1); } + switch (dec) { + case SpvDecorationLocation: + case SpvDecorationComponent: + // Location is used for input, output and ray tracing stages. + if (sc != SpvStorageClassInput && sc != SpvStorageClassOutput && + sc != SpvStorageClassRayPayloadKHR && + sc != SpvStorageClassIncomingRayPayloadKHR && + sc != SpvStorageClassHitAttributeKHR && + sc != SpvStorageClassCallableDataKHR && + sc != SpvStorageClassIncomingCallableDataKHR && + sc != SpvStorageClassShaderRecordBufferKHR) { + return _.diag(SPV_ERROR_INVALID_ID, target) + << _.VkErrorID(6672) << _.SpvDecorationString(dec) + << " decoration must not be applied to this storage class"; + } + break; + case SpvDecorationIndex: + // Langauge from SPIR-V definition of Index + if (sc != SpvStorageClassOutput) { + return fail(0) << "must be in the Output storage class"; + } + break; + case SpvDecorationBinding: + case SpvDecorationDescriptorSet: + if (sc != SpvStorageClassStorageBuffer && + sc != SpvStorageClassUniform && + sc != SpvStorageClassUniformConstant) { + return fail(6491) << "must be in the StorageBuffer, Uniform, or " + "UniformConstant storage class"; + } + break; + case SpvDecorationInputAttachmentIndex: + if (sc != SpvStorageClassUniformConstant) { + return fail(6678) << "must be in the UniformConstant storage class"; + } + break; + case SpvDecorationFlat: + case SpvDecorationNoPerspective: + case SpvDecorationCentroid: + case SpvDecorationSample: + if (sc != SpvStorageClassInput && sc != SpvStorageClassOutput) { + return fail(4670) << "storage class must be Input or Output"; + } + break; + case SpvDecorationPerVertexKHR: + if (sc != SpvStorageClassInput) { + return fail(6777) << "storage class must be Input"; + } + break; + default: + break; + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) { + const auto decoration = inst->GetOperandAs(1); + const auto target_id = inst->GetOperandAs(0); + const auto target = _.FindDef(target_id); + if (!target) { + return _.diag(SPV_ERROR_INVALID_ID, inst) << "target is not defined"; } if (spvIsVulkanEnv(_.context()->target_env)) { @@ -187,7 +259,7 @@ spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) { (decoration == SpvDecorationGLSLPacked)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << _.VkErrorID(4669) << "OpDecorate decoration '" - << LogStringForDecoration(decoration) + << _.SpvDecorationString(decoration) << "' is not valid for the Vulkan execution environment."; } } @@ -197,17 +269,34 @@ spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) { << "Decorations taking ID parameters may not be used with " "OpDecorateId"; } + + if (target->opcode() != SpvOpDecorationGroup) { + if (IsMemberDecorationOnly(decoration)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.SpvDecorationString(decoration) + << " can only be applied to structure members"; + } + + if (auto error = ValidateDecorationTarget(_, decoration, inst, target)) { + return error; + } + } + // TODO: Add validations for all decorations. return SPV_SUCCESS; } spv_result_t ValidateDecorateId(ValidationState_t& _, const Instruction* inst) { - const auto decoration = inst->GetOperandAs(1); + const auto decoration = inst->GetOperandAs(1); if (!DecorationTakesIdParameters(decoration)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "Decorations that don't take ID parameters may not be used with " "OpDecorateId"; } + + // No member decorations take id parameters, so we don't bother checking if + // we are using a member only decoration here. + // TODO: Add validations for these decorations. // UniformId is covered elsewhere. return SPV_SUCCESS; @@ -219,8 +308,8 @@ spv_result_t ValidateMemberDecorate(ValidationState_t& _, const auto struct_type = _.FindDef(struct_type_id); if (!struct_type || SpvOpTypeStruct != struct_type->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpMemberDecorate Structure type '" - << _.getIdName(struct_type_id) << "' is not a struct type."; + << "OpMemberDecorate Structure type " + << _.getIdName(struct_type_id) << " is not a struct type."; } const auto member = inst->GetOperandAs(1); const auto member_count = @@ -234,6 +323,13 @@ spv_result_t ValidateMemberDecorate(ValidationState_t& _, << " members. Largest valid index is " << member_count - 1 << "."; } + const auto decoration = inst->GetOperandAs(2); + if (IsNotMemberDecoration(decoration)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.SpvDecorationString(decoration) + << " cannot be applied to structure members"; + } + return SPV_SUCCESS; } @@ -262,17 +358,16 @@ spv_result_t ValidateGroupDecorate(ValidationState_t& _, auto decoration_group = _.FindDef(decoration_group_id); if (!decoration_group || SpvOpDecorationGroup != decoration_group->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpGroupDecorate Decoration group '" - << _.getIdName(decoration_group_id) - << "' is not a decoration group."; + << "OpGroupDecorate Decoration group " + << _.getIdName(decoration_group_id) << " is not a decoration group."; } for (unsigned i = 1; i < inst->operands().size(); ++i) { auto target_id = inst->GetOperandAs(i); auto target = _.FindDef(target_id); if (!target || target->opcode() == SpvOpDecorationGroup) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpGroupDecorate may not target OpDecorationGroup '" - << _.getIdName(target_id) << "'"; + << "OpGroupDecorate may not target OpDecorationGroup " + << _.getIdName(target_id); } } return SPV_SUCCESS; @@ -284,9 +379,8 @@ spv_result_t ValidateGroupMemberDecorate(ValidationState_t& _, const auto decoration_group = _.FindDef(decoration_group_id); if (!decoration_group || SpvOpDecorationGroup != decoration_group->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpGroupMemberDecorate Decoration group '" - << _.getIdName(decoration_group_id) - << "' is not a decoration group."; + << "OpGroupMemberDecorate Decoration group " + << _.getIdName(decoration_group_id) << " is not a decoration group."; } // Grammar checks ensures that the number of arguments to this instruction // is an odd number: 1 decoration group + (id,literal) pairs. @@ -296,8 +390,8 @@ spv_result_t ValidateGroupMemberDecorate(ValidationState_t& _, auto struct_instr = _.FindDef(struct_id); if (!struct_instr || SpvOpTypeStruct != struct_instr->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpGroupMemberDecorate Structure type '" - << _.getIdName(struct_id) << "' is not a struct type."; + << "OpGroupMemberDecorate Structure type " + << _.getIdName(struct_id) << " is not a struct type."; } const uint32_t num_struct_members = static_cast(struct_instr->words().size() - 2); @@ -353,7 +447,7 @@ spv_result_t RegisterDecorations(ValidationState_t& _, // Word 1 is the group . All subsequent words are target s that // are going to be decorated with the decorations. const uint32_t decoration_group_id = inst->word(1); - std::vector& group_decorations = + std::set& group_decorations = _.id_decorations(decoration_group_id); for (size_t i = 2; i < inst->words().size(); ++i) { const uint32_t target_id = inst->word(i); @@ -367,7 +461,7 @@ spv_result_t RegisterDecorations(ValidationState_t& _, // pairs. All decorations of the group should be applied to all the struct // members that are specified in the instructions. const uint32_t decoration_group_id = inst->word(1); - std::vector& group_decorations = + std::set& group_decorations = _.id_decorations(decoration_group_id); // Grammar checks ensures that the number of arguments to this instruction // is an odd number: 1 decoration group + (id,literal) pairs. diff --git a/source/val/validate_arithmetics.cpp b/source/val/validate_arithmetics.cpp index 433330d7..bae9b5dc 100644 --- a/source/val/validate_arithmetics.cpp +++ b/source/val/validate_arithmetics.cpp @@ -155,7 +155,7 @@ spv_result_t ArithmeticsPass(ValidationState_t& _, const Instruction* inst) { first_vector_num_components = num_components; } else if (num_components != first_vector_num_components) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected operands to have the same number of componenets: " + << "Expected operands to have the same number of components: " << spvOpcodeString(opcode); } } diff --git a/source/val/validate_atomics.cpp b/source/val/validate_atomics.cpp index fa53ca1f..bf565c31 100644 --- a/source/val/validate_atomics.cpp +++ b/source/val/validate_atomics.cpp @@ -39,7 +39,8 @@ bool IsStorageClassAllowedByUniversalRules(uint32_t storage_class) { case SpvStorageClassAtomicCounter: case SpvStorageClassImage: case SpvStorageClassFunction: - case SpvStorageClassPhysicalStorageBufferEXT: + case SpvStorageClassPhysicalStorageBuffer: + case SpvStorageClassTaskPayloadWorkgroupEXT: return true; break; default: @@ -184,7 +185,7 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) { } // Can't use result_type because OpAtomicStore doesn't have a result - if (_.GetBitWidth(data_type) == 64 && _.IsIntScalarType(data_type) && + if ( _.IsIntScalarType(data_type) &&_.GetBitWidth(data_type) == 64 && !_.HasCapability(SpvCapabilityInt64Atomics)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << spvOpcodeString(opcode) @@ -206,12 +207,13 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) { (storage_class != SpvStorageClassStorageBuffer) && (storage_class != SpvStorageClassWorkgroup) && (storage_class != SpvStorageClassImage) && - (storage_class != SpvStorageClassPhysicalStorageBuffer)) { + (storage_class != SpvStorageClassPhysicalStorageBuffer) && + (storage_class != SpvStorageClassTaskPayloadWorkgroupEXT)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << _.VkErrorID(4686) << spvOpcodeString(opcode) << ": Vulkan spec only allows storage classes for atomic to " - "be: Uniform, Workgroup, Image, StorageBuffer, or " - "PhysicalStorageBuffer."; + "be: Uniform, Workgroup, Image, StorageBuffer, " + "PhysicalStorageBuffer or TaskPayloadWorkgroupEXT."; } } else if (storage_class == SpvStorageClassFunction) { return _.diag(SPV_ERROR_INVALID_DATA, inst) @@ -222,6 +224,13 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) { if (opcode == SpvOpAtomicFAddEXT) { // result type being float checked already + if ((_.GetBitWidth(result_type) == 16) && + (!_.HasCapability(SpvCapabilityAtomicFloat16AddEXT))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float add atomics require the AtomicFloat32AddEXT " + "capability"; + } if ((_.GetBitWidth(result_type) == 32) && (!_.HasCapability(SpvCapabilityAtomicFloat32AddEXT))) { return _.diag(SPV_ERROR_INVALID_DATA, inst) diff --git a/source/val/validate_barriers.cpp b/source/val/validate_barriers.cpp index 3a9e3e7c..03225d86 100644 --- a/source/val/validate_barriers.cpp +++ b/source/val/validate_barriers.cpp @@ -50,7 +50,8 @@ spv_result_t BarriersPass(ValidationState_t& _, const Instruction* inst) { *message = "OpControlBarrier requires one of the following " "Execution " - "Models: TessellationControl, GLCompute or Kernel"; + "Models: TessellationControl, GLCompute, Kernel, " + "MeshNV or TaskNV"; } return false; } diff --git a/source/val/validate_bitwise.cpp b/source/val/validate_bitwise.cpp index d46b3fca..e6e97c4a 100644 --- a/source/val/validate_bitwise.cpp +++ b/source/val/validate_bitwise.cpp @@ -14,16 +14,48 @@ // Validates correctness of bitwise instructions. -#include "source/val/validate.h" - #include "source/diagnostic.h" #include "source/opcode.h" +#include "source/spirv_target_env.h" #include "source/val/instruction.h" +#include "source/val/validate.h" #include "source/val/validation_state.h" namespace spvtools { namespace val { +// Validates when base and result need to be the same type +spv_result_t ValidateBaseType(ValidationState_t& _, const Instruction* inst, + const uint32_t base_type) { + const SpvOp opcode = inst->opcode(); + + if (!_.IsIntScalarType(base_type) && !_.IsIntVectorType(base_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4781) + << "Expected int scalar or vector type for Base operand: " + << spvOpcodeString(opcode); + } + + // Vulkan has a restriction to 32 bit for base + if (spvIsVulkanEnv(_.context()->target_env)) { + if (_.GetBitWidth(base_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4781) + << "Expected 32-bit int type for Base operand: " + << spvOpcodeString(opcode); + } + } + + // OpBitCount just needs same number of components + if (base_type != inst->type_id() && opcode != SpvOpBitCount) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Base Type to be equal to Result Type: " + << spvOpcodeString(opcode); + } + + return SPV_SUCCESS; +} + // Validates correctness of bitwise instructions. spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) { const SpvOp opcode = inst->opcode(); @@ -109,20 +141,14 @@ spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) { } case SpvOpBitFieldInsert: { - if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected int scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - const uint32_t base_type = _.GetOperandTypeId(inst, 2); const uint32_t insert_type = _.GetOperandTypeId(inst, 3); const uint32_t offset_type = _.GetOperandTypeId(inst, 4); const uint32_t count_type = _.GetOperandTypeId(inst, 5); - if (base_type != result_type) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Base Type to be equal to Result Type: " - << spvOpcodeString(opcode); + if (spv_result_t error = ValidateBaseType(_, inst, base_type)) { + return error; + } if (insert_type != result_type) return _.diag(SPV_ERROR_INVALID_DATA, inst) @@ -143,19 +169,13 @@ spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) { case SpvOpBitFieldSExtract: case SpvOpBitFieldUExtract: { - if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected int scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - const uint32_t base_type = _.GetOperandTypeId(inst, 2); const uint32_t offset_type = _.GetOperandTypeId(inst, 3); const uint32_t count_type = _.GetOperandTypeId(inst, 4); - if (base_type != result_type) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Base Type to be equal to Result Type: " - << spvOpcodeString(opcode); + if (spv_result_t error = ValidateBaseType(_, inst, base_type)) { + return error; + } if (!offset_type || !_.IsIntScalarType(offset_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) @@ -170,17 +190,12 @@ spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) { } case SpvOpBitReverse: { - if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected int scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - const uint32_t base_type = _.GetOperandTypeId(inst, 2); - if (base_type != result_type) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Base Type to be equal to Result Type: " - << spvOpcodeString(opcode); + if (spv_result_t error = ValidateBaseType(_, inst, base_type)) { + return error; + } + break; } @@ -191,15 +206,13 @@ spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) { << spvOpcodeString(opcode); const uint32_t base_type = _.GetOperandTypeId(inst, 2); - if (!base_type || - (!_.IsIntScalarType(base_type) && !_.IsIntVectorType(base_type))) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Base Type to be int scalar or vector: " - << spvOpcodeString(opcode); - const uint32_t base_dimension = _.GetDimension(base_type); const uint32_t result_dimension = _.GetDimension(result_type); + if (spv_result_t error = ValidateBaseType(_, inst, base_type)) { + return error; + } + if (base_dimension != result_dimension) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Base dimension to be equal to Result Type " diff --git a/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp index 9c737ec0..6f4b0f9c 100644 --- a/source/val/validate_builtins.cpp +++ b/source/val/validate_builtins.cpp @@ -120,7 +120,7 @@ typedef enum VUIDError_ { VUIDErrorMax, } VUIDError; -const static uint32_t NumVUIDBuiltins = 33; +const static uint32_t NumVUIDBuiltins = 36; typedef struct { SpvBuiltIn builtIn; @@ -162,6 +162,9 @@ std::array builtinVUIDInfo = {{ {SpvBuiltInFragSizeEXT, {4220, 4221, 4222}}, {SpvBuiltInFragStencilRefEXT, {4223, 4224, 4225}}, {SpvBuiltInFullyCoveredEXT, {4232, 4233, 4234}}, + {SpvBuiltInCullMaskKHR, {6735, 6736, 6737}}, + {SpvBuiltInBaryCoordKHR, {4154, 4155, 4156}}, + {SpvBuiltInBaryCoordNoPerspKHR, {4160, 4161, 4162}}, // clang-format off } }; @@ -208,6 +211,7 @@ bool IsExecutionModelValidForRtBuiltIn(SpvBuiltIn builtin, case SpvBuiltInRayTmaxKHR: case SpvBuiltInWorldRayDirectionKHR: case SpvBuiltInWorldRayOriginKHR: + case SpvBuiltInCullMaskKHR: switch (stage) { case SpvExecutionModelIntersectionKHR: case SpvExecutionModelAnyHitKHR: @@ -329,9 +333,11 @@ class BuiltInsValidator { // Used for GlobalInvocationId, LocalInvocationId, NumWorkgroups, WorkgroupId. spv_result_t ValidateComputeShaderI32Vec3InputAtDefinition( const Decoration& decoration, const Instruction& inst); - spv_result_t ValidateSMBuiltinsAtDefinition(const Decoration& decoration, + spv_result_t ValidateNVSMOrARMCoreBuiltinsAtDefinition(const Decoration& decoration, const Instruction& inst); - + // Used for BaryCoord, BaryCoordNoPersp. + spv_result_t ValidateFragmentShaderF32Vec3InputAtDefinition( + const Decoration& decoration, const Instruction& inst); // Used for SubgroupEqMask, SubgroupGeMask, SubgroupGtMask, SubgroupLtMask, // SubgroupLeMask. spv_result_t ValidateI32Vec4InputAtDefinition(const Decoration& decoration, @@ -509,13 +515,20 @@ class BuiltInsValidator { const Decoration& decoration, const Instruction& built_in_inst, const Instruction& referenced_inst, const Instruction& referenced_from_inst); + + // Used for BaryCoord, BaryCoordNoPersp. + spv_result_t ValidateFragmentShaderF32Vec3InputAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + // Used for SubgroupId and NumSubgroups. spv_result_t ValidateComputeI32InputAtReference( const Decoration& decoration, const Instruction& built_in_inst, const Instruction& referenced_inst, const Instruction& referenced_from_inst); - spv_result_t ValidateSMBuiltinsAtReference( + spv_result_t ValidateNVSMOrARMCoreBuiltinsAtReference( const Decoration& decoration, const Instruction& built_in_inst, const Instruction& referenced_inst, const Instruction& referenced_from_inst); @@ -1166,9 +1179,16 @@ spv_result_t BuiltInsValidator::ValidateClipOrCullDistanceAtReference( &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid, "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be " "used for variables with Input storage class if execution model is " - "Vertex.", + "MeshNV.", SpvExecutionModelMeshNV, decoration, built_in_inst, referenced_from_inst, std::placeholders::_1)); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid, + "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be " + "used for variables with Input storage class if execution model is " + "MeshEXT.", + SpvExecutionModelMeshEXT, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); } if (storage_class == SpvStorageClassOutput) { @@ -1211,7 +1231,8 @@ spv_result_t BuiltInsValidator::ValidateClipOrCullDistanceAtReference( case SpvExecutionModelTessellationControl: case SpvExecutionModelTessellationEvaluation: case SpvExecutionModelGeometry: - case SpvExecutionModelMeshNV: { + case SpvExecutionModelMeshNV: + case SpvExecutionModelMeshEXT: { if (decoration.struct_member_index() != Decoration::kInvalidMember) { // The outer level of array is applied on the variable. if (spv_result_t error = ValidateF32Arr( @@ -1843,7 +1864,8 @@ spv_result_t BuiltInsValidator::ValidatePointSizeAtReference( case SpvExecutionModelTessellationControl: case SpvExecutionModelTessellationEvaluation: case SpvExecutionModelGeometry: - case SpvExecutionModelMeshNV: { + case SpvExecutionModelMeshNV: + case SpvExecutionModelMeshEXT: { // PointSize can be a per-vertex variable for tessellation control, // tessellation evaluation and geometry shader stages. In such cases // variables will have an array of 32-bit floats. @@ -1944,6 +1966,13 @@ spv_result_t BuiltInsValidator::ValidatePositionAtReference( "with Input storage class if execution model is MeshNV.", SpvExecutionModelMeshNV, decoration, built_in_inst, referenced_from_inst, std::placeholders::_1)); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4319, + "Vulkan spec doesn't allow BuiltIn Position to be used " + "for variables " + "with Input storage class if execution model is MeshEXT.", + SpvExecutionModelMeshEXT, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); } for (const SpvExecutionModel execution_model : execution_models_) { @@ -1967,7 +1996,8 @@ spv_result_t BuiltInsValidator::ValidatePositionAtReference( case SpvExecutionModelGeometry: case SpvExecutionModelTessellationControl: case SpvExecutionModelTessellationEvaluation: - case SpvExecutionModelMeshNV: { + case SpvExecutionModelMeshNV: + case SpvExecutionModelMeshEXT: { // Position can be a per-vertex variable for tessellation control, // tessellation evaluation, geometry and mesh shader stages. In such // cases variables will have an array of 4-component 32-bit float @@ -2138,6 +2168,7 @@ spv_result_t BuiltInsValidator::ValidatePrimitiveIdAtReference( case SpvExecutionModelTessellationEvaluation: case SpvExecutionModelGeometry: case SpvExecutionModelMeshNV: + case SpvExecutionModelMeshEXT: case SpvExecutionModelIntersectionKHR: case SpvExecutionModelAnyHitKHR: case SpvExecutionModelClosestHitKHR: { @@ -2150,9 +2181,8 @@ spv_result_t BuiltInsValidator::ValidatePrimitiveIdAtReference( << _.VkErrorID(4330) << "Vulkan spec allows BuiltIn PrimitiveId to be used only " "with Fragment, TessellationControl, " - "TessellationEvaluation, Geometry, MeshNV, " - "IntersectionKHR, " - "AnyHitKHR, and ClosestHitKHR execution models. " + "TessellationEvaluation, Geometry, MeshNV, MeshEXT, " + "IntersectionKHR, AnyHitKHR, and ClosestHitKHR execution models. " << GetReferenceDesc(decoration, built_in_inst, referenced_inst, referenced_from_inst, execution_model); } @@ -2700,7 +2730,8 @@ spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference( assert(function_id_ == 0); for (const auto em : {SpvExecutionModelVertex, SpvExecutionModelTessellationEvaluation, - SpvExecutionModelGeometry, SpvExecutionModelMeshNV}) { + SpvExecutionModelGeometry, SpvExecutionModelMeshNV, + SpvExecutionModelMeshEXT}) { id_to_at_reference_checks_[referenced_from_inst.id()].push_back( std::bind(&BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, ((operand == SpvBuiltInLayer) ? 4274 : 4406), @@ -2708,7 +2739,7 @@ spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference( "ViewportIndex to be " "used for variables with Input storage class if " "execution model is Vertex, TessellationEvaluation, " - "Geometry, or MeshNV.", + "Geometry, MeshNV or MeshEXT.", em, decoration, built_in_inst, referenced_from_inst, std::placeholders::_1)); } @@ -2733,6 +2764,7 @@ spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference( case SpvExecutionModelGeometry: case SpvExecutionModelFragment: case SpvExecutionModelMeshNV: + case SpvExecutionModelMeshEXT: // Ok. break; case SpvExecutionModelVertex: @@ -2788,6 +2820,80 @@ spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference( return SPV_SUCCESS; } +spv_result_t BuiltInsValidator::ValidateFragmentShaderF32Vec3InputAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]); + if (spv_result_t error = ValidateF32Vec( + decoration, inst, 3, + [this, &inst, builtin](const std::string& message) -> spv_result_t { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + builtin) + << " variable needs to be a 3-component 32-bit float " + "vector. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateFragmentShaderF32Vec3InputAtReference(decoration, inst, inst, + inst); +} + +spv_result_t BuiltInsValidator::ValidateFragmentShaderF32Vec3InputAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]); + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelFragment) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin) + << " to be used only with Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateFragmentShaderF32Vec3InputAtReference, this, + decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtDefinition( const Decoration& decoration, const Instruction& inst) { if (spvIsVulkanEnv(_.context()->target_env)) { @@ -2838,7 +2944,10 @@ spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference( for (const SpvExecutionModel execution_model : execution_models_) { bool has_vulkan_model = execution_model == SpvExecutionModelGLCompute || execution_model == SpvExecutionModelTaskNV || - execution_model == SpvExecutionModelMeshNV; + execution_model == SpvExecutionModelMeshNV || + execution_model == SpvExecutionModelTaskEXT || + execution_model == SpvExecutionModelMeshEXT; + if (spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) { uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel); return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) @@ -2846,7 +2955,8 @@ spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference( << spvLogStringForEnv(_.context()->target_env) << " spec allows BuiltIn " << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin) - << " to be used only with GLCompute execution model. " + << " to be used only with GLCompute, MeshNV, TaskNV, MeshEXT or" + << " TaskEXT execution model. " << GetReferenceDesc(decoration, built_in_inst, referenced_inst, referenced_from_inst, execution_model); } @@ -2920,7 +3030,9 @@ spv_result_t BuiltInsValidator::ValidateComputeI32InputAtReference( for (const SpvExecutionModel execution_model : execution_models_) { bool has_vulkan_model = execution_model == SpvExecutionModelGLCompute || execution_model == SpvExecutionModelTaskNV || - execution_model == SpvExecutionModelMeshNV; + execution_model == SpvExecutionModelMeshNV || + execution_model == SpvExecutionModelTaskEXT || + execution_model == SpvExecutionModelMeshEXT; if (spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) { uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel); return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) @@ -2928,7 +3040,8 @@ spv_result_t BuiltInsValidator::ValidateComputeI32InputAtReference( << spvLogStringForEnv(_.context()->target_env) << " spec allows BuiltIn " << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin) - << " to be used only with GLCompute execution model. " + << " to be used only with GLCompute, MeshNV, TaskNV, MeshEXT or " + << "TaskEXT execution model. " << GetReferenceDesc(decoration, built_in_inst, referenced_inst, referenced_from_inst, execution_model); } @@ -3070,14 +3183,19 @@ spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtReference( const Instruction& referenced_from_inst) { if (spvIsVulkanEnv(_.context()->target_env)) { for (const SpvExecutionModel execution_model : execution_models_) { - if (execution_model != SpvExecutionModelGLCompute) { + if (execution_model != SpvExecutionModelGLCompute && + execution_model != SpvExecutionModelTaskNV && + execution_model != SpvExecutionModelMeshNV && + execution_model != SpvExecutionModelTaskEXT && + execution_model != SpvExecutionModelMeshEXT) { return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) << _.VkErrorID(4425) << spvLogStringForEnv(_.context()->target_env) << " spec allows BuiltIn " << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0]) - << " to be used only with GLCompute execution model. " + << " to be used only with GLCompute, MeshNV, TaskNV, MeshEXT or " + << "TaskEXT execution model. " << GetReferenceDesc(decoration, built_in_inst, referenced_inst, referenced_from_inst, execution_model); } @@ -3208,12 +3326,15 @@ spv_result_t BuiltInsValidator::ValidateDrawIndexAtReference( for (const SpvExecutionModel execution_model : execution_models_) { if (execution_model != SpvExecutionModelVertex && execution_model != SpvExecutionModelMeshNV && - execution_model != SpvExecutionModelTaskNV) { + execution_model != SpvExecutionModelTaskNV && + execution_model != SpvExecutionModelMeshEXT && + execution_model != SpvExecutionModelTaskEXT) { return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) << _.VkErrorID(4207) << "Vulkan spec allows BuiltIn " << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, operand) - << " to be used only with Vertex, MeshNV, or TaskNV execution " + << " to be used only with Vertex, MeshNV, TaskNV , MeshEXT or" + << " TaskEXT execution " "model. " << GetReferenceDesc(decoration, built_in_inst, referenced_inst, referenced_from_inst, execution_model); @@ -3628,7 +3749,7 @@ spv_result_t BuiltInsValidator::ValidateFullyCoveredAtReference( return SPV_SUCCESS; } -spv_result_t BuiltInsValidator::ValidateSMBuiltinsAtDefinition( +spv_result_t BuiltInsValidator::ValidateNVSMOrARMCoreBuiltinsAtDefinition( const Decoration& decoration, const Instruction& inst) { if (spvIsVulkanEnv(_.context()->target_env)) { if (spv_result_t error = ValidateI32( @@ -3649,10 +3770,10 @@ spv_result_t BuiltInsValidator::ValidateSMBuiltinsAtDefinition( } // Seed at reference checks with this built-in. - return ValidateSMBuiltinsAtReference(decoration, inst, inst, inst); + return ValidateNVSMOrARMCoreBuiltinsAtReference(decoration, inst, inst, inst); } -spv_result_t BuiltInsValidator::ValidateSMBuiltinsAtReference( +spv_result_t BuiltInsValidator::ValidateNVSMOrARMCoreBuiltinsAtReference( const Decoration& decoration, const Instruction& built_in_inst, const Instruction& referenced_inst, const Instruction& referenced_from_inst) { @@ -3676,7 +3797,7 @@ spv_result_t BuiltInsValidator::ValidateSMBuiltinsAtReference( if (function_id_ == 0) { // Propagate this rule to all dependant ids in the global scope. id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( - &BuiltInsValidator::ValidateSMBuiltinsAtReference, this, decoration, + &BuiltInsValidator::ValidateNVSMOrARMCoreBuiltinsAtReference, this, decoration, built_in_inst, referenced_from_inst, std::placeholders::_1)); } @@ -3729,6 +3850,7 @@ spv_result_t BuiltInsValidator::ValidatePrimitiveShadingRateAtReference( case SpvExecutionModelVertex: case SpvExecutionModelGeometry: case SpvExecutionModelMeshNV: + case SpvExecutionModelMeshEXT: break; default: { return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) @@ -3849,6 +3971,7 @@ spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtDefinition( case SpvBuiltInInstanceId: case SpvBuiltInRayGeometryIndexKHR: case SpvBuiltInIncomingRayFlagsKHR: + case SpvBuiltInCullMaskKHR: // i32 scalar if (spv_result_t error = ValidateI32( decoration, inst, @@ -3991,14 +4114,6 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition( const Decoration& decoration, const Instruction& inst) { const SpvBuiltIn label = SpvBuiltIn(decoration.params()[0]); - // Builtins can only be applied to variables, structures or constants. - auto target_opcode = inst.opcode(); - if (target_opcode != SpvOpTypeStruct && target_opcode != SpvOpVariable && - !spvOpcodeIsConstant(target_opcode)) { - return _.diag(SPV_ERROR_INVALID_DATA, &inst) - << "BuiltIns can only target variables, structs or constants"; - } - if (!spvIsVulkanEnv(_.context()->target_env)) { // Early return. All currently implemented rules are based on Vulkan spec. // @@ -4033,6 +4148,10 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition( case SpvBuiltInWorkgroupId: { return ValidateComputeShaderI32Vec3InputAtDefinition(decoration, inst); } + case SpvBuiltInBaryCoordKHR: + case SpvBuiltInBaryCoordNoPerspKHR: { + return ValidateFragmentShaderF32Vec3InputAtDefinition(decoration, inst); + } case SpvBuiltInHelperInvocation: { return ValidateHelperInvocationAtDefinition(decoration, inst); } @@ -4106,11 +4225,16 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition( case SpvBuiltInLocalInvocationIndex: { return ValidateLocalInvocationIndexAtDefinition(decoration, inst); } + case SpvBuiltInCoreIDARM: + case SpvBuiltInCoreCountARM: + case SpvBuiltInCoreMaxIDARM: + case SpvBuiltInWarpIDARM: + case SpvBuiltInWarpMaxIDARM: case SpvBuiltInWarpsPerSMNV: case SpvBuiltInSMCountNV: case SpvBuiltInWarpIDNV: case SpvBuiltInSMIDNV: { - return ValidateSMBuiltinsAtDefinition(decoration, inst); + return ValidateNVSMOrARMCoreBuiltinsAtDefinition(decoration, inst); } case SpvBuiltInBaseInstance: case SpvBuiltInBaseVertex: { @@ -4157,49 +4281,19 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition( case SpvBuiltInObjectToWorldKHR: // alias SpvBuiltInObjectToWorldNV case SpvBuiltInWorldToObjectKHR: // alias SpvBuiltInWorldToObjectNV case SpvBuiltInIncomingRayFlagsKHR: // alias SpvBuiltInIncomingRayFlagsNV - case SpvBuiltInRayGeometryIndexKHR: { // NOT present in NV + case SpvBuiltInRayGeometryIndexKHR: // NOT present in NV + case SpvBuiltInCullMaskKHR: { return ValidateRayTracingBuiltinsAtDefinition(decoration, inst); } - case SpvBuiltInWorkDim: - case SpvBuiltInGlobalSize: - case SpvBuiltInEnqueuedWorkgroupSize: - case SpvBuiltInGlobalOffset: - case SpvBuiltInGlobalLinearId: - case SpvBuiltInSubgroupMaxSize: - case SpvBuiltInNumEnqueuedSubgroups: - case SpvBuiltInBaryCoordNoPerspAMD: - case SpvBuiltInBaryCoordNoPerspCentroidAMD: - case SpvBuiltInBaryCoordNoPerspSampleAMD: - case SpvBuiltInBaryCoordSmoothAMD: - case SpvBuiltInBaryCoordSmoothCentroidAMD: - case SpvBuiltInBaryCoordSmoothSampleAMD: - case SpvBuiltInBaryCoordPullModelAMD: - case SpvBuiltInViewportMaskNV: - case SpvBuiltInSecondaryPositionNV: - case SpvBuiltInSecondaryViewportMaskNV: - case SpvBuiltInPositionPerViewNV: - case SpvBuiltInViewportMaskPerViewNV: - case SpvBuiltInMax: - case SpvBuiltInTaskCountNV: - case SpvBuiltInPrimitiveCountNV: - case SpvBuiltInPrimitiveIndicesNV: - case SpvBuiltInClipDistancePerViewNV: - case SpvBuiltInCullDistancePerViewNV: - case SpvBuiltInLayerPerViewNV: - case SpvBuiltInMeshViewCountNV: - case SpvBuiltInMeshViewIndicesNV: - case SpvBuiltInBaryCoordNV: - case SpvBuiltInBaryCoordNoPerspNV: - case SpvBuiltInCurrentRayTimeNV: - // No validation rules (for the moment). - break; - case SpvBuiltInPrimitiveShadingRateKHR: { return ValidatePrimitiveShadingRateAtDefinition(decoration, inst); } case SpvBuiltInShadingRateKHR: { return ValidateShadingRateAtDefinition(decoration, inst); } + default: + // No validation rules (for the moment). + break; } return SPV_SUCCESS; } diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp index 36f632a8..cc0b999f 100644 --- a/source/val/validate_cfg.cpp +++ b/source/val/validate_cfg.cpp @@ -27,6 +27,7 @@ #include "source/cfa.h" #include "source/opcode.h" +#include "source/spirv_constant.h" #include "source/spirv_target_env.h" #include "source/spirv_validator_options.h" #include "source/val/basic_block.h" @@ -54,8 +55,7 @@ spv_result_t ValidatePhi(ValidationState_t& _, const Instruction* inst) { } if (_.IsPointerType(inst->type_id()) && _.addressing_model() == SpvAddressingModelLogical) { - if (!_.features().variable_pointers && - !_.features().variable_pointers_storage_buffer) { + if (!_.features().variable_pointers) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Using pointers with OpPhi requires capability " << "VariablePointers or VariablePointersStorageBuffer"; @@ -66,7 +66,8 @@ spv_result_t ValidatePhi(ValidationState_t& _, const Instruction* inst) { assert(type_inst); const SpvOp type_opcode = type_inst->opcode(); - if (!_.options()->before_hlsl_legalization) { + if (!_.options()->before_hlsl_legalization && + !_.HasCapability(SpvCapabilityBindlessTextureNV)) { if (type_opcode == SpvOpTypeSampledImage || (_.HasCapability(SpvCapabilityShader) && (type_opcode == SpvOpTypeImage || type_opcode == SpvOpTypeSampler))) { @@ -191,6 +192,12 @@ spv_result_t ValidateBranchConditional(ValidationState_t& _, "ID of an OpLabel instruction"; } + if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 6) && true_id == false_id) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "In SPIR-V 1.6 or later, True Label and False Label must be " + "different labels"; + } + return SPV_SUCCESS; } @@ -199,6 +206,18 @@ spv_result_t ValidateSwitch(ValidationState_t& _, const Instruction* inst) { // At least two operands (selector, default), any more than that are // literal/target. + const auto sel_type_id = _.GetOperandTypeId(inst, 0); + if (!_.IsIntScalarType(sel_type_id)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Selector type must be OpTypeInt"; + } + + const auto default_label = _.FindDef(inst->GetOperandAs(1)); + if (default_label->opcode() != SpvOpLabel) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Default must be an OpLabel instruction"; + } + // target operands must be OpLabel for (size_t i = 2; i < num_operands; i += 2) { // literal, id @@ -220,27 +239,23 @@ spv_result_t ValidateReturnValue(ValidationState_t& _, const auto value = _.FindDef(value_id); if (!value || !value->type_id()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpReturnValue Value '" << _.getIdName(value_id) - << "' does not represent a value."; + << "OpReturnValue Value " << _.getIdName(value_id) + << " does not represent a value."; } auto value_type = _.FindDef(value->type_id()); if (!value_type || SpvOpTypeVoid == value_type->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpReturnValue value's type '" - << _.getIdName(value->type_id()) << "' is missing or void."; + << "OpReturnValue value's type " + << _.getIdName(value->type_id()) << " is missing or void."; } - const bool uses_variable_pointer = - _.features().variable_pointers || - _.features().variable_pointers_storage_buffer; - if (_.addressing_model() == SpvAddressingModelLogical && - SpvOpTypePointer == value_type->opcode() && !uses_variable_pointer && - !_.options()->relax_logical_pointer) { + SpvOpTypePointer == value_type->opcode() && + !_.features().variable_pointers && !_.options()->relax_logical_pointer) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpReturnValue value's type '" + << "OpReturnValue value's type " << _.getIdName(value->type_id()) - << "' is a pointer, which is invalid in the Logical addressing " + << " is a pointer, which is invalid in the Logical addressing " "model."; } @@ -248,8 +263,8 @@ spv_result_t ValidateReturnValue(ValidationState_t& _, const auto return_type = _.FindDef(function->GetResultTypeId()); if (!return_type || return_type->id() != value_type->id()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpReturnValue Value '" << _.getIdName(value_id) - << "'s type does not match OpFunction's return type."; + << "OpReturnValue Value " << _.getIdName(value_id) + << "s type does not match OpFunction's return type."; } return SPV_SUCCESS; @@ -452,7 +467,7 @@ spv_result_t FindCaseFallThrough( std::vector stack; stack.push_back(target_block); std::unordered_set visited; - bool target_reachable = target_block->reachable(); + bool target_reachable = target_block->structurally_reachable(); int target_depth = function->GetBlockDepth(target_block); while (!stack.empty()) { auto block = stack.back(); @@ -462,8 +477,8 @@ spv_result_t FindCaseFallThrough( if (!visited.insert(block).second) continue; - if (target_reachable && block->reachable() && - target_block->dominates(*block)) { + if (target_reachable && block->structurally_reachable() && + target_block->structurally_dominates(*block)) { // Still in the case construct. for (auto successor : *block->successors()) { stack.push_back(successor); @@ -535,11 +550,12 @@ spv_result_t StructuredSwitchChecks(ValidationState_t& _, Function* function, if (seen_iter == seen_to_fall_through.end()) { const auto target_block = function->GetBlock(target).first; // OpSwitch must dominate all its case constructs. - if (header->reachable() && target_block->reachable() && - !header->dominates(*target_block)) { + if (header->structurally_reachable() && + target_block->structurally_reachable() && + !header->structurally_dominates(*target_block)) { return _.diag(SPV_ERROR_INVALID_CFG, header->label()) << "Selection header " << _.getIdName(header->id()) - << " does not dominate its case construct " + << " does not structurally dominate its case construct " << _.getIdName(target); } @@ -639,7 +655,7 @@ spv_result_t ValidateStructuredSelections( } // Skip unreachable blocks. - if (!block->reachable()) continue; + if (!block->structurally_reachable()) continue; if (terminator->opcode() == SpvOpBranchConditional) { const auto true_label = terminator->GetOperandAs(1); @@ -647,16 +663,16 @@ spv_result_t ValidateStructuredSelections( // Mark the upcoming blocks as seen now, but only error out if this block // was missing a merge instruction and both labels hadn't been seen // previously. - const bool both_unseen = - seen.insert(true_label).second && seen.insert(false_label).second; - if (!merge && both_unseen) { + const bool true_label_unseen = seen.insert(true_label).second; + const bool false_label_unseen = seen.insert(false_label).second; + if (!merge && true_label_unseen && false_label_unseen) { return _.diag(SPV_ERROR_INVALID_CFG, terminator) << "Selection must be structured"; } } else if (terminator->opcode() == SpvOpSwitch) { if (!merge) { return _.diag(SPV_ERROR_INVALID_CFG, terminator) - << "OpSwitch must be preceeded by an OpSelectionMerge " + << "OpSwitch must be preceded by an OpSelectionMerge " "instruction"; } // Mark the targets as seen. @@ -694,7 +710,7 @@ spv_result_t StructuredControlFlowChecks( // Check the loop headers have exactly one back-edge branching to it for (BasicBlock* loop_header : function->ordered_blocks()) { - if (!loop_header->reachable()) continue; + if (!loop_header->structurally_reachable()) continue; if (!loop_header->is_type(kBlockTypeLoop)) continue; auto loop_header_id = loop_header->id(); auto num_latch_blocks = loop_latch_blocks[loop_header_id].size(); @@ -709,9 +725,10 @@ spv_result_t StructuredControlFlowChecks( // Check construct rules for (const Construct& construct : function->constructs()) { auto header = construct.entry_block(); + if (!header->structurally_reachable()) continue; auto merge = construct.exit_block(); - if (header->reachable() && !merge) { + if (!merge) { std::string construct_name, header_name, exit_name; std::tie(construct_name, header_name, exit_name) = ConstructNames(construct.type()); @@ -721,32 +738,31 @@ spv_result_t StructuredControlFlowChecks( exit_name + ". This may be a bug in the validator."; } - // If the exit block is reachable then it's dominated by the - // header. - if (merge && merge->reachable()) { - if (!header->dominates(*merge)) { - return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id())) - << ConstructErrorString(construct, _.getIdName(header->id()), - _.getIdName(merge->id()), - "does not dominate"); - } - // If it's really a merge block for a selection or loop, then it must be - // *strictly* dominated by the header. - if (construct.ExitBlockIsMergeBlock() && (header == merge)) { - return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id())) - << ConstructErrorString(construct, _.getIdName(header->id()), - _.getIdName(merge->id()), - "does not strictly dominate"); - } + // If the header is reachable, the merge is guaranteed to be structurally + // reachable. + if (!header->structurally_dominates(*merge)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id())) + << ConstructErrorString(construct, _.getIdName(header->id()), + _.getIdName(merge->id()), + "does not structurally dominate"); } + // If it's really a merge block for a selection or loop, then it must be + // *strictly* structrually dominated by the header. + if (construct.ExitBlockIsMergeBlock() && (header == merge)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id())) + << ConstructErrorString(construct, _.getIdName(header->id()), + _.getIdName(merge->id()), + "does not strictly structurally dominate"); + } + // Check post-dominance for continue constructs. But dominance and // post-dominance only make sense when the construct is reachable. - if (header->reachable() && construct.type() == ConstructType::kContinue) { - if (!merge->postdominates(*header)) { + if (construct.type() == ConstructType::kContinue) { + if (!merge->structurally_postdominates(*header)) { return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id())) << ConstructErrorString(construct, _.getIdName(header->id()), _.getIdName(merge->id()), - "is not post dominated by"); + "is not structurally post dominated by"); } } @@ -757,7 +773,7 @@ spv_result_t StructuredControlFlowChecks( for (auto block : construct_blocks) { // Check that all exits from the construct are via structured exits. for (auto succ : *block->successors()) { - if (block->reachable() && !construct_blocks.count(succ) && + if (!construct_blocks.count(succ) && !construct.IsStructuredExit(_, succ)) { return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) << "block " << _.getIdName(block->id()) << " exits the " @@ -770,7 +786,7 @@ spv_result_t StructuredControlFlowChecks( // Check that for all non-header blocks, all predecessors are within this // construct. for (auto pred : *block->predecessors()) { - if (pred->reachable() && !construct_blocks.count(pred)) { + if (pred->structurally_reachable() && !construct_blocks.count(pred)) { return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(pred->id())) << "block " << pred->id() << " branches to the " << construct_name << " construct, but not to the " @@ -786,7 +802,7 @@ spv_result_t StructuredControlFlowChecks( merge_inst.opcode() == SpvOpLoopMerge) { uint32_t merge_id = merge_inst.GetOperandAs(0); auto merge_block = function->GetBlock(merge_id).first; - if (merge_block->reachable() && + if (merge_block->structurally_reachable() && !construct_blocks.count(merge_block)) { return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) << "Header block " << _.getIdName(block->id()) @@ -799,6 +815,43 @@ spv_result_t StructuredControlFlowChecks( } } + if (construct.type() == ConstructType::kLoop) { + // If the continue target differs from the loop header, then check that + // all edges into the continue construct come from within the loop. + const auto index = header->terminator() - &_.ordered_instructions()[0]; + const auto& merge_inst = _.ordered_instructions()[index - 1]; + const auto continue_id = merge_inst.GetOperandAs(1); + const auto* continue_inst = _.FindDef(continue_id); + // OpLabel instructions aren't stored as part of the basic block for + // legacy reaasons. Grab the next instruction and use it's block pointer + // instead. + const auto next_index = + (continue_inst - &_.ordered_instructions()[0]) + 1; + const auto& next_inst = _.ordered_instructions()[next_index]; + const auto* continue_target = next_inst.block(); + if (header->id() != continue_id) { + for (auto pred : *continue_target->predecessors()) { + // Ignore back-edges from within the continue construct. + bool is_back_edge = false; + for (auto back_edge : back_edges) { + uint32_t back_edge_block; + uint32_t header_block; + std::tie(back_edge_block, header_block) = back_edge; + if (header_block == continue_id && back_edge_block == pred->id()) + is_back_edge = true; + } + if (!construct_blocks.count(pred) && !is_back_edge) { + return _.diag(SPV_ERROR_INVALID_CFG, pred->terminator()) + << "Block " << _.getIdName(pred->id()) + << " branches to the loop continue target " + << _.getIdName(continue_id) + << ", but is not contained in the associated loop construct " + << _.getIdName(header->id()); + } + } + } + } + // Checks rules for case constructs. if (construct.type() == ConstructType::kSelection && header->terminator()->opcode() == SpvOpSwitch) { @@ -836,52 +889,27 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) { << _.getIdName(function.id()); } - // Set each block's immediate dominator and immediate postdominator, - // and find all back-edges. + // Set each block's immediate dominator. // // We want to analyze all the blocks in the function, even in degenerate // control flow cases including unreachable blocks. So use the augmented // CFG to ensure we cover all the blocks. std::vector postorder; - std::vector postdom_postorder; - std::vector> back_edges; auto ignore_block = [](const BasicBlock*) {}; - auto ignore_edge = [](const BasicBlock*, const BasicBlock*) {}; + auto no_terminal_blocks = [](const BasicBlock*) { return false; }; if (!function.ordered_blocks().empty()) { /// calculate dominators CFA::DepthFirstTraversal( function.first_block(), function.AugmentedCFGSuccessorsFunction(), ignore_block, [&](const BasicBlock* b) { postorder.push_back(b); }, - ignore_edge); + no_terminal_blocks); auto edges = CFA::CalculateDominators( postorder, function.AugmentedCFGPredecessorsFunction()); for (auto edge : edges) { if (edge.first != edge.second) edge.first->SetImmediateDominator(edge.second); } - - /// calculate post dominators - CFA::DepthFirstTraversal( - function.pseudo_exit_block(), - function.AugmentedCFGPredecessorsFunction(), ignore_block, - [&](const BasicBlock* b) { postdom_postorder.push_back(b); }, - ignore_edge); - auto postdom_edges = CFA::CalculateDominators( - postdom_postorder, function.AugmentedCFGSuccessorsFunction()); - for (auto edge : postdom_edges) { - edge.first->SetImmediatePostDominator(edge.second); - } - /// calculate back edges. - CFA::DepthFirstTraversal( - function.pseudo_entry_block(), - function - .AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge(), - ignore_block, ignore_block, - [&](const BasicBlock* from, const BasicBlock* to) { - back_edges.emplace_back(from->id(), to->id()); - }); } - UpdateContinueConstructExitBlocks(function, back_edges); auto& blocks = function.ordered_blocks(); if (!blocks.empty()) { @@ -898,7 +926,7 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) { } } } - // If we have structed control flow, check that no block has a control + // If we have structured control flow, check that no block has a control // flow nesting depth larger than the limit. if (_.HasCapability(SpvCapabilityShader)) { const int control_flow_nesting_depth_limit = @@ -915,6 +943,52 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) { /// Structured control flow checks are only required for shader capabilities if (_.HasCapability(SpvCapabilityShader)) { + // Calculate structural dominance. + postorder.clear(); + std::vector postdom_postorder; + std::vector> back_edges; + if (!function.ordered_blocks().empty()) { + /// calculate dominators + CFA::DepthFirstTraversal( + function.first_block(), + function.AugmentedStructuralCFGSuccessorsFunction(), ignore_block, + [&](const BasicBlock* b) { postorder.push_back(b); }, + no_terminal_blocks); + auto edges = CFA::CalculateDominators( + postorder, function.AugmentedStructuralCFGPredecessorsFunction()); + for (auto edge : edges) { + if (edge.first != edge.second) + edge.first->SetImmediateStructuralDominator(edge.second); + } + + /// calculate post dominators + CFA::DepthFirstTraversal( + function.pseudo_exit_block(), + function.AugmentedStructuralCFGPredecessorsFunction(), ignore_block, + [&](const BasicBlock* b) { postdom_postorder.push_back(b); }, + no_terminal_blocks); + auto postdom_edges = CFA::CalculateDominators( + postdom_postorder, + function.AugmentedStructuralCFGSuccessorsFunction()); + for (auto edge : postdom_edges) { + edge.first->SetImmediateStructuralPostDominator(edge.second); + } + /// calculate back edges. + CFA::DepthFirstTraversal( + function.pseudo_entry_block(), + function.AugmentedStructuralCFGSuccessorsFunction(), ignore_block, + ignore_block, + [&](const BasicBlock* from, const BasicBlock* to) { + // A back edge must be a real edge. Since the augmented successors + // contain structural edges, filter those from consideration. + for (const auto* succ : *(from->successors())) { + if (succ == to) back_edges.emplace_back(from->id(), to->id()); + } + }, + no_terminal_blocks); + } + UpdateContinueConstructExitBlocks(function, back_edges); + if (auto error = StructuredControlFlowChecks(_, &function, back_edges, postorder)) return error; @@ -992,7 +1066,9 @@ spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) { case SpvOpTerminateInvocation: case SpvOpIgnoreIntersectionKHR: case SpvOpTerminateRayKHR: + case SpvOpEmitMeshTasksEXT: _.current_function().RegisterBlockEnd(std::vector()); + // Ops with dedicated passes check for the Execution Model there if (opcode == SpvOpKill) { _.current_function().RegisterExecutionModelLimitation( SpvExecutionModelFragment, @@ -1006,12 +1082,12 @@ spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) { if (opcode == SpvOpIgnoreIntersectionKHR) { _.current_function().RegisterExecutionModelLimitation( SpvExecutionModelAnyHitKHR, - "OpIgnoreIntersectionKHR requires AnyHit execution model"); + "OpIgnoreIntersectionKHR requires AnyHitKHR execution model"); } if (opcode == SpvOpTerminateRayKHR) { _.current_function().RegisterExecutionModelLimitation( SpvExecutionModelAnyHitKHR, - "OpTerminateRayKHR requires AnyHit execution model"); + "OpTerminateRayKHR requires AnyHitKHR execution model"); } break; @@ -1040,6 +1116,26 @@ void ReachabilityPass(ValidationState_t& _) { } } } + + // Repeat for structural reachability. + for (auto& f : _.functions()) { + std::vector 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->structurally_reachable()) continue; + + block->set_structurally_reachable(true); + for (auto succ : *block->structural_successors()) { + stack.push_back(succ); + } + } + } } spv_result_t ControlFlowPass(ValidationState_t& _, const Instruction* inst) { diff --git a/source/val/validate_composites.cpp b/source/val/validate_composites.cpp index 5d6c5e37..c3d948dc 100644 --- a/source/val/validate_composites.cpp +++ b/source/val/validate_composites.cpp @@ -505,8 +505,8 @@ spv_result_t ValidateVectorShuffle(ValidationState_t& _, if (componentCount != resultVectorDimension) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpVectorShuffle component literals count does not match " - "Result Type '" - << _.getIdName(resultType->id()) << "'s vector component count."; + "Result Type " + << _.getIdName(resultType->id()) << "s vector component count."; } // Vector 1 and Vector 2 must both have vector types, with the same Component diff --git a/source/val/validate_constants.cpp b/source/val/validate_constants.cpp index dea95c8a..fdfaea5f 100644 --- a/source/val/validate_constants.cpp +++ b/source/val/validate_constants.cpp @@ -26,8 +26,8 @@ spv_result_t ValidateConstantBool(ValidationState_t& _, auto type = _.FindDef(inst->type_id()); if (!type || type->opcode() != SpvOpTypeBool) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Op" << spvOpcodeString(inst->opcode()) << " Result Type '" - << _.getIdName(inst->type_id()) << "' is not a boolean type."; + << "Op" << spvOpcodeString(inst->opcode()) << " Result Type " + << _.getIdName(inst->type_id()) << " is not a boolean type."; } return SPV_SUCCESS; @@ -40,8 +40,8 @@ spv_result_t ValidateConstantComposite(ValidationState_t& _, const auto result_type = _.FindDef(inst->type_id()); if (!result_type || !spvOpcodeIsComposite(result_type->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << opcode_name << " Result Type '" - << _.getIdName(inst->type_id()) << "' is not a composite type."; + << opcode_name << " Result Type " + << _.getIdName(inst->type_id()) << " is not a composite type."; } const auto constituent_count = inst->words().size() - 3; @@ -53,9 +53,8 @@ spv_result_t ValidateConstantComposite(ValidationState_t& _, return _.diag(SPV_ERROR_INVALID_ID, inst) << opcode_name << " Constituent count does not match " - "Result Type '" - << _.getIdName(result_type->id()) - << "'s vector component count."; + "Result Type " + << _.getIdName(result_type->id()) << "s vector component count."; } const auto component_type = _.FindDef(result_type->GetOperandAs(1)); @@ -71,18 +70,18 @@ spv_result_t ValidateConstantComposite(ValidationState_t& _, if (!constituent || !spvOpcodeIsConstantOrUndef(constituent->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << opcode_name << " Constituent '" + << opcode_name << " Constituent " << _.getIdName(constituent_id) - << "' is not a constant or undef."; + << " is not a constant or undef."; } const auto constituent_result_type = _.FindDef(constituent->type_id()); if (!constituent_result_type || component_type->opcode() != constituent_result_type->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << opcode_name << " Constituent '" + << opcode_name << " Constituent " << _.getIdName(constituent_id) - << "'s type does not match Result Type '" - << _.getIdName(result_type->id()) << "'s vector element type."; + << "s type does not match Result Type " + << _.getIdName(result_type->id()) << "s vector element type."; } } } break; @@ -93,8 +92,8 @@ spv_result_t ValidateConstantComposite(ValidationState_t& _, return _.diag(SPV_ERROR_INVALID_ID, inst) << opcode_name << " Constituent count does not match " - "Result Type '" - << _.getIdName(result_type->id()) << "'s matrix column count."; + "Result Type " + << _.getIdName(result_type->id()) << "s matrix column count."; } const auto column_type = _.FindDef(result_type->words()[2]); @@ -120,9 +119,9 @@ spv_result_t ValidateConstantComposite(ValidationState_t& _, // The message says "... or undef" because the spec does not say // undef is a constant. return _.diag(SPV_ERROR_INVALID_ID, inst) - << opcode_name << " Constituent '" + << opcode_name << " Constituent " << _.getIdName(constituent_id) - << "' is not a constant or undef."; + << " is not a constant or undef."; } const auto vector = _.FindDef(constituent->type_id()); if (!vector) { @@ -131,28 +130,28 @@ spv_result_t ValidateConstantComposite(ValidationState_t& _, } if (column_type->opcode() != vector->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << opcode_name << " Constituent '" + << opcode_name << " Constituent " << _.getIdName(constituent_id) - << "' type does not match Result Type '" - << _.getIdName(result_type->id()) << "'s matrix column type."; + << " type does not match Result Type " + << _.getIdName(result_type->id()) << "s matrix column type."; } const auto vector_component_type = _.FindDef(vector->GetOperandAs(1)); if (component_type->id() != vector_component_type->id()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << opcode_name << " Constituent '" + << opcode_name << " Constituent " << _.getIdName(constituent_id) - << "' component type does not match Result Type '" + << " component type does not match Result Type " << _.getIdName(result_type->id()) - << "'s matrix column component type."; + << "s matrix column component type."; } if (component_count != vector->words()[3]) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << opcode_name << " Constituent '" + << opcode_name << " Constituent " << _.getIdName(constituent_id) - << "' vector component count does not match Result Type '" + << " vector component count does not match Result Type " << _.getIdName(result_type->id()) - << "'s vector component count."; + << "s vector component count."; } } } break; @@ -175,8 +174,8 @@ spv_result_t ValidateConstantComposite(ValidationState_t& _, return _.diag(SPV_ERROR_INVALID_ID, inst) << opcode_name << " Constituent count does not match " - "Result Type '" - << _.getIdName(result_type->id()) << "'s array length."; + "Result Type " + << _.getIdName(result_type->id()) << "s array length."; } for (size_t constituent_index = 2; constituent_index < inst->operands().size(); constituent_index++) { @@ -186,9 +185,9 @@ spv_result_t ValidateConstantComposite(ValidationState_t& _, if (!constituent || !spvOpcodeIsConstantOrUndef(constituent->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << opcode_name << " Constituent '" + << opcode_name << " Constituent " << _.getIdName(constituent_id) - << "' is not a constant or undef."; + << " is not a constant or undef."; } const auto constituent_type = _.FindDef(constituent->type_id()); if (!constituent_type) { @@ -197,10 +196,10 @@ spv_result_t ValidateConstantComposite(ValidationState_t& _, } if (element_type->id() != constituent_type->id()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << opcode_name << " Constituent '" + << opcode_name << " Constituent " << _.getIdName(constituent_id) - << "'s type does not match Result Type '" - << _.getIdName(result_type->id()) << "'s array element type."; + << "s type does not match Result Type " + << _.getIdName(result_type->id()) << "s array element type."; } } } break; @@ -208,10 +207,10 @@ spv_result_t ValidateConstantComposite(ValidationState_t& _, const auto member_count = result_type->words().size() - 2; if (member_count != constituent_count) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << opcode_name << " Constituent '" + << opcode_name << " Constituent " << _.getIdName(inst->type_id()) - << "' count does not match Result Type '" - << _.getIdName(result_type->id()) << "'s struct member count."; + << " count does not match Result Type " + << _.getIdName(result_type->id()) << "s struct member count."; } for (uint32_t constituent_index = 2, member_index = 1; constituent_index < inst->operands().size(); @@ -222,9 +221,9 @@ spv_result_t ValidateConstantComposite(ValidationState_t& _, if (!constituent || !spvOpcodeIsConstantOrUndef(constituent->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << opcode_name << " Constituent '" + << opcode_name << " Constituent " << _.getIdName(constituent_id) - << "' is not a constant or undef."; + << " is not a constant or undef."; } const auto constituent_type = _.FindDef(constituent->type_id()); if (!constituent_type) { @@ -237,26 +236,25 @@ spv_result_t ValidateConstantComposite(ValidationState_t& _, const auto member_type = _.FindDef(member_type_id); if (!member_type || member_type->id() != constituent_type->id()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << opcode_name << " Constituent '" + << opcode_name << " Constituent " << _.getIdName(constituent_id) - << "' type does not match the Result Type '" - << _.getIdName(result_type->id()) << "'s member type."; + << " type does not match the Result Type " + << _.getIdName(result_type->id()) << "s member type."; } } } break; case SpvOpTypeCooperativeMatrixNV: { if (1 != constituent_count) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << opcode_name << " Constituent '" - << _.getIdName(inst->type_id()) << "' count must be one."; + << opcode_name << " Constituent " + << _.getIdName(inst->type_id()) << " count must be one."; } const auto constituent_id = inst->GetOperandAs(2); const auto constituent = _.FindDef(constituent_id); if (!constituent || !spvOpcodeIsConstantOrUndef(constituent->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << opcode_name << " Constituent '" - << _.getIdName(constituent_id) - << "' is not a constant or undef."; + << opcode_name << " Constituent " + << _.getIdName(constituent_id) << " is not a constant or undef."; } const auto constituent_type = _.FindDef(constituent->type_id()); if (!constituent_type) { @@ -268,10 +266,10 @@ spv_result_t ValidateConstantComposite(ValidationState_t& _, const auto component_type = _.FindDef(component_type_id); if (!component_type || component_type->id() != constituent_type->id()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << opcode_name << " Constituent '" + << opcode_name << " Constituent " << _.getIdName(constituent_id) - << "' type does not match the Result Type '" - << _.getIdName(result_type->id()) << "'s component type."; + << " type does not match the Result Type " + << _.getIdName(result_type->id()) << "s component type."; } } break; default: @@ -285,8 +283,8 @@ spv_result_t ValidateConstantSampler(ValidationState_t& _, const auto result_type = _.FindDef(inst->type_id()); if (!result_type || result_type->opcode() != SpvOpTypeSampler) { return _.diag(SPV_ERROR_INVALID_ID, result_type) - << "OpConstantSampler Result Type '" - << _.getIdName(inst->type_id()) << "' is not a sampler type."; + << "OpConstantSampler Result Type " + << _.getIdName(inst->type_id()) << " is not a sampler type."; } return SPV_SUCCESS; @@ -339,8 +337,8 @@ spv_result_t ValidateConstantNull(ValidationState_t& _, const auto result_type = _.FindDef(inst->type_id()); if (!result_type || !IsTypeNullable(result_type->words(), _)) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpConstantNull Result Type '" - << _.getIdName(inst->type_id()) << "' cannot have a null value."; + << "OpConstantNull Result Type " << _.getIdName(inst->type_id()) + << " cannot have a null value."; } return SPV_SUCCESS; diff --git a/source/val/validate_conversion.cpp b/source/val/validate_conversion.cpp index b4e39cfe..dc6b1517 100644 --- a/source/val/validate_conversion.cpp +++ b/source/val/validate_conversion.cpp @@ -534,6 +534,24 @@ spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) { break; } + case SpvOpConvertUToAccelerationStructureKHR: { + if (!_.IsAccelerationStructureType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a Acceleration Structure: " + << spvOpcodeString(opcode); + } + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!input_type || !_.IsUnsigned64BitHandle(input_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 64-bit uint scalar or 2-component 32-bit uint " + "vector as input: " + << spvOpcodeString(opcode); + } + + break; + } + default: break; } diff --git a/source/val/validate_debug.cpp b/source/val/validate_debug.cpp index 0a25d8ab..7ab597a1 100644 --- a/source/val/validate_debug.cpp +++ b/source/val/validate_debug.cpp @@ -28,16 +28,16 @@ spv_result_t ValidateMemberName(ValidationState_t& _, const Instruction* inst) { const auto type = _.FindDef(type_id); if (!type || SpvOpTypeStruct != type->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpMemberName Type '" << _.getIdName(type_id) - << "' is not a struct type."; + << "OpMemberName Type " << _.getIdName(type_id) + << " is not a struct type."; } const auto member_id = inst->GetOperandAs(1); const auto member_count = (uint32_t)(type->words().size() - 2); if (member_count <= member_id) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpMemberName Member '" << _.getIdName(member_id) - << "' index is larger than Type '" << _.getIdName(type->id()) - << "'s member count."; + << "OpMemberName Member " << _.getIdName(member_id) + << " index is larger than Type " << _.getIdName(type->id()) + << "s member count."; } return SPV_SUCCESS; } @@ -47,8 +47,8 @@ spv_result_t ValidateLine(ValidationState_t& _, const Instruction* inst) { const auto file = _.FindDef(file_id); if (!file || SpvOpString != file->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpLine Target '" << _.getIdName(file_id) - << "' is not an OpString."; + << "OpLine Target " << _.getIdName(file_id) + << " is not an OpString."; } return SPV_SUCCESS; } diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp index f076b04c..75058501 100644 --- a/source/val/validate_decorations.cpp +++ b/source/val/validate_decorations.cpp @@ -21,11 +21,13 @@ #include #include +#include "source/binary.h" #include "source/diagnostic.h" #include "source/opcode.h" #include "source/spirv_constant.h" #include "source/spirv_target_env.h" #include "source/spirv_validator_options.h" +#include "source/util/string_utils.h" #include "source/val/validate_scopes.h" #include "source/val/validation_state.h" @@ -96,6 +98,14 @@ bool isBuiltInStruct(uint32_t struct_id, ValidationState_t& vstate) { }); } +// Returns true if the given structure type has a Block decoration. +bool isBlock(uint32_t struct_id, ValidationState_t& vstate) { + const auto& decorations = vstate.id_decorations(struct_id); + return std::any_of( + decorations.begin(), decorations.end(), + [](const Decoration& d) { return SpvDecorationBlock == d.dec_type(); }); +} + // Returns true if the given ID has the Import LinkageAttributes decoration. bool hasImportLinkageAttribute(uint32_t id, ValidationState_t& vstate) { const auto& decorations = vstate.id_decorations(id); @@ -129,18 +139,30 @@ std::vector getStructMembers(uint32_t struct_id, SpvOp type, // Returns whether the given structure is missing Offset decoration for any // member. Handles also nested structures. bool isMissingOffsetInStruct(uint32_t struct_id, ValidationState_t& vstate) { - std::vector hasOffset(getStructMembers(struct_id, vstate).size(), - false); - // Check offsets of member decorations - for (auto& decoration : vstate.id_decorations(struct_id)) { - if (SpvDecorationOffset == decoration.dec_type() && - Decoration::kInvalidMember != decoration.struct_member_index()) { - hasOffset[decoration.struct_member_index()] = true; + const auto* inst = vstate.FindDef(struct_id); + std::vector hasOffset; + std::vector struct_members; + if (inst->opcode() == SpvOpTypeStruct) { + // Check offsets of member decorations. + struct_members = getStructMembers(struct_id, vstate); + hasOffset.resize(struct_members.size(), false); + + for (auto& decoration : vstate.id_decorations(struct_id)) { + if (SpvDecorationOffset == decoration.dec_type() && + Decoration::kInvalidMember != decoration.struct_member_index()) { + // Offset 0xffffffff is not valid so ignore it for simplicity's sake. + if (decoration.params()[0] == 0xffffffff) return true; + hasOffset[decoration.struct_member_index()] = true; + } } + } else if (inst->opcode() == SpvOpTypeArray || + inst->opcode() == SpvOpTypeRuntimeArray) { + hasOffset.resize(1, true); + struct_members.push_back(inst->GetOperandAs(1u)); } - // Check also nested structures + // Look through nested structs (which may be in an array). bool nestedStructsMissingOffset = false; - for (auto id : getStructMembers(struct_id, SpvOpTypeStruct, vstate)) { + for (auto id : struct_members) { if (isMissingOffsetInStruct(id, vstate)) { nestedStructsMissingOffset = true; break; @@ -157,8 +179,9 @@ uint32_t align(uint32_t x, uint32_t alignment) { } // Returns base alignment of struct member. If |roundUp| is true, also -// ensure that structs and arrays are aligned at least to a multiple of 16 -// bytes. +// ensure that structs, arrays, and matrices are aligned at least to a +// multiple of 16 bytes. (That is, when roundUp is true, this function +// returns the *extended* alignment as it's called by the Vulkan spec.) uint32_t getBaseAlignment(uint32_t member_id, bool roundUp, const LayoutConstraints& inherited, MemberConstraints& constraints, @@ -168,6 +191,13 @@ uint32_t getBaseAlignment(uint32_t member_id, bool roundUp, // Minimal alignment is byte-aligned. uint32_t baseAlignment = 1; switch (inst->opcode()) { + case SpvOpTypeSampledImage: + case SpvOpTypeSampler: + case SpvOpTypeImage: + if (vstate.HasCapability(SpvCapabilityBindlessTextureNV)) + return baseAlignment = vstate.samplerimage_variable_address_mode() / 8; + assert(0); + return 0; case SpvOpTypeInt: case SpvOpTypeFloat: baseAlignment = words[2] / 8; @@ -197,6 +227,7 @@ uint32_t getBaseAlignment(uint32_t member_id, bool roundUp, baseAlignment = componentAlignment * (num_columns == 3 ? 4 : num_columns); } + if (roundUp) baseAlignment = align(baseAlignment, 16u); } break; case SpvOpTypeArray: case SpvOpTypeRuntimeArray: @@ -234,6 +265,13 @@ uint32_t getScalarAlignment(uint32_t type_id, ValidationState_t& vstate) { const auto inst = vstate.FindDef(type_id); const auto& words = inst->words(); switch (inst->opcode()) { + case SpvOpTypeSampledImage: + case SpvOpTypeSampler: + case SpvOpTypeImage: + if (vstate.HasCapability(SpvCapabilityBindlessTextureNV)) + return vstate.samplerimage_variable_address_mode() / 8; + assert(0); + return 0; case SpvOpTypeInt: case SpvOpTypeFloat: return words[2] / 8; @@ -274,6 +312,13 @@ uint32_t getSize(uint32_t member_id, const LayoutConstraints& inherited, const auto inst = vstate.FindDef(member_id); const auto& words = inst->words(); switch (inst->opcode()) { + case SpvOpTypeSampledImage: + case SpvOpTypeSampler: + case SpvOpTypeImage: + if (vstate.HasCapability(SpvCapabilityBindlessTextureNV)) + return vstate.samplerimage_variable_address_mode() / 8; + assert(0); + return 0; case SpvOpTypeInt: case SpvOpTypeFloat: return words[2] / 8; @@ -324,10 +369,13 @@ uint32_t getSize(uint32_t member_id, const LayoutConstraints& inherited, const auto& lastMember = members.back(); uint32_t offset = 0xffffffff; // Find the offset of the last element and add the size. - for (auto& decoration : vstate.id_decorations(member_id)) { - if (SpvDecorationOffset == decoration.dec_type() && - decoration.struct_member_index() == (int)lastIdx) { - offset = decoration.params()[0]; + auto member_decorations = + vstate.id_member_decorations(member_id, lastIdx); + for (auto decoration = member_decorations.begin; + decoration != member_decorations.end; ++decoration) { + assert(decoration->struct_member_index() == (int)lastIdx); + if (SpvDecorationOffset == decoration->dec_type()) { + offset = decoration->params()[0]; } } // This check depends on the fact that all members have offsets. This @@ -423,15 +471,17 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str, for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size()); memberIdx < numMembers; memberIdx++) { uint32_t offset = 0xffffffff; - for (auto& decoration : vstate.id_decorations(struct_id)) { - if (decoration.struct_member_index() == (int)memberIdx) { - switch (decoration.dec_type()) { - case SpvDecorationOffset: - offset = decoration.params()[0]; - break; - default: - break; - } + auto member_decorations = + vstate.id_member_decorations(struct_id, memberIdx); + for (auto decoration = member_decorations.begin; + decoration != member_decorations.end; ++decoration) { + assert(decoration->struct_member_index() == (int)memberIdx); + switch (decoration->dec_type()) { + case SpvDecorationOffset: + offset = decoration->params()[0]; + break; + default: + break; } } member_offsets.push_back( @@ -443,7 +493,7 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str, return lhs.offset < rhs.offset; }); - // Now scan from lowest offest to highest offset. + // Now scan from lowest offset to highest offset. uint32_t nextValidOffset = 0; for (size_t ordered_member_idx = 0; ordered_member_idx < member_offsets.size(); ordered_member_idx++) { @@ -506,12 +556,10 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str, return recursive_status; // Check matrix stride. if (SpvOpTypeMatrix == opcode) { - for (auto& decoration : vstate.id_decorations(id)) { - if (SpvDecorationMatrixStride == decoration.dec_type() && - !IsAlignedTo(decoration.params()[0], alignment)) - return fail(memberIdx) - << "is a matrix with stride " << decoration.params()[0] - << " not satisfying alignment to " << alignment; + const auto stride = constraint.matrix_stride; + if (!IsAlignedTo(stride, alignment)) { + return fail(memberIdx) << "is a matrix with stride " << stride + << " not satisfying alignment to " << alignment; } } @@ -549,17 +597,23 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str, // limitation to this check if the array size is a spec constant or is a // runtime array then we will only check a single element. This means // some improper straddles might be missed. - for (uint32_t i = 0; i < num_elements; ++i) { - uint32_t next_offset = i * array_stride + offset; - if (SpvOpTypeStruct == element_inst->opcode() && - SPV_SUCCESS != (recursive_status = checkLayout( - typeId, storage_class_str, decoration_str, - blockRules, scalar_block_layout, - next_offset, constraints, vstate))) - return recursive_status; - // If offsets accumulate up to a 16-byte multiple stop checking since - // it will just repeat. - if (i > 0 && (next_offset % 16 == 0)) break; + if (SpvOpTypeStruct == element_inst->opcode()) { + std::vector seen(16, false); + for (uint32_t i = 0; i < num_elements; ++i) { + uint32_t next_offset = i * array_stride + offset; + // Stop checking if offsets repeat in terms of 16-byte multiples. + if (seen[next_offset % 16]) { + break; + } + + if (SPV_SUCCESS != + (recursive_status = checkLayout( + typeId, storage_class_str, decoration_str, blockRules, + scalar_block_layout, next_offset, constraints, vstate))) + return recursive_status; + + seen[next_offset % 16] = true; + } } // Proceed to the element in case it is an array. @@ -607,7 +661,8 @@ bool hasDecoration(uint32_t id, SpvDecoration decoration, } // Returns true if all ids of given type have a specified decoration. -bool checkForRequiredDecoration(uint32_t struct_id, SpvDecoration decoration, +bool checkForRequiredDecoration(uint32_t struct_id, + std::function checker, SpvOp type, ValidationState_t& vstate) { const auto& members = getStructMembers(struct_id, vstate); for (size_t memberIdx = 0; memberIdx < members.size(); memberIdx++) { @@ -615,10 +670,10 @@ bool checkForRequiredDecoration(uint32_t struct_id, SpvDecoration decoration, if (type != vstate.FindDef(id)->opcode()) continue; bool found = false; for (auto& dec : vstate.id_decorations(id)) { - if (decoration == dec.dec_type()) found = true; + if (checker(dec.dec_type())) found = true; } for (auto& dec : vstate.id_decorations(struct_id)) { - if (decoration == dec.dec_type() && + if (checker(dec.dec_type()) && (int)memberIdx == dec.struct_member_index()) { found = true; } @@ -628,7 +683,7 @@ bool checkForRequiredDecoration(uint32_t struct_id, SpvDecoration decoration, } } for (auto id : getStructMembers(struct_id, SpvOpTypeStruct, vstate)) { - if (!checkForRequiredDecoration(id, decoration, type, vstate)) { + if (!checkForRequiredDecoration(id, checker, type, vstate)) { return false; } } @@ -686,7 +741,7 @@ spv_result_t CheckBuiltInVariable(uint32_t var_id, ValidationState_t& vstate) { if (d.dec_type() == SpvDecorationLocation || d.dec_type() == SpvDecorationComponent) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) - << "A BuiltIn variable (id " << var_id + << vstate.VkErrorID(4915) << "A BuiltIn variable (id " << var_id << ") cannot have any Location or Component decorations"; } } @@ -694,12 +749,12 @@ spv_result_t CheckBuiltInVariable(uint32_t var_id, ValidationState_t& vstate) { return SPV_SUCCESS; } -// Checks whether proper decorations have been appied to the entry points. +// Checks whether proper decorations have been applied to the entry points. spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { for (uint32_t entry_point : vstate.entry_points()) { const auto& descs = vstate.entry_point_descriptions(entry_point); - int num_builtin_inputs = 0; - int num_builtin_outputs = 0; + int num_builtin_block_inputs = 0; + int num_builtin_block_outputs = 0; int num_workgroup_variables = 0; int num_workgroup_variables_with_block = 0; int num_workgroup_variables_with_aliased = 0; @@ -750,9 +805,20 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { Instruction* type_instr = vstate.FindDef(type_id); if (type_instr && SpvOpTypeStruct == type_instr->opcode() && isBuiltInStruct(type_id, vstate)) { - if (storage_class == SpvStorageClassInput) ++num_builtin_inputs; - if (storage_class == SpvStorageClassOutput) ++num_builtin_outputs; - if (num_builtin_inputs > 1 || num_builtin_outputs > 1) break; + if (!isBlock(type_id, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_DATA, vstate.FindDef(type_id)) + << vstate.VkErrorID(4919) + << "Interface struct has no Block decoration but has " + "BuiltIn members. " + "Location decorations must be used on each member of " + "OpVariable with a structure type that is a block not " + "decorated with Location."; + } + if (storage_class == SpvStorageClassInput) ++num_builtin_block_inputs; + if (storage_class == SpvStorageClassOutput) + ++num_builtin_block_outputs; + if (num_builtin_block_inputs > 1 || num_builtin_block_outputs > 1) + break; if (auto error = CheckBuiltInVariable(interface, vstate)) return error; } else if (isBuiltInVar(interface, vstate)) { @@ -769,8 +835,58 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { ++num_workgroup_variables_with_aliased; } } + + if (spvIsVulkanEnv(vstate.context()->target_env)) { + const auto* models = vstate.GetExecutionModels(entry_point); + const bool has_frag = + models->find(SpvExecutionModelFragment) != models->end(); + const bool has_vert = + models->find(SpvExecutionModelVertex) != models->end(); + for (const auto& decoration : + vstate.id_decorations(var_instr->id())) { + if (decoration == SpvDecorationFlat || + decoration == SpvDecorationNoPerspective || + decoration == SpvDecorationSample || + decoration == SpvDecorationCentroid) { + // VUID 04670 already validates these decorations are input/output + if (storage_class == SpvStorageClassInput && + (models->size() > 1 || has_vert)) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << vstate.VkErrorID(6202) + << "OpEntryPoint interfaces variable must not be vertex " + "execution model with an input storage class for " + "Entry Point id " + << entry_point << "."; + } else if (storage_class == SpvStorageClassOutput && + (models->size() > 1 || has_frag)) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << vstate.VkErrorID(6201) + << "OpEntryPoint interfaces variable must not be " + "fragment " + "execution model with an output storage class for " + "Entry Point id " + << entry_point << "."; + } + } + } + + const bool has_flat = + hasDecoration(var_instr->id(), SpvDecorationFlat, vstate); + if (has_frag && storage_class == SpvStorageClassInput && !has_flat && + ((vstate.IsFloatScalarType(type_id) && + vstate.GetBitWidth(type_id) == 64) || + vstate.IsIntScalarOrVectorType(type_id))) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << vstate.VkErrorID(4744) + << "Fragment OpEntryPoint operand " + << interface << " with Input interfaces with integer or " + "float type must have a Flat decoration " + "for Entry Point id " + << entry_point << "."; + } + } } - if (num_builtin_inputs > 1 || num_builtin_outputs > 1) { + if (num_builtin_block_inputs > 1 || num_builtin_block_outputs > 1) { return vstate.diag(SPV_ERROR_INVALID_BINARY, vstate.FindDef(entry_point)) << "There must be at most one object per Storage Class that can " @@ -782,8 +898,8 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { // targeted by an OpEntryPoint instruction for (auto& decoration : vstate.id_decorations(entry_point)) { if (SpvDecorationLinkageAttributes == decoration.dec_type()) { - const char* linkage_name = - reinterpret_cast(&decoration.params()[0]); + const std::string linkage_name = + spvtools::utils::MakeString(decoration.params()); return vstate.diag(SPV_ERROR_INVALID_BINARY, vstate.FindDef(entry_point)) << "The LinkageAttributes Decoration (Linkage name: " @@ -841,21 +957,23 @@ void ComputeMemberConstraintsForStruct(MemberConstraints* constraints, LayoutConstraints& constraint = (*constraints)[std::make_pair(struct_id, memberIdx)]; constraint = inherited; - for (auto& decoration : vstate.id_decorations(struct_id)) { - if (decoration.struct_member_index() == (int)memberIdx) { - switch (decoration.dec_type()) { - case SpvDecorationRowMajor: - constraint.majorness = kRowMajor; - break; - case SpvDecorationColMajor: - constraint.majorness = kColumnMajor; - break; - case SpvDecorationMatrixStride: - constraint.matrix_stride = decoration.params()[0]; - break; - default: - break; - } + auto member_decorations = + vstate.id_member_decorations(struct_id, memberIdx); + for (auto decoration = member_decorations.begin; + decoration != member_decorations.end; ++decoration) { + assert(decoration->struct_member_index() == (int)memberIdx); + switch (decoration->dec_type()) { + case SpvDecorationRowMajor: + constraint.majorness = kRowMajor; + break; + case SpvDecorationColMajor: + constraint.majorness = kColumnMajor; + break; + case SpvDecorationMatrixStride: + constraint.matrix_stride = decoration->params()[0]; + break; + default: + break; } } @@ -919,41 +1037,41 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { const bool storage_buffer = storageClass == SpvStorageClassStorageBuffer; if (spvIsVulkanEnv(vstate.context()->target_env)) { - // Vulkan 14.5.1: There must be no more than one PushConstant block - // per entry point. + // Vulkan: There must be no more than one PushConstant block per entry + // point. if (push_constant) { auto entry_points = vstate.EntryPointReferences(var_id); for (auto ep_id : entry_points) { const bool already_used = !uses_push_constant.insert(ep_id).second; if (already_used) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) - << "Entry point id '" << ep_id + << vstate.VkErrorID(6674) << "Entry point id '" << ep_id << "' uses more than one PushConstant interface.\n" - << "From Vulkan spec, section 14.5.1:\n" + << "From Vulkan spec:\n" << "There must be no more than one push constant block " << "statically used per shader entry point."; } } } - // Vulkan 14.5.2: Check DescriptorSet and Binding decoration for + // Vulkan: Check DescriptorSet and Binding decoration for // UniformConstant which cannot be a struct. if (uniform_constant) { auto entry_points = vstate.EntryPointReferences(var_id); if (!entry_points.empty() && !hasDecoration(var_id, SpvDecorationDescriptorSet, vstate)) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) - << "UniformConstant id '" << var_id + << vstate.VkErrorID(6677) << "UniformConstant id '" << var_id << "' is missing DescriptorSet decoration.\n" - << "From Vulkan spec, section 14.5.2:\n" + << "From Vulkan spec:\n" << "These variables must have DescriptorSet and Binding " "decorations specified"; } if (!entry_points.empty() && !hasDecoration(var_id, SpvDecorationBinding, vstate)) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) - << "UniformConstant id '" << var_id + << vstate.VkErrorID(6677) << "UniformConstant id '" << var_id << "' is missing Binding decoration.\n" - << "From Vulkan spec, section 14.5.2:\n" + << "From Vulkan spec:\n" << "These variables must have DescriptorSet and Binding " "decorations specified"; } @@ -980,8 +1098,10 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { } const bool phys_storage_buffer = - storageClass == SpvStorageClassPhysicalStorageBufferEXT; - const bool workgroup = storageClass == SpvStorageClassWorkgroup; + storageClass == SpvStorageClassPhysicalStorageBuffer; + const bool workgroup = + storageClass == SpvStorageClassWorkgroup && + vstate.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayoutKHR); if (uniform || push_constant || storage_buffer || phys_storage_buffer || workgroup) { const auto ptrInst = vstate.FindDef(words[1]); @@ -1012,55 +1132,55 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { hasDecoration(id, SpvDecorationBufferBlock, vstate); if (storage_buffer && buffer_block) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) - << "Storage buffer id '" << var_id + << vstate.VkErrorID(6675) << "Storage buffer id '" << var_id << " In Vulkan, BufferBlock is disallowed on variables in " "the StorageBuffer storage class"; } - // Vulkan 14.5.1/2: Check Block decoration for PushConstant, Uniform + // Vulkan: Check Block decoration for PushConstant, Uniform // and StorageBuffer variables. Uniform can also use BufferBlock. if (push_constant && !block) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) - << "PushConstant id '" << id + << vstate.VkErrorID(6675) << "PushConstant id '" << id << "' is missing Block decoration.\n" - << "From Vulkan spec, section 14.5.1:\n" + << "From Vulkan spec:\n" << "Such variables must be identified with a Block " "decoration"; } if (storage_buffer && !block) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) - << "StorageBuffer id '" << id + << vstate.VkErrorID(6675) << "StorageBuffer id '" << id << "' is missing Block decoration.\n" - << "From Vulkan spec, section 14.5.2:\n" + << "From Vulkan spec:\n" << "Such variables must be identified with a Block " "decoration"; } if (uniform && !block && !buffer_block) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) - << "Uniform id '" << id + << vstate.VkErrorID(6676) << "Uniform id '" << id << "' is missing Block or BufferBlock decoration.\n" - << "From Vulkan spec, section 14.5.2:\n" + << "From Vulkan spec:\n" << "Such variables must be identified with a Block or " "BufferBlock decoration"; } - // Vulkan 14.5.2: Check DescriptorSet and Binding decoration for + // Vulkan: Check DescriptorSet and Binding decoration for // Uniform and StorageBuffer variables. if (uniform || storage_buffer) { auto entry_points = vstate.EntryPointReferences(var_id); if (!entry_points.empty() && !hasDecoration(var_id, SpvDecorationDescriptorSet, vstate)) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) - << sc_str << " id '" << var_id + << vstate.VkErrorID(6677) << sc_str << " id '" << var_id << "' is missing DescriptorSet decoration.\n" - << "From Vulkan spec, section 14.5.2:\n" + << "From Vulkan spec:\n" << "These variables must have DescriptorSet and Binding " "decorations specified"; } if (!entry_points.empty() && !hasDecoration(var_id, SpvDecorationBinding, vstate)) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) - << sc_str << " id '" << var_id + << vstate.VkErrorID(6677) << sc_str << " id '" << var_id << "' is missing Binding decoration.\n" - << "From Vulkan spec, section 14.5.2:\n" + << "From Vulkan spec:\n" << "These variables must have DescriptorSet and Binding " "decorations specified"; } @@ -1105,30 +1225,48 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) << "Structure id " << id << " decorated as " << deco_str << " must not use GLSLPacked decoration."; - } else if (!checkForRequiredDecoration(id, SpvDecorationArrayStride, - SpvOpTypeArray, vstate)) { + } else if (!checkForRequiredDecoration( + id, + [](SpvDecoration d) { + return d == SpvDecorationArrayStride; + }, + SpvOpTypeArray, vstate)) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) << "Structure id " << id << " decorated as " << deco_str << " must be explicitly laid out with ArrayStride " "decorations."; - } else if (!checkForRequiredDecoration(id, - SpvDecorationMatrixStride, - SpvOpTypeMatrix, vstate)) { + } else if (!checkForRequiredDecoration( + id, + [](SpvDecoration d) { + return d == SpvDecorationMatrixStride; + }, + SpvOpTypeMatrix, vstate)) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) << "Structure id " << id << " decorated as " << deco_str << " must be explicitly laid out with MatrixStride " "decorations."; + } else if (!checkForRequiredDecoration( + id, + [](SpvDecoration d) { + return d == SpvDecorationRowMajor || + d == SpvDecorationColMajor; + }, + SpvOpTypeMatrix, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << "Structure id " << id << " decorated as " << deco_str + << " must be explicitly laid out with RowMajor or " + "ColMajor decorations."; } else if (blockRules && - (SPV_SUCCESS != (recursive_status = checkLayout( - id, sc_str, deco_str, true, - scalar_block_layout, 0, - constraints, vstate)))) { + (SPV_SUCCESS != + (recursive_status = checkLayout( + id, sc_str, deco_str, true, scalar_block_layout, 0, + constraints, vstate)))) { return recursive_status; } else if (bufferRules && - (SPV_SUCCESS != (recursive_status = checkLayout( - id, sc_str, deco_str, false, - scalar_block_layout, 0, - constraints, vstate)))) { + (SPV_SUCCESS != + (recursive_status = checkLayout( + id, sc_str, deco_str, false, scalar_block_layout, + 0, constraints, vstate)))) { return recursive_status; } } @@ -1158,32 +1296,6 @@ bool AtMostOncePerMember(SpvDecoration decoration) { } } -// Returns the string name for |decoration|. -const char* GetDecorationName(SpvDecoration decoration) { - switch (decoration) { - case SpvDecorationAliased: - return "Aliased"; - case SpvDecorationRestrict: - return "Restrict"; - case SpvDecorationArrayStride: - return "ArrayStride"; - case SpvDecorationOffset: - return "Offset"; - case SpvDecorationMatrixStride: - return "MatrixStride"; - case SpvDecorationRowMajor: - return "RowMajor"; - case SpvDecorationColMajor: - return "ColMajor"; - case SpvDecorationBlock: - return "Block"; - case SpvDecorationBufferBlock: - return "BufferBlock"; - default: - return ""; - } -} - spv_result_t CheckDecorationsCompatibility(ValidationState_t& vstate) { using PerIDKey = std::tuple; using PerMemberKey = std::tuple; @@ -1216,7 +1328,7 @@ spv_result_t CheckDecorationsCompatibility(ValidationState_t& vstate) { if (already_used && AtMostOncePerId(dec_type)) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) << "ID '" << id << "' decorated with " - << GetDecorationName(dec_type) + << vstate.SpvDecorationString(dec_type) << " multiple times is not allowed."; } // Verify certain mutually exclusive decorations are not both applied on @@ -1236,8 +1348,9 @@ spv_result_t CheckDecorationsCompatibility(ValidationState_t& vstate) { if (seen_per_id.find(excl_k) != seen_per_id.end()) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) << "ID '" << id << "' decorated with both " - << GetDecorationName(dec_type) << " and " - << GetDecorationName(excl_dec_type) << " is not allowed."; + << vstate.SpvDecorationString(dec_type) << " and " + << vstate.SpvDecorationString(excl_dec_type) + << " is not allowed."; } } } else if (SpvOpMemberDecorate == inst.opcode()) { @@ -1249,7 +1362,7 @@ spv_result_t CheckDecorationsCompatibility(ValidationState_t& vstate) { if (already_used && AtMostOncePerMember(dec_type)) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) << "ID '" << id << "', member '" << member_id - << "' decorated with " << GetDecorationName(dec_type) + << "' decorated with " << vstate.SpvDecorationString(dec_type) << " multiple times is not allowed."; } // Verify certain mutually exclusive decorations are not both applied on @@ -1269,8 +1382,9 @@ spv_result_t CheckDecorationsCompatibility(ValidationState_t& vstate) { if (seen_per_member.find(excl_k) != seen_per_member.end()) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) << "ID '" << id << "', member '" << member_id - << "' decorated with both " << GetDecorationName(dec_type) - << " and " << GetDecorationName(excl_dec_type) + << "' decorated with both " + << vstate.SpvDecorationString(dec_type) << " and " + << vstate.SpvDecorationString(excl_dec_type) << " is not allowed."; } } @@ -1368,11 +1482,11 @@ spv_result_t CheckFPRoundingModeForShaders(ValidationState_t& vstate, storage != SpvStorageClassUniform && storage != SpvStorageClassPushConstant && storage != SpvStorageClassInput && storage != SpvStorageClassOutput && - storage != SpvStorageClassPhysicalStorageBufferEXT) { + storage != SpvStorageClassPhysicalStorageBuffer) { return vstate.diag(SPV_ERROR_INVALID_ID, &inst) << "FPRoundingMode decoration can be applied only to the " "Object operand of an OpStore in the StorageBuffer, " - "PhysicalStorageBufferEXT, Uniform, PushConstant, Input, or " + "PhysicalStorageBuffer, Uniform, PushConstant, Input, or " "Output Storage Classes."; } } @@ -1615,6 +1729,24 @@ spv_result_t CheckLocationDecoration(ValidationState_t& vstate, "of a structure type"; } +spv_result_t CheckRelaxPrecisionDecoration(ValidationState_t& vstate, + const Instruction& inst, + const Decoration& decoration) { + // This is not the most precise check, but the rules for RelaxPrecision are + // very general, and it will be difficult to implement precisely. For now, + // I will only check for the cases that cause problems for the optimizer. + if (!spvOpcodeGeneratesType(inst.opcode())) { + return SPV_SUCCESS; + } + + if (decoration.struct_member_index() != Decoration::kInvalidMember && + inst.opcode() == SpvOpTypeStruct) { + return SPV_SUCCESS; + } + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "RelaxPrecision decoration cannot be applied to a type"; +} + #define PASS_OR_BAIL_AT_LINE(X, LINE) \ { \ spv_result_t e##LINE = (X); \ @@ -1669,6 +1801,10 @@ spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) { case SpvDecorationLocation: PASS_OR_BAIL(CheckLocationDecoration(vstate, *inst, decoration)); break; + case SpvDecorationRelaxedPrecision: + PASS_OR_BAIL( + CheckRelaxPrecisionDecoration(vstate, *inst, decoration)); + break; default: break; } diff --git a/source/val/validate_derivatives.cpp b/source/val/validate_derivatives.cpp index 067cc964..25b941ab 100644 --- a/source/val/validate_derivatives.cpp +++ b/source/val/validate_derivatives.cpp @@ -79,11 +79,13 @@ spv_result_t DerivativesPass(ValidationState_t& _, const Instruction* inst) { std::string* message) { const auto* models = state.GetExecutionModels(entry_point->id()); const auto* modes = state.GetExecutionModes(entry_point->id()); - if (models->find(SpvExecutionModelGLCompute) != models->end() && - modes->find(SpvExecutionModeDerivativeGroupLinearNV) == - modes->end() && - modes->find(SpvExecutionModeDerivativeGroupQuadsNV) == - modes->end()) { + if (models && + models->find(SpvExecutionModelGLCompute) != models->end() && + (!modes || + (modes->find(SpvExecutionModeDerivativeGroupLinearNV) == + modes->end() && + modes->find(SpvExecutionModeDerivativeGroupQuadsNV) == + modes->end()))) { if (message) { *message = std::string( "Derivative instructions require " diff --git a/source/val/validate_execution_limitations.cpp b/source/val/validate_execution_limitations.cpp index aac1c498..e1f4d7b0 100644 --- a/source/val/validate_execution_limitations.cpp +++ b/source/val/validate_execution_limitations.cpp @@ -44,8 +44,8 @@ spv_result_t ValidateExecutionLimitations(ValidationState_t& _, std::string reason; if (!func->IsCompatibleWithExecutionModel(model, &reason)) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpEntryPoint Entry Point '" << _.getIdName(entry_id) - << "'s callgraph contains function " + << "OpEntryPoint Entry Point " << _.getIdName(entry_id) + << "s callgraph contains function " << _.getIdName(inst->id()) << ", which cannot be used with the current execution " "model:\n" @@ -57,9 +57,8 @@ spv_result_t ValidateExecutionLimitations(ValidationState_t& _, std::string reason; if (!func->CheckLimitations(_, _.function(entry_id), &reason)) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpEntryPoint Entry Point '" << _.getIdName(entry_id) - << "'s callgraph contains function " - << _.getIdName(inst->id()) + << "OpEntryPoint Entry Point " << _.getIdName(entry_id) + << "s callgraph contains function " << _.getIdName(inst->id()) << ", which cannot be used with the current execution " "modes:\n" << reason; diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp index a7167fc1..1e69cb37 100644 --- a/source/val/validate_extensions.cpp +++ b/source/val/validate_extensions.cpp @@ -20,7 +20,9 @@ #include "spirv/unified1/NonSemanticClspvReflection.h" +#include "NonSemanticShaderDebugInfo100.h" #include "OpenCLDebugInfo100.h" +#include "source/common_debug_info.h" #include "source/diagnostic.h" #include "source/enum_string_mapping.h" #include "source/extensions.h" @@ -45,6 +47,34 @@ uint32_t GetSizeTBitWidth(const ValidationState_t& _) { return 0; } +bool IsIntScalar(ValidationState_t& _, uint32_t id, bool must_len32, + bool must_unsigned) { + auto type = _.FindDef(id); + if (!type || type->opcode() != SpvOpTypeInt) { + return false; + } + + if (must_len32 && type->GetOperandAs(1) != 32) { + return false; + } + + return !must_unsigned || type->GetOperandAs(2) == 0; +} + +bool IsUint32Constant(ValidationState_t& _, uint32_t id) { + auto inst = _.FindDef(id); + if (!inst || inst->opcode() != SpvOpConstant) { + return false; + } + + return IsIntScalar(_, inst->type_id(), true, true); +} + +uint32_t GetUint32Constant(ValidationState_t& _, uint32_t id) { + auto inst = _.FindDef(id); + return inst->word(3); +} + // Check that the operand of a debug info instruction |inst| at |word_index| // is a result id of an instruction with |expected_opcode|. spv_result_t ValidateOperandForDebugInfo( @@ -68,6 +98,22 @@ spv_result_t ValidateOperandForDebugInfo( return SPV_SUCCESS; } +// For NonSemantic.Shader.DebugInfo.100 check that the operand of a debug info +// instruction |inst| at |word_index| is a result id of a 32-bit integer +// OpConstant instruction. For OpenCL.DebugInfo.100 the parameter is a literal +// word so cannot be validated. +spv_result_t ValidateUint32ConstantOperandForDebugInfo( + ValidationState_t& _, const std::string& operand_name, + const Instruction* inst, uint32_t word_index, + const std::function& ext_inst_name) { + if (!IsUint32Constant(_, inst->word(word_index))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": expected operand " << operand_name + << " must be a result id of 32-bit unsigned OpConstant"; + } + return SPV_SUCCESS; +} + #define CHECK_OPERAND(NAME, opcode, index) \ do { \ auto result = ValidateOperandForDebugInfo(_, NAME, opcode, inst, index, \ @@ -75,18 +121,45 @@ spv_result_t ValidateOperandForDebugInfo( if (result != SPV_SUCCESS) return result; \ } while (0) +#define CHECK_CONST_UINT_OPERAND(NAME, index) \ + if (vulkanDebugInfo) { \ + auto result = ValidateUint32ConstantOperandForDebugInfo( \ + _, NAME, inst, index, ext_inst_name); \ + if (result != SPV_SUCCESS) return result; \ + } + // True if the operand of a debug info instruction |inst| at |word_index| -// satisifies |expectation| that is given as a function. Otherwise, +// satisfies |expectation| that is given as a function. Otherwise, // returns false. bool DoesDebugInfoOperandMatchExpectation( const ValidationState_t& _, - const std::function& expectation, + const std::function& expectation, const Instruction* inst, uint32_t word_index) { if (inst->words().size() <= word_index) return false; auto* debug_inst = _.FindDef(inst->word(word_index)); if (debug_inst->opcode() != SpvOpExtInst || - debug_inst->ext_inst_type() != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || - !expectation(OpenCLDebugInfo100Instructions(debug_inst->word(4)))) { + (debug_inst->ext_inst_type() != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 && + debug_inst->ext_inst_type() != + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) || + !expectation(CommonDebugInfoInstructions(debug_inst->word(4)))) { + return false; + } + return true; +} + +// Overload for NonSemanticShaderDebugInfo100Instructions. +bool DoesDebugInfoOperandMatchExpectation( + const ValidationState_t& _, + const std::function& + expectation, + const Instruction* inst, uint32_t word_index) { + if (inst->words().size() <= word_index) return false; + auto* debug_inst = _.FindDef(inst->word(word_index)); + if (debug_inst->opcode() != SpvOpExtInst || + (debug_inst->ext_inst_type() != + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) || + !expectation( + NonSemanticShaderDebugInfo100Instructions(debug_inst->word(4)))) { return false; } return true; @@ -97,20 +170,18 @@ bool DoesDebugInfoOperandMatchExpectation( // is |expected_debug_inst|. spv_result_t ValidateDebugInfoOperand( ValidationState_t& _, const std::string& debug_inst_name, - OpenCLDebugInfo100Instructions expected_debug_inst, const Instruction* inst, + CommonDebugInfoInstructions expected_debug_inst, const Instruction* inst, uint32_t word_index, const std::function& ext_inst_name) { - std::function expectation = - [expected_debug_inst](OpenCLDebugInfo100Instructions dbg_inst) { + std::function expectation = + [expected_debug_inst](CommonDebugInfoInstructions dbg_inst) { return dbg_inst == expected_debug_inst; }; if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index)) return SPV_SUCCESS; spv_ext_inst_desc desc = nullptr; - _.grammar().lookupExtInst(SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, - expected_debug_inst, &desc); - if (_.grammar().lookupExtInst(SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, - expected_debug_inst, &desc) != SPV_SUCCESS || + if (_.grammar().lookupExtInst(inst->ext_inst_type(), expected_debug_inst, + &desc) != SPV_SUCCESS || !desc) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << ext_inst_name() << ": " @@ -134,9 +205,8 @@ spv_result_t ValidateDebugInfoOperand( spv_result_t ValidateOperandBaseType( ValidationState_t& _, const Instruction* inst, uint32_t word_index, const std::function& ext_inst_name) { - return ValidateDebugInfoOperand(_, "Base Type", - OpenCLDebugInfo100DebugTypeBasic, inst, - word_index, ext_inst_name); + return ValidateDebugInfoOperand(_, "Base Type", CommonDebugInfoDebugTypeBasic, + inst, word_index, ext_inst_name); } // Check that the operand of a debug info instruction |inst| at |word_index| @@ -147,12 +217,12 @@ spv_result_t ValidateOperandLexicalScope( ValidationState_t& _, const std::string& debug_inst_name, const Instruction* inst, uint32_t word_index, const std::function& ext_inst_name) { - std::function expectation = - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == OpenCLDebugInfo100DebugCompilationUnit || - dbg_inst == OpenCLDebugInfo100DebugFunction || - dbg_inst == OpenCLDebugInfo100DebugLexicalBlock || - dbg_inst == OpenCLDebugInfo100DebugTypeComposite; + std::function expectation = + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugCompilationUnit || + dbg_inst == CommonDebugInfoDebugFunction || + dbg_inst == CommonDebugInfoDebugLexicalBlock || + dbg_inst == CommonDebugInfoDebugTypeComposite; }; if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index)) return SPV_SUCCESS; @@ -171,16 +241,27 @@ spv_result_t ValidateOperandDebugType( const Instruction* inst, uint32_t word_index, const std::function& ext_inst_name, bool allow_template_param) { - std::function expectation = - [&allow_template_param](OpenCLDebugInfo100Instructions dbg_inst) { + // Check for NonSemanticShaderDebugInfo100 specific types. + if (inst->ext_inst_type() == + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + std::function expectation = + [](NonSemanticShaderDebugInfo100Instructions dbg_inst) { + return dbg_inst == NonSemanticShaderDebugInfo100DebugTypeMatrix; + }; + if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index)) + return SPV_SUCCESS; + } + + // Check for common types. + std::function expectation = + [&allow_template_param](CommonDebugInfoInstructions dbg_inst) { if (allow_template_param && - (dbg_inst == OpenCLDebugInfo100DebugTypeTemplateParameter || - dbg_inst == - OpenCLDebugInfo100DebugTypeTemplateTemplateParameter)) { + (dbg_inst == CommonDebugInfoDebugTypeTemplateParameter || + dbg_inst == CommonDebugInfoDebugTypeTemplateTemplateParameter)) { return true; } - return OpenCLDebugInfo100DebugTypeBasic <= dbg_inst && - dbg_inst <= OpenCLDebugInfo100DebugTypeTemplate; + return CommonDebugInfoDebugTypeBasic <= dbg_inst && + dbg_inst <= CommonDebugInfoDebugTypeTemplate; }; if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index)) return SPV_SUCCESS; @@ -191,28 +272,6 @@ spv_result_t ValidateOperandDebugType( << " is not a valid debug type"; } -bool IsUint32Constant(ValidationState_t& _, uint32_t id) { - auto inst = _.FindDef(id); - if (!inst || inst->opcode() != SpvOpConstant) { - return false; - } - - auto type = _.FindDef(inst->type_id()); - if (!type || type->opcode() != SpvOpTypeInt) { - return false; - } - - if (type->GetOperandAs(1) != 32) { - return false; - } - - if (type->GetOperandAs(2) != 0) { - return false; - } - - return true; -} - spv_result_t ValidateClspvReflectionKernel(ValidationState_t& _, const Instruction* inst) { const auto kernel_id = inst->GetOperandAs(4); @@ -251,8 +310,7 @@ spv_result_t ValidateClspvReflectionKernel(ValidationState_t& _, return _.diag(SPV_ERROR_INVALID_ID, inst) << "Name must be an OpString"; } - const std::string name_str = reinterpret_cast( - name->words().data() + name->operands()[1].offset); + const std::string name_str = name->GetOperandAs(1); bool found = false; for (auto& desc : _.entry_point_descriptions(kernel_id)) { if (name_str == desc.name) { @@ -667,18 +725,26 @@ bool IsDebugVariableWithIntScalarType(ValidationState_t& _, const Instruction* inst, uint32_t word_index) { auto* dbg_int_scalar_var = _.FindDef(inst->word(word_index)); - if (OpenCLDebugInfo100Instructions(dbg_int_scalar_var->word(4)) == - OpenCLDebugInfo100DebugLocalVariable || - OpenCLDebugInfo100Instructions(dbg_int_scalar_var->word(4)) == - OpenCLDebugInfo100DebugGlobalVariable) { + if (CommonDebugInfoInstructions(dbg_int_scalar_var->word(4)) == + CommonDebugInfoDebugLocalVariable || + CommonDebugInfoInstructions(dbg_int_scalar_var->word(4)) == + CommonDebugInfoDebugGlobalVariable) { auto* dbg_type = _.FindDef(dbg_int_scalar_var->word(6)); - if (OpenCLDebugInfo100Instructions(dbg_type->word(4)) == - OpenCLDebugInfo100DebugTypeBasic && - (OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(dbg_type->word(7)) == - OpenCLDebugInfo100Signed || - OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(dbg_type->word(7)) == - OpenCLDebugInfo100Unsigned)) { - return true; + if (CommonDebugInfoInstructions(dbg_type->word(4)) == + CommonDebugInfoDebugTypeBasic) { + const spv_ext_inst_type_t ext_inst_type = + spv_ext_inst_type_t(inst->ext_inst_type()); + const bool vulkanDebugInfo = + ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100; + uint32_t encoding = dbg_type->word(7); + if (!vulkanDebugInfo || IsUint32Constant(_, encoding)) { + auto ocl_encoding = OpenCLDebugInfo100DebugBaseTypeAttributeEncoding( + vulkanDebugInfo ? GetUint32Constant(_, encoding) : encoding); + if (ocl_encoding == OpenCLDebugInfo100Signed || + ocl_encoding == OpenCLDebugInfo100Unsigned) { + return true; + } + } } } return false; @@ -690,10 +756,10 @@ spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) { if (_.version() < SPV_SPIRV_VERSION_WORD(1, 4)) { std::string extension = GetExtensionString(&(inst->c_inst())); if (extension == - ExtensionToString(kSPV_KHR_workgroup_memory_explicit_layout)) { + ExtensionToString(kSPV_KHR_workgroup_memory_explicit_layout) || + extension == ExtensionToString(kSPV_EXT_mesh_shader)) { return _.diag(SPV_ERROR_WRONG_VERSION, inst) - << "SPV_KHR_workgroup_memory_explicit_layout extension " - "requires SPIR-V version 1.4 or later."; + << extension << " extension requires SPIR-V version 1.4 or later."; } } @@ -703,9 +769,9 @@ spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) { spv_result_t ValidateExtInstImport(ValidationState_t& _, const Instruction* inst) { const auto name_id = 1; - if (!_.HasExtension(kSPV_KHR_non_semantic_info)) { - const std::string name(reinterpret_cast( - inst->words().data() + inst->operands()[name_id].offset)); + if (_.version() <= SPV_SPIRV_VERSION_WORD(1, 5) && + !_.HasExtension(kSPV_KHR_non_semantic_info)) { + const std::string name = inst->GetOperandAs(name_id); if (name.find("NonSemantic.") == 0) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "NonSemantic extended instruction sets cannot be declared " @@ -737,7 +803,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { assert(import_inst); std::ostringstream ss; - ss << reinterpret_cast(import_inst->words().data() + 2); + ss << import_inst->GetOperandAs(1); ss << " "; ss << desc->name; @@ -812,7 +878,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { for (uint32_t operand_index = 4; operand_index < num_operands; ++operand_index) { const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); - if (!_.IsIntScalarOrVectorType(operand_type)) { + if (!operand_type || !_.IsIntScalarOrVectorType(operand_type)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << ext_inst_name() << ": " << "expected all operands to be int scalars or vectors"; @@ -2668,7 +2734,9 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { break; } } - } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { + } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || + ext_inst_type == + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { if (!_.IsVoidType(result_type)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << ext_inst_name() << ": " @@ -2676,451 +2744,637 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { << "OpTypeVoid"; } + const bool vulkanDebugInfo = + ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100; + auto num_words = inst->words().size(); - const OpenCLDebugInfo100Instructions ext_inst_key = - OpenCLDebugInfo100Instructions(ext_inst_index); - switch (ext_inst_key) { - case OpenCLDebugInfo100DebugInfoNone: - case OpenCLDebugInfo100DebugNoScope: - case OpenCLDebugInfo100DebugOperation: - // The binary parser validates the opcode for DebugInfoNone, - // DebugNoScope, DebugOperation, and the literal values don't need - // further checks. - break; - case OpenCLDebugInfo100DebugCompilationUnit: { - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); - break; - } - case OpenCLDebugInfo100DebugSource: { - CHECK_OPERAND("File", SpvOpString, 5); - if (num_words == 7) CHECK_OPERAND("Text", SpvOpString, 6); - break; - } - case OpenCLDebugInfo100DebugTypeBasic: { - CHECK_OPERAND("Name", SpvOpString, 5); - CHECK_OPERAND("Size", SpvOpConstant, 6); - // "Encoding" param is already validated by the binary parsing stage. - break; - } - case OpenCLDebugInfo100DebugTypePointer: - case OpenCLDebugInfo100DebugTypeQualifier: { - auto validate_base_type = - ValidateOperandBaseType(_, inst, 5, ext_inst_name); - if (validate_base_type != SPV_SUCCESS) return validate_base_type; - break; - } - case OpenCLDebugInfo100DebugTypeVector: { - auto validate_base_type = - ValidateOperandBaseType(_, inst, 5, ext_inst_name); - if (validate_base_type != SPV_SUCCESS) return validate_base_type; + // Handle any non-common NonSemanticShaderDebugInfo instructions. + if (vulkanDebugInfo) { + const NonSemanticShaderDebugInfo100Instructions ext_inst_key = + NonSemanticShaderDebugInfo100Instructions(ext_inst_index); + switch (ext_inst_key) { + // The following block of instructions will be handled by the common + // validation. + case NonSemanticShaderDebugInfo100DebugInfoNone: + case NonSemanticShaderDebugInfo100DebugCompilationUnit: + case NonSemanticShaderDebugInfo100DebugTypeBasic: + case NonSemanticShaderDebugInfo100DebugTypePointer: + case NonSemanticShaderDebugInfo100DebugTypeQualifier: + case NonSemanticShaderDebugInfo100DebugTypeArray: + case NonSemanticShaderDebugInfo100DebugTypeVector: + case NonSemanticShaderDebugInfo100DebugTypedef: + case NonSemanticShaderDebugInfo100DebugTypeFunction: + case NonSemanticShaderDebugInfo100DebugTypeEnum: + case NonSemanticShaderDebugInfo100DebugTypeComposite: + case NonSemanticShaderDebugInfo100DebugTypeMember: + case NonSemanticShaderDebugInfo100DebugTypeInheritance: + case NonSemanticShaderDebugInfo100DebugTypePtrToMember: + case NonSemanticShaderDebugInfo100DebugTypeTemplate: + case NonSemanticShaderDebugInfo100DebugTypeTemplateParameter: + case NonSemanticShaderDebugInfo100DebugTypeTemplateTemplateParameter: + case NonSemanticShaderDebugInfo100DebugTypeTemplateParameterPack: + case NonSemanticShaderDebugInfo100DebugGlobalVariable: + case NonSemanticShaderDebugInfo100DebugFunctionDeclaration: + case NonSemanticShaderDebugInfo100DebugFunction: + case NonSemanticShaderDebugInfo100DebugLexicalBlock: + case NonSemanticShaderDebugInfo100DebugLexicalBlockDiscriminator: + case NonSemanticShaderDebugInfo100DebugScope: + case NonSemanticShaderDebugInfo100DebugNoScope: + case NonSemanticShaderDebugInfo100DebugInlinedAt: + case NonSemanticShaderDebugInfo100DebugLocalVariable: + case NonSemanticShaderDebugInfo100DebugInlinedVariable: + case NonSemanticShaderDebugInfo100DebugDeclare: + case NonSemanticShaderDebugInfo100DebugValue: + case NonSemanticShaderDebugInfo100DebugOperation: + case NonSemanticShaderDebugInfo100DebugExpression: + case NonSemanticShaderDebugInfo100DebugMacroDef: + case NonSemanticShaderDebugInfo100DebugMacroUndef: + case NonSemanticShaderDebugInfo100DebugImportedEntity: + case NonSemanticShaderDebugInfo100DebugSource: + break; + case NonSemanticShaderDebugInfo100DebugTypeMatrix: { + CHECK_DEBUG_OPERAND("Vector Type", CommonDebugInfoDebugTypeVector, 5); - uint32_t component_count = inst->word(6); - if (!component_count || component_count > 4) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": Component Count must be positive " - << "integer less than or equal to 4"; + CHECK_CONST_UINT_OPERAND("Vector Count", 6); + + uint32_t vector_count = inst->word(6); + uint64_t const_val; + if (!_.GetConstantValUint64(vector_count, &const_val)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() + << ": Vector Count must be 32-bit integer OpConstant"; + } + + vector_count = const_val & 0xffffffff; + if (!vector_count || vector_count > 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": Vector Count must be positive " + << "integer less than or equal to 4"; + } + break; } - break; + // TODO: Add validation rules for remaining cases as well. + case NonSemanticShaderDebugInfo100DebugFunctionDefinition: + case NonSemanticShaderDebugInfo100DebugSourceContinued: + case NonSemanticShaderDebugInfo100DebugLine: + case NonSemanticShaderDebugInfo100DebugNoLine: + case NonSemanticShaderDebugInfo100DebugBuildIdentifier: + case NonSemanticShaderDebugInfo100DebugStoragePath: + case NonSemanticShaderDebugInfo100DebugEntryPoint: + break; + case NonSemanticShaderDebugInfo100InstructionsMax: + assert(0); + break; } - case OpenCLDebugInfo100DebugTypeArray: { - auto validate_base_type = ValidateOperandDebugType( - _, "Base Type", inst, 5, ext_inst_name, false); - if (validate_base_type != SPV_SUCCESS) return validate_base_type; - for (uint32_t i = 6; i < num_words; ++i) { - bool invalid = false; - auto* component_count = _.FindDef(inst->word(i)); - if (IsConstIntScalarTypeWith32Or64Bits(_, component_count)) { - // TODO: We need a spec discussion for the bindless array. - if (!component_count->word(3)) { - invalid = true; + } + + // Handle any non-common OpenCL insts, then common + if (ext_inst_type != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || + OpenCLDebugInfo100Instructions(ext_inst_index) != + OpenCLDebugInfo100DebugModuleINTEL) { + const CommonDebugInfoInstructions ext_inst_key = + CommonDebugInfoInstructions(ext_inst_index); + switch (ext_inst_key) { + case CommonDebugInfoDebugInfoNone: + case CommonDebugInfoDebugNoScope: + break; + // The binary parser validates the opcode for DebugInfoNone, + // DebugNoScope, DebugOperation. We just check the parameters to + // DebugOperation are properly constants for vulkan debug info. + case CommonDebugInfoDebugOperation: { + CHECK_CONST_UINT_OPERAND("Operation", 5); + for (uint32_t i = 6; i < num_words; ++i) { + CHECK_CONST_UINT_OPERAND("Operand", i); + } + break; + } + case CommonDebugInfoDebugCompilationUnit: { + CHECK_CONST_UINT_OPERAND("Version", 5); + CHECK_CONST_UINT_OPERAND("DWARF Version", 6); + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Language", 8); + break; + } + case CommonDebugInfoDebugSource: { + CHECK_OPERAND("File", SpvOpString, 5); + if (num_words == 7) CHECK_OPERAND("Text", SpvOpString, 6); + break; + } + case CommonDebugInfoDebugTypeBasic: { + CHECK_OPERAND("Name", SpvOpString, 5); + CHECK_OPERAND("Size", SpvOpConstant, 6); + CHECK_CONST_UINT_OPERAND("Encoding", 7); + break; + } + case CommonDebugInfoDebugTypePointer: { + auto validate_base_type = + ValidateOperandBaseType(_, inst, 5, ext_inst_name); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + CHECK_CONST_UINT_OPERAND("Storage Class", 6); + CHECK_CONST_UINT_OPERAND("Flags", 7); + break; + } + case CommonDebugInfoDebugTypeQualifier: { + auto validate_base_type = + ValidateOperandBaseType(_, inst, 5, ext_inst_name); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + CHECK_CONST_UINT_OPERAND("Type Qualifier", 6); + break; + } + case CommonDebugInfoDebugTypeVector: { + auto validate_base_type = + ValidateOperandBaseType(_, inst, 5, ext_inst_name); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + + CHECK_CONST_UINT_OPERAND("Component Count", 6); + uint32_t component_count = inst->word(6); + if (vulkanDebugInfo) { + uint64_t const_val; + if (!_.GetConstantValUint64(component_count, &const_val)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() + << ": Component Count must be 32-bit integer OpConstant"; } - } else if (component_count->words().size() > 6 && - (OpenCLDebugInfo100Instructions(component_count->word( - 4)) == OpenCLDebugInfo100DebugLocalVariable || - OpenCLDebugInfo100Instructions(component_count->word( - 4)) == OpenCLDebugInfo100DebugGlobalVariable)) { - auto* component_count_type = _.FindDef(component_count->word(6)); - if (component_count_type->words().size() > 7) { - if (OpenCLDebugInfo100Instructions(component_count_type->word( - 4)) != OpenCLDebugInfo100DebugTypeBasic || - OpenCLDebugInfo100DebugBaseTypeAttributeEncoding( - component_count_type->word(7)) != - OpenCLDebugInfo100Unsigned) { + component_count = const_val & 0xffffffff; + } + + if (!component_count || component_count > 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": Component Count must be positive " + << "integer less than or equal to 4"; + } + break; + } + case CommonDebugInfoDebugTypeArray: { + auto validate_base_type = ValidateOperandDebugType( + _, "Base Type", inst, 5, ext_inst_name, false); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + for (uint32_t i = 6; i < num_words; ++i) { + bool invalid = false; + auto* component_count = _.FindDef(inst->word(i)); + if (IsConstIntScalarTypeWith32Or64Bits(_, component_count)) { + // TODO: We need a spec discussion for the runtime array for + // OpenCL. + if (!vulkanDebugInfo && !component_count->word(3)) { invalid = true; - } else { - // DebugTypeBasic for DebugLocalVariable/DebugGlobalVariable - // must have Unsigned encoding and 32 or 64 as its size in bits. - Instruction* size_in_bits = - _.FindDef(component_count_type->word(6)); - if (!_.IsIntScalarType(size_in_bits->type_id()) || - (size_in_bits->word(3) != 32 && - size_in_bits->word(3) != 64)) { + } + } else if (component_count->words().size() > 6 && + (CommonDebugInfoInstructions(component_count->word(4)) == + CommonDebugInfoDebugLocalVariable || + CommonDebugInfoInstructions(component_count->word(4)) == + CommonDebugInfoDebugGlobalVariable)) { + auto* component_count_type = _.FindDef(component_count->word(6)); + if (component_count_type->words().size() > 7) { + uint32_t encoding = component_count_type->word(7); + if (CommonDebugInfoInstructions(component_count_type->word( + 4)) != CommonDebugInfoDebugTypeBasic || + (vulkanDebugInfo && !IsUint32Constant(_, encoding)) || + OpenCLDebugInfo100DebugBaseTypeAttributeEncoding( + vulkanDebugInfo + ? GetUint32Constant(_, encoding) + : encoding) != OpenCLDebugInfo100Unsigned) { invalid = true; + } else { + // DebugTypeBasic for DebugLocalVariable/DebugGlobalVariable + // must have Unsigned encoding and 32 or 64 as its size in + // bits. + Instruction* size_in_bits = + _.FindDef(component_count_type->word(6)); + if (!_.IsIntScalarType(size_in_bits->type_id()) || + (size_in_bits->word(3) != 32 && + size_in_bits->word(3) != 64)) { + invalid = true; + } } + } else { + invalid = true; } } else { invalid = true; } + if (invalid) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": Component Count must be " + << "OpConstant with a 32- or 64-bits integer scalar type " + "or " + << "DebugGlobalVariable or DebugLocalVariable with a 32- " + "or " + << "64-bits unsigned integer scalar type"; + } + } + break; + } + case CommonDebugInfoDebugTypedef: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_base_type = + ValidateOperandBaseType(_, inst, 6, ext_inst_name); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + break; + } + case CommonDebugInfoDebugTypeFunction: { + CHECK_CONST_UINT_OPERAND("Flags", 5); + auto* return_type = _.FindDef(inst->word(6)); + // TODO: We need a spec discussion that we have to allow return and + // parameter types of a DebugTypeFunction to have template parameter. + if (return_type->opcode() != SpvOpTypeVoid) { + auto validate_return = ValidateOperandDebugType( + _, "Return Type", inst, 6, ext_inst_name, true); + if (validate_return != SPV_SUCCESS) return validate_return; + } + for (uint32_t word_index = 7; word_index < num_words; ++word_index) { + auto validate_param = ValidateOperandDebugType( + _, "Parameter Types", inst, word_index, ext_inst_name, true); + if (validate_param != SPV_SUCCESS) return validate_param; + } + break; + } + case CommonDebugInfoDebugTypeEnum: { + CHECK_OPERAND("Name", SpvOpString, 5); + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 6)) { + auto validate_underlying_type = ValidateOperandDebugType( + _, "Underlying Types", inst, 6, ext_inst_name, false); + if (validate_underlying_type != SPV_SUCCESS) + return validate_underlying_type; + } + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Size", SpvOpConstant, 11); + auto* size = _.FindDef(inst->word(11)); + if (!_.IsIntScalarType(size->type_id()) || !size->word(3)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": expected operand Size is a " + << "positive integer"; + } + CHECK_CONST_UINT_OPERAND("Flags", 12); + for (uint32_t word_index = 13; word_index + 1 < num_words; + word_index += 2) { + CHECK_OPERAND("Value", SpvOpConstant, word_index); + CHECK_OPERAND("Name", SpvOpString, word_index + 1); + } + break; + } + case CommonDebugInfoDebugTypeComposite: { + CHECK_OPERAND("Name", SpvOpString, 5); + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Linkage Name", SpvOpString, 11); + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 12)) { + CHECK_OPERAND("Size", SpvOpConstant, 12); + } + CHECK_CONST_UINT_OPERAND("Flags", 13); + for (uint32_t word_index = 14; word_index < num_words; ++word_index) { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugTypeMember || + dbg_inst == CommonDebugInfoDebugFunction || + dbg_inst == CommonDebugInfoDebugTypeInheritance; + }, + inst, word_index)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Members " + << "must be DebugTypeMember, DebugFunction, or " + "DebugTypeInheritance"; + } + } + break; + } + case CommonDebugInfoDebugTypeMember: { + CHECK_OPERAND("Name", SpvOpString, 5); + // TODO: We need a spec discussion that we have to allow member types + // to have template parameter. + auto validate_type = + ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + // NonSemantic.Shader.DebugInfo doesn't have the Parent operand + if (vulkanDebugInfo) { + CHECK_OPERAND("Offset", SpvOpConstant, 10); + CHECK_OPERAND("Size", SpvOpConstant, 11); + CHECK_CONST_UINT_OPERAND("Flags", 12); + if (num_words == 14) CHECK_OPERAND("Value", SpvOpConstant, 13); } else { - invalid = true; - } - if (invalid) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": Component Count must be " - << "OpConstant with a 32- or 64-bits integer scalar type or " - << "DebugGlobalVariable or DebugLocalVariable with a 32- or " - << "64-bits unsigned integer scalar type"; + CHECK_DEBUG_OPERAND("Parent", CommonDebugInfoDebugTypeComposite, + 10); + CHECK_OPERAND("Offset", SpvOpConstant, 11); + CHECK_OPERAND("Size", SpvOpConstant, 12); + CHECK_CONST_UINT_OPERAND("Flags", 13); + if (num_words == 15) CHECK_OPERAND("Value", SpvOpConstant, 14); } + break; } - break; - } - case OpenCLDebugInfo100DebugTypedef: { - CHECK_OPERAND("Name", SpvOpString, 5); - auto validate_base_type = - ValidateOperandBaseType(_, inst, 6, ext_inst_name); - if (validate_base_type != SPV_SUCCESS) return validate_base_type; - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); - auto validate_parent = - ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); - if (validate_parent != SPV_SUCCESS) return validate_parent; - break; - } - case OpenCLDebugInfo100DebugTypeFunction: { - auto* return_type = _.FindDef(inst->word(6)); - // TODO: We need a spec discussion that we have to allow return and - // parameter types of a DebugTypeFunction to have template parameter. - if (return_type->opcode() != SpvOpTypeVoid) { - auto validate_return = ValidateOperandDebugType( - _, "Return Type", inst, 6, ext_inst_name, true); - if (validate_return != SPV_SUCCESS) return validate_return; - } - for (uint32_t word_index = 7; word_index < num_words; ++word_index) { - auto validate_param = ValidateOperandDebugType( - _, "Parameter Types", inst, word_index, ext_inst_name, true); - if (validate_param != SPV_SUCCESS) return validate_param; - } - break; - } - case OpenCLDebugInfo100DebugTypeEnum: { - CHECK_OPERAND("Name", SpvOpString, 5); - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == OpenCLDebugInfo100DebugInfoNone; - }, - inst, 6)) { - auto validate_underlying_type = ValidateOperandDebugType( - _, "Underlying Types", inst, 6, ext_inst_name, false); - if (validate_underlying_type != SPV_SUCCESS) - return validate_underlying_type; - } - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); - auto validate_parent = - ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); - if (validate_parent != SPV_SUCCESS) return validate_parent; - CHECK_OPERAND("Size", SpvOpConstant, 11); - auto* size = _.FindDef(inst->word(11)); - if (!_.IsIntScalarType(size->type_id()) || !size->word(3)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": expected operand Size is a " - << "positive integer"; - } - for (uint32_t word_index = 13; word_index + 1 < num_words; - word_index += 2) { - CHECK_OPERAND("Value", SpvOpConstant, word_index); - CHECK_OPERAND("Name", SpvOpString, word_index + 1); - } - break; - } - case OpenCLDebugInfo100DebugTypeComposite: { - CHECK_OPERAND("Name", SpvOpString, 5); - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); - auto validate_parent = - ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); - if (validate_parent != SPV_SUCCESS) return validate_parent; - CHECK_OPERAND("Linkage Name", SpvOpString, 11); - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == OpenCLDebugInfo100DebugInfoNone; - }, - inst, 12)) { - CHECK_OPERAND("Size", SpvOpConstant, 12); - } - for (uint32_t word_index = 14; word_index < num_words; ++word_index) { - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == OpenCLDebugInfo100DebugTypeMember || - dbg_inst == OpenCLDebugInfo100DebugFunction || - dbg_inst == OpenCLDebugInfo100DebugTypeInheritance; - }, - inst, word_index)) { + case CommonDebugInfoDebugTypeInheritance: { + CHECK_DEBUG_OPERAND("Child", CommonDebugInfoDebugTypeComposite, 5); + auto* debug_inst = _.FindDef(inst->word(5)); + auto composite_type = + OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6)); + if (composite_type != OpenCLDebugInfo100Class && + composite_type != OpenCLDebugInfo100Structure) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << ext_inst_name() << ": " - << "expected operand Members " - << "must be DebugTypeMember, DebugFunction, or " - "DebugTypeInheritance"; + << "expected operand Child must be class or struct debug " + "type"; } - } - break; - } - case OpenCLDebugInfo100DebugTypeMember: { - CHECK_OPERAND("Name", SpvOpString, 5); - // TODO: We need a spec discussion that we have to allow member types - // to have template parameter. - auto validate_type = - ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true); - if (validate_type != SPV_SUCCESS) return validate_type; - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); - CHECK_DEBUG_OPERAND("Parent", OpenCLDebugInfo100DebugTypeComposite, 10); - CHECK_OPERAND("Offset", SpvOpConstant, 11); - CHECK_OPERAND("Size", SpvOpConstant, 12); - if (num_words == 15) CHECK_OPERAND("Value", SpvOpConstant, 14); - break; - } - case OpenCLDebugInfo100DebugTypeInheritance: { - CHECK_DEBUG_OPERAND("Child", OpenCLDebugInfo100DebugTypeComposite, 5); - auto* debug_inst = _.FindDef(inst->word(5)); - auto composite_type = - OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6)); - if (composite_type != OpenCLDebugInfo100Class && - composite_type != OpenCLDebugInfo100Structure) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Child must be class or struct debug type"; - } - CHECK_DEBUG_OPERAND("Parent", OpenCLDebugInfo100DebugTypeComposite, 6); - debug_inst = _.FindDef(inst->word(6)); - composite_type = - OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6)); - if (composite_type != OpenCLDebugInfo100Class && - composite_type != OpenCLDebugInfo100Structure) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Parent must be class or struct debug " - "type"; - } - CHECK_OPERAND("Offset", SpvOpConstant, 7); - CHECK_OPERAND("Size", SpvOpConstant, 8); - break; - } - case OpenCLDebugInfo100DebugFunction: { - CHECK_OPERAND("Name", SpvOpString, 5); - auto validate_type = - ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, false); - if (validate_type != SPV_SUCCESS) return validate_type; - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); - auto validate_parent = - ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); - if (validate_parent != SPV_SUCCESS) return validate_parent; - CHECK_OPERAND("Linkage Name", SpvOpString, 11); - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == OpenCLDebugInfo100DebugInfoNone; - }, - inst, 14)) { - CHECK_OPERAND("Function", SpvOpFunction, 14); - } - if (num_words == 16) { - CHECK_DEBUG_OPERAND("Declaration", - OpenCLDebugInfo100DebugFunctionDeclaration, 15); - } - break; - } - case OpenCLDebugInfo100DebugFunctionDeclaration: { - CHECK_OPERAND("Name", SpvOpString, 5); - auto validate_type = - ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, false); - if (validate_type != SPV_SUCCESS) return validate_type; - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); - auto validate_parent = - ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); - if (validate_parent != SPV_SUCCESS) return validate_parent; - CHECK_OPERAND("Linkage Name", SpvOpString, 11); - break; - } - case OpenCLDebugInfo100DebugLexicalBlock: { - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 5); - auto validate_parent = - ValidateOperandLexicalScope(_, "Parent", inst, 8, ext_inst_name); - if (validate_parent != SPV_SUCCESS) return validate_parent; - if (num_words == 10) CHECK_OPERAND("Name", SpvOpString, 9); - break; - } - case OpenCLDebugInfo100DebugScope: { - auto validate_scope = - ValidateOperandLexicalScope(_, "Scope", inst, 5, ext_inst_name); - if (validate_scope != SPV_SUCCESS) return validate_scope; - if (num_words == 7) { - CHECK_DEBUG_OPERAND("Inlined At", OpenCLDebugInfo100DebugInlinedAt, - 6); - } - break; - } - case OpenCLDebugInfo100DebugLocalVariable: { - CHECK_OPERAND("Name", SpvOpString, 5); - // TODO: We need a spec discussion that we have to allow local variable - // types to have template parameter. - auto validate_type = - ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true); - if (validate_type != SPV_SUCCESS) return validate_type; - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); - auto validate_parent = - ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); - if (validate_parent != SPV_SUCCESS) return validate_parent; - break; - } - case OpenCLDebugInfo100DebugDeclare: { - CHECK_DEBUG_OPERAND("Local Variable", - OpenCLDebugInfo100DebugLocalVariable, 5); - auto* operand = _.FindDef(inst->word(6)); - if (operand->opcode() != SpvOpVariable && - operand->opcode() != SpvOpFunctionParameter) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Variable must be a result id of " - "OpVariable or OpFunctionParameter"; - } - - CHECK_DEBUG_OPERAND("Expression", OpenCLDebugInfo100DebugExpression, 7); - break; - } - case OpenCLDebugInfo100DebugExpression: { - for (uint32_t word_index = 5; word_index < num_words; ++word_index) { - CHECK_DEBUG_OPERAND("Operation", OpenCLDebugInfo100DebugOperation, - word_index); - } - break; - } - case OpenCLDebugInfo100DebugTypeTemplate: { - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == OpenCLDebugInfo100DebugTypeComposite || - dbg_inst == OpenCLDebugInfo100DebugFunction; - }, - inst, 5)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Target must be DebugTypeComposite " - << "or DebugFunction"; - } - for (uint32_t word_index = 6; word_index < num_words; ++word_index) { - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == - OpenCLDebugInfo100DebugTypeTemplateParameter || - dbg_inst == - OpenCLDebugInfo100DebugTypeTemplateTemplateParameter; - }, - inst, word_index)) { + CHECK_DEBUG_OPERAND("Parent", CommonDebugInfoDebugTypeComposite, 6); + debug_inst = _.FindDef(inst->word(6)); + composite_type = + OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6)); + if (composite_type != OpenCLDebugInfo100Class && + composite_type != OpenCLDebugInfo100Structure) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << ext_inst_name() << ": " - << "expected operand Parameters must be " - << "DebugTypeTemplateParameter or " - << "DebugTypeTemplateTemplateParameter"; + << "expected operand Parent must be class or struct debug " + "type"; } + CHECK_OPERAND("Offset", SpvOpConstant, 7); + CHECK_OPERAND("Size", SpvOpConstant, 8); + CHECK_CONST_UINT_OPERAND("Flags", 9); + break; } - break; - } - case OpenCLDebugInfo100DebugTypeTemplateParameter: { - CHECK_OPERAND("Name", SpvOpString, 5); - auto validate_actual_type = ValidateOperandDebugType( - _, "Actual Type", inst, 6, ext_inst_name, false); - if (validate_actual_type != SPV_SUCCESS) return validate_actual_type; - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == OpenCLDebugInfo100DebugInfoNone; - }, - inst, 7)) { - CHECK_OPERAND("Value", SpvOpConstant, 7); + case CommonDebugInfoDebugFunction: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_type = ValidateOperandDebugType(_, "Type", inst, 6, + ext_inst_name, false); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Linkage Name", SpvOpString, 11); + CHECK_CONST_UINT_OPERAND("Flags", 12); + CHECK_CONST_UINT_OPERAND("Scope Line", 13); + // NonSemantic.Shader.DebugInfo.100 doesn't include a reference to the + // OpFunction + if (vulkanDebugInfo) { + if (num_words == 15) { + CHECK_DEBUG_OPERAND("Declaration", + CommonDebugInfoDebugFunctionDeclaration, 14); + } + } else { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 14)) { + CHECK_OPERAND("Function", SpvOpFunction, 14); + } + if (num_words == 16) { + CHECK_DEBUG_OPERAND("Declaration", + CommonDebugInfoDebugFunctionDeclaration, 15); + } + } + break; } - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 8); - break; - } - case OpenCLDebugInfo100DebugGlobalVariable: { - CHECK_OPERAND("Name", SpvOpString, 5); - auto validate_type = - ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, false); - if (validate_type != SPV_SUCCESS) return validate_type; - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); - auto validate_scope = - ValidateOperandLexicalScope(_, "Scope", inst, 10, ext_inst_name); - if (validate_scope != SPV_SUCCESS) return validate_scope; - CHECK_OPERAND("Linkage Name", SpvOpString, 11); - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == OpenCLDebugInfo100DebugInfoNone; - }, - inst, 12)) { - auto* operand = _.FindDef(inst->word(12)); + case CommonDebugInfoDebugFunctionDeclaration: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_type = ValidateOperandDebugType(_, "Type", inst, 6, + ext_inst_name, false); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Linkage Name", SpvOpString, 11); + CHECK_CONST_UINT_OPERAND("Flags", 12); + break; + } + case CommonDebugInfoDebugLexicalBlock: { + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 5); + CHECK_CONST_UINT_OPERAND("Line", 6); + CHECK_CONST_UINT_OPERAND("Column", 7); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 8, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + if (num_words == 10) CHECK_OPERAND("Name", SpvOpString, 9); + break; + } + case CommonDebugInfoDebugScope: { + auto validate_scope = + ValidateOperandLexicalScope(_, "Scope", inst, 5, ext_inst_name); + if (validate_scope != SPV_SUCCESS) return validate_scope; + if (num_words == 7) { + CHECK_DEBUG_OPERAND("Inlined At", CommonDebugInfoDebugInlinedAt, 6); + } + break; + } + case CommonDebugInfoDebugLocalVariable: { + CHECK_OPERAND("Name", SpvOpString, 5); + // TODO: We need a spec discussion that we have to allow local + // variable types to have template parameter. + auto validate_type = + ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_CONST_UINT_OPERAND("Flags", 11); + if (num_words == 13) { + CHECK_CONST_UINT_OPERAND("ArgNumber", 12); + } + break; + } + case CommonDebugInfoDebugDeclare: { + CHECK_DEBUG_OPERAND("Local Variable", + CommonDebugInfoDebugLocalVariable, 5); + auto* operand = _.FindDef(inst->word(6)); if (operand->opcode() != SpvOpVariable && - operand->opcode() != SpvOpConstant) { + operand->opcode() != SpvOpFunctionParameter) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << ext_inst_name() << ": " << "expected operand Variable must be a result id of " - "OpVariable or OpConstant or DebugInfoNone"; + "OpVariable or OpFunctionParameter"; } - } - if (num_words == 15) { - CHECK_DEBUG_OPERAND("Static Member Declaration", - OpenCLDebugInfo100DebugTypeMember, 14); - } - break; - } - case OpenCLDebugInfo100DebugInlinedAt: { - auto validate_scope = - ValidateOperandLexicalScope(_, "Scope", inst, 6, ext_inst_name); - if (validate_scope != SPV_SUCCESS) return validate_scope; - if (num_words == 8) { - CHECK_DEBUG_OPERAND("Inlined", OpenCLDebugInfo100DebugInlinedAt, 7); - } - break; - } - case OpenCLDebugInfo100DebugValue: { - CHECK_DEBUG_OPERAND("Local Variable", - OpenCLDebugInfo100DebugLocalVariable, 5); - CHECK_DEBUG_OPERAND("Expression", OpenCLDebugInfo100DebugExpression, 7); - for (uint32_t word_index = 8; word_index < num_words; ++word_index) { - // TODO: The following code simply checks if it is a const int scalar - // or a DebugLocalVariable or DebugGlobalVariable, but we have to - // check it using the same validation for Indexes of OpAccessChain. - if (!IsConstWithIntScalarType(_, inst, word_index) && - !IsDebugVariableWithIntScalarType(_, inst, word_index)) { + CHECK_DEBUG_OPERAND("Expression", CommonDebugInfoDebugExpression, 7); + + if (vulkanDebugInfo) { + for (uint32_t word_index = 8; word_index < num_words; + ++word_index) { + auto index_inst = _.FindDef(inst->word(word_index)); + auto type_id = index_inst != nullptr ? index_inst->type_id() : 0; + if (type_id == 0 || !IsIntScalar(_, type_id, false, false)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected index must be scalar integer"; + } + } + break; + } + case CommonDebugInfoDebugExpression: { + for (uint32_t word_index = 5; word_index < num_words; ++word_index) { + CHECK_DEBUG_OPERAND("Operation", CommonDebugInfoDebugOperation, + word_index); + } + break; + } + case CommonDebugInfoDebugTypeTemplate: { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugTypeComposite || + dbg_inst == CommonDebugInfoDebugFunction; + }, + inst, 5)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": expected operand Indexes is " - << "OpConstant, DebugGlobalVariable, or " - << "type is OpConstant with an integer scalar type"; + << ext_inst_name() << ": " + << "expected operand Target must be DebugTypeComposite " + << "or DebugFunction"; } + for (uint32_t word_index = 6; word_index < num_words; ++word_index) { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == + CommonDebugInfoDebugTypeTemplateParameter || + dbg_inst == + CommonDebugInfoDebugTypeTemplateTemplateParameter; + }, + inst, word_index)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Parameters must be " + << "DebugTypeTemplateParameter or " + << "DebugTypeTemplateTemplateParameter"; + } + } + break; } - break; - } + case CommonDebugInfoDebugTypeTemplateParameter: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_actual_type = ValidateOperandDebugType( + _, "Actual Type", inst, 6, ext_inst_name, false); + if (validate_actual_type != SPV_SUCCESS) return validate_actual_type; + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 7)) { + CHECK_OPERAND("Value", SpvOpConstant, 7); + } + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 8); + CHECK_CONST_UINT_OPERAND("Line", 9); + CHECK_CONST_UINT_OPERAND("Column", 10); + break; + } + case CommonDebugInfoDebugGlobalVariable: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_type = ValidateOperandDebugType(_, "Type", inst, 6, + ext_inst_name, false); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_scope = + ValidateOperandLexicalScope(_, "Scope", inst, 10, ext_inst_name); + if (validate_scope != SPV_SUCCESS) return validate_scope; + CHECK_OPERAND("Linkage Name", SpvOpString, 11); + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 12)) { + auto* operand = _.FindDef(inst->word(12)); + if (operand->opcode() != SpvOpVariable && + operand->opcode() != SpvOpConstant) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Variable must be a result id of " + "OpVariable or OpConstant or DebugInfoNone"; + } + } + if (num_words == 15) { + CHECK_DEBUG_OPERAND("Static Member Declaration", + CommonDebugInfoDebugTypeMember, 14); + } + break; + } + case CommonDebugInfoDebugInlinedAt: { + CHECK_CONST_UINT_OPERAND("Line", 5); + auto validate_scope = + ValidateOperandLexicalScope(_, "Scope", inst, 6, ext_inst_name); + if (validate_scope != SPV_SUCCESS) return validate_scope; + if (num_words == 8) { + CHECK_DEBUG_OPERAND("Inlined", CommonDebugInfoDebugInlinedAt, 7); + } + break; + } + case CommonDebugInfoDebugValue: { + CHECK_DEBUG_OPERAND("Local Variable", + CommonDebugInfoDebugLocalVariable, 5); + CHECK_DEBUG_OPERAND("Expression", CommonDebugInfoDebugExpression, 7); - // TODO: Add validation rules for remaining cases as well. - case OpenCLDebugInfo100DebugTypePtrToMember: - case OpenCLDebugInfo100DebugTypeTemplateTemplateParameter: - case OpenCLDebugInfo100DebugTypeTemplateParameterPack: - case OpenCLDebugInfo100DebugLexicalBlockDiscriminator: - case OpenCLDebugInfo100DebugInlinedVariable: - case OpenCLDebugInfo100DebugMacroDef: - case OpenCLDebugInfo100DebugMacroUndef: - case OpenCLDebugInfo100DebugImportedEntity: - break; - case OpenCLDebugInfo100InstructionsMax: - assert(0); - break; + for (uint32_t word_index = 8; word_index < num_words; ++word_index) { + // TODO: The following code simply checks if it is a const int + // scalar or a DebugLocalVariable or DebugGlobalVariable, but we + // have to check it using the same validation for Indexes of + // OpAccessChain. + if (!IsConstWithIntScalarType(_, inst, word_index) && + !IsDebugVariableWithIntScalarType(_, inst, word_index)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": expected operand Indexes is " + << "OpConstant, DebugGlobalVariable, or " + << "type is OpConstant with an integer scalar type"; + } + } + break; + } + + // TODO: Add validation rules for remaining cases as well. + case CommonDebugInfoDebugTypePtrToMember: + case CommonDebugInfoDebugTypeTemplateTemplateParameter: + case CommonDebugInfoDebugTypeTemplateParameterPack: + case CommonDebugInfoDebugLexicalBlockDiscriminator: + case CommonDebugInfoDebugInlinedVariable: + case CommonDebugInfoDebugMacroDef: + case CommonDebugInfoDebugMacroUndef: + case CommonDebugInfoDebugImportedEntity: + break; + case CommonDebugInfoInstructionsMax: + assert(0); + break; + } } } else if (ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) { auto import_inst = _.FindDef(inst->GetOperandAs(2)); - const std::string name(reinterpret_cast( - import_inst->words().data() + import_inst->operands()[1].offset)); + const std::string name = import_inst->GetOperandAs(1); const std::string reflection = "NonSemantic.ClspvReflection."; char* end_ptr; auto version_string = name.substr(reflection.size()); diff --git a/source/val/validate_function.cpp b/source/val/validate_function.cpp index 596186bb..0ccf5a9e 100644 --- a/source/val/validate_function.cpp +++ b/source/val/validate_function.cpp @@ -58,16 +58,16 @@ spv_result_t ValidateFunction(ValidationState_t& _, const Instruction* inst) { const auto function_type = _.FindDef(function_type_id); if (!function_type || SpvOpTypeFunction != function_type->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpFunction Function Type '" << _.getIdName(function_type_id) - << "' is not a function type."; + << "OpFunction Function Type " << _.getIdName(function_type_id) + << " is not a function type."; } const auto return_id = function_type->GetOperandAs(1); if (return_id != inst->type_id()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpFunction Result Type '" << _.getIdName(inst->type_id()) - << "' does not match the Function Type's return type '" - << _.getIdName(return_id) << "'."; + << "OpFunction Result Type " << _.getIdName(inst->type_id()) + << " does not match the Function Type's return type " + << _.getIdName(return_id) << "."; } const std::vector acceptable = { @@ -141,14 +141,14 @@ spv_result_t ValidateFunctionParameter(ValidationState_t& _, _.FindDef(function_type->GetOperandAs(param_index + 2)); if (!param_type || inst->type_id() != param_type->id()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpFunctionParameter Result Type '" + << "OpFunctionParameter Result Type " << _.getIdName(inst->type_id()) - << "' does not match the OpTypeFunction parameter " + << " does not match the OpTypeFunction parameter " "type of the same index."; } - // Validate that PhysicalStorageBufferEXT have one of Restrict, Aliased, - // RestrictPointerEXT, or AliasedPointerEXT. + // Validate that PhysicalStorageBuffer have one of Restrict, Aliased, + // RestrictPointer, or AliasedPointer. auto param_nonarray_type_id = param_type->id(); while (_.GetIdOpcode(param_nonarray_type_id) == SpvOpTypeArray) { param_nonarray_type_id = @@ -157,7 +157,7 @@ spv_result_t ValidateFunctionParameter(ValidationState_t& _, if (_.GetIdOpcode(param_nonarray_type_id) == SpvOpTypePointer) { auto param_nonarray_type = _.FindDef(param_nonarray_type_id); if (param_nonarray_type->GetOperandAs(1u) == - SpvStorageClassPhysicalStorageBufferEXT) { + SpvStorageClassPhysicalStorageBuffer) { // check for Aliased or Restrict const auto& decorations = _.id_decorations(inst->id()); @@ -174,14 +174,14 @@ spv_result_t ValidateFunctionParameter(ValidationState_t& _, if (!foundAliased && !foundRestrict) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpFunctionParameter " << inst->id() - << ": expected Aliased or Restrict for PhysicalStorageBufferEXT " + << ": expected Aliased or Restrict for PhysicalStorageBuffer " "pointer."; } if (foundAliased && foundRestrict) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpFunctionParameter " << inst->id() << ": can't specify both Aliased and Restrict for " - "PhysicalStorageBufferEXT pointer."; + "PhysicalStorageBuffer pointer."; } } else { const auto pointee_type_id = @@ -189,31 +189,31 @@ spv_result_t ValidateFunctionParameter(ValidationState_t& _, const auto pointee_type = _.FindDef(pointee_type_id); if (SpvOpTypePointer == pointee_type->opcode() && pointee_type->GetOperandAs(1u) == - SpvStorageClassPhysicalStorageBufferEXT) { - // check for AliasedPointerEXT/RestrictPointerEXT + SpvStorageClassPhysicalStorageBuffer) { + // check for AliasedPointer/RestrictPointer const auto& decorations = _.id_decorations(inst->id()); bool foundAliased = std::any_of( decorations.begin(), decorations.end(), [](const Decoration& d) { - return SpvDecorationAliasedPointerEXT == d.dec_type(); + return SpvDecorationAliasedPointer == d.dec_type(); }); bool foundRestrict = std::any_of( decorations.begin(), decorations.end(), [](const Decoration& d) { - return SpvDecorationRestrictPointerEXT == d.dec_type(); + return SpvDecorationRestrictPointer == d.dec_type(); }); if (!foundAliased && !foundRestrict) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpFunctionParameter " << inst->id() - << ": expected AliasedPointerEXT or RestrictPointerEXT for " - "PhysicalStorageBufferEXT pointer."; + << ": expected AliasedPointer or RestrictPointer for " + "PhysicalStorageBuffer pointer."; } if (foundAliased && foundRestrict) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpFunctionParameter " << inst->id() - << ": can't specify both AliasedPointerEXT and " - "RestrictPointerEXT for PhysicalStorageBufferEXT pointer."; + << ": can't specify both AliasedPointer and " + "RestrictPointer for PhysicalStorageBuffer pointer."; } } } @@ -228,17 +228,16 @@ spv_result_t ValidateFunctionCall(ValidationState_t& _, const auto function = _.FindDef(function_id); if (!function || SpvOpFunction != function->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpFunctionCall Function '" << _.getIdName(function_id) - << "' is not a function."; + << "OpFunctionCall Function " << _.getIdName(function_id) + << " is not a function."; } auto return_type = _.FindDef(function->type_id()); if (!return_type || return_type->id() != inst->type_id()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpFunctionCall Result Type '" - << _.getIdName(inst->type_id()) - << "'s type does not match Function '" - << _.getIdName(return_type->id()) << "'s return type."; + << "OpFunctionCall Result Type " << _.getIdName(inst->type_id()) + << "s type does not match Function " + << _.getIdName(return_type->id()) << "s return type."; } const auto function_type_id = function->GetOperandAs(3); @@ -280,9 +279,9 @@ spv_result_t ValidateFunctionCall(ValidationState_t& _, if (!_.options()->before_hlsl_legalization || !DoPointeesLogicallyMatch(argument_type, parameter_type, _)) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpFunctionCall Argument '" << _.getIdName(argument_id) - << "'s type does not match Function '" - << _.getIdName(parameter_type_id) << "'s parameter type."; + << "OpFunctionCall Argument " << _.getIdName(argument_id) + << "s type does not match Function " + << _.getIdName(parameter_type_id) << "s parameter type."; } } @@ -300,7 +299,7 @@ spv_result_t ValidateFunctionCall(ValidationState_t& _, // These are always allowed. break; case SpvStorageClassStorageBuffer: - if (!_.features().variable_pointers_storage_buffer) { + if (!_.features().variable_pointers) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "StorageBuffer pointer operand " << _.getIdName(argument_id) @@ -316,11 +315,10 @@ spv_result_t ValidateFunctionCall(ValidationState_t& _, // Validate memory object declaration requirements. if (argument->opcode() != SpvOpVariable && argument->opcode() != SpvOpFunctionParameter) { - const bool ssbo_vptr = - _.features().variable_pointers_storage_buffer && - sc == SpvStorageClassStorageBuffer; - const bool wg_vptr = - _.features().variable_pointers && sc == SpvStorageClassWorkgroup; + const bool ssbo_vptr = _.features().variable_pointers && + sc == SpvStorageClassStorageBuffer; + const bool wg_vptr = _.HasCapability(SpvCapabilityVariablePointers) && + sc == SpvStorageClassWorkgroup; const bool uc_ptr = sc == SpvStorageClassUniformConstant; if (!ssbo_vptr && !wg_vptr && !uc_ptr) { return _.diag(SPV_ERROR_INVALID_ID, inst) diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp index bdb2516e..9c7c8c1c 100644 --- a/source/val/validate_image.cpp +++ b/source/val/validate_image.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Google Inc. +// Copyright (c) 2017 Google Inc. // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights // reserved. // @@ -20,6 +20,7 @@ #include "source/diagnostic.h" #include "source/opcode.h" +#include "source/spirv_constant.h" #include "source/spirv_target_env.h" #include "source/util/bitutils.h" #include "source/val/instruction.h" @@ -66,6 +67,12 @@ bool CheckAllImageOperandsHandled() { case SpvImageOperandsVolatileTexelKHRMask: case SpvImageOperandsSignExtendMask: case SpvImageOperandsZeroExtendMask: + // TODO(jaebaek): Move this line properly after handling image offsets + // operand. This line temporarily fixes CI failure that + // blocks other PRs. + // https://github.com/KhronosGroup/SPIRV-Tools/issues/4565 + case SpvImageOperandsOffsetsMask: + case SpvImageOperandsNontemporalMask: return true; } return false; @@ -253,7 +260,8 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, mask & ~uint32_t(SpvImageOperandsNonPrivateTexelKHRMask | SpvImageOperandsVolatileTexelKHRMask | SpvImageOperandsSignExtendMask | - SpvImageOperandsZeroExtendMask); + SpvImageOperandsZeroExtendMask | + SpvImageOperandsNontemporalMask); size_t expected_num_image_operand_words = spvtools::utils::CountSetBits(mask_bits_having_operands); if (mask & SpvImageOperandsGradMask) { @@ -281,13 +289,14 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, // the module to be invalid. if (mask == 0) return SPV_SUCCESS; - if (spvtools::utils::CountSetBits( - mask & (SpvImageOperandsOffsetMask | SpvImageOperandsConstOffsetMask | - SpvImageOperandsConstOffsetsMask)) > 1) { + if (spvtools::utils::CountSetBits(mask & (SpvImageOperandsOffsetMask | + SpvImageOperandsConstOffsetMask | + SpvImageOperandsConstOffsetsMask | + SpvImageOperandsOffsetsMask)) > 1) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << _.VkErrorID(4662) - << "Image Operands Offset, ConstOffset, ConstOffsets cannot be used " - << "together"; + << "Image Operands Offset, ConstOffset, ConstOffsets, Offsets " + "cannot be used together"; } const bool is_implicit_lod = IsImplicitLod(opcode); @@ -442,7 +451,8 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, << " components, but given " << offset_size; } - if (spvIsVulkanEnv(_.context()->target_env)) { + if (!_.options()->before_hlsl_legalization && + spvIsVulkanEnv(_.context()->target_env)) { if (opcode != SpvOpImageGather && opcode != SpvOpImageDrefGather && opcode != SpvOpImageSparseGather && opcode != SpvOpImageSparseDrefGather) { @@ -493,7 +503,7 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, if (!_.IsIntVectorType(component_type) || _.GetDimension(component_type) != 2) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image Operand ConstOffsets array componenets to be " + << "Expected Image Operand ConstOffsets array components to be " "int vectors of size 2"; } @@ -619,68 +629,75 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, // setup. } + if (mask & SpvImageOperandsOffsetsMask) { + // TODO: add validation + } + + if (mask & SpvImageOperandsNontemporalMask) { + // Checked elsewhere: SPIR-V 1.6 version or later. + } + return SPV_SUCCESS; } -// Checks some of the validation rules which are common to multiple opcodes. -spv_result_t ValidateImageCommon(ValidationState_t& _, const Instruction* inst, - const ImageTypeInfo& info) { - const SpvOp opcode = inst->opcode(); - if (IsProj(opcode)) { - if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D && - info.dim != SpvDimRect) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image 'Dim' parameter to be 1D, 2D, 3D or Rect"; - } - - if (info.multisampled != 0) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image 'MS' parameter to be 0"; - } - - if (info.arrayed != 0) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image 'arrayed' parameter to be 0"; - } +// Validate OpImage*Proj* instructions +spv_result_t ValidateImageProj(ValidationState_t& _, const Instruction* inst, + const ImageTypeInfo& info) { + if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D && + info.dim != SpvDimRect) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Dim' parameter to be 1D, 2D, 3D or Rect"; } - if (opcode == SpvOpImageRead || opcode == SpvOpImageSparseRead || - opcode == SpvOpImageWrite) { - if (info.sampled == 0) { - } else if (info.sampled == 2) { - if (info.dim == SpvDim1D && !_.HasCapability(SpvCapabilityImage1D)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Capability Image1D is required to access storage image"; - } else if (info.dim == SpvDimRect && - !_.HasCapability(SpvCapabilityImageRect)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Capability ImageRect is required to access storage image"; - } else if (info.dim == SpvDimBuffer && - !_.HasCapability(SpvCapabilityImageBuffer)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Capability ImageBuffer is required to access storage image"; - } else if (info.dim == SpvDimCube && info.arrayed == 1 && - !_.HasCapability(SpvCapabilityImageCubeArray)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Capability ImageCubeArray is required to access " - << "storage image"; - } + if (info.multisampled != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'MS' parameter to be 0"; + } - if (info.multisampled == 1 && - !_.HasCapability(SpvCapabilityImageMSArray)) { -#if 0 - // TODO(atgoo@github.com) The description of this rule in the spec - // is unclear and Glslang doesn't declare ImageMSArray. Need to clarify - // and reenable. - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Capability ImageMSArray is required to access storage " - << "image"; -#endif - } - } else { + if (info.arrayed != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'arrayed' parameter to be 0"; + } + + return SPV_SUCCESS; +} + +// Validate OpImage*Read and OpImage*Write instructions +spv_result_t ValidateImageReadWrite(ValidationState_t& _, + const Instruction* inst, + const ImageTypeInfo& info) { + if (info.sampled == 2) { + if (info.dim == SpvDim1D && !_.HasCapability(SpvCapabilityImage1D)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image 'Sampled' parameter to be 0 or 2"; + << "Capability Image1D is required to access storage image"; + } else if (info.dim == SpvDimRect && + !_.HasCapability(SpvCapabilityImageRect)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability ImageRect is required to access storage image"; + } else if (info.dim == SpvDimBuffer && + !_.HasCapability(SpvCapabilityImageBuffer)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability ImageBuffer is required to access storage image"; + } else if (info.dim == SpvDimCube && info.arrayed == 1 && + !_.HasCapability(SpvCapabilityImageCubeArray)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability ImageCubeArray is required to access " + << "storage image"; } + + if (info.multisampled == 1 && !_.HasCapability(SpvCapabilityImageMSArray)) { +#if 0 + // TODO(atgoo@github.com) The description of this rule in the spec + // is unclear and Glslang doesn't declare ImageMSArray. Need to clarify + // and reenable. + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability ImageMSArray is required to access storage " + << "image"; +#endif + } + } else if (info.sampled != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled' parameter to be 0 or 2"; } return SPV_SUCCESS; @@ -795,7 +812,8 @@ spv_result_t ValidateTypeImage(ValidationState_t& _, const Instruction* inst) { } } - // Dim is checked elsewhere. + // Universal checks on image type operands + // Dim and Format and Access Qualifier are checked elsewhere. if (info.depth > 2) { return _.diag(SPV_ERROR_INVALID_DATA, inst) @@ -807,6 +825,35 @@ spv_result_t ValidateTypeImage(ValidationState_t& _, const Instruction* inst) { << "Invalid Arrayed " << info.arrayed << " (must be 0 or 1)"; } + if (info.multisampled > 1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Invalid MS " << info.multisampled << " (must be 0 or 1)"; + } + + if (info.sampled > 2) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Invalid Sampled " << info.sampled << " (must be 0, 1 or 2)"; + } + + if (info.dim == SpvDimSubpassData) { + if (info.sampled != 2) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(6214) << "Dim SubpassData requires Sampled to be 2"; + } + + if (info.format != SpvImageFormatUnknown) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Dim SubpassData requires format Unknown"; + } + } else { + if (info.multisampled && (info.sampled == 2) && + !_.HasCapability(SpvCapabilityStorageImageMultisample)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability StorageImageMultisample is required when using " + "multisampled storage image"; + } + } + if (spvIsOpenCLEnv(target_env)) { if ((info.arrayed == 1) && (info.dim != SpvDim1D) && (info.dim != SpvDim2D)) { @@ -814,23 +861,22 @@ spv_result_t ValidateTypeImage(ValidationState_t& _, const Instruction* inst) { << "In the OpenCL environment, Arrayed may only be set to 1 " << "when Dim is either 1D or 2D."; } - } - if (info.multisampled > 1) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Invalid MS " << info.multisampled << " (must be 0 or 1)"; - } - - if (spvIsOpenCLEnv(target_env)) { if (info.multisampled != 0) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "MS must be 0 in the OpenCL environment."; } - } - if (info.sampled > 2) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Invalid Sampled " << info.sampled << " (must be 0, 1 or 2)"; + if (info.sampled != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Sampled must be 0 in the OpenCL environment."; + } + + if (info.access_qualifier == SpvAccessQualifierMax) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "In the OpenCL environment, the optional Access Qualifier" + << " must be present."; + } } if (spvIsVulkanEnv(target_env)) { @@ -839,43 +885,10 @@ spv_result_t ValidateTypeImage(ValidationState_t& _, const Instruction* inst) { << _.VkErrorID(4657) << "Sampled must be 1 or 2 in the Vulkan environment."; } - } - if (spvIsOpenCLEnv(_.context()->target_env)) { - if (info.sampled != 0) { + if (info.dim == SpvDimSubpassData && info.arrayed != 0) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Sampled must be 0 in the OpenCL environment."; - } - } - - if (info.dim == SpvDimSubpassData) { - if (info.sampled != 2) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Dim SubpassData requires Sampled to be 2"; - } - - if (info.format != SpvImageFormatUnknown) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Dim SubpassData requires format Unknown"; - } - } - - // Format and Access Qualifier are also checked elsewhere. - - if (spvIsOpenCLEnv(_.context()->target_env)) { - if (info.access_qualifier == SpvAccessQualifierMax) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "In the OpenCL environment, the optional Access Qualifier" - << " must be present."; - } - } - - if (info.multisampled && (info.sampled == 2) && - (info.dim != SpvDimSubpassData)) { - if (!_.HasCapability(SpvCapabilityStorageImageMultisample)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Capability StorageImageMultisample is required when using " - "multisampled storage image"; + << _.VkErrorID(6214) << "Dim SubpassData requires Arrayed to be 0"; } } @@ -904,10 +917,17 @@ spv_result_t ValidateTypeSampledImage(ValidationState_t& _, "operand set to 0 or 1"; } + // This covers both OpTypeSampledImage and OpSampledImage. + if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 6) && info.dim == SpvDimBuffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "In SPIR-V 1.6 or later, sampled image dimension must not be " + "Buffer"; + } + return SPV_SUCCESS; } -bool IsAllowedSampledImageOperand(SpvOp opcode) { +bool IsAllowedSampledImageOperand(SpvOp opcode, ValidationState_t& _) { switch (opcode) { case SpvOpSampledImage: case SpvOpImageSampleImplicitLod: @@ -930,6 +950,9 @@ bool IsAllowedSampledImageOperand(SpvOp opcode) { case SpvOpImageSparseDrefGather: case SpvOpCopyObject: return true; + case SpvOpStore: + if (_.HasCapability(SpvCapabilityBindlessTextureNV)) return true; + return false; default: return false; } @@ -960,8 +983,9 @@ spv_result_t ValidateSampledImage(ValidationState_t& _, if (spvIsVulkanEnv(_.context()->target_env)) { if (info.sampled != 1) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image 'Sampled' parameter to be 1 " - << "for Vulkan environment."; + << _.VkErrorID(6671) + << "Expected Image 'Sampled' parameter to be 1 for Vulkan " + "environment."; } } else { if (info.sampled != 0 && info.sampled != 1) { @@ -996,11 +1020,11 @@ spv_result_t ValidateSampledImage(ValidationState_t& _, << "All OpSampledImage instructions must be in the same block " "in " "which their Result are consumed. OpSampledImage Result " - "Type '" + "Type " << _.getIdName(inst->id()) - << "' has a consumer in a different basic " - "block. The consumer instruction is '" - << _.getIdName(consumer_instr->id()) << "'."; + << " has a consumer in a different basic " + "block. The consumer instruction is " + << _.getIdName(consumer_instr->id()) << "."; } if (consumer_opcode == SpvOpPhi || consumer_opcode == SpvOpSelect) { @@ -1009,21 +1033,21 @@ spv_result_t ValidateSampledImage(ValidationState_t& _, "as " "operands of Op" << spvOpcodeString(static_cast(consumer_opcode)) << "." - << " Found result '" << _.getIdName(inst->id()) - << "' as an operand of '" - << _.getIdName(consumer_instr->id()) << "'."; + << " Found result " << _.getIdName(inst->id()) + << " as an operand of " << _.getIdName(consumer_instr->id()) + << "."; } - if (!IsAllowedSampledImageOperand(consumer_opcode)) { + if (!IsAllowedSampledImageOperand(consumer_opcode, _)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "Result from OpSampledImage instruction must not appear " "as operand for Op" << spvOpcodeString(static_cast(consumer_opcode)) - << ", since it is not specificed as taking an " + << ", since it is not specified as taking an " << "OpTypeSampledImage." - << " Found result '" << _.getIdName(inst->id()) - << "' as an operand of '" - << _.getIdName(consumer_instr->id()) << "'."; + << " Found result " << _.getIdName(inst->id()) + << " as an operand of " << _.getIdName(consumer_instr->id()) + << "."; } } } @@ -1180,7 +1204,9 @@ spv_result_t ValidateImageLod(ValidationState_t& _, const Instruction* inst) { << "Corrupt image type definition"; } - if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result; + if (IsProj(opcode)) { + if (spv_result_t result = ValidateImageProj(_, inst, info)) return result; + } if (info.multisampled) { // When using image operands, the Sample image operand is required if and @@ -1243,6 +1269,27 @@ spv_result_t ValidateImageLod(ValidationState_t& _, const Instruction* inst) { return SPV_SUCCESS; } +// Validates anything OpImage*Dref* instruction +spv_result_t ValidateImageDref(ValidationState_t& _, const Instruction* inst, + const ImageTypeInfo& info) { + const uint32_t dref_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Dref to be of 32-bit float type"; + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (info.dim == SpvDim3D) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4777) + << "In Vulkan, OpImage*Dref* instructions must not use images " + "with a 3D Dim"; + } + } + + return SPV_SUCCESS; +} + spv_result_t ValidateImageDrefLod(ValidationState_t& _, const Instruction* inst) { const SpvOp opcode = inst->opcode(); @@ -1270,7 +1317,9 @@ spv_result_t ValidateImageDrefLod(ValidationState_t& _, << "Corrupt image type definition"; } - if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result; + if (IsProj(opcode)) { + if (spv_result_t result = ValidateImageProj(_, inst, info)) return result; + } if (info.multisampled) { // When using image operands, the Sample image operand is required if and @@ -1300,11 +1349,7 @@ spv_result_t ValidateImageDrefLod(ValidationState_t& _, << " components, but given only " << actual_coord_size; } - const uint32_t dref_type = _.GetOperandTypeId(inst, 4); - if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Dref to be of 32-bit float type"; - } + if (spv_result_t result = ValidateImageDref(_, inst, info)) return result; if (spv_result_t result = ValidateImageOperands(_, inst, info, /* word_index = */ 7)) @@ -1439,7 +1484,8 @@ spv_result_t ValidateImageGather(ValidationState_t& _, if (info.dim != SpvDim2D && info.dim != SpvDimCube && info.dim != SpvDimRect) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image 'Dim' cannot be Cube"; + << _.VkErrorID(4777) + << "Expected Image 'Dim' to be 2D, Cube, or Rect"; } const uint32_t coord_type = _.GetOperandTypeId(inst, 3); @@ -1475,11 +1521,7 @@ spv_result_t ValidateImageGather(ValidationState_t& _, } else { assert(opcode == SpvOpImageDrefGather || opcode == SpvOpImageSparseDrefGather); - const uint32_t dref_type = _.GetOperandTypeId(inst, 4); - if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Dref to be of 32-bit float type"; - } + if (spv_result_t result = ValidateImageDref(_, inst, info)) return result; } if (spv_result_t result = @@ -1509,8 +1551,8 @@ spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) { if (spvIsVulkanEnv(target_env)) { if (_.GetDimension(actual_result_type) != 4) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected " << GetActualResultTypeStr(opcode) - << " to have 4 components"; + << _.VkErrorID(4780) << "Expected " + << GetActualResultTypeStr(opcode) << " to have 4 components"; } } // Check OpenCL below, after we get the image info. @@ -1547,6 +1589,13 @@ spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) { << " to have 4 components"; } } + + const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5); + if (mask & SpvImageOperandsConstOffsetMask) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "ConstOffset image operand not allowed " + << "in the OpenCL environment."; + } } if (info.dim == SpvDimSubpassData) { @@ -1572,7 +1621,8 @@ spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) { } } - if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result; + if (spv_result_t result = ValidateImageReadWrite(_, inst, info)) + return result; const uint32_t coord_type = _.GetOperandTypeId(inst, 3); if (!_.IsIntScalarOrVectorType(coord_type)) { @@ -1597,16 +1647,6 @@ spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) { } } - const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5); - - if (mask & SpvImageOperandsConstOffsetMask) { - if (spvIsOpenCLEnv(_.context()->target_env)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "ConstOffset image operand not allowed " - << "in the OpenCL environment."; - } - } - if (spv_result_t result = ValidateImageOperands(_, inst, info, /* word_index = */ 6)) return result; @@ -1632,7 +1672,8 @@ spv_result_t ValidateImageWrite(ValidationState_t& _, const Instruction* inst) { << "Image 'Dim' cannot be SubpassData"; } - if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result; + if (spv_result_t result = ValidateImageReadWrite(_, inst, info)) + return result; const uint32_t coord_type = _.GetOperandTypeId(inst, 1); if (!_.IsIntScalarOrVectorType(coord_type)) { @@ -1648,8 +1689,7 @@ spv_result_t ValidateImageWrite(ValidationState_t& _, const Instruction* inst) { << " components, but given only " << actual_coord_size; } - // TODO(atgoo@github.com) The spec doesn't explicitely say what the type - // of texel should be. + // because it needs to match with 'Sampled Type' the Texel can't be a boolean const uint32_t texel_type = _.GetOperandTypeId(inst, 2); if (!_.IsIntScalarOrVectorType(texel_type) && !_.IsFloatScalarOrVectorType(texel_type)) { @@ -1657,14 +1697,6 @@ spv_result_t ValidateImageWrite(ValidationState_t& _, const Instruction* inst) { << "Expected Texel to be int or float vector or scalar"; } -#if 0 - // TODO: See above. - if (_.GetDimension(texel_type) != 4) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Texel to have 4 components"; - } -#endif - if (_.GetIdOpcode(info.sampled_type) != SpvOpTypeVoid) { const uint32_t texel_component_type = _.GetComponentType(texel_type); if (texel_component_type != info.sampled_type) { @@ -2057,11 +2089,13 @@ spv_result_t ImagePass(ValidationState_t& _, const Instruction* inst) { std::string* message) { const auto* models = state.GetExecutionModels(entry_point->id()); const auto* modes = state.GetExecutionModes(entry_point->id()); - if (models->find(SpvExecutionModelGLCompute) != models->end() && - modes->find(SpvExecutionModeDerivativeGroupLinearNV) == - modes->end() && - modes->find(SpvExecutionModeDerivativeGroupQuadsNV) == - modes->end()) { + if (models && + models->find(SpvExecutionModelGLCompute) != models->end() && + (!modes || + (modes->find(SpvExecutionModeDerivativeGroupLinearNV) == + modes->end() && + modes->find(SpvExecutionModeDerivativeGroupQuadsNV) == + modes->end()))) { if (message) { *message = std::string( diff --git a/source/val/validate_instruction.cpp b/source/val/validate_instruction.cpp index 9d395fb4..767c0cee 100644 --- a/source/val/validate_instruction.cpp +++ b/source/val/validate_instruction.cpp @@ -297,7 +297,7 @@ spv_result_t VersionCheck(ValidationState_t& _, const Instruction* inst) { } // OpTerminateInvocation is special because it is enabled by Shader - // capability, but also requries a extension and/or version check. + // capability, but also requires an extension and/or version check. const bool capability_check_is_sufficient = inst->opcode() != SpvOpTerminateInvocation; @@ -318,10 +318,9 @@ spv_result_t VersionCheck(ValidationState_t& _, const Instruction* inst) { if (module_version < min_version) { return _.diag(SPV_ERROR_WRONG_VERSION, inst) - << spvOpcodeString(opcode) << " requires " - << spvTargetEnvDescription( - static_cast(min_version)) - << " at minimum."; + << spvOpcodeString(opcode) << " requires SPIR-V version " + << SPV_SPIRV_VERSION_MAJOR_PART(min_version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(min_version) << " at minimum."; } } else if (!_.HasAnyOfExtensions(exts)) { // Otherwise, we only error out when no enabling extensions are @@ -407,7 +406,7 @@ spv_result_t LimitCheckSwitch(ValidationState_t& _, const Instruction* inst) { // The instruction syntax is as follows: // OpSwitch literal label literal label ... // literal,label pairs come after the first 2 operands. - // It is guaranteed at this point that num_operands is an even numner. + // It is guaranteed at this point that num_operands is an even number. size_t num_pairs = (inst->operands().size() - 2) / 2; const unsigned int num_pairs_limit = _.options()->universal_limits_.max_switch_branches; @@ -484,6 +483,22 @@ spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst) { if (auto error = LimitCheckNumVars(_, inst->id(), storage_class)) { return error; } + } else if (opcode == SpvOpSamplerImageAddressingModeNV) { + if (!_.HasCapability(SpvCapabilityBindlessTextureNV)) { + return _.diag(SPV_ERROR_MISSING_EXTENSION, inst) + << "OpSamplerImageAddressingModeNV supported only with extension " + "SPV_NV_bindless_texture"; + } + uint32_t bitwidth = inst->GetOperandAs(0); + if (_.samplerimage_variable_address_mode() != 0) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "OpSamplerImageAddressingModeNV should only be provided once"; + } + if (bitwidth != 32 && bitwidth != 64) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "OpSamplerImageAddressingModeNV bitwidth should be 64 or 32"; + } + _.set_samplerimage_variable_address_mode(bitwidth); } if (auto error = ReservedCheck(_, inst)) return error; diff --git a/source/val/validate_interfaces.cpp b/source/val/validate_interfaces.cpp index d16d48e2..7f2d6488 100644 --- a/source/val/validate_interfaces.cpp +++ b/source/val/validate_interfaces.cpp @@ -27,6 +27,10 @@ namespace spvtools { namespace val { namespace { +// Limit the number of checked locations to 4096. Multiplied by 4 to represent +// all the components. This limit is set to be well beyond practical use cases. +const uint32_t kMaxLocations = 4096 * 4; + // Returns true if \c inst is an input or output variable. bool is_interface_variable(const Instruction* inst, bool is_spv_1_4) { if (is_spv_1_4) { @@ -150,7 +154,7 @@ spv_result_t NumConsumedLocations(ValidationState_t& _, const Instruction* type, // 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"; + << _.VkErrorID(4918) << "Members cannot be assigned a location"; } // Structs consume locations equal to the sum of the locations consumed @@ -195,6 +199,10 @@ uint32_t NumConsumedComponents(ValidationState_t& _, const Instruction* type) { NumConsumedComponents(_, _.FindDef(type->GetOperandAs(1))); num_components *= type->GetOperandAs(2); break; + case SpvOpTypeArray: + // Skip the array. + return NumConsumedComponents(_, + _.FindDef(type->GetOperandAs(1))); default: // This is an error that is validated elsewhere. break; @@ -230,7 +238,7 @@ spv_result_t GetLocationsForVariable( uint32_t index = 0; bool has_patch = false; bool has_per_task_nv = false; - bool has_per_vertex_nv = false; + bool has_per_vertex_khr = false; for (auto& dec : _.id_decorations(variable->id())) { if (dec.dec_type() == SpvDecorationLocation) { if (has_location && dec.params()[0] != location) { @@ -264,8 +272,20 @@ spv_result_t GetLocationsForVariable( 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; + } else if (dec.dec_type() == SpvDecorationPerVertexKHR) { + if (!is_fragment) { + return _.diag(SPV_ERROR_INVALID_DATA, variable) + << _.VkErrorID(6777) + << "PerVertexKHR can only be applied to Fragment Execution " + "Models"; + } + if (type->opcode() != SpvOpTypeArray && + type->opcode() != SpvOpTypeRuntimeArray) { + return _.diag(SPV_ERROR_INVALID_DATA, variable) + << _.VkErrorID(6778) + << "PerVertexKHR must be declared as arrays"; + } + has_per_vertex_khr = true; } } @@ -290,7 +310,7 @@ spv_result_t GetLocationsForVariable( } break; case SpvExecutionModelFragment: - if (!is_output && has_per_vertex_nv) { + if (!is_output && has_per_vertex_khr) { is_arrayed = true; } break; @@ -318,8 +338,9 @@ spv_result_t GetLocationsForVariable( // 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) { + const auto vuid = (type->opcode() == SpvOpTypeStruct) ? 4917 : 4916; return _.diag(SPV_ERROR_INVALID_DATA, variable) - << "Variable must be decorated with a location"; + << _.VkErrorID(vuid) << "Variable must be decorated with a location"; } const std::string storage_class = is_output ? "output" : "input"; @@ -347,6 +368,11 @@ spv_result_t GetLocationsForVariable( uint32_t num_components = NumConsumedComponents(_, sub_type); uint32_t array_location = location + (num_locations * array_idx); uint32_t start = array_location * 4; + if (kMaxLocations <= start) { + // Too many locations, give up. + break; + } + uint32_t end = (array_location + num_locations) * 4; if (num_components != 0) { start += component; @@ -398,7 +424,7 @@ spv_result_t GetLocationsForVariable( auto where = member_locations.find(i - 1); if (where == member_locations.end()) { return _.diag(SPV_ERROR_INVALID_DATA, type) - << "Member index " << i - 1 + << _.VkErrorID(4919) << "Member index " << i - 1 << " is missing a location assignment"; } @@ -416,17 +442,41 @@ spv_result_t GetLocationsForVariable( } uint32_t start = location * 4; - uint32_t end = (location + num_locations) * 4; - if (num_components != 0) { - start += component; - end = location * 4 + component + num_components; + if (kMaxLocations <= start) { + // Too many locations, give up. + continue; } - 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; + + if (member->opcode() == SpvOpTypeArray && num_components >= 1 && + num_components < 4) { + // When an array has an element that takes less than a location in + // size, calculate the used locations in a strided manner. + for (uint32_t l = location; l < num_locations + location; ++l) { + for (uint32_t c = component; c < component + num_components; ++c) { + uint32_t check = 4 * l + c; + if (!locations->insert(check).second) { + return _.diag(SPV_ERROR_INVALID_DATA, entry_point) + << "Entry-point has conflicting " << storage_class + << " location assignment at location " << l + << ", component " << c; + } + } + } + } else { + // TODO: There is a hole here is the member is an array of 3- or + // 4-element vectors of 64-bit types. + 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; + } } } } @@ -439,6 +489,9 @@ spv_result_t ValidateLocations(ValidationState_t& _, const Instruction* entry_point) { // According to Vulkan 14.1 only the following execution models have // locations assigned. + // TODO(dneto): SPV_NV_ray_tracing also uses locations on interface variables, + // in other shader stages. Similarly, the *provisional* version of + // SPV_KHR_ray_tracing did as well, but not the final version. switch (entry_point->GetOperandAs(0)) { case SpvExecutionModelVertex: case SpvExecutionModelTessellationControl: @@ -454,6 +507,7 @@ spv_result_t ValidateLocations(ValidationState_t& _, std::unordered_set input_locations; std::unordered_set output_locations_index0; std::unordered_set output_locations_index1; + std::unordered_set seen; for (uint32_t i = 3; i < entry_point->operands().size(); ++i) { auto interface_id = entry_point->GetOperandAs(i); auto interface_var = _.FindDef(interface_id); @@ -462,6 +516,11 @@ spv_result_t ValidateLocations(ValidationState_t& _, storage_class != SpvStorageClassOutput) { continue; } + if (!seen.insert(interface_id).second) { + // Pre-1.4 an interface variable could be listed multiple times in an + // entry point. Validation for 1.4 or later is done elsewhere. + continue; + } auto locations = (storage_class == SpvStorageClassInput) ? &input_locations diff --git a/source/val/validate_layout.cpp b/source/val/validate_layout.cpp index b53f991e..6f951352 100644 --- a/source/val/validate_layout.cpp +++ b/source/val/validate_layout.cpp @@ -17,6 +17,7 @@ #include #include "DebugInfo.h" +#include "NonSemanticShaderDebugInfo100.h" #include "OpenCLDebugInfo100.h" #include "source/diagnostic.h" #include "source/opcode.h" @@ -37,17 +38,7 @@ spv_result_t ModuleScopedInstructions(ValidationState_t& _, const Instruction* inst, SpvOp opcode) { switch (opcode) { case SpvOpExtInst: - if (spvExtInstIsNonSemantic(inst->ext_inst_type())) { - // non-semantic extinst opcodes are allowed beginning in the types - // section, but since they must name a return type they cannot be the - // first instruction in the types section. Therefore check that we are - // already in it. - if (_.current_layout_section() < kLayoutTypes) { - return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) - << "Non-semantic OpExtInst must not appear before types " - << "section"; - } - } else if (spvExtInstIsDebugInfo(inst->ext_inst_type())) { + if (spvExtInstIsDebugInfo(inst->ext_inst_type())) { const uint32_t ext_inst_index = inst->word(4); bool local_debug_info = false; if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { @@ -59,6 +50,20 @@ spv_result_t ModuleScopedInstructions(ValidationState_t& _, ext_inst_key == OpenCLDebugInfo100DebugValue) { local_debug_info = true; } + } else if (inst->ext_inst_type() == + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + const NonSemanticShaderDebugInfo100Instructions ext_inst_key = + NonSemanticShaderDebugInfo100Instructions(ext_inst_index); + if (ext_inst_key == NonSemanticShaderDebugInfo100DebugScope || + ext_inst_key == NonSemanticShaderDebugInfo100DebugNoScope || + ext_inst_key == NonSemanticShaderDebugInfo100DebugDeclare || + ext_inst_key == NonSemanticShaderDebugInfo100DebugValue || + ext_inst_key == NonSemanticShaderDebugInfo100DebugLine || + ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine || + ext_inst_key == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) { + local_debug_info = true; + } } else { const DebugInfoInstructions ext_inst_key = DebugInfoInstructions(ext_inst_index); @@ -94,6 +99,16 @@ spv_result_t ModuleScopedInstructions(ValidationState_t& _, << "declarations)"; } } + } else if (spvExtInstIsNonSemantic(inst->ext_inst_type())) { + // non-semantic extinst opcodes are allowed beginning in the types + // section, but since they must name a return type they cannot be the + // first instruction in the types section. Therefore check that we are + // already in it. + if (_.current_layout_section() < kLayoutTypes) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Non-semantic OpExtInst must not appear before types " + << "section"; + } } else { // otherwise they must be used in a block if (_.current_layout_section() < kLayoutFunctionDefinitions) { @@ -230,20 +245,7 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _, break; case SpvOpExtInst: - if (spvExtInstIsNonSemantic(inst->ext_inst_type())) { - // non-semantic extinst opcodes are allowed beginning in the types - // section, but must either be placed outside a function declaration, - // or inside a block. - if (_.current_layout_section() < kLayoutTypes) { - return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) - << "Non-semantic OpExtInst must not appear before types " - << "section"; - } else if (_.in_function_body() && _.in_block() == false) { - return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) - << "Non-semantic OpExtInst within function definition must " - "appear in a block"; - } - } else if (spvExtInstIsDebugInfo(inst->ext_inst_type())) { + if (spvExtInstIsDebugInfo(inst->ext_inst_type())) { const uint32_t ext_inst_index = inst->word(4); bool local_debug_info = false; if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { @@ -255,6 +257,20 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _, ext_inst_key == OpenCLDebugInfo100DebugValue) { local_debug_info = true; } + } else if (inst->ext_inst_type() == + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + const NonSemanticShaderDebugInfo100Instructions ext_inst_key = + NonSemanticShaderDebugInfo100Instructions(ext_inst_index); + if (ext_inst_key == NonSemanticShaderDebugInfo100DebugScope || + ext_inst_key == NonSemanticShaderDebugInfo100DebugNoScope || + ext_inst_key == NonSemanticShaderDebugInfo100DebugDeclare || + ext_inst_key == NonSemanticShaderDebugInfo100DebugValue || + ext_inst_key == NonSemanticShaderDebugInfo100DebugLine || + ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine || + ext_inst_key == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) { + local_debug_info = true; + } } else { const DebugInfoInstructions ext_inst_key = DebugInfoInstructions(ext_inst_index); @@ -290,6 +306,19 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _, << "declarations)"; } } + } else if (spvExtInstIsNonSemantic(inst->ext_inst_type())) { + // non-semantic extinst opcodes are allowed beginning in the types + // section, but must either be placed outside a function declaration, + // or inside a block. + if (_.current_layout_section() < kLayoutTypes) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Non-semantic OpExtInst must not appear before types " + << "section"; + } else if (_.in_function_body() && _.in_block() == false) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Non-semantic OpExtInst within function definition must " + "appear in a block"; + } } else { // otherwise they must be used in a block if (_.in_block() == false) { @@ -334,6 +363,7 @@ spv_result_t ModuleLayoutPass(ValidationState_t& _, const Instruction* inst) { case kLayoutExtensions: case kLayoutExtInstImport: case kLayoutMemoryModel: + case kLayoutSamplerImageAddressMode: case kLayoutEntryPoint: case kLayoutExecutionMode: case kLayoutDebug1: diff --git a/source/val/validate_logicals.cpp b/source/val/validate_logicals.cpp index bb35f558..ec1e207b 100644 --- a/source/val/validate_logicals.cpp +++ b/source/val/validate_logicals.cpp @@ -163,14 +163,23 @@ spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst) { switch (type_opcode) { case SpvOpTypePointer: { if (_.addressing_model() == SpvAddressingModelLogical && - !_.features().variable_pointers && - !_.features().variable_pointers_storage_buffer) + !_.features().variable_pointers) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Using pointers with OpSelect requires capability " << "VariablePointers or VariablePointersStorageBuffer"; break; } + case SpvOpTypeSampledImage: + case SpvOpTypeImage: + case SpvOpTypeSampler: { + if (!_.HasCapability(SpvCapabilityBindlessTextureNV)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using image/sampler with OpSelect requires capability " + << "BindlessTextureNV"; + break; + } + case SpvOpTypeVector: { dimension = type_inst->word(3); break; diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp index a4bc0fab..074bdb88 100644 --- a/source/val/validate_memory.cpp +++ b/source/val/validate_memory.cpp @@ -35,8 +35,8 @@ bool HaveLayoutCompatibleMembers(ValidationState_t&, const Instruction*, const Instruction*); bool HaveSameLayoutDecorations(ValidationState_t&, const Instruction*, const Instruction*); -bool HasConflictingMemberOffsets(const std::vector&, - const std::vector&); +bool HasConflictingMemberOffsets(const std::set&, + const std::set&); bool IsAllowedTypeOrArrayOfSame(ValidationState_t& _, const Instruction* type, std::initializer_list allowed) { @@ -105,10 +105,8 @@ bool HaveSameLayoutDecorations(ValidationState_t& _, const Instruction* type1, "type1 must be an OpTypeStruct instruction."); assert(type2->opcode() == SpvOpTypeStruct && "type2 must be an OpTypeStruct instruction."); - const std::vector& type1_decorations = - _.id_decorations(type1->id()); - const std::vector& type2_decorations = - _.id_decorations(type2->id()); + const std::set& type1_decorations = _.id_decorations(type1->id()); + const std::set& type2_decorations = _.id_decorations(type2->id()); // TODO: Will have to add other check for arrays an matricies if we want to // handle them. @@ -120,8 +118,8 @@ bool HaveSameLayoutDecorations(ValidationState_t& _, const Instruction* type1, } bool HasConflictingMemberOffsets( - const std::vector& type1_decorations, - const std::vector& type2_decorations) { + const std::set& type1_decorations, + const std::set& type2_decorations) { { // We are interested in conflicting decoration. If a decoration is in one // list but not the other, then we will assume the code is correct. We are @@ -315,11 +313,12 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst, SpvStorageClass dst_sc, src_sc; std::tie(dst_sc, src_sc) = GetStorageClass(_, inst); if (inst->operands().size() <= index) { - if (src_sc == SpvStorageClassPhysicalStorageBufferEXT || - dst_sc == SpvStorageClassPhysicalStorageBufferEXT) { + // Cases where lack of some operand is invalid + if (src_sc == SpvStorageClassPhysicalStorageBuffer || + dst_sc == SpvStorageClassPhysicalStorageBuffer) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Memory accesses with PhysicalStorageBufferEXT must use " - "Aligned."; + << _.VkErrorID(4708) + << "Memory accesses with PhysicalStorageBuffer must use Aligned."; } return SPV_SUCCESS; } @@ -368,7 +367,7 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst, dst_sc != SpvStorageClassCrossWorkgroup && dst_sc != SpvStorageClassGeneric && dst_sc != SpvStorageClassImage && dst_sc != SpvStorageClassStorageBuffer && - dst_sc != SpvStorageClassPhysicalStorageBufferEXT) { + dst_sc != SpvStorageClassPhysicalStorageBuffer) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "NonPrivatePointerKHR requires a pointer in Uniform, " << "Workgroup, CrossWorkgroup, Generic, Image or StorageBuffer " @@ -379,7 +378,7 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst, src_sc != SpvStorageClassCrossWorkgroup && src_sc != SpvStorageClassGeneric && src_sc != SpvStorageClassImage && src_sc != SpvStorageClassStorageBuffer && - src_sc != SpvStorageClassPhysicalStorageBufferEXT) { + src_sc != SpvStorageClassPhysicalStorageBuffer) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "NonPrivatePointerKHR requires a pointer in Uniform, " << "Workgroup, CrossWorkgroup, Generic, Image or StorageBuffer " @@ -388,11 +387,11 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst, } if (!(mask & SpvMemoryAccessAlignedMask)) { - if (src_sc == SpvStorageClassPhysicalStorageBufferEXT || - dst_sc == SpvStorageClassPhysicalStorageBufferEXT) { + if (src_sc == SpvStorageClassPhysicalStorageBuffer || + dst_sc == SpvStorageClassPhysicalStorageBuffer) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Memory accesses with PhysicalStorageBufferEXT must use " - "Aligned."; + << _.VkErrorID(4708) + << "Memory accesses with PhysicalStorageBuffer must use Aligned."; } } @@ -403,8 +402,8 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { auto result_type = _.FindDef(inst->type_id()); if (!result_type || result_type->opcode() != SpvOpTypePointer) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpVariable Result Type '" << _.getIdName(inst->type_id()) - << "' is not a pointer type."; + << "OpVariable Result Type " << _.getIdName(inst->type_id()) + << " is not a pointer type."; } const auto type_index = 2; @@ -424,8 +423,8 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { initializer && spvOpcodeIsConstant(initializer->opcode()); if (!initializer || !(is_constant || is_module_scope_var)) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpVariable Initializer '" << _.getIdName(initializer_id) - << "' is not a constant or module-scope variable."; + << "OpVariable Initializer " << _.getIdName(initializer_id) + << " is not a constant or module-scope variable."; } if (initializer->type_id() != value_id) { return _.diag(SPV_ERROR_INVALID_ID, inst) @@ -439,11 +438,12 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { storage_class != SpvStorageClassCrossWorkgroup && storage_class != SpvStorageClassPrivate && storage_class != SpvStorageClassFunction && - storage_class != SpvStorageClassRayPayloadNV && - storage_class != SpvStorageClassIncomingRayPayloadNV && - storage_class != SpvStorageClassHitAttributeNV && - storage_class != SpvStorageClassCallableDataNV && - storage_class != SpvStorageClassIncomingCallableDataNV) { + storage_class != SpvStorageClassRayPayloadKHR && + storage_class != SpvStorageClassIncomingRayPayloadKHR && + storage_class != SpvStorageClassHitAttributeKHR && + storage_class != SpvStorageClassCallableDataKHR && + storage_class != SpvStorageClassIncomingCallableDataKHR && + storage_class != SpvStorageClassTaskPayloadWorkgroupEXT) { bool storage_input_or_output = storage_class == SpvStorageClassInput || storage_class == SpvStorageClassOutput; bool builtin = false; @@ -455,12 +455,24 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { } } } - if (!(storage_input_or_output && builtin) && + if (!builtin && ContainsInvalidBool(_, value_type, storage_input_or_output)) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "If OpTypeBool is stored in conjunction with OpVariable, it " - << "can only be used with non-externally visible shader Storage " - << "Classes: Workgroup, CrossWorkgroup, Private, and Function"; + if (storage_input_or_output) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(7290) + << "If OpTypeBool is stored in conjunction with OpVariable " + "using Input or Output Storage Classes it requires a BuiltIn " + "decoration"; + + } else { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "If OpTypeBool is stored in conjunction with OpVariable, it " + "can only be used with non-externally visible shader Storage " + "Classes: Workgroup, CrossWorkgroup, Private, Function, " + "Input, Output, RayPayloadKHR, IncomingRayPayloadKHR, " + "HitAttributeKHR, CallableDataKHR, or " + "IncomingCallableDataKHR"; + } } } @@ -519,28 +531,29 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { } } - // Vulkan 14.5.1: Check type of PushConstant variables. - // Vulkan 14.5.2: Check type of UniformConstant and Uniform variables. if (spvIsVulkanEnv(_.context()->target_env)) { + // Vulkan Push Constant Interface section: Check type of PushConstant + // variables. if (storage_class == SpvStorageClassPushConstant) { - if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) { + if (pointee->opcode() != SpvOpTypeStruct) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "PushConstant OpVariable '" << _.getIdName(inst->id()) - << "' has illegal type.\n" - << "From Vulkan spec, section 14.5.1:\n" - << "Such variables must be typed as OpTypeStruct, " - << "or an array of this type"; + << _.VkErrorID(6808) << "PushConstant OpVariable " + << _.getIdName(inst->id()) << " has illegal type.\n" + << "From Vulkan spec, Push Constant Interface section:\n" + << "Such variables must be typed as OpTypeStruct"; } } + // Vulkan Descriptor Set Interface: Check type of UniformConstant and + // Uniform variables. if (storage_class == SpvStorageClassUniformConstant) { if (!IsAllowedTypeOrArrayOfSame( _, pointee, {SpvOpTypeImage, SpvOpTypeSampler, SpvOpTypeSampledImage, SpvOpTypeAccelerationStructureKHR})) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << _.VkErrorID(4655) << "UniformConstant OpVariable '" - << _.getIdName(inst->id()) << "' has illegal type.\n" + << _.VkErrorID(4655) << "UniformConstant OpVariable " + << _.getIdName(inst->id()) << " has illegal type.\n" << "Variables identified with the UniformConstant storage class " << "are used only as handles to refer to opaque resources. Such " << "variables must be typed as OpTypeImage, OpTypeSampler, " @@ -552,9 +565,9 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { if (storage_class == SpvStorageClassUniform) { if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Uniform OpVariable '" << _.getIdName(inst->id()) - << "' has illegal type.\n" - << "From Vulkan spec, section 14.5.2:\n" + << _.VkErrorID(6807) << "Uniform OpVariable " + << _.getIdName(inst->id()) << " has illegal type.\n" + << "From Vulkan spec:\n" << "Variables identified with the Uniform storage class are " << "used to access transparent buffer backed resources. Such " << "variables must be typed as OpTypeStruct, or an array of " @@ -565,9 +578,9 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { if (storage_class == SpvStorageClassStorageBuffer) { if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "StorageBuffer OpVariable '" << _.getIdName(inst->id()) - << "' has illegal type.\n" - << "From Vulkan spec, section 14.5.2:\n" + << _.VkErrorID(6807) << "StorageBuffer OpVariable " + << _.getIdName(inst->id()) << " has illegal type.\n" + << "From Vulkan spec:\n" << "Variables identified with the StorageBuffer storage class " "are used to access transparent buffer backed resources. " "Such variables must be typed as OpTypeStruct, or an array " @@ -596,27 +609,27 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { } } } - } - // Vulkan Appendix A: Check that if contains initializer, then - // storage class is Output, Private, or Function. - if (inst->operands().size() > 3 && storage_class != SpvStorageClassOutput && - storage_class != SpvStorageClassPrivate && - storage_class != SpvStorageClassFunction) { - if (spvIsVulkanEnv(_.context()->target_env)) { + // Initializers in Vulkan are only allowed in some storage clases + if (inst->operands().size() > 3) { if (storage_class == SpvStorageClassWorkgroup) { auto init_id = inst->GetOperandAs(3); auto init = _.FindDef(init_id); if (init->opcode() != SpvOpConstantNull) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Variable initializers in Workgroup storage class are " - "limited to OpConstantNull"; + << _.VkErrorID(4734) << "OpVariable, " + << _.getIdName(inst->id()) + << ", initializers are limited to OpConstantNull in " + "Workgroup " + "storage class"; } - } else { + } else if (storage_class != SpvStorageClassOutput && + storage_class != SpvStorageClassPrivate && + storage_class != SpvStorageClassFunction) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << _.VkErrorID(4651) << "OpVariable, '" + << _.VkErrorID(4651) << "OpVariable, " << _.getIdName(inst->id()) - << "', has a disallowed initializer & storage class " + << ", has a disallowed initializer & storage class " << "combination.\n" << "From " << spvLogStringForEnv(_.context()->target_env) << " spec:\n" @@ -627,9 +640,22 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { } } - if (storage_class == SpvStorageClassPhysicalStorageBufferEXT) { + if (inst->operands().size() > 3) { + if (storage_class == SpvStorageClassTaskPayloadWorkgroupEXT) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpVariable, " << _.getIdName(inst->id()) + << ", initializer are not allowed for TaskPayloadWorkgroupEXT"; + } + if (storage_class == SpvStorageClassInput) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpVariable, " << _.getIdName(inst->id()) + << ", initializer are not allowed for Input"; + } + } + + if (storage_class == SpvStorageClassPhysicalStorageBuffer) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "PhysicalStorageBufferEXT must not be used with OpVariable."; + << "PhysicalStorageBuffer must not be used with OpVariable."; } auto pointee_base = pointee; @@ -638,23 +664,23 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { } if (pointee_base->opcode() == SpvOpTypePointer) { if (pointee_base->GetOperandAs(1u) == - SpvStorageClassPhysicalStorageBufferEXT) { - // check for AliasedPointerEXT/RestrictPointerEXT + SpvStorageClassPhysicalStorageBuffer) { + // check for AliasedPointer/RestrictPointer bool foundAliased = - _.HasDecoration(inst->id(), SpvDecorationAliasedPointerEXT); + _.HasDecoration(inst->id(), SpvDecorationAliasedPointer); bool foundRestrict = - _.HasDecoration(inst->id(), SpvDecorationRestrictPointerEXT); + _.HasDecoration(inst->id(), SpvDecorationRestrictPointer); if (!foundAliased && !foundRestrict) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpVariable " << inst->id() - << ": expected AliasedPointerEXT or RestrictPointerEXT for " - << "PhysicalStorageBufferEXT pointer."; + << ": expected AliasedPointer or RestrictPointer for " + << "PhysicalStorageBuffer pointer."; } if (foundAliased && foundRestrict) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpVariable " << inst->id() - << ": can't specify both AliasedPointerEXT and " - << "RestrictPointerEXT for PhysicalStorageBufferEXT pointer."; + << ": can't specify both AliasedPointer and " + << "RestrictPointer for PhysicalStorageBuffer pointer."; } } } @@ -667,8 +693,9 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { if (value_type && value_type->opcode() == SpvOpTypeRuntimeArray) { if (!_.HasCapability(SpvCapabilityRuntimeDescriptorArrayEXT)) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpVariable, '" << _.getIdName(inst->id()) - << "', is attempting to create memory for an illegal type, " + << _.VkErrorID(4680) << "OpVariable, " + << _.getIdName(inst->id()) + << ", is attempting to create memory for an illegal type, " << "OpTypeRuntimeArray.\nFor Vulkan OpTypeRuntimeArray can only " << "appear as the final member of an OpTypeStruct, thus cannot " << "be instantiated via OpVariable"; @@ -679,6 +706,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { storage_class != SpvStorageClassUniform && storage_class != SpvStorageClassUniformConstant) { return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4680) << "For Vulkan with RuntimeDescriptorArrayEXT, a variable " << "containing OpTypeRuntimeArray must have storage class of " << "StorageBuffer, Uniform, or UniformConstant."; @@ -692,25 +720,30 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { // as BufferBlock. if (value_type && value_type->opcode() == SpvOpTypeStruct) { if (DoesStructContainRTA(_, value_type)) { - if (storage_class == SpvStorageClassStorageBuffer) { + if (storage_class == SpvStorageClassStorageBuffer || + storage_class == SpvStorageClassPhysicalStorageBuffer) { if (!_.HasDecoration(value_id, SpvDecorationBlock)) { return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4680) << "For Vulkan, an OpTypeStruct variable containing an " << "OpTypeRuntimeArray must be decorated with Block if it " - << "has storage class StorageBuffer."; + << "has storage class StorageBuffer or " + "PhysicalStorageBuffer."; } } else if (storage_class == SpvStorageClassUniform) { if (!_.HasDecoration(value_id, SpvDecorationBufferBlock)) { return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4680) << "For Vulkan, an OpTypeStruct variable containing an " << "OpTypeRuntimeArray must be decorated with BufferBlock " << "if it has storage class Uniform."; } } else { return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4680) << "For Vulkan, OpTypeStruct variables containing " << "OpTypeRuntimeArray must have storage class of " - << "StorageBuffer or Uniform."; + << "StorageBuffer, PhysicalStorageBuffer, or Uniform."; } } } @@ -745,7 +778,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { SPV_OPERAND_TYPE_STORAGE_CLASS, storage_class); switch (storage_class) { case SpvStorageClassStorageBuffer: - case SpvStorageClassPhysicalStorageBufferEXT: + case SpvStorageClassPhysicalStorageBuffer: if (!_.HasCapability(SpvCapabilityStorageBuffer16BitAccess)) { storage_class_ok = false; } @@ -807,7 +840,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { SPV_OPERAND_TYPE_STORAGE_CLASS, storage_class); switch (storage_class) { case SpvStorageClassStorageBuffer: - case SpvStorageClassPhysicalStorageBufferEXT: + case SpvStorageClassPhysicalStorageBuffer: if (!_.HasCapability(SpvCapabilityStorageBuffer8BitAccess)) { storage_class_ok = false; } @@ -857,40 +890,46 @@ spv_result_t ValidateLoad(ValidationState_t& _, const Instruction* inst) { const auto result_type = _.FindDef(inst->type_id()); if (!result_type) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpLoad Result Type '" << _.getIdName(inst->type_id()) - << "' is not defined."; + << "OpLoad Result Type " << _.getIdName(inst->type_id()) + << " is not defined."; } - const bool uses_variable_pointers = - _.features().variable_pointers || - _.features().variable_pointers_storage_buffer; const auto pointer_index = 2; const auto pointer_id = inst->GetOperandAs(pointer_index); const auto pointer = _.FindDef(pointer_id); if (!pointer || ((_.addressing_model() == SpvAddressingModelLogical) && - ((!uses_variable_pointers && + ((!_.features().variable_pointers && !spvOpcodeReturnsLogicalPointer(pointer->opcode())) || - (uses_variable_pointers && + (_.features().variable_pointers && !spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpLoad Pointer '" << _.getIdName(pointer_id) - << "' is not a logical pointer."; + << "OpLoad Pointer " << _.getIdName(pointer_id) + << " is not a logical pointer."; } const auto pointer_type = _.FindDef(pointer->type_id()); if (!pointer_type || pointer_type->opcode() != SpvOpTypePointer) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpLoad type for pointer '" << _.getIdName(pointer_id) - << "' is not a pointer type."; + << "OpLoad type for pointer " << _.getIdName(pointer_id) + << " is not a pointer type."; } - const auto pointee_type = _.FindDef(pointer_type->GetOperandAs(2)); - if (!pointee_type || result_type->id() != pointee_type->id()) { + uint32_t pointee_data_type; + uint32_t storage_class; + if (!_.GetPointerTypeInfo(pointer_type->id(), &pointee_data_type, + &storage_class) || + result_type->id() != pointee_data_type) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpLoad Result Type '" << _.getIdName(inst->type_id()) - << "' does not match Pointer '" << _.getIdName(pointer->id()) - << "'s type."; + << "OpLoad Result Type " << _.getIdName(inst->type_id()) + << " does not match Pointer " << _.getIdName(pointer->id()) + << "s type."; + } + + if (!_.options()->before_hlsl_legalization && + _.ContainsRuntimeArray(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cannot load a runtime-sized array"; } if (auto error = CheckMemoryAccess(_, inst, 3)) return error; @@ -911,34 +950,31 @@ spv_result_t ValidateLoad(ValidationState_t& _, const Instruction* inst) { } spv_result_t ValidateStore(ValidationState_t& _, const Instruction* inst) { - const bool uses_variable_pointer = - _.features().variable_pointers || - _.features().variable_pointers_storage_buffer; const auto pointer_index = 0; const auto pointer_id = inst->GetOperandAs(pointer_index); const auto pointer = _.FindDef(pointer_id); if (!pointer || (_.addressing_model() == SpvAddressingModelLogical && - ((!uses_variable_pointer && + ((!_.features().variable_pointers && !spvOpcodeReturnsLogicalPointer(pointer->opcode())) || - (uses_variable_pointer && + (_.features().variable_pointers && !spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpStore Pointer '" << _.getIdName(pointer_id) - << "' is not a logical pointer."; + << "OpStore Pointer " << _.getIdName(pointer_id) + << " is not a logical pointer."; } const auto pointer_type = _.FindDef(pointer->type_id()); if (!pointer_type || pointer_type->opcode() != SpvOpTypePointer) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpStore type for pointer '" << _.getIdName(pointer_id) - << "' is not a pointer type."; + << "OpStore type for pointer " << _.getIdName(pointer_id) + << " is not a pointer type."; } const auto type_id = pointer_type->GetOperandAs(2); const auto type = _.FindDef(type_id); if (!type || SpvOpTypeVoid == type->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpStore Pointer '" << _.getIdName(pointer_id) - << "'s type is void."; + << "OpStore Pointer " << _.getIdName(pointer_id) + << "s type is void."; } // validate storage class @@ -947,16 +983,36 @@ spv_result_t ValidateStore(ValidationState_t& _, const Instruction* inst) { uint32_t storage_class; if (!_.GetPointerTypeInfo(pointer_type->id(), &data_type, &storage_class)) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpStore Pointer '" << _.getIdName(pointer_id) - << "' is not pointer type"; + << "OpStore Pointer " << _.getIdName(pointer_id) + << " is not pointer type"; } if (storage_class == SpvStorageClassUniformConstant || storage_class == SpvStorageClassInput || storage_class == SpvStorageClassPushConstant) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpStore Pointer '" << _.getIdName(pointer_id) - << "' storage class is read-only"; + << "OpStore Pointer " << _.getIdName(pointer_id) + << " storage class is read-only"; + } else if (storage_class == SpvStorageClassShaderRecordBufferKHR) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "ShaderRecordBufferKHR Storage Class variables are read only"; + } else if (storage_class == SpvStorageClassHitAttributeKHR) { + std::string errorVUID = _.VkErrorID(4703); + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [errorVUID](SpvExecutionModel model, std::string* message) { + if (model == SpvExecutionModelAnyHitKHR || + model == SpvExecutionModelClosestHitKHR) { + if (message) { + *message = + errorVUID + + "HitAttributeKHR Storage Class variables are read only " + "with AnyHitKHR and ClosestHitKHR"; + } + return false; + } + return true; + }); } if (spvIsVulkanEnv(_.context()->target_env) && @@ -973,6 +1029,7 @@ spv_result_t ValidateStore(ValidationState_t& _, const Instruction* inst) { } if (_.HasDecoration(base_type->id(), SpvDecorationBlock)) { return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(6925) << "In the Vulkan environment, cannot store to Uniform Blocks"; } } @@ -984,31 +1041,31 @@ spv_result_t ValidateStore(ValidationState_t& _, const Instruction* inst) { const auto object = _.FindDef(object_id); if (!object || !object->type_id()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpStore Object '" << _.getIdName(object_id) - << "' is not an object."; + << "OpStore Object " << _.getIdName(object_id) + << " is not an object."; } const auto object_type = _.FindDef(object->type_id()); if (!object_type || SpvOpTypeVoid == object_type->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpStore Object '" << _.getIdName(object_id) - << "'s type is void."; + << "OpStore Object " << _.getIdName(object_id) + << "s type is void."; } if (type->id() != object_type->id()) { if (!_.options()->relax_struct_store || type->opcode() != SpvOpTypeStruct || object_type->opcode() != SpvOpTypeStruct) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpStore Pointer '" << _.getIdName(pointer_id) - << "'s type does not match Object '" - << _.getIdName(object->id()) << "'s type."; + << "OpStore Pointer " << _.getIdName(pointer_id) + << "s type does not match Object " + << _.getIdName(object->id()) << "s type."; } // TODO: Check for layout compatible matricies and arrays as well. if (!AreLayoutCompatibleStructs(_, type, object_type)) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpStore Pointer '" << _.getIdName(pointer_id) - << "'s layout does not match Object '" - << _.getIdName(object->id()) << "'s layout."; + << "OpStore Pointer " << _.getIdName(pointer_id) + << "s layout does not match Object " + << _.getIdName(object->id()) << "s layout."; } } @@ -1080,8 +1137,8 @@ spv_result_t ValidateCopyMemory(ValidationState_t& _, const Instruction* inst) { const auto target = _.FindDef(target_id); if (!target) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Target operand '" << _.getIdName(target_id) - << "' is not defined."; + << "Target operand " << _.getIdName(target_id) + << " is not defined."; } const auto source_index = 1; @@ -1089,24 +1146,24 @@ spv_result_t ValidateCopyMemory(ValidationState_t& _, const Instruction* inst) { const auto source = _.FindDef(source_id); if (!source) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Source operand '" << _.getIdName(source_id) - << "' is not defined."; + << "Source operand " << _.getIdName(source_id) + << " is not defined."; } const auto target_pointer_type = _.FindDef(target->type_id()); if (!target_pointer_type || target_pointer_type->opcode() != SpvOpTypePointer) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Target operand '" << _.getIdName(target_id) - << "' is not a pointer."; + << "Target operand " << _.getIdName(target_id) + << " is not a pointer."; } const auto source_pointer_type = _.FindDef(source->type_id()); if (!source_pointer_type || source_pointer_type->opcode() != SpvOpTypePointer) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Source operand '" << _.getIdName(source_id) - << "' is not a pointer."; + << "Source operand " << _.getIdName(source_id) + << " is not a pointer."; } if (inst->opcode() == SpvOpCopyMemory) { @@ -1114,70 +1171,66 @@ spv_result_t ValidateCopyMemory(ValidationState_t& _, const Instruction* inst) { _.FindDef(target_pointer_type->GetOperandAs(2)); if (!target_type || target_type->opcode() == SpvOpTypeVoid) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Target operand '" << _.getIdName(target_id) - << "' cannot be a void pointer."; + << "Target operand " << _.getIdName(target_id) + << " cannot be a void pointer."; } const auto source_type = _.FindDef(source_pointer_type->GetOperandAs(2)); if (!source_type || source_type->opcode() == SpvOpTypeVoid) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Source operand '" << _.getIdName(source_id) - << "' cannot be a void pointer."; + << "Source operand " << _.getIdName(source_id) + << " cannot be a void pointer."; } if (target_type->id() != source_type->id()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Target '" << _.getIdName(source_id) - << "'s type does not match Source '" - << _.getIdName(source_type->id()) << "'s type."; + << "Target " << _.getIdName(source_id) + << "s type does not match Source " + << _.getIdName(source_type->id()) << "s type."; } - - if (auto error = CheckMemoryAccess(_, inst, 2)) return error; } else { const auto size_id = inst->GetOperandAs(2); const auto size = _.FindDef(size_id); if (!size) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Size operand '" << _.getIdName(size_id) - << "' is not defined."; + << "Size operand " << _.getIdName(size_id) + << " is not defined."; } const auto size_type = _.FindDef(size->type_id()); if (!_.IsIntScalarType(size_type->id())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Size operand '" << _.getIdName(size_id) - << "' must be a scalar integer type."; + << "Size operand " << _.getIdName(size_id) + << " must be a scalar integer type."; } bool is_zero = true; switch (size->opcode()) { case SpvOpConstantNull: return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Size operand '" << _.getIdName(size_id) - << "' cannot be a constant zero."; + << "Size operand " << _.getIdName(size_id) + << " cannot be a constant zero."; case SpvOpConstant: if (size_type->word(3) == 1 && size->word(size->words().size() - 1) & 0x80000000) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Size operand '" << _.getIdName(size_id) - << "' cannot have the sign bit set to 1."; + << "Size operand " << _.getIdName(size_id) + << " cannot have the sign bit set to 1."; } for (size_t i = 3; is_zero && i < size->words().size(); ++i) { is_zero &= (size->word(i) == 0); } if (is_zero) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Size operand '" << _.getIdName(size_id) - << "' cannot be a constant zero."; + << "Size operand " << _.getIdName(size_id) + << " cannot be a constant zero."; } break; default: // Cannot infer any other opcodes. break; } - - if (auto error = CheckMemoryAccess(_, inst, 3)) return error; } if (auto error = ValidateCopyMemoryMemoryAccess(_, inst)) return error; @@ -1204,8 +1257,8 @@ spv_result_t ValidateAccessChain(ValidationState_t& _, auto result_type = _.FindDef(inst->type_id()); if (SpvOpTypePointer != result_type->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "The Result Type of " << instr_name << " '" - << _.getIdName(inst->id()) << "' must be OpTypePointer. Found Op" + << "The Result Type of " << instr_name << " " + << _.getIdName(inst->id()) << " must be OpTypePointer. Found Op" << spvOpcodeString(static_cast(result_type->opcode())) << "."; } @@ -1221,7 +1274,7 @@ spv_result_t ValidateAccessChain(ValidationState_t& _, const auto base_type = _.FindDef(base->type_id()); if (!base_type || SpvOpTypePointer != base_type->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "The Base '" << _.getIdName(base_id) << "' in " << instr_name + << "The Base " << _.getIdName(base_id) << " in " << instr_name << " instruction must be a pointer."; } @@ -1313,8 +1366,8 @@ spv_result_t ValidateAccessChain(ValidationState_t& _, return _.diag(SPV_ERROR_INVALID_ID, cur_word_instr) << "Index is out of bounds: " << instr_name << " can not find index " << cur_index - << " into the structure '" - << _.getIdName(type_pointee->id()) << "'. This structure has " + << " into the structure " + << _.getIdName(type_pointee->id()) << ". This structure has " << num_struct_members << " members. Largest valid index is " << num_struct_members - 1 << "."; } @@ -1351,8 +1404,7 @@ spv_result_t ValidateAccessChain(ValidationState_t& _, spv_result_t ValidatePtrAccessChain(ValidationState_t& _, const Instruction* inst) { if (_.addressing_model() == SpvAddressingModelLogical) { - if (!_.features().variable_pointers && - !_.features().variable_pointers_storage_buffer) { + if (!_.features().variable_pointers) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Generating variable pointers requires capability " << "VariablePointers or VariablePointersStorageBuffer"; @@ -1372,9 +1424,9 @@ spv_result_t ValidateArrayLength(ValidationState_t& state, result_type->GetOperandAs(1) != 32 || result_type->GetOperandAs(2) != 0) { return state.diag(SPV_ERROR_INVALID_ID, inst) - << "The Result Type of " << instr_name << " '" + << "The Result Type of " << instr_name << " " << state.getIdName(inst->id()) - << "' must be OpTypeInt with width 32 and signedness 0."; + << " must be OpTypeInt with width 32 and signedness 0."; } // The structure that is passed in must be an pointer to a structure, whose @@ -1383,17 +1435,17 @@ spv_result_t ValidateArrayLength(ValidationState_t& state, auto pointer_type = state.FindDef(pointer->type_id()); if (pointer_type->opcode() != SpvOpTypePointer) { return state.diag(SPV_ERROR_INVALID_ID, inst) - << "The Struture's type in " << instr_name << " '" + << "The Structure's type in " << instr_name << " " << state.getIdName(inst->id()) - << "' must be a pointer to an OpTypeStruct."; + << " must be a pointer to an OpTypeStruct."; } auto structure_type = state.FindDef(pointer_type->GetOperandAs(2)); if (structure_type->opcode() != SpvOpTypeStruct) { return state.diag(SPV_ERROR_INVALID_ID, inst) - << "The Struture's type in " << instr_name << " '" + << "The Structure's type in " << instr_name << " " << state.getIdName(inst->id()) - << "' must be a pointer to an OpTypeStruct."; + << " must be a pointer to an OpTypeStruct."; } auto num_of_members = structure_type->operands().size() - 1; @@ -1401,17 +1453,17 @@ spv_result_t ValidateArrayLength(ValidationState_t& state, state.FindDef(structure_type->GetOperandAs(num_of_members)); if (last_member->opcode() != SpvOpTypeRuntimeArray) { return state.diag(SPV_ERROR_INVALID_ID, inst) - << "The Struture's last member in " << instr_name << " '" - << state.getIdName(inst->id()) << "' must be an OpTypeRuntimeArray."; + << "The Structure's last member in " << instr_name << " " + << state.getIdName(inst->id()) << " must be an OpTypeRuntimeArray."; } - // The array member must the the index of the last element (the run time + // The array member must the index of the last element (the run time // array). if (inst->GetOperandAs(3) != num_of_members - 1) { return state.diag(SPV_ERROR_INVALID_ID, inst) - << "The array member in " << instr_name << " '" + << "The array member in " << instr_name << " " << state.getIdName(inst->id()) - << "' must be an the last member of the struct."; + << " must be an the last member of the struct."; } return SPV_SUCCESS; } @@ -1427,18 +1479,17 @@ spv_result_t ValidateCooperativeMatrixLengthNV(ValidationState_t& state, result_type->GetOperandAs(1) != 32 || result_type->GetOperandAs(2) != 0) { return state.diag(SPV_ERROR_INVALID_ID, inst) - << "The Result Type of " << instr_name << " '" + << "The Result Type of " << instr_name << " " << state.getIdName(inst->id()) - << "' must be OpTypeInt with width 32 and signedness 0."; + << " must be OpTypeInt with width 32 and signedness 0."; } auto type_id = inst->GetOperandAs(2); auto type = state.FindDef(type_id); if (type->opcode() != SpvOpTypeCooperativeMatrixNV) { return state.diag(SPV_ERROR_INVALID_ID, inst) - << "The type in " << instr_name << " '" - << state.getIdName(type_id) - << "' must be OpTypeCooperativeMatrixNV."; + << "The type in " << instr_name << " " + << state.getIdName(type_id) << " must be OpTypeCooperativeMatrixNV."; } return SPV_SUCCESS; } @@ -1461,39 +1512,36 @@ spv_result_t ValidateCooperativeMatrixLoadStoreNV(ValidationState_t& _, if (matrix_type->opcode() != SpvOpTypeCooperativeMatrixNV) { if (inst->opcode() == SpvOpCooperativeMatrixLoadNV) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "SpvOpCooperativeMatrixLoadNV Result Type '" - << _.getIdName(type_id) << "' is not a cooperative matrix type."; + << "SpvOpCooperativeMatrixLoadNV Result Type " + << _.getIdName(type_id) << " is not a cooperative matrix type."; } else { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "SpvOpCooperativeMatrixStoreNV Object type '" - << _.getIdName(type_id) << "' is not a cooperative matrix type."; + << "SpvOpCooperativeMatrixStoreNV Object type " + << _.getIdName(type_id) << " is not a cooperative matrix type."; } } - const bool uses_variable_pointers = - _.features().variable_pointers || - _.features().variable_pointers_storage_buffer; const auto pointer_index = (inst->opcode() == SpvOpCooperativeMatrixLoadNV) ? 2u : 0u; const auto pointer_id = inst->GetOperandAs(pointer_index); const auto pointer = _.FindDef(pointer_id); if (!pointer || ((_.addressing_model() == SpvAddressingModelLogical) && - ((!uses_variable_pointers && + ((!_.features().variable_pointers && !spvOpcodeReturnsLogicalPointer(pointer->opcode())) || - (uses_variable_pointers && + (_.features().variable_pointers && !spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << opname << " Pointer '" << _.getIdName(pointer_id) - << "' is not a logical pointer."; + << opname << " Pointer " << _.getIdName(pointer_id) + << " is not a logical pointer."; } const auto pointer_type_id = pointer->type_id(); const auto pointer_type = _.FindDef(pointer_type_id); if (!pointer_type || pointer_type->opcode() != SpvOpTypePointer) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << opname << " type for pointer '" << _.getIdName(pointer_id) - << "' is not a pointer type."; + << opname << " type for pointer " << _.getIdName(pointer_id) + << " is not a pointer type."; } const auto storage_class_index = 1u; @@ -1502,11 +1550,11 @@ spv_result_t ValidateCooperativeMatrixLoadStoreNV(ValidationState_t& _, if (storage_class != SpvStorageClassWorkgroup && storage_class != SpvStorageClassStorageBuffer && - storage_class != SpvStorageClassPhysicalStorageBufferEXT) { + storage_class != SpvStorageClassPhysicalStorageBuffer) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << opname << " storage class for pointer type '" + << opname << " storage class for pointer type " << _.getIdName(pointer_type_id) - << "' is not Workgroup or StorageBuffer."; + << " is not Workgroup or StorageBuffer."; } const auto pointee_id = pointer_type->GetOperandAs(2); @@ -1514,8 +1562,8 @@ spv_result_t ValidateCooperativeMatrixLoadStoreNV(ValidationState_t& _, if (!pointee_type || !(_.IsIntScalarOrVectorType(pointee_id) || _.IsFloatScalarOrVectorType(pointee_id))) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << opname << " Pointer '" << _.getIdName(pointer->id()) - << "'s Type must be a scalar or vector type."; + << opname << " Pointer " << _.getIdName(pointer->id()) + << "s Type must be a scalar or vector type."; } const auto stride_index = @@ -1524,8 +1572,8 @@ spv_result_t ValidateCooperativeMatrixLoadStoreNV(ValidationState_t& _, const auto stride = _.FindDef(stride_id); if (!stride || !_.IsIntScalarType(stride->type_id())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Stride operand '" << _.getIdName(stride_id) - << "' must be a scalar integer type."; + << "Stride operand " << _.getIdName(stride_id) + << " must be a scalar integer type."; } const auto colmajor_index = @@ -1536,8 +1584,8 @@ spv_result_t ValidateCooperativeMatrixLoadStoreNV(ValidationState_t& _, !(spvOpcodeIsConstant(colmajor->opcode()) || spvOpcodeIsSpecConstant(colmajor->opcode()))) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Column Major operand '" << _.getIdName(colmajor_id) - << "' must be a boolean constant instruction."; + << "Column Major operand " << _.getIdName(colmajor_id) + << " must be a boolean constant instruction."; } const auto memory_access_index = @@ -1553,10 +1601,10 @@ spv_result_t ValidateCooperativeMatrixLoadStoreNV(ValidationState_t& _, spv_result_t ValidatePtrComparison(ValidationState_t& _, const Instruction* inst) { if (_.addressing_model() == SpvAddressingModelLogical && - !_.features().variable_pointers_storage_buffer) { + !_.features().variable_pointers) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Instruction cannot be used without a variable pointers " - "capability"; + << "Instruction cannot for logical addressing model be used without " + "a variable pointers capability"; } const auto result_type = _.FindDef(inst->type_id()); @@ -1591,7 +1639,8 @@ spv_result_t ValidatePtrComparison(ValidationState_t& _, << "Invalid pointer storage class"; } - if (sc == SpvStorageClassWorkgroup && !_.features().variable_pointers) { + if (sc == SpvStorageClassWorkgroup && + !_.HasCapability(SpvCapabilityVariablePointers)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "Workgroup storage class pointer requires VariablePointers " "capability to be specified"; diff --git a/source/val/validate_mesh_shading.cpp b/source/val/validate_mesh_shading.cpp new file mode 100644 index 00000000..a7f07267 --- /dev/null +++ b/source/val/validate_mesh_shading.cpp @@ -0,0 +1,123 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates ray query instructions from SPV_KHR_ray_query + +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +spv_result_t MeshShadingPass(ValidationState_t& _, const Instruction* inst) { + const SpvOp opcode = inst->opcode(); + switch (opcode) { + case SpvOpEmitMeshTasksEXT: { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [](SpvExecutionModel model, std::string* message) { + if (model != SpvExecutionModelTaskEXT) { + if (message) { + *message = + "OpEmitMeshTasksEXT requires TaskEXT execution model"; + } + return false; + } + return true; + }); + + const uint32_t group_count_x = _.GetOperandTypeId(inst, 0); + if (!_.IsUnsignedIntScalarType(group_count_x) || + _.GetBitWidth(group_count_x) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Group Count X must be a 32-bit unsigned int scalar"; + } + + const uint32_t group_count_y = _.GetOperandTypeId(inst, 1); + if (!_.IsUnsignedIntScalarType(group_count_y) || + _.GetBitWidth(group_count_y) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Group Count Y must be a 32-bit unsigned int scalar"; + } + + const uint32_t group_count_z = _.GetOperandTypeId(inst, 2); + if (!_.IsUnsignedIntScalarType(group_count_z) || + _.GetBitWidth(group_count_z) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Group Count Z must be a 32-bit unsigned int scalar"; + } + + if (inst->operands().size() == 4) { + const auto payload = _.FindDef(inst->GetOperandAs(3)); + if (payload->opcode() != SpvOpVariable) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Payload must be the result of a OpVariable"; + } + if (SpvStorageClass(payload->GetOperandAs(2)) != + SpvStorageClassTaskPayloadWorkgroupEXT) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Payload OpVariable must have a storage class of " + "TaskPayloadWorkgroupEXT"; + } + } + break; + } + + case SpvOpSetMeshOutputsEXT: { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [](SpvExecutionModel model, std::string* message) { + if (model != SpvExecutionModelMeshEXT) { + if (message) { + *message = + "OpSetMeshOutputsEXT requires MeshEXT execution model"; + } + return false; + } + return true; + }); + + const uint32_t vertex_count = _.GetOperandTypeId(inst, 0); + if (!_.IsUnsignedIntScalarType(vertex_count) || + _.GetBitWidth(vertex_count) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Vertex Count must be a 32-bit unsigned int scalar"; + } + + const uint32_t primitive_count = _.GetOperandTypeId(inst, 1); + if (!_.IsUnsignedIntScalarType(primitive_count) || + _.GetBitWidth(primitive_count) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Primitive Count must be a 32-bit unsigned int scalar"; + } + + break; + } + + case SpvOpWritePackedPrimitiveIndices4x8NV: { + // No validation rules (for the moment). + break; + } + + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/source/val/validate_misc.cpp b/source/val/validate_misc.cpp index 3bc15ca0..5acc21ea 100644 --- a/source/val/validate_misc.cpp +++ b/source/val/validate_misc.cpp @@ -59,10 +59,7 @@ spv_result_t ValidateShaderClock(ValidationState_t& _, // a vector of two - components of 32 - // bit unsigned integer type const uint32_t result_type = inst->type_id(); - if (!(_.IsUnsignedIntScalarType(result_type) && - _.GetBitWidth(result_type) == 64) && - !(_.IsUnsignedIntVectorType(result_type) && - _.GetDimension(result_type) == 2 && _.GetBitWidth(result_type) == 32)) { + if (!_.IsUnsigned64BitHandle(result_type)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Value to be a " "vector of two components" " of unsigned integer" diff --git a/source/val/validate_mode_setting.cpp b/source/val/validate_mode_setting.cpp index 79f82d8d..672192b9 100644 --- a/source/val/validate_mode_setting.cpp +++ b/source/val/validate_mode_setting.cpp @@ -12,13 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. // -#include "source/val/validate.h" - #include #include "source/opcode.h" #include "source/spirv_target_env.h" #include "source/val/instruction.h" +#include "source/val/validate.h" #include "source/val/validation_state.h" namespace spvtools { @@ -30,8 +29,8 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) { auto entry_point = _.FindDef(entry_point_id); if (!entry_point || SpvOpFunction != entry_point->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpEntryPoint Entry Point '" << _.getIdName(entry_point_id) - << "' is not a function."; + << "OpEntryPoint Entry Point " << _.getIdName(entry_point_id) + << " is not a function."; } // Only check the shader execution models @@ -42,18 +41,18 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) { const auto entry_point_type = _.FindDef(entry_point_type_id); if (!entry_point_type || 3 != entry_point_type->words().size()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << _.VkErrorID(4633) << "OpEntryPoint Entry Point '" + << _.VkErrorID(4633) << "OpEntryPoint Entry Point " << _.getIdName(entry_point_id) - << "'s function parameter count is not zero."; + << "s function parameter count is not zero."; } } auto return_type = _.FindDef(entry_point->type_id()); if (!return_type || SpvOpTypeVoid != return_type->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << _.VkErrorID(4633) << "OpEntryPoint Entry Point '" + << _.VkErrorID(4633) << "OpEntryPoint Entry Point " << _.getIdName(entry_point_id) - << "'s function return type is not void."; + << "s function return type is not void."; } const auto* execution_modes = _.GetExecutionModes(entry_point_id); @@ -112,6 +111,44 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) { << "Fragment execution model entry points can specify at most " "one fragment shader interlock execution mode."; } + if (execution_modes && + 1 < std::count_if( + execution_modes->begin(), execution_modes->end(), + [](const SpvExecutionMode& mode) { + switch (mode) { + case SpvExecutionModeStencilRefUnchangedFrontAMD: + case SpvExecutionModeStencilRefLessFrontAMD: + case SpvExecutionModeStencilRefGreaterFrontAMD: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Fragment execution model entry points can specify at most " + "one of StencilRefUnchangedFrontAMD, " + "StencilRefLessFrontAMD or StencilRefGreaterFrontAMD " + "execution modes."; + } + if (execution_modes && + 1 < std::count_if( + execution_modes->begin(), execution_modes->end(), + [](const SpvExecutionMode& mode) { + switch (mode) { + case SpvExecutionModeStencilRefUnchangedBackAMD: + case SpvExecutionModeStencilRefLessBackAMD: + case SpvExecutionModeStencilRefGreaterBackAMD: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Fragment execution model entry points can specify at most " + "one of StencilRefUnchangedBackAMD, " + "StencilRefLessBackAMD or StencilRefGreaterBackAMD " + "execution modes."; + } break; case SpvExecutionModelTessellationControl: case SpvExecutionModelTessellationEvaluation: @@ -204,6 +241,39 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) { "OutputTriangleStrip execution modes."; } break; + case SpvExecutionModelMeshEXT: + if (!execution_modes || + 1 != std::count_if(execution_modes->begin(), execution_modes->end(), + [](const SpvExecutionMode& mode) { + switch (mode) { + case SpvExecutionModeOutputPoints: + case SpvExecutionModeOutputLinesEXT: + case SpvExecutionModeOutputTrianglesEXT: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "MeshEXT execution model entry points must specify exactly " + "one of OutputPoints, OutputLinesEXT, or " + "OutputTrianglesEXT Execution Modes."; + } else if (2 != std::count_if( + execution_modes->begin(), execution_modes->end(), + [](const SpvExecutionMode& mode) { + switch (mode) { + case SpvExecutionModeOutputPrimitivesEXT: + case SpvExecutionModeOutputVertices: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "MeshEXT execution model entry points must specify both " + "OutputPrimitivesEXT and OutputVertices Execution Modes."; + } + break; default: break; } @@ -225,14 +295,21 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) { } } } + if (i.opcode() == SpvOpExecutionModeId) { + const auto mode = i.GetOperandAs(1); + if (mode == SpvExecutionModeLocalSizeId) { + ok = true; + break; + } + } } if (!ok) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(4683) + << _.VkErrorID(6426) << "In the Vulkan environment, GLCompute execution model " - "entry points require either the LocalSize execution " - "mode or an object decorated with WorkgroupSize must be " - "specified."; + "entry points require either the LocalSize or " + "LocalSizeId execution mode or an object decorated with " + "WorkgroupSize must be specified."; } } break; @@ -251,9 +328,8 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _, _.entry_points().cend(), entry_point_id); if (found == _.entry_points().cend()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpExecutionMode Entry Point '" - << _.getIdName(entry_point_id) - << "' is not the Entry Point " + << "OpExecutionMode Entry Point " << _.getIdName(entry_point_id) + << " is not the Entry Point " "operand of an OpEntryPoint."; } @@ -314,14 +390,18 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _, return true; case SpvExecutionModelMeshNV: return _.HasCapability(SpvCapabilityMeshShadingNV); + case SpvExecutionModelMeshEXT: + return _.HasCapability( + SpvCapabilityMeshShadingEXT); default: return false; } })) { - if (_.HasCapability(SpvCapabilityMeshShadingNV)) { + if (_.HasCapability(SpvCapabilityMeshShadingNV) || + _.HasCapability(SpvCapabilityMeshShadingEXT)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Execution mode can only be used with the Geometry or " - "MeshNV execution model."; + << "Execution mode can only be used with the Geometry " + "MeshNV or MeshEXT execution model."; } else { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Execution mode can only be used with the Geometry " @@ -376,14 +456,18 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _, return true; case SpvExecutionModelMeshNV: return _.HasCapability(SpvCapabilityMeshShadingNV); + case SpvExecutionModelMeshEXT: + return _.HasCapability( + SpvCapabilityMeshShadingEXT); default: return false; } })) { - if (_.HasCapability(SpvCapabilityMeshShadingNV)) { + if (_.HasCapability(SpvCapabilityMeshShadingNV) || + _.HasCapability(SpvCapabilityMeshShadingEXT)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Execution mode can only be used with a Geometry, " - "tessellation or MeshNV execution model."; + "tessellation, MeshNV or MeshEXT execution model."; } else { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Execution mode can only be used with a Geometry or " @@ -391,6 +475,20 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _, } } break; + case SpvExecutionModeOutputLinesEXT: + case SpvExecutionModeOutputTrianglesEXT: + case SpvExecutionModeOutputPrimitivesEXT: + if (!std::all_of(models->begin(), models->end(), + [](const SpvExecutionModel& model) { + return (model == SpvExecutionModelMeshEXT || + model == SpvExecutionModelMeshNV); + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with the MeshEXT or MeshNV " + "execution " + "model."; + } + break; case SpvExecutionModePixelCenterInteger: case SpvExecutionModeOriginUpperLeft: case SpvExecutionModeOriginLowerLeft: @@ -405,6 +503,13 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _, case SpvExecutionModeSampleInterlockUnorderedEXT: case SpvExecutionModeShadingRateInterlockOrderedEXT: case SpvExecutionModeShadingRateInterlockUnorderedEXT: + case SpvExecutionModeEarlyAndLateFragmentTestsAMD: + case SpvExecutionModeStencilRefUnchangedFrontAMD: + case SpvExecutionModeStencilRefGreaterFrontAMD: + case SpvExecutionModeStencilRefLessFrontAMD: + case SpvExecutionModeStencilRefUnchangedBackAMD: + case SpvExecutionModeStencilRefGreaterBackAMD: + case SpvExecutionModeStencilRefLessBackAMD: if (!std::all_of(models->begin(), models->end(), [](const SpvExecutionModel& model) { return model == SpvExecutionModelFragment; @@ -429,6 +534,10 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _, break; case SpvExecutionModeLocalSize: case SpvExecutionModeLocalSizeId: + if (mode == SpvExecutionModeLocalSizeId && !_.IsLocalSizeIdAllowed()) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "LocalSizeId mode is not allowed by the current environment."; + if (!std::all_of(models->begin(), models->end(), [&_](const SpvExecutionModel& model) { switch (model) { @@ -438,14 +547,19 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _, case SpvExecutionModelTaskNV: case SpvExecutionModelMeshNV: return _.HasCapability(SpvCapabilityMeshShadingNV); + case SpvExecutionModelTaskEXT: + case SpvExecutionModelMeshEXT: + return _.HasCapability( + SpvCapabilityMeshShadingEXT); default: return false; } })) { - if (_.HasCapability(SpvCapabilityMeshShadingNV)) { + if (_.HasCapability(SpvCapabilityMeshShadingNV) || + _.HasCapability(SpvCapabilityMeshShadingEXT)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Execution mode can only be used with a Kernel, GLCompute, " - "MeshNV, or TaskNV execution model."; + "MeshNV, MeshEXT, TaskNV or TaskEXT execution model."; } else { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Execution mode can only be used with a Kernel or " diff --git a/source/val/validate_non_uniform.cpp b/source/val/validate_non_uniform.cpp index 2b6eb8b5..6d4f8a28 100644 --- a/source/val/validate_non_uniform.cpp +++ b/source/val/validate_non_uniform.cpp @@ -63,6 +63,59 @@ spv_result_t ValidateGroupNonUniformBallotBitCount(ValidationState_t& _, return SPV_SUCCESS; } +spv_result_t ValidateGroupNonUniformRotateKHR(ValidationState_t& _, + const Instruction* inst) { + // Scope is already checked by ValidateExecutionScope() above. + const uint32_t result_type = inst->type_id(); + if (!_.IsIntScalarOrVectorType(result_type) && + !_.IsFloatScalarOrVectorType(result_type) && + !_.IsBoolScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a scalar or vector of " + "floating-point, integer or boolean type."; + } + + const uint32_t value_type = _.GetTypeId(inst->GetOperandAs(3)); + if (value_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type must be the same as the type of Value."; + } + + const uint32_t delta_type = _.GetTypeId(inst->GetOperandAs(4)); + if (!_.IsUnsignedIntScalarType(delta_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Delta must be a scalar of integer type, whose Signedness " + "operand is 0."; + } + + if (inst->words().size() > 6) { + const uint32_t cluster_size_op_id = inst->GetOperandAs(5); + const uint32_t cluster_size_type = _.GetTypeId(cluster_size_op_id); + if (!_.IsUnsignedIntScalarType(cluster_size_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "ClusterSize must be a scalar of integer type, whose " + "Signedness operand is 0."; + } + + uint64_t cluster_size; + if (!_.GetConstantValUint64(cluster_size_op_id, &cluster_size)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "ClusterSize must come from a constant instruction."; + } + + if ((cluster_size == 0) || ((cluster_size & (cluster_size - 1)) != 0)) { + return _.diag(SPV_WARNING, inst) + << "Behavior is undefined unless ClusterSize is at least 1 and a " + "power of 2."; + } + + // TODO(kpet) Warn about undefined behavior when ClusterSize is greater than + // the declared SubGroupSize + } + + return SPV_SUCCESS; +} + } // namespace // Validates correctness of non-uniform group instructions. @@ -79,6 +132,8 @@ spv_result_t NonUniformPass(ValidationState_t& _, const Instruction* inst) { switch (opcode) { case SpvOpGroupNonUniformBallotBitCount: return ValidateGroupNonUniformBallotBitCount(_, inst); + case SpvOpGroupNonUniformRotateKHR: + return ValidateGroupNonUniformRotateKHR(_, inst); default: break; } diff --git a/source/val/validate_ray_query.cpp b/source/val/validate_ray_query.cpp new file mode 100644 index 00000000..b553449d --- /dev/null +++ b/source/val/validate_ray_query.cpp @@ -0,0 +1,273 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates ray query instructions from SPV_KHR_ray_query + +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +spv_result_t ValidateRayQueryPointer(ValidationState_t& _, + const Instruction* inst, + uint32_t ray_query_index) { + const uint32_t ray_query_id = inst->GetOperandAs(ray_query_index); + auto variable = _.FindDef(ray_query_id); + const auto var_opcode = variable->opcode(); + if (!variable || + (var_opcode != SpvOpVariable && var_opcode != SpvOpFunctionParameter && + var_opcode != SpvOpAccessChain)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Query must be a memory object declaration"; + } + auto pointer = _.FindDef(variable->GetOperandAs(0)); + if (!pointer || pointer->opcode() != SpvOpTypePointer) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Query must be a pointer"; + } + auto type = _.FindDef(pointer->GetOperandAs(2)); + if (!type || type->opcode() != SpvOpTypeRayQueryKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Query must be a pointer to OpTypeRayQueryKHR"; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateIntersectionId(ValidationState_t& _, + const Instruction* inst, + uint32_t intersection_index) { + const uint32_t intersection_id = + inst->GetOperandAs(intersection_index); + const uint32_t intersection_type = _.GetTypeId(intersection_id); + const SpvOp intersection_opcode = _.GetIdOpcode(intersection_id); + if (!_.IsIntScalarType(intersection_type) || + _.GetBitWidth(intersection_type) != 32 || + !spvOpcodeIsConstant(intersection_opcode)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Intersection ID to be a constant 32-bit int scalar"; + } + + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t RayQueryPass(ValidationState_t& _, const Instruction* inst) { + const SpvOp opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + + switch (opcode) { + case SpvOpRayQueryInitializeKHR: { + if (auto error = ValidateRayQueryPointer(_, inst, 0)) return error; + + if (_.GetIdOpcode(_.GetOperandTypeId(inst, 1)) != + SpvOpTypeAccelerationStructureKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Acceleration Structure to be of type " + "OpTypeAccelerationStructureKHR"; + } + + const uint32_t ray_flags = _.GetOperandTypeId(inst, 2); + if (!_.IsIntScalarType(ray_flags) || _.GetBitWidth(ray_flags) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Flags must be a 32-bit int scalar"; + } + + const uint32_t cull_mask = _.GetOperandTypeId(inst, 3); + if (!_.IsIntScalarType(cull_mask) || _.GetBitWidth(cull_mask) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cull Mask must be a 32-bit int scalar"; + } + + const uint32_t ray_origin = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatVectorType(ray_origin) || _.GetDimension(ray_origin) != 3 || + _.GetBitWidth(ray_origin) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Origin must be a 32-bit float 3-component vector"; + } + + const uint32_t ray_tmin = _.GetOperandTypeId(inst, 5); + if (!_.IsFloatScalarType(ray_tmin) || _.GetBitWidth(ray_tmin) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray TMin must be a 32-bit float scalar"; + } + + const uint32_t ray_direction = _.GetOperandTypeId(inst, 6); + if (!_.IsFloatVectorType(ray_direction) || + _.GetDimension(ray_direction) != 3 || + _.GetBitWidth(ray_direction) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Direction must be a 32-bit float 3-component vector"; + } + + const uint32_t ray_tmax = _.GetOperandTypeId(inst, 7); + if (!_.IsFloatScalarType(ray_tmax) || _.GetBitWidth(ray_tmax) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray TMax must be a 32-bit float scalar"; + } + break; + } + + case SpvOpRayQueryTerminateKHR: + case SpvOpRayQueryConfirmIntersectionKHR: { + if (auto error = ValidateRayQueryPointer(_, inst, 0)) return error; + break; + } + + case SpvOpRayQueryGenerateIntersectionKHR: { + if (auto error = ValidateRayQueryPointer(_, inst, 0)) return error; + + const uint32_t hit_t_id = _.GetOperandTypeId(inst, 1); + if (!_.IsFloatScalarType(hit_t_id) || _.GetBitWidth(hit_t_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hit T must be a 32-bit float scalar"; + } + + break; + } + + case SpvOpRayQueryGetIntersectionFrontFaceKHR: + case SpvOpRayQueryProceedKHR: + case SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + + if (!_.IsBoolScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be bool scalar type"; + } + + if (opcode == SpvOpRayQueryGetIntersectionFrontFaceKHR) { + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + } + + break; + } + + case SpvOpRayQueryGetIntersectionTKHR: + case SpvOpRayQueryGetRayTMinKHR: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + + if (!_.IsFloatScalarType(result_type) || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be 32-bit float scalar type"; + } + + if (opcode == SpvOpRayQueryGetIntersectionTKHR) { + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + } + + break; + } + + case SpvOpRayQueryGetIntersectionTypeKHR: + case SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR: + case SpvOpRayQueryGetIntersectionInstanceIdKHR: + case SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: + case SpvOpRayQueryGetIntersectionGeometryIndexKHR: + case SpvOpRayQueryGetIntersectionPrimitiveIndexKHR: + case SpvOpRayQueryGetRayFlagsKHR: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + + if (!_.IsIntScalarType(result_type) || _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be 32-bit int scalar type"; + } + + if (opcode != SpvOpRayQueryGetRayFlagsKHR) { + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + } + + break; + } + + case SpvOpRayQueryGetIntersectionObjectRayDirectionKHR: + case SpvOpRayQueryGetIntersectionObjectRayOriginKHR: + case SpvOpRayQueryGetWorldRayDirectionKHR: + case SpvOpRayQueryGetWorldRayOriginKHR: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + + if (!_.IsFloatVectorType(result_type) || + _.GetDimension(result_type) != 3 || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be 32-bit float 3-component " + "vector type"; + } + + if (opcode == SpvOpRayQueryGetIntersectionObjectRayDirectionKHR || + opcode == SpvOpRayQueryGetIntersectionObjectRayOriginKHR) { + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + } + + break; + } + + case SpvOpRayQueryGetIntersectionBarycentricsKHR: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + + if (!_.IsFloatVectorType(result_type) || + _.GetDimension(result_type) != 2 || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be 32-bit float 2-component " + "vector type"; + } + + break; + } + + case SpvOpRayQueryGetIntersectionObjectToWorldKHR: + case SpvOpRayQueryGetIntersectionWorldToObjectKHR: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + + uint32_t num_rows = 0; + uint32_t num_cols = 0; + uint32_t col_type = 0; + uint32_t component_type = 0; + if (!_.GetMatrixTypeInfo(result_type, &num_rows, &num_cols, &col_type, + &component_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected matrix type as Result Type"; + } + + if (num_cols != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type matrix to have a Column Count of 4"; + } + + if (!_.IsFloatScalarType(component_type) || + _.GetBitWidth(result_type) != 32 || num_rows != 3) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type matrix to have a Column Type of " + "3-component 32-bit float vectors"; + } + break; + } + + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/source/val/validate_ray_tracing.cpp b/source/val/validate_ray_tracing.cpp new file mode 100644 index 00000000..5b5c8da1 --- /dev/null +++ b/source/val/validate_ray_tracing.cpp @@ -0,0 +1,209 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates ray tracing instructions from SPV_KHR_ray_tracing + +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +spv_result_t RayTracingPass(ValidationState_t& _, const Instruction* inst) { + const SpvOp opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + + switch (opcode) { + case SpvOpTraceRayKHR: { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [](SpvExecutionModel model, std::string* message) { + if (model != SpvExecutionModelRayGenerationKHR && + model != SpvExecutionModelClosestHitKHR && + model != SpvExecutionModelMissKHR) { + if (message) { + *message = + "OpTraceRayKHR requires RayGenerationKHR, " + "ClosestHitKHR and MissKHR execution models"; + } + return false; + } + return true; + }); + + if (_.GetIdOpcode(_.GetOperandTypeId(inst, 0)) != + SpvOpTypeAccelerationStructureKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Acceleration Structure to be of type " + "OpTypeAccelerationStructureKHR"; + } + + const uint32_t ray_flags = _.GetOperandTypeId(inst, 1); + if (!_.IsIntScalarType(ray_flags) || _.GetBitWidth(ray_flags) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Flags must be a 32-bit int scalar"; + } + + const uint32_t cull_mask = _.GetOperandTypeId(inst, 2); + if (!_.IsIntScalarType(cull_mask) || _.GetBitWidth(cull_mask) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cull Mask must be a 32-bit int scalar"; + } + + const uint32_t sbt_offset = _.GetOperandTypeId(inst, 3); + if (!_.IsIntScalarType(sbt_offset) || _.GetBitWidth(sbt_offset) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "SBT Offset must be a 32-bit int scalar"; + } + + const uint32_t sbt_stride = _.GetOperandTypeId(inst, 4); + if (!_.IsIntScalarType(sbt_stride) || _.GetBitWidth(sbt_stride) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "SBT Stride must be a 32-bit int scalar"; + } + + const uint32_t miss_index = _.GetOperandTypeId(inst, 5); + if (!_.IsIntScalarType(miss_index) || _.GetBitWidth(miss_index) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Miss Index must be a 32-bit int scalar"; + } + + const uint32_t ray_origin = _.GetOperandTypeId(inst, 6); + if (!_.IsFloatVectorType(ray_origin) || _.GetDimension(ray_origin) != 3 || + _.GetBitWidth(ray_origin) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Origin must be a 32-bit float 3-component vector"; + } + + const uint32_t ray_tmin = _.GetOperandTypeId(inst, 7); + if (!_.IsFloatScalarType(ray_tmin) || _.GetBitWidth(ray_tmin) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray TMin must be a 32-bit float scalar"; + } + + const uint32_t ray_direction = _.GetOperandTypeId(inst, 8); + if (!_.IsFloatVectorType(ray_direction) || + _.GetDimension(ray_direction) != 3 || + _.GetBitWidth(ray_direction) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Direction must be a 32-bit float 3-component vector"; + } + + const uint32_t ray_tmax = _.GetOperandTypeId(inst, 9); + if (!_.IsFloatScalarType(ray_tmax) || _.GetBitWidth(ray_tmax) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray TMax must be a 32-bit float scalar"; + } + + const Instruction* payload = _.FindDef(inst->GetOperandAs(10)); + if (payload->opcode() != SpvOpVariable) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Payload must be the result of a OpVariable"; + } else if (payload->GetOperandAs(2) != + SpvStorageClassRayPayloadKHR && + payload->GetOperandAs(2) != + SpvStorageClassIncomingRayPayloadKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Payload must have storage class RayPayloadKHR or " + "IncomingRayPayloadKHR"; + } + break; + } + + case SpvOpReportIntersectionKHR: { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [](SpvExecutionModel model, std::string* message) { + if (model != SpvExecutionModelIntersectionKHR) { + if (message) { + *message = + "OpReportIntersectionKHR requires IntersectionKHR " + "execution model"; + } + return false; + } + return true; + }); + + if (!_.IsBoolScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be bool scalar type"; + } + + const uint32_t hit = _.GetOperandTypeId(inst, 2); + if (!_.IsFloatScalarType(hit) || _.GetBitWidth(hit) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hit must be a 32-bit int scalar"; + } + + const uint32_t hit_kind = _.GetOperandTypeId(inst, 3); + if (!_.IsUnsignedIntScalarType(hit_kind) || + _.GetBitWidth(hit_kind) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hit Kind must be a 32-bit unsigned int scalar"; + } + break; + } + + case SpvOpExecuteCallableKHR: { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation([](SpvExecutionModel model, + std::string* message) { + if (model != SpvExecutionModelRayGenerationKHR && + model != SpvExecutionModelClosestHitKHR && + model != SpvExecutionModelMissKHR && + model != SpvExecutionModelCallableKHR) { + if (message) { + *message = + "OpExecuteCallableKHR requires RayGenerationKHR, " + "ClosestHitKHR, MissKHR and CallableKHR execution models"; + } + return false; + } + return true; + }); + + const uint32_t sbt_index = _.GetOperandTypeId(inst, 0); + if (!_.IsUnsignedIntScalarType(sbt_index) || + _.GetBitWidth(sbt_index) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "SBT Index must be a 32-bit unsigned int scalar"; + } + + const auto callable_data = _.FindDef(inst->GetOperandAs(1)); + if (callable_data->opcode() != SpvOpVariable) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Callable Data must be the result of a OpVariable"; + } else if (callable_data->GetOperandAs(2) != + SpvStorageClassCallableDataKHR && + callable_data->GetOperandAs(2) != + SpvStorageClassIncomingCallableDataKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Callable Data must have storage class CallableDataKHR or " + "IncomingCallableDataKHR"; + } + + break; + } + + default: + break; + } + + return SPV_SUCCESS; +} +} // namespace val +} // namespace spvtools diff --git a/source/val/validate_scopes.cpp b/source/val/validate_scopes.cpp index 29ba5831..e9781802 100644 --- a/source/val/validate_scopes.cpp +++ b/source/val/validate_scopes.cpp @@ -144,14 +144,16 @@ spv_result_t ValidateExecutionScope(ValidationState_t& _, [errorVUID](SpvExecutionModel model, std::string* message) { if (model != SpvExecutionModelTaskNV && model != SpvExecutionModelMeshNV && + model != SpvExecutionModelTaskEXT && + model != SpvExecutionModelMeshEXT && model != SpvExecutionModelTessellationControl && model != SpvExecutionModelGLCompute) { if (message) { *message = errorVUID + "in Vulkan environment, Workgroup execution scope is " - "only for TaskNV, MeshNV, TessellationControl, and " - "GLCompute execution models"; + "only for TaskNV, MeshNV, TaskEXT, MeshEXT, " + "TessellationControl, and GLCompute execution models"; } return false; } @@ -220,30 +222,23 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst, // Vulkan Specific rules if (spvIsVulkanEnv(_.context()->target_env)) { - if (value == SpvScopeCrossDevice) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(4638) << spvOpcodeString(opcode) - << ": in Vulkan environment, Memory Scope cannot be CrossDevice"; - } - // Vulkan 1.0 specifc rules - if (_.context()->target_env == SPV_ENV_VULKAN_1_0 && - value != SpvScopeDevice && value != SpvScopeWorkgroup && - value != SpvScopeInvocation) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(4638) << spvOpcodeString(opcode) - << ": in Vulkan 1.0 environment Memory Scope is limited to " - << "Device, Workgroup and Invocation"; - } - // Vulkan 1.1 specifc rules - if ((_.context()->target_env == SPV_ENV_VULKAN_1_1 || - _.context()->target_env == SPV_ENV_VULKAN_1_2) && - value != SpvScopeDevice && value != SpvScopeWorkgroup && + if (value != SpvScopeDevice && value != SpvScopeWorkgroup && value != SpvScopeSubgroup && value != SpvScopeInvocation && - value != SpvScopeShaderCallKHR) { + value != SpvScopeShaderCallKHR && value != SpvScopeQueueFamily) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << _.VkErrorID(4638) << spvOpcodeString(opcode) - << ": in Vulkan 1.1 and 1.2 environment Memory Scope is limited " - << "to Device, Workgroup, Invocation, and ShaderCall"; + << ": in Vulkan environment Memory Scope is limited to Device, " + "QueueFamily, Workgroup, ShaderCallKHR, Subgroup, or " + "Invocation"; + } else if (_.context()->target_env == SPV_ENV_VULKAN_1_0 && + value == SpvScopeSubgroup && + !_.HasCapability(SpvCapabilitySubgroupBallotKHR) && + !_.HasCapability(SpvCapabilitySubgroupVoteKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(6997) << spvOpcodeString(opcode) + << ": in Vulkan 1.0 environment Memory Scope is can not be " + "Subgroup without SubgroupBallotKHR or SubgroupVoteKHR " + "declared"; } if (value == SpvScopeShaderCallKHR) { @@ -270,22 +265,44 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst, } if (value == SpvScopeWorkgroup) { - std::string errorVUID = _.VkErrorID(4639); + std::string errorVUID = _.VkErrorID(7321); _.function(inst->function()->id()) ->RegisterExecutionModelLimitation( [errorVUID](SpvExecutionModel model, std::string* message) { if (model != SpvExecutionModelGLCompute && + model != SpvExecutionModelTessellationControl && model != SpvExecutionModelTaskNV && - model != SpvExecutionModelMeshNV) { + model != SpvExecutionModelMeshNV && + model != SpvExecutionModelTaskEXT && + model != SpvExecutionModelMeshEXT) { if (message) { *message = errorVUID + "Workgroup Memory Scope is limited to MeshNV, " - "TaskNV, and GLCompute execution model"; + "TaskNV, MeshEXT, TaskEXT, TessellationControl, " + "and GLCompute execution model"; } return false; } return true; }); + + if (_.memory_model() == SpvMemoryModelGLSL450) { + errorVUID = _.VkErrorID(7320); + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [errorVUID](SpvExecutionModel model, std::string* message) { + if (model == SpvExecutionModelTessellationControl) { + if (message) { + *message = + errorVUID + + "Workgroup Memory Scope can't be used with " + "TessellationControl using GLSL450 Memory Model"; + } + return false; + } + return true; + }); + } } } diff --git a/source/val/validate_type.cpp b/source/val/validate_type.cpp index 612fc5c2..6b0881cf 100644 --- a/source/val/validate_type.cpp +++ b/source/val/validate_type.cpp @@ -152,8 +152,8 @@ spv_result_t ValidateTypeVector(ValidationState_t& _, const Instruction* inst) { const auto component_type = _.FindDef(component_id); if (!component_type || !spvOpcodeIsScalarType(component_type->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeVector Component Type '" << _.getIdName(component_id) - << "' is not a scalar type."; + << "OpTypeVector Component Type " << _.getIdName(component_id) + << " is not a scalar type."; } // Validates that the number of components in the vector is valid. @@ -215,21 +215,21 @@ spv_result_t ValidateTypeArray(ValidationState_t& _, const Instruction* inst) { const auto element_type = _.FindDef(element_type_id); if (!element_type || !spvOpcodeGeneratesType(element_type->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeArray Element Type '" << _.getIdName(element_type_id) - << "' is not a type."; + << "OpTypeArray Element Type " << _.getIdName(element_type_id) + << " is not a type."; } if (element_type->opcode() == SpvOpTypeVoid) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeArray Element Type '" << _.getIdName(element_type_id) - << "' is a void type."; + << "OpTypeArray Element Type " << _.getIdName(element_type_id) + << " is a void type."; } if (spvIsVulkanEnv(_.context()->target_env) && element_type->opcode() == SpvOpTypeRuntimeArray) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeArray Element Type '" << _.getIdName(element_type_id) - << "' is not valid in " + << _.VkErrorID(4680) << "OpTypeArray Element Type " + << _.getIdName(element_type_id) << " is not valid in " << spvLogStringForEnv(_.context()->target_env) << " environments."; } @@ -238,8 +238,8 @@ spv_result_t ValidateTypeArray(ValidationState_t& _, const Instruction* inst) { const auto length = _.FindDef(length_id); if (!length || !spvOpcodeIsConstant(length->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeArray Length '" << _.getIdName(length_id) - << "' is not a scalar constant type."; + << "OpTypeArray Length " << _.getIdName(length_id) + << " is not a scalar constant type."; } // NOTE: Check the initialiser value of the constant @@ -248,8 +248,8 @@ spv_result_t ValidateTypeArray(ValidationState_t& _, const Instruction* inst) { const auto const_result_type = _.FindDef(const_inst[const_result_type_index]); if (!const_result_type || SpvOpTypeInt != const_result_type->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeArray Length '" << _.getIdName(length_id) - << "' is not a constant integer type."; + << "OpTypeArray Length " << _.getIdName(length_id) + << " is not a constant integer type."; } switch (length->opcode()) { @@ -261,14 +261,14 @@ spv_result_t ValidateTypeArray(ValidationState_t& _, const Instruction* inst) { const int64_t ivalue = ConstantLiteralAsInt64(width, length->words()); if (ivalue == 0 || (ivalue < 0 && is_signed)) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeArray Length '" << _.getIdName(length_id) - << "' default value must be at least 1: found " << ivalue; + << "OpTypeArray Length " << _.getIdName(length_id) + << " default value must be at least 1: found " << ivalue; } } break; case SpvOpConstantNull: return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeArray Length '" << _.getIdName(length_id) - << "' default value must be at least 1."; + << "OpTypeArray Length " << _.getIdName(length_id) + << " default value must be at least 1."; case SpvOpSpecConstantOp: // Assume it's OK, rather than try to evaluate the operation. break; @@ -285,56 +285,27 @@ spv_result_t ValidateTypeRuntimeArray(ValidationState_t& _, const auto element_type = _.FindDef(element_id); if (!element_type || !spvOpcodeGeneratesType(element_type->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeRuntimeArray Element Type '" - << _.getIdName(element_id) << "' is not a type."; + << "OpTypeRuntimeArray Element Type " << _.getIdName(element_id) + << " is not a type."; } if (element_type->opcode() == SpvOpTypeVoid) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeRuntimeArray Element Type '" - << _.getIdName(element_id) << "' is a void type."; + << "OpTypeRuntimeArray Element Type " << _.getIdName(element_id) + << " is a void type."; } if (spvIsVulkanEnv(_.context()->target_env) && element_type->opcode() == SpvOpTypeRuntimeArray) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeRuntimeArray Element Type '" - << _.getIdName(element_id) << "' is not valid in " + << _.VkErrorID(4680) << "OpTypeRuntimeArray Element Type " + << _.getIdName(element_id) << " is not valid in " << spvLogStringForEnv(_.context()->target_env) << " environments."; } return SPV_SUCCESS; } -bool ContainsOpaqueType(ValidationState_t& _, const Instruction* str) { - const size_t elem_type_index = 1; - uint32_t elem_type_id; - Instruction* elem_type; - - if (spvOpcodeIsBaseOpaqueType(str->opcode())) { - return true; - } - - switch (str->opcode()) { - case SpvOpTypeArray: - case SpvOpTypeRuntimeArray: - elem_type_id = str->GetOperandAs(elem_type_index); - elem_type = _.FindDef(elem_type_id); - return ContainsOpaqueType(_, elem_type); - case SpvOpTypeStruct: - for (size_t member_type_index = 1; - member_type_index < str->operands().size(); ++member_type_index) { - auto member_type_id = str->GetOperandAs(member_type_index); - auto member_type = _.FindDef(member_type_id); - if (ContainsOpaqueType(_, member_type)) return true; - } - break; - default: - break; - } - return false; -} - spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) { const uint32_t struct_id = inst->GetOperandAs(0); for (size_t member_type_index = 1; @@ -348,8 +319,8 @@ spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) { auto member_type = _.FindDef(member_type_id); if (!member_type || !spvOpcodeGeneratesType(member_type->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeStruct Member Type '" << _.getIdName(member_type_id) - << "' is not a type."; + << "OpTypeStruct Member Type " << _.getIdName(member_type_id) + << " is not a type."; } if (member_type->opcode() == SpvOpTypeVoid) { return _.diag(SPV_ERROR_INVALID_ID, inst) @@ -373,7 +344,8 @@ spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) { member_type_index == inst->operands().size() - 1; if (!is_last_member) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "In " << spvLogStringForEnv(_.context()->target_env) + << _.VkErrorID(4680) << "In " + << spvLogStringForEnv(_.context()->target_env) << ", OpTypeRuntimeArray must only be used for the last member " "of an OpTypeStruct"; } @@ -424,8 +396,21 @@ spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) { _.RegisterStructTypeWithBuiltInMember(struct_id); } + const auto isOpaqueType = [&_](const Instruction* opaque_inst) { + auto opcode = opaque_inst->opcode(); + if (_.HasCapability(SpvCapabilityBindlessTextureNV) && + (opcode == SpvOpTypeImage || opcode == SpvOpTypeSampler || + opcode == SpvOpTypeSampledImage)) { + return false; + } else if (spvOpcodeIsBaseOpaqueType(opcode)) { + return true; + } + return false; + }; + if (spvIsVulkanEnv(_.context()->target_env) && - !_.options()->before_hlsl_legalization && ContainsOpaqueType(_, inst)) { + !_.options()->before_hlsl_legalization && + _.ContainsType(inst->id(), isOpaqueType)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << _.VkErrorID(4667) << "In " << spvLogStringForEnv(_.context()->target_env) @@ -441,8 +426,8 @@ spv_result_t ValidateTypePointer(ValidationState_t& _, auto type = _.FindDef(type_id); if (!type || !spvOpcodeGeneratesType(type->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypePointer Type '" << _.getIdName(type_id) - << "' is not a type."; + << "OpTypePointer Type " << _.getIdName(type_id) + << " is not a type."; } // See if this points to a storage image. const auto storage_class = inst->GetOperandAs(1); @@ -476,8 +461,8 @@ spv_result_t ValidateTypeFunction(ValidationState_t& _, const auto return_type = _.FindDef(return_type_id); if (!return_type || !spvOpcodeGeneratesType(return_type->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeFunction Return Type '" << _.getIdName(return_type_id) - << "' is not a type."; + << "OpTypeFunction Return Type " << _.getIdName(return_type_id) + << " is not a type."; } size_t num_args = 0; for (size_t param_type_index = 2; param_type_index < inst->operands().size(); @@ -486,14 +471,14 @@ spv_result_t ValidateTypeFunction(ValidationState_t& _, const auto param_type = _.FindDef(param_id); if (!param_type || !spvOpcodeGeneratesType(param_type->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeFunction Parameter Type '" << _.getIdName(param_id) - << "' is not a type."; + << "OpTypeFunction Parameter Type " << _.getIdName(param_id) + << " is not a type."; } if (param_type->opcode() == SpvOpTypeVoid) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeFunction Parameter Type '" << _.getIdName(param_id) - << "' cannot be OpTypeVoid."; + << "OpTypeFunction Parameter Type " << _.getIdName(param_id) + << " cannot be OpTypeVoid."; } } const uint32_t num_function_args_limit = @@ -501,8 +486,8 @@ spv_result_t ValidateTypeFunction(ValidationState_t& _, if (num_args > num_function_args_limit) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpTypeFunction may not take more than " - << num_function_args_limit << " arguments. OpTypeFunction '" - << _.getIdName(inst->GetOperandAs(0)) << "' has " + << num_function_args_limit << " arguments. OpTypeFunction " + << _.getIdName(inst->GetOperandAs(0)) << " has " << num_args << " arguments."; } @@ -565,9 +550,9 @@ spv_result_t ValidateTypeCooperativeMatrixNV(ValidationState_t& _, if (!component_type || (SpvOpTypeFloat != component_type->opcode() && SpvOpTypeInt != component_type->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeCooperativeMatrixNV Component Type '" + << "OpTypeCooperativeMatrixNV Component Type " << _.getIdName(component_type_id) - << "' is not a scalar numerical type."; + << " is not a scalar numerical type."; } const auto scope_index = 2; @@ -576,8 +561,8 @@ spv_result_t ValidateTypeCooperativeMatrixNV(ValidationState_t& _, if (!scope || !_.IsIntScalarType(scope->type_id()) || !spvOpcodeIsConstant(scope->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeCooperativeMatrixNV Scope '" << _.getIdName(scope_id) - << "' is not a constant instruction with scalar integer type."; + << "OpTypeCooperativeMatrixNV Scope " << _.getIdName(scope_id) + << " is not a constant instruction with scalar integer type."; } const auto rows_index = 3; @@ -586,8 +571,8 @@ spv_result_t ValidateTypeCooperativeMatrixNV(ValidationState_t& _, if (!rows || !_.IsIntScalarType(rows->type_id()) || !spvOpcodeIsConstant(rows->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeCooperativeMatrixNV Rows '" << _.getIdName(rows_id) - << "' is not a constant instruction with scalar integer type."; + << "OpTypeCooperativeMatrixNV Rows " << _.getIdName(rows_id) + << " is not a constant instruction with scalar integer type."; } const auto cols_index = 4; @@ -596,8 +581,8 @@ spv_result_t ValidateTypeCooperativeMatrixNV(ValidationState_t& _, if (!cols || !_.IsIntScalarType(cols->type_id()) || !spvOpcodeIsConstant(cols->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeCooperativeMatrixNV Cols '" << _.getIdName(rows_id) - << "' is not a constant instruction with scalar integer type."; + << "OpTypeCooperativeMatrixNV Cols " << _.getIdName(cols_id) + << " is not a constant instruction with scalar integer type."; } return SPV_SUCCESS; diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index db86fd25..d5ddc9c1 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp @@ -90,6 +90,8 @@ ModuleLayoutSection InstructionLayoutSection( if (current_section == kLayoutFunctionDeclarations) return kLayoutFunctionDeclarations; return kLayoutFunctionDefinitions; + case SpvOpSamplerImageAddressingModeNV: + return kLayoutSamplerImageAddressMode; default: break; } @@ -161,6 +163,7 @@ ValidationState_t::ValidationState_t(const spv_const_context ctx, addressing_model_(SpvAddressingModelMax), memory_model_(SpvMemoryModelMax), pointer_size_and_alignment_(0), + sampler_image_addressing_mode_(0), in_function_(false), num_of_warnings_(0), max_num_of_warnings_(max_warnings) { @@ -175,6 +178,19 @@ ValidationState_t::ValidationState_t(const spv_const_context ctx, } } + // LocalSizeId is only disallowed prior to Vulkan 1.3 without maintenance4. + switch (env) { + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_VULKAN_1_2: + features_.env_allow_localsizeid = false; + break; + default: + features_.env_allow_localsizeid = true; + break; + } + // Only attempt to count if we have words, otherwise let the other validation // fail and generate an error. if (num_words > 0) { @@ -192,9 +208,12 @@ ValidationState_t::ValidationState_t(const spv_const_context ctx, } UpdateFeaturesBasedOnSpirvVersion(&features_, version_); - friendly_mapper_ = spvtools::MakeUnique( - context_, words_, num_words_); - name_mapper_ = friendly_mapper_->GetNameMapper(); + name_mapper_ = spvtools::GetTrivialNameMapper(); + if (options_->use_friendly_names) { + friendly_mapper_ = spvtools::MakeUnique( + context_, words_, num_words_); + name_mapper_ = friendly_mapper_->GetNameMapper(); + } } void ValidationState_t::preallocateStorage() { @@ -229,7 +248,7 @@ std::string ValidationState_t::getIdName(uint32_t id) const { const std::string id_name = name_mapper_(id); std::stringstream out; - out << id << "[%" << id_name << "]"; + out << "'" << id << "[%" << id_name << "]'"; return out.str(); } @@ -379,13 +398,19 @@ void ValidationState_t::RegisterCapability(SpvCapability cap) { features_.free_fp_rounding_mode = true; break; case SpvCapabilityVariablePointers: - features_.variable_pointers = true; - features_.variable_pointers_storage_buffer = true; - break; case SpvCapabilityVariablePointersStorageBuffer: - features_.variable_pointers_storage_buffer = true; + features_.variable_pointers = true; break; default: + // TODO(dneto): For now don't validate SPV_NV_ray_tracing, which uses + // capability SpvCapabilityRayTracingNV. + // SpvCapabilityRayTracingProvisionalKHR would need the same treatment. + // One of the differences going from SPV_KHR_ray_tracing from + // provisional to final spec was the provisional spec uses Locations + // for variables in certain storage classes, just like the + // SPV_NV_ray_tracing extension. So it mimics the NVIDIA extension. + // The final SPV_KHR_ray_tracing uses a different capability token + // number, so it doesn't fall into this case. break; } } @@ -438,7 +463,7 @@ void ValidationState_t::set_addressing_model(SpvAddressingModel am) { default: // fall through case SpvAddressingModelPhysical64: - case SpvAddressingModelPhysicalStorageBuffer64EXT: + case SpvAddressingModelPhysicalStorageBuffer64: pointer_size_and_alignment_ = 8; break; } @@ -454,6 +479,15 @@ void ValidationState_t::set_memory_model(SpvMemoryModel mm) { SpvMemoryModel ValidationState_t::memory_model() const { return memory_model_; } +void ValidationState_t::set_samplerimage_variable_address_mode( + uint32_t bit_width) { + sampler_image_addressing_mode_ = bit_width; +} + +uint32_t ValidationState_t::samplerimage_variable_address_mode() const { + return sampler_image_addressing_mode_; +} + spv_result_t ValidationState_t::RegisterFunction( uint32_t id, uint32_t ret_type_id, SpvFunctionControlMask function_control, uint32_t function_type_id) { @@ -476,7 +510,7 @@ spv_result_t ValidationState_t::RegisterFunctionEnd() { "inside of another function"); assert(in_block() == false && "RegisterFunctionParameter can only be called when parsing the binary " - "ouside of a block"); + "outside of a block"); current_function().RegisterFunctionEnd(); in_function_ = false; return SPV_SUCCESS; @@ -494,15 +528,13 @@ void ValidationState_t::RegisterDebugInstruction(const Instruction* inst) { switch (inst->opcode()) { case SpvOpName: { const auto target = inst->GetOperandAs(0); - const auto* str = reinterpret_cast(inst->words().data() + - inst->operand(1).offset); + const std::string str = inst->GetOperandAs(1); AssignNameToId(target, str); break; } case SpvOpMemberName: { const auto target = inst->GetOperandAs(0); - const auto* str = reinterpret_cast(inst->words().data() + - inst->operand(2).offset); + const std::string str = inst->GetOperandAs(2); AssignNameToId(target, str); break; } @@ -520,17 +552,39 @@ void ValidationState_t::RegisterDebugInstruction(const Instruction* inst) { void ValidationState_t::RegisterInstruction(Instruction* inst) { if (inst->id()) all_definitions_.insert(std::make_pair(inst->id(), inst)); - // If the instruction is using an OpTypeSampledImage as an operand, it should - // be recorded. The validator will ensure that all usages of an - // OpTypeSampledImage and its definition are in the same basic block. - for (uint16_t i = 0; i < inst->operands().size(); ++i) { + // Some validation checks are easier by getting all the consumers + for (size_t i = 0; i < inst->operands().size(); ++i) { const spv_parsed_operand_t& operand = inst->operand(i); - if (SPV_OPERAND_TYPE_ID == operand.type) { + if ((SPV_OPERAND_TYPE_ID == operand.type) || + (SPV_OPERAND_TYPE_TYPE_ID == operand.type)) { const uint32_t operand_word = inst->word(operand.offset); Instruction* operand_inst = FindDef(operand_word); - if (operand_inst && SpvOpSampledImage == operand_inst->opcode()) { + if (!operand_inst) { + continue; + } + + // If the instruction is using an OpTypeSampledImage as an operand, it + // should be recorded. The validator will ensure that all usages of an + // OpTypeSampledImage and its definition are in the same basic block. + if ((SPV_OPERAND_TYPE_ID == operand.type) && + (SpvOpSampledImage == operand_inst->opcode())) { RegisterSampledImageConsumer(operand_word, inst); } + + // In order to track storage classes (not Function) used per execution + // model we can't use RegisterExecutionModelLimitation on instructions + // like OpTypePointer which are going to be in the pre-function section. + // Instead just need to register storage class usage for consumers in a + // function block. + if (inst->function()) { + if (operand_inst->opcode() == SpvOpTypePointer) { + RegisterStorageClassConsumer( + operand_inst->GetOperandAs(1), inst); + } else if (operand_inst->opcode() == SpvOpVariable) { + RegisterStorageClassConsumer( + operand_inst->GetOperandAs(2), inst); + } + } } } } @@ -550,6 +604,188 @@ void ValidationState_t::RegisterSampledImageConsumer(uint32_t sampled_image_id, sampled_image_consumers_[sampled_image_id].push_back(consumer); } +void ValidationState_t::RegisterStorageClassConsumer( + SpvStorageClass storage_class, Instruction* consumer) { + if (spvIsVulkanEnv(context()->target_env)) { + if (storage_class == SpvStorageClassOutput) { + std::string errorVUID = VkErrorID(4644); + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation([errorVUID]( + SpvExecutionModel model, + std::string* message) { + if (model == SpvExecutionModelGLCompute || + model == SpvExecutionModelRayGenerationKHR || + model == SpvExecutionModelIntersectionKHR || + model == SpvExecutionModelAnyHitKHR || + model == SpvExecutionModelClosestHitKHR || + model == SpvExecutionModelMissKHR || + model == SpvExecutionModelCallableKHR) { + if (message) { + *message = + errorVUID + + "in Vulkan environment, Output Storage Class must not be " + "used in GLCompute, RayGenerationKHR, IntersectionKHR, " + "AnyHitKHR, ClosestHitKHR, MissKHR, or CallableKHR " + "execution models"; + } + return false; + } + return true; + }); + } + + if (storage_class == SpvStorageClassWorkgroup) { + std::string errorVUID = VkErrorID(4645); + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation([errorVUID]( + SpvExecutionModel model, + std::string* message) { + if (model != SpvExecutionModelGLCompute && + model != SpvExecutionModelTaskNV && + model != SpvExecutionModelMeshNV && + model != SpvExecutionModelTaskEXT && + model != SpvExecutionModelMeshEXT) { + if (message) { + *message = + errorVUID + + "in Vulkan environment, Workgroup Storage Class is limited " + "to MeshNV, TaskNV, and GLCompute execution model"; + } + return false; + } + return true; + }); + } + } + + if (storage_class == SpvStorageClassCallableDataKHR) { + std::string errorVUID = VkErrorID(4704); + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation([errorVUID](SpvExecutionModel model, + std::string* message) { + if (model != SpvExecutionModelRayGenerationKHR && + model != SpvExecutionModelClosestHitKHR && + model != SpvExecutionModelCallableKHR && + model != SpvExecutionModelMissKHR) { + if (message) { + *message = errorVUID + + "CallableDataKHR Storage Class is limited to " + "RayGenerationKHR, ClosestHitKHR, CallableKHR, and " + "MissKHR execution model"; + } + return false; + } + return true; + }); + } else if (storage_class == SpvStorageClassIncomingCallableDataKHR) { + std::string errorVUID = VkErrorID(4705); + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation([errorVUID](SpvExecutionModel model, + std::string* message) { + if (model != SpvExecutionModelCallableKHR) { + if (message) { + *message = errorVUID + + "IncomingCallableDataKHR Storage Class is limited to " + "CallableKHR execution model"; + } + return false; + } + return true; + }); + } else if (storage_class == SpvStorageClassRayPayloadKHR) { + std::string errorVUID = VkErrorID(4698); + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation([errorVUID](SpvExecutionModel model, + std::string* message) { + if (model != SpvExecutionModelRayGenerationKHR && + model != SpvExecutionModelClosestHitKHR && + model != SpvExecutionModelMissKHR) { + if (message) { + *message = + errorVUID + + "RayPayloadKHR Storage Class is limited to RayGenerationKHR, " + "ClosestHitKHR, and MissKHR execution model"; + } + return false; + } + return true; + }); + } else if (storage_class == SpvStorageClassHitAttributeKHR) { + std::string errorVUID = VkErrorID(4701); + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation( + [errorVUID](SpvExecutionModel model, std::string* message) { + if (model != SpvExecutionModelIntersectionKHR && + model != SpvExecutionModelAnyHitKHR && + model != SpvExecutionModelClosestHitKHR) { + if (message) { + *message = errorVUID + + "HitAttributeKHR Storage Class is limited to " + "IntersectionKHR, AnyHitKHR, sand ClosestHitKHR " + "execution model"; + } + return false; + } + return true; + }); + } else if (storage_class == SpvStorageClassIncomingRayPayloadKHR) { + std::string errorVUID = VkErrorID(4699); + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation( + [errorVUID](SpvExecutionModel model, std::string* message) { + if (model != SpvExecutionModelAnyHitKHR && + model != SpvExecutionModelClosestHitKHR && + model != SpvExecutionModelMissKHR) { + if (message) { + *message = + errorVUID + + "IncomingRayPayloadKHR Storage Class is limited to " + "AnyHitKHR, ClosestHitKHR, and MissKHR execution model"; + } + return false; + } + return true; + }); + } else if (storage_class == SpvStorageClassShaderRecordBufferKHR) { + std::string errorVUID = VkErrorID(7119); + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation( + [errorVUID](SpvExecutionModel model, std::string* message) { + if (model != SpvExecutionModelRayGenerationKHR && + model != SpvExecutionModelIntersectionKHR && + model != SpvExecutionModelAnyHitKHR && + model != SpvExecutionModelClosestHitKHR && + model != SpvExecutionModelCallableKHR && + model != SpvExecutionModelMissKHR) { + if (message) { + *message = + errorVUID + + "ShaderRecordBufferKHR Storage Class is limited to " + "RayGenerationKHR, IntersectionKHR, AnyHitKHR, " + "ClosestHitKHR, CallableKHR, and MissKHR execution model"; + } + return false; + } + return true; + }); + } else if (storage_class == SpvStorageClassTaskPayloadWorkgroupEXT) { + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation( + [](SpvExecutionModel model, std::string* message) { + if (model != SpvExecutionModelTaskEXT && + model != SpvExecutionModelMeshEXT) { + if (message) { + *message = + "TaskPayloadWorkgroupEXT Storage Class is limited to " + "TaskEXT and MeshKHR execution model"; + } + return false; + } + return true; + }); + } +} + uint32_t ValidationState_t::getIdBound() const { return id_bound_; } void ValidationState_t::setIdBound(const uint32_t bound) { id_bound_ = bound; } @@ -656,19 +892,19 @@ uint32_t ValidationState_t::GetBitWidth(uint32_t id) const { bool ValidationState_t::IsVoidType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypeVoid; + return inst && inst->opcode() == SpvOpTypeVoid; } bool ValidationState_t::IsFloatScalarType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypeFloat; + return inst && inst->opcode() == SpvOpTypeFloat; } bool ValidationState_t::IsFloatVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeVector) { return IsFloatScalarType(GetComponentType(id)); @@ -679,7 +915,9 @@ bool ValidationState_t::IsFloatVectorType(uint32_t id) const { bool ValidationState_t::IsFloatScalarOrVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeFloat) { return true; @@ -694,13 +932,14 @@ bool ValidationState_t::IsFloatScalarOrVectorType(uint32_t id) const { bool ValidationState_t::IsIntScalarType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypeInt; + return inst && inst->opcode() == SpvOpTypeInt; } bool ValidationState_t::IsIntVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeVector) { return IsIntScalarType(GetComponentType(id)); @@ -711,7 +950,9 @@ bool ValidationState_t::IsIntVectorType(uint32_t id) const { bool ValidationState_t::IsIntScalarOrVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeInt) { return true; @@ -726,13 +967,14 @@ bool ValidationState_t::IsIntScalarOrVectorType(uint32_t id) const { bool ValidationState_t::IsUnsignedIntScalarType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypeInt && inst->word(3) == 0; + return inst && inst->opcode() == SpvOpTypeInt && inst->word(3) == 0; } bool ValidationState_t::IsUnsignedIntVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeVector) { return IsUnsignedIntScalarType(GetComponentType(id)); @@ -743,13 +985,14 @@ bool ValidationState_t::IsUnsignedIntVectorType(uint32_t id) const { bool ValidationState_t::IsSignedIntScalarType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypeInt && inst->word(3) == 1; + return inst && inst->opcode() == SpvOpTypeInt && inst->word(3) == 1; } bool ValidationState_t::IsSignedIntVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeVector) { return IsSignedIntScalarType(GetComponentType(id)); @@ -760,13 +1003,14 @@ bool ValidationState_t::IsSignedIntVectorType(uint32_t id) const { bool ValidationState_t::IsBoolScalarType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypeBool; + return inst && inst->opcode() == SpvOpTypeBool; } bool ValidationState_t::IsBoolVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeVector) { return IsBoolScalarType(GetComponentType(id)); @@ -777,7 +1021,9 @@ bool ValidationState_t::IsBoolVectorType(uint32_t id) const { bool ValidationState_t::IsBoolScalarOrVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeBool) { return true; @@ -792,7 +1038,9 @@ bool ValidationState_t::IsBoolScalarOrVectorType(uint32_t id) const { bool ValidationState_t::IsFloatMatrixType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeMatrix) { return IsFloatScalarType(GetComponentType(id)); @@ -847,8 +1095,7 @@ bool ValidationState_t::GetStructMemberTypes( bool ValidationState_t::IsPointerType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypePointer; + return inst && inst->opcode() == SpvOpTypePointer; } bool ValidationState_t::GetPointerTypeInfo(uint32_t id, uint32_t* data_type, @@ -864,10 +1111,14 @@ bool ValidationState_t::GetPointerTypeInfo(uint32_t id, uint32_t* data_type, return true; } +bool ValidationState_t::IsAccelerationStructureType(uint32_t id) const { + const Instruction* inst = FindDef(id); + return inst && inst->opcode() == SpvOpTypeAccelerationStructureKHR; +} + bool ValidationState_t::IsCooperativeMatrixType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypeCooperativeMatrixNV; + return inst && inst->opcode() == SpvOpTypeCooperativeMatrixNV; } bool ValidationState_t::IsFloatCooperativeMatrixType(uint32_t id) const { @@ -885,6 +1136,13 @@ bool ValidationState_t::IsUnsignedIntCooperativeMatrixType(uint32_t id) const { return IsUnsignedIntScalarType(FindDef(id)->word(2)); } +// Either a 32 bit 2-component uint vector or a 64 bit uint scalar +bool ValidationState_t::IsUnsigned64BitHandle(uint32_t id) const { + return ((IsUnsignedIntScalarType(id) && GetBitWidth(id) == 64) || + (IsUnsignedIntVectorType(id) && GetDimension(id) == 2 && + GetBitWidth(id) == 32)); +} + spv_result_t ValidationState_t::CooperativeMatrixShapesMatch( const Instruction* inst, uint32_t m1, uint32_t m2) { const auto m1_type = FindDef(m1); @@ -1189,16 +1447,13 @@ const Instruction* ValidationState_t::TracePointer( return base_ptr; } -bool ValidationState_t::ContainsSizedIntOrFloatType(uint32_t id, SpvOp type, - uint32_t width) const { - if (type != SpvOpTypeInt && type != SpvOpTypeFloat) return false; - +bool ValidationState_t::ContainsType( + uint32_t id, const std::function& f, + bool traverse_all_types) const { const auto inst = FindDef(id); if (!inst) return false; - if (inst->opcode() == type) { - return inst->GetOperandAs(1u) == width; - } + if (f(inst)) return true; switch (inst->opcode()) { case SpvOpTypeArray: @@ -1208,24 +1463,45 @@ bool ValidationState_t::ContainsSizedIntOrFloatType(uint32_t id, SpvOp type, case SpvOpTypeImage: case SpvOpTypeSampledImage: case SpvOpTypeCooperativeMatrixNV: - return ContainsSizedIntOrFloatType(inst->GetOperandAs(1u), type, - width); + return ContainsType(inst->GetOperandAs(1u), f, + traverse_all_types); case SpvOpTypePointer: if (IsForwardPointer(id)) return false; - return ContainsSizedIntOrFloatType(inst->GetOperandAs(2u), type, - width); - case SpvOpTypeFunction: - case SpvOpTypeStruct: { - for (uint32_t i = 1; i < inst->operands().size(); ++i) { - if (ContainsSizedIntOrFloatType(inst->GetOperandAs(i), type, - width)) - return true; + if (traverse_all_types) { + return ContainsType(inst->GetOperandAs(2u), f, + traverse_all_types); } - return false; - } + break; + case SpvOpTypeFunction: + case SpvOpTypeStruct: + if (inst->opcode() == SpvOpTypeFunction && !traverse_all_types) { + return false; + } + for (uint32_t i = 1; i < inst->operands().size(); ++i) { + if (ContainsType(inst->GetOperandAs(i), f, + traverse_all_types)) { + return true; + } + } + break; default: - return false; + break; } + + return false; +} + +bool ValidationState_t::ContainsSizedIntOrFloatType(uint32_t id, SpvOp type, + uint32_t width) const { + if (type != SpvOpTypeInt && type != SpvOpTypeFloat) return false; + + const auto f = [type, width](const Instruction* inst) { + if (inst->opcode() == type) { + return inst->GetOperandAs(1u) == width; + } + return false; + }; + return ContainsType(id, f); } bool ValidationState_t::ContainsLimitedUseIntOrFloatType(uint32_t id) const { @@ -1240,6 +1516,13 @@ bool ValidationState_t::ContainsLimitedUseIntOrFloatType(uint32_t id) const { return false; } +bool ValidationState_t::ContainsRuntimeArray(uint32_t id) const { + const auto f = [](const Instruction* inst) { + return inst->opcode() == SpvOpTypeRuntimeArray; + }; + return ContainsType(id, f, /* traverse_all_types = */ false); +} + bool ValidationState_t::IsValidStorageClass( SpvStorageClass storage_class) const { if (spvIsVulkanEnv(context()->target_env)) { @@ -1261,6 +1544,7 @@ bool ValidationState_t::IsValidStorageClass( case SpvStorageClassCallableDataKHR: case SpvStorageClassIncomingCallableDataKHR: case SpvStorageClassShaderRecordBufferKHR: + case SpvStorageClassTaskPayloadWorkgroupEXT: return true; default: return false; @@ -1279,13 +1563,25 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return ""; } - // This large switch case is only searched when an error has occured. + // This large switch case is only searched when an error has occurred. // If an id is changed, the old case must be modified or removed. Each string // here is interpreted as being "implemented" // Clang format adds spaces between hyphens // clang-format off switch (id) { + case 4154: + return VUID_WRAP(VUID-BaryCoordKHR-BaryCoordKHR-04154); + case 4155: + return VUID_WRAP(VUID-BaryCoordKHR-BaryCoordKHR-04155); + case 4156: + return VUID_WRAP(VUID-BaryCoordKHR-BaryCoordKHR-04156); + case 4160: + return VUID_WRAP(VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04160); + case 4161: + return VUID_WRAP(VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04161); + case 4162: + return VUID_WRAP(VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04162); case 4181: return VUID_WRAP(VUID-BaseInstance-BaseInstance-04181); case 4182: @@ -1318,6 +1614,12 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-CullDistance-CullDistance-04199); case 4200: return VUID_WRAP(VUID-CullDistance-CullDistance-04200); + case 6735: + return VUID_WRAP(VUID-CullMaskKHR-CullMaskKHR-06735); // Execution Model + case 6736: + return VUID_WRAP(VUID-CullMaskKHR-CullMaskKHR-06736); // input storage + case 6737: + return VUID_WRAP(VUID-CullMaskKHR-CullMaskKHR-06737); // 32 int scalar case 4205: return VUID_WRAP(VUID-DeviceIndex-DeviceIndex-04205); case 4206: @@ -1686,8 +1988,8 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-StandaloneSpirv-None-04637); case 4638: return VUID_WRAP(VUID-StandaloneSpirv-None-04638); - case 4639: - return VUID_WRAP(VUID-StandaloneSpirv-None-04639); + case 7321: + return VUID_WRAP(VUID-StandaloneSpirv-None-07321); case 4640: return VUID_WRAP(VUID-StandaloneSpirv-None-04640); case 4641: @@ -1696,6 +1998,10 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-StandaloneSpirv-None-04642); case 4643: return VUID_WRAP(VUID-StandaloneSpirv-None-04643); + case 4644: + return VUID_WRAP(VUID-StandaloneSpirv-None-04644); + case 4645: + return VUID_WRAP(VUID-StandaloneSpirv-None-04645); case 4651: return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-04651); case 4652: @@ -1724,18 +2030,38 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-StandaloneSpirv-None-04667); case 4669: return VUID_WRAP(VUID-StandaloneSpirv-GLSLShared-04669); + case 4670: + return VUID_WRAP(VUID-StandaloneSpirv-Flat-04670); case 4675: return VUID_WRAP(VUID-StandaloneSpirv-FPRoundingMode-04675); case 4677: return VUID_WRAP(VUID-StandaloneSpirv-Invariant-04677); + case 4680: + return VUID_WRAP(VUID-StandaloneSpirv-OpTypeRuntimeArray-04680); case 4682: return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04682); - case 4683: - return VUID_WRAP(VUID-StandaloneSpirv-LocalSize-04683); + case 6426: + return VUID_WRAP(VUID-StandaloneSpirv-LocalSize-06426); // formally 04683 case 4685: return VUID_WRAP(VUID-StandaloneSpirv-OpGroupNonUniformBallotBitCount-04685); case 4686: return VUID_WRAP(VUID-StandaloneSpirv-None-04686); + case 4698: + return VUID_WRAP(VUID-StandaloneSpirv-RayPayloadKHR-04698); + case 4699: + return VUID_WRAP(VUID-StandaloneSpirv-IncomingRayPayloadKHR-04699); + case 4701: + return VUID_WRAP(VUID-StandaloneSpirv-HitAttributeKHR-04701); + case 4703: + return VUID_WRAP(VUID-StandaloneSpirv-HitAttributeKHR-04703); + case 4704: + return VUID_WRAP(VUID-StandaloneSpirv-CallableDataKHR-04704); + case 4705: + return VUID_WRAP(VUID-StandaloneSpirv-IncomingCallableDataKHR-04705); + case 7119: + return VUID_WRAP(VUID-StandaloneSpirv-ShaderRecordBufferKHR-07119); + case 4708: + return VUID_WRAP(VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708); case 4710: return VUID_WRAP(VUID-StandaloneSpirv-PhysicalStorageBuffer64-04710); case 4711: @@ -1748,6 +2074,66 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-StandaloneSpirv-OpMemoryBarrier-04732); case 4733: return VUID_WRAP(VUID-StandaloneSpirv-OpMemoryBarrier-04733); + case 4734: + return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-04734); + case 4744: + return VUID_WRAP(VUID-StandaloneSpirv-Flat-04744); + case 4777: + return VUID_WRAP(VUID-StandaloneSpirv-OpImage-04777); + case 4780: + return VUID_WRAP(VUID-StandaloneSpirv-Result-04780); + case 4781: + return VUID_WRAP(VUID-StandaloneSpirv-Base-04781); + case 4915: + return VUID_WRAP(VUID-StandaloneSpirv-Location-04915); + case 4916: + return VUID_WRAP(VUID-StandaloneSpirv-Location-04916); + case 4917: + return VUID_WRAP(VUID-StandaloneSpirv-Location-04917); + case 4918: + return VUID_WRAP(VUID-StandaloneSpirv-Location-04918); + case 4919: + return VUID_WRAP(VUID-StandaloneSpirv-Location-04919); + case 6201: + return VUID_WRAP(VUID-StandaloneSpirv-Flat-06201); + case 6202: + return VUID_WRAP(VUID-StandaloneSpirv-Flat-06202); + case 6214: + return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-06214); + case 6491: + return VUID_WRAP(VUID-StandaloneSpirv-DescriptorSet-06491); + case 6671: + return VUID_WRAP(VUID-StandaloneSpirv-OpTypeSampledImage-06671); + case 6672: + return VUID_WRAP(VUID-StandaloneSpirv-Location-06672); + case 6674: + return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-06674); + case 6675: + return VUID_WRAP(VUID-StandaloneSpirv-PushConstant-06675); + case 6676: + return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06676); + case 6677: + return VUID_WRAP(VUID-StandaloneSpirv-UniformConstant-06677); + case 6678: + return VUID_WRAP(VUID-StandaloneSpirv-InputAttachmentIndex-06678); + case 6777: + return VUID_WRAP(VUID-StandaloneSpirv-PerVertexKHR-06777); + case 6778: + return VUID_WRAP(VUID-StandaloneSpirv-Input-06778); + case 6807: + return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06807); + case 6808: + return VUID_WRAP(VUID-StandaloneSpirv-PushConstant-06808); + case 6925: + return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06925); + case 6997: + return VUID_WRAP(VUID-StandaloneSpirv-SubgroupVoteKHR-06997); + case 7102: + return VUID_WRAP(VUID-StandaloneSpirv-MeshEXT-07102); + case 7320: + return VUID_WRAP(VUID-StandaloneSpirv-ExecutionModel-07320); + case 7290: + return VUID_WRAP(VUID-StandaloneSpirv-Input-07290); default: return ""; // unknown id } diff --git a/source/val/validation_state.h b/source/val/validation_state.h index 85111391..1b599ff3 100644 --- a/source/val/validation_state.h +++ b/source/val/validation_state.h @@ -44,19 +44,20 @@ namespace val { /// of the SPIRV spec for additional details of the order. The enumerant values /// are in the same order as the vector returned by GetModuleOrder enum ModuleLayoutSection { - kLayoutCapabilities, /// < Section 2.4 #1 - kLayoutExtensions, /// < Section 2.4 #2 - kLayoutExtInstImport, /// < Section 2.4 #3 - kLayoutMemoryModel, /// < Section 2.4 #4 - kLayoutEntryPoint, /// < Section 2.4 #5 - kLayoutExecutionMode, /// < Section 2.4 #6 - kLayoutDebug1, /// < Section 2.4 #7 > 1 - kLayoutDebug2, /// < Section 2.4 #7 > 2 - kLayoutDebug3, /// < Section 2.4 #7 > 3 - kLayoutAnnotations, /// < Section 2.4 #8 - kLayoutTypes, /// < Section 2.4 #9 - kLayoutFunctionDeclarations, /// < Section 2.4 #10 - kLayoutFunctionDefinitions /// < Section 2.4 #11 + kLayoutCapabilities, /// < Section 2.4 #1 + kLayoutExtensions, /// < Section 2.4 #2 + kLayoutExtInstImport, /// < Section 2.4 #3 + kLayoutMemoryModel, /// < Section 2.4 #4 + kLayoutSamplerImageAddressMode, /// < Section 2.4 #5 + kLayoutEntryPoint, /// < Section 2.4 #6 + kLayoutExecutionMode, /// < Section 2.4 #7 + kLayoutDebug1, /// < Section 2.4 #8 > 1 + kLayoutDebug2, /// < Section 2.4 #8 > 2 + kLayoutDebug3, /// < Section 2.4 #8 > 3 + kLayoutAnnotations, /// < Section 2.4 #9 + kLayoutTypes, /// < Section 2.4 #10 + kLayoutFunctionDeclarations, /// < Section 2.4 #11 + kLayoutFunctionDefinitions /// < Section 2.4 #12 }; /// This class manages the state of the SPIR-V validation as it is being parsed. @@ -67,14 +68,12 @@ class ValidationState_t { bool declare_int16_type = false; // Allow OpTypeInt with 16 bit width? bool declare_float16_type = false; // Allow OpTypeFloat with 16 bit width? bool free_fp_rounding_mode = false; // Allow the FPRoundingMode decoration - // and its vaules to be used without + // and its values to be used without // requiring any capability - // Allow functionalities enabled by VariablePointers capability. + // Allow functionalities enabled by VariablePointers or + // VariablePointersStorageBuffer capability. bool variable_pointers = false; - // Allow functionalities enabled by VariablePointersStorageBuffer - // capability. - bool variable_pointers_storage_buffer = false; // Permit group oerations Reduce, InclusiveScan, ExclusiveScan bool group_ops_reduce_and_scans = false; @@ -90,23 +89,6 @@ class ValidationState_t { // conversion opcodes bool use_int8_type = false; - // Use scalar block layout. See VK_EXT_scalar_block_layout: - // Defines scalar alignment: - // - scalar alignment equals the scalar size in bytes - // - array alignment is same as its element alignment - // - array alignment is max alignment of any of its members - // - vector alignment is same as component alignment - // - matrix alignment is same as component alignment - // For struct in Uniform, StorageBuffer, PushConstant: - // - Offset of a member is multiple of scalar alignment of that member - // - ArrayStride and MatrixStride are multiples of scalar alignment - // Members need not be listed in offset order - bool scalar_block_layout = false; - - // Use scalar block layout (as defined above) for Workgroup block - // variables. See VK_KHR_workgroup_memory_explicit_layout. - bool workgroup_scalar_block_layout = false; - // SPIR-V 1.4 allows us to select between any two composite values // of the same type. bool select_between_composites = false; @@ -121,6 +103,9 @@ class ValidationState_t { // SPIR-V 1.4 allows Function and Private variables to be NonWritable bool nonwritable_var_in_function_or_private = false; + + // Whether LocalSizeId execution mode is allowed by the environment. + bool env_allow_localsizeid = false; }; ValidationState_t(const spv_const_context context, @@ -376,6 +361,20 @@ class ValidationState_t { /// Returns the memory model of this module, or Simple if uninitialized. SpvMemoryModel memory_model() const; + /// Sets the bit width for sampler/image type variables. If not set, they are + /// considered opaque + void set_samplerimage_variable_address_mode(uint32_t bit_width); + + /// Get the addressing mode currently set. If 0, it means addressing mode is + /// invalid Sampler/Image type variables must be considered opaque This mode + /// is only valid after the instruction has been read + uint32_t samplerimage_variable_address_mode() const; + + /// Returns true if the OpSamplerImageAddressingModeNV was found. + bool has_samplerimage_variable_address_mode_specified() const { + return sampler_image_addressing_mode_ != 0; + } + const AssemblyGrammar& grammar() const { return grammar_; } /// Inserts the instruction into the list of ordered instructions in the file. @@ -391,17 +390,14 @@ class ValidationState_t { /// Registers the decoration for the given void RegisterDecorationForId(uint32_t id, const Decoration& dec) { auto& dec_list = id_decorations_[id]; - auto lb = std::find(dec_list.begin(), dec_list.end(), dec); - if (lb == dec_list.end()) { - dec_list.push_back(dec); - } + dec_list.insert(dec); } /// Registers the list of decorations for the given template void RegisterDecorationsForId(uint32_t id, InputIt begin, InputIt end) { - std::vector& cur_decs = id_decorations_[id]; - cur_decs.insert(cur_decs.end(), begin, end); + std::set& cur_decs = id_decorations_[id]; + cur_decs.insert(begin, end); } /// Registers the list of decorations for the given member of the given @@ -410,21 +406,44 @@ class ValidationState_t { void RegisterDecorationsForStructMember(uint32_t struct_id, uint32_t member_index, InputIt begin, InputIt end) { - RegisterDecorationsForId(struct_id, begin, end); - for (auto& decoration : id_decorations_[struct_id]) { - decoration.set_struct_member_index(member_index); + std::set& cur_decs = id_decorations_[struct_id]; + for (InputIt iter = begin; iter != end; ++iter) { + Decoration dec = *iter; + dec.set_struct_member_index(member_index); + cur_decs.insert(dec); } } /// Returns all the decorations for the given . If no decorations exist - /// for the , it registers an empty vector for it in the map and - /// returns the empty vector. - std::vector& id_decorations(uint32_t id) { + /// for the , it registers an empty set for it in the map and + /// returns the empty set. + std::set& id_decorations(uint32_t id) { return id_decorations_[id]; } + /// Returns the range of decorations for the given field of the given . + struct FieldDecorationsIter { + std::set::const_iterator begin; + std::set::const_iterator end; + }; + FieldDecorationsIter id_member_decorations(uint32_t id, + uint32_t member_index) { + const auto& decorations = id_decorations_[id]; + + // The decorations are sorted by member_index, so this look up will give the + // exact range of decorations for this member index. + Decoration min_decoration((SpvDecoration)0, {}, member_index); + Decoration max_decoration(SpvDecorationMax, {}, member_index); + + FieldDecorationsIter result; + result.begin = decorations.lower_bound(min_decoration); + result.end = decorations.upper_bound(max_decoration); + + return result; + } + // Returns const pointer to the internal decoration container. - const std::map>& id_decorations() const { + const std::map>& id_decorations() const { return id_decorations_; } @@ -465,6 +484,10 @@ class ValidationState_t { void RegisterSampledImageConsumer(uint32_t sampled_image_id, Instruction* consumer); + // Record a function's storage class consumer instruction + void RegisterStorageClassConsumer(SpvStorageClass storage_class, + Instruction* consumer); + /// Returns the set of Global Variables. std::unordered_set& global_vars() { return global_vars_; } @@ -489,6 +512,12 @@ class ValidationState_t { return features_.env_relaxed_block_layout || options()->relax_block_layout; } + // Returns true if allowing localsizeid, either because the environment always + // allows it, or because it is enabled from the command-line. + bool IsLocalSizeIdAllowed() const { + return features_.env_allow_localsizeid || options()->allow_localsizeid; + } + /// Sets the struct nesting depth for a given struct ID void set_struct_nesting_depth(uint32_t id, uint32_t depth) { struct_nesting_depth_[id] = depth; @@ -578,10 +607,12 @@ class ValidationState_t { bool IsBoolVectorType(uint32_t id) const; bool IsBoolScalarOrVectorType(uint32_t id) const; bool IsPointerType(uint32_t id) const; + bool IsAccelerationStructureType(uint32_t id) const; bool IsCooperativeMatrixType(uint32_t id) const; bool IsFloatCooperativeMatrixType(uint32_t id) const; bool IsIntCooperativeMatrixType(uint32_t id) const; bool IsUnsignedIntCooperativeMatrixType(uint32_t id) const; + bool IsUnsigned64BitHandle(uint32_t id) const; // Returns true if |id| is a type id that contains |type| (or integer or // floating point type) of |width| bits. @@ -591,6 +622,17 @@ class ValidationState_t { // 16-bit float that is not generally enabled for use. bool ContainsLimitedUseIntOrFloatType(uint32_t id) const; + // Returns true if |id| is a type that contains a runtime-sized array. + // Does not consider a pointers as contains the array. + bool ContainsRuntimeArray(uint32_t id) const; + + // Generic type traversal. + // Only traverse pointers and functions if |traverse_all_types| is true. + // Recursively tests |f| against the type hierarchy headed by |id|. + bool ContainsType(uint32_t id, + const std::function& f, + bool traverse_all_types = true) const; + // Gets value from OpConstant and OpSpecConstant as uint64. // Returns false on failure (no instruction, wrong instruction, not int). bool GetConstantValUint64(uint32_t id, uint64_t* val) const; @@ -681,6 +723,16 @@ class ValidationState_t { // Returns the disassembly string for the given instruction. std::string Disassemble(const uint32_t* words, uint16_t num_words) const; + // Returns the string name for |decoration|. + std::string SpvDecorationString(uint32_t decoration) { + spv_operand_desc desc = nullptr; + if (grammar_.lookupOperand(SPV_OPERAND_TYPE_DECORATION, decoration, + &desc) != SPV_SUCCESS) { + return std::string("Unknown"); + } + return std::string(desc->name); + } + // Returns whether type m1 and type m2 are cooperative matrices with // the same "shape" (matching scope, rows, cols). If any are specialization // constants, we assume they can match because we can't prove they don't. @@ -790,7 +842,7 @@ class ValidationState_t { /// IDs that are entry points, ie, arguments to OpEntryPoint. std::vector entry_points_; - /// Maps an entry point id to its desciptions. + /// Maps an entry point id to its descriptions. std::unordered_map> entry_point_descriptions_; @@ -821,7 +873,7 @@ class ValidationState_t { struct_has_nested_blockorbufferblock_struct_; /// Stores the list of decorations for a given - std::map> id_decorations_; + std::map> id_decorations_; /// Stores type declarations which need to be unique (i.e. non-aggregates), /// in the form [opcode, operand words], result_id is not stored. @@ -837,6 +889,9 @@ class ValidationState_t { // have the same pointer size (for physical pointer types). uint32_t pointer_size_and_alignment_; + /// bit width of sampler/image type variables. Valid values are 32 and 64 + uint32_t sampler_image_addressing_mode_; + /// NOTE: See correspoding getter functions bool in_function_; diff --git a/source/wasm/README.md b/source/wasm/README.md new file mode 100644 index 00000000..aca0f70c --- /dev/null +++ b/source/wasm/README.md @@ -0,0 +1,43 @@ +# SPIRV-Tools + +Wasm (WebAssembly) build of https://github.com/KhronosGroup/SPIRV-Tools + +## Usage + +```js +const spirvTools = require("spirv-tools"); + +const test = async () => { + // Load the library + const spv = await spirvTools(); + + // assemble + const source = ` + OpCapability Linkage + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpSource GLSL 450 + OpDecorate %spec SpecId 1 + %int = OpTypeInt 32 1 + %spec = OpSpecConstant %int 0 + %const = OpConstant %int 42`; + const asResult = spv.as( + source, + spv.SPV_ENV_UNIVERSAL_1_3, + spv.SPV_TEXT_TO_BINARY_OPTION_NONE + ); + console.log(`as returned ${asResult.byteLength} bytes`); + + // re-disassemble + const disResult = spv.dis( + asResult, + spv.SPV_ENV_UNIVERSAL_1_3, + spv.SPV_BINARY_TO_TEXT_OPTION_INDENT | + spv.SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | + spv.SPV_BINARY_TO_TEXT_OPTION_COLOR + ); + console.log("dis:\n", disResult); +}; + +test(); +``` diff --git a/source/wasm/build.sh b/source/wasm/build.sh new file mode 100755 index 00000000..f02ae525 --- /dev/null +++ b/source/wasm/build.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +# Copyright (c) 2020 The Khronos Group Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +NUM_CORES=$(nproc) +echo "Detected $NUM_CORES cores for building" + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +VERSION=$(sed -n '0,/^v20/ s/^v\(20[0-9.]*\).*/\1/p' $DIR/../../CHANGES).${GITHUB_RUN_NUMBER:-0} +echo "Version: $VERSION" + +build() { + type=$1 + shift + args=$@ + mkdir -p build/$type + pushd build/$type + echo $args + emcmake cmake \ + -DCMAKE_BUILD_TYPE=Release \ + $args \ + ../.. + emmake make -j $(( $NUM_CORES )) SPIRV-Tools-static + + echo Building js interface + emcc \ + --bind \ + -I../../include \ + -std=c++11 \ + ../../source/wasm/spirv-tools.cpp \ + source/libSPIRV-Tools.a \ + -o spirv-tools.js \ + -s MODULARIZE \ + -Oz + + popd + mkdir -p out/$type + + # copy other js files + cp source/wasm/spirv-tools.d.ts out/$type/ + sed -e 's/\("version"\s*:\s*\).*/\1"'$VERSION'",/' source/wasm/package.json > out/$type/package.json + cp source/wasm/README.md out/$type/ + cp LICENSE out/$type/ + + cp build/$type/spirv-tools.js out/$type/ + gzip -9 -k -f out/$type/spirv-tools.js + if [ -e build/$type/spirv-tools.wasm ] ; then + cp build/$type/spirv-tools.wasm out/$type/ + gzip -9 -k -f out/$type/spirv-tools.wasm + fi +} + +if [ ! -d external/spirv-headers ] ; then + echo "Fetching SPIRV-headers" + git clone https://github.com/KhronosGroup/SPIRV-Headers.git external/spirv-headers +fi + +echo Building ${BASH_REMATCH[1]} +build web\ + -DSPIRV_COLOR_TERMINAL=OFF\ + -DSPIRV_SKIP_TESTS=ON\ + -DSPIRV_SKIP_EXECUTABLES=ON + +wc -c out/*/* diff --git a/source/wasm/package.json b/source/wasm/package.json new file mode 100644 index 00000000..78273538 --- /dev/null +++ b/source/wasm/package.json @@ -0,0 +1,17 @@ +{ + "name": "spirv-tools", + "version": "VERSION", + "license": "Apache-2.0", + "main": "spirv-tools", + "types": "spirv-tools.d.ts", + "files": [ + "*.wasm", + "*.js", + "*.d.ts" + ], + "repository": { + "type": "git", + "url": "https://github.com/KhronosGroup/SPIRV-Tools" + }, + "homepage": "https://github.com/KhronosGroup/SPIRV-Tools" +} diff --git a/source/wasm/spirv-tools.cpp b/source/wasm/spirv-tools.cpp new file mode 100644 index 00000000..33f2f05f --- /dev/null +++ b/source/wasm/spirv-tools.cpp @@ -0,0 +1,94 @@ +// Copyright (c) 2020 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "spirv-tools/libspirv.hpp" + +#include +#include +#include + +#include +#include +using namespace emscripten; + +void print_msg_to_stderr (spv_message_level_t, const char*, + const spv_position_t&, const char* m) { + std::cerr << "error: " << m << std::endl; +}; + +std::string dis(std::string const& buffer, uint32_t env, uint32_t options) { + spvtools::SpirvTools core(static_cast(env)); + core.SetMessageConsumer(print_msg_to_stderr); + + std::vector spirv; + const uint32_t* ptr = reinterpret_cast(buffer.data()); + spirv.assign(ptr, ptr + buffer.size() / 4); + std::string disassembly; + if (!core.Disassemble(spirv, &disassembly, options)) return "Error"; + return disassembly; +} + +emscripten::val as(std::string const& source, uint32_t env, uint32_t options) { + spvtools::SpirvTools core(static_cast(env)); + core.SetMessageConsumer(print_msg_to_stderr); + + std::vector spirv; + if (!core.Assemble(source, &spirv, options)) spirv.clear(); + const uint8_t* ptr = reinterpret_cast(spirv.data()); + return emscripten::val(emscripten::typed_memory_view(spirv.size() * 4, + ptr)); +} + +EMSCRIPTEN_BINDINGS(my_module) { + function("dis", &dis); + function("as", &as); + + constant("SPV_ENV_UNIVERSAL_1_0", static_cast(SPV_ENV_UNIVERSAL_1_0)); + constant("SPV_ENV_VULKAN_1_0", static_cast(SPV_ENV_VULKAN_1_0)); + constant("SPV_ENV_UNIVERSAL_1_1", static_cast(SPV_ENV_UNIVERSAL_1_1)); + constant("SPV_ENV_OPENCL_2_1", static_cast(SPV_ENV_OPENCL_2_1)); + constant("SPV_ENV_OPENCL_2_2", static_cast(SPV_ENV_OPENCL_2_2)); + constant("SPV_ENV_OPENGL_4_0", static_cast(SPV_ENV_OPENGL_4_0)); + constant("SPV_ENV_OPENGL_4_1", static_cast(SPV_ENV_OPENGL_4_1)); + constant("SPV_ENV_OPENGL_4_2", static_cast(SPV_ENV_OPENGL_4_2)); + constant("SPV_ENV_OPENGL_4_3", static_cast(SPV_ENV_OPENGL_4_3)); + constant("SPV_ENV_OPENGL_4_5", static_cast(SPV_ENV_OPENGL_4_5)); + constant("SPV_ENV_UNIVERSAL_1_2", static_cast(SPV_ENV_UNIVERSAL_1_2)); + constant("SPV_ENV_OPENCL_1_2", static_cast(SPV_ENV_OPENCL_1_2)); + constant("SPV_ENV_OPENCL_EMBEDDED_1_2", static_cast(SPV_ENV_OPENCL_EMBEDDED_1_2)); + constant("SPV_ENV_OPENCL_2_0", static_cast(SPV_ENV_OPENCL_2_0)); + constant("SPV_ENV_OPENCL_EMBEDDED_2_0", static_cast(SPV_ENV_OPENCL_EMBEDDED_2_0)); + constant("SPV_ENV_OPENCL_EMBEDDED_2_1", static_cast(SPV_ENV_OPENCL_EMBEDDED_2_1)); + constant("SPV_ENV_OPENCL_EMBEDDED_2_2", static_cast(SPV_ENV_OPENCL_EMBEDDED_2_2)); + constant("SPV_ENV_UNIVERSAL_1_3", static_cast(SPV_ENV_UNIVERSAL_1_3)); + constant("SPV_ENV_VULKAN_1_1", static_cast(SPV_ENV_VULKAN_1_1)); + constant("SPV_ENV_WEBGPU_0", static_cast(SPV_ENV_WEBGPU_0)); + constant("SPV_ENV_UNIVERSAL_1_4", static_cast(SPV_ENV_UNIVERSAL_1_4)); + constant("SPV_ENV_VULKAN_1_1_SPIRV_1_4", static_cast(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + constant("SPV_ENV_UNIVERSAL_1_5", static_cast(SPV_ENV_UNIVERSAL_1_5)); + constant("SPV_ENV_VULKAN_1_2", static_cast(SPV_ENV_VULKAN_1_2)); + constant("SPV_ENV_UNIVERSAL_1_6", + static_cast(SPV_ENV_UNIVERSAL_1_6)); + + constant("SPV_BINARY_TO_TEXT_OPTION_NONE", static_cast(SPV_BINARY_TO_TEXT_OPTION_NONE)); + constant("SPV_BINARY_TO_TEXT_OPTION_PRINT", static_cast(SPV_BINARY_TO_TEXT_OPTION_PRINT)); + constant("SPV_BINARY_TO_TEXT_OPTION_COLOR", static_cast(SPV_BINARY_TO_TEXT_OPTION_COLOR)); + constant("SPV_BINARY_TO_TEXT_OPTION_INDENT", static_cast(SPV_BINARY_TO_TEXT_OPTION_INDENT)); + constant("SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET", static_cast(SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET)); + constant("SPV_BINARY_TO_TEXT_OPTION_NO_HEADER", static_cast(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER)); + constant("SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES", static_cast(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES)); + + constant("SPV_TEXT_TO_BINARY_OPTION_NONE", static_cast(SPV_TEXT_TO_BINARY_OPTION_NONE)); + constant("SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS", static_cast(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS)); +} diff --git a/source/wasm/spirv-tools.d.ts b/source/wasm/spirv-tools.d.ts new file mode 100644 index 00000000..c06bdf18 --- /dev/null +++ b/source/wasm/spirv-tools.d.ts @@ -0,0 +1,57 @@ +// Copyright (c) 2020 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +declare interface SpirvTools { + as(input: string, env: number, options: number): Uint8Array; + dis(input: Uint8Array, env: number, options: number): string; + + SPV_ENV_UNIVERSAL_1_0: number; + SPV_ENV_VULKAN_1_0: number; + SPV_ENV_UNIVERSAL_1_1: number; + SPV_ENV_OPENCL_2_1: number; + SPV_ENV_OPENCL_2_2: number; + SPV_ENV_OPENGL_4_0: number; + SPV_ENV_OPENGL_4_1: number; + SPV_ENV_OPENGL_4_2: number; + SPV_ENV_OPENGL_4_3: number; + SPV_ENV_OPENGL_4_5: number; + SPV_ENV_UNIVERSAL_1_2: number; + SPV_ENV_OPENCL_1_2: number; + SPV_ENV_OPENCL_EMBEDDED_1_2: number; + SPV_ENV_OPENCL_2_0: number; + SPV_ENV_OPENCL_EMBEDDED_2_0: number; + SPV_ENV_OPENCL_EMBEDDED_2_1: number; + SPV_ENV_OPENCL_EMBEDDED_2_2: number; + SPV_ENV_UNIVERSAL_1_3: number; + SPV_ENV_VULKAN_1_1: number; + SPV_ENV_WEBGPU_0: number; + SPV_ENV_UNIVERSAL_1_4: number; + SPV_ENV_VULKAN_1_1_SPIRV_1_4: number; + SPV_ENV_UNIVERSAL_1_5: number; + SPV_ENV_VULKAN_1_2: number; + SPV_ENV_UNIVERSAL_1_6: number; + + SPV_TEXT_TO_BINARY_OPTION_NONE: number; + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS: number; + + SPV_BINARY_TO_TEXT_OPTION_NONE: number; + SPV_BINARY_TO_TEXT_OPTION_PRINT: number; + SPV_BINARY_TO_TEXT_OPTION_COLOR: number; + SPV_BINARY_TO_TEXT_OPTION_INDENT: number; + SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET: number; + SPV_BINARY_TO_TEXT_OPTION_NO_HEADER: number; + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES: number; +} + +export default function (): Promise; diff --git a/test.txt b/test.txt new file mode 100644 index 00000000..e69de29b diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8ede58bf..4ca8ef8f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -185,10 +185,13 @@ add_spvtools_unittest( endif() +add_subdirectory(diff) add_subdirectory(link) +add_subdirectory(lint) add_subdirectory(opt) add_subdirectory(reduce) add_subdirectory(fuzz) add_subdirectory(tools) add_subdirectory(util) add_subdirectory(val) +add_subdirectory(fuzzers) diff --git a/test/binary_header_get_test.cpp b/test/binary_header_get_test.cpp index 3ce0b63a..f94f0c1a 100644 --- a/test/binary_header_get_test.cpp +++ b/test/binary_header_get_test.cpp @@ -51,8 +51,8 @@ TEST_F(BinaryHeaderGet, Default) { ASSERT_EQ(SPV_SUCCESS, spvBinaryHeaderGet(&const_bin, endian, &header)); ASSERT_EQ(static_cast(SpvMagicNumber), header.magic); - // Expect SPIRV-Headers updated to SPIR-V 1.5. - ASSERT_EQ(0x00010500u, header.version); + // Expect SPIRV-Headers updated to SPIR-V 1.6. + ASSERT_EQ(0x00010600u, header.version); ASSERT_EQ(static_cast(SPV_GENERATOR_CODEPLAY), header.generator); ASSERT_EQ(1u, header.bound); ASSERT_EQ(0u, header.schema); diff --git a/test/binary_parse_test.cpp b/test/binary_parse_test.cpp index 9a13f22c..f0810a35 100644 --- a/test/binary_parse_test.cpp +++ b/test/binary_parse_test.cpp @@ -203,16 +203,7 @@ class BinaryParseTest : public spvtest::TextToBinaryTestBase<::testing::Test> { void Parse(const SpirvVector& words, spv_result_t expected_result, bool flip_words = false) { SpirvVector flipped_words(words); - SCOPED_TRACE(flip_words ? "Flipped Endianness" : "Normal Endianness"); - if (flip_words) { - std::transform(flipped_words.begin(), flipped_words.end(), - flipped_words.begin(), [](const uint32_t raw_word) { - return spvFixWord(raw_word, - I32_ENDIAN_HOST == I32_ENDIAN_BIG - ? SPV_ENDIANNESS_LITTLE - : SPV_ENDIANNESS_BIG); - }); - } + MaybeFlipWords(flip_words, flipped_words.begin(), flipped_words.end()); EXPECT_EQ(expected_result, spvBinaryParse(ScopedContext().context, &client_, flipped_words.data(), flipped_words.size(), @@ -486,27 +477,27 @@ TEST_F(BinaryParseTest, EarlyReturnWithTwoPassingCallbacks) { } TEST_F(BinaryParseTest, InstructionWithStringOperand) { - const std::string str = - "the future is already here, it's just not evenly distributed"; - const auto str_words = MakeVector(str); - const auto instruction = MakeInstruction(SpvOpName, {99}, str_words); - const auto words = Concatenate({ExpectedHeaderForBound(100), instruction}); - InSequence calls_expected_in_specific_order; - EXPECT_HEADER(100).WillOnce(Return(SPV_SUCCESS)); - const auto operands = std::vector{ - MakeSimpleOperand(1, SPV_OPERAND_TYPE_ID), - MakeLiteralStringOperand(2, static_cast(str_words.size()))}; - EXPECT_CALL(client_, - Instruction(ParsedInstruction(spv_parsed_instruction_t{ - instruction.data(), static_cast(instruction.size()), - SpvOpName, SPV_EXT_INST_TYPE_NONE, 0 /*type id*/, - 0 /* No result id for OpName*/, operands.data(), - static_cast(operands.size())}))) - .WillOnce(Return(SPV_SUCCESS)); - // Since we are actually checking the output, don't test the - // endian-swapped version. - Parse(words, SPV_SUCCESS, false); - EXPECT_EQ(nullptr, diagnostic_); + for (bool endian_swap : kSwapEndians) { + const std::string str = + "the future is already here, it's just not evenly distributed"; + const auto str_words = MakeVector(str); + const auto instruction = MakeInstruction(SpvOpName, {99}, str_words); + const auto words = Concatenate({ExpectedHeaderForBound(100), instruction}); + InSequence calls_expected_in_specific_order; + EXPECT_HEADER(100).WillOnce(Return(SPV_SUCCESS)); + const auto operands = std::vector{ + MakeSimpleOperand(1, SPV_OPERAND_TYPE_ID), + MakeLiteralStringOperand(2, static_cast(str_words.size()))}; + EXPECT_CALL(client_, Instruction(ParsedInstruction(spv_parsed_instruction_t{ + instruction.data(), + static_cast(instruction.size()), + SpvOpName, SPV_EXT_INST_TYPE_NONE, 0 /*type id*/, + 0 /* No result id for OpName*/, operands.data(), + static_cast(operands.size())}))) + .WillOnce(Return(SPV_SUCCESS)); + Parse(words, SPV_SUCCESS, endian_swap); + EXPECT_EQ(nullptr, diagnostic_); + } } // Checks for non-zero values for the result_id and ext_inst_type members @@ -613,7 +604,7 @@ INSTANTIATE_TEST_SUITE_P( MakeInstruction(SpvOpNop, {42})}), "Invalid instruction OpNop starting at word 5: expected " "no more operands after 1 words, but stated word count is 2."}, - // Supply several more unexpectd words. + // Supply several more unexpected words. {Concatenate({ExpectedHeaderForBound(1), MakeInstruction(SpvOpNop, {42, 43, 44, 45, 46, 47})}), "Invalid instruction OpNop starting at word 5: expected " diff --git a/test/binary_to_text.literal_test.cpp b/test/binary_to_text.literal_test.cpp index 02daac76..5956984b 100644 --- a/test/binary_to_text.literal_test.cpp +++ b/test/binary_to_text.literal_test.cpp @@ -27,8 +27,15 @@ using ::testing::Eq; using RoundTripLiteralsTest = spvtest::TextToBinaryTestBase<::testing::TestWithParam>; +static const bool kSwapEndians[] = {false, true}; + TEST_P(RoundTripLiteralsTest, Sample) { - EXPECT_THAT(EncodeAndDecodeSuccessfully(GetParam()), Eq(GetParam())); + for (bool endian_swap : kSwapEndians) { + EXPECT_THAT( + EncodeAndDecodeSuccessfully(GetParam(), SPV_BINARY_TO_TEXT_OPTION_NONE, + SPV_ENV_UNIVERSAL_1_0, endian_swap), + Eq(GetParam())); + } } // clang-format off @@ -58,8 +65,12 @@ using RoundTripSpecialCaseLiteralsTest = spvtest::TextToBinaryTestBase< // Test case where the generated disassembly is not the same as the // assembly passed in. TEST_P(RoundTripSpecialCaseLiteralsTest, Sample) { - EXPECT_THAT(EncodeAndDecodeSuccessfully(std::get<0>(GetParam())), - Eq(std::get<1>(GetParam()))); + for (bool endian_swap : kSwapEndians) { + EXPECT_THAT(EncodeAndDecodeSuccessfully(std::get<0>(GetParam()), + SPV_BINARY_TO_TEXT_OPTION_NONE, + SPV_ENV_UNIVERSAL_1_0, endian_swap), + Eq(std::get<1>(GetParam()))); + } } // clang-format off diff --git a/test/binary_to_text_test.cpp b/test/binary_to_text_test.cpp index 9cad9661..44705f2a 100644 --- a/test/binary_to_text_test.cpp +++ b/test/binary_to_text_test.cpp @@ -101,6 +101,17 @@ TEST_F(BinaryToText, Default) { spvTextDestroy(text); } +TEST_F(BinaryToText, Print) { + spv_text text = nullptr; + spv_diagnostic diagnostic = nullptr; + ASSERT_EQ( + SPV_SUCCESS, + spvBinaryToText(context, binary->code, binary->wordCount, + SPV_BINARY_TO_TEXT_OPTION_PRINT, &text, &diagnostic)); + ASSERT_EQ(text, nullptr); + spvTextDestroy(text); +} + TEST_F(BinaryToText, MissingModule) { spv_text text; spv_diagnostic diagnostic = nullptr; @@ -386,7 +397,7 @@ INSTANTIATE_TEST_SUITE_P( ::testing::ValuesIn(std::vector{ "OpExecutionModeId %1 SubgroupsPerWorkgroupId %2\n", "OpExecutionModeId %1 LocalSizeId %2 %3 %4\n", - "OpExecutionModeId %1 LocalSizeHintId %2\n", + "OpExecutionModeId %1 LocalSizeHintId %2 %3 %4\n", "OpDecorateId %1 AlignmentId %2\n", "OpDecorateId %1 MaxByteOffsetId %2\n", }))); diff --git a/test/c_interface_test.cpp b/test/c_interface_test.cpp index 841bb2c6..4424d7f5 100644 --- a/test/c_interface_test.cpp +++ b/test/c_interface_test.cpp @@ -117,12 +117,15 @@ TEST(CInterface, SpecifyConsumerNullDiagnosticForAssembling) { const spv_position_t& position, const char* message) { ++invocation; EXPECT_EQ(SPV_MSG_ERROR, level); - // The error happens at scanning the begining of second line. + // The error happens at scanning the beginning of second line. EXPECT_STREQ("input", source); EXPECT_EQ(1u, position.line); EXPECT_EQ(0u, position.column); EXPECT_EQ(12u, position.index); - EXPECT_STREQ("Expected operand, found end of stream.", message); + EXPECT_STREQ( + "Expected operand for OpName instruction, but found the end of the " + "stream.", + message); }); spv_binary binary = nullptr; @@ -228,7 +231,10 @@ TEST(CInterface, SpecifyConsumerSpecifyDiagnosticForAssembling) { spvTextToBinary(context, input_text, sizeof(input_text), &binary, &diagnostic)); EXPECT_EQ(0, invocation); // Consumer should not be invoked at all. - EXPECT_STREQ("Expected operand, found end of stream.", diagnostic->error); + EXPECT_STREQ( + "Expected operand for OpName instruction, but found the end of the " + "stream.", + diagnostic->error); spvDiagnosticDestroy(diagnostic); spvBinaryDestroy(binary); diff --git a/test/cpp_interface_test.cpp b/test/cpp_interface_test.cpp index 538d40fd..4cab4dfd 100644 --- a/test/cpp_interface_test.cpp +++ b/test/cpp_interface_test.cpp @@ -58,7 +58,7 @@ TEST(CppInterface, SuccessfulRoundTrip) { EXPECT_EQ(0u, position.line); EXPECT_EQ(0u, position.column); EXPECT_EQ(1u, position.index); - EXPECT_STREQ("ID 1[%1] has not been defined\n %2 = OpSizeOf %1 %3\n", + EXPECT_STREQ("ID '1[%1]' has not been defined\n %2 = OpSizeOf %1 %3\n", message); }); EXPECT_FALSE(t.Validate(binary)); diff --git a/test/diff/CMakeLists.txt b/test/diff/CMakeLists.txt new file mode 100644 index 00000000..811805bd --- /dev/null +++ b/test/diff/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright (c) 2022 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(diff_files/diff_test_files_autogen.cmake) + +add_spvtools_unittest(TARGET lcs + SRCS lcs_test.cpp + LIBS SPIRV-Tools-diff +) + +add_spvtools_unittest(TARGET diff + SRCS diff_test.cpp diff_test_utils.h diff_test_utils.cpp + ${DIFF_TEST_FILES} ${spirv-tools_SOURCE_DIR}/tools/util/cli_consumer.cpp + LIBS SPIRV-Tools-diff +) diff --git a/test/diff/diff_files/.gitignore b/test/diff/diff_files/.gitignore new file mode 100644 index 00000000..727526ef --- /dev/null +++ b/test/diff/diff_files/.gitignore @@ -0,0 +1,3 @@ +# To aid debugging no-dbg variants, the temporary files used to strip debug information are placed +# in a hidden directory and aren't removed after generation. +.no_dbg/ diff --git a/test/diff/diff_files/OpExtInst_in_dst_only_autogen.cpp b/test/diff/diff_files/OpExtInst_in_dst_only_autogen.cpp new file mode 100644 index 00000000..ce899edd --- /dev/null +++ b/test/diff/diff_files/OpExtInst_in_dst_only_autogen.cpp @@ -0,0 +1,242 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests a diff where the src shader doesn't have OpExtImport while the +// dst shader does (and uses OpExtInst). This test ensures that when matching, +// the OpExtImport instruction from the correct module is referenced. +constexpr char kSrc[] = R"( OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpCompositeConstruct %7 %12 %12 %12 %12 + OpStore %9 %13 + OpReturn + OpFunctionEnd +)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpExtInst %6 %1 Log2 %12 + %14 = OpCompositeConstruct %7 %13 %13 %13 %13 + OpStore %9 %14 + OpReturn + OpFunctionEnd + +)"; + +TEST(DiffTest, OpextinstInDstOnly) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 14 ++; Bound: 16 + ; Schema: 0 + OpCapability Shader ++%14 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision ++OpDecorate %15 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 ++%15 = OpExtInst %6 %14 Log2 %12 +-%13 = OpCompositeConstruct %7 %12 %12 %12 %12 ++%13 = OpCompositeConstruct %7 %15 %15 %15 %15 + OpStore %9 %13 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, OpextinstInDstOnlyNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpCompositeConstruct %7 %12 %12 %12 %12 + OpStore %9 %13 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpExtInst %6 %1 Log2 %12 + %14 = OpCompositeConstruct %7 %13 %13 %13 %13 + OpStore %9 %14 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 14 ++; Bound: 16 + ; Schema: 0 + OpCapability Shader ++%14 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision ++OpDecorate %15 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 ++%15 = OpExtInst %6 %14 Log2 %12 +-%13 = OpCompositeConstruct %7 %12 %12 %12 %12 ++%13 = OpCompositeConstruct %7 %15 %15 %15 %15 + OpStore %9 %13 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/OpExtInst_in_dst_only_dst.spvasm b/test/diff/diff_files/OpExtInst_in_dst_only_dst.spvasm new file mode 100644 index 00000000..e599d1e9 --- /dev/null +++ b/test/diff/diff_files/OpExtInst_in_dst_only_dst.spvasm @@ -0,0 +1,33 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpExtInst %6 %1 Log2 %12 + %14 = OpCompositeConstruct %7 %13 %13 %13 %13 + OpStore %9 %14 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/OpExtInst_in_dst_only_src.spvasm b/test/diff/diff_files/OpExtInst_in_dst_only_src.spvasm new file mode 100644 index 00000000..9f6fc18c --- /dev/null +++ b/test/diff/diff_files/OpExtInst_in_dst_only_src.spvasm @@ -0,0 +1,33 @@ +;; Tests a diff where the src shader doesn't have OpExtImport while the +;; dst shader does (and uses OpExtInst). This test ensures that when matching, +;; the OpExtImport instruction from the correct module is referenced. + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpCompositeConstruct %7 %12 %12 %12 %12 + OpStore %9 %13 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/OpExtInst_in_src_only_autogen.cpp b/test/diff/diff_files/OpExtInst_in_src_only_autogen.cpp new file mode 100644 index 00000000..9944c2cf --- /dev/null +++ b/test/diff/diff_files/OpExtInst_in_src_only_autogen.cpp @@ -0,0 +1,242 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests a diff where the dst shader doesn't have OpExtImport while the +// src shader does (and uses OpExtInst). This test ensures that when matching, +// the OpExtImport instruction from the correct module is referenced. +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpExtInst %6 %1 Log2 %12 + %14 = OpCompositeConstruct %7 %13 %13 %13 %13 + OpStore %9 %14 + OpReturn + OpFunctionEnd +)"; +constexpr char kDst[] = R"( OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpCompositeConstruct %7 %12 %12 %12 %12 + OpStore %9 %13 + OpReturn + OpFunctionEnd + +)"; + +TEST(DiffTest, OpextinstInSrcOnly) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 15 ++; Bound: 16 + ; Schema: 0 + OpCapability Shader +-%1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision +-OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 +-%13 = OpExtInst %6 %1 Log2 %12 +-%14 = OpCompositeConstruct %7 %13 %13 %13 %13 ++%14 = OpCompositeConstruct %7 %12 %12 %12 %12 + OpStore %9 %14 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, OpextinstInSrcOnlyNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpExtInst %6 %1 Log2 %12 + %14 = OpCompositeConstruct %7 %13 %13 %13 %13 + OpStore %9 %14 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpCompositeConstruct %7 %12 %12 %12 %12 + OpStore %9 %13 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 15 ++; Bound: 16 + ; Schema: 0 + OpCapability Shader +-%1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision +-OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 +-%13 = OpExtInst %6 %1 Log2 %12 +-%14 = OpCompositeConstruct %7 %13 %13 %13 %13 ++%14 = OpCompositeConstruct %7 %12 %12 %12 %12 + OpStore %9 %14 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/OpExtInst_in_src_only_dst.spvasm b/test/diff/diff_files/OpExtInst_in_src_only_dst.spvasm new file mode 100644 index 00000000..47233051 --- /dev/null +++ b/test/diff/diff_files/OpExtInst_in_src_only_dst.spvasm @@ -0,0 +1,30 @@ + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpCompositeConstruct %7 %12 %12 %12 %12 + OpStore %9 %13 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/OpExtInst_in_src_only_src.spvasm b/test/diff/diff_files/OpExtInst_in_src_only_src.spvasm new file mode 100644 index 00000000..efb663a9 --- /dev/null +++ b/test/diff/diff_files/OpExtInst_in_src_only_src.spvasm @@ -0,0 +1,36 @@ +;; Tests a diff where the dst shader doesn't have OpExtImport while the +;; src shader does (and uses OpExtInst). This test ensures that when matching, +;; the OpExtImport instruction from the correct module is referenced. + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpExtInst %6 %1 Log2 %12 + %14 = OpCompositeConstruct %7 %13 %13 %13 %13 + OpStore %9 %14 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/OpTypeForwardPointer_basic_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_basic_autogen.cpp new file mode 100644 index 00000000..af252b11 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_basic_autogen.cpp @@ -0,0 +1,136 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Basic test that OpTypeForwardPointer is matched +constexpr char kSrc[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %structptr "structptr" + OpTypeForwardPointer %structptr UniformConstant + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1)"; +constexpr char kDst[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %structptr "structptr" + OpName %structptr2 "structptr2" + OpTypeForwardPointer %structptr UniformConstant + OpTypeForwardPointer %structptr2 Function + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1 + %structptr2 = OpTypePointer Function %structt1 +)"; + +TEST(DiffTest, OptypeforwardpointerBasic) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 7 ++; Bound: 8 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %1 "structptr" ++OpName %7 "structptr2" + OpTypeForwardPointer %1 UniformConstant ++OpTypeForwardPointer %7 Function + %2 = OpTypeInt 32 0 + %3 = OpTypeStruct %1 %2 + %4 = OpTypeStruct %2 %1 + %5 = OpTypeStruct %2 %2 %1 + %6 = OpTypeStruct %2 %2 %2 %1 + %1 = OpTypePointer UniformConstant %3 ++%7 = OpTypePointer Function %3 +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, OptypeforwardpointerBasicNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %structptr UniformConstant + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1 +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %structptr UniformConstant + OpTypeForwardPointer %structptr2 Function + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1 + %structptr2 = OpTypePointer Function %structt1 +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 7 ++; Bound: 8 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %1 UniformConstant ++OpTypeForwardPointer %7 Function + %2 = OpTypeInt 32 0 + %3 = OpTypeStruct %1 %2 + %4 = OpTypeStruct %2 %1 + %5 = OpTypeStruct %2 %2 %1 + %6 = OpTypeStruct %2 %2 %2 %1 + %1 = OpTypePointer UniformConstant %3 ++%7 = OpTypePointer Function %3 +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/OpTypeForwardPointer_basic_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_basic_dst.spvasm new file mode 100644 index 00000000..0c6e0cbe --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_basic_dst.spvasm @@ -0,0 +1,15 @@ + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %structptr "structptr" + OpName %structptr2 "structptr2" + OpTypeForwardPointer %structptr UniformConstant + OpTypeForwardPointer %structptr2 Function + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1 + %structptr2 = OpTypePointer Function %structt1 diff --git a/test/diff/diff_files/OpTypeForwardPointer_basic_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_basic_src.spvasm new file mode 100644 index 00000000..408ec983 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_basic_src.spvasm @@ -0,0 +1,13 @@ +;; Basic test that OpTypeForwardPointer is matched + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %structptr "structptr" + OpTypeForwardPointer %structptr UniformConstant + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1 diff --git a/test/diff/diff_files/OpTypeForwardPointer_intertwined_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_intertwined_autogen.cpp new file mode 100644 index 00000000..f2c9008e --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_intertwined_autogen.cpp @@ -0,0 +1,138 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests that two forwarded types whose declarations are intertwined match +// correctly +constexpr char kSrc[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpName %Bptr "Bptr" + OpTypeForwardPointer %Aptr UniformConstant + OpTypeForwardPointer %Bptr UniformConstant + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint %Bptr + %B = OpTypeStruct %uint %Aptr %Bptr + %Aptr = OpTypePointer UniformConstant %A + %Bptr = OpTypePointer UniformConstant %B)"; +constexpr char kDst[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpName %Bptr "Bptr" + OpTypeForwardPointer %Bptr UniformConstant + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %B = OpTypeStruct %uint %Aptr %Bptr %uint + %A = OpTypeStruct %Aptr %uint %Bptr + %Aptr = OpTypePointer UniformConstant %A + %Bptr = OpTypePointer UniformConstant %B +)"; + +TEST(DiffTest, OptypeforwardpointerIntertwined) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 6 ++; Bound: 7 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %1 "Aptr" + OpName %2 "Bptr" + OpTypeForwardPointer %1 UniformConstant + OpTypeForwardPointer %2 UniformConstant + %3 = OpTypeInt 32 0 ++%6 = OpTypeStruct %3 %1 %2 %3 + %4 = OpTypeStruct %1 %3 %2 +-%5 = OpTypeStruct %3 %1 %2 + %1 = OpTypePointer UniformConstant %4 +-%2 = OpTypePointer UniformConstant %5 ++%2 = OpTypePointer UniformConstant %6 +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, OptypeforwardpointerIntertwinedNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %Aptr UniformConstant + OpTypeForwardPointer %Bptr UniformConstant + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint %Bptr + %B = OpTypeStruct %uint %Aptr %Bptr + %Aptr = OpTypePointer UniformConstant %A + %Bptr = OpTypePointer UniformConstant %B +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %Bptr UniformConstant + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %B = OpTypeStruct %uint %Aptr %Bptr %uint + %A = OpTypeStruct %Aptr %uint %Bptr + %Aptr = OpTypePointer UniformConstant %A + %Bptr = OpTypePointer UniformConstant %B +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 6 ++; Bound: 10 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL +-OpTypeForwardPointer %1 UniformConstant +-OpTypeForwardPointer %2 UniformConstant ++OpTypeForwardPointer %6 UniformConstant ++OpTypeForwardPointer %7 UniformConstant + %3 = OpTypeInt 32 0 +-%4 = OpTypeStruct %1 %3 %2 +-%5 = OpTypeStruct %3 %1 %2 +-%1 = OpTypePointer UniformConstant %4 +-%2 = OpTypePointer UniformConstant %5 ++%8 = OpTypeStruct %3 %7 %6 %3 ++%9 = OpTypeStruct %7 %3 %6 ++%7 = OpTypePointer UniformConstant %9 ++%6 = OpTypePointer UniformConstant %8 +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/OpTypeForwardPointer_intertwined_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_intertwined_dst.spvasm new file mode 100644 index 00000000..bd735010 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_intertwined_dst.spvasm @@ -0,0 +1,13 @@ + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpName %Bptr "Bptr" + OpTypeForwardPointer %Bptr UniformConstant + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %B = OpTypeStruct %uint %Aptr %Bptr %uint + %A = OpTypeStruct %Aptr %uint %Bptr + %Aptr = OpTypePointer UniformConstant %A + %Bptr = OpTypePointer UniformConstant %B diff --git a/test/diff/diff_files/OpTypeForwardPointer_intertwined_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_intertwined_src.spvasm new file mode 100644 index 00000000..8fdaf28f --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_intertwined_src.spvasm @@ -0,0 +1,15 @@ +;; Tests that two forwarded types whose declarations are intertwined match +;; correctly + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpName %Bptr "Bptr" + OpTypeForwardPointer %Aptr UniformConstant + OpTypeForwardPointer %Bptr UniformConstant + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint %Bptr + %B = OpTypeStruct %uint %Aptr %Bptr + %Aptr = OpTypePointer UniformConstant %A + %Bptr = OpTypePointer UniformConstant %B diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_autogen.cpp new file mode 100644 index 00000000..0a59be32 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_autogen.cpp @@ -0,0 +1,116 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests that two forwarded type pointers with mismatching storage classes +// aren't matched +constexpr char kSrc[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint + %Aptr = OpTypePointer UniformConstant %A)"; +constexpr char kDst[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr Function + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint + %Aptr = OpTypePointer Function %A +)"; + +TEST(DiffTest, OptypeforwardpointerMismatchingClass) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 4 ++; Bound: 6 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL +-OpName %1 "Aptr" ++OpName %4 "Aptr" +-OpTypeForwardPointer %1 UniformConstant ++OpTypeForwardPointer %4 Function + %2 = OpTypeInt 32 0 +-%3 = OpTypeStruct %1 %2 +-%1 = OpTypePointer UniformConstant %3 ++%5 = OpTypeStruct %4 %2 ++%4 = OpTypePointer Function %5 +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, OptypeforwardpointerMismatchingClassNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint + %Aptr = OpTypePointer UniformConstant %A +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %Aptr Function + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint + %Aptr = OpTypePointer Function %A +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 4 ++; Bound: 6 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL +-OpTypeForwardPointer %1 UniformConstant ++OpTypeForwardPointer %4 Function + %2 = OpTypeInt 32 0 +-%3 = OpTypeStruct %1 %2 +-%1 = OpTypePointer UniformConstant %3 ++%5 = OpTypeStruct %4 %2 ++%4 = OpTypePointer Function %5 +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_dst.spvasm new file mode 100644 index 00000000..e874a0ca --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_dst.spvasm @@ -0,0 +1,9 @@ + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr Function + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint + %Aptr = OpTypePointer Function %A diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_src.spvasm new file mode 100644 index 00000000..8a339331 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_src.spvasm @@ -0,0 +1,11 @@ +;; Tests that two forwarded type pointers with mismatching storage classes +;; aren't matched + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint + %Aptr = OpTypePointer UniformConstant %A diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_autogen.cpp new file mode 100644 index 00000000..0067cdf6 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_autogen.cpp @@ -0,0 +1,111 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests that two forwarded type pointers with mismatching types aren't matched +constexpr char kSrc[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint + %Aptr = OpTypePointer UniformConstant %A)"; +constexpr char kDst[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %Aptr = OpTypePointer UniformConstant %uint +)"; + +TEST(DiffTest, OptypeforwardpointerMismatchingType) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 4 ++; Bound: 5 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL +-OpName %1 "Aptr" ++OpName %4 "Aptr" +-OpTypeForwardPointer %1 UniformConstant ++OpTypeForwardPointer %4 UniformConstant + %2 = OpTypeInt 32 0 +-%3 = OpTypeStruct %1 %2 +-%1 = OpTypePointer UniformConstant %3 ++%4 = OpTypePointer UniformConstant %2 +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, OptypeforwardpointerMismatchingTypeNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint + %Aptr = OpTypePointer UniformConstant %A +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %Aptr = OpTypePointer UniformConstant %uint +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 4 ++; Bound: 5 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL +-OpTypeForwardPointer %1 UniformConstant ++OpTypeForwardPointer %4 UniformConstant + %2 = OpTypeInt 32 0 +-%3 = OpTypeStruct %1 %2 +-%1 = OpTypePointer UniformConstant %3 ++%4 = OpTypePointer UniformConstant %2 +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_dst.spvasm new file mode 100644 index 00000000..ee3d35c0 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_dst.spvasm @@ -0,0 +1,8 @@ + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %Aptr = OpTypePointer UniformConstant %uint diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_src.spvasm new file mode 100644 index 00000000..a4596a07 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_src.spvasm @@ -0,0 +1,10 @@ +;; Tests that two forwarded type pointers with mismatching types aren't matched + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint + %Aptr = OpTypePointer UniformConstant %A diff --git a/test/diff/diff_files/OpTypeForwardPointer_nested_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_nested_autogen.cpp new file mode 100644 index 00000000..d66c28ae --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_nested_autogen.cpp @@ -0,0 +1,127 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests that two forwarded declarations match even if the type pointer is used +// in a nested struct declaration, and in multiple places +constexpr char kSrc[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %C = OpTypeStruct %Aptr %uint %Aptr + %B = OpTypeStruct %C %Aptr %uint + %A = OpTypeStruct %B %C %B + %Aptr = OpTypePointer UniformConstant %A)"; +constexpr char kDst[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %C = OpTypeStruct %Aptr %uint %Aptr + %B = OpTypeStruct %C %Aptr + %A = OpTypeStruct %B %C %B + %Aptr = OpTypePointer UniformConstant %A +)"; + +TEST(DiffTest, OptypeforwardpointerNested) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 6 ++; Bound: 8 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %1 "Aptr" + OpTypeForwardPointer %1 UniformConstant + %2 = OpTypeInt 32 0 + %3 = OpTypeStruct %1 %2 %1 +-%4 = OpTypeStruct %3 %1 %2 +-%5 = OpTypeStruct %4 %3 %4 ++%6 = OpTypeStruct %3 %1 ++%7 = OpTypeStruct %6 %3 %6 +-%1 = OpTypePointer UniformConstant %5 ++%1 = OpTypePointer UniformConstant %7 +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, OptypeforwardpointerNestedNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %C = OpTypeStruct %Aptr %uint %Aptr + %B = OpTypeStruct %C %Aptr %uint + %A = OpTypeStruct %B %C %B + %Aptr = OpTypePointer UniformConstant %A +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %C = OpTypeStruct %Aptr %uint %Aptr + %B = OpTypeStruct %C %Aptr + %A = OpTypeStruct %B %C %B + %Aptr = OpTypePointer UniformConstant %A +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 6 ++; Bound: 8 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %1 UniformConstant + %2 = OpTypeInt 32 0 + %3 = OpTypeStruct %1 %2 %1 +-%4 = OpTypeStruct %3 %1 %2 +-%5 = OpTypeStruct %4 %3 %4 ++%6 = OpTypeStruct %3 %1 ++%7 = OpTypeStruct %6 %3 %6 +-%1 = OpTypePointer UniformConstant %5 ++%1 = OpTypePointer UniformConstant %7 +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/OpTypeForwardPointer_nested_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_nested_dst.spvasm new file mode 100644 index 00000000..e2483556 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_nested_dst.spvasm @@ -0,0 +1,11 @@ + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %C = OpTypeStruct %Aptr %uint %Aptr + %B = OpTypeStruct %C %Aptr + %A = OpTypeStruct %B %C %B + %Aptr = OpTypePointer UniformConstant %A diff --git a/test/diff/diff_files/OpTypeForwardPointer_nested_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_nested_src.spvasm new file mode 100644 index 00000000..035410e0 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_nested_src.spvasm @@ -0,0 +1,13 @@ +;; Tests that two forwarded declarations match even if the type pointer is used +;; in a nested struct declaration, and in multiple places + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %C = OpTypeStruct %Aptr %uint %Aptr + %B = OpTypeStruct %C %Aptr %uint + %A = OpTypeStruct %B %C %B + %Aptr = OpTypePointer UniformConstant %A diff --git a/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_autogen.cpp new file mode 100644 index 00000000..df86fef2 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_autogen.cpp @@ -0,0 +1,124 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test that OpTypeForwardPointer is matched when one SPIR-V doesn't have debug +// info +constexpr char kSrc[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %structptr "structptr" + OpTypeForwardPointer %structptr UniformConstant + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1)"; +constexpr char kDst[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %structptr UniformConstant + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1 +)"; + +TEST(DiffTest, OptypeforwardpointerOnesidedDebug) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 7 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL +-OpName %1 "structptr" + OpTypeForwardPointer %1 UniformConstant + %2 = OpTypeInt 32 0 + %3 = OpTypeStruct %1 %2 + %4 = OpTypeStruct %2 %1 + %5 = OpTypeStruct %2 %2 %1 + %6 = OpTypeStruct %2 %2 %2 %1 + %1 = OpTypePointer UniformConstant %3 +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, OptypeforwardpointerOnesidedDebugNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %structptr UniformConstant + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1 +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %structptr UniformConstant + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1 +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 7 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %1 UniformConstant + %2 = OpTypeInt 32 0 + %3 = OpTypeStruct %1 %2 + %4 = OpTypeStruct %2 %1 + %5 = OpTypeStruct %2 %2 %1 + %6 = OpTypeStruct %2 %2 %2 %1 + %1 = OpTypePointer UniformConstant %3 +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_dst.spvasm new file mode 100644 index 00000000..7e25710e --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_dst.spvasm @@ -0,0 +1,11 @@ + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %structptr UniformConstant + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1 diff --git a/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_src.spvasm new file mode 100644 index 00000000..e949b272 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_src.spvasm @@ -0,0 +1,14 @@ +;; Test that OpTypeForwardPointer is matched when one SPIR-V doesn't have debug +;; info + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %structptr "structptr" + OpTypeForwardPointer %structptr UniformConstant + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1 diff --git a/test/diff/diff_files/README.md b/test/diff/diff_files/README.md new file mode 100644 index 00000000..5dcee25a --- /dev/null +++ b/test/diff/diff_files/README.md @@ -0,0 +1,17 @@ +# Diff tests + +This directory contains files used to ensure correctness of the `spirv-diff` implementation. The +`generate_tests.py` script takes `name_src.spvasm` and `name_dst.spvasm` (for each `name`) and +produces unit test files in the form of `name_autogen.cpp`. + +The unit test files test the diff between the src and dst inputs, as well as between debug-stripped +versions of those. Additionally, based on the `{variant}_TESTS` lists defined in +`generate_tests.py`, extra unit tests are added to exercise different options of spirv-diff. + +New tests are added simply by placing a new `name_src.spvasm` and `name_dst.spvasm` pair in this +directory and running `generate_tests.py`. Note that this script needs the path to the spirv-diff +executable that is built. + +The `generate_tests.py` script additionally expects `name_src.spvasm` to include a heading where the +purpose of the test is explained. This heading is parsed as a block of lines starting with `;;` at +the top of the file. diff --git a/test/diff/diff_files/basic_autogen.cpp b/test/diff/diff_files/basic_autogen.cpp new file mode 100644 index 00000000..f3afc701 --- /dev/null +++ b/test/diff/diff_files/basic_autogen.cpp @@ -0,0 +1,407 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Basic test for spirv-diff +constexpr char kSrc[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %22 "main" %4 %14 %19 + OpSource GLSL 450 + OpName %4 "_ua_position" + OpName %14 "ANGLEXfbPosition" + OpName %17 "gl_PerVertex" + OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" + OpMemberName %17 3 "gl_CullDistance" + OpName %19 "" + OpName %22 "main" + OpDecorate %4 Location 0 + OpDecorate %14 Location 0 + OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%6 = OpTypeInt 32 1 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %6 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%14 = OpVariable %13 Output +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd)"; +constexpr char kDst[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 28 +; Schema: 0 +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %4 "main" %13 %17 %27 +OpSource GLSL 450 +OpName %4 "main" +OpName %11 "gl_PerVertex" +OpMemberName %11 0 "gl_Position" +OpMemberName %11 1 "gl_PointSize" +OpMemberName %11 2 "gl_ClipDistance" +OpMemberName %11 3 "gl_CullDistance" +OpName %13 "" +OpName %17 "_ua_position" +OpName %27 "ANGLEXfbPosition" +OpMemberDecorate %11 0 BuiltIn Position +OpMemberDecorate %11 1 BuiltIn PointSize +OpMemberDecorate %11 2 BuiltIn ClipDistance +OpMemberDecorate %11 3 BuiltIn CullDistance +OpDecorate %11 Block +OpDecorate %17 Location 0 +OpDecorate %27 Location 0 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 4 +%8 = OpTypeInt 32 0 +%9 = OpConstant %8 1 +%10 = OpTypeArray %6 %9 +%11 = OpTypeStruct %7 %6 %10 %10 +%12 = OpTypePointer Output %11 +%13 = OpVariable %12 Output +%14 = OpTypeInt 32 1 +%15 = OpConstant %14 0 +%16 = OpTypePointer Input %7 +%17 = OpVariable %16 Input +%19 = OpTypePointer Output %7 +%27 = OpVariable %19 Output +%4 = OpFunction %2 None %3 +%5 = OpLabel +%18 = OpLoad %7 %17 +%20 = OpAccessChain %19 %13 %15 +OpStore %20 %18 +OpReturn +OpFunctionEnd +)"; + +TEST(DiffTest, Basic) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 27 ++; Bound: 36 + ; Schema: 0 + OpCapability Shader ++%27 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %22 "main" %4 %14 %19 ++OpEntryPoint Vertex %22 "main" %19 %4 %14 + OpSource GLSL 450 + OpName %4 "_ua_position" + OpName %14 "ANGLEXfbPosition" + OpName %17 "gl_PerVertex" + OpMemberName %17 0 "gl_Position" + OpMemberName %17 1 "gl_PointSize" + OpMemberName %17 2 "gl_ClipDistance" + OpMemberName %17 3 "gl_CullDistance" + OpName %19 "" + OpName %22 "main" + OpDecorate %4 Location 0 + OpDecorate %14 Location 0 +-OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize + OpMemberDecorate %17 2 BuiltIn ClipDistance + OpMemberDecorate %17 3 BuiltIn CullDistance + OpDecorate %17 Block + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeInt 32 0 + %6 = OpTypeInt 32 1 +-%15 = OpConstant %5 8 +-%16 = OpTypeArray %1 %15 +-%17 = OpTypeStruct %2 %1 %16 %16 ++%17 = OpTypeStruct %2 %1 %29 %29 ++%28 = OpConstant %5 1 ++%29 = OpTypeArray %1 %28 + %20 = OpTypeVoid + %25 = OpConstant %6 0 + %3 = OpTypePointer Input %2 + %13 = OpTypePointer Output %2 + %18 = OpTypePointer Output %17 + %21 = OpTypeFunction %20 + %4 = OpVariable %3 Input + %14 = OpVariable %13 Output + %19 = OpVariable %18 Output + %22 = OpFunction %20 None %21 + %23 = OpLabel + %24 = OpLoad %2 %4 + %26 = OpAccessChain %13 %19 %25 + OpStore %26 %24 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, BasicNoDebug) { + constexpr char kSrcNoDebug[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %22 "main" %4 %14 %19 + OpSource GLSL 450 + OpDecorate %4 Location 0 + OpDecorate %14 Location 0 + OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%6 = OpTypeInt 32 1 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %6 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%14 = OpVariable %13 Output +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 28 +; Schema: 0 +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %4 "main" %13 %17 %27 +OpSource GLSL 450 +OpMemberDecorate %11 0 BuiltIn Position +OpMemberDecorate %11 1 BuiltIn PointSize +OpMemberDecorate %11 2 BuiltIn ClipDistance +OpMemberDecorate %11 3 BuiltIn CullDistance +OpDecorate %11 Block +OpDecorate %17 Location 0 +OpDecorate %27 Location 0 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 4 +%8 = OpTypeInt 32 0 +%9 = OpConstant %8 1 +%10 = OpTypeArray %6 %9 +%11 = OpTypeStruct %7 %6 %10 %10 +%12 = OpTypePointer Output %11 +%13 = OpVariable %12 Output +%14 = OpTypeInt 32 1 +%15 = OpConstant %14 0 +%16 = OpTypePointer Input %7 +%17 = OpVariable %16 Input +%19 = OpTypePointer Output %7 +%27 = OpVariable %19 Output +%4 = OpFunction %2 None %3 +%5 = OpLabel +%18 = OpLoad %7 %17 +%20 = OpAccessChain %19 %13 %15 +OpStore %20 %18 +OpReturn +OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 27 ++; Bound: 36 + ; Schema: 0 + OpCapability Shader ++%27 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %22 "main" %4 %14 %19 ++OpEntryPoint Vertex %22 "main" %19 %4 %14 + OpSource GLSL 450 + OpDecorate %4 Location 0 + OpDecorate %14 Location 0 +-OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize + OpMemberDecorate %17 2 BuiltIn ClipDistance + OpMemberDecorate %17 3 BuiltIn CullDistance + OpDecorate %17 Block + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeInt 32 0 + %6 = OpTypeInt 32 1 +-%15 = OpConstant %5 8 +-%16 = OpTypeArray %1 %15 +-%17 = OpTypeStruct %2 %1 %16 %16 ++%17 = OpTypeStruct %2 %1 %29 %29 ++%28 = OpConstant %5 1 ++%29 = OpTypeArray %1 %28 + %20 = OpTypeVoid + %25 = OpConstant %6 0 + %3 = OpTypePointer Input %2 + %13 = OpTypePointer Output %2 + %18 = OpTypePointer Output %17 + %21 = OpTypeFunction %20 + %4 = OpVariable %3 Input + %14 = OpVariable %13 Output + %19 = OpVariable %18 Output + %22 = OpFunction %20 None %21 + %23 = OpLabel + %24 = OpLoad %2 %4 + %26 = OpAccessChain %13 %19 %25 + OpStore %26 %24 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +TEST(DiffTest, BasicDumpIds) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 27 ++; Bound: 36 + ; Schema: 0 + OpCapability Shader ++%27 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %22 "main" %4 %14 %19 ++OpEntryPoint Vertex %22 "main" %19 %4 %14 + OpSource GLSL 450 + OpName %4 "_ua_position" + OpName %14 "ANGLEXfbPosition" + OpName %17 "gl_PerVertex" + OpMemberName %17 0 "gl_Position" + OpMemberName %17 1 "gl_PointSize" + OpMemberName %17 2 "gl_ClipDistance" + OpMemberName %17 3 "gl_CullDistance" + OpName %19 "" + OpName %22 "main" + OpDecorate %4 Location 0 + OpDecorate %14 Location 0 +-OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize + OpMemberDecorate %17 2 BuiltIn ClipDistance + OpMemberDecorate %17 3 BuiltIn CullDistance + OpDecorate %17 Block + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeInt 32 0 + %6 = OpTypeInt 32 1 +-%15 = OpConstant %5 8 +-%16 = OpTypeArray %1 %15 +-%17 = OpTypeStruct %2 %1 %16 %16 ++%17 = OpTypeStruct %2 %1 %29 %29 ++%28 = OpConstant %5 1 ++%29 = OpTypeArray %1 %28 + %20 = OpTypeVoid + %25 = OpConstant %6 0 + %3 = OpTypePointer Input %2 + %13 = OpTypePointer Output %2 + %18 = OpTypePointer Output %17 + %21 = OpTypeFunction %20 + %4 = OpVariable %3 Input + %14 = OpVariable %13 Output + %19 = OpVariable %18 Output + %22 = OpFunction %20 None %21 + %23 = OpLabel + %24 = OpLoad %2 %4 + %26 = OpAccessChain %13 %19 %25 + OpStore %26 %24 + OpReturn + OpFunctionEnd + Src -> Dst + 1 -> 6 [TypeFloat] + 2 -> 7 [TypeVector] + 3 -> 16 [TypePointer] + 4 -> 17 [Variable] + 5 -> 8 [TypeInt] + 6 -> 14 [TypeInt] + 13 -> 19 [TypePointer] + 14 -> 27 [Variable] + 15 -> 34 [Constant] + 16 -> 35 [TypeArray] + 17 -> 11 [TypeStruct] + 18 -> 12 [TypePointer] + 19 -> 13 [Variable] + 20 -> 2 [TypeVoid] + 21 -> 3 [TypeFunction] + 22 -> 4 [Function] + 23 -> 5 [Label] + 24 -> 18 [Load] + 25 -> 15 [Constant] + 26 -> 20 [AccessChain] +)"; + Options options; + options.dump_id_map = true; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/basic_dst.spvasm b/test/diff/diff_files/basic_dst.spvasm new file mode 100644 index 00000000..79cb8887 --- /dev/null +++ b/test/diff/diff_files/basic_dst.spvasm @@ -0,0 +1,49 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 28 +; Schema: 0 +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %4 "main" %13 %17 %27 +OpSource GLSL 450 +OpName %4 "main" +OpName %11 "gl_PerVertex" +OpMemberName %11 0 "gl_Position" +OpMemberName %11 1 "gl_PointSize" +OpMemberName %11 2 "gl_ClipDistance" +OpMemberName %11 3 "gl_CullDistance" +OpName %13 "" +OpName %17 "_ua_position" +OpName %27 "ANGLEXfbPosition" +OpMemberDecorate %11 0 BuiltIn Position +OpMemberDecorate %11 1 BuiltIn PointSize +OpMemberDecorate %11 2 BuiltIn ClipDistance +OpMemberDecorate %11 3 BuiltIn CullDistance +OpDecorate %11 Block +OpDecorate %17 Location 0 +OpDecorate %27 Location 0 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 4 +%8 = OpTypeInt 32 0 +%9 = OpConstant %8 1 +%10 = OpTypeArray %6 %9 +%11 = OpTypeStruct %7 %6 %10 %10 +%12 = OpTypePointer Output %11 +%13 = OpVariable %12 Output +%14 = OpTypeInt 32 1 +%15 = OpConstant %14 0 +%16 = OpTypePointer Input %7 +%17 = OpVariable %16 Input +%19 = OpTypePointer Output %7 +%27 = OpVariable %19 Output +%4 = OpFunction %2 None %3 +%5 = OpLabel +%18 = OpLoad %7 %17 +%20 = OpAccessChain %19 %13 %15 +OpStore %20 %18 +OpReturn +OpFunctionEnd diff --git a/test/diff/diff_files/basic_src.spvasm b/test/diff/diff_files/basic_src.spvasm new file mode 100644 index 00000000..c55ec7af --- /dev/null +++ b/test/diff/diff_files/basic_src.spvasm @@ -0,0 +1,50 @@ +;; Basic test for spirv-diff +; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %22 "main" %4 %14 %19 + OpSource GLSL 450 + OpName %4 "_ua_position" + OpName %14 "ANGLEXfbPosition" + OpName %17 "gl_PerVertex" + OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" + OpMemberName %17 3 "gl_CullDistance" + OpName %19 "" + OpName %22 "main" + OpDecorate %4 Location 0 + OpDecorate %14 Location 0 + OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%6 = OpTypeInt 32 1 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %6 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%14 = OpVariable %13 Output +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd diff --git a/test/diff/diff_files/constant_array_size_autogen.cpp b/test/diff/diff_files/constant_array_size_autogen.cpp new file mode 100644 index 00000000..16975ff4 --- /dev/null +++ b/test/diff/diff_files/constant_array_size_autogen.cpp @@ -0,0 +1,306 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests that identical integer constants are matched when used as array size, +// regardless of int or uint. +constexpr char kSrc[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpName %4 "_ua_position" +OpName %17 "gl_PerVertex" +OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" +OpMemberName %17 3 "gl_CullDistance" +OpName %19 "" +OpName %22 "main" +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd)"; +constexpr char kDst[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpName %4 "_ua_position" +OpName %17 "gl_PerVertex" +OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" +OpMemberName %17 3 "gl_CullDistance" +OpName %19 "" +OpName %22 "main" +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%6 = OpTypeInt 32 1 +%8 = OpTypeVector %5 4 +%15 = OpConstant %6 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd +)"; + +TEST(DiffTest, ConstantArraySize) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 27 ++; Bound: 34 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %22 "main" %4 %19 + OpSource GLSL 450 + OpName %4 "_ua_position" + OpName %17 "gl_PerVertex" + OpMemberName %17 0 "gl_Position" + OpMemberName %17 1 "gl_PointSize" + OpMemberName %17 2 "gl_ClipDistance" + OpMemberName %17 3 "gl_CullDistance" + OpName %19 "" + OpName %22 "main" + OpDecorate %4 Location 0 + OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize + OpMemberDecorate %17 2 BuiltIn ClipDistance + OpMemberDecorate %17 3 BuiltIn CullDistance + OpDecorate %17 Block + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeInt 32 0 ++%27 = OpTypeInt 32 1 + %8 = OpTypeVector %5 4 +-%15 = OpConstant %5 8 ++%15 = OpConstant %27 8 + %16 = OpTypeArray %1 %15 + %17 = OpTypeStruct %2 %1 %16 %16 + %20 = OpTypeVoid + %25 = OpConstant %5 0 + %3 = OpTypePointer Input %2 + %13 = OpTypePointer Output %2 + %18 = OpTypePointer Output %17 + %21 = OpTypeFunction %20 + %4 = OpVariable %3 Input + %19 = OpVariable %18 Output + %22 = OpFunction %20 None %21 + %23 = OpLabel + %24 = OpLoad %2 %4 + %26 = OpAccessChain %13 %19 %25 + OpStore %26 %24 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, ConstantArraySizeNoDebug) { + constexpr char kSrcNoDebug[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%6 = OpTypeInt 32 1 +%8 = OpTypeVector %5 4 +%15 = OpConstant %6 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 27 ++; Bound: 34 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %22 "main" %4 %19 + OpSource GLSL 450 + OpDecorate %4 Location 0 + OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize + OpMemberDecorate %17 2 BuiltIn ClipDistance + OpMemberDecorate %17 3 BuiltIn CullDistance + OpDecorate %17 Block + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeInt 32 0 ++%27 = OpTypeInt 32 1 + %8 = OpTypeVector %5 4 +-%15 = OpConstant %5 8 ++%15 = OpConstant %27 8 + %16 = OpTypeArray %1 %15 + %17 = OpTypeStruct %2 %1 %16 %16 + %20 = OpTypeVoid + %25 = OpConstant %5 0 + %3 = OpTypePointer Input %2 + %13 = OpTypePointer Output %2 + %18 = OpTypePointer Output %17 + %21 = OpTypeFunction %20 + %4 = OpVariable %3 Input + %19 = OpVariable %18 Output + %22 = OpFunction %20 None %21 + %23 = OpLabel + %24 = OpLoad %2 %4 + %26 = OpAccessChain %13 %19 %25 + OpStore %26 %24 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/constant_array_size_dst.spvasm b/test/diff/diff_files/constant_array_size_dst.spvasm new file mode 100644 index 00000000..a432b612 --- /dev/null +++ b/test/diff/diff_files/constant_array_size_dst.spvasm @@ -0,0 +1,47 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpName %4 "_ua_position" +OpName %17 "gl_PerVertex" +OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" +OpMemberName %17 3 "gl_CullDistance" +OpName %19 "" +OpName %22 "main" +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%6 = OpTypeInt 32 1 +%8 = OpTypeVector %5 4 +%15 = OpConstant %6 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd diff --git a/test/diff/diff_files/constant_array_size_src.spvasm b/test/diff/diff_files/constant_array_size_src.spvasm new file mode 100644 index 00000000..4e1ba646 --- /dev/null +++ b/test/diff/diff_files/constant_array_size_src.spvasm @@ -0,0 +1,48 @@ +;; Tests that identical integer constants are matched when used as array size, +;; regardless of int or uint. +; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpName %4 "_ua_position" +OpName %17 "gl_PerVertex" +OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" +OpMemberName %17 3 "gl_CullDistance" +OpName %19 "" +OpName %22 "main" +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd diff --git a/test/diff/diff_files/diff_test_files_autogen.cmake b/test/diff/diff_files/diff_test_files_autogen.cmake new file mode 100644 index 00000000..6440d0b9 --- /dev/null +++ b/test/diff/diff_files/diff_test_files_autogen.cmake @@ -0,0 +1,47 @@ +# GENERATED FILE - DO NOT EDIT. +# Generated by generate_tests.py +# +# Copyright (c) 2022 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. + +list(APPEND DIFF_TEST_FILES +"diff_files/OpExtInst_in_dst_only_autogen.cpp" +"diff_files/OpExtInst_in_src_only_autogen.cpp" +"diff_files/OpTypeForwardPointer_basic_autogen.cpp" +"diff_files/OpTypeForwardPointer_intertwined_autogen.cpp" +"diff_files/OpTypeForwardPointer_mismatching_class_autogen.cpp" +"diff_files/OpTypeForwardPointer_mismatching_type_autogen.cpp" +"diff_files/OpTypeForwardPointer_nested_autogen.cpp" +"diff_files/OpTypeForwardPointer_onesided_debug_autogen.cpp" +"diff_files/basic_autogen.cpp" +"diff_files/constant_array_size_autogen.cpp" +"diff_files/different_decorations_fragment_autogen.cpp" +"diff_files/different_decorations_vertex_autogen.cpp" +"diff_files/different_function_parameter_count_autogen.cpp" +"diff_files/extra_if_block_autogen.cpp" +"diff_files/index_signedness_autogen.cpp" +"diff_files/int_vs_uint_constants_autogen.cpp" +"diff_files/large_functions_large_diffs_autogen.cpp" +"diff_files/large_functions_small_diffs_autogen.cpp" +"diff_files/multiple_different_entry_points_autogen.cpp" +"diff_files/multiple_same_entry_points_autogen.cpp" +"diff_files/reordered_if_blocks_autogen.cpp" +"diff_files/reordered_switch_blocks_autogen.cpp" +"diff_files/small_functions_small_diffs_autogen.cpp" +"diff_files/spec_constant_array_size_autogen.cpp" +"diff_files/spec_constant_composite_autogen.cpp" +"diff_files/spec_constant_op_autogen.cpp" +"diff_files/spec_constant_specid_autogen.cpp" +"diff_files/unrelated_shaders_autogen.cpp" +) diff --git a/test/diff/diff_files/different_decorations_fragment_autogen.cpp b/test/diff/diff_files/different_decorations_fragment_autogen.cpp new file mode 100644 index 00000000..0d34654f --- /dev/null +++ b/test/diff/diff_files/different_decorations_fragment_autogen.cpp @@ -0,0 +1,1626 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test where variable set/binding/location decorations are different between +// src and dst fragment shaders. +constexpr char kSrc[] = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %63 "main" %4 %22 +OpExecutionMode %63 OriginUpperLeft +OpSource GLSL 450 +OpName %4 "_ue" +OpName %8 "_uf" +OpName %11 "_ug" +OpName %12 "_uA" +OpMemberName %12 0 "_ux" +OpName %14 "_uc" +OpName %15 "_uB" +OpMemberName %15 0 "_ux" +OpName %20 "_ud" +OpName %22 "_ucol" +OpName %26 "ANGLEDepthRangeParams" +OpMemberName %26 0 "near" +OpMemberName %26 1 "far" +OpMemberName %26 2 "diff" +OpMemberName %26 3 "reserved" +OpName %27 "ANGLEUniformBlock" +OpMemberName %27 0 "viewport" +OpMemberName %27 1 "clipDistancesEnabled" +OpMemberName %27 2 "xfbActiveUnpaused" +OpMemberName %27 3 "xfbVerticesPerInstance" +OpMemberName %27 4 "numSamples" +OpMemberName %27 5 "xfbBufferOffsets" +OpMemberName %27 6 "acbBufferOffsets" +OpMemberName %27 7 "depthRange" +OpName %29 "ANGLEUniforms" +OpName %33 "_uc" +OpName %32 "_uh" +OpName %49 "_ux" +OpName %50 "_uy" +OpName %48 "_ui" +OpName %63 "main" +OpName %65 "param" +OpName %68 "param" +OpName %73 "param" +OpDecorate %4 Location 0 +OpDecorate %8 RelaxedPrecision +OpDecorate %8 DescriptorSet 0 +OpDecorate %8 Binding 0 +OpDecorate %11 DescriptorSet 0 +OpDecorate %11 Binding 1 +OpMemberDecorate %12 0 Offset 0 +OpMemberDecorate %12 0 RelaxedPrecision +OpDecorate %12 Block +OpDecorate %14 DescriptorSet 0 +OpDecorate %14 Binding 2 +OpMemberDecorate %15 0 Offset 0 +OpMemberDecorate %15 0 RelaxedPrecision +OpDecorate %15 BufferBlock +OpDecorate %20 DescriptorSet 0 +OpDecorate %20 Binding 3 +OpDecorate %22 RelaxedPrecision +OpDecorate %22 Location 0 +OpMemberDecorate %26 0 Offset 0 +OpMemberDecorate %26 1 Offset 4 +OpMemberDecorate %26 2 Offset 8 +OpMemberDecorate %26 3 Offset 12 +OpMemberDecorate %27 0 Offset 0 +OpMemberDecorate %27 1 Offset 16 +OpMemberDecorate %27 2 Offset 20 +OpMemberDecorate %27 3 Offset 24 +OpMemberDecorate %27 4 Offset 28 +OpMemberDecorate %27 5 Offset 32 +OpMemberDecorate %27 6 Offset 48 +OpMemberDecorate %27 7 Offset 64 +OpMemberDecorate %27 2 RelaxedPrecision +OpMemberDecorate %27 4 RelaxedPrecision +OpDecorate %27 Block +OpDecorate %29 DescriptorSet 0 +OpDecorate %29 Binding 4 +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %38 RelaxedPrecision +OpDecorate %39 RelaxedPrecision +OpDecorate %41 RelaxedPrecision +OpDecorate %42 RelaxedPrecision +OpDecorate %43 RelaxedPrecision +OpDecorate %48 RelaxedPrecision +OpDecorate %49 RelaxedPrecision +OpDecorate %50 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %53 RelaxedPrecision +OpDecorate %54 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +OpDecorate %57 RelaxedPrecision +OpDecorate %58 RelaxedPrecision +OpDecorate %59 RelaxedPrecision +OpDecorate %60 RelaxedPrecision +OpDecorate %67 RelaxedPrecision +OpDecorate %68 RelaxedPrecision +OpDecorate %72 RelaxedPrecision +OpDecorate %73 RelaxedPrecision +OpDecorate %75 RelaxedPrecision +OpDecorate %76 RelaxedPrecision +OpDecorate %77 RelaxedPrecision +OpDecorate %80 RelaxedPrecision +OpDecorate %81 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeImage %1 2D 0 0 0 1 Unknown +%6 = OpTypeSampledImage %5 +%9 = OpTypeImage %1 2D 0 0 0 2 Rgba8 +%12 = OpTypeStruct %2 +%15 = OpTypeStruct %2 +%16 = OpTypeInt 32 0 +%17 = OpConstant %16 2 +%18 = OpTypeArray %15 %17 +%23 = OpTypeInt 32 1 +%24 = OpTypeVector %23 4 +%25 = OpTypeVector %16 4 +%26 = OpTypeStruct %1 %1 %1 %1 +%27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26 +%35 = OpTypeVector %1 2 +%40 = OpTypeVector %23 2 +%61 = OpTypeVoid +%69 = OpConstant %16 0 +%78 = OpConstant %16 1 +%3 = OpTypePointer Input %2 +%7 = OpTypePointer UniformConstant %6 +%10 = OpTypePointer UniformConstant %9 +%13 = OpTypePointer Uniform %12 +%19 = OpTypePointer Uniform %18 +%21 = OpTypePointer Output %2 +%28 = OpTypePointer Uniform %27 +%30 = OpTypePointer Function %2 +%70 = OpTypePointer Uniform %2 +%31 = OpTypeFunction %2 %30 +%47 = OpTypeFunction %2 %30 %30 +%62 = OpTypeFunction %61 +%4 = OpVariable %3 Input +%8 = OpVariable %7 UniformConstant +%11 = OpVariable %10 UniformConstant +%14 = OpVariable %13 Uniform +%20 = OpVariable %19 Uniform +%22 = OpVariable %21 Output +%29 = OpVariable %28 Uniform +%32 = OpFunction %2 None %31 +%33 = OpFunctionParameter %30 +%34 = OpLabel +%36 = OpLoad %6 %8 +%37 = OpLoad %2 %33 +%38 = OpVectorShuffle %35 %37 %37 0 1 +%39 = OpImageSampleImplicitLod %2 %36 %38 +%41 = OpLoad %2 %33 +%42 = OpVectorShuffle %35 %41 %41 2 3 +%43 = OpConvertFToS %40 %42 +%44 = OpLoad %9 %11 +%45 = OpImageRead %2 %44 %43 +%46 = OpFAdd %2 %39 %45 +OpReturnValue %46 +OpFunctionEnd +%48 = OpFunction %2 None %47 +%49 = OpFunctionParameter %30 +%50 = OpFunctionParameter %30 +%51 = OpLabel +%52 = OpLoad %2 %49 +%53 = OpVectorShuffle %35 %52 %52 0 1 +%54 = OpLoad %2 %50 +%55 = OpVectorShuffle %35 %54 %54 2 3 +%56 = OpCompositeExtract %1 %53 0 +%57 = OpCompositeExtract %1 %53 1 +%58 = OpCompositeExtract %1 %55 0 +%59 = OpCompositeExtract %1 %55 1 +%60 = OpCompositeConstruct %2 %56 %57 %58 %59 +OpReturnValue %60 +OpFunctionEnd +%63 = OpFunction %61 None %62 +%64 = OpLabel +%65 = OpVariable %30 Function +%68 = OpVariable %30 Function +%73 = OpVariable %30 Function +%66 = OpLoad %2 %4 +OpStore %65 %66 +%67 = OpFunctionCall %2 %32 %65 +%71 = OpAccessChain %70 %14 %69 +%72 = OpLoad %2 %71 +OpStore %68 %72 +%74 = OpAccessChain %70 %20 %69 %69 +%75 = OpLoad %2 %74 +OpStore %73 %75 +%76 = OpFunctionCall %2 %48 %68 %73 +%77 = OpFAdd %2 %67 %76 +%79 = OpAccessChain %70 %20 %78 %69 +%80 = OpLoad %2 %79 +%81 = OpFAdd %2 %77 %80 +OpStore %22 %81 +OpReturn +OpFunctionEnd +)"; +constexpr char kDst[] = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %63 "main" %4 %22 +OpExecutionMode %63 OriginUpperLeft +OpSource GLSL 450 +OpName %4 "_ue" +OpName %8 "_uf" +OpName %11 "_ug" +OpName %12 "_uA" +OpMemberName %12 0 "_ux" +OpName %14 "_uc" +OpName %15 "_uB" +OpMemberName %15 0 "_ux" +OpName %20 "_ud" +OpName %22 "_ucol" +OpName %26 "ANGLEDepthRangeParams" +OpMemberName %26 0 "near" +OpMemberName %26 1 "far" +OpMemberName %26 2 "diff" +OpMemberName %26 3 "reserved" +OpName %27 "ANGLEUniformBlock" +OpMemberName %27 0 "viewport" +OpMemberName %27 1 "clipDistancesEnabled" +OpMemberName %27 2 "xfbActiveUnpaused" +OpMemberName %27 3 "xfbVerticesPerInstance" +OpMemberName %27 4 "numSamples" +OpMemberName %27 5 "xfbBufferOffsets" +OpMemberName %27 6 "acbBufferOffsets" +OpMemberName %27 7 "depthRange" +OpName %29 "ANGLEUniforms" +OpName %33 "_uc" +OpName %32 "_uh" +OpName %49 "_ux" +OpName %50 "_uy" +OpName %48 "_ui" +OpName %63 "main" +OpName %65 "param" +OpName %68 "param" +OpName %73 "param" +OpDecorate %4 Location 1 +OpDecorate %8 RelaxedPrecision +OpDecorate %8 DescriptorSet 2 +OpDecorate %8 Binding 0 +OpDecorate %11 DescriptorSet 3 +OpDecorate %11 Binding 0 +OpMemberDecorate %12 0 Offset 0 +OpMemberDecorate %12 0 RelaxedPrecision +OpDecorate %12 Block +OpDecorate %14 DescriptorSet 3 +OpDecorate %14 Binding 1 +OpMemberDecorate %15 0 Offset 0 +OpMemberDecorate %15 0 RelaxedPrecision +OpDecorate %15 BufferBlock +OpDecorate %20 DescriptorSet 3 +OpDecorate %20 Binding 2 +OpDecorate %22 RelaxedPrecision +OpDecorate %22 Location 1 +OpMemberDecorate %26 0 Offset 0 +OpMemberDecorate %26 1 Offset 4 +OpMemberDecorate %26 2 Offset 8 +OpMemberDecorate %26 3 Offset 12 +OpMemberDecorate %27 0 Offset 0 +OpMemberDecorate %27 1 Offset 16 +OpMemberDecorate %27 2 Offset 20 +OpMemberDecorate %27 3 Offset 24 +OpMemberDecorate %27 4 Offset 28 +OpMemberDecorate %27 5 Offset 32 +OpMemberDecorate %27 6 Offset 48 +OpMemberDecorate %27 7 Offset 64 +OpMemberDecorate %27 2 RelaxedPrecision +OpMemberDecorate %27 4 RelaxedPrecision +OpDecorate %27 Block +OpDecorate %29 DescriptorSet 0 +OpDecorate %29 Binding 0 +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %38 RelaxedPrecision +OpDecorate %39 RelaxedPrecision +OpDecorate %41 RelaxedPrecision +OpDecorate %42 RelaxedPrecision +OpDecorate %43 RelaxedPrecision +OpDecorate %48 RelaxedPrecision +OpDecorate %49 RelaxedPrecision +OpDecorate %50 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %53 RelaxedPrecision +OpDecorate %54 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +OpDecorate %57 RelaxedPrecision +OpDecorate %58 RelaxedPrecision +OpDecorate %59 RelaxedPrecision +OpDecorate %60 RelaxedPrecision +OpDecorate %67 RelaxedPrecision +OpDecorate %68 RelaxedPrecision +OpDecorate %72 RelaxedPrecision +OpDecorate %73 RelaxedPrecision +OpDecorate %75 RelaxedPrecision +OpDecorate %76 RelaxedPrecision +OpDecorate %77 RelaxedPrecision +OpDecorate %80 RelaxedPrecision +OpDecorate %81 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeImage %1 2D 0 0 0 1 Unknown +%6 = OpTypeSampledImage %5 +%9 = OpTypeImage %1 2D 0 0 0 2 Rgba8 +%12 = OpTypeStruct %2 +%15 = OpTypeStruct %2 +%16 = OpTypeInt 32 0 +%17 = OpConstant %16 2 +%18 = OpTypeArray %15 %17 +%23 = OpTypeInt 32 1 +%24 = OpTypeVector %23 4 +%25 = OpTypeVector %16 4 +%26 = OpTypeStruct %1 %1 %1 %1 +%27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26 +%35 = OpTypeVector %1 2 +%40 = OpTypeVector %23 2 +%61 = OpTypeVoid +%69 = OpConstant %16 0 +%78 = OpConstant %16 1 +%82 = OpTypePointer Private %2 +%3 = OpTypePointer Input %2 +%7 = OpTypePointer UniformConstant %6 +%10 = OpTypePointer UniformConstant %9 +%13 = OpTypePointer Uniform %12 +%19 = OpTypePointer Uniform %18 +%83 = OpTypePointer Private %2 +%21 = OpTypePointer Output %2 +%28 = OpTypePointer Uniform %27 +%30 = OpTypePointer Function %2 +%70 = OpTypePointer Uniform %2 +%31 = OpTypeFunction %2 %30 +%47 = OpTypeFunction %2 %30 %30 +%62 = OpTypeFunction %61 +%4 = OpVariable %3 Input +%8 = OpVariable %7 UniformConstant +%11 = OpVariable %10 UniformConstant +%14 = OpVariable %13 Uniform +%20 = OpVariable %19 Uniform +%22 = OpVariable %21 Output +%29 = OpVariable %28 Uniform +%84 = OpConstant %23 0 +%85 = OpConstant %1 0.5 +%32 = OpFunction %2 None %31 +%33 = OpFunctionParameter %30 +%34 = OpLabel +%36 = OpLoad %6 %8 +%37 = OpLoad %2 %33 +%38 = OpVectorShuffle %35 %37 %37 0 1 +%39 = OpImageSampleImplicitLod %2 %36 %38 +%41 = OpLoad %2 %33 +%42 = OpVectorShuffle %35 %41 %41 2 3 +%43 = OpConvertFToS %40 %42 +%44 = OpLoad %9 %11 +%45 = OpImageRead %2 %44 %43 +%46 = OpFAdd %2 %39 %45 +OpReturnValue %46 +OpFunctionEnd +%48 = OpFunction %2 None %47 +%49 = OpFunctionParameter %30 +%50 = OpFunctionParameter %30 +%51 = OpLabel +%52 = OpLoad %2 %49 +%53 = OpVectorShuffle %35 %52 %52 0 1 +%54 = OpLoad %2 %50 +%55 = OpVectorShuffle %35 %54 %54 2 3 +%56 = OpCompositeExtract %1 %53 0 +%57 = OpCompositeExtract %1 %53 1 +%58 = OpCompositeExtract %1 %55 0 +%59 = OpCompositeExtract %1 %55 1 +%60 = OpCompositeConstruct %2 %56 %57 %58 %59 +OpReturnValue %60 +OpFunctionEnd +%63 = OpFunction %61 None %62 +%64 = OpLabel +%65 = OpVariable %30 Function +%68 = OpVariable %30 Function +%73 = OpVariable %30 Function +%66 = OpLoad %2 %4 +OpStore %65 %66 +%67 = OpFunctionCall %2 %32 %65 +%71 = OpAccessChain %70 %14 %69 +%72 = OpLoad %2 %71 +OpStore %68 %72 +%74 = OpAccessChain %70 %20 %69 %69 +%75 = OpLoad %2 %74 +OpStore %73 %75 +%76 = OpFunctionCall %2 %48 %68 %73 +%77 = OpFAdd %2 %67 %76 +%79 = OpAccessChain %70 %20 %78 %69 +%80 = OpLoad %2 %79 +%81 = OpFAdd %2 %77 %80 +OpStore %22 %81 +OpReturn +OpFunctionEnd +)"; + +TEST(DiffTest, DifferentDecorationsFragment) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 82 ++; Bound: 86 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %63 "main" %4 %22 + OpExecutionMode %63 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "_ue" + OpName %8 "_uf" + OpName %11 "_ug" + OpName %12 "_uA" + OpMemberName %12 0 "_ux" + OpName %14 "_uc" + OpName %15 "_uB" + OpMemberName %15 0 "_ux" + OpName %20 "_ud" + OpName %22 "_ucol" + OpName %26 "ANGLEDepthRangeParams" + OpMemberName %26 0 "near" + OpMemberName %26 1 "far" + OpMemberName %26 2 "diff" + OpMemberName %26 3 "reserved" + OpName %27 "ANGLEUniformBlock" + OpMemberName %27 0 "viewport" + OpMemberName %27 1 "clipDistancesEnabled" + OpMemberName %27 2 "xfbActiveUnpaused" + OpMemberName %27 3 "xfbVerticesPerInstance" + OpMemberName %27 4 "numSamples" + OpMemberName %27 5 "xfbBufferOffsets" + OpMemberName %27 6 "acbBufferOffsets" + OpMemberName %27 7 "depthRange" + OpName %29 "ANGLEUniforms" + OpName %33 "_uc" + OpName %32 "_uh" + OpName %49 "_ux" + OpName %50 "_uy" + OpName %48 "_ui" + OpName %63 "main" + OpName %65 "param" + OpName %68 "param" + OpName %73 "param" +-OpDecorate %4 Location 0 ++OpDecorate %4 Location 1 + OpDecorate %8 RelaxedPrecision +-OpDecorate %8 DescriptorSet 0 ++OpDecorate %8 DescriptorSet 2 + OpDecorate %8 Binding 0 +-OpDecorate %11 DescriptorSet 0 ++OpDecorate %11 DescriptorSet 3 +-OpDecorate %11 Binding 1 ++OpDecorate %11 Binding 0 + OpMemberDecorate %12 0 Offset 0 + OpMemberDecorate %12 0 RelaxedPrecision + OpDecorate %12 Block +-OpDecorate %14 DescriptorSet 0 ++OpDecorate %14 DescriptorSet 3 +-OpDecorate %14 Binding 2 ++OpDecorate %14 Binding 1 + OpMemberDecorate %15 0 Offset 0 + OpMemberDecorate %15 0 RelaxedPrecision + OpDecorate %15 BufferBlock +-OpDecorate %20 DescriptorSet 0 ++OpDecorate %20 DescriptorSet 3 +-OpDecorate %20 Binding 3 ++OpDecorate %20 Binding 2 + OpDecorate %22 RelaxedPrecision +-OpDecorate %22 Location 0 ++OpDecorate %22 Location 1 + OpMemberDecorate %26 0 Offset 0 + OpMemberDecorate %26 1 Offset 4 + OpMemberDecorate %26 2 Offset 8 + OpMemberDecorate %26 3 Offset 12 + OpMemberDecorate %27 0 Offset 0 + OpMemberDecorate %27 1 Offset 16 + OpMemberDecorate %27 2 Offset 20 + OpMemberDecorate %27 3 Offset 24 + OpMemberDecorate %27 4 Offset 28 + OpMemberDecorate %27 5 Offset 32 + OpMemberDecorate %27 6 Offset 48 + OpMemberDecorate %27 7 Offset 64 + OpMemberDecorate %27 2 RelaxedPrecision + OpMemberDecorate %27 4 RelaxedPrecision + OpDecorate %27 Block + OpDecorate %29 DescriptorSet 0 +-OpDecorate %29 Binding 4 ++OpDecorate %29 Binding 0 + OpDecorate %32 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %43 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %49 RelaxedPrecision + OpDecorate %50 RelaxedPrecision + OpDecorate %52 RelaxedPrecision + OpDecorate %53 RelaxedPrecision + OpDecorate %54 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %59 RelaxedPrecision + OpDecorate %60 RelaxedPrecision + OpDecorate %67 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %72 RelaxedPrecision + OpDecorate %73 RelaxedPrecision + OpDecorate %75 RelaxedPrecision + OpDecorate %76 RelaxedPrecision + OpDecorate %77 RelaxedPrecision + OpDecorate %80 RelaxedPrecision + OpDecorate %81 RelaxedPrecision + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeImage %1 2D 0 0 0 1 Unknown + %6 = OpTypeSampledImage %5 + %9 = OpTypeImage %1 2D 0 0 0 2 Rgba8 + %12 = OpTypeStruct %2 + %15 = OpTypeStruct %2 + %16 = OpTypeInt 32 0 + %17 = OpConstant %16 2 + %18 = OpTypeArray %15 %17 + %23 = OpTypeInt 32 1 + %24 = OpTypeVector %23 4 + %25 = OpTypeVector %16 4 + %26 = OpTypeStruct %1 %1 %1 %1 + %27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26 + %35 = OpTypeVector %1 2 + %40 = OpTypeVector %23 2 + %61 = OpTypeVoid + %69 = OpConstant %16 0 + %78 = OpConstant %16 1 ++%82 = OpTypePointer Private %2 + %3 = OpTypePointer Input %2 + %7 = OpTypePointer UniformConstant %6 + %10 = OpTypePointer UniformConstant %9 + %13 = OpTypePointer Uniform %12 + %19 = OpTypePointer Uniform %18 ++%83 = OpTypePointer Private %2 + %21 = OpTypePointer Output %2 + %28 = OpTypePointer Uniform %27 + %30 = OpTypePointer Function %2 + %70 = OpTypePointer Uniform %2 + %31 = OpTypeFunction %2 %30 + %47 = OpTypeFunction %2 %30 %30 + %62 = OpTypeFunction %61 + %4 = OpVariable %3 Input + %8 = OpVariable %7 UniformConstant + %11 = OpVariable %10 UniformConstant + %14 = OpVariable %13 Uniform + %20 = OpVariable %19 Uniform + %22 = OpVariable %21 Output + %29 = OpVariable %28 Uniform ++%84 = OpConstant %23 0 ++%85 = OpConstant %1 0.5 + %32 = OpFunction %2 None %31 + %33 = OpFunctionParameter %30 + %34 = OpLabel + %36 = OpLoad %6 %8 + %37 = OpLoad %2 %33 + %38 = OpVectorShuffle %35 %37 %37 0 1 + %39 = OpImageSampleImplicitLod %2 %36 %38 + %41 = OpLoad %2 %33 + %42 = OpVectorShuffle %35 %41 %41 2 3 + %43 = OpConvertFToS %40 %42 + %44 = OpLoad %9 %11 + %45 = OpImageRead %2 %44 %43 + %46 = OpFAdd %2 %39 %45 + OpReturnValue %46 + OpFunctionEnd + %48 = OpFunction %2 None %47 + %49 = OpFunctionParameter %30 + %50 = OpFunctionParameter %30 + %51 = OpLabel + %52 = OpLoad %2 %49 + %53 = OpVectorShuffle %35 %52 %52 0 1 + %54 = OpLoad %2 %50 + %55 = OpVectorShuffle %35 %54 %54 2 3 + %56 = OpCompositeExtract %1 %53 0 + %57 = OpCompositeExtract %1 %53 1 + %58 = OpCompositeExtract %1 %55 0 + %59 = OpCompositeExtract %1 %55 1 + %60 = OpCompositeConstruct %2 %56 %57 %58 %59 + OpReturnValue %60 + OpFunctionEnd + %63 = OpFunction %61 None %62 + %64 = OpLabel + %65 = OpVariable %30 Function + %68 = OpVariable %30 Function + %73 = OpVariable %30 Function + %66 = OpLoad %2 %4 + OpStore %65 %66 + %67 = OpFunctionCall %2 %32 %65 + %71 = OpAccessChain %70 %14 %69 + %72 = OpLoad %2 %71 + OpStore %68 %72 + %74 = OpAccessChain %70 %20 %69 %69 + %75 = OpLoad %2 %74 + OpStore %73 %75 + %76 = OpFunctionCall %2 %48 %68 %73 + %77 = OpFAdd %2 %67 %76 + %79 = OpAccessChain %70 %20 %78 %69 + %80 = OpLoad %2 %79 + %81 = OpFAdd %2 %77 %80 + OpStore %22 %81 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, DifferentDecorationsFragmentNoDebug) { + constexpr char kSrcNoDebug[] = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %63 "main" %4 %22 +OpExecutionMode %63 OriginUpperLeft +OpSource GLSL 450 +OpDecorate %4 Location 0 +OpDecorate %8 RelaxedPrecision +OpDecorate %8 DescriptorSet 0 +OpDecorate %8 Binding 0 +OpDecorate %11 DescriptorSet 0 +OpDecorate %11 Binding 1 +OpMemberDecorate %12 0 Offset 0 +OpMemberDecorate %12 0 RelaxedPrecision +OpDecorate %12 Block +OpDecorate %14 DescriptorSet 0 +OpDecorate %14 Binding 2 +OpMemberDecorate %15 0 Offset 0 +OpMemberDecorate %15 0 RelaxedPrecision +OpDecorate %15 BufferBlock +OpDecorate %20 DescriptorSet 0 +OpDecorate %20 Binding 3 +OpDecorate %22 RelaxedPrecision +OpDecorate %22 Location 0 +OpMemberDecorate %26 0 Offset 0 +OpMemberDecorate %26 1 Offset 4 +OpMemberDecorate %26 2 Offset 8 +OpMemberDecorate %26 3 Offset 12 +OpMemberDecorate %27 0 Offset 0 +OpMemberDecorate %27 1 Offset 16 +OpMemberDecorate %27 2 Offset 20 +OpMemberDecorate %27 3 Offset 24 +OpMemberDecorate %27 4 Offset 28 +OpMemberDecorate %27 5 Offset 32 +OpMemberDecorate %27 6 Offset 48 +OpMemberDecorate %27 7 Offset 64 +OpMemberDecorate %27 2 RelaxedPrecision +OpMemberDecorate %27 4 RelaxedPrecision +OpDecorate %27 Block +OpDecorate %29 DescriptorSet 0 +OpDecorate %29 Binding 4 +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %38 RelaxedPrecision +OpDecorate %39 RelaxedPrecision +OpDecorate %41 RelaxedPrecision +OpDecorate %42 RelaxedPrecision +OpDecorate %43 RelaxedPrecision +OpDecorate %48 RelaxedPrecision +OpDecorate %49 RelaxedPrecision +OpDecorate %50 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %53 RelaxedPrecision +OpDecorate %54 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +OpDecorate %57 RelaxedPrecision +OpDecorate %58 RelaxedPrecision +OpDecorate %59 RelaxedPrecision +OpDecorate %60 RelaxedPrecision +OpDecorate %67 RelaxedPrecision +OpDecorate %68 RelaxedPrecision +OpDecorate %72 RelaxedPrecision +OpDecorate %73 RelaxedPrecision +OpDecorate %75 RelaxedPrecision +OpDecorate %76 RelaxedPrecision +OpDecorate %77 RelaxedPrecision +OpDecorate %80 RelaxedPrecision +OpDecorate %81 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeImage %1 2D 0 0 0 1 Unknown +%6 = OpTypeSampledImage %5 +%9 = OpTypeImage %1 2D 0 0 0 2 Rgba8 +%12 = OpTypeStruct %2 +%15 = OpTypeStruct %2 +%16 = OpTypeInt 32 0 +%17 = OpConstant %16 2 +%18 = OpTypeArray %15 %17 +%23 = OpTypeInt 32 1 +%24 = OpTypeVector %23 4 +%25 = OpTypeVector %16 4 +%26 = OpTypeStruct %1 %1 %1 %1 +%27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26 +%35 = OpTypeVector %1 2 +%40 = OpTypeVector %23 2 +%61 = OpTypeVoid +%69 = OpConstant %16 0 +%78 = OpConstant %16 1 +%3 = OpTypePointer Input %2 +%7 = OpTypePointer UniformConstant %6 +%10 = OpTypePointer UniformConstant %9 +%13 = OpTypePointer Uniform %12 +%19 = OpTypePointer Uniform %18 +%21 = OpTypePointer Output %2 +%28 = OpTypePointer Uniform %27 +%30 = OpTypePointer Function %2 +%70 = OpTypePointer Uniform %2 +%31 = OpTypeFunction %2 %30 +%47 = OpTypeFunction %2 %30 %30 +%62 = OpTypeFunction %61 +%4 = OpVariable %3 Input +%8 = OpVariable %7 UniformConstant +%11 = OpVariable %10 UniformConstant +%14 = OpVariable %13 Uniform +%20 = OpVariable %19 Uniform +%22 = OpVariable %21 Output +%29 = OpVariable %28 Uniform +%32 = OpFunction %2 None %31 +%33 = OpFunctionParameter %30 +%34 = OpLabel +%36 = OpLoad %6 %8 +%37 = OpLoad %2 %33 +%38 = OpVectorShuffle %35 %37 %37 0 1 +%39 = OpImageSampleImplicitLod %2 %36 %38 +%41 = OpLoad %2 %33 +%42 = OpVectorShuffle %35 %41 %41 2 3 +%43 = OpConvertFToS %40 %42 +%44 = OpLoad %9 %11 +%45 = OpImageRead %2 %44 %43 +%46 = OpFAdd %2 %39 %45 +OpReturnValue %46 +OpFunctionEnd +%48 = OpFunction %2 None %47 +%49 = OpFunctionParameter %30 +%50 = OpFunctionParameter %30 +%51 = OpLabel +%52 = OpLoad %2 %49 +%53 = OpVectorShuffle %35 %52 %52 0 1 +%54 = OpLoad %2 %50 +%55 = OpVectorShuffle %35 %54 %54 2 3 +%56 = OpCompositeExtract %1 %53 0 +%57 = OpCompositeExtract %1 %53 1 +%58 = OpCompositeExtract %1 %55 0 +%59 = OpCompositeExtract %1 %55 1 +%60 = OpCompositeConstruct %2 %56 %57 %58 %59 +OpReturnValue %60 +OpFunctionEnd +%63 = OpFunction %61 None %62 +%64 = OpLabel +%65 = OpVariable %30 Function +%68 = OpVariable %30 Function +%73 = OpVariable %30 Function +%66 = OpLoad %2 %4 +OpStore %65 %66 +%67 = OpFunctionCall %2 %32 %65 +%71 = OpAccessChain %70 %14 %69 +%72 = OpLoad %2 %71 +OpStore %68 %72 +%74 = OpAccessChain %70 %20 %69 %69 +%75 = OpLoad %2 %74 +OpStore %73 %75 +%76 = OpFunctionCall %2 %48 %68 %73 +%77 = OpFAdd %2 %67 %76 +%79 = OpAccessChain %70 %20 %78 %69 +%80 = OpLoad %2 %79 +%81 = OpFAdd %2 %77 %80 +OpStore %22 %81 +OpReturn +OpFunctionEnd + +)"; + constexpr char kDstNoDebug[] = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %63 "main" %4 %22 +OpExecutionMode %63 OriginUpperLeft +OpSource GLSL 450 +OpDecorate %4 Location 1 +OpDecorate %8 RelaxedPrecision +OpDecorate %8 DescriptorSet 2 +OpDecorate %8 Binding 0 +OpDecorate %11 DescriptorSet 3 +OpDecorate %11 Binding 0 +OpMemberDecorate %12 0 Offset 0 +OpMemberDecorate %12 0 RelaxedPrecision +OpDecorate %12 Block +OpDecorate %14 DescriptorSet 3 +OpDecorate %14 Binding 1 +OpMemberDecorate %15 0 Offset 0 +OpMemberDecorate %15 0 RelaxedPrecision +OpDecorate %15 BufferBlock +OpDecorate %20 DescriptorSet 3 +OpDecorate %20 Binding 2 +OpDecorate %22 RelaxedPrecision +OpDecorate %22 Location 1 +OpMemberDecorate %26 0 Offset 0 +OpMemberDecorate %26 1 Offset 4 +OpMemberDecorate %26 2 Offset 8 +OpMemberDecorate %26 3 Offset 12 +OpMemberDecorate %27 0 Offset 0 +OpMemberDecorate %27 1 Offset 16 +OpMemberDecorate %27 2 Offset 20 +OpMemberDecorate %27 3 Offset 24 +OpMemberDecorate %27 4 Offset 28 +OpMemberDecorate %27 5 Offset 32 +OpMemberDecorate %27 6 Offset 48 +OpMemberDecorate %27 7 Offset 64 +OpMemberDecorate %27 2 RelaxedPrecision +OpMemberDecorate %27 4 RelaxedPrecision +OpDecorate %27 Block +OpDecorate %29 DescriptorSet 0 +OpDecorate %29 Binding 0 +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %38 RelaxedPrecision +OpDecorate %39 RelaxedPrecision +OpDecorate %41 RelaxedPrecision +OpDecorate %42 RelaxedPrecision +OpDecorate %43 RelaxedPrecision +OpDecorate %48 RelaxedPrecision +OpDecorate %49 RelaxedPrecision +OpDecorate %50 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %53 RelaxedPrecision +OpDecorate %54 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +OpDecorate %57 RelaxedPrecision +OpDecorate %58 RelaxedPrecision +OpDecorate %59 RelaxedPrecision +OpDecorate %60 RelaxedPrecision +OpDecorate %67 RelaxedPrecision +OpDecorate %68 RelaxedPrecision +OpDecorate %72 RelaxedPrecision +OpDecorate %73 RelaxedPrecision +OpDecorate %75 RelaxedPrecision +OpDecorate %76 RelaxedPrecision +OpDecorate %77 RelaxedPrecision +OpDecorate %80 RelaxedPrecision +OpDecorate %81 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeImage %1 2D 0 0 0 1 Unknown +%6 = OpTypeSampledImage %5 +%9 = OpTypeImage %1 2D 0 0 0 2 Rgba8 +%12 = OpTypeStruct %2 +%15 = OpTypeStruct %2 +%16 = OpTypeInt 32 0 +%17 = OpConstant %16 2 +%18 = OpTypeArray %15 %17 +%23 = OpTypeInt 32 1 +%24 = OpTypeVector %23 4 +%25 = OpTypeVector %16 4 +%26 = OpTypeStruct %1 %1 %1 %1 +%27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26 +%35 = OpTypeVector %1 2 +%40 = OpTypeVector %23 2 +%61 = OpTypeVoid +%69 = OpConstant %16 0 +%78 = OpConstant %16 1 +%82 = OpTypePointer Private %2 +%3 = OpTypePointer Input %2 +%7 = OpTypePointer UniformConstant %6 +%10 = OpTypePointer UniformConstant %9 +%13 = OpTypePointer Uniform %12 +%19 = OpTypePointer Uniform %18 +%83 = OpTypePointer Private %2 +%21 = OpTypePointer Output %2 +%28 = OpTypePointer Uniform %27 +%30 = OpTypePointer Function %2 +%70 = OpTypePointer Uniform %2 +%31 = OpTypeFunction %2 %30 +%47 = OpTypeFunction %2 %30 %30 +%62 = OpTypeFunction %61 +%4 = OpVariable %3 Input +%8 = OpVariable %7 UniformConstant +%11 = OpVariable %10 UniformConstant +%14 = OpVariable %13 Uniform +%20 = OpVariable %19 Uniform +%22 = OpVariable %21 Output +%29 = OpVariable %28 Uniform +%84 = OpConstant %23 0 +%85 = OpConstant %1 0.5 +%32 = OpFunction %2 None %31 +%33 = OpFunctionParameter %30 +%34 = OpLabel +%36 = OpLoad %6 %8 +%37 = OpLoad %2 %33 +%38 = OpVectorShuffle %35 %37 %37 0 1 +%39 = OpImageSampleImplicitLod %2 %36 %38 +%41 = OpLoad %2 %33 +%42 = OpVectorShuffle %35 %41 %41 2 3 +%43 = OpConvertFToS %40 %42 +%44 = OpLoad %9 %11 +%45 = OpImageRead %2 %44 %43 +%46 = OpFAdd %2 %39 %45 +OpReturnValue %46 +OpFunctionEnd +%48 = OpFunction %2 None %47 +%49 = OpFunctionParameter %30 +%50 = OpFunctionParameter %30 +%51 = OpLabel +%52 = OpLoad %2 %49 +%53 = OpVectorShuffle %35 %52 %52 0 1 +%54 = OpLoad %2 %50 +%55 = OpVectorShuffle %35 %54 %54 2 3 +%56 = OpCompositeExtract %1 %53 0 +%57 = OpCompositeExtract %1 %53 1 +%58 = OpCompositeExtract %1 %55 0 +%59 = OpCompositeExtract %1 %55 1 +%60 = OpCompositeConstruct %2 %56 %57 %58 %59 +OpReturnValue %60 +OpFunctionEnd +%63 = OpFunction %61 None %62 +%64 = OpLabel +%65 = OpVariable %30 Function +%68 = OpVariable %30 Function +%73 = OpVariable %30 Function +%66 = OpLoad %2 %4 +OpStore %65 %66 +%67 = OpFunctionCall %2 %32 %65 +%71 = OpAccessChain %70 %14 %69 +%72 = OpLoad %2 %71 +OpStore %68 %72 +%74 = OpAccessChain %70 %20 %69 %69 +%75 = OpLoad %2 %74 +OpStore %73 %75 +%76 = OpFunctionCall %2 %48 %68 %73 +%77 = OpFAdd %2 %67 %76 +%79 = OpAccessChain %70 %20 %78 %69 +%80 = OpLoad %2 %79 +%81 = OpFAdd %2 %77 %80 +OpStore %22 %81 +OpReturn +OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 82 ++; Bound: 92 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %63 "main" %4 %22 + OpExecutionMode %63 OriginUpperLeft + OpSource GLSL 450 +-OpDecorate %4 Location 0 ++OpDecorate %4 Location 1 + OpDecorate %8 RelaxedPrecision +-OpDecorate %8 DescriptorSet 0 ++OpDecorate %8 DescriptorSet 2 + OpDecorate %8 Binding 0 +-OpDecorate %11 DescriptorSet 0 ++OpDecorate %11 DescriptorSet 3 +-OpDecorate %11 Binding 1 ++OpDecorate %11 Binding 0 + OpMemberDecorate %12 0 Offset 0 + OpMemberDecorate %12 0 RelaxedPrecision + OpDecorate %12 Block ++OpDecorate %82 DescriptorSet 3 ++OpDecorate %82 Binding 1 +-OpDecorate %14 DescriptorSet 0 ++OpDecorate %14 DescriptorSet 3 + OpDecorate %14 Binding 2 + OpMemberDecorate %15 0 Offset 0 + OpMemberDecorate %15 0 RelaxedPrecision + OpDecorate %15 BufferBlock +-OpDecorate %20 DescriptorSet 0 +-OpDecorate %20 Binding 3 + OpDecorate %22 RelaxedPrecision +-OpDecorate %22 Location 0 ++OpDecorate %22 Location 1 + OpMemberDecorate %26 0 Offset 0 + OpMemberDecorate %26 1 Offset 4 + OpMemberDecorate %26 2 Offset 8 + OpMemberDecorate %26 3 Offset 12 + OpMemberDecorate %27 0 Offset 0 + OpMemberDecorate %27 1 Offset 16 + OpMemberDecorate %27 2 Offset 20 + OpMemberDecorate %27 3 Offset 24 + OpMemberDecorate %27 4 Offset 28 + OpMemberDecorate %27 5 Offset 32 + OpMemberDecorate %27 6 Offset 48 + OpMemberDecorate %27 7 Offset 64 + OpMemberDecorate %27 2 RelaxedPrecision + OpMemberDecorate %27 4 RelaxedPrecision + OpDecorate %27 Block +-OpDecorate %29 DescriptorSet 0 +-OpDecorate %29 Binding 4 ++OpDecorate %83 DescriptorSet 0 ++OpDecorate %83 Binding 0 + OpDecorate %32 RelaxedPrecision +-OpDecorate %33 RelaxedPrecision ++OpDecorate %84 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %43 RelaxedPrecision + OpDecorate %48 RelaxedPrecision +-OpDecorate %49 RelaxedPrecision +-OpDecorate %50 RelaxedPrecision ++OpDecorate %85 RelaxedPrecision ++OpDecorate %86 RelaxedPrecision + OpDecorate %52 RelaxedPrecision + OpDecorate %53 RelaxedPrecision + OpDecorate %54 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %59 RelaxedPrecision + OpDecorate %60 RelaxedPrecision + OpDecorate %67 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %72 RelaxedPrecision + OpDecorate %73 RelaxedPrecision + OpDecorate %75 RelaxedPrecision + OpDecorate %76 RelaxedPrecision + OpDecorate %77 RelaxedPrecision + OpDecorate %80 RelaxedPrecision + OpDecorate %81 RelaxedPrecision + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeImage %1 2D 0 0 0 1 Unknown + %6 = OpTypeSampledImage %5 + %9 = OpTypeImage %1 2D 0 0 0 2 Rgba8 + %12 = OpTypeStruct %2 + %15 = OpTypeStruct %2 + %16 = OpTypeInt 32 0 + %17 = OpConstant %16 2 + %18 = OpTypeArray %15 %17 + %23 = OpTypeInt 32 1 + %24 = OpTypeVector %23 4 + %25 = OpTypeVector %16 4 + %26 = OpTypeStruct %1 %1 %1 %1 + %27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26 + %35 = OpTypeVector %1 2 + %40 = OpTypeVector %23 2 + %61 = OpTypeVoid + %69 = OpConstant %16 0 + %78 = OpConstant %16 1 ++%88 = OpTypePointer Private %2 + %3 = OpTypePointer Input %2 + %7 = OpTypePointer UniformConstant %6 + %10 = OpTypePointer UniformConstant %9 + %13 = OpTypePointer Uniform %12 + %19 = OpTypePointer Uniform %18 ++%89 = OpTypePointer Private %2 + %21 = OpTypePointer Output %2 + %28 = OpTypePointer Uniform %27 + %30 = OpTypePointer Function %2 + %70 = OpTypePointer Uniform %2 + %31 = OpTypeFunction %2 %30 + %47 = OpTypeFunction %2 %30 %30 + %62 = OpTypeFunction %61 + %4 = OpVariable %3 Input + %8 = OpVariable %7 UniformConstant + %11 = OpVariable %10 UniformConstant ++%82 = OpVariable %13 Uniform +-%14 = OpVariable %13 Uniform ++%14 = OpVariable %19 Uniform +-%20 = OpVariable %19 Uniform + %22 = OpVariable %21 Output +-%29 = OpVariable %28 Uniform ++%83 = OpVariable %28 Uniform ++%90 = OpConstant %23 0 ++%91 = OpConstant %1 0.5 + %32 = OpFunction %2 None %31 +-%33 = OpFunctionParameter %30 ++%84 = OpFunctionParameter %30 + %34 = OpLabel + %36 = OpLoad %6 %8 +-%37 = OpLoad %2 %33 ++%37 = OpLoad %2 %84 + %38 = OpVectorShuffle %35 %37 %37 0 1 + %39 = OpImageSampleImplicitLod %2 %36 %38 +-%41 = OpLoad %2 %33 ++%41 = OpLoad %2 %84 + %42 = OpVectorShuffle %35 %41 %41 2 3 + %43 = OpConvertFToS %40 %42 + %44 = OpLoad %9 %11 + %45 = OpImageRead %2 %44 %43 + %46 = OpFAdd %2 %39 %45 + OpReturnValue %46 + OpFunctionEnd + %48 = OpFunction %2 None %47 +-%49 = OpFunctionParameter %30 +-%50 = OpFunctionParameter %30 ++%85 = OpFunctionParameter %30 ++%86 = OpFunctionParameter %30 + %51 = OpLabel +-%52 = OpLoad %2 %49 ++%52 = OpLoad %2 %85 + %53 = OpVectorShuffle %35 %52 %52 0 1 +-%54 = OpLoad %2 %50 ++%54 = OpLoad %2 %86 + %55 = OpVectorShuffle %35 %54 %54 2 3 + %56 = OpCompositeExtract %1 %53 0 + %57 = OpCompositeExtract %1 %53 1 + %58 = OpCompositeExtract %1 %55 0 + %59 = OpCompositeExtract %1 %55 1 + %60 = OpCompositeConstruct %2 %56 %57 %58 %59 + OpReturnValue %60 + OpFunctionEnd + %63 = OpFunction %61 None %62 + %64 = OpLabel + %65 = OpVariable %30 Function + %68 = OpVariable %30 Function + %73 = OpVariable %30 Function + %66 = OpLoad %2 %4 + OpStore %65 %66 + %67 = OpFunctionCall %2 %32 %65 +-%71 = OpAccessChain %70 %14 %69 ++%87 = OpAccessChain %70 %82 %69 +-%72 = OpLoad %2 %71 ++%72 = OpLoad %2 %87 + OpStore %68 %72 +-%74 = OpAccessChain %70 %20 %69 %69 ++%74 = OpAccessChain %70 %14 %69 %69 + %75 = OpLoad %2 %74 + OpStore %73 %75 + %76 = OpFunctionCall %2 %48 %68 %73 + %77 = OpFAdd %2 %67 %76 +-%79 = OpAccessChain %70 %20 %78 %69 ++%79 = OpAccessChain %70 %14 %78 %69 + %80 = OpLoad %2 %79 + %81 = OpFAdd %2 %77 %80 + OpStore %22 %81 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +TEST(DiffTest, DifferentDecorationsFragmentIgnoreLocation) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 82 ++; Bound: 86 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %63 "main" %4 %22 + OpExecutionMode %63 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "_ue" + OpName %8 "_uf" + OpName %11 "_ug" + OpName %12 "_uA" + OpMemberName %12 0 "_ux" + OpName %14 "_uc" + OpName %15 "_uB" + OpMemberName %15 0 "_ux" + OpName %20 "_ud" + OpName %22 "_ucol" + OpName %26 "ANGLEDepthRangeParams" + OpMemberName %26 0 "near" + OpMemberName %26 1 "far" + OpMemberName %26 2 "diff" + OpMemberName %26 3 "reserved" + OpName %27 "ANGLEUniformBlock" + OpMemberName %27 0 "viewport" + OpMemberName %27 1 "clipDistancesEnabled" + OpMemberName %27 2 "xfbActiveUnpaused" + OpMemberName %27 3 "xfbVerticesPerInstance" + OpMemberName %27 4 "numSamples" + OpMemberName %27 5 "xfbBufferOffsets" + OpMemberName %27 6 "acbBufferOffsets" + OpMemberName %27 7 "depthRange" + OpName %29 "ANGLEUniforms" + OpName %33 "_uc" + OpName %32 "_uh" + OpName %49 "_ux" + OpName %50 "_uy" + OpName %48 "_ui" + OpName %63 "main" + OpName %65 "param" + OpName %68 "param" + OpName %73 "param" +-OpDecorate %4 Location 0 ++OpDecorate %4 Location 1 + OpDecorate %8 RelaxedPrecision +-OpDecorate %8 DescriptorSet 0 ++OpDecorate %8 DescriptorSet 2 + OpDecorate %8 Binding 0 +-OpDecorate %11 DescriptorSet 0 ++OpDecorate %11 DescriptorSet 3 +-OpDecorate %11 Binding 1 ++OpDecorate %11 Binding 0 + OpMemberDecorate %12 0 Offset 0 + OpMemberDecorate %12 0 RelaxedPrecision + OpDecorate %12 Block +-OpDecorate %14 DescriptorSet 0 ++OpDecorate %14 DescriptorSet 3 +-OpDecorate %14 Binding 2 ++OpDecorate %14 Binding 1 + OpMemberDecorate %15 0 Offset 0 + OpMemberDecorate %15 0 RelaxedPrecision + OpDecorate %15 BufferBlock +-OpDecorate %20 DescriptorSet 0 ++OpDecorate %20 DescriptorSet 3 +-OpDecorate %20 Binding 3 ++OpDecorate %20 Binding 2 + OpDecorate %22 RelaxedPrecision +-OpDecorate %22 Location 0 ++OpDecorate %22 Location 1 + OpMemberDecorate %26 0 Offset 0 + OpMemberDecorate %26 1 Offset 4 + OpMemberDecorate %26 2 Offset 8 + OpMemberDecorate %26 3 Offset 12 + OpMemberDecorate %27 0 Offset 0 + OpMemberDecorate %27 1 Offset 16 + OpMemberDecorate %27 2 Offset 20 + OpMemberDecorate %27 3 Offset 24 + OpMemberDecorate %27 4 Offset 28 + OpMemberDecorate %27 5 Offset 32 + OpMemberDecorate %27 6 Offset 48 + OpMemberDecorate %27 7 Offset 64 + OpMemberDecorate %27 2 RelaxedPrecision + OpMemberDecorate %27 4 RelaxedPrecision + OpDecorate %27 Block + OpDecorate %29 DescriptorSet 0 +-OpDecorate %29 Binding 4 ++OpDecorate %29 Binding 0 + OpDecorate %32 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %43 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %49 RelaxedPrecision + OpDecorate %50 RelaxedPrecision + OpDecorate %52 RelaxedPrecision + OpDecorate %53 RelaxedPrecision + OpDecorate %54 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %59 RelaxedPrecision + OpDecorate %60 RelaxedPrecision + OpDecorate %67 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %72 RelaxedPrecision + OpDecorate %73 RelaxedPrecision + OpDecorate %75 RelaxedPrecision + OpDecorate %76 RelaxedPrecision + OpDecorate %77 RelaxedPrecision + OpDecorate %80 RelaxedPrecision + OpDecorate %81 RelaxedPrecision + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeImage %1 2D 0 0 0 1 Unknown + %6 = OpTypeSampledImage %5 + %9 = OpTypeImage %1 2D 0 0 0 2 Rgba8 + %12 = OpTypeStruct %2 + %15 = OpTypeStruct %2 + %16 = OpTypeInt 32 0 + %17 = OpConstant %16 2 + %18 = OpTypeArray %15 %17 + %23 = OpTypeInt 32 1 + %24 = OpTypeVector %23 4 + %25 = OpTypeVector %16 4 + %26 = OpTypeStruct %1 %1 %1 %1 + %27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26 + %35 = OpTypeVector %1 2 + %40 = OpTypeVector %23 2 + %61 = OpTypeVoid + %69 = OpConstant %16 0 + %78 = OpConstant %16 1 ++%82 = OpTypePointer Private %2 + %3 = OpTypePointer Input %2 + %7 = OpTypePointer UniformConstant %6 + %10 = OpTypePointer UniformConstant %9 + %13 = OpTypePointer Uniform %12 + %19 = OpTypePointer Uniform %18 ++%83 = OpTypePointer Private %2 + %21 = OpTypePointer Output %2 + %28 = OpTypePointer Uniform %27 + %30 = OpTypePointer Function %2 + %70 = OpTypePointer Uniform %2 + %31 = OpTypeFunction %2 %30 + %47 = OpTypeFunction %2 %30 %30 + %62 = OpTypeFunction %61 + %4 = OpVariable %3 Input + %8 = OpVariable %7 UniformConstant + %11 = OpVariable %10 UniformConstant + %14 = OpVariable %13 Uniform + %20 = OpVariable %19 Uniform + %22 = OpVariable %21 Output + %29 = OpVariable %28 Uniform ++%84 = OpConstant %23 0 ++%85 = OpConstant %1 0.5 + %32 = OpFunction %2 None %31 + %33 = OpFunctionParameter %30 + %34 = OpLabel + %36 = OpLoad %6 %8 + %37 = OpLoad %2 %33 + %38 = OpVectorShuffle %35 %37 %37 0 1 + %39 = OpImageSampleImplicitLod %2 %36 %38 + %41 = OpLoad %2 %33 + %42 = OpVectorShuffle %35 %41 %41 2 3 + %43 = OpConvertFToS %40 %42 + %44 = OpLoad %9 %11 + %45 = OpImageRead %2 %44 %43 + %46 = OpFAdd %2 %39 %45 + OpReturnValue %46 + OpFunctionEnd + %48 = OpFunction %2 None %47 + %49 = OpFunctionParameter %30 + %50 = OpFunctionParameter %30 + %51 = OpLabel + %52 = OpLoad %2 %49 + %53 = OpVectorShuffle %35 %52 %52 0 1 + %54 = OpLoad %2 %50 + %55 = OpVectorShuffle %35 %54 %54 2 3 + %56 = OpCompositeExtract %1 %53 0 + %57 = OpCompositeExtract %1 %53 1 + %58 = OpCompositeExtract %1 %55 0 + %59 = OpCompositeExtract %1 %55 1 + %60 = OpCompositeConstruct %2 %56 %57 %58 %59 + OpReturnValue %60 + OpFunctionEnd + %63 = OpFunction %61 None %62 + %64 = OpLabel + %65 = OpVariable %30 Function + %68 = OpVariable %30 Function + %73 = OpVariable %30 Function + %66 = OpLoad %2 %4 + OpStore %65 %66 + %67 = OpFunctionCall %2 %32 %65 + %71 = OpAccessChain %70 %14 %69 + %72 = OpLoad %2 %71 + OpStore %68 %72 + %74 = OpAccessChain %70 %20 %69 %69 + %75 = OpLoad %2 %74 + OpStore %73 %75 + %76 = OpFunctionCall %2 %48 %68 %73 + %77 = OpFAdd %2 %67 %76 + %79 = OpAccessChain %70 %20 %78 %69 + %80 = OpLoad %2 %79 + %81 = OpFAdd %2 %77 %80 + OpStore %22 %81 + OpReturn + OpFunctionEnd +)"; + Options options; + options.ignore_location = true; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, DifferentDecorationsFragmentIgnoreSetBindingLocation) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 82 ++; Bound: 86 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %63 "main" %4 %22 + OpExecutionMode %63 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "_ue" + OpName %8 "_uf" + OpName %11 "_ug" + OpName %12 "_uA" + OpMemberName %12 0 "_ux" + OpName %14 "_uc" + OpName %15 "_uB" + OpMemberName %15 0 "_ux" + OpName %20 "_ud" + OpName %22 "_ucol" + OpName %26 "ANGLEDepthRangeParams" + OpMemberName %26 0 "near" + OpMemberName %26 1 "far" + OpMemberName %26 2 "diff" + OpMemberName %26 3 "reserved" + OpName %27 "ANGLEUniformBlock" + OpMemberName %27 0 "viewport" + OpMemberName %27 1 "clipDistancesEnabled" + OpMemberName %27 2 "xfbActiveUnpaused" + OpMemberName %27 3 "xfbVerticesPerInstance" + OpMemberName %27 4 "numSamples" + OpMemberName %27 5 "xfbBufferOffsets" + OpMemberName %27 6 "acbBufferOffsets" + OpMemberName %27 7 "depthRange" + OpName %29 "ANGLEUniforms" + OpName %33 "_uc" + OpName %32 "_uh" + OpName %49 "_ux" + OpName %50 "_uy" + OpName %48 "_ui" + OpName %63 "main" + OpName %65 "param" + OpName %68 "param" + OpName %73 "param" +-OpDecorate %4 Location 0 ++OpDecorate %4 Location 1 + OpDecorate %8 RelaxedPrecision +-OpDecorate %8 DescriptorSet 0 ++OpDecorate %8 DescriptorSet 2 + OpDecorate %8 Binding 0 +-OpDecorate %11 DescriptorSet 0 ++OpDecorate %11 DescriptorSet 3 +-OpDecorate %11 Binding 1 ++OpDecorate %11 Binding 0 + OpMemberDecorate %12 0 Offset 0 + OpMemberDecorate %12 0 RelaxedPrecision + OpDecorate %12 Block +-OpDecorate %14 DescriptorSet 0 ++OpDecorate %14 DescriptorSet 3 +-OpDecorate %14 Binding 2 ++OpDecorate %14 Binding 1 + OpMemberDecorate %15 0 Offset 0 + OpMemberDecorate %15 0 RelaxedPrecision + OpDecorate %15 BufferBlock +-OpDecorate %20 DescriptorSet 0 ++OpDecorate %20 DescriptorSet 3 +-OpDecorate %20 Binding 3 ++OpDecorate %20 Binding 2 + OpDecorate %22 RelaxedPrecision +-OpDecorate %22 Location 0 ++OpDecorate %22 Location 1 + OpMemberDecorate %26 0 Offset 0 + OpMemberDecorate %26 1 Offset 4 + OpMemberDecorate %26 2 Offset 8 + OpMemberDecorate %26 3 Offset 12 + OpMemberDecorate %27 0 Offset 0 + OpMemberDecorate %27 1 Offset 16 + OpMemberDecorate %27 2 Offset 20 + OpMemberDecorate %27 3 Offset 24 + OpMemberDecorate %27 4 Offset 28 + OpMemberDecorate %27 5 Offset 32 + OpMemberDecorate %27 6 Offset 48 + OpMemberDecorate %27 7 Offset 64 + OpMemberDecorate %27 2 RelaxedPrecision + OpMemberDecorate %27 4 RelaxedPrecision + OpDecorate %27 Block + OpDecorate %29 DescriptorSet 0 +-OpDecorate %29 Binding 4 ++OpDecorate %29 Binding 0 + OpDecorate %32 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %43 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %49 RelaxedPrecision + OpDecorate %50 RelaxedPrecision + OpDecorate %52 RelaxedPrecision + OpDecorate %53 RelaxedPrecision + OpDecorate %54 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %59 RelaxedPrecision + OpDecorate %60 RelaxedPrecision + OpDecorate %67 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %72 RelaxedPrecision + OpDecorate %73 RelaxedPrecision + OpDecorate %75 RelaxedPrecision + OpDecorate %76 RelaxedPrecision + OpDecorate %77 RelaxedPrecision + OpDecorate %80 RelaxedPrecision + OpDecorate %81 RelaxedPrecision + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeImage %1 2D 0 0 0 1 Unknown + %6 = OpTypeSampledImage %5 + %9 = OpTypeImage %1 2D 0 0 0 2 Rgba8 + %12 = OpTypeStruct %2 + %15 = OpTypeStruct %2 + %16 = OpTypeInt 32 0 + %17 = OpConstant %16 2 + %18 = OpTypeArray %15 %17 + %23 = OpTypeInt 32 1 + %24 = OpTypeVector %23 4 + %25 = OpTypeVector %16 4 + %26 = OpTypeStruct %1 %1 %1 %1 + %27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26 + %35 = OpTypeVector %1 2 + %40 = OpTypeVector %23 2 + %61 = OpTypeVoid + %69 = OpConstant %16 0 + %78 = OpConstant %16 1 ++%82 = OpTypePointer Private %2 + %3 = OpTypePointer Input %2 + %7 = OpTypePointer UniformConstant %6 + %10 = OpTypePointer UniformConstant %9 + %13 = OpTypePointer Uniform %12 + %19 = OpTypePointer Uniform %18 ++%83 = OpTypePointer Private %2 + %21 = OpTypePointer Output %2 + %28 = OpTypePointer Uniform %27 + %30 = OpTypePointer Function %2 + %70 = OpTypePointer Uniform %2 + %31 = OpTypeFunction %2 %30 + %47 = OpTypeFunction %2 %30 %30 + %62 = OpTypeFunction %61 + %4 = OpVariable %3 Input + %8 = OpVariable %7 UniformConstant + %11 = OpVariable %10 UniformConstant + %14 = OpVariable %13 Uniform + %20 = OpVariable %19 Uniform + %22 = OpVariable %21 Output + %29 = OpVariable %28 Uniform ++%84 = OpConstant %23 0 ++%85 = OpConstant %1 0.5 + %32 = OpFunction %2 None %31 + %33 = OpFunctionParameter %30 + %34 = OpLabel + %36 = OpLoad %6 %8 + %37 = OpLoad %2 %33 + %38 = OpVectorShuffle %35 %37 %37 0 1 + %39 = OpImageSampleImplicitLod %2 %36 %38 + %41 = OpLoad %2 %33 + %42 = OpVectorShuffle %35 %41 %41 2 3 + %43 = OpConvertFToS %40 %42 + %44 = OpLoad %9 %11 + %45 = OpImageRead %2 %44 %43 + %46 = OpFAdd %2 %39 %45 + OpReturnValue %46 + OpFunctionEnd + %48 = OpFunction %2 None %47 + %49 = OpFunctionParameter %30 + %50 = OpFunctionParameter %30 + %51 = OpLabel + %52 = OpLoad %2 %49 + %53 = OpVectorShuffle %35 %52 %52 0 1 + %54 = OpLoad %2 %50 + %55 = OpVectorShuffle %35 %54 %54 2 3 + %56 = OpCompositeExtract %1 %53 0 + %57 = OpCompositeExtract %1 %53 1 + %58 = OpCompositeExtract %1 %55 0 + %59 = OpCompositeExtract %1 %55 1 + %60 = OpCompositeConstruct %2 %56 %57 %58 %59 + OpReturnValue %60 + OpFunctionEnd + %63 = OpFunction %61 None %62 + %64 = OpLabel + %65 = OpVariable %30 Function + %68 = OpVariable %30 Function + %73 = OpVariable %30 Function + %66 = OpLoad %2 %4 + OpStore %65 %66 + %67 = OpFunctionCall %2 %32 %65 + %71 = OpAccessChain %70 %14 %69 + %72 = OpLoad %2 %71 + OpStore %68 %72 + %74 = OpAccessChain %70 %20 %69 %69 + %75 = OpLoad %2 %74 + OpStore %73 %75 + %76 = OpFunctionCall %2 %48 %68 %73 + %77 = OpFAdd %2 %67 %76 + %79 = OpAccessChain %70 %20 %78 %69 + %80 = OpLoad %2 %79 + %81 = OpFAdd %2 %77 %80 + OpStore %22 %81 + OpReturn + OpFunctionEnd +)"; + Options options; + options.ignore_set_binding = true; + options.ignore_location = true; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/different_decorations_fragment_dst.spvasm b/test/diff/diff_files/different_decorations_fragment_dst.spvasm new file mode 100644 index 00000000..b5d1c9f7 --- /dev/null +++ b/test/diff/diff_files/different_decorations_fragment_dst.spvasm @@ -0,0 +1,199 @@ +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %63 "main" %4 %22 +OpExecutionMode %63 OriginUpperLeft +OpSource GLSL 450 +OpName %4 "_ue" +OpName %8 "_uf" +OpName %11 "_ug" +OpName %12 "_uA" +OpMemberName %12 0 "_ux" +OpName %14 "_uc" +OpName %15 "_uB" +OpMemberName %15 0 "_ux" +OpName %20 "_ud" +OpName %22 "_ucol" +OpName %26 "ANGLEDepthRangeParams" +OpMemberName %26 0 "near" +OpMemberName %26 1 "far" +OpMemberName %26 2 "diff" +OpMemberName %26 3 "reserved" +OpName %27 "ANGLEUniformBlock" +OpMemberName %27 0 "viewport" +OpMemberName %27 1 "clipDistancesEnabled" +OpMemberName %27 2 "xfbActiveUnpaused" +OpMemberName %27 3 "xfbVerticesPerInstance" +OpMemberName %27 4 "numSamples" +OpMemberName %27 5 "xfbBufferOffsets" +OpMemberName %27 6 "acbBufferOffsets" +OpMemberName %27 7 "depthRange" +OpName %29 "ANGLEUniforms" +OpName %33 "_uc" +OpName %32 "_uh" +OpName %49 "_ux" +OpName %50 "_uy" +OpName %48 "_ui" +OpName %63 "main" +OpName %65 "param" +OpName %68 "param" +OpName %73 "param" +OpDecorate %4 Location 1 +OpDecorate %8 RelaxedPrecision +OpDecorate %8 DescriptorSet 2 +OpDecorate %8 Binding 0 +OpDecorate %11 DescriptorSet 3 +OpDecorate %11 Binding 0 +OpMemberDecorate %12 0 Offset 0 +OpMemberDecorate %12 0 RelaxedPrecision +OpDecorate %12 Block +OpDecorate %14 DescriptorSet 3 +OpDecorate %14 Binding 1 +OpMemberDecorate %15 0 Offset 0 +OpMemberDecorate %15 0 RelaxedPrecision +OpDecorate %15 BufferBlock +OpDecorate %20 DescriptorSet 3 +OpDecorate %20 Binding 2 +OpDecorate %22 RelaxedPrecision +OpDecorate %22 Location 1 +OpMemberDecorate %26 0 Offset 0 +OpMemberDecorate %26 1 Offset 4 +OpMemberDecorate %26 2 Offset 8 +OpMemberDecorate %26 3 Offset 12 +OpMemberDecorate %27 0 Offset 0 +OpMemberDecorate %27 1 Offset 16 +OpMemberDecorate %27 2 Offset 20 +OpMemberDecorate %27 3 Offset 24 +OpMemberDecorate %27 4 Offset 28 +OpMemberDecorate %27 5 Offset 32 +OpMemberDecorate %27 6 Offset 48 +OpMemberDecorate %27 7 Offset 64 +OpMemberDecorate %27 2 RelaxedPrecision +OpMemberDecorate %27 4 RelaxedPrecision +OpDecorate %27 Block +OpDecorate %29 DescriptorSet 0 +OpDecorate %29 Binding 0 +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %38 RelaxedPrecision +OpDecorate %39 RelaxedPrecision +OpDecorate %41 RelaxedPrecision +OpDecorate %42 RelaxedPrecision +OpDecorate %43 RelaxedPrecision +OpDecorate %48 RelaxedPrecision +OpDecorate %49 RelaxedPrecision +OpDecorate %50 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %53 RelaxedPrecision +OpDecorate %54 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +OpDecorate %57 RelaxedPrecision +OpDecorate %58 RelaxedPrecision +OpDecorate %59 RelaxedPrecision +OpDecorate %60 RelaxedPrecision +OpDecorate %67 RelaxedPrecision +OpDecorate %68 RelaxedPrecision +OpDecorate %72 RelaxedPrecision +OpDecorate %73 RelaxedPrecision +OpDecorate %75 RelaxedPrecision +OpDecorate %76 RelaxedPrecision +OpDecorate %77 RelaxedPrecision +OpDecorate %80 RelaxedPrecision +OpDecorate %81 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeImage %1 2D 0 0 0 1 Unknown +%6 = OpTypeSampledImage %5 +%9 = OpTypeImage %1 2D 0 0 0 2 Rgba8 +%12 = OpTypeStruct %2 +%15 = OpTypeStruct %2 +%16 = OpTypeInt 32 0 +%17 = OpConstant %16 2 +%18 = OpTypeArray %15 %17 +%23 = OpTypeInt 32 1 +%24 = OpTypeVector %23 4 +%25 = OpTypeVector %16 4 +%26 = OpTypeStruct %1 %1 %1 %1 +%27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26 +%35 = OpTypeVector %1 2 +%40 = OpTypeVector %23 2 +%61 = OpTypeVoid +%69 = OpConstant %16 0 +%78 = OpConstant %16 1 +%82 = OpTypePointer Private %2 +%3 = OpTypePointer Input %2 +%7 = OpTypePointer UniformConstant %6 +%10 = OpTypePointer UniformConstant %9 +%13 = OpTypePointer Uniform %12 +%19 = OpTypePointer Uniform %18 +%83 = OpTypePointer Private %2 +%21 = OpTypePointer Output %2 +%28 = OpTypePointer Uniform %27 +%30 = OpTypePointer Function %2 +%70 = OpTypePointer Uniform %2 +%31 = OpTypeFunction %2 %30 +%47 = OpTypeFunction %2 %30 %30 +%62 = OpTypeFunction %61 +%4 = OpVariable %3 Input +%8 = OpVariable %7 UniformConstant +%11 = OpVariable %10 UniformConstant +%14 = OpVariable %13 Uniform +%20 = OpVariable %19 Uniform +%22 = OpVariable %21 Output +%29 = OpVariable %28 Uniform +%84 = OpConstant %23 0 +%85 = OpConstant %1 0.5 +%32 = OpFunction %2 None %31 +%33 = OpFunctionParameter %30 +%34 = OpLabel +%36 = OpLoad %6 %8 +%37 = OpLoad %2 %33 +%38 = OpVectorShuffle %35 %37 %37 0 1 +%39 = OpImageSampleImplicitLod %2 %36 %38 +%41 = OpLoad %2 %33 +%42 = OpVectorShuffle %35 %41 %41 2 3 +%43 = OpConvertFToS %40 %42 +%44 = OpLoad %9 %11 +%45 = OpImageRead %2 %44 %43 +%46 = OpFAdd %2 %39 %45 +OpReturnValue %46 +OpFunctionEnd +%48 = OpFunction %2 None %47 +%49 = OpFunctionParameter %30 +%50 = OpFunctionParameter %30 +%51 = OpLabel +%52 = OpLoad %2 %49 +%53 = OpVectorShuffle %35 %52 %52 0 1 +%54 = OpLoad %2 %50 +%55 = OpVectorShuffle %35 %54 %54 2 3 +%56 = OpCompositeExtract %1 %53 0 +%57 = OpCompositeExtract %1 %53 1 +%58 = OpCompositeExtract %1 %55 0 +%59 = OpCompositeExtract %1 %55 1 +%60 = OpCompositeConstruct %2 %56 %57 %58 %59 +OpReturnValue %60 +OpFunctionEnd +%63 = OpFunction %61 None %62 +%64 = OpLabel +%65 = OpVariable %30 Function +%68 = OpVariable %30 Function +%73 = OpVariable %30 Function +%66 = OpLoad %2 %4 +OpStore %65 %66 +%67 = OpFunctionCall %2 %32 %65 +%71 = OpAccessChain %70 %14 %69 +%72 = OpLoad %2 %71 +OpStore %68 %72 +%74 = OpAccessChain %70 %20 %69 %69 +%75 = OpLoad %2 %74 +OpStore %73 %75 +%76 = OpFunctionCall %2 %48 %68 %73 +%77 = OpFAdd %2 %67 %76 +%79 = OpAccessChain %70 %20 %78 %69 +%80 = OpLoad %2 %79 +%81 = OpFAdd %2 %77 %80 +OpStore %22 %81 +OpReturn +OpFunctionEnd diff --git a/test/diff/diff_files/different_decorations_fragment_src.spvasm b/test/diff/diff_files/different_decorations_fragment_src.spvasm new file mode 100644 index 00000000..2c8cd644 --- /dev/null +++ b/test/diff/diff_files/different_decorations_fragment_src.spvasm @@ -0,0 +1,198 @@ +;; Test where variable set/binding/location decorations are different between +;; src and dst fragment shaders. +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %63 "main" %4 %22 +OpExecutionMode %63 OriginUpperLeft +OpSource GLSL 450 +OpName %4 "_ue" +OpName %8 "_uf" +OpName %11 "_ug" +OpName %12 "_uA" +OpMemberName %12 0 "_ux" +OpName %14 "_uc" +OpName %15 "_uB" +OpMemberName %15 0 "_ux" +OpName %20 "_ud" +OpName %22 "_ucol" +OpName %26 "ANGLEDepthRangeParams" +OpMemberName %26 0 "near" +OpMemberName %26 1 "far" +OpMemberName %26 2 "diff" +OpMemberName %26 3 "reserved" +OpName %27 "ANGLEUniformBlock" +OpMemberName %27 0 "viewport" +OpMemberName %27 1 "clipDistancesEnabled" +OpMemberName %27 2 "xfbActiveUnpaused" +OpMemberName %27 3 "xfbVerticesPerInstance" +OpMemberName %27 4 "numSamples" +OpMemberName %27 5 "xfbBufferOffsets" +OpMemberName %27 6 "acbBufferOffsets" +OpMemberName %27 7 "depthRange" +OpName %29 "ANGLEUniforms" +OpName %33 "_uc" +OpName %32 "_uh" +OpName %49 "_ux" +OpName %50 "_uy" +OpName %48 "_ui" +OpName %63 "main" +OpName %65 "param" +OpName %68 "param" +OpName %73 "param" +OpDecorate %4 Location 0 +OpDecorate %8 RelaxedPrecision +OpDecorate %8 DescriptorSet 0 +OpDecorate %8 Binding 0 +OpDecorate %11 DescriptorSet 0 +OpDecorate %11 Binding 1 +OpMemberDecorate %12 0 Offset 0 +OpMemberDecorate %12 0 RelaxedPrecision +OpDecorate %12 Block +OpDecorate %14 DescriptorSet 0 +OpDecorate %14 Binding 2 +OpMemberDecorate %15 0 Offset 0 +OpMemberDecorate %15 0 RelaxedPrecision +OpDecorate %15 BufferBlock +OpDecorate %20 DescriptorSet 0 +OpDecorate %20 Binding 3 +OpDecorate %22 RelaxedPrecision +OpDecorate %22 Location 0 +OpMemberDecorate %26 0 Offset 0 +OpMemberDecorate %26 1 Offset 4 +OpMemberDecorate %26 2 Offset 8 +OpMemberDecorate %26 3 Offset 12 +OpMemberDecorate %27 0 Offset 0 +OpMemberDecorate %27 1 Offset 16 +OpMemberDecorate %27 2 Offset 20 +OpMemberDecorate %27 3 Offset 24 +OpMemberDecorate %27 4 Offset 28 +OpMemberDecorate %27 5 Offset 32 +OpMemberDecorate %27 6 Offset 48 +OpMemberDecorate %27 7 Offset 64 +OpMemberDecorate %27 2 RelaxedPrecision +OpMemberDecorate %27 4 RelaxedPrecision +OpDecorate %27 Block +OpDecorate %29 DescriptorSet 0 +OpDecorate %29 Binding 4 +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %38 RelaxedPrecision +OpDecorate %39 RelaxedPrecision +OpDecorate %41 RelaxedPrecision +OpDecorate %42 RelaxedPrecision +OpDecorate %43 RelaxedPrecision +OpDecorate %48 RelaxedPrecision +OpDecorate %49 RelaxedPrecision +OpDecorate %50 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %53 RelaxedPrecision +OpDecorate %54 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +OpDecorate %57 RelaxedPrecision +OpDecorate %58 RelaxedPrecision +OpDecorate %59 RelaxedPrecision +OpDecorate %60 RelaxedPrecision +OpDecorate %67 RelaxedPrecision +OpDecorate %68 RelaxedPrecision +OpDecorate %72 RelaxedPrecision +OpDecorate %73 RelaxedPrecision +OpDecorate %75 RelaxedPrecision +OpDecorate %76 RelaxedPrecision +OpDecorate %77 RelaxedPrecision +OpDecorate %80 RelaxedPrecision +OpDecorate %81 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeImage %1 2D 0 0 0 1 Unknown +%6 = OpTypeSampledImage %5 +%9 = OpTypeImage %1 2D 0 0 0 2 Rgba8 +%12 = OpTypeStruct %2 +%15 = OpTypeStruct %2 +%16 = OpTypeInt 32 0 +%17 = OpConstant %16 2 +%18 = OpTypeArray %15 %17 +%23 = OpTypeInt 32 1 +%24 = OpTypeVector %23 4 +%25 = OpTypeVector %16 4 +%26 = OpTypeStruct %1 %1 %1 %1 +%27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26 +%35 = OpTypeVector %1 2 +%40 = OpTypeVector %23 2 +%61 = OpTypeVoid +%69 = OpConstant %16 0 +%78 = OpConstant %16 1 +%3 = OpTypePointer Input %2 +%7 = OpTypePointer UniformConstant %6 +%10 = OpTypePointer UniformConstant %9 +%13 = OpTypePointer Uniform %12 +%19 = OpTypePointer Uniform %18 +%21 = OpTypePointer Output %2 +%28 = OpTypePointer Uniform %27 +%30 = OpTypePointer Function %2 +%70 = OpTypePointer Uniform %2 +%31 = OpTypeFunction %2 %30 +%47 = OpTypeFunction %2 %30 %30 +%62 = OpTypeFunction %61 +%4 = OpVariable %3 Input +%8 = OpVariable %7 UniformConstant +%11 = OpVariable %10 UniformConstant +%14 = OpVariable %13 Uniform +%20 = OpVariable %19 Uniform +%22 = OpVariable %21 Output +%29 = OpVariable %28 Uniform +%32 = OpFunction %2 None %31 +%33 = OpFunctionParameter %30 +%34 = OpLabel +%36 = OpLoad %6 %8 +%37 = OpLoad %2 %33 +%38 = OpVectorShuffle %35 %37 %37 0 1 +%39 = OpImageSampleImplicitLod %2 %36 %38 +%41 = OpLoad %2 %33 +%42 = OpVectorShuffle %35 %41 %41 2 3 +%43 = OpConvertFToS %40 %42 +%44 = OpLoad %9 %11 +%45 = OpImageRead %2 %44 %43 +%46 = OpFAdd %2 %39 %45 +OpReturnValue %46 +OpFunctionEnd +%48 = OpFunction %2 None %47 +%49 = OpFunctionParameter %30 +%50 = OpFunctionParameter %30 +%51 = OpLabel +%52 = OpLoad %2 %49 +%53 = OpVectorShuffle %35 %52 %52 0 1 +%54 = OpLoad %2 %50 +%55 = OpVectorShuffle %35 %54 %54 2 3 +%56 = OpCompositeExtract %1 %53 0 +%57 = OpCompositeExtract %1 %53 1 +%58 = OpCompositeExtract %1 %55 0 +%59 = OpCompositeExtract %1 %55 1 +%60 = OpCompositeConstruct %2 %56 %57 %58 %59 +OpReturnValue %60 +OpFunctionEnd +%63 = OpFunction %61 None %62 +%64 = OpLabel +%65 = OpVariable %30 Function +%68 = OpVariable %30 Function +%73 = OpVariable %30 Function +%66 = OpLoad %2 %4 +OpStore %65 %66 +%67 = OpFunctionCall %2 %32 %65 +%71 = OpAccessChain %70 %14 %69 +%72 = OpLoad %2 %71 +OpStore %68 %72 +%74 = OpAccessChain %70 %20 %69 %69 +%75 = OpLoad %2 %74 +OpStore %73 %75 +%76 = OpFunctionCall %2 %48 %68 %73 +%77 = OpFAdd %2 %67 %76 +%79 = OpAccessChain %70 %20 %78 %69 +%80 = OpLoad %2 %79 +%81 = OpFAdd %2 %77 %80 +OpStore %22 %81 +OpReturn +OpFunctionEnd + diff --git a/test/diff/diff_files/different_decorations_vertex_autogen.cpp b/test/diff/diff_files/different_decorations_vertex_autogen.cpp new file mode 100644 index 00000000..f65ee5a1 --- /dev/null +++ b/test/diff/diff_files/different_decorations_vertex_autogen.cpp @@ -0,0 +1,1322 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test where variable set/binding/location decorations are different between +// src and dst vertex shaders. +constexpr char kSrc[] = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %20 %25 +OpSource GLSL 450 +OpName %4 "_ub" +OpName %5 "_uc" +OpName %6 "_ud" +OpName %8 "_ue" +OpName %9 "defaultUniformsVS" +OpMemberName %9 0 "_ua" +OpName %11 "" +OpName %16 "ANGLEDepthRangeParams" +OpMemberName %16 0 "near" +OpMemberName %16 1 "far" +OpMemberName %16 2 "diff" +OpMemberName %16 3 "reserved" +OpName %17 "ANGLEUniformBlock" +OpMemberName %17 0 "viewport" +OpMemberName %17 1 "clipDistancesEnabled" +OpMemberName %17 2 "xfbActiveUnpaused" +OpMemberName %17 3 "xfbVerticesPerInstance" +OpMemberName %17 4 "numSamples" +OpMemberName %17 5 "xfbBufferOffsets" +OpMemberName %17 6 "acbBufferOffsets" +OpMemberName %17 7 "depthRange" +OpName %19 "ANGLEUniforms" +OpName %20 "ANGLEXfbPosition" +OpName %23 "gl_PerVertex" +OpMemberName %23 0 "gl_Position" +OpMemberName %23 1 "gl_PointSize" +OpMemberName %23 2 "gl_ClipDistance" +OpMemberName %23 3 "gl_CullDistance" +OpName %25 "" +OpName %29 "_ua" +OpName %28 "_uf" +OpName %33 "_uf" +OpName %32 "_ug" +OpName %40 "main" +OpName %42 "param" +OpName %50 "param" +OpName %53 "param" +OpDecorate %4 Location 0 +OpDecorate %5 Location 1 +OpDecorate %6 Location 2 +OpDecorate %8 Location 0 +OpMemberDecorate %9 0 Offset 0 +OpDecorate %9 Block +OpDecorate %11 DescriptorSet 0 +OpDecorate %11 Binding 0 +OpMemberDecorate %16 0 Offset 0 +OpMemberDecorate %16 1 Offset 4 +OpMemberDecorate %16 2 Offset 8 +OpMemberDecorate %16 3 Offset 12 +OpMemberDecorate %17 0 Offset 0 +OpMemberDecorate %17 1 Offset 16 +OpMemberDecorate %17 2 Offset 20 +OpMemberDecorate %17 3 Offset 24 +OpMemberDecorate %17 4 Offset 28 +OpMemberDecorate %17 5 Offset 32 +OpMemberDecorate %17 6 Offset 48 +OpMemberDecorate %17 7 Offset 64 +OpMemberDecorate %17 2 RelaxedPrecision +OpMemberDecorate %17 4 RelaxedPrecision +OpDecorate %17 Block +OpDecorate %19 DescriptorSet 0 +OpDecorate %19 Binding 1 +OpDecorate %20 Location 1 +OpMemberDecorate %23 0 BuiltIn Position +OpMemberDecorate %23 1 BuiltIn PointSize +OpMemberDecorate %23 2 BuiltIn ClipDistance +OpMemberDecorate %23 3 BuiltIn CullDistance +OpDecorate %23 Block +OpDecorate %28 RelaxedPrecision +OpDecorate %29 RelaxedPrecision +OpDecorate %31 RelaxedPrecision +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %35 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %44 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%9 = OpTypeStruct %2 +%12 = OpTypeInt 32 0 +%13 = OpTypeInt 32 1 +%14 = OpTypeVector %13 4 +%15 = OpTypeVector %12 4 +%16 = OpTypeStruct %1 %1 %1 %1 +%17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16 +%21 = OpConstant %12 8 +%22 = OpTypeArray %1 %21 +%23 = OpTypeStruct %2 %1 %22 %22 +%38 = OpTypeVoid +%45 = OpConstant %12 0 +%3 = OpTypePointer Input %2 +%7 = OpTypePointer Output %2 +%10 = OpTypePointer Uniform %9 +%18 = OpTypePointer Uniform %17 +%24 = OpTypePointer Output %23 +%26 = OpTypePointer Function %2 +%46 = OpTypePointer Uniform %2 +%27 = OpTypeFunction %2 %26 +%39 = OpTypeFunction %38 +%4 = OpVariable %3 Input +%5 = OpVariable %3 Input +%6 = OpVariable %3 Input +%8 = OpVariable %7 Output +%11 = OpVariable %10 Uniform +%19 = OpVariable %18 Uniform +%20 = OpVariable %7 Output +%25 = OpVariable %24 Output +%28 = OpFunction %2 None %27 +%29 = OpFunctionParameter %26 +%30 = OpLabel +%31 = OpLoad %2 %29 +OpReturnValue %31 +OpFunctionEnd +%32 = OpFunction %2 None %27 +%33 = OpFunctionParameter %26 +%34 = OpLabel +%35 = OpLoad %2 %33 +%36 = OpLoad %2 %33 +%37 = OpFAdd %2 %35 %36 +OpReturnValue %37 +OpFunctionEnd +%40 = OpFunction %38 None %39 +%41 = OpLabel +%42 = OpVariable %26 Function +%50 = OpVariable %26 Function +%53 = OpVariable %26 Function +%43 = OpLoad %2 %4 +OpStore %42 %43 +%44 = OpFunctionCall %2 %28 %42 +%47 = OpAccessChain %46 %11 %45 +%48 = OpLoad %2 %47 +%49 = OpFAdd %2 %44 %48 +OpStore %8 %49 +%51 = OpLoad %2 %5 +OpStore %50 %51 +%52 = OpFunctionCall %2 %32 %50 +%54 = OpLoad %2 %6 +OpStore %53 %54 +%55 = OpFunctionCall %2 %28 %53 +%56 = OpFAdd %2 %52 %55 +%57 = OpAccessChain %7 %25 %45 +OpStore %57 %56 +OpReturn +OpFunctionEnd +)"; +constexpr char kDst[] = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %25 +OpSource GLSL 450 +OpName %4 "_ub" +OpName %5 "_uc" +OpName %6 "_ud" +OpName %8 "_ue" +OpName %9 "defaultUniformsVS" +OpMemberName %9 0 "_ua" +OpName %11 "" +OpName %16 "ANGLEDepthRangeParams" +OpMemberName %16 0 "near" +OpMemberName %16 1 "far" +OpMemberName %16 2 "diff" +OpMemberName %16 3 "reserved" +OpName %17 "ANGLEUniformBlock" +OpMemberName %17 0 "viewport" +OpMemberName %17 1 "clipDistancesEnabled" +OpMemberName %17 2 "xfbActiveUnpaused" +OpMemberName %17 3 "xfbVerticesPerInstance" +OpMemberName %17 4 "numSamples" +OpMemberName %17 5 "xfbBufferOffsets" +OpMemberName %17 6 "acbBufferOffsets" +OpMemberName %17 7 "depthRange" +OpName %19 "ANGLEUniforms" +OpName %23 "gl_PerVertex" +OpMemberName %23 0 "gl_Position" +OpName %25 "" +OpName %29 "_ua" +OpName %28 "_uf" +OpName %33 "_uf" +OpName %32 "_ug" +OpName %40 "main" +OpName %42 "param" +OpName %50 "param" +OpName %53 "param" +OpDecorate %4 Location 1 +OpDecorate %5 Location 2 +OpDecorate %6 Location 0 +OpDecorate %8 Location 1 +OpMemberDecorate %9 0 Offset 0 +OpDecorate %9 Block +OpDecorate %11 DescriptorSet 0 +OpDecorate %11 Binding 1 +OpMemberDecorate %16 0 Offset 0 +OpMemberDecorate %16 1 Offset 4 +OpMemberDecorate %16 2 Offset 8 +OpMemberDecorate %16 3 Offset 12 +OpMemberDecorate %17 0 Offset 0 +OpMemberDecorate %17 1 Offset 16 +OpMemberDecorate %17 2 Offset 20 +OpMemberDecorate %17 3 Offset 24 +OpMemberDecorate %17 4 Offset 28 +OpMemberDecorate %17 5 Offset 32 +OpMemberDecorate %17 6 Offset 48 +OpMemberDecorate %17 7 Offset 64 +OpMemberDecorate %17 2 RelaxedPrecision +OpMemberDecorate %17 4 RelaxedPrecision +OpDecorate %17 Block +OpDecorate %19 DescriptorSet 2 +OpDecorate %19 Binding 0 +OpMemberDecorate %23 0 BuiltIn Position +OpDecorate %23 Block +OpDecorate %28 RelaxedPrecision +OpDecorate %29 RelaxedPrecision +OpDecorate %31 RelaxedPrecision +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %35 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %44 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%9 = OpTypeStruct %2 +%12 = OpTypeInt 32 0 +%13 = OpTypeInt 32 1 +%14 = OpTypeVector %13 4 +%15 = OpTypeVector %12 4 +%16 = OpTypeStruct %1 %1 %1 %1 +%17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16 +%21 = OpConstant %12 8 +%22 = OpTypeArray %1 %21 +%23 = OpTypeStruct %2 +%38 = OpTypeVoid +%45 = OpConstant %12 0 +%58 = OpTypePointer Private %2 +%3 = OpTypePointer Input %2 +%59 = OpTypePointer Private %2 +%7 = OpTypePointer Output %2 +%10 = OpTypePointer Uniform %9 +%18 = OpTypePointer Uniform %17 +%24 = OpTypePointer Output %23 +%26 = OpTypePointer Function %2 +%46 = OpTypePointer Uniform %2 +%27 = OpTypeFunction %2 %26 +%39 = OpTypeFunction %38 +%4 = OpVariable %3 Input +%5 = OpVariable %3 Input +%6 = OpVariable %3 Input +%8 = OpVariable %7 Output +%11 = OpVariable %10 Uniform +%19 = OpVariable %18 Uniform +%20 = OpVariable %59 Private +%25 = OpVariable %24 Output +%60 = OpConstant %13 0 +%61 = OpConstant %1 0.5 +%28 = OpFunction %2 None %27 +%29 = OpFunctionParameter %26 +%30 = OpLabel +%31 = OpLoad %2 %29 +OpReturnValue %31 +OpFunctionEnd +%32 = OpFunction %2 None %27 +%33 = OpFunctionParameter %26 +%34 = OpLabel +%35 = OpLoad %2 %33 +%36 = OpLoad %2 %33 +%37 = OpFAdd %2 %35 %36 +OpReturnValue %37 +OpFunctionEnd +%40 = OpFunction %38 None %39 +%41 = OpLabel +%42 = OpVariable %26 Function +%50 = OpVariable %26 Function +%53 = OpVariable %26 Function +%43 = OpLoad %2 %4 +OpStore %42 %43 +%44 = OpFunctionCall %2 %28 %42 +%47 = OpAccessChain %46 %11 %45 +%48 = OpLoad %2 %47 +%49 = OpFAdd %2 %44 %48 +OpStore %8 %49 +%51 = OpLoad %2 %5 +OpStore %50 %51 +%52 = OpFunctionCall %2 %32 %50 +%54 = OpLoad %2 %6 +OpStore %53 %54 +%55 = OpFunctionCall %2 %28 %53 +%56 = OpFAdd %2 %52 %55 +%57 = OpAccessChain %7 %25 %45 +OpStore %57 %56 +%62 = OpAccessChain %7 %25 %60 +%63 = OpLoad %2 %62 +%64 = OpCompositeExtract %1 %63 0 +%65 = OpCompositeExtract %1 %63 1 +%66 = OpCompositeExtract %1 %63 2 +%67 = OpCompositeExtract %1 %63 3 +%69 = OpFNegate %1 %64 +%70 = OpFAdd %1 %66 %67 +%71 = OpFMul %1 %70 %61 +%68 = OpCompositeConstruct %2 %65 %69 %71 %67 +OpStore %62 %68 +OpReturn +OpFunctionEnd +)"; + +TEST(DiffTest, DifferentDecorationsVertex) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 58 ++; Bound: 73 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %20 %25 ++OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %25 + OpSource GLSL 450 + OpName %4 "_ub" + OpName %5 "_uc" + OpName %6 "_ud" + OpName %8 "_ue" + OpName %9 "defaultUniformsVS" + OpMemberName %9 0 "_ua" + OpName %11 "" + OpName %16 "ANGLEDepthRangeParams" + OpMemberName %16 0 "near" + OpMemberName %16 1 "far" + OpMemberName %16 2 "diff" + OpMemberName %16 3 "reserved" + OpName %17 "ANGLEUniformBlock" + OpMemberName %17 0 "viewport" + OpMemberName %17 1 "clipDistancesEnabled" + OpMemberName %17 2 "xfbActiveUnpaused" + OpMemberName %17 3 "xfbVerticesPerInstance" + OpMemberName %17 4 "numSamples" + OpMemberName %17 5 "xfbBufferOffsets" + OpMemberName %17 6 "acbBufferOffsets" + OpMemberName %17 7 "depthRange" + OpName %19 "ANGLEUniforms" +-OpName %20 "ANGLEXfbPosition" + OpName %23 "gl_PerVertex" + OpMemberName %23 0 "gl_Position" +-OpMemberName %23 1 "gl_PointSize" +-OpMemberName %23 2 "gl_ClipDistance" +-OpMemberName %23 3 "gl_CullDistance" + OpName %25 "" + OpName %29 "_ua" + OpName %28 "_uf" + OpName %33 "_uf" + OpName %32 "_ug" + OpName %40 "main" + OpName %42 "param" + OpName %50 "param" + OpName %53 "param" +-OpDecorate %4 Location 0 ++OpDecorate %4 Location 1 +-OpDecorate %5 Location 1 ++OpDecorate %5 Location 2 +-OpDecorate %6 Location 2 ++OpDecorate %6 Location 0 +-OpDecorate %8 Location 0 ++OpDecorate %8 Location 1 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 Block + OpDecorate %11 DescriptorSet 0 +-OpDecorate %11 Binding 0 ++OpDecorate %11 Binding 1 + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 Offset 4 + OpMemberDecorate %16 2 Offset 8 + OpMemberDecorate %16 3 Offset 12 + OpMemberDecorate %17 0 Offset 0 + OpMemberDecorate %17 1 Offset 16 + OpMemberDecorate %17 2 Offset 20 + OpMemberDecorate %17 3 Offset 24 + OpMemberDecorate %17 4 Offset 28 + OpMemberDecorate %17 5 Offset 32 + OpMemberDecorate %17 6 Offset 48 + OpMemberDecorate %17 7 Offset 64 + OpMemberDecorate %17 2 RelaxedPrecision + OpMemberDecorate %17 4 RelaxedPrecision + OpDecorate %17 Block +-OpDecorate %19 DescriptorSet 0 ++OpDecorate %19 DescriptorSet 2 +-OpDecorate %19 Binding 1 ++OpDecorate %19 Binding 0 +-OpDecorate %20 Location 1 + OpMemberDecorate %23 0 BuiltIn Position +-OpMemberDecorate %23 1 BuiltIn PointSize +-OpMemberDecorate %23 2 BuiltIn ClipDistance +-OpMemberDecorate %23 3 BuiltIn CullDistance + OpDecorate %23 Block + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %52 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %9 = OpTypeStruct %2 + %12 = OpTypeInt 32 0 + %13 = OpTypeInt 32 1 + %14 = OpTypeVector %13 4 + %15 = OpTypeVector %12 4 + %16 = OpTypeStruct %1 %1 %1 %1 + %17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16 + %21 = OpConstant %12 8 + %22 = OpTypeArray %1 %21 +-%23 = OpTypeStruct %2 %1 %22 %22 ++%23 = OpTypeStruct %2 + %38 = OpTypeVoid + %45 = OpConstant %12 0 ++%59 = OpTypePointer Private %2 + %3 = OpTypePointer Input %2 ++%60 = OpTypePointer Private %2 + %7 = OpTypePointer Output %2 + %10 = OpTypePointer Uniform %9 + %18 = OpTypePointer Uniform %17 + %24 = OpTypePointer Output %23 + %26 = OpTypePointer Function %2 + %46 = OpTypePointer Uniform %2 + %27 = OpTypeFunction %2 %26 + %39 = OpTypeFunction %38 + %4 = OpVariable %3 Input + %5 = OpVariable %3 Input + %6 = OpVariable %3 Input + %8 = OpVariable %7 Output + %11 = OpVariable %10 Uniform + %19 = OpVariable %18 Uniform +-%20 = OpVariable %7 Output ++%58 = OpVariable %60 Private + %25 = OpVariable %24 Output ++%61 = OpConstant %13 0 ++%62 = OpConstant %1 0.5 + %28 = OpFunction %2 None %27 + %29 = OpFunctionParameter %26 + %30 = OpLabel + %31 = OpLoad %2 %29 + OpReturnValue %31 + OpFunctionEnd + %32 = OpFunction %2 None %27 + %33 = OpFunctionParameter %26 + %34 = OpLabel + %35 = OpLoad %2 %33 + %36 = OpLoad %2 %33 + %37 = OpFAdd %2 %35 %36 + OpReturnValue %37 + OpFunctionEnd + %40 = OpFunction %38 None %39 + %41 = OpLabel + %42 = OpVariable %26 Function + %50 = OpVariable %26 Function + %53 = OpVariable %26 Function + %43 = OpLoad %2 %4 + OpStore %42 %43 + %44 = OpFunctionCall %2 %28 %42 + %47 = OpAccessChain %46 %11 %45 + %48 = OpLoad %2 %47 + %49 = OpFAdd %2 %44 %48 + OpStore %8 %49 + %51 = OpLoad %2 %5 + OpStore %50 %51 + %52 = OpFunctionCall %2 %32 %50 + %54 = OpLoad %2 %6 + OpStore %53 %54 + %55 = OpFunctionCall %2 %28 %53 + %56 = OpFAdd %2 %52 %55 + %57 = OpAccessChain %7 %25 %45 + OpStore %57 %56 ++%63 = OpAccessChain %7 %25 %61 ++%64 = OpLoad %2 %63 ++%65 = OpCompositeExtract %1 %64 0 ++%66 = OpCompositeExtract %1 %64 1 ++%67 = OpCompositeExtract %1 %64 2 ++%68 = OpCompositeExtract %1 %64 3 ++%70 = OpFNegate %1 %65 ++%71 = OpFAdd %1 %67 %68 ++%72 = OpFMul %1 %71 %62 ++%69 = OpCompositeConstruct %2 %66 %70 %72 %68 ++OpStore %63 %69 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, DifferentDecorationsVertexNoDebug) { + constexpr char kSrcNoDebug[] = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %20 %25 +OpSource GLSL 450 +OpDecorate %4 Location 0 +OpDecorate %5 Location 1 +OpDecorate %6 Location 2 +OpDecorate %8 Location 0 +OpMemberDecorate %9 0 Offset 0 +OpDecorate %9 Block +OpDecorate %11 DescriptorSet 0 +OpDecorate %11 Binding 0 +OpMemberDecorate %16 0 Offset 0 +OpMemberDecorate %16 1 Offset 4 +OpMemberDecorate %16 2 Offset 8 +OpMemberDecorate %16 3 Offset 12 +OpMemberDecorate %17 0 Offset 0 +OpMemberDecorate %17 1 Offset 16 +OpMemberDecorate %17 2 Offset 20 +OpMemberDecorate %17 3 Offset 24 +OpMemberDecorate %17 4 Offset 28 +OpMemberDecorate %17 5 Offset 32 +OpMemberDecorate %17 6 Offset 48 +OpMemberDecorate %17 7 Offset 64 +OpMemberDecorate %17 2 RelaxedPrecision +OpMemberDecorate %17 4 RelaxedPrecision +OpDecorate %17 Block +OpDecorate %19 DescriptorSet 0 +OpDecorate %19 Binding 1 +OpDecorate %20 Location 1 +OpMemberDecorate %23 0 BuiltIn Position +OpMemberDecorate %23 1 BuiltIn PointSize +OpMemberDecorate %23 2 BuiltIn ClipDistance +OpMemberDecorate %23 3 BuiltIn CullDistance +OpDecorate %23 Block +OpDecorate %28 RelaxedPrecision +OpDecorate %29 RelaxedPrecision +OpDecorate %31 RelaxedPrecision +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %35 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %44 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%9 = OpTypeStruct %2 +%12 = OpTypeInt 32 0 +%13 = OpTypeInt 32 1 +%14 = OpTypeVector %13 4 +%15 = OpTypeVector %12 4 +%16 = OpTypeStruct %1 %1 %1 %1 +%17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16 +%21 = OpConstant %12 8 +%22 = OpTypeArray %1 %21 +%23 = OpTypeStruct %2 %1 %22 %22 +%38 = OpTypeVoid +%45 = OpConstant %12 0 +%3 = OpTypePointer Input %2 +%7 = OpTypePointer Output %2 +%10 = OpTypePointer Uniform %9 +%18 = OpTypePointer Uniform %17 +%24 = OpTypePointer Output %23 +%26 = OpTypePointer Function %2 +%46 = OpTypePointer Uniform %2 +%27 = OpTypeFunction %2 %26 +%39 = OpTypeFunction %38 +%4 = OpVariable %3 Input +%5 = OpVariable %3 Input +%6 = OpVariable %3 Input +%8 = OpVariable %7 Output +%11 = OpVariable %10 Uniform +%19 = OpVariable %18 Uniform +%20 = OpVariable %7 Output +%25 = OpVariable %24 Output +%28 = OpFunction %2 None %27 +%29 = OpFunctionParameter %26 +%30 = OpLabel +%31 = OpLoad %2 %29 +OpReturnValue %31 +OpFunctionEnd +%32 = OpFunction %2 None %27 +%33 = OpFunctionParameter %26 +%34 = OpLabel +%35 = OpLoad %2 %33 +%36 = OpLoad %2 %33 +%37 = OpFAdd %2 %35 %36 +OpReturnValue %37 +OpFunctionEnd +%40 = OpFunction %38 None %39 +%41 = OpLabel +%42 = OpVariable %26 Function +%50 = OpVariable %26 Function +%53 = OpVariable %26 Function +%43 = OpLoad %2 %4 +OpStore %42 %43 +%44 = OpFunctionCall %2 %28 %42 +%47 = OpAccessChain %46 %11 %45 +%48 = OpLoad %2 %47 +%49 = OpFAdd %2 %44 %48 +OpStore %8 %49 +%51 = OpLoad %2 %5 +OpStore %50 %51 +%52 = OpFunctionCall %2 %32 %50 +%54 = OpLoad %2 %6 +OpStore %53 %54 +%55 = OpFunctionCall %2 %28 %53 +%56 = OpFAdd %2 %52 %55 +%57 = OpAccessChain %7 %25 %45 +OpStore %57 %56 +OpReturn +OpFunctionEnd + +)"; + constexpr char kDstNoDebug[] = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %25 +OpSource GLSL 450 +OpDecorate %4 Location 1 +OpDecorate %5 Location 2 +OpDecorate %6 Location 0 +OpDecorate %8 Location 1 +OpMemberDecorate %9 0 Offset 0 +OpDecorate %9 Block +OpDecorate %11 DescriptorSet 0 +OpDecorate %11 Binding 1 +OpMemberDecorate %16 0 Offset 0 +OpMemberDecorate %16 1 Offset 4 +OpMemberDecorate %16 2 Offset 8 +OpMemberDecorate %16 3 Offset 12 +OpMemberDecorate %17 0 Offset 0 +OpMemberDecorate %17 1 Offset 16 +OpMemberDecorate %17 2 Offset 20 +OpMemberDecorate %17 3 Offset 24 +OpMemberDecorate %17 4 Offset 28 +OpMemberDecorate %17 5 Offset 32 +OpMemberDecorate %17 6 Offset 48 +OpMemberDecorate %17 7 Offset 64 +OpMemberDecorate %17 2 RelaxedPrecision +OpMemberDecorate %17 4 RelaxedPrecision +OpDecorate %17 Block +OpDecorate %19 DescriptorSet 2 +OpDecorate %19 Binding 0 +OpMemberDecorate %23 0 BuiltIn Position +OpDecorate %23 Block +OpDecorate %28 RelaxedPrecision +OpDecorate %29 RelaxedPrecision +OpDecorate %31 RelaxedPrecision +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %35 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %44 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%9 = OpTypeStruct %2 +%12 = OpTypeInt 32 0 +%13 = OpTypeInt 32 1 +%14 = OpTypeVector %13 4 +%15 = OpTypeVector %12 4 +%16 = OpTypeStruct %1 %1 %1 %1 +%17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16 +%21 = OpConstant %12 8 +%22 = OpTypeArray %1 %21 +%23 = OpTypeStruct %2 +%38 = OpTypeVoid +%45 = OpConstant %12 0 +%58 = OpTypePointer Private %2 +%3 = OpTypePointer Input %2 +%59 = OpTypePointer Private %2 +%7 = OpTypePointer Output %2 +%10 = OpTypePointer Uniform %9 +%18 = OpTypePointer Uniform %17 +%24 = OpTypePointer Output %23 +%26 = OpTypePointer Function %2 +%46 = OpTypePointer Uniform %2 +%27 = OpTypeFunction %2 %26 +%39 = OpTypeFunction %38 +%4 = OpVariable %3 Input +%5 = OpVariable %3 Input +%6 = OpVariable %3 Input +%8 = OpVariable %7 Output +%11 = OpVariable %10 Uniform +%19 = OpVariable %18 Uniform +%20 = OpVariable %59 Private +%25 = OpVariable %24 Output +%60 = OpConstant %13 0 +%61 = OpConstant %1 0.5 +%28 = OpFunction %2 None %27 +%29 = OpFunctionParameter %26 +%30 = OpLabel +%31 = OpLoad %2 %29 +OpReturnValue %31 +OpFunctionEnd +%32 = OpFunction %2 None %27 +%33 = OpFunctionParameter %26 +%34 = OpLabel +%35 = OpLoad %2 %33 +%36 = OpLoad %2 %33 +%37 = OpFAdd %2 %35 %36 +OpReturnValue %37 +OpFunctionEnd +%40 = OpFunction %38 None %39 +%41 = OpLabel +%42 = OpVariable %26 Function +%50 = OpVariable %26 Function +%53 = OpVariable %26 Function +%43 = OpLoad %2 %4 +OpStore %42 %43 +%44 = OpFunctionCall %2 %28 %42 +%47 = OpAccessChain %46 %11 %45 +%48 = OpLoad %2 %47 +%49 = OpFAdd %2 %44 %48 +OpStore %8 %49 +%51 = OpLoad %2 %5 +OpStore %50 %51 +%52 = OpFunctionCall %2 %32 %50 +%54 = OpLoad %2 %6 +OpStore %53 %54 +%55 = OpFunctionCall %2 %28 %53 +%56 = OpFAdd %2 %52 %55 +%57 = OpAccessChain %7 %25 %45 +OpStore %57 %56 +%62 = OpAccessChain %7 %25 %60 +%63 = OpLoad %2 %62 +%64 = OpCompositeExtract %1 %63 0 +%65 = OpCompositeExtract %1 %63 1 +%66 = OpCompositeExtract %1 %63 2 +%67 = OpCompositeExtract %1 %63 3 +%69 = OpFNegate %1 %64 +%70 = OpFAdd %1 %66 %67 +%71 = OpFMul %1 %70 %61 +%68 = OpCompositeConstruct %2 %65 %69 %71 %67 +OpStore %62 %68 +OpReturn +OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 58 ++; Bound: 79 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %20 %25 ++OpEntryPoint Vertex %40 "main" %5 %6 %4 %20 %25 + OpSource GLSL 450 + OpDecorate %4 Location 0 + OpDecorate %5 Location 1 + OpDecorate %6 Location 2 +-OpDecorate %8 Location 0 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 Block +-OpDecorate %11 DescriptorSet 0 ++OpDecorate %11 DescriptorSet 2 + OpDecorate %11 Binding 0 + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 Offset 4 + OpMemberDecorate %16 2 Offset 8 + OpMemberDecorate %16 3 Offset 12 + OpMemberDecorate %17 0 Offset 0 + OpMemberDecorate %17 1 Offset 16 + OpMemberDecorate %17 2 Offset 20 + OpMemberDecorate %17 3 Offset 24 + OpMemberDecorate %17 4 Offset 28 + OpMemberDecorate %17 5 Offset 32 + OpMemberDecorate %17 6 Offset 48 + OpMemberDecorate %17 7 Offset 64 + OpMemberDecorate %17 2 RelaxedPrecision + OpMemberDecorate %17 4 RelaxedPrecision + OpDecorate %17 Block + OpDecorate %19 DescriptorSet 0 + OpDecorate %19 Binding 1 + OpDecorate %20 Location 1 + OpMemberDecorate %23 0 BuiltIn Position +-OpMemberDecorate %23 1 BuiltIn PointSize +-OpMemberDecorate %23 2 BuiltIn ClipDistance +-OpMemberDecorate %23 3 BuiltIn CullDistance + OpDecorate %23 Block + OpDecorate %28 RelaxedPrecision +-OpDecorate %29 RelaxedPrecision ++OpDecorate %59 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %32 RelaxedPrecision +-OpDecorate %33 RelaxedPrecision ++OpDecorate %60 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %52 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %9 = OpTypeStruct %2 + %12 = OpTypeInt 32 0 + %13 = OpTypeInt 32 1 + %14 = OpTypeVector %13 4 + %15 = OpTypeVector %12 4 + %16 = OpTypeStruct %1 %1 %1 %1 + %17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16 + %21 = OpConstant %12 8 + %22 = OpTypeArray %1 %21 +-%23 = OpTypeStruct %2 %1 %22 %22 ++%23 = OpTypeStruct %2 + %38 = OpTypeVoid + %45 = OpConstant %12 0 ++%65 = OpTypePointer Private %2 + %3 = OpTypePointer Input %2 ++%66 = OpTypePointer Private %2 + %7 = OpTypePointer Output %2 + %10 = OpTypePointer Uniform %9 + %18 = OpTypePointer Uniform %17 + %24 = OpTypePointer Output %23 + %26 = OpTypePointer Function %2 + %46 = OpTypePointer Uniform %2 + %27 = OpTypeFunction %2 %26 + %39 = OpTypeFunction %38 + %4 = OpVariable %3 Input + %5 = OpVariable %3 Input + %6 = OpVariable %3 Input +-%8 = OpVariable %7 Output +-%11 = OpVariable %10 Uniform ++%11 = OpVariable %18 Uniform +-%19 = OpVariable %18 Uniform ++%19 = OpVariable %10 Uniform + %20 = OpVariable %7 Output ++%58 = OpVariable %66 Private + %25 = OpVariable %24 Output ++%67 = OpConstant %13 0 ++%68 = OpConstant %1 0.5 + %28 = OpFunction %2 None %27 +-%29 = OpFunctionParameter %26 ++%59 = OpFunctionParameter %26 + %30 = OpLabel +-%31 = OpLoad %2 %29 ++%31 = OpLoad %2 %59 + OpReturnValue %31 + OpFunctionEnd + %32 = OpFunction %2 None %27 +-%33 = OpFunctionParameter %26 ++%60 = OpFunctionParameter %26 + %34 = OpLabel +-%35 = OpLoad %2 %33 ++%35 = OpLoad %2 %60 +-%36 = OpLoad %2 %33 ++%36 = OpLoad %2 %60 + %37 = OpFAdd %2 %35 %36 + OpReturnValue %37 + OpFunctionEnd + %40 = OpFunction %38 None %39 + %41 = OpLabel + %42 = OpVariable %26 Function + %50 = OpVariable %26 Function + %53 = OpVariable %26 Function +-%43 = OpLoad %2 %4 ++%61 = OpLoad %2 %5 +-OpStore %42 %43 ++OpStore %42 %61 + %44 = OpFunctionCall %2 %28 %42 +-%47 = OpAccessChain %46 %11 %45 ++%62 = OpAccessChain %46 %19 %45 +-%48 = OpLoad %2 %47 ++%48 = OpLoad %2 %62 + %49 = OpFAdd %2 %44 %48 +-OpStore %8 %49 ++OpStore %20 %49 +-%51 = OpLoad %2 %5 ++%63 = OpLoad %2 %6 +-OpStore %50 %51 ++OpStore %50 %63 + %52 = OpFunctionCall %2 %32 %50 +-%54 = OpLoad %2 %6 ++%64 = OpLoad %2 %4 +-OpStore %53 %54 ++OpStore %53 %64 + %55 = OpFunctionCall %2 %28 %53 + %56 = OpFAdd %2 %52 %55 + %57 = OpAccessChain %7 %25 %45 + OpStore %57 %56 ++%69 = OpAccessChain %7 %25 %67 ++%70 = OpLoad %2 %69 ++%71 = OpCompositeExtract %1 %70 0 ++%72 = OpCompositeExtract %1 %70 1 ++%73 = OpCompositeExtract %1 %70 2 ++%74 = OpCompositeExtract %1 %70 3 ++%76 = OpFNegate %1 %71 ++%77 = OpFAdd %1 %73 %74 ++%78 = OpFMul %1 %77 %68 ++%75 = OpCompositeConstruct %2 %72 %76 %78 %74 ++OpStore %69 %75 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +TEST(DiffTest, DifferentDecorationsVertexIgnoreSetBinding) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 58 ++; Bound: 73 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %20 %25 ++OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %25 + OpSource GLSL 450 + OpName %4 "_ub" + OpName %5 "_uc" + OpName %6 "_ud" + OpName %8 "_ue" + OpName %9 "defaultUniformsVS" + OpMemberName %9 0 "_ua" + OpName %11 "" + OpName %16 "ANGLEDepthRangeParams" + OpMemberName %16 0 "near" + OpMemberName %16 1 "far" + OpMemberName %16 2 "diff" + OpMemberName %16 3 "reserved" + OpName %17 "ANGLEUniformBlock" + OpMemberName %17 0 "viewport" + OpMemberName %17 1 "clipDistancesEnabled" + OpMemberName %17 2 "xfbActiveUnpaused" + OpMemberName %17 3 "xfbVerticesPerInstance" + OpMemberName %17 4 "numSamples" + OpMemberName %17 5 "xfbBufferOffsets" + OpMemberName %17 6 "acbBufferOffsets" + OpMemberName %17 7 "depthRange" + OpName %19 "ANGLEUniforms" +-OpName %20 "ANGLEXfbPosition" + OpName %23 "gl_PerVertex" + OpMemberName %23 0 "gl_Position" +-OpMemberName %23 1 "gl_PointSize" +-OpMemberName %23 2 "gl_ClipDistance" +-OpMemberName %23 3 "gl_CullDistance" + OpName %25 "" + OpName %29 "_ua" + OpName %28 "_uf" + OpName %33 "_uf" + OpName %32 "_ug" + OpName %40 "main" + OpName %42 "param" + OpName %50 "param" + OpName %53 "param" +-OpDecorate %4 Location 0 ++OpDecorate %4 Location 1 +-OpDecorate %5 Location 1 ++OpDecorate %5 Location 2 +-OpDecorate %6 Location 2 ++OpDecorate %6 Location 0 +-OpDecorate %8 Location 0 ++OpDecorate %8 Location 1 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 Block + OpDecorate %11 DescriptorSet 0 +-OpDecorate %11 Binding 0 ++OpDecorate %11 Binding 1 + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 Offset 4 + OpMemberDecorate %16 2 Offset 8 + OpMemberDecorate %16 3 Offset 12 + OpMemberDecorate %17 0 Offset 0 + OpMemberDecorate %17 1 Offset 16 + OpMemberDecorate %17 2 Offset 20 + OpMemberDecorate %17 3 Offset 24 + OpMemberDecorate %17 4 Offset 28 + OpMemberDecorate %17 5 Offset 32 + OpMemberDecorate %17 6 Offset 48 + OpMemberDecorate %17 7 Offset 64 + OpMemberDecorate %17 2 RelaxedPrecision + OpMemberDecorate %17 4 RelaxedPrecision + OpDecorate %17 Block +-OpDecorate %19 DescriptorSet 0 ++OpDecorate %19 DescriptorSet 2 +-OpDecorate %19 Binding 1 ++OpDecorate %19 Binding 0 +-OpDecorate %20 Location 1 + OpMemberDecorate %23 0 BuiltIn Position +-OpMemberDecorate %23 1 BuiltIn PointSize +-OpMemberDecorate %23 2 BuiltIn ClipDistance +-OpMemberDecorate %23 3 BuiltIn CullDistance + OpDecorate %23 Block + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %52 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %9 = OpTypeStruct %2 + %12 = OpTypeInt 32 0 + %13 = OpTypeInt 32 1 + %14 = OpTypeVector %13 4 + %15 = OpTypeVector %12 4 + %16 = OpTypeStruct %1 %1 %1 %1 + %17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16 + %21 = OpConstant %12 8 + %22 = OpTypeArray %1 %21 +-%23 = OpTypeStruct %2 %1 %22 %22 ++%23 = OpTypeStruct %2 + %38 = OpTypeVoid + %45 = OpConstant %12 0 ++%59 = OpTypePointer Private %2 + %3 = OpTypePointer Input %2 ++%60 = OpTypePointer Private %2 + %7 = OpTypePointer Output %2 + %10 = OpTypePointer Uniform %9 + %18 = OpTypePointer Uniform %17 + %24 = OpTypePointer Output %23 + %26 = OpTypePointer Function %2 + %46 = OpTypePointer Uniform %2 + %27 = OpTypeFunction %2 %26 + %39 = OpTypeFunction %38 + %4 = OpVariable %3 Input + %5 = OpVariable %3 Input + %6 = OpVariable %3 Input + %8 = OpVariable %7 Output + %11 = OpVariable %10 Uniform + %19 = OpVariable %18 Uniform +-%20 = OpVariable %7 Output ++%58 = OpVariable %60 Private + %25 = OpVariable %24 Output ++%61 = OpConstant %13 0 ++%62 = OpConstant %1 0.5 + %28 = OpFunction %2 None %27 + %29 = OpFunctionParameter %26 + %30 = OpLabel + %31 = OpLoad %2 %29 + OpReturnValue %31 + OpFunctionEnd + %32 = OpFunction %2 None %27 + %33 = OpFunctionParameter %26 + %34 = OpLabel + %35 = OpLoad %2 %33 + %36 = OpLoad %2 %33 + %37 = OpFAdd %2 %35 %36 + OpReturnValue %37 + OpFunctionEnd + %40 = OpFunction %38 None %39 + %41 = OpLabel + %42 = OpVariable %26 Function + %50 = OpVariable %26 Function + %53 = OpVariable %26 Function + %43 = OpLoad %2 %4 + OpStore %42 %43 + %44 = OpFunctionCall %2 %28 %42 + %47 = OpAccessChain %46 %11 %45 + %48 = OpLoad %2 %47 + %49 = OpFAdd %2 %44 %48 + OpStore %8 %49 + %51 = OpLoad %2 %5 + OpStore %50 %51 + %52 = OpFunctionCall %2 %32 %50 + %54 = OpLoad %2 %6 + OpStore %53 %54 + %55 = OpFunctionCall %2 %28 %53 + %56 = OpFAdd %2 %52 %55 + %57 = OpAccessChain %7 %25 %45 + OpStore %57 %56 ++%63 = OpAccessChain %7 %25 %61 ++%64 = OpLoad %2 %63 ++%65 = OpCompositeExtract %1 %64 0 ++%66 = OpCompositeExtract %1 %64 1 ++%67 = OpCompositeExtract %1 %64 2 ++%68 = OpCompositeExtract %1 %64 3 ++%70 = OpFNegate %1 %65 ++%71 = OpFAdd %1 %67 %68 ++%72 = OpFMul %1 %71 %62 ++%69 = OpCompositeConstruct %2 %66 %70 %72 %68 ++OpStore %63 %69 + OpReturn + OpFunctionEnd +)"; + Options options; + options.ignore_set_binding = true; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, DifferentDecorationsVertexIgnoreSetBindingLocation) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 58 ++; Bound: 73 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %20 %25 ++OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %25 + OpSource GLSL 450 + OpName %4 "_ub" + OpName %5 "_uc" + OpName %6 "_ud" + OpName %8 "_ue" + OpName %9 "defaultUniformsVS" + OpMemberName %9 0 "_ua" + OpName %11 "" + OpName %16 "ANGLEDepthRangeParams" + OpMemberName %16 0 "near" + OpMemberName %16 1 "far" + OpMemberName %16 2 "diff" + OpMemberName %16 3 "reserved" + OpName %17 "ANGLEUniformBlock" + OpMemberName %17 0 "viewport" + OpMemberName %17 1 "clipDistancesEnabled" + OpMemberName %17 2 "xfbActiveUnpaused" + OpMemberName %17 3 "xfbVerticesPerInstance" + OpMemberName %17 4 "numSamples" + OpMemberName %17 5 "xfbBufferOffsets" + OpMemberName %17 6 "acbBufferOffsets" + OpMemberName %17 7 "depthRange" + OpName %19 "ANGLEUniforms" +-OpName %20 "ANGLEXfbPosition" + OpName %23 "gl_PerVertex" + OpMemberName %23 0 "gl_Position" +-OpMemberName %23 1 "gl_PointSize" +-OpMemberName %23 2 "gl_ClipDistance" +-OpMemberName %23 3 "gl_CullDistance" + OpName %25 "" + OpName %29 "_ua" + OpName %28 "_uf" + OpName %33 "_uf" + OpName %32 "_ug" + OpName %40 "main" + OpName %42 "param" + OpName %50 "param" + OpName %53 "param" +-OpDecorate %4 Location 0 ++OpDecorate %4 Location 1 +-OpDecorate %5 Location 1 ++OpDecorate %5 Location 2 +-OpDecorate %6 Location 2 ++OpDecorate %6 Location 0 +-OpDecorate %8 Location 0 ++OpDecorate %8 Location 1 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 Block + OpDecorate %11 DescriptorSet 0 +-OpDecorate %11 Binding 0 ++OpDecorate %11 Binding 1 + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 Offset 4 + OpMemberDecorate %16 2 Offset 8 + OpMemberDecorate %16 3 Offset 12 + OpMemberDecorate %17 0 Offset 0 + OpMemberDecorate %17 1 Offset 16 + OpMemberDecorate %17 2 Offset 20 + OpMemberDecorate %17 3 Offset 24 + OpMemberDecorate %17 4 Offset 28 + OpMemberDecorate %17 5 Offset 32 + OpMemberDecorate %17 6 Offset 48 + OpMemberDecorate %17 7 Offset 64 + OpMemberDecorate %17 2 RelaxedPrecision + OpMemberDecorate %17 4 RelaxedPrecision + OpDecorate %17 Block +-OpDecorate %19 DescriptorSet 0 ++OpDecorate %19 DescriptorSet 2 +-OpDecorate %19 Binding 1 ++OpDecorate %19 Binding 0 +-OpDecorate %20 Location 1 + OpMemberDecorate %23 0 BuiltIn Position +-OpMemberDecorate %23 1 BuiltIn PointSize +-OpMemberDecorate %23 2 BuiltIn ClipDistance +-OpMemberDecorate %23 3 BuiltIn CullDistance + OpDecorate %23 Block + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %52 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %9 = OpTypeStruct %2 + %12 = OpTypeInt 32 0 + %13 = OpTypeInt 32 1 + %14 = OpTypeVector %13 4 + %15 = OpTypeVector %12 4 + %16 = OpTypeStruct %1 %1 %1 %1 + %17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16 + %21 = OpConstant %12 8 + %22 = OpTypeArray %1 %21 +-%23 = OpTypeStruct %2 %1 %22 %22 ++%23 = OpTypeStruct %2 + %38 = OpTypeVoid + %45 = OpConstant %12 0 ++%59 = OpTypePointer Private %2 + %3 = OpTypePointer Input %2 ++%60 = OpTypePointer Private %2 + %7 = OpTypePointer Output %2 + %10 = OpTypePointer Uniform %9 + %18 = OpTypePointer Uniform %17 + %24 = OpTypePointer Output %23 + %26 = OpTypePointer Function %2 + %46 = OpTypePointer Uniform %2 + %27 = OpTypeFunction %2 %26 + %39 = OpTypeFunction %38 + %4 = OpVariable %3 Input + %5 = OpVariable %3 Input + %6 = OpVariable %3 Input + %8 = OpVariable %7 Output + %11 = OpVariable %10 Uniform + %19 = OpVariable %18 Uniform +-%20 = OpVariable %7 Output ++%58 = OpVariable %60 Private + %25 = OpVariable %24 Output ++%61 = OpConstant %13 0 ++%62 = OpConstant %1 0.5 + %28 = OpFunction %2 None %27 + %29 = OpFunctionParameter %26 + %30 = OpLabel + %31 = OpLoad %2 %29 + OpReturnValue %31 + OpFunctionEnd + %32 = OpFunction %2 None %27 + %33 = OpFunctionParameter %26 + %34 = OpLabel + %35 = OpLoad %2 %33 + %36 = OpLoad %2 %33 + %37 = OpFAdd %2 %35 %36 + OpReturnValue %37 + OpFunctionEnd + %40 = OpFunction %38 None %39 + %41 = OpLabel + %42 = OpVariable %26 Function + %50 = OpVariable %26 Function + %53 = OpVariable %26 Function + %43 = OpLoad %2 %4 + OpStore %42 %43 + %44 = OpFunctionCall %2 %28 %42 + %47 = OpAccessChain %46 %11 %45 + %48 = OpLoad %2 %47 + %49 = OpFAdd %2 %44 %48 + OpStore %8 %49 + %51 = OpLoad %2 %5 + OpStore %50 %51 + %52 = OpFunctionCall %2 %32 %50 + %54 = OpLoad %2 %6 + OpStore %53 %54 + %55 = OpFunctionCall %2 %28 %53 + %56 = OpFAdd %2 %52 %55 + %57 = OpAccessChain %7 %25 %45 + OpStore %57 %56 ++%63 = OpAccessChain %7 %25 %61 ++%64 = OpLoad %2 %63 ++%65 = OpCompositeExtract %1 %64 0 ++%66 = OpCompositeExtract %1 %64 1 ++%67 = OpCompositeExtract %1 %64 2 ++%68 = OpCompositeExtract %1 %64 3 ++%70 = OpFNegate %1 %65 ++%71 = OpFAdd %1 %67 %68 ++%72 = OpFMul %1 %71 %62 ++%69 = OpCompositeConstruct %2 %66 %70 %72 %68 ++OpStore %63 %69 + OpReturn + OpFunctionEnd +)"; + Options options; + options.ignore_set_binding = true; + options.ignore_location = true; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/different_decorations_vertex_dst.spvasm b/test/diff/diff_files/different_decorations_vertex_dst.spvasm new file mode 100644 index 00000000..33c6a9c1 --- /dev/null +++ b/test/diff/diff_files/different_decorations_vertex_dst.spvasm @@ -0,0 +1,159 @@ +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %25 +OpSource GLSL 450 +OpName %4 "_ub" +OpName %5 "_uc" +OpName %6 "_ud" +OpName %8 "_ue" +OpName %9 "defaultUniformsVS" +OpMemberName %9 0 "_ua" +OpName %11 "" +OpName %16 "ANGLEDepthRangeParams" +OpMemberName %16 0 "near" +OpMemberName %16 1 "far" +OpMemberName %16 2 "diff" +OpMemberName %16 3 "reserved" +OpName %17 "ANGLEUniformBlock" +OpMemberName %17 0 "viewport" +OpMemberName %17 1 "clipDistancesEnabled" +OpMemberName %17 2 "xfbActiveUnpaused" +OpMemberName %17 3 "xfbVerticesPerInstance" +OpMemberName %17 4 "numSamples" +OpMemberName %17 5 "xfbBufferOffsets" +OpMemberName %17 6 "acbBufferOffsets" +OpMemberName %17 7 "depthRange" +OpName %19 "ANGLEUniforms" +OpName %23 "gl_PerVertex" +OpMemberName %23 0 "gl_Position" +OpName %25 "" +OpName %29 "_ua" +OpName %28 "_uf" +OpName %33 "_uf" +OpName %32 "_ug" +OpName %40 "main" +OpName %42 "param" +OpName %50 "param" +OpName %53 "param" +OpDecorate %4 Location 1 +OpDecorate %5 Location 2 +OpDecorate %6 Location 0 +OpDecorate %8 Location 1 +OpMemberDecorate %9 0 Offset 0 +OpDecorate %9 Block +OpDecorate %11 DescriptorSet 0 +OpDecorate %11 Binding 1 +OpMemberDecorate %16 0 Offset 0 +OpMemberDecorate %16 1 Offset 4 +OpMemberDecorate %16 2 Offset 8 +OpMemberDecorate %16 3 Offset 12 +OpMemberDecorate %17 0 Offset 0 +OpMemberDecorate %17 1 Offset 16 +OpMemberDecorate %17 2 Offset 20 +OpMemberDecorate %17 3 Offset 24 +OpMemberDecorate %17 4 Offset 28 +OpMemberDecorate %17 5 Offset 32 +OpMemberDecorate %17 6 Offset 48 +OpMemberDecorate %17 7 Offset 64 +OpMemberDecorate %17 2 RelaxedPrecision +OpMemberDecorate %17 4 RelaxedPrecision +OpDecorate %17 Block +OpDecorate %19 DescriptorSet 2 +OpDecorate %19 Binding 0 +OpMemberDecorate %23 0 BuiltIn Position +OpDecorate %23 Block +OpDecorate %28 RelaxedPrecision +OpDecorate %29 RelaxedPrecision +OpDecorate %31 RelaxedPrecision +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %35 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %44 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%9 = OpTypeStruct %2 +%12 = OpTypeInt 32 0 +%13 = OpTypeInt 32 1 +%14 = OpTypeVector %13 4 +%15 = OpTypeVector %12 4 +%16 = OpTypeStruct %1 %1 %1 %1 +%17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16 +%21 = OpConstant %12 8 +%22 = OpTypeArray %1 %21 +%23 = OpTypeStruct %2 +%38 = OpTypeVoid +%45 = OpConstant %12 0 +%58 = OpTypePointer Private %2 +%3 = OpTypePointer Input %2 +%59 = OpTypePointer Private %2 +%7 = OpTypePointer Output %2 +%10 = OpTypePointer Uniform %9 +%18 = OpTypePointer Uniform %17 +%24 = OpTypePointer Output %23 +%26 = OpTypePointer Function %2 +%46 = OpTypePointer Uniform %2 +%27 = OpTypeFunction %2 %26 +%39 = OpTypeFunction %38 +%4 = OpVariable %3 Input +%5 = OpVariable %3 Input +%6 = OpVariable %3 Input +%8 = OpVariable %7 Output +%11 = OpVariable %10 Uniform +%19 = OpVariable %18 Uniform +%20 = OpVariable %59 Private +%25 = OpVariable %24 Output +%60 = OpConstant %13 0 +%61 = OpConstant %1 0.5 +%28 = OpFunction %2 None %27 +%29 = OpFunctionParameter %26 +%30 = OpLabel +%31 = OpLoad %2 %29 +OpReturnValue %31 +OpFunctionEnd +%32 = OpFunction %2 None %27 +%33 = OpFunctionParameter %26 +%34 = OpLabel +%35 = OpLoad %2 %33 +%36 = OpLoad %2 %33 +%37 = OpFAdd %2 %35 %36 +OpReturnValue %37 +OpFunctionEnd +%40 = OpFunction %38 None %39 +%41 = OpLabel +%42 = OpVariable %26 Function +%50 = OpVariable %26 Function +%53 = OpVariable %26 Function +%43 = OpLoad %2 %4 +OpStore %42 %43 +%44 = OpFunctionCall %2 %28 %42 +%47 = OpAccessChain %46 %11 %45 +%48 = OpLoad %2 %47 +%49 = OpFAdd %2 %44 %48 +OpStore %8 %49 +%51 = OpLoad %2 %5 +OpStore %50 %51 +%52 = OpFunctionCall %2 %32 %50 +%54 = OpLoad %2 %6 +OpStore %53 %54 +%55 = OpFunctionCall %2 %28 %53 +%56 = OpFAdd %2 %52 %55 +%57 = OpAccessChain %7 %25 %45 +OpStore %57 %56 +%62 = OpAccessChain %7 %25 %60 +%63 = OpLoad %2 %62 +%64 = OpCompositeExtract %1 %63 0 +%65 = OpCompositeExtract %1 %63 1 +%66 = OpCompositeExtract %1 %63 2 +%67 = OpCompositeExtract %1 %63 3 +%69 = OpFNegate %1 %64 +%70 = OpFAdd %1 %66 %67 +%71 = OpFMul %1 %70 %61 +%68 = OpCompositeConstruct %2 %65 %69 %71 %67 +OpStore %62 %68 +OpReturn +OpFunctionEnd diff --git a/test/diff/diff_files/different_decorations_vertex_src.spvasm b/test/diff/diff_files/different_decorations_vertex_src.spvasm new file mode 100644 index 00000000..ce1680cb --- /dev/null +++ b/test/diff/diff_files/different_decorations_vertex_src.spvasm @@ -0,0 +1,155 @@ +;; Test where variable set/binding/location decorations are different between +;; src and dst vertex shaders. +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %20 %25 +OpSource GLSL 450 +OpName %4 "_ub" +OpName %5 "_uc" +OpName %6 "_ud" +OpName %8 "_ue" +OpName %9 "defaultUniformsVS" +OpMemberName %9 0 "_ua" +OpName %11 "" +OpName %16 "ANGLEDepthRangeParams" +OpMemberName %16 0 "near" +OpMemberName %16 1 "far" +OpMemberName %16 2 "diff" +OpMemberName %16 3 "reserved" +OpName %17 "ANGLEUniformBlock" +OpMemberName %17 0 "viewport" +OpMemberName %17 1 "clipDistancesEnabled" +OpMemberName %17 2 "xfbActiveUnpaused" +OpMemberName %17 3 "xfbVerticesPerInstance" +OpMemberName %17 4 "numSamples" +OpMemberName %17 5 "xfbBufferOffsets" +OpMemberName %17 6 "acbBufferOffsets" +OpMemberName %17 7 "depthRange" +OpName %19 "ANGLEUniforms" +OpName %20 "ANGLEXfbPosition" +OpName %23 "gl_PerVertex" +OpMemberName %23 0 "gl_Position" +OpMemberName %23 1 "gl_PointSize" +OpMemberName %23 2 "gl_ClipDistance" +OpMemberName %23 3 "gl_CullDistance" +OpName %25 "" +OpName %29 "_ua" +OpName %28 "_uf" +OpName %33 "_uf" +OpName %32 "_ug" +OpName %40 "main" +OpName %42 "param" +OpName %50 "param" +OpName %53 "param" +OpDecorate %4 Location 0 +OpDecorate %5 Location 1 +OpDecorate %6 Location 2 +OpDecorate %8 Location 0 +OpMemberDecorate %9 0 Offset 0 +OpDecorate %9 Block +OpDecorate %11 DescriptorSet 0 +OpDecorate %11 Binding 0 +OpMemberDecorate %16 0 Offset 0 +OpMemberDecorate %16 1 Offset 4 +OpMemberDecorate %16 2 Offset 8 +OpMemberDecorate %16 3 Offset 12 +OpMemberDecorate %17 0 Offset 0 +OpMemberDecorate %17 1 Offset 16 +OpMemberDecorate %17 2 Offset 20 +OpMemberDecorate %17 3 Offset 24 +OpMemberDecorate %17 4 Offset 28 +OpMemberDecorate %17 5 Offset 32 +OpMemberDecorate %17 6 Offset 48 +OpMemberDecorate %17 7 Offset 64 +OpMemberDecorate %17 2 RelaxedPrecision +OpMemberDecorate %17 4 RelaxedPrecision +OpDecorate %17 Block +OpDecorate %19 DescriptorSet 0 +OpDecorate %19 Binding 1 +OpDecorate %20 Location 1 +OpMemberDecorate %23 0 BuiltIn Position +OpMemberDecorate %23 1 BuiltIn PointSize +OpMemberDecorate %23 2 BuiltIn ClipDistance +OpMemberDecorate %23 3 BuiltIn CullDistance +OpDecorate %23 Block +OpDecorate %28 RelaxedPrecision +OpDecorate %29 RelaxedPrecision +OpDecorate %31 RelaxedPrecision +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %35 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %44 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%9 = OpTypeStruct %2 +%12 = OpTypeInt 32 0 +%13 = OpTypeInt 32 1 +%14 = OpTypeVector %13 4 +%15 = OpTypeVector %12 4 +%16 = OpTypeStruct %1 %1 %1 %1 +%17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16 +%21 = OpConstant %12 8 +%22 = OpTypeArray %1 %21 +%23 = OpTypeStruct %2 %1 %22 %22 +%38 = OpTypeVoid +%45 = OpConstant %12 0 +%3 = OpTypePointer Input %2 +%7 = OpTypePointer Output %2 +%10 = OpTypePointer Uniform %9 +%18 = OpTypePointer Uniform %17 +%24 = OpTypePointer Output %23 +%26 = OpTypePointer Function %2 +%46 = OpTypePointer Uniform %2 +%27 = OpTypeFunction %2 %26 +%39 = OpTypeFunction %38 +%4 = OpVariable %3 Input +%5 = OpVariable %3 Input +%6 = OpVariable %3 Input +%8 = OpVariable %7 Output +%11 = OpVariable %10 Uniform +%19 = OpVariable %18 Uniform +%20 = OpVariable %7 Output +%25 = OpVariable %24 Output +%28 = OpFunction %2 None %27 +%29 = OpFunctionParameter %26 +%30 = OpLabel +%31 = OpLoad %2 %29 +OpReturnValue %31 +OpFunctionEnd +%32 = OpFunction %2 None %27 +%33 = OpFunctionParameter %26 +%34 = OpLabel +%35 = OpLoad %2 %33 +%36 = OpLoad %2 %33 +%37 = OpFAdd %2 %35 %36 +OpReturnValue %37 +OpFunctionEnd +%40 = OpFunction %38 None %39 +%41 = OpLabel +%42 = OpVariable %26 Function +%50 = OpVariable %26 Function +%53 = OpVariable %26 Function +%43 = OpLoad %2 %4 +OpStore %42 %43 +%44 = OpFunctionCall %2 %28 %42 +%47 = OpAccessChain %46 %11 %45 +%48 = OpLoad %2 %47 +%49 = OpFAdd %2 %44 %48 +OpStore %8 %49 +%51 = OpLoad %2 %5 +OpStore %50 %51 +%52 = OpFunctionCall %2 %32 %50 +%54 = OpLoad %2 %6 +OpStore %53 %54 +%55 = OpFunctionCall %2 %28 %53 +%56 = OpFAdd %2 %52 %55 +%57 = OpAccessChain %7 %25 %45 +OpStore %57 %56 +OpReturn +OpFunctionEnd + diff --git a/test/diff/diff_files/different_function_parameter_count_autogen.cpp b/test/diff/diff_files/different_function_parameter_count_autogen.cpp new file mode 100644 index 00000000..3a077fb0 --- /dev/null +++ b/test/diff/diff_files/different_function_parameter_count_autogen.cpp @@ -0,0 +1,339 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test where src and dst have a function with different parameter count. +constexpr char kSrc[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 25 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %20 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + OpName %11 "f(vf2;" + OpName %10 "v" + OpName %20 "o" + OpName %23 "param" + OpDecorate %20 RelaxedPrecision + OpDecorate %20 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %9 = OpTypeFunction %7 %8 + %14 = OpConstant %6 0.5 + %15 = OpConstantComposite %7 %14 %14 + %19 = OpTypePointer Output %7 + %20 = OpVariable %19 Output + %21 = OpConstant %6 0 + %22 = OpConstantComposite %7 %21 %21 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %23 = OpVariable %8 Function + OpStore %23 %22 + %24 = OpFunctionCall %7 %11 %23 + OpStore %20 %24 + OpReturn + OpFunctionEnd + %11 = OpFunction %7 None %9 + %10 = OpFunctionParameter %8 + %12 = OpLabel + %13 = OpLoad %7 %10 + %16 = OpFAdd %7 %13 %15 + OpReturnValue %16 + OpFunctionEnd)"; +constexpr char kDst[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 28 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %20 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + OpName %12 "f(vf2;vf2;" + OpName %10 "v" + OpName %11 "v2" + OpName %20 "o" + OpName %25 "param" + OpName %26 "param" + OpDecorate %20 RelaxedPrecision + OpDecorate %20 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %9 = OpTypeFunction %7 %8 %8 + %19 = OpTypePointer Output %7 + %20 = OpVariable %19 Output + %21 = OpConstant %6 0 + %22 = OpConstantComposite %7 %21 %21 + %23 = OpConstant %6 0.5 + %24 = OpConstantComposite %7 %23 %23 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %25 = OpVariable %8 Function + %26 = OpVariable %8 Function + OpStore %25 %22 + OpStore %26 %24 + %27 = OpFunctionCall %7 %12 %25 %26 + OpStore %20 %27 + OpReturn + OpFunctionEnd + %12 = OpFunction %7 None %9 + %10 = OpFunctionParameter %8 + %11 = OpFunctionParameter %8 + %13 = OpLabel + %14 = OpLoad %7 %10 + %15 = OpLoad %7 %11 + %16 = OpFAdd %7 %14 %15 + OpReturnValue %16 + OpFunctionEnd + +)"; + +TEST(DiffTest, DifferentFunctionParameterCount) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 25 ++; Bound: 33 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %20 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" +-OpName %11 "f(vf2;" ++OpName %11 "f(vf2;vf2;" + OpName %10 "v" ++OpName %26 "v2" + OpName %20 "o" + OpName %23 "param" ++OpName %31 "param" + OpDecorate %20 RelaxedPrecision + OpDecorate %20 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 +-%9 = OpTypeFunction %7 %8 ++%25 = OpTypeFunction %7 %8 %8 + %14 = OpConstant %6 0.5 + %15 = OpConstantComposite %7 %14 %14 + %19 = OpTypePointer Output %7 + %20 = OpVariable %19 Output + %21 = OpConstant %6 0 + %22 = OpConstantComposite %7 %21 %21 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %23 = OpVariable %8 Function ++%31 = OpVariable %8 Function + OpStore %23 %22 +-%24 = OpFunctionCall %7 %11 %23 ++OpStore %31 %15 ++%32 = OpFunctionCall %7 %11 %23 %31 +-OpStore %20 %24 ++OpStore %20 %32 + OpReturn + OpFunctionEnd +-%11 = OpFunction %7 None %9 ++%11 = OpFunction %7 None %25 + %10 = OpFunctionParameter %8 ++%26 = OpFunctionParameter %8 + %12 = OpLabel + %13 = OpLoad %7 %10 +-%16 = OpFAdd %7 %13 %15 ++%27 = OpLoad %7 %26 ++%28 = OpFAdd %7 %13 %27 +-OpReturnValue %16 ++OpReturnValue %28 + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, DifferentFunctionParameterCountNoDebug) { + constexpr char kSrcNoDebug[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 25 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %20 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpDecorate %20 RelaxedPrecision + OpDecorate %20 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %9 = OpTypeFunction %7 %8 + %14 = OpConstant %6 0.5 + %15 = OpConstantComposite %7 %14 %14 + %19 = OpTypePointer Output %7 + %20 = OpVariable %19 Output + %21 = OpConstant %6 0 + %22 = OpConstantComposite %7 %21 %21 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %23 = OpVariable %8 Function + OpStore %23 %22 + %24 = OpFunctionCall %7 %11 %23 + OpStore %20 %24 + OpReturn + OpFunctionEnd + %11 = OpFunction %7 None %9 + %10 = OpFunctionParameter %8 + %12 = OpLabel + %13 = OpLoad %7 %10 + %16 = OpFAdd %7 %13 %15 + OpReturnValue %16 + OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 28 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %20 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpDecorate %20 RelaxedPrecision + OpDecorate %20 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %9 = OpTypeFunction %7 %8 %8 + %19 = OpTypePointer Output %7 + %20 = OpVariable %19 Output + %21 = OpConstant %6 0 + %22 = OpConstantComposite %7 %21 %21 + %23 = OpConstant %6 0.5 + %24 = OpConstantComposite %7 %23 %23 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %25 = OpVariable %8 Function + %26 = OpVariable %8 Function + OpStore %25 %22 + OpStore %26 %24 + %27 = OpFunctionCall %7 %12 %25 %26 + OpStore %20 %27 + OpReturn + OpFunctionEnd + %12 = OpFunction %7 None %9 + %10 = OpFunctionParameter %8 + %11 = OpFunctionParameter %8 + %13 = OpLabel + %14 = OpLoad %7 %10 + %15 = OpLoad %7 %11 + %16 = OpFAdd %7 %14 %15 + OpReturnValue %16 + OpFunctionEnd + +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 25 ++; Bound: 34 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %20 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpDecorate %20 RelaxedPrecision + OpDecorate %20 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 +-%9 = OpTypeFunction %7 %8 ++%25 = OpTypeFunction %7 %8 %8 + %14 = OpConstant %6 0.5 + %15 = OpConstantComposite %7 %14 %14 + %19 = OpTypePointer Output %7 + %20 = OpVariable %19 Output + %21 = OpConstant %6 0 + %22 = OpConstantComposite %7 %21 %21 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %23 = OpVariable %8 Function ++%32 = OpVariable %8 Function + OpStore %23 %22 +-%24 = OpFunctionCall %7 %11 %23 ++OpStore %32 %15 ++%33 = OpFunctionCall %7 %11 %23 %32 +-OpStore %20 %24 ++OpStore %20 %33 + OpReturn + OpFunctionEnd +-%11 = OpFunction %7 None %9 ++%11 = OpFunction %7 None %25 +-%10 = OpFunctionParameter %8 ++%26 = OpFunctionParameter %8 ++%27 = OpFunctionParameter %8 + %12 = OpLabel +-%13 = OpLoad %7 %10 ++%13 = OpLoad %7 %26 +-%16 = OpFAdd %7 %13 %15 ++%28 = OpLoad %7 %27 ++%29 = OpFAdd %7 %13 %28 +-OpReturnValue %16 ++OpReturnValue %29 + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/different_function_parameter_count_dst.spvasm b/test/diff/diff_files/different_function_parameter_count_dst.spvasm new file mode 100644 index 00000000..92442601 --- /dev/null +++ b/test/diff/diff_files/different_function_parameter_count_dst.spvasm @@ -0,0 +1,52 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 28 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %20 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + OpName %12 "f(vf2;vf2;" + OpName %10 "v" + OpName %11 "v2" + OpName %20 "o" + OpName %25 "param" + OpName %26 "param" + OpDecorate %20 RelaxedPrecision + OpDecorate %20 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %9 = OpTypeFunction %7 %8 %8 + %19 = OpTypePointer Output %7 + %20 = OpVariable %19 Output + %21 = OpConstant %6 0 + %22 = OpConstantComposite %7 %21 %21 + %23 = OpConstant %6 0.5 + %24 = OpConstantComposite %7 %23 %23 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %25 = OpVariable %8 Function + %26 = OpVariable %8 Function + OpStore %25 %22 + OpStore %26 %24 + %27 = OpFunctionCall %7 %12 %25 %26 + OpStore %20 %27 + OpReturn + OpFunctionEnd + %12 = OpFunction %7 None %9 + %10 = OpFunctionParameter %8 + %11 = OpFunctionParameter %8 + %13 = OpLabel + %14 = OpLoad %7 %10 + %15 = OpLoad %7 %11 + %16 = OpFAdd %7 %14 %15 + OpReturnValue %16 + OpFunctionEnd + diff --git a/test/diff/diff_files/different_function_parameter_count_src.spvasm b/test/diff/diff_files/different_function_parameter_count_src.spvasm new file mode 100644 index 00000000..6ee2408a --- /dev/null +++ b/test/diff/diff_files/different_function_parameter_count_src.spvasm @@ -0,0 +1,46 @@ +;; Test where src and dst have a function with different parameter count. +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 25 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %20 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + OpName %11 "f(vf2;" + OpName %10 "v" + OpName %20 "o" + OpName %23 "param" + OpDecorate %20 RelaxedPrecision + OpDecorate %20 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %9 = OpTypeFunction %7 %8 + %14 = OpConstant %6 0.5 + %15 = OpConstantComposite %7 %14 %14 + %19 = OpTypePointer Output %7 + %20 = OpVariable %19 Output + %21 = OpConstant %6 0 + %22 = OpConstantComposite %7 %21 %21 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %23 = OpVariable %8 Function + OpStore %23 %22 + %24 = OpFunctionCall %7 %11 %23 + OpStore %20 %24 + OpReturn + OpFunctionEnd + %11 = OpFunction %7 None %9 + %10 = OpFunctionParameter %8 + %12 = OpLabel + %13 = OpLoad %7 %10 + %16 = OpFAdd %7 %13 %15 + OpReturnValue %16 + OpFunctionEnd diff --git a/test/diff/diff_files/extra_if_block_autogen.cpp b/test/diff/diff_files/extra_if_block_autogen.cpp new file mode 100644 index 00000000..4f913198 --- /dev/null +++ b/test/diff/diff_files/extra_if_block_autogen.cpp @@ -0,0 +1,867 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test where src has an extra if block in one function, and dst has an extra +// if block in another function. +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %63 %68 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "f1(" + OpName %10 "f2(" + OpName %13 "v" + OpName %16 "Buffer" + OpMemberName %16 0 "flag1" + OpMemberName %16 1 "flag2" + OpName %18 "" + OpName %45 "v" + OpName %63 "color" + OpName %68 "v" + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpMemberDecorate %16 0 RelaxedPrecision + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 RelaxedPrecision + OpMemberDecorate %16 1 Offset 4 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + OpDecorate %23 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %50 RelaxedPrecision + OpDecorate %51 RelaxedPrecision + OpDecorate %54 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %63 RelaxedPrecision + OpDecorate %63 Location 0 + OpDecorate %64 RelaxedPrecision + OpDecorate %65 RelaxedPrecision + OpDecorate %66 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %68 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeFunction %6 + %12 = OpTypePointer Function %6 + %14 = OpConstant %6 0 + %15 = OpTypeInt 32 0 + %16 = OpTypeStruct %15 %15 + %17 = OpTypePointer Uniform %16 + %18 = OpVariable %17 Uniform + %19 = OpTypeInt 32 1 + %20 = OpConstant %19 0 + %21 = OpTypePointer Uniform %15 + %24 = OpConstant %15 0 + %25 = OpTypeBool + %29 = OpConstant %6 1 + %32 = OpConstant %19 1 + %49 = OpConstant %6 10 + %52 = OpConstant %6 0.5 + %53 = OpConstant %6 0.699999988 + %61 = OpTypeVector %6 4 + %62 = OpTypePointer Output %61 + %63 = OpVariable %62 Output + %67 = OpTypePointer Input %6 + %68 = OpVariable %67 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %64 = OpFunctionCall %6 %8 + %65 = OpFunctionCall %6 %10 + %66 = OpCompositeConstruct %61 %64 %65 %14 %29 + OpStore %63 %66 + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %13 = OpVariable %12 Function + OpStore %13 %14 + %22 = OpAccessChain %21 %18 %20 + %23 = OpLoad %15 %22 + %26 = OpINotEqual %25 %23 %24 + OpSelectionMerge %28 None + OpBranchConditional %26 %27 %28 + %27 = OpLabel + %30 = OpLoad %6 %13 + %31 = OpFAdd %6 %30 %29 + OpStore %13 %31 + OpBranch %28 + %28 = OpLabel + %33 = OpAccessChain %21 %18 %32 + %34 = OpLoad %15 %33 + %35 = OpConvertUToF %6 %34 + %36 = OpExtInst %6 %1 Log2 %35 + %37 = OpLoad %6 %13 + %38 = OpFAdd %6 %37 %36 + OpStore %13 %38 + %39 = OpLoad %6 %13 + %40 = OpLoad %6 %13 + %41 = OpExtInst %6 %1 Sqrt %40 + %42 = OpFSub %6 %39 %41 + OpReturnValue %42 + OpFunctionEnd + %10 = OpFunction %6 None %7 + %11 = OpLabel + %45 = OpVariable %12 Function + %46 = OpAccessChain %21 %18 %20 + %47 = OpLoad %15 %46 + %48 = OpConvertUToF %6 %47 + %50 = OpFDiv %6 %48 %49 + OpStore %45 %50 + %51 = OpLoad %6 %45 + %54 = OpExtInst %6 %1 FClamp %51 %52 %53 + %55 = OpLoad %6 %45 + %56 = OpFMul %6 %55 %54 + OpStore %45 %56 + %57 = OpLoad %6 %45 + %58 = OpExtInst %6 %1 Exp %57 + OpReturnValue %58 + OpFunctionEnd +)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %63 %69 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "f1(" + OpName %10 "f2(" + OpName %13 "v" + OpName %16 "Buffer" + OpMemberName %16 0 "flag1" + OpMemberName %16 1 "flag2" + OpName %18 "" + OpName %34 "v" + OpName %63 "color" + OpName %69 "v" + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpMemberDecorate %16 0 RelaxedPrecision + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 RelaxedPrecision + OpMemberDecorate %16 1 Offset 4 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + OpDecorate %23 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %46 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %63 RelaxedPrecision + OpDecorate %63 Location 0 + OpDecorate %64 RelaxedPrecision + OpDecorate %65 RelaxedPrecision + OpDecorate %67 RelaxedPrecision + OpDecorate %69 RelaxedPrecision + OpDecorate %69 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeFunction %6 + %12 = OpTypePointer Function %6 + %14 = OpConstant %6 0 + %15 = OpTypeInt 32 0 + %16 = OpTypeStruct %15 %15 + %17 = OpTypePointer Uniform %16 + %18 = OpVariable %17 Uniform + %19 = OpTypeInt 32 1 + %20 = OpConstant %19 1 + %21 = OpTypePointer Uniform %15 + %35 = OpConstant %19 0 + %39 = OpConstant %6 10 + %42 = OpConstant %6 0.5 + %43 = OpConstant %6 0.699999988 + %49 = OpConstant %15 0 + %50 = OpTypeBool + %54 = OpConstant %6 0.100000001 + %61 = OpTypeVector %6 4 + %62 = OpTypePointer Output %61 + %63 = OpVariable %62 Output + %66 = OpConstant %6 1 + %68 = OpTypePointer Input %6 + %69 = OpVariable %68 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %64 = OpFunctionCall %6 %8 + %65 = OpFunctionCall %6 %10 + %67 = OpCompositeConstruct %61 %64 %65 %14 %66 + OpStore %63 %67 + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %13 = OpVariable %12 Function + OpStore %13 %14 + %22 = OpAccessChain %21 %18 %20 + %23 = OpLoad %15 %22 + %24 = OpConvertUToF %6 %23 + %25 = OpExtInst %6 %1 Log2 %24 + %26 = OpLoad %6 %13 + %27 = OpFAdd %6 %26 %25 + OpStore %13 %27 + %28 = OpLoad %6 %13 + %29 = OpLoad %6 %13 + %30 = OpExtInst %6 %1 Sqrt %29 + %31 = OpFSub %6 %28 %30 + OpReturnValue %31 + OpFunctionEnd + %10 = OpFunction %6 None %7 + %11 = OpLabel + %34 = OpVariable %12 Function + %36 = OpAccessChain %21 %18 %35 + %37 = OpLoad %15 %36 + %38 = OpConvertUToF %6 %37 + %40 = OpFDiv %6 %38 %39 + OpStore %34 %40 + %41 = OpLoad %6 %34 + %44 = OpExtInst %6 %1 FClamp %41 %42 %43 + %45 = OpLoad %6 %34 + %46 = OpFMul %6 %45 %44 + OpStore %34 %46 + %47 = OpAccessChain %21 %18 %20 + %48 = OpLoad %15 %47 + %51 = OpINotEqual %50 %48 %49 + OpSelectionMerge %53 None + OpBranchConditional %51 %52 %53 + %52 = OpLabel + %55 = OpLoad %6 %34 + %56 = OpFSub %6 %55 %54 + OpStore %34 %56 + OpBranch %53 + %53 = OpLabel + %57 = OpLoad %6 %34 + %58 = OpExtInst %6 %1 Exp %57 + OpReturnValue %58 + OpFunctionEnd + +)"; + +TEST(DiffTest, ExtraIfBlock) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 69 ++; Bound: 81 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %63 %68 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "f1(" + OpName %10 "f2(" + OpName %13 "v" + OpName %16 "Buffer" + OpMemberName %16 0 "flag1" + OpMemberName %16 1 "flag2" + OpName %18 "" + OpName %45 "v" + OpName %63 "color" + OpName %68 "v" + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpMemberDecorate %16 0 RelaxedPrecision + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 RelaxedPrecision + OpMemberDecorate %16 1 Offset 4 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 +-OpDecorate %23 RelaxedPrecision +-OpDecorate %30 RelaxedPrecision +-OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %50 RelaxedPrecision + OpDecorate %51 RelaxedPrecision + OpDecorate %54 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision ++OpDecorate %72 RelaxedPrecision + OpDecorate %57 RelaxedPrecision ++OpDecorate %77 RelaxedPrecision ++OpDecorate %78 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %63 RelaxedPrecision + OpDecorate %63 Location 0 + OpDecorate %64 RelaxedPrecision + OpDecorate %65 RelaxedPrecision + OpDecorate %66 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %68 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeFunction %6 + %12 = OpTypePointer Function %6 + %14 = OpConstant %6 0 + %15 = OpTypeInt 32 0 + %16 = OpTypeStruct %15 %15 + %17 = OpTypePointer Uniform %16 + %18 = OpVariable %17 Uniform + %19 = OpTypeInt 32 1 + %20 = OpConstant %19 0 + %21 = OpTypePointer Uniform %15 + %24 = OpConstant %15 0 + %25 = OpTypeBool + %29 = OpConstant %6 1 + %32 = OpConstant %19 1 + %49 = OpConstant %6 10 + %52 = OpConstant %6 0.5 ++%76 = OpConstant %6 0.100000001 + %53 = OpConstant %6 0.699999988 + %61 = OpTypeVector %6 4 + %62 = OpTypePointer Output %61 + %63 = OpVariable %62 Output + %67 = OpTypePointer Input %6 + %68 = OpVariable %67 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %64 = OpFunctionCall %6 %8 + %65 = OpFunctionCall %6 %10 + %66 = OpCompositeConstruct %61 %64 %65 %14 %29 + OpStore %63 %66 + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %13 = OpVariable %12 Function + OpStore %13 %14 +-%22 = OpAccessChain %21 %18 %20 +-%23 = OpLoad %15 %22 +-%26 = OpINotEqual %25 %23 %24 +-OpSelectionMerge %28 None +-OpBranchConditional %26 %27 %28 +-%27 = OpLabel +-%30 = OpLoad %6 %13 +-%31 = OpFAdd %6 %30 %29 +-OpStore %13 %31 +-OpBranch %28 +-%28 = OpLabel + %33 = OpAccessChain %21 %18 %32 + %34 = OpLoad %15 %33 + %35 = OpConvertUToF %6 %34 + %36 = OpExtInst %6 %1 Log2 %35 + %37 = OpLoad %6 %13 + %38 = OpFAdd %6 %37 %36 + OpStore %13 %38 + %39 = OpLoad %6 %13 + %40 = OpLoad %6 %13 + %41 = OpExtInst %6 %1 Sqrt %40 + %42 = OpFSub %6 %39 %41 + OpReturnValue %42 + OpFunctionEnd + %10 = OpFunction %6 None %7 + %11 = OpLabel + %45 = OpVariable %12 Function + %46 = OpAccessChain %21 %18 %20 + %47 = OpLoad %15 %46 + %48 = OpConvertUToF %6 %47 + %50 = OpFDiv %6 %48 %49 + OpStore %45 %50 + %51 = OpLoad %6 %45 + %54 = OpExtInst %6 %1 FClamp %51 %52 %53 + %55 = OpLoad %6 %45 + %56 = OpFMul %6 %55 %54 + OpStore %45 %56 ++%71 = OpAccessChain %21 %18 %32 ++%72 = OpLoad %15 %71 ++%73 = OpINotEqual %25 %72 %24 ++OpSelectionMerge %75 None ++OpBranchConditional %73 %74 %75 ++%74 = OpLabel + %57 = OpLoad %6 %45 ++%77 = OpFSub %6 %57 %76 ++OpStore %45 %77 ++OpBranch %75 ++%75 = OpLabel ++%78 = OpLoad %6 %45 +-%58 = OpExtInst %6 %1 Exp %57 ++%58 = OpExtInst %6 %1 Exp %78 + OpReturnValue %58 + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, ExtraIfBlockNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %63 %68 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpMemberDecorate %16 0 RelaxedPrecision + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 RelaxedPrecision + OpMemberDecorate %16 1 Offset 4 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + OpDecorate %23 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %50 RelaxedPrecision + OpDecorate %51 RelaxedPrecision + OpDecorate %54 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %63 RelaxedPrecision + OpDecorate %63 Location 0 + OpDecorate %64 RelaxedPrecision + OpDecorate %65 RelaxedPrecision + OpDecorate %66 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %68 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeFunction %6 + %12 = OpTypePointer Function %6 + %14 = OpConstant %6 0 + %15 = OpTypeInt 32 0 + %16 = OpTypeStruct %15 %15 + %17 = OpTypePointer Uniform %16 + %18 = OpVariable %17 Uniform + %19 = OpTypeInt 32 1 + %20 = OpConstant %19 0 + %21 = OpTypePointer Uniform %15 + %24 = OpConstant %15 0 + %25 = OpTypeBool + %29 = OpConstant %6 1 + %32 = OpConstant %19 1 + %49 = OpConstant %6 10 + %52 = OpConstant %6 0.5 + %53 = OpConstant %6 0.699999988 + %61 = OpTypeVector %6 4 + %62 = OpTypePointer Output %61 + %63 = OpVariable %62 Output + %67 = OpTypePointer Input %6 + %68 = OpVariable %67 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %64 = OpFunctionCall %6 %8 + %65 = OpFunctionCall %6 %10 + %66 = OpCompositeConstruct %61 %64 %65 %14 %29 + OpStore %63 %66 + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %13 = OpVariable %12 Function + OpStore %13 %14 + %22 = OpAccessChain %21 %18 %20 + %23 = OpLoad %15 %22 + %26 = OpINotEqual %25 %23 %24 + OpSelectionMerge %28 None + OpBranchConditional %26 %27 %28 + %27 = OpLabel + %30 = OpLoad %6 %13 + %31 = OpFAdd %6 %30 %29 + OpStore %13 %31 + OpBranch %28 + %28 = OpLabel + %33 = OpAccessChain %21 %18 %32 + %34 = OpLoad %15 %33 + %35 = OpConvertUToF %6 %34 + %36 = OpExtInst %6 %1 Log2 %35 + %37 = OpLoad %6 %13 + %38 = OpFAdd %6 %37 %36 + OpStore %13 %38 + %39 = OpLoad %6 %13 + %40 = OpLoad %6 %13 + %41 = OpExtInst %6 %1 Sqrt %40 + %42 = OpFSub %6 %39 %41 + OpReturnValue %42 + OpFunctionEnd + %10 = OpFunction %6 None %7 + %11 = OpLabel + %45 = OpVariable %12 Function + %46 = OpAccessChain %21 %18 %20 + %47 = OpLoad %15 %46 + %48 = OpConvertUToF %6 %47 + %50 = OpFDiv %6 %48 %49 + OpStore %45 %50 + %51 = OpLoad %6 %45 + %54 = OpExtInst %6 %1 FClamp %51 %52 %53 + %55 = OpLoad %6 %45 + %56 = OpFMul %6 %55 %54 + OpStore %45 %56 + %57 = OpLoad %6 %45 + %58 = OpExtInst %6 %1 Exp %57 + OpReturnValue %58 + OpFunctionEnd + +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %63 %69 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpMemberDecorate %16 0 RelaxedPrecision + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 RelaxedPrecision + OpMemberDecorate %16 1 Offset 4 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + OpDecorate %23 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %46 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %63 RelaxedPrecision + OpDecorate %63 Location 0 + OpDecorate %64 RelaxedPrecision + OpDecorate %65 RelaxedPrecision + OpDecorate %67 RelaxedPrecision + OpDecorate %69 RelaxedPrecision + OpDecorate %69 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeFunction %6 + %12 = OpTypePointer Function %6 + %14 = OpConstant %6 0 + %15 = OpTypeInt 32 0 + %16 = OpTypeStruct %15 %15 + %17 = OpTypePointer Uniform %16 + %18 = OpVariable %17 Uniform + %19 = OpTypeInt 32 1 + %20 = OpConstant %19 1 + %21 = OpTypePointer Uniform %15 + %35 = OpConstant %19 0 + %39 = OpConstant %6 10 + %42 = OpConstant %6 0.5 + %43 = OpConstant %6 0.699999988 + %49 = OpConstant %15 0 + %50 = OpTypeBool + %54 = OpConstant %6 0.100000001 + %61 = OpTypeVector %6 4 + %62 = OpTypePointer Output %61 + %63 = OpVariable %62 Output + %66 = OpConstant %6 1 + %68 = OpTypePointer Input %6 + %69 = OpVariable %68 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %64 = OpFunctionCall %6 %8 + %65 = OpFunctionCall %6 %10 + %67 = OpCompositeConstruct %61 %64 %65 %14 %66 + OpStore %63 %67 + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %13 = OpVariable %12 Function + OpStore %13 %14 + %22 = OpAccessChain %21 %18 %20 + %23 = OpLoad %15 %22 + %24 = OpConvertUToF %6 %23 + %25 = OpExtInst %6 %1 Log2 %24 + %26 = OpLoad %6 %13 + %27 = OpFAdd %6 %26 %25 + OpStore %13 %27 + %28 = OpLoad %6 %13 + %29 = OpLoad %6 %13 + %30 = OpExtInst %6 %1 Sqrt %29 + %31 = OpFSub %6 %28 %30 + OpReturnValue %31 + OpFunctionEnd + %10 = OpFunction %6 None %7 + %11 = OpLabel + %34 = OpVariable %12 Function + %36 = OpAccessChain %21 %18 %35 + %37 = OpLoad %15 %36 + %38 = OpConvertUToF %6 %37 + %40 = OpFDiv %6 %38 %39 + OpStore %34 %40 + %41 = OpLoad %6 %34 + %44 = OpExtInst %6 %1 FClamp %41 %42 %43 + %45 = OpLoad %6 %34 + %46 = OpFMul %6 %45 %44 + OpStore %34 %46 + %47 = OpAccessChain %21 %18 %20 + %48 = OpLoad %15 %47 + %51 = OpINotEqual %50 %48 %49 + OpSelectionMerge %53 None + OpBranchConditional %51 %52 %53 + %52 = OpLabel + %55 = OpLoad %6 %34 + %56 = OpFSub %6 %55 %54 + OpStore %34 %56 + OpBranch %53 + %53 = OpLabel + %57 = OpLoad %6 %34 + %58 = OpExtInst %6 %1 Exp %57 + OpReturnValue %58 + OpFunctionEnd + +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 69 ++; Bound: 81 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %63 %68 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpMemberDecorate %16 0 RelaxedPrecision + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 RelaxedPrecision + OpMemberDecorate %16 1 Offset 4 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 +-OpDecorate %23 RelaxedPrecision +-OpDecorate %30 RelaxedPrecision +-OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %50 RelaxedPrecision + OpDecorate %51 RelaxedPrecision + OpDecorate %54 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision ++OpDecorate %72 RelaxedPrecision + OpDecorate %57 RelaxedPrecision ++OpDecorate %77 RelaxedPrecision ++OpDecorate %78 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %63 RelaxedPrecision + OpDecorate %63 Location 0 + OpDecorate %64 RelaxedPrecision + OpDecorate %65 RelaxedPrecision + OpDecorate %66 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %68 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeFunction %6 + %12 = OpTypePointer Function %6 + %14 = OpConstant %6 0 + %15 = OpTypeInt 32 0 + %16 = OpTypeStruct %15 %15 + %17 = OpTypePointer Uniform %16 + %18 = OpVariable %17 Uniform + %19 = OpTypeInt 32 1 + %20 = OpConstant %19 0 + %21 = OpTypePointer Uniform %15 + %24 = OpConstant %15 0 + %25 = OpTypeBool + %29 = OpConstant %6 1 + %32 = OpConstant %19 1 + %49 = OpConstant %6 10 + %52 = OpConstant %6 0.5 ++%76 = OpConstant %6 0.100000001 + %53 = OpConstant %6 0.699999988 + %61 = OpTypeVector %6 4 + %62 = OpTypePointer Output %61 + %63 = OpVariable %62 Output + %67 = OpTypePointer Input %6 + %68 = OpVariable %67 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %64 = OpFunctionCall %6 %8 + %65 = OpFunctionCall %6 %10 + %66 = OpCompositeConstruct %61 %64 %65 %14 %29 + OpStore %63 %66 + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %13 = OpVariable %12 Function + OpStore %13 %14 +-%22 = OpAccessChain %21 %18 %20 +-%23 = OpLoad %15 %22 +-%26 = OpINotEqual %25 %23 %24 +-OpSelectionMerge %28 None +-OpBranchConditional %26 %27 %28 +-%27 = OpLabel +-%30 = OpLoad %6 %13 +-%31 = OpFAdd %6 %30 %29 +-OpStore %13 %31 +-OpBranch %28 +-%28 = OpLabel + %33 = OpAccessChain %21 %18 %32 + %34 = OpLoad %15 %33 + %35 = OpConvertUToF %6 %34 + %36 = OpExtInst %6 %1 Log2 %35 + %37 = OpLoad %6 %13 + %38 = OpFAdd %6 %37 %36 + OpStore %13 %38 + %39 = OpLoad %6 %13 + %40 = OpLoad %6 %13 + %41 = OpExtInst %6 %1 Sqrt %40 + %42 = OpFSub %6 %39 %41 + OpReturnValue %42 + OpFunctionEnd + %10 = OpFunction %6 None %7 + %11 = OpLabel + %45 = OpVariable %12 Function + %46 = OpAccessChain %21 %18 %20 + %47 = OpLoad %15 %46 + %48 = OpConvertUToF %6 %47 + %50 = OpFDiv %6 %48 %49 + OpStore %45 %50 + %51 = OpLoad %6 %45 + %54 = OpExtInst %6 %1 FClamp %51 %52 %53 + %55 = OpLoad %6 %45 + %56 = OpFMul %6 %55 %54 + OpStore %45 %56 ++%71 = OpAccessChain %21 %18 %32 ++%72 = OpLoad %15 %71 ++%73 = OpINotEqual %25 %72 %24 ++OpSelectionMerge %75 None ++OpBranchConditional %73 %74 %75 ++%74 = OpLabel + %57 = OpLoad %6 %45 ++%77 = OpFSub %6 %57 %76 ++OpStore %45 %77 ++OpBranch %75 ++%75 = OpLabel ++%78 = OpLoad %6 %45 +-%58 = OpExtInst %6 %1 Exp %57 ++%58 = OpExtInst %6 %1 Exp %78 + OpReturnValue %58 + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/extra_if_block_dst.spvasm b/test/diff/diff_files/extra_if_block_dst.spvasm new file mode 100644 index 00000000..79bda830 --- /dev/null +++ b/test/diff/diff_files/extra_if_block_dst.spvasm @@ -0,0 +1,136 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %63 %69 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "f1(" + OpName %10 "f2(" + OpName %13 "v" + OpName %16 "Buffer" + OpMemberName %16 0 "flag1" + OpMemberName %16 1 "flag2" + OpName %18 "" + OpName %34 "v" + OpName %63 "color" + OpName %69 "v" + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpMemberDecorate %16 0 RelaxedPrecision + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 RelaxedPrecision + OpMemberDecorate %16 1 Offset 4 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + OpDecorate %23 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %46 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %63 RelaxedPrecision + OpDecorate %63 Location 0 + OpDecorate %64 RelaxedPrecision + OpDecorate %65 RelaxedPrecision + OpDecorate %67 RelaxedPrecision + OpDecorate %69 RelaxedPrecision + OpDecorate %69 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeFunction %6 + %12 = OpTypePointer Function %6 + %14 = OpConstant %6 0 + %15 = OpTypeInt 32 0 + %16 = OpTypeStruct %15 %15 + %17 = OpTypePointer Uniform %16 + %18 = OpVariable %17 Uniform + %19 = OpTypeInt 32 1 + %20 = OpConstant %19 1 + %21 = OpTypePointer Uniform %15 + %35 = OpConstant %19 0 + %39 = OpConstant %6 10 + %42 = OpConstant %6 0.5 + %43 = OpConstant %6 0.699999988 + %49 = OpConstant %15 0 + %50 = OpTypeBool + %54 = OpConstant %6 0.100000001 + %61 = OpTypeVector %6 4 + %62 = OpTypePointer Output %61 + %63 = OpVariable %62 Output + %66 = OpConstant %6 1 + %68 = OpTypePointer Input %6 + %69 = OpVariable %68 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %64 = OpFunctionCall %6 %8 + %65 = OpFunctionCall %6 %10 + %67 = OpCompositeConstruct %61 %64 %65 %14 %66 + OpStore %63 %67 + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %13 = OpVariable %12 Function + OpStore %13 %14 + %22 = OpAccessChain %21 %18 %20 + %23 = OpLoad %15 %22 + %24 = OpConvertUToF %6 %23 + %25 = OpExtInst %6 %1 Log2 %24 + %26 = OpLoad %6 %13 + %27 = OpFAdd %6 %26 %25 + OpStore %13 %27 + %28 = OpLoad %6 %13 + %29 = OpLoad %6 %13 + %30 = OpExtInst %6 %1 Sqrt %29 + %31 = OpFSub %6 %28 %30 + OpReturnValue %31 + OpFunctionEnd + %10 = OpFunction %6 None %7 + %11 = OpLabel + %34 = OpVariable %12 Function + %36 = OpAccessChain %21 %18 %35 + %37 = OpLoad %15 %36 + %38 = OpConvertUToF %6 %37 + %40 = OpFDiv %6 %38 %39 + OpStore %34 %40 + %41 = OpLoad %6 %34 + %44 = OpExtInst %6 %1 FClamp %41 %42 %43 + %45 = OpLoad %6 %34 + %46 = OpFMul %6 %45 %44 + OpStore %34 %46 + %47 = OpAccessChain %21 %18 %20 + %48 = OpLoad %15 %47 + %51 = OpINotEqual %50 %48 %49 + OpSelectionMerge %53 None + OpBranchConditional %51 %52 %53 + %52 = OpLabel + %55 = OpLoad %6 %34 + %56 = OpFSub %6 %55 %54 + OpStore %34 %56 + OpBranch %53 + %53 = OpLabel + %57 = OpLoad %6 %34 + %58 = OpExtInst %6 %1 Exp %57 + OpReturnValue %58 + OpFunctionEnd + diff --git a/test/diff/diff_files/extra_if_block_src.spvasm b/test/diff/diff_files/extra_if_block_src.spvasm new file mode 100644 index 00000000..1b43ccb1 --- /dev/null +++ b/test/diff/diff_files/extra_if_block_src.spvasm @@ -0,0 +1,137 @@ +;; Test where src has an extra if block in one function, and dst has an extra +;; if block in another function. + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %63 %68 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "f1(" + OpName %10 "f2(" + OpName %13 "v" + OpName %16 "Buffer" + OpMemberName %16 0 "flag1" + OpMemberName %16 1 "flag2" + OpName %18 "" + OpName %45 "v" + OpName %63 "color" + OpName %68 "v" + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpMemberDecorate %16 0 RelaxedPrecision + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 RelaxedPrecision + OpMemberDecorate %16 1 Offset 4 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + OpDecorate %23 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %50 RelaxedPrecision + OpDecorate %51 RelaxedPrecision + OpDecorate %54 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %63 RelaxedPrecision + OpDecorate %63 Location 0 + OpDecorate %64 RelaxedPrecision + OpDecorate %65 RelaxedPrecision + OpDecorate %66 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %68 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeFunction %6 + %12 = OpTypePointer Function %6 + %14 = OpConstant %6 0 + %15 = OpTypeInt 32 0 + %16 = OpTypeStruct %15 %15 + %17 = OpTypePointer Uniform %16 + %18 = OpVariable %17 Uniform + %19 = OpTypeInt 32 1 + %20 = OpConstant %19 0 + %21 = OpTypePointer Uniform %15 + %24 = OpConstant %15 0 + %25 = OpTypeBool + %29 = OpConstant %6 1 + %32 = OpConstant %19 1 + %49 = OpConstant %6 10 + %52 = OpConstant %6 0.5 + %53 = OpConstant %6 0.699999988 + %61 = OpTypeVector %6 4 + %62 = OpTypePointer Output %61 + %63 = OpVariable %62 Output + %67 = OpTypePointer Input %6 + %68 = OpVariable %67 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %64 = OpFunctionCall %6 %8 + %65 = OpFunctionCall %6 %10 + %66 = OpCompositeConstruct %61 %64 %65 %14 %29 + OpStore %63 %66 + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %13 = OpVariable %12 Function + OpStore %13 %14 + %22 = OpAccessChain %21 %18 %20 + %23 = OpLoad %15 %22 + %26 = OpINotEqual %25 %23 %24 + OpSelectionMerge %28 None + OpBranchConditional %26 %27 %28 + %27 = OpLabel + %30 = OpLoad %6 %13 + %31 = OpFAdd %6 %30 %29 + OpStore %13 %31 + OpBranch %28 + %28 = OpLabel + %33 = OpAccessChain %21 %18 %32 + %34 = OpLoad %15 %33 + %35 = OpConvertUToF %6 %34 + %36 = OpExtInst %6 %1 Log2 %35 + %37 = OpLoad %6 %13 + %38 = OpFAdd %6 %37 %36 + OpStore %13 %38 + %39 = OpLoad %6 %13 + %40 = OpLoad %6 %13 + %41 = OpExtInst %6 %1 Sqrt %40 + %42 = OpFSub %6 %39 %41 + OpReturnValue %42 + OpFunctionEnd + %10 = OpFunction %6 None %7 + %11 = OpLabel + %45 = OpVariable %12 Function + %46 = OpAccessChain %21 %18 %20 + %47 = OpLoad %15 %46 + %48 = OpConvertUToF %6 %47 + %50 = OpFDiv %6 %48 %49 + OpStore %45 %50 + %51 = OpLoad %6 %45 + %54 = OpExtInst %6 %1 FClamp %51 %52 %53 + %55 = OpLoad %6 %45 + %56 = OpFMul %6 %55 %54 + OpStore %45 %56 + %57 = OpLoad %6 %45 + %58 = OpExtInst %6 %1 Exp %57 + OpReturnValue %58 + OpFunctionEnd + diff --git a/test/diff/diff_files/generate_tests.py b/test/diff/diff_files/generate_tests.py new file mode 100755 index 00000000..cc3175d3 --- /dev/null +++ b/test/diff/diff_files/generate_tests.py @@ -0,0 +1,304 @@ +#! /usr/bin/python3 +# +# Copyright (c) 2022 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. + +import glob +import os +import subprocess +import sys + +# A handful of relevant tests are hand-picked to generate extra unit tests with +# specific options of spirv-diff. +IGNORE_SET_BINDING_TESTS = ['different_decorations_vertex'] +IGNORE_LOCATION_TESTS = ['different_decorations_fragment'] +IGNORE_DECORATIONS_TESTS = ['different_decorations_vertex', 'different_decorations_fragment'] +DUMP_IDS_TESTS = ['basic', 'int_vs_uint_constants', 'multiple_same_entry_points', 'small_functions_small_diffs'] + +LICENSE = u"""Copyright (c) 2022 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. +""" + +TEMPLATE_TEST_FILE = u"""// GENERATED FILE - DO NOT EDIT. +// Generated by {script_name} +// +{license} + +#include "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools {{ +namespace diff {{ +namespace {{ + +{test_comment} +constexpr char kSrc[] = R"({src_spirv})"; +constexpr char kDst[] = R"({dst_spirv})"; + +TEST(DiffTest, {test_name}) {{ + constexpr char kDiff[] = R"({diff_spirv})"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +}} + +TEST(DiffTest, {test_name}NoDebug) {{ + constexpr char kSrcNoDebug[] = R"({src_spirv_no_debug})"; + constexpr char kDstNoDebug[] = R"({dst_spirv_no_debug})"; + constexpr char kDiff[] = R"({diff_spirv_no_debug})"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +}} +{extra_tests} +}} // namespace +}} // namespace diff +}} // namespace spvtools +""" + +TEMPLATE_TEST_FUNC = u""" +TEST(DiffTest, {test_name}{test_tag}) {{ + constexpr char kDiff[] = R"({diff_spirv})"; + Options options; + {test_options} + DoStringDiffTest(kSrc, kDst, kDiff, options); +}} +""" + +TEMPLATE_TEST_FILES_CMAKE = u"""# GENERATED FILE - DO NOT EDIT. +# Generated by {script_name} +# +{license} + +list(APPEND DIFF_TEST_FILES +{test_files} +) +""" + +VARIANT_NONE = 0 +VARIANT_IGNORE_SET_BINDING = 1 +VARIANT_IGNORE_LOCATION = 2 +VARIANT_IGNORE_DECORATIONS = 3 +VARIANT_DUMP_IDS = 4 + +def print_usage(): + print("Usage: {} ".format(sys.argv[0])) + +def remove_debug_info(in_path): + tmp_dir = '.no_dbg' + + if not os.path.exists(tmp_dir): + os.makedirs(tmp_dir) + + (in_basename, in_ext) = os.path.splitext(in_path) + out_name = in_basename + '_no_dbg' + in_ext + out_path = os.path.join(tmp_dir, out_name) + + with open(in_path, 'r') as fin: + with open(out_path, 'w') as fout: + for line in fin: + ops = line.strip().split() + op = ops[0] if len(ops) > 0 else '' + if (op != ';;' and op != 'OpName' and op != 'OpMemberName' and op != 'OpString' and + op != 'OpLine' and op != 'OpNoLine' and op != 'OpModuleProcessed'): + fout.write(line) + + return out_path + +def make_src_file(test_name): + return '{}_src.spvasm'.format(test_name) + +def make_dst_file(test_name): + return '{}_dst.spvasm'.format(test_name) + +def make_cpp_file(test_name): + return '{}_autogen.cpp'.format(test_name) + +def make_camel_case(test_name): + return test_name.replace('_', ' ').title().replace(' ', '') + +def make_comment(text, comment_prefix): + return '\n'.join([comment_prefix + (' ' if line.strip() else '') + line for line in text.splitlines()]) + +def read_file(file_name): + with open(file_name, 'r') as f: + content = f.read() + + # Use unix line endings. + content = content.replace('\r\n', '\n') + + return content + +def parse_test_comment(src_spirv_file_name, src_spirv): + src_spirv_lines = src_spirv.splitlines() + comment_line_count = 0 + while comment_line_count < len(src_spirv_lines): + if not src_spirv_lines[comment_line_count].strip().startswith(';;'): + break + comment_line_count += 1 + + if comment_line_count == 0: + print("Expected comment on test file '{}'. See README.md next to this file.".format(src_spirv_file_name)) + sys.exit(1) + + comment_block = src_spirv_lines[:comment_line_count] + spirv_block = src_spirv_lines[comment_line_count:] + + comment_block = ['// ' + line.replace(';;', '').strip() for line in comment_block] + + return '\n'.join(spirv_block), '\n'.join(comment_block) + +def run_diff_tool(diff_tool, src_file, dst_file, variant): + args = [diff_tool] + + if variant == VARIANT_IGNORE_SET_BINDING or variant == VARIANT_IGNORE_DECORATIONS: + args.append('--ignore-set-binding') + + if variant == VARIANT_IGNORE_LOCATION or variant == VARIANT_IGNORE_DECORATIONS: + args.append('--ignore-location') + + if variant == VARIANT_DUMP_IDS: + args.append('--with-id-map') + + args.append('--no-color') + args.append('--no-indent') + + args.append(src_file) + args.append(dst_file) + + success = True + print(' '.join(args)) + process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + out, err = process.communicate() + + if process.returncode != 0: + print(err) + sys.exit(process.returncode) + + # Use unix line endings. + out = out.replace('\r\n', '\n') + + return out + +def generate_extra_test(diff_tool, src_file, dst_file, variant, test_name_camel_case, test_tag, test_options): + diff = run_diff_tool(diff_tool, src_file, dst_file, variant) + return TEMPLATE_TEST_FUNC.format( + test_name = test_name_camel_case, + test_tag = test_tag, + test_options = test_options, + diff_spirv = diff) + +def generate_test(diff_tool, test_name): + src_file = make_src_file(test_name) + dst_file = make_dst_file(test_name) + src_file_no_debug = remove_debug_info(src_file) + dst_file_no_debug = remove_debug_info(dst_file) + + src_spirv = read_file(src_file) + dst_spirv = read_file(dst_file) + src_spirv_no_debug = read_file(src_file_no_debug) + dst_spirv_no_debug = read_file(dst_file_no_debug) + + test_name_camel_case = make_camel_case(test_name) + + diff_spirv = run_diff_tool(diff_tool, src_file, dst_file, VARIANT_NONE) + diff_spirv_no_debug = run_diff_tool(diff_tool, src_file_no_debug, dst_file_no_debug, VARIANT_NONE) + + extra_tests = [] + + if test_name in IGNORE_SET_BINDING_TESTS: + extra_tests.append(generate_extra_test(diff_tool, src_file, dst_file, VARIANT_IGNORE_SET_BINDING, + test_name_camel_case, 'IgnoreSetBinding', 'options.ignore_set_binding = true;')) + + if test_name in IGNORE_LOCATION_TESTS: + extra_tests.append(generate_extra_test(diff_tool, src_file, dst_file, VARIANT_IGNORE_LOCATION, + test_name_camel_case, 'IgnoreLocation', 'options.ignore_location = true;')) + + if test_name in IGNORE_DECORATIONS_TESTS: + extra_tests.append(generate_extra_test(diff_tool, src_file, dst_file, VARIANT_IGNORE_DECORATIONS, + test_name_camel_case, 'IgnoreSetBindingLocation', + '\n '.join(['options.ignore_set_binding = true;', 'options.ignore_location = true;']))) + + if test_name in DUMP_IDS_TESTS: + extra_tests.append(generate_extra_test(diff_tool, src_file, dst_file, VARIANT_DUMP_IDS, + test_name_camel_case, 'DumpIds', 'options.dump_id_map = true;')) + + src_spirv, test_comment = parse_test_comment(src_file, src_spirv) + + test_file = TEMPLATE_TEST_FILE.format( + script_name = os.path.basename(__file__), + license = make_comment(LICENSE, '//'), + test_comment = test_comment, + test_name = test_name_camel_case, + src_spirv = src_spirv, + dst_spirv = dst_spirv, + diff_spirv = diff_spirv, + src_spirv_no_debug = src_spirv_no_debug, + dst_spirv_no_debug = dst_spirv_no_debug, + diff_spirv_no_debug = diff_spirv_no_debug, + extra_tests = ''.join(extra_tests)) + + test_file_name = make_cpp_file(test_name) + with open(test_file_name, 'wb') as fout: + fout.write(str.encode(test_file)) + + return test_file_name + +def generate_tests(diff_tool, test_names): + return [generate_test(diff_tool, test_name) for test_name in test_names] + +def generate_cmake(test_files): + cmake = TEMPLATE_TEST_FILES_CMAKE.format( + script_name = os.path.basename(__file__), + license = make_comment(LICENSE, '#'), + test_files = '\n'.join(['"diff_files/{}"'.format(f) for f in test_files])) + + with open('diff_test_files_autogen.cmake', 'wb') as fout: + fout.write(str.encode(cmake)) + +def main(): + + if len(sys.argv) != 2: + print_usage() + return 1 + + diff_tool = sys.argv[1] + if not os.path.exists(diff_tool): + print("No such file: {}".format(diff_tool)) + print_usage() + return 1 + + diff_tool = os.path.realpath(diff_tool) + os.chdir(os.path.dirname(__file__)) + + test_names = sorted([f[:-11] for f in glob.glob("*_src.spvasm")]) + + test_files = generate_tests(diff_tool, test_names) + + generate_cmake(test_files) + + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/test/diff/diff_files/index_signedness_autogen.cpp b/test/diff/diff_files/index_signedness_autogen.cpp new file mode 100644 index 00000000..ab650be1 --- /dev/null +++ b/test/diff/diff_files/index_signedness_autogen.cpp @@ -0,0 +1,733 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test where signedness of indices are different between src and dst. +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %13 "BufferOut" + OpMemberName %13 0 "o1" + OpMemberName %13 1 "o2" + OpMemberName %13 2 "o3" + OpName %15 "" + OpName %22 "BufferIn" + OpMemberName %22 0 "i1" + OpMemberName %22 1 "i2" + OpName %24 "" + OpDecorate %8 ArrayStride 4 + OpDecorate %9 ArrayStride 4 + OpDecorate %11 ArrayStride 4 + OpDecorate %12 ArrayStride 8 + OpMemberDecorate %13 0 Offset 0 + OpMemberDecorate %13 1 Offset 12 + OpMemberDecorate %13 2 Offset 24 + OpDecorate %13 BufferBlock + OpDecorate %15 DescriptorSet 0 + OpDecorate %15 Binding 1 + OpDecorate %18 ArrayStride 16 + OpDecorate %19 ArrayStride 48 + OpDecorate %21 ArrayStride 16 + OpMemberDecorate %22 0 Offset 0 + OpMemberDecorate %22 1 Offset 96 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpConstant %6 3 + %8 = OpTypeArray %6 %7 + %9 = OpTypeArray %6 %7 + %10 = OpConstant %6 2 + %11 = OpTypeArray %6 %10 + %12 = OpTypeArray %11 %10 + %13 = OpTypeStruct %8 %9 %12 + %14 = OpTypePointer Uniform %13 + %15 = OpVariable %14 Uniform + %16 = OpTypeInt 32 1 + %17 = OpConstant %16 0 + %18 = OpTypeArray %6 %7 + %19 = OpTypeArray %18 %10 + %20 = OpConstant %6 4 + %21 = OpTypeArray %6 %20 + %22 = OpTypeStruct %19 %21 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %6 + %28 = OpConstant %6 1 + %31 = OpConstant %16 1 + %34 = OpConstant %6 0 + %37 = OpConstant %16 2 + %61 = OpConstant %16 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %26 = OpAccessChain %25 %24 %17 %17 %17 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + %30 = OpAccessChain %25 %15 %17 %17 + OpStore %30 %29 + %32 = OpAccessChain %25 %24 %17 %31 %17 + %33 = OpLoad %6 %32 + %35 = OpIAdd %6 %33 %34 + %36 = OpAccessChain %25 %15 %17 %31 + OpStore %36 %35 + %38 = OpAccessChain %25 %24 %17 %31 %31 + %39 = OpLoad %6 %38 + %40 = OpIAdd %6 %39 %10 + %41 = OpAccessChain %25 %15 %17 %37 + OpStore %41 %40 + %42 = OpAccessChain %25 %24 %17 %17 %37 + %43 = OpLoad %6 %42 + %44 = OpAccessChain %25 %15 %31 %17 + OpStore %44 %43 + %45 = OpAccessChain %25 %24 %17 %17 %31 + %46 = OpLoad %6 %45 + %47 = OpIMul %6 %46 %7 + %48 = OpAccessChain %25 %15 %31 %31 + OpStore %48 %47 + %49 = OpAccessChain %25 %24 %17 %31 %37 + %50 = OpLoad %6 %49 + %51 = OpAccessChain %25 %15 %31 %37 + OpStore %51 %50 + %52 = OpAccessChain %25 %24 %31 %17 + %53 = OpLoad %6 %52 + %54 = OpAccessChain %25 %15 %37 %17 %17 + OpStore %54 %53 + %55 = OpAccessChain %25 %24 %31 %31 + %56 = OpLoad %6 %55 + %57 = OpAccessChain %25 %15 %37 %17 %31 + OpStore %57 %56 + %58 = OpAccessChain %25 %24 %31 %37 + %59 = OpLoad %6 %58 + %60 = OpAccessChain %25 %15 %37 %31 %17 + OpStore %60 %59 + %62 = OpAccessChain %25 %24 %31 %61 + %63 = OpLoad %6 %62 + %64 = OpAccessChain %25 %15 %37 %31 %31 + OpStore %64 %63 + OpReturn + OpFunctionEnd +)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %13 "BufferOut" + OpMemberName %13 0 "o1" + OpMemberName %13 1 "o2" + OpMemberName %13 2 "o3" + OpName %15 "" + OpName %22 "BufferIn" + OpMemberName %22 0 "i1" + OpMemberName %22 1 "i2" + OpName %24 "" + OpDecorate %8 ArrayStride 4 + OpDecorate %9 ArrayStride 4 + OpDecorate %11 ArrayStride 4 + OpDecorate %12 ArrayStride 8 + OpMemberDecorate %13 0 Offset 0 + OpMemberDecorate %13 1 Offset 12 + OpMemberDecorate %13 2 Offset 24 + OpDecorate %13 BufferBlock + OpDecorate %15 DescriptorSet 0 + OpDecorate %15 Binding 1 + OpDecorate %18 ArrayStride 16 + OpDecorate %19 ArrayStride 48 + OpDecorate %21 ArrayStride 16 + OpMemberDecorate %22 0 Offset 0 + OpMemberDecorate %22 1 Offset 96 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %16 = OpTypeInt 32 1 + %7 = OpConstant %16 3 + %8 = OpTypeArray %6 %7 + %9 = OpTypeArray %6 %7 + %10 = OpConstant %16 2 + %11 = OpTypeArray %6 %10 + %12 = OpTypeArray %11 %10 + %13 = OpTypeStruct %8 %9 %12 + %14 = OpTypePointer Uniform %13 + %15 = OpVariable %14 Uniform + %18 = OpTypeArray %6 %7 + %19 = OpTypeArray %18 %10 + %20 = OpConstant %16 4 + %21 = OpTypeArray %6 %20 + %22 = OpTypeStruct %19 %21 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %6 + %17 = OpConstant %16 0 + %28 = OpConstant %16 1 + %31 = OpConstant %6 1 + %34 = OpConstant %6 0 + %37 = OpConstant %6 2 + %61 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %26 = OpAccessChain %25 %24 %17 %17 %17 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + %30 = OpAccessChain %25 %15 %17 %17 + OpStore %30 %29 + %32 = OpAccessChain %25 %24 %17 %31 %17 + %33 = OpLoad %6 %32 + %35 = OpIAdd %6 %33 %34 + %36 = OpAccessChain %25 %15 %17 %31 + OpStore %36 %35 + %38 = OpAccessChain %25 %24 %17 %31 %31 + %39 = OpLoad %6 %38 + %40 = OpIAdd %6 %39 %37 + %41 = OpAccessChain %25 %15 %17 %10 + OpStore %41 %40 + %42 = OpAccessChain %25 %24 %17 %17 %10 + %43 = OpLoad %6 %42 + %44 = OpAccessChain %25 %15 %31 %17 + OpStore %44 %43 + %45 = OpAccessChain %25 %24 %17 %17 %31 + %46 = OpLoad %6 %45 + %47 = OpIMul %6 %46 %7 + %48 = OpAccessChain %25 %15 %31 %31 + OpStore %48 %47 + %49 = OpAccessChain %25 %24 %17 %31 %10 + %50 = OpLoad %6 %49 + %51 = OpAccessChain %25 %15 %31 %10 + OpStore %51 %50 + %52 = OpAccessChain %25 %24 %31 %17 + %53 = OpLoad %6 %52 + %54 = OpAccessChain %25 %15 %37 %17 %17 + OpStore %54 %53 + %55 = OpAccessChain %25 %24 %31 %31 + %56 = OpLoad %6 %55 + %57 = OpAccessChain %25 %15 %37 %17 %31 + OpStore %57 %56 + %58 = OpAccessChain %25 %24 %31 %37 + %59 = OpLoad %6 %58 + %60 = OpAccessChain %25 %15 %37 %31 %17 + OpStore %60 %59 + %62 = OpAccessChain %25 %24 %31 %61 + %63 = OpLoad %6 %62 + %64 = OpAccessChain %25 %15 %37 %31 %31 + OpStore %64 %63 + OpReturn + OpFunctionEnd + +)"; + +TEST(DiffTest, IndexSignedness) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 65 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %13 "BufferOut" + OpMemberName %13 0 "o1" + OpMemberName %13 1 "o2" + OpMemberName %13 2 "o3" + OpName %15 "" + OpName %22 "BufferIn" + OpMemberName %22 0 "i1" + OpMemberName %22 1 "i2" + OpName %24 "" + OpDecorate %8 ArrayStride 4 + OpDecorate %9 ArrayStride 4 + OpDecorate %11 ArrayStride 4 + OpDecorate %12 ArrayStride 8 + OpMemberDecorate %13 0 Offset 0 + OpMemberDecorate %13 1 Offset 12 + OpMemberDecorate %13 2 Offset 24 + OpDecorate %13 BufferBlock + OpDecorate %15 DescriptorSet 0 + OpDecorate %15 Binding 1 + OpDecorate %18 ArrayStride 16 + OpDecorate %19 ArrayStride 48 + OpDecorate %21 ArrayStride 16 + OpMemberDecorate %22 0 Offset 0 + OpMemberDecorate %22 1 Offset 96 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpConstant %6 3 +-%8 = OpTypeArray %6 %7 ++%8 = OpTypeArray %6 %61 +-%9 = OpTypeArray %6 %7 ++%9 = OpTypeArray %6 %61 + %10 = OpConstant %6 2 +-%11 = OpTypeArray %6 %10 ++%11 = OpTypeArray %6 %37 +-%12 = OpTypeArray %11 %10 ++%12 = OpTypeArray %11 %37 + %13 = OpTypeStruct %8 %9 %12 + %14 = OpTypePointer Uniform %13 + %15 = OpVariable %14 Uniform + %16 = OpTypeInt 32 1 + %17 = OpConstant %16 0 +-%18 = OpTypeArray %6 %7 ++%18 = OpTypeArray %6 %61 +-%19 = OpTypeArray %18 %10 ++%19 = OpTypeArray %18 %37 +-%20 = OpConstant %6 4 ++%20 = OpConstant %16 4 + %21 = OpTypeArray %6 %20 + %22 = OpTypeStruct %19 %21 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %6 + %28 = OpConstant %6 1 + %31 = OpConstant %16 1 + %34 = OpConstant %6 0 + %37 = OpConstant %16 2 + %61 = OpConstant %16 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %26 = OpAccessChain %25 %24 %17 %17 %17 + %27 = OpLoad %6 %26 +-%29 = OpIAdd %6 %27 %28 ++%29 = OpIAdd %6 %27 %31 + %30 = OpAccessChain %25 %15 %17 %17 + OpStore %30 %29 +-%32 = OpAccessChain %25 %24 %17 %31 %17 ++%32 = OpAccessChain %25 %24 %17 %28 %17 + %33 = OpLoad %6 %32 + %35 = OpIAdd %6 %33 %34 +-%36 = OpAccessChain %25 %15 %17 %31 ++%36 = OpAccessChain %25 %15 %17 %28 + OpStore %36 %35 +-%38 = OpAccessChain %25 %24 %17 %31 %31 ++%38 = OpAccessChain %25 %24 %17 %28 %28 + %39 = OpLoad %6 %38 + %40 = OpIAdd %6 %39 %10 + %41 = OpAccessChain %25 %15 %17 %37 + OpStore %41 %40 + %42 = OpAccessChain %25 %24 %17 %17 %37 + %43 = OpLoad %6 %42 +-%44 = OpAccessChain %25 %15 %31 %17 ++%44 = OpAccessChain %25 %15 %28 %17 + OpStore %44 %43 +-%45 = OpAccessChain %25 %24 %17 %17 %31 ++%45 = OpAccessChain %25 %24 %17 %17 %28 + %46 = OpLoad %6 %45 +-%47 = OpIMul %6 %46 %7 ++%47 = OpIMul %6 %46 %61 +-%48 = OpAccessChain %25 %15 %31 %31 ++%48 = OpAccessChain %25 %15 %28 %28 + OpStore %48 %47 +-%49 = OpAccessChain %25 %24 %17 %31 %37 ++%49 = OpAccessChain %25 %24 %17 %28 %37 + %50 = OpLoad %6 %49 +-%51 = OpAccessChain %25 %15 %31 %37 ++%51 = OpAccessChain %25 %15 %28 %37 + OpStore %51 %50 +-%52 = OpAccessChain %25 %24 %31 %17 ++%52 = OpAccessChain %25 %24 %28 %17 + %53 = OpLoad %6 %52 +-%54 = OpAccessChain %25 %15 %37 %17 %17 ++%54 = OpAccessChain %25 %15 %10 %17 %17 + OpStore %54 %53 +-%55 = OpAccessChain %25 %24 %31 %31 ++%55 = OpAccessChain %25 %24 %28 %28 + %56 = OpLoad %6 %55 +-%57 = OpAccessChain %25 %15 %37 %17 %31 ++%57 = OpAccessChain %25 %15 %10 %17 %28 + OpStore %57 %56 +-%58 = OpAccessChain %25 %24 %31 %37 ++%58 = OpAccessChain %25 %24 %28 %10 + %59 = OpLoad %6 %58 +-%60 = OpAccessChain %25 %15 %37 %31 %17 ++%60 = OpAccessChain %25 %15 %10 %28 %17 + OpStore %60 %59 +-%62 = OpAccessChain %25 %24 %31 %61 ++%62 = OpAccessChain %25 %24 %28 %7 + %63 = OpLoad %6 %62 +-%64 = OpAccessChain %25 %15 %37 %31 %31 ++%64 = OpAccessChain %25 %15 %10 %28 %28 + OpStore %64 %63 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, IndexSignednessNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpDecorate %8 ArrayStride 4 + OpDecorate %9 ArrayStride 4 + OpDecorate %11 ArrayStride 4 + OpDecorate %12 ArrayStride 8 + OpMemberDecorate %13 0 Offset 0 + OpMemberDecorate %13 1 Offset 12 + OpMemberDecorate %13 2 Offset 24 + OpDecorate %13 BufferBlock + OpDecorate %15 DescriptorSet 0 + OpDecorate %15 Binding 1 + OpDecorate %18 ArrayStride 16 + OpDecorate %19 ArrayStride 48 + OpDecorate %21 ArrayStride 16 + OpMemberDecorate %22 0 Offset 0 + OpMemberDecorate %22 1 Offset 96 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpConstant %6 3 + %8 = OpTypeArray %6 %7 + %9 = OpTypeArray %6 %7 + %10 = OpConstant %6 2 + %11 = OpTypeArray %6 %10 + %12 = OpTypeArray %11 %10 + %13 = OpTypeStruct %8 %9 %12 + %14 = OpTypePointer Uniform %13 + %15 = OpVariable %14 Uniform + %16 = OpTypeInt 32 1 + %17 = OpConstant %16 0 + %18 = OpTypeArray %6 %7 + %19 = OpTypeArray %18 %10 + %20 = OpConstant %6 4 + %21 = OpTypeArray %6 %20 + %22 = OpTypeStruct %19 %21 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %6 + %28 = OpConstant %6 1 + %31 = OpConstant %16 1 + %34 = OpConstant %6 0 + %37 = OpConstant %16 2 + %61 = OpConstant %16 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %26 = OpAccessChain %25 %24 %17 %17 %17 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + %30 = OpAccessChain %25 %15 %17 %17 + OpStore %30 %29 + %32 = OpAccessChain %25 %24 %17 %31 %17 + %33 = OpLoad %6 %32 + %35 = OpIAdd %6 %33 %34 + %36 = OpAccessChain %25 %15 %17 %31 + OpStore %36 %35 + %38 = OpAccessChain %25 %24 %17 %31 %31 + %39 = OpLoad %6 %38 + %40 = OpIAdd %6 %39 %10 + %41 = OpAccessChain %25 %15 %17 %37 + OpStore %41 %40 + %42 = OpAccessChain %25 %24 %17 %17 %37 + %43 = OpLoad %6 %42 + %44 = OpAccessChain %25 %15 %31 %17 + OpStore %44 %43 + %45 = OpAccessChain %25 %24 %17 %17 %31 + %46 = OpLoad %6 %45 + %47 = OpIMul %6 %46 %7 + %48 = OpAccessChain %25 %15 %31 %31 + OpStore %48 %47 + %49 = OpAccessChain %25 %24 %17 %31 %37 + %50 = OpLoad %6 %49 + %51 = OpAccessChain %25 %15 %31 %37 + OpStore %51 %50 + %52 = OpAccessChain %25 %24 %31 %17 + %53 = OpLoad %6 %52 + %54 = OpAccessChain %25 %15 %37 %17 %17 + OpStore %54 %53 + %55 = OpAccessChain %25 %24 %31 %31 + %56 = OpLoad %6 %55 + %57 = OpAccessChain %25 %15 %37 %17 %31 + OpStore %57 %56 + %58 = OpAccessChain %25 %24 %31 %37 + %59 = OpLoad %6 %58 + %60 = OpAccessChain %25 %15 %37 %31 %17 + OpStore %60 %59 + %62 = OpAccessChain %25 %24 %31 %61 + %63 = OpLoad %6 %62 + %64 = OpAccessChain %25 %15 %37 %31 %31 + OpStore %64 %63 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpDecorate %8 ArrayStride 4 + OpDecorate %9 ArrayStride 4 + OpDecorate %11 ArrayStride 4 + OpDecorate %12 ArrayStride 8 + OpMemberDecorate %13 0 Offset 0 + OpMemberDecorate %13 1 Offset 12 + OpMemberDecorate %13 2 Offset 24 + OpDecorate %13 BufferBlock + OpDecorate %15 DescriptorSet 0 + OpDecorate %15 Binding 1 + OpDecorate %18 ArrayStride 16 + OpDecorate %19 ArrayStride 48 + OpDecorate %21 ArrayStride 16 + OpMemberDecorate %22 0 Offset 0 + OpMemberDecorate %22 1 Offset 96 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %16 = OpTypeInt 32 1 + %7 = OpConstant %16 3 + %8 = OpTypeArray %6 %7 + %9 = OpTypeArray %6 %7 + %10 = OpConstant %16 2 + %11 = OpTypeArray %6 %10 + %12 = OpTypeArray %11 %10 + %13 = OpTypeStruct %8 %9 %12 + %14 = OpTypePointer Uniform %13 + %15 = OpVariable %14 Uniform + %18 = OpTypeArray %6 %7 + %19 = OpTypeArray %18 %10 + %20 = OpConstant %16 4 + %21 = OpTypeArray %6 %20 + %22 = OpTypeStruct %19 %21 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %6 + %17 = OpConstant %16 0 + %28 = OpConstant %16 1 + %31 = OpConstant %6 1 + %34 = OpConstant %6 0 + %37 = OpConstant %6 2 + %61 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %26 = OpAccessChain %25 %24 %17 %17 %17 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + %30 = OpAccessChain %25 %15 %17 %17 + OpStore %30 %29 + %32 = OpAccessChain %25 %24 %17 %31 %17 + %33 = OpLoad %6 %32 + %35 = OpIAdd %6 %33 %34 + %36 = OpAccessChain %25 %15 %17 %31 + OpStore %36 %35 + %38 = OpAccessChain %25 %24 %17 %31 %31 + %39 = OpLoad %6 %38 + %40 = OpIAdd %6 %39 %37 + %41 = OpAccessChain %25 %15 %17 %10 + OpStore %41 %40 + %42 = OpAccessChain %25 %24 %17 %17 %10 + %43 = OpLoad %6 %42 + %44 = OpAccessChain %25 %15 %31 %17 + OpStore %44 %43 + %45 = OpAccessChain %25 %24 %17 %17 %31 + %46 = OpLoad %6 %45 + %47 = OpIMul %6 %46 %7 + %48 = OpAccessChain %25 %15 %31 %31 + OpStore %48 %47 + %49 = OpAccessChain %25 %24 %17 %31 %10 + %50 = OpLoad %6 %49 + %51 = OpAccessChain %25 %15 %31 %10 + OpStore %51 %50 + %52 = OpAccessChain %25 %24 %31 %17 + %53 = OpLoad %6 %52 + %54 = OpAccessChain %25 %15 %37 %17 %17 + OpStore %54 %53 + %55 = OpAccessChain %25 %24 %31 %31 + %56 = OpLoad %6 %55 + %57 = OpAccessChain %25 %15 %37 %17 %31 + OpStore %57 %56 + %58 = OpAccessChain %25 %24 %31 %37 + %59 = OpLoad %6 %58 + %60 = OpAccessChain %25 %15 %37 %31 %17 + OpStore %60 %59 + %62 = OpAccessChain %25 %24 %31 %61 + %63 = OpLoad %6 %62 + %64 = OpAccessChain %25 %15 %37 %31 %31 + OpStore %64 %63 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 65 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpDecorate %8 ArrayStride 4 + OpDecorate %9 ArrayStride 4 + OpDecorate %11 ArrayStride 4 + OpDecorate %12 ArrayStride 8 + OpMemberDecorate %13 0 Offset 0 + OpMemberDecorate %13 1 Offset 12 + OpMemberDecorate %13 2 Offset 24 + OpDecorate %13 BufferBlock + OpDecorate %15 DescriptorSet 0 + OpDecorate %15 Binding 1 + OpDecorate %18 ArrayStride 16 + OpDecorate %19 ArrayStride 48 + OpDecorate %21 ArrayStride 16 + OpMemberDecorate %22 0 Offset 0 + OpMemberDecorate %22 1 Offset 96 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpConstant %6 3 +-%8 = OpTypeArray %6 %7 ++%8 = OpTypeArray %6 %61 +-%9 = OpTypeArray %6 %7 ++%9 = OpTypeArray %6 %61 + %10 = OpConstant %6 2 +-%11 = OpTypeArray %6 %10 ++%11 = OpTypeArray %6 %37 +-%12 = OpTypeArray %11 %10 ++%12 = OpTypeArray %11 %37 + %13 = OpTypeStruct %8 %9 %12 + %14 = OpTypePointer Uniform %13 + %15 = OpVariable %14 Uniform + %16 = OpTypeInt 32 1 + %17 = OpConstant %16 0 +-%18 = OpTypeArray %6 %7 ++%18 = OpTypeArray %6 %61 +-%19 = OpTypeArray %18 %10 ++%19 = OpTypeArray %18 %37 +-%20 = OpConstant %6 4 ++%20 = OpConstant %16 4 + %21 = OpTypeArray %6 %20 + %22 = OpTypeStruct %19 %21 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %6 + %28 = OpConstant %6 1 + %31 = OpConstant %16 1 + %34 = OpConstant %6 0 + %37 = OpConstant %16 2 + %61 = OpConstant %16 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %26 = OpAccessChain %25 %24 %17 %17 %17 + %27 = OpLoad %6 %26 +-%29 = OpIAdd %6 %27 %28 ++%29 = OpIAdd %6 %27 %31 + %30 = OpAccessChain %25 %15 %17 %17 + OpStore %30 %29 +-%32 = OpAccessChain %25 %24 %17 %31 %17 ++%32 = OpAccessChain %25 %24 %17 %28 %17 + %33 = OpLoad %6 %32 + %35 = OpIAdd %6 %33 %34 +-%36 = OpAccessChain %25 %15 %17 %31 ++%36 = OpAccessChain %25 %15 %17 %28 + OpStore %36 %35 +-%38 = OpAccessChain %25 %24 %17 %31 %31 ++%38 = OpAccessChain %25 %24 %17 %28 %28 + %39 = OpLoad %6 %38 + %40 = OpIAdd %6 %39 %10 + %41 = OpAccessChain %25 %15 %17 %37 + OpStore %41 %40 + %42 = OpAccessChain %25 %24 %17 %17 %37 + %43 = OpLoad %6 %42 +-%44 = OpAccessChain %25 %15 %31 %17 ++%44 = OpAccessChain %25 %15 %28 %17 + OpStore %44 %43 +-%45 = OpAccessChain %25 %24 %17 %17 %31 ++%45 = OpAccessChain %25 %24 %17 %17 %28 + %46 = OpLoad %6 %45 +-%47 = OpIMul %6 %46 %7 ++%47 = OpIMul %6 %46 %61 +-%48 = OpAccessChain %25 %15 %31 %31 ++%48 = OpAccessChain %25 %15 %28 %28 + OpStore %48 %47 +-%49 = OpAccessChain %25 %24 %17 %31 %37 ++%49 = OpAccessChain %25 %24 %17 %28 %37 + %50 = OpLoad %6 %49 +-%51 = OpAccessChain %25 %15 %31 %37 ++%51 = OpAccessChain %25 %15 %28 %37 + OpStore %51 %50 +-%52 = OpAccessChain %25 %24 %31 %17 ++%52 = OpAccessChain %25 %24 %28 %17 + %53 = OpLoad %6 %52 +-%54 = OpAccessChain %25 %15 %37 %17 %17 ++%54 = OpAccessChain %25 %15 %10 %17 %17 + OpStore %54 %53 +-%55 = OpAccessChain %25 %24 %31 %31 ++%55 = OpAccessChain %25 %24 %28 %28 + %56 = OpLoad %6 %55 +-%57 = OpAccessChain %25 %15 %37 %17 %31 ++%57 = OpAccessChain %25 %15 %10 %17 %28 + OpStore %57 %56 +-%58 = OpAccessChain %25 %24 %31 %37 ++%58 = OpAccessChain %25 %24 %28 %10 + %59 = OpLoad %6 %58 +-%60 = OpAccessChain %25 %15 %37 %31 %17 ++%60 = OpAccessChain %25 %15 %10 %28 %17 + OpStore %60 %59 +-%62 = OpAccessChain %25 %24 %31 %61 ++%62 = OpAccessChain %25 %24 %28 %7 + %63 = OpLoad %6 %62 +-%64 = OpAccessChain %25 %15 %37 %31 %31 ++%64 = OpAccessChain %25 %15 %10 %28 %28 + OpStore %64 %63 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/index_signedness_dst.spvasm b/test/diff/diff_files/index_signedness_dst.spvasm new file mode 100644 index 00000000..08c1939b --- /dev/null +++ b/test/diff/diff_files/index_signedness_dst.spvasm @@ -0,0 +1,110 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %13 "BufferOut" + OpMemberName %13 0 "o1" + OpMemberName %13 1 "o2" + OpMemberName %13 2 "o3" + OpName %15 "" + OpName %22 "BufferIn" + OpMemberName %22 0 "i1" + OpMemberName %22 1 "i2" + OpName %24 "" + OpDecorate %8 ArrayStride 4 + OpDecorate %9 ArrayStride 4 + OpDecorate %11 ArrayStride 4 + OpDecorate %12 ArrayStride 8 + OpMemberDecorate %13 0 Offset 0 + OpMemberDecorate %13 1 Offset 12 + OpMemberDecorate %13 2 Offset 24 + OpDecorate %13 BufferBlock + OpDecorate %15 DescriptorSet 0 + OpDecorate %15 Binding 1 + OpDecorate %18 ArrayStride 16 + OpDecorate %19 ArrayStride 48 + OpDecorate %21 ArrayStride 16 + OpMemberDecorate %22 0 Offset 0 + OpMemberDecorate %22 1 Offset 96 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %16 = OpTypeInt 32 1 + %7 = OpConstant %16 3 + %8 = OpTypeArray %6 %7 + %9 = OpTypeArray %6 %7 + %10 = OpConstant %16 2 + %11 = OpTypeArray %6 %10 + %12 = OpTypeArray %11 %10 + %13 = OpTypeStruct %8 %9 %12 + %14 = OpTypePointer Uniform %13 + %15 = OpVariable %14 Uniform + %18 = OpTypeArray %6 %7 + %19 = OpTypeArray %18 %10 + %20 = OpConstant %16 4 + %21 = OpTypeArray %6 %20 + %22 = OpTypeStruct %19 %21 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %6 + %17 = OpConstant %16 0 + %28 = OpConstant %16 1 + %31 = OpConstant %6 1 + %34 = OpConstant %6 0 + %37 = OpConstant %6 2 + %61 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %26 = OpAccessChain %25 %24 %17 %17 %17 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + %30 = OpAccessChain %25 %15 %17 %17 + OpStore %30 %29 + %32 = OpAccessChain %25 %24 %17 %31 %17 + %33 = OpLoad %6 %32 + %35 = OpIAdd %6 %33 %34 + %36 = OpAccessChain %25 %15 %17 %31 + OpStore %36 %35 + %38 = OpAccessChain %25 %24 %17 %31 %31 + %39 = OpLoad %6 %38 + %40 = OpIAdd %6 %39 %37 + %41 = OpAccessChain %25 %15 %17 %10 + OpStore %41 %40 + %42 = OpAccessChain %25 %24 %17 %17 %10 + %43 = OpLoad %6 %42 + %44 = OpAccessChain %25 %15 %31 %17 + OpStore %44 %43 + %45 = OpAccessChain %25 %24 %17 %17 %31 + %46 = OpLoad %6 %45 + %47 = OpIMul %6 %46 %7 + %48 = OpAccessChain %25 %15 %31 %31 + OpStore %48 %47 + %49 = OpAccessChain %25 %24 %17 %31 %10 + %50 = OpLoad %6 %49 + %51 = OpAccessChain %25 %15 %31 %10 + OpStore %51 %50 + %52 = OpAccessChain %25 %24 %31 %17 + %53 = OpLoad %6 %52 + %54 = OpAccessChain %25 %15 %37 %17 %17 + OpStore %54 %53 + %55 = OpAccessChain %25 %24 %31 %31 + %56 = OpLoad %6 %55 + %57 = OpAccessChain %25 %15 %37 %17 %31 + OpStore %57 %56 + %58 = OpAccessChain %25 %24 %31 %37 + %59 = OpLoad %6 %58 + %60 = OpAccessChain %25 %15 %37 %31 %17 + OpStore %60 %59 + %62 = OpAccessChain %25 %24 %31 %61 + %63 = OpLoad %6 %62 + %64 = OpAccessChain %25 %15 %37 %31 %31 + OpStore %64 %63 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/index_signedness_src.spvasm b/test/diff/diff_files/index_signedness_src.spvasm new file mode 100644 index 00000000..06dfd18b --- /dev/null +++ b/test/diff/diff_files/index_signedness_src.spvasm @@ -0,0 +1,111 @@ +;; Test where signedness of indices are different between src and dst. + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %13 "BufferOut" + OpMemberName %13 0 "o1" + OpMemberName %13 1 "o2" + OpMemberName %13 2 "o3" + OpName %15 "" + OpName %22 "BufferIn" + OpMemberName %22 0 "i1" + OpMemberName %22 1 "i2" + OpName %24 "" + OpDecorate %8 ArrayStride 4 + OpDecorate %9 ArrayStride 4 + OpDecorate %11 ArrayStride 4 + OpDecorate %12 ArrayStride 8 + OpMemberDecorate %13 0 Offset 0 + OpMemberDecorate %13 1 Offset 12 + OpMemberDecorate %13 2 Offset 24 + OpDecorate %13 BufferBlock + OpDecorate %15 DescriptorSet 0 + OpDecorate %15 Binding 1 + OpDecorate %18 ArrayStride 16 + OpDecorate %19 ArrayStride 48 + OpDecorate %21 ArrayStride 16 + OpMemberDecorate %22 0 Offset 0 + OpMemberDecorate %22 1 Offset 96 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpConstant %6 3 + %8 = OpTypeArray %6 %7 + %9 = OpTypeArray %6 %7 + %10 = OpConstant %6 2 + %11 = OpTypeArray %6 %10 + %12 = OpTypeArray %11 %10 + %13 = OpTypeStruct %8 %9 %12 + %14 = OpTypePointer Uniform %13 + %15 = OpVariable %14 Uniform + %16 = OpTypeInt 32 1 + %17 = OpConstant %16 0 + %18 = OpTypeArray %6 %7 + %19 = OpTypeArray %18 %10 + %20 = OpConstant %6 4 + %21 = OpTypeArray %6 %20 + %22 = OpTypeStruct %19 %21 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %6 + %28 = OpConstant %6 1 + %31 = OpConstant %16 1 + %34 = OpConstant %6 0 + %37 = OpConstant %16 2 + %61 = OpConstant %16 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %26 = OpAccessChain %25 %24 %17 %17 %17 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + %30 = OpAccessChain %25 %15 %17 %17 + OpStore %30 %29 + %32 = OpAccessChain %25 %24 %17 %31 %17 + %33 = OpLoad %6 %32 + %35 = OpIAdd %6 %33 %34 + %36 = OpAccessChain %25 %15 %17 %31 + OpStore %36 %35 + %38 = OpAccessChain %25 %24 %17 %31 %31 + %39 = OpLoad %6 %38 + %40 = OpIAdd %6 %39 %10 + %41 = OpAccessChain %25 %15 %17 %37 + OpStore %41 %40 + %42 = OpAccessChain %25 %24 %17 %17 %37 + %43 = OpLoad %6 %42 + %44 = OpAccessChain %25 %15 %31 %17 + OpStore %44 %43 + %45 = OpAccessChain %25 %24 %17 %17 %31 + %46 = OpLoad %6 %45 + %47 = OpIMul %6 %46 %7 + %48 = OpAccessChain %25 %15 %31 %31 + OpStore %48 %47 + %49 = OpAccessChain %25 %24 %17 %31 %37 + %50 = OpLoad %6 %49 + %51 = OpAccessChain %25 %15 %31 %37 + OpStore %51 %50 + %52 = OpAccessChain %25 %24 %31 %17 + %53 = OpLoad %6 %52 + %54 = OpAccessChain %25 %15 %37 %17 %17 + OpStore %54 %53 + %55 = OpAccessChain %25 %24 %31 %31 + %56 = OpLoad %6 %55 + %57 = OpAccessChain %25 %15 %37 %17 %31 + OpStore %57 %56 + %58 = OpAccessChain %25 %24 %31 %37 + %59 = OpLoad %6 %58 + %60 = OpAccessChain %25 %15 %37 %31 %17 + OpStore %60 %59 + %62 = OpAccessChain %25 %24 %31 %61 + %63 = OpLoad %6 %62 + %64 = OpAccessChain %25 %15 %37 %31 %31 + OpStore %64 %63 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/int_vs_uint_constants_autogen.cpp b/test/diff/diff_files/int_vs_uint_constants_autogen.cpp new file mode 100644 index 00000000..187722e8 --- /dev/null +++ b/test/diff/diff_files/int_vs_uint_constants_autogen.cpp @@ -0,0 +1,396 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests that identical integer constants are matched, regardless of int or +// uint. This helps compare output from different generators that default to +// int or uint for constants such as those passed to OpAccessChain. +constexpr char kSrc[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpName %4 "_ua_position" +OpName %17 "gl_PerVertex" +OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" +OpMemberName %17 3 "gl_CullDistance" +OpName %19 "" +OpName %22 "main" +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd)"; +constexpr char kDst[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 28 +; Schema: 0 +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %4 "main" %13 %17 +OpSource GLSL 450 +OpName %4 "main" +OpName %11 "gl_PerVertex" +OpMemberName %11 0 "gl_Position" +OpMemberName %11 1 "gl_PointSize" +OpMemberName %11 2 "gl_ClipDistance" +OpMemberName %11 3 "gl_CullDistance" +OpName %13 "" +OpName %17 "_ua_position" +OpMemberDecorate %11 0 BuiltIn Position +OpMemberDecorate %11 1 BuiltIn PointSize +OpMemberDecorate %11 2 BuiltIn ClipDistance +OpMemberDecorate %11 3 BuiltIn CullDistance +OpDecorate %11 Block +OpDecorate %17 Location 0 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 4 +%8 = OpTypeInt 32 0 +%9 = OpConstant %8 1 +%10 = OpTypeArray %6 %9 +%11 = OpTypeStruct %7 %6 %10 %10 +%12 = OpTypePointer Output %11 +%13 = OpVariable %12 Output +%14 = OpTypeInt 32 1 +%15 = OpConstant %14 0 +%16 = OpTypePointer Input %7 +%17 = OpVariable %16 Input +%19 = OpTypePointer Output %7 +%4 = OpFunction %2 None %3 +%5 = OpLabel +%18 = OpLoad %7 %17 +%20 = OpAccessChain %19 %13 %15 +OpStore %20 %18 +OpReturn +OpFunctionEnd +)"; + +TEST(DiffTest, IntVsUintConstants) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 27 ++; Bound: 31 + ; Schema: 0 + OpCapability Shader ++%27 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %22 "main" %4 %19 ++OpEntryPoint Vertex %22 "main" %19 %4 + OpSource GLSL 450 + OpName %4 "_ua_position" + OpName %17 "gl_PerVertex" + OpMemberName %17 0 "gl_Position" + OpMemberName %17 1 "gl_PointSize" + OpMemberName %17 2 "gl_ClipDistance" + OpMemberName %17 3 "gl_CullDistance" + OpName %19 "" + OpName %22 "main" + OpDecorate %4 Location 0 +-OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize + OpMemberDecorate %17 2 BuiltIn ClipDistance + OpMemberDecorate %17 3 BuiltIn CullDistance + OpDecorate %17 Block + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeInt 32 0 +-%8 = OpTypeVector %5 4 +-%15 = OpConstant %5 8 +-%16 = OpTypeArray %1 %15 +-%17 = OpTypeStruct %2 %1 %16 %16 ++%17 = OpTypeStruct %2 %1 %29 %29 + %20 = OpTypeVoid ++%28 = OpConstant %5 1 ++%29 = OpTypeArray %1 %28 +-%25 = OpConstant %5 0 ++%25 = OpConstant %30 0 + %3 = OpTypePointer Input %2 + %13 = OpTypePointer Output %2 ++%30 = OpTypeInt 32 1 + %18 = OpTypePointer Output %17 + %21 = OpTypeFunction %20 + %4 = OpVariable %3 Input + %19 = OpVariable %18 Output + %22 = OpFunction %20 None %21 + %23 = OpLabel + %24 = OpLoad %2 %4 + %26 = OpAccessChain %13 %19 %25 + OpStore %26 %24 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, IntVsUintConstantsNoDebug) { + constexpr char kSrcNoDebug[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 28 +; Schema: 0 +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %4 "main" %13 %17 +OpSource GLSL 450 +OpMemberDecorate %11 0 BuiltIn Position +OpMemberDecorate %11 1 BuiltIn PointSize +OpMemberDecorate %11 2 BuiltIn ClipDistance +OpMemberDecorate %11 3 BuiltIn CullDistance +OpDecorate %11 Block +OpDecorate %17 Location 0 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 4 +%8 = OpTypeInt 32 0 +%9 = OpConstant %8 1 +%10 = OpTypeArray %6 %9 +%11 = OpTypeStruct %7 %6 %10 %10 +%12 = OpTypePointer Output %11 +%13 = OpVariable %12 Output +%14 = OpTypeInt 32 1 +%15 = OpConstant %14 0 +%16 = OpTypePointer Input %7 +%17 = OpVariable %16 Input +%19 = OpTypePointer Output %7 +%4 = OpFunction %2 None %3 +%5 = OpLabel +%18 = OpLoad %7 %17 +%20 = OpAccessChain %19 %13 %15 +OpStore %20 %18 +OpReturn +OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 27 ++; Bound: 31 + ; Schema: 0 + OpCapability Shader ++%27 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %22 "main" %4 %19 ++OpEntryPoint Vertex %22 "main" %19 %4 + OpSource GLSL 450 + OpDecorate %4 Location 0 +-OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize + OpMemberDecorate %17 2 BuiltIn ClipDistance + OpMemberDecorate %17 3 BuiltIn CullDistance + OpDecorate %17 Block + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeInt 32 0 +-%8 = OpTypeVector %5 4 +-%15 = OpConstant %5 8 +-%16 = OpTypeArray %1 %15 +-%17 = OpTypeStruct %2 %1 %16 %16 ++%17 = OpTypeStruct %2 %1 %29 %29 + %20 = OpTypeVoid ++%28 = OpConstant %5 1 ++%29 = OpTypeArray %1 %28 +-%25 = OpConstant %5 0 ++%25 = OpConstant %30 0 + %3 = OpTypePointer Input %2 + %13 = OpTypePointer Output %2 ++%30 = OpTypeInt 32 1 + %18 = OpTypePointer Output %17 + %21 = OpTypeFunction %20 + %4 = OpVariable %3 Input + %19 = OpVariable %18 Output + %22 = OpFunction %20 None %21 + %23 = OpLabel + %24 = OpLoad %2 %4 + %26 = OpAccessChain %13 %19 %25 + OpStore %26 %24 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +TEST(DiffTest, IntVsUintConstantsDumpIds) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 27 ++; Bound: 31 + ; Schema: 0 + OpCapability Shader ++%27 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %22 "main" %4 %19 ++OpEntryPoint Vertex %22 "main" %19 %4 + OpSource GLSL 450 + OpName %4 "_ua_position" + OpName %17 "gl_PerVertex" + OpMemberName %17 0 "gl_Position" + OpMemberName %17 1 "gl_PointSize" + OpMemberName %17 2 "gl_ClipDistance" + OpMemberName %17 3 "gl_CullDistance" + OpName %19 "" + OpName %22 "main" + OpDecorate %4 Location 0 +-OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize + OpMemberDecorate %17 2 BuiltIn ClipDistance + OpMemberDecorate %17 3 BuiltIn CullDistance + OpDecorate %17 Block + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeInt 32 0 +-%8 = OpTypeVector %5 4 +-%15 = OpConstant %5 8 +-%16 = OpTypeArray %1 %15 +-%17 = OpTypeStruct %2 %1 %16 %16 ++%17 = OpTypeStruct %2 %1 %29 %29 + %20 = OpTypeVoid ++%28 = OpConstant %5 1 ++%29 = OpTypeArray %1 %28 +-%25 = OpConstant %5 0 ++%25 = OpConstant %30 0 + %3 = OpTypePointer Input %2 + %13 = OpTypePointer Output %2 ++%30 = OpTypeInt 32 1 + %18 = OpTypePointer Output %17 + %21 = OpTypeFunction %20 + %4 = OpVariable %3 Input + %19 = OpVariable %18 Output + %22 = OpFunction %20 None %21 + %23 = OpLabel + %24 = OpLoad %2 %4 + %26 = OpAccessChain %13 %19 %25 + OpStore %26 %24 + OpReturn + OpFunctionEnd + Src -> Dst + 1 -> 6 [TypeFloat] + 2 -> 7 [TypeVector] + 3 -> 16 [TypePointer] + 4 -> 17 [Variable] + 5 -> 8 [TypeInt] + 8 -> 23 [TypeVector] + 13 -> 19 [TypePointer] + 15 -> 29 [Constant] + 16 -> 30 [TypeArray] + 17 -> 11 [TypeStruct] + 18 -> 12 [TypePointer] + 19 -> 13 [Variable] + 20 -> 2 [TypeVoid] + 21 -> 3 [TypeFunction] + 22 -> 4 [Function] + 23 -> 5 [Label] + 24 -> 18 [Load] + 25 -> 15 [Constant] + 26 -> 20 [AccessChain] +)"; + Options options; + options.dump_id_map = true; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/int_vs_uint_constants_dst.spvasm b/test/diff/diff_files/int_vs_uint_constants_dst.spvasm new file mode 100644 index 00000000..da2618b2 --- /dev/null +++ b/test/diff/diff_files/int_vs_uint_constants_dst.spvasm @@ -0,0 +1,46 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 28 +; Schema: 0 +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %4 "main" %13 %17 +OpSource GLSL 450 +OpName %4 "main" +OpName %11 "gl_PerVertex" +OpMemberName %11 0 "gl_Position" +OpMemberName %11 1 "gl_PointSize" +OpMemberName %11 2 "gl_ClipDistance" +OpMemberName %11 3 "gl_CullDistance" +OpName %13 "" +OpName %17 "_ua_position" +OpMemberDecorate %11 0 BuiltIn Position +OpMemberDecorate %11 1 BuiltIn PointSize +OpMemberDecorate %11 2 BuiltIn ClipDistance +OpMemberDecorate %11 3 BuiltIn CullDistance +OpDecorate %11 Block +OpDecorate %17 Location 0 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 4 +%8 = OpTypeInt 32 0 +%9 = OpConstant %8 1 +%10 = OpTypeArray %6 %9 +%11 = OpTypeStruct %7 %6 %10 %10 +%12 = OpTypePointer Output %11 +%13 = OpVariable %12 Output +%14 = OpTypeInt 32 1 +%15 = OpConstant %14 0 +%16 = OpTypePointer Input %7 +%17 = OpVariable %16 Input +%19 = OpTypePointer Output %7 +%4 = OpFunction %2 None %3 +%5 = OpLabel +%18 = OpLoad %7 %17 +%20 = OpAccessChain %19 %13 %15 +OpStore %20 %18 +OpReturn +OpFunctionEnd diff --git a/test/diff/diff_files/int_vs_uint_constants_src.spvasm b/test/diff/diff_files/int_vs_uint_constants_src.spvasm new file mode 100644 index 00000000..214b49bd --- /dev/null +++ b/test/diff/diff_files/int_vs_uint_constants_src.spvasm @@ -0,0 +1,49 @@ +;; Tests that identical integer constants are matched, regardless of int or +;; uint. This helps compare output from different generators that default to +;; int or uint for constants such as those passed to OpAccessChain. +; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpName %4 "_ua_position" +OpName %17 "gl_PerVertex" +OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" +OpMemberName %17 3 "gl_CullDistance" +OpName %19 "" +OpName %22 "main" +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd diff --git a/test/diff/diff_files/large_functions_large_diffs_autogen.cpp b/test/diff/diff_files/large_functions_large_diffs_autogen.cpp new file mode 100644 index 00000000..12cb6219 --- /dev/null +++ b/test/diff/diff_files/large_functions_large_diffs_autogen.cpp @@ -0,0 +1,1534 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test where src and dst have a few large functions with large differences. +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %12 "x" + OpName %15 "gl_LocalInvocationID" + OpName %20 "y" + OpName %27 "image" + OpName %44 "sum" + OpName %46 "i" + OpName %56 "j" + OpName %80 "BufferOut" + OpMemberName %80 0 "o_uv4" + OpMemberName %80 1 "o_v3" + OpMemberName %80 2 "o_i" + OpName %82 "" + OpName %88 "BufferIn" + OpMemberName %88 0 "i_u" + OpMemberName %88 1 "i_v4" + OpMemberName %88 2 "i_f" + OpName %90 "" + OpName %101 "i" + OpName %128 "image2" + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 + OpDecorate %128 DescriptorSet 0 + OpDecorate %128 Binding 3 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform + %91 = OpTypePointer Uniform %87 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 + %110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 + %128 = OpVariable %26 UniformConstant + %130 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %136 = OpFunctionCall %2 %6 + %137 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function + %46 = OpVariable %43 Function + %56 = OpVariable %43 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + %28 = OpLoad %25 %27 + %30 = OpLoad %13 %15 + %31 = OpVectorShuffle %29 %30 %30 0 1 + %33 = OpBitcast %32 %31 + %34 = OpLoad %10 %12 + %35 = OpLoad %10 %20 + %36 = OpIAdd %10 %34 %35 + %37 = OpBitcast %24 %36 + %39 = OpCompositeConstruct %38 %37 %37 %37 %37 + OpImageWrite %28 %33 %39 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + OpStore %44 %45 + OpStore %46 %45 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %24 %46 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel + OpStore %56 %45 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %24 %56 + %63 = OpSLessThan %54 %62 %53 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %64 = OpLoad %25 %27 + %65 = OpLoad %24 %46 + %66 = OpLoad %24 %56 + %67 = OpCompositeConstruct %32 %65 %66 + %68 = OpImageRead %38 %64 %67 + %69 = OpCompositeExtract %24 %68 0 + %70 = OpLoad %24 %44 + %71 = OpIMul %24 %70 %69 + OpStore %44 %71 + OpBranch %60 + %60 = OpLabel + %72 = OpLoad %24 %56 + %74 = OpIAdd %24 %72 %73 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %50 + %50 = OpLabel + %75 = OpLoad %24 %46 + %76 = OpIAdd %24 %75 %73 + OpStore %46 %76 + OpBranch %47 + %49 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %83 = OpLoad %24 %44 + %85 = OpAccessChain %84 %82 %53 + OpStore %85 %83 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function + %92 = OpAccessChain %91 %90 %73 + %93 = OpLoad %87 %92 + %95 = OpAccessChain %94 %82 %45 + %96 = OpLoad %77 %95 + %97 = OpConvertUToF %86 %96 + %98 = OpMatrixTimesVector %86 %93 %97 + %99 = OpConvertFToU %77 %98 + %100 = OpAccessChain %94 %82 %45 + OpStore %100 %99 + OpStore %101 %45 + OpBranch %102 + %102 = OpLabel + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 + OpBranchConditional %109 %103 %104 + %103 = OpLabel + %111 = OpAccessChain %110 %82 %73 + %112 = OpLoad %79 %111 + %114 = OpAccessChain %113 %90 %53 + %115 = OpLoad %78 %114 + %116 = OpVectorTimesScalar %79 %112 %115 + %117 = OpConvertFToU %13 %116 + %118 = OpCompositeExtract %10 %117 0 + %119 = OpCompositeExtract %10 %117 1 + %120 = OpCompositeExtract %10 %117 2 + %121 = OpCompositeConstruct %77 %118 %119 %120 %16 + %122 = OpAccessChain %94 %82 %45 + %123 = OpLoad %77 %122 + %124 = OpIAdd %77 %123 %121 + %125 = OpAccessChain %94 %82 %45 + OpStore %125 %124 + OpBranch %105 + %105 = OpLabel + %126 = OpLoad %24 %101 + %127 = OpIAdd %24 %126 %73 + OpStore %101 %127 + OpBranch %102 + %104 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %129 = OpLoad %25 %128 + %131 = OpImageRead %38 %129 %130 + %132 = OpCompositeExtract %24 %131 0 + %133 = OpConvertSToF %78 %132 + %134 = OpCompositeConstruct %79 %133 %133 %133 + %135 = OpAccessChain %110 %82 %73 + OpStore %135 %134 + OpReturn + OpFunctionEnd +)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 %110 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %12 "x" + OpName %15 "gl_GlobalInvocationID" + OpName %20 "z" + OpName %26 "i" + OpName %40 "BufferOut" + OpMemberName %40 0 "o_uv4" + OpMemberName %40 1 "o_v3" + OpMemberName %40 2 "o_i" + OpName %42 "" + OpName %63 "image2" + OpName %79 "image" + OpName %89 "i" + OpName %110 "gl_LocalInvocationID" + OpName %127 "BufferIn" + OpMemberName %127 0 "i_u" + OpMemberName %127 1 "i_v4" + OpMemberName %127 2 "i_f" + OpName %129 "" + OpDecorate %15 BuiltIn GlobalInvocationId + OpMemberDecorate %40 0 Offset 0 + OpMemberDecorate %40 1 Offset 16 + OpMemberDecorate %40 2 Offset 28 + OpDecorate %40 BufferBlock + OpDecorate %42 DescriptorSet 0 + OpDecorate %42 Binding 1 + OpDecorate %63 DescriptorSet 0 + OpDecorate %63 Binding 3 + OpDecorate %79 DescriptorSet 0 + OpDecorate %79 Binding 2 + OpDecorate %110 BuiltIn LocalInvocationId + OpMemberDecorate %127 0 Offset 0 + OpMemberDecorate %127 1 RowMajor + OpMemberDecorate %127 1 Offset 16 + OpMemberDecorate %127 1 MatrixStride 16 + OpMemberDecorate %127 2 Offset 80 + OpDecorate %127 Block + OpDecorate %129 DescriptorSet 0 + OpDecorate %129 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypePointer Function %24 + %27 = OpConstant %24 0 + %34 = OpConstant %24 2 + %35 = OpTypeBool + %37 = OpTypeVector %10 4 + %38 = OpTypeFloat 32 + %39 = OpTypeVector %38 3 + %40 = OpTypeStruct %37 %39 %24 + %41 = OpTypePointer Uniform %40 + %42 = OpVariable %41 Uniform + %46 = OpTypeVector %10 2 + %48 = OpTypePointer Uniform %37 + %53 = OpTypePointer Uniform %10 + %59 = OpConstant %24 1 + %61 = OpTypeImage %24 2D 0 0 0 2 R32i + %62 = OpTypePointer UniformConstant %61 + %63 = OpVariable %62 UniformConstant + %69 = OpTypeVector %24 2 + %71 = OpTypeVector %24 4 + %74 = OpTypePointer Uniform %24 + %76 = OpConstant %10 2 + %77 = OpConstant %10 3400 + %78 = OpConstant %10 264 + %79 = OpVariable %62 UniformConstant + %96 = OpConstant %24 3 + %103 = OpConstantComposite %69 %27 %27 + %107 = OpTypePointer Uniform %38 + %110 = OpVariable %14 Input + %113 = OpTypeVector %38 2 + %125 = OpTypeVector %38 4 + %126 = OpTypeMatrix %125 4 + %127 = OpTypeStruct %10 %126 %38 + %128 = OpTypePointer Uniform %127 + %129 = OpVariable %128 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %123 = OpFunctionCall %2 %8 + %124 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %26 = OpVariable %25 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + OpStore %26 %27 + OpBranch %28 + %28 = OpLabel + OpLoopMerge %30 %31 None + OpBranch %32 + %32 = OpLabel + %33 = OpLoad %24 %26 + %36 = OpSLessThan %35 %33 %34 + OpBranchConditional %36 %29 %30 + %29 = OpLabel + %43 = OpLoad %10 %12 + %44 = OpLoad %10 %20 + %45 = OpIAdd %10 %43 %44 + %47 = OpCompositeConstruct %46 %45 %45 + %49 = OpAccessChain %48 %42 %27 + %50 = OpLoad %37 %49 + %51 = OpVectorShuffle %46 %50 %50 0 1 + %52 = OpIAdd %46 %51 %47 + %54 = OpAccessChain %53 %42 %27 %16 + %55 = OpCompositeExtract %10 %52 0 + OpStore %54 %55 + %56 = OpAccessChain %53 %42 %27 %21 + %57 = OpCompositeExtract %10 %52 1 + OpStore %56 %57 + OpBranch %31 + %31 = OpLabel + %58 = OpLoad %24 %26 + %60 = OpIAdd %24 %58 %59 + OpStore %26 %60 + OpBranch %28 + %30 = OpLabel + %64 = OpLoad %61 %63 + %65 = OpLoad %10 %12 + %66 = OpBitcast %24 %65 + %67 = OpLoad %10 %20 + %68 = OpBitcast %24 %67 + %70 = OpCompositeConstruct %69 %66 %68 + %72 = OpImageRead %71 %64 %70 + %73 = OpCompositeExtract %24 %72 1 + %75 = OpAccessChain %74 %42 %34 + OpStore %75 %73 + OpMemoryBarrier %76 %77 + OpControlBarrier %76 %76 %78 + %80 = OpLoad %61 %79 + %81 = OpLoad %10 %20 + %82 = OpBitcast %24 %81 + %83 = OpLoad %10 %12 + %84 = OpBitcast %24 %83 + %85 = OpCompositeConstruct %69 %82 %84 + %86 = OpAccessChain %74 %42 %34 + %87 = OpLoad %24 %86 + %88 = OpCompositeConstruct %71 %87 %27 %27 %27 + OpImageWrite %80 %85 %88 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %89 = OpVariable %25 Function + OpStore %89 %27 + OpBranch %90 + %90 = OpLabel + OpLoopMerge %92 %93 None + OpBranch %94 + %94 = OpLabel + %95 = OpLoad %24 %89 + %97 = OpSLessThan %35 %95 %96 + OpBranchConditional %97 %91 %92 + %91 = OpLabel + %98 = OpLoad %24 %89 + %99 = OpIEqual %35 %98 %27 + OpSelectionMerge %101 None + OpBranchConditional %99 %100 %109 + %100 = OpLabel + %102 = OpLoad %61 %63 + %104 = OpImageRead %71 %102 %103 + %105 = OpCompositeExtract %24 %104 0 + %106 = OpConvertSToF %38 %105 + %108 = OpAccessChain %107 %42 %59 %16 + OpStore %108 %106 + OpBranch %101 + %109 = OpLabel + %111 = OpLoad %13 %110 + %112 = OpConvertUToF %39 %111 + %114 = OpCompositeExtract %38 %112 0 + %115 = OpCompositeExtract %38 %112 1 + %116 = OpCompositeConstruct %113 %114 %115 + %117 = OpAccessChain %107 %42 %59 %21 + %118 = OpCompositeExtract %38 %116 0 + OpStore %117 %118 + %119 = OpAccessChain %107 %42 %59 %76 + %120 = OpCompositeExtract %38 %116 1 + OpStore %119 %120 + OpBranch %101 + %101 = OpLabel + OpBranch %93 + %93 = OpLabel + %121 = OpLoad %24 %89 + %122 = OpIAdd %24 %121 %59 + OpStore %89 %122 + OpBranch %90 + %92 = OpLabel + OpReturn + OpFunctionEnd + +)"; + +TEST(DiffTest, LargeFunctionsLargeDiffs) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 138 ++; Bound: 190 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint GLCompute %4 "main" %15 ++OpEntryPoint GLCompute %4 "main" %138 %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %12 "x" ++OpName %138 "gl_GlobalInvocationID" + OpName %15 "gl_LocalInvocationID" +-OpName %20 "y" ++OpName %20 "z" + OpName %27 "image" +-OpName %44 "sum" ++OpName %44 "i" +-OpName %46 "i" +-OpName %56 "j" + OpName %80 "BufferOut" + OpMemberName %80 0 "o_uv4" + OpMemberName %80 1 "o_v3" + OpMemberName %80 2 "o_i" + OpName %82 "" + OpName %88 "BufferIn" + OpMemberName %88 0 "i_u" + OpMemberName %88 1 "i_v4" + OpMemberName %88 2 "i_f" + OpName %90 "" + OpName %101 "i" + OpName %128 "image2" ++OpDecorate %138 BuiltIn GlobalInvocationId + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 + OpDecorate %128 DescriptorSet 0 + OpDecorate %128 Binding 3 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 ++%138 = OpVariable %14 Input + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 ++%149 = OpTypePointer Uniform %10 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform +-%91 = OpTypePointer Uniform %87 ++%179 = OpTypeVector %78 2 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 +-%110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 + %128 = OpVariable %26 UniformConstant + %130 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel +-%136 = OpFunctionCall %2 %6 + %137 = OpFunctionCall %2 %8 ++%189 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function +-%46 = OpVariable %43 Function +-%56 = OpVariable %43 Function +-%18 = OpAccessChain %17 %15 %16 ++%139 = OpAccessChain %17 %138 %16 +-%19 = OpLoad %10 %18 ++%19 = OpLoad %10 %139 + OpStore %12 %19 +-%22 = OpAccessChain %17 %15 %21 ++%140 = OpAccessChain %17 %138 %21 +-%23 = OpLoad %10 %22 ++%23 = OpLoad %10 %140 + OpStore %20 %23 +-%28 = OpLoad %25 %27 +-%30 = OpLoad %13 %15 +-%31 = OpVectorShuffle %29 %30 %30 0 1 +-%33 = OpBitcast %32 %31 +-%34 = OpLoad %10 %12 +-%35 = OpLoad %10 %20 +-%36 = OpIAdd %10 %34 %35 +-%37 = OpBitcast %24 %36 +-%39 = OpCompositeConstruct %38 %37 %37 %37 %37 +-OpImageWrite %28 %33 %39 +-OpMemoryBarrier %40 %41 +-OpControlBarrier %40 %40 %42 + OpStore %44 %45 +-OpStore %46 %45 + OpBranch %47 + %47 = OpLabel +-OpLoopMerge %49 %50 None ++OpLoopMerge %49 %59 None + OpBranch %51 + %51 = OpLabel +-%52 = OpLoad %24 %46 ++%52 = OpLoad %24 %44 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel +-OpStore %56 %45 +-OpBranch %57 +-%57 = OpLabel +-OpLoopMerge %59 %60 None +-OpBranch %61 +-%61 = OpLabel +-%62 = OpLoad %24 %56 +-%63 = OpSLessThan %54 %62 %53 +-OpBranchConditional %63 %58 %59 +-%58 = OpLabel +-%64 = OpLoad %25 %27 +-%65 = OpLoad %24 %46 +-%66 = OpLoad %24 %56 +-%67 = OpCompositeConstruct %32 %65 %66 +-%68 = OpImageRead %38 %64 %67 +-%69 = OpCompositeExtract %24 %68 0 +-%70 = OpLoad %24 %44 +-%71 = OpIMul %24 %70 %69 ++%141 = OpLoad %10 %12 ++%142 = OpLoad %10 %20 ++%143 = OpIAdd %10 %141 %142 ++%144 = OpCompositeConstruct %29 %143 %143 ++%145 = OpAccessChain %94 %82 %45 ++%146 = OpLoad %77 %145 ++%147 = OpVectorShuffle %29 %146 %146 0 1 ++%148 = OpIAdd %29 %147 %144 ++%150 = OpAccessChain %149 %82 %45 %16 ++%151 = OpCompositeExtract %10 %148 0 +-OpStore %44 %71 ++OpStore %150 %151 +-OpBranch %60 +-%60 = OpLabel +-%72 = OpLoad %24 %56 +-%74 = OpIAdd %24 %72 %73 ++%152 = OpAccessChain %149 %82 %45 %21 ++%153 = OpCompositeExtract %10 %148 1 +-OpStore %56 %74 ++OpStore %152 %153 +-OpBranch %57 ++OpBranch %59 + %59 = OpLabel +-OpBranch %50 +-%50 = OpLabel +-%75 = OpLoad %24 %46 ++%75 = OpLoad %24 %44 + %76 = OpIAdd %24 %75 %73 +-OpStore %46 %76 ++OpStore %44 %76 + OpBranch %47 + %49 = OpLabel ++%154 = OpLoad %25 %128 ++%155 = OpLoad %10 %12 ++%156 = OpBitcast %24 %155 ++%157 = OpLoad %10 %20 ++%158 = OpBitcast %24 %157 ++%159 = OpCompositeConstruct %32 %156 %158 ++%160 = OpImageRead %38 %154 %159 ++%161 = OpCompositeExtract %24 %160 1 ++%162 = OpAccessChain %84 %82 %53 ++OpStore %162 %161 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 +-%83 = OpLoad %24 %44 ++%163 = OpLoad %25 %27 ++%164 = OpLoad %10 %20 ++%165 = OpBitcast %24 %164 ++%166 = OpLoad %10 %12 ++%167 = OpBitcast %24 %166 ++%168 = OpCompositeConstruct %32 %165 %167 + %85 = OpAccessChain %84 %82 %53 +-OpStore %85 %83 ++%169 = OpLoad %24 %85 ++%170 = OpCompositeConstruct %38 %169 %45 %45 %45 ++OpImageWrite %163 %168 %170 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function +-%92 = OpAccessChain %91 %90 %73 +-%93 = OpLoad %87 %92 +-%95 = OpAccessChain %94 %82 %45 +-%96 = OpLoad %77 %95 +-%97 = OpConvertUToF %86 %96 +-%98 = OpMatrixTimesVector %86 %93 %97 +-%99 = OpConvertFToU %77 %98 +-%100 = OpAccessChain %94 %82 %45 +-OpStore %100 %99 ++OpStore %101 %45 +-OpStore %101 %45 + OpBranch %102 + %102 = OpLabel +-OpLoopMerge %104 %105 None ++OpLoopMerge %171 %172 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 +-OpBranchConditional %109 %103 %104 ++OpBranchConditional %109 %103 %171 + %103 = OpLabel +-%111 = OpAccessChain %110 %82 %73 +-%112 = OpLoad %79 %111 +-%114 = OpAccessChain %113 %90 %53 +-%115 = OpLoad %78 %114 +-%116 = OpVectorTimesScalar %79 %112 %115 +-%117 = OpConvertFToU %13 %116 +-%118 = OpCompositeExtract %10 %117 0 +-%119 = OpCompositeExtract %10 %117 1 +-%120 = OpCompositeExtract %10 %117 2 +-%121 = OpCompositeConstruct %77 %118 %119 %120 %16 +-%122 = OpAccessChain %94 %82 %45 +-%123 = OpLoad %77 %122 +-%124 = OpIAdd %77 %123 %121 +-%125 = OpAccessChain %94 %82 %45 +-OpStore %125 %124 +-OpBranch %105 +-%105 = OpLabel + %126 = OpLoad %24 %101 +-%127 = OpIAdd %24 %126 %73 ++%173 = OpIEqual %54 %126 %45 ++OpSelectionMerge %174 None ++OpBranchConditional %173 %104 %176 ++%176 = OpLabel ++%177 = OpLoad %13 %15 ++%178 = OpConvertUToF %79 %177 ++%180 = OpCompositeExtract %78 %178 0 ++%181 = OpCompositeExtract %78 %178 1 ++%182 = OpCompositeConstruct %179 %180 %181 ++%183 = OpAccessChain %113 %82 %73 %21 ++%184 = OpCompositeExtract %78 %182 0 +-OpStore %101 %127 ++OpStore %183 %184 ++%185 = OpAccessChain %113 %82 %73 %40 ++%186 = OpCompositeExtract %78 %182 1 ++OpStore %185 %186 +-OpBranch %102 ++OpBranch %174 + %104 = OpLabel +-OpMemoryBarrier %40 %41 +-OpControlBarrier %40 %40 %42 + %129 = OpLoad %25 %128 + %131 = OpImageRead %38 %129 %130 + %132 = OpCompositeExtract %24 %131 0 + %133 = OpConvertSToF %78 %132 +-%134 = OpCompositeConstruct %79 %133 %133 %133 +-%135 = OpAccessChain %110 %82 %73 ++%175 = OpAccessChain %113 %82 %73 %16 +-OpStore %135 %134 ++OpStore %175 %133 ++OpBranch %174 ++%174 = OpLabel ++OpBranch %172 ++%172 = OpLabel ++%187 = OpLoad %24 %101 ++%188 = OpIAdd %24 %187 %73 ++OpStore %101 %188 ++OpBranch %102 ++%171 = OpLabel + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, LargeFunctionsLargeDiffsNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 + OpDecorate %128 DescriptorSet 0 + OpDecorate %128 Binding 3 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform + %91 = OpTypePointer Uniform %87 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 + %110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 + %128 = OpVariable %26 UniformConstant + %130 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %136 = OpFunctionCall %2 %6 + %137 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function + %46 = OpVariable %43 Function + %56 = OpVariable %43 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + %28 = OpLoad %25 %27 + %30 = OpLoad %13 %15 + %31 = OpVectorShuffle %29 %30 %30 0 1 + %33 = OpBitcast %32 %31 + %34 = OpLoad %10 %12 + %35 = OpLoad %10 %20 + %36 = OpIAdd %10 %34 %35 + %37 = OpBitcast %24 %36 + %39 = OpCompositeConstruct %38 %37 %37 %37 %37 + OpImageWrite %28 %33 %39 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + OpStore %44 %45 + OpStore %46 %45 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %24 %46 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel + OpStore %56 %45 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %24 %56 + %63 = OpSLessThan %54 %62 %53 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %64 = OpLoad %25 %27 + %65 = OpLoad %24 %46 + %66 = OpLoad %24 %56 + %67 = OpCompositeConstruct %32 %65 %66 + %68 = OpImageRead %38 %64 %67 + %69 = OpCompositeExtract %24 %68 0 + %70 = OpLoad %24 %44 + %71 = OpIMul %24 %70 %69 + OpStore %44 %71 + OpBranch %60 + %60 = OpLabel + %72 = OpLoad %24 %56 + %74 = OpIAdd %24 %72 %73 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %50 + %50 = OpLabel + %75 = OpLoad %24 %46 + %76 = OpIAdd %24 %75 %73 + OpStore %46 %76 + OpBranch %47 + %49 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %83 = OpLoad %24 %44 + %85 = OpAccessChain %84 %82 %53 + OpStore %85 %83 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function + %92 = OpAccessChain %91 %90 %73 + %93 = OpLoad %87 %92 + %95 = OpAccessChain %94 %82 %45 + %96 = OpLoad %77 %95 + %97 = OpConvertUToF %86 %96 + %98 = OpMatrixTimesVector %86 %93 %97 + %99 = OpConvertFToU %77 %98 + %100 = OpAccessChain %94 %82 %45 + OpStore %100 %99 + OpStore %101 %45 + OpBranch %102 + %102 = OpLabel + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 + OpBranchConditional %109 %103 %104 + %103 = OpLabel + %111 = OpAccessChain %110 %82 %73 + %112 = OpLoad %79 %111 + %114 = OpAccessChain %113 %90 %53 + %115 = OpLoad %78 %114 + %116 = OpVectorTimesScalar %79 %112 %115 + %117 = OpConvertFToU %13 %116 + %118 = OpCompositeExtract %10 %117 0 + %119 = OpCompositeExtract %10 %117 1 + %120 = OpCompositeExtract %10 %117 2 + %121 = OpCompositeConstruct %77 %118 %119 %120 %16 + %122 = OpAccessChain %94 %82 %45 + %123 = OpLoad %77 %122 + %124 = OpIAdd %77 %123 %121 + %125 = OpAccessChain %94 %82 %45 + OpStore %125 %124 + OpBranch %105 + %105 = OpLabel + %126 = OpLoad %24 %101 + %127 = OpIAdd %24 %126 %73 + OpStore %101 %127 + OpBranch %102 + %104 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %129 = OpLoad %25 %128 + %131 = OpImageRead %38 %129 %130 + %132 = OpCompositeExtract %24 %131 0 + %133 = OpConvertSToF %78 %132 + %134 = OpCompositeConstruct %79 %133 %133 %133 + %135 = OpAccessChain %110 %82 %73 + OpStore %135 %134 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 %110 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpDecorate %15 BuiltIn GlobalInvocationId + OpMemberDecorate %40 0 Offset 0 + OpMemberDecorate %40 1 Offset 16 + OpMemberDecorate %40 2 Offset 28 + OpDecorate %40 BufferBlock + OpDecorate %42 DescriptorSet 0 + OpDecorate %42 Binding 1 + OpDecorate %63 DescriptorSet 0 + OpDecorate %63 Binding 3 + OpDecorate %79 DescriptorSet 0 + OpDecorate %79 Binding 2 + OpDecorate %110 BuiltIn LocalInvocationId + OpMemberDecorate %127 0 Offset 0 + OpMemberDecorate %127 1 RowMajor + OpMemberDecorate %127 1 Offset 16 + OpMemberDecorate %127 1 MatrixStride 16 + OpMemberDecorate %127 2 Offset 80 + OpDecorate %127 Block + OpDecorate %129 DescriptorSet 0 + OpDecorate %129 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypePointer Function %24 + %27 = OpConstant %24 0 + %34 = OpConstant %24 2 + %35 = OpTypeBool + %37 = OpTypeVector %10 4 + %38 = OpTypeFloat 32 + %39 = OpTypeVector %38 3 + %40 = OpTypeStruct %37 %39 %24 + %41 = OpTypePointer Uniform %40 + %42 = OpVariable %41 Uniform + %46 = OpTypeVector %10 2 + %48 = OpTypePointer Uniform %37 + %53 = OpTypePointer Uniform %10 + %59 = OpConstant %24 1 + %61 = OpTypeImage %24 2D 0 0 0 2 R32i + %62 = OpTypePointer UniformConstant %61 + %63 = OpVariable %62 UniformConstant + %69 = OpTypeVector %24 2 + %71 = OpTypeVector %24 4 + %74 = OpTypePointer Uniform %24 + %76 = OpConstant %10 2 + %77 = OpConstant %10 3400 + %78 = OpConstant %10 264 + %79 = OpVariable %62 UniformConstant + %96 = OpConstant %24 3 + %103 = OpConstantComposite %69 %27 %27 + %107 = OpTypePointer Uniform %38 + %110 = OpVariable %14 Input + %113 = OpTypeVector %38 2 + %125 = OpTypeVector %38 4 + %126 = OpTypeMatrix %125 4 + %127 = OpTypeStruct %10 %126 %38 + %128 = OpTypePointer Uniform %127 + %129 = OpVariable %128 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %123 = OpFunctionCall %2 %8 + %124 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %26 = OpVariable %25 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + OpStore %26 %27 + OpBranch %28 + %28 = OpLabel + OpLoopMerge %30 %31 None + OpBranch %32 + %32 = OpLabel + %33 = OpLoad %24 %26 + %36 = OpSLessThan %35 %33 %34 + OpBranchConditional %36 %29 %30 + %29 = OpLabel + %43 = OpLoad %10 %12 + %44 = OpLoad %10 %20 + %45 = OpIAdd %10 %43 %44 + %47 = OpCompositeConstruct %46 %45 %45 + %49 = OpAccessChain %48 %42 %27 + %50 = OpLoad %37 %49 + %51 = OpVectorShuffle %46 %50 %50 0 1 + %52 = OpIAdd %46 %51 %47 + %54 = OpAccessChain %53 %42 %27 %16 + %55 = OpCompositeExtract %10 %52 0 + OpStore %54 %55 + %56 = OpAccessChain %53 %42 %27 %21 + %57 = OpCompositeExtract %10 %52 1 + OpStore %56 %57 + OpBranch %31 + %31 = OpLabel + %58 = OpLoad %24 %26 + %60 = OpIAdd %24 %58 %59 + OpStore %26 %60 + OpBranch %28 + %30 = OpLabel + %64 = OpLoad %61 %63 + %65 = OpLoad %10 %12 + %66 = OpBitcast %24 %65 + %67 = OpLoad %10 %20 + %68 = OpBitcast %24 %67 + %70 = OpCompositeConstruct %69 %66 %68 + %72 = OpImageRead %71 %64 %70 + %73 = OpCompositeExtract %24 %72 1 + %75 = OpAccessChain %74 %42 %34 + OpStore %75 %73 + OpMemoryBarrier %76 %77 + OpControlBarrier %76 %76 %78 + %80 = OpLoad %61 %79 + %81 = OpLoad %10 %20 + %82 = OpBitcast %24 %81 + %83 = OpLoad %10 %12 + %84 = OpBitcast %24 %83 + %85 = OpCompositeConstruct %69 %82 %84 + %86 = OpAccessChain %74 %42 %34 + %87 = OpLoad %24 %86 + %88 = OpCompositeConstruct %71 %87 %27 %27 %27 + OpImageWrite %80 %85 %88 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %89 = OpVariable %25 Function + OpStore %89 %27 + OpBranch %90 + %90 = OpLabel + OpLoopMerge %92 %93 None + OpBranch %94 + %94 = OpLabel + %95 = OpLoad %24 %89 + %97 = OpSLessThan %35 %95 %96 + OpBranchConditional %97 %91 %92 + %91 = OpLabel + %98 = OpLoad %24 %89 + %99 = OpIEqual %35 %98 %27 + OpSelectionMerge %101 None + OpBranchConditional %99 %100 %109 + %100 = OpLabel + %102 = OpLoad %61 %63 + %104 = OpImageRead %71 %102 %103 + %105 = OpCompositeExtract %24 %104 0 + %106 = OpConvertSToF %38 %105 + %108 = OpAccessChain %107 %42 %59 %16 + OpStore %108 %106 + OpBranch %101 + %109 = OpLabel + %111 = OpLoad %13 %110 + %112 = OpConvertUToF %39 %111 + %114 = OpCompositeExtract %38 %112 0 + %115 = OpCompositeExtract %38 %112 1 + %116 = OpCompositeConstruct %113 %114 %115 + %117 = OpAccessChain %107 %42 %59 %21 + %118 = OpCompositeExtract %38 %116 0 + OpStore %117 %118 + %119 = OpAccessChain %107 %42 %59 %76 + %120 = OpCompositeExtract %38 %116 1 + OpStore %119 %120 + OpBranch %101 + %101 = OpLabel + OpBranch %93 + %93 = OpLabel + %121 = OpLoad %24 %89 + %122 = OpIAdd %24 %121 %59 + OpStore %89 %122 + OpBranch %90 + %92 = OpLabel + OpReturn + OpFunctionEnd + +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 138 ++; Bound: 220 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint GLCompute %4 "main" %15 ++OpEntryPoint GLCompute %4 "main" %143 %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 ++OpDecorate %143 BuiltIn GlobalInvocationId + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 + OpDecorate %128 DescriptorSet 0 + OpDecorate %128 Binding 3 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 ++%143 = OpVariable %14 Input + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 ++%165 = OpTypePointer Uniform %10 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform +-%91 = OpTypePointer Uniform %87 ++%210 = OpTypeVector %78 2 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 +-%110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 + %128 = OpVariable %26 UniformConstant + %130 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel +-%136 = OpFunctionCall %2 %6 ++%136 = OpFunctionCall %2 %140 +-%137 = OpFunctionCall %2 %8 ++%137 = OpFunctionCall %2 %138 + OpReturn + OpFunctionEnd +-%6 = OpFunction %2 None %3 +-%7 = OpLabel +-%12 = OpVariable %11 Function +-%20 = OpVariable %11 Function +-%44 = OpVariable %43 Function +-%46 = OpVariable %43 Function +-%56 = OpVariable %43 Function +-%18 = OpAccessChain %17 %15 %16 +-%19 = OpLoad %10 %18 +-OpStore %12 %19 +-%22 = OpAccessChain %17 %15 %21 +-%23 = OpLoad %10 %22 +-OpStore %20 %23 +-%28 = OpLoad %25 %27 +-%30 = OpLoad %13 %15 +-%31 = OpVectorShuffle %29 %30 %30 0 1 +-%33 = OpBitcast %32 %31 +-%34 = OpLoad %10 %12 +-%35 = OpLoad %10 %20 +-%36 = OpIAdd %10 %34 %35 +-%37 = OpBitcast %24 %36 +-%39 = OpCompositeConstruct %38 %37 %37 %37 %37 +-OpImageWrite %28 %33 %39 +-OpMemoryBarrier %40 %41 +-OpControlBarrier %40 %40 %42 +-OpStore %44 %45 +-OpStore %46 %45 +-OpBranch %47 +-%47 = OpLabel +-OpLoopMerge %49 %50 None +-OpBranch %51 +-%51 = OpLabel +-%52 = OpLoad %24 %46 +-%55 = OpSLessThan %54 %52 %53 +-OpBranchConditional %55 %48 %49 +-%48 = OpLabel +-OpStore %56 %45 +-OpBranch %57 +-%57 = OpLabel +-OpLoopMerge %59 %60 None +-OpBranch %61 +-%61 = OpLabel +-%62 = OpLoad %24 %56 +-%63 = OpSLessThan %54 %62 %53 +-OpBranchConditional %63 %58 %59 +-%58 = OpLabel +-%64 = OpLoad %25 %27 +-%65 = OpLoad %24 %46 +-%66 = OpLoad %24 %56 +-%67 = OpCompositeConstruct %32 %65 %66 +-%68 = OpImageRead %38 %64 %67 +-%69 = OpCompositeExtract %24 %68 0 +-%70 = OpLoad %24 %44 +-%71 = OpIMul %24 %70 %69 +-OpStore %44 %71 +-OpBranch %60 +-%60 = OpLabel +-%72 = OpLoad %24 %56 +-%74 = OpIAdd %24 %72 %73 +-OpStore %56 %74 +-OpBranch %57 +-%59 = OpLabel +-OpBranch %50 +-%50 = OpLabel +-%75 = OpLoad %24 %46 +-%76 = OpIAdd %24 %75 %73 +-OpStore %46 %76 +-OpBranch %47 +-%49 = OpLabel +-OpMemoryBarrier %40 %41 +-OpControlBarrier %40 %40 %42 +-%83 = OpLoad %24 %44 +-%85 = OpAccessChain %84 %82 %53 +-OpStore %85 %83 +-OpReturn +-OpFunctionEnd +-%8 = OpFunction %2 None %3 +-%9 = OpLabel +-%101 = OpVariable %43 Function +-%92 = OpAccessChain %91 %90 %73 +-%93 = OpLoad %87 %92 +-%95 = OpAccessChain %94 %82 %45 +-%96 = OpLoad %77 %95 +-%97 = OpConvertUToF %86 %96 +-%98 = OpMatrixTimesVector %86 %93 %97 +-%99 = OpConvertFToU %77 %98 +-%100 = OpAccessChain %94 %82 %45 +-OpStore %100 %99 +-OpStore %101 %45 +-OpBranch %102 +-%102 = OpLabel +-OpLoopMerge %104 %105 None +-OpBranch %106 +-%106 = OpLabel +-%107 = OpLoad %24 %101 +-%109 = OpSLessThan %54 %107 %108 +-OpBranchConditional %109 %103 %104 +-%103 = OpLabel +-%111 = OpAccessChain %110 %82 %73 +-%112 = OpLoad %79 %111 +-%114 = OpAccessChain %113 %90 %53 +-%115 = OpLoad %78 %114 +-%116 = OpVectorTimesScalar %79 %112 %115 +-%117 = OpConvertFToU %13 %116 +-%118 = OpCompositeExtract %10 %117 0 +-%119 = OpCompositeExtract %10 %117 1 +-%120 = OpCompositeExtract %10 %117 2 +-%121 = OpCompositeConstruct %77 %118 %119 %120 %16 +-%122 = OpAccessChain %94 %82 %45 +-%123 = OpLoad %77 %122 +-%124 = OpIAdd %77 %123 %121 +-%125 = OpAccessChain %94 %82 %45 +-OpStore %125 %124 +-OpBranch %105 +-%105 = OpLabel +-%126 = OpLoad %24 %101 +-%127 = OpIAdd %24 %126 %73 +-OpStore %101 %127 +-OpBranch %102 +-%104 = OpLabel +-OpMemoryBarrier %40 %41 +-OpControlBarrier %40 %40 %42 +-%129 = OpLoad %25 %128 +-%131 = OpImageRead %38 %129 %130 +-%132 = OpCompositeExtract %24 %131 0 +-%133 = OpConvertSToF %78 %132 +-%134 = OpCompositeConstruct %79 %133 %133 %133 +-%135 = OpAccessChain %110 %82 %73 +-OpStore %135 %134 +-OpReturn +-OpFunctionEnd ++%138 = OpFunction %2 None %3 ++%139 = OpLabel ++%142 = OpVariable %11 Function ++%146 = OpVariable %11 Function ++%149 = OpVariable %43 Function ++%144 = OpAccessChain %17 %143 %16 ++%145 = OpLoad %10 %144 ++OpStore %142 %145 ++%147 = OpAccessChain %17 %143 %21 ++%148 = OpLoad %10 %147 ++OpStore %146 %148 ++OpStore %149 %45 ++OpBranch %150 ++%150 = OpLabel ++OpLoopMerge %152 %153 None ++OpBranch %154 ++%154 = OpLabel ++%155 = OpLoad %24 %149 ++%156 = OpSLessThan %54 %155 %53 ++OpBranchConditional %156 %151 %152 ++%151 = OpLabel ++%157 = OpLoad %10 %142 ++%158 = OpLoad %10 %146 ++%159 = OpIAdd %10 %157 %158 ++%160 = OpCompositeConstruct %29 %159 %159 ++%161 = OpAccessChain %94 %82 %45 ++%162 = OpLoad %77 %161 ++%163 = OpVectorShuffle %29 %162 %162 0 1 ++%164 = OpIAdd %29 %163 %160 ++%166 = OpAccessChain %165 %82 %45 %16 ++%167 = OpCompositeExtract %10 %164 0 ++OpStore %166 %167 ++%168 = OpAccessChain %165 %82 %45 %21 ++%169 = OpCompositeExtract %10 %164 1 ++OpStore %168 %169 ++OpBranch %153 ++%153 = OpLabel ++%170 = OpLoad %24 %149 ++%171 = OpIAdd %24 %170 %73 ++OpStore %149 %171 ++OpBranch %150 ++%152 = OpLabel ++%172 = OpLoad %25 %128 ++%173 = OpLoad %10 %142 ++%174 = OpBitcast %24 %173 ++%175 = OpLoad %10 %146 ++%176 = OpBitcast %24 %175 ++%177 = OpCompositeConstruct %32 %174 %176 ++%178 = OpImageRead %38 %172 %177 ++%179 = OpCompositeExtract %24 %178 1 ++%180 = OpAccessChain %84 %82 %53 ++OpStore %180 %179 ++OpMemoryBarrier %40 %41 ++OpControlBarrier %40 %40 %42 ++%181 = OpLoad %25 %27 ++%182 = OpLoad %10 %146 ++%183 = OpBitcast %24 %182 ++%184 = OpLoad %10 %142 ++%185 = OpBitcast %24 %184 ++%186 = OpCompositeConstruct %32 %183 %185 ++%187 = OpAccessChain %84 %82 %53 ++%188 = OpLoad %24 %187 ++%189 = OpCompositeConstruct %38 %188 %45 %45 %45 ++OpImageWrite %181 %186 %189 ++OpReturn ++OpFunctionEnd ++%140 = OpFunction %2 None %3 ++%141 = OpLabel ++%190 = OpVariable %43 Function ++OpStore %190 %45 ++OpBranch %191 ++%191 = OpLabel ++OpLoopMerge %193 %194 None ++OpBranch %195 ++%195 = OpLabel ++%196 = OpLoad %24 %190 ++%197 = OpSLessThan %54 %196 %108 ++OpBranchConditional %197 %192 %193 ++%192 = OpLabel ++%198 = OpLoad %24 %190 ++%199 = OpIEqual %54 %198 %45 ++OpSelectionMerge %201 None ++OpBranchConditional %199 %200 %207 ++%207 = OpLabel ++%208 = OpLoad %13 %15 ++%209 = OpConvertUToF %79 %208 ++%211 = OpCompositeExtract %78 %209 0 ++%212 = OpCompositeExtract %78 %209 1 ++%213 = OpCompositeConstruct %210 %211 %212 ++%214 = OpAccessChain %113 %82 %73 %21 ++%215 = OpCompositeExtract %78 %213 0 ++OpStore %214 %215 ++%216 = OpAccessChain %113 %82 %73 %40 ++%217 = OpCompositeExtract %78 %213 1 ++OpStore %216 %217 ++OpBranch %201 ++%200 = OpLabel ++%202 = OpLoad %25 %128 ++%203 = OpImageRead %38 %202 %130 ++%204 = OpCompositeExtract %24 %203 0 ++%205 = OpConvertSToF %78 %204 ++%206 = OpAccessChain %113 %82 %73 %16 ++OpStore %206 %205 ++OpBranch %201 ++%201 = OpLabel ++OpBranch %194 ++%194 = OpLabel ++%218 = OpLoad %24 %190 ++%219 = OpIAdd %24 %218 %73 ++OpStore %190 %219 ++OpBranch %191 ++%193 = OpLabel ++OpReturn ++OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/large_functions_large_diffs_dst.spvasm b/test/diff/diff_files/large_functions_large_diffs_dst.spvasm new file mode 100644 index 00000000..be7c1d5f --- /dev/null +++ b/test/diff/diff_files/large_functions_large_diffs_dst.spvasm @@ -0,0 +1,213 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 %110 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %12 "x" + OpName %15 "gl_GlobalInvocationID" + OpName %20 "z" + OpName %26 "i" + OpName %40 "BufferOut" + OpMemberName %40 0 "o_uv4" + OpMemberName %40 1 "o_v3" + OpMemberName %40 2 "o_i" + OpName %42 "" + OpName %63 "image2" + OpName %79 "image" + OpName %89 "i" + OpName %110 "gl_LocalInvocationID" + OpName %127 "BufferIn" + OpMemberName %127 0 "i_u" + OpMemberName %127 1 "i_v4" + OpMemberName %127 2 "i_f" + OpName %129 "" + OpDecorate %15 BuiltIn GlobalInvocationId + OpMemberDecorate %40 0 Offset 0 + OpMemberDecorate %40 1 Offset 16 + OpMemberDecorate %40 2 Offset 28 + OpDecorate %40 BufferBlock + OpDecorate %42 DescriptorSet 0 + OpDecorate %42 Binding 1 + OpDecorate %63 DescriptorSet 0 + OpDecorate %63 Binding 3 + OpDecorate %79 DescriptorSet 0 + OpDecorate %79 Binding 2 + OpDecorate %110 BuiltIn LocalInvocationId + OpMemberDecorate %127 0 Offset 0 + OpMemberDecorate %127 1 RowMajor + OpMemberDecorate %127 1 Offset 16 + OpMemberDecorate %127 1 MatrixStride 16 + OpMemberDecorate %127 2 Offset 80 + OpDecorate %127 Block + OpDecorate %129 DescriptorSet 0 + OpDecorate %129 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypePointer Function %24 + %27 = OpConstant %24 0 + %34 = OpConstant %24 2 + %35 = OpTypeBool + %37 = OpTypeVector %10 4 + %38 = OpTypeFloat 32 + %39 = OpTypeVector %38 3 + %40 = OpTypeStruct %37 %39 %24 + %41 = OpTypePointer Uniform %40 + %42 = OpVariable %41 Uniform + %46 = OpTypeVector %10 2 + %48 = OpTypePointer Uniform %37 + %53 = OpTypePointer Uniform %10 + %59 = OpConstant %24 1 + %61 = OpTypeImage %24 2D 0 0 0 2 R32i + %62 = OpTypePointer UniformConstant %61 + %63 = OpVariable %62 UniformConstant + %69 = OpTypeVector %24 2 + %71 = OpTypeVector %24 4 + %74 = OpTypePointer Uniform %24 + %76 = OpConstant %10 2 + %77 = OpConstant %10 3400 + %78 = OpConstant %10 264 + %79 = OpVariable %62 UniformConstant + %96 = OpConstant %24 3 + %103 = OpConstantComposite %69 %27 %27 + %107 = OpTypePointer Uniform %38 + %110 = OpVariable %14 Input + %113 = OpTypeVector %38 2 + %125 = OpTypeVector %38 4 + %126 = OpTypeMatrix %125 4 + %127 = OpTypeStruct %10 %126 %38 + %128 = OpTypePointer Uniform %127 + %129 = OpVariable %128 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %123 = OpFunctionCall %2 %8 + %124 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %26 = OpVariable %25 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + OpStore %26 %27 + OpBranch %28 + %28 = OpLabel + OpLoopMerge %30 %31 None + OpBranch %32 + %32 = OpLabel + %33 = OpLoad %24 %26 + %36 = OpSLessThan %35 %33 %34 + OpBranchConditional %36 %29 %30 + %29 = OpLabel + %43 = OpLoad %10 %12 + %44 = OpLoad %10 %20 + %45 = OpIAdd %10 %43 %44 + %47 = OpCompositeConstruct %46 %45 %45 + %49 = OpAccessChain %48 %42 %27 + %50 = OpLoad %37 %49 + %51 = OpVectorShuffle %46 %50 %50 0 1 + %52 = OpIAdd %46 %51 %47 + %54 = OpAccessChain %53 %42 %27 %16 + %55 = OpCompositeExtract %10 %52 0 + OpStore %54 %55 + %56 = OpAccessChain %53 %42 %27 %21 + %57 = OpCompositeExtract %10 %52 1 + OpStore %56 %57 + OpBranch %31 + %31 = OpLabel + %58 = OpLoad %24 %26 + %60 = OpIAdd %24 %58 %59 + OpStore %26 %60 + OpBranch %28 + %30 = OpLabel + %64 = OpLoad %61 %63 + %65 = OpLoad %10 %12 + %66 = OpBitcast %24 %65 + %67 = OpLoad %10 %20 + %68 = OpBitcast %24 %67 + %70 = OpCompositeConstruct %69 %66 %68 + %72 = OpImageRead %71 %64 %70 + %73 = OpCompositeExtract %24 %72 1 + %75 = OpAccessChain %74 %42 %34 + OpStore %75 %73 + OpMemoryBarrier %76 %77 + OpControlBarrier %76 %76 %78 + %80 = OpLoad %61 %79 + %81 = OpLoad %10 %20 + %82 = OpBitcast %24 %81 + %83 = OpLoad %10 %12 + %84 = OpBitcast %24 %83 + %85 = OpCompositeConstruct %69 %82 %84 + %86 = OpAccessChain %74 %42 %34 + %87 = OpLoad %24 %86 + %88 = OpCompositeConstruct %71 %87 %27 %27 %27 + OpImageWrite %80 %85 %88 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %89 = OpVariable %25 Function + OpStore %89 %27 + OpBranch %90 + %90 = OpLabel + OpLoopMerge %92 %93 None + OpBranch %94 + %94 = OpLabel + %95 = OpLoad %24 %89 + %97 = OpSLessThan %35 %95 %96 + OpBranchConditional %97 %91 %92 + %91 = OpLabel + %98 = OpLoad %24 %89 + %99 = OpIEqual %35 %98 %27 + OpSelectionMerge %101 None + OpBranchConditional %99 %100 %109 + %100 = OpLabel + %102 = OpLoad %61 %63 + %104 = OpImageRead %71 %102 %103 + %105 = OpCompositeExtract %24 %104 0 + %106 = OpConvertSToF %38 %105 + %108 = OpAccessChain %107 %42 %59 %16 + OpStore %108 %106 + OpBranch %101 + %109 = OpLabel + %111 = OpLoad %13 %110 + %112 = OpConvertUToF %39 %111 + %114 = OpCompositeExtract %38 %112 0 + %115 = OpCompositeExtract %38 %112 1 + %116 = OpCompositeConstruct %113 %114 %115 + %117 = OpAccessChain %107 %42 %59 %21 + %118 = OpCompositeExtract %38 %116 0 + OpStore %117 %118 + %119 = OpAccessChain %107 %42 %59 %76 + %120 = OpCompositeExtract %38 %116 1 + OpStore %119 %120 + OpBranch %101 + %101 = OpLabel + OpBranch %93 + %93 = OpLabel + %121 = OpLoad %24 %89 + %122 = OpIAdd %24 %121 %59 + OpStore %89 %122 + OpBranch %90 + %92 = OpLabel + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/large_functions_large_diffs_src.spvasm b/test/diff/diff_files/large_functions_large_diffs_src.spvasm new file mode 100644 index 00000000..8f0aa614 --- /dev/null +++ b/test/diff/diff_files/large_functions_large_diffs_src.spvasm @@ -0,0 +1,230 @@ +;; Test where src and dst have a few large functions with large differences. + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %12 "x" + OpName %15 "gl_LocalInvocationID" + OpName %20 "y" + OpName %27 "image" + OpName %44 "sum" + OpName %46 "i" + OpName %56 "j" + OpName %80 "BufferOut" + OpMemberName %80 0 "o_uv4" + OpMemberName %80 1 "o_v3" + OpMemberName %80 2 "o_i" + OpName %82 "" + OpName %88 "BufferIn" + OpMemberName %88 0 "i_u" + OpMemberName %88 1 "i_v4" + OpMemberName %88 2 "i_f" + OpName %90 "" + OpName %101 "i" + OpName %128 "image2" + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 + OpDecorate %128 DescriptorSet 0 + OpDecorate %128 Binding 3 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform + %91 = OpTypePointer Uniform %87 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 + %110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 + %128 = OpVariable %26 UniformConstant + %130 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %136 = OpFunctionCall %2 %6 + %137 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function + %46 = OpVariable %43 Function + %56 = OpVariable %43 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + %28 = OpLoad %25 %27 + %30 = OpLoad %13 %15 + %31 = OpVectorShuffle %29 %30 %30 0 1 + %33 = OpBitcast %32 %31 + %34 = OpLoad %10 %12 + %35 = OpLoad %10 %20 + %36 = OpIAdd %10 %34 %35 + %37 = OpBitcast %24 %36 + %39 = OpCompositeConstruct %38 %37 %37 %37 %37 + OpImageWrite %28 %33 %39 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + OpStore %44 %45 + OpStore %46 %45 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %24 %46 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel + OpStore %56 %45 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %24 %56 + %63 = OpSLessThan %54 %62 %53 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %64 = OpLoad %25 %27 + %65 = OpLoad %24 %46 + %66 = OpLoad %24 %56 + %67 = OpCompositeConstruct %32 %65 %66 + %68 = OpImageRead %38 %64 %67 + %69 = OpCompositeExtract %24 %68 0 + %70 = OpLoad %24 %44 + %71 = OpIMul %24 %70 %69 + OpStore %44 %71 + OpBranch %60 + %60 = OpLabel + %72 = OpLoad %24 %56 + %74 = OpIAdd %24 %72 %73 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %50 + %50 = OpLabel + %75 = OpLoad %24 %46 + %76 = OpIAdd %24 %75 %73 + OpStore %46 %76 + OpBranch %47 + %49 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %83 = OpLoad %24 %44 + %85 = OpAccessChain %84 %82 %53 + OpStore %85 %83 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function + %92 = OpAccessChain %91 %90 %73 + %93 = OpLoad %87 %92 + %95 = OpAccessChain %94 %82 %45 + %96 = OpLoad %77 %95 + %97 = OpConvertUToF %86 %96 + %98 = OpMatrixTimesVector %86 %93 %97 + %99 = OpConvertFToU %77 %98 + %100 = OpAccessChain %94 %82 %45 + OpStore %100 %99 + OpStore %101 %45 + OpBranch %102 + %102 = OpLabel + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 + OpBranchConditional %109 %103 %104 + %103 = OpLabel + %111 = OpAccessChain %110 %82 %73 + %112 = OpLoad %79 %111 + %114 = OpAccessChain %113 %90 %53 + %115 = OpLoad %78 %114 + %116 = OpVectorTimesScalar %79 %112 %115 + %117 = OpConvertFToU %13 %116 + %118 = OpCompositeExtract %10 %117 0 + %119 = OpCompositeExtract %10 %117 1 + %120 = OpCompositeExtract %10 %117 2 + %121 = OpCompositeConstruct %77 %118 %119 %120 %16 + %122 = OpAccessChain %94 %82 %45 + %123 = OpLoad %77 %122 + %124 = OpIAdd %77 %123 %121 + %125 = OpAccessChain %94 %82 %45 + OpStore %125 %124 + OpBranch %105 + %105 = OpLabel + %126 = OpLoad %24 %101 + %127 = OpIAdd %24 %126 %73 + OpStore %101 %127 + OpBranch %102 + %104 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %129 = OpLoad %25 %128 + %131 = OpImageRead %38 %129 %130 + %132 = OpCompositeExtract %24 %131 0 + %133 = OpConvertSToF %78 %132 + %134 = OpCompositeConstruct %79 %133 %133 %133 + %135 = OpAccessChain %110 %82 %73 + OpStore %135 %134 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/large_functions_small_diffs_autogen.cpp b/test/diff/diff_files/large_functions_small_diffs_autogen.cpp new file mode 100644 index 00000000..02838d9a --- /dev/null +++ b/test/diff/diff_files/large_functions_small_diffs_autogen.cpp @@ -0,0 +1,1364 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test where src and dst have a few large functions with small differences. +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %12 "x" + OpName %15 "gl_LocalInvocationID" + OpName %20 "y" + OpName %27 "image" + OpName %44 "sum" + OpName %46 "i" + OpName %56 "j" + OpName %80 "BufferOut" + OpMemberName %80 0 "o_uv4" + OpMemberName %80 1 "o_v3" + OpMemberName %80 2 "o_i" + OpName %82 "" + OpName %88 "BufferIn" + OpMemberName %88 0 "i_u" + OpMemberName %88 1 "i_v4" + OpMemberName %88 2 "i_f" + OpName %90 "" + OpName %101 "i" + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform + %91 = OpTypePointer Uniform %87 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 + %110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 + %129 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %135 = OpFunctionCall %2 %6 + %136 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function + %46 = OpVariable %43 Function + %56 = OpVariable %43 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + %28 = OpLoad %25 %27 + %30 = OpLoad %13 %15 + %31 = OpVectorShuffle %29 %30 %30 0 1 + %33 = OpBitcast %32 %31 + %34 = OpLoad %10 %12 + %35 = OpLoad %10 %20 + %36 = OpIAdd %10 %34 %35 + %37 = OpBitcast %24 %36 + %39 = OpCompositeConstruct %38 %37 %37 %37 %37 + OpImageWrite %28 %33 %39 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + OpStore %44 %45 + OpStore %46 %45 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %24 %46 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel + OpStore %56 %45 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %24 %56 + %63 = OpSLessThan %54 %62 %53 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %64 = OpLoad %25 %27 + %65 = OpLoad %24 %46 + %66 = OpLoad %24 %56 + %67 = OpCompositeConstruct %32 %65 %66 + %68 = OpImageRead %38 %64 %67 + %69 = OpCompositeExtract %24 %68 0 + %70 = OpLoad %24 %44 + %71 = OpIAdd %24 %70 %69 + OpStore %44 %71 + OpBranch %60 + %60 = OpLabel + %72 = OpLoad %24 %56 + %74 = OpIAdd %24 %72 %73 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %50 + %50 = OpLabel + %75 = OpLoad %24 %46 + %76 = OpIAdd %24 %75 %73 + OpStore %46 %76 + OpBranch %47 + %49 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %83 = OpLoad %24 %44 + %85 = OpAccessChain %84 %82 %53 + OpStore %85 %83 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function + %92 = OpAccessChain %91 %90 %73 + %93 = OpLoad %87 %92 + %95 = OpAccessChain %94 %82 %45 + %96 = OpLoad %77 %95 + %97 = OpConvertUToF %86 %96 + %98 = OpMatrixTimesVector %86 %93 %97 + %99 = OpConvertFToU %77 %98 + %100 = OpAccessChain %94 %82 %45 + OpStore %100 %99 + OpStore %101 %45 + OpBranch %102 + %102 = OpLabel + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 + OpBranchConditional %109 %103 %104 + %103 = OpLabel + %111 = OpAccessChain %110 %82 %73 + %112 = OpLoad %79 %111 + %114 = OpAccessChain %113 %90 %53 + %115 = OpLoad %78 %114 + %116 = OpVectorTimesScalar %79 %112 %115 + %117 = OpConvertFToU %13 %116 + %118 = OpCompositeExtract %10 %117 0 + %119 = OpCompositeExtract %10 %117 1 + %120 = OpCompositeExtract %10 %117 2 + %121 = OpCompositeConstruct %77 %118 %119 %120 %16 + %122 = OpAccessChain %94 %82 %45 + %123 = OpLoad %77 %122 + %124 = OpIAdd %77 %123 %121 + %125 = OpAccessChain %94 %82 %45 + OpStore %125 %124 + OpBranch %105 + %105 = OpLabel + %126 = OpLoad %24 %101 + %127 = OpIAdd %24 %126 %73 + OpStore %101 %127 + OpBranch %102 + %104 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %128 = OpLoad %25 %27 + %130 = OpImageRead %38 %128 %129 + %131 = OpCompositeExtract %24 %130 0 + %132 = OpConvertSToF %78 %131 + %133 = OpCompositeConstruct %79 %132 %132 %132 + %134 = OpAccessChain %110 %82 %73 + OpStore %134 %133 + OpReturn + OpFunctionEnd +)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %12 "x" + OpName %15 "gl_LocalInvocationID" + OpName %20 "y" + OpName %27 "image" + OpName %44 "sum" + OpName %46 "i" + OpName %56 "j" + OpName %80 "BufferOut" + OpMemberName %80 0 "o_uv4" + OpMemberName %80 1 "o_v3" + OpMemberName %80 2 "o_i" + OpName %82 "" + OpName %88 "BufferIn" + OpMemberName %88 0 "i_u" + OpMemberName %88 1 "i_v4" + OpMemberName %88 2 "i_f" + OpName %90 "" + OpName %101 "i" + OpName %128 "image2" + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 + OpDecorate %128 DescriptorSet 0 + OpDecorate %128 Binding 3 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform + %91 = OpTypePointer Uniform %87 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 + %110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 + %128 = OpVariable %26 UniformConstant + %130 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %136 = OpFunctionCall %2 %6 + %137 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function + %46 = OpVariable %43 Function + %56 = OpVariable %43 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + %28 = OpLoad %25 %27 + %30 = OpLoad %13 %15 + %31 = OpVectorShuffle %29 %30 %30 0 1 + %33 = OpBitcast %32 %31 + %34 = OpLoad %10 %12 + %35 = OpLoad %10 %20 + %36 = OpIAdd %10 %34 %35 + %37 = OpBitcast %24 %36 + %39 = OpCompositeConstruct %38 %37 %37 %37 %37 + OpImageWrite %28 %33 %39 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + OpStore %44 %45 + OpStore %46 %45 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %24 %46 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel + OpStore %56 %45 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %24 %56 + %63 = OpSLessThan %54 %62 %53 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %64 = OpLoad %25 %27 + %65 = OpLoad %24 %46 + %66 = OpLoad %24 %56 + %67 = OpCompositeConstruct %32 %65 %66 + %68 = OpImageRead %38 %64 %67 + %69 = OpCompositeExtract %24 %68 0 + %70 = OpLoad %24 %44 + %71 = OpIMul %24 %70 %69 + OpStore %44 %71 + OpBranch %60 + %60 = OpLabel + %72 = OpLoad %24 %56 + %74 = OpIAdd %24 %72 %73 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %50 + %50 = OpLabel + %75 = OpLoad %24 %46 + %76 = OpIAdd %24 %75 %73 + OpStore %46 %76 + OpBranch %47 + %49 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %83 = OpLoad %24 %44 + %85 = OpAccessChain %84 %82 %53 + OpStore %85 %83 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function + %92 = OpAccessChain %91 %90 %73 + %93 = OpLoad %87 %92 + %95 = OpAccessChain %94 %82 %45 + %96 = OpLoad %77 %95 + %97 = OpConvertUToF %86 %96 + %98 = OpMatrixTimesVector %86 %93 %97 + %99 = OpConvertFToU %77 %98 + %100 = OpAccessChain %94 %82 %45 + OpStore %100 %99 + OpStore %101 %45 + OpBranch %102 + %102 = OpLabel + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 + OpBranchConditional %109 %103 %104 + %103 = OpLabel + %111 = OpAccessChain %110 %82 %73 + %112 = OpLoad %79 %111 + %114 = OpAccessChain %113 %90 %53 + %115 = OpLoad %78 %114 + %116 = OpVectorTimesScalar %79 %112 %115 + %117 = OpConvertFToU %13 %116 + %118 = OpCompositeExtract %10 %117 0 + %119 = OpCompositeExtract %10 %117 1 + %120 = OpCompositeExtract %10 %117 2 + %121 = OpCompositeConstruct %77 %118 %119 %120 %16 + %122 = OpAccessChain %94 %82 %45 + %123 = OpLoad %77 %122 + %124 = OpIAdd %77 %123 %121 + %125 = OpAccessChain %94 %82 %45 + OpStore %125 %124 + OpBranch %105 + %105 = OpLabel + %126 = OpLoad %24 %101 + %127 = OpIAdd %24 %126 %73 + OpStore %101 %127 + OpBranch %102 + %104 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %129 = OpLoad %25 %128 + %131 = OpImageRead %38 %129 %130 + %132 = OpCompositeExtract %24 %131 0 + %133 = OpConvertSToF %78 %132 + %134 = OpCompositeConstruct %79 %133 %133 %133 + %135 = OpAccessChain %110 %82 %73 + OpStore %135 %134 + OpReturn + OpFunctionEnd + +)"; + +TEST(DiffTest, LargeFunctionsSmallDiffs) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 137 ++; Bound: 140 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %12 "x" + OpName %15 "gl_LocalInvocationID" + OpName %20 "y" + OpName %27 "image" + OpName %44 "sum" + OpName %46 "i" + OpName %56 "j" + OpName %80 "BufferOut" + OpMemberName %80 0 "o_uv4" + OpMemberName %80 1 "o_v3" + OpMemberName %80 2 "o_i" + OpName %82 "" + OpName %88 "BufferIn" + OpMemberName %88 0 "i_u" + OpMemberName %88 1 "i_v4" + OpMemberName %88 2 "i_f" + OpName %90 "" + OpName %101 "i" ++OpName %138 "image2" + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 ++OpDecorate %138 DescriptorSet 0 ++OpDecorate %138 Binding 3 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform + %91 = OpTypePointer Uniform %87 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 + %110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 ++%138 = OpVariable %26 UniformConstant + %129 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %135 = OpFunctionCall %2 %6 + %136 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function + %46 = OpVariable %43 Function + %56 = OpVariable %43 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + %28 = OpLoad %25 %27 + %30 = OpLoad %13 %15 + %31 = OpVectorShuffle %29 %30 %30 0 1 + %33 = OpBitcast %32 %31 + %34 = OpLoad %10 %12 + %35 = OpLoad %10 %20 + %36 = OpIAdd %10 %34 %35 + %37 = OpBitcast %24 %36 + %39 = OpCompositeConstruct %38 %37 %37 %37 %37 + OpImageWrite %28 %33 %39 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + OpStore %44 %45 + OpStore %46 %45 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %24 %46 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel + OpStore %56 %45 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %24 %56 + %63 = OpSLessThan %54 %62 %53 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %64 = OpLoad %25 %27 + %65 = OpLoad %24 %46 + %66 = OpLoad %24 %56 + %67 = OpCompositeConstruct %32 %65 %66 + %68 = OpImageRead %38 %64 %67 + %69 = OpCompositeExtract %24 %68 0 + %70 = OpLoad %24 %44 +-%71 = OpIAdd %24 %70 %69 ++%137 = OpIMul %24 %70 %69 +-OpStore %44 %71 ++OpStore %44 %137 + OpBranch %60 + %60 = OpLabel + %72 = OpLoad %24 %56 + %74 = OpIAdd %24 %72 %73 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %50 + %50 = OpLabel + %75 = OpLoad %24 %46 + %76 = OpIAdd %24 %75 %73 + OpStore %46 %76 + OpBranch %47 + %49 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %83 = OpLoad %24 %44 + %85 = OpAccessChain %84 %82 %53 + OpStore %85 %83 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function + %92 = OpAccessChain %91 %90 %73 + %93 = OpLoad %87 %92 + %95 = OpAccessChain %94 %82 %45 + %96 = OpLoad %77 %95 + %97 = OpConvertUToF %86 %96 + %98 = OpMatrixTimesVector %86 %93 %97 + %99 = OpConvertFToU %77 %98 + %100 = OpAccessChain %94 %82 %45 + OpStore %100 %99 + OpStore %101 %45 + OpBranch %102 + %102 = OpLabel + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 + OpBranchConditional %109 %103 %104 + %103 = OpLabel + %111 = OpAccessChain %110 %82 %73 + %112 = OpLoad %79 %111 + %114 = OpAccessChain %113 %90 %53 + %115 = OpLoad %78 %114 + %116 = OpVectorTimesScalar %79 %112 %115 + %117 = OpConvertFToU %13 %116 + %118 = OpCompositeExtract %10 %117 0 + %119 = OpCompositeExtract %10 %117 1 + %120 = OpCompositeExtract %10 %117 2 + %121 = OpCompositeConstruct %77 %118 %119 %120 %16 + %122 = OpAccessChain %94 %82 %45 + %123 = OpLoad %77 %122 + %124 = OpIAdd %77 %123 %121 + %125 = OpAccessChain %94 %82 %45 + OpStore %125 %124 + OpBranch %105 + %105 = OpLabel + %126 = OpLoad %24 %101 + %127 = OpIAdd %24 %126 %73 + OpStore %101 %127 + OpBranch %102 + %104 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 +-%128 = OpLoad %25 %27 ++%139 = OpLoad %25 %138 +-%130 = OpImageRead %38 %128 %129 ++%130 = OpImageRead %38 %139 %129 + %131 = OpCompositeExtract %24 %130 0 + %132 = OpConvertSToF %78 %131 + %133 = OpCompositeConstruct %79 %132 %132 %132 + %134 = OpAccessChain %110 %82 %73 + OpStore %134 %133 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, LargeFunctionsSmallDiffsNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform + %91 = OpTypePointer Uniform %87 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 + %110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 + %129 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %135 = OpFunctionCall %2 %6 + %136 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function + %46 = OpVariable %43 Function + %56 = OpVariable %43 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + %28 = OpLoad %25 %27 + %30 = OpLoad %13 %15 + %31 = OpVectorShuffle %29 %30 %30 0 1 + %33 = OpBitcast %32 %31 + %34 = OpLoad %10 %12 + %35 = OpLoad %10 %20 + %36 = OpIAdd %10 %34 %35 + %37 = OpBitcast %24 %36 + %39 = OpCompositeConstruct %38 %37 %37 %37 %37 + OpImageWrite %28 %33 %39 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + OpStore %44 %45 + OpStore %46 %45 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %24 %46 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel + OpStore %56 %45 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %24 %56 + %63 = OpSLessThan %54 %62 %53 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %64 = OpLoad %25 %27 + %65 = OpLoad %24 %46 + %66 = OpLoad %24 %56 + %67 = OpCompositeConstruct %32 %65 %66 + %68 = OpImageRead %38 %64 %67 + %69 = OpCompositeExtract %24 %68 0 + %70 = OpLoad %24 %44 + %71 = OpIAdd %24 %70 %69 + OpStore %44 %71 + OpBranch %60 + %60 = OpLabel + %72 = OpLoad %24 %56 + %74 = OpIAdd %24 %72 %73 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %50 + %50 = OpLabel + %75 = OpLoad %24 %46 + %76 = OpIAdd %24 %75 %73 + OpStore %46 %76 + OpBranch %47 + %49 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %83 = OpLoad %24 %44 + %85 = OpAccessChain %84 %82 %53 + OpStore %85 %83 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function + %92 = OpAccessChain %91 %90 %73 + %93 = OpLoad %87 %92 + %95 = OpAccessChain %94 %82 %45 + %96 = OpLoad %77 %95 + %97 = OpConvertUToF %86 %96 + %98 = OpMatrixTimesVector %86 %93 %97 + %99 = OpConvertFToU %77 %98 + %100 = OpAccessChain %94 %82 %45 + OpStore %100 %99 + OpStore %101 %45 + OpBranch %102 + %102 = OpLabel + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 + OpBranchConditional %109 %103 %104 + %103 = OpLabel + %111 = OpAccessChain %110 %82 %73 + %112 = OpLoad %79 %111 + %114 = OpAccessChain %113 %90 %53 + %115 = OpLoad %78 %114 + %116 = OpVectorTimesScalar %79 %112 %115 + %117 = OpConvertFToU %13 %116 + %118 = OpCompositeExtract %10 %117 0 + %119 = OpCompositeExtract %10 %117 1 + %120 = OpCompositeExtract %10 %117 2 + %121 = OpCompositeConstruct %77 %118 %119 %120 %16 + %122 = OpAccessChain %94 %82 %45 + %123 = OpLoad %77 %122 + %124 = OpIAdd %77 %123 %121 + %125 = OpAccessChain %94 %82 %45 + OpStore %125 %124 + OpBranch %105 + %105 = OpLabel + %126 = OpLoad %24 %101 + %127 = OpIAdd %24 %126 %73 + OpStore %101 %127 + OpBranch %102 + %104 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %128 = OpLoad %25 %27 + %130 = OpImageRead %38 %128 %129 + %131 = OpCompositeExtract %24 %130 0 + %132 = OpConvertSToF %78 %131 + %133 = OpCompositeConstruct %79 %132 %132 %132 + %134 = OpAccessChain %110 %82 %73 + OpStore %134 %133 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 + OpDecorate %128 DescriptorSet 0 + OpDecorate %128 Binding 3 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform + %91 = OpTypePointer Uniform %87 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 + %110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 + %128 = OpVariable %26 UniformConstant + %130 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %136 = OpFunctionCall %2 %6 + %137 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function + %46 = OpVariable %43 Function + %56 = OpVariable %43 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + %28 = OpLoad %25 %27 + %30 = OpLoad %13 %15 + %31 = OpVectorShuffle %29 %30 %30 0 1 + %33 = OpBitcast %32 %31 + %34 = OpLoad %10 %12 + %35 = OpLoad %10 %20 + %36 = OpIAdd %10 %34 %35 + %37 = OpBitcast %24 %36 + %39 = OpCompositeConstruct %38 %37 %37 %37 %37 + OpImageWrite %28 %33 %39 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + OpStore %44 %45 + OpStore %46 %45 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %24 %46 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel + OpStore %56 %45 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %24 %56 + %63 = OpSLessThan %54 %62 %53 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %64 = OpLoad %25 %27 + %65 = OpLoad %24 %46 + %66 = OpLoad %24 %56 + %67 = OpCompositeConstruct %32 %65 %66 + %68 = OpImageRead %38 %64 %67 + %69 = OpCompositeExtract %24 %68 0 + %70 = OpLoad %24 %44 + %71 = OpIMul %24 %70 %69 + OpStore %44 %71 + OpBranch %60 + %60 = OpLabel + %72 = OpLoad %24 %56 + %74 = OpIAdd %24 %72 %73 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %50 + %50 = OpLabel + %75 = OpLoad %24 %46 + %76 = OpIAdd %24 %75 %73 + OpStore %46 %76 + OpBranch %47 + %49 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %83 = OpLoad %24 %44 + %85 = OpAccessChain %84 %82 %53 + OpStore %85 %83 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function + %92 = OpAccessChain %91 %90 %73 + %93 = OpLoad %87 %92 + %95 = OpAccessChain %94 %82 %45 + %96 = OpLoad %77 %95 + %97 = OpConvertUToF %86 %96 + %98 = OpMatrixTimesVector %86 %93 %97 + %99 = OpConvertFToU %77 %98 + %100 = OpAccessChain %94 %82 %45 + OpStore %100 %99 + OpStore %101 %45 + OpBranch %102 + %102 = OpLabel + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 + OpBranchConditional %109 %103 %104 + %103 = OpLabel + %111 = OpAccessChain %110 %82 %73 + %112 = OpLoad %79 %111 + %114 = OpAccessChain %113 %90 %53 + %115 = OpLoad %78 %114 + %116 = OpVectorTimesScalar %79 %112 %115 + %117 = OpConvertFToU %13 %116 + %118 = OpCompositeExtract %10 %117 0 + %119 = OpCompositeExtract %10 %117 1 + %120 = OpCompositeExtract %10 %117 2 + %121 = OpCompositeConstruct %77 %118 %119 %120 %16 + %122 = OpAccessChain %94 %82 %45 + %123 = OpLoad %77 %122 + %124 = OpIAdd %77 %123 %121 + %125 = OpAccessChain %94 %82 %45 + OpStore %125 %124 + OpBranch %105 + %105 = OpLabel + %126 = OpLoad %24 %101 + %127 = OpIAdd %24 %126 %73 + OpStore %101 %127 + OpBranch %102 + %104 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %129 = OpLoad %25 %128 + %131 = OpImageRead %38 %129 %130 + %132 = OpCompositeExtract %24 %131 0 + %133 = OpConvertSToF %78 %132 + %134 = OpCompositeConstruct %79 %133 %133 %133 + %135 = OpAccessChain %110 %82 %73 + OpStore %135 %134 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 137 ++; Bound: 140 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 ++OpDecorate %138 DescriptorSet 0 ++OpDecorate %138 Binding 3 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform + %91 = OpTypePointer Uniform %87 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 + %110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 ++%138 = OpVariable %26 UniformConstant + %129 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %135 = OpFunctionCall %2 %6 + %136 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function + %46 = OpVariable %43 Function + %56 = OpVariable %43 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + %28 = OpLoad %25 %27 + %30 = OpLoad %13 %15 + %31 = OpVectorShuffle %29 %30 %30 0 1 + %33 = OpBitcast %32 %31 + %34 = OpLoad %10 %12 + %35 = OpLoad %10 %20 + %36 = OpIAdd %10 %34 %35 + %37 = OpBitcast %24 %36 + %39 = OpCompositeConstruct %38 %37 %37 %37 %37 + OpImageWrite %28 %33 %39 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + OpStore %44 %45 + OpStore %46 %45 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %24 %46 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel + OpStore %56 %45 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %24 %56 + %63 = OpSLessThan %54 %62 %53 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %64 = OpLoad %25 %27 + %65 = OpLoad %24 %46 + %66 = OpLoad %24 %56 + %67 = OpCompositeConstruct %32 %65 %66 + %68 = OpImageRead %38 %64 %67 + %69 = OpCompositeExtract %24 %68 0 + %70 = OpLoad %24 %44 +-%71 = OpIAdd %24 %70 %69 ++%137 = OpIMul %24 %70 %69 +-OpStore %44 %71 ++OpStore %44 %137 + OpBranch %60 + %60 = OpLabel + %72 = OpLoad %24 %56 + %74 = OpIAdd %24 %72 %73 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %50 + %50 = OpLabel + %75 = OpLoad %24 %46 + %76 = OpIAdd %24 %75 %73 + OpStore %46 %76 + OpBranch %47 + %49 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %83 = OpLoad %24 %44 + %85 = OpAccessChain %84 %82 %53 + OpStore %85 %83 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function + %92 = OpAccessChain %91 %90 %73 + %93 = OpLoad %87 %92 + %95 = OpAccessChain %94 %82 %45 + %96 = OpLoad %77 %95 + %97 = OpConvertUToF %86 %96 + %98 = OpMatrixTimesVector %86 %93 %97 + %99 = OpConvertFToU %77 %98 + %100 = OpAccessChain %94 %82 %45 + OpStore %100 %99 + OpStore %101 %45 + OpBranch %102 + %102 = OpLabel + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 + OpBranchConditional %109 %103 %104 + %103 = OpLabel + %111 = OpAccessChain %110 %82 %73 + %112 = OpLoad %79 %111 + %114 = OpAccessChain %113 %90 %53 + %115 = OpLoad %78 %114 + %116 = OpVectorTimesScalar %79 %112 %115 + %117 = OpConvertFToU %13 %116 + %118 = OpCompositeExtract %10 %117 0 + %119 = OpCompositeExtract %10 %117 1 + %120 = OpCompositeExtract %10 %117 2 + %121 = OpCompositeConstruct %77 %118 %119 %120 %16 + %122 = OpAccessChain %94 %82 %45 + %123 = OpLoad %77 %122 + %124 = OpIAdd %77 %123 %121 + %125 = OpAccessChain %94 %82 %45 + OpStore %125 %124 + OpBranch %105 + %105 = OpLabel + %126 = OpLoad %24 %101 + %127 = OpIAdd %24 %126 %73 + OpStore %101 %127 + OpBranch %102 + %104 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 +-%128 = OpLoad %25 %27 ++%139 = OpLoad %25 %138 +-%130 = OpImageRead %38 %128 %129 ++%130 = OpImageRead %38 %139 %129 + %131 = OpCompositeExtract %24 %130 0 + %132 = OpConvertSToF %78 %131 + %133 = OpCompositeConstruct %79 %132 %132 %132 + %134 = OpAccessChain %110 %82 %73 + OpStore %134 %133 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/large_functions_small_diffs_dst.spvasm b/test/diff/diff_files/large_functions_small_diffs_dst.spvasm new file mode 100644 index 00000000..f788e0b6 --- /dev/null +++ b/test/diff/diff_files/large_functions_small_diffs_dst.spvasm @@ -0,0 +1,229 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %12 "x" + OpName %15 "gl_LocalInvocationID" + OpName %20 "y" + OpName %27 "image" + OpName %44 "sum" + OpName %46 "i" + OpName %56 "j" + OpName %80 "BufferOut" + OpMemberName %80 0 "o_uv4" + OpMemberName %80 1 "o_v3" + OpMemberName %80 2 "o_i" + OpName %82 "" + OpName %88 "BufferIn" + OpMemberName %88 0 "i_u" + OpMemberName %88 1 "i_v4" + OpMemberName %88 2 "i_f" + OpName %90 "" + OpName %101 "i" + OpName %128 "image2" + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 + OpDecorate %128 DescriptorSet 0 + OpDecorate %128 Binding 3 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform + %91 = OpTypePointer Uniform %87 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 + %110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 + %128 = OpVariable %26 UniformConstant + %130 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %136 = OpFunctionCall %2 %6 + %137 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function + %46 = OpVariable %43 Function + %56 = OpVariable %43 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + %28 = OpLoad %25 %27 + %30 = OpLoad %13 %15 + %31 = OpVectorShuffle %29 %30 %30 0 1 + %33 = OpBitcast %32 %31 + %34 = OpLoad %10 %12 + %35 = OpLoad %10 %20 + %36 = OpIAdd %10 %34 %35 + %37 = OpBitcast %24 %36 + %39 = OpCompositeConstruct %38 %37 %37 %37 %37 + OpImageWrite %28 %33 %39 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + OpStore %44 %45 + OpStore %46 %45 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %24 %46 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel + OpStore %56 %45 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %24 %56 + %63 = OpSLessThan %54 %62 %53 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %64 = OpLoad %25 %27 + %65 = OpLoad %24 %46 + %66 = OpLoad %24 %56 + %67 = OpCompositeConstruct %32 %65 %66 + %68 = OpImageRead %38 %64 %67 + %69 = OpCompositeExtract %24 %68 0 + %70 = OpLoad %24 %44 + %71 = OpIMul %24 %70 %69 + OpStore %44 %71 + OpBranch %60 + %60 = OpLabel + %72 = OpLoad %24 %56 + %74 = OpIAdd %24 %72 %73 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %50 + %50 = OpLabel + %75 = OpLoad %24 %46 + %76 = OpIAdd %24 %75 %73 + OpStore %46 %76 + OpBranch %47 + %49 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %83 = OpLoad %24 %44 + %85 = OpAccessChain %84 %82 %53 + OpStore %85 %83 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function + %92 = OpAccessChain %91 %90 %73 + %93 = OpLoad %87 %92 + %95 = OpAccessChain %94 %82 %45 + %96 = OpLoad %77 %95 + %97 = OpConvertUToF %86 %96 + %98 = OpMatrixTimesVector %86 %93 %97 + %99 = OpConvertFToU %77 %98 + %100 = OpAccessChain %94 %82 %45 + OpStore %100 %99 + OpStore %101 %45 + OpBranch %102 + %102 = OpLabel + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 + OpBranchConditional %109 %103 %104 + %103 = OpLabel + %111 = OpAccessChain %110 %82 %73 + %112 = OpLoad %79 %111 + %114 = OpAccessChain %113 %90 %53 + %115 = OpLoad %78 %114 + %116 = OpVectorTimesScalar %79 %112 %115 + %117 = OpConvertFToU %13 %116 + %118 = OpCompositeExtract %10 %117 0 + %119 = OpCompositeExtract %10 %117 1 + %120 = OpCompositeExtract %10 %117 2 + %121 = OpCompositeConstruct %77 %118 %119 %120 %16 + %122 = OpAccessChain %94 %82 %45 + %123 = OpLoad %77 %122 + %124 = OpIAdd %77 %123 %121 + %125 = OpAccessChain %94 %82 %45 + OpStore %125 %124 + OpBranch %105 + %105 = OpLabel + %126 = OpLoad %24 %101 + %127 = OpIAdd %24 %126 %73 + OpStore %101 %127 + OpBranch %102 + %104 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %129 = OpLoad %25 %128 + %131 = OpImageRead %38 %129 %130 + %132 = OpCompositeExtract %24 %131 0 + %133 = OpConvertSToF %78 %132 + %134 = OpCompositeConstruct %79 %133 %133 %133 + %135 = OpAccessChain %110 %82 %73 + OpStore %135 %134 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/large_functions_small_diffs_src.spvasm b/test/diff/diff_files/large_functions_small_diffs_src.spvasm new file mode 100644 index 00000000..78a92785 --- /dev/null +++ b/test/diff/diff_files/large_functions_small_diffs_src.spvasm @@ -0,0 +1,226 @@ +;; Test where src and dst have a few large functions with small differences. + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %12 "x" + OpName %15 "gl_LocalInvocationID" + OpName %20 "y" + OpName %27 "image" + OpName %44 "sum" + OpName %46 "i" + OpName %56 "j" + OpName %80 "BufferOut" + OpMemberName %80 0 "o_uv4" + OpMemberName %80 1 "o_v3" + OpMemberName %80 2 "o_i" + OpName %82 "" + OpName %88 "BufferIn" + OpMemberName %88 0 "i_u" + OpMemberName %88 1 "i_v4" + OpMemberName %88 2 "i_f" + OpName %90 "" + OpName %101 "i" + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform + %91 = OpTypePointer Uniform %87 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 + %110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 + %129 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %135 = OpFunctionCall %2 %6 + %136 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function + %46 = OpVariable %43 Function + %56 = OpVariable %43 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + %28 = OpLoad %25 %27 + %30 = OpLoad %13 %15 + %31 = OpVectorShuffle %29 %30 %30 0 1 + %33 = OpBitcast %32 %31 + %34 = OpLoad %10 %12 + %35 = OpLoad %10 %20 + %36 = OpIAdd %10 %34 %35 + %37 = OpBitcast %24 %36 + %39 = OpCompositeConstruct %38 %37 %37 %37 %37 + OpImageWrite %28 %33 %39 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + OpStore %44 %45 + OpStore %46 %45 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %24 %46 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel + OpStore %56 %45 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %24 %56 + %63 = OpSLessThan %54 %62 %53 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %64 = OpLoad %25 %27 + %65 = OpLoad %24 %46 + %66 = OpLoad %24 %56 + %67 = OpCompositeConstruct %32 %65 %66 + %68 = OpImageRead %38 %64 %67 + %69 = OpCompositeExtract %24 %68 0 + %70 = OpLoad %24 %44 + %71 = OpIAdd %24 %70 %69 + OpStore %44 %71 + OpBranch %60 + %60 = OpLabel + %72 = OpLoad %24 %56 + %74 = OpIAdd %24 %72 %73 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %50 + %50 = OpLabel + %75 = OpLoad %24 %46 + %76 = OpIAdd %24 %75 %73 + OpStore %46 %76 + OpBranch %47 + %49 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %83 = OpLoad %24 %44 + %85 = OpAccessChain %84 %82 %53 + OpStore %85 %83 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function + %92 = OpAccessChain %91 %90 %73 + %93 = OpLoad %87 %92 + %95 = OpAccessChain %94 %82 %45 + %96 = OpLoad %77 %95 + %97 = OpConvertUToF %86 %96 + %98 = OpMatrixTimesVector %86 %93 %97 + %99 = OpConvertFToU %77 %98 + %100 = OpAccessChain %94 %82 %45 + OpStore %100 %99 + OpStore %101 %45 + OpBranch %102 + %102 = OpLabel + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 + OpBranchConditional %109 %103 %104 + %103 = OpLabel + %111 = OpAccessChain %110 %82 %73 + %112 = OpLoad %79 %111 + %114 = OpAccessChain %113 %90 %53 + %115 = OpLoad %78 %114 + %116 = OpVectorTimesScalar %79 %112 %115 + %117 = OpConvertFToU %13 %116 + %118 = OpCompositeExtract %10 %117 0 + %119 = OpCompositeExtract %10 %117 1 + %120 = OpCompositeExtract %10 %117 2 + %121 = OpCompositeConstruct %77 %118 %119 %120 %16 + %122 = OpAccessChain %94 %82 %45 + %123 = OpLoad %77 %122 + %124 = OpIAdd %77 %123 %121 + %125 = OpAccessChain %94 %82 %45 + OpStore %125 %124 + OpBranch %105 + %105 = OpLabel + %126 = OpLoad %24 %101 + %127 = OpIAdd %24 %126 %73 + OpStore %101 %127 + OpBranch %102 + %104 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %128 = OpLoad %25 %27 + %130 = OpImageRead %38 %128 %129 + %131 = OpCompositeExtract %24 %130 0 + %132 = OpConvertSToF %78 %131 + %133 = OpCompositeConstruct %79 %132 %132 %132 + %134 = OpAccessChain %110 %82 %73 + OpStore %134 %133 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/multiple_different_entry_points_autogen.cpp b/test/diff/diff_files/multiple_different_entry_points_autogen.cpp new file mode 100644 index 00000000..29d4b1d8 --- /dev/null +++ b/test/diff/diff_files/multiple_different_entry_points_autogen.cpp @@ -0,0 +1,330 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Basic test for multiple entry points. The entry points have different +// execution models and so can be trivially matched. +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %mainv "mainv" %vo %a + OpEntryPoint Fragment %mainf "mainf" %color %vi + OpExecutionMode %mainf OriginUpperLeft + OpSource ESSL 310 + OpName %mainv "mainv" + OpName %mainf "mainf" + OpName %a "a" + OpName %vo "v" + OpName %vi "v" + OpName %color "color" + OpDecorate %a Location 0 + OpDecorate %vo Location 0 + OpDecorate %vi Location 0 + OpDecorate %color Location 0 + OpDecorate %color RelaxedPrecision + OpDecorate %vi RelaxedPrecision + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + +%_ptr_Input_float = OpTypePointer Input %float + %a = OpVariable %_ptr_Input_float Input +%_ptr_Output_float = OpTypePointer Output %float + %vo = OpVariable %_ptr_Output_float Output + %vi = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %color = OpVariable %_ptr_Output_v4float Output + + %mainv = OpFunction %void None %3 + %5 = OpLabel + %11 = OpLoad %float %a + OpStore %vo %11 + OpReturn + OpFunctionEnd + + %mainf = OpFunction %void None %3 + %6 = OpLabel + %12 = OpLoad %float %vi + %13 = OpCompositeConstruct %v4float %12 %12 %12 %12 + OpStore %color %13 + OpReturn + OpFunctionEnd)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %frag "frag" %vi %color + OpEntryPoint Vertex %vert "vert" %a %vo + OpExecutionMode %frag OriginUpperLeft + OpSource ESSL 310 + OpName %frag "frag" + OpName %vert "vert" + OpName %vo "v" + OpName %a "a" + OpName %color "color" + OpName %vi "v" + OpDecorate %vi Location 0 + OpDecorate %color Location 0 + OpDecorate %a Location 0 + OpDecorate %vo Location 0 + OpDecorate %color RelaxedPrecision + OpDecorate %vi RelaxedPrecision + OpDecorate %14 RelaxedPrecision + OpDecorate %17 RelaxedPrecision + + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %void = OpTypeVoid + %3 = OpTypeFunction %void + +%_ptr_Output_float = OpTypePointer Output %float + %vo = OpVariable %_ptr_Output_float Output +%_ptr_Input_float = OpTypePointer Input %float + %a = OpVariable %_ptr_Input_float Input + %vi = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %color = OpVariable %_ptr_Output_v4float Output + + %frag = OpFunction %void None %3 + %7 = OpLabel + %14 = OpLoad %float %vi + %17 = OpCompositeConstruct %v4float %14 %14 %14 %14 + OpStore %color %17 + OpReturn + OpFunctionEnd + + %vert = OpFunction %void None %3 + %8 = OpLabel + %13 = OpLoad %float %a + OpStore %vo %13 + OpReturn + OpFunctionEnd +)"; + +TEST(DiffTest, MultipleDifferentEntryPoints) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 20 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %2 "mainv" %4 %7 ++OpEntryPoint Vertex %2 "vert" %7 %4 +-OpEntryPoint Fragment %8 "mainf" %9 %10 ++OpEntryPoint Fragment %8 "frag" %10 %9 + OpExecutionMode %8 OriginUpperLeft + OpSource ESSL 310 +-OpName %2 "mainv" ++OpName %2 "vert" +-OpName %8 "mainf" ++OpName %8 "frag" + OpName %7 "a" + OpName %4 "v" + OpName %10 "v" + OpName %9 "color" + OpDecorate %7 Location 0 + OpDecorate %4 Location 0 + OpDecorate %10 Location 0 + OpDecorate %9 Location 0 + OpDecorate %9 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %14 = OpTypeVoid + %3 = OpTypeFunction %14 + %15 = OpTypeFloat 32 + %16 = OpTypeVector %15 4 + %17 = OpTypePointer Input %15 + %7 = OpVariable %17 Input + %18 = OpTypePointer Output %15 + %4 = OpVariable %18 Output + %10 = OpVariable %17 Input + %19 = OpTypePointer Output %16 + %9 = OpVariable %19 Output + %2 = OpFunction %14 None %3 + %5 = OpLabel + %11 = OpLoad %15 %7 + OpStore %4 %11 + OpReturn + OpFunctionEnd + %8 = OpFunction %14 None %3 + %6 = OpLabel + %12 = OpLoad %15 %10 + %13 = OpCompositeConstruct %16 %12 %12 %12 %12 + OpStore %9 %13 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, MultipleDifferentEntryPointsNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %mainv "mainv" %vo %a + OpEntryPoint Fragment %mainf "mainf" %color %vi + OpExecutionMode %mainf OriginUpperLeft + OpSource ESSL 310 + OpDecorate %a Location 0 + OpDecorate %vo Location 0 + OpDecorate %vi Location 0 + OpDecorate %color Location 0 + OpDecorate %color RelaxedPrecision + OpDecorate %vi RelaxedPrecision + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + +%_ptr_Input_float = OpTypePointer Input %float + %a = OpVariable %_ptr_Input_float Input +%_ptr_Output_float = OpTypePointer Output %float + %vo = OpVariable %_ptr_Output_float Output + %vi = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %color = OpVariable %_ptr_Output_v4float Output + + %mainv = OpFunction %void None %3 + %5 = OpLabel + %11 = OpLoad %float %a + OpStore %vo %11 + OpReturn + OpFunctionEnd + + %mainf = OpFunction %void None %3 + %6 = OpLabel + %12 = OpLoad %float %vi + %13 = OpCompositeConstruct %v4float %12 %12 %12 %12 + OpStore %color %13 + OpReturn + OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %frag "frag" %vi %color + OpEntryPoint Vertex %vert "vert" %a %vo + OpExecutionMode %frag OriginUpperLeft + OpSource ESSL 310 + OpDecorate %vi Location 0 + OpDecorate %color Location 0 + OpDecorate %a Location 0 + OpDecorate %vo Location 0 + OpDecorate %color RelaxedPrecision + OpDecorate %vi RelaxedPrecision + OpDecorate %14 RelaxedPrecision + OpDecorate %17 RelaxedPrecision + + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %void = OpTypeVoid + %3 = OpTypeFunction %void + +%_ptr_Output_float = OpTypePointer Output %float + %vo = OpVariable %_ptr_Output_float Output +%_ptr_Input_float = OpTypePointer Input %float + %a = OpVariable %_ptr_Input_float Input + %vi = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %color = OpVariable %_ptr_Output_v4float Output + + %frag = OpFunction %void None %3 + %7 = OpLabel + %14 = OpLoad %float %vi + %17 = OpCompositeConstruct %v4float %14 %14 %14 %14 + OpStore %color %17 + OpReturn + OpFunctionEnd + + %vert = OpFunction %void None %3 + %8 = OpLabel + %13 = OpLoad %float %a + OpStore %vo %13 + OpReturn + OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 20 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %2 "mainv" %4 %7 ++OpEntryPoint Vertex %2 "vert" %7 %4 +-OpEntryPoint Fragment %8 "mainf" %9 %10 ++OpEntryPoint Fragment %8 "frag" %10 %9 + OpExecutionMode %8 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %7 Location 0 + OpDecorate %4 Location 0 + OpDecorate %10 Location 0 + OpDecorate %9 Location 0 + OpDecorate %9 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %14 = OpTypeVoid + %3 = OpTypeFunction %14 + %15 = OpTypeFloat 32 + %16 = OpTypeVector %15 4 + %17 = OpTypePointer Input %15 + %7 = OpVariable %17 Input + %18 = OpTypePointer Output %15 + %4 = OpVariable %18 Output + %10 = OpVariable %17 Input + %19 = OpTypePointer Output %16 + %9 = OpVariable %19 Output + %2 = OpFunction %14 None %3 + %5 = OpLabel + %11 = OpLoad %15 %7 + OpStore %4 %11 + OpReturn + OpFunctionEnd + %8 = OpFunction %14 None %3 + %6 = OpLabel + %12 = OpLoad %15 %10 + %13 = OpCompositeConstruct %16 %12 %12 %12 %12 + OpStore %9 %13 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/multiple_different_entry_points_dst.spvasm b/test/diff/diff_files/multiple_different_entry_points_dst.spvasm new file mode 100644 index 00000000..72cfe283 --- /dev/null +++ b/test/diff/diff_files/multiple_different_entry_points_dst.spvasm @@ -0,0 +1,49 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %frag "frag" %vi %color + OpEntryPoint Vertex %vert "vert" %a %vo + OpExecutionMode %frag OriginUpperLeft + OpSource ESSL 310 + OpName %frag "frag" + OpName %vert "vert" + OpName %vo "v" + OpName %a "a" + OpName %color "color" + OpName %vi "v" + OpDecorate %vi Location 0 + OpDecorate %color Location 0 + OpDecorate %a Location 0 + OpDecorate %vo Location 0 + OpDecorate %color RelaxedPrecision + OpDecorate %vi RelaxedPrecision + OpDecorate %14 RelaxedPrecision + OpDecorate %17 RelaxedPrecision + + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %void = OpTypeVoid + %3 = OpTypeFunction %void + +%_ptr_Output_float = OpTypePointer Output %float + %vo = OpVariable %_ptr_Output_float Output +%_ptr_Input_float = OpTypePointer Input %float + %a = OpVariable %_ptr_Input_float Input + %vi = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %color = OpVariable %_ptr_Output_v4float Output + + %frag = OpFunction %void None %3 + %7 = OpLabel + %14 = OpLoad %float %vi + %17 = OpCompositeConstruct %v4float %14 %14 %14 %14 + OpStore %color %17 + OpReturn + OpFunctionEnd + + %vert = OpFunction %void None %3 + %8 = OpLabel + %13 = OpLoad %float %a + OpStore %vo %13 + OpReturn + OpFunctionEnd diff --git a/test/diff/diff_files/multiple_different_entry_points_src.spvasm b/test/diff/diff_files/multiple_different_entry_points_src.spvasm new file mode 100644 index 00000000..2119aa73 --- /dev/null +++ b/test/diff/diff_files/multiple_different_entry_points_src.spvasm @@ -0,0 +1,51 @@ +;; Basic test for multiple entry points. The entry points have different +;; execution models and so can be trivially matched. + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %mainv "mainv" %vo %a + OpEntryPoint Fragment %mainf "mainf" %color %vi + OpExecutionMode %mainf OriginUpperLeft + OpSource ESSL 310 + OpName %mainv "mainv" + OpName %mainf "mainf" + OpName %a "a" + OpName %vo "v" + OpName %vi "v" + OpName %color "color" + OpDecorate %a Location 0 + OpDecorate %vo Location 0 + OpDecorate %vi Location 0 + OpDecorate %color Location 0 + OpDecorate %color RelaxedPrecision + OpDecorate %vi RelaxedPrecision + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + +%_ptr_Input_float = OpTypePointer Input %float + %a = OpVariable %_ptr_Input_float Input +%_ptr_Output_float = OpTypePointer Output %float + %vo = OpVariable %_ptr_Output_float Output + %vi = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %color = OpVariable %_ptr_Output_v4float Output + + %mainv = OpFunction %void None %3 + %5 = OpLabel + %11 = OpLoad %float %a + OpStore %vo %11 + OpReturn + OpFunctionEnd + + %mainf = OpFunction %void None %3 + %6 = OpLabel + %12 = OpLoad %float %vi + %13 = OpCompositeConstruct %v4float %12 %12 %12 %12 + OpStore %color %13 + OpReturn + OpFunctionEnd diff --git a/test/diff/diff_files/multiple_same_entry_points_autogen.cpp b/test/diff/diff_files/multiple_same_entry_points_autogen.cpp new file mode 100644 index 00000000..9d011661 --- /dev/null +++ b/test/diff/diff_files/multiple_same_entry_points_autogen.cpp @@ -0,0 +1,375 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test for multiple entry points with the same execution model. +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main1" %8 %10 + OpEntryPoint Vertex %12 "main2" %13 %14 %15 + OpSource ESSL 310 + OpName %4 "main1" + OpName %12 "main2" + OpName %8 "v" + OpName %10 "a" + OpName %13 "v" + OpName %14 "a" + OpName %15 "b" + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + OpDecorate %13 Location 0 + OpDecorate %14 Location 0 + OpDecorate %15 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + + %7 = OpTypePointer Output %6 + %9 = OpTypePointer Input %6 + %8 = OpVariable %7 Output + %10 = OpVariable %9 Input + %13 = OpVariable %7 Output + %14 = OpVariable %9 Input + %15 = OpVariable %9 Input + + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd + + %12 = OpFunction %2 None %3 + %16 = OpLabel + %17 = OpLoad %6 %14 + %18 = OpLoad %6 %15 + %19 = OpFAdd %6 %17 %18 + OpStore %13 %19 + OpReturn + OpFunctionEnd)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main2" %13 %14 %15 + OpEntryPoint Vertex %12 "main1" %8 %10 + OpSource ESSL 310 + OpName %12 "main1" + OpName %4 "main2" + OpName %8 "v" + OpName %10 "a" + OpName %13 "v" + OpName %14 "a" + OpName %15 "b" + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + OpDecorate %13 Location 0 + OpDecorate %14 Location 0 + OpDecorate %15 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + + %7 = OpTypePointer Output %6 + %9 = OpTypePointer Input %6 + %8 = OpVariable %7 Output + %10 = OpVariable %9 Input + %13 = OpVariable %7 Output + %14 = OpVariable %9 Input + %15 = OpVariable %9 Input + + %4 = OpFunction %2 None %3 + %16 = OpLabel + %17 = OpLoad %6 %14 + %18 = OpLoad %6 %15 + %19 = OpFAdd %6 %17 %18 + OpStore %13 %19 + OpReturn + OpFunctionEnd + + %12 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd +)"; + +TEST(DiffTest, MultipleSameEntryPoints) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 20 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 ++OpEntryPoint Vertex %12 "main2" %13 %14 %15 + OpEntryPoint Vertex %4 "main1" %8 %10 +-OpEntryPoint Vertex %12 "main2" %13 %14 %15 + OpSource ESSL 310 + OpName %4 "main1" + OpName %12 "main2" + OpName %8 "v" + OpName %10 "a" + OpName %13 "v" + OpName %14 "a" + OpName %15 "b" + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + OpDecorate %13 Location 0 + OpDecorate %14 Location 0 + OpDecorate %15 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Output %6 + %9 = OpTypePointer Input %6 + %8 = OpVariable %7 Output + %10 = OpVariable %9 Input + %13 = OpVariable %7 Output + %14 = OpVariable %9 Input + %15 = OpVariable %9 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %16 = OpLabel + %17 = OpLoad %6 %14 + %18 = OpLoad %6 %15 + %19 = OpFAdd %6 %17 %18 + OpStore %13 %19 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, MultipleSameEntryPointsNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main1" %8 %10 + OpEntryPoint Vertex %12 "main2" %13 %14 %15 + OpSource ESSL 310 + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + OpDecorate %13 Location 0 + OpDecorate %14 Location 0 + OpDecorate %15 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + + %7 = OpTypePointer Output %6 + %9 = OpTypePointer Input %6 + %8 = OpVariable %7 Output + %10 = OpVariable %9 Input + %13 = OpVariable %7 Output + %14 = OpVariable %9 Input + %15 = OpVariable %9 Input + + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd + + %12 = OpFunction %2 None %3 + %16 = OpLabel + %17 = OpLoad %6 %14 + %18 = OpLoad %6 %15 + %19 = OpFAdd %6 %17 %18 + OpStore %13 %19 + OpReturn + OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main2" %13 %14 %15 + OpEntryPoint Vertex %12 "main1" %8 %10 + OpSource ESSL 310 + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + OpDecorate %13 Location 0 + OpDecorate %14 Location 0 + OpDecorate %15 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + + %7 = OpTypePointer Output %6 + %9 = OpTypePointer Input %6 + %8 = OpVariable %7 Output + %10 = OpVariable %9 Input + %13 = OpVariable %7 Output + %14 = OpVariable %9 Input + %15 = OpVariable %9 Input + + %4 = OpFunction %2 None %3 + %16 = OpLabel + %17 = OpLoad %6 %14 + %18 = OpLoad %6 %15 + %19 = OpFAdd %6 %17 %18 + OpStore %13 %19 + OpReturn + OpFunctionEnd + + %12 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 20 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 ++OpEntryPoint Vertex %12 "main2" %13 %14 %15 + OpEntryPoint Vertex %4 "main1" %8 %10 +-OpEntryPoint Vertex %12 "main2" %13 %14 %15 + OpSource ESSL 310 + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + OpDecorate %13 Location 0 + OpDecorate %14 Location 0 + OpDecorate %15 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Output %6 + %9 = OpTypePointer Input %6 + %8 = OpVariable %7 Output + %10 = OpVariable %9 Input + %13 = OpVariable %7 Output + %14 = OpVariable %9 Input + %15 = OpVariable %9 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %16 = OpLabel + %17 = OpLoad %6 %14 + %18 = OpLoad %6 %15 + %19 = OpFAdd %6 %17 %18 + OpStore %13 %19 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +TEST(DiffTest, MultipleSameEntryPointsDumpIds) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 20 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 ++OpEntryPoint Vertex %12 "main2" %13 %14 %15 + OpEntryPoint Vertex %4 "main1" %8 %10 +-OpEntryPoint Vertex %12 "main2" %13 %14 %15 + OpSource ESSL 310 + OpName %4 "main1" + OpName %12 "main2" + OpName %8 "v" + OpName %10 "a" + OpName %13 "v" + OpName %14 "a" + OpName %15 "b" + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + OpDecorate %13 Location 0 + OpDecorate %14 Location 0 + OpDecorate %15 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Output %6 + %9 = OpTypePointer Input %6 + %8 = OpVariable %7 Output + %10 = OpVariable %9 Input + %13 = OpVariable %7 Output + %14 = OpVariable %9 Input + %15 = OpVariable %9 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %16 = OpLabel + %17 = OpLoad %6 %14 + %18 = OpLoad %6 %15 + %19 = OpFAdd %6 %17 %18 + OpStore %13 %19 + OpReturn + OpFunctionEnd + Src -> Dst + 1 -> 1 [ExtInstImport] + 2 -> 2 [TypeVoid] + 3 -> 3 [TypeFunction] + 4 -> 12 [Function] + 5 -> 5 [Label] + 6 -> 6 [TypeFloat] + 7 -> 7 [TypePointer] + 8 -> 8 [Variable] + 9 -> 9 [TypePointer] + 10 -> 10 [Variable] + 11 -> 11 [Load] + 12 -> 4 [Function] + 13 -> 13 [Variable] + 14 -> 14 [Variable] + 15 -> 15 [Variable] + 16 -> 16 [Label] + 17 -> 17 [Load] + 18 -> 18 [Load] + 19 -> 19 [FAdd] +)"; + Options options; + options.dump_id_map = true; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/multiple_same_entry_points_dst.spvasm b/test/diff/diff_files/multiple_same_entry_points_dst.spvasm new file mode 100644 index 00000000..e2007220 --- /dev/null +++ b/test/diff/diff_files/multiple_same_entry_points_dst.spvasm @@ -0,0 +1,45 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main2" %13 %14 %15 + OpEntryPoint Vertex %12 "main1" %8 %10 + OpSource ESSL 310 + OpName %12 "main1" + OpName %4 "main2" + OpName %8 "v" + OpName %10 "a" + OpName %13 "v" + OpName %14 "a" + OpName %15 "b" + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + OpDecorate %13 Location 0 + OpDecorate %14 Location 0 + OpDecorate %15 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + + %7 = OpTypePointer Output %6 + %9 = OpTypePointer Input %6 + %8 = OpVariable %7 Output + %10 = OpVariable %9 Input + %13 = OpVariable %7 Output + %14 = OpVariable %9 Input + %15 = OpVariable %9 Input + + %4 = OpFunction %2 None %3 + %16 = OpLabel + %17 = OpLoad %6 %14 + %18 = OpLoad %6 %15 + %19 = OpFAdd %6 %17 %18 + OpStore %13 %19 + OpReturn + OpFunctionEnd + + %12 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd diff --git a/test/diff/diff_files/multiple_same_entry_points_src.spvasm b/test/diff/diff_files/multiple_same_entry_points_src.spvasm new file mode 100644 index 00000000..17001b57 --- /dev/null +++ b/test/diff/diff_files/multiple_same_entry_points_src.spvasm @@ -0,0 +1,46 @@ +;; Test for multiple entry points with the same execution model. + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main1" %8 %10 + OpEntryPoint Vertex %12 "main2" %13 %14 %15 + OpSource ESSL 310 + OpName %4 "main1" + OpName %12 "main2" + OpName %8 "v" + OpName %10 "a" + OpName %13 "v" + OpName %14 "a" + OpName %15 "b" + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + OpDecorate %13 Location 0 + OpDecorate %14 Location 0 + OpDecorate %15 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + + %7 = OpTypePointer Output %6 + %9 = OpTypePointer Input %6 + %8 = OpVariable %7 Output + %10 = OpVariable %9 Input + %13 = OpVariable %7 Output + %14 = OpVariable %9 Input + %15 = OpVariable %9 Input + + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd + + %12 = OpFunction %2 None %3 + %16 = OpLabel + %17 = OpLoad %6 %14 + %18 = OpLoad %6 %15 + %19 = OpFAdd %6 %17 %18 + OpStore %13 %19 + OpReturn + OpFunctionEnd diff --git a/test/diff/diff_files/reordered_if_blocks_autogen.cpp b/test/diff/diff_files/reordered_if_blocks_autogen.cpp new file mode 100644 index 00000000..0788199f --- /dev/null +++ b/test/diff/diff_files/reordered_if_blocks_autogen.cpp @@ -0,0 +1,568 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test where src and dst have the true and false blocks of an if reordered. +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %8 %44 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "v" + OpName %44 "color" + OpDecorate %8 RelaxedPrecision + OpDecorate %8 Location 0 + OpDecorate %9 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %20 RelaxedPrecision + OpDecorate %23 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %44 Location 0 + OpDecorate %45 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Input %6 + %8 = OpVariable %7 Input + %10 = OpConstant %6 0 + %11 = OpTypeBool + %15 = OpTypeVector %6 4 + %16 = OpTypePointer Function %15 + %21 = OpConstant %6 -0.5 + %22 = OpConstant %6 -0.300000012 + %38 = OpConstant %6 0.5 + %43 = OpTypePointer Output %15 + %44 = OpVariable %43 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpLoad %6 %8 + %12 = OpFOrdLessThanEqual %11 %9 %10 + OpSelectionMerge %14 None + OpBranchConditional %12 %13 %32 + %13 = OpLabel + %18 = OpLoad %6 %8 + %19 = OpExtInst %6 %1 Log %18 + %20 = OpLoad %6 %8 + %23 = OpExtInst %6 %1 FClamp %20 %21 %22 + %24 = OpFMul %6 %19 %23 + %25 = OpLoad %6 %8 + %26 = OpExtInst %6 %1 Sin %25 + %27 = OpLoad %6 %8 + %28 = OpExtInst %6 %1 Cos %27 + %29 = OpLoad %6 %8 + %30 = OpExtInst %6 %1 Exp %29 + %31 = OpCompositeConstruct %15 %24 %26 %28 %30 + OpBranch %14 + %32 = OpLabel + %33 = OpLoad %6 %8 + %34 = OpExtInst %6 %1 Sqrt %33 + %35 = OpLoad %6 %8 + %36 = OpExtInst %6 %1 FSign %35 + %37 = OpLoad %6 %8 + %39 = OpExtInst %6 %1 FMax %37 %38 + %40 = OpLoad %6 %8 + %41 = OpExtInst %6 %1 Floor %40 + %42 = OpCompositeConstruct %15 %34 %36 %39 %41 + OpBranch %14 + %14 = OpLabel + %45 = OpPhi %15 %31 %13 %42 %32 + OpStore %44 %45 + OpReturn + OpFunctionEnd)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %8 %44 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "v" + OpName %44 "color" + OpDecorate %8 RelaxedPrecision + OpDecorate %8 Location 0 + OpDecorate %9 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %20 RelaxedPrecision + OpDecorate %21 RelaxedPrecision + OpDecorate %22 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %44 Location 0 + OpDecorate %45 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Input %6 + %8 = OpVariable %7 Input + %10 = OpConstant %6 0 + %11 = OpTypeBool + %15 = OpTypeVector %6 4 + %16 = OpTypePointer Function %15 + %23 = OpConstant %6 0.5 + %32 = OpConstant %6 -0.5 + %33 = OpConstant %6 -0.300000012 + %43 = OpTypePointer Output %15 + %44 = OpVariable %43 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpLoad %6 %8 + %12 = OpFOrdLessThanEqual %11 %9 %10 + OpSelectionMerge %14 None + OpBranchConditional %12 %28 %13 + %13 = OpLabel + %18 = OpLoad %6 %8 + %19 = OpExtInst %6 %1 Sqrt %18 + %20 = OpLoad %6 %8 + %21 = OpExtInst %6 %1 FSign %20 + %22 = OpLoad %6 %8 + %24 = OpExtInst %6 %1 FMax %22 %23 + %25 = OpLoad %6 %8 + %26 = OpExtInst %6 %1 Floor %25 + %27 = OpCompositeConstruct %15 %19 %21 %24 %26 + OpBranch %14 + %28 = OpLabel + %29 = OpLoad %6 %8 + %30 = OpExtInst %6 %1 Log %29 + %31 = OpLoad %6 %8 + %34 = OpExtInst %6 %1 FClamp %31 %32 %33 + %35 = OpFMul %6 %30 %34 + %36 = OpLoad %6 %8 + %37 = OpExtInst %6 %1 Sin %36 + %38 = OpLoad %6 %8 + %39 = OpExtInst %6 %1 Cos %38 + %40 = OpLoad %6 %8 + %41 = OpExtInst %6 %1 Exp %40 + %42 = OpCompositeConstruct %15 %35 %37 %39 %41 + OpBranch %14 + %14 = OpLabel + %45 = OpPhi %15 %27 %13 %42 %28 + OpStore %44 %45 + OpReturn + OpFunctionEnd + +)"; + +TEST(DiffTest, ReorderedIfBlocks) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 46 ++; Bound: 47 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %8 %44 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "v" + OpName %44 "color" + OpDecorate %8 RelaxedPrecision + OpDecorate %8 Location 0 + OpDecorate %9 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %20 RelaxedPrecision + OpDecorate %23 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %44 Location 0 + OpDecorate %45 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Input %6 + %8 = OpVariable %7 Input + %10 = OpConstant %6 0 + %11 = OpTypeBool + %15 = OpTypeVector %6 4 + %16 = OpTypePointer Function %15 + %21 = OpConstant %6 -0.5 + %22 = OpConstant %6 -0.300000012 + %38 = OpConstant %6 0.5 + %43 = OpTypePointer Output %15 + %44 = OpVariable %43 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpLoad %6 %8 + %12 = OpFOrdLessThanEqual %11 %9 %10 + OpSelectionMerge %14 None + OpBranchConditional %12 %13 %32 + %32 = OpLabel + %33 = OpLoad %6 %8 + %34 = OpExtInst %6 %1 Sqrt %33 + %35 = OpLoad %6 %8 + %36 = OpExtInst %6 %1 FSign %35 + %37 = OpLoad %6 %8 + %39 = OpExtInst %6 %1 FMax %37 %38 + %40 = OpLoad %6 %8 + %41 = OpExtInst %6 %1 Floor %40 + %42 = OpCompositeConstruct %15 %34 %36 %39 %41 + OpBranch %14 + %13 = OpLabel + %18 = OpLoad %6 %8 + %19 = OpExtInst %6 %1 Log %18 + %20 = OpLoad %6 %8 + %23 = OpExtInst %6 %1 FClamp %20 %21 %22 + %24 = OpFMul %6 %19 %23 + %25 = OpLoad %6 %8 + %26 = OpExtInst %6 %1 Sin %25 + %27 = OpLoad %6 %8 + %28 = OpExtInst %6 %1 Cos %27 + %29 = OpLoad %6 %8 + %30 = OpExtInst %6 %1 Exp %29 + %31 = OpCompositeConstruct %15 %24 %26 %28 %30 + OpBranch %14 + %14 = OpLabel +-%45 = OpPhi %15 %31 %13 %42 %32 ++%45 = OpPhi %15 %42 %32 %31 %13 + OpStore %44 %45 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, ReorderedIfBlocksNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %8 %44 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %8 RelaxedPrecision + OpDecorate %8 Location 0 + OpDecorate %9 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %20 RelaxedPrecision + OpDecorate %23 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %44 Location 0 + OpDecorate %45 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Input %6 + %8 = OpVariable %7 Input + %10 = OpConstant %6 0 + %11 = OpTypeBool + %15 = OpTypeVector %6 4 + %16 = OpTypePointer Function %15 + %21 = OpConstant %6 -0.5 + %22 = OpConstant %6 -0.300000012 + %38 = OpConstant %6 0.5 + %43 = OpTypePointer Output %15 + %44 = OpVariable %43 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpLoad %6 %8 + %12 = OpFOrdLessThanEqual %11 %9 %10 + OpSelectionMerge %14 None + OpBranchConditional %12 %13 %32 + %13 = OpLabel + %18 = OpLoad %6 %8 + %19 = OpExtInst %6 %1 Log %18 + %20 = OpLoad %6 %8 + %23 = OpExtInst %6 %1 FClamp %20 %21 %22 + %24 = OpFMul %6 %19 %23 + %25 = OpLoad %6 %8 + %26 = OpExtInst %6 %1 Sin %25 + %27 = OpLoad %6 %8 + %28 = OpExtInst %6 %1 Cos %27 + %29 = OpLoad %6 %8 + %30 = OpExtInst %6 %1 Exp %29 + %31 = OpCompositeConstruct %15 %24 %26 %28 %30 + OpBranch %14 + %32 = OpLabel + %33 = OpLoad %6 %8 + %34 = OpExtInst %6 %1 Sqrt %33 + %35 = OpLoad %6 %8 + %36 = OpExtInst %6 %1 FSign %35 + %37 = OpLoad %6 %8 + %39 = OpExtInst %6 %1 FMax %37 %38 + %40 = OpLoad %6 %8 + %41 = OpExtInst %6 %1 Floor %40 + %42 = OpCompositeConstruct %15 %34 %36 %39 %41 + OpBranch %14 + %14 = OpLabel + %45 = OpPhi %15 %31 %13 %42 %32 + OpStore %44 %45 + OpReturn + OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %8 %44 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %8 RelaxedPrecision + OpDecorate %8 Location 0 + OpDecorate %9 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %20 RelaxedPrecision + OpDecorate %21 RelaxedPrecision + OpDecorate %22 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %44 Location 0 + OpDecorate %45 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Input %6 + %8 = OpVariable %7 Input + %10 = OpConstant %6 0 + %11 = OpTypeBool + %15 = OpTypeVector %6 4 + %16 = OpTypePointer Function %15 + %23 = OpConstant %6 0.5 + %32 = OpConstant %6 -0.5 + %33 = OpConstant %6 -0.300000012 + %43 = OpTypePointer Output %15 + %44 = OpVariable %43 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpLoad %6 %8 + %12 = OpFOrdLessThanEqual %11 %9 %10 + OpSelectionMerge %14 None + OpBranchConditional %12 %28 %13 + %13 = OpLabel + %18 = OpLoad %6 %8 + %19 = OpExtInst %6 %1 Sqrt %18 + %20 = OpLoad %6 %8 + %21 = OpExtInst %6 %1 FSign %20 + %22 = OpLoad %6 %8 + %24 = OpExtInst %6 %1 FMax %22 %23 + %25 = OpLoad %6 %8 + %26 = OpExtInst %6 %1 Floor %25 + %27 = OpCompositeConstruct %15 %19 %21 %24 %26 + OpBranch %14 + %28 = OpLabel + %29 = OpLoad %6 %8 + %30 = OpExtInst %6 %1 Log %29 + %31 = OpLoad %6 %8 + %34 = OpExtInst %6 %1 FClamp %31 %32 %33 + %35 = OpFMul %6 %30 %34 + %36 = OpLoad %6 %8 + %37 = OpExtInst %6 %1 Sin %36 + %38 = OpLoad %6 %8 + %39 = OpExtInst %6 %1 Cos %38 + %40 = OpLoad %6 %8 + %41 = OpExtInst %6 %1 Exp %40 + %42 = OpCompositeConstruct %15 %35 %37 %39 %41 + OpBranch %14 + %14 = OpLabel + %45 = OpPhi %15 %27 %13 %42 %28 + OpStore %44 %45 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 46 ++; Bound: 47 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %8 %44 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %8 RelaxedPrecision + OpDecorate %8 Location 0 + OpDecorate %9 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %20 RelaxedPrecision + OpDecorate %23 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %44 Location 0 + OpDecorate %45 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Input %6 + %8 = OpVariable %7 Input + %10 = OpConstant %6 0 + %11 = OpTypeBool + %15 = OpTypeVector %6 4 + %16 = OpTypePointer Function %15 + %21 = OpConstant %6 -0.5 + %22 = OpConstant %6 -0.300000012 + %38 = OpConstant %6 0.5 + %43 = OpTypePointer Output %15 + %44 = OpVariable %43 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpLoad %6 %8 + %12 = OpFOrdLessThanEqual %11 %9 %10 + OpSelectionMerge %14 None + OpBranchConditional %12 %13 %32 + %32 = OpLabel + %33 = OpLoad %6 %8 + %34 = OpExtInst %6 %1 Sqrt %33 + %35 = OpLoad %6 %8 + %36 = OpExtInst %6 %1 FSign %35 + %37 = OpLoad %6 %8 + %39 = OpExtInst %6 %1 FMax %37 %38 + %40 = OpLoad %6 %8 + %41 = OpExtInst %6 %1 Floor %40 + %42 = OpCompositeConstruct %15 %34 %36 %39 %41 + OpBranch %14 + %13 = OpLabel + %18 = OpLoad %6 %8 + %19 = OpExtInst %6 %1 Log %18 + %20 = OpLoad %6 %8 + %23 = OpExtInst %6 %1 FClamp %20 %21 %22 + %24 = OpFMul %6 %19 %23 + %25 = OpLoad %6 %8 + %26 = OpExtInst %6 %1 Sin %25 + %27 = OpLoad %6 %8 + %28 = OpExtInst %6 %1 Cos %27 + %29 = OpLoad %6 %8 + %30 = OpExtInst %6 %1 Exp %29 + %31 = OpCompositeConstruct %15 %24 %26 %28 %30 + OpBranch %14 + %14 = OpLabel +-%45 = OpPhi %15 %31 %13 %42 %32 ++%45 = OpPhi %15 %42 %32 %31 %13 + OpStore %44 %45 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/reordered_if_blocks_dst.spvasm b/test/diff/diff_files/reordered_if_blocks_dst.spvasm new file mode 100644 index 00000000..cd1d6d52 --- /dev/null +++ b/test/diff/diff_files/reordered_if_blocks_dst.spvasm @@ -0,0 +1,87 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %8 %44 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "v" + OpName %44 "color" + OpDecorate %8 RelaxedPrecision + OpDecorate %8 Location 0 + OpDecorate %9 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %20 RelaxedPrecision + OpDecorate %21 RelaxedPrecision + OpDecorate %22 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %44 Location 0 + OpDecorate %45 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Input %6 + %8 = OpVariable %7 Input + %10 = OpConstant %6 0 + %11 = OpTypeBool + %15 = OpTypeVector %6 4 + %16 = OpTypePointer Function %15 + %23 = OpConstant %6 0.5 + %32 = OpConstant %6 -0.5 + %33 = OpConstant %6 -0.300000012 + %43 = OpTypePointer Output %15 + %44 = OpVariable %43 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpLoad %6 %8 + %12 = OpFOrdLessThanEqual %11 %9 %10 + OpSelectionMerge %14 None + OpBranchConditional %12 %28 %13 + %13 = OpLabel + %18 = OpLoad %6 %8 + %19 = OpExtInst %6 %1 Sqrt %18 + %20 = OpLoad %6 %8 + %21 = OpExtInst %6 %1 FSign %20 + %22 = OpLoad %6 %8 + %24 = OpExtInst %6 %1 FMax %22 %23 + %25 = OpLoad %6 %8 + %26 = OpExtInst %6 %1 Floor %25 + %27 = OpCompositeConstruct %15 %19 %21 %24 %26 + OpBranch %14 + %28 = OpLabel + %29 = OpLoad %6 %8 + %30 = OpExtInst %6 %1 Log %29 + %31 = OpLoad %6 %8 + %34 = OpExtInst %6 %1 FClamp %31 %32 %33 + %35 = OpFMul %6 %30 %34 + %36 = OpLoad %6 %8 + %37 = OpExtInst %6 %1 Sin %36 + %38 = OpLoad %6 %8 + %39 = OpExtInst %6 %1 Cos %38 + %40 = OpLoad %6 %8 + %41 = OpExtInst %6 %1 Exp %40 + %42 = OpCompositeConstruct %15 %35 %37 %39 %41 + OpBranch %14 + %14 = OpLabel + %45 = OpPhi %15 %27 %13 %42 %28 + OpStore %44 %45 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/reordered_if_blocks_src.spvasm b/test/diff/diff_files/reordered_if_blocks_src.spvasm new file mode 100644 index 00000000..209cb45b --- /dev/null +++ b/test/diff/diff_files/reordered_if_blocks_src.spvasm @@ -0,0 +1,87 @@ +;; Test where src and dst have the true and false blocks of an if reordered. + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %8 %44 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "v" + OpName %44 "color" + OpDecorate %8 RelaxedPrecision + OpDecorate %8 Location 0 + OpDecorate %9 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %20 RelaxedPrecision + OpDecorate %23 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %44 Location 0 + OpDecorate %45 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Input %6 + %8 = OpVariable %7 Input + %10 = OpConstant %6 0 + %11 = OpTypeBool + %15 = OpTypeVector %6 4 + %16 = OpTypePointer Function %15 + %21 = OpConstant %6 -0.5 + %22 = OpConstant %6 -0.300000012 + %38 = OpConstant %6 0.5 + %43 = OpTypePointer Output %15 + %44 = OpVariable %43 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpLoad %6 %8 + %12 = OpFOrdLessThanEqual %11 %9 %10 + OpSelectionMerge %14 None + OpBranchConditional %12 %13 %32 + %13 = OpLabel + %18 = OpLoad %6 %8 + %19 = OpExtInst %6 %1 Log %18 + %20 = OpLoad %6 %8 + %23 = OpExtInst %6 %1 FClamp %20 %21 %22 + %24 = OpFMul %6 %19 %23 + %25 = OpLoad %6 %8 + %26 = OpExtInst %6 %1 Sin %25 + %27 = OpLoad %6 %8 + %28 = OpExtInst %6 %1 Cos %27 + %29 = OpLoad %6 %8 + %30 = OpExtInst %6 %1 Exp %29 + %31 = OpCompositeConstruct %15 %24 %26 %28 %30 + OpBranch %14 + %32 = OpLabel + %33 = OpLoad %6 %8 + %34 = OpExtInst %6 %1 Sqrt %33 + %35 = OpLoad %6 %8 + %36 = OpExtInst %6 %1 FSign %35 + %37 = OpLoad %6 %8 + %39 = OpExtInst %6 %1 FMax %37 %38 + %40 = OpLoad %6 %8 + %41 = OpExtInst %6 %1 Floor %40 + %42 = OpCompositeConstruct %15 %34 %36 %39 %41 + OpBranch %14 + %14 = OpLabel + %45 = OpPhi %15 %31 %13 %42 %32 + OpStore %44 %45 + OpReturn + OpFunctionEnd diff --git a/test/diff/diff_files/reordered_switch_blocks_autogen.cpp b/test/diff/diff_files/reordered_switch_blocks_autogen.cpp new file mode 100644 index 00000000..c0ba48d1 --- /dev/null +++ b/test/diff/diff_files/reordered_switch_blocks_autogen.cpp @@ -0,0 +1,582 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test where src and dst have cases of a switch in different order. +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %7 "BufferIn" + OpMemberName %7 0 "i" + OpName %9 "" + OpName %23 "BufferOut" + OpMemberName %23 0 "o" + OpName %25 "" + OpMemberDecorate %7 0 Offset 0 + OpDecorate %7 Block + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + OpMemberDecorate %23 0 Offset 0 + OpDecorate %23 BufferBlock + OpDecorate %25 DescriptorSet 0 + OpDecorate %25 Binding 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Uniform %7 + %9 = OpVariable %8 Uniform + %10 = OpTypeInt 32 1 + %11 = OpConstant %10 0 + %12 = OpTypePointer Uniform %6 + %23 = OpTypeStruct %6 + %24 = OpTypePointer Uniform %23 + %25 = OpVariable %24 Uniform + %28 = OpConstant %10 1 + %34 = OpConstant %6 2 + %52 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpAccessChain %12 %9 %11 + %14 = OpLoad %6 %13 + OpSelectionMerge %22 None + OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20 + %21 = OpLabel + %54 = OpAccessChain %12 %25 %11 + %55 = OpLoad %6 %54 + %56 = OpIAdd %6 %55 %34 + %57 = OpAccessChain %12 %25 %11 + OpStore %57 %56 + OpBranch %22 + %15 = OpLabel + %26 = OpAccessChain %12 %25 %11 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + OpStore %26 %29 + OpBranch %22 + %16 = OpLabel + %31 = OpAccessChain %12 %25 %11 + %32 = OpLoad %6 %31 + %33 = OpISub %6 %32 %28 + OpStore %31 %33 + OpBranch %17 + %17 = OpLabel + %35 = OpAccessChain %12 %25 %11 + %36 = OpLoad %6 %35 + %37 = OpIMul %6 %36 %34 + %38 = OpAccessChain %12 %25 %11 + OpStore %38 %37 + OpBranch %22 + %18 = OpLabel + %40 = OpAccessChain %12 %25 %11 + %41 = OpLoad %6 %40 + %42 = OpUDiv %6 %41 %34 + %43 = OpAccessChain %12 %25 %11 + OpStore %43 %42 + OpBranch %22 + %19 = OpLabel + %45 = OpAccessChain %12 %25 %11 + %46 = OpLoad %6 %45 + %47 = OpAccessChain %12 %25 %11 + %48 = OpLoad %6 %47 + %49 = OpIMul %6 %46 %48 + %50 = OpAccessChain %12 %25 %11 + OpStore %50 %49 + OpBranch %22 + %20 = OpLabel + %53 = OpAccessChain %12 %25 %11 + OpStore %53 %52 + OpBranch %21 + %22 = OpLabel + OpReturn + OpFunctionEnd)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %7 "BufferIn" + OpMemberName %7 0 "i" + OpName %9 "" + OpName %23 "BufferOut" + OpMemberName %23 0 "o" + OpName %25 "" + OpMemberDecorate %7 0 Offset 0 + OpDecorate %7 Block + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + OpMemberDecorate %23 0 Offset 0 + OpDecorate %23 BufferBlock + OpDecorate %25 DescriptorSet 0 + OpDecorate %25 Binding 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Uniform %7 + %9 = OpVariable %8 Uniform + %10 = OpTypeInt 32 1 + %11 = OpConstant %10 0 + %12 = OpTypePointer Uniform %6 + %23 = OpTypeStruct %6 + %24 = OpTypePointer Uniform %23 + %25 = OpVariable %24 Uniform + %28 = OpConstant %10 1 + %34 = OpConstant %6 2 + %52 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpAccessChain %12 %9 %11 + %14 = OpLoad %6 %13 + OpSelectionMerge %22 None + OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20 + %17 = OpLabel + %35 = OpAccessChain %12 %25 %11 + %36 = OpLoad %6 %35 + %37 = OpIMul %6 %36 %34 + %38 = OpAccessChain %12 %25 %11 + OpStore %38 %37 + OpBranch %22 + %18 = OpLabel + %40 = OpAccessChain %12 %25 %11 + %41 = OpLoad %6 %40 + %42 = OpUDiv %6 %41 %34 + %43 = OpAccessChain %12 %25 %11 + OpStore %43 %42 + OpBranch %22 + %21 = OpLabel + %54 = OpAccessChain %12 %25 %11 + %55 = OpLoad %6 %54 + %56 = OpIAdd %6 %55 %34 + %57 = OpAccessChain %12 %25 %11 + OpStore %57 %56 + OpBranch %22 + %20 = OpLabel + %53 = OpAccessChain %12 %25 %11 + OpStore %53 %52 + OpBranch %21 + %15 = OpLabel + %26 = OpAccessChain %12 %25 %11 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + OpStore %26 %29 + OpBranch %22 + %19 = OpLabel + %45 = OpAccessChain %12 %25 %11 + %46 = OpLoad %6 %45 + %47 = OpAccessChain %12 %25 %11 + %48 = OpLoad %6 %47 + %49 = OpIMul %6 %46 %48 + %50 = OpAccessChain %12 %25 %11 + OpStore %50 %49 + OpBranch %22 + %16 = OpLabel + %31 = OpAccessChain %12 %25 %11 + %32 = OpLoad %6 %31 + %33 = OpISub %6 %32 %28 + OpStore %31 %33 + OpBranch %17 + %22 = OpLabel + OpReturn + OpFunctionEnd +)"; + +TEST(DiffTest, ReorderedSwitchBlocks) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 58 ++; Bound: 62 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %7 "BufferIn" + OpMemberName %7 0 "i" + OpName %9 "" + OpName %23 "BufferOut" + OpMemberName %23 0 "o" + OpName %25 "" + OpMemberDecorate %7 0 Offset 0 + OpDecorate %7 Block + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + OpMemberDecorate %23 0 Offset 0 + OpDecorate %23 BufferBlock + OpDecorate %25 DescriptorSet 0 + OpDecorate %25 Binding 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Uniform %7 + %9 = OpVariable %8 Uniform + %10 = OpTypeInt 32 1 + %11 = OpConstant %10 0 + %12 = OpTypePointer Uniform %6 + %23 = OpTypeStruct %6 + %24 = OpTypePointer Uniform %23 + %25 = OpVariable %24 Uniform + %28 = OpConstant %10 1 + %34 = OpConstant %6 2 + %52 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpAccessChain %12 %9 %11 + %14 = OpLoad %6 %13 + OpSelectionMerge %22 None + OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20 + %20 = OpLabel + %53 = OpAccessChain %12 %25 %11 + OpStore %53 %52 + OpBranch %21 + %19 = OpLabel + %45 = OpAccessChain %12 %25 %11 + %46 = OpLoad %6 %45 + %47 = OpAccessChain %12 %25 %11 + %48 = OpLoad %6 %47 + %49 = OpIMul %6 %46 %48 + %50 = OpAccessChain %12 %25 %11 + OpStore %50 %49 + OpBranch %22 + %18 = OpLabel + %40 = OpAccessChain %12 %25 %11 + %41 = OpLoad %6 %40 + %42 = OpUDiv %6 %41 %34 + %43 = OpAccessChain %12 %25 %11 + OpStore %43 %42 + OpBranch %22 + %16 = OpLabel + %31 = OpAccessChain %12 %25 %11 + %32 = OpLoad %6 %31 + %33 = OpISub %6 %32 %28 + OpStore %31 %33 + OpBranch %17 + %17 = OpLabel + %35 = OpAccessChain %12 %25 %11 + %36 = OpLoad %6 %35 + %37 = OpIMul %6 %36 %34 + %38 = OpAccessChain %12 %25 %11 + OpStore %38 %37 + OpBranch %22 + %15 = OpLabel + %26 = OpAccessChain %12 %25 %11 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + OpStore %26 %29 + OpBranch %22 + %21 = OpLabel + %54 = OpAccessChain %12 %25 %11 + %55 = OpLoad %6 %54 + %56 = OpIAdd %6 %55 %34 + %57 = OpAccessChain %12 %25 %11 + OpStore %57 %56 + OpBranch %22 + %22 = OpLabel + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, ReorderedSwitchBlocksNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpMemberDecorate %7 0 Offset 0 + OpDecorate %7 Block + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + OpMemberDecorate %23 0 Offset 0 + OpDecorate %23 BufferBlock + OpDecorate %25 DescriptorSet 0 + OpDecorate %25 Binding 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Uniform %7 + %9 = OpVariable %8 Uniform + %10 = OpTypeInt 32 1 + %11 = OpConstant %10 0 + %12 = OpTypePointer Uniform %6 + %23 = OpTypeStruct %6 + %24 = OpTypePointer Uniform %23 + %25 = OpVariable %24 Uniform + %28 = OpConstant %10 1 + %34 = OpConstant %6 2 + %52 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpAccessChain %12 %9 %11 + %14 = OpLoad %6 %13 + OpSelectionMerge %22 None + OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20 + %21 = OpLabel + %54 = OpAccessChain %12 %25 %11 + %55 = OpLoad %6 %54 + %56 = OpIAdd %6 %55 %34 + %57 = OpAccessChain %12 %25 %11 + OpStore %57 %56 + OpBranch %22 + %15 = OpLabel + %26 = OpAccessChain %12 %25 %11 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + OpStore %26 %29 + OpBranch %22 + %16 = OpLabel + %31 = OpAccessChain %12 %25 %11 + %32 = OpLoad %6 %31 + %33 = OpISub %6 %32 %28 + OpStore %31 %33 + OpBranch %17 + %17 = OpLabel + %35 = OpAccessChain %12 %25 %11 + %36 = OpLoad %6 %35 + %37 = OpIMul %6 %36 %34 + %38 = OpAccessChain %12 %25 %11 + OpStore %38 %37 + OpBranch %22 + %18 = OpLabel + %40 = OpAccessChain %12 %25 %11 + %41 = OpLoad %6 %40 + %42 = OpUDiv %6 %41 %34 + %43 = OpAccessChain %12 %25 %11 + OpStore %43 %42 + OpBranch %22 + %19 = OpLabel + %45 = OpAccessChain %12 %25 %11 + %46 = OpLoad %6 %45 + %47 = OpAccessChain %12 %25 %11 + %48 = OpLoad %6 %47 + %49 = OpIMul %6 %46 %48 + %50 = OpAccessChain %12 %25 %11 + OpStore %50 %49 + OpBranch %22 + %20 = OpLabel + %53 = OpAccessChain %12 %25 %11 + OpStore %53 %52 + OpBranch %21 + %22 = OpLabel + OpReturn + OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpMemberDecorate %7 0 Offset 0 + OpDecorate %7 Block + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + OpMemberDecorate %23 0 Offset 0 + OpDecorate %23 BufferBlock + OpDecorate %25 DescriptorSet 0 + OpDecorate %25 Binding 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Uniform %7 + %9 = OpVariable %8 Uniform + %10 = OpTypeInt 32 1 + %11 = OpConstant %10 0 + %12 = OpTypePointer Uniform %6 + %23 = OpTypeStruct %6 + %24 = OpTypePointer Uniform %23 + %25 = OpVariable %24 Uniform + %28 = OpConstant %10 1 + %34 = OpConstant %6 2 + %52 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpAccessChain %12 %9 %11 + %14 = OpLoad %6 %13 + OpSelectionMerge %22 None + OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20 + %17 = OpLabel + %35 = OpAccessChain %12 %25 %11 + %36 = OpLoad %6 %35 + %37 = OpIMul %6 %36 %34 + %38 = OpAccessChain %12 %25 %11 + OpStore %38 %37 + OpBranch %22 + %18 = OpLabel + %40 = OpAccessChain %12 %25 %11 + %41 = OpLoad %6 %40 + %42 = OpUDiv %6 %41 %34 + %43 = OpAccessChain %12 %25 %11 + OpStore %43 %42 + OpBranch %22 + %21 = OpLabel + %54 = OpAccessChain %12 %25 %11 + %55 = OpLoad %6 %54 + %56 = OpIAdd %6 %55 %34 + %57 = OpAccessChain %12 %25 %11 + OpStore %57 %56 + OpBranch %22 + %20 = OpLabel + %53 = OpAccessChain %12 %25 %11 + OpStore %53 %52 + OpBranch %21 + %15 = OpLabel + %26 = OpAccessChain %12 %25 %11 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + OpStore %26 %29 + OpBranch %22 + %19 = OpLabel + %45 = OpAccessChain %12 %25 %11 + %46 = OpLoad %6 %45 + %47 = OpAccessChain %12 %25 %11 + %48 = OpLoad %6 %47 + %49 = OpIMul %6 %46 %48 + %50 = OpAccessChain %12 %25 %11 + OpStore %50 %49 + OpBranch %22 + %16 = OpLabel + %31 = OpAccessChain %12 %25 %11 + %32 = OpLoad %6 %31 + %33 = OpISub %6 %32 %28 + OpStore %31 %33 + OpBranch %17 + %22 = OpLabel + OpReturn + OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 58 ++; Bound: 62 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpMemberDecorate %7 0 Offset 0 + OpDecorate %7 Block + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + OpMemberDecorate %23 0 Offset 0 + OpDecorate %23 BufferBlock + OpDecorate %25 DescriptorSet 0 + OpDecorate %25 Binding 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Uniform %7 + %9 = OpVariable %8 Uniform + %10 = OpTypeInt 32 1 + %11 = OpConstant %10 0 + %12 = OpTypePointer Uniform %6 + %23 = OpTypeStruct %6 + %24 = OpTypePointer Uniform %23 + %25 = OpVariable %24 Uniform + %28 = OpConstant %10 1 + %34 = OpConstant %6 2 + %52 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpAccessChain %12 %9 %11 + %14 = OpLoad %6 %13 + OpSelectionMerge %22 None + OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20 + %20 = OpLabel + %53 = OpAccessChain %12 %25 %11 + OpStore %53 %52 + OpBranch %21 + %19 = OpLabel + %45 = OpAccessChain %12 %25 %11 + %46 = OpLoad %6 %45 + %47 = OpAccessChain %12 %25 %11 + %48 = OpLoad %6 %47 + %49 = OpIMul %6 %46 %48 + %50 = OpAccessChain %12 %25 %11 + OpStore %50 %49 + OpBranch %22 + %18 = OpLabel + %40 = OpAccessChain %12 %25 %11 + %41 = OpLoad %6 %40 + %42 = OpUDiv %6 %41 %34 + %43 = OpAccessChain %12 %25 %11 + OpStore %43 %42 + OpBranch %22 + %16 = OpLabel + %31 = OpAccessChain %12 %25 %11 + %32 = OpLoad %6 %31 + %33 = OpISub %6 %32 %28 + OpStore %31 %33 + OpBranch %17 + %17 = OpLabel + %35 = OpAccessChain %12 %25 %11 + %36 = OpLoad %6 %35 + %37 = OpIMul %6 %36 %34 + %38 = OpAccessChain %12 %25 %11 + OpStore %38 %37 + OpBranch %22 + %15 = OpLabel + %26 = OpAccessChain %12 %25 %11 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + OpStore %26 %29 + OpBranch %22 + %21 = OpLabel + %54 = OpAccessChain %12 %25 %11 + %55 = OpLoad %6 %54 + %56 = OpIAdd %6 %55 %34 + %57 = OpAccessChain %12 %25 %11 + OpStore %57 %56 + OpBranch %22 + %22 = OpLabel + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/reordered_switch_blocks_dst.spvasm b/test/diff/diff_files/reordered_switch_blocks_dst.spvasm new file mode 100644 index 00000000..8eabb4ef --- /dev/null +++ b/test/diff/diff_files/reordered_switch_blocks_dst.spvasm @@ -0,0 +1,91 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %7 "BufferIn" + OpMemberName %7 0 "i" + OpName %9 "" + OpName %23 "BufferOut" + OpMemberName %23 0 "o" + OpName %25 "" + OpMemberDecorate %7 0 Offset 0 + OpDecorate %7 Block + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + OpMemberDecorate %23 0 Offset 0 + OpDecorate %23 BufferBlock + OpDecorate %25 DescriptorSet 0 + OpDecorate %25 Binding 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Uniform %7 + %9 = OpVariable %8 Uniform + %10 = OpTypeInt 32 1 + %11 = OpConstant %10 0 + %12 = OpTypePointer Uniform %6 + %23 = OpTypeStruct %6 + %24 = OpTypePointer Uniform %23 + %25 = OpVariable %24 Uniform + %28 = OpConstant %10 1 + %34 = OpConstant %6 2 + %52 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpAccessChain %12 %9 %11 + %14 = OpLoad %6 %13 + OpSelectionMerge %22 None + OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20 + %17 = OpLabel + %35 = OpAccessChain %12 %25 %11 + %36 = OpLoad %6 %35 + %37 = OpIMul %6 %36 %34 + %38 = OpAccessChain %12 %25 %11 + OpStore %38 %37 + OpBranch %22 + %18 = OpLabel + %40 = OpAccessChain %12 %25 %11 + %41 = OpLoad %6 %40 + %42 = OpUDiv %6 %41 %34 + %43 = OpAccessChain %12 %25 %11 + OpStore %43 %42 + OpBranch %22 + %21 = OpLabel + %54 = OpAccessChain %12 %25 %11 + %55 = OpLoad %6 %54 + %56 = OpIAdd %6 %55 %34 + %57 = OpAccessChain %12 %25 %11 + OpStore %57 %56 + OpBranch %22 + %20 = OpLabel + %53 = OpAccessChain %12 %25 %11 + OpStore %53 %52 + OpBranch %21 + %15 = OpLabel + %26 = OpAccessChain %12 %25 %11 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + OpStore %26 %29 + OpBranch %22 + %19 = OpLabel + %45 = OpAccessChain %12 %25 %11 + %46 = OpLoad %6 %45 + %47 = OpAccessChain %12 %25 %11 + %48 = OpLoad %6 %47 + %49 = OpIMul %6 %46 %48 + %50 = OpAccessChain %12 %25 %11 + OpStore %50 %49 + OpBranch %22 + %16 = OpLabel + %31 = OpAccessChain %12 %25 %11 + %32 = OpLoad %6 %31 + %33 = OpISub %6 %32 %28 + OpStore %31 %33 + OpBranch %17 + %22 = OpLabel + OpReturn + OpFunctionEnd diff --git a/test/diff/diff_files/reordered_switch_blocks_src.spvasm b/test/diff/diff_files/reordered_switch_blocks_src.spvasm new file mode 100644 index 00000000..e3772282 --- /dev/null +++ b/test/diff/diff_files/reordered_switch_blocks_src.spvasm @@ -0,0 +1,92 @@ +;; Test where src and dst have cases of a switch in different order. + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %7 "BufferIn" + OpMemberName %7 0 "i" + OpName %9 "" + OpName %23 "BufferOut" + OpMemberName %23 0 "o" + OpName %25 "" + OpMemberDecorate %7 0 Offset 0 + OpDecorate %7 Block + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + OpMemberDecorate %23 0 Offset 0 + OpDecorate %23 BufferBlock + OpDecorate %25 DescriptorSet 0 + OpDecorate %25 Binding 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Uniform %7 + %9 = OpVariable %8 Uniform + %10 = OpTypeInt 32 1 + %11 = OpConstant %10 0 + %12 = OpTypePointer Uniform %6 + %23 = OpTypeStruct %6 + %24 = OpTypePointer Uniform %23 + %25 = OpVariable %24 Uniform + %28 = OpConstant %10 1 + %34 = OpConstant %6 2 + %52 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpAccessChain %12 %9 %11 + %14 = OpLoad %6 %13 + OpSelectionMerge %22 None + OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20 + %21 = OpLabel + %54 = OpAccessChain %12 %25 %11 + %55 = OpLoad %6 %54 + %56 = OpIAdd %6 %55 %34 + %57 = OpAccessChain %12 %25 %11 + OpStore %57 %56 + OpBranch %22 + %15 = OpLabel + %26 = OpAccessChain %12 %25 %11 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + OpStore %26 %29 + OpBranch %22 + %16 = OpLabel + %31 = OpAccessChain %12 %25 %11 + %32 = OpLoad %6 %31 + %33 = OpISub %6 %32 %28 + OpStore %31 %33 + OpBranch %17 + %17 = OpLabel + %35 = OpAccessChain %12 %25 %11 + %36 = OpLoad %6 %35 + %37 = OpIMul %6 %36 %34 + %38 = OpAccessChain %12 %25 %11 + OpStore %38 %37 + OpBranch %22 + %18 = OpLabel + %40 = OpAccessChain %12 %25 %11 + %41 = OpLoad %6 %40 + %42 = OpUDiv %6 %41 %34 + %43 = OpAccessChain %12 %25 %11 + OpStore %43 %42 + OpBranch %22 + %19 = OpLabel + %45 = OpAccessChain %12 %25 %11 + %46 = OpLoad %6 %45 + %47 = OpAccessChain %12 %25 %11 + %48 = OpLoad %6 %47 + %49 = OpIMul %6 %46 %48 + %50 = OpAccessChain %12 %25 %11 + OpStore %50 %49 + OpBranch %22 + %20 = OpLabel + %53 = OpAccessChain %12 %25 %11 + OpStore %53 %52 + OpBranch %21 + %22 = OpLabel + OpReturn + OpFunctionEnd diff --git a/test/diff/diff_files/small_functions_small_diffs_autogen.cpp b/test/diff/diff_files/small_functions_small_diffs_autogen.cpp new file mode 100644 index 00000000..c1a91009 --- /dev/null +++ b/test/diff/diff_files/small_functions_small_diffs_autogen.cpp @@ -0,0 +1,747 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test where src and dst have many small functions with small differences. +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %10 "f3(" + OpName %12 "f4(" + OpName %14 "f5(" + OpName %17 "BufferOut" + OpMemberName %17 0 "o" + OpName %19 "" + OpName %22 "BufferIn" + OpMemberName %22 0 "i" + OpName %24 "" + OpMemberDecorate %17 0 Offset 0 + OpDecorate %17 BufferBlock + OpDecorate %19 DescriptorSet 0 + OpDecorate %19 Binding 1 + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %16 = OpTypeInt 32 0 + %17 = OpTypeStruct %16 + %18 = OpTypePointer Uniform %17 + %19 = OpVariable %18 Uniform + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 0 + %22 = OpTypeStruct %16 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %16 + %31 = OpConstant %20 1 + %36 = OpConstant %16 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %45 = OpFunctionCall %2 %6 + %46 = OpFunctionCall %2 %8 + %47 = OpFunctionCall %2 %10 + %48 = OpFunctionCall %2 %12 + %49 = OpFunctionCall %2 %14 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %26 = OpAccessChain %25 %24 %21 + %27 = OpLoad %16 %26 + %28 = OpAccessChain %25 %19 %21 + OpStore %28 %27 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %29 = OpAccessChain %25 %19 %21 + %30 = OpLoad %16 %29 + %32 = OpIAdd %16 %30 %31 + OpStore %29 %32 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %33 = OpAccessChain %25 %19 %21 + %34 = OpLoad %16 %33 + %35 = OpISub %16 %34 %31 + OpStore %33 %35 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %37 = OpAccessChain %25 %19 %21 + %38 = OpLoad %16 %37 + %39 = OpIMul %16 %38 %36 + %40 = OpAccessChain %25 %19 %21 + OpStore %40 %39 + OpReturn + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %41 = OpAccessChain %25 %19 %21 + %42 = OpLoad %16 %41 + %43 = OpUDiv %16 %42 %36 + %44 = OpAccessChain %25 %19 %21 + OpStore %44 %43 + OpReturn + OpFunctionEnd +)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %10 "f3(" + OpName %12 "f4(" + OpName %14 "f5(" + OpName %17 "BufferOut" + OpMemberName %17 0 "o" + OpName %19 "" + OpName %22 "BufferIn" + OpMemberName %22 0 "i" + OpName %24 "" + OpMemberDecorate %17 0 Offset 0 + OpDecorate %17 BufferBlock + OpDecorate %19 DescriptorSet 0 + OpDecorate %19 Binding 1 + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %16 = OpTypeInt 32 0 + %17 = OpTypeStruct %16 + %18 = OpTypePointer Uniform %17 + %19 = OpVariable %18 Uniform + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 0 + %22 = OpTypeStruct %16 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %16 + %31 = OpConstant %20 1 + %36 = OpConstant %16 2 + %6 = OpFunction %2 None %3 + %7 = OpLabel + %26 = OpAccessChain %25 %24 %21 + %27 = OpLoad %16 %26 + %28 = OpAccessChain %25 %19 %21 + OpStore %28 %27 + OpReturn + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %41 = OpAccessChain %25 %19 %21 + %42 = OpLoad %16 %41 + %43 = OpIAdd %16 %42 %36 + %44 = OpAccessChain %25 %19 %21 + OpStore %44 %43 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %29 = OpAccessChain %25 %19 %21 + %30 = OpLoad %16 %29 + %32 = OpISub %16 %30 %31 + OpStore %29 %32 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %33 = OpAccessChain %25 %19 %21 + %34 = OpLoad %16 %33 + %35 = OpIAdd %16 %34 %31 + OpStore %33 %35 + OpReturn + OpFunctionEnd + %4 = OpFunction %2 None %3 + %5 = OpLabel + %45 = OpFunctionCall %2 %6 + %46 = OpFunctionCall %2 %8 + %47 = OpFunctionCall %2 %10 + %48 = OpFunctionCall %2 %12 + %49 = OpFunctionCall %2 %14 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %37 = OpAccessChain %25 %19 %21 + %38 = OpLoad %16 %37 + %39 = OpISub %16 %38 %36 + %40 = OpAccessChain %25 %19 %21 + OpStore %40 %39 + OpReturn + OpFunctionEnd + +)"; + +TEST(DiffTest, SmallFunctionsSmallDiffs) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 50 ++; Bound: 54 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %10 "f3(" + OpName %12 "f4(" + OpName %14 "f5(" + OpName %17 "BufferOut" + OpMemberName %17 0 "o" + OpName %19 "" + OpName %22 "BufferIn" + OpMemberName %22 0 "i" + OpName %24 "" + OpMemberDecorate %17 0 Offset 0 + OpDecorate %17 BufferBlock + OpDecorate %19 DescriptorSet 0 + OpDecorate %19 Binding 1 + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %16 = OpTypeInt 32 0 + %17 = OpTypeStruct %16 + %18 = OpTypePointer Uniform %17 + %19 = OpVariable %18 Uniform + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 0 + %22 = OpTypeStruct %16 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %16 + %31 = OpConstant %20 1 + %36 = OpConstant %16 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %45 = OpFunctionCall %2 %6 + %46 = OpFunctionCall %2 %8 + %47 = OpFunctionCall %2 %10 + %48 = OpFunctionCall %2 %12 + %49 = OpFunctionCall %2 %14 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %26 = OpAccessChain %25 %24 %21 + %27 = OpLoad %16 %26 + %28 = OpAccessChain %25 %19 %21 + OpStore %28 %27 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %29 = OpAccessChain %25 %19 %21 + %30 = OpLoad %16 %29 +-%32 = OpIAdd %16 %30 %31 ++%50 = OpISub %16 %30 %31 +-OpStore %29 %32 ++OpStore %29 %50 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %33 = OpAccessChain %25 %19 %21 + %34 = OpLoad %16 %33 +-%35 = OpISub %16 %34 %31 ++%51 = OpIAdd %16 %34 %31 +-OpStore %33 %35 ++OpStore %33 %51 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %37 = OpAccessChain %25 %19 %21 + %38 = OpLoad %16 %37 +-%39 = OpIMul %16 %38 %36 ++%52 = OpISub %16 %38 %36 + %40 = OpAccessChain %25 %19 %21 +-OpStore %40 %39 ++OpStore %40 %52 + OpReturn + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %41 = OpAccessChain %25 %19 %21 + %42 = OpLoad %16 %41 +-%43 = OpUDiv %16 %42 %36 ++%53 = OpIAdd %16 %42 %36 + %44 = OpAccessChain %25 %19 %21 +-OpStore %44 %43 ++OpStore %44 %53 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, SmallFunctionsSmallDiffsNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpMemberDecorate %17 0 Offset 0 + OpDecorate %17 BufferBlock + OpDecorate %19 DescriptorSet 0 + OpDecorate %19 Binding 1 + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %16 = OpTypeInt 32 0 + %17 = OpTypeStruct %16 + %18 = OpTypePointer Uniform %17 + %19 = OpVariable %18 Uniform + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 0 + %22 = OpTypeStruct %16 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %16 + %31 = OpConstant %20 1 + %36 = OpConstant %16 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %45 = OpFunctionCall %2 %6 + %46 = OpFunctionCall %2 %8 + %47 = OpFunctionCall %2 %10 + %48 = OpFunctionCall %2 %12 + %49 = OpFunctionCall %2 %14 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %26 = OpAccessChain %25 %24 %21 + %27 = OpLoad %16 %26 + %28 = OpAccessChain %25 %19 %21 + OpStore %28 %27 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %29 = OpAccessChain %25 %19 %21 + %30 = OpLoad %16 %29 + %32 = OpIAdd %16 %30 %31 + OpStore %29 %32 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %33 = OpAccessChain %25 %19 %21 + %34 = OpLoad %16 %33 + %35 = OpISub %16 %34 %31 + OpStore %33 %35 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %37 = OpAccessChain %25 %19 %21 + %38 = OpLoad %16 %37 + %39 = OpIMul %16 %38 %36 + %40 = OpAccessChain %25 %19 %21 + OpStore %40 %39 + OpReturn + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %41 = OpAccessChain %25 %19 %21 + %42 = OpLoad %16 %41 + %43 = OpUDiv %16 %42 %36 + %44 = OpAccessChain %25 %19 %21 + OpStore %44 %43 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpMemberDecorate %17 0 Offset 0 + OpDecorate %17 BufferBlock + OpDecorate %19 DescriptorSet 0 + OpDecorate %19 Binding 1 + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %16 = OpTypeInt 32 0 + %17 = OpTypeStruct %16 + %18 = OpTypePointer Uniform %17 + %19 = OpVariable %18 Uniform + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 0 + %22 = OpTypeStruct %16 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %16 + %31 = OpConstant %20 1 + %36 = OpConstant %16 2 + %6 = OpFunction %2 None %3 + %7 = OpLabel + %26 = OpAccessChain %25 %24 %21 + %27 = OpLoad %16 %26 + %28 = OpAccessChain %25 %19 %21 + OpStore %28 %27 + OpReturn + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %41 = OpAccessChain %25 %19 %21 + %42 = OpLoad %16 %41 + %43 = OpIAdd %16 %42 %36 + %44 = OpAccessChain %25 %19 %21 + OpStore %44 %43 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %29 = OpAccessChain %25 %19 %21 + %30 = OpLoad %16 %29 + %32 = OpISub %16 %30 %31 + OpStore %29 %32 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %33 = OpAccessChain %25 %19 %21 + %34 = OpLoad %16 %33 + %35 = OpIAdd %16 %34 %31 + OpStore %33 %35 + OpReturn + OpFunctionEnd + %4 = OpFunction %2 None %3 + %5 = OpLabel + %45 = OpFunctionCall %2 %6 + %46 = OpFunctionCall %2 %8 + %47 = OpFunctionCall %2 %10 + %48 = OpFunctionCall %2 %12 + %49 = OpFunctionCall %2 %14 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %37 = OpAccessChain %25 %19 %21 + %38 = OpLoad %16 %37 + %39 = OpISub %16 %38 %36 + %40 = OpAccessChain %25 %19 %21 + OpStore %40 %39 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 50 ++; Bound: 52 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpMemberDecorate %17 0 Offset 0 + OpDecorate %17 BufferBlock + OpDecorate %19 DescriptorSet 0 + OpDecorate %19 Binding 1 + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %16 = OpTypeInt 32 0 + %17 = OpTypeStruct %16 + %18 = OpTypePointer Uniform %17 + %19 = OpVariable %18 Uniform + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 0 + %22 = OpTypeStruct %16 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %16 + %31 = OpConstant %20 1 + %36 = OpConstant %16 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %45 = OpFunctionCall %2 %6 +-%46 = OpFunctionCall %2 %8 ++%46 = OpFunctionCall %2 %10 +-%47 = OpFunctionCall %2 %10 ++%47 = OpFunctionCall %2 %8 + %48 = OpFunctionCall %2 %12 + %49 = OpFunctionCall %2 %14 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %26 = OpAccessChain %25 %24 %21 + %27 = OpLoad %16 %26 + %28 = OpAccessChain %25 %19 %21 + OpStore %28 %27 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %29 = OpAccessChain %25 %19 %21 + %30 = OpLoad %16 %29 + %32 = OpIAdd %16 %30 %31 + OpStore %29 %32 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %33 = OpAccessChain %25 %19 %21 + %34 = OpLoad %16 %33 + %35 = OpISub %16 %34 %31 + OpStore %33 %35 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %37 = OpAccessChain %25 %19 %21 + %38 = OpLoad %16 %37 +-%39 = OpIMul %16 %38 %36 ++%50 = OpISub %16 %38 %36 + %40 = OpAccessChain %25 %19 %21 +-OpStore %40 %39 ++OpStore %40 %50 + OpReturn + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %41 = OpAccessChain %25 %19 %21 + %42 = OpLoad %16 %41 +-%43 = OpUDiv %16 %42 %36 ++%51 = OpIAdd %16 %42 %36 + %44 = OpAccessChain %25 %19 %21 +-OpStore %44 %43 ++OpStore %44 %51 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +TEST(DiffTest, SmallFunctionsSmallDiffsDumpIds) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 50 ++; Bound: 54 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %10 "f3(" + OpName %12 "f4(" + OpName %14 "f5(" + OpName %17 "BufferOut" + OpMemberName %17 0 "o" + OpName %19 "" + OpName %22 "BufferIn" + OpMemberName %22 0 "i" + OpName %24 "" + OpMemberDecorate %17 0 Offset 0 + OpDecorate %17 BufferBlock + OpDecorate %19 DescriptorSet 0 + OpDecorate %19 Binding 1 + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %16 = OpTypeInt 32 0 + %17 = OpTypeStruct %16 + %18 = OpTypePointer Uniform %17 + %19 = OpVariable %18 Uniform + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 0 + %22 = OpTypeStruct %16 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %16 + %31 = OpConstant %20 1 + %36 = OpConstant %16 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %45 = OpFunctionCall %2 %6 + %46 = OpFunctionCall %2 %8 + %47 = OpFunctionCall %2 %10 + %48 = OpFunctionCall %2 %12 + %49 = OpFunctionCall %2 %14 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %26 = OpAccessChain %25 %24 %21 + %27 = OpLoad %16 %26 + %28 = OpAccessChain %25 %19 %21 + OpStore %28 %27 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %29 = OpAccessChain %25 %19 %21 + %30 = OpLoad %16 %29 +-%32 = OpIAdd %16 %30 %31 ++%50 = OpISub %16 %30 %31 +-OpStore %29 %32 ++OpStore %29 %50 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %33 = OpAccessChain %25 %19 %21 + %34 = OpLoad %16 %33 +-%35 = OpISub %16 %34 %31 ++%51 = OpIAdd %16 %34 %31 +-OpStore %33 %35 ++OpStore %33 %51 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %37 = OpAccessChain %25 %19 %21 + %38 = OpLoad %16 %37 +-%39 = OpIMul %16 %38 %36 ++%52 = OpISub %16 %38 %36 + %40 = OpAccessChain %25 %19 %21 +-OpStore %40 %39 ++OpStore %40 %52 + OpReturn + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %41 = OpAccessChain %25 %19 %21 + %42 = OpLoad %16 %41 +-%43 = OpUDiv %16 %42 %36 ++%53 = OpIAdd %16 %42 %36 + %44 = OpAccessChain %25 %19 %21 +-OpStore %44 %43 ++OpStore %44 %53 + OpReturn + OpFunctionEnd + Src -> Dst + 1 -> 1 [ExtInstImport] + 2 -> 2 [TypeVoid] + 3 -> 3 [TypeFunction] + 4 -> 4 [Function] + 5 -> 5 [Label] + 6 -> 6 [Function] + 7 -> 7 [Label] + 8 -> 8 [Function] + 9 -> 9 [Label] + 10 -> 10 [Function] + 11 -> 11 [Label] + 12 -> 12 [Function] + 13 -> 13 [Label] + 14 -> 14 [Function] + 15 -> 15 [Label] + 16 -> 16 [TypeInt] + 17 -> 17 [TypeStruct] + 18 -> 18 [TypePointer] + 19 -> 19 [Variable] + 20 -> 20 [TypeInt] + 21 -> 21 [Constant] + 22 -> 22 [TypeStruct] + 23 -> 23 [TypePointer] + 24 -> 24 [Variable] + 25 -> 25 [TypePointer] + 26 -> 26 [AccessChain] + 27 -> 27 [Load] + 28 -> 28 [AccessChain] + 29 -> 29 [AccessChain] + 30 -> 30 [Load] + 31 -> 31 [Constant] + 32 -> 50 [IAdd] + 33 -> 33 [AccessChain] + 34 -> 34 [Load] + 35 -> 51 [ISub] + 36 -> 36 [Constant] + 37 -> 37 [AccessChain] + 38 -> 38 [Load] + 39 -> 52 [IMul] + 40 -> 40 [AccessChain] + 41 -> 41 [AccessChain] + 42 -> 42 [Load] + 43 -> 53 [UDiv] + 44 -> 44 [AccessChain] + 45 -> 45 [FunctionCall] + 46 -> 46 [FunctionCall] + 47 -> 47 [FunctionCall] + 48 -> 48 [FunctionCall] + 49 -> 49 [FunctionCall] +)"; + Options options; + options.dump_id_map = true; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/small_functions_small_diffs_dst.spvasm b/test/diff/diff_files/small_functions_small_diffs_dst.spvasm new file mode 100644 index 00000000..fabf5692 --- /dev/null +++ b/test/diff/diff_files/small_functions_small_diffs_dst.spvasm @@ -0,0 +1,92 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %10 "f3(" + OpName %12 "f4(" + OpName %14 "f5(" + OpName %17 "BufferOut" + OpMemberName %17 0 "o" + OpName %19 "" + OpName %22 "BufferIn" + OpMemberName %22 0 "i" + OpName %24 "" + OpMemberDecorate %17 0 Offset 0 + OpDecorate %17 BufferBlock + OpDecorate %19 DescriptorSet 0 + OpDecorate %19 Binding 1 + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %16 = OpTypeInt 32 0 + %17 = OpTypeStruct %16 + %18 = OpTypePointer Uniform %17 + %19 = OpVariable %18 Uniform + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 0 + %22 = OpTypeStruct %16 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %16 + %31 = OpConstant %20 1 + %36 = OpConstant %16 2 + %6 = OpFunction %2 None %3 + %7 = OpLabel + %26 = OpAccessChain %25 %24 %21 + %27 = OpLoad %16 %26 + %28 = OpAccessChain %25 %19 %21 + OpStore %28 %27 + OpReturn + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %41 = OpAccessChain %25 %19 %21 + %42 = OpLoad %16 %41 + %43 = OpIAdd %16 %42 %36 + %44 = OpAccessChain %25 %19 %21 + OpStore %44 %43 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %29 = OpAccessChain %25 %19 %21 + %30 = OpLoad %16 %29 + %32 = OpISub %16 %30 %31 + OpStore %29 %32 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %33 = OpAccessChain %25 %19 %21 + %34 = OpLoad %16 %33 + %35 = OpIAdd %16 %34 %31 + OpStore %33 %35 + OpReturn + OpFunctionEnd + %4 = OpFunction %2 None %3 + %5 = OpLabel + %45 = OpFunctionCall %2 %6 + %46 = OpFunctionCall %2 %8 + %47 = OpFunctionCall %2 %10 + %48 = OpFunctionCall %2 %12 + %49 = OpFunctionCall %2 %14 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %37 = OpAccessChain %25 %19 %21 + %38 = OpLoad %16 %37 + %39 = OpISub %16 %38 %36 + %40 = OpAccessChain %25 %19 %21 + OpStore %40 %39 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/small_functions_small_diffs_src.spvasm b/test/diff/diff_files/small_functions_small_diffs_src.spvasm new file mode 100644 index 00000000..895285b1 --- /dev/null +++ b/test/diff/diff_files/small_functions_small_diffs_src.spvasm @@ -0,0 +1,93 @@ +;; Test where src and dst have many small functions with small differences. + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %10 "f3(" + OpName %12 "f4(" + OpName %14 "f5(" + OpName %17 "BufferOut" + OpMemberName %17 0 "o" + OpName %19 "" + OpName %22 "BufferIn" + OpMemberName %22 0 "i" + OpName %24 "" + OpMemberDecorate %17 0 Offset 0 + OpDecorate %17 BufferBlock + OpDecorate %19 DescriptorSet 0 + OpDecorate %19 Binding 1 + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %16 = OpTypeInt 32 0 + %17 = OpTypeStruct %16 + %18 = OpTypePointer Uniform %17 + %19 = OpVariable %18 Uniform + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 0 + %22 = OpTypeStruct %16 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %16 + %31 = OpConstant %20 1 + %36 = OpConstant %16 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %45 = OpFunctionCall %2 %6 + %46 = OpFunctionCall %2 %8 + %47 = OpFunctionCall %2 %10 + %48 = OpFunctionCall %2 %12 + %49 = OpFunctionCall %2 %14 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %26 = OpAccessChain %25 %24 %21 + %27 = OpLoad %16 %26 + %28 = OpAccessChain %25 %19 %21 + OpStore %28 %27 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %29 = OpAccessChain %25 %19 %21 + %30 = OpLoad %16 %29 + %32 = OpIAdd %16 %30 %31 + OpStore %29 %32 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %33 = OpAccessChain %25 %19 %21 + %34 = OpLoad %16 %33 + %35 = OpISub %16 %34 %31 + OpStore %33 %35 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %37 = OpAccessChain %25 %19 %21 + %38 = OpLoad %16 %37 + %39 = OpIMul %16 %38 %36 + %40 = OpAccessChain %25 %19 %21 + OpStore %40 %39 + OpReturn + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %41 = OpAccessChain %25 %19 %21 + %42 = OpLoad %16 %41 + %43 = OpUDiv %16 %42 %36 + %44 = OpAccessChain %25 %19 %21 + OpStore %44 %43 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/spec_constant_array_size_autogen.cpp b/test/diff/diff_files/spec_constant_array_size_autogen.cpp new file mode 100644 index 00000000..1962d27e --- /dev/null +++ b/test/diff/diff_files/spec_constant_array_size_autogen.cpp @@ -0,0 +1,310 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests that identical specialization constants are not matched with constants +// when used as array size. +constexpr char kSrc[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpName %4 "_ua_position" +OpName %17 "gl_PerVertex" +OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" +OpMemberName %17 3 "gl_CullDistance" +OpName %19 "" +OpName %22 "main" +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd)"; +constexpr char kDst[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpName %4 "_ua_position" +OpName %17 "gl_PerVertex" +OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" +OpMemberName %17 3 "gl_CullDistance" +OpName %19 "" +OpName %22 "main" +OpDecorate %4 Location 0 +OpDecorate %15 SpecId 4 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpSpecConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd +)"; + +TEST(DiffTest, SpecConstantArraySize) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 27 ++; Bound: 36 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %22 "main" %4 %19 + OpSource GLSL 450 + OpName %4 "_ua_position" + OpName %17 "gl_PerVertex" + OpMemberName %17 0 "gl_Position" + OpMemberName %17 1 "gl_PointSize" + OpMemberName %17 2 "gl_ClipDistance" + OpMemberName %17 3 "gl_CullDistance" + OpName %19 "" + OpName %22 "main" + OpDecorate %4 Location 0 ++OpDecorate %34 SpecId 4 + OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize + OpMemberDecorate %17 2 BuiltIn ClipDistance + OpMemberDecorate %17 3 BuiltIn CullDistance + OpDecorate %17 Block + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeInt 32 0 + %8 = OpTypeVector %5 4 +-%15 = OpConstant %5 8 +-%16 = OpTypeArray %1 %15 ++%34 = OpSpecConstant %5 8 ++%35 = OpTypeArray %1 %34 +-%17 = OpTypeStruct %2 %1 %16 %16 ++%17 = OpTypeStruct %2 %1 %35 %35 + %20 = OpTypeVoid + %25 = OpConstant %5 0 + %3 = OpTypePointer Input %2 + %13 = OpTypePointer Output %2 + %18 = OpTypePointer Output %17 + %21 = OpTypeFunction %20 + %4 = OpVariable %3 Input + %19 = OpVariable %18 Output + %22 = OpFunction %20 None %21 + %23 = OpLabel + %24 = OpLoad %2 %4 + %26 = OpAccessChain %13 %19 %25 + OpStore %26 %24 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, SpecConstantArraySizeNoDebug) { + constexpr char kSrcNoDebug[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpDecorate %4 Location 0 +OpDecorate %15 SpecId 4 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpSpecConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 27 ++; Bound: 36 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %22 "main" %4 %19 + OpSource GLSL 450 + OpDecorate %4 Location 0 ++OpDecorate %34 SpecId 4 + OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize + OpMemberDecorate %17 2 BuiltIn ClipDistance + OpMemberDecorate %17 3 BuiltIn CullDistance + OpDecorate %17 Block + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeInt 32 0 + %8 = OpTypeVector %5 4 +-%15 = OpConstant %5 8 +-%16 = OpTypeArray %1 %15 ++%34 = OpSpecConstant %5 8 ++%35 = OpTypeArray %1 %34 +-%17 = OpTypeStruct %2 %1 %16 %16 ++%17 = OpTypeStruct %2 %1 %35 %35 + %20 = OpTypeVoid + %25 = OpConstant %5 0 + %3 = OpTypePointer Input %2 + %13 = OpTypePointer Output %2 + %18 = OpTypePointer Output %17 + %21 = OpTypeFunction %20 + %4 = OpVariable %3 Input + %19 = OpVariable %18 Output + %22 = OpFunction %20 None %21 + %23 = OpLabel + %24 = OpLoad %2 %4 + %26 = OpAccessChain %13 %19 %25 + OpStore %26 %24 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/spec_constant_array_size_dst.spvasm b/test/diff/diff_files/spec_constant_array_size_dst.spvasm new file mode 100644 index 00000000..7cedf083 --- /dev/null +++ b/test/diff/diff_files/spec_constant_array_size_dst.spvasm @@ -0,0 +1,47 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpName %4 "_ua_position" +OpName %17 "gl_PerVertex" +OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" +OpMemberName %17 3 "gl_CullDistance" +OpName %19 "" +OpName %22 "main" +OpDecorate %4 Location 0 +OpDecorate %15 SpecId 4 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpSpecConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd diff --git a/test/diff/diff_files/spec_constant_array_size_src.spvasm b/test/diff/diff_files/spec_constant_array_size_src.spvasm new file mode 100644 index 00000000..e17e04ac --- /dev/null +++ b/test/diff/diff_files/spec_constant_array_size_src.spvasm @@ -0,0 +1,48 @@ +;; Tests that identical specialization constants are not matched with constants +;; when used as array size. +; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpName %4 "_ua_position" +OpName %17 "gl_PerVertex" +OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" +OpMemberName %17 3 "gl_CullDistance" +OpName %19 "" +OpName %22 "main" +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd diff --git a/test/diff/diff_files/spec_constant_composite_autogen.cpp b/test/diff/diff_files/spec_constant_composite_autogen.cpp new file mode 100644 index 00000000..e4b52cb6 --- /dev/null +++ b/test/diff/diff_files/spec_constant_composite_autogen.cpp @@ -0,0 +1,186 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests OpSpecConstantComposite matching. +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpDecorate %7 SpecId 3 + OpDecorate %8 SpecId 4 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %7 = OpSpecConstant %uint 1 + %8 = OpSpecConstant %uint 1 + %uint_1 = OpConstant %uint 1 + %v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %7 %8 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpDecorate %7 SpecId 3 + OpDecorate %8 SpecId 4 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %7 = OpSpecConstant %uint 2048 + %8 = OpSpecConstant %uint 1 + %uint_1 = OpConstant %uint 1 + %v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %7 %8 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + +TEST(DiffTest, SpecConstantComposite) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 12 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpExecutionMode %2 LocalSize 1 1 1 + OpSource GLSL 450 + OpName %2 "main" + OpDecorate %7 SpecId 3 + OpDecorate %8 SpecId 4 + OpDecorate %4 BuiltIn WorkgroupSize + %6 = OpTypeVoid + %3 = OpTypeFunction %6 + %9 = OpTypeInt 32 0 +-%7 = OpSpecConstant %9 1 ++%7 = OpSpecConstant %9 2048 + %8 = OpSpecConstant %9 1 + %10 = OpConstant %9 1 + %11 = OpTypeVector %9 3 + %4 = OpSpecConstantComposite %11 %7 %8 %10 + %2 = OpFunction %6 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, SpecConstantCompositeNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpDecorate %7 SpecId 3 + OpDecorate %8 SpecId 4 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %7 = OpSpecConstant %uint 1 + %8 = OpSpecConstant %uint 1 + %uint_1 = OpConstant %uint 1 + %v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %7 %8 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpDecorate %7 SpecId 3 + OpDecorate %8 SpecId 4 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %7 = OpSpecConstant %uint 2048 + %8 = OpSpecConstant %uint 1 + %uint_1 = OpConstant %uint 1 + %v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %7 %8 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 12 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpExecutionMode %2 LocalSize 1 1 1 + OpSource GLSL 450 + OpDecorate %7 SpecId 3 + OpDecorate %8 SpecId 4 + OpDecorate %4 BuiltIn WorkgroupSize + %6 = OpTypeVoid + %3 = OpTypeFunction %6 + %9 = OpTypeInt 32 0 +-%7 = OpSpecConstant %9 1 ++%7 = OpSpecConstant %9 2048 + %8 = OpSpecConstant %9 1 + %10 = OpConstant %9 1 + %11 = OpTypeVector %9 3 + %4 = OpSpecConstantComposite %11 %7 %8 %10 + %2 = OpFunction %6 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/spec_constant_composite_dst.spvasm b/test/diff/diff_files/spec_constant_composite_dst.spvasm new file mode 100644 index 00000000..3ab8d4d3 --- /dev/null +++ b/test/diff/diff_files/spec_constant_composite_dst.spvasm @@ -0,0 +1,22 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpDecorate %7 SpecId 3 + OpDecorate %8 SpecId 4 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %7 = OpSpecConstant %uint 2048 + %8 = OpSpecConstant %uint 1 + %uint_1 = OpConstant %uint 1 + %v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %7 %8 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd diff --git a/test/diff/diff_files/spec_constant_composite_src.spvasm b/test/diff/diff_files/spec_constant_composite_src.spvasm new file mode 100644 index 00000000..ee48ef9d --- /dev/null +++ b/test/diff/diff_files/spec_constant_composite_src.spvasm @@ -0,0 +1,23 @@ +;; Tests OpSpecConstantComposite matching. + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpDecorate %7 SpecId 3 + OpDecorate %8 SpecId 4 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %7 = OpSpecConstant %uint 1 + %8 = OpSpecConstant %uint 1 + %uint_1 = OpConstant %uint 1 + %v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %7 %8 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd diff --git a/test/diff/diff_files/spec_constant_op_autogen.cpp b/test/diff/diff_files/spec_constant_op_autogen.cpp new file mode 100644 index 00000000..e88e15be --- /dev/null +++ b/test/diff/diff_files/spec_constant_op_autogen.cpp @@ -0,0 +1,162 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests OpSpecConstantOp matching. +constexpr char kSrc[] = R"( OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 0 + %5 = OpTypeVector %4 3 + %6 = OpConstant %4 1 + %7 = OpSpecConstantComposite %5 %6 %6 %6 + %8 = OpSpecConstantOp %4 CompositeExtract %7 2 + %9 = OpSpecConstantOp %4 CompositeExtract %7 1 + %10 = OpSpecConstantOp %4 CompositeExtract %7 0 + %1 = OpFunction %2 None %3 + %11 = OpLabel + OpReturn + OpFunctionEnd)"; +constexpr char kDst[] = R"( OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 0 + %5 = OpTypeVector %4 3 + %6 = OpConstant %4 1 + %7 = OpSpecConstantComposite %5 %6 %6 %6 + %8 = OpSpecConstantOp %4 CompositeExtract %7 2 + %9 = OpSpecConstantOp %4 CompositeExtract %7 3 + %10 = OpSpecConstantOp %4 IMul %8 %8 + %1 = OpFunction %2 None %3 + %11 = OpLabel + OpReturn + OpFunctionEnd +)"; + +TEST(DiffTest, SpecConstantOp) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 12 ++; Bound: 14 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 0 + %5 = OpTypeVector %4 3 + %6 = OpConstant %4 1 + %7 = OpSpecConstantComposite %5 %6 %6 %6 + %8 = OpSpecConstantOp %4 CompositeExtract %7 2 +-%9 = OpSpecConstantOp %4 CompositeExtract %7 1 +-%10 = OpSpecConstantOp %4 CompositeExtract %7 0 ++%12 = OpSpecConstantOp %4 CompositeExtract %7 3 ++%13 = OpSpecConstantOp %4 IMul %8 %8 + %1 = OpFunction %2 None %3 + %11 = OpLabel + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, SpecConstantOpNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 0 + %5 = OpTypeVector %4 3 + %6 = OpConstant %4 1 + %7 = OpSpecConstantComposite %5 %6 %6 %6 + %8 = OpSpecConstantOp %4 CompositeExtract %7 2 + %9 = OpSpecConstantOp %4 CompositeExtract %7 1 + %10 = OpSpecConstantOp %4 CompositeExtract %7 0 + %1 = OpFunction %2 None %3 + %11 = OpLabel + OpReturn + OpFunctionEnd)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 0 + %5 = OpTypeVector %4 3 + %6 = OpConstant %4 1 + %7 = OpSpecConstantComposite %5 %6 %6 %6 + %8 = OpSpecConstantOp %4 CompositeExtract %7 2 + %9 = OpSpecConstantOp %4 CompositeExtract %7 3 + %10 = OpSpecConstantOp %4 IMul %8 %8 + %1 = OpFunction %2 None %3 + %11 = OpLabel + OpReturn + OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 12 ++; Bound: 14 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 0 + %5 = OpTypeVector %4 3 + %6 = OpConstant %4 1 + %7 = OpSpecConstantComposite %5 %6 %6 %6 + %8 = OpSpecConstantOp %4 CompositeExtract %7 2 +-%9 = OpSpecConstantOp %4 CompositeExtract %7 1 +-%10 = OpSpecConstantOp %4 CompositeExtract %7 0 ++%12 = OpSpecConstantOp %4 CompositeExtract %7 3 ++%13 = OpSpecConstantOp %4 IMul %8 %8 + %1 = OpFunction %2 None %3 + %11 = OpLabel + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/spec_constant_op_dst.spvasm b/test/diff/diff_files/spec_constant_op_dst.spvasm new file mode 100644 index 00000000..b5b10e52 --- /dev/null +++ b/test/diff/diff_files/spec_constant_op_dst.spvasm @@ -0,0 +1,17 @@ + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 0 + %5 = OpTypeVector %4 3 + %6 = OpConstant %4 1 + %7 = OpSpecConstantComposite %5 %6 %6 %6 + %8 = OpSpecConstantOp %4 CompositeExtract %7 2 + %9 = OpSpecConstantOp %4 CompositeExtract %7 3 + %10 = OpSpecConstantOp %4 IMul %8 %8 + %1 = OpFunction %2 None %3 + %11 = OpLabel + OpReturn + OpFunctionEnd diff --git a/test/diff/diff_files/spec_constant_op_src.spvasm b/test/diff/diff_files/spec_constant_op_src.spvasm new file mode 100644 index 00000000..09306f8b --- /dev/null +++ b/test/diff/diff_files/spec_constant_op_src.spvasm @@ -0,0 +1,18 @@ +;; Tests OpSpecConstantOp matching. + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 0 + %5 = OpTypeVector %4 3 + %6 = OpConstant %4 1 + %7 = OpSpecConstantComposite %5 %6 %6 %6 + %8 = OpSpecConstantOp %4 CompositeExtract %7 2 + %9 = OpSpecConstantOp %4 CompositeExtract %7 1 + %10 = OpSpecConstantOp %4 CompositeExtract %7 0 + %1 = OpFunction %2 None %3 + %11 = OpLabel + OpReturn + OpFunctionEnd \ No newline at end of file diff --git a/test/diff/diff_files/spec_constant_specid_autogen.cpp b/test/diff/diff_files/spec_constant_specid_autogen.cpp new file mode 100644 index 00000000..240dc59c --- /dev/null +++ b/test/diff/diff_files/spec_constant_specid_autogen.cpp @@ -0,0 +1,141 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests OpSpecConstantComposite matching. +constexpr char kSrc[] = R"( OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %sc SpecId 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 + %sc = OpSpecConstant %uint 10 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd)"; +constexpr char kDst[] = R"( OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 + %ss = OpSpecConstant %uint 10 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + +TEST(DiffTest, SpecConstantSpecid) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 8 ++; Bound: 9 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 +-OpDecorate %2 SpecId 0 + %4 = OpTypeVoid + %3 = OpTypeFunction %4 + %6 = OpTypeInt 32 0 + %7 = OpTypeVector %6 3 +-%2 = OpSpecConstant %6 10 ++%8 = OpSpecConstant %6 10 + %1 = OpFunction %4 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, SpecConstantSpecidNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %sc SpecId 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 + %sc = OpSpecConstant %uint 10 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 + %ss = OpSpecConstant %uint 10 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 8 ++; Bound: 9 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 +-OpDecorate %2 SpecId 0 + %4 = OpTypeVoid + %3 = OpTypeFunction %4 + %6 = OpTypeInt 32 0 + %7 = OpTypeVector %6 3 +-%2 = OpSpecConstant %6 10 ++%8 = OpSpecConstant %6 10 + %1 = OpFunction %4 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/spec_constant_specid_dst.spvasm b/test/diff/diff_files/spec_constant_specid_dst.spvasm new file mode 100644 index 00000000..0e840edc --- /dev/null +++ b/test/diff/diff_files/spec_constant_specid_dst.spvasm @@ -0,0 +1,13 @@ + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 + %ss = OpSpecConstant %uint 10 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd diff --git a/test/diff/diff_files/spec_constant_specid_src.spvasm b/test/diff/diff_files/spec_constant_specid_src.spvasm new file mode 100644 index 00000000..7fdfeba2 --- /dev/null +++ b/test/diff/diff_files/spec_constant_specid_src.spvasm @@ -0,0 +1,15 @@ +;; Tests OpSpecConstantComposite matching. + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %sc SpecId 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 + %sc = OpSpecConstant %uint 10 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd diff --git a/test/diff/diff_files/unrelated_shaders_autogen.cpp b/test/diff/diff_files/unrelated_shaders_autogen.cpp new file mode 100644 index 00000000..e1a58ccc --- /dev/null +++ b/test/diff/diff_files/unrelated_shaders_autogen.cpp @@ -0,0 +1,230 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests diff of unrelated shaders (with different execution models). +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main" %8 %10 + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "v" + OpName %10 "a" + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Output %6 + %8 = OpVariable %7 Output + %9 = OpTypePointer Input %6 + %10 = OpVariable %9 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd +)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpCompositeConstruct %7 %12 %12 %12 %12 + OpStore %9 %13 + OpReturn + OpFunctionEnd + +)"; + +TEST(DiffTest, UnrelatedShaders) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 12 ++; Bound: 16 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %4 "main" %8 %10 ++OpEntryPoint Fragment %4 "main" %14 %8 ++OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" ++OpName %14 "color" + OpName %8 "v" +-OpName %10 "a" ++OpDecorate %14 RelaxedPrecision ++OpDecorate %14 Location 0 ++OpDecorate %8 RelaxedPrecision + OpDecorate %8 Location 0 +-OpDecorate %10 Location 0 ++OpDecorate %11 RelaxedPrecision ++OpDecorate %15 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 +-%7 = OpTypePointer Output %6 ++%12 = OpTypeVector %6 4 ++%13 = OpTypePointer Output %12 ++%14 = OpVariable %13 Output +-%8 = OpVariable %7 Output ++%8 = OpVariable %9 Input + %9 = OpTypePointer Input %6 +-%10 = OpVariable %9 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel +-%11 = OpLoad %6 %10 ++%11 = OpLoad %6 %8 +-OpStore %8 %11 ++%15 = OpCompositeConstruct %12 %11 %11 %11 %11 ++OpStore %14 %15 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, UnrelatedShadersNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main" %8 %10 + OpSource ESSL 310 + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Output %6 + %8 = OpVariable %7 Output + %9 = OpTypePointer Input %6 + %10 = OpVariable %9 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpCompositeConstruct %7 %12 %12 %12 %12 + OpStore %9 %13 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 12 ++; Bound: 15 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %4 "main" %8 %10 ++OpEntryPoint Fragment %4 "main" %8 %10 ++OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 ++OpDecorate %8 RelaxedPrecision + OpDecorate %8 Location 0 ++OpDecorate %10 RelaxedPrecision + OpDecorate %10 Location 0 ++OpDecorate %11 RelaxedPrecision ++OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 +-%7 = OpTypePointer Output %6 ++%12 = OpTypeVector %6 4 ++%13 = OpTypePointer Output %12 +-%8 = OpVariable %7 Output ++%8 = OpVariable %13 Output + %9 = OpTypePointer Input %6 + %10 = OpVariable %9 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 ++%14 = OpCompositeConstruct %12 %11 %11 %11 %11 +-OpStore %8 %11 ++OpStore %8 %14 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/unrelated_shaders_dst.spvasm b/test/diff/diff_files/unrelated_shaders_dst.spvasm new file mode 100644 index 00000000..719715ba --- /dev/null +++ b/test/diff/diff_files/unrelated_shaders_dst.spvasm @@ -0,0 +1,31 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpCompositeConstruct %7 %12 %12 %12 %12 + OpStore %9 %13 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/unrelated_shaders_src.spvasm b/test/diff/diff_files/unrelated_shaders_src.spvasm new file mode 100644 index 00000000..e77b2d20 --- /dev/null +++ b/test/diff/diff_files/unrelated_shaders_src.spvasm @@ -0,0 +1,25 @@ +;; Tests diff of unrelated shaders (with different execution models). + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main" %8 %10 + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "v" + OpName %10 "a" + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Output %6 + %8 = OpVariable %7 Output + %9 = OpTypePointer Input %6 + %10 = OpVariable %9 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_test.cpp b/test/diff/diff_test.cpp new file mode 100644 index 00000000..5b11d0eb --- /dev/null +++ b/test/diff/diff_test.cpp @@ -0,0 +1,228 @@ +// Copyright (c) 2022 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/diff/diff.h" + +#include "diff_test_utils.h" + +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" +#include "source/spirv_constant.h" +#include "spirv-tools/libspirv.hpp" +#include "tools/io.h" +#include "tools/util/cli_consumer.h" + +#include +#include + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +constexpr auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; + +std::unique_ptr Assemble(const std::string& spirv) { + spvtools::SpirvTools t(kDefaultEnvironment); + t.SetMessageConsumer(spvtools::utils::CLIMessageConsumer); + std::vector binary; + if (!t.Assemble(spirv, &binary, + spvtools::SpirvTools::kDefaultAssembleOption | + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS)) + return nullptr; + return spvtools::BuildModule(kDefaultEnvironment, + spvtools::utils::CLIMessageConsumer, + binary.data(), binary.size()); +} + +TEST(DiffIndentTest, Diff) { + const std::string src = R"(OpCapability Shader + %ext_inst = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %void = OpTypeVoid + %func = OpTypeFunction %void + + %main = OpFunction %void None %func + %main_entry = OpLabel + OpReturn + OpFunctionEnd;)"; + + const std::string dst = R"(OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %void = OpTypeVoid + %func = OpTypeFunction %void + + %main = OpFunction %void None %func + %main_entry = OpLabel + OpReturn + OpFunctionEnd;)"; + + const std::string diff = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 6 + ; Schema: 0 + OpCapability Shader +- %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %2 = OpFunction %3 None %4 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + + Options options; + options.indent = true; + DoStringDiffTest(src, dst, diff, options); +} + +TEST(DiffNoHeaderTest, Diff) { + const std::string src = R"(OpCapability Shader + %ext_inst = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %void = OpTypeVoid + %func = OpTypeFunction %void + + %main = OpFunction %void None %func + %main_entry = OpLabel + OpReturn + OpFunctionEnd;)"; + + const std::string dst = R"(OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %void = OpTypeVoid + %func = OpTypeFunction %void + + %main = OpFunction %void None %func + %main_entry = OpLabel + OpReturn + OpFunctionEnd;)"; + + const std::string diff = R"( OpCapability Shader +-%1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %2 = OpFunction %3 None %4 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + + Options options; + options.no_header = true; + DoStringDiffTest(src, dst, diff, options); +} + +TEST(DiffHeaderTest, Diff) { + const std::string src_spirv = R"(OpCapability Shader + %ext_inst = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %void = OpTypeVoid + %func = OpTypeFunction %void + + %main = OpFunction %void None %func + %main_entry = OpLabel + OpReturn + OpFunctionEnd;)"; + + const std::string dst_spirv = R"(OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %void = OpTypeVoid + %func = OpTypeFunction %void + + %main = OpFunction %void None %func + %main_entry = OpLabel + OpReturn + OpFunctionEnd;)"; + + const std::string diff = R"( ; SPIR-V +-; Version: 1.3 ++; Version: 1.2 +-; Generator: Khronos SPIR-V Tools Assembler; 3 ++; Generator: Khronos Glslang Reference Front End; 10 + ; Bound: 6 + ; Schema: 0 + OpCapability Shader +-%1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %2 = OpFunction %3 None %4 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + + // Load the src and dst modules + std::unique_ptr src = Assemble(src_spirv); + ASSERT_TRUE(src); + + std::unique_ptr dst = Assemble(dst_spirv); + ASSERT_TRUE(dst); + + // Differentiate them in the header. + const spvtools::opt::ModuleHeader src_header = { + SpvMagicNumber, + SPV_SPIRV_VERSION_WORD(1, 3), + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, 3), + src->module()->IdBound(), + src->module()->schema(), + }; + const spvtools::opt::ModuleHeader dst_header = { + SpvMagicNumber, + SPV_SPIRV_VERSION_WORD(1, 2), + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_GLSLANG, 10), + dst->module()->IdBound(), + dst->module()->schema(), + }; + + src->module()->SetHeader(src_header); + dst->module()->SetHeader(dst_header); + + // Take the diff + Options options; + std::ostringstream diff_result; + spv_result_t result = + spvtools::diff::Diff(src.get(), dst.get(), diff_result, options); + ASSERT_EQ(result, SPV_SUCCESS); + + // Expect they match + EXPECT_EQ(diff_result.str(), diff); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_test_utils.cpp b/test/diff/diff_test_utils.cpp new file mode 100644 index 00000000..14bb8215 --- /dev/null +++ b/test/diff/diff_test_utils.cpp @@ -0,0 +1,58 @@ +// Copyright (c) 2022 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 "diff_test_utils.h" + +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" + +#include "spirv-tools/libspirv.hpp" +#include "tools/io.h" +#include "tools/util/cli_consumer.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { + +static constexpr auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; + +void DoStringDiffTest(const std::string& src_spirv, + const std::string& dst_spirv, + const std::string& expected_diff, Options options) { + // Load the src and dst modules + std::unique_ptr src = spvtools::BuildModule( + kDefaultEnvironment, spvtools::utils::CLIMessageConsumer, src_spirv, + spvtools::SpirvTools::kDefaultAssembleOption | + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_TRUE(src); + + std::unique_ptr dst = spvtools::BuildModule( + kDefaultEnvironment, spvtools::utils::CLIMessageConsumer, dst_spirv, + spvtools::SpirvTools::kDefaultAssembleOption | + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_TRUE(dst); + + // Take the diff + std::ostringstream diff_result; + spv_result_t result = + spvtools::diff::Diff(src.get(), dst.get(), diff_result, options); + ASSERT_EQ(result, SPV_SUCCESS); + + // Expect they match + EXPECT_EQ(diff_result.str(), expected_diff); +} + +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_test_utils.h b/test/diff/diff_test_utils.h new file mode 100644 index 00000000..938236e6 --- /dev/null +++ b/test/diff/diff_test_utils.h @@ -0,0 +1,30 @@ +// Copyright (c) 2022 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 TEST_DIFF_DIFF_TEST_UTILS_H_ +#define TEST_DIFF_DIFF_TEST_UTILS_H_ + +#include "source/diff/diff.h" + +namespace spvtools { +namespace diff { + +void DoStringDiffTest(const std::string& src_spirv, + const std::string& dst_spirv, + const std::string& expected_diff, Options options); + +} // namespace diff +} // namespace spvtools + +#endif // TEST_DIFF_DIFF_TEST_UTILS_H_ diff --git a/test/diff/lcs_test.cpp b/test/diff/lcs_test.cpp new file mode 100644 index 00000000..3e097b3e --- /dev/null +++ b/test/diff/lcs_test.cpp @@ -0,0 +1,329 @@ +// Copyright (c) 2022 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/diff/lcs.h" + +#include + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +using Sequence = std::vector; +using LCS = LongestCommonSubsequence; + +void VerifyMatch(const Sequence& src, const Sequence& dst, + size_t expected_match_count) { + DiffMatch src_match, dst_match; + + LCS lcs(src, dst); + size_t match_count = + lcs.Get([](int s, int d) { return s == d; }, &src_match, &dst_match); + + EXPECT_EQ(match_count, expected_match_count); + + size_t src_cur = 0; + size_t dst_cur = 0; + size_t matches_seen = 0; + + while (src_cur < src.size() && dst_cur < dst.size()) { + if (src_match[src_cur] && dst_match[dst_cur]) { + EXPECT_EQ(src[src_cur], dst[dst_cur]) + << "Src: " << src_cur << " Dst: " << dst_cur; + ++src_cur; + ++dst_cur; + ++matches_seen; + continue; + } + if (!src_match[src_cur]) { + ++src_cur; + } + if (!dst_match[dst_cur]) { + ++dst_cur; + } + } + + EXPECT_EQ(matches_seen, expected_match_count); +} + +TEST(LCSTest, EmptySequences) { + Sequence src, dst; + + DiffMatch src_match, dst_match; + + LCS lcs(src, dst); + size_t match_count = + lcs.Get([](int s, int d) { return s == d; }, &src_match, &dst_match); + + EXPECT_EQ(match_count, 0u); + EXPECT_TRUE(src_match.empty()); + EXPECT_TRUE(dst_match.empty()); +} + +TEST(LCSTest, EmptySrc) { + Sequence src, dst = {1, 2, 3}; + + DiffMatch src_match, dst_match; + + LCS lcs(src, dst); + size_t match_count = + lcs.Get([](int s, int d) { return s == d; }, &src_match, &dst_match); + + EXPECT_EQ(match_count, 0u); + EXPECT_TRUE(src_match.empty()); + EXPECT_EQ(dst_match, DiffMatch(3, false)); +} + +TEST(LCSTest, EmptyDst) { + Sequence src = {1, 2, 3}, dst; + + DiffMatch src_match, dst_match; + + LCS lcs(src, dst); + size_t match_count = + lcs.Get([](int s, int d) { return s == d; }, &src_match, &dst_match); + + EXPECT_EQ(match_count, 0u); + EXPECT_EQ(src_match, DiffMatch(3, false)); + EXPECT_TRUE(dst_match.empty()); +} + +TEST(LCSTest, Identical) { + Sequence src = {1, 2, 3, 4, 5, 6}, dst = {1, 2, 3, 4, 5, 6}; + + DiffMatch src_match, dst_match; + + LCS lcs(src, dst); + size_t match_count = + lcs.Get([](int s, int d) { return s == d; }, &src_match, &dst_match); + + EXPECT_EQ(match_count, 6u); + EXPECT_EQ(src_match, DiffMatch(6, true)); + EXPECT_EQ(dst_match, DiffMatch(6, true)); +} + +TEST(LCSTest, SrcPrefix) { + Sequence src = {1, 2, 3, 4}, dst = {1, 2, 3, 4, 5, 6}; + + DiffMatch src_match, dst_match; + + LCS lcs(src, dst); + size_t match_count = + lcs.Get([](int s, int d) { return s == d; }, &src_match, &dst_match); + + const DiffMatch src_expect = {true, true, true, true}; + const DiffMatch dst_expect = {true, true, true, true, false, false}; + + EXPECT_EQ(match_count, 4u); + EXPECT_EQ(src_match, src_expect); + EXPECT_EQ(dst_match, dst_expect); +} + +TEST(LCSTest, DstPrefix) { + Sequence src = {1, 2, 3, 4, 5, 6}, dst = {1, 2, 3, 4, 5}; + + DiffMatch src_match, dst_match; + + LCS lcs(src, dst); + size_t match_count = + lcs.Get([](int s, int d) { return s == d; }, &src_match, &dst_match); + + const DiffMatch src_expect = {true, true, true, true, true, false}; + const DiffMatch dst_expect = {true, true, true, true, true}; + + EXPECT_EQ(match_count, 5u); + EXPECT_EQ(src_match, src_expect); + EXPECT_EQ(dst_match, dst_expect); +} + +TEST(LCSTest, SrcSuffix) { + Sequence src = {3, 4, 5, 6}, dst = {1, 2, 3, 4, 5, 6}; + + DiffMatch src_match, dst_match; + + LCS lcs(src, dst); + size_t match_count = + lcs.Get([](int s, int d) { return s == d; }, &src_match, &dst_match); + + const DiffMatch src_expect = {true, true, true, true}; + const DiffMatch dst_expect = {false, false, true, true, true, true}; + + EXPECT_EQ(match_count, 4u); + EXPECT_EQ(src_match, src_expect); + EXPECT_EQ(dst_match, dst_expect); +} + +TEST(LCSTest, DstSuffix) { + Sequence src = {1, 2, 3, 4, 5, 6}, dst = {5, 6}; + + DiffMatch src_match, dst_match; + + LCS lcs(src, dst); + size_t match_count = + lcs.Get([](int s, int d) { return s == d; }, &src_match, &dst_match); + + const DiffMatch src_expect = {false, false, false, false, true, true}; + const DiffMatch dst_expect = {true, true}; + + EXPECT_EQ(match_count, 2u); + EXPECT_EQ(src_match, src_expect); + EXPECT_EQ(dst_match, dst_expect); +} + +TEST(LCSTest, None) { + Sequence src = {1, 3, 5, 7, 9}, dst = {2, 4, 6, 8, 10, 12}; + + DiffMatch src_match, dst_match; + + LCS lcs(src, dst); + size_t match_count = + lcs.Get([](int s, int d) { return s == d; }, &src_match, &dst_match); + + EXPECT_EQ(match_count, 0u); + EXPECT_EQ(src_match, DiffMatch(5, false)); + EXPECT_EQ(dst_match, DiffMatch(6, false)); +} + +TEST(LCSTest, NonContiguous) { + Sequence src = {1, 2, 3, 4, 5, 6, 10}, dst = {2, 4, 5, 8, 9, 10, 12}; + + DiffMatch src_match, dst_match; + + LCS lcs(src, dst); + size_t match_count = + lcs.Get([](int s, int d) { return s == d; }, &src_match, &dst_match); + + const DiffMatch src_expect = {false, true, false, true, true, false, true}; + const DiffMatch dst_expect = {true, true, true, false, false, true, false}; + + EXPECT_EQ(match_count, 4u); + EXPECT_EQ(src_match, src_expect); + EXPECT_EQ(dst_match, dst_expect); +} + +TEST(LCSTest, WithDuplicates) { + Sequence src = {1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4}, + dst = {1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4}; + VerifyMatch(src, dst, 6); +} + +TEST(LCSTest, Large) { + const std::string src_str = + "GUJwrJSlkKJXxCVIAxlVgnUyOrdyRyFtlZwWMmFhYGfkFTNnhiBmClgHyrcXMVwfrRxNUfQk" + "qaoGvCbPZHAzXsaZpXHPfJxOMCUtRDmIQpfiXKbHQbhTfPqhxBDWvmTQAqwsWTLajZYtMUnf" + "hNNCfkuAXkZsaebwEbIZOxTDZsqSMUfCMoGeKJGVSNFgLTiBMbdvchHGfFRkHKcYCDjBfIcj" + "todPnvDjzQYWBcvIfVvyBzHikrwpDORaGEZLhmyztIFCLJOqeLhOzERYmVqzlsoUzruTXTXq" + "DLTxQRakOCMRrgRzCDTXfwwfDcKMBVnxRZemjcwcEsOVxwtwdBCWJycsDcZKlvrCvZaenKlv" + "vyByDQeLdxAyBnPkIMQlMQwqjUfRLybeoaOanlbFkpTPPZdHelQrIvucTHzMpWWQTbuANwvN" + "OVhCGGoIcGNDpfIsaBexlMMdHsxMGerTngmjpdPeQQJHfvKZkdYqAzrtDohqtDsaFMxQViVQ" + "YszDVgyoSHZdOXAvXkJidojLvGZOzhRajVPhWDwKuGqdaympELxHsrXAJYufdCPwJdGJfWqq" + "yvTWpcrFHOIuCEmNLnSCDsxQGRVDwyCykBJazhApfCnrOadnafvqfVuFqEXMSrYbHTfTnzbz" + "MhISyOtMUITaurCXvanCbuOXBhHyCjOhVbxnMvhlPmZBMQgEHCghtAJVMXGPNRtszVZlPxVl" + "QIPTBnPUPejlyZGPqeICyNngdQGkvKbIoWlTLBtVhMdBeUMozNlKQTIPYBeImVcMdLafuxUf" + "TIXysmcTrUTcspOSKBxhdhLwiRnREGFWJTfUKsgGOAQeYojXdrqsGjMJfiKalyoiqrgLnlij" + "CtOapoxDGVOOBalNYGzCtBlxbvaAzxipGnJpOEbmXcpeoIsAdxspKBzBDgoPVxnuRBUwmTSr" + "CRpWhxikgUYQVCwalLUIeBPRyhhsECGCXJmGDZSCIUaBwROkigzdeVPOXhgCGBEprWtNdYfL" + "tOUYJHQXxiIJgSGmWntezJFpNQoTPbRRYAGhtYvAechvBcYWocLkYFxsDAuszvQNLXdhmAHw" + "DErcjbtCdQllnKcDADVNWVezljjLrAuyGHetINMgAvJZwOEYakihYVUbZGCsHEufluLNyNHy" + "gqtSTSFFjBHiIqQejTPWybLdpWNwZrWvIWnlzUcGNQPEYHVPCbteWknjAnWrdTBeCbHUDBoK" + "aHvDStmpNRGIjvlumiZTbdZNAzUeSFnFChCsSExwXeEfDJfjyOoSBofHzJqJErvHLNyUJTjX" + "qmtgKPpMKohUPBMhtCteQFcNEpWrUVGbibMOpvBwdiWYXNissArpSasVJFgDzrqTyGkerTMX" + "gcrzFUGFZRhNdekaJeKYPogsofJaRsUQmIRyYdkrxKeMgLPpwOfSKJOqzXDoeHljTzhOwEVy" + "krOEnACFrWhufajsMitjOWdLOHHchQDddGPzxknEgdwmZepKDvRZGCuPqzeQkjOPqUBKpKLJ" + "eKieSsRXkaqxSPGajfvPKmwFWdLByEcLgvrmteazgFjmMGrLYqRRxzUOfOCokenqHVYstBHf" + "AwsWsqPTvqsRJUfGGTaYiylZMGbQqTzINhFHvdlRQvvYKBcuAHdBeKlHSxVrSsEKbcAvnIcf" + "xzdVDdwQPHMCHeZZRpGHWvKzgTGzSTbYTeOPyKvvYWmQToTpsjAtKUJUjcEHWhmdBLDTBMHJ" + "ivBXcLGtCsumNNVFyGbVviGmqHTdyBlkneibXBesKJGOUzOtIwXCPJggqBekSzNQYkALlItk" + "cbEhbdXAIKVHYpInLwxXalKZrkrpxtfuagqMGmRJnJbFQaEoYMoqPsxZpocddPXXPyvxVkaF" + "qdKISejWDhBImnEEOPDcyWTubbfVfwUztciaFJcsPLhgYVfhqlOfoNjKbmTFptFttYuyBrUI" + "zzmZypOqrjQHTGFwlHStpIwxPtMvtsEDpsmWIgwzYgwmdpbMOnfElZMYpVIcvzSWejeJcdUB" + "QUoBRUmGQVVWvEDseuozrDjgdXFScPwwsgaUPwSzScfBNrkpmEFDSZLKfNjMqvOmUtocUkbo" + "VGFEKgGLbNruwLgXHTloWDrnqymPVAtzjWPutonIsMDPeeCmTjYWAFXcyTAlBeiJTIRkZxiM" + "kLjMnAflSNJzmZkatXkYiPEMYSmzHbLKEizHbEjQOxBDzpRHiFjhedqiyMiUMvThjaRFmwll" + "aMGgwKBIKepwyoEdnuhtzJzboiNEAFKiqiWxxmkRFRoTiFWXLPAWLuzSCrajgkQhDxAQDqyM" + "VwZlhZicQLEDYYisEalesDWZAYzcvENuHUwRutIsGgsdoYwOZiURhcgdbTGWBNqhrFjvTQCj" + "VlTPNlRdRLaaqzUBBwbdtyXFkCBUYYMbmRrkFxfxbCqkgZNGyHPKLkOPnezfVTRmRQgCgHbx" + "wcZlInVOwmFePnSIbThMJosimzkhfuiqYEpwHQiemqsSDNNdbNhBLzbsPZBJZujSHJGtYKGb" + "HaAYGJZxBumsKUrATwPuqXFLfwNyImLQbchBKiJAYRZhkcrKCHXBEGYyBhBGvSqvabcRUrfq" + "AbPiMzjHAehGYjDEmxAnYLyoSFdeWVrfJUCuYZPluhXEBuyUpKaRXDKXeiCvGidpvATwMbcz" + "DZpzxrhTZYyrFORFQWTbPLCBjMKMhlRMFEiarDgGPttjmkrQVlujztMSkxXffXFNqLWOLThI" + "KBoyMHoFTEPCdUAZjLTifAdjjUehyDLEGKlRTFoLpjalziRSUjZfRYbNzhiHgTHowMMkKTwE" + "ZgnqiirMtnNpaBJqhcIVrWXPpcPWZfRpsPstHleFJDZYAsxYhOREVbFtebXTZRAIjGgWeoiN" + "qPLCCAVadqmUrjOcqIbdCTpcDRWuDVbHrZOQRPhqbyvOWwxAWJphjLiDgoAybcjzgfVktPlj" + "kNBCjelpuQfnYsiTgPpCNKYtOrxGaLEEtAuLdGdDsONHNhSn"; + const std::string dst_str = + "KzitfifORCbGhfNEbnbObUdFLLaAsLOpMkOeKupjCoatzqfHBkNJfSgqSMYouswfNMnoQngK" + "jWwyPKmEnoZWyPBUdQRmKUNudUclueKXKQefUdXWUyyqtumzsFKznrLVLwfvPZpLChNYrrHK" + "AtpfOuVHiUKyeRCrktJAhkyFKmPWrASEMvBLNOzuGlvinZjvZUUXazNEkyMPiOLdqXvCIroC" + "MeWsvjHShlLhDwLZrVlpYBnDJmILcsNFDSoaLWOKNNkNGBgNBvVjPCJXAuKfsrKZhYcdEpxK" + "UihiRkYvMiLyOUvaqBMklLDwEhvQBfCXHSRoqsLsSCzLZQhIYMhBapvHaPbDoRrHoJXZsNXc" + "rxZYCrOMIzYcVPwDCFiHBFnPNTTeAeKEMGeVUeCaAeuWZmngyPWlQBcgWumSUIfbhjVYdnpV" + "hRSJXrIoFZubBXfNOMhilAkVPixrhILZKgDoFTvytPFPfBLMnbhSOBmLWCbJsLQxrCrMAlOw" + "RmfSQyGhrjhzYVqFSBHeoQBagFwyxIjcHFZngntpVHbSwqhwHeMnWSsISPljTxSNXfCxLebW" + "GhMdlphtJbdvhEcjNpwPCFqhdquxCyOxkjsDUPNgjpDcpIMhMwMclNhfESTrroJaoyeGQclV" + "gonnhuQRmXcBwcsWeLqjNngZOlyMyfeQBwnwMVJEvGqknDyzSApniRTPgJpFoDkJJhXQFuFB" + "VqhuEPMRGCeTDOSEFmXeIHOnDxaJacvnmORwVpmrRhGjDpUCkuODNPdZMdupYExDEDnDLdNF" + "iObKBaVWpGVMKdgNLgsNxcpypBPPKKoaajeSGPZQJWSOKrkLjiFexYVmUGxJnbTNsCXXLfZp" + "jfxQAEVYvqKehBzMsVHVGWmTshWFAoCNDkNppzzjHBZWckrzSTANICioCJSpLwPwQvtXVxst" + "nTRBAboPFREEUFazibpFesCsjzUOnECwoPCOFiwGORlIZVLpUkJyhYXCENmzTBLVigOFuCWO" + "IiXBYmiMtsxnUdoqSTTGyEFFrQsNAjcDdOKDtHwlANWoUVwiJCMCQFILdGqzEePuSXFbOEOz" + "dLlEnTJbKRSTfAFToOZNtDXTfFgvQiefAKbSUWUXFcpCjRYCBNXCCcLMjjuUDXErpiNsRuIx" + "mgHsrObTEXcnmjdqxTGhTjTeYizNnkrJRhNQIqDXmZMwArBccnixpcuiGOOexjgkpcEyGAnz" + "UbgiBfflTUyJfZeFFLrZVueFkSRosebnnwAnakIrywTGByhQKWvmNQJsWQezqLhHQzXnEpeD" + "rFRTSQSpVxPzSeEzfWYzfpcenxsUyzOMLxhNEhfcuprDtqubsXehuqKqZlLQeSclvoGjuKJK" + "XoWrazsgjXXnkWHdqFESZdMGDYldyYdbpSZcgBPgEKLWZHfBirNPLUadmajYkiEzmGuWGELB" + "WLiSrMdaGSbptKmgYVqMGcQaaATStiZYteGAPxSEBHuAzzjlRHYsrdDkaGNXmzRGoalJMiCC" + "GMtWSDMhgvRSEgKnywbRgnqWXFlwrhXbbvcgLGtWSuKQBiqIlWkfPMozOTWgVoLHavDJGRYI" + "YerrmZnTMtuuxmZALWakfzUbksTwoetqkOiRPGqGZepcVXHoZyOaaaijjZWQLlIhYwiQNbfc" + "KCwhhFaMQBoaCnOecJEdKzdsMPFEYQuJNPYiiNtsYxaWBRuWjlLqGokHMNtyTQfSJKbgGdol" + "fWlOZdupouQMfUWXIYHzyJHefMDnqxxasDxtgArvDqtwjDBaVEMACPkLFpiDOoKCHqkWVizh" + "lKqbOHpsPKkhjRQRNGYRYEfxtBjYvlCvHBNUwVuIwDJYMqHxEFtwdLqYWvjdOfQmNiviDfUq" + "pbucbNwjNQfMYgwUuPnQWIPOlqHcbjtuDXvTzLtkdBQanJbrmLSyFqSapZCSPMDOrxWVYzyO" + "lwDTTJFmKxoyfPunadkHcrcSQaQsAbrQtbhqwSTXGTPURYTCbNozjAVwbmcyVxIbZudBZWYm" + "rnSDyelGCRRWYtrUxvOVWlTLHHdYuAmVMGnGbHscbjmjmAzmYLaCxNNwhmMYdExKvySxuYpE" + "rVGwfqMngBCHnZodotNaNJZiNRFWubuPDfiywXPiyVWoQMeOlSuWmpilLTIFOvfpjmJTgrWa" + "dgoxYeyPyOaglOvZVGdFOBSeqEcGXBwjoeUAXqkpvOxEpSXhmklKZydTvRVYVvfQdRNNDkCT" + "dLNfcZCFQbZORdcDOhwotoyccrSbWvlqYMoiAYeEpDzZTvkamapzZMmCpEutZFCcHBWGIIkr" + "urwDNHrobaErPpclyEegLJDtkfUWSNWZosWSbBGAHIvJsFNUlJXbnkSVycLkOVQVcNcUtiBy" + "djLDIFsycbPBEWaMvCbntNtJlOeCttvXypGnHAQFnFSiXFWWqonWuVIKmVPpKXuJtFguXCWC" + "rNExYYvxLGEmuZJLJDjHgjlQyOzeieCpizJxkrdqKCgomyEkvsyVYSsLeyLvOZQrrgEJgRFK" + "CjYtoOfluNrLdRMTRkQXmAiMRFwloYECpXCReAMxOkNiwCtutsrqWoMHsrogRqPoUCueonvW" + "MTwmkAkajfGJkhnQidwpwIMEttQkzIMOPvvyWZHpqkMHWlNTeSKibfRfwDyxveKENZhtlPwP" + "dfAjwegjRcavtFnkkTNVYdCdCrgdUvzsIcqmUjwGmVvuuQvjVrWWIDBmAzQtiZPYvCOEWjce" + "rWzeqVKeiYTJBOedmQCVidOgUIEjfRnbGvUbctYxfRybJkdmeAkLZQMRMGPOnsPbFswXAoCK" + "IxWGwohoPpEJxslbqHFKSwknxTmrDCITRZWEDkGQeucPxHBdYkduwbYhKnoxCKhgjBFiFawC" + "QtgTDldTQmlOsBiGLquMjuecAbrUJJvNtXbFNGjWxaZPimSRXUJWgRbydpsczOqSFIeEtuKA" + "ZpRhmLtPdVNKdSDQZeeImUFmUwXApRTUNHItyvFyJtNtn"; + + Sequence src; + Sequence dst; + + src.reserve(src_str.length()); + dst.reserve(dst_str.length()); + + for (char c : src_str) { + src.push_back(c); + } + for (char c : dst_str) { + dst.push_back(c); + } + + VerifyMatch(src, dst, 723); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/ext_inst.cldebug100_test.cpp b/test/ext_inst.cldebug100_test.cpp index 4f1e1067..0bbdd3a9 100644 --- a/test/ext_inst.cldebug100_test.cpp +++ b/test/ext_inst.cldebug100_test.cpp @@ -581,7 +581,7 @@ INSTANTIATE_TEST_SUITE_P( OpenCLDebugInfo100DebugSource, ExtInstCLDebugInfo100RoundTripTest, ::testing::ValuesIn(std::vector({ // TODO(dneto): Should this be a list of sourc texts, - // to accomodate length limits? + // to accommodate length limits? CASE_I(Source), CASE_II(Source), }))); diff --git a/test/ext_inst.opencl_test.cpp b/test/ext_inst.opencl_test.cpp index 7547d922..d80a9bd7 100644 --- a/test/ext_inst.opencl_test.cpp +++ b/test/ext_inst.opencl_test.cpp @@ -233,7 +233,7 @@ INSTANTIATE_TEST_SUITE_P( CASE3(UMad_hi, u_mad_hi), // enum value 204 }))); -// OpenCL.std: 2.3 Common instrucitons +// OpenCL.std: 2.3 Common instructions INSTANTIATE_TEST_SUITE_P( OpenCLCommon, ExtInstOpenCLStdRoundTripTest, ::testing::ValuesIn(std::vector({ diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt index 56fbed81..56af0b9d 100644 --- a/test/fuzz/CMakeLists.txt +++ b/test/fuzz/CMakeLists.txt @@ -31,6 +31,7 @@ if (${SPIRV_BUILD_FUZZER}) fuzzer_pass_construct_composites_test.cpp fuzzer_pass_donate_modules_test.cpp fuzzer_pass_outline_functions_test.cpp + fuzzerutil_test.cpp instruction_descriptor_test.cpp fuzzer_pass_test.cpp replayer_test.cpp @@ -114,11 +115,14 @@ if (${SPIRV_BUILD_FUZZER}) transformation_store_test.cpp transformation_swap_commutable_operands_test.cpp transformation_swap_conditional_branch_operands_test.cpp + transformation_swap_function_variables_test.cpp + transformation_swap_two_functions_test.cpp transformation_toggle_access_chain_instruction_test.cpp transformation_record_synonymous_constants_test.cpp transformation_vector_shuffle_test.cpp transformation_wrap_early_terminator_in_function_test.cpp transformation_wrap_region_in_selection_test.cpp + transformation_wrap_vector_synonym_test.cpp uniform_buffer_element_descriptor_test.cpp) if (${SPIRV_ENABLE_LONG_FUZZER_TESTS}) diff --git a/test/fuzz/fuzz_test_util.cpp b/test/fuzz/fuzz_test_util.cpp index 28d3f892..93c9c584 100644 --- a/test/fuzz/fuzz_test_util.cpp +++ b/test/fuzz/fuzz_test_util.cpp @@ -149,7 +149,7 @@ void DumpTransformationsJson( json_options.add_whitespace = true; auto json_generation_status = google::protobuf::util::MessageToJsonString( transformations, &json_string, json_options); - if (json_generation_status == google::protobuf::util::Status::OK) { + if (json_generation_status.ok()) { std::ofstream transformations_json_file(filename); transformations_json_file << json_string; transformations_json_file.close(); @@ -160,13 +160,19 @@ void ApplyAndCheckFreshIds( const Transformation& transformation, opt::IRContext* ir_context, TransformationContext* transformation_context, const std::unordered_set& issued_overflow_ids) { + // To ensure that we cover all ToMessage and message-based constructor methods + // in our tests, we turn this into a message and back into a transformation, + // and use the reconstructed transformation in the rest of the function. + auto message = transformation.ToMessage(); + auto reconstructed_transformation = Transformation::FromMessage(message); + opt::analysis::DefUseManager::IdToDefMap before_transformation = ir_context->get_def_use_mgr()->id_to_defs(); - transformation.Apply(ir_context, transformation_context); + reconstructed_transformation->Apply(ir_context, transformation_context); opt::analysis::DefUseManager::IdToDefMap after_transformation = ir_context->get_def_use_mgr()->id_to_defs(); std::unordered_set fresh_ids_for_transformation = - transformation.GetFreshIds(); + reconstructed_transformation->GetFreshIds(); for (auto& entry : after_transformation) { uint32_t id = entry.first; bool introduced_by_transformation_message = diff --git a/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp b/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp index 734f47af..1045f8a1 100644 --- a/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp +++ b/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp @@ -134,7 +134,7 @@ TEST(FuzzerPassAddOpPhiSynonymsTest, HelperFunctions) { FuzzerPassAddOpPhiSynonyms fuzzer_pass(context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence); + &transformation_sequence, false); SetUpIdSynonyms(transformation_context.GetFactManager()); diff --git a/test/fuzz/fuzzer_pass_construct_composites_test.cpp b/test/fuzz/fuzzer_pass_construct_composites_test.cpp index a02176b2..a858e4ce 100644 --- a/test/fuzz/fuzzer_pass_construct_composites_test.cpp +++ b/test/fuzz/fuzzer_pass_construct_composites_test.cpp @@ -92,7 +92,7 @@ TEST(FuzzerPassConstructCompositesTest, IsomorphicStructs) { FuzzerPassConstructComposites fuzzer_pass( context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence); + &transformation_sequence, false); fuzzer_pass.Apply(); @@ -173,7 +173,7 @@ TEST(FuzzerPassConstructCompositesTest, IsomorphicArrays) { FuzzerPassConstructComposites fuzzer_pass( context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence); + &transformation_sequence, false); fuzzer_pass.Apply(); diff --git a/test/fuzz/fuzzer_pass_donate_modules_test.cpp b/test/fuzz/fuzzer_pass_donate_modules_test.cpp index f11885d4..fe8e671d 100644 --- a/test/fuzz/fuzzer_pass_donate_modules_test.cpp +++ b/test/fuzz/fuzzer_pass_donate_modules_test.cpp @@ -210,7 +210,7 @@ TEST(FuzzerPassDonateModulesTest, BasicDonation) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -291,7 +291,7 @@ TEST(FuzzerPassDonateModulesTest, DonationWithUniforms) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -422,7 +422,7 @@ TEST(FuzzerPassDonateModulesTest, DonationWithInputAndOutputVariables) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -517,7 +517,7 @@ TEST(FuzzerPassDonateModulesTest, DonateFunctionTypeWithDifferentPointers) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -531,6 +531,7 @@ TEST(FuzzerPassDonateModulesTest, DonateOpConstantNull) { std::string recipient_shader = R"( OpCapability Shader OpCapability ImageQuery + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -548,6 +549,7 @@ TEST(FuzzerPassDonateModulesTest, DonateOpConstantNull) { std::string donor_shader = R"( OpCapability Shader OpCapability ImageQuery + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -587,7 +589,7 @@ TEST(FuzzerPassDonateModulesTest, DonateOpConstantNull) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -715,7 +717,7 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImages) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -811,7 +813,7 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesSampler) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -943,7 +945,7 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImageStructField) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -1079,7 +1081,7 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImageFunctionParameter) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -1161,7 +1163,7 @@ TEST(FuzzerPassDonateModulesTest, DonateShaderWithImageStorageClass) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), true); @@ -1248,7 +1250,7 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithRuntimeArray) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -1352,7 +1354,7 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithRuntimeArrayLivesafe) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), true); @@ -1424,7 +1426,7 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithWorkgroupVariables) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), true); @@ -1534,7 +1536,7 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithAtomics) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), true); @@ -1718,7 +1720,7 @@ TEST(FuzzerPassDonateModulesTest, Miscellaneous1) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -1790,7 +1792,7 @@ TEST(FuzzerPassDonateModulesTest, OpSpecConstantInstructions) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -1947,7 +1949,7 @@ TEST(FuzzerPassDonateModulesTest, DonationSupportsOpTypeRuntimeArray) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -2020,7 +2022,7 @@ TEST(FuzzerPassDonateModulesTest, HandlesCapabilities) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); ASSERT_TRUE(donor_context->get_feature_mgr()->HasCapability( SpvCapabilityVariablePointersStorageBuffer)); @@ -2253,7 +2255,7 @@ TEST(FuzzerPassDonateModulesTest, HandlesOpPhisInMergeBlock) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), true); diff --git a/test/fuzz/fuzzer_pass_outline_functions_test.cpp b/test/fuzz/fuzzer_pass_outline_functions_test.cpp index 0d2c5bf7..a088e17b 100644 --- a/test/fuzz/fuzzer_pass_outline_functions_test.cpp +++ b/test/fuzz/fuzzer_pass_outline_functions_test.cpp @@ -130,7 +130,7 @@ TEST(FuzzerPassOutlineFunctionsTest, EntryIsAlreadySuitable) { FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence); + &transformation_sequence, false); // Block 28 auto suitable_entry_block = @@ -173,7 +173,7 @@ TEST(FuzzerPassOutlineFunctionsTest, EntryHasOpVariable) { FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence); + &transformation_sequence, false); // Block 20 auto suitable_entry_block = @@ -297,7 +297,7 @@ TEST(FuzzerPassOutlineFunctionsTest, EntryBlockIsHeader) { FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence); + &transformation_sequence, false); // Block 21 auto suitable_entry_block = @@ -464,7 +464,7 @@ TEST(FuzzerPassOutlineFunctionsTest, ExitBlock) { FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence); + &transformation_sequence, false); // Block 39 is not a merge block, so it is already suitable. auto suitable_exit_block = fuzzer_pass.MaybeGetExitBlockSuitableForOutlining( diff --git a/test/fuzz/fuzzer_pass_test.cpp b/test/fuzz/fuzzer_pass_test.cpp index b035de74..31b8582a 100644 --- a/test/fuzz/fuzzer_pass_test.cpp +++ b/test/fuzz/fuzzer_pass_test.cpp @@ -29,7 +29,7 @@ class FuzzerPassMock : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, false) {} ~FuzzerPassMock() override = default; diff --git a/test/fuzz/fuzzer_replayer_test.cpp b/test/fuzz/fuzzer_replayer_test.cpp index 6dc7ffb1..2a22e6a5 100644 --- a/test/fuzz/fuzzer_replayer_test.cpp +++ b/test/fuzz/fuzzer_replayer_test.cpp @@ -1596,6 +1596,23 @@ const std::string kTestShader6 = R"( OpFunctionEnd )"; +// A virtually empty piece of SPIR-V. + +const std::string kTestShader7 = 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 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + void AddConstantUniformFact(protobufs::FactSequence* facts, uint32_t descriptor_set, uint32_t binding, std::vector&& indices, uint32_t value) { @@ -1668,7 +1685,7 @@ void RunFuzzerAndReplayer(const std::string& shader, Fuzzer fuzzer(std::move(ir_context), std::move(transformation_context), std::move(fuzzer_context), kConsoleMessageConsumer, donor_suppliers, enable_all_passes, - strategies[strategy_index], true, validator_options); + strategies[strategy_index], true, validator_options, false); auto fuzzer_result = fuzzer.Run(0); // Cycle the repeated pass strategy so that we try a different one next time @@ -1760,6 +1777,13 @@ TEST(FuzzerReplayerTest, Miscellaneous6) { kNumFuzzerRuns); } +TEST(FuzzerReplayerTest, Miscellaneous7) { + // Do some fuzzer runs, starting from an initial seed of 1 (seed value chosen + // arbitrarily). + RunFuzzerAndReplayer(kTestShader7, protobufs::FactSequence(), 1, + kNumFuzzerRuns); +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/test/fuzz/fuzzer_shrinker_test.cpp b/test/fuzz/fuzzer_shrinker_test.cpp index e7921169..acee03c7 100644 --- a/test/fuzz/fuzzer_shrinker_test.cpp +++ b/test/fuzz/fuzzer_shrinker_test.cpp @@ -1068,7 +1068,7 @@ void RunFuzzerAndShrinker(const std::string& shader, Fuzzer fuzzer(std::move(ir_context), std::move(transformation_context), std::move(fuzzer_context), kConsoleMessageConsumer, donor_suppliers, enable_all_passes, repeated_pass_strategy, - true, validator_options); + true, validator_options, false); auto fuzzer_result = fuzzer.Run(0); ASSERT_NE(Fuzzer::Status::kFuzzerPassLedToInvalidModule, fuzzer_result.status); diff --git a/test/fuzz/fuzzerutil_test.cpp b/test/fuzz/fuzzerutil_test.cpp new file mode 100644 index 00000000..1286d38d --- /dev/null +++ b/test/fuzz/fuzzerutil_test.cpp @@ -0,0 +1,1807 @@ +// Copyright (c) 2021 Shiyu Liu +// +// 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 "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(FuzzerUtilMaybeFindBlockTest, 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 %8 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %10 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpBranch %11 + %11 = OpLabel + OpStore %8 %9 + OpBranch %12 + %12 = OpLabel + OpStore %8 %10 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Only blocks with id 11 and 12 can be found. + // Should return nullptr when id is not a label or id was not found. + uint32_t block_id1 = 11; + uint32_t block_id2 = 12; + uint32_t block_id3 = 13; + uint32_t block_id4 = 8; + + opt::IRContext* ir_context = context.get(); + // Block with id 11 should be found. + ASSERT_TRUE(fuzzerutil::MaybeFindBlock(ir_context, block_id1) != nullptr); + // Block with id 12 should be found. + ASSERT_TRUE(fuzzerutil::MaybeFindBlock(ir_context, block_id2) != nullptr); + // Block with id 13 cannot be found. + ASSERT_FALSE(fuzzerutil::MaybeFindBlock(ir_context, block_id3) != nullptr); + // Block with id 8 exists but don't not of type OpLabel. + ASSERT_FALSE(fuzzerutil::MaybeFindBlock(ir_context, block_id4) != nullptr); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetBoolConstantTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %36 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "b1" + OpName %10 "b2" + OpName %12 "b3" + OpName %13 "b4" + OpName %16 "f1" + OpName %18 "f2" + OpName %20 "cf1" + OpName %22 "cf2" + OpName %26 "i1" + OpName %28 "i2" + OpName %30 "ci1" + OpName %32 "ci2" + OpName %36 "value" + OpDecorate %26 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %36 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypePointer Function %6 + %9 = OpConstantTrue %6 + %11 = OpConstantFalse %6 + %14 = OpTypeFloat 32 + %15 = OpTypePointer Function %14 + %17 = OpConstant %14 1.23000002 + %19 = OpConstant %14 1.11000001 + %21 = OpConstant %14 2 + %23 = OpConstant %14 3.29999995 + %24 = OpTypeInt 32 1 + %25 = OpTypePointer Function %24 + %27 = OpConstant %24 1 + %29 = OpConstant %24 100 + %31 = OpConstant %24 123 + %33 = OpConstant %24 1111 + %35 = OpTypePointer Input %14 + %36 = OpVariable %35 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + %13 = OpVariable %7 Function + %16 = OpVariable %15 Function + %18 = OpVariable %15 Function + %20 = OpVariable %15 Function + %22 = OpVariable %15 Function + %26 = OpVariable %25 Function + %28 = OpVariable %25 Function + %30 = OpVariable %25 Function + %32 = OpVariable %25 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %12 %9 + OpStore %13 %11 + OpStore %16 %17 + OpStore %18 %19 + OpStore %20 %21 + OpStore %22 %23 + OpStore %26 %27 + OpStore %28 %29 + OpStore %30 %31 + OpStore %32 %33 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + opt::IRContext* ir_context = context.get(); + // A bool constant with value false exists and the id is 11. + ASSERT_EQ(11, fuzzerutil::MaybeGetBoolConstant( + ir_context, transformation_context, false, false)); + // A bool constant with value true exists and the id is 9. + ASSERT_EQ(9, fuzzerutil::MaybeGetBoolConstant( + ir_context, transformation_context, true, false)); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetBoolTypeTest) { + std::string 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 + %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 std::unique_ptr context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + opt::IRContext* ir_context = context.get(); + // A bool type with result id of 34 exists. + ASSERT_TRUE(fuzzerutil::MaybeGetBoolType(ir_context)); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetCompositeConstantTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %54 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "b1" + OpName %10 "b2" + OpName %12 "b3" + OpName %13 "b4" + OpName %16 "f1" + OpName %18 "f2" + OpName %22 "zc" + OpName %24 "i1" + OpName %28 "i2" + OpName %30 "i3" + OpName %32 "i4" + OpName %37 "f_arr" + OpName %47 "i_arr" + OpName %54 "value" + OpDecorate %22 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %54 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypePointer Function %6 + %9 = OpConstantTrue %6 + %11 = OpConstantFalse %6 + %14 = OpTypeFloat 32 + %15 = OpTypePointer Function %14 + %17 = OpConstant %14 1.23000002 + %19 = OpConstant %14 1.11000001 + %20 = OpTypeInt 32 1 + %21 = OpTypePointer Function %20 + %23 = OpConstant %20 0 + %25 = OpConstant %20 1 + %26 = OpTypeInt 32 0 + %27 = OpTypePointer Function %26 + %29 = OpConstant %26 100 + %31 = OpConstant %20 -1 + %33 = OpConstant %20 -99 + %34 = OpConstant %26 5 + %35 = OpTypeArray %14 %34 + %36 = OpTypePointer Function %35 + %38 = OpConstant %14 5.5 + %39 = OpConstant %14 4.4000001 + %40 = OpConstant %14 3.29999995 + %41 = OpConstant %14 2.20000005 + %42 = OpConstant %14 1.10000002 + %43 = OpConstantComposite %35 %38 %39 %40 %41 %42 + %44 = OpConstant %26 3 + %45 = OpTypeArray %20 %44 + %46 = OpTypePointer Function %45 + %48 = OpConstant %20 3 + %49 = OpConstant %20 7 + %50 = OpConstant %20 9 + %51 = OpConstantComposite %45 %48 %49 %50 + %53 = OpTypePointer Input %14 + %54 = OpVariable %53 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + %13 = OpVariable %7 Function + %16 = OpVariable %15 Function + %18 = OpVariable %15 Function + %22 = OpVariable %21 Function + %24 = OpVariable %21 Function + %28 = OpVariable %27 Function + %30 = OpVariable %21 Function + %32 = OpVariable %21 Function + %37 = OpVariable %36 Function + %47 = OpVariable %46 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %12 %9 + OpStore %13 %11 + OpStore %16 %17 + OpStore %18 %19 + OpStore %22 %23 + OpStore %24 %25 + OpStore %28 %29 + OpStore %30 %31 + OpStore %32 %33 + OpStore %37 %43 + OpStore %47 %51 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + opt::IRContext* ir_context = context.get(); + + // %43 = OpConstantComposite %35 %38 %39 %40 %41 %42 + // %51 = OpConstantComposite %45 %48 %49 %50 + // This should pass as a float array with 5 elements exist and its id is 43. + ASSERT_EQ(43, fuzzerutil::MaybeGetCompositeConstant( + ir_context, transformation_context, {38, 39, 40, 41, 42}, + 35, false)); + // This should pass as an int array with 3 elements exist and its id is 51. + ASSERT_EQ(51, + fuzzerutil::MaybeGetCompositeConstant( + ir_context, transformation_context, {48, 49, 50}, 45, false)); + // An int array with 2 elements does not exist. + ASSERT_EQ(0, fuzzerutil::MaybeGetCompositeConstant( + ir_context, transformation_context, {48, 49}, 45, false)); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetFloatConstantTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %36 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "b1" + OpName %10 "b2" + OpName %12 "b3" + OpName %13 "b4" + OpName %16 "f1" + OpName %18 "f2" + OpName %20 "cf1" + OpName %22 "cf2" + OpName %26 "i1" + OpName %28 "i2" + OpName %30 "ci1" + OpName %32 "ci2" + OpName %36 "value" + OpDecorate %26 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %36 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypePointer Function %6 + %9 = OpConstantTrue %6 + %11 = OpConstantFalse %6 + %14 = OpTypeFloat 32 + %15 = OpTypePointer Function %14 + %17 = OpConstant %14 1.23000002 + %19 = OpConstant %14 1.11000001 + %21 = OpConstant %14 2 + %23 = OpConstant %14 3.29999995 + %24 = OpTypeInt 32 1 + %25 = OpTypePointer Function %24 + %27 = OpConstant %24 1 + %29 = OpConstant %24 100 + %31 = OpConstant %24 123 + %33 = OpConstant %24 1111 + %35 = OpTypePointer Input %14 + %36 = OpVariable %35 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + %13 = OpVariable %7 Function + %16 = OpVariable %15 Function + %18 = OpVariable %15 Function + %20 = OpVariable %15 Function + %22 = OpVariable %15 Function + %26 = OpVariable %25 Function + %28 = OpVariable %25 Function + %30 = OpVariable %25 Function + %32 = OpVariable %25 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %12 %9 + OpStore %13 %11 + OpStore %16 %17 + OpStore %18 %19 + OpStore %20 %21 + OpStore %22 %23 + OpStore %26 %27 + OpStore %28 %29 + OpStore %30 %31 + OpStore %32 %33 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + opt::IRContext* ir_context = context.get(); + + uint32_t word1 = fuzzerutil::FloatToWord(2); + uint32_t word2 = fuzzerutil::FloatToWord(1.23f); + + // A 32 bit float constant of value 2 exists and its id is 21. + ASSERT_EQ(21, fuzzerutil::MaybeGetFloatConstant( + ir_context, transformation_context, + std::vector{word1}, 32, false)); + // A 32 bit float constant of value 1.23 exists and its id is 17. + ASSERT_EQ(17, fuzzerutil::MaybeGetFloatConstant( + ir_context, transformation_context, + std::vector{word2}, 32, false)); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetFloatTypeTest) { + std::string 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 + %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 std::unique_ptr context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + opt::IRContext* ir_context = context.get(); + // A float type with width = 32 and result id of 7 exists. + ASSERT_EQ(7, fuzzerutil::MaybeGetFloatType(ir_context, 32)); + + // A float int type with width = 32 exists, but the id should be 7. + ASSERT_NE(5, fuzzerutil::MaybeGetFloatType(ir_context, 32)); + + // A float type with width 30 does not exist. + ASSERT_EQ(0, fuzzerutil::MaybeGetFloatType(ir_context, 30)); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetIntegerConstantFromValueAndTypeTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %36 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "b1" + OpName %10 "b2" + OpName %12 "b3" + OpName %13 "b4" + OpName %16 "f1" + OpName %18 "f2" + OpName %22 "zc" + OpName %24 "i1" + OpName %28 "i2" + OpName %30 "i3" + OpName %32 "i4" + OpName %36 "value" + OpDecorate %22 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %36 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypePointer Function %6 + %9 = OpConstantTrue %6 + %11 = OpConstantFalse %6 + %14 = OpTypeFloat 32 + %15 = OpTypePointer Function %14 + %17 = OpConstant %14 1.23000002 + %19 = OpConstant %14 1.11000001 + %20 = OpTypeInt 32 1 + %21 = OpTypePointer Function %20 + %23 = OpConstant %20 0 + %25 = OpConstant %20 1 + %26 = OpTypeInt 32 0 + %27 = OpTypePointer Function %26 + %29 = OpConstant %26 100 + %31 = OpConstant %20 -1 + %33 = OpConstant %20 -99 + %35 = OpTypePointer Input %14 + %36 = OpVariable %35 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + %13 = OpVariable %7 Function + %16 = OpVariable %15 Function + %18 = OpVariable %15 Function + %22 = OpVariable %21 Function + %24 = OpVariable %21 Function + %28 = OpVariable %27 Function + %30 = OpVariable %21 Function + %32 = OpVariable %21 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %12 %9 + OpStore %13 %11 + OpStore %16 %17 + OpStore %18 %19 + OpStore %22 %23 + OpStore %24 %25 + OpStore %28 %29 + OpStore %30 %31 + OpStore %32 %33 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + opt::IRContext* ir_context = context.get(); + + // A 32 bit signed int constant (with int type id 20) with value 1 exists and + // the id is 25. + ASSERT_EQ(25, fuzzerutil::MaybeGetIntegerConstantFromValueAndType(ir_context, + 1, 20)); + // A 32 bit unsigned int constant (with int type id 0) with value 100 exists + // and the id is 29. + ASSERT_EQ(29, fuzzerutil::MaybeGetIntegerConstantFromValueAndType(ir_context, + 100, 26)); + // A 32 bit unsigned int constant with value 50 does not exist. + ASSERT_EQ(0, fuzzerutil::MaybeGetIntegerConstantFromValueAndType(ir_context, + 50, 26)); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetIntegerConstantTest) { + std::string shader = R"( +OpCapability Shader + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %36 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "b1" + OpName %10 "b2" + OpName %12 "b3" + OpName %13 "b4" + OpName %16 "f1" + OpName %18 "f2" + OpName %22 "zc" + OpName %24 "i1" + OpName %28 "i2" + OpName %30 "i3" + OpName %32 "i4" + OpName %36 "value" + OpDecorate %22 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %36 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypePointer Function %6 + %9 = OpConstantTrue %6 + %11 = OpConstantFalse %6 + %14 = OpTypeFloat 32 + %15 = OpTypePointer Function %14 + %17 = OpConstant %14 1.23000002 + %19 = OpConstant %14 1.11000001 + %20 = OpTypeInt 32 1 + %21 = OpTypePointer Function %20 + %23 = OpConstant %20 0 + %25 = OpConstant %20 1 + %26 = OpTypeInt 32 0 + %27 = OpTypePointer Function %26 + %29 = OpConstant %26 100 + %31 = OpConstant %20 -1 + %33 = OpConstant %20 -99 + %35 = OpTypePointer Input %14 + %36 = OpVariable %35 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + %13 = OpVariable %7 Function + %16 = OpVariable %15 Function + %18 = OpVariable %15 Function + %22 = OpVariable %21 Function + %24 = OpVariable %21 Function + %28 = OpVariable %27 Function + %30 = OpVariable %21 Function + %32 = OpVariable %21 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %12 %9 + OpStore %13 %11 + OpStore %16 %17 + OpStore %18 %19 + OpStore %22 %23 + OpStore %24 %25 + OpStore %28 %29 + OpStore %30 %31 + OpStore %32 %33 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + opt::IRContext* ir_context = context.get(); + + // A 32 bit unsigned int constant with value 1 exists and the id is 25. + ASSERT_EQ(25, fuzzerutil::MaybeGetIntegerConstant( + ir_context, transformation_context, + std::vector{1}, 32, true, false)); + // A 32 bit unsigned int constant with value 100 exists and the id is 29. + ASSERT_EQ(29, fuzzerutil::MaybeGetIntegerConstant( + ir_context, transformation_context, + std::vector{100}, 32, false, false)); + // A 32 bit signed int constant with value 99 doesn't not exist and should + // return 0. + ASSERT_EQ(0, fuzzerutil::MaybeGetIntegerConstant( + ir_context, transformation_context, + std::vector{99}, 32, true, false)); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetIntegerTypeTest) { + std::string 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 + %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 std::unique_ptr context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + opt::IRContext* ir_context = context.get(); + + // A signed int type with width = 32 and result id of 6 exists. + ASSERT_EQ(6, fuzzerutil::MaybeGetIntegerType(ir_context, 32, true)); + + // A signed int type with width = 32 exists, but the id should be 6. + ASSERT_FALSE(fuzzerutil::MaybeGetIntegerType(ir_context, 32, true) == 5); + + // A int type with width = 32 and result id of 6 exists, but it should be a + // signed int. + ASSERT_EQ(0, fuzzerutil::MaybeGetIntegerType(ir_context, 32, false)); + // A signed int type with width 30 does not exist. + ASSERT_EQ(0, fuzzerutil::MaybeGetIntegerType(ir_context, 30, true)); + // An unsigned int type with width 22 does not exist. + ASSERT_EQ(0, fuzzerutil::MaybeGetIntegerType(ir_context, 22, false)); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetPointerTypeTest) { + std::string 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 + %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 std::unique_ptr context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + opt::IRContext* ir_context = context.get(); + auto private_storage_class = SpvStorageClassPrivate; + auto function_storage_class = SpvStorageClassFunction; + auto input_storage_class = SpvStorageClassInput; + + // A valid pointer must have the correct |pointee_type_id| and |storageClass|. + // A function type pointer with id = 9 and pointee type id 8 should be found. + ASSERT_EQ(9, fuzzerutil::MaybeGetPointerType(ir_context, 8, + function_storage_class)); + // A function type pointer with id = 15 and pointee type id 6 should be found. + ASSERT_EQ(15, fuzzerutil::MaybeGetPointerType(ir_context, 6, + function_storage_class)); + // A function type pointer with id = 25 and pointee type id 7 should be found. + ASSERT_EQ(25, fuzzerutil::MaybeGetPointerType(ir_context, 7, + function_storage_class)); + + // A private type pointer with id=51 and pointee type id 6 should be found. + ASSERT_EQ(51, fuzzerutil::MaybeGetPointerType(ir_context, 6, + private_storage_class)); + // A function pointer with id=50 and pointee type id 7 should be found. + ASSERT_EQ(50, fuzzerutil::MaybeGetPointerType(ir_context, 7, + private_storage_class)); + + // A input type pointer with id=91 and pointee type id 90 should be found. + ASSERT_EQ( + 91, fuzzerutil::MaybeGetPointerType(ir_context, 90, input_storage_class)); + + // A pointer with id=91 and pointee type 90 exists, but the type should be + // input. + ASSERT_EQ(0, fuzzerutil::MaybeGetPointerType(ir_context, 90, + function_storage_class)); + // A input type pointer with id=91 exists but the pointee id should be 90. + ASSERT_EQ( + 0, fuzzerutil::MaybeGetPointerType(ir_context, 89, input_storage_class)); + // A input type pointer with pointee id 90 exists but result id of the pointer + // should be 91. + ASSERT_NE( + 58, fuzzerutil::MaybeGetPointerType(ir_context, 90, input_storage_class)); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetScalarConstantTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %56 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "b1" + OpName %10 "b2" + OpName %12 "b3" + OpName %13 "b4" + OpName %16 "f1" + OpName %18 "f2" + OpName %22 "zc" + OpName %24 "i1" + OpName %28 "i2" + OpName %30 "i" + OpName %32 "i3" + OpName %34 "i4" + OpName %39 "f_arr" + OpName %49 "i_arr" + OpName %56 "value" + OpDecorate %22 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %49 RelaxedPrecision + OpDecorate %56 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypePointer Function %6 + %9 = OpConstantTrue %6 + %11 = OpConstantFalse %6 + %14 = OpTypeFloat 32 + %15 = OpTypePointer Function %14 + %17 = OpConstant %14 1.23000002 + %19 = OpConstant %14 1.11000001 + %20 = OpTypeInt 32 1 + %21 = OpTypePointer Function %20 + %23 = OpConstant %20 0 + %25 = OpConstant %20 1 + %26 = OpTypeInt 32 0 + %27 = OpTypePointer Function %26 + %29 = OpConstant %26 100 + %31 = OpConstant %26 0 + %33 = OpConstant %20 -1 + %35 = OpConstant %20 -99 + %36 = OpConstant %26 5 + %37 = OpTypeArray %14 %36 + %38 = OpTypePointer Function %37 + %40 = OpConstant %14 5.5 + %41 = OpConstant %14 4.4000001 + %42 = OpConstant %14 3.29999995 + %43 = OpConstant %14 2.20000005 + %44 = OpConstant %14 1.10000002 + %45 = OpConstantComposite %37 %40 %41 %42 %43 %44 + %46 = OpConstant %26 3 + %47 = OpTypeArray %20 %46 + %48 = OpTypePointer Function %47 + %50 = OpConstant %20 3 + %51 = OpConstant %20 7 + %52 = OpConstant %20 9 + %53 = OpConstantComposite %47 %50 %51 %52 + %55 = OpTypePointer Input %14 + %56 = OpVariable %55 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + %13 = OpVariable %7 Function + %16 = OpVariable %15 Function + %18 = OpVariable %15 Function + %22 = OpVariable %21 Function + %24 = OpVariable %21 Function + %28 = OpVariable %27 Function + %30 = OpVariable %27 Function + %32 = OpVariable %21 Function + %34 = OpVariable %21 Function + %39 = OpVariable %38 Function + %49 = OpVariable %48 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %12 %9 + OpStore %13 %11 + OpStore %16 %17 + OpStore %18 %19 + OpStore %22 %23 + OpStore %24 %25 + OpStore %28 %29 + OpStore %30 %31 + OpStore %32 %33 + OpStore %34 %35 + OpStore %39 %45 + OpStore %49 %53 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + opt::IRContext* ir_context = context.get(); + + std::vector uint_words1 = fuzzerutil::IntToWords(100, 32, false); + std::vector uint_words2 = fuzzerutil::IntToWords(0, 32, false); + std::vector int_words1 = fuzzerutil::IntToWords(-99, 32, true); + std::vector int_words2 = fuzzerutil::IntToWords(1, 32, true); + uint32_t float_word1 = fuzzerutil::FloatToWord(1.11f); + uint32_t float_word2 = fuzzerutil::FloatToWord(4.4f); + + // A unsigned int of value 100 that has a scalar type id of 26 exists and its + // id is 29. + ASSERT_EQ( + 29, fuzzerutil::MaybeGetScalarConstant(ir_context, transformation_context, + uint_words1, 26, false)); + // A unsigned int of value 0 that has a scalar type id of 26 exists and its id + // is 29. + ASSERT_EQ( + 31, fuzzerutil::MaybeGetScalarConstant(ir_context, transformation_context, + uint_words2, 26, false)); + // A signed int of value -99 that has a scalar type id of 20 exists and its id + // is 35. + ASSERT_EQ(35, fuzzerutil::MaybeGetScalarConstant( + ir_context, transformation_context, int_words1, 20, false)); + // A signed int of value 1 that has a scalar type id of 20 exists and its id + // is 25. + ASSERT_EQ(25, fuzzerutil::MaybeGetScalarConstant( + ir_context, transformation_context, int_words2, 20, false)); + // A float of value 1.11 that has a scalar type id of 14 exists and its id + // is 19. + ASSERT_EQ(19, fuzzerutil::MaybeGetScalarConstant( + ir_context, transformation_context, + std::vector{float_word1}, 14, false)); + // A signed int of value 1 that has a scalar type id of 20 exists and its id + // is 25. + ASSERT_EQ(41, fuzzerutil::MaybeGetScalarConstant( + ir_context, transformation_context, + std::vector{float_word2}, 14, false)); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetStructTypeTest) { + std::string 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 + %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 std::unique_ptr context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + opt::IRContext* ir_context = context.get(); + + // 6 and 7 are all valid ids from OpTypeInt and OpTypeFloat + // so the result id of 8 should be found. + ASSERT_EQ(8, fuzzerutil::MaybeGetStructType(ir_context, + std::vector{6, 7})); + + // |component_type_id| of 16 does not exist in the module, so such a struct + // type cannot be found. + ASSERT_EQ(0, fuzzerutil::MaybeGetStructType(ir_context, + std::vector(6, 16))); + + // |component_type_id| of 10 is of OpTypeFunction type and thus the struct + // cannot be found. + ASSERT_EQ(0, fuzzerutil::MaybeGetStructType(ir_context, + std::vector(6, 10))); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetVectorTypeTest) { + std::string 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 + %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 std::unique_ptr context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + opt::IRContext* ir_context = context.get(); + // The vector type with |element_count| 4 and |component_type_id| 7 + // is present and has a result id of 90. + ASSERT_EQ(90, fuzzerutil::MaybeGetVectorType(ir_context, 7, 4)); + + // The vector type with |element_count| 3 and |component_type_id| 7 + // is not present in the module. + ASSERT_EQ(0, fuzzerutil::MaybeGetVectorType(ir_context, 7, 3)); + +#ifndef NDEBUG + // It should abort with |component_type_id| of 100 + // |component_type_id| must be a valid result id of an OpTypeInt, + // OpTypeFloat or OpTypeBool instruction in the module. + ASSERT_DEATH(fuzzerutil::MaybeGetVectorType(ir_context, 100, 4), + "\\|component_type_id\\| is invalid"); + + // It should abort with |element_count| of 5. + // |element_count| must be in the range [2,4]. + ASSERT_DEATH(fuzzerutil::MaybeGetVectorType(ir_context, 7, 5), + "Precondition: component count must be in range \\[2, 4\\]."); +#endif +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetVoidTypeTest) { + std::string 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 + %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 std::unique_ptr context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + opt::IRContext* ir_context = context.get(); + // A void type with a result id of 2 can be found. + ASSERT_EQ(2, fuzzerutil::MaybeGetVoidType(ir_context)); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetZeroConstantTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %56 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "b1" + OpName %10 "b2" + OpName %12 "b3" + OpName %13 "b4" + OpName %16 "f1" + OpName %18 "f2" + OpName %22 "zc" + OpName %24 "i1" + OpName %28 "i2" + OpName %30 "i" + OpName %32 "i3" + OpName %34 "i4" + OpName %39 "f_arr" + OpName %49 "i_arr" + OpName %56 "value" + OpDecorate %22 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %49 RelaxedPrecision + OpDecorate %56 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypePointer Function %6 + %9 = OpConstantTrue %6 + %11 = OpConstantFalse %6 + %14 = OpTypeFloat 32 + %15 = OpTypePointer Function %14 + %17 = OpConstant %14 1.23000002 + %19 = OpConstant %14 1.11000001 + %20 = OpTypeInt 32 1 + %21 = OpTypePointer Function %20 + %23 = OpConstant %20 0 + %25 = OpConstant %20 1 + %26 = OpTypeInt 32 0 + %27 = OpTypePointer Function %26 + %29 = OpConstant %26 100 + %31 = OpConstant %26 0 + %33 = OpConstant %20 -1 + %35 = OpConstant %20 -99 + %36 = OpConstant %26 5 + %37 = OpTypeArray %14 %36 + %38 = OpTypePointer Function %37 + %40 = OpConstant %14 5.5 + %41 = OpConstant %14 4.4000001 + %42 = OpConstant %14 3.29999995 + %43 = OpConstant %14 2.20000005 + %44 = OpConstant %14 1.10000002 + %45 = OpConstantComposite %37 %40 %41 %42 %43 %44 + %46 = OpConstant %26 3 + %47 = OpTypeArray %20 %46 + %48 = OpTypePointer Function %47 + %50 = OpConstant %20 3 + %51 = OpConstant %20 7 + %52 = OpConstant %20 9 + %53 = OpConstantComposite %47 %50 %51 %52 + %55 = OpTypePointer Input %14 + %56 = OpVariable %55 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + %13 = OpVariable %7 Function + %16 = OpVariable %15 Function + %18 = OpVariable %15 Function + %22 = OpVariable %21 Function + %24 = OpVariable %21 Function + %28 = OpVariable %27 Function + %30 = OpVariable %27 Function + %32 = OpVariable %21 Function + %34 = OpVariable %21 Function + %39 = OpVariable %38 Function + %49 = OpVariable %48 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %12 %9 + OpStore %13 %11 + OpStore %16 %17 + OpStore %18 %19 + OpStore %22 %23 + OpStore %24 %25 + OpStore %28 %29 + OpStore %30 %31 + OpStore %32 %33 + OpStore %34 %35 + OpStore %39 %45 + OpStore %49 %53 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + opt::IRContext* ir_context = context.get(); + + // The id of a boolean constant will be returned give boolean type id 6. + uint32_t maybe_bool_id = fuzzerutil::MaybeGetZeroConstant( + ir_context, transformation_context, 6, false); + // The id of a 32 bit float constant will be returned given the float type + // id 14. + uint32_t maybe_float_id = fuzzerutil::MaybeGetZeroConstant( + ir_context, transformation_context, 14, false); + uint32_t maybe_signed_int_id = fuzzerutil::MaybeGetZeroConstant( + ir_context, transformation_context, 20, false); + uint32_t maybe_unsigned_int_id = fuzzerutil::MaybeGetZeroConstant( + ir_context, transformation_context, 26, false); + + // Lists of possible ids for float, signed int, unsigned int and array. + std::vector float_ids{17, 19}; + std::vector signed_int_ids{23, 25, 31, 33}; + + ASSERT_TRUE(maybe_bool_id == 9 || maybe_bool_id == 11); + ASSERT_TRUE(std::find(signed_int_ids.begin(), signed_int_ids.end(), + maybe_signed_int_id) != signed_int_ids.end()); + + // There is a unsigned int typed zero constant and its id is 31. + ASSERT_EQ(31, maybe_unsigned_int_id); + + // There is no zero float constant. + ASSERT_TRUE(std::find(float_ids.begin(), float_ids.end(), maybe_float_id) == + float_ids.end()); +} + +TEST(FuzzerutilTest, TypesAreCompatible) { + const 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 + %9 = OpTypeInt 32 0 + %8 = OpTypeStruct %6 + %10 = OpTypePointer StorageBuffer %8 + %11 = OpVariable %10 StorageBuffer + %86 = OpTypeStruct %9 + %87 = OpTypePointer Workgroup %86 + %88 = OpVariable %87 Workgroup + %89 = OpTypePointer Workgroup %9 + %19 = OpConstant %9 0 + %18 = OpConstant %9 1 + %12 = OpConstant %6 0 + %13 = OpTypePointer StorageBuffer %6 + %15 = OpConstant %6 2 + %16 = OpConstant %6 7 + %20 = OpConstant %9 64 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %14 = OpAccessChain %13 %11 %12 + %90 = OpAccessChain %89 %88 %19 + %21 = OpAtomicLoad %6 %14 %15 %20 + %22 = OpAtomicExchange %6 %14 %15 %20 %16 + %23 = OpAtomicCompareExchange %6 %14 %15 %20 %12 %16 %15 + %24 = OpAtomicIIncrement %6 %14 %15 %20 + %25 = OpAtomicIDecrement %6 %14 %15 %20 + %26 = OpAtomicIAdd %6 %14 %15 %20 %16 + %27 = OpAtomicISub %6 %14 %15 %20 %16 + %28 = OpAtomicSMin %6 %14 %15 %20 %16 + %29 = OpAtomicUMin %9 %90 %15 %20 %18 + %30 = OpAtomicSMax %6 %14 %15 %20 %15 + %31 = OpAtomicUMax %9 %90 %15 %20 %18 + %32 = OpAtomicAnd %6 %14 %15 %20 %16 + %33 = OpAtomicOr %6 %14 %15 %20 %16 + %34 = OpAtomicXor %6 %14 %15 %20 %16 + OpAtomicStore %14 %15 %20 %16 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + const uint32_t int_type = 6; // The id of OpTypeInt 32 1 + const uint32_t uint_type = 9; // The id of OpTypeInt 32 0 + + // OpAtomicLoad +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicLoad, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicLoad, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicLoad, 2, + int_type, uint_type)); + + // OpAtomicExchange +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicExchange, 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicExchange, + 1, int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicExchange, + 2, int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicExchange, 3, int_type, uint_type)); + + // OpAtomicStore +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, + 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, + 3, int_type, uint_type)); + + // OpAtomicCompareExchange +#ifndef NDEBUG + ASSERT_DEATH( + fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicCompareExchange, + 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicCompareExchange, 1, int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicCompareExchange, 2, int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicCompareExchange, 3, int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicCompareExchange, 4, int_type, uint_type)); + + // OpAtomicIIncrement +#ifndef NDEBUG + ASSERT_DEATH( + fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIIncrement, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicIIncrement, 1, int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicIIncrement, 2, int_type, uint_type)); + +// OpAtomicIDecrement +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, + 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 2, + int_type, uint_type)); + +// OpAtomicIAdd +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 3, + int_type, uint_type)); + +// OpAtomicISub +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 3, + int_type, uint_type)); + +// OpAtomicSMin +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 3, + int_type, uint_type)); + +// OpAtomicUMin +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 3, + int_type, uint_type)); + +// OpAtomicSMax +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 3, + int_type, uint_type)); + +// OpAtomicUMax +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 3, + int_type, uint_type)); + +// OpAtomicAnd +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 3, + int_type, uint_type)); + +// OpAtomicOr +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 3, + int_type, uint_type)); + +// OpAtomicXor +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 3, + int_type, uint_type)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/test/fuzz/shrinker_test.cpp b/test/fuzz/shrinker_test.cpp index 447ebecd..942de29a 100644 --- a/test/fuzz/shrinker_test.cpp +++ b/test/fuzz/shrinker_test.cpp @@ -171,7 +171,7 @@ TEST(ShrinkerTest, ReduceAddedFunctions) { protobufs::TransformationSequence transformations; FuzzerPassDonateModules pass(variant_ir_context.get(), &transformation_context, &fuzzer_context, - &transformations, {}); + &transformations, false, {}); pass.DonateSingleModule(donor_ir_context.get(), true); protobufs::FactSequence no_facts; @@ -349,7 +349,7 @@ TEST(ShrinkerTest, HitStepLimitWhenReducingAddedFunctions) { protobufs::TransformationSequence transformations; FuzzerPassDonateModules pass(variant_ir_context.get(), &transformation_context, &fuzzer_context, - &transformations, {}); + &transformations, false, {}); pass.DonateSingleModule(donor_ir_context.get(), true); protobufs::FactSequence no_facts; diff --git a/test/fuzz/transformation_access_chain_test.cpp b/test/fuzz/transformation_access_chain_test.cpp index 5c431279..bddcf5fe 100644 --- a/test/fuzz/transformation_access_chain_test.cpp +++ b/test/fuzz/transformation_access_chain_test.cpp @@ -26,6 +26,7 @@ namespace { TEST(TransformationAccessChainTest, BasicTest) { std::string shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %48 %54 @@ -63,7 +64,6 @@ TEST(TransformationAccessChainTest, BasicTest) { %85 = OpConstant %10 5 %52 = OpTypeArray %50 %51 %53 = OpTypePointer Private %52 - %45 = OpUndef %9 %46 = OpConstantNull %9 %47 = OpTypePointer Private %8 %48 = OpVariable %47 Private @@ -127,6 +127,16 @@ TEST(TransformationAccessChainTest, BasicTest) { transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( 54); + // Check the case where the index type is not a 32-bit integer. + TransformationAccessChain invalid_index_example1( + 101, 28, {29}, MakeInstructionDescriptor(42, SpvOpReturn, 0)); + + // Since the index is not a 32-bit integer type but a 32-bit float type, + // ValidIndexComposite should return false and thus the transformation is not + // applicable. + ASSERT_FALSE(invalid_index_example1.IsApplicable(context.get(), + transformation_context)); + // Bad: id is not fresh ASSERT_FALSE(TransformationAccessChain( 43, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) @@ -193,15 +203,6 @@ TEST(TransformationAccessChainTest, BasicTest) { #ifndef NDEBUG // Bad: pointer is null - 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_DEATH( TransformationAccessChain(100, 46, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) @@ -304,9 +305,24 @@ TEST(TransformationAccessChainTest, BasicTest) { ASSERT_FALSE( transformation_context.GetFactManager()->PointeeValueIsIrrelevant(107)); } + { + // Check the case where the access chain's base pointer has the irrelevant + // pointee fact; the resulting access chain should inherit this fact. + TransformationAccessChain transformation( + 107, 54, {}, MakeInstructionDescriptor(24, SpvOpLoad, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(54)); + } std::string after_transformation = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %48 %54 @@ -344,7 +360,6 @@ TEST(TransformationAccessChainTest, BasicTest) { %85 = OpConstant %10 5 %52 = OpTypeArray %50 %51 %53 = OpTypePointer Private %52 - %45 = OpUndef %9 %46 = OpConstantNull %9 %47 = OpTypePointer Private %8 %48 = OpVariable %47 Private @@ -383,6 +398,7 @@ TEST(TransformationAccessChainTest, BasicTest) { %23 = OpConvertFToS %10 %22 %100 = OpAccessChain %70 %43 %80 %106 = OpAccessChain %11 %14 + %107 = OpAccessChain %53 %54 %24 = OpLoad %10 %14 %25 = OpIAdd %10 %23 %24 OpReturnValue %25 @@ -391,6 +407,44 @@ TEST(TransformationAccessChainTest, BasicTest) { ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } +TEST(TransformationAccessChainTest, StructIndexMustBeConstant) { + 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 + %20 = OpUndef %6 + %7 = OpTypeStruct %6 %6 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 0 + %11 = OpConstant %6 2 + %12 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Bad: %9 is a pointer to a struct, but %20 is not a constant. + ASSERT_FALSE(TransformationAccessChain( + 100, 9, {20}, MakeInstructionDescriptor(9, SpvOpReturn, 0)) + .IsApplicable(context.get(), transformation_context)); +} + TEST(TransformationAccessChainTest, IsomorphicStructs) { std::string shader = R"( OpCapability Shader diff --git a/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp b/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp index ccfbcf53..d6b2ce51 100644 --- a/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp +++ b/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp @@ -937,11 +937,7 @@ TEST(TransformationAddBitInstructionSynonymTest, NoSynonymWhenBlockIsDead) { MakeDataDescriptor(166, {}), MakeDataDescriptor(39, {}))); } -TEST(TransformationAddBitInstructionSynonymTest, DISABLED_DifferentSingedness) { - // This test will fail due to a bug in the transformation. The reason is that - // OpNot supports its operand and result type having different signedness. - // OpBitFieldUExtract and OpBitFieldInsert, however, don't support this - // (these instructions are added by the transformation). +TEST(TransformationAddBitInstructionSynonymTest, DifferentSingedness) { std::string reference_shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -987,11 +983,14 @@ TEST(TransformationAddBitInstructionSynonymTest, DISABLED_DifferentSingedness) { %34 = OpConstant %2 29 %35 = OpConstant %2 30 %36 = OpConstant %2 31 + %45 = OpConstant %200 32 ; main function %37 = OpFunction %3 None %4 %38 = OpLabel %39 = OpNot %200 %5 ; bit instruction + %40 = OpBitwiseOr %200 %6 %45 ; bit instruction + %41 = OpBitwiseAnd %2 %5 %6 ; bit instruction OpReturn OpFunctionEnd )"; @@ -1006,17 +1005,61 @@ TEST(TransformationAddBitInstructionSynonymTest, DISABLED_DifferentSingedness) { TransformationContext transformation_context( MakeUnique(context.get()), validator_options); - // Adds OpNot synonym. + // Invalid because the sign of id 200 result is not equal to the sign of id 5 + // operand in OpNot. auto transformation = TransformationAddBitInstructionSynonym( - 39, {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, - 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, - 82, 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, 123, - 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134}); + 39, {300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, + 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, + 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, + 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, + 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, + 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, + 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, + 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, + 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427}); ASSERT_FALSE( transformation.IsApplicable(context.get(), transformation_context)); + + // Invalid because the sign of two operands not the same and the first operand + // sign not equal the result sign in OpBitwiseOr. + transformation = TransformationAddBitInstructionSynonym( + 40, {300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, + 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, + 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, + 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, + 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, + 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, + 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, + 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, + 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Successful transformation + { + // Instruction operands are the same and it's equal with the result sign in + // OpBitwiseAnd bitwise operation. + transformation = TransformationAddBitInstructionSynonym( + 41, {46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 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, 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}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } } } // namespace diff --git a/test/fuzz/transformation_add_constant_scalar_test.cpp b/test/fuzz/transformation_add_constant_scalar_test.cpp index 91339dbb..00c05416 100644 --- a/test/fuzz/transformation_add_constant_scalar_test.cpp +++ b/test/fuzz/transformation_add_constant_scalar_test.cpp @@ -114,6 +114,11 @@ TEST(TransformationAddConstantScalarTest, IsApplicable) { transformation = TransformationAddConstantScalar(19, 5, {0, 1, 2}, false); ASSERT_FALSE( transformation.IsApplicable(context.get(), transformation_context)); + + // Tests |words| having 2 words for a 32-bit float type. + transformation = TransformationAddConstantScalar(19, 4, {0, 1}, false); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddConstantScalarTest, Apply) { diff --git a/test/fuzz/transformation_add_copy_memory_test.cpp b/test/fuzz/transformation_add_copy_memory_test.cpp index 642a5561..ff8ac72e 100644 --- a/test/fuzz/transformation_add_copy_memory_test.cpp +++ b/test/fuzz/transformation_add_copy_memory_test.cpp @@ -26,6 +26,7 @@ namespace { TEST(TransformationAddCopyMemoryTest, BasicTest) { std::string shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -64,7 +65,6 @@ TEST(TransformationAddCopyMemoryTest, BasicTest) { %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 @@ -182,12 +182,6 @@ TEST(TransformationAddCopyMemoryTest, BasicTest) { 90, 40, SpvStorageClassPrivate, 0) .IsApplicable(context.get(), transformation_context)); - // Source instruction is OpUndef. - ASSERT_FALSE( - TransformationAddCopyMemory(MakeInstructionDescriptor(41, SpvOpLoad, 0), - 90, 87, SpvStorageClassPrivate, 0) - .IsApplicable(context.get(), transformation_context)); - // Source instruction is OpConstantNull. ASSERT_FALSE( TransformationAddCopyMemory(MakeInstructionDescriptor(41, SpvOpLoad, 0), @@ -255,6 +249,7 @@ TEST(TransformationAddCopyMemoryTest, BasicTest) { std::string expected = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -293,7 +288,6 @@ TEST(TransformationAddCopyMemoryTest, BasicTest) { %67 = OpTypePointer Function %66 %83 = OpTypePointer Private %66 %86 = OpVariable %79 Private %20 - %87 = OpUndef %79 %88 = OpConstantNull %79 %90 = OpVariable %79 Private %20 %92 = OpVariable %78 Private %25 diff --git a/test/fuzz/transformation_add_dead_block_test.cpp b/test/fuzz/transformation_add_dead_block_test.cpp index 687f00c7..3c9e6b43 100644 --- a/test/fuzz/transformation_add_dead_block_test.cpp +++ b/test/fuzz/transformation_add_dead_block_test.cpp @@ -142,6 +142,99 @@ TEST(TransformationAddDeadBlockTest, BasicTest) { ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); } +TEST(TransformationAddDeadBlockTest, ApplicableWithFalseCondition) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %6 "main" + OpExecutionMode %6 OriginUpperLeft + +; Types + %2 = OpTypeBool + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + +; Constants + %5 = OpConstantFalse %2 + +; main function + %6 = OpFunction %3 None %4 + %7 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %5 %8 %9 + %8 = OpLabel + OpBranch %10 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpBranch %13 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto transformation = TransformationAddDeadBlock(14, 11, false); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %6 "main" + OpExecutionMode %6 OriginUpperLeft + +; Types + %2 = OpTypeBool + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + +; Constants + %5 = OpConstantFalse %2 + +; main function + %6 = OpFunction %3 None %4 + %7 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %5 %8 %9 + %8 = OpLabel + OpBranch %10 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpSelectionMerge %13 None + OpBranchConditional %5 %14 %13 + %14 = OpLabel + OpBranch %13 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + TEST(TransformationAddDeadBlockTest, TargetBlockMustNotBeSelectionMerge) { std::string shader = R"( OpCapability Shader diff --git a/test/fuzz/transformation_add_parameter_test.cpp b/test/fuzz/transformation_add_parameter_test.cpp index 7b2a15f7..7f069b55 100644 --- a/test/fuzz/transformation_add_parameter_test.cpp +++ b/test/fuzz/transformation_add_parameter_test.cpp @@ -35,6 +35,10 @@ TEST(TransformationAddParameterTest, NonPointerBasicTest) { %7 = OpTypeBool %11 = OpTypeInt 32 1 %16 = OpTypeFloat 32 + %51 = OpConstant %11 2 + %52 = OpTypeArray %16 %51 + %53 = OpConstant %16 7 + %54 = OpConstantComposite %52 %53 %53 %3 = OpTypeFunction %2 %6 = OpTypeFunction %7 %7 %8 = OpConstant %11 23 @@ -140,6 +144,14 @@ TEST(TransformationAddParameterTest, NonPointerBasicTest) { context.get(), validator_options, kConsoleMessageConsumer)); ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(60)); } + { + TransformationAddParameter correct(9, 68, 52, {{{13, 54}}}, 69); + ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(correct, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(68)); + } { TransformationAddParameter correct(17, 62, 7, {{}}, 63); ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context)); @@ -177,6 +189,10 @@ TEST(TransformationAddParameterTest, NonPointerBasicTest) { %7 = OpTypeBool %11 = OpTypeInt 32 1 %16 = OpTypeFloat 32 + %51 = OpConstant %11 2 + %52 = OpTypeArray %16 %51 + %53 = OpConstant %16 7 + %54 = OpConstantComposite %52 %53 %53 %3 = OpTypeFunction %2 %8 = OpConstant %11 23 %12 = OpConstantTrue %7 @@ -188,11 +204,11 @@ TEST(TransformationAddParameterTest, NonPointerBasicTest) { %41 = OpTypeStruct %11 %16 %42 = OpConstantComposite %41 %8 %32 %44 = OpTypeFunction %2 %41 %7 - %6 = OpTypeFunction %7 %7 %11 + %6 = OpTypeFunction %7 %7 %11 %52 %65 = OpTypeFunction %2 %31 %4 = OpFunction %2 None %3 %5 = OpLabel - %13 = OpFunctionCall %7 %9 %12 %8 + %13 = OpFunctionCall %7 %9 %12 %8 %54 OpReturn OpFunctionEnd @@ -200,6 +216,7 @@ TEST(TransformationAddParameterTest, NonPointerBasicTest) { %9 = OpFunction %7 None %6 %14 = OpFunctionParameter %7 %60 = OpFunctionParameter %11 + %68 = OpFunctionParameter %52 %10 = OpLabel OpReturnValue %12 OpFunctionEnd @@ -350,7 +367,7 @@ TEST(TransformationAddParameterTest, NonPointerNotApplicableTest) { transformation_bad_3.IsApplicable(context.get(), transformation_context)); // Function with id 14 does not have any callers. - // Bad: Id 18 is not a vaild type. + // Bad: Id 18 is not a valid type. TransformationAddParameter transformation_bad_4(14, 50, 18, {{}}, 51); ASSERT_FALSE( transformation_bad_4.IsApplicable(context.get(), transformation_context)); diff --git a/test/fuzz/transformation_add_synonym_test.cpp b/test/fuzz/transformation_add_synonym_test.cpp index 3803fa3a..ffcf1c9b 100644 --- a/test/fuzz/transformation_add_synonym_test.cpp +++ b/test/fuzz/transformation_add_synonym_test.cpp @@ -197,6 +197,7 @@ TEST(TransformationAddSynonymTest, AddZeroSubZeroMulOne) { %37 = OpTypeVector %36 2 %38 = OpConstantTrue %36 %39 = OpConstantComposite %37 %38 %38 + %40 = OpConstant %6 37 %4 = OpFunction %2 None %3 %5 = OpLabel OpReturn @@ -249,6 +250,29 @@ TEST(TransformationAddSynonymTest, AddZeroSubZeroMulOne) { ++fresh_id; } } + { + TransformationAddSynonym transformation( + 40, protobufs::TransformationAddSynonym::BITWISE_OR, fresh_id, + insert_before); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {}), MakeDataDescriptor(fresh_id, {}))); + ++fresh_id; + } + { + TransformationAddSynonym transformation( + 40, protobufs::TransformationAddSynonym::BITWISE_XOR, fresh_id, + insert_before); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {}), MakeDataDescriptor(fresh_id, {}))); + } std::string expected_shader = R"( OpCapability Shader @@ -289,6 +313,7 @@ TEST(TransformationAddSynonymTest, AddZeroSubZeroMulOne) { %37 = OpTypeVector %36 2 %38 = OpConstantTrue %36 %39 = OpConstantComposite %37 %38 %38 + %40 = OpConstant %6 37 %4 = OpFunction %2 None %3 %5 = OpLabel %50 = OpIAdd %6 %9 %7 @@ -303,6 +328,8 @@ TEST(TransformationAddSynonymTest, AddZeroSubZeroMulOne) { %59 = OpFMul %14 %17 %16 %60 = OpFMul %18 %23 %20 %61 = OpIMul %24 %29 %26 + %62 = OpBitwiseOr %6 %40 %7 + %63 = OpBitwiseXor %6 %40 %7 OpReturn OpFunctionEnd )"; @@ -1215,9 +1242,10 @@ TEST(TransformationAddSynonymTest, MiscellaneousCopies) { ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } -TEST(TransformationAddSynonymTest, DoNotCopyNullOrUndefPointers) { +TEST(TransformationAddSynonymTest, DoNotCopyNullPointers) { std::string shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -1228,7 +1256,6 @@ TEST(TransformationAddSynonymTest, DoNotCopyNullOrUndefPointers) { %6 = OpTypeInt 32 1 %7 = OpTypePointer Function %6 %8 = OpConstantNull %7 - %9 = OpUndef %7 %4 = OpFunction %2 None %3 %5 = OpLabel OpReturn @@ -1248,12 +1275,6 @@ TEST(TransformationAddSynonymTest, DoNotCopyNullOrUndefPointers) { 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) { diff --git a/test/fuzz/transformation_add_type_float_test.cpp b/test/fuzz/transformation_add_type_float_test.cpp index dc57ccda..9275bec1 100644 --- a/test/fuzz/transformation_add_type_float_test.cpp +++ b/test/fuzz/transformation_add_type_float_test.cpp @@ -61,7 +61,8 @@ TEST(TransformationAddTypeFloatTest, IsApplicable) { ASSERT_FALSE( transformation.IsApplicable(context.get(), transformation_context)); - // Tests existing 16-bit float type. + // The transformation is not applicable because there is already a 16-bit + // float type declared in the module. transformation = TransformationAddTypeFloat(7, 16); ASSERT_FALSE( transformation.IsApplicable(context.get(), transformation_context)); @@ -70,6 +71,19 @@ TEST(TransformationAddTypeFloatTest, IsApplicable) { transformation = TransformationAddTypeFloat(7, 32); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); + + // By default, SPIR-V does not support 64-bit float types. + // Below we add such capability, so the test should now pass. + context.get()->get_feature_mgr()->AddCapability(SpvCapabilityFloat64); + ASSERT_TRUE(TransformationAddTypeFloat(7, 64).IsApplicable( + context.get(), transformation_context)); + +#ifndef NDEBUG + // Should not be able to add float type of width different from 16/32/64 + ASSERT_DEATH(TransformationAddTypeFloat(7, 20).IsApplicable( + context.get(), transformation_context), + "Unexpected float type width"); +#endif } TEST(TransformationAddTypeFloatTest, Apply) { diff --git a/test/fuzz/transformation_add_type_int_test.cpp b/test/fuzz/transformation_add_type_int_test.cpp index 273420c5..4cbfed0f 100644 --- a/test/fuzz/transformation_add_type_int_test.cpp +++ b/test/fuzz/transformation_add_type_int_test.cpp @@ -85,6 +85,29 @@ TEST(TransformationAddTypeIntTest, IsApplicable) { transformation = TransformationAddTypeInt(7, 32, true); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); + + // By default SPIR-V does not support 16-bit integers. + // Below we add such capability, so the test should now be successful. + context.get()->get_feature_mgr()->AddCapability(SpvCapabilityInt16); + ASSERT_TRUE(TransformationAddTypeInt(7, 16, true) + .IsApplicable(context.get(), transformation_context)); + + // By default SPIR-V does not support 64-bit integers. + // Below we add such capability, so the test should now pass. + context.get()->get_feature_mgr()->AddCapability(SpvCapabilityInt64); + ASSERT_TRUE(TransformationAddTypeInt(7, 64, true) + .IsApplicable(context.get(), transformation_context)); + +#ifndef NDEBUG + // Should not be able to add signed/unsigned integers of width different from + // 16/32/64 bits. + ASSERT_DEATH(TransformationAddTypeInt(7, 20, false) + .IsApplicable(context.get(), transformation_context), + "Unexpected integer type width"); + ASSERT_DEATH(TransformationAddTypeInt(12, 15, false) + .IsApplicable(context.get(), transformation_context), + "Unexpected integer type width"); +#endif } TEST(TransformationAddTypeIntTest, Apply) { diff --git a/test/fuzz/transformation_adjust_branch_weights_test.cpp b/test/fuzz/transformation_adjust_branch_weights_test.cpp index 1bf2c593..5984a3e9 100644 --- a/test/fuzz/transformation_adjust_branch_weights_test.cpp +++ b/test/fuzz/transformation_adjust_branch_weights_test.cpp @@ -106,7 +106,7 @@ TEST(TransformationAdjustBranchWeightsTest, IsApplicableTest) { kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); - // Tests OpBranchConditional instruction with weigths. + // Tests OpBranchConditional instruction with weights. auto instruction_descriptor = MakeInstructionDescriptor(33, SpvOpBranchConditional, 0); auto transformation = diff --git a/test/fuzz/transformation_composite_construct_test.cpp b/test/fuzz/transformation_composite_construct_test.cpp index 9b68721d..3c5f7310 100644 --- a/test/fuzz/transformation_composite_construct_test.cpp +++ b/test/fuzz/transformation_composite_construct_test.cpp @@ -142,10 +142,16 @@ TEST(TransformationCompositeConstructTest, ConstructArrays) { TransformationCompositeConstruct make_vec2_array_length_3_bad( 37, {41, 45, 27, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 200); + // The first component does not correspond to an instruction with a result + // type so this check should return false. + TransformationCompositeConstruct make_vec2_array_length_3_nores( + 37, {2, 45, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 200); ASSERT_TRUE(make_vec2_array_length_3.IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(make_vec2_array_length_3_bad.IsApplicable( context.get(), transformation_context)); + ASSERT_FALSE(make_vec2_array_length_3_nores.IsApplicable( + context.get(), transformation_context)); ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(200)); ASSERT_EQ(nullptr, context->get_instr_block(200)); uint32_t num_uses_of_41_before = context->get_def_use_mgr()->NumUses(41); @@ -423,9 +429,15 @@ TEST(TransformationCompositeConstructTest, ConstructMatrices) { // Bad: %35 is mat4x3, not mat3x4. TransformationCompositeConstruct make_mat34_bad( 35, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200); + // The first component does not correspond to an instruction with a result + // type so this check should return false. + TransformationCompositeConstruct make_mat34_nores( + 32, {2, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200); ASSERT_TRUE(make_mat34.IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( make_mat34_bad.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_mat34_nores.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(make_mat34, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); @@ -636,9 +648,15 @@ TEST(TransformationCompositeConstructTest, ConstructStructs) { // Bad: Too few fields to make the struct. TransformationCompositeConstruct make_inner_bad( 9, {25}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200); + // The first component does not correspond to an instruction with a result + // type so this check should return false. + TransformationCompositeConstruct make_inner_nores( + 9, {2, 19}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200); ASSERT_TRUE(make_inner.IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( make_inner_bad.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_inner_nores.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(make_inner, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); diff --git a/test/fuzz/transformation_load_test.cpp b/test/fuzz/transformation_load_test.cpp index a03ffdd6..370826eb 100644 --- a/test/fuzz/transformation_load_test.cpp +++ b/test/fuzz/transformation_load_test.cpp @@ -26,6 +26,7 @@ namespace { TEST(TransformationLoadTest, BasicTest) { std::string shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -49,7 +50,6 @@ TEST(TransformationLoadTest, BasicTest) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %4 = OpFunction %2 None %3 @@ -129,65 +129,69 @@ TEST(TransformationLoadTest, BasicTest) { // Pointers that cannot be used: // 60 - null - // 61 - undefined // Bad: id is not fresh - ASSERT_FALSE(TransformationLoad( - 33, 33, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationLoad(33, 33, false, 0, 0, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to load from 11 from outside its function - ASSERT_FALSE(TransformationLoad( - 100, 11, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationLoad(100, 11, false, 0, 0, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: pointer is not available - ASSERT_FALSE(TransformationLoad( - 100, 33, MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationLoad(100, 33, false, 0, 0, + MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to insert before OpVariable - ASSERT_FALSE(TransformationLoad( - 100, 27, MakeInstructionDescriptor(27, SpvOpVariable, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationLoad(100, 27, false, 0, 0, + MakeInstructionDescriptor(27, SpvOpVariable, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: pointer id does not exist ASSERT_FALSE( - TransformationLoad(100, 1000, + TransformationLoad(100, 1000, false, 0, 0, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) .IsApplicable(context.get(), transformation_context)); // Bad: pointer id exists but does not have a type - ASSERT_FALSE(TransformationLoad( - 100, 5, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); - - // Bad: pointer id exists and has a type, but is not a pointer - ASSERT_FALSE(TransformationLoad( - 100, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); - - // Bad: attempt to load from null pointer - ASSERT_FALSE(TransformationLoad( - 100, 60, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); - - // Bad: attempt to load from undefined pointer - ASSERT_FALSE(TransformationLoad( - 100, 61, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); - // Bad: %40 is not available at the program point ASSERT_FALSE( - TransformationLoad(100, 40, MakeInstructionDescriptor(37, SpvOpReturn, 0)) + TransformationLoad(100, 5, false, 0, 0, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) .IsApplicable(context.get(), transformation_context)); - // Bad: The described instruction does not exist - ASSERT_FALSE(TransformationLoad( - 100, 33, MakeInstructionDescriptor(1000, SpvOpReturn, 0)) + // Bad: pointer id exists and has a type, but is not a pointer + ASSERT_FALSE( + TransformationLoad(100, 24, false, 0, 0, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: attempt to load from null pointer + ASSERT_FALSE( + TransformationLoad(100, 60, false, 0, 0, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: %40 is not available at the program point + ASSERT_FALSE(TransformationLoad(100, 40, false, 0, 0, + MakeInstructionDescriptor(37, SpvOpReturn, 0)) .IsApplicable(context.get(), transformation_context)); + // Bad: The described instruction does not exist + ASSERT_FALSE( + TransformationLoad(100, 33, false, 0, 0, + MakeInstructionDescriptor(1000, SpvOpReturn, 0)) + .IsApplicable(context.get(), transformation_context)); + { TransformationLoad transformation( - 100, 33, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); + 100, 33, false, 0, 0, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -198,7 +202,8 @@ TEST(TransformationLoadTest, BasicTest) { { TransformationLoad transformation( - 101, 46, MakeInstructionDescriptor(16, SpvOpReturnValue, 0)); + 101, 46, false, 0, 0, + MakeInstructionDescriptor(16, SpvOpReturnValue, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -209,7 +214,8 @@ TEST(TransformationLoadTest, BasicTest) { { TransformationLoad transformation( - 102, 16, MakeInstructionDescriptor(16, SpvOpReturnValue, 0)); + 102, 16, false, 0, 0, + MakeInstructionDescriptor(16, SpvOpReturnValue, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -220,7 +226,8 @@ TEST(TransformationLoadTest, BasicTest) { { TransformationLoad transformation( - 103, 40, MakeInstructionDescriptor(43, SpvOpAccessChain, 0)); + 103, 40, false, 0, 0, + MakeInstructionDescriptor(43, SpvOpAccessChain, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -231,6 +238,7 @@ TEST(TransformationLoadTest, BasicTest) { std::string after_transformation = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -254,7 +262,6 @@ TEST(TransformationLoadTest, BasicTest) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %4 = OpFunction %2 None %3 @@ -293,6 +300,296 @@ TEST(TransformationLoadTest, BasicTest) { ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } +TEST(TransformationLoadTest, AtomicLoadTestCase) { + const std::string shader = R"( + OpCapability Shader + OpCapability Int8 + %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 + %7 = OpTypeInt 8 1 + %9 = OpTypeInt 32 0 + %26 = OpTypeFloat 32 + %8 = OpTypeStruct %6 + %10 = OpTypePointer StorageBuffer %8 + %11 = OpVariable %10 StorageBuffer + %19 = OpConstant %26 0 + %18 = OpConstant %9 1 + %12 = OpConstant %6 0 + %13 = OpTypePointer StorageBuffer %6 + %15 = OpConstant %6 4 + %16 = OpConstant %6 7 + %17 = OpConstant %7 4 + %20 = OpConstant %9 64 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %14 = OpAccessChain %13 %11 %12 + %24 = OpAccessChain %13 %11 %12 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Bad: id is not fresh. + ASSERT_FALSE( + TransformationLoad(14, 14, true, 15, 20, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: id 100 of memory scope instruction does not exist. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 100, 20, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: id 100 of memory semantics instruction does not exist. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 15, 100, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: memory scope should be |OpConstant| opcode. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 5, 20, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: memory semantics should be |OpConstant| opcode. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 15, 5, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: The memory scope instruction must have an Integer operand. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 15, 19, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: The memory memory semantics instruction must have an Integer operand. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 19, 20, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Integer size of the memory scope must be equal to 32 bits. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 17, 20, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Integer size of memory semantics must be equal to 32 bits. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 15, 17, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: memory scope value must be 4 (SpvScopeInvocation). + ASSERT_FALSE( + TransformationLoad(21, 14, true, 16, 20, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: memory semantics value must be either: + // 64 (SpvMemorySemanticsUniformMemoryMask) + // 256 (SpvMemorySemanticsWorkgroupMemoryMask) + ASSERT_FALSE( + TransformationLoad(21, 14, true, 15, 16, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: The described instruction does not exist + ASSERT_FALSE( + TransformationLoad(21, 14, false, 15, 20, + MakeInstructionDescriptor(150, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Successful transformations. + { + TransformationLoad transformation( + 21, 14, true, 15, 20, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + const std::string after_transformation = R"( + OpCapability Shader + OpCapability Int8 + %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 + %7 = OpTypeInt 8 1 + %9 = OpTypeInt 32 0 + %26 = OpTypeFloat 32 + %8 = OpTypeStruct %6 + %10 = OpTypePointer StorageBuffer %8 + %11 = OpVariable %10 StorageBuffer + %19 = OpConstant %26 0 + %18 = OpConstant %9 1 + %12 = OpConstant %6 0 + %13 = OpTypePointer StorageBuffer %6 + %15 = OpConstant %6 4 + %16 = OpConstant %6 7 + %17 = OpConstant %7 4 + %20 = OpConstant %9 64 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %14 = OpAccessChain %13 %11 %12 + %21 = OpAtomicLoad %6 %14 %15 %20 + %24 = OpAccessChain %13 %11 %12 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationLoadTest, AtomicLoadTestCaseForWorkgroupMemory) { + std::string shader = R"( + OpCapability Shader + OpCapability Int8 + %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 + %26 = OpTypeFloat 32 + %27 = OpTypeInt 8 1 + %7 = OpTypeInt 32 0 ; 0 means unsigned + %8 = OpConstant %7 0 + %17 = OpConstant %27 4 + %19 = OpConstant %26 0 + %9 = OpTypePointer Function %6 + %13 = OpTypeStruct %6 + %12 = OpTypePointer Workgroup %13 + %11 = OpVariable %12 Workgroup + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 4 + %23 = OpConstant %6 256 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Workgroup %6 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %53 = OpVariable %51 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %38 = OpAccessChain %50 %11 %14 + %40 = OpAccessChain %50 %11 %14 + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Bad: Can't insert OpAccessChain before the id 23 of memory scope. + ASSERT_FALSE( + TransformationLoad(60, 38, true, 21, 23, + MakeInstructionDescriptor(23, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Can't insert OpAccessChain before the id 23 of memory semantics. + ASSERT_FALSE( + TransformationLoad(60, 38, true, 21, 23, + MakeInstructionDescriptor(21, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Successful transformations. + { + TransformationLoad transformation( + 60, 38, true, 21, 23, + MakeInstructionDescriptor(40, SpvOpAccessChain, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + OpCapability Int8 + %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 + %26 = OpTypeFloat 32 + %27 = OpTypeInt 8 1 + %7 = OpTypeInt 32 0 ; 0 means unsigned + %8 = OpConstant %7 0 + %17 = OpConstant %27 4 + %19 = OpConstant %26 0 + %9 = OpTypePointer Function %6 + %13 = OpTypeStruct %6 + %12 = OpTypePointer Workgroup %13 + %11 = OpVariable %12 Workgroup + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 4 + %23 = OpConstant %6 256 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Workgroup %6 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %53 = OpVariable %51 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %38 = OpAccessChain %50 %11 %14 + %60 = OpAtomicLoad %6 %38 %21 %23 + %40 = OpAccessChain %50 %11 %14 + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/test/fuzz/transformation_merge_function_returns_test.cpp b/test/fuzz/transformation_merge_function_returns_test.cpp index e60d3458..400d49ad 100644 --- a/test/fuzz/transformation_merge_function_returns_test.cpp +++ b/test/fuzz/transformation_merge_function_returns_test.cpp @@ -147,12 +147,12 @@ TEST(TransformationMergeFunctionReturnsTest, SimpleInapplicable) { MakeUnique(context.get()), validator_options); // Function %1 does not exist. - ASSERT_FALSE(TransformationMergeFunctionReturns(1, 100, 101, 0, 0, {{}}) + ASSERT_FALSE(TransformationMergeFunctionReturns(1, 100, 200, 101, 0, 0, {{}}) .IsApplicable(context.get(), transformation_context)); // The entry block (%22) of function %15 does not branch unconditionally to // the following block. - ASSERT_FALSE(TransformationMergeFunctionReturns(16, 100, 101, 0, 0, {{}}) + ASSERT_FALSE(TransformationMergeFunctionReturns(16, 100, 200, 101, 0, 0, {{}}) .IsApplicable(context.get(), transformation_context)); // Block %28 is the merge block of a loop containing a return instruction, but @@ -160,7 +160,7 @@ TEST(TransformationMergeFunctionReturnsTest, SimpleInapplicable) { // not OpLabel, OpPhi or OpBranch). ASSERT_FALSE( TransformationMergeFunctionReturns( - 18, 100, 101, 0, 0, {{MakeReturnMergingInfo(29, 102, 0, {{}})}}) + 18, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(29, 102, 0, {{}})}}) .IsApplicable(context.get(), transformation_context)); // Block %34 is the merge block of a loop containing a return instruction, but @@ -168,21 +168,24 @@ TEST(TransformationMergeFunctionReturnsTest, SimpleInapplicable) { // that are not OpLabel, OpPhi or OpBranch). ASSERT_FALSE( TransformationMergeFunctionReturns( - 20, 100, 101, 0, 0, {{MakeReturnMergingInfo(35, 102, 0, {{}})}}) + 20, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(35, 102, 0, {{}})}}) .IsApplicable(context.get(), transformation_context)); // Id %1000 cannot be found in the module and there is no id of the correct // type (float) available at the end of the entry block of function %21. - ASSERT_FALSE(TransformationMergeFunctionReturns(22, 100, 101, 102, 1000, {{}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationMergeFunctionReturns(22, 100, 200, 101, 102, 1000, {{}}) + .IsApplicable(context.get(), transformation_context)); // Id %47 is of type float, while function %45 has return type int. - ASSERT_FALSE(TransformationMergeFunctionReturns(45, 100, 101, 102, 47, {{}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationMergeFunctionReturns(45, 100, 200, 101, 102, 47, {{}}) + .IsApplicable(context.get(), transformation_context)); // Id %50 is not available at the end of the entry block of function %45. - ASSERT_FALSE(TransformationMergeFunctionReturns(45, 100, 101, 102, 50, {{}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationMergeFunctionReturns(45, 100, 200, 101, 102, 50, {{}}) + .IsApplicable(context.get(), transformation_context)); } TEST(TransformationMergeFunctionReturnsTest, MissingBooleans) { @@ -235,8 +238,9 @@ TEST(TransformationMergeFunctionReturnsTest, MissingBooleans) { TransformationContext transformation_context( MakeUnique(context.get()), validator_options); - ASSERT_FALSE(TransformationMergeFunctionReturns(3, 100, 101, 0, 0, {{}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationMergeFunctionReturns(3, 100, 200, 101, 0, 0, {{}}) + .IsApplicable(context.get(), transformation_context)); } { // OpConstantFalse is missing. @@ -287,8 +291,9 @@ TEST(TransformationMergeFunctionReturnsTest, MissingBooleans) { TransformationContext transformation_context( MakeUnique(context.get()), validator_options); - ASSERT_FALSE(TransformationMergeFunctionReturns(3, 100, 101, 0, 0, {{}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationMergeFunctionReturns(3, 100, 200, 101, 0, 0, {{}}) + .IsApplicable(context.get(), transformation_context)); } } @@ -386,59 +391,65 @@ TEST(TransformationMergeFunctionReturnsTest, InvalidIds) { // Fresh id %100 is used twice. ASSERT_FALSE( TransformationMergeFunctionReturns( - 17, 100, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}}) + 17, 100, 200, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}}) .IsApplicable(context.get(), transformation_context)); // Fresh id %100 is used twice. ASSERT_FALSE( TransformationMergeFunctionReturns( - 17, 100, 101, 0, 0, {{MakeReturnMergingInfo(20, 100, 0, {{}})}}) + 17, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(20, 100, 0, {{}})}}) .IsApplicable(context.get(), transformation_context)); // %0 cannot be a fresh id for the new merge block. ASSERT_FALSE( TransformationMergeFunctionReturns( - 17, 100, 0, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}}) + 17, 100, 200, 0, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}}) + .IsApplicable(context.get(), transformation_context)); + + // %0 cannot be a fresh id for the new continue block. + ASSERT_FALSE( + TransformationMergeFunctionReturns( + 17, 100, 0, 200, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}}) .IsApplicable(context.get(), transformation_context)); // %0 cannot be a fresh id for the new header block. ASSERT_FALSE( TransformationMergeFunctionReturns( - 17, 0, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}}) + 17, 0, 200, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}}) .IsApplicable(context.get(), transformation_context)); // %0 cannot be a fresh id for the new |is_returning| instruction in an // existing merge block. ASSERT_FALSE( TransformationMergeFunctionReturns( - 17, 100, 101, 0, 0, {{MakeReturnMergingInfo(20, 0, 0, {{}})}}) + 17, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(20, 0, 0, {{}})}}) .IsApplicable(context.get(), transformation_context)); // %0 cannot be a fresh id for the new |return_val| instruction in the new // return block. - ASSERT_FALSE( - TransformationMergeFunctionReturns( - 14, 100, 101, 0, 10, {{MakeReturnMergingInfo(27, 102, 103, {{}})}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationMergeFunctionReturns( + 14, 100, 200, 101, 0, 10, + {{MakeReturnMergingInfo(27, 102, 103, {{}})}}) + .IsApplicable(context.get(), transformation_context)); // %0 cannot be a fresh id for the new |maybe_return_val| instruction in an // existing merge block, inside a non-void function. - ASSERT_FALSE( - TransformationMergeFunctionReturns( - 14, 100, 101, 102, 10, {{MakeReturnMergingInfo(27, 103, 0, {{}})}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationMergeFunctionReturns( + 14, 100, 200, 101, 102, 10, + {{MakeReturnMergingInfo(27, 103, 0, {{}})}}) + .IsApplicable(context.get(), transformation_context)); // Fresh id %102 is repeated. - ASSERT_FALSE( - TransformationMergeFunctionReturns( - 14, 100, 101, 102, 10, {{MakeReturnMergingInfo(27, 102, 104, {{}})}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationMergeFunctionReturns( + 14, 100, 200, 101, 102, 10, + {{MakeReturnMergingInfo(27, 102, 104, {{}})}}) + .IsApplicable(context.get(), transformation_context)); // Id %11 (type int) does not have the correct type (float) for OpPhi // instruction %31. ASSERT_FALSE( TransformationMergeFunctionReturns( - 16, 100, 101, 0, 0, + 16, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(36, 103, 104, {{{31, 11}, {32, 11}}})}}) .IsApplicable(context.get(), transformation_context)); @@ -446,21 +457,21 @@ TEST(TransformationMergeFunctionReturnsTest, InvalidIds) { // instruction %31. ASSERT_FALSE( TransformationMergeFunctionReturns( - 16, 100, 101, 0, 0, + 16, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(36, 102, 0, {{{31, 11}, {32, 11}}})}}) .IsApplicable(context.get(), transformation_context)); // Id %43 is not available at the end of the entry block. ASSERT_FALSE( TransformationMergeFunctionReturns( - 16, 100, 101, 0, 0, + 16, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(36, 102, 0, {{{31, 44}, {32, 11}}})}}) .IsApplicable(context.get(), transformation_context)); // There is not a mapping for id %31 (float OpPhi instruction in a loop merge // block) and no suitable id is available at the end of the entry block. ASSERT_FALSE(TransformationMergeFunctionReturns( - 16, 100, 101, 0, 0, + 16, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(36, 102, 0, {{{32, 11}}})}}) .IsApplicable(context.get(), transformation_context)); @@ -468,7 +479,7 @@ TEST(TransformationMergeFunctionReturnsTest, InvalidIds) { // available at the end of the entry block. ASSERT_FALSE( TransformationMergeFunctionReturns( - 16, 100, 101, 0, 0, + 16, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(36, 102, 0, {{{31, 1000}, {32, 11}}})}}) .IsApplicable(context.get(), transformation_context)); } @@ -560,7 +571,7 @@ TEST(TransformationMergeFunctionReturnsTest, Simple) { // The 0s are allowed because the function's return type is void. auto transformation1 = - TransformationMergeFunctionReturns(14, 100, 101, 0, 0, {{}}); + TransformationMergeFunctionReturns(14, 100, 200, 101, 0, 0, {{}}); ASSERT_TRUE( transformation1.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation1, context.get(), @@ -570,13 +581,14 @@ TEST(TransformationMergeFunctionReturnsTest, Simple) { // %12 is available at the end of the entry block of %19 (it is a global // variable). - ASSERT_TRUE(TransformationMergeFunctionReturns(19, 110, 111, 112, 12, {{}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationMergeFunctionReturns(19, 110, 210, 111, 112, 12, {{}}) + .IsApplicable(context.get(), transformation_context)); // %1000 cannot be found in the module, but there is a suitable id available // at the end of the entry block (%12). auto transformation2 = - TransformationMergeFunctionReturns(19, 110, 111, 112, 1000, {{}}); + TransformationMergeFunctionReturns(19, 110, 210, 111, 112, 1000, {{}}); ASSERT_TRUE( transformation2.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation2, context.get(), @@ -586,13 +598,14 @@ TEST(TransformationMergeFunctionReturnsTest, Simple) { // %27 is available at the end of the entry block of %26 (it is a function // parameter). - ASSERT_TRUE(TransformationMergeFunctionReturns(26, 120, 121, 122, 27, {{}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationMergeFunctionReturns(26, 120, 220, 121, 122, 27, {{}}) + .IsApplicable(context.get(), transformation_context)); // %1000 cannot be found in the module, but there is a suitable id available // at the end of the entry block (%27). auto transformation3 = - TransformationMergeFunctionReturns(26, 120, 121, 122, 1000, {{}}); + TransformationMergeFunctionReturns(26, 120, 220, 121, 122, 1000, {{}}); ASSERT_TRUE( transformation3.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation3, context.get(), @@ -602,13 +615,14 @@ TEST(TransformationMergeFunctionReturnsTest, Simple) { // %35 is available at the end of the entry block of %33 (it is in the entry // block). - ASSERT_TRUE(TransformationMergeFunctionReturns(26, 130, 131, 132, 27, {{}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationMergeFunctionReturns(26, 130, 230, 131, 132, 27, {{}}) + .IsApplicable(context.get(), transformation_context)); // %1000 cannot be found in the module, but there is a suitable id available // at the end of the entry block (%35). auto transformation4 = - TransformationMergeFunctionReturns(33, 130, 131, 132, 1000, {{}}); + TransformationMergeFunctionReturns(33, 130, 230, 131, 132, 1000, {{}}); ASSERT_TRUE( transformation4.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation4, context.get(), @@ -642,8 +656,8 @@ TEST(TransformationMergeFunctionReturnsTest, Simple) { %15 = OpLabel OpBranch %100 %100 = OpLabel - OpLoopMerge %101 %100 None - OpBranchConditional %11 %16 %100 + OpLoopMerge %101 %200 None + OpBranch %16 %16 = OpLabel OpSelectionMerge %17 None OpBranchConditional %11 %18 %17 @@ -653,13 +667,15 @@ TEST(TransformationMergeFunctionReturnsTest, Simple) { OpBranch %101 %101 = OpLabel OpReturn + %200 = OpLabel + OpBranch %100 OpFunctionEnd %19 = OpFunction %5 None %6 %20 = OpLabel OpBranch %110 %110 = OpLabel - OpLoopMerge %111 %110 None - OpBranchConditional %11 %21 %110 + OpLoopMerge %111 %210 None + OpBranch %21 %21 = OpLabel OpSelectionMerge %22 None OpBranchConditional %11 %23 %24 @@ -673,14 +689,16 @@ TEST(TransformationMergeFunctionReturnsTest, Simple) { %111 = OpLabel %112 = OpPhi %5 %12 %23 %25 %24 OpReturnValue %112 + %210 = OpLabel + OpBranch %110 OpFunctionEnd %26 = OpFunction %7 None %8 %27 = OpFunctionParameter %7 %28 = OpLabel OpBranch %120 %120 = OpLabel - OpLoopMerge %121 %120 None - OpBranchConditional %11 %29 %120 + OpLoopMerge %121 %220 None + OpBranch %29 %29 = OpLabel OpSelectionMerge %30 None OpBranchConditional %11 %31 %30 @@ -692,14 +710,16 @@ TEST(TransformationMergeFunctionReturnsTest, Simple) { %121 = OpLabel %122 = OpPhi %7 %27 %30 %32 %31 OpReturnValue %122 + %220 = OpLabel + OpBranch %120 OpFunctionEnd %33 = OpFunction %7 None %9 %34 = OpLabel %35 = OpConvertSToF %7 %12 OpBranch %130 %130 = OpLabel - OpLoopMerge %131 %130 None - OpBranchConditional %11 %36 %130 + OpLoopMerge %131 %230 None + OpBranch %36 %36 = OpLabel OpSelectionMerge %37 None OpBranchConditional %11 %38 %37 @@ -711,6 +731,8 @@ TEST(TransformationMergeFunctionReturnsTest, Simple) { %131 = OpLabel %132 = OpPhi %7 %35 %37 %39 %38 OpReturnValue %132 + %230 = OpLabel + OpBranch %130 OpFunctionEnd )"; @@ -743,8 +765,8 @@ TEST(TransformationMergeFunctionReturnsTest, NestedLoops) { %15 = OpLabel OpBranch %16 %16 = OpLabel - OpLoopMerge %17 %16 None - OpBranchConditional %8 %18 %16 + OpLoopMerge %17 %916 None + OpBranch %18 %18 = OpLabel OpLoopMerge %19 %20 None OpBranchConditional %8 %19 %21 @@ -767,8 +789,8 @@ TEST(TransformationMergeFunctionReturnsTest, NestedLoops) { %28 = OpLabel OpBranch %29 %29 = OpLabel - OpLoopMerge %30 %29 None - OpBranchConditional %8 %30 %29 + OpLoopMerge %30 %929 None + OpBranch %30 %30 = OpLabel OpLoopMerge %31 %32 None OpBranch %33 @@ -792,6 +814,10 @@ TEST(TransformationMergeFunctionReturnsTest, NestedLoops) { OpBranch %38 %38 = OpLabel OpReturnValue %12 + %916 = OpLabel + OpBranch %16 + %929 = OpLabel + OpBranch %29 OpFunctionEnd )"; @@ -805,7 +831,7 @@ TEST(TransformationMergeFunctionReturnsTest, NestedLoops) { MakeUnique(context.get()), validator_options); auto transformation = TransformationMergeFunctionReturns( - 14, 100, 101, 102, 11, + 14, 100, 200, 101, 102, 11, {{MakeReturnMergingInfo(19, 103, 104, {{}}), MakeReturnMergingInfo(17, 105, 106, {{}}), MakeReturnMergingInfo(31, 107, 108, {{{35, 10}, {36, 12}}}), @@ -841,11 +867,11 @@ TEST(TransformationMergeFunctionReturnsTest, NestedLoops) { %15 = OpLabel OpBranch %100 %100 = OpLabel - OpLoopMerge %101 %100 None - OpBranchConditional %8 %16 %100 + OpLoopMerge %101 %200 None + OpBranch %16 %16 = OpLabel - OpLoopMerge %17 %16 None - OpBranchConditional %8 %18 %16 + OpLoopMerge %17 %916 None + OpBranch %18 %18 = OpLabel OpLoopMerge %19 %20 None OpBranchConditional %8 %19 %21 @@ -872,8 +898,8 @@ TEST(TransformationMergeFunctionReturnsTest, NestedLoops) { %28 = OpLabel OpBranch %29 %29 = OpLabel - OpLoopMerge %30 %29 None - OpBranchConditional %8 %30 %29 + OpLoopMerge %30 %929 None + OpBranch %30 %30 = OpLabel OpLoopMerge %31 %32 None OpBranch %33 @@ -901,9 +927,15 @@ TEST(TransformationMergeFunctionReturnsTest, NestedLoops) { OpBranchConditional %109 %101 %38 %38 = OpLabel OpBranch %101 + %916 = OpLabel + OpBranch %16 + %929 = OpLabel + OpBranch %29 %101 = OpLabel %102 = OpPhi %5 %106 %17 %110 %23 %12 %38 OpReturnValue %102 + %200 = OpLabel + OpBranch %100 OpFunctionEnd )"; @@ -997,7 +1029,7 @@ TEST(TransformationMergeFunctionReturnsTest, OverflowIds) { // No mapping from merge block %16 to fresh ids is given, so overflow ids are // needed. auto transformation1 = - TransformationMergeFunctionReturns(12, 100, 101, 102, 10, {{}}); + TransformationMergeFunctionReturns(12, 100, 200, 101, 102, 10, {{}}); #ifndef NDEBUG ASSERT_DEATH( @@ -1016,7 +1048,7 @@ TEST(TransformationMergeFunctionReturnsTest, OverflowIds) { // No mapping from merge block %27 to fresh ids is given, so overflow ids are // needed. auto transformation2 = - TransformationMergeFunctionReturns(24, 110, 111, 0, 0, {{}}); + TransformationMergeFunctionReturns(24, 110, 210, 111, 0, 0, {{}}); #ifndef NDEBUG ASSERT_DEATH( @@ -1054,8 +1086,8 @@ TEST(TransformationMergeFunctionReturnsTest, OverflowIds) { %13 = OpLabel OpBranch %100 %100 = OpLabel - OpLoopMerge %101 %100 None - OpBranchConditional %8 %14 %100 + OpLoopMerge %101 %200 None + OpBranch %14 %14 = OpLabel %15 = OpIAdd %5 %10 %10 OpLoopMerge %16 %17 None @@ -1081,13 +1113,15 @@ TEST(TransformationMergeFunctionReturnsTest, OverflowIds) { %101 = OpLabel %102 = OpPhi %5 %1001 %16 %22 %23 OpReturnValue %102 + %200 = OpLabel + OpBranch %100 OpFunctionEnd %24 = OpFunction %3 None %4 %25 = OpLabel OpBranch %110 %110 = OpLabel - OpLoopMerge %111 %110 None - OpBranchConditional %8 %26 %110 + OpLoopMerge %111 %210 None + OpBranch %26 %26 = OpLabel OpLoopMerge %27 %28 None OpBranch %29 @@ -1110,6 +1144,8 @@ TEST(TransformationMergeFunctionReturnsTest, OverflowIds) { OpBranch %111 %111 = OpLabel OpReturn + %210 = OpLabel + OpBranch %110 OpFunctionEnd )"; @@ -1179,7 +1215,7 @@ TEST(TransformationMergeFunctionReturnsTest, MissingIdsForOpPhi) { // corresponding mapping. auto transformation = TransformationMergeFunctionReturns( - 12, 101, 102, 0, 0, + 12, 101, 200, 102, 0, 0, {{MakeReturnMergingInfo(17, 103, 0, {{{25, 7}, {35, 8}}})}}); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); @@ -1212,8 +1248,8 @@ TEST(TransformationMergeFunctionReturnsTest, MissingIdsForOpPhi) { %15 = OpConvertSToF %10 %13 OpBranch %101 %101 = OpLabel - OpLoopMerge %102 %101 None - OpBranchConditional %6 %16 %101 + OpLoopMerge %102 %200 None + OpBranch %16 %16 = OpLabel OpLoopMerge %17 %18 None OpBranch %19 @@ -1238,6 +1274,8 @@ TEST(TransformationMergeFunctionReturnsTest, MissingIdsForOpPhi) { OpBranch %102 %102 = OpLabel OpReturn + %200 = OpLabel + OpBranch %101 OpFunctionEnd )"; @@ -1324,14 +1362,14 @@ TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules1) { // block %14 is added. ASSERT_FALSE( TransformationMergeFunctionReturns( - 2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}) + 2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}) .IsApplicable(context.get(), transformation_context)); // In function %18, The definition of id %26 will still dominate its use in // instruction %27 (inside merge block %21), because %27 is an OpPhi // instruction. auto transformation = TransformationMergeFunctionReturns( - 18, 100, 101, 0, 0, {{MakeReturnMergingInfo(21, 102, 103, {{}})}}); + 18, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(21, 102, 103, {{}})}}); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); @@ -1376,8 +1414,8 @@ TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules1) { %19 = OpLabel OpBranch %100 %100 = OpLabel - OpLoopMerge %101 %100 None - OpBranchConditional %6 %20 %100 + OpLoopMerge %101 %200 None + OpBranch %20 %20 = OpLabel OpLoopMerge %21 %22 None OpBranch %23 @@ -1399,6 +1437,8 @@ TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules1) { OpBranch %101 %101 = OpLabel OpReturn + %200 = OpLabel + OpBranch %100 OpFunctionEnd )"; @@ -1482,17 +1522,17 @@ TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules2) { // block %14 to merge block %10 is added. ASSERT_FALSE( TransformationMergeFunctionReturns( - 2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}) + 2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}) .IsApplicable(context.get(), transformation_context)); // In function %18, the definition of id %26 will not dominate its use in // instruction %28 (inside block %27) after a new branch from return // block %25 to merge block %21 is added. - ASSERT_FALSE(TransformationMergeFunctionReturns( - 2, 100, 101, 0, 0, - {{MakeReturnMergingInfo(10, 102, 0, {{}}), - MakeReturnMergingInfo(21, 103, 0, {{}})}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationMergeFunctionReturns( + 2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 0, {{}}), + MakeReturnMergingInfo(21, 103, 0, {{}})}}) + .IsApplicable(context.get(), transformation_context)); } TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules3) { @@ -1556,7 +1596,7 @@ TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules3) { // fact that the id definition dominates the uses does not depend on it // dominating the merge block. auto transformation = TransformationMergeFunctionReturns( - 2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}); + 2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); @@ -1579,8 +1619,8 @@ TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules3) { %8 = OpLabel OpBranch %100 %100 = OpLabel - OpLoopMerge %101 %100 None - OpBranchConditional %6 %9 %100 + OpLoopMerge %101 %200 None + OpBranch %9 %9 = OpLabel OpLoopMerge %10 %11 None OpBranch %12 @@ -1608,6 +1648,8 @@ TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules3) { OpBranch %101 %101 = OpLabel OpReturn + %200 = OpLabel + OpBranch %100 OpFunctionEnd )"; @@ -1700,7 +1742,7 @@ TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules4) { // instruction %19 after the transformation is applied, because %13 dominates // all of the return blocks. auto transformation = TransformationMergeFunctionReturns( - 2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}); + 2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); @@ -1710,10 +1752,10 @@ TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules4) { // In function %20, the definition of id %28 will not dominate its use in // instruction %32 after the transformation is applied, because %28 dominates // only one of the return blocks. - ASSERT_FALSE( - TransformationMergeFunctionReturns( - 20, 100, 101, 0, 0, {{MakeReturnMergingInfo(23, 102, 103, {{}})}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationMergeFunctionReturns( + 20, 100, 200, 101, 0, 0, + {{MakeReturnMergingInfo(23, 102, 103, {{}})}}) + .IsApplicable(context.get(), transformation_context)); std::string after_transformation = R"( OpCapability Shader @@ -1731,8 +1773,8 @@ TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules4) { %8 = OpLabel OpBranch %100 %100 = OpLabel - OpLoopMerge %101 %100 None - OpBranchConditional %6 %9 %100 + OpLoopMerge %101 %200 None + OpBranch %9 %9 = OpLabel OpLoopMerge %10 %11 None OpBranch %12 @@ -1759,6 +1801,8 @@ TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules4) { OpBranch %101 %101 = OpLabel OpReturn + %200 = OpLabel + OpBranch %100 OpFunctionEnd %20 = OpFunction %3 None %4 %21 = OpLabel @@ -1830,7 +1874,7 @@ TEST(TransformationMergeFunctionReturnsTest, OpPhiAfterFirstBlock) { MakeUnique(context.get()), validator_options); auto transformation = - TransformationMergeFunctionReturns(2, 100, 101, 0, 0, {}); + TransformationMergeFunctionReturns(2, 100, 200, 101, 0, 0, {}); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); @@ -1863,8 +1907,8 @@ TEST(TransformationMergeFunctionReturnsTest, OpPhiAfterFirstBlock) { %8 = OpLabel OpBranch %100 %100 = OpLabel - OpLoopMerge %101 %100 None - OpBranchConditional %6 %9 %100 + OpLoopMerge %101 %200 None + OpBranch %9 %9 = OpLabel %10 = OpPhi %5 %6 %100 OpSelectionMerge %11 None @@ -1875,6 +1919,8 @@ TEST(TransformationMergeFunctionReturnsTest, OpPhiAfterFirstBlock) { OpBranch %101 %101 = OpLabel OpReturn + %200 = OpLabel + OpBranch %100 OpFunctionEnd )"; diff --git a/test/fuzz/transformation_mutate_pointer_test.cpp b/test/fuzz/transformation_mutate_pointer_test.cpp index ae423100..e869efa6 100644 --- a/test/fuzz/transformation_mutate_pointer_test.cpp +++ b/test/fuzz/transformation_mutate_pointer_test.cpp @@ -26,6 +26,7 @@ namespace { TEST(TransformationMutatePointerTest, BasicTest) { std::string shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -60,7 +61,6 @@ TEST(TransformationMutatePointerTest, BasicTest) { %23 = OpTypePointer Output %6 %24 = OpVariable %23 Output %27 = OpTypeFunction %2 %13 - %32 = OpUndef %16 %33 = OpConstantNull %16 %4 = OpFunction %2 None %3 %5 = OpLabel @@ -110,10 +110,6 @@ TEST(TransformationMutatePointerTest, BasicTest) { ASSERT_FALSE(TransformationMutatePointer(11, 70, insert_before) .IsApplicable(context.get(), transformation_context)); - // |pointer_id| is a result id of OpUndef. - ASSERT_FALSE(TransformationMutatePointer(32, 70, insert_before) - .IsApplicable(context.get(), transformation_context)); - // |pointer_id| is a result id of OpConstantNull. ASSERT_FALSE(TransformationMutatePointer(33, 70, insert_before) .IsApplicable(context.get(), transformation_context)); @@ -163,6 +159,7 @@ TEST(TransformationMutatePointerTest, BasicTest) { std::string after_transformation = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -197,7 +194,6 @@ TEST(TransformationMutatePointerTest, BasicTest) { %23 = OpTypePointer Output %6 %24 = OpVariable %23 Output %27 = OpTypeFunction %2 %13 - %32 = OpUndef %16 %33 = OpConstantNull %16 %4 = OpFunction %2 None %3 %5 = OpLabel diff --git a/test/fuzz/transformation_outline_function_test.cpp b/test/fuzz/transformation_outline_function_test.cpp index c567680c..cc62049d 100644 --- a/test/fuzz/transformation_outline_function_test.cpp +++ b/test/fuzz/transformation_outline_function_test.cpp @@ -3207,6 +3207,45 @@ TEST(TransformationOutlineFunctionTest, Miscellaneous4) { ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } +TEST(TransformationOutlineFunctionTest, NoOutlineWithUnreachableBlocks) { + // This checks that outlining will not be performed if a node in the region + // has an unreachable predecessor. + + 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 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %7 = OpLabel + OpBranch %5 + %5 = OpLabel + OpReturn + %6 = OpLabel + OpBranch %5 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation(5, 5, /* not relevant */ 200, + 100, 101, 102, 103, + /* not relevant */ 201, {}, {}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/test/fuzz/transformation_permute_phi_operands_test.cpp b/test/fuzz/transformation_permute_phi_operands_test.cpp index 3df399a5..0774dcf3 100644 --- a/test/fuzz/transformation_permute_phi_operands_test.cpp +++ b/test/fuzz/transformation_permute_phi_operands_test.cpp @@ -60,6 +60,7 @@ TEST(TransformationPermutePhiOperandsTest, BasicTest) { OpBranch %17 %17 = OpLabel %25 = OpPhi %6 %20 %16 %24 %21 + %30 = OpIAdd %6 %25 %25 OpStore %8 %25 OpReturn OpFunctionEnd @@ -121,6 +122,30 @@ TEST(TransformationPermutePhiOperandsTest, BasicTest) { return true; }); } + bool found_use_in_store = false; + bool found_use_in_add_lhs = false; + bool found_use_in_add_rhs = false; + context->get_def_use_mgr()->ForEachUse( + 25, [&found_use_in_store, &found_use_in_add_lhs, &found_use_in_add_rhs]( + opt::Instruction* inst, uint32_t operand_index) { + if (inst->opcode() == SpvOpStore) { + ASSERT_FALSE(found_use_in_store); + found_use_in_store = true; + } else { + ASSERT_EQ(SpvOpIAdd, inst->opcode()); + if (operand_index == 2) { + ASSERT_FALSE(found_use_in_add_lhs); + found_use_in_add_lhs = true; + } else { + ASSERT_EQ(3, operand_index); + ASSERT_FALSE(found_use_in_add_rhs); + found_use_in_add_rhs = true; + } + } + }); + ASSERT_TRUE(found_use_in_store); + ASSERT_TRUE(found_use_in_add_lhs); + ASSERT_TRUE(found_use_in_add_rhs); std::string after_transformation = R"( OpCapability Shader @@ -159,6 +184,7 @@ TEST(TransformationPermutePhiOperandsTest, BasicTest) { OpBranch %17 %17 = OpLabel %25 = OpPhi %6 %24 %21 %20 %16 + %30 = OpIAdd %6 %25 %25 OpStore %8 %25 OpReturn OpFunctionEnd diff --git a/test/fuzz/transformation_push_id_through_variable_test.cpp b/test/fuzz/transformation_push_id_through_variable_test.cpp index 7487cab9..b0fff588 100644 --- a/test/fuzz/transformation_push_id_through_variable_test.cpp +++ b/test/fuzz/transformation_push_id_through_variable_test.cpp @@ -26,6 +26,7 @@ namespace { TEST(TransformationPushIdThroughVariableTest, IsApplicable) { std::string reference_shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %92 %52 %53 @@ -50,7 +51,6 @@ TEST(TransformationPushIdThroughVariableTest, IsApplicable) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -257,6 +257,7 @@ TEST(TransformationPushIdThroughVariableTest, IsApplicable) { TEST(TransformationPushIdThroughVariableTest, Apply) { std::string reference_shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %92 %52 %53 @@ -281,7 +282,6 @@ TEST(TransformationPushIdThroughVariableTest, Apply) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -419,6 +419,7 @@ TEST(TransformationPushIdThroughVariableTest, Apply) { std::string variant_shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %92 %52 %53 %109 %111 @@ -443,7 +444,6 @@ TEST(TransformationPushIdThroughVariableTest, Apply) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -519,6 +519,7 @@ TEST(TransformationPushIdThroughVariableTest, Apply) { TEST(TransformationPushIdThroughVariableTest, AddSynonymsForRelevantIds) { std::string reference_shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %92 %52 %53 @@ -543,7 +544,6 @@ TEST(TransformationPushIdThroughVariableTest, AddSynonymsForRelevantIds) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -620,6 +620,7 @@ TEST(TransformationPushIdThroughVariableTest, AddSynonymsForRelevantIds) { TEST(TransformationPushIdThroughVariableTest, DontAddSynonymsForIrrelevantIds) { std::string reference_shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %92 %52 %53 @@ -644,7 +645,6 @@ TEST(TransformationPushIdThroughVariableTest, DontAddSynonymsForIrrelevantIds) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 diff --git a/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/test/fuzz/transformation_replace_id_with_synonym_test.cpp index 629a00ee..b33dd48c 100644 --- a/test/fuzz/transformation_replace_id_with_synonym_test.cpp +++ b/test/fuzz/transformation_replace_id_with_synonym_test.cpp @@ -1796,6 +1796,383 @@ TEST(TransformationReplaceIdWithSynonymTest, IncompatibleTypes) { .IsApplicable(context.get(), transformation_context)); } +TEST(TransformationReplaceIdWithSynonymTest, + AtomicScopeAndMemorySemanticsMustBeConstant) { + const std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 320 + OpSourceExtension "GL_KHR_memory_scope_semantics" + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 Block + OpDecorate %11 DescriptorSet 0 + OpDecorate %11 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %17 = OpTypeInt 32 0 + %7 = OpTypePointer Function %6 + %9 = OpTypeStruct %6 + %10 = OpTypePointer StorageBuffer %9 + %11 = OpVariable %10 StorageBuffer + %86 = OpTypeStruct %17 + %87 = OpTypePointer Workgroup %86 + %88 = OpVariable %87 Workgroup + %12 = OpConstant %6 0 + %13 = OpTypePointer StorageBuffer %6 + %15 = OpConstant %6 2 + %16 = OpConstant %6 64 + %89 = OpTypePointer Workgroup %17 + %18 = OpConstant %17 1 + %19 = OpConstant %17 0 + %20 = OpConstant %17 64 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %100 = OpCopyObject %6 %15 ; A non-constant version of %15 + %101 = OpCopyObject %17 %20 ; A non-constant version of %20 + %14 = OpAccessChain %13 %11 %12 + %90 = OpAccessChain %89 %88 %19 + %21 = OpAtomicLoad %6 %14 %15 %20 + %22 = OpAtomicExchange %6 %14 %15 %20 %16 + %23 = OpAtomicCompareExchange %6 %14 %15 %20 %12 %16 %15 + %24 = OpAtomicIIncrement %6 %14 %15 %20 + %25 = OpAtomicIDecrement %6 %14 %15 %20 + %26 = OpAtomicIAdd %6 %14 %15 %20 %16 + %27 = OpAtomicISub %6 %14 %15 %20 %16 + %28 = OpAtomicSMin %6 %14 %15 %20 %16 + %29 = OpAtomicUMin %17 %90 %15 %20 %18 + %30 = OpAtomicSMax %6 %14 %15 %20 %15 + %31 = OpAtomicUMax %17 %90 %15 %20 %18 + %32 = OpAtomicAnd %6 %14 %15 %20 %16 + %33 = OpAtomicOr %6 %14 %15 %20 %16 + %34 = OpAtomicXor %6 %14 %15 %20 %16 + OpStore %8 %21 + OpAtomicStore %14 %15 %20 %12 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Tell the fact manager that %100 and %15 are synonymous + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(100, {}), MakeDataDescriptor(15, {})); + + // Tell the fact manager that %101 and %20 are synonymous + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(101, {}), MakeDataDescriptor(20, {})); + // OpAtomicLoad + const auto& scope_operand = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(21), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(21), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand, 101) + .IsApplicable(context.get(), transformation_context)); + // OpAtomicExchange. + const auto& scope_operand2 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(22), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand2, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand2 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(22), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand2, 101) + .IsApplicable(context.get(), transformation_context)); + // OpAtomicCompareExchange. + const auto& scope_operand3 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(23), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand3, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_equal_operand3 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(23), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_equal_operand3, 101) + .IsApplicable(context.get(), transformation_context)); + const auto& semantics_unequal_operand3 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(23), 3); + ASSERT_FALSE( + TransformationReplaceIdWithSynonym(semantics_unequal_operand3, 101) + .IsApplicable(context.get(), transformation_context)); + // OpAtomicIIncrement. + const auto& scope_operand4 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(24), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand4, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand4 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(24), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand4, 101) + .IsApplicable(context.get(), transformation_context)); + + // OpAtomicIDecrement. + const auto& scope_operand5 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(25), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand5, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand5 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(25), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand5, 101) + .IsApplicable(context.get(), transformation_context)); + + // OpAtomicIAdd. + const auto& scope_operand6 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(26), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand6, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand6 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(26), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand6, 101) + .IsApplicable(context.get(), transformation_context)); + // OpAtomicISub + const auto& scope_operand8 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(27), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand8, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand8 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(27), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand8, 101) + .IsApplicable(context.get(), transformation_context)); + + // OpAtomicSMin + const auto& scope_operand9 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(28), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand9, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand9 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(28), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand9, 101) + .IsApplicable(context.get(), transformation_context)); + // OpAtomicUMin + const auto& scope_operand10 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(29), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand10, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand10 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(29), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand10, 101) + .IsApplicable(context.get(), transformation_context)); + + // OpAtomicSMax + const auto& scope_operand11 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(30), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand11, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand11 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(30), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand11, 101) + .IsApplicable(context.get(), transformation_context)); + // OpAtomicUMax + const auto& scope_operand12 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(31), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand12, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand12 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(31), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand12, 101) + .IsApplicable(context.get(), transformation_context)); + + // OpAtomicAnd + const auto& scope_operand13 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(32), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand13, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand13 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(32), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand13, 101) + .IsApplicable(context.get(), transformation_context)); + + // OpAtomicOr + const auto& scope_operand14 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(33), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand14, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand14 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(33), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand14, 101) + .IsApplicable(context.get(), transformation_context)); + + // OpAtomicXor + const auto& scope_operand15 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(34), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand15, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand15 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(34), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand15, 101) + .IsApplicable(context.get(), transformation_context)); +} + +// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4345): Improve this +// test so that it covers more atomic operations, and enable the test once the +// issue is fixed. +TEST(TransformationReplaceIdWithSynonymTest, + DISABLED_SignOfAtomicScopeAndMemorySemanticsDoesNotMatter) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4345): both the + // GLSL comment and the corresponding SPIR-V should be updated to cover a + // larger number of atomic operations. + // The following SPIR-V came from this GLSL, edited to add some synonyms: + // + // #version 320 es + // + // #extension GL_KHR_memory_scope_semantics : enable + // + // layout(set = 0, binding = 0) buffer Buf { + // int x; + // }; + // + // void main() { + // int tmp = atomicLoad(x, + // gl_ScopeWorkgroup, + // gl_StorageSemanticsBuffer, + // gl_SemanticsRelaxed); + // } + const std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 320 + OpSourceExtension "GL_KHR_memory_scope_semantics" + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 Block + OpDecorate %11 DescriptorSet 0 + OpDecorate %11 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpTypeStruct %6 + %10 = OpTypePointer StorageBuffer %9 + %11 = OpVariable %10 StorageBuffer + %12 = OpConstant %6 0 + %13 = OpTypePointer StorageBuffer %6 + %15 = OpConstant %6 2 + %16 = OpConstant %6 64 + %17 = OpTypeInt 32 0 + %100 = OpConstant %17 2 ; The same as %15, but with unsigned int type + %18 = OpConstant %17 1 + %19 = OpConstant %17 0 + %20 = OpConstant %17 64 + %101 = OpConstant %6 64 ; The same as %20, but with signed int type + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %14 = OpAccessChain %13 %11 %12 + %21 = OpAtomicLoad %6 %14 %15 %20 + OpStore %8 %21 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Tell the fact manager that %100 and %15 are synonymous + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(100, {}), MakeDataDescriptor(15, {})); + + // Tell the fact manager that %101 and %20 are synonymous + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(101, {}), MakeDataDescriptor(20, {})); + + { + const auto& scope_operand = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(21), 1); + TransformationReplaceIdWithSynonym replace_scope(scope_operand, 100); + ASSERT_TRUE( + replace_scope.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replace_scope, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + { + const auto& semantics_operand = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(21), 2); + TransformationReplaceIdWithSynonym replace_semantics(semantics_operand, + 101); + ASSERT_TRUE( + replace_semantics.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replace_semantics, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + const std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 320 + OpSourceExtension "GL_KHR_memory_scope_semantics" + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 Block + OpDecorate %11 DescriptorSet 0 + OpDecorate %11 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpTypeStruct %6 + %10 = OpTypePointer StorageBuffer %9 + %11 = OpVariable %10 StorageBuffer + %12 = OpConstant %6 0 + %13 = OpTypePointer StorageBuffer %6 + %15 = OpConstant %6 2 + %16 = OpConstant %6 64 + %17 = OpTypeInt 32 0 + %100 = OpConstant %17 2 + %18 = OpConstant %17 1 + %19 = OpConstant %17 0 + %20 = OpConstant %17 64 + %101 = OpConstant %6 64 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %14 = OpAccessChain %13 %11 %12 + %21 = OpAtomicLoad %6 %14 %100 %101 + OpStore %8 %21 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/test/fuzz/transformation_store_test.cpp b/test/fuzz/transformation_store_test.cpp index 93257d0d..dd653e28 100644 --- a/test/fuzz/transformation_store_test.cpp +++ b/test/fuzz/transformation_store_test.cpp @@ -26,6 +26,7 @@ namespace { TEST(TransformationStoreTest, BasicTest) { std::string shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %92 %52 %53 @@ -50,7 +51,6 @@ TEST(TransformationStoreTest, BasicTest) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -148,90 +148,107 @@ TEST(TransformationStoreTest, BasicTest) { // 61 - undefined // Bad: attempt to store to 11 from outside its function - ASSERT_FALSE(TransformationStore( - 11, 80, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(11, false, 0, 0, 80, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: pointer is not available - ASSERT_FALSE(TransformationStore( - 81, 80, MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(81, false, 0, 0, 80, + MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to insert before OpVariable - ASSERT_FALSE(TransformationStore( - 52, 24, MakeInstructionDescriptor(27, SpvOpVariable, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(52, false, 0, 0, 24, + MakeInstructionDescriptor(27, SpvOpVariable, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: pointer id does not exist - ASSERT_FALSE(TransformationStore( - 1000, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(1000, false, 0, 0, 24, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: pointer id exists but does not have a type - ASSERT_FALSE(TransformationStore( - 5, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(5, false, 0, 0, 24, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: pointer id exists and has a type, but is not a pointer - ASSERT_FALSE(TransformationStore( - 24, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(24, false, 0, 0, 24, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to store to a null pointer - ASSERT_FALSE(TransformationStore( - 60, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(60, false, 0, 0, 24, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to store to an undefined pointer - ASSERT_FALSE(TransformationStore( - 61, 21, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(61, false, 0, 0, 21, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: %82 is not available at the program point ASSERT_FALSE( - TransformationStore(82, 80, MakeInstructionDescriptor(37, SpvOpReturn, 0)) + TransformationStore(82, false, 0, 0, 80, + MakeInstructionDescriptor(37, SpvOpReturn, 0)) .IsApplicable(context.get(), transformation_context)); // Bad: value id does not exist - ASSERT_FALSE(TransformationStore( - 27, 1000, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(27, false, 0, 0, 1000, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: value id exists but does not have a type - ASSERT_FALSE(TransformationStore( - 27, 15, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(27, false, 0, 0, 15, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: value id exists but has the wrong type - ASSERT_FALSE(TransformationStore( - 27, 14, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(27, false, 0, 0, 14, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to store to read-only variable - ASSERT_FALSE(TransformationStore( - 92, 93, MakeInstructionDescriptor(40, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(92, false, 0, 0, 93, + MakeInstructionDescriptor(40, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: value is not available - ASSERT_FALSE(TransformationStore( - 27, 95, MakeInstructionDescriptor(40, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(27, false, 0, 0, 95, + MakeInstructionDescriptor(40, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: variable being stored to does not have an irrelevant pointee value, // and the store is not in a dead block. - ASSERT_FALSE(TransformationStore( - 20, 95, MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(20, false, 0, 0, 95, + MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) + .IsApplicable(context.get(), transformation_context)); // The described instruction does not exist. - ASSERT_FALSE(TransformationStore( - 27, 80, MakeInstructionDescriptor(1000, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(27, false, 0, 0, 80, + MakeInstructionDescriptor(1000, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); { // Store to irrelevant variable from dead block. TransformationStore transformation( - 27, 80, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); + 27, false, 0, 0, 80, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -243,7 +260,8 @@ TEST(TransformationStoreTest, BasicTest) { { // Store to irrelevant variable from live block. TransformationStore transformation( - 11, 95, MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); + 11, false, 0, 0, 95, + MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -255,7 +273,8 @@ TEST(TransformationStoreTest, BasicTest) { { // Store to irrelevant variable from live block. TransformationStore transformation( - 46, 80, MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); + 46, false, 0, 0, 80, + MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -267,7 +286,8 @@ TEST(TransformationStoreTest, BasicTest) { { // Store to irrelevant variable from live block. TransformationStore transformation( - 16, 21, MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); + 16, false, 0, 0, 21, + MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -279,7 +299,8 @@ TEST(TransformationStoreTest, BasicTest) { { // Store to non-irrelevant variable from dead block. TransformationStore transformation( - 53, 21, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); + 53, false, 0, 0, 21, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -290,6 +311,7 @@ TEST(TransformationStoreTest, BasicTest) { std::string after_transformation = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %92 %52 %53 @@ -314,7 +336,6 @@ TEST(TransformationStoreTest, BasicTest) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -414,16 +435,190 @@ TEST(TransformationStoreTest, DoNotAllowStoresToReadOnlyMemory) { transformation_context.GetFactManager()->AddFactBlockIsDead(5); ASSERT_FALSE( - TransformationStore(15, 13, MakeInstructionDescriptor(27, SpvOpReturn, 0)) + TransformationStore(15, false, 0, 0, 13, + MakeInstructionDescriptor(27, SpvOpReturn, 0)) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( - TransformationStore(19, 50, MakeInstructionDescriptor(27, SpvOpReturn, 0)) + TransformationStore(19, false, 0, 0, 50, + MakeInstructionDescriptor(27, SpvOpReturn, 0)) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( - TransformationStore(27, 50, MakeInstructionDescriptor(27, SpvOpReturn, 0)) + TransformationStore(27, false, 0, 0, 50, + MakeInstructionDescriptor(27, SpvOpReturn, 0)) .IsApplicable(context.get(), transformation_context)); } +TEST(TransformationStoreTest, SupportAtomicStore) { + const std::string shader = R"( + OpCapability Shader + OpCapability Int8 + %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 + %7 = OpTypeInt 8 1 + %9 = OpTypeInt 32 0 + %26 = OpTypeFloat 32 + %8 = OpTypeStruct %6 + %10 = OpTypePointer StorageBuffer %8 + %11 = OpVariable %10 StorageBuffer + %19 = OpConstant %26 0 + %18 = OpConstant %9 1 + %12 = OpConstant %6 0 + %13 = OpTypePointer StorageBuffer %6 + %15 = OpConstant %6 4 + %16 = OpConstant %6 7 + %17 = OpConstant %7 4 + %20 = OpConstant %9 64 + %21 = OpConstant %6 15 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %14 = OpAccessChain %13 %11 %12 + %24 = OpAccessChain %13 %11 %12 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 14); + + // Bad: id 100 of memory scope instruction does not exist. + ASSERT_FALSE( + TransformationStore(14, true, 100, 20, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: id 100 of memory semantics instruction does not exist. + ASSERT_FALSE( + TransformationStore(14, true, 15, 100, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: memory scope should be |OpConstant| opcode. + ASSERT_FALSE( + TransformationStore(14, true, 5, 20, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: memory semantics should be |OpConstant| opcode. + ASSERT_FALSE( + TransformationStore(14, true, 15, 5, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: The memory scope instruction must have an Integer operand. + ASSERT_FALSE( + TransformationStore(14, true, 15, 19, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: The memory memory semantics instruction must have an Integer operand. + ASSERT_FALSE( + TransformationStore(14, true, 19, 20, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Integer size of the memory scope must be equal to 32 bits. + ASSERT_FALSE( + TransformationStore(14, true, 17, 20, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Integer size of memory semantics must be equal to 32 bits. + ASSERT_FALSE( + TransformationStore(14, true, 15, 17, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: memory scope value must be 4 (SpvScopeInvocation). + ASSERT_FALSE( + TransformationStore(14, true, 16, 20, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: memory semantics value must be either: + // 64 (SpvMemorySemanticsUniformMemoryMask) + // 256 (SpvMemorySemanticsWorkgroupMemoryMask) + ASSERT_FALSE( + TransformationStore(14, true, 15, 16, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: The described instruction does not exist + ASSERT_FALSE( + TransformationStore(14, true, 15, 20, 21, + MakeInstructionDescriptor(150, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Can't insert OpAccessChain before the id 15 of memory scope. + ASSERT_FALSE( + TransformationStore(14, true, 15, 20, 21, + MakeInstructionDescriptor(15, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Can't insert OpAccessChain before the id 20 of memory semantics. + ASSERT_FALSE( + TransformationStore(14, true, 15, 20, 21, + MakeInstructionDescriptor(20, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Successful transformations. + { + TransformationStore transformation( + 14, true, 15, 20, 21, MakeInstructionDescriptor(24, SpvOpReturn, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + const std::string after_transformation = R"( + OpCapability Shader + OpCapability Int8 + %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 + %7 = OpTypeInt 8 1 + %9 = OpTypeInt 32 0 + %26 = OpTypeFloat 32 + %8 = OpTypeStruct %6 + %10 = OpTypePointer StorageBuffer %8 + %11 = OpVariable %10 StorageBuffer + %19 = OpConstant %26 0 + %18 = OpConstant %9 1 + %12 = OpConstant %6 0 + %13 = OpTypePointer StorageBuffer %6 + %15 = OpConstant %6 4 + %16 = OpConstant %6 7 + %17 = OpConstant %7 4 + %20 = OpConstant %9 64 + %21 = OpConstant %6 15 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %14 = OpAccessChain %13 %11 %12 + %24 = OpAccessChain %13 %11 %12 + OpAtomicStore %14 %15 %20 %21 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/test/fuzz/transformation_swap_function_variables_test.cpp b/test/fuzz/transformation_swap_function_variables_test.cpp new file mode 100644 index 00000000..8132aa4b --- /dev/null +++ b/test/fuzz/transformation_swap_function_variables_test.cpp @@ -0,0 +1,288 @@ +// Copyright (c) 2021 Mostafa Ashraf +// +// 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_function_variables.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationSwapFunctionVariables, NotApplicable) { + 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 + %7 = OpTypePointer Function %6 + %8 = OpTypeFloat 32 + %9 = OpTypePointer Function %8 + %10 = OpTypeVector %8 2 + %11 = OpTypePointer Function %10 + %12 = OpTypeVector %8 3 + %13 = OpTypeMatrix %12 3 + %14 = OpTypePointer Function %13 + %15 = OpTypeFunction %2 %7 %9 %11 %14 %7 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %24 = OpVariable %7 Function + %25 = OpVariable %9 Function + %26 = OpVariable %11 Function + %27 = OpVariable %14 Function + %28 = OpVariable %7 Function + %29 = OpVariable %7 Function + %30 = OpVariable %7 Function + %32 = OpVariable %9 Function + %34 = OpVariable %11 Function + %36 = OpVariable %14 Function + %38 = OpVariable %7 Function + %40 = OpVariable %7 Function + %31 = OpLoad %6 %24 + OpStore %30 %31 + %33 = OpLoad %8 %25 + OpStore %32 %33 + %35 = OpLoad %10 %26 + OpStore %34 %35 + %37 = OpLoad %13 %27 + OpStore %36 %37 + %39 = OpLoad %6 %28 + OpStore %38 %39 + %41 = OpLoad %6 %29 + OpStore %40 %41 + %42 = OpFunctionCall %2 %22 %30 %32 %34 %36 %38 %40 + OpReturn + OpFunctionEnd + %22 = OpFunction %2 None %15 + %16 = OpFunctionParameter %7 + %17 = OpFunctionParameter %9 + %18 = OpFunctionParameter %11 + %19 = OpFunctionParameter %14 + %20 = OpFunctionParameter %7 + %21 = OpFunctionParameter %7 + %23 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + +#ifndef NDEBUG + // Can't swap variable with itself. + ASSERT_DEATH(TransformationSwapFunctionVariables(7, 7).IsApplicable( + context.get(), transformation_context), + "Two results ids are equal"); +#endif + + // Invalid because 200 is not the id of an instruction. + ASSERT_FALSE(TransformationSwapFunctionVariables(1, 200).IsApplicable( + context.get(), transformation_context)); + // Invalid because 5 is not the id of an instruction. + ASSERT_FALSE(TransformationSwapFunctionVariables(5, 24).IsApplicable( + context.get(), transformation_context)); + // Can't swap two instructions from two different blocks. + ASSERT_FALSE(TransformationSwapFunctionVariables(16, 26).IsApplicable( + context.get(), transformation_context)); +} + +TEST(TransformationSwapFunctionVariables, IsApplicable) { + 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 + %7 = OpTypePointer Function %6 + %8 = OpTypeFloat 32 + %9 = OpTypePointer Function %8 + %10 = OpTypeVector %8 2 + %11 = OpTypePointer Function %10 + %12 = OpTypeVector %8 3 + %13 = OpTypeMatrix %12 3 + %14 = OpTypePointer Function %13 + %15 = OpTypeFunction %2 %7 %9 %11 %14 %7 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %24 = OpVariable %7 Function + %25 = OpVariable %9 Function + %26 = OpVariable %11 Function + %27 = OpVariable %14 Function + %28 = OpVariable %7 Function + %29 = OpVariable %7 Function + %30 = OpVariable %7 Function + %32 = OpVariable %9 Function + %34 = OpVariable %11 Function + %36 = OpVariable %14 Function + %38 = OpVariable %7 Function + %40 = OpVariable %7 Function + %31 = OpLoad %6 %24 + OpStore %30 %31 + %33 = OpLoad %8 %25 + OpStore %32 %33 + %35 = OpLoad %10 %26 + OpStore %34 %35 + %37 = OpLoad %13 %27 + OpStore %36 %37 + %39 = OpLoad %6 %28 + OpStore %38 %39 + %41 = OpLoad %6 %29 + OpStore %40 %41 + %42 = OpFunctionCall %2 %22 %30 %32 %34 %36 %38 %40 + OpReturn + OpFunctionEnd + %22 = OpFunction %2 None %15 + %16 = OpFunctionParameter %7 + %17 = OpFunctionParameter %9 + %18 = OpFunctionParameter %11 + %19 = OpFunctionParameter %14 + %20 = OpFunctionParameter %7 + %21 = OpFunctionParameter %7 + %23 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + // Get Unique pointer of IRContext. + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Successful transformations + { + auto first_instruction = context->get_def_use_mgr()->GetDef(24); + auto second_instruction = context->get_def_use_mgr()->GetDef(28); + // Swap two OpVariable instructions in the same function. + TransformationSwapFunctionVariables transformation(24, 28); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + + ASSERT_EQ(first_instruction, context->get_def_use_mgr()->GetDef(24)); + ASSERT_EQ(second_instruction, context->get_def_use_mgr()->GetDef(28)); + } + { + auto first_instruction = context->get_def_use_mgr()->GetDef(38); + auto second_instruction = context->get_def_use_mgr()->GetDef(40); + // Swap two OpVariable instructions in the same function. + TransformationSwapFunctionVariables transformation(38, 40); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + + ASSERT_EQ(first_instruction, context->get_def_use_mgr()->GetDef(38)); + ASSERT_EQ(second_instruction, context->get_def_use_mgr()->GetDef(40)); + } + std::string after_transformation = 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 + %7 = OpTypePointer Function %6 + %8 = OpTypeFloat 32 + %9 = OpTypePointer Function %8 + %10 = OpTypeVector %8 2 + %11 = OpTypePointer Function %10 + %12 = OpTypeVector %8 3 + %13 = OpTypeMatrix %12 3 + %14 = OpTypePointer Function %13 + %15 = OpTypeFunction %2 %7 %9 %11 %14 %7 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %28 = OpVariable %7 Function + %25 = OpVariable %9 Function + %26 = OpVariable %11 Function + %27 = OpVariable %14 Function + %24 = OpVariable %7 Function + %29 = OpVariable %7 Function + %30 = OpVariable %7 Function + %32 = OpVariable %9 Function + %34 = OpVariable %11 Function + %36 = OpVariable %14 Function + %40 = OpVariable %7 Function + %38 = OpVariable %7 Function + %31 = OpLoad %6 %24 + OpStore %30 %31 + %33 = OpLoad %8 %25 + OpStore %32 %33 + %35 = OpLoad %10 %26 + OpStore %34 %35 + %37 = OpLoad %13 %27 + OpStore %36 %37 + %39 = OpLoad %6 %28 + OpStore %38 %39 + %41 = OpLoad %6 %29 + OpStore %40 %41 + %42 = OpFunctionCall %2 %22 %30 %32 %34 %36 %38 %40 + OpReturn + OpFunctionEnd + %22 = OpFunction %2 None %15 + %16 = OpFunctionParameter %7 + %17 = OpFunctionParameter %9 + %18 = OpFunctionParameter %11 + %19 = OpFunctionParameter %14 + %20 = OpFunctionParameter %7 + %21 = OpFunctionParameter %7 + %23 = OpLabel + OpReturn + OpFunctionEnd +)"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/test/fuzz/transformation_swap_two_functions_test.cpp b/test/fuzz/transformation_swap_two_functions_test.cpp new file mode 100644 index 00000000..20342526 --- /dev/null +++ b/test/fuzz/transformation_swap_two_functions_test.cpp @@ -0,0 +1,257 @@ +// Copyright (c) 2021 Shiyu Liu +// +// 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_two_functions.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationSwapTwoFunctionsTest, SimpleTest) { + // float multiplyBy2(in float value) { + // return value*2.0; + // } + + // float multiplyBy4(in float value) { + // return multiplyBy2(value)*2.0; + // } + + // float multiplyBy8(in float value) { + // return multiplyBy2(value)*multiplyBy4(value); + // } + + // layout(location=0) in float value; + // void main() { //4 + // multiplyBy2(3.7); //10 + // multiplyBy4(3.9); //13 + // multiplyBy8(5.0); //16 + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %48 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "multiplyBy2(f1;" + OpName %9 "value" + OpName %13 "multiplyBy4(f1;" + OpName %12 "value" + OpName %16 "multiplyBy8(f1;" + OpName %15 "value" + OpName %23 "param" + OpName %29 "param" + OpName %32 "param" + OpName %39 "param" + OpName %42 "param" + OpName %45 "param" + OpName %48 "value" + OpDecorate %48 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %6 %7 + %19 = OpConstant %6 2 + %38 = OpConstant %6 3.70000005 + %41 = OpConstant %6 3.9000001 + %44 = OpConstant %6 5 + %47 = OpTypePointer Input %6 + %48 = OpVariable %47 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %39 = OpVariable %7 Function + %42 = OpVariable %7 Function + %45 = OpVariable %7 Function + OpStore %39 %38 + %40 = OpFunctionCall %6 %10 %39 + OpStore %42 %41 + %43 = OpFunctionCall %6 %13 %42 + OpStore %45 %44 + %46 = OpFunctionCall %6 %16 %45 + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %18 = OpLoad %6 %9 + %20 = OpFMul %6 %18 %19 + OpReturnValue %20 + OpFunctionEnd + %13 = OpFunction %6 None %8 + %12 = OpFunctionParameter %7 + %14 = OpLabel + %23 = OpVariable %7 Function + %24 = OpLoad %6 %12 + OpStore %23 %24 + %25 = OpFunctionCall %6 %10 %23 + %26 = OpFMul %6 %25 %19 + OpReturnValue %26 + OpFunctionEnd + %16 = OpFunction %6 None %8 + %15 = OpFunctionParameter %7 + %17 = OpLabel + %29 = OpVariable %7 Function + %32 = OpVariable %7 Function + %30 = OpLoad %6 %15 + OpStore %29 %30 + %31 = OpFunctionCall %6 %10 %29 + %33 = OpLoad %6 %15 + OpStore %32 %33 + %34 = OpFunctionCall %6 %13 %32 + %35 = OpFMul %6 %31 %34 + OpReturnValue %35 + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + // Check context validity. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + +#ifndef NDEBUG + // Function should not swap with itself. + ASSERT_DEATH(TransformationSwapTwoFunctions(4, 4).IsApplicable( + context.get(), transformation_context), + "The two function ids cannot be the same."); +#endif + + // Function with id 29 does not exist. + ASSERT_FALSE(TransformationSwapTwoFunctions(10, 29).IsApplicable( + context.get(), transformation_context)); + + // Function with id 30 does not exist. + ASSERT_FALSE(TransformationSwapTwoFunctions(30, 13).IsApplicable( + context.get(), transformation_context)); + + // Both functions with id 5 and 6 do not exist. + ASSERT_FALSE(TransformationSwapTwoFunctions(5, 6).IsApplicable( + context.get(), transformation_context)); + + // Function with result_id 10 and 13 should swap successfully. + auto swap_test5 = TransformationSwapTwoFunctions(10, 13); + ASSERT_TRUE(swap_test5.IsApplicable(context.get(), transformation_context)); + + // Get the definitions of functions 10 and 13, as recorded by the def-use + // manager. + auto def_use_manager = context->get_def_use_mgr(); + auto function_10_inst = def_use_manager->GetDef(10); + auto function_13_inst = def_use_manager->GetDef(13); + + ApplyAndCheckFreshIds(swap_test5, context.get(), &transformation_context); + + // Check that def-use information for functions 10 and 13 has been preserved + // by the transformation. + ASSERT_EQ(function_10_inst, def_use_manager->GetDef(10)); + ASSERT_EQ(function_13_inst, def_use_manager->GetDef(13)); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %48 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "multiplyBy2(f1;" + OpName %9 "value" + OpName %13 "multiplyBy4(f1;" + OpName %12 "value" + OpName %16 "multiplyBy8(f1;" + OpName %15 "value" + OpName %23 "param" + OpName %29 "param" + OpName %32 "param" + OpName %39 "param" + OpName %42 "param" + OpName %45 "param" + OpName %48 "value" + OpDecorate %48 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %6 %7 + %19 = OpConstant %6 2 + %38 = OpConstant %6 3.70000005 + %41 = OpConstant %6 3.9000001 + %44 = OpConstant %6 5 + %47 = OpTypePointer Input %6 + %48 = OpVariable %47 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %39 = OpVariable %7 Function + %42 = OpVariable %7 Function + %45 = OpVariable %7 Function + OpStore %39 %38 + %40 = OpFunctionCall %6 %10 %39 + OpStore %42 %41 + %43 = OpFunctionCall %6 %13 %42 + OpStore %45 %44 + %46 = OpFunctionCall %6 %16 %45 + OpReturn + OpFunctionEnd + %13 = OpFunction %6 None %8 + %12 = OpFunctionParameter %7 + %14 = OpLabel + %23 = OpVariable %7 Function + %24 = OpLoad %6 %12 + OpStore %23 %24 + %25 = OpFunctionCall %6 %10 %23 + %26 = OpFMul %6 %25 %19 + OpReturnValue %26 + OpFunctionEnd + %10 = OpFunction %6 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %18 = OpLoad %6 %9 + %20 = OpFMul %6 %18 %19 + OpReturnValue %20 + OpFunctionEnd + %16 = OpFunction %6 None %8 + %15 = OpFunctionParameter %7 + %17 = OpLabel + %29 = OpVariable %7 Function + %32 = OpVariable %7 Function + %30 = OpLoad %6 %15 + OpStore %29 %30 + %31 = OpFunctionCall %6 %10 %29 + %33 = OpLoad %6 %15 + OpStore %32 %33 + %34 = OpFunctionCall %6 %13 %32 + %35 = OpFMul %6 %31 %34 + OpReturnValue %35 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/test/fuzz/transformation_wrap_vector_synonym_test.cpp b/test/fuzz/transformation_wrap_vector_synonym_test.cpp new file mode 100644 index 00000000..0d1009b3 --- /dev/null +++ b/test/fuzz/transformation_wrap_vector_synonym_test.cpp @@ -0,0 +1,1553 @@ +// Copyright (c) 2021 Shiyu Liu +// +// 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_wrap_vector_synonym.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationWrapVectorSynonym, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %97 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %11 = OpConstant %6 -5 + %12 = OpTypeVector %6 2 + %13 = OpTypePointer Function %12 + %18 = OpTypeInt 32 0 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 8 + %23 = OpConstant %18 2 + %24 = OpTypeVector %18 3 + %25 = OpTypePointer Function %24 + %31 = OpTypeFloat 32 + %32 = OpTypePointer Function %31 + %34 = OpConstant %31 3.29999995 + %36 = OpConstant %31 1.10000002 + %37 = OpTypeVector %31 4 + %38 = OpTypePointer Function %37 + %96 = OpTypePointer Input %31 + %97 = OpVariable %96 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %33 = OpVariable %32 Function + %35 = OpVariable %32 Function + %39 = OpVariable %38 Function + %47 = OpVariable %7 Function + %51 = OpVariable %7 Function + %55 = OpVariable %7 Function + %59 = OpVariable %7 Function + %63 = OpVariable %19 Function + %67 = OpVariable %19 Function + %71 = OpVariable %19 Function + %75 = OpVariable %19 Function + %79 = OpVariable %32 Function + %83 = OpVariable %32 Function + %87 = OpVariable %32 Function + %91 = OpVariable %32 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + OpStore %20 %21 + OpStore %22 %23 + %27 = OpLoad %18 %20 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %22 + %30 = OpCompositeConstruct %24 %27 %28 %29 + OpStore %26 %30 + OpStore %33 %34 + OpStore %35 %36 + %40 = OpLoad %31 %33 + %41 = OpLoad %31 %33 + %42 = OpLoad %31 %35 + %43 = OpLoad %31 %35 + %44 = OpCompositeConstruct %37 %40 %41 %42 %43 + %45 = OpLoad %37 %39 + %46 = OpVectorShuffle %37 %45 %44 5 6 7 4 + OpStore %39 %46 + %48 = OpLoad %6 %8 + %49 = OpLoad %6 %10 + %100 = OpCompositeConstruct %12 %48 %48 + %101 = OpCompositeConstruct %12 %49 %49 + %50 = OpIAdd %6 %48 %49 + OpStore %47 %50 + %52 = OpLoad %6 %8 + %53 = OpLoad %6 %10 + %54 = OpISub %6 %52 %53 + OpStore %51 %54 + %56 = OpLoad %6 %8 + %57 = OpLoad %6 %10 + %58 = OpIMul %6 %56 %57 + OpStore %55 %58 + %60 = OpLoad %6 %8 + %61 = OpLoad %6 %10 + %62 = OpSDiv %6 %60 %61 + OpStore %59 %62 + %64 = OpLoad %18 %20 + %65 = OpLoad %18 %22 + %66 = OpIAdd %18 %64 %65 + OpStore %63 %66 + %68 = OpLoad %18 %20 + %69 = OpLoad %18 %22 + %70 = OpISub %18 %68 %69 + OpStore %67 %70 + %72 = OpLoad %18 %20 + %73 = OpLoad %18 %22 + %74 = OpIMul %18 %72 %73 + OpStore %71 %74 + %76 = OpLoad %18 %20 + %77 = OpLoad %18 %22 + %78 = OpUDiv %18 %76 %77 + OpStore %75 %78 + %80 = OpLoad %31 %33 + %81 = OpLoad %31 %35 + %82 = OpFAdd %31 %80 %81 + OpStore %79 %82 + %84 = OpLoad %31 %33 + %85 = OpLoad %31 %35 + %86 = OpFSub %31 %84 %85 + OpStore %83 %86 + %88 = OpLoad %31 %33 + %89 = OpLoad %31 %35 + %90 = OpFMul %31 %88 %89 + OpStore %87 %90 + %92 = OpLoad %31 %33 + %93 = OpLoad %31 %35 + %94 = OpFDiv %31 %92 %93 + OpStore %91 %94 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + // Check context validity. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Vec Type Id | Vector Type | Element Type id | Element Type | + // ------------+----------------+------------------+-----------------+ + // 12 | vec2 | 6 | int32 | + // 24 | vec3 | 18 | uint32 | + // 37 | vec4 | 31 | float | + + // Instruction Id | Opcode | Type Id | constant id 1 | constant id 2 | + // ---------------+---------+---------+---------------+---------------+ + // 50 | OpIAdd | 6 | 48 | 49 | + // 54 | OpISub | 6 | 52 | 53 | + // 58 | OpIMul | 6 | 56 | 57 | + // 62 | OpSDiv | 6 | 60 | 61 | + // 66 | OpIAdd | 18 | 64 | 65 | + // 70 | OpISub | 18 | 68 | 69 | + // 74 | OpIMul | 18 | 72 | 73 | + // 78 | OpUDiv | 18 | 76 | 77 | + // 82 | OpFAdd | 31 | 80 | 81 | + // 86 | OpFSub | 31 | 84 | 85 | + // 90 | OpFMul | 31 | 88 | 89 | + // 94 | OpFDiv | 31 | 92 | 93 | + + // Assert that the target scalar instruction result id is relevant. + ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(50)); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(100, {1}), MakeDataDescriptor(48, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(101, {1}), MakeDataDescriptor(49, {})); + + // The following are all invalid use. + { + // Bad: Instruction id does not exist. + TransformationWrapVectorSynonym wrap_add_int_bad1(103, 100, 101, 102, 1); + ASSERT_FALSE( + wrap_add_int_bad1.IsApplicable(context.get(), transformation_context)); + + // Bad: Instruction id given is not of a valid arithmetic operation typed + // instruction. + TransformationWrapVectorSynonym wrap_add_int_bad2(80, 100, 101, 102, 1); + ASSERT_FALSE( + wrap_add_int_bad1.IsApplicable(context.get(), transformation_context)); + + // Bad: the id for the first vector does not exist. + TransformationWrapVectorSynonym wrap_add_int_bad3(50, 105, 101, 102, 1); + ASSERT_FALSE( + wrap_add_int_bad3.IsApplicable(context.get(), transformation_context)); + + // Bad: the id for the second vector does not exist. + TransformationWrapVectorSynonym wrap_add_int_bad4(50, 100, 105, 102, 1); + ASSERT_FALSE( + wrap_add_int_bad4.IsApplicable(context.get(), transformation_context)); + + // Bad: vector id is not fresh. + TransformationWrapVectorSynonym wrap_add_int_bad6(50, 100, 101, 94, 1); + ASSERT_FALSE( + wrap_add_int_bad6.IsApplicable(context.get(), transformation_context)); + + // Bad: The position goes out of bound for the given vector type. + TransformationWrapVectorSynonym wrap_add_int_bad8(50, 100, 101, 102, 2); + ASSERT_FALSE( + wrap_add_int_bad8.IsApplicable(context.get(), transformation_context)); + + // Bad: The original instruction is not a valid scalar operation + // instruction. + TransformationWrapVectorSynonym wrap_add_int(27, 100, 101, 102, 1); + ASSERT_FALSE( + wrap_add_int.IsApplicable(context.get(), transformation_context)); + } + + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_add_int(50, 100, 101, 102, 1); + ASSERT_TRUE(wrap_add_int.IsApplicable(context.get(), transformation_context)); + // Insert an arithmetic instruction of the same type to add two vectors. + ApplyAndCheckFreshIds(wrap_add_int, context.get(), &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should be + // synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(102, {1}), MakeDataDescriptor(50, {}))); + + // After applying transformations, the instruction: + // + // %102 = OpIAdd %12 %100 %101 + // + // should be added before: + // + // %50 = OpIAdd %6 %48 %49 + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %97 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %11 = OpConstant %6 -5 + %12 = OpTypeVector %6 2 + %13 = OpTypePointer Function %12 + %18 = OpTypeInt 32 0 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 8 + %23 = OpConstant %18 2 + %24 = OpTypeVector %18 3 + %25 = OpTypePointer Function %24 + %31 = OpTypeFloat 32 + %32 = OpTypePointer Function %31 + %34 = OpConstant %31 3.29999995 + %36 = OpConstant %31 1.10000002 + %37 = OpTypeVector %31 4 + %38 = OpTypePointer Function %37 + %96 = OpTypePointer Input %31 + %97 = OpVariable %96 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %33 = OpVariable %32 Function + %35 = OpVariable %32 Function + %39 = OpVariable %38 Function + %47 = OpVariable %7 Function + %51 = OpVariable %7 Function + %55 = OpVariable %7 Function + %59 = OpVariable %7 Function + %63 = OpVariable %19 Function + %67 = OpVariable %19 Function + %71 = OpVariable %19 Function + %75 = OpVariable %19 Function + %79 = OpVariable %32 Function + %83 = OpVariable %32 Function + %87 = OpVariable %32 Function + %91 = OpVariable %32 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + OpStore %20 %21 + OpStore %22 %23 + %27 = OpLoad %18 %20 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %22 + %30 = OpCompositeConstruct %24 %27 %28 %29 + OpStore %26 %30 + OpStore %33 %34 + OpStore %35 %36 + %40 = OpLoad %31 %33 + %41 = OpLoad %31 %33 + %42 = OpLoad %31 %35 + %43 = OpLoad %31 %35 + %44 = OpCompositeConstruct %37 %40 %41 %42 %43 + %45 = OpLoad %37 %39 + %46 = OpVectorShuffle %37 %45 %44 5 6 7 4 + OpStore %39 %46 + %48 = OpLoad %6 %8 + %49 = OpLoad %6 %10 + %100 = OpCompositeConstruct %12 %48 %48 + %101 = OpCompositeConstruct %12 %49 %49 + %102 = OpIAdd %12 %100 %101 + %50 = OpIAdd %6 %48 %49 + OpStore %47 %50 + %52 = OpLoad %6 %8 + %53 = OpLoad %6 %10 + %54 = OpISub %6 %52 %53 + OpStore %51 %54 + %56 = OpLoad %6 %8 + %57 = OpLoad %6 %10 + %58 = OpIMul %6 %56 %57 + OpStore %55 %58 + %60 = OpLoad %6 %8 + %61 = OpLoad %6 %10 + %62 = OpSDiv %6 %60 %61 + OpStore %59 %62 + %64 = OpLoad %18 %20 + %65 = OpLoad %18 %22 + %66 = OpIAdd %18 %64 %65 + OpStore %63 %66 + %68 = OpLoad %18 %20 + %69 = OpLoad %18 %22 + %70 = OpISub %18 %68 %69 + OpStore %67 %70 + %72 = OpLoad %18 %20 + %73 = OpLoad %18 %22 + %74 = OpIMul %18 %72 %73 + OpStore %71 %74 + %76 = OpLoad %18 %20 + %77 = OpLoad %18 %22 + %78 = OpUDiv %18 %76 %77 + OpStore %75 %78 + %80 = OpLoad %31 %33 + %81 = OpLoad %31 %35 + %82 = OpFAdd %31 %80 %81 + OpStore %79 %82 + %84 = OpLoad %31 %33 + %85 = OpLoad %31 %35 + %86 = OpFSub %31 %84 %85 + OpStore %83 %86 + %88 = OpLoad %31 %33 + %89 = OpLoad %31 %35 + %90 = OpFMul %31 %88 %89 + OpStore %87 %90 + %92 = OpLoad %31 %33 + %93 = OpLoad %31 %35 + %94 = OpFDiv %31 %92 %93 + OpStore %91 %94 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationWrapVectorSynonym, OperationSupportTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %97 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %11 = OpConstant %6 -5 + %12 = OpTypeVector %6 2 + %13 = OpTypePointer Function %12 + %18 = OpTypeInt 32 0 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 8 + %23 = OpConstant %18 2 + %24 = OpTypeVector %18 3 + %25 = OpTypePointer Function %24 + %31 = OpTypeFloat 32 + %32 = OpTypePointer Function %31 + %34 = OpConstant %31 3.29999995 + %36 = OpConstant %31 1.10000002 + %37 = OpTypeVector %31 4 + %38 = OpTypePointer Function %37 + %96 = OpTypePointer Input %31 + %97 = OpVariable %96 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %33 = OpVariable %32 Function + %35 = OpVariable %32 Function + %39 = OpVariable %38 Function + %47 = OpVariable %7 Function + %51 = OpVariable %7 Function + %55 = OpVariable %7 Function + %59 = OpVariable %7 Function + %63 = OpVariable %19 Function + %67 = OpVariable %19 Function + %71 = OpVariable %19 Function + %75 = OpVariable %19 Function + %79 = OpVariable %32 Function + %83 = OpVariable %32 Function + %87 = OpVariable %32 Function + %91 = OpVariable %32 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + OpStore %20 %21 + OpStore %22 %23 + %27 = OpLoad %18 %20 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %22 + %30 = OpCompositeConstruct %24 %27 %28 %29 + OpStore %26 %30 + OpStore %33 %34 + OpStore %35 %36 + %40 = OpLoad %31 %33 + %41 = OpLoad %31 %33 + %42 = OpLoad %31 %35 + %43 = OpLoad %31 %35 + %44 = OpCompositeConstruct %37 %40 %41 %42 %43 + %45 = OpLoad %37 %39 + %46 = OpVectorShuffle %37 %45 %44 5 6 7 4 + OpStore %39 %46 + %48 = OpLoad %6 %8 + %49 = OpLoad %6 %10 + %50 = OpIAdd %6 %48 %49 + OpStore %47 %50 + %52 = OpLoad %6 %8 + %53 = OpLoad %6 %10 + %100 = OpCompositeConstruct %12 %52 %52 + %101 = OpCompositeConstruct %12 %53 %53 + %54 = OpISub %6 %52 %53 + OpStore %51 %54 + %56 = OpLoad %6 %8 + %57 = OpLoad %6 %10 + %103 = OpCompositeConstruct %12 %56 %56 + %104 = OpCompositeConstruct %12 %57 %57 + %58 = OpIMul %6 %56 %57 + OpStore %55 %58 + %60 = OpLoad %6 %8 + %61 = OpLoad %6 %10 + %62 = OpSDiv %6 %60 %61 + OpStore %59 %62 + %64 = OpLoad %18 %20 + %65 = OpLoad %18 %22 + %106 = OpCompositeConstruct %24 %64 %64 %64 + %107 = OpCompositeConstruct %24 %65 %65 %65 + %66 = OpIAdd %18 %64 %65 + OpStore %63 %66 + %68 = OpLoad %18 %20 + %69 = OpLoad %18 %22 + %109 = OpCompositeConstruct %24 %68 %68 %68 + %110 = OpCompositeConstruct %24 %69 %69 %69 + %70 = OpISub %18 %68 %69 + OpStore %67 %70 + %72 = OpLoad %18 %20 + %73 = OpLoad %18 %22 + %112 = OpCompositeConstruct %24 %72 %72 %72 + %113 = OpCompositeConstruct %24 %73 %73 %73 + %74 = OpIMul %18 %72 %73 + OpStore %71 %74 + %76 = OpLoad %18 %20 + %77 = OpLoad %18 %22 + %78 = OpUDiv %18 %76 %77 + OpStore %75 %78 + %80 = OpLoad %31 %33 + %81 = OpLoad %31 %35 + %115 = OpCompositeConstruct %37 %80 %80 %80 %80 + %116 = OpCompositeConstruct %37 %81 %81 %81 %81 + %82 = OpFAdd %31 %80 %81 + OpStore %79 %82 + %84 = OpLoad %31 %33 + %85 = OpLoad %31 %35 + %118 = OpCompositeConstruct %37 %84 %84 %84 %84 + %119 = OpCompositeConstruct %37 %85 %85 %85 %85 + %86 = OpFSub %31 %84 %85 + OpStore %83 %86 + %88 = OpLoad %31 %33 + %89 = OpLoad %31 %35 + %121 = OpCompositeConstruct %37 %88 %88 %88 %88 + %122 = OpCompositeConstruct %37 %89 %89 %89 %89 + %90 = OpFMul %31 %88 %89 + OpStore %87 %90 + %92 = OpLoad %31 %33 + %93 = OpLoad %31 %35 + %94 = OpFDiv %31 %92 %93 + OpStore %91 %94 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + { + // Add synonym facts between the vector operands at pos and the operands to + // the scalar instruction. + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(100, {1}), MakeDataDescriptor(52, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(101, {1}), MakeDataDescriptor(53, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(103, {0}), MakeDataDescriptor(56, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(104, {0}), MakeDataDescriptor(57, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(106, {2}), MakeDataDescriptor(64, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(107, {2}), MakeDataDescriptor(65, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(109, {2}), MakeDataDescriptor(68, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(110, {2}), MakeDataDescriptor(69, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(112, {1}), MakeDataDescriptor(72, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(113, {1}), MakeDataDescriptor(73, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(115, {2}), MakeDataDescriptor(80, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(116, {2}), MakeDataDescriptor(81, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(118, {3}), MakeDataDescriptor(84, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(119, {3}), MakeDataDescriptor(85, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(121, {1}), MakeDataDescriptor(88, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(122, {1}), MakeDataDescriptor(89, {})); + } + + // Test OpISub for signed integer. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_sub_int(54, 100, 101, 102, 1); + ASSERT_TRUE( + wrap_sub_int.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_sub_int, context.get(), &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(102, {1}), MakeDataDescriptor(54, {}))); + } + + // Test OpIMul for signed integer. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_mul_int(58, 103, 104, 105, 0); + ASSERT_TRUE( + wrap_mul_int.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_mul_int, context.get(), &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(105, {0}), MakeDataDescriptor(58, {}))); + } + + // Test OpIAdd for unsigned integer. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_add_uint(66, 106, 107, 108, 2); + ASSERT_TRUE( + wrap_add_uint.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_add_uint, context.get(), + &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(108, {2}), MakeDataDescriptor(66, {}))); + } + + // Test OpISub for signed integer. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_sub_uint(70, 109, 110, 111, 2); + ASSERT_TRUE( + wrap_sub_uint.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_sub_uint, context.get(), + &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(111, {2}), MakeDataDescriptor(70, {}))); + } + + // Test OpIMul for signed integer. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_mul_uint(74, 112, 113, 114, 1); + ASSERT_TRUE( + wrap_mul_uint.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_mul_uint, context.get(), + &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(114, {1}), MakeDataDescriptor(74, {}))); + } + + // Test OpFAdd for float. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_add_float(82, 115, 116, 117, 2); + ASSERT_TRUE( + wrap_add_float.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_add_float, context.get(), + &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(117, {2}), MakeDataDescriptor(82, {}))); + } + + // Test OpFSub for float. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_add_float(86, 118, 119, 120, 3); + ASSERT_TRUE( + wrap_add_float.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_add_float, context.get(), + &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(120, {3}), MakeDataDescriptor(86, {}))); + } + + // Test OpFMul for float. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_mul_float(90, 121, 122, 123, 1); + ASSERT_TRUE( + wrap_mul_float.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_mul_float, context.get(), + &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(123, {1}), MakeDataDescriptor(90, {}))); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %97 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %11 = OpConstant %6 -5 + %12 = OpTypeVector %6 2 + %13 = OpTypePointer Function %12 + %18 = OpTypeInt 32 0 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 8 + %23 = OpConstant %18 2 + %24 = OpTypeVector %18 3 + %25 = OpTypePointer Function %24 + %31 = OpTypeFloat 32 + %32 = OpTypePointer Function %31 + %34 = OpConstant %31 3.29999995 + %36 = OpConstant %31 1.10000002 + %37 = OpTypeVector %31 4 + %38 = OpTypePointer Function %37 + %96 = OpTypePointer Input %31 + %97 = OpVariable %96 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %33 = OpVariable %32 Function + %35 = OpVariable %32 Function + %39 = OpVariable %38 Function + %47 = OpVariable %7 Function + %51 = OpVariable %7 Function + %55 = OpVariable %7 Function + %59 = OpVariable %7 Function + %63 = OpVariable %19 Function + %67 = OpVariable %19 Function + %71 = OpVariable %19 Function + %75 = OpVariable %19 Function + %79 = OpVariable %32 Function + %83 = OpVariable %32 Function + %87 = OpVariable %32 Function + %91 = OpVariable %32 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + OpStore %20 %21 + OpStore %22 %23 + %27 = OpLoad %18 %20 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %22 + %30 = OpCompositeConstruct %24 %27 %28 %29 + OpStore %26 %30 + OpStore %33 %34 + OpStore %35 %36 + %40 = OpLoad %31 %33 + %41 = OpLoad %31 %33 + %42 = OpLoad %31 %35 + %43 = OpLoad %31 %35 + %44 = OpCompositeConstruct %37 %40 %41 %42 %43 + %45 = OpLoad %37 %39 + %46 = OpVectorShuffle %37 %45 %44 5 6 7 4 + OpStore %39 %46 + %48 = OpLoad %6 %8 + %49 = OpLoad %6 %10 + %50 = OpIAdd %6 %48 %49 + OpStore %47 %50 + %52 = OpLoad %6 %8 + %53 = OpLoad %6 %10 + %100 = OpCompositeConstruct %12 %52 %52 + %101 = OpCompositeConstruct %12 %53 %53 + %102 = OpISub %12 %100 %101 + %54 = OpISub %6 %52 %53 + OpStore %51 %54 + %56 = OpLoad %6 %8 + %57 = OpLoad %6 %10 + %103 = OpCompositeConstruct %12 %56 %56 + %104 = OpCompositeConstruct %12 %57 %57 + %105 = OpIMul %12 %103 %104 + %58 = OpIMul %6 %56 %57 + OpStore %55 %58 + %60 = OpLoad %6 %8 + %61 = OpLoad %6 %10 + %62 = OpSDiv %6 %60 %61 + OpStore %59 %62 + %64 = OpLoad %18 %20 + %65 = OpLoad %18 %22 + %106 = OpCompositeConstruct %24 %64 %64 %64 + %107 = OpCompositeConstruct %24 %65 %65 %65 + %108 = OpIAdd %24 %106 %107 + %66 = OpIAdd %18 %64 %65 + OpStore %63 %66 + %68 = OpLoad %18 %20 + %69 = OpLoad %18 %22 + %109 = OpCompositeConstruct %24 %68 %68 %68 + %110 = OpCompositeConstruct %24 %69 %69 %69 + %111 = OpISub %24 %109 %110 + %70 = OpISub %18 %68 %69 + OpStore %67 %70 + %72 = OpLoad %18 %20 + %73 = OpLoad %18 %22 + %112 = OpCompositeConstruct %24 %72 %72 %72 + %113 = OpCompositeConstruct %24 %73 %73 %73 + %114 = OpIMul %24 %112 %113 + %74 = OpIMul %18 %72 %73 + OpStore %71 %74 + %76 = OpLoad %18 %20 + %77 = OpLoad %18 %22 + %78 = OpUDiv %18 %76 %77 + OpStore %75 %78 + %80 = OpLoad %31 %33 + %81 = OpLoad %31 %35 + %115 = OpCompositeConstruct %37 %80 %80 %80 %80 + %116 = OpCompositeConstruct %37 %81 %81 %81 %81 + %117 = OpFAdd %37 %115 %116 + %82 = OpFAdd %31 %80 %81 + OpStore %79 %82 + %84 = OpLoad %31 %33 + %85 = OpLoad %31 %35 + %118 = OpCompositeConstruct %37 %84 %84 %84 %84 + %119 = OpCompositeConstruct %37 %85 %85 %85 %85 + %120 = OpFSub %37 %118 %119 + %86 = OpFSub %31 %84 %85 + OpStore %83 %86 + %88 = OpLoad %31 %33 + %89 = OpLoad %31 %35 + %121 = OpCompositeConstruct %37 %88 %88 %88 %88 + %122 = OpCompositeConstruct %37 %89 %89 %89 %89 + %123 = OpFMul %37 %121 %122 + %90 = OpFMul %31 %88 %89 + OpStore %87 %90 + %92 = OpLoad %31 %33 + %93 = OpLoad %31 %35 + %94 = OpFDiv %31 %92 %93 + OpStore %91 %94 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationWrapVectorSynonym, DivSupportTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %97 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %11 = OpConstant %6 -5 + %12 = OpTypeVector %6 2 + %13 = OpTypePointer Function %12 + %18 = OpTypeInt 32 0 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 8 + %23 = OpConstant %18 2 + %24 = OpTypeVector %18 3 + %25 = OpTypePointer Function %24 + %31 = OpTypeFloat 32 + %32 = OpTypePointer Function %31 + %34 = OpConstant %31 3.29999995 + %36 = OpConstant %31 1.10000002 + %37 = OpTypeVector %31 4 + %38 = OpTypePointer Function %37 + %96 = OpTypePointer Input %31 + %97 = OpVariable %96 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %33 = OpVariable %32 Function + %35 = OpVariable %32 Function + %39 = OpVariable %38 Function + %47 = OpVariable %7 Function + %51 = OpVariable %7 Function + %55 = OpVariable %7 Function + %59 = OpVariable %7 Function + %63 = OpVariable %19 Function + %67 = OpVariable %19 Function + %71 = OpVariable %19 Function + %75 = OpVariable %19 Function + %79 = OpVariable %32 Function + %83 = OpVariable %32 Function + %87 = OpVariable %32 Function + %91 = OpVariable %32 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + OpStore %20 %21 + OpStore %22 %23 + %27 = OpLoad %18 %20 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %22 + %30 = OpCompositeConstruct %24 %27 %28 %29 + OpStore %26 %30 + OpStore %33 %34 + OpStore %35 %36 + %40 = OpLoad %31 %33 + %41 = OpLoad %31 %33 + %42 = OpLoad %31 %35 + %43 = OpLoad %31 %35 + %44 = OpCompositeConstruct %37 %40 %41 %42 %43 + %45 = OpLoad %37 %39 + %46 = OpVectorShuffle %37 %45 %44 5 6 7 4 + OpStore %39 %46 + %48 = OpLoad %6 %8 + %49 = OpLoad %6 %10 + %50 = OpIAdd %6 %48 %49 + OpStore %47 %50 + %52 = OpLoad %6 %8 + %53 = OpLoad %6 %10 + %54 = OpISub %6 %52 %53 + OpStore %51 %54 + %56 = OpLoad %6 %8 + %57 = OpLoad %6 %10 + %58 = OpIMul %6 %56 %57 + OpStore %55 %58 + %60 = OpLoad %6 %8 + %61 = OpLoad %6 %10 + %100 = OpCompositeConstruct %12 %60 %60 + %101 = OpCompositeConstruct %12 %61 %61 + %62 = OpSDiv %6 %60 %61 + OpStore %59 %62 + %64 = OpLoad %18 %20 + %65 = OpLoad %18 %22 + %66 = OpIAdd %18 %64 %65 + OpStore %63 %66 + %68 = OpLoad %18 %20 + %69 = OpLoad %18 %22 + %70 = OpISub %18 %68 %69 + OpStore %67 %70 + %72 = OpLoad %18 %20 + %73 = OpLoad %18 %22 + %74 = OpIMul %18 %72 %73 + OpStore %71 %74 + %76 = OpLoad %18 %20 + %77 = OpLoad %18 %22 + %102 = OpCompositeConstruct %24 %76 %76 %76 + %103 = OpCompositeConstruct %24 %77 %77 %77 + %78 = OpUDiv %18 %76 %77 + OpStore %75 %78 + %80 = OpLoad %31 %33 + %81 = OpLoad %31 %35 + %82 = OpFAdd %31 %80 %81 + OpStore %79 %82 + %84 = OpLoad %31 %33 + %85 = OpLoad %31 %35 + %86 = OpFSub %31 %84 %85 + OpStore %83 %86 + %88 = OpLoad %31 %33 + %89 = OpLoad %31 %35 + %90 = OpFMul %31 %88 %89 + OpStore %87 %90 + %92 = OpLoad %31 %33 + %93 = OpLoad %31 %35 + %104 = OpCompositeConstruct %37 %92 %92 %92 %92 + %105 = OpCompositeConstruct %37 %93 %93 %93 %93 + %94 = OpFDiv %31 %92 %93 + OpStore %91 %94 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(100, {1}), MakeDataDescriptor(60, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(101, {1}), MakeDataDescriptor(61, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(102, {1}), MakeDataDescriptor(76, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(103, {1}), MakeDataDescriptor(77, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(104, {1}), MakeDataDescriptor(92, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(105, {1}), MakeDataDescriptor(93, {})); + + // Div operations are not currently supported. + { + TransformationWrapVectorSynonym wrap_div_bad1(62, 100, 101, 106, 1); + ASSERT_FALSE( + wrap_div_bad1.IsApplicable(context.get(), transformation_context)); + + TransformationWrapVectorSynonym wrap_div_bad2(78, 102, 103, 106, 1); + ASSERT_FALSE( + wrap_div_bad2.IsApplicable(context.get(), transformation_context)); + + TransformationWrapVectorSynonym wrap_div_bad3(94, 104, 105, 106, 1); + ASSERT_FALSE( + wrap_div_bad3.IsApplicable(context.get(), transformation_context)); + } +} + +TEST(TransformationWrapVectorSynonym, AdditionalWidthSupportTest) { + std::string shader = R"( + OpCapability Shader + OpCapability Int64 + OpCapability Float64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %97 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 64 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %11 = OpConstant %6 -5 + %12 = OpTypeVector %6 2 + %13 = OpTypePointer Function %12 + %18 = OpTypeInt 64 0 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 8 + %23 = OpConstant %18 2 + %24 = OpTypeVector %18 3 + %25 = OpTypePointer Function %24 + %31 = OpTypeFloat 64 + %32 = OpTypePointer Function %31 + %34 = OpConstant %31 3.29999995 + %36 = OpConstant %31 1.10000002 + %37 = OpTypeVector %31 4 + %38 = OpTypePointer Function %37 + %96 = OpTypePointer Input %31 + %97 = OpVariable %96 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %33 = OpVariable %32 Function + %35 = OpVariable %32 Function + %39 = OpVariable %38 Function + %47 = OpVariable %7 Function + %51 = OpVariable %7 Function + %55 = OpVariable %7 Function + %59 = OpVariable %7 Function + %63 = OpVariable %19 Function + %67 = OpVariable %19 Function + %71 = OpVariable %19 Function + %75 = OpVariable %19 Function + %79 = OpVariable %32 Function + %83 = OpVariable %32 Function + %87 = OpVariable %32 Function + %91 = OpVariable %32 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + OpStore %20 %21 + OpStore %22 %23 + %27 = OpLoad %18 %20 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %22 + %30 = OpCompositeConstruct %24 %27 %28 %29 + OpStore %26 %30 + OpStore %33 %34 + OpStore %35 %36 + %40 = OpLoad %31 %33 + %41 = OpLoad %31 %33 + %42 = OpLoad %31 %35 + %43 = OpLoad %31 %35 + %44 = OpCompositeConstruct %37 %40 %41 %42 %43 + %45 = OpLoad %37 %39 + %46 = OpVectorShuffle %37 %45 %44 5 6 7 4 + OpStore %39 %46 + %48 = OpLoad %6 %8 + %49 = OpLoad %6 %10 + %100 = OpCompositeConstruct %12 %48 %48 + %101 = OpCompositeConstruct %12 %49 %49 + %50 = OpIAdd %6 %48 %49 + OpStore %47 %50 + %52 = OpLoad %6 %8 + %53 = OpLoad %6 %10 + %54 = OpISub %6 %52 %53 + OpStore %51 %54 + %56 = OpLoad %6 %8 + %57 = OpLoad %6 %10 + %58 = OpIMul %6 %56 %57 + OpStore %55 %58 + %60 = OpLoad %6 %8 + %61 = OpLoad %6 %10 + %62 = OpSDiv %6 %60 %61 + OpStore %59 %62 + %64 = OpLoad %18 %20 + %65 = OpLoad %18 %22 + %66 = OpIAdd %18 %64 %65 + OpStore %63 %66 + %68 = OpLoad %18 %20 + %69 = OpLoad %18 %22 + %103 = OpCompositeConstruct %24 %68 %68 %68 + %104 = OpCompositeConstruct %24 %69 %69 %69 + %70 = OpISub %18 %68 %69 + OpStore %67 %70 + %72 = OpLoad %18 %20 + %73 = OpLoad %18 %22 + %74 = OpIMul %18 %72 %73 + OpStore %71 %74 + %76 = OpLoad %18 %20 + %77 = OpLoad %18 %22 + %78 = OpUDiv %18 %76 %77 + OpStore %75 %78 + %80 = OpLoad %31 %33 + %81 = OpLoad %31 %35 + %82 = OpFAdd %31 %80 %81 + OpStore %79 %82 + %84 = OpLoad %31 %33 + %85 = OpLoad %31 %35 + %86 = OpFSub %31 %84 %85 + OpStore %83 %86 + %88 = OpLoad %31 %33 + %89 = OpLoad %31 %35 + %106 = OpCompositeConstruct %37 %88 %88 %88 %88 + %107 = OpCompositeConstruct %37 %89 %89 %89 %89 + %90 = OpFMul %31 %88 %89 + OpStore %87 %90 + %92 = OpLoad %31 %33 + %93 = OpLoad %31 %35 + %94 = OpFDiv %31 %92 %93 + OpStore %91 %94 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + // Check context validity. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Vec Type Id | Vector Type | Element Type id | Element Type | + // ------------+----------------+------------------+-----------------+ + // 12 | vec2 | 6 | int64 | + // 24 | vec3 | 18 | uint64 | + // 37 | vec4 | 31 | float64 | + + // Test support for 64-bit signed int. + { + // Assert that the target scalar instruction result id is relevant. + ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(50)); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(100, {1}), MakeDataDescriptor(48, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(101, {1}), MakeDataDescriptor(49, {})); + + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_add_int64(50, 100, 101, 102, 1); + ASSERT_TRUE( + wrap_add_int64.IsApplicable(context.get(), transformation_context)); + // Insert an arithmetic instruction of the same type to add two vectors. + ApplyAndCheckFreshIds(wrap_add_int64, context.get(), + &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(102, {1}), MakeDataDescriptor(50, {}))); + } + + // Test support for 64-bit unsigned int. + { + ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(70)); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(103, {2}), MakeDataDescriptor(68, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(104, {2}), MakeDataDescriptor(69, {})); + + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_sub_uint64(70, 103, 104, 105, 2); + ASSERT_TRUE( + wrap_sub_uint64.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(wrap_sub_uint64, context.get(), + &transformation_context); + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(105, {2}), MakeDataDescriptor(70, {}))); + } + + // Test support for 64-bit float. + { + ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(90)); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(106, {3}), MakeDataDescriptor(88, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(107, {3}), MakeDataDescriptor(89, {})); + + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_mul_float64(90, 106, 107, 108, 3); + ASSERT_TRUE( + wrap_mul_float64.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(wrap_mul_float64, context.get(), + &transformation_context); + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(108, {3}), MakeDataDescriptor(90, {}))); + } + + std::string after_transformation = R"( + OpCapability Shader + OpCapability Int64 + OpCapability Float64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %97 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 64 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %11 = OpConstant %6 -5 + %12 = OpTypeVector %6 2 + %13 = OpTypePointer Function %12 + %18 = OpTypeInt 64 0 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 8 + %23 = OpConstant %18 2 + %24 = OpTypeVector %18 3 + %25 = OpTypePointer Function %24 + %31 = OpTypeFloat 64 + %32 = OpTypePointer Function %31 + %34 = OpConstant %31 3.29999995 + %36 = OpConstant %31 1.10000002 + %37 = OpTypeVector %31 4 + %38 = OpTypePointer Function %37 + %96 = OpTypePointer Input %31 + %97 = OpVariable %96 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %33 = OpVariable %32 Function + %35 = OpVariable %32 Function + %39 = OpVariable %38 Function + %47 = OpVariable %7 Function + %51 = OpVariable %7 Function + %55 = OpVariable %7 Function + %59 = OpVariable %7 Function + %63 = OpVariable %19 Function + %67 = OpVariable %19 Function + %71 = OpVariable %19 Function + %75 = OpVariable %19 Function + %79 = OpVariable %32 Function + %83 = OpVariable %32 Function + %87 = OpVariable %32 Function + %91 = OpVariable %32 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + OpStore %20 %21 + OpStore %22 %23 + %27 = OpLoad %18 %20 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %22 + %30 = OpCompositeConstruct %24 %27 %28 %29 + OpStore %26 %30 + OpStore %33 %34 + OpStore %35 %36 + %40 = OpLoad %31 %33 + %41 = OpLoad %31 %33 + %42 = OpLoad %31 %35 + %43 = OpLoad %31 %35 + %44 = OpCompositeConstruct %37 %40 %41 %42 %43 + %45 = OpLoad %37 %39 + %46 = OpVectorShuffle %37 %45 %44 5 6 7 4 + OpStore %39 %46 + %48 = OpLoad %6 %8 + %49 = OpLoad %6 %10 + %100 = OpCompositeConstruct %12 %48 %48 + %101 = OpCompositeConstruct %12 %49 %49 + %102 = OpIAdd %12 %100 %101 + %50 = OpIAdd %6 %48 %49 + OpStore %47 %50 + %52 = OpLoad %6 %8 + %53 = OpLoad %6 %10 + %54 = OpISub %6 %52 %53 + OpStore %51 %54 + %56 = OpLoad %6 %8 + %57 = OpLoad %6 %10 + %58 = OpIMul %6 %56 %57 + OpStore %55 %58 + %60 = OpLoad %6 %8 + %61 = OpLoad %6 %10 + %62 = OpSDiv %6 %60 %61 + OpStore %59 %62 + %64 = OpLoad %18 %20 + %65 = OpLoad %18 %22 + %66 = OpIAdd %18 %64 %65 + OpStore %63 %66 + %68 = OpLoad %18 %20 + %69 = OpLoad %18 %22 + %103 = OpCompositeConstruct %24 %68 %68 %68 + %104 = OpCompositeConstruct %24 %69 %69 %69 + %105 = OpISub %24 %103 %104 + %70 = OpISub %18 %68 %69 + OpStore %67 %70 + %72 = OpLoad %18 %20 + %73 = OpLoad %18 %22 + %74 = OpIMul %18 %72 %73 + OpStore %71 %74 + %76 = OpLoad %18 %20 + %77 = OpLoad %18 %22 + %78 = OpUDiv %18 %76 %77 + OpStore %75 %78 + %80 = OpLoad %31 %33 + %81 = OpLoad %31 %35 + %82 = OpFAdd %31 %80 %81 + OpStore %79 %82 + %84 = OpLoad %31 %33 + %85 = OpLoad %31 %35 + %86 = OpFSub %31 %84 %85 + OpStore %83 %86 + %88 = OpLoad %31 %33 + %89 = OpLoad %31 %35 + %106 = OpCompositeConstruct %37 %88 %88 %88 %88 + %107 = OpCompositeConstruct %37 %89 %89 %89 %89 + %108 = OpFMul %37 %106 %107 + %90 = OpFMul %31 %88 %89 + OpStore %87 %90 + %92 = OpLoad %31 %33 + %93 = OpLoad %31 %35 + %94 = OpFDiv %31 %92 %93 + OpStore %91 %94 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationWrapVectorSynonym, DifferentVectorSignedness) { + 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 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstant %6 0 + %12 = OpConstantComposite %7 %10 %11 + %14 = OpTypeInt 32 0 + %15 = OpTypeVector %14 2 + %18 = OpConstant %14 3 + %19 = OpConstant %14 0 + %20 = OpConstantComposite %15 %18 %19 + %21 = OpConstantComposite %15 %19 %18 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %100 = OpIAdd %14 %10 %18 + %101 = OpIAdd %6 %10 %18 + %102 = OpIAdd %6 %18 %19 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + // Check context validity. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(10, {}), MakeDataDescriptor(12, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(18, {}), MakeDataDescriptor(20, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(19, {}), MakeDataDescriptor(21, {0})); + + { + TransformationWrapVectorSynonym transformation1(100, 12, 20, 200, 0); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(200, {0}), MakeDataDescriptor(100, {}))); + } + + { + TransformationWrapVectorSynonym transformation2(101, 12, 20, 201, 0); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(201, {0}), MakeDataDescriptor(101, {}))); + } + + { + TransformationWrapVectorSynonym transformation3(102, 20, 21, 202, 0); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(202, {0}), MakeDataDescriptor(102, {}))); + } + + std::string after_transformation = 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 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstant %6 0 + %12 = OpConstantComposite %7 %10 %11 + %14 = OpTypeInt 32 0 + %15 = OpTypeVector %14 2 + %18 = OpConstant %14 3 + %19 = OpConstant %14 0 + %20 = OpConstantComposite %15 %18 %19 + %21 = OpConstantComposite %15 %19 %18 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %200 = OpIAdd %15 %12 %20 + %100 = OpIAdd %14 %10 %18 + %201 = OpIAdd %7 %12 %20 + %101 = OpIAdd %6 %10 %18 + %202 = OpIAdd %7 %20 %21 + %102 = OpIAdd %6 %18 %19 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationWrapVectorSynonym, SignednessDoesNotMatchResultType) { + 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 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstant %6 0 + %12 = OpConstantComposite %7 %10 %11 + %13 = OpConstantComposite %7 %11 %10 + %14 = OpTypeInt 32 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %100 = OpIAdd %14 %10 %11 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + // Check context validity. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(10, {}), MakeDataDescriptor(12, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(11, {}), MakeDataDescriptor(13, {0})); + + ASSERT_FALSE(TransformationWrapVectorSynonym(100, 12, 13, 200, 0) + .IsApplicable(context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/test/fuzzers/BUILD.gn b/test/fuzzers/BUILD.gn index b6366f4c..fc458a4f 100644 --- a/test/fuzzers/BUILD.gn +++ b/test/fuzzers/BUILD.gn @@ -48,6 +48,7 @@ template("spvtools_fuzzer") { source_set(target_name) { testonly = true sources = invoker.sources + sources += [ "random_generator.cpp" ] deps = [ "../..:spvtools", "../..:spvtools_opt", @@ -66,79 +67,107 @@ template("spvtools_fuzzer") { } spvtools_fuzzer("spvtools_as_fuzzer_src") { - sources = [ "spvtools_as_fuzzer.cpp" ] + sources = [ + "spvtools_as_fuzzer.cpp", + ] } spvtools_fuzzer("spvtools_binary_parser_fuzzer_src") { - sources = [ "spvtools_binary_parser_fuzzer.cpp" ] + sources = [ + "spvtools_binary_parser_fuzzer.cpp", + ] } spvtools_fuzzer("spvtools_dis_fuzzer_src") { - sources = [ "spvtools_dis_fuzzer.cpp" ] + sources = [ + "spvtools_dis_fuzzer.cpp", + ] } spvtools_fuzzer("spvtools_opt_performance_fuzzer_src") { - sources = [ "spvtools_opt_performance_fuzzer.cpp" ] + sources = [ + "spvtools_opt_performance_fuzzer.cpp", + "spvtools_opt_fuzzer_common.cpp", + ] } spvtools_fuzzer("spvtools_opt_legalization_fuzzer_src") { - sources = [ "spvtools_opt_legalization_fuzzer.cpp" ] + sources = [ + "spvtools_opt_legalization_fuzzer.cpp", + "spvtools_opt_fuzzer_common.cpp", + ] } spvtools_fuzzer("spvtools_opt_size_fuzzer_src") { - sources = [ "spvtools_opt_size_fuzzer.cpp" ] + sources = [ + "spvtools_opt_size_fuzzer.cpp", + "spvtools_opt_fuzzer_common.cpp", + ] } spvtools_fuzzer("spvtools_val_fuzzer_src") { - sources = [ "spvtools_val_fuzzer.cpp" ] + sources = [ + "spvtools_val_fuzzer.cpp", + ] } if (!build_with_chromium || use_fuzzing_engine) { fuzzer_test("spvtools_as_fuzzer") { sources = [] - deps = [ ":spvtools_as_fuzzer_src" ] - + deps = [ + ":spvtools_as_fuzzer_src", + ] # Intentionally doesn't use the seed corpus, because it consumes # part of the input as not part of the file. } fuzzer_test("spvtools_binary_parser_fuzzer") { sources = [] - deps = [ ":spvtools_binary_parser_fuzzer_src" ] - + deps = [ + ":spvtools_binary_parser_fuzzer_src", + ] # Intentionally doesn't use the seed corpus, because it consumes # part of the input as not part of the file. } fuzzer_test("spvtools_dis_fuzzer") { sources = [] - deps = [ ":spvtools_dis_fuzzer_src" ] - + deps = [ + ":spvtools_dis_fuzzer_src", + ] # Intentionally doesn't use the seed corpus, because it consumes # part of the input as not part of the file. } fuzzer_test("spvtools_opt_performance_fuzzer") { sources = [] - deps = [ ":spvtools_opt_performance_fuzzer_src" ] + deps = [ + ":spvtools_opt_performance_fuzzer_src", + ] seed_corpus = "corpora/spv" } fuzzer_test("spvtools_opt_legalization_fuzzer") { sources = [] - deps = [ ":spvtools_opt_legalization_fuzzer_src" ] + deps = [ + ":spvtools_opt_legalization_fuzzer_src", + ] seed_corpus = "corpora/spv" } fuzzer_test("spvtools_opt_size_fuzzer") { sources = [] - deps = [ ":spvtools_opt_size_fuzzer_src" ] + deps = [ + ":spvtools_opt_size_fuzzer_src", + ] seed_corpus = "corpora/spv" } fuzzer_test("spvtools_val_fuzzer") { sources = [] - deps = [ ":spvtools_val_fuzzer_src" ] + deps = [ + ":spvtools_val_fuzzer_src", + ] seed_corpus = "corpora/spv" } } diff --git a/test/fuzzers/CMakeLists.txt b/test/fuzzers/CMakeLists.txt new file mode 100644 index 00000000..e1fe516a --- /dev/null +++ b/test/fuzzers/CMakeLists.txt @@ -0,0 +1,56 @@ +# Copyright (c) 2021 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. + +function(add_spvtools_libfuzzer_target) + set(one_value_args TARGET) + set(multi_value_args SRCS LIBS) + cmake_parse_arguments( + ARG "" "${one_value_args}" "${multi_value_args}" ${ARGN}) + + add_executable(${ARG_TARGET} ${ARG_SRCS}) + spvtools_default_compile_options(${ARG_TARGET}) + target_link_libraries(${ARG_TARGET} PRIVATE ${ARG_LIBS}) + target_include_directories(${ARG_TARGET} PRIVATE + ${spirv-tools_SOURCE_DIR} + ${spirv-tools_BINARY_DIR} + ) + set_property(TARGET ${ARG_TARGET} PROPERTY FOLDER "SPIRV-Tools libFuzzer targets") + if(NOT ${SPIRV_LIB_FUZZING_ENGINE_LINK_OPTIONS} STREQUAL "") + # This is set when the fuzzers are being built by OSS-Fuzz. In this case the + # variable provides the necessary linker flags, and OSS-Fuzz will take care + # of passing suitable compiler flags. + target_link_options(${ARG_TARGET} PRIVATE ${SPIRV_LIB_FUZZING_ENGINE_LINK_OPTIONS}) + else() + # When the fuzzers are being built outside of OSS-Fuzz, standard libFuzzer + # arguments to enable fuzzing are used. + target_compile_options(${ARG_TARGET} PRIVATE "-fsanitize=fuzzer") + target_link_options(${ARG_TARGET} PRIVATE "-fsanitize=fuzzer") + endif() +endfunction() + +if (${SPIRV_BUILD_LIBFUZZER_TARGETS}) + if(NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + message(FATAL_ERROR "The libFuzzer targets are only supported with the Clang compiler. Compiler '${CMAKE_CXX_COMPILER_ID}' is not supported!") + endif() + add_spvtools_libfuzzer_target(TARGET spvtools_as_fuzzer SRCS spvtools_as_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_binary_parser_fuzzer SRCS spvtools_binary_parser_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_dis_fuzzer SRCS spvtools_dis_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_opt_legalization_fuzzer SRCS spvtools_opt_legalization_fuzzer.cpp spvtools_opt_fuzzer_common.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_opt_performance_fuzzer SRCS spvtools_opt_performance_fuzzer.cpp spvtools_opt_fuzzer_common.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_opt_size_fuzzer SRCS spvtools_opt_size_fuzzer.cpp spvtools_opt_fuzzer_common.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_val_fuzzer SRCS spvtools_val_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + if (${SPIRV_BUILD_FUZZER}) + add_spvtools_libfuzzer_target(TARGET spvtools_fuzz_fuzzer SRCS spvtools_fuzz_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-fuzz ${SPIRV_TOOLS_FULL_VISIBILITY}) + endif() +endif() diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_000.spv b/test/fuzzers/corpora/spv/graphicsfuzz_000.spv new file mode 100644 index 00000000..c203cec4 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_000.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_001.spv b/test/fuzzers/corpora/spv/graphicsfuzz_001.spv new file mode 100644 index 00000000..c3dfbd9a Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_001.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_002.spv b/test/fuzzers/corpora/spv/graphicsfuzz_002.spv new file mode 100644 index 00000000..5a3c145f Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_002.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_003.spv b/test/fuzzers/corpora/spv/graphicsfuzz_003.spv new file mode 100644 index 00000000..c0e0a298 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_003.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_004.spv b/test/fuzzers/corpora/spv/graphicsfuzz_004.spv new file mode 100644 index 00000000..f1afc725 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_004.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_005.spv b/test/fuzzers/corpora/spv/graphicsfuzz_005.spv new file mode 100644 index 00000000..c6bf437d Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_005.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_006.spv b/test/fuzzers/corpora/spv/graphicsfuzz_006.spv new file mode 100644 index 00000000..d0d4a700 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_006.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_007.spv b/test/fuzzers/corpora/spv/graphicsfuzz_007.spv new file mode 100644 index 00000000..7f2f982d Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_007.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_008.spv b/test/fuzzers/corpora/spv/graphicsfuzz_008.spv new file mode 100644 index 00000000..c67dc976 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_008.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_009.spv b/test/fuzzers/corpora/spv/graphicsfuzz_009.spv new file mode 100644 index 00000000..68740b39 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_009.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_010.spv b/test/fuzzers/corpora/spv/graphicsfuzz_010.spv new file mode 100644 index 00000000..fedc8b4c Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_010.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_011.spv b/test/fuzzers/corpora/spv/graphicsfuzz_011.spv new file mode 100644 index 00000000..882bfe6b Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_011.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_012.spv b/test/fuzzers/corpora/spv/graphicsfuzz_012.spv new file mode 100644 index 00000000..bd3c1144 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_012.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_013.spv b/test/fuzzers/corpora/spv/graphicsfuzz_013.spv new file mode 100644 index 00000000..d7e35b4b Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_013.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_014.spv b/test/fuzzers/corpora/spv/graphicsfuzz_014.spv new file mode 100644 index 00000000..abf54889 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_014.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_015.spv b/test/fuzzers/corpora/spv/graphicsfuzz_015.spv new file mode 100644 index 00000000..868ab046 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_015.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_016.spv b/test/fuzzers/corpora/spv/graphicsfuzz_016.spv new file mode 100644 index 00000000..a7cef28a Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_016.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_017.spv b/test/fuzzers/corpora/spv/graphicsfuzz_017.spv new file mode 100644 index 00000000..6d338a91 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_017.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_018.spv b/test/fuzzers/corpora/spv/graphicsfuzz_018.spv new file mode 100644 index 00000000..c7bf440c Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_018.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_019.spv b/test/fuzzers/corpora/spv/graphicsfuzz_019.spv new file mode 100644 index 00000000..99d7e2df Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_019.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_020.spv b/test/fuzzers/corpora/spv/graphicsfuzz_020.spv new file mode 100644 index 00000000..9e241246 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_020.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_021.spv b/test/fuzzers/corpora/spv/graphicsfuzz_021.spv new file mode 100644 index 00000000..02734af1 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_021.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_022.spv b/test/fuzzers/corpora/spv/graphicsfuzz_022.spv new file mode 100644 index 00000000..cd9ab76b Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_022.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_023.spv b/test/fuzzers/corpora/spv/graphicsfuzz_023.spv new file mode 100644 index 00000000..d0ec5d31 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_023.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_024.spv b/test/fuzzers/corpora/spv/graphicsfuzz_024.spv new file mode 100644 index 00000000..503627f6 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_024.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_025.spv b/test/fuzzers/corpora/spv/graphicsfuzz_025.spv new file mode 100644 index 00000000..c4e0aa35 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_025.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_026.spv b/test/fuzzers/corpora/spv/graphicsfuzz_026.spv new file mode 100644 index 00000000..c5b3cedd Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_026.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_027.spv b/test/fuzzers/corpora/spv/graphicsfuzz_027.spv new file mode 100644 index 00000000..626e606f Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_027.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_028.spv b/test/fuzzers/corpora/spv/graphicsfuzz_028.spv new file mode 100644 index 00000000..a9a43577 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_028.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_029.spv b/test/fuzzers/corpora/spv/graphicsfuzz_029.spv new file mode 100644 index 00000000..18e49a5a Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_029.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_030.spv b/test/fuzzers/corpora/spv/graphicsfuzz_030.spv new file mode 100644 index 00000000..ac4ea364 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_030.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_031.spv b/test/fuzzers/corpora/spv/graphicsfuzz_031.spv new file mode 100644 index 00000000..2f8eff69 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_031.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_032.spv b/test/fuzzers/corpora/spv/graphicsfuzz_032.spv new file mode 100644 index 00000000..a5808ecf Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_032.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_033.spv b/test/fuzzers/corpora/spv/graphicsfuzz_033.spv new file mode 100644 index 00000000..98cf02e2 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_033.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_034.spv b/test/fuzzers/corpora/spv/graphicsfuzz_034.spv new file mode 100644 index 00000000..306fd845 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_034.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_035.spv b/test/fuzzers/corpora/spv/graphicsfuzz_035.spv new file mode 100644 index 00000000..7ed19e09 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_035.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_036.spv b/test/fuzzers/corpora/spv/graphicsfuzz_036.spv new file mode 100644 index 00000000..22e67839 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_036.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_037.spv b/test/fuzzers/corpora/spv/graphicsfuzz_037.spv new file mode 100644 index 00000000..19fbd20f Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_037.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_038.spv b/test/fuzzers/corpora/spv/graphicsfuzz_038.spv new file mode 100644 index 00000000..7bc54890 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_038.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_039.spv b/test/fuzzers/corpora/spv/graphicsfuzz_039.spv new file mode 100644 index 00000000..01b908de Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_039.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_040.spv b/test/fuzzers/corpora/spv/graphicsfuzz_040.spv new file mode 100644 index 00000000..bd131571 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_040.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_041.spv b/test/fuzzers/corpora/spv/graphicsfuzz_041.spv new file mode 100644 index 00000000..6e355c20 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_041.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_042.spv b/test/fuzzers/corpora/spv/graphicsfuzz_042.spv new file mode 100644 index 00000000..d356cb19 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_042.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_043.spv b/test/fuzzers/corpora/spv/graphicsfuzz_043.spv new file mode 100644 index 00000000..49c87ed4 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_043.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_044.spv b/test/fuzzers/corpora/spv/graphicsfuzz_044.spv new file mode 100644 index 00000000..c7e36d5c Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_044.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_045.spv b/test/fuzzers/corpora/spv/graphicsfuzz_045.spv new file mode 100644 index 00000000..ad197263 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_045.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_046.spv b/test/fuzzers/corpora/spv/graphicsfuzz_046.spv new file mode 100644 index 00000000..3b3678ad Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_046.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_047.spv b/test/fuzzers/corpora/spv/graphicsfuzz_047.spv new file mode 100644 index 00000000..7253247b Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_047.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_048.spv b/test/fuzzers/corpora/spv/graphicsfuzz_048.spv new file mode 100644 index 00000000..5c05c1c0 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_048.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_049.spv b/test/fuzzers/corpora/spv/graphicsfuzz_049.spv new file mode 100644 index 00000000..22bc9c21 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_049.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_050.spv b/test/fuzzers/corpora/spv/graphicsfuzz_050.spv new file mode 100644 index 00000000..8f847880 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_050.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_051.spv b/test/fuzzers/corpora/spv/graphicsfuzz_051.spv new file mode 100644 index 00000000..727c64d2 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_051.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_052.spv b/test/fuzzers/corpora/spv/graphicsfuzz_052.spv new file mode 100644 index 00000000..d9ba7418 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_052.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_053.spv b/test/fuzzers/corpora/spv/graphicsfuzz_053.spv new file mode 100644 index 00000000..d8270049 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_053.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_054.spv b/test/fuzzers/corpora/spv/graphicsfuzz_054.spv new file mode 100644 index 00000000..a3aec7b6 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_054.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_055.spv b/test/fuzzers/corpora/spv/graphicsfuzz_055.spv new file mode 100644 index 00000000..2da1375f Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_055.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_056.spv b/test/fuzzers/corpora/spv/graphicsfuzz_056.spv new file mode 100644 index 00000000..515d4f1c Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_056.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_057.spv b/test/fuzzers/corpora/spv/graphicsfuzz_057.spv new file mode 100644 index 00000000..2a0f4897 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_057.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_058.spv b/test/fuzzers/corpora/spv/graphicsfuzz_058.spv new file mode 100644 index 00000000..3211ec20 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_058.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_059.spv b/test/fuzzers/corpora/spv/graphicsfuzz_059.spv new file mode 100644 index 00000000..89e34ecf Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_059.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_060.spv b/test/fuzzers/corpora/spv/graphicsfuzz_060.spv new file mode 100644 index 00000000..d1fdaecc Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_060.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_061.spv b/test/fuzzers/corpora/spv/graphicsfuzz_061.spv new file mode 100644 index 00000000..de10f68b Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_061.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_062.spv b/test/fuzzers/corpora/spv/graphicsfuzz_062.spv new file mode 100644 index 00000000..503304c3 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_062.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_063.spv b/test/fuzzers/corpora/spv/graphicsfuzz_063.spv new file mode 100644 index 00000000..75d9b4c4 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_063.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_064.spv b/test/fuzzers/corpora/spv/graphicsfuzz_064.spv new file mode 100644 index 00000000..0b902cb2 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_064.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_065.spv b/test/fuzzers/corpora/spv/graphicsfuzz_065.spv new file mode 100644 index 00000000..437e45b0 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_065.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_066.spv b/test/fuzzers/corpora/spv/graphicsfuzz_066.spv new file mode 100644 index 00000000..5585a6f8 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_066.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_067.spv b/test/fuzzers/corpora/spv/graphicsfuzz_067.spv new file mode 100644 index 00000000..763fd34f Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_067.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_068.spv b/test/fuzzers/corpora/spv/graphicsfuzz_068.spv new file mode 100644 index 00000000..1ad0bd50 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_068.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_069.spv b/test/fuzzers/corpora/spv/graphicsfuzz_069.spv new file mode 100644 index 00000000..f1e7950b Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_069.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_070.spv b/test/fuzzers/corpora/spv/graphicsfuzz_070.spv new file mode 100644 index 00000000..47f4c829 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_070.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_071.spv b/test/fuzzers/corpora/spv/graphicsfuzz_071.spv new file mode 100644 index 00000000..3eac74aa Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_071.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_072.spv b/test/fuzzers/corpora/spv/graphicsfuzz_072.spv new file mode 100644 index 00000000..e2461157 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_072.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_073.spv b/test/fuzzers/corpora/spv/graphicsfuzz_073.spv new file mode 100644 index 00000000..b7e915be Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_073.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_074.spv b/test/fuzzers/corpora/spv/graphicsfuzz_074.spv new file mode 100644 index 00000000..35887578 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_074.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_075.spv b/test/fuzzers/corpora/spv/graphicsfuzz_075.spv new file mode 100644 index 00000000..d3dc46dc Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_075.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_076.spv b/test/fuzzers/corpora/spv/graphicsfuzz_076.spv new file mode 100644 index 00000000..11bee684 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_076.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_077.spv b/test/fuzzers/corpora/spv/graphicsfuzz_077.spv new file mode 100644 index 00000000..d1b8c76a Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_077.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_078.spv b/test/fuzzers/corpora/spv/graphicsfuzz_078.spv new file mode 100644 index 00000000..4739a9ac Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_078.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_079.spv b/test/fuzzers/corpora/spv/graphicsfuzz_079.spv new file mode 100644 index 00000000..6553ecf4 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_079.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_080.spv b/test/fuzzers/corpora/spv/graphicsfuzz_080.spv new file mode 100644 index 00000000..802375b4 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_080.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_081.spv b/test/fuzzers/corpora/spv/graphicsfuzz_081.spv new file mode 100644 index 00000000..72d1027f Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_081.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_082.spv b/test/fuzzers/corpora/spv/graphicsfuzz_082.spv new file mode 100644 index 00000000..619c742c Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_082.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_083.spv b/test/fuzzers/corpora/spv/graphicsfuzz_083.spv new file mode 100644 index 00000000..fa27c33a Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_083.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_084.spv b/test/fuzzers/corpora/spv/graphicsfuzz_084.spv new file mode 100644 index 00000000..14f7bfdd Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_084.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_085.spv b/test/fuzzers/corpora/spv/graphicsfuzz_085.spv new file mode 100644 index 00000000..5f5fc03d Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_085.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_086.spv b/test/fuzzers/corpora/spv/graphicsfuzz_086.spv new file mode 100644 index 00000000..a3119927 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_086.spv differ diff --git a/test/fuzzers/random_generator.cpp b/test/fuzzers/random_generator.cpp new file mode 100644 index 00000000..801a9ffe --- /dev/null +++ b/test/fuzzers/random_generator.cpp @@ -0,0 +1,135 @@ +// Copyright (c) 2021 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/fuzzers/random_generator.h" + +#include +#include +#include + +namespace spvtools { +namespace fuzzers { + +namespace { +/// Generate integer from uniform distribution +/// @tparam I - integer type +/// @param engine - random number engine to use +/// @param lower - Lower bound of integer generated +/// @param upper - Upper bound of integer generated +/// @returns i, where lower <= i < upper +template +I RandomUInt(std::mt19937_64* engine, I lower, I upper) { + assert(lower < upper && "|lower| must be stictly less than |upper|"); + return std::uniform_int_distribution(lower, upper - 1)(*engine); +} + +/// Helper for obtaining a seed bias value for HashCombine with a bit-width +/// dependent on the size of size_t. +template +struct HashCombineOffset {}; +/// Specialization of HashCombineOffset for size_t == 4. +template <> +struct HashCombineOffset<4> { + /// @returns the seed bias value for HashCombine() + static constexpr inline uint32_t value() { + return 0x9e3779b9; // Fractional portion of Golden Ratio, suggested by + // Linux Kernel and Knuth's Art of Computer Programming + } +}; +/// Specialization of HashCombineOffset for size_t == 8. +template <> +struct HashCombineOffset<8> { + /// @returns the seed bias value for HashCombine() + static constexpr inline uint64_t value() { + return 0x9e3779b97f4a7c16; // Fractional portion of Golden Ratio, suggested + // by Linux Kernel and Knuth's Art of Computer + // Programming + } +}; + +/// HashCombine "hashes" together an existing hash and hashable values. +template +void HashCombine(size_t* hash, const T& value) { + constexpr size_t offset = HashCombineOffset::value(); + *hash ^= std::hash()(value) + offset + (*hash << 6) + (*hash >> 2); +} + +/// Calculate the hash for the contents of a C-style data buffer +/// @param data - pointer to buffer to be hashed +/// @param size - number of elements in buffer +/// @returns hash of the data in the buffer +size_t HashBuffer(const uint8_t* data, const size_t size) { + size_t hash = + static_cast(0xCA8945571519E991); // seed with an arbitrary prime + HashCombine(&hash, size); + for (size_t i = 0; i < size; i++) { + HashCombine(&hash, data[i]); + } + return hash; +} + +} // namespace + +RandomGenerator::RandomGenerator(uint64_t seed) : engine_(seed) {} + +RandomGenerator::RandomGenerator(const uint8_t* data, size_t size) { + RandomGenerator(RandomGenerator::CalculateSeed(data, size)); +} + +spv_target_env RandomGenerator::GetTargetEnv() { + spv_target_env result; + + // Need to check that the generated value isn't for a deprecated target env. + do { + result = static_cast( + RandomUInt(&engine_, 0u, static_cast(SPV_ENV_MAX))); + } while (!spvIsValidEnv(result)); + + return result; +} + +uint32_t RandomGenerator::GetUInt32(uint32_t lower, uint32_t upper) { + return RandomUInt(&engine_, lower, upper); +} + +uint32_t RandomGenerator::GetUInt32(uint32_t bound) { + assert(bound > 0 && "|bound| must be greater than 0"); + return RandomUInt(&engine_, 0u, bound); +} + +uint64_t RandomGenerator::CalculateSeed(const uint8_t* data, size_t size) { + assert(data != nullptr && "|data| must be !nullptr"); + + // Number of bytes we want to skip at the start of data for the hash. + // Fewer bytes may be skipped when `size` is small. + // Has lower precedence than kHashDesiredMinBytes. + static const int64_t kHashDesiredLeadingSkipBytes = 5; + // Minimum number of bytes we want to use in the hash. + // Used for short buffers. + static const int64_t kHashDesiredMinBytes = 4; + // Maximum number of bytes we want to use in the hash. + static const int64_t kHashDesiredMaxBytes = 32; + int64_t size_i64 = static_cast(size); + int64_t hash_begin_i64 = + std::min(kHashDesiredLeadingSkipBytes, + std::max(size_i64 - kHashDesiredMinBytes, 0)); + int64_t hash_end_i64 = + std::min(hash_begin_i64 + kHashDesiredMaxBytes, size_i64); + size_t hash_begin = static_cast(hash_begin_i64); + size_t hash_size = static_cast(hash_end_i64) - hash_begin; + return HashBuffer(data + hash_begin, hash_size); +} + +} // namespace fuzzers +} // namespace spvtools diff --git a/test/fuzzers/random_generator.h b/test/fuzzers/random_generator.h new file mode 100644 index 00000000..b121fe8c --- /dev/null +++ b/test/fuzzers/random_generator.h @@ -0,0 +1,68 @@ +// Copyright (c) 2021 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEST_FUZZERS_RANDOM_GENERATOR_H_ +#define TEST_FUZZERS_RANDOM_GENERATOR_H_ + +#include +#include + +#include "source/spirv_target_env.h" + +namespace spvtools { +namespace fuzzers { + +/// Pseudo random generator utility class for fuzzing +class RandomGenerator { + public: + /// @brief Initializes the internal engine + /// @param seed - seed value passed to engine + explicit RandomGenerator(uint64_t seed); + + /// @brief Initializes the internal engine + /// @param data - data to calculate the seed from + /// @param size - size of the data + explicit RandomGenerator(const uint8_t* data, size_t size); + + ~RandomGenerator() {} + + /// Calculate a seed value based on a blob of data. + /// Currently hashes bytes near the front of the buffer, after skipping N + /// bytes. + /// @param data - pointer to data to base calculation off of, must be !nullptr + /// @param size - number of elements in |data|, must be > 0 + static uint64_t CalculateSeed(const uint8_t* data, size_t size); + + /// Get random valid target env. + spv_target_env GetTargetEnv(); + + /// Get uint32_t value from uniform distribution. + /// @param lower - lower bound of integer generated + /// @param upper - upper bound of integer generated + /// @returns i, where lower <= i < upper + uint32_t GetUInt32(uint32_t lower, uint32_t upper); + + /// Get uint32_t value from uniform distribution. + /// @param bound - Upper bound of integer generated + /// @returns i, where 0 <= i < bound + uint32_t GetUInt32(uint32_t bound); + + private: + std::mt19937_64 engine_; +}; // class RandomGenerator + +} // namespace fuzzers +} // namespace spvtools + +#endif // TEST_FUZZERS_RANDOM_GENERATOR_UTILS_H_ diff --git a/test/fuzzers/spvtools_as_fuzzer.cpp b/test/fuzzers/spvtools_as_fuzzer.cpp index 8cecb05f..8ead1cff 100644 --- a/test/fuzzers/spvtools_as_fuzzer.cpp +++ b/test/fuzzers/spvtools_as_fuzzer.cpp @@ -18,27 +18,27 @@ #include "source/spirv_target_env.h" #include "spirv-tools/libspirv.hpp" +#include "test/fuzzers/random_generator.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size < sizeof(spv_target_env) + 1) return 0; + spv_target_env target_env = SPV_ENV_UNIVERSAL_1_0; + if (size > 0) { + spvtools::fuzzers::RandomGenerator random_gen(data, size); + target_env = random_gen.GetTargetEnv(); + } - const spv_context context = - spvContextCreate(*reinterpret_cast(data)); - if (context == nullptr) return 0; + const spv_context context = spvContextCreate(target_env); + if (context == nullptr) { + return 0; + } - data += sizeof(spv_target_env); - size -= sizeof(spv_target_env); - - std::vector input; - - std::vector input_str; - size_t char_count = input.size() * sizeof(uint32_t) / sizeof(char); - input_str.resize(char_count); - memcpy(input_str.data(), input.data(), input.size() * sizeof(uint32_t)); + std::vector contents; + contents.resize(size); + memcpy(contents.data(), data, size); spv_binary binary = nullptr; spv_diagnostic diagnostic = nullptr; - spvTextToBinaryWithOptions(context, input_str.data(), input_str.size(), + spvTextToBinaryWithOptions(context, contents.data(), contents.size(), SPV_TEXT_TO_BINARY_OPTION_NONE, &binary, &diagnostic); if (diagnostic) { @@ -52,7 +52,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { binary = nullptr; } - spvTextToBinaryWithOptions(context, input_str.data(), input_str.size(), + spvTextToBinaryWithOptions(context, contents.data(), contents.size(), SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS, &binary, &diagnostic); if (diagnostic) { diff --git a/test/fuzzers/spvtools_binary_parser_fuzzer.cpp b/test/fuzzers/spvtools_binary_parser_fuzzer.cpp index 76ba4d9e..3a97db41 100644 --- a/test/fuzzers/spvtools_binary_parser_fuzzer.cpp +++ b/test/fuzzers/spvtools_binary_parser_fuzzer.cpp @@ -16,16 +16,18 @@ #include #include "spirv-tools/libspirv.hpp" +#include "test/fuzzers/random_generator.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size < sizeof(spv_target_env) + 1) return 0; + if (size < 1) { + return 0; + } - const spv_context context = - spvContextCreate(*reinterpret_cast(data)); - if (context == nullptr) return 0; - - data += sizeof(spv_target_env); - size -= sizeof(spv_target_env); + spvtools::fuzzers::RandomGenerator random_gen(data, size); + const spv_context context = spvContextCreate(random_gen.GetTargetEnv()); + if (context == nullptr) { + return 0; + } std::vector input; input.resize(size >> 2); diff --git a/test/fuzzers/spvtools_dis_fuzzer.cpp b/test/fuzzers/spvtools_dis_fuzzer.cpp index ca9a52d8..0cb0eff5 100644 --- a/test/fuzzers/spvtools_dis_fuzzer.cpp +++ b/test/fuzzers/spvtools_dis_fuzzer.cpp @@ -18,16 +18,20 @@ #include "source/spirv_target_env.h" #include "spirv-tools/libspirv.hpp" +#include "test/fuzzers/random_generator.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size < sizeof(spv_target_env) + 1) return 0; + if (size < 4) { + // There are not enough bytes to constitute a binary that can be + // disassembled. + return 0; + } - const spv_context context = - spvContextCreate(*reinterpret_cast(data)); - if (context == nullptr) return 0; - - data += sizeof(spv_target_env); - size -= sizeof(spv_target_env); + spvtools::fuzzers::RandomGenerator random_gen(data, size); + const spv_context context = spvContextCreate(random_gen.GetTargetEnv()); + if (context == nullptr) { + return 0; + } std::vector input; input.resize(size >> 2); diff --git a/test/fuzzers/spvtools_fuzz_fuzzer.cpp b/test/fuzzers/spvtools_fuzz_fuzzer.cpp new file mode 100644 index 00000000..d43920cf --- /dev/null +++ b/test/fuzzers/spvtools_fuzz_fuzzer.cpp @@ -0,0 +1,80 @@ +// Copyright (c) 2021 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 +#include + +#include "source/fuzz/fuzzer.h" +#include "source/fuzz/pseudo_random_generator.h" +#include "spirv-tools/libspirv.hpp" +#include "test/fuzzers/random_generator.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size == 0 || (size % sizeof(uint32_t)) != 0) { + // An empty binary, or a binary whose size is not a multiple of word-size, + // cannot be valid, so can be rejected immediately. + return 0; + } + + std::vector initial_binary(size / sizeof(uint32_t)); + memcpy(initial_binary.data(), data, size); + + spvtools::ValidatorOptions validator_options; + + spvtools::MessageConsumer message_consumer = + [](spv_message_level_t, const char*, const spv_position_t&, const char*) { + }; + + spvtools::fuzzers::RandomGenerator random_gen(data, size); + auto target_env = random_gen.GetTargetEnv(); + std::unique_ptr ir_context; + if (!spvtools::fuzz::fuzzerutil::BuildIRContext( + target_env, message_consumer, initial_binary, validator_options, + &ir_context)) { + // The input is invalid - give up. + return 0; + } + + std::vector donor_suppliers = { + [&initial_binary, message_consumer, target_env, + &validator_options]() -> std::unique_ptr { + std::unique_ptr result; + if (!spvtools::fuzz::fuzzerutil::BuildIRContext( + target_env, message_consumer, initial_binary, validator_options, + &result)) { + // The input was successfully parsed and validated first time around, + // so something is wrong if it is now invalid. + abort(); + } + return result; + }}; + + uint32_t seed = random_gen.GetUInt32(std::numeric_limits::max()); + auto fuzzer_context = spvtools::MakeUnique( + spvtools::MakeUnique(seed), + spvtools::fuzz::FuzzerContext::GetMinFreshId(ir_context.get()), false); + + auto transformation_context = + spvtools::MakeUnique( + spvtools::MakeUnique(ir_context.get()), + validator_options); + + spvtools::fuzz::Fuzzer fuzzer( + std::move(ir_context), std::move(transformation_context), + std::move(fuzzer_context), message_consumer, donor_suppliers, false, + spvtools::fuzz::RepeatedPassStrategy::kLoopedWithRecommendations, true, + validator_options); + fuzzer.Run(0); + return 0; +} diff --git a/test/fuzzers/spvtools_opt_fuzzer_common.cpp b/test/fuzzers/spvtools_opt_fuzzer_common.cpp new file mode 100644 index 00000000..49785090 --- /dev/null +++ b/test/fuzzers/spvtools_opt_fuzzer_common.cpp @@ -0,0 +1,84 @@ +// Copyright (c) 2021 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 "test/fuzzers/spvtools_opt_fuzzer_common.h" + +#include "source/opt/build_module.h" +#include "test/fuzzers/random_generator.h" + +namespace spvtools { +namespace fuzzers { + +int OptFuzzerTestOneInput( + const uint8_t* data, size_t size, + const std::function& register_passes) { + if (size < 1) { + return 0; + } + + spvtools::fuzzers::RandomGenerator random_gen(data, size); + auto target_env = random_gen.GetTargetEnv(); + spvtools::Optimizer optimizer(target_env); + optimizer.SetMessageConsumer([](spv_message_level_t, const char*, + const spv_position_t&, const char*) {}); + + std::vector input; + input.resize(size >> 2); + + size_t count = 0; + for (size_t i = 0; (i + 3) < size; i += 4) { + input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | + (data[i + 3]) << 24; + } + + // The largest possible id bound is used when running the optimizer, to avoid + // the problem of id overflows. + const size_t kFinalIdLimit = UINT32_MAX; + + // The input is scanned to check that it does not already use an id too close + // to this limit. This still gives the optimizer a large set of ids to + // consume. It is thus very unlikely that id overflow will occur during + // fuzzing. If it does, then the initial id limit should be decreased. + const size_t kInitialIdLimit = kFinalIdLimit - 1000000U; + + // Build the module and scan it to check that all used ids are below the + // initial limit. + auto ir_context = + spvtools::BuildModule(target_env, nullptr, input.data(), input.size()); + if (ir_context == nullptr) { + // It was not possible to build a valid module; that's OK - skip this input. + return 0; + } + if (ir_context->module()->id_bound() >= kInitialIdLimit) { + // The input already has a very large id bound. The input is thus abandoned, + // to avoid the possibility of ending up hitting the id bound limit. + return 0; + } + + // Set the optimizer and its validator up with the largest possible id bound + // limit. + spvtools::ValidatorOptions validator_options; + spvtools::OptimizerOptions optimizer_options; + optimizer_options.set_max_id_bound(kFinalIdLimit); + validator_options.SetUniversalLimit(spv_validator_limit_max_id_bound, + kFinalIdLimit); + optimizer_options.set_validator_options(validator_options); + register_passes(optimizer); + optimizer.Run(input.data(), input.size(), &input, optimizer_options); + + return 0; +} + +} // namespace fuzzers +} // namespace spvtools diff --git a/test/fuzzers/spvtools_opt_fuzzer_common.h b/test/fuzzers/spvtools_opt_fuzzer_common.h new file mode 100644 index 00000000..b8d4281c --- /dev/null +++ b/test/fuzzers/spvtools_opt_fuzzer_common.h @@ -0,0 +1,35 @@ +// Copyright (c) 2021 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 TEST_FUZZERS_SPVTOOLS_OPT_FUZZER_COMMON_H_ +#define TEST_FUZZERS_SPVTOOLS_OPT_FUZZER_COMMON_H_ + +#include +#include +#include + +#include "spirv-tools/optimizer.hpp" + +namespace spvtools { +namespace fuzzers { + +// Helper function capturing the common logic for the various optimizer fuzzers. +int OptFuzzerTestOneInput( + const uint8_t* data, size_t size, + const std::function& register_passes); + +} // namespace fuzzers +} // namespace spvtools + +#endif // TEST_FUZZERS_SPVTOOLS_OPT_FUZZER_COMMON_H_ diff --git a/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp b/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp index b45a98c3..fac4d23c 100644 --- a/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp +++ b/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp @@ -12,27 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include -#include +#include +#include +#include #include "spirv-tools/optimizer.hpp" +#include "test/fuzzers/spvtools_opt_fuzzer_common.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_3); - optimizer.SetMessageConsumer([](spv_message_level_t, const char*, - const spv_position_t&, const char*) {}); - - std::vector input; - input.resize(size >> 2); - - size_t count = 0; - for (size_t i = 0; (i + 3) < size; i += 4) { - input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | - (data[i + 3]) << 24; - } - - optimizer.RegisterLegalizationPasses(); - optimizer.Run(input.data(), input.size(), &input); - - return 0; + return spvtools::fuzzers::OptFuzzerTestOneInput( + data, size, [](spvtools::Optimizer& optimizer) -> void { + optimizer.RegisterLegalizationPasses(); + }); } diff --git a/test/fuzzers/spvtools_opt_performance_fuzzer.cpp b/test/fuzzers/spvtools_opt_performance_fuzzer.cpp index 6c3bd6ab..e6038b90 100644 --- a/test/fuzzers/spvtools_opt_performance_fuzzer.cpp +++ b/test/fuzzers/spvtools_opt_performance_fuzzer.cpp @@ -12,27 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include -#include +#include +#include +#include #include "spirv-tools/optimizer.hpp" +#include "test/fuzzers/spvtools_opt_fuzzer_common.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_3); - optimizer.SetMessageConsumer([](spv_message_level_t, const char*, - const spv_position_t&, const char*) {}); - - std::vector input; - input.resize(size >> 2); - - size_t count = 0; - for (size_t i = 0; (i + 3) < size; i += 4) { - input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | - (data[i + 3]) << 24; - } - - optimizer.RegisterPerformancePasses(); - optimizer.Run(input.data(), input.size(), &input); - - return 0; + return spvtools::fuzzers::OptFuzzerTestOneInput( + data, size, [](spvtools::Optimizer& optimizer) -> void { + optimizer.RegisterPerformancePasses(); + }); } diff --git a/test/fuzzers/spvtools_opt_size_fuzzer.cpp b/test/fuzzers/spvtools_opt_size_fuzzer.cpp index 68c79747..65492b1a 100644 --- a/test/fuzzers/spvtools_opt_size_fuzzer.cpp +++ b/test/fuzzers/spvtools_opt_size_fuzzer.cpp @@ -12,27 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include -#include +#include +#include +#include #include "spirv-tools/optimizer.hpp" +#include "test/fuzzers/spvtools_opt_fuzzer_common.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_3); - optimizer.SetMessageConsumer([](spv_message_level_t, const char*, - const spv_position_t&, const char*) {}); - - std::vector input; - input.resize(size >> 2); - - size_t count = 0; - for (size_t i = 0; (i + 3) < size; i += 4) { - input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | - (data[i + 3]) << 24; - } - - optimizer.RegisterSizePasses(); - optimizer.Run(input.data(), input.size(), &input); - - return 0; + return spvtools::fuzzers::OptFuzzerTestOneInput( + data, size, [](spvtools::Optimizer& optimizer) -> void { + optimizer.RegisterSizePasses(); + }); } diff --git a/test/fuzzers/spvtools_val_fuzzer.cpp b/test/fuzzers/spvtools_val_fuzzer.cpp index 5dc4303b..fd6396cd 100644 --- a/test/fuzzers/spvtools_val_fuzzer.cpp +++ b/test/fuzzers/spvtools_val_fuzzer.cpp @@ -16,9 +16,15 @@ #include #include "spirv-tools/libspirv.hpp" +#include "test/fuzzers/random_generator.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_3); + if (size < 1) { + return 0; + } + + spvtools::fuzzers::RandomGenerator random_gen(data, size); + spvtools::SpirvTools tools(random_gen.GetTargetEnv()); tools.SetMessageConsumer([](spv_message_level_t, const char*, const spv_position_t&, const char*) {}); diff --git a/test/hex_float_test.cpp b/test/hex_float_test.cpp index c422f756..25d3c707 100644 --- a/test/hex_float_test.cpp +++ b/test/hex_float_test.cpp @@ -1325,6 +1325,117 @@ TEST(FloatProxy, Lowest) { Eq(std::numeric_limits::lowest())); } +template +struct StreamParseCase { + StreamParseCase(const std::string& lit, bool succ, const std::string& suffix, + T value) + : literal(lit), + expect_success(succ), + expected_suffix(suffix), + expected_value(HexFloat>(value)) {} + + std::string literal; + bool expect_success; + std::string expected_suffix; + HexFloat> expected_value; +}; + +template +std::ostream& operator<<(std::ostream& os, const StreamParseCase& fspc) { + os << "StreamParseCase(" << fspc.literal + << ", expect_success:" << int(fspc.expect_success) << "," + << fspc.expected_suffix << "," << fspc.expected_value << ")"; + return os; +} + +using FloatStreamParseTest = ::testing::TestWithParam>; + +TEST_P(FloatStreamParseTest, Samples) { + std::stringstream input(GetParam().literal); + HexFloat> parsed_value(0.0f); + // Hex floats must be read with the stream input operator. + input >> parsed_value; + if (GetParam().expect_success) { + EXPECT_FALSE(input.fail()); + std::string suffix; + input >> suffix; + // EXPECT_EQ(suffix, GetParam().expected_suffix); + EXPECT_EQ(parsed_value.value().getAsFloat(), + GetParam().expected_value.value().getAsFloat()); + } else { + EXPECT_TRUE(input.fail()); + } +} + +INSTANTIATE_TEST_SUITE_P( + HexFloatExponentMissingDigits, FloatStreamParseTest, + ::testing::ValuesIn(std::vector>{ + {"0x1.0p1", true, "", 2.0f}, + {"0x1.0p1a", true, "a", 2.0f}, + {"-0x1.0p1f", true, "f", -2.0f}, + {"0x1.0p", false, "", 0.0f}, + {"0x1.0pa", false, "", 0.0f}, + {"0x1.0p!", false, "", 0.0f}, + {"0x1.0p+", false, "", 0.0f}, + {"0x1.0p+a", false, "", 0.0f}, + {"0x1.0p+!", false, "", 0.0f}, + {"0x1.0p-", false, "", 0.0f}, + {"0x1.0p-a", false, "", 0.0f}, + {"0x1.0p-!", false, "", 0.0f}, + {"0x1.0p++", false, "", 0.0f}, + {"0x1.0p+-", false, "", 0.0f}, + {"0x1.0p-+", false, "", 0.0f}, + {"0x1.0p--", false, "", 0.0f}})); + +INSTANTIATE_TEST_SUITE_P( + HexFloatExponentTrailingSign, FloatStreamParseTest, + ::testing::ValuesIn(std::vector>{ + // Don't consume a sign after the binary exponent digits. + {"0x1.0p1", true, "", 2.0f}, + {"0x1.0p1+", true, "+", 2.0f}, + {"0x1.0p1-", true, "-", 2.0f}})); + +INSTANTIATE_TEST_SUITE_P( + HexFloatPositiveExponentOverflow, FloatStreamParseTest, + ::testing::ValuesIn(std::vector>{ + // Positive exponents + {"0x1.0p1", true, "", 2.0f}, // fine, a normal number + {"0x1.0p15", true, "", 32768.0f}, // fine, a normal number + {"0x1.0p127", true, "", float(ldexp(1.0f, 127))}, // good large number + {"0x0.8p128", true, "", float(ldexp(1.0f, 127))}, // good large number + {"0x0.1p131", true, "", float(ldexp(1.0f, 127))}, // good large number + {"0x0.01p135", true, "", float(ldexp(1.0f, 127))}, // good large number + {"0x1.0p128", true, "", float(ldexp(1.0f, 128))}, // infinity + {"0x1.0p4294967295", true, "", float(ldexp(1.0f, 128))}, // infinity + {"0x1.0p5000000000", true, "", float(ldexp(1.0f, 128))}, // infinity + {"0x0.0p5000000000", true, "", 0.0f}, // zero mantissa, zero result + })); + +INSTANTIATE_TEST_SUITE_P( + HexFloatNegativeExponentOverflow, FloatStreamParseTest, + ::testing::ValuesIn(std::vector>{ + // Positive results, digits before '.' + {"0x1.0p-126", true, "", + float(ldexp(1.0f, -126))}, // fine, a small normal number + {"0x1.0p-127", true, "", float(ldexp(1.0f, -127))}, // denorm number + {"0x1.0p-149", true, "", + float(ldexp(1.0f, -149))}, // smallest positive denormal + {"0x0.8p-148", true, "", + float(ldexp(1.0f, -149))}, // smallest positive denormal + {"0x0.1p-145", true, "", + float(ldexp(1.0f, -149))}, // smallest positive denormal + {"0x0.01p-141", true, "", + float(ldexp(1.0f, -149))}, // smallest positive denormal + + // underflow rounds down to zero + {"0x1.0p-150", true, "", 0.0f}, + {"0x1.0p-4294967296", true, "", + 0.0f}, // avoid exponent overflow in parser + {"0x1.0p-5000000000", true, "", + 0.0f}, // avoid exponent overflow in parser + {"0x0.0p-5000000000", true, "", 0.0f}, // zero mantissa, zero result + })); + // TODO(awoloszyn): Add fp16 tests and HexFloatTraits. } // namespace } // namespace utils diff --git a/test/link/binary_version_test.cpp b/test/link/binary_version_test.cpp index 80aab0fd..a56030f4 100644 --- a/test/link/binary_version_test.cpp +++ b/test/link/binary_version_test.cpp @@ -20,40 +20,57 @@ namespace spvtools { namespace { +using ::testing::HasSubstr; using BinaryVersion = spvtest::LinkerTest; -TEST_F(BinaryVersion, LinkerChoosesMaxSpirvVersion) { +spvtest::Binary CreateBinary(uint32_t version) { + return { + // clang-format off + // Header + SpvMagicNumber, + version, + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0), + 1u, // NOTE: Bound + 0u, // NOTE: Schema; reserved + + // OpCapability Shader + SpvOpCapability | 2u << SpvWordCountShift, + SpvCapabilityShader, + + // OpMemoryModel Logical Simple + SpvOpMemoryModel | 3u << SpvWordCountShift, + SpvAddressingModelLogical, + SpvMemoryModelSimple + // clang-format on + }; +} + +TEST_F(BinaryVersion, Match) { // clang-format off spvtest::Binaries binaries = { - { - SpvMagicNumber, - 0x00010300u, - SPV_GENERATOR_CODEPLAY, - 1u, // NOTE: Bound - 0u // NOTE: Schema; reserved - }, - { - SpvMagicNumber, - 0x00010500u, - SPV_GENERATOR_CODEPLAY, - 1u, // NOTE: Bound - 0u // NOTE: Schema; reserved - }, - { - SpvMagicNumber, - 0x00010100u, - SPV_GENERATOR_CODEPLAY, - 1u, // NOTE: Bound - 0u // NOTE: Schema; reserved - } + CreateBinary(SPV_SPIRV_VERSION_WORD(1, 3)), + CreateBinary(SPV_SPIRV_VERSION_WORD(1, 3)), }; // clang-format on spvtest::Binary linked_binary; - - ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)); + ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)) << GetErrorMessage(); EXPECT_THAT(GetErrorMessage(), std::string()); + EXPECT_EQ(SPV_SPIRV_VERSION_WORD(1, 3), linked_binary[1]); +} - EXPECT_EQ(0x00010500u, linked_binary[1]); +TEST_F(BinaryVersion, Mismatch) { + // clang-format off + spvtest::Binaries binaries = { + CreateBinary(SPV_SPIRV_VERSION_WORD(1, 3)), + CreateBinary(SPV_SPIRV_VERSION_WORD(1, 5)), + }; + // clang-format on + spvtest::Binary linked_binary; + ASSERT_EQ(SPV_ERROR_INTERNAL, Link(binaries, &linked_binary)) + << GetErrorMessage(); + EXPECT_THAT(GetErrorMessage(), + HasSubstr("Conflicting SPIR-V versions: 1.3 (input modules 1 " + "through 1) vs 1.5 (input module 2).")); } } // namespace diff --git a/test/link/entry_points_test.cpp b/test/link/entry_points_test.cpp index bac8e02e..edf9f424 100644 --- a/test/link/entry_points_test.cpp +++ b/test/link/entry_points_test.cpp @@ -26,6 +26,8 @@ class EntryPoints : public spvtest::LinkerTest {}; TEST_F(EntryPoints, SameModelDifferentName) { const std::string body1 = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %3 "foo" %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -33,6 +35,8 @@ OpEntryPoint GLCompute %3 "foo" OpFunctionEnd )"; const std::string body2 = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %3 "bar" %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -41,12 +45,15 @@ OpFunctionEnd )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)); + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + << GetErrorMessage(); EXPECT_THAT(GetErrorMessage(), std::string()); } TEST_F(EntryPoints, DifferentModelSameName) { const std::string body1 = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %3 "foo" %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -54,6 +61,8 @@ OpEntryPoint GLCompute %3 "foo" OpFunctionEnd )"; const std::string body2 = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %3 "foo" %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -62,12 +71,15 @@ OpFunctionEnd )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)); + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + << GetErrorMessage(); EXPECT_THAT(GetErrorMessage(), std::string()); } TEST_F(EntryPoints, SameModelAndName) { const std::string body1 = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %3 "foo" %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -75,6 +87,8 @@ OpEntryPoint GLCompute %3 "foo" OpFunctionEnd )"; const std::string body2 = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %3 "foo" %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -90,5 +104,48 @@ OpFunctionEnd "GLCompute, was already defined.")); } +TEST_F(EntryPoints, LinkedVariables) { + const std::string body1 = R"( + OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpMemoryModel Physical64 OpenCL +OpDecorate %7 LinkageAttributes "foo" Export +%1 = OpTypeInt 32 0 +%2 = OpTypeVector %1 3 +%3 = OpTypePointer Input %2 +%4 = OpVariable %3 Input +%5 = OpTypeVoid +%6 = OpTypeFunction %5 +%7 = OpFunction %5 None %6 +%8 = OpLabel +%9 = OpLoad %2 %4 Aligned 32 +OpReturn +OpFunctionEnd +)"; + const std::string body2 = R"( +OpCapability Linkage +OpCapability Kernel +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %4 "bar" +OpDecorate %3 LinkageAttributes "foo" Import +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +OpFunctionEnd +%4 = OpFunction %1 None %2 +%5 = OpLabel +%6 = OpFunctionCall %1 %3 +OpReturn +OpFunctionEnd +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)); + EXPECT_THAT(GetErrorMessage(), std::string()); + EXPECT_TRUE(Validate(linked_binary)); + EXPECT_THAT(GetErrorMessage(), std::string()); +} + } // namespace } // namespace spvtools diff --git a/test/link/global_values_amount_test.cpp b/test/link/global_values_amount_test.cpp index 2c4ee1f0..3158b7ec 100644 --- a/test/link/global_values_amount_test.cpp +++ b/test/link/global_values_amount_test.cpp @@ -22,85 +22,56 @@ namespace { using ::testing::HasSubstr; +const uint32_t binary_count = 2u; + class EntryPointsAmountTest : public spvtest::LinkerTest { public: - EntryPointsAmountTest() { binaries.reserve(0xFFFF); } + EntryPointsAmountTest() { binaries.reserve(binary_count + 1u); } void SetUp() override { - binaries.push_back({SpvMagicNumber, - SpvVersion, - SPV_GENERATOR_CODEPLAY, - 10u, // NOTE: Bound - 0u, // NOTE: Schema; reserved + const uint32_t global_variable_count_per_binary = + (SPV_LIMIT_GLOBAL_VARIABLES_MAX - 1u) / binary_count; - 3u << SpvWordCountShift | SpvOpTypeFloat, - 1u, // NOTE: Result ID - 32u, // NOTE: Width + spvtest::Binary common_binary = { + // clang-format off + SpvMagicNumber, + SpvVersion, + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0), + 3u + global_variable_count_per_binary, // NOTE: Bound + 0u, // NOTE: Schema; reserved - 4u << SpvWordCountShift | SpvOpTypePointer, - 2u, // NOTE: Result ID - SpvStorageClassInput, - 1u, // NOTE: Type ID + SpvOpCapability | 2u << SpvWordCountShift, + SpvCapabilityShader, - 2u << SpvWordCountShift | SpvOpTypeVoid, - 3u, // NOTE: Result ID + SpvOpMemoryModel | 3u << SpvWordCountShift, + SpvAddressingModelLogical, + SpvMemoryModelSimple, - 3u << SpvWordCountShift | SpvOpTypeFunction, - 4u, // NOTE: Result ID - 3u, // NOTE: Return type + SpvOpTypeFloat | 3u << SpvWordCountShift, + 1u, // NOTE: Result ID + 32u, // NOTE: Width - 5u << SpvWordCountShift | SpvOpFunction, - 3u, // NOTE: Result type - 5u, // NOTE: Result ID - SpvFunctionControlMaskNone, - 4u, // NOTE: Function type + SpvOpTypePointer | 4u << SpvWordCountShift, + 2u, // NOTE: Result ID + SpvStorageClassInput, + 1u // NOTE: Type ID + // clang-format on + }; - 2u << SpvWordCountShift | SpvOpLabel, - 6u, // NOTE: Result ID + binaries.push_back({}); + spvtest::Binary& binary = binaries.back(); + binary.reserve(common_binary.size() + global_variable_count_per_binary * 4); + binary.insert(binary.end(), common_binary.cbegin(), common_binary.cend()); - 4u << SpvWordCountShift | SpvOpVariable, - 2u, // NOTE: Type ID - 7u, // NOTE: Result ID - SpvStorageClassFunction, + for (uint32_t i = 0u; i < global_variable_count_per_binary; ++i) { + binary.push_back(SpvOpVariable | 4u << SpvWordCountShift); + binary.push_back(2u); // NOTE: Type ID + binary.push_back(3u + i); // NOTE: Result ID + binary.push_back(SpvStorageClassInput); + } - 4u << SpvWordCountShift | SpvOpVariable, - 2u, // NOTE: Type ID - 8u, // NOTE: Result ID - SpvStorageClassFunction, - - 4u << SpvWordCountShift | SpvOpVariable, - 2u, // NOTE: Type ID - 9u, // NOTE: Result ID - SpvStorageClassFunction, - - 1u << SpvWordCountShift | SpvOpReturn, - - 1u << SpvWordCountShift | SpvOpFunctionEnd}); - for (size_t i = 0u; i < 2u; ++i) { - spvtest::Binary binary = { - SpvMagicNumber, - SpvVersion, - SPV_GENERATOR_CODEPLAY, - 103u, // NOTE: Bound - 0u, // NOTE: Schema; reserved - - 3u << SpvWordCountShift | SpvOpTypeFloat, - 1u, // NOTE: Result ID - 32u, // NOTE: Width - - 4u << SpvWordCountShift | SpvOpTypePointer, - 2u, // NOTE: Result ID - SpvStorageClassInput, - 1u // NOTE: Type ID - }; - - for (uint32_t j = 0u; j < 0xFFFFu / 2u; ++j) { - binary.push_back(4u << SpvWordCountShift | SpvOpVariable); - binary.push_back(2u); // NOTE: Type ID - binary.push_back(j + 3u); // NOTE: Result ID - binary.push_back(SpvStorageClassInput); - } - binaries.push_back(binary); + for (uint32_t i = 0u; i < binary_count - 1u; ++i) { + binaries.push_back(binaries.back()); } } void TearDown() override { binaries.clear(); } @@ -111,42 +82,53 @@ class EntryPointsAmountTest : public spvtest::LinkerTest { TEST_F(EntryPointsAmountTest, UnderLimit) { spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)); + ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)) << GetErrorMessage(); EXPECT_THAT(GetErrorMessage(), std::string()); } TEST_F(EntryPointsAmountTest, OverLimit) { - binaries.push_back({SpvMagicNumber, - SpvVersion, - SPV_GENERATOR_CODEPLAY, - 5u, // NOTE: Bound - 0u, // NOTE: Schema; reserved + binaries.push_back({ + // clang-format off + SpvMagicNumber, + SpvVersion, + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0), + 5u, // NOTE: Bound + 0u, // NOTE: Schema; reserved - 3u << SpvWordCountShift | SpvOpTypeFloat, - 1u, // NOTE: Result ID - 32u, // NOTE: Width + SpvOpCapability | 2u << SpvWordCountShift, + SpvCapabilityShader, - 4u << SpvWordCountShift | SpvOpTypePointer, - 2u, // NOTE: Result ID - SpvStorageClassInput, - 1u, // NOTE: Type ID + SpvOpMemoryModel | 3u << SpvWordCountShift, + SpvAddressingModelLogical, + SpvMemoryModelSimple, - 4u << SpvWordCountShift | SpvOpVariable, - 2u, // NOTE: Type ID - 3u, // NOTE: Result ID - SpvStorageClassInput, + SpvOpTypeFloat | 3u << SpvWordCountShift, + 1u, // NOTE: Result ID + 32u, // NOTE: Width - 4u << SpvWordCountShift | SpvOpVariable, - 2u, // NOTE: Type ID - 4u, // NOTE: Result ID - SpvStorageClassInput}); + SpvOpTypePointer | 4u << SpvWordCountShift, + 2u, // NOTE: Result ID + SpvStorageClassInput, + 1u, // NOTE: Type ID + + SpvOpVariable | 4u << SpvWordCountShift, + 2u, // NOTE: Type ID + 3u, // NOTE: Result ID + SpvStorageClassInput, + + SpvOpVariable | 4u << SpvWordCountShift, + 2u, // NOTE: Type ID + 4u, // NOTE: Result ID + SpvStorageClassInput + // clang-format on + }); spvtest::Binary linked_binary; - - EXPECT_EQ(SPV_ERROR_INTERNAL, Link(binaries, &linked_binary)); - EXPECT_THAT(GetErrorMessage(), - HasSubstr("The limit of global values, 65535, was exceeded; " - "65536 global values were found.")); + ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)) << GetErrorMessage(); + EXPECT_THAT( + GetErrorMessage(), + HasSubstr("The minimum limit of global values, 65535, was exceeded; " + "65536 global values were found.")); } } // namespace diff --git a/test/link/ids_limit_test.cpp b/test/link/ids_limit_test.cpp index 6d7815a2..846fbef8 100644 --- a/test/link/ids_limit_test.cpp +++ b/test/link/ids_limit_test.cpp @@ -21,51 +21,114 @@ namespace spvtools { namespace { using ::testing::HasSubstr; -using IdsLimit = spvtest::LinkerTest; -TEST_F(IdsLimit, UnderLimit) { - spvtest::Binaries binaries = { - { - SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY, - 0x2FFFFFu, // NOTE: Bound - 0u, // NOTE: Schema; reserved - }, - { - SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY, - 0x100000u, // NOTE: Bound - 0u, // NOTE: Schema; reserved - }}; - spvtest::Binary linked_binary; +class IdsLimit : public spvtest::LinkerTest { + public: + IdsLimit() { binaries.reserve(2u); } - ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)); - EXPECT_THAT(GetErrorMessage(), std::string()); - EXPECT_EQ(0x3FFFFEu, linked_binary[3]); + void SetUp() override { + const uint32_t id_bound = SPV_LIMIT_RESULT_ID_BOUND - 1u; + const uint32_t constant_count = + id_bound - + 2u; // One ID is used for TypeBool, and (constant_count + 1) < id_bound + + // This is needed, as otherwise the ID bound will get reset to 1 while + // running the RemoveDuplicates pass. + spvtest::Binary common_binary = { + // clang-format off + SpvMagicNumber, + SpvVersion, + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0), + id_bound, // NOTE: Bound + 0u, // NOTE: Schema; reserved + + SpvOpCapability | 2u << SpvWordCountShift, + SpvCapabilityShader, + + SpvOpMemoryModel | 3u << SpvWordCountShift, + SpvAddressingModelLogical, + SpvMemoryModelSimple, + + SpvOpTypeBool | 2u << SpvWordCountShift, + 1u // NOTE: Result ID + // clang-format on + }; + + binaries.push_back({}); + spvtest::Binary& binary = binaries.back(); + binary.reserve(common_binary.size() + constant_count * 3u); + binary.insert(binary.end(), common_binary.cbegin(), common_binary.cend()); + + for (uint32_t i = 0u; i < constant_count; ++i) { + binary.push_back(SpvOpConstantTrue | 3u << SpvWordCountShift); + binary.push_back(1u); // NOTE: Type ID + binary.push_back(2u + i); // NOTE: Result ID + } + } + void TearDown() override { binaries.clear(); } + + spvtest::Binaries binaries; +}; + +spvtest::Binary CreateBinary(uint32_t id_bound) { + return { + // clang-format off + // Header + SpvMagicNumber, + SpvVersion, + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0), + id_bound, // NOTE: Bound + 0u, // NOTE: Schema; reserved + + // OpCapability Shader + SpvOpCapability | 2u << SpvWordCountShift, + SpvCapabilityShader, + + // OpMemoryModel Logical Simple + SpvOpMemoryModel | 3u << SpvWordCountShift, + SpvAddressingModelLogical, + SpvMemoryModelSimple + // clang-format on + }; } -TEST_F(IdsLimit, OverLimit) { - spvtest::Binaries binaries = { - { - SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY, - 0x2FFFFFu, // NOTE: Bound - 0u, // NOTE: Schema; reserved - }, - { - SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY, - 0x100000u, // NOTE: Bound - 0u, // NOTE: Schema; reserved - }, - { - SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY, - 3u, // NOTE: Bound - 0u, // NOTE: Schema; reserved - }}; +TEST_F(IdsLimit, DISABLED_UnderLimit) { + spvtest::Binary linked_binary; + ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)) << GetErrorMessage(); + EXPECT_THAT(GetErrorMessage(), std::string()); + EXPECT_EQ(0x3FFFFFu, linked_binary[3]); +} + +TEST_F(IdsLimit, DISABLED_OverLimit) { + spvtest::Binary& binary = binaries.back(); + + const uint32_t id_bound = binary[3]; + binary[3] = id_bound + 1u; + + binary.push_back(SpvOpConstantFalse | 3u << SpvWordCountShift); + binary.push_back(1u); // NOTE: Type ID + binary.push_back(id_bound); // NOTE: Result ID + + spvtest::Binary linked_binary; + ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)) << GetErrorMessage(); + EXPECT_THAT( + GetErrorMessage(), + HasSubstr("The minimum limit of IDs, 4194303, was exceeded: 4194304 is " + "the current ID bound.")); + EXPECT_EQ(0x400000u, linked_binary[3]); +} + +TEST_F(IdsLimit, DISABLED_Overflow) { + spvtest::Binaries binaries = {CreateBinary(0xFFFFFFFFu), + CreateBinary(0x00000002u)}; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_ERROR_INVALID_ID, Link(binaries, &linked_binary)); - EXPECT_THAT(GetErrorMessage(), - HasSubstr("The limit of IDs, 4194303, was exceeded: 4194304 is " - "the current ID bound.")); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, Link(binaries, &linked_binary)); + EXPECT_THAT( + GetErrorMessage(), + HasSubstr("Too many IDs (4294967296): combining all modules would " + "overflow the 32-bit word of the SPIR-V header.")); } } // namespace diff --git a/test/link/linker_fixture.h b/test/link/linker_fixture.h index 7bb12235..d005288c 100644 --- a/test/link/linker_fixture.h +++ b/test/link/linker_fixture.h @@ -208,6 +208,10 @@ class LinkerTest : public ::testing::Test { // Returns the accumulated error messages for the test. std::string GetErrorMessage() const { return error_message_; } + bool Validate(const spvtest::Binary& binary) { + return tools_.Validate(binary); + } + private: spvtools::Context context_; spvtools::SpirvTools diff --git a/test/link/matching_imports_to_exports_test.cpp b/test/link/matching_imports_to_exports_test.cpp index e76c69fb..6b02fc46 100644 --- a/test/link/matching_imports_to_exports_test.cpp +++ b/test/link/matching_imports_to_exports_test.cpp @@ -26,6 +26,9 @@ using MatchingImportsToExports = spvtest::LinkerTest; TEST_F(MatchingImportsToExports, Default) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import %2 = OpTypeFloat 32 %1 = OpVariable %2 Uniform @@ -33,6 +36,9 @@ OpDecorate %1 LinkageAttributes "foo" Import )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 %3 = OpConstant %2 42 @@ -40,11 +46,14 @@ OpDecorate %1 LinkageAttributes "foo" Export )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) << GetErrorMessage(); const std::string expected_res = - R"(OpModuleProcessed "Linked by SPIR-V Tools Linker" + R"(OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL +OpModuleProcessed "Linked by SPIR-V Tools Linker" %1 = OpTypeFloat 32 %2 = OpVariable %1 Input %3 = OpConstant %1 42 @@ -52,7 +61,7 @@ OpDecorate %1 LinkageAttributes "foo" Export )"; std::string res_body; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); - EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) << GetErrorMessage(); EXPECT_EQ(expected_res, res_body); } @@ -60,23 +69,29 @@ OpDecorate %1 LinkageAttributes "foo" Export TEST_F(MatchingImportsToExports, NotALibraryExtraExports) { const std::string body = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 %1 = OpVariable %2 Uniform )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body}, &linked_binary)) + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body}, &linked_binary)) << GetErrorMessage(); const std::string expected_res = - R"(OpModuleProcessed "Linked by SPIR-V Tools Linker" + R"(OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL +OpModuleProcessed "Linked by SPIR-V Tools Linker" %1 = OpTypeFloat 32 %2 = OpVariable %1 Uniform )"; std::string res_body; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); - EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) << GetErrorMessage(); EXPECT_EQ(expected_res, res_body); } @@ -84,6 +99,9 @@ OpDecorate %1 LinkageAttributes "foo" Export TEST_F(MatchingImportsToExports, LibraryExtraExports) { const std::string body = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 %1 = OpVariable %2 Uniform @@ -92,10 +110,13 @@ OpDecorate %1 LinkageAttributes "foo" Export spvtest::Binary linked_binary; LinkerOptions options; options.SetCreateLibrary(true); - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body}, &linked_binary, options)) + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body}, &linked_binary, options)) << GetErrorMessage(); const std::string expected_res = R"(OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpModuleProcessed "Linked by SPIR-V Tools Linker" OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 @@ -103,7 +124,7 @@ OpDecorate %1 LinkageAttributes "foo" Export )"; std::string res_body; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); - EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) << GetErrorMessage(); EXPECT_EQ(expected_res, res_body); } @@ -111,11 +132,18 @@ OpDecorate %1 LinkageAttributes "foo" Export TEST_F(MatchingImportsToExports, UnresolvedImports) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import %2 = OpTypeFloat 32 %1 = OpVariable %2 Uniform )"; - const std::string body2 = R"()"; + const std::string body2 = R"( +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL +)"; spvtest::Binary linked_binary; EXPECT_EQ(SPV_ERROR_INVALID_BINARY, @@ -127,6 +155,9 @@ OpDecorate %1 LinkageAttributes "foo" Import TEST_F(MatchingImportsToExports, TypeMismatch) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import %2 = OpTypeFloat 32 %1 = OpVariable %2 Uniform @@ -134,6 +165,9 @@ OpDecorate %1 LinkageAttributes "foo" Import )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeInt 32 0 %3 = OpConstant %2 42 @@ -153,6 +187,9 @@ OpDecorate %1 LinkageAttributes "foo" Export TEST_F(MatchingImportsToExports, MultipleDefinitions) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import %2 = OpTypeFloat 32 %1 = OpVariable %2 Uniform @@ -160,6 +197,9 @@ OpDecorate %1 LinkageAttributes "foo" Import )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 %3 = OpConstant %2 42 @@ -167,6 +207,9 @@ OpDecorate %1 LinkageAttributes "foo" Export )"; const std::string body3 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 %3 = OpConstant %2 -1 @@ -185,6 +228,9 @@ OpDecorate %1 LinkageAttributes "foo" Export TEST_F(MatchingImportsToExports, SameNameDifferentTypes) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import %2 = OpTypeFloat 32 %1 = OpVariable %2 Uniform @@ -192,6 +238,9 @@ OpDecorate %1 LinkageAttributes "foo" Import )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeInt 32 0 %3 = OpConstant %2 42 @@ -199,6 +248,9 @@ OpDecorate %1 LinkageAttributes "foo" Export )"; const std::string body3 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 %3 = OpConstant %2 12 @@ -217,6 +269,9 @@ OpDecorate %1 LinkageAttributes "foo" Export TEST_F(MatchingImportsToExports, DecorationMismatch) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import OpDecorate %2 Constant %2 = OpTypeFloat 32 @@ -225,6 +280,9 @@ OpDecorate %2 Constant )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 %3 = OpConstant %2 42 @@ -244,8 +302,10 @@ OpDecorate %1 LinkageAttributes "foo" Export TEST_F(MatchingImportsToExports, FuncParamAttrDifferButStillMatchExportToImport) { const std::string body1 = R"( -OpCapability Kernel OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import OpDecorate %2 FuncParamAttr Zext %3 = OpTypeVoid @@ -256,8 +316,10 @@ OpDecorate %2 FuncParamAttr Zext OpFunctionEnd )"; const std::string body2 = R"( -OpCapability Kernel OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export OpDecorate %2 FuncParamAttr Sext %3 = OpTypeVoid @@ -271,10 +333,12 @@ OpFunctionEnd )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) << GetErrorMessage(); - const std::string expected_res = R"(OpCapability Kernel + const std::string expected_res = R"(OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpModuleProcessed "Linked by SPIR-V Tools Linker" OpDecorate %1 FuncParamAttr Sext %2 = OpTypeVoid @@ -288,7 +352,7 @@ OpFunctionEnd )"; std::string res_body; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); - EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) << GetErrorMessage(); EXPECT_EQ(expected_res, res_body); } @@ -296,6 +360,9 @@ OpFunctionEnd TEST_F(MatchingImportsToExports, FunctionCtrl) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import %2 = OpTypeVoid %3 = OpTypeFunction %2 @@ -306,6 +373,9 @@ OpFunctionEnd )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeVoid %3 = OpTypeFunction %2 @@ -316,11 +386,14 @@ OpFunctionEnd )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) << GetErrorMessage(); const std::string expected_res = - R"(OpModuleProcessed "Linked by SPIR-V Tools Linker" + R"(OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL +OpModuleProcessed "Linked by SPIR-V Tools Linker" %1 = OpTypeVoid %2 = OpTypeFunction %1 %3 = OpTypeFloat 32 @@ -332,15 +405,17 @@ OpFunctionEnd )"; std::string res_body; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); - EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) << GetErrorMessage(); EXPECT_EQ(expected_res, res_body); } TEST_F(MatchingImportsToExports, UseExportedFuncParamAttr) { const std::string body1 = R"( -OpCapability Kernel OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import OpDecorate %2 FuncParamAttr Zext %2 = OpDecorationGroup @@ -356,8 +431,10 @@ OpFunctionEnd OpFunctionEnd )"; const std::string body2 = R"( -OpCapability Kernel OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export OpDecorate %2 FuncParamAttr Sext %3 = OpTypeVoid @@ -371,10 +448,12 @@ OpFunctionEnd )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) << GetErrorMessage(); - const std::string expected_res = R"(OpCapability Kernel + const std::string expected_res = R"(OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpModuleProcessed "Linked by SPIR-V Tools Linker" OpDecorate %1 FuncParamAttr Zext %1 = OpDecorationGroup @@ -394,15 +473,17 @@ OpFunctionEnd )"; std::string res_body; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); - EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) << GetErrorMessage(); EXPECT_EQ(expected_res, res_body); } TEST_F(MatchingImportsToExports, NamesAndDecorations) { const std::string body1 = R"( -OpCapability Kernel OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpName %1 "foo" OpName %3 "param" OpDecorate %1 LinkageAttributes "foo" Import @@ -422,8 +503,10 @@ OpFunctionEnd OpFunctionEnd )"; const std::string body2 = R"( -OpCapability Kernel OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpName %1 "foo" OpName %2 "param" OpDecorate %1 LinkageAttributes "foo" Export @@ -440,10 +523,12 @@ OpFunctionEnd )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) << GetErrorMessage(); - const std::string expected_res = R"(OpCapability Kernel + const std::string expected_res = R"(OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpName %1 "foo" OpName %2 "param" OpModuleProcessed "Linked by SPIR-V Tools Linker" @@ -467,7 +552,7 @@ OpFunctionEnd )"; std::string res_body; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); - EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) << GetErrorMessage(); EXPECT_EQ(expected_res, res_body); } diff --git a/test/link/memory_model_test.cpp b/test/link/memory_model_test.cpp index 2add5046..280a776a 100644 --- a/test/link/memory_model_test.cpp +++ b/test/link/memory_model_test.cpp @@ -50,9 +50,9 @@ OpMemoryModel Physical32 Simple spvtest::Binary linked_binary; EXPECT_EQ(SPV_ERROR_INTERNAL, AssembleAndLink({body1, body2}, &linked_binary)); - EXPECT_THAT( - GetErrorMessage(), - HasSubstr("Conflicting addressing models: Logical vs Physical32.")); + EXPECT_THAT(GetErrorMessage(), + HasSubstr("Conflicting addressing models: Logical (input modules " + "1 through 1) vs Physical32 (input module 2).")); } TEST_F(MemoryModel, MemoryMismatch) { @@ -67,7 +67,38 @@ OpMemoryModel Logical GLSL450 EXPECT_EQ(SPV_ERROR_INTERNAL, AssembleAndLink({body1, body2}, &linked_binary)); EXPECT_THAT(GetErrorMessage(), - HasSubstr("Conflicting memory models: Simple vs GLSL450.")); + HasSubstr("Conflicting memory models: Simple (input modules 1 " + "through 1) vs GLSL450 (input module 2).")); +} + +TEST_F(MemoryModel, FirstLackMemoryModel) { + const std::string body1 = R"( +)"; + const std::string body2 = R"( +OpMemoryModel Logical GLSL450 +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + AssembleAndLink({body1, body2}, &linked_binary)); + EXPECT_THAT( + GetErrorMessage(), + HasSubstr("Input module 1 is lacking an OpMemoryModel instruction.")); +} + +TEST_F(MemoryModel, SecondLackMemoryModel) { + const std::string body1 = R"( +OpMemoryModel Logical GLSL450 +)"; + const std::string body2 = R"( +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + AssembleAndLink({body1, body2}, &linked_binary)); + EXPECT_THAT( + GetErrorMessage(), + HasSubstr("Input module 2 is lacking an OpMemoryModel instruction.")); } } // namespace diff --git a/test/link/partial_linkage_test.cpp b/test/link/partial_linkage_test.cpp index c43b06e5..bf4b508b 100644 --- a/test/link/partial_linkage_test.cpp +++ b/test/link/partial_linkage_test.cpp @@ -26,6 +26,9 @@ using PartialLinkage = spvtest::LinkerTest; TEST_F(PartialLinkage, Allowed) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import OpDecorate %2 LinkageAttributes "bar" Import %3 = OpTypeFloat 32 @@ -34,6 +37,9 @@ OpDecorate %2 LinkageAttributes "bar" Import )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "bar" Export %2 = OpTypeFloat 32 %3 = OpConstant %2 3.1415 @@ -44,9 +50,13 @@ OpDecorate %1 LinkageAttributes "bar" Export LinkerOptions linker_options; linker_options.SetAllowPartialLinkage(true); ASSERT_EQ(SPV_SUCCESS, - AssembleAndLink({body1, body2}, &linked_binary, linker_options)); + AssembleAndLink({body1, body2}, &linked_binary, linker_options)) + << GetErrorMessage(); const std::string expected_res = R"(OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpModuleProcessed "Linked by SPIR-V Tools Linker" OpDecorate %1 LinkageAttributes "foo" Import %2 = OpTypeFloat 32 @@ -64,6 +74,9 @@ OpDecorate %1 LinkageAttributes "foo" Import TEST_F(PartialLinkage, Disallowed) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import OpDecorate %2 LinkageAttributes "bar" Import %3 = OpTypeFloat 32 @@ -72,6 +85,9 @@ OpDecorate %2 LinkageAttributes "bar" Import )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "bar" Export %2 = OpTypeFloat 32 %3 = OpConstant %2 3.1415 diff --git a/test/link/type_match_test.cpp b/test/link/type_match_test.cpp index dae70c16..efc5cf7e 100644 --- a/test/link/type_match_test.cpp +++ b/test/link/type_match_test.cpp @@ -66,6 +66,7 @@ using TypeMatch = spvtest::LinkerTest; "OpCapability Kernel\n" \ "OpCapability Shader\n" \ "OpCapability Addresses\n" \ + "OpMemoryModel Physical64 OpenCL\n" \ "OpDecorate %var LinkageAttributes \"foo\" " \ "{Import,Export}\n" \ "; CHECK: [[baseint:%\\w+]] = OpTypeInt 32 1\n" \ diff --git a/test/link/unique_ids_test.cpp b/test/link/unique_ids_test.cpp index 55c70ea6..16a9b52a 100644 --- a/test/link/unique_ids_test.cpp +++ b/test/link/unique_ids_test.cpp @@ -135,7 +135,8 @@ TEST_F(UniqueIds, UniquelyMerged) { LinkerOptions options; options.SetVerifyIds(true); spv_result_t res = AssembleAndLink(bodies, &linked_binary, options); - EXPECT_EQ(SPV_SUCCESS, res); + ASSERT_EQ(SPV_SUCCESS, res) << GetErrorMessage(); + EXPECT_THAT(GetErrorMessage(), std::string()); } } // namespace diff --git a/test/lint/CMakeLists.txt b/test/lint/CMakeLists.txt new file mode 100644 index 00000000..09bc6d39 --- /dev/null +++ b/test/lint/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright (c) 2021 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. + +add_spvtools_unittest(TARGET lint + SRCS divergence_analysis_test.cpp + LIBS SPIRV-Tools-lint SPIRV-Tools-opt +) diff --git a/test/lint/divergence_analysis_test.cpp b/test/lint/divergence_analysis_test.cpp new file mode 100644 index 00000000..36cd32dd --- /dev/null +++ b/test/lint/divergence_analysis_test.cpp @@ -0,0 +1,700 @@ +// Copyright (c) 2021 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/lint/divergence_analysis.h" + +#include + +#include "gtest/gtest.h" +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace lint { +namespace { + +void CLIMessageConsumer(spv_message_level_t level, const char*, + const spv_position_t& position, const char* message) { + switch (level) { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + std::cerr << "error: line " << position.index << ": " << message + << std::endl; + break; + case SPV_MSG_WARNING: + std::cout << "warning: line " << position.index << ": " << message + << std::endl; + break; + case SPV_MSG_INFO: + std::cout << "info: line " << position.index << ": " << message + << std::endl; + break; + default: + break; + } +} + +class DivergenceTest : public ::testing::Test { + protected: + std::unique_ptr context_; + std::unique_ptr divergence_; + + void Build(std::string text, uint32_t function_id = 1) { + context_ = BuildModule(SPV_ENV_UNIVERSAL_1_0, CLIMessageConsumer, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context_.get()); + opt::Module* module = context_->module(); + ASSERT_NE(nullptr, module); + // First function should have the given ID. + ASSERT_NE(module->begin(), module->end()); + opt::Function* function = &*module->begin(); + ASSERT_EQ(function->result_id(), function_id); + divergence_.reset(new DivergenceAnalysis(*context_)); + divergence_->Run(function); + } +}; + +// Makes assertions a bit shorter. +using Level = DivergenceAnalysis::DivergenceLevel; + +namespace { +std::string Preamble() { + return R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" %x %y + OpExecutionMode %1 OriginLowerLeft + OpDecorate %y Flat + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %bool = OpTypeBool + %float = OpTypeFloat 32 + %false = OpConstantFalse %bool + %true = OpConstantTrue %bool + %zero = OpConstant %float 0 + %one = OpConstant %float 1 + %x_t = OpTypePointer Input %float + %x = OpVariable %x_t Input + %y = OpVariable %x_t Input + %1 = OpFunction %void None %void_f + )"; +} +} // namespace + +TEST_F(DivergenceTest, SimpleTest) { + // pseudocode: + // %10: + // %11 = load x + // if (%12 = (%11 < 0)) { + // %13: + // // do nothing + // } + // %14: + // return + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %11 = OpLoad %float %x + %12 = OpFOrdLessThan %bool %11 %zero + OpSelectionMerge %14 None + OpBranchConditional %12 %13 %14 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + OpReturn + OpFunctionEnd + )")); + // Control flow divergence. + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13)); + EXPECT_EQ(12, divergence_->GetDivergenceSource(13)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(14)); + // Value divergence. + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(0, divergence_->GetDivergenceSource(11)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(11, divergence_->GetDivergenceSource(12)); +} + +TEST_F(DivergenceTest, FlowTypesTest) { + // pseudocode: + // %10: + // %11 = load x + // %12 = x < 0 // data -> data + // if (%12) { + // %13: // data -> control + // if (true) { + // %14: // control -> control + // } + // %15: + // %16 = 1 + // } else { + // %17: + // %18 = 2 + // } + // %19: + // %19 = phi(%16 from %15, %18 from %17) // control -> data + // return + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %11 = OpLoad %float %x + %12 = OpFOrdLessThan %bool %11 %zero + OpSelectionMerge %19 None + OpBranchConditional %12 %13 %17 + %13 = OpLabel + OpSelectionMerge %15 None + OpBranchConditional %true %14 %15 + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + %16 = OpFAdd %float %zero %zero + OpBranch %19 + %17 = OpLabel + %18 = OpFAdd %float %zero %one + OpBranch %19 + %19 = OpLabel + %20 = OpPhi %float %16 %15 %18 %17 + OpReturn + OpFunctionEnd + )")); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(0, divergence_->GetDivergenceSource(11)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(11, divergence_->GetDivergenceSource(12)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13)); + EXPECT_EQ(12, divergence_->GetDivergenceSource(13)); + EXPECT_EQ(10, divergence_->GetDivergenceDependenceSource(13)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14)); + EXPECT_EQ(13, divergence_->GetDivergenceSource(14)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15)); + EXPECT_EQ(12, divergence_->GetDivergenceSource(15)); + EXPECT_EQ(10, divergence_->GetDivergenceDependenceSource(15)); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(16)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(17)); + EXPECT_EQ(12, divergence_->GetDivergenceSource(17)); + EXPECT_EQ(10, divergence_->GetDivergenceDependenceSource(17)); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(18)); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(19)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(20)); + EXPECT_TRUE(divergence_->GetDivergenceSource(20) == 15 || + divergence_->GetDivergenceDependenceSource(20) == 17) + << "Got: " << divergence_->GetDivergenceDependenceSource(20); +} + +TEST_F(DivergenceTest, ExitDependenceTest) { + // pseudocode: + // %10: + // %11 = load x + // %12 = %11 < 0 + // %13: + // do { + // %14: + // if (%12) { + // %15: + // continue; + // } + // %16: + // %17: + // continue; + // } %18: while(false); + // %19: + // return + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %11 = OpLoad %float %x + %12 = OpFOrdLessThan %bool %11 %zero ; data -> data + OpBranch %13 + %13 = OpLabel + OpLoopMerge %19 %18 None + OpBranch %14 + %14 = OpLabel + OpSelectionMerge %16 None + OpBranchConditional %12 %15 %16 + %15 = OpLabel + OpBranch %18 ; continue + %16 = OpLabel + OpBranch %17 + %17 = OpLabel + OpBranch %18 ; continue + %18 = OpLabel + OpBranchConditional %false %13 %19 + %19 = OpLabel + OpReturn + OpFunctionEnd + )")); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(0, divergence_->GetDivergenceSource(11)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(11, divergence_->GetDivergenceSource(12)); + + // Since both branches continue, there's no divergent control dependence + // to 13. + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(13)); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(14)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15)); + EXPECT_EQ(12, divergence_->GetDivergenceSource(15)); + EXPECT_EQ(14, divergence_->GetDivergenceDependenceSource(15)); + + // These two blocks are outside the if but are still control dependent. + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(16)); + EXPECT_EQ(12, divergence_->GetDivergenceSource(16)); + EXPECT_EQ(14, divergence_->GetDivergenceDependenceSource(16)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(17)); + EXPECT_EQ(12, divergence_->GetDivergenceSource(17)); + EXPECT_EQ(14, divergence_->GetDivergenceDependenceSource(17)); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(18)); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(19)); +} + +TEST_F(DivergenceTest, ReconvergencePromotionTest) { + // pseudocode: + // %10: + // %11 = load y + // %12 = %11 < 0 + // if (%12) { + // %13: + // %14: + // %15: + // if (true) { + // %16: + // } + // // Reconvergence *not* guaranteed as + // // control is not uniform on the IG level + // // at %15. + // %17: + // %18: + // %19: + // %20 = load x + // } + // %21: + // %22 = phi(%11, %20) + // return + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %11 = OpLoad %float %y + %12 = OpFOrdLessThan %bool %11 %zero + OpSelectionMerge %21 None + OpBranchConditional %12 %13 %21 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + OpSelectionMerge %17 None + OpBranchConditional %true %16 %17 + %16 = OpLabel + OpBranch %17 + %17 = OpLabel + OpBranch %18 + %18 = OpLabel + OpBranch %19 + %19 = OpLabel + %20 = OpLoad %float %y + OpBranch %21 + %21 = OpLabel + %22 = OpPhi %float %11 %10 %20 %19 + OpReturn + OpFunctionEnd + )")); + ASSERT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + ASSERT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(21)); + + ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(11)); + ASSERT_EQ(0, divergence_->GetDivergenceSource(11)); + ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(12)); + ASSERT_EQ(11, divergence_->GetDivergenceSource(12)); + ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(13)); + ASSERT_EQ(12, divergence_->GetDivergenceSource(13)); + ASSERT_EQ(10, divergence_->GetDivergenceDependenceSource(13)); + ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(14)); + ASSERT_EQ(12, divergence_->GetDivergenceSource(14)); + ASSERT_EQ(10, divergence_->GetDivergenceDependenceSource(14)); + ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(15)); + ASSERT_EQ(12, divergence_->GetDivergenceSource(15)); + ASSERT_EQ(10, divergence_->GetDivergenceDependenceSource(15)); + ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(16)); + ASSERT_EQ(15, divergence_->GetDivergenceSource(16)); + + ASSERT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(17)); + ASSERT_EQ(12, divergence_->GetDivergenceSource(17)); + ASSERT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(18)); + ASSERT_EQ(12, divergence_->GetDivergenceSource(18)); + ASSERT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(19)); + ASSERT_EQ(12, divergence_->GetDivergenceSource(19)); + + ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(20)); + ASSERT_EQ(0, divergence_->GetDivergenceSource(20)); + ASSERT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(22)); + ASSERT_EQ(19, divergence_->GetDivergenceSource(22)); + ASSERT_EQ(10, divergence_->GetDivergenceDependenceSource(15)); +} + +TEST_F(DivergenceTest, FunctionCallTest) { + // pseudocode: + // %2() { + // %20: + // %21 = load x + // %22 = %21 < 0 + // if (%22) { + // %23: + // return + // } + // %24: + // return + // } + // + // main() { + // %10: + // %11 = %2(); + // // Reconvergence *not* guaranteed. + // %12: + // return + // } + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %11 = OpFunctionCall %void %2 + OpBranch %12 + %12 = OpLabel + OpReturn + OpFunctionEnd + + %2 = OpFunction %void None %void_f + %20 = OpLabel + %21 = OpLoad %float %x + %22 = OpFOrdLessThan %bool %21 %zero + OpSelectionMerge %24 None + OpBranchConditional %22 %23 %24 + %23 = OpLabel + OpReturn + %24 = OpLabel + OpReturn + OpFunctionEnd + )")); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + // Conservatively assume function return value is uniform. + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(11)); + // TODO(dongja): blocks reachable from diverging function calls should be + // divergent. + // EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(12)); // Wrong! +} + +TEST_F(DivergenceTest, LateMergeTest) { + // pseudocode: + // %10: + // %11 = load y + // %12 = %11 < 0 + // [merge: %15] + // if (%12) { + // %13: + // } + // %14: // Reconvergence hasn't happened by here. + // %15: + // return + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %11 = OpLoad %float %x + %12 = OpFOrdLessThan %bool %11 %zero + OpSelectionMerge %15 None + OpBranchConditional %12 %13 %14 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + OpReturn + OpFunctionEnd + )")); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13)); + // TODO(dongja): + // EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(14)); // Wrong! + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(15)); +} + +// The following series of tests makes sure that we find the least fixpoint. +TEST_F(DivergenceTest, UniformFixpointTest) { + // pseudocode: + // %10: + // %20 = load x + // %21 = load y + // do { + // %11: + // %12: + // %13 = phi(%zero from %11, %14 from %16) + // %14 = %13 + 1 + // %15 = %13 < 1 + // } %16: while (%15) + // %17: + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %20 = OpLoad %float %x + %21 = OpLoad %float %y + OpBranch %11 + %11 = OpLabel + %13 = OpPhi %float %zero %10 %14 %16 + OpLoopMerge %17 %16 None + OpBranch %12 + %12 = OpLabel + %14 = OpFAdd %float %13 %one + %15 = OpFOrdLessThan %bool %13 %one + OpBranch %16 + %16 = OpLabel + OpBranchConditional %15 %11 %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )")); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(13)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(14)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(15)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(16)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17)); +} + +TEST_F(DivergenceTest, PartiallyUniformFixpointTest) { + // pseudocode: + // %10: + // %20 = load x + // %21 = load y + // do { + // %11: + // %12: + // %13 = phi(%zero from %11, %14 from %16) + // %14 = %13 + 1 + // %15 = %13 < %21 + // } %16: while (%15) + // %17: + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %20 = OpLoad %float %x + %21 = OpLoad %float %y + OpBranch %11 + %11 = OpLabel + %13 = OpPhi %float %zero %10 %14 %16 + OpLoopMerge %17 %16 None + OpBranch %12 + %12 = OpLabel + %14 = OpFAdd %float %13 %one + %15 = OpFOrdLessThan %bool %13 %21 + OpBranch %16 + %16 = OpLabel + OpBranchConditional %15 %11 %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )")); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(13)); + EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(14)); + EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(15)); + EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(16)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17)); +} + +TEST_F(DivergenceTest, DivergentFixpointTest) { + // pseudocode: + // %10: + // %20 = load x + // %21 = load y + // do { + // %11: + // %12: + // %13 = phi(%zero from %11, %14 from %16) + // %14 = %13 + 1 + // %15 = %13 < %20 + // } %16: while (%15) + // %17: + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %20 = OpLoad %float %x + %21 = OpLoad %float %y + OpBranch %11 + %11 = OpLabel + %13 = OpPhi %float %zero %10 %14 %16 + OpLoopMerge %17 %16 None + OpBranch %12 + %12 = OpLabel + %14 = OpFAdd %float %13 %one + %15 = OpFOrdLessThan %bool %13 %20 + OpBranch %16 + %16 = OpLabel + OpBranchConditional %15 %11 %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )")); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(16)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17)); +} + +TEST_F(DivergenceTest, DivergentOverridesPartiallyUniformTest) { + // pseudocode: + // %10: + // %20 = load x + // %21 = load y + // %11: + // do { + // %12: + // %13 = phi(%21 from %11, %14 from %16) + // %14 = %13 + 1 + // %15 = %13 < %20 + // } %16: while (%15) + // %17: + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %20 = OpLoad %float %x + %21 = OpLoad %float %y + OpBranch %11 + %11 = OpLabel + %13 = OpPhi %float %zero %10 %14 %16 + OpLoopMerge %17 %16 None + OpBranch %12 + %12 = OpLabel + %14 = OpFAdd %float %13 %one + %15 = OpFOrdLessThan %bool %13 %20 + OpBranch %16 + %16 = OpLabel + OpBranchConditional %15 %11 %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )")); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(16)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17)); +} + +TEST_F(DivergenceTest, NestedFixpointTest) { + // pseudocode: + // %10: + // %20 = load x + // %21 = load y + // do { + // %22: + // %23: + // %24 = phi(%zero from %22, %25 from %26) + // %11: + // do { + // %12: + // %13 = phi(%zero from %11, %14 from %16) + // %14 = %13 + 1 + // %15 = %13 < %24 + // } %16: while (%15) + // %17: + // %25 = load x + // } %26: while (false) + // %27: + // return + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %20 = OpLoad %float %x + %21 = OpLoad %float %y + OpBranch %22 + %22 = OpLabel + %24 = OpPhi %float %zero %10 %25 %26 + OpLoopMerge %27 %26 None + OpBranch %23 + %23 = OpLabel + OpBranch %11 + %11 = OpLabel + %13 = OpPhi %float %zero %23 %14 %16 + OpLoopMerge %17 %16 None + OpBranch %12 + %12 = OpLabel + %14 = OpFAdd %float %13 %one + %15 = OpFOrdLessThan %bool %13 %24 + OpBranch %16 + %16 = OpLabel + OpBranchConditional %15 %11 %17 + %17 = OpLabel + %25 = OpLoad %float %x + OpBranch %26 + %26 = OpLabel + OpBranchConditional %false %22 %27 + %27 = OpLabel + OpReturn + OpFunctionEnd + )")); + // This test makes sure that divergent values flowing upward can influence the + // fixpoint of a loop. + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(16)); + // Control of the outer loop is still uniform. + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(22)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(23)); + // Seed divergent values. + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(24)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(25)); + // Outer loop control. + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(26)); + // Merged. + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(27)); +} + +} // namespace +} // namespace lint +} // namespace spvtools diff --git a/test/operand_capabilities_test.cpp b/test/operand_capabilities_test.cpp index 0aec7918..60503461 100644 --- a/test/operand_capabilities_test.cpp +++ b/test/operand_capabilities_test.cpp @@ -97,6 +97,14 @@ TEST_P(EnumCapabilityTest, Sample) { } \ } +#define CASE6(TYPE, VALUE, CAP1, CAP2, CAP3, CAP4, CAP5, CAP6) \ + { \ + SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet { \ + SpvCapability##CAP1, SpvCapability##CAP2, SpvCapability##CAP3, \ + SpvCapability##CAP4, SpvCapability##CAP5, SpvCapability##CAP6 \ + } \ + } + // See SPIR-V Section 3.3 Execution Model INSTANTIATE_TEST_SUITE_P( ExecutionModel, EnumCapabilityTest, @@ -168,10 +176,10 @@ INSTANTIATE_TEST_SUITE_P( Geometry), CASE1(EXECUTION_MODE, ExecutionModeQuads, Tessellation), CASE1(EXECUTION_MODE, ExecutionModeIsolines, Tessellation), - CASE3(EXECUTION_MODE, ExecutionModeOutputVertices, Geometry, - Tessellation, MeshShadingNV), - CASE2(EXECUTION_MODE, ExecutionModeOutputPoints, Geometry, - MeshShadingNV), + CASE4(EXECUTION_MODE, ExecutionModeOutputVertices, Geometry, + Tessellation, MeshShadingNV, MeshShadingEXT), + CASE3(EXECUTION_MODE, ExecutionModeOutputPoints, Geometry, + MeshShadingNV, MeshShadingEXT), CASE1(EXECUTION_MODE, ExecutionModeOutputLineStrip, Geometry), CASE1(EXECUTION_MODE, ExecutionModeOutputTriangleStrip, Geometry), CASE1(EXECUTION_MODE, ExecutionModeVecTypeHint, Kernel), @@ -199,7 +207,8 @@ INSTANTIATE_TEST_SUITE_P( CASE1(STORAGE_CLASS, StorageClassOutput, Shader), CASE0(STORAGE_CLASS, StorageClassWorkgroup), CASE0(STORAGE_CLASS, StorageClassCrossWorkgroup), - CASE1(STORAGE_CLASS, StorageClassPrivate, Shader), + CASE2(STORAGE_CLASS, StorageClassPrivate, Shader, + VectorComputeINTEL), CASE0(STORAGE_CLASS, StorageClassFunction), CASE1(STORAGE_CLASS, StorageClassGeneric, GenericPointer), // Bug 14287 @@ -367,19 +376,6 @@ INSTANTIATE_TEST_SUITE_P( // clang-format on }))); -// See SPIR-V Section 3.15 FP Fast Math Mode -INSTANTIATE_TEST_SUITE_P( - FPFastMathMode, EnumCapabilityTest, - Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), - ValuesIn(std::vector{ - CASE0(FP_FAST_MATH_MODE, FPFastMathModeMaskNone), - CASE1(FP_FAST_MATH_MODE, FPFastMathModeNotNaNMask, Kernel), - CASE1(FP_FAST_MATH_MODE, FPFastMathModeNotInfMask, Kernel), - CASE1(FP_FAST_MATH_MODE, FPFastMathModeNSZMask, Kernel), - CASE1(FP_FAST_MATH_MODE, FPFastMathModeAllowRecipMask, Kernel), - CASE1(FP_FAST_MATH_MODE, FPFastMathModeFastMask, Kernel), - }))); - // See SPIR-V Section 3.17 Linkage Type INSTANTIATE_TEST_SUITE_P( LinkageType, EnumCapabilityTest, @@ -498,11 +494,11 @@ INSTANTIATE_TEST_SUITE_P( CASE1(BUILT_IN, BuiltInCullDistance, CullDistance), // Bug 1407, 15234 CASE1(BUILT_IN, BuiltInVertexId, Shader), CASE1(BUILT_IN, BuiltInInstanceId, Shader), - CASE5(BUILT_IN, BuiltInPrimitiveId, Geometry, Tessellation, - RayTracingNV, RayTracingKHR, MeshShadingNV), + CASE6(BUILT_IN, BuiltInPrimitiveId, Geometry, Tessellation, + RayTracingNV, RayTracingKHR, MeshShadingNV, MeshShadingEXT), CASE2(BUILT_IN, BuiltInInvocationId, Geometry, Tessellation), - CASE3(BUILT_IN, BuiltInLayer, Geometry, ShaderViewportIndexLayerEXT, MeshShadingNV), - CASE3(BUILT_IN, BuiltInViewportIndex, MultiViewport, ShaderViewportIndexLayerEXT, MeshShadingNV), // Bug 15234 + CASE4(BUILT_IN, BuiltInLayer, Geometry, ShaderViewportIndexLayerEXT, MeshShadingNV, MeshShadingEXT), + CASE4(BUILT_IN, BuiltInViewportIndex, MultiViewport, ShaderViewportIndexLayerEXT, MeshShadingNV, MeshShadingEXT), // Bug 15234 CASE1(BUILT_IN, BuiltInTessLevelOuter, Tessellation), CASE1(BUILT_IN, BuiltInTessLevelInner, Tessellation), CASE1(BUILT_IN, BuiltInTessCoord, Tessellation), @@ -545,11 +541,11 @@ INSTANTIATE_TEST_SUITE_P( Values(SPV_ENV_UNIVERSAL_1_5), ValuesIn(std::vector{ // SPIR-V 1.5 adds new capabilities to enable these two builtins. - CASE4(BUILT_IN, BuiltInLayer, Geometry, ShaderLayer, - ShaderViewportIndexLayerEXT, MeshShadingNV), - CASE4(BUILT_IN, BuiltInViewportIndex, MultiViewport, + CASE5(BUILT_IN, BuiltInLayer, Geometry, ShaderLayer, + ShaderViewportIndexLayerEXT, MeshShadingNV, MeshShadingEXT), + CASE5(BUILT_IN, BuiltInViewportIndex, MultiViewport, ShaderViewportIndex, ShaderViewportIndexLayerEXT, - MeshShadingNV), + MeshShadingNV, MeshShadingEXT), }))); // See SPIR-V Section 3.22 Selection Control diff --git a/test/operand_pattern_test.cpp b/test/operand_pattern_test.cpp index 1caf008f..a98a9d76 100644 --- a/test/operand_pattern_test.cpp +++ b/test/operand_pattern_test.cpp @@ -91,9 +91,14 @@ INSTANTIATE_TEST_SUITE_P( {SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, 0, {PREFIX0}, {PREFIX0}}, // Unknown bits means no change. Use all bits that aren't in the // grammar. - // The last mask enum is 0x20 + // The used mask bits are: + // 1 through... + // 0x20 SpvMemoryAccessNonPrivatePointerMask + // also + // 0x10000 SpvMemoryAccessAliasScopeINTELMaskShift + // 0x20000 SpvMemoryAccessNoAliasINTELMaskMask {SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, - 0xffffffc0, + 0xffffffc0 ^ (0x10000) ^ (0x20000), {PREFIX1}, {PREFIX1}}, // Volatile has no operands. @@ -111,6 +116,7 @@ INSTANTIATE_TEST_SUITE_P( SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask, {PREFIX1}, {PREFIX1, SPV_OPERAND_TYPE_LITERAL_INTEGER}}, + // Newer masks are not tested })); #undef PREFIX0 #undef PREFIX1 diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt index f65d2ff3..15966c18 100644 --- a/test/opt/CMakeLists.txt +++ b/test/opt/CMakeLists.txt @@ -28,8 +28,11 @@ add_spvtools_unittest(TARGET opt compact_ids_test.cpp constants_test.cpp constant_manager_test.cpp + control_dependence.cpp convert_relaxed_to_half_test.cpp + convert_to_sampled_image_test.cpp copy_prop_array_test.cpp + dataflow.cpp dead_branch_elim_test.cpp dead_insert_elim_test.cpp dead_variable_elim_test.cpp @@ -39,8 +42,10 @@ add_spvtools_unittest(TARGET opt desc_sroa_test.cpp eliminate_dead_const_test.cpp eliminate_dead_functions_test.cpp + eliminate_dead_input_components_test.cpp eliminate_dead_member_test.cpp feature_manager_test.cpp + fix_func_call_arguments_test.cpp fix_storage_class_test.cpp flatten_decoration_test.cpp fold_spec_const_op_composite_test.cpp @@ -57,6 +62,7 @@ add_spvtools_unittest(TARGET opt inst_debug_printf_test.cpp instruction_list_test.cpp instruction_test.cpp + interface_var_sroa_test.cpp interp_fixup_test.cpp ir_builder.cpp ir_context_test.cpp @@ -79,16 +85,20 @@ add_spvtools_unittest(TARGET opt propagator_test.cpp reduce_load_size_test.cpp redundancy_elimination_test.cpp + remove_dontinline_test.cpp + remove_unused_interface_variables_test.cpp register_liveness.cpp relax_float_ops_test.cpp + replace_desc_array_access_using_var_index_test.cpp replace_invalid_opc_test.cpp scalar_analysis.cpp scalar_replacement_test.cpp set_spec_const_default_value_test.cpp simplification_test.cpp + spread_volatile_semantics_test.cpp strength_reduction_test.cpp strip_debug_info_test.cpp - strip_reflect_info_test.cpp + strip_nonsemantic_info_test.cpp struct_cfg_analysis_test.cpp type_manager_test.cpp types_test.cpp diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp index 5b4291da..e51098ed 100644 --- a/test/opt/aggressive_dead_code_elim_test.cpp +++ b/test/opt/aggressive_dead_code_elim_test.cpp @@ -38,55 +38,45 @@ TEST_F(AggressiveDCETest, EliminateExtendedInst) { // vec4 dv = sqrt(Dead); // gl_FragColor = v; // } - - const std::string predefs1 = - R"(OpCapability Shader + const std::string spirv = R"( +OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 +; CHECK: OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor OpEntryPoint Fragment %main "main" %BaseColor %Dead %gl_FragColor OpExecutionMode %main OriginUpperLeft OpSource GLSL 140 -)"; - - const std::string names_before = - R"(OpName %main "main" +OpName %main "main" OpName %v "v" OpName %BaseColor "BaseColor" +; CHECK-NOT: OpName %dv "dv" OpName %dv "dv" +; CHECK-NOT: OpName %Dead "Dead" OpName %Dead "Dead" OpName %gl_FragColor "gl_FragColor" -)"; - - const std::string names_after = - R"(OpName %main "main" -OpName %v "v" -OpName %BaseColor "BaseColor" -OpName %Dead "Dead" -OpName %gl_FragColor "gl_FragColor" -)"; - - const std::string predefs2 = - R"(%void = OpTypeVoid +%void = OpTypeVoid %9 = OpTypeFunction %void %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 %_ptr_Function_v4float = OpTypePointer Function %v4float %_ptr_Input_v4float = OpTypePointer Input %v4float %BaseColor = OpVariable %_ptr_Input_v4float Input +; CHECK-NOT: %Dead = OpVariable %Dead = OpVariable %_ptr_Input_v4float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %gl_FragColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %9 +%main = OpFunction %void None %9 %15 = OpLabel %v = OpVariable %_ptr_Function_v4float Function +; CHECK-NOT: %dv = OpVariable %dv = OpVariable %_ptr_Function_v4float Function %16 = OpLoad %v4float %BaseColor OpStore %v %16 +; CHECK-NOT: OpLoad %v4float %Dead %17 = OpLoad %v4float %Dead +; CHECK-NOT: OpExtInst %v4float %1 Sqrt %18 = OpExtInst %v4float %1 Sqrt %17 +; CHECK-NOT: OpStore %dv OpStore %dv %18 %19 = OpLoad %v4float %v OpStore %gl_FragColor %19 @@ -94,21 +84,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %9 -%15 = OpLabel -%v = OpVariable %_ptr_Function_v4float Function -%16 = OpLoad %v4float %BaseColor -OpStore %v %16 -%19 = OpLoad %v4float %v -OpStore %gl_FragColor %19 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck( - predefs1 + names_before + predefs2 + func_before, - predefs1 + names_after + predefs2 + func_after, true, true); + SinglePassRunAndMatch(spirv, true); } TEST_F(AggressiveDCETest, NoEliminateFrexp) { @@ -242,35 +218,23 @@ TEST_F(AggressiveDCETest, EliminateDecorate) { // gl_FragColor = v; // } - const std::string predefs1 = - R"(OpCapability Shader + const std::string spirv = + R"( +OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %BaseColor %Dead %gl_FragColor OpExecutionMode %main OriginUpperLeft OpSource GLSL 140 -)"; - - const std::string names_before = - R"(OpName %main "main" +OpName %main "main" OpName %v "v" OpName %BaseColor "BaseColor" OpName %dv "dv" OpName %Dead "Dead" OpName %gl_FragColor "gl_FragColor" +; CHECK-NOT: OpDecorate OpDecorate %8 RelaxedPrecision -)"; - - const std::string names_after = - R"(OpName %main "main" -OpName %v "v" -OpName %BaseColor "BaseColor" -OpName %Dead "Dead" -OpName %gl_FragColor "gl_FragColor" -)"; - - const std::string predefs2_before = - R"(%void = OpTypeVoid +%void = OpTypeVoid %10 = OpTypeFunction %void %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 @@ -281,29 +245,14 @@ OpName %gl_FragColor "gl_FragColor" %float_0_5 = OpConstant %float 0.5 %_ptr_Output_v4float = OpTypePointer Output %v4float %gl_FragColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string predefs2_after = - R"(%void = OpTypeVoid -%10 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Function_v4float = OpTypePointer Function %v4float -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%Dead = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%gl_FragColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %10 +%main = OpFunction %void None %10 %17 = OpLabel %v = OpVariable %_ptr_Function_v4float Function %dv = OpVariable %_ptr_Function_v4float Function %18 = OpLoad %v4float %BaseColor OpStore %v %18 %19 = OpLoad %v4float %Dead +; CHECK-NOT: OpVectorTimesScalar %8 = OpVectorTimesScalar %v4float %19 %float_0_5 OpStore %dv %8 %20 = OpLoad %v4float %v @@ -312,21 +261,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %10 -%17 = OpLabel -%v = OpVariable %_ptr_Function_v4float Function -%18 = OpLoad %v4float %BaseColor -OpStore %v %18 -%20 = OpLoad %v4float %v -OpStore %gl_FragColor %20 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck( - predefs1 + names_before + predefs2_before + func_before, - predefs1 + names_after + predefs2_after + func_after, true, true); + SinglePassRunAndMatch(spirv, true); } TEST_F(AggressiveDCETest, Simple) { @@ -342,53 +277,44 @@ TEST_F(AggressiveDCETest, Simple) { // gl_FragColor = v; // } - const std::string predefs1 = - R"(OpCapability Shader + const std::string spirv = + R"( +OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 +; CHECK: OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor OpEntryPoint Fragment %main "main" %BaseColor %Dead %gl_FragColor OpExecutionMode %main OriginUpperLeft OpSource GLSL 140 -)"; - - const std::string names_before = - R"(OpName %main "main" +OpName %main "main" OpName %v "v" OpName %BaseColor "BaseColor" +; CHECK-NOT: OpName %dv "dv" OpName %dv "dv" +; CHECK-NOT: OpName %Dead "Dead" OpName %Dead "Dead" OpName %gl_FragColor "gl_FragColor" -)"; - - const std::string names_after = - R"(OpName %main "main" -OpName %v "v" -OpName %BaseColor "BaseColor" -OpName %Dead "Dead" -OpName %gl_FragColor "gl_FragColor" -)"; - - const std::string predefs2 = - R"(%void = OpTypeVoid +%void = OpTypeVoid %9 = OpTypeFunction %void %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 %_ptr_Function_v4float = OpTypePointer Function %v4float %_ptr_Input_v4float = OpTypePointer Input %v4float %BaseColor = OpVariable %_ptr_Input_v4float Input +; CHECK-NOT: %Dead = OpVariable %Dead = OpVariable %_ptr_Input_v4float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %gl_FragColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %9 +%main = OpFunction %void None %9 %15 = OpLabel %v = OpVariable %_ptr_Function_v4float Function +; CHECK-NOT: %dv = OpVariable %dv = OpVariable %_ptr_Function_v4float Function %16 = OpLoad %v4float %BaseColor OpStore %v %16 +; CHECK-NOT: OpLoad %v4float %Dead %17 = OpLoad %v4float %Dead +; CHECK-NOT: OpStore %dv OpStore %dv %17 %18 = OpLoad %v4float %v OpStore %gl_FragColor %18 @@ -396,21 +322,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %9 -%15 = OpLabel -%v = OpVariable %_ptr_Function_v4float Function -%16 = OpLoad %v4float %BaseColor -OpStore %v %16 -%18 = OpLoad %v4float %v -OpStore %gl_FragColor %18 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck( - predefs1 + names_before + predefs2 + func_before, - predefs1 + names_after + predefs2 + func_after, true, true); + SinglePassRunAndMatch(spirv, true); } TEST_F(AggressiveDCETest, OptAllowListExtension) { @@ -426,35 +338,22 @@ TEST_F(AggressiveDCETest, OptAllowListExtension) { // gl_FragColor = v; // } - const std::string predefs1 = + const std::string spirv = R"(OpCapability Shader OpExtension "SPV_AMD_gpu_shader_int16" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 +; CHECK: OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor OpEntryPoint Fragment %main "main" %BaseColor %Dead %gl_FragColor OpExecutionMode %main OriginUpperLeft OpSource GLSL 140 -)"; - - const std::string names_before = - R"(OpName %main "main" +OpName %main "main" OpName %v "v" OpName %BaseColor "BaseColor" OpName %dv "dv" OpName %Dead "Dead" OpName %gl_FragColor "gl_FragColor" -)"; - - const std::string names_after = - R"(OpName %main "main" -OpName %v "v" -OpName %BaseColor "BaseColor" -OpName %Dead "Dead" -OpName %gl_FragColor "gl_FragColor" -)"; - - const std::string predefs2 = - R"(%void = OpTypeVoid +%void = OpTypeVoid %9 = OpTypeFunction %void %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 @@ -464,10 +363,7 @@ OpName %gl_FragColor "gl_FragColor" %Dead = OpVariable %_ptr_Input_v4float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %gl_FragColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %9 +%main = OpFunction %void None %9 %15 = OpLabel %v = OpVariable %_ptr_Function_v4float Function %dv = OpVariable %_ptr_Function_v4float Function @@ -481,21 +377,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %9 -%15 = OpLabel -%v = OpVariable %_ptr_Function_v4float Function -%16 = OpLoad %v4float %BaseColor -OpStore %v %16 -%18 = OpLoad %v4float %v -OpStore %gl_FragColor %18 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck( - predefs1 + names_before + predefs2 + func_before, - predefs1 + names_after + predefs2 + func_after, true, true); + SinglePassRunAndMatch(spirv, true); } TEST_F(AggressiveDCETest, NoOptDenyListExtension) { @@ -571,7 +453,7 @@ TEST_F(AggressiveDCETest, ElimWithCall) { // gl_FragColor = vec4(0.0); // } - const std::string defs_before = + const std::string text = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -600,54 +482,25 @@ OpName %gl_FragColor "gl_FragColor" %gl_FragColor = OpVariable %_ptr_Output_v4float Output %float_0 = OpConstant %float 0 %20 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 -)"; - - const std::string defs_after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %i1 %i2 %gl_FragColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 140 -OpName %main "main" -OpName %nothing_vf4_ "nothing(vf4;" -OpName %v "v" -OpName %v1 "v1" -OpName %i1 "i1" -OpName %i2 "i2" -OpName %param "param" -OpName %gl_FragColor "gl_FragColor" -%void = OpTypeVoid -%12 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Function_v4float = OpTypePointer Function %v4float -%16 = OpTypeFunction %void %_ptr_Function_v4float -%_ptr_Input_v4float = OpTypePointer Input %v4float -%i1 = OpVariable %_ptr_Input_v4float Input -%i2 = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%gl_FragColor = OpVariable %_ptr_Output_v4float Output -%float_0 = OpConstant %float 0 -%20 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %12 +%main = OpFunction %void None %12 %21 = OpLabel %v1 = OpVariable %_ptr_Function_v4float Function %v2 = OpVariable %_ptr_Function_v4float Function %param = OpVariable %_ptr_Function_v4float Function %22 = OpLoad %v4float %i1 OpStore %v1 %22 +; CHECK-NOT: OpLoad %v4float %i2 %23 = OpLoad %v4float %i2 +; CHECK-NOT: OpStore %v2 OpStore %v2 %23 %24 = OpLoad %v4float %v1 OpStore %param %24 +; CHECK: OpFunctionCall %void %nothing_vf4_ %25 = OpFunctionCall %void %nothing_vf4_ %param OpStore %gl_FragColor %20 OpReturn OpFunctionEnd +; CHECK: %nothing_vf4_ = OpFunction %nothing_vf4_ = OpFunction %void None %16 %v = OpFunctionParameter %_ptr_Function_v4float %26 = OpLabel @@ -655,28 +508,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %12 -%21 = OpLabel -%v1 = OpVariable %_ptr_Function_v4float Function -%param = OpVariable %_ptr_Function_v4float Function -%22 = OpLoad %v4float %i1 -OpStore %v1 %22 -%24 = OpLoad %v4float %v1 -OpStore %param %24 -%25 = OpFunctionCall %void %nothing_vf4_ %param -OpStore %gl_FragColor %20 -OpReturn -OpFunctionEnd -%nothing_vf4_ = OpFunction %void None %16 -%v = OpFunctionParameter %_ptr_Function_v4float -%26 = OpLabel -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck(defs_before + func_before, - defs_after + func_after, true, true); + SinglePassRunAndMatch(text, true); } TEST_F(AggressiveDCETest, NoParamElim) { @@ -998,7 +830,7 @@ TEST_F(AggressiveDCETest, PrivateStoreElimInEntryNoCalls) { // OutColor = v; // } - const std::string predefs_before = + const std::string spirv = R"(OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -1008,6 +840,7 @@ OpSource GLSL 450 OpName %main "main" OpName %v "v" OpName %BaseColor "BaseColor" +; CHECK-NOT: OpName %dv "dv" OpName %dv "dv" OpName %Dead "Dead" OpName %OutColor "OutColor" @@ -1019,49 +852,22 @@ OpDecorate %OutColor Location 0 %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 %_ptr_Function_v4float = OpTypePointer Function %v4float +; CHECK-NOT: OpTypePointer Private %_ptr_Private_v4float = OpTypePointer Private %v4float %_ptr_Input_v4float = OpTypePointer Input %v4float %BaseColor = OpVariable %_ptr_Input_v4float Input %Dead = OpVariable %_ptr_Input_v4float Input %_ptr_Output_v4float = OpTypePointer Output %v4float +; CHECK-NOT: %dv = OpVariable %dv = OpVariable %_ptr_Private_v4float Private %OutColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string predefs_after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %BaseColor %Dead %OutColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpName %main "main" -OpName %v "v" -OpName %BaseColor "BaseColor" -OpName %Dead "Dead" -OpName %OutColor "OutColor" -OpDecorate %BaseColor Location 0 -OpDecorate %Dead Location 1 -OpDecorate %OutColor Location 0 -%void = OpTypeVoid -%9 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Function_v4float = OpTypePointer Function %v4float -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%Dead = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%OutColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string main_before = - R"(%main = OpFunction %void None %9 +%main = OpFunction %void None %9 %16 = OpLabel %v = OpVariable %_ptr_Function_v4float Function %17 = OpLoad %v4float %BaseColor OpStore %v %17 %18 = OpLoad %v4float %Dead +; CHECK-NOT: OpStore %dv OpStore %dv %18 %19 = OpLoad %v4float %v %20 = OpFNegate %v4float %19 @@ -1070,21 +876,7 @@ OpReturn OpFunctionEnd )"; - const std::string main_after = - R"(%main = OpFunction %void None %9 -%16 = OpLabel -%v = OpVariable %_ptr_Function_v4float Function -%17 = OpLoad %v4float %BaseColor -OpStore %v %17 -%19 = OpLoad %v4float %v -%20 = OpFNegate %v4float %19 -OpStore %OutColor %20 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck( - predefs_before + main_before, predefs_after + main_after, true, true); + SinglePassRunAndMatch(spirv, true); } TEST_F(AggressiveDCETest, NoPrivateStoreElimIfLoad) { @@ -1288,7 +1080,7 @@ TEST_F(AggressiveDCETest, WorkgroupStoreElimInEntryNoCalls) { // OutColor = v; // } - const std::string predefs_before = + const std::string spirv = R"(OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -1298,6 +1090,7 @@ OpSource GLSL 450 OpName %main "main" OpName %v "v" OpName %BaseColor "BaseColor" +; CHECK-NOT: OpName %dv "dv" OpName %dv "dv" OpName %Dead "Dead" OpName %OutColor "OutColor" @@ -1309,49 +1102,22 @@ OpDecorate %OutColor Location 0 %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 %_ptr_Function_v4float = OpTypePointer Function %v4float +; CHECK-NOT: OpTypePointer Workgroup %_ptr_Workgroup_v4float = OpTypePointer Workgroup %v4float %_ptr_Input_v4float = OpTypePointer Input %v4float %BaseColor = OpVariable %_ptr_Input_v4float Input %Dead = OpVariable %_ptr_Input_v4float Input %_ptr_Output_v4float = OpTypePointer Output %v4float +; CHECK-NOT: %dv = OpVariable %dv = OpVariable %_ptr_Workgroup_v4float Workgroup %OutColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string predefs_after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %BaseColor %Dead %OutColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpName %main "main" -OpName %v "v" -OpName %BaseColor "BaseColor" -OpName %Dead "Dead" -OpName %OutColor "OutColor" -OpDecorate %BaseColor Location 0 -OpDecorate %Dead Location 1 -OpDecorate %OutColor Location 0 -%void = OpTypeVoid -%9 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Function_v4float = OpTypePointer Function %v4float -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%Dead = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%OutColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string main_before = - R"(%main = OpFunction %void None %9 +%main = OpFunction %void None %9 %16 = OpLabel %v = OpVariable %_ptr_Function_v4float Function %17 = OpLoad %v4float %BaseColor OpStore %v %17 %18 = OpLoad %v4float %Dead +; CHECK-NOT: OpStore %dv OpStore %dv %18 %19 = OpLoad %v4float %v %20 = OpFNegate %v4float %19 @@ -1360,21 +1126,7 @@ OpReturn OpFunctionEnd )"; - const std::string main_after = - R"(%main = OpFunction %void None %9 -%16 = OpLabel -%v = OpVariable %_ptr_Function_v4float Function -%17 = OpLoad %v4float %BaseColor -OpStore %v %17 -%19 = OpLoad %v4float %v -%20 = OpFNegate %v4float %19 -OpStore %OutColor %20 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck( - predefs_before + main_before, predefs_after + main_after, true, true); + SinglePassRunAndMatch(spirv, true); } TEST_F(AggressiveDCETest, EliminateDeadIfThenElse) { @@ -1393,7 +1145,7 @@ TEST_F(AggressiveDCETest, EliminateDeadIfThenElse) { // OutColor = vec4(1.0,1.0,1.0,1.0); // } - const std::string predefs_before = + const std::string spirv = R"(OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -1424,34 +1176,11 @@ OpDecorate %OutColor Location 0 %OutColor = OpVariable %_ptr_Output_v4float Output %float_1 = OpConstant %float 1 %21 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 -)"; - - const std::string predefs_after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %BaseColor %OutColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpName %main "main" -OpName %BaseColor "BaseColor" -OpName %OutColor "OutColor" -OpDecorate %BaseColor Location 0 -OpDecorate %OutColor Location 0 -%void = OpTypeVoid -%7 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%OutColor = OpVariable %_ptr_Output_v4float Output -%float_1 = OpConstant %float 1 -%21 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %7 +; CHECK: = OpFunction %void +; CHECK-NEXT: %22 = OpLabel +; CHECK-NEXT: OpBranch %26 +; CHECK-NEXT: %26 = OpLabel +%main = OpFunction %void None %7 %22 = OpLabel %d = OpVariable %_ptr_Function_float Function %23 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0 @@ -1475,18 +1204,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %7 -%22 = OpLabel -OpBranch %26 -%26 = OpLabel -OpStore %OutColor %21 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck( - predefs_before + func_before, predefs_after + func_after, true, true); + SinglePassRunAndMatch(spirv, true); } TEST_F(AggressiveDCETest, EliminateDeadIfThen) { @@ -1503,7 +1221,7 @@ TEST_F(AggressiveDCETest, EliminateDeadIfThen) { // OutColor = vec4(1.0,1.0,1.0,1.0); // } - const std::string predefs_before = + const std::string spirv = R"(OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -1533,34 +1251,11 @@ OpDecorate %OutColor Location 0 %OutColor = OpVariable %_ptr_Output_v4float Output %float_1 = OpConstant %float 1 %20 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 -)"; - - const std::string predefs_after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %BaseColor %OutColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpName %main "main" -OpName %BaseColor "BaseColor" -OpName %OutColor "OutColor" -OpDecorate %BaseColor Location 0 -OpDecorate %OutColor Location 0 -%void = OpTypeVoid -%7 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%OutColor = OpVariable %_ptr_Output_v4float Output -%float_1 = OpConstant %float 1 -%20 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %7 +; CHECK: = OpFunction +; CHECK-NEXT: %21 = OpLabel +; CHECK-NEXT: OpBranch [[target:%\w+]] +; CHECK-NEXT: [[target]] = OpLabel +%main = OpFunction %void None %7 %21 = OpLabel %d = OpVariable %_ptr_Function_float Function %22 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0 @@ -1579,18 +1274,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %7 -%21 = OpLabel -OpBranch %25 -%25 = OpLabel -OpStore %OutColor %20 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck( - predefs_before + func_before, predefs_after + func_after, true, true); + SinglePassRunAndMatch(spirv, true); } TEST_F(AggressiveDCETest, EliminateDeadSwitch) { @@ -1609,7 +1293,7 @@ TEST_F(AggressiveDCETest, EliminateDeadSwitch) { // } // OutColor = vec4(1.0,1.0,1.0,1.0); // } - const std::string before = + const std::string spirv = R"(OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -1642,6 +1326,10 @@ TEST_F(AggressiveDCETest, EliminateDeadSwitch) { %OutColor = OpVariable %_ptr_Output_v4float Output %float_1 = OpConstant %float 1 %27 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +; CHECK: = OpFunction +; CHECK-NEXT: = OpLabel +; CHECK-NEXT: OpBranch [[target:%\w+]] +; CHECK-NEXT: [[target]] = OpLabel %main = OpFunction %void None %3 %5 = OpLabel %d = OpVariable %_ptr_Function_float Function @@ -1658,45 +1346,7 @@ TEST_F(AggressiveDCETest, EliminateDeadSwitch) { OpReturn OpFunctionEnd)"; - const std::string after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %x %BaseColor %OutColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpName %main "main" -OpName %x "x" -OpName %BaseColor "BaseColor" -OpName %OutColor "OutColor" -OpDecorate %x Flat -OpDecorate %x Location 1 -OpDecorate %BaseColor Location 0 -OpDecorate %OutColor Location 0 -%void = OpTypeVoid -%3 = OpTypeFunction %void -%int = OpTypeInt 32 1 -%_ptr_Input_int = OpTypePointer Input %int -%x = OpVariable %_ptr_Input_int Input -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%OutColor = OpVariable %_ptr_Output_v4float Output -%float_1 = OpConstant %float 1 -%27 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 -%main = OpFunction %void None %3 -%5 = OpLabel -OpBranch %11 -%11 = OpLabel -OpStore %OutColor %27 -OpReturn -OpFunctionEnd -)"; - - SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck(before, after, true, true); + SinglePassRunAndMatch(spirv, true); } TEST_F(AggressiveDCETest, EliminateDeadIfThenElseNested) { @@ -1721,7 +1371,7 @@ TEST_F(AggressiveDCETest, EliminateDeadIfThenElseNested) { // OutColor = vec4(1.0,1.0,1.0,1.0); // } - const std::string predefs_before = + const std::string spirv = R"(OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -1754,34 +1404,14 @@ OpDecorate %OutColor Location 0 %OutColor = OpVariable %_ptr_Output_v4float Output %float_1 = OpConstant %float 1 %23 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 -)"; - const std::string predefs_after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %BaseColor %OutColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpName %main "main" -OpName %BaseColor "BaseColor" -OpName %OutColor "OutColor" -OpDecorate %BaseColor Location 0 -OpDecorate %OutColor Location 0 -%void = OpTypeVoid -%7 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%OutColor = OpVariable %_ptr_Output_v4float Output -%float_1 = OpConstant %float 1 -%23 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 -)"; +; CHECK: = OpFunction +; CHECK-NEXT: = OpLabel +; CHECK-NEXT: OpBranch [[target:%\w+]] +; CHECK-NEXT: [[target]] = OpLabel +; CHECK-NOT: OpLabel - const std::string func_before = - R"(%main = OpFunction %void None %7 +%main = OpFunction %void None %7 %24 = OpLabel %d = OpVariable %_ptr_Function_float Function %25 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0 @@ -1823,18 +1453,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %7 -%24 = OpLabel -OpBranch %28 -%28 = OpLabel -OpStore %OutColor %23 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck( - predefs_before + func_before, predefs_after + func_after, true, true); + SinglePassRunAndMatch(spirv, true); } TEST_F(AggressiveDCETest, NoEliminateLiveIfThenElse) { @@ -2578,7 +2197,7 @@ TEST_F(AggressiveDCETest, EliminateEntireFunctionBody) { // d = BaseColor.z; // } - const std::string predefs_before = + const std::string spirv = R"(OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -2607,32 +2226,15 @@ OpDecorate %OutColor Location 0 %uint_2 = OpConstant %uint 2 %_ptr_Output_v4float = OpTypePointer Output %v4float %OutColor = OpVariable %_ptr_Output_v4float Output -)"; - const std::string predefs_after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %BaseColor %OutColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpName %main "main" -OpName %BaseColor "BaseColor" -OpName %OutColor "OutColor" -OpDecorate %BaseColor Location 0 -OpDecorate %OutColor Location 0 -%void = OpTypeVoid -%7 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%OutColor = OpVariable %_ptr_Output_v4float Output -)"; +; CHECK: = OpFunction +; CHECK-NEXT: = OpLabel +; CHECK-NEXT: OpBranch [[target:%\w+]] +; CHECK-NEXT: [[target]] = OpLabel +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd - const std::string func_before = - R"(%main = OpFunction %void None %7 +%main = OpFunction %void None %7 %20 = OpLabel %d = OpVariable %_ptr_Function_float Function %21 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0 @@ -2655,17 +2257,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %7 -%20 = OpLabel -OpBranch %24 -%24 = OpLabel -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck( - predefs_before + func_before, predefs_after + func_after, true, true); + SinglePassRunAndMatch(spirv, true); } TEST_F(AggressiveDCETest, EliminateUselessInnerLoop) { @@ -3949,9 +3541,11 @@ OpLoopMerge %14 %15 None OpBranch %16 %16 = OpLabel OpSelectionMerge %18 None -OpSwitch %13 %18 0 %17 1 %15 +OpSwitch %13 %18 0 %17 1 %19 %17 = OpLabel OpStore %3 %uint_1 +OpBranch %19 +%19 = OpLabel OpBranch %15 %15 = OpLabel OpBranch %12 @@ -4731,7 +4325,7 @@ INSTANTIATE_TEST_SUITE_P( }, // Uint vector type spec constants. One vector has all component dead, - // another vector has one dead unsigend integer and one used unsigned + // another vector has one dead unsigned integer and one used unsigned // integer. { /* .used_consts = */ @@ -5521,10 +5115,9 @@ OpCapability ImageBuffer OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 -OpEntryPoint GLCompute %2 "min" %gl_GlobalInvocationID +OpEntryPoint GLCompute %2 "min" OpExecutionMode %2 LocalSize 64 1 1 OpSource HLSL 600 -OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId OpDecorate %4 DescriptorSet 4 OpDecorate %4 Binding 70 %uint = OpTypeInt 32 0 @@ -5535,12 +5128,9 @@ OpDecorate %4 Binding 70 %10 = OpTypeFunction %void %uint_0 = OpConstant %uint 0 %uint_1 = OpConstant %uint 1 -%v3uint = OpTypeVector %uint 3 -%_ptr_Input_v3uint = OpTypePointer Input %v3uint %_ptr_Image_uint = OpTypePointer Image %uint %4 = OpVariable %_ptr_UniformConstant_6 UniformConstant %16 = OpVariable %_ptr_Private_6 Private -%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input %2 = OpFunction %void None %10 %17 = OpLabel %18 = OpLoad %6 %4 @@ -6393,8 +5983,8 @@ OpFunctionEnd TEST_F(AggressiveDCETest, DeadInputInterfaceV13) { const std::string spirv = R"( -; CHECK: OpEntryPoint GLCompute %main "main" [[var:%\w+]] -; CHECK: [[var]] = OpVariable +; CHECK: OpEntryPoint GLCompute %main "main" +; CHECK-NOT: OpVariable OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" %dead @@ -6417,8 +6007,8 @@ OpFunctionEnd TEST_F(AggressiveDCETest, DeadInputInterfaceV14) { const std::string spirv = R"( -; CHECK: OpEntryPoint GLCompute %main "main" [[var:%\w+]] -; CHECK: [[var]] = OpVariable +; CHECK: OpEntryPoint GLCompute %main "main" +; CHECK-NOT: OpVariable OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" %dead @@ -7063,6 +6653,287 @@ TEST_F(AggressiveDCETest, DebugInfoKeepInFunctionElimStoreVar) { SinglePassRunAndMatch(text, true); } +TEST_F(AggressiveDCETest, ShaderDebugInfoKeepInFunctionElimStoreVar) { + // Verify that dead local variable tc and store eliminated but all + // in-function NonSemantic Shader debuginfo kept. + + const std::string text = R"( + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "NonSemantic.Shader.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_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %uint_4 = OpConstant %uint 4 + %uint_5 = OpConstant %uint 5 + %uint_7 = OpConstant %uint 7 + %uint_8 = OpConstant %uint 8 + %uint_10 = OpConstant %uint 10 + %uint_11 = OpConstant %uint 11 + %uint_12 = OpConstant %uint 12 + %uint_14 = OpConstant %uint 14 + %uint_15 = OpConstant %uint 15 + %uint_16 = OpConstant %uint 16 + %uint_17 = OpConstant %uint 17 + %uint_19 = OpConstant %uint 19 + %uint_20 = OpConstant %uint 20 + %uint_21 = OpConstant %uint 21 + %uint_25 = OpConstant %uint 25 + %uint_29 = OpConstant %uint 29 + %uint_30 = OpConstant %uint 30 + %uint_35 = OpConstant %uint 35 + %uint_41 = OpConstant %uint 41 + %uint_48 = OpConstant %uint 48 + %uint_53 = OpConstant %uint 53 + %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 %uint_0 + %54 = OpExtInst %void %1 DebugExpression %53 + %55 = OpExtInst %void %1 DebugSource %7 + %56 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %55 %uint_5 + %59 = OpExtInst %void %1 DebugTypeBasic %9 %uint_32 %uint_3 %uint_0 + %60 = OpExtInst %void %1 DebugTypeVector %59 %uint_4 + %58 = OpExtInst %void %1 DebugTypeMember %10 %60 %55 %uint_12 %uint_5 %uint_0 %uint_128 %uint_3 + %57 = OpExtInst %void %1 DebugTypeComposite %8 %uint_1 %55 %uint_10 %uint_1 %56 %8 %uint_128 %uint_3 %58 + %63 = OpExtInst %void %1 DebugTypeVector %59 %uint_2 + %62 = OpExtInst %void %1 DebugTypeMember %12 %63 %55 %uint_7 %uint_5 %uint_0 %uint_64 %uint_3 + %61 = OpExtInst %void %1 DebugTypeComposite %11 %uint_1 %55 %uint_5 %uint_1 %56 %11 %uint_64 %uint_3 %62 + %64 = OpExtInst %void %1 DebugTypeComposite %13 %uint_0 %55 %uint_0 %uint_0 %56 %14 %51 %uint_3 + %67 = OpExtInst %void %1 DebugTypeFunction %uint_3 %57 %61 + %68 = OpExtInst %void %1 DebugFunction %16 %67 %55 %uint_15 %uint_1 %56 %16 %uint_3 %uint_16 + %69 = OpExtInst %void %1 DebugLexicalBlock %55 %uint_16 %uint_1 %68 + %70 = OpExtInst %void %1 DebugLocalVariable %17 %63 %55 %uint_19 %uint_12 %69 %uint_4 + %71 = OpExtInst %void %1 DebugLocalVariable %18 %57 %55 %uint_17 %uint_15 %69 %uint_4 + %72 = OpExtInst %void %1 DebugLocalVariable %19 %61 %55 %uint_15 %uint_29 %68 %uint_4 %uint_1 + %73 = OpExtInst %void %1 DebugTypeComposite %20 %uint_1 %55 %uint_0 %uint_0 %56 %21 %51 %uint_3 + %74 = OpExtInst %void %1 DebugGlobalVariable %22 %73 %55 %uint_3 %uint_14 %56 %22 %g_sAniso %uint_8 + %75 = OpExtInst %void %1 DebugGlobalVariable %23 %64 %55 %uint_1 %uint_11 %56 %23 %g_tColor %uint_8 + %MainPs = OpFunction %void None %45 + %76 = OpLabel + %78 = OpVariable %_ptr_Function_PS_OUTPUT Function + %79 = OpVariable %_ptr_Function_v2float Function + %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 + %112 = OpExtInst %void %1 DebugFunctionDefinition %68 %MainPs + %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 DebugFunctionDefinition %68 %MainPs +;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 + %300 = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_17 %uint_30 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_17 %uint_30 + %88 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0 + %89 = OpLoad %v2float %88 + %301 = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_12 %uint_35 + OpStore %79 %89 +;CHECK-NOT: OpStore %79 %89 + %302 = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_12 %uint_35 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_12 %uint_35 + %106 = OpExtInst %void %1 DebugValue %70 %89 %52 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugValue %70 %89 %52 + %303 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_25 %uint_32 + %91 = OpLoad %type_2d_image %g_tColor + %304 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_41 %uint_48 + %92 = OpLoad %type_sampler %g_sAniso + %305 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_25 %uint_53 + %94 = OpSampledImage %type_sampled_image %91 %92 + %95 = OpImageSampleImplicitLod %v4float %94 %89 None + %306 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_5 %uint_53 + %96 = OpAccessChain %_ptr_Function_v4float %78 %int_0 + OpStore %96 %95 + %307 = OpExtInst %void %1 DebugLine %55 %uint_21 %uint_21 %uint_12 %uint_20 + %97 = OpLoad %PS_OUTPUT %78 + %308 = OpExtInst %void %1 DebugLine %55 %uint_21 %uint_21 %uint_5 %uint_20 + OpStore %81 %97 + %309 = OpExtInst %void %1 DebugNoLine +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugNoLine + %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(text, true); +} + +TEST_F(AggressiveDCETest, ShaderDebugInfoGlobalDCE) { + // Verify that DebugGlobalVariable for eliminated private variable has + // variable operand replaced with DebugInfoNone. + + const std::string text = R"(OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %out_var_SV_Target0 %a +OpExecutionMode %MainPs OriginUpperLeft +%5 = OpString "source2.hlsl" +%24 = OpString "float" +%29 = OpString "vColor" +%33 = OpString "PS_OUTPUT" +%37 = OpString "MainPs" +%38 = OpString "" +%42 = OpString "ps_output" +%46 = OpString "a" +OpName %a "a" +OpName %out_var_SV_Target0 "out.var.SV_Target0" +OpName %MainPs "MainPs" +OpName %PS_OUTPUT "PS_OUTPUT" +OpMemberName %PS_OUTPUT 0 "vColor" +OpDecorate %out_var_SV_Target0 Location 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%8 = OpConstantNull %v4float +%float_0 = OpConstant %float 0 +%10 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%_ptr_Private_v4float = OpTypePointer Private %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%uint_1 = OpConstant %uint 1 +%uint_4 = OpConstant %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_3 = OpConstant %uint 3 +%uint_0 = OpConstant %uint 0 +%uint_128 = OpConstant %uint 128 +%uint_12 = OpConstant %uint 12 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_10 = OpConstant %uint 10 +%uint_15 = OpConstant %uint 15 +%48 = OpTypeFunction %void +%PS_OUTPUT = OpTypeStruct %v4float +%54 = OpTypeFunction %PS_OUTPUT +%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT +%_ptr_Function_v4float = OpTypePointer Function %v4float +%a = OpVariable %_ptr_Private_v4float Private +;CHECK-NOT: %a = OpVariable %_ptr_Private_v4float Private +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output +;CHECK: [[dbg_none:%\w+]] = OpExtInst %void %1 DebugInfoNone +%18 = OpExtInst %void %1 DebugExpression +%19 = OpExtInst %void %1 DebugSource %5 +%20 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %19 %uint_5 +%25 = OpExtInst %void %1 DebugTypeBasic %24 %uint_32 %uint_3 %uint_0 +%28 = OpExtInst %void %1 DebugTypeVector %25 %uint_4 +%31 = OpExtInst %void %1 DebugTypeMember %29 %28 %19 %uint_5 %uint_12 %uint_0 %uint_128 %uint_3 +%34 = OpExtInst %void %1 DebugTypeComposite %33 %uint_1 %19 %uint_3 %uint_8 %20 %33 %uint_128 %uint_3 %31 +%36 = OpExtInst %void %1 DebugTypeFunction %uint_3 %34 +%39 = OpExtInst %void %1 DebugFunction %37 %36 %19 %uint_8 %uint_1 %20 %38 %uint_3 %uint_9 +%41 = OpExtInst %void %1 DebugLexicalBlock %19 %uint_9 %uint_1 %39 +%43 = OpExtInst %void %1 DebugLocalVariable %42 %34 %19 %uint_10 %uint_15 %41 %uint_4 +%47 = OpExtInst %void %1 DebugGlobalVariable %46 %28 %19 %uint_1 %uint_15 %20 %46 %a %uint_8 +;CHECK: %47 = OpExtInst %void %1 DebugGlobalVariable %46 %28 %19 %uint_1 %uint_15 %20 %46 [[dbg_none]] %uint_8 +%MainPs = OpFunction %void None %48 +%49 = OpLabel +%65 = OpVariable %_ptr_Function_PS_OUTPUT Function +%66 = OpVariable %_ptr_Function_PS_OUTPUT Function +OpStore %a %8 +%72 = OpExtInst %void %1 DebugScope %41 +%69 = OpExtInst %void %1 DebugDeclare %43 %65 %18 +OpLine %5 11 5 +%70 = OpAccessChain %_ptr_Function_v4float %65 %int_0 +OpStore %70 %10 +OpLine %5 12 12 +%71 = OpLoad %PS_OUTPUT %65 +OpLine %5 12 5 +OpStore %66 %71 +%73 = OpExtInst %void %1 DebugNoLine +%74 = OpExtInst %void %1 DebugNoScope +%51 = OpLoad %PS_OUTPUT %66 +%53 = OpCompositeExtract %v4float %51 0 +OpStore %out_var_SV_Target0 %53 +OpLine %5 13 1 +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + TEST_F(AggressiveDCETest, DebugInfoDeclareKeepsStore) { // Verify that local variable tc and its store are kept by DebugDeclare. // @@ -7584,11 +7455,327 @@ TEST_F(AggressiveDCETest, KeepDebugScopeParent) { SinglePassRunAndMatch(text, true); } -// TODO(greg-lunarg): Add tests to verify handling of these cases: -// -// Check that logical addressing required -// Check that function calls inhibit optimization -// Others? +TEST_F(AggressiveDCETest, KeepExportFunctions) { + // All functions are reachable. In particular, ExportedFunc and Constant are + // reachable because ExportedFunc is exported. Nothing should be removed. + const std::vector text = { + // clang-format off + "OpCapability Shader", + "OpCapability Linkage", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Fragment %main \"main\"", + "OpName %main \"main\"", + "OpName %ExportedFunc \"ExportedFunc\"", + "OpName %Live \"Live\"", + "OpDecorate %ExportedFunc LinkageAttributes \"ExportedFunc\" Export", + "%void = OpTypeVoid", + "%7 = OpTypeFunction %void", + "%main = OpFunction %void None %7", + "%15 = OpLabel", + "OpReturn", + "OpFunctionEnd", +"%ExportedFunc = OpFunction %void None %7", + "%19 = OpLabel", + "%16 = OpFunctionCall %void %Live", + "OpReturn", + "OpFunctionEnd", + "%Live = OpFunction %void None %7", + "%20 = OpLabel", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + std::string assembly = JoinAllInsts(text); + auto result = SinglePassRunAndDisassemble( + assembly, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); + EXPECT_EQ(assembly, std::get<0>(result)); +} + +TEST_F(AggressiveDCETest, KeepPrivateVarInExportFunctions) { + // The loads and stores from the private variable should not be removed + // because the functions are exported and could be called. + const std::string text = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %privateVar "privateVar" +OpName %ReadPrivate "ReadPrivate" +OpName %WritePrivate "WritePrivate" +OpName %value "value" +OpDecorate %ReadPrivate LinkageAttributes "ReadPrivate" Export +OpDecorate %WritePrivate LinkageAttributes "WritePrivate" Export +%int = OpTypeInt 32 1 +%_ptr_Private_int = OpTypePointer Private %int +%6 = OpTypeFunction %int +%void = OpTypeVoid +%_ptr_Function_int = OpTypePointer Function %int +%10 = OpTypeFunction %void %_ptr_Function_int +%privateVar = OpVariable %_ptr_Private_int Private +%ReadPrivate = OpFunction %int None %6 +%12 = OpLabel +%8 = OpLoad %int %privateVar +OpReturnValue %8 +OpFunctionEnd +%WritePrivate = OpFunction %void None %10 +%value = OpFunctionParameter %_ptr_Function_int +%13 = OpLabel +%14 = OpLoad %int %value +OpStore %privateVar %14 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); + EXPECT_EQ(text, std::get<0>(result)); +} + +TEST_F(AggressiveDCETest, KeepLableNames) { + const std::string text = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %WritePrivate "WritePrivate" +OpName %entry "entry" +OpName %target "target" +OpDecorate %WritePrivate LinkageAttributes "WritePrivate" Export +%void = OpTypeVoid +%3 = OpTypeFunction %void +%WritePrivate = OpFunction %void None %3 +%entry = OpLabel +OpBranch %target +%target = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); + EXPECT_EQ(text, std::get<0>(result)); +} + +TEST_F(AggressiveDCETest, PreserveInterface) { + // Set preserve_interface to true. Verify that unused uniform + // constant in entry point interface is not eliminated. + const std::string text = R"(OpCapability RayTracingKHR +OpExtension "SPV_KHR_ray_tracing" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint RayGenerationNV %2 "main" %3 %4 +OpDecorate %3 Location 0 +OpDecorate %4 DescriptorSet 2 +OpDecorate %4 Binding 0 +%void = OpTypeVoid +%6 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%float = OpTypeFloat 32 +%_ptr_CallableDataNV_float = OpTypePointer CallableDataNV %float +%3 = OpVariable %_ptr_CallableDataNV_float CallableDataNV +%13 = OpTypeAccelerationStructureKHR +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%4 = OpVariable %_ptr_UniformConstant_13 UniformConstant +%2 = OpFunction %void None %6 +%15 = OpLabel +OpExecuteCallableKHR %uint_0 %3 +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false, + /* preserve_interface */ true); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); + EXPECT_EQ(text, std::get<0>(result)); +} + +TEST_F(AggressiveDCETest, EmptyContinueWithConditionalBranch) { + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +%void = OpTypeVoid +%4 = OpTypeFunction %void +%bool = OpTypeBool +%false = OpConstantFalse %bool +%2 = OpFunction %void None %4 +%9 = OpLabel +OpBranch %10 +%10 = OpLabel +OpLoopMerge %11 %12 None +OpBranch %13 +%13 = OpLabel +OpKill +%12 = OpLabel +OpBranchConditional %false %10 %10 +%11 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(text, text, false); +} + +TEST_F(AggressiveDCETest, FunctionBecomesUnreachableAfterDCE) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 320 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %7 = OpTypeFunction %int + %int_n1 = OpConstant %int -1 + %2 = OpFunction %void None %4 + %9 = OpLabel + OpKill + %10 = OpLabel + %11 = OpFunctionCall %int %12 + OpReturn + OpFunctionEnd +; CHECK: {{%\w+}} = OpFunction %int DontInline|Pure + %12 = OpFunction %int DontInline|Pure %7 +; CHECK-NEXT: {{%\w+}} = OpLabel + %13 = OpLabel + %14 = OpVariable %_ptr_Function_int Function +; CHECK-NEXT: OpBranch [[header:%\w+]] + OpBranch %15 +; CHECK-NEXT: [[header]] = OpLabel +; CHECK-NEXT: OpBranch [[merge:%\w+]] + %15 = OpLabel + OpLoopMerge %16 %17 None + OpBranch %18 + %18 = OpLabel + %19 = OpLoad %int %14 + OpBranch %17 + %17 = OpLabel + OpBranch %15 +; CHECK-NEXT: [[merge]] = OpLabel + %16 = OpLabel +; CHECK-NEXT: OpReturnValue %int_n1 + OpReturnValue %int_n1 +; CHECK-NEXT: OpFunctionEnd + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(AggressiveDCETest, KeepTopLevelDebugInfo) { + // Don't eliminate DebugCompilationUnit, DebugSourceContinued, and + // DebugEntryPoint + const std::string text = R"( + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPs "MainPs" %out_var_SV_Target0 + OpExecutionMode %MainPs OriginUpperLeft + %4 = OpString "foo2.frag" + %5 = OpString " +struct PS_OUTPUT +{ + float4 vColor : SV_Target0 ; +} ; + +" + %6 = OpString " +PS_OUTPUT MainPs ( ) +{ + PS_OUTPUT ps_output ; + ps_output . vColor = float4( 1.0, 0.0, 0.0, 0.0 ); + return ps_output ; +} + +" + %7 = OpString "float" + %8 = OpString "vColor" + %9 = OpString "PS_OUTPUT" + %10 = OpString "MainPs" + %11 = OpString "" + %12 = OpString "ps_output" + %13 = OpString "97a939fb" + %14 = OpString " foo2.frag -E MainPs -T ps_6_1 -spirv -fspv-target-env=vulkan1.2 -fspv-debug=vulkan-with-source -fcgl -Fo foo2.frag.nopt.spv -Qembed_debug" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %MainPs "MainPs" + OpDecorate %out_var_SV_Target0 Location 0 + %float = OpTypeFloat 32 + %float_1 = OpConstant %float 1 + %float_0 = OpConstant %float 0 + %v4float = OpTypeVector %float 4 + %23 = OpConstantComposite %v4float %float_1 %float_0 %float_0 %float_0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %uint_1 = OpConstant %uint 1 + %uint_4 = OpConstant %uint 4 + %uint_5 = OpConstant %uint 5 + %uint_3 = OpConstant %uint 3 + %uint_0 = OpConstant %uint 0 + %uint_128 = OpConstant %uint 128 + %uint_12 = OpConstant %uint 12 + %uint_2 = OpConstant %uint 2 + %uint_8 = OpConstant %uint 8 + %uint_7 = OpConstant %uint 7 + %uint_9 = OpConstant %uint 9 + %uint_15 = OpConstant %uint 15 + %42 = OpTypeFunction %void + %uint_10 = OpConstant %uint 10 + %uint_53 = OpConstant %uint 53 +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %49 = OpExtInst %void %1 DebugExpression + %50 = OpExtInst %void %1 DebugSource %4 %5 + %51 = OpExtInst %void %1 DebugSourceContinued %6 +; CHECK: %51 = OpExtInst %void %1 DebugSourceContinued %6 + %52 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %50 %uint_5 +; CHECK: %52 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %50 %uint_5 + %53 = OpExtInst %void %1 DebugTypeBasic %7 %uint_32 %uint_3 %uint_0 + %54 = OpExtInst %void %1 DebugTypeVector %53 %uint_4 + %55 = OpExtInst %void %1 DebugTypeMember %8 %54 %50 %uint_4 %uint_12 %uint_0 %uint_128 %uint_3 + %56 = OpExtInst %void %1 DebugTypeComposite %9 %uint_1 %50 %uint_2 %uint_8 %52 %9 %uint_128 %uint_3 %55 + %57 = OpExtInst %void %1 DebugTypeFunction %uint_3 %56 + %58 = OpExtInst %void %1 DebugFunction %10 %57 %50 %uint_7 %uint_1 %52 %11 %uint_3 %uint_8 + %59 = OpExtInst %void %1 DebugLexicalBlock %50 %uint_8 %uint_1 %58 + %60 = OpExtInst %void %1 DebugLocalVariable %12 %56 %50 %uint_9 %uint_15 %59 %uint_4 + %61 = OpExtInst %void %1 DebugEntryPoint %58 %52 %13 %14 +; CHECK: %61 = OpExtInst %void %1 DebugEntryPoint %58 %52 %13 %14 + %MainPs = OpFunction %void None %42 + %62 = OpLabel + %63 = OpExtInst %void %1 DebugFunctionDefinition %58 %MainPs + %112 = OpExtInst %void %1 DebugScope %59 + %111 = OpExtInst %void %1 DebugLine %50 %uint_10 %uint_10 %uint_5 %uint_53 + %110 = OpExtInst %void %1 DebugValue %60 %23 %49 %int_0 + %113 = OpExtInst %void %1 DebugNoLine + %114 = OpExtInst %void %1 DebugNoScope + OpStore %out_var_SV_Target0 %23 + %66 = OpExtInst %void %1 DebugLine %50 %uint_12 %uint_12 %uint_1 %uint_1 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} } // namespace } // namespace opt diff --git a/test/opt/assembly_builder.h b/test/opt/assembly_builder.h index 1673c092..b94e90f5 100644 --- a/test/opt/assembly_builder.h +++ b/test/opt/assembly_builder.h @@ -70,7 +70,7 @@ class AssemblyBuilder { static const uint32_t SPEC_ID_BASE = 200; public: - // Initalize a minimal SPIR-V assembly code as the template. The minimal + // Initialize a minimal SPIR-V assembly code as the template. The minimal // module contains an empty main function and some predefined names for the // main function. AssemblyBuilder() @@ -102,7 +102,7 @@ class AssemblyBuilder { }); } - // Appends OpName instructions to this builder. Instrcution strings that do + // Appends OpName instructions to this builder. Instruction strings that do // not start with 'OpName ' will be skipped. Returns the references of this // assembly builder. AssemblyBuilder& AppendNames(const std::vector& vec_asm_code) { diff --git a/test/opt/block_merge_test.cpp b/test/opt/block_merge_test.cpp index 140a5c09..9698fed2 100644 --- a/test/opt/block_merge_test.cpp +++ b/test/opt/block_merge_test.cpp @@ -90,6 +90,63 @@ OpFunctionEnd true); } +TEST_F(BlockMergeTest, BlockMergeForLinkage) { + const std::string before = + R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %bb_entry "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%8 = OpTypeFunction %v4float %_ptr_Function_v4float +%main = OpFunction %v4float None %8 +%BaseColor = OpFunctionParameter %_ptr_Function_v4float +%bb_entry = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%9 = OpLoad %v4float %BaseColor +OpStore %v %9 +OpBranch %10 +%10 = OpLabel +%11 = OpLoad %v4float %v +OpBranch %12 +%12 = OpLabel +OpReturnValue %11 +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %bb_entry "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%8 = OpTypeFunction %v4float %_ptr_Function_v4float +%main = OpFunction %v4float None %8 +%BaseColor = OpFunctionParameter %_ptr_Function_v4float +%bb_entry = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%9 = OpLoad %v4float %BaseColor +OpStore %v %9 +%11 = OpLoad %v4float %v +OpReturnValue %11 +OpFunctionEnd +)"; + SinglePassRunAndCheck(before, after, true, true); +} + TEST_F(BlockMergeTest, EmptyBlock) { // Note: SPIR-V hand edited to insert empty block // after two statements in main. @@ -827,7 +884,7 @@ TEST_F(BlockMergeTest, MergeHeaders) { ; CHECK-NEXT: [[header]] = OpLabel ; CHECK-NEXT: OpSelectionMerge [[merge:%\w+]] ; CHECK: [[merge]] = OpLabel -; CHEKC: OpReturn +; CHECK: OpReturn OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %func "func" @@ -1038,6 +1095,112 @@ OpFunctionEnd SinglePassRunAndCheck(spirv, spirv, true, true); } +TEST_F(BlockMergeTest, DebugMerge) { + // Verify merge can be done completely, cleanly and validly in presence of + // NonSemantic.Shader.DebugInfo.100 instructions + const std::string text = R"( +; CHECK: OpLoopMerge +; CHECK-NEXT: OpBranch +; CHECK-NOT: OpBranch +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET +OpExecutionMode %main OriginUpperLeft +%5 = OpString "lexblock.hlsl" +%20 = OpString "float" +%32 = OpString "main" +%33 = OpString "" +%46 = OpString "b" +%49 = OpString "a" +%58 = OpString "c" +%63 = OpString "color" +OpName %in_var_COLOR "in.var.COLOR" +OpName %out_var_SV_TARGET "out.var.SV_TARGET" +OpName %main "main" +OpDecorate %in_var_COLOR Location 0 +OpDecorate %out_var_SV_TARGET Location 0 +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%v4float = OpTypeVector %float 4 +%9 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%float_1 = OpConstant %float 1 +%13 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%uint_3 = OpConstant %uint 3 +%uint_0 = OpConstant %uint 0 +%uint_4 = OpConstant %uint 4 +%uint_1 = OpConstant %uint 1 +%uint_5 = OpConstant %uint 5 +%uint_12 = OpConstant %uint 12 +%uint_13 = OpConstant %uint 13 +%uint_20 = OpConstant %uint 20 +%uint_15 = OpConstant %uint 15 +%uint_17 = OpConstant %uint 17 +%uint_16 = OpConstant %uint 16 +%uint_14 = OpConstant %uint 14 +%uint_10 = OpConstant %uint 10 +%65 = OpTypeFunction %void +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output +%62 = OpExtInst %void %1 DebugExpression +%22 = OpExtInst %void %1 DebugTypeBasic %20 %uint_32 %uint_3 %uint_0 +%25 = OpExtInst %void %1 DebugTypeVector %22 %uint_4 +%27 = OpExtInst %void %1 DebugTypeFunction %uint_3 %25 %25 +%28 = OpExtInst %void %1 DebugSource %5 +%29 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %28 %uint_5 +%34 = OpExtInst %void %1 DebugFunction %32 %27 %28 %uint_12 %uint_1 %29 %33 %uint_3 %uint_13 +%37 = OpExtInst %void %1 DebugLexicalBlock %28 %uint_13 %uint_1 %34 +%52 = OpExtInst %void %1 DebugLexicalBlock %28 %uint_15 %uint_12 %37 +%54 = OpExtInst %void %1 DebugLocalVariable %46 %25 %28 %uint_17 %uint_12 %52 %uint_4 +%56 = OpExtInst %void %1 DebugLocalVariable %49 %25 %28 %uint_16 %uint_12 %52 %uint_4 +%59 = OpExtInst %void %1 DebugLocalVariable %58 %25 %28 %uint_14 %uint_10 %37 %uint_4 +%64 = OpExtInst %void %1 DebugLocalVariable %63 %25 %28 %uint_12 %uint_20 %34 %uint_4 %uint_1 +%main = OpFunction %void None %65 +%66 = OpLabel +%69 = OpLoad %v4float %in_var_COLOR +%168 = OpExtInst %void %1 DebugValue %64 %69 %62 +%169 = OpExtInst %void %1 DebugScope %37 +OpLine %5 14 10 +%164 = OpExtInst %void %1 DebugValue %59 %9 %62 +OpLine %5 15 3 +OpBranch %150 +%150 = OpLabel +%165 = OpPhi %v4float %9 %66 %158 %159 +%167 = OpExtInst %void %1 DebugValue %59 %165 %62 +%170 = OpExtInst %void %1 DebugScope %37 +OpLine %5 15 12 +%171 = OpExtInst %void %1 DebugNoScope +OpLoopMerge %160 %159 None +OpBranch %151 +%151 = OpLabel +OpLine %5 16 12 +%162 = OpExtInst %void %1 DebugValue %56 %9 %62 +OpLine %5 17 12 +%163 = OpExtInst %void %1 DebugValue %54 %13 %62 +OpLine %5 18 15 +%158 = OpFAdd %v4float %165 %13 +OpLine %5 18 5 +%166 = OpExtInst %void %1 DebugValue %59 %158 %62 +%172 = OpExtInst %void %1 DebugScope %37 +OpLine %5 19 3 +OpBranch %159 +%159 = OpLabel +OpLine %5 19 3 +OpBranch %150 +%160 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + // TODO(greg-lunarg): Add tests to verify handling of these cases: // // More complex control flow diff --git a/test/opt/ccp_test.cpp b/test/opt/ccp_test.cpp index ef734353..f0f24362 100644 --- a/test/opt/ccp_test.cpp +++ b/test/opt/ccp_test.cpp @@ -582,6 +582,35 @@ TEST_F(CCPTest, SkipSpecConstantInstrucitons) { EXPECT_EQ(std::get<1>(res), Pass::Status::SuccessWithoutChange); } +TEST_F(CCPTest, FoldConstantCompositeInstrucitonsWithSpecConst) { + const std::string spv_asm = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %4 = OpTypeFunction %void + %bool = OpTypeBool + %v3bool = OpTypeVector %bool 3 + %_struct_8 = OpTypeStruct %v3bool + %true = OpConstantTrue %bool +; CHECK: [[spec_const:%\w+]] = OpSpecConstantComposite %v3bool + %11 = OpSpecConstantComposite %v3bool %true %true %true + %12 = OpConstantComposite %_struct_8 %11 +; CHECK: OpFunction + %1 = OpFunction %void None %4 + %29 = OpLabel + %31 = OpCompositeExtract %v3bool %12 0 +; CHECK: OpCompositeExtract %bool [[spec_const]] 0 + %32 = OpCompositeExtract %bool %31 0 + OpReturn + OpFunctionEnd + )"; + + auto result = SinglePassRunAndMatch(spv_asm, true); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + TEST_F(CCPTest, UpdateSubsequentPhisToVarying) { const std::string text = R"( OpCapability Shader @@ -1208,6 +1237,112 @@ TEST_F(CCPTest, CCPNoChangeFailureWithUnfoldableInstr) { auto result = SinglePassRunAndMatch(text, true); EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); } + +TEST_F(CCPTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} + +// Test from https://github.com/KhronosGroup/SPIRV-Tools/issues/4462. +// The test was causing a lateral movement in the constant lattice, which was +// not being detected as varying by CCP. In this test, FClamp is evaluated +// twice. On the first evaluation, if computes FClamp(0.5, 0.5, -1) which +// returns -1. On the second evaluation, it computes FClamp(0.5, 0.5, VARYING) +// which returns 0.5. +// +// Both fold() computations are correct given the semantics of FClamp() but +// this causes a lateral transition in the constant lattice which was not being +// considered VARYING by CCP. +TEST_F(CCPTest, LateralLatticeTransition) { + const std::string text = R"(OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %outColor + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %gl_FragCoord "gl_FragCoord" + OpName %outColor "outColor" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %outColor Location 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %float = OpTypeFloat 32 + %float_0_5 = OpConstant %float 0.5 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %float_0 = OpConstant %float 0 + %bool = OpTypeBool + %float_n1 = OpConstant %float -1 + %float_1 = OpConstant %float 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %outColor = OpVariable %_ptr_Output_v4float Output + +; This constant is created during the first evaluation of the CompositeConstruct +; CHECK: [[new_constant:%\d+]] = OpConstantComposite %v4float %float_n1 %float_0_5 %float_0 %float_1 + + %main = OpFunction %void None %6 + %19 = OpLabel + %20 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %21 = OpLoad %float %20 + %22 = OpFOrdLessThan %bool %21 %float_0 + OpSelectionMerge %23 None + OpBranchConditional %22 %24 %25 + %24 = OpLabel + OpBranch %23 + %25 = OpLabel + OpBranch %26 + %26 = OpLabel + OpBranch %23 + %23 = OpLabel + %27 = OpPhi %float %float_n1 %24 %float_0_5 %26 + %28 = OpExtInst %float %1 FClamp %float_0_5 %float_0_5 %27 + + ; On first evaluation, the result from FClamp will return 0.5. + ; But on second evaluation, FClamp should return VARYING. Check + ; that CCP is not keeping the first result. + ; CHECK-NOT: %29 = OpCompositeConstruct %v4float %float_0_5 %float_0_5 %float_0 %float_1 + %29 = OpCompositeConstruct %v4float %28 %float_0_5 %float_0 %float_1 + + ; CHECK-NOT: OpCopyObject %v4float [[new_constant]] + %42 = OpCopyObject %v4float %29 + + ; CHECK-NOT: OpStore %outColor [[new_constant]] + OpStore %outColor %42 + + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndMatch(text, true); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/cfg_test.cpp b/test/opt/cfg_test.cpp index 2cfc9f37..7dfd2bc8 100644 --- a/test/opt/cfg_test.cpp +++ b/test/opt/cfg_test.cpp @@ -200,6 +200,125 @@ OpFunctionEnd ContainerEq(expected_result2))); } +TEST_F(CFGTest, SplitLoopHeaderForSingleBlockLoop) { + const std::string test = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %6 = OpTypeFunction %void + %2 = OpFunction %void None %6 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + %9 = OpPhi %uint %uint_0 %7 %9 %8 + OpLoopMerge %10 %8 None + OpBranch %8 + %10 = OpLabel + OpUnreachable + OpFunctionEnd +)"; + + const std::string expected_result = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%6 = OpTypeFunction %void +%2 = OpFunction %void None %6 +%7 = OpLabel +OpBranch %8 +%8 = OpLabel +OpBranch %11 +%11 = OpLabel +%9 = OpPhi %uint %9 %11 %uint_0 %8 +OpLoopMerge %10 %11 None +OpBranch %11 +%10 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, test, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + BasicBlock* loop_header = context->get_instr_block(8); + ASSERT_TRUE(loop_header->GetLoopMergeInst() != nullptr); + + CFG* cfg = context->cfg(); + cfg->SplitLoopHeader(loop_header); + + std::vector binary; + bool skip_nop = false; + context->module()->ToBinary(&binary, skip_nop); + + std::string optimized_asm; + SpirvTools tools(SPV_ENV_UNIVERSAL_1_1); + EXPECT_TRUE(tools.Disassemble(binary, &optimized_asm, + SpirvTools::kDefaultDisassembleOption)) + << "Disassembling failed for shader\n" + << std::endl; + + EXPECT_EQ(optimized_asm, expected_result); +} + +TEST_F(CFGTest, ComputeStructedOrderForLoop) { + const std::string test = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpName %main "main" +%bool = OpTypeBool +%true = OpConstantTrue %bool +%void = OpTypeVoid +%4 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%5 = OpConstant %uint 5 +%main = OpFunction %void None %4 +%8 = OpLabel +OpBranch %9 +%9 = OpLabel +OpLoopMerge %11 %10 None +OpBranchConditional %true %11 %10 +%10 = OpLabel +OpBranch %9 +%11 = OpLabel +OpBranch %12 +%12 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, test, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + CFG* cfg = context->cfg(); + Module* module = context->module(); + Function* function = &*module->begin(); + std::list order; + cfg->ComputeStructuredOrder(function, context->get_instr_block(9), + context->get_instr_block(11), &order); + + // Order should contain the loop header, the continue target, and the merge + // node. + std::list expected_result = {context->get_instr_block(9), + context->get_instr_block(10), + context->get_instr_block(11)}; + EXPECT_THAT(order, ContainerEq(expected_result)); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/code_sink_test.cpp b/test/opt/code_sink_test.cpp index f1bd1275..bf5029b6 100644 --- a/test/opt/code_sink_test.cpp +++ b/test/opt/code_sink_test.cpp @@ -378,7 +378,7 @@ TEST_F(CodeSinkTest, MoveReadOnlyLoadWithSync) { %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %uint_4 = OpConstant %uint 4 -%mem_semantics = OpConstant %uint 0x42 ; Uniform memeory arquire +%mem_semantics = OpConstant %uint 0x42 ; Uniform memory arquire %_arr_uint_uint_4 = OpTypeArray %uint %uint_4 %_ptr_Uniform_uint = OpTypePointer Uniform %uint %_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4 @@ -419,7 +419,7 @@ TEST_F(CodeSinkTest, DontMoveBecauseOfSync) { %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %uint_4 = OpConstant %uint 4 -%mem_semantics = OpConstant %uint 0x42 ; Uniform memeory arquire +%mem_semantics = OpConstant %uint 0x42 ; Uniform memory arquire %_arr_uint_uint_4 = OpTypeStruct %uint %_ptr_Uniform_uint = OpTypePointer Uniform %uint %_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4 @@ -460,7 +460,7 @@ TEST_F(CodeSinkTest, DontMoveBecauseOfAtomicWithSync) { %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %uint_4 = OpConstant %uint 4 -%mem_semantics = OpConstant %uint 0x42 ; Uniform memeory arquire +%mem_semantics = OpConstant %uint 0x42 ; Uniform memory arquire %_arr_uint_uint_4 = OpTypeStruct %uint %_ptr_Uniform_uint = OpTypePointer Uniform %uint %_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4 diff --git a/test/opt/combine_access_chains_test.cpp b/test/opt/combine_access_chains_test.cpp index aed14c9d..5be3ba63 100644 --- a/test/opt/combine_access_chains_test.cpp +++ b/test/opt/combine_access_chains_test.cpp @@ -768,6 +768,32 @@ OpFunctionEnd SinglePassRunAndMatch(text, true); } +TEST_F(CombineAccessChainsTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/compact_ids_test.cpp b/test/opt/compact_ids_test.cpp index ba31d84b..7c232fe4 100644 --- a/test/opt/compact_ids_test.cpp +++ b/test/opt/compact_ids_test.cpp @@ -310,6 +310,41 @@ OpFunctionEnd EXPECT_THAT(disassembly, ::testing::Eq(expected)); } +TEST(CompactIds, ResetIdBound) { + const std::string input(R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +%void = OpTypeVoid +%3 = OpTypeFunction %void +%1 = OpFunction %void None %3 +%4 = OpLabel +OpReturn +OpFunctionEnd +)"); + + spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1); + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, input, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(context, nullptr); + + CompactIdsPass compact_id_pass; + context->module()->SetIdBound(20000); + const auto status = compact_id_pass.Run(context.get()); + EXPECT_EQ(status, Pass::Status::SuccessWithChange); + EXPECT_EQ(context->module()->id_bound(), 5); + + // Test output just in case + std::vector binary; + context->module()->ToBinary(&binary, false); + std::string disassembly; + tools.Disassemble(binary, &disassembly, + SpirvTools::kDefaultDisassembleOption); + + EXPECT_THAT(disassembly, ::testing::Eq(input)); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/control_dependence.cpp b/test/opt/control_dependence.cpp new file mode 100644 index 00000000..46655472 --- /dev/null +++ b/test/opt/control_dependence.cpp @@ -0,0 +1,306 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/control_dependence.h" + +#include +#include + +#include "gmock/gmock-matchers.h" +#include "gtest/gtest.h" +#include "source/opt/build_module.h" +#include "source/opt/cfg.h" +#include "test/opt/function_utils.h" + +namespace spvtools { +namespace opt { + +namespace { +void GatherEdges(const ControlDependenceAnalysis& cdg, + std::vector& ret) { + cdg.ForEachBlockLabel([&](uint32_t label) { + ret.reserve(ret.size() + cdg.GetDependenceTargets(label).size()); + ret.insert(ret.end(), cdg.GetDependenceTargets(label).begin(), + cdg.GetDependenceTargets(label).end()); + }); + std::sort(ret.begin(), ret.end()); + // Verify that reverse graph is the same. + std::vector reverse_edges; + reverse_edges.reserve(ret.size()); + cdg.ForEachBlockLabel([&](uint32_t label) { + reverse_edges.insert(reverse_edges.end(), + cdg.GetDependenceSources(label).begin(), + cdg.GetDependenceSources(label).end()); + }); + std::sort(reverse_edges.begin(), reverse_edges.end()); + ASSERT_THAT(reverse_edges, testing::ElementsAreArray(ret)); +} + +using ControlDependenceTest = ::testing::Test; + +TEST(ControlDependenceTest, DependenceSimpleCFG) { + const std::string text = R"( + OpCapability Addresses + OpCapability Kernel + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %1 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeBool + %5 = OpTypeInt 32 0 + %6 = OpConstant %5 0 + %7 = OpConstantFalse %4 + %8 = OpConstantTrue %4 + %9 = OpConstant %5 1 + %1 = OpFunction %2 None %3 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpSwitch %6 %12 1 %13 + %12 = OpLabel + OpBranch %14 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + OpBranchConditional %8 %15 %16 + %15 = OpLabel + OpBranch %19 + %16 = OpLabel + OpBranchConditional %8 %17 %18 + %17 = OpLabel + OpBranch %18 + %18 = OpLabel + OpBranch %19 + %19 = OpLabel + OpReturn + OpFunctionEnd +)"; + + // CFG: (all edges pointing downward) + // %10 + // | + // %11 + // / \ (R: %6 == 1, L: default) + // %12 %13 + // \ / + // %14 + // T/ \F + // %15 %16 + // | T/ |F + // | %17| + // | \ | + // | %18 + // | / + // %19 + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* fn = spvtest::GetFunction(module, 1); + const BasicBlock* entry = spvtest::GetBasicBlock(fn, 10); + EXPECT_EQ(entry, fn->entry().get()) + << "The entry node is not the expected one"; + + { + PostDominatorAnalysis pdom; + const CFG& cfg = *context->cfg(); + pdom.InitializeTree(cfg, fn); + ControlDependenceAnalysis cdg; + cdg.ComputeControlDependenceGraph(cfg, pdom); + + // Test HasBlock. + for (uint32_t id = 10; id <= 19; id++) { + EXPECT_TRUE(cdg.HasBlock(id)); + } + EXPECT_TRUE(cdg.HasBlock(ControlDependenceAnalysis::kPseudoEntryBlock)); + // Check blocks before/after valid range. + EXPECT_FALSE(cdg.HasBlock(5)); + EXPECT_FALSE(cdg.HasBlock(25)); + EXPECT_FALSE(cdg.HasBlock(UINT32_MAX)); + + // Test ForEachBlockLabel. + std::set block_labels; + cdg.ForEachBlockLabel([&block_labels](uint32_t id) { + bool inserted = block_labels.insert(id).second; + EXPECT_TRUE(inserted); // Should have no duplicates. + }); + EXPECT_THAT(block_labels, testing::ElementsAre(0, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19)); + + { + // Test WhileEachBlockLabel. + uint32_t iters = 0; + EXPECT_TRUE(cdg.WhileEachBlockLabel([&iters](uint32_t) { + ++iters; + return true; + })); + EXPECT_EQ((uint32_t)block_labels.size(), iters); + iters = 0; + EXPECT_FALSE(cdg.WhileEachBlockLabel([&iters](uint32_t) { + ++iters; + return false; + })); + EXPECT_EQ(1, iters); + } + + // Test IsDependent. + EXPECT_TRUE(cdg.IsDependent(12, 11)); + EXPECT_TRUE(cdg.IsDependent(13, 11)); + EXPECT_TRUE(cdg.IsDependent(15, 14)); + EXPECT_TRUE(cdg.IsDependent(16, 14)); + EXPECT_TRUE(cdg.IsDependent(18, 14)); + EXPECT_TRUE(cdg.IsDependent(17, 16)); + EXPECT_TRUE(cdg.IsDependent(10, 0)); + EXPECT_TRUE(cdg.IsDependent(11, 0)); + EXPECT_TRUE(cdg.IsDependent(14, 0)); + EXPECT_TRUE(cdg.IsDependent(19, 0)); + EXPECT_FALSE(cdg.IsDependent(14, 11)); + EXPECT_FALSE(cdg.IsDependent(17, 14)); + EXPECT_FALSE(cdg.IsDependent(19, 14)); + EXPECT_FALSE(cdg.IsDependent(12, 0)); + + // Test GetDependenceSources/Targets. + std::vector edges; + GatherEdges(cdg, edges); + EXPECT_THAT(edges, + testing::ElementsAre( + ControlDependence(0, 10), ControlDependence(0, 11, 10), + ControlDependence(0, 14, 10), ControlDependence(0, 19, 10), + ControlDependence(11, 12), ControlDependence(11, 13), + ControlDependence(14, 15), ControlDependence(14, 16), + ControlDependence(14, 18, 16), ControlDependence(16, 17))); + + const uint32_t expected_condition_ids[] = { + 0, 0, 0, 0, 6, 6, 8, 8, 8, 8, + }; + + for (uint32_t i = 0; i < edges.size(); i++) { + EXPECT_EQ(expected_condition_ids[i], edges[i].GetConditionID(cfg)); + } + } +} + +TEST(ControlDependenceTest, DependencePaperCFG) { + const std::string text = R"( + OpCapability Addresses + OpCapability Kernel + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %101 "main" + %102 = OpTypeVoid + %103 = OpTypeFunction %102 + %104 = OpTypeBool + %108 = OpConstantTrue %104 + %101 = OpFunction %102 None %103 + %1 = OpLabel + OpBranch %2 + %2 = OpLabel + OpBranchConditional %108 %3 %7 + %3 = OpLabel + OpBranchConditional %108 %4 %5 + %4 = OpLabel + OpBranch %6 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpBranch %8 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpBranchConditional %108 %10 %11 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpBranchConditional %108 %12 %9 + %12 = OpLabel + OpBranchConditional %108 %13 %2 + %13 = OpLabel + OpReturn + OpFunctionEnd +)"; + + // CFG: (edges pointing downward if no arrow) + // %1 + // | + // %2 <----+ + // T/ \F | + // %3 \ | + // T/ \F \ | + // %4 %5 %7 | + // \ / / | + // %6 / | + // \ / | + // %8 | + // | | + // %9 <-+ | + // T/ | | | + // %10 | | | + // \ | | | + // %11-F+ | + // T| | + // %12-F---+ + // T| + // %13 + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* fn = spvtest::GetFunction(module, 101); + const BasicBlock* entry = spvtest::GetBasicBlock(fn, 1); + EXPECT_EQ(entry, fn->entry().get()) + << "The entry node is not the expected one"; + + { + PostDominatorAnalysis pdom; + const CFG& cfg = *context->cfg(); + pdom.InitializeTree(cfg, fn); + ControlDependenceAnalysis cdg; + cdg.ComputeControlDependenceGraph(cfg, pdom); + + std::vector edges; + GatherEdges(cdg, edges); + EXPECT_THAT( + edges, testing::ElementsAre( + ControlDependence(0, 1), ControlDependence(0, 2, 1), + ControlDependence(0, 8, 1), ControlDependence(0, 9, 1), + ControlDependence(0, 11, 1), ControlDependence(0, 12, 1), + ControlDependence(0, 13, 1), ControlDependence(2, 3), + ControlDependence(2, 6, 3), ControlDependence(2, 7), + ControlDependence(3, 4), ControlDependence(3, 5), + ControlDependence(9, 10), ControlDependence(11, 9), + ControlDependence(11, 11, 9), ControlDependence(12, 2), + ControlDependence(12, 8, 2), ControlDependence(12, 9, 2), + ControlDependence(12, 11, 2), ControlDependence(12, 12, 2))); + + const uint32_t expected_condition_ids[] = { + 0, 0, 0, 0, 0, 0, 0, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + }; + + for (uint32_t i = 0; i < edges.size(); i++) { + EXPECT_EQ(expected_condition_ids[i], edges[i].GetConditionID(cfg)); + } + } +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/test/opt/convert_relaxed_to_half_test.cpp b/test/opt/convert_relaxed_to_half_test.cpp index ca6ee583..6a06de84 100644 --- a/test/opt/convert_relaxed_to_half_test.cpp +++ b/test/opt/convert_relaxed_to_half_test.cpp @@ -204,6 +204,98 @@ OpFunctionEnd defs_after + func_after, true, true); } +TEST_F(ConvertToHalfTest, ConvertToHalfForLinkage) { + const std::string before = + R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %type_cbuff "type.cbuff" +OpMemberName %type_cbuff 0 "c" +OpName %cbuff "cbuff" +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %bb_entry "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +OpDecorate %cbuff DescriptorSet 0 +OpDecorate %cbuff Binding 0 +OpMemberDecorate %type_cbuff 0 Offset 0 +OpDecorate %type_cbuff Block +OpDecorate %18 RelaxedPrecision +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%type_cbuff = OpTypeStruct %float +%_ptr_Uniform_type_cbuff = OpTypePointer Uniform %type_cbuff +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%9 = OpTypeFunction %v4float %_ptr_Function_v4float +%_ptr_Uniform_float = OpTypePointer Uniform %float +%cbuff = OpVariable %_ptr_Uniform_type_cbuff Uniform +%main = OpFunction %v4float None %9 +%BaseColor = OpFunctionParameter %_ptr_Function_v4float +%bb_entry = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%14 = OpLoad %v4float %BaseColor +%16 = OpAccessChain %_ptr_Uniform_float %cbuff %int_0 +%17 = OpLoad %float %16 +%18 = OpVectorTimesScalar %v4float %14 %17 +OpStore %v %18 +%19 = OpLoad %v4float %v +OpReturnValue %19 +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +OpCapability Linkage +OpCapability Float16 +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %type_cbuff "type.cbuff" +OpMemberName %type_cbuff 0 "c" +OpName %cbuff "cbuff" +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %bb_entry "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +OpDecorate %cbuff DescriptorSet 0 +OpDecorate %cbuff Binding 0 +OpMemberDecorate %type_cbuff 0 Offset 0 +OpDecorate %type_cbuff Block +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%type_cbuff = OpTypeStruct %float +%_ptr_Uniform_type_cbuff = OpTypePointer Uniform %type_cbuff +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%14 = OpTypeFunction %v4float %_ptr_Function_v4float +%_ptr_Uniform_float = OpTypePointer Uniform %float +%cbuff = OpVariable %_ptr_Uniform_type_cbuff Uniform +%half = OpTypeFloat 16 +%v4half = OpTypeVector %half 4 +%main = OpFunction %v4float None %14 +%BaseColor = OpFunctionParameter %_ptr_Function_v4float +%bb_entry = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%16 = OpLoad %v4float %BaseColor +%17 = OpAccessChain %_ptr_Uniform_float %cbuff %int_0 +%18 = OpLoad %float %17 +%22 = OpFConvert %v4half %16 +%23 = OpFConvert %half %18 +%7 = OpVectorTimesScalar %v4half %22 %23 +%24 = OpFConvert %v4float %7 +OpStore %v %24 +%19 = OpLoad %v4float %v +OpReturnValue %19 +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before, after, true, true); +} TEST_F(ConvertToHalfTest, ConvertToHalfWithDrefSample) { // The resulting SPIR-V was processed with --relax-float-ops. // @@ -1397,6 +1489,87 @@ TEST_F(ConvertToHalfTest, RemoveRelaxDec) { EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); } +TEST_F(ConvertToHalfTest, HandleNonRelaxedPhi) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/4452 + + // This test is a case with a non-relaxed phi with a relaxed operand. + // A convert must be inserted at the end of the block associated with + // the operand. + const std::string test = + R"( +; CHECK: [[fcvt:%\w+]] = OpFConvert %v3float {{%\w+}} +; CHECK-NEXT: OpSelectionMerge {{%\w+}} None +; CHECK: {{%\w+}} = OpPhi %v3float [[fcvt]] {{%\w+}} {{%\w+}} {{%\w+}} + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %output_color + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %MaterialParams "MaterialParams" + OpMemberName %MaterialParams 0 "foo" + OpName %materialParams "materialParams" + OpName %output_color "output_color" + OpMemberDecorate %MaterialParams 0 Offset 0 + OpDecorate %MaterialParams Block + OpDecorate %materialParams DescriptorSet 0 + OpDecorate %materialParams Binding 5 + OpDecorate %output_color Location 0 + OpDecorate %57 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 +%MaterialParams = OpTypeStruct %float +%_ptr_Uniform_MaterialParams = OpTypePointer Uniform %MaterialParams +%materialParams = OpVariable %_ptr_Uniform_MaterialParams Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %float_0 = OpConstant %float 0 + %bool = OpTypeBool + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%output_color = OpVariable %_ptr_Output_v4float Output + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Output_float = OpTypePointer Output %float + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %float_0_5 = OpConstant %float 0.5 + %61 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5 + %main = OpFunction %void None %3 + %5 = OpLabel + %55 = OpAccessChain %_ptr_Uniform_float %materialParams %int_0 + %56 = OpLoad %float %55 + %57 = OpCompositeConstruct %v3float %56 %56 %56 + %31 = OpFOrdGreaterThan %bool %56 %float_0 + OpSelectionMerge %33 None + OpBranchConditional %31 %32 %33 + %32 = OpLabel + %37 = OpFMul %v3float %57 %61 + OpBranch %33 + %33 = OpLabel + %58 = OpPhi %v3float %57 %5 %37 %32 + %45 = OpAccessChain %_ptr_Output_float %output_color %uint_0 + %46 = OpCompositeExtract %float %58 0 + OpStore %45 %46 + %48 = OpAccessChain %_ptr_Output_float %output_color %uint_1 + %49 = OpCompositeExtract %float %58 1 + OpStore %48 %49 + %51 = OpAccessChain %_ptr_Output_float %output_color %uint_2 + %52 = OpCompositeExtract %float %58 2 + OpStore %51 %52 + OpReturn + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto result = SinglePassRunAndMatch(test, true); + EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/convert_to_sampled_image_test.cpp b/test/opt/convert_to_sampled_image_test.cpp new file mode 100644 index 00000000..37f65601 --- /dev/null +++ b/test/opt/convert_to_sampled_image_test.cpp @@ -0,0 +1,353 @@ +// Copyright (c) 2021 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 + +#include "gmock/gmock.h" +#include "source/opt/convert_to_sampled_image_pass.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using testing::Eq; +using VectorOfDescriptorSetAndBindingPairs = + std::vector; + +struct DescriptorSetAndBindingStringParsingTestCase { + const char* descriptor_set_binding_str; + bool expect_success; + VectorOfDescriptorSetAndBindingPairs expected_descriptor_set_binding_pairs; +}; + +using DescriptorSetAndBindingStringParsingTest = + ::testing::TestWithParam; + +TEST_P(DescriptorSetAndBindingStringParsingTest, TestCase) { + const auto& tc = GetParam(); + auto actual_descriptor_set_binding_pairs = + ConvertToSampledImagePass::ParseDescriptorSetBindingPairsString( + tc.descriptor_set_binding_str); + if (tc.expect_success) { + EXPECT_NE(nullptr, actual_descriptor_set_binding_pairs); + if (actual_descriptor_set_binding_pairs) { + EXPECT_THAT(*actual_descriptor_set_binding_pairs, + Eq(tc.expected_descriptor_set_binding_pairs)); + } + } else { + EXPECT_EQ(nullptr, actual_descriptor_set_binding_pairs); + } +} + +INSTANTIATE_TEST_SUITE_P( + ValidString, DescriptorSetAndBindingStringParsingTest, + ::testing::ValuesIn(std::vector< + DescriptorSetAndBindingStringParsingTestCase>{ + // 0. empty vector + {"", true, VectorOfDescriptorSetAndBindingPairs({})}, + // 1. one pair + {"100:1024", true, + VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{100, + 1024}})}, + // 2. two pairs + {"100:1024 200:2048", true, + VectorOfDescriptorSetAndBindingPairs( + {DescriptorSetAndBinding{100, 1024}, + DescriptorSetAndBinding{200, 2048}})}, + // 3. spaces between entries + {"100:1024 \n \r \t \v \f 200:2048", true, + VectorOfDescriptorSetAndBindingPairs( + {DescriptorSetAndBinding{100, 1024}, + DescriptorSetAndBinding{200, 2048}})}, + // 4. \t, \n, \r and spaces before spec id + {" \n \r\t \t \v \f 100:1024", true, + VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{100, + 1024}})}, + // 5. \t, \n, \r and spaces after value string + {"100:1024 \n \r\t \t \v \f ", true, + VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{100, + 1024}})}, + // 6. maximum spec id + {"4294967295:0", true, + VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{ + 4294967295, 0}})}, + // 7. minimum spec id + {"0:100", true, + VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{0, + 100}})}, + // 8. multiple entries + {"101:1 102:2 103:3 104:4 200:201 9999:1000", true, + VectorOfDescriptorSetAndBindingPairs( + {DescriptorSetAndBinding{101, 1}, DescriptorSetAndBinding{102, 2}, + DescriptorSetAndBinding{103, 3}, DescriptorSetAndBinding{104, 4}, + DescriptorSetAndBinding{200, 201}, + DescriptorSetAndBinding{9999, 1000}})}, + })); + +INSTANTIATE_TEST_SUITE_P( + InvalidString, DescriptorSetAndBindingStringParsingTest, + ::testing::ValuesIn( + std::vector{ + // 0. missing default value + {"100:", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 1. descriptor set is not an integer + {"100.0:200", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 2. descriptor set is not a number + {"something_not_a_number:1", false, + VectorOfDescriptorSetAndBindingPairs{}}, + // 3. only descriptor set number + {"100", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 4. empty descriptor set + {":3", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 5. only colon + {":", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 6. descriptor set overflow + {"4294967296:200", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 7. descriptor set less than 0 + {"-1:200", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 8. nullptr + {nullptr, false, VectorOfDescriptorSetAndBindingPairs{}}, + // 9. only a number is invalid + {"1234", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 10. invalid entry separator + {"12:34;23:14", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 11. invalid descriptor set and default value separator + {"12@34", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 12. spaces before colon + {"100 :1024", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 13. spaces after colon + {"100: 1024", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 14. descriptor set represented in hex float format is invalid + {"0x3p10:200", false, VectorOfDescriptorSetAndBindingPairs{}}, + })); + +std::string BuildShader(const char* shader_decorate_instructions, + const char* shader_image_and_sampler_variables, + const char* shader_body) { + // Base HLSL code: + // + // SamplerState sam : register(s2); + // Texture2D texture : register(t5); + // + // float4 main() : SV_TARGET { + // return texture.SampleLevel(sam, float2(1, 2), 10, 2); + // } + std::stringstream ss; + ss << R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_TARGET + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_sampler "type.sampler" + OpName %type_2d_image "type.2d.image" + 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 + )"; + ss << shader_decorate_instructions; + ss << R"( + %float = OpTypeFloat 32 + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %v2float = OpTypeVector %float 2 + %12 = OpConstantComposite %v2float %float_1 %float_2 + %float_10 = OpConstant %float 10 + %int = OpTypeInt 32 1 + %int_2 = OpConstant %int 2 + %v2int = OpTypeVector %int 2 + %17 = OpConstantComposite %v2int %int_2 %int_2 +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %23 = OpTypeFunction %void +%type_sampled_image = OpTypeSampledImage %type_2d_image + )"; + ss << shader_image_and_sampler_variables; + ss << R"( +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %23 + %24 = OpLabel + )"; + ss << shader_body; + ss << R"( + OpReturn + OpFunctionEnd + )"; + return ss.str(); +} + +using ConvertToSampledImageTest = PassTest<::testing::Test>; + +TEST_F(ConvertToSampledImageTest, Texture2DAndSamplerToSampledImage) { + const std::string shader = BuildShader( + R"( + OpDecorate %sam DescriptorSet 0 + OpDecorate %sam Binding 5 + OpDecorate %texture DescriptorSet 0 + OpDecorate %texture Binding 5 + )", + R"( + ; CHECK-NOT: OpVariable %_ptr_UniformConstant_type_2d_image + + ; CHECK: [[tex:%\w+]] = OpVariable %_ptr_UniformConstant_type_sampled_image UniformConstant + %sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + )", + R"( + ; CHECK: [[load:%\w+]] = OpLoad %type_sampled_image [[tex]] + ; CHECK: OpImageSampleExplicitLod %v4float [[load]] + %25 = OpLoad %type_2d_image %texture + %26 = OpLoad %type_sampler %sam + %27 = OpSampledImage %type_sampled_image %25 %26 + %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17 + OpStore %out_var_SV_TARGET %28 + )"); + + auto result = SinglePassRunAndMatch( + shader, /* do_validate = */ true, + VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 5}}); + + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(ConvertToSampledImageTest, Texture2DToSampledImage) { + const std::string shader = BuildShader( + R"( + OpDecorate %sam DescriptorSet 0 + OpDecorate %sam Binding 2 + OpDecorate %texture DescriptorSet 0 + OpDecorate %texture Binding 5 + )", + R"( + ; CHECK: [[tex:%\w+]] = OpVariable %_ptr_UniformConstant_type_sampled_image UniformConstant + %sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + )", + R"( + ; CHECK: [[load:%\w+]] = OpLoad %type_sampled_image [[tex]] + ; CHECK: [[image_extraction:%\w+]] = OpImage %type_2d_image [[load]] + ; CHECK: OpSampledImage %type_sampled_image [[image_extraction]] + %25 = OpLoad %type_2d_image %texture + %26 = OpLoad %type_sampler %sam + %27 = OpSampledImage %type_sampled_image %25 %26 + %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17 + OpStore %out_var_SV_TARGET %28 + )"); + + auto result = SinglePassRunAndMatch( + shader, /* do_validate = */ true, + VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 5}}); + + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(ConvertToSampledImageTest, SamplerToSampledImage) { + const std::string shader = BuildShader( + R"( + OpDecorate %sam DescriptorSet 0 + OpDecorate %sam Binding 2 + OpDecorate %texture DescriptorSet 0 + OpDecorate %texture Binding 5 + )", + R"( + %sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + )", + R"( + %25 = OpLoad %type_2d_image %texture + %26 = OpLoad %type_sampler %sam + %27 = OpSampledImage %type_sampled_image %25 %26 + %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17 + OpStore %out_var_SV_TARGET %28 + )"); + + auto result = SinglePassRunToBinary( + shader, /* skip_nop = */ false, + VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 2}}); + + EXPECT_EQ(std::get<1>(result), Pass::Status::Failure); +} + +TEST_F(ConvertToSampledImageTest, TwoImagesWithDuplicatedDescriptorSetBinding) { + const std::string shader = BuildShader( + R"( + OpDecorate %sam DescriptorSet 0 + OpDecorate %sam Binding 2 + OpDecorate %texture0 DescriptorSet 0 + OpDecorate %texture0 Binding 5 + OpDecorate %texture1 DescriptorSet 0 + OpDecorate %texture1 Binding 5 + )", + R"( + %sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %texture0 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + %texture1 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + )", + R"( + %25 = OpLoad %type_2d_image %texture0 + %26 = OpLoad %type_sampler %sam + %27 = OpSampledImage %type_sampled_image %25 %26 + %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17 + OpStore %out_var_SV_TARGET %28 + )"); + + auto result = SinglePassRunToBinary( + shader, /* skip_nop = */ false, + VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 5}}); + + EXPECT_EQ(std::get<1>(result), Pass::Status::Failure); +} + +TEST_F(ConvertToSampledImageTest, + TwoSamplersWithDuplicatedDescriptorSetBinding) { + const std::string shader = BuildShader( + R"( + OpDecorate %sam0 DescriptorSet 0 + OpDecorate %sam0 Binding 2 + OpDecorate %sam1 DescriptorSet 0 + OpDecorate %sam1 Binding 2 + OpDecorate %texture DescriptorSet 0 + OpDecorate %texture Binding 5 + )", + R"( + %sam0 = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %sam1 = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + )", + R"( + %25 = OpLoad %type_2d_image %texture + %26 = OpLoad %type_sampler %sam0 + %27 = OpSampledImage %type_sampled_image %25 %26 + %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17 + OpStore %out_var_SV_TARGET %28 + )"); + + auto result = SinglePassRunToBinary( + shader, /* skip_nop = */ false, + VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 2}}); + + EXPECT_EQ(std::get<1>(result), Pass::Status::Failure); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/test/opt/copy_prop_array_test.cpp b/test/opt/copy_prop_array_test.cpp index 72bc7f6f..d6e376ec 100644 --- a/test/opt/copy_prop_array_test.cpp +++ b/test/opt/copy_prop_array_test.cpp @@ -1814,6 +1814,135 @@ OpFunctionEnd SinglePassRunAndMatch(before, false); } +TEST_F(CopyPropArrayPassTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} + +// Since Spir-V 1.4, resources that are used by a shader must be on the +// OpEntryPoint instruction with the inputs and outputs. This test ensures that +// this does not stop the pass from working. +TEST_F(CopyPropArrayPassTest, EntryPointUser) { + const std::string before = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %g_rwTexture3d +OpExecutionMode %main LocalSize 256 1 1 +OpSource HLSL 660 +OpName %type_3d_image "type.3d.image" +OpName %g_rwTexture3d "g_rwTexture3d" +OpName %main "main" +OpDecorate %g_rwTexture3d DescriptorSet 0 +OpDecorate %g_rwTexture3d Binding 0 +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%v3uint = OpTypeVector %uint 3 +%10 = OpConstantComposite %v3uint %uint_1 %uint_2 %uint_3 +%type_3d_image = OpTypeImage %uint 3D 2 0 0 2 R32ui +%_ptr_UniformConstant_type_3d_image = OpTypePointer UniformConstant %type_3d_image +%void = OpTypeVoid +%13 = OpTypeFunction %void +%_ptr_Function_type_3d_image = OpTypePointer Function %type_3d_image +%_ptr_Image_uint = OpTypePointer Image %uint +%g_rwTexture3d = OpVariable %_ptr_UniformConstant_type_3d_image UniformConstant +%main = OpFunction %void None %13 +%16 = OpLabel +%17 = OpVariable %_ptr_Function_type_3d_image Function +%18 = OpLoad %type_3d_image %g_rwTexture3d +OpStore %17 %18 +; CHECK: %19 = OpImageTexelPointer %_ptr_Image_uint %g_rwTexture3d %10 %uint_0 +%19 = OpImageTexelPointer %_ptr_Image_uint %17 %10 %uint_0 +%20 = OpAtomicIAdd %uint %19 %uint_1 %uint_0 %uint_1 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SinglePassRunAndMatch(before, false); +} + +// As per SPIRV spec, struct cannot be indexed with non-constant indices +// through OpAccessChain, only arrays. +// The copy-propagate-array pass tries to remove superfluous copies when the +// original array could be indexed instead of the copy. +// +// This test verifies we handle this case: +// struct SRC { int field1; ...; int fieldN } +// int tmp_arr[N] = { SRC.field1, ..., SRC.fieldN } +// return tmp_arr[index]; +// +// In such case, we cannot optimize the access: this array was added to allow +// dynamic indexing in the struct. +TEST_F(CopyPropArrayPassTest, StructIndexCannotBecomeDynamic) { + const std::string text = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %1 "main" +OpDecorate %2 DescriptorSet 0 +OpDecorate %2 Binding 0 +OpMemberDecorate %_struct_3 0 Offset 0 +OpDecorate %_struct_3 Block +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_struct_3 = OpTypeStruct %v4float +%_ptr_Uniform__struct_3 = OpTypePointer Uniform %_struct_3 +%uint = OpTypeInt 32 0 +%void = OpTypeVoid +%11 = OpTypeFunction %void +%_ptr_Function_uint = OpTypePointer Function %uint +%13 = OpTypeFunction %v4float %_ptr_Function_uint +%uint_1 = OpConstant %uint 1 +%_arr_v4float_uint_1 = OpTypeArray %v4float %uint_1 +%_ptr_Function__arr_v4float_uint_1 = OpTypePointer Function %_arr_v4float_uint_1 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%2 = OpVariable %_ptr_Uniform__struct_3 Uniform +%19 = OpUndef %v4float +%1 = OpFunction %void None %11 +%20 = OpLabel +OpReturn +OpFunctionEnd +%21 = OpFunction %v4float None %13 +%22 = OpFunctionParameter %_ptr_Function_uint +%23 = OpLabel +%24 = OpVariable %_ptr_Function__arr_v4float_uint_1 Function +%25 = OpAccessChain %_ptr_Uniform_v4float %2 %int_0 +%26 = OpLoad %v4float %25 +%27 = OpCompositeConstruct %_arr_v4float_uint_1 %26 +OpStore %24 %27 +%28 = OpLoad %uint %22 +%29 = OpAccessChain %_ptr_Function_v4float %24 %28 +OpReturnValue %19 +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/dataflow.cpp b/test/opt/dataflow.cpp new file mode 100644 index 00000000..4742015a --- /dev/null +++ b/test/opt/dataflow.cpp @@ -0,0 +1,225 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/dataflow.h" + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "opt/function_utils.h" +#include "source/opt/build_module.h" + +namespace spvtools { +namespace opt { +namespace { + +using DataFlowTest = ::testing::Test; + +// Simple analyses for testing: + +// Stores the result IDs of visited instructions in visit order. +struct VisitOrder : public ForwardDataFlowAnalysis { + std::vector visited_result_ids; + + VisitOrder(IRContext& context, LabelPosition label_position) + : ForwardDataFlowAnalysis(context, label_position) {} + + VisitResult Visit(Instruction* inst) override { + if (inst->HasResultId()) { + visited_result_ids.push_back(inst->result_id()); + } + return DataFlowAnalysis::VisitResult::kResultFixed; + } +}; + +// For each block, stores the set of blocks it can be preceded by. +// For example, with the following CFG: +// V-----------. +// -> 11 -> 12 -> 13 -> 15 +// \-> 14 ---^ +// +// The answer is: +// 11: 11, 12, 13 +// 12: 11, 12, 13 +// 13: 11, 12, 13 +// 14: 11, 12, 13 +// 15: 11, 12, 13, 14 +struct BackwardReachability : public ForwardDataFlowAnalysis { + std::map> reachable_from; + + BackwardReachability(IRContext& context) + : ForwardDataFlowAnalysis( + context, ForwardDataFlowAnalysis::LabelPosition::kLabelsOnly) {} + + VisitResult Visit(Instruction* inst) override { + // Conditional branches can be enqueued from labels, so skip them. + if (inst->opcode() != SpvOpLabel) + return DataFlowAnalysis::VisitResult::kResultFixed; + uint32_t id = inst->result_id(); + VisitResult ret = DataFlowAnalysis::VisitResult::kResultFixed; + std::set& precedents = reachable_from[id]; + for (uint32_t pred : context().cfg()->preds(id)) { + bool pred_inserted = precedents.insert(pred).second; + if (pred_inserted) { + ret = DataFlowAnalysis::VisitResult::kResultChanged; + } + for (uint32_t block : reachable_from[pred]) { + bool inserted = precedents.insert(block).second; + if (inserted) { + ret = DataFlowAnalysis::VisitResult::kResultChanged; + } + } + } + return ret; + } + + void InitializeWorklist(Function* function, + bool is_first_iteration) override { + // Since successor function is exact, only need one pass. + if (is_first_iteration) { + ForwardDataFlowAnalysis::InitializeWorklist(function, true); + } + } +}; + +TEST_F(DataFlowTest, ReversePostOrder) { + // Note: labels and IDs are intentionally out of order. + // + // CFG: (order of branches is from bottom to top) + // V-----------. + // -> 50 -> 40 -> 20 -> 60 -> 70 + // \-> 30 ---^ + + // DFS tree with RPO numbering: + // -> 50[0] -> 40[1] -> 20[2] 60[4] -> 70[5] + // \-> 30[3] ---^ + + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %6 = OpTypeBool + %5 = OpConstantTrue %6 + %2 = OpFunction %3 None %4 + %50 = OpLabel + %51 = OpUndef %6 + %52 = OpUndef %6 + OpBranch %40 + %70 = OpLabel + %69 = OpUndef %6 + OpReturn + %60 = OpLabel + %61 = OpUndef %6 + OpBranchConditional %5 %70 %40 + %30 = OpLabel + %29 = OpUndef %6 + OpBranch %60 + %20 = OpLabel + %21 = OpUndef %6 + OpBranch %60 + %40 = OpLabel + %39 = OpUndef %6 + OpBranchConditional %5 %30 %20 + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(context, nullptr); + + Function* function = spvtest::GetFunction(context->module(), 2); + + std::map> + expected_order; + expected_order[ForwardDataFlowAnalysis::LabelPosition::kLabelsOnly] = { + 50, 40, 20, 30, 60, 70, + }; + expected_order[ForwardDataFlowAnalysis::LabelPosition::kLabelsAtBeginning] = { + 50, 51, 52, 40, 39, 20, 21, 30, 29, 60, 61, 70, 69, + }; + expected_order[ForwardDataFlowAnalysis::LabelPosition::kLabelsAtEnd] = { + 51, 52, 50, 39, 40, 21, 20, 29, 30, 61, 60, 69, 70, + }; + expected_order[ForwardDataFlowAnalysis::LabelPosition::kNoLabels] = { + 51, 52, 39, 21, 29, 61, 69, + }; + + for (const auto& test_case : expected_order) { + VisitOrder analysis(*context, test_case.first); + analysis.Run(function); + EXPECT_EQ(test_case.second, analysis.visited_result_ids); + } +} + +TEST_F(DataFlowTest, BackwardReachability) { + // CFG: + // V-----------. + // -> 11 -> 12 -> 13 -> 15 + // \-> 14 ---^ + + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %6 = OpTypeBool + %5 = OpConstantTrue %6 + %2 = OpFunction %3 None %4 + %11 = OpLabel + OpBranch %12 + %12 = OpLabel + OpBranchConditional %5 %14 %13 + %13 = OpLabel + OpBranchConditional %5 %15 %11 + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(context, nullptr); + + Function* function = spvtest::GetFunction(context->module(), 2); + + BackwardReachability analysis(*context); + analysis.Run(function); + + std::map> expected_result; + expected_result[11] = {11, 12, 13}; + expected_result[12] = {11, 12, 13}; + expected_result[13] = {11, 12, 13}; + expected_result[14] = {11, 12, 13}; + expected_result[15] = {11, 12, 13, 14}; + EXPECT_EQ(expected_result, analysis.reachable_from); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/test/opt/dead_branch_elim_test.cpp b/test/opt/dead_branch_elim_test.cpp index f89befb4..1095d3b8 100644 --- a/test/opt/dead_branch_elim_test.cpp +++ b/test/opt/dead_branch_elim_test.cpp @@ -1613,7 +1613,7 @@ OpBranch %7 %11 = OpLogicalOr %bool %true %false OpBranch %7 %7 = OpLabel -; This phi is in a block preceeding the merge %14! +; This phi is in a block preceding the merge %14! %8 = OpPhi %bool %10 %5 %11 %6 OpBranch %14 %14 = OpLabel @@ -2570,9 +2570,8 @@ OpFunctionEnd TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoop3) { // Checks that if a selection merge construct contains a conditional branch - // to the merge of a surrounding loop, the selection merge, and another block - // inside the selection merge, then we must keep the OpSelectionMerge - // instruction on that branch. + // to the selection merge, and another block inside the selection merge, + // then we must keep the OpSelectionMerge instruction on that branch. const std::string predefs = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -2586,6 +2585,7 @@ OpSource GLSL 140 %true = OpConstantTrue %bool %uint = OpTypeInt 32 0 %undef_int = OpUndef %uint +%undef_bool = OpUndef %bool )"; const std::string body = @@ -2596,7 +2596,7 @@ OpSource GLSL 140 ; CHECK-NEXT: OpBranch [[bb2:%\w+]] ; CHECK: [[bb2]] = OpLabel ; CHECK-NEXT: OpSelectionMerge [[sel_merge:%\w+]] None -; CHECK-NEXT: OpSwitch {{%\w+}} [[sel_merge]] 0 [[loop_merge]] 1 [[bb3:%\w+]] +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[sel_merge]] [[bb3:%\w+]] ; CHECK: [[bb3]] = OpLabel ; CHECK-NEXT: OpBranch [[sel_merge]] ; CHECK: [[sel_merge]] = OpLabel @@ -2613,7 +2613,8 @@ OpBranch %bb1 OpSelectionMerge %sel_merge None OpBranchConditional %true %bb2 %bb4 %bb2 = OpLabel -OpSwitch %undef_int %sel_merge 0 %loop_merge 1 %bb3 +;OpSwitch %undef_int %sel_merge 0 %loop_merge 1 %bb3 +OpBranchConditional %undef_bool %sel_merge %bb3 %bb3 = OpLabel OpBranch %sel_merge %bb4 = OpLabel @@ -2632,9 +2633,8 @@ OpFunctionEnd TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoopContinue3) { // Checks that if a selection merge construct contains a conditional branch - // to the merge of a surrounding loop, the selection merge, and another block - // inside the selection merge, then we must keep the OpSelectionMerge - // instruction on that branch. + // the selection merge, and another block inside the selection merge, then we + // must keep the OpSelectionMerge instruction on that branch. const std::string predefs = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -2648,6 +2648,7 @@ OpSource GLSL 140 %true = OpConstantTrue %bool %uint = OpTypeInt 32 0 %undef_int = OpUndef %uint +%undef_bool = OpUndef %bool )"; const std::string body = @@ -2660,7 +2661,7 @@ OpSource GLSL 140 ; CHECK-NEXT: OpBranch [[bb2:%\w+]] ; CHECK: [[bb2]] = OpLabel ; CHECK-NEXT: OpSelectionMerge [[sel_merge:%\w+]] None -; CHECK-NEXT: OpSwitch {{%\w+}} [[sel_merge]] 0 [[loop_continue]] 1 [[bb3:%\w+]] +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[sel_merge]] [[bb3:%\w+]] ; CHECK: [[bb3]] = OpLabel ; CHECK-NEXT: OpBranch [[sel_merge]] ; CHECK: [[sel_merge]] = OpLabel @@ -2679,131 +2680,7 @@ OpBranch %bb1 OpSelectionMerge %sel_merge None OpBranchConditional %true %bb2 %bb4 %bb2 = OpLabel -OpSwitch %undef_int %sel_merge 0 %cont 1 %bb3 -%bb3 = OpLabel -OpBranch %sel_merge -%bb4 = OpLabel -OpBranch %sel_merge -%sel_merge = OpLabel -OpBranch %loop_merge -%cont = OpLabel -OpBranch %loop_header -%loop_merge = OpLabel -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndMatch(predefs + body, true); -} - -TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoop4) { - // Same as |SelectionMergeWithExitToLoop|, except the branch in the selection - // construct is an |OpSwitch| instead of an |OpConditionalBranch|. The - // OpSelectionMerge instruction is not needed in this case either. - const std::string predefs = R"( -OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 140 -%void = OpTypeVoid -%func_type = OpTypeFunction %void -%bool = OpTypeBool -%true = OpConstantTrue %bool -%uint = OpTypeInt 32 0 -%undef_int = OpUndef %uint -)"; - - const std::string body = - R"( -; CHECK: OpLoopMerge [[loop_merge:%\w+]] -; CHECK-NEXT: OpBranch [[bb1:%\w+]] -; CHECK: [[bb1]] = OpLabel -; CHECK-NEXT: OpBranch [[bb2:%\w+]] -; CHECK: [[bb2]] = OpLabel -; CHECK-NEXT: OpSelectionMerge -; CHECK-NEXT: OpSwitch {{%\w+}} [[bb3:%\w+]] 0 [[loop_merge]] 1 [[bb3:%\w+]] -; CHECK: [[bb3]] = OpLabel -; CHECK-NEXT: OpBranch [[sel_merge:%\w+]] -; CHECK: [[sel_merge]] = OpLabel -; CHECK-NEXT: OpBranch [[loop_merge]] -; CHECK: [[loop_merge]] = OpLabel -; CHECK-NEXT: OpReturn -%main = OpFunction %void None %func_type -%entry_bb = OpLabel -OpBranch %loop_header -%loop_header = OpLabel -OpLoopMerge %loop_merge %cont None -OpBranch %bb1 -%bb1 = OpLabel -OpSelectionMerge %sel_merge None -OpBranchConditional %true %bb2 %bb4 -%bb2 = OpLabel -OpSelectionMerge %bb3 None -OpSwitch %undef_int %bb3 0 %loop_merge 1 %bb3 -%bb3 = OpLabel -OpBranch %sel_merge -%bb4 = OpLabel -OpBranch %sel_merge -%sel_merge = OpLabel -OpBranch %loop_merge -%cont = OpLabel -OpBranch %loop_header -%loop_merge = OpLabel -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndMatch(predefs + body, true); -} - -TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoopContinue4) { - // Same as |SelectionMergeWithExitToLoopContinue|, except the branch in the - // selection construct is an |OpSwitch| instead of an |OpConditionalBranch|. - // The OpSelectionMerge instruction is not needed in this case either. - const std::string predefs = R"( -OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 140 -%void = OpTypeVoid -%func_type = OpTypeFunction %void -%bool = OpTypeBool -%true = OpConstantTrue %bool -%uint = OpTypeInt 32 0 -%undef_int = OpUndef %uint -)"; - - const std::string body = - R"( -; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]] -; CHECK-NEXT: OpBranch [[bb1:%\w+]] -; CHECK: [[bb1]] = OpLabel -; CHECK-NEXT: OpBranch [[bb2:%\w+]] -; CHECK: [[bb2]] = OpLabel -; CHECK-NEXT: OpSelectionMerge -; CHECK-NEXT: OpSwitch {{%\w+}} [[bb3:%\w+]] 0 [[loop_cont]] 1 [[bb3:%\w+]] -; CHECK: [[bb3]] = OpLabel -; CHECK-NEXT: OpBranch [[sel_merge:%\w+]] -; CHECK: [[sel_merge]] = OpLabel -; CHECK-NEXT: OpBranch [[loop_merge]] -; CHECK: [[loop_merge]] = OpLabel -; CHECK-NEXT: OpReturn -%main = OpFunction %void None %func_type -%entry_bb = OpLabel -OpBranch %loop_header -%loop_header = OpLabel -OpLoopMerge %loop_merge %cont None -OpBranch %bb1 -%bb1 = OpLabel -OpSelectionMerge %sel_merge None -OpBranchConditional %true %bb2 %bb4 -%bb2 = OpLabel -OpSelectionMerge %bb3 None -OpSwitch %undef_int %bb3 0 %cont 1 %bb3 +OpBranchConditional %undef_bool %sel_merge %bb3 %bb3 = OpLabel OpBranch %sel_merge %bb4 = OpLabel @@ -3036,9 +2913,11 @@ TEST_F(DeadBranchElimTest, UnreachableMergeAndContinueSameBlock) { ; CHECK-NEXT: OpLoopMerge [[outer_merge:%\w+]] [[outer_cont:%\w+]] None ; CHECK-NEXT: OpBranch [[inner:%\w+]] ; CHECK: [[inner]] = OpLabel -; CHECK: OpLoopMerge [[outer_cont]] [[inner_cont:%\w+]] None +; CHECK: OpLoopMerge [[inner_merge:%\w+]] [[inner_cont:%\w+]] None ; CHECK: [[inner_cont]] = OpLabel ; CHECK-NEXT: OpBranch [[inner]] +; CHECK: [[inner_merge]] = OpLabel +; CHECK-NEXT: OpUnreachable ; CHECK: [[outer_cont]] = OpLabel ; CHECK-NEXT: OpBranch [[outer]] ; CHECK: [[outer_merge]] = OpLabel @@ -3058,7 +2937,7 @@ OpBranch %outer_loop OpLoopMerge %outer_merge %outer_continue None OpBranch %inner_loop %inner_loop = OpLabel -OpLoopMerge %outer_continue %inner_continue None +OpLoopMerge %inner_merge %inner_continue None OpBranch %inner_body %inner_body = OpLabel OpSelectionMerge %inner_continue None @@ -3066,7 +2945,9 @@ OpBranchConditional %true %ret %inner_continue %ret = OpLabel OpReturn %inner_continue = OpLabel -OpBranchConditional %true %outer_continue %inner_loop +OpBranchConditional %true %inner_merge %inner_loop +%inner_merge = OpLabel +OpBranch %outer_continue %outer_continue = OpLabel OpBranchConditional %true %outer_merge %outer_loop %outer_merge = OpLabel @@ -3403,6 +3284,71 @@ OpFunctionEnd SinglePassRunAndMatch(text, true); } +TEST_F(DeadBranchElimTest, DontTransferDecorations) { + // When replacing %4 with %14, we don't want %14 to inherit %4's decorations. + const std::string text = R"( +; CHECK-NOT: OpDecorate {{%\w+}} RelaxedPrecision +; CHECK: [[div:%\w+]] = OpFDiv +; CHECK: {{%\w+}} = OpCopyObject %float [[div]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %3 = OpString "STEVEN" + OpDecorate %4 RelaxedPrecision + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %void = OpTypeVoid + %float_1 = OpConstant %float 1 + %uint_0 = OpConstant %uint 0 + %10 = OpTypeFunction %void + %2 = OpFunction %void None %10 + %11 = OpLabel + OpSelectionMerge %12 None + OpSwitch %uint_0 %13 + %13 = OpLabel + %14 = OpFDiv %float %float_1 %float_1 + OpLine %3 0 0 + OpBranch %12 + %15 = OpLabel + OpBranch %12 + %12 = OpLabel + %4 = OpPhi %float %float_1 %15 %14 %13 + %16 = OpCopyObject %float %4 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DeadBranchElimTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} + // TODO(greg-lunarg): Add tests to verify handling of these cases: // // More complex control flow diff --git a/test/opt/dead_insert_elim_test.cpp b/test/opt/dead_insert_elim_test.cpp index 9ea948a4..268e6590 100644 --- a/test/opt/dead_insert_elim_test.cpp +++ b/test/opt/dead_insert_elim_test.cpp @@ -170,6 +170,72 @@ OpFunctionEnd after_predefs + after, true, true); } +TEST_F(DeadInsertElimTest, DeadInsertForLinkage) { + const std::string before = + R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %bb_entry "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%14 = OpTypeFunction %v2float %_ptr_Function_v2float +%_ptr_Function_float = OpTypePointer Function %float +%main = OpFunction %v2float None %14 +%BaseColor = OpFunctionParameter %_ptr_Function_v2float +%bb_entry = OpLabel +%v = OpVariable %_ptr_Function_v2float Function +%16 = OpLoad %v2float %v +%17 = OpAccessChain %_ptr_Function_float %BaseColor %int_1 +%18 = OpLoad %float %17 +%19 = OpCompositeInsert %v2float %18 %16 0 +%20 = OpCompositeInsert %v2float %float_0 %19 0 +OpReturnValue %20 +OpFunctionEnd +)"; + const std::string after = + R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %bb_entry "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%14 = OpTypeFunction %v2float %_ptr_Function_v2float +%_ptr_Function_float = OpTypePointer Function %float +%main = OpFunction %v2float None %14 +%BaseColor = OpFunctionParameter %_ptr_Function_v2float +%bb_entry = OpLabel +%v = OpVariable %_ptr_Function_v2float Function +%16 = OpLoad %v2float %v +%20 = OpCompositeInsert %v2float %float_0 %16 0 +OpReturnValue %20 +OpFunctionEnd +)"; + SinglePassRunAndCheck(before, after, true, true); +} + TEST_F(DeadInsertElimTest, DeadInsertInChainWithPhi) { // Dead insert eliminated with phi in insertion chain. // diff --git a/test/opt/debug_info_manager_test.cpp b/test/opt/debug_info_manager_test.cpp index 49407fd5..e87d0bea 100644 --- a/test/opt/debug_info_manager_test.cpp +++ b/test/opt/debug_info_manager_test.cpp @@ -185,6 +185,122 @@ void main(float in_var_color : COLOR) { 100U); } +TEST(DebugInfoManager, CreateDebugInlinedAtWithConstantManager) { + // Show that CreateDebugInlinedAt will use the Constant manager to generate + // its line operand if the Constant and DefUse managers are valid. This is + // proven by checking that the id for the line operand 7 is the same as the + // existing constant 7. + // + // int function1() { + // return 1; + // } + // + // void main() { + // function1(); + // } + const std::string text = R"(OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%3 = OpString "parent3.hlsl" +%8 = OpString "int" +%19 = OpString "function1" +%20 = OpString "" +%26 = OpString "main" +OpName %main "main" +OpName %src_main "src.main" +OpName %bb_entry "bb.entry" +OpName %function1 "function1" +OpName %bb_entry_0 "bb.entry" +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%void = OpTypeVoid +%uint_4 = OpConstant %uint 4 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_1 = OpConstant %uint 1 +%uint_5 = OpConstant %uint 5 +%uint_2 = OpConstant %uint 2 +%uint_17 = OpConstant %uint 17 +%uint_6 = OpConstant %uint 6 +%uint_13 = OpConstant %uint 13 +%100 = OpConstant %uint 7 +%31 = OpTypeFunction %void +%42 = OpTypeFunction %int +%10 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 %uint_4 %uint_0 +%13 = OpExtInst %void %1 DebugTypeFunction %uint_3 %10 +%15 = OpExtInst %void %1 DebugSource %3 +%16 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %15 %uint_5 +%21 = OpExtInst %void %1 DebugFunction %19 %13 %15 %uint_2 %uint_1 %16 %20 %uint_3 %uint_2 +%23 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_2 %uint_17 %21 +%25 = OpExtInst %void %1 DebugTypeFunction %uint_3 %void +%27 = OpExtInst %void %1 DebugFunction %26 %25 %15 %uint_6 %uint_1 %16 %20 %uint_3 %uint_6 +%29 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_6 %uint_13 %27 +%main = OpFunction %void None %31 +%32 = OpLabel +%33 = OpFunctionCall %void %src_main +OpLine %3 8 1 +OpReturn +OpFunctionEnd +OpLine %3 6 1 +%src_main = OpFunction %void None %31 +OpNoLine +%bb_entry = OpLabel +%47 = OpExtInst %void %1 DebugScope %27 +%37 = OpExtInst %void %1 DebugFunctionDefinition %27 %src_main +%48 = OpExtInst %void %1 DebugScope %29 +OpLine %3 7 3 +%39 = OpFunctionCall %int %function1 +%49 = OpExtInst %void %1 DebugScope %27 +OpLine %3 8 1 +OpReturn +%50 = OpExtInst %void %1 DebugNoScope +OpFunctionEnd +OpLine %3 2 1 +%function1 = OpFunction %int None %42 +OpNoLine +%bb_entry_0 = OpLabel +%51 = OpExtInst %void %1 DebugScope %21 +%45 = OpExtInst %void %1 DebugFunctionDefinition %21 %function1 +%52 = OpExtInst %void %1 DebugScope %23 +OpLine %3 3 3 +OpReturnValue %int_1 +%53 = OpExtInst %void %1 DebugNoScope +OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + const uint32_t line_number = 7U; + Instruction line(context.get(), SpvOpLine); + line.SetInOperands({ + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {5U}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {line_number}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {0U}}, + }); + + DebugScope scope(29U, 0U); + + auto db_manager = context.get()->get_debug_info_mgr(); + auto du_manager = context.get()->get_def_use_mgr(); + auto c_manager = context.get()->get_constant_mgr(); + + (void)du_manager; + (void)c_manager; + + uint32_t inlined_at_id = db_manager->CreateDebugInlinedAt(&line, scope); + auto* inlined_at = db_manager->GetDebugInlinedAt(inlined_at_id); + EXPECT_NE(inlined_at, nullptr); + EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex), + 100); +} + TEST(DebugInfoManager, GetDebugInfoNone) { const std::string text = R"( OpCapability Shader diff --git a/test/opt/decoration_manager_test.cpp b/test/opt/decoration_manager_test.cpp index fcfbff06..c9fabe78 100644 --- a/test/opt/decoration_manager_test.cpp +++ b/test/opt/decoration_manager_test.cpp @@ -118,7 +118,7 @@ class DecorationManagerTest : public ::testing::Test { TEST_F(DecorationManagerTest, ComparingDecorationsWithDiffOpcodesDecorateDecorateId) { IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer()); - // This parameter can be interprated both as { SpvDecorationConstant } + // This parameter can be interpreted both as { SpvDecorationConstant } // and also as a list of IDs: { 22 } const std::vector param{SpvDecorationConstant}; // OpDecorate %1 Constant @@ -137,7 +137,7 @@ TEST_F(DecorationManagerTest, TEST_F(DecorationManagerTest, ComparingDecorationsWithDiffOpcodesDecorateDecorateString) { IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer()); - // This parameter can be interprated both as { SpvDecorationConstant } + // This parameter can be interpreted both as { SpvDecorationConstant } // and also as a null-terminated string with a single character with value 22. const std::vector param{SpvDecorationConstant}; // OpDecorate %1 Constant diff --git a/test/opt/def_use_test.cpp b/test/opt/def_use_test.cpp index cfdad74a..0210095d 100644 --- a/test/opt/def_use_test.cpp +++ b/test/opt/def_use_test.cpp @@ -1656,7 +1656,7 @@ INSTANTIATE_TEST_SUITE_P( "OpGroupDecorate %1 %2 %3", }, }, - // memeber decorate + // member decorate { // code "OpMemberDecorate %1 0 RelaxedPrecision " @@ -1707,9 +1707,10 @@ TEST_F(UpdateUsesTest, KeepOldUses) { def->SetInOperands({{SPV_OPERAND_TYPE_ID, {25}}}); context->UpdateDefUse(def); - auto users = def_use_mgr->id_to_users(); - UserEntry entry = {def, use}; - EXPECT_THAT(users, Contains(entry)); + auto scanUser = [&](Instruction* user) { return user != use; }; + bool userFound = !def_use_mgr->WhileEachUser(def, scanUser); + + EXPECT_TRUE(userFound); } // clang-format on diff --git a/test/opt/desc_sroa_test.cpp b/test/opt/desc_sroa_test.cpp index b35ad474..91c950e8 100644 --- a/test/opt/desc_sroa_test.cpp +++ b/test/opt/desc_sroa_test.cpp @@ -770,6 +770,156 @@ TEST_F(DescriptorScalarReplacementTest, BindingForResourceArrayOfStructs) { SinglePassRunAndMatch(shader, true); } +TEST_F(DescriptorScalarReplacementTest, MemberDecorationForResourceStruct) { + // Check that an OpMemberDecorate instruction is correctly converted to a + // OpDecorate instruction. + + const std::string shader = R"( +; CHECK: OpDecorate [[t:%\w+]] DescriptorSet 0 +; CHECK: OpDecorate [[t]] Binding 0 +; CHECK: OpDecorate [[t]] RelaxedPrecision +; CHECK: OpDecorate [[s:%\w+]] DescriptorSet 0 +; CHECK: OpDecorate [[s]] Binding 1 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %PSMain "PSMain" %in_var_TEXCOORD %out_var_SV_Target + OpExecutionMode %PSMain OriginUpperLeft + OpSource HLSL 600 + OpName %sampler2D_h "sampler2D_h" + OpMemberName %sampler2D_h 0 "t" + OpMemberName %sampler2D_h 1 "s" + OpName %type_2d_image "type.2d.image" + OpName %type_sampler "type.sampler" + OpName %_MainTex "_MainTex" + OpName %in_var_TEXCOORD "in.var.TEXCOORD" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %PSMain "PSMain" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %in_var_TEXCOORD Location 0 + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %_MainTex DescriptorSet 0 + OpDecorate %_MainTex Binding 0 + OpMemberDecorate %sampler2D_h 0 RelaxedPrecision + OpDecorate %out_var_SV_Target RelaxedPrecision + OpDecorate %69 RelaxedPrecision + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%type_sampler = OpTypeSampler +%sampler2D_h = OpTypeStruct %type_2d_image %type_sampler +%_ptr_UniformConstant_sampler2D_h = OpTypePointer UniformConstant %sampler2D_h + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %35 = OpTypeFunction %void +%type_sampled_image = OpTypeSampledImage %type_2d_image + %_MainTex = OpVariable %_ptr_UniformConstant_sampler2D_h UniformConstant +%in_var_TEXCOORD = OpVariable %_ptr_Input_v2float Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output + %PSMain = OpFunction %void None %35 + %43 = OpLabel + %44 = OpLoad %v2float %in_var_TEXCOORD + %57 = OpLoad %sampler2D_h %_MainTex + %72 = OpCompositeExtract %type_2d_image %57 0 + %73 = OpCompositeExtract %type_sampler %57 1 + %68 = OpSampledImage %type_sampled_image %72 %73 + %69 = OpImageSampleImplicitLod %v4float %68 %44 None + OpStore %out_var_SV_Target %69 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(shader, true); +} + +TEST_F(DescriptorScalarReplacementTest, DecorateStringForReflect) { + // Check that an OpDecorateString instruction is correctly cloned to new + // variable. + + const std::string shader = R"( +; CHECK: OpName %g_testTextures_0_ "g_testTextures[0]" +; CHECK: OpDecorate %g_testTextures_0_ DescriptorSet 0 +; CHECK: OpDecorate %g_testTextures_0_ Binding 0 +; CHECK: OpDecorateString %g_testTextures_0_ UserTypeGOOGLE "texture2d" + OpCapability Shader + OpExtension "SPV_GOOGLE_hlsl_functionality1" + OpExtension "SPV_GOOGLE_user_type" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_2d_image "type.2d.image" + OpName %g_testTextures "g_testTextures" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %main "main" + OpName %param_var_vPixelPos "param.var.vPixelPos" + OpName %src_main "src.main" + OpName %vPixelPos "vPixelPos" + OpName %bb_entry "bb.entry" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorateString %gl_FragCoord UserSemantic "SV_Position" + OpDecorateString %out_var_SV_Target UserSemantic "SV_Target" + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %g_testTextures DescriptorSet 0 + OpDecorate %g_testTextures Binding 0 + OpDecorateString %g_testTextures UserTypeGOOGLE "texture2d" + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint_2 = OpConstant %uint 2 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_arr_type_2d_image_uint_2 = OpTypeArray %type_2d_image %uint_2 +%_ptr_UniformConstant__arr_type_2d_image_uint_2 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_2 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %18 = OpTypeFunction %void +%_ptr_Function_v4float = OpTypePointer Function %v4float + %25 = OpTypeFunction %v4float %_ptr_Function_v4float + %v2float = OpTypeVector %float 2 + %v3uint = OpTypeVector %uint 3 + %v3int = OpTypeVector %int 3 + %v2int = OpTypeVector %int 2 +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%g_testTextures = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_2 UniformConstant +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %18 + %19 = OpLabel +%param_var_vPixelPos = OpVariable %_ptr_Function_v4float Function + %22 = OpLoad %v4float %gl_FragCoord + OpStore %param_var_vPixelPos %22 + %23 = OpFunctionCall %v4float %src_main %param_var_vPixelPos + OpStore %out_var_SV_Target %23 + OpReturn + OpFunctionEnd + %src_main = OpFunction %v4float None %25 + %vPixelPos = OpFunctionParameter %_ptr_Function_v4float + %bb_entry = OpLabel + %28 = OpLoad %v4float %vPixelPos + %30 = OpVectorShuffle %v2float %28 %28 0 1 + %31 = OpCompositeExtract %float %30 0 + %32 = OpCompositeExtract %float %30 1 + %33 = OpConvertFToU %uint %31 + %34 = OpConvertFToU %uint %32 + %36 = OpCompositeConstruct %v3uint %33 %34 %uint_0 + %38 = OpBitcast %v3int %36 + %40 = OpVectorShuffle %v2int %38 %38 0 1 + %41 = OpCompositeExtract %int %38 2 + %43 = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_testTextures %int_0 + %44 = OpLoad %type_2d_image %43 + %45 = OpImageFetch %v4float %44 %40 Lod %41 + OpReturnValue %45 + OpFunctionEnd +)"; + + SinglePassRunAndMatch(shader, true); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/dominator_tree/generated.cpp b/test/opt/dominator_tree/generated.cpp index 534f770b..4fccef05 100644 --- a/test/opt/dominator_tree/generated.cpp +++ b/test/opt/dominator_tree/generated.cpp @@ -57,7 +57,7 @@ void check_dominance(const DominatorAnalysisBase& dom_tree, const Function* fn, } } -// Check that x does not dominates y and vise versa +// Check that x does not dominates y and vice versa void check_no_dominance(const DominatorAnalysisBase& dom_tree, const Function* fn, uint32_t x, uint32_t y) { SCOPED_TRACE("Check no domination for Basic Block " + std::to_string(x) + diff --git a/test/opt/eliminate_dead_const_test.cpp b/test/opt/eliminate_dead_const_test.cpp index 59f06f9f..87aab545 100644 --- a/test/opt/eliminate_dead_const_test.cpp +++ b/test/opt/eliminate_dead_const_test.cpp @@ -547,7 +547,7 @@ INSTANTIATE_TEST_SUITE_P( }, // Uint vector type spec constants. One vector has all component dead, - // another vector has one dead unsigend integer and one used unsigned + // another vector has one dead unsigned integer and one used unsigned // integer. { /* .used_consts = */ diff --git a/test/opt/eliminate_dead_functions_test.cpp b/test/opt/eliminate_dead_functions_test.cpp index 96ecdc60..96deb2a6 100644 --- a/test/opt/eliminate_dead_functions_test.cpp +++ b/test/opt/eliminate_dead_functions_test.cpp @@ -439,6 +439,84 @@ OpFunctionEnd SinglePassRunAndMatch(text, true); } +TEST_F(EliminateDeadFunctionsBasicTest, NonSemanticInfoRemoveDebugPrintf) { + const std::string text = R"( +; CHECK-NOT: %foo_ = OpFunction %void None % 3 +; CHECK-NOT: % 7 = OpLabel +; CHECK-NOT: %c = OpVariable %_ptr_Function_v4float Function +; CHECK-NOT: % 22 = OpAccessChain %_ptr_UniformConstant_13 %samplers %int_0 +; CHECK-NOT: % 23 = OpLoad % 13 % 22 +; CHECK-NOT: % 27 = OpImageSampleExplicitLod %v4float % 23 % 26 Lod %float_0 +; CHECK-NOT: OpStore %c % 27 +; CHECK-NOT: % 31 = OpAccessChain %_ptr_Function_float %c %uint_0 +; CHECK-NOT: % 32 = OpLoad %float %31 +; CHECK-NOT: % 34 = OpExtInst %void %33 1 % 28 % 32 +OpCapability RayTracingKHR +OpExtension "SPV_KHR_non_semantic_info" +OpExtension "SPV_KHR_ray_tracing" +%1 = OpExtInstImport "GLSL.std.450" +%33 = OpExtInstImport "NonSemantic.DebugPrintf" +OpMemoryModel Logical GLSL450 +OpEntryPoint ClosestHitNV %main "main" %samplers +%28 = OpString "%f" +OpSource GLSL 460 +OpSourceExtension "GL_EXT_debug_printf" +OpName %main "main" +OpName %foo_ "foo(" +OpName %c "c" +OpName %samplers "samplers" +OpDecorate %samplers DescriptorSet 0 +OpDecorate %samplers Binding 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%12 = OpTypeImage %float 3D 0 0 0 1 Unknown +%13 = OpTypeSampledImage %12 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%_arr_13_uint_1 = OpTypeArray %13 %uint_1 +%_ptr_UniformConstant__arr_13_uint_1 = OpTypePointer UniformConstant %_arr_13_uint_1 +%samplers = OpVariable %_ptr_UniformConstant__arr_13_uint_1 UniformConstant +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v3float = OpTypeVector %float 3 +%float_0 = OpConstant %float 0 +%26 = OpConstantComposite %v3float %float_0 %float_0 %float_0 +%uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float +%main = OpFunction %void None %3 +%5 = OpLabel +%36 = OpVariable %_ptr_Function_v4float Function +%38 = OpAccessChain %_ptr_UniformConstant_13 %samplers %int_0 +%39 = OpLoad %13 %38 +%40 = OpImageSampleExplicitLod %v4float %39 %26 Lod %float_0 +OpStore %36 %40 +%41 = OpAccessChain %_ptr_Function_float %36 %uint_0 +%42 = OpLoad %float %41 +%43 = OpExtInst %void %33 1 %28 %42 +OpReturn +OpFunctionEnd +%foo_ = OpFunction %void None %3 +%7 = OpLabel +%c = OpVariable %_ptr_Function_v4float Function +%22 = OpAccessChain %_ptr_UniformConstant_13 %samplers %int_0 +%23 = OpLoad %13 %22 +%27 = OpImageSampleExplicitLod %v4float %23 %26 Lod %float_0 +OpStore %c %27 +%31 = OpAccessChain %_ptr_Function_float %c %uint_0 +%32 = OpLoad %float %31 +%34 = OpExtInst %void %33 1 %28 %32 +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SinglePassRunAndMatch(text, true); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/eliminate_dead_input_components_test.cpp b/test/opt/eliminate_dead_input_components_test.cpp new file mode 100644 index 00000000..822914a8 --- /dev/null +++ b/test/opt/eliminate_dead_input_components_test.cpp @@ -0,0 +1,468 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// Copyright (c) 2022 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ElimDeadInputComponentsTest = PassTest<::testing::Test>; + +TEST_F(ElimDeadInputComponentsTest, ElimOneConstantIndex) { + // Should reduce to uv[2] + // + // #version 450 + // + // layout(location = 0) in vec4 uv[8]; + // + // out gl_PerVertex { + // vec4 gl_Position; + // }; + // + // void main() + // { + // gl_Position = uv[1]; + // } + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ %uv + OpSource GLSL 450 + OpName %main "main" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpName %_ "" + OpName %uv "uv" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpDecorate %gl_PerVertex Block + OpDecorate %uv Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%gl_PerVertex = OpTypeStruct %v4float +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_8 = OpConstant %uint 8 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8 + %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input + %int_1 = OpConstant %int 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +;CHECK-NOT: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input +;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_2 Input + %main = OpFunction %void None %3 + %5 = OpLabel + %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1 + %21 = OpLoad %v4float %20 + %23 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %23 %21 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + +TEST_F(ElimDeadInputComponentsTest, ElimOneConstantIndexInBounds) { + // Same as ElimOneConstantIndex but with OpInBoundsAccessChain + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ %uv + OpSource GLSL 450 + OpName %main "main" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpName %_ "" + OpName %uv "uv" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpDecorate %gl_PerVertex Block + OpDecorate %uv Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%gl_PerVertex = OpTypeStruct %v4float +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_8 = OpConstant %uint 8 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8 + %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input + %int_1 = OpConstant %int 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +;CHECK-NOT: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input +;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_2 Input + %main = OpFunction %void None %3 + %5 = OpLabel + %20 = OpInBoundsAccessChain %_ptr_Input_v4float %uv %int_1 + %21 = OpLoad %v4float %20 + %23 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %23 %21 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + +TEST_F(ElimDeadInputComponentsTest, ElimTwoConstantIndices) { + // Should reduce to uv[4] + // + // #version 450 + // + // layout(location = 0) in vec4 uv[8]; + // + // out gl_PerVertex { + // vec4 gl_Position; + // }; + // + // void main() + // { + // gl_Position = uv[1] + uv[3]; + // } + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ %uv + OpSource GLSL 450 + OpName %main "main" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpName %_ "" + OpName %uv "uv" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpDecorate %gl_PerVertex Block + OpDecorate %uv Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%gl_PerVertex = OpTypeStruct %v4float +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_8 = OpConstant %uint 8 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8 + %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input + %int_1 = OpConstant %int 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %int_3 = OpConstant %int 3 +%_ptr_Output_v4float = OpTypePointer Output %v4float +;CHECK-NOT: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input +;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_4 Input + %main = OpFunction %void None %3 + %5 = OpLabel + %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1 + %21 = OpLoad %v4float %20 + %23 = OpAccessChain %_ptr_Input_v4float %uv %int_3 + %24 = OpLoad %v4float %23 + %25 = OpFAdd %v4float %21 %24 + %27 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %27 %25 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + +TEST_F(ElimDeadInputComponentsTest, NoElimMaxConstantIndex) { + // Should not reduce uv[8] because of max index of 7 + // + // #version 450 + // + // layout(location = 0) in vec4 uv[8]; + // + // out gl_PerVertex { + // vec4 gl_Position; + // }; + // + // void main() + // { + // gl_Position = uv[1] + uv[7]; + // } + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ %uv + OpSource GLSL 450 + OpName %main "main" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpName %_ "" + OpName %uv "uv" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpDecorate %gl_PerVertex Block + OpDecorate %uv Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%gl_PerVertex = OpTypeStruct %v4float +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_8 = OpConstant %uint 8 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8 + %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input + %int_1 = OpConstant %int 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %int_7 = OpConstant %int 7 +%_ptr_Output_v4float = OpTypePointer Output %v4float +;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input + %main = OpFunction %void None %3 + %5 = OpLabel + %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1 + %21 = OpLoad %v4float %20 + %23 = OpAccessChain %_ptr_Input_v4float %uv %int_7 + %24 = OpLoad %v4float %23 + %25 = OpFAdd %v4float %21 %24 + %27 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %27 %25 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + +TEST_F(ElimDeadInputComponentsTest, NoElimNonConstantIndex) { + // Should not reduce uv[8] because of non-constant index of ui + // + // #version 450 + // + // layout(location = 0) in vec4 uv[8]; + // + // out gl_PerVertex { + // vec4 gl_Position; + // }; + // + // uniform ubname { + // int ui; + // } ubinst; + // + // void main() + // { + // gl_Position = uv[1] + uv[ubinst.ui]; + // } + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ %uv %ubinst + OpSource GLSL 450 + OpName %main "main" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpName %_ "" + OpName %uv "uv" + OpName %ubname "ubname" + OpMemberName %ubname 0 "ui" + OpName %ubinst "ubinst" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpDecorate %gl_PerVertex Block + OpDecorate %uv Location 0 + OpMemberDecorate %ubname 0 Offset 0 + OpDecorate %ubname Block + OpDecorate %ubinst DescriptorSet 0 + OpDecorate %ubinst Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%gl_PerVertex = OpTypeStruct %v4float +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_8 = OpConstant %uint 8 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8 + %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input + %int_1 = OpConstant %int 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %ubname = OpTypeStruct %int +%_ptr_Uniform_ubname = OpTypePointer Uniform %ubname + %ubinst = OpVariable %_ptr_Uniform_ubname Uniform +%_ptr_Uniform_int = OpTypePointer Uniform %int +%_ptr_Output_v4float = OpTypePointer Output %v4float +;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input + %main = OpFunction %void None %3 + %5 = OpLabel + %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1 + %21 = OpLoad %v4float %20 + %26 = OpAccessChain %_ptr_Uniform_int %ubinst %int_0 + %27 = OpLoad %int %26 + %28 = OpAccessChain %_ptr_Input_v4float %uv %27 + %29 = OpLoad %v4float %28 + %30 = OpFAdd %v4float %21 %29 + %32 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %32 %30 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + +TEST_F(ElimDeadInputComponentsTest, NoElimNonIndexedAccessChain) { + // Should not change due to non-indexed access chain + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ %uv + OpSource GLSL 450 + OpName %main "main" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpName %_ "" + OpName %uv "uv" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpDecorate %gl_PerVertex Block + OpDecorate %uv Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%gl_PerVertex = OpTypeStruct %v4float +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_8 = OpConstant %uint 8 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8 + %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input + %int_1 = OpConstant %int 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input + %main = OpFunction %void None %3 + %5 = OpLabel + %20 = OpAccessChain %_ptr_Input__arr_v4float_uint_8 %uv + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + +TEST_F(ElimDeadInputComponentsTest, ElimStructMember) { + // Should eliminate uv + // + // #version 450 + // + // in Vertex { + // vec4 Cd; + // vec2 uv; + // } iVert; + // + // out vec4 fragColor; + // + // void main() + // { + // vec4 color = vec4(iVert.Cd); + // fragColor = color; + // } + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %iVert %fragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %Vertex "Vertex" + OpMemberName %Vertex 0 "Cd" + OpMemberName %Vertex 1 "uv" + OpName %iVert "iVert" + OpName %fragColor "fragColor" + OpDecorate %Vertex Block + OpDecorate %iVert Location 0 + OpDecorate %fragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %v2float = OpTypeVector %float 2 + %Vertex = OpTypeStruct %v4float %v2float +; CHECK: %Vertex = OpTypeStruct %v4float %v2float +; CHECK: [[sty:%\w+]] = OpTypeStruct %v4float +%_ptr_Input_Vertex = OpTypePointer Input %Vertex +; CHECK: [[pty:%\w+]] = OpTypePointer Input [[sty]] + %iVert = OpVariable %_ptr_Input_Vertex Input +; CHECK: %iVert = OpVariable [[pty]] Input + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %fragColor = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %17 = OpAccessChain %_ptr_Input_v4float %iVert %int_0 + %18 = OpLoad %v4float %17 + OpStore %fragColor %18 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/test/opt/eliminate_dead_member_test.cpp b/test/opt/eliminate_dead_member_test.cpp index 7728782e..e277999e 100644 --- a/test/opt/eliminate_dead_member_test.cpp +++ b/test/opt/eliminate_dead_member_test.cpp @@ -576,7 +576,6 @@ TEST_F(EliminateDeadMemberTest, RemoveMembersUpdateArrayLength) { %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform %main = OpFunction %void None %9 %10 = OpLabel - %11 = OpLoad %type__Globals %_Globals %12 = OpArrayLength %uint %_Globals 2 OpReturn OpFunctionEnd @@ -627,6 +626,67 @@ TEST_F(EliminateDeadMemberTest, KeepMembersOpStore) { EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); } +TEST_F(EliminateDeadMemberTest, KeepStorageBufferMembers) { + // Test that all members of the storage buffer struct %S are kept. + // No change expected. + const std::string text = R"( + OpCapability Shader + OpExtension "SPV_GOOGLE_hlsl_functionality1" + OpExtension "SPV_GOOGLE_user_type" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %PSMain "PSMain" %out_var_SV_TARGET + OpExecutionMode %PSMain OriginUpperLeft + OpSource HLSL 600 + OpName %type_StructuredBuffer_S "type.StructuredBuffer.S" + OpName %S "S" + OpMemberName %S 0 "A" + OpMemberName %S 1 "B" + OpName %Buf "Buf" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %PSMain "PSMain" + OpDecorateString %out_var_SV_TARGET UserSemantic "SV_TARGET" + OpDecorate %out_var_SV_TARGET Location 0 + OpDecorate %Buf DescriptorSet 0 + OpDecorate %Buf Binding 0 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 16 + OpDecorate %_runtimearr_S ArrayStride 32 + OpMemberDecorate %type_StructuredBuffer_S 0 Offset 0 + OpMemberDecorate %type_StructuredBuffer_S 0 NonWritable + OpDecorate %type_StructuredBuffer_S BufferBlock + OpDecorateString %Buf UserTypeGOOGLE "structuredbuffer" + %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 + %v4float = OpTypeVector %float 4 + %S = OpTypeStruct %v4float %v4float +%_runtimearr_S = OpTypeRuntimeArray %S +%type_StructuredBuffer_S = OpTypeStruct %_runtimearr_S +%_ptr_Uniform_type_StructuredBuffer_S = OpTypePointer Uniform %type_StructuredBuffer_S +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %18 = OpTypeFunction %void +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %Buf = OpVariable %_ptr_Uniform_type_StructuredBuffer_S Uniform +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %PSMain = OpFunction %void None %18 + %20 = OpLabel + %21 = OpAccessChain %_ptr_Uniform_v4float %Buf %int_0 %uint_0 %int_1 + %22 = OpLoad %v4float %21 + OpStore %out_var_SV_TARGET %22 + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ true); + EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + TEST_F(EliminateDeadMemberTest, KeepMembersOpCopyMemory) { // Test that all members are kept because of an OpCopyMemory. // No change expected. diff --git a/test/opt/fix_func_call_arguments_test.cpp b/test/opt/fix_func_call_arguments_test.cpp new file mode 100644 index 00000000..ecd13a86 --- /dev/null +++ b/test/opt/fix_func_call_arguments_test.cpp @@ -0,0 +1,152 @@ +// Copyright (c) 2022 Advanced Micro Devices, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gmock/gmock.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using FixFuncCallArgumentsTest = PassTest<::testing::Test>; +TEST_F(FixFuncCallArgumentsTest, Simple) { + const std::string text = R"( +; +; CHECK: [[v0:%\w+]] = OpVariable %_ptr_Function_float Function +; CHECK: [[v1:%\w+]] = OpVariable %_ptr_Function_float Function +; CHECK: [[v2:%\w+]] = OpVariable %_ptr_Function_T Function +; CHECK: [[ac0:%\w+]] = OpAccessChain %_ptr_Function_float %t %int_0 +; CHECK: [[ac1:%\w+]] = OpAccessChain %_ptr_Uniform_float %r1 %int_0 %uint_0 +; CHECK: [[ld0:%\w+]] = OpLoad %float [[ac0]] +; CHECK: OpStore [[v1]] [[ld0]] +; CHECK: [[ld1:%\w+]] = OpLoad %float [[ac1]] +; CHECK: OpStore [[v0]] [[ld1]] +; CHECK: [[func:%\w+]] = OpFunctionCall %void %fn [[v1]] [[v0]] +; CHECK: [[ld2:%\w+]] = OpLoad %float [[v0]] +; CHECK: OpStore [[ac1]] [[ld2]] +; CHECK: [[ld3:%\w+]] = OpLoad %float [[v1]] +; CHECK: OpStore [[ac0]] [[ld3]] +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %type_RWStructuredBuffer_float "type.RWStructuredBuffer.float" +OpName %r1 "r1" +OpName %type_ACSBuffer_counter "type.ACSBuffer.counter" +OpMemberName %type_ACSBuffer_counter 0 "counter" +OpName %counter_var_r1 "counter.var.r1" +OpName %main "main" +OpName %bb_entry "bb.entry" +OpName %T "T" +OpMemberName %T 0 "t0" +OpName %t "t" +OpName %fn "fn" +OpName %p0 "p0" +OpName %p2 "p2" +OpName %bb_entry_0 "bb.entry" +OpDecorate %main LinkageAttributes "main" Export +OpDecorate %r1 DescriptorSet 0 +OpDecorate %r1 Binding 0 +OpDecorate %counter_var_r1 DescriptorSet 0 +OpDecorate %counter_var_r1 Binding 1 +OpDecorate %_runtimearr_float ArrayStride 4 +OpMemberDecorate %type_RWStructuredBuffer_float 0 Offset 0 +OpDecorate %type_RWStructuredBuffer_float BufferBlock +OpMemberDecorate %type_ACSBuffer_counter 0 Offset 0 +OpDecorate %type_ACSBuffer_counter 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 +%_runtimearr_float = OpTypeRuntimeArray %float +%type_RWStructuredBuffer_float = OpTypeStruct %_runtimearr_float +%_ptr_Uniform_type_RWStructuredBuffer_float = OpTypePointer Uniform %type_RWStructuredBuffer_float +%type_ACSBuffer_counter = OpTypeStruct %int +%_ptr_Uniform_type_ACSBuffer_counter = OpTypePointer Uniform %type_ACSBuffer_counter +%15 = OpTypeFunction %int +%T = OpTypeStruct %float +%_ptr_Function_T = OpTypePointer Function %T +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Uniform_float = OpTypePointer Uniform %float +%void = OpTypeVoid +%27 = OpTypeFunction %void %_ptr_Function_float %_ptr_Function_float +%r1 = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_float Uniform +%counter_var_r1 = OpVariable %_ptr_Uniform_type_ACSBuffer_counter Uniform +%main = OpFunction %int None %15 +%bb_entry = OpLabel +%t = OpVariable %_ptr_Function_T Function +%21 = OpAccessChain %_ptr_Function_float %t %int_0 +%23 = OpAccessChain %_ptr_Uniform_float %r1 %int_0 %uint_0 +%25 = OpFunctionCall %void %fn %21 %23 +OpReturnValue %int_1 +OpFunctionEnd +%fn = OpFunction %void DontInline %27 +%p0 = OpFunctionParameter %_ptr_Function_float +%p2 = OpFunctionParameter %_ptr_Function_float +%bb_entry_0 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(FixFuncCallArgumentsTest, NotAccessChainInput) { + const std::string text = R"( +; +; CHECK: [[o:%\w+]] = OpCopyObject %_ptr_Function_float %t +; CHECK: [[func:%\w+]] = OpFunctionCall %void %fn [[o]] +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %bb_entry "bb.entry" +OpName %t "t" +OpName %fn "fn" +OpName %p0 "p0" +OpName %bb_entry_0 "bb.entry" +OpDecorate %main LinkageAttributes "main" Export +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%4 = OpTypeFunction %int +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%void = OpTypeVoid +%12 = OpTypeFunction %void %_ptr_Function_float +%main = OpFunction %int None %4 +%bb_entry = OpLabel +%t = OpVariable %_ptr_Function_float Function +%t1 = OpCopyObject %_ptr_Function_float %t +%10 = OpFunctionCall %void %fn %t1 +OpReturnValue %int_1 +OpFunctionEnd +%fn = OpFunction %void DontInline %12 +%p0 = OpFunctionParameter %_ptr_Function_float +%bb_entry_0 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, false); +} + +} // namespace +} // namespace opt +} // namespace spvtools \ No newline at end of file diff --git a/test/opt/flatten_decoration_test.cpp b/test/opt/flatten_decoration_test.cpp index d8d88677..63207fd2 100644 --- a/test/opt/flatten_decoration_test.cpp +++ b/test/opt/flatten_decoration_test.cpp @@ -43,7 +43,7 @@ OpName %Camera "Camera" )"; } -// Retuns types +// Returns types std::string TypesAndFunctionsAssembly() { return R"(%void = OpTypeVoid diff --git a/test/opt/fold_spec_const_op_composite_test.cpp b/test/opt/fold_spec_const_op_composite_test.cpp index 7eddf7e9..c98a44c3 100644 --- a/test/opt/fold_spec_const_op_composite_test.cpp +++ b/test/opt/fold_spec_const_op_composite_test.cpp @@ -105,6 +105,209 @@ TEST_F(FoldSpecConstantOpAndCompositePassBasicTest, builder.GetCode(), builder.GetCode(), /* skip_nop = */ true); } +// Test where OpSpecConstantOp depends on another OpSpecConstantOp with +// CompositeExtract +TEST_F(FoldSpecConstantOpAndCompositePassBasicTest, StackedCompositeExtract) { + AssemblyBuilder builder; + builder.AppendTypesConstantsGlobals({ + // clang-format off + "%uint = OpTypeInt 32 0", + "%v3uint = OpTypeVector %uint 3", + "%uint_2 = OpConstant %uint 2", + "%uint_3 = OpConstant %uint 3", + // Folding target: + "%composite_0 = OpSpecConstantComposite %v3uint %uint_2 %uint_3 %uint_2", + "%op_0 = OpSpecConstantOp %uint CompositeExtract %composite_0 0", + "%op_1 = OpSpecConstantOp %uint CompositeExtract %composite_0 1", + "%op_2 = OpSpecConstantOp %uint IMul %op_0 %op_1", + "%composite_1 = OpSpecConstantComposite %v3uint %op_0 %op_1 %op_2", + "%op_3 = OpSpecConstantOp %uint CompositeExtract %composite_1 0", + "%op_4 = OpSpecConstantOp %uint IMul %op_2 %op_3", + // clang-format on + }); + + std::vector expected = { + // clang-format off + "OpCapability Shader", + "OpCapability Float64", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + "OpName %void \"void\"", + "OpName %main_func_type \"main_func_type\"", + "OpName %main \"main\"", + "OpName %main_func_entry_block \"main_func_entry_block\"", + "OpName %uint \"uint\"", + "OpName %v3uint \"v3uint\"", + "OpName %uint_2 \"uint_2\"", + "OpName %uint_3 \"uint_3\"", + "OpName %composite_0 \"composite_0\"", + "OpName %op_0 \"op_0\"", + "OpName %op_1 \"op_1\"", + "OpName %op_2 \"op_2\"", + "OpName %composite_1 \"composite_1\"", + "OpName %op_3 \"op_3\"", + "OpName %op_4 \"op_4\"", + "%void = OpTypeVoid", +"%main_func_type = OpTypeFunction %void", + "%uint = OpTypeInt 32 0", + "%v3uint = OpTypeVector %uint 3", + "%uint_2 = OpConstant %uint 2", + "%uint_3 = OpConstant %uint 3", +"%composite_0 = OpConstantComposite %v3uint %uint_2 %uint_3 %uint_2", + "%op_0 = OpConstant %uint 2", + "%op_1 = OpConstant %uint 3", + "%op_2 = OpConstant %uint 6", +"%composite_1 = OpConstantComposite %v3uint %op_0 %op_1 %op_2", +"%op_3 = OpConstant %uint 2", + "%op_4 = OpConstant %uint 12", + "%main = OpFunction %void None %main_func_type", +"%main_func_entry_block = OpLabel", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + SinglePassRunAndCheck( + builder.GetCode(), JoinAllInsts(expected), /* skip_nop = */ true); +} + +// Test where OpSpecConstantOp depends on another OpSpecConstantOp with +// VectorShuffle +TEST_F(FoldSpecConstantOpAndCompositePassBasicTest, StackedVectorShuffle) { + AssemblyBuilder builder; + builder.AppendTypesConstantsGlobals({ + // clang-format off + "%uint = OpTypeInt 32 0", + "%v3uint = OpTypeVector %uint 3", + "%uint_1 = OpConstant %uint 1", + "%uint_2 = OpConstant %uint 2", + "%uint_3 = OpConstant %uint 3", + "%uint_4 = OpConstant %uint 4", + "%uint_5 = OpConstant %uint 5", + "%uint_6 = OpConstant %uint 6", + // Folding target: + "%composite_0 = OpSpecConstantComposite %v3uint %uint_1 %uint_2 %uint_3", + "%composite_1 = OpSpecConstantComposite %v3uint %uint_4 %uint_5 %uint_6", + "%vecshuffle = OpSpecConstantOp %v3uint VectorShuffle %composite_0 %composite_1 0 5 3", + "%op = OpSpecConstantOp %uint CompositeExtract %vecshuffle 1", + // clang-format on + }); + + std::vector expected = { + // clang-format off + "OpCapability Shader", + "OpCapability Float64", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + "OpName %void \"void\"", + "OpName %main_func_type \"main_func_type\"", + "OpName %main \"main\"", + "OpName %main_func_entry_block \"main_func_entry_block\"", + "OpName %uint \"uint\"", + "OpName %v3uint \"v3uint\"", + "OpName %uint_1 \"uint_1\"", + "OpName %uint_2 \"uint_2\"", + "OpName %uint_3 \"uint_3\"", + "OpName %uint_4 \"uint_4\"", + "OpName %uint_5 \"uint_5\"", + "OpName %uint_6 \"uint_6\"", + "OpName %composite_0 \"composite_0\"", + "OpName %composite_1 \"composite_1\"", + "OpName %vecshuffle \"vecshuffle\"", + "OpName %op \"op\"", + "%void = OpTypeVoid", +"%main_func_type = OpTypeFunction %void", + "%uint = OpTypeInt 32 0", + "%v3uint = OpTypeVector %uint 3", + "%uint_1 = OpConstant %uint 1", + "%uint_2 = OpConstant %uint 2", + "%uint_3 = OpConstant %uint 3", + "%uint_4 = OpConstant %uint 4", + "%uint_5 = OpConstant %uint 5", + "%uint_6 = OpConstant %uint 6", +"%composite_0 = OpConstantComposite %v3uint %uint_1 %uint_2 %uint_3", +"%composite_1 = OpConstantComposite %v3uint %uint_4 %uint_5 %uint_6", +"%vecshuffle = OpConstantComposite %v3uint %uint_1 %uint_6 %uint_4", + "%op = OpConstant %uint 6", + "%main = OpFunction %void None %main_func_type", +"%main_func_entry_block = OpLabel", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + SinglePassRunAndCheck( + builder.GetCode(), JoinAllInsts(expected), /* skip_nop = */ true); +} + +// Test CompositeExtract with matrix +TEST_F(FoldSpecConstantOpAndCompositePassBasicTest, CompositeExtractMaxtrix) { + AssemblyBuilder builder; + builder.AppendTypesConstantsGlobals({ + // clang-format off + "%uint = OpTypeInt 32 0", + "%v3uint = OpTypeVector %uint 3", + "%mat3x3 = OpTypeMatrix %v3uint 3", + "%uint_1 = OpConstant %uint 1", + "%uint_2 = OpConstant %uint 2", + "%uint_3 = OpConstant %uint 3", + // Folding target: + "%a = OpSpecConstantComposite %v3uint %uint_1 %uint_1 %uint_1", + "%b = OpSpecConstantComposite %v3uint %uint_1 %uint_1 %uint_3", + "%c = OpSpecConstantComposite %v3uint %uint_1 %uint_2 %uint_1", + "%op = OpSpecConstantComposite %mat3x3 %a %b %c", + "%x = OpSpecConstantOp %uint CompositeExtract %op 2 1", + "%y = OpSpecConstantOp %uint CompositeExtract %op 1 2", + // clang-format on + }); + + std::vector expected = { + // clang-format off + "OpCapability Shader", + "OpCapability Float64", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + "OpName %void \"void\"", + "OpName %main_func_type \"main_func_type\"", + "OpName %main \"main\"", + "OpName %main_func_entry_block \"main_func_entry_block\"", + "OpName %uint \"uint\"", + "OpName %v3uint \"v3uint\"", + "OpName %mat3x3 \"mat3x3\"", + "OpName %uint_1 \"uint_1\"", + "OpName %uint_2 \"uint_2\"", + "OpName %uint_3 \"uint_3\"", + "OpName %a \"a\"", + "OpName %b \"b\"", + "OpName %c \"c\"", + "OpName %op \"op\"", + "OpName %x \"x\"", + "OpName %y \"y\"", + "%void = OpTypeVoid", +"%main_func_type = OpTypeFunction %void", + "%uint = OpTypeInt 32 0", + "%v3uint = OpTypeVector %uint 3", + "%mat3x3 = OpTypeMatrix %v3uint 3", + "%uint_1 = OpConstant %uint 1", + "%uint_2 = OpConstant %uint 2", + "%uint_3 = OpConstant %uint 3", + "%a = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1", + "%b = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_3", + "%c = OpConstantComposite %v3uint %uint_1 %uint_2 %uint_1", + "%op = OpConstantComposite %mat3x3 %a %b %c", + "%x = OpConstant %uint 2", + "%y = OpConstant %uint 3", + "%main = OpFunction %void None %main_func_type", +"%main_func_entry_block = OpLabel", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + SinglePassRunAndCheck( + builder.GetCode(), JoinAllInsts(expected), /* skip_nop = */ true); +} + // All types and some common constants that are potentially required in // FoldSpecConstantOpAndCompositeTest. std::vector CommonTypesAndConstants() { @@ -199,7 +402,7 @@ std::string StripOpNameInstructions(const std::string& str) { struct FoldSpecConstantOpAndCompositePassTestCase { // Original constants with unfolded spec constants. std::vector original; - // Expected cosntants after folding. + // Expected constant after folding. std::vector expected; }; diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp index bb6098cc..b3248032 100644 --- a/test/opt/fold_test.cpp +++ b/test/opt/fold_test.cpp @@ -92,8 +92,13 @@ TEST_P(IntegerInstructionFoldingTest, Case) { inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); EXPECT_EQ(inst->opcode(), SpvOpConstant); analysis::ConstantManager* const_mrg = context->get_constant_mgr(); - const analysis::IntConstant* result = - const_mrg->GetConstantFromInst(inst)->AsIntConstant(); + const analysis::Constant* constant = const_mrg->GetConstantFromInst(inst); + // We expect to see either integer types or 16-bit float types here. + EXPECT_TRUE((constant->AsIntConstant() != nullptr) || + ((constant->AsFloatConstant() != nullptr) && + (constant->type()->AsFloat()->width() == 16))); + const analysis::ScalarConstant* result = + const_mrg->GetConstantFromInst(inst)->AsScalarConstant(); EXPECT_NE(result, nullptr); if (result != nullptr) { EXPECT_EQ(result->GetU32BitValue(), tc.expected_result); @@ -115,6 +120,7 @@ const std::string& Header() { static const std::string header = R"(OpCapability Shader OpCapability Float16 OpCapability Float64 +OpCapability Int8 OpCapability Int16 OpCapability Int64 %1 = OpExtInstImport "GLSL.std.450" @@ -134,17 +140,25 @@ OpName %main "main" %false = OpConstantFalse %bool %bool_null = OpConstantNull %bool %short = OpTypeInt 16 1 +%ushort = OpTypeInt 16 0 +%byte = OpTypeInt 8 1 +%ubyte = OpTypeInt 8 0 %int = OpTypeInt 32 1 %long = OpTypeInt 64 1 %uint = OpTypeInt 32 0 +%ulong = OpTypeInt 64 0 %v2int = OpTypeVector %int 2 %v4int = OpTypeVector %int 4 %v4float = OpTypeVector %float 4 %v4double = OpTypeVector %double 4 +%v2uint = OpTypeVector %uint 2 %v2float = OpTypeVector %float 2 %v2double = OpTypeVector %double 2 %v2half = OpTypeVector %half 2 %v2bool = OpTypeVector %bool 2 +%m2x2int = OpTypeMatrix %v2int 2 +%mat4v4float = OpTypeMatrix %v4float 4 +%mat4v4double = OpTypeMatrix %v4double 4 %struct_v2int_int_int = OpTypeStruct %v2int %int %int %_ptr_int = OpTypePointer Function %int %_ptr_uint = OpTypePointer Function %uint @@ -153,6 +167,7 @@ OpName %main "main" %_ptr_double = OpTypePointer Function %double %_ptr_half = OpTypePointer Function %half %_ptr_long = OpTypePointer Function %long +%_ptr_ulong = OpTypePointer Function %ulong %_ptr_v2int = OpTypePointer Function %v2int %_ptr_v4int = OpTypePointer Function %v4int %_ptr_v4float = OpTypePointer Function %v4float @@ -163,6 +178,8 @@ OpName %main "main" %short_0 = OpConstant %short 0 %short_2 = OpConstant %short 2 %short_3 = OpConstant %short 3 +%ubyte_1 = OpConstant %ubyte 1 +%byte_n1 = OpConstant %byte -1 %100 = OpConstant %int 0 ; Need a def with an numerical id to define id maps. %103 = OpConstant %int 7 ; Need a def with an numerical id to define id maps. %int_0 = OpConstant %int 0 @@ -170,12 +187,23 @@ OpName %main "main" %int_2 = OpConstant %int 2 %int_3 = OpConstant %int 3 %int_4 = OpConstant %int 4 +%int_10 = OpConstant %int 10 +%int_1073741824 = OpConstant %int 1073741824 +%int_n1 = OpConstant %int -1 %int_n24 = OpConstant %int -24 +%int_n858993459 = OpConstant %int -858993459 %int_min = OpConstant %int -2147483648 %int_max = OpConstant %int 2147483647 %long_0 = OpConstant %long 0 +%long_1 = OpConstant %long 1 %long_2 = OpConstant %long 2 %long_3 = OpConstant %long 3 +%long_10 = OpConstant %long 10 +%long_4611686018427387904 = OpConstant %long 4611686018427387904 +%long_n1 = OpConstant %long -1 +%long_n3689348814741910323 = OpConstant %long -3689348814741910323 +%long_min = OpConstant %long -9223372036854775808 +%long_max = OpConstant %long 9223372036854775807 %uint_0 = OpConstant %uint 0 %uint_1 = OpConstant %uint 1 %uint_2 = OpConstant %uint 2 @@ -183,7 +211,13 @@ OpName %main "main" %uint_4 = OpConstant %uint 4 %uint_32 = OpConstant %uint 32 %uint_42 = OpConstant %uint 42 +%uint_2147483649 = OpConstant %uint 2147483649 %uint_max = OpConstant %uint 4294967295 +%ulong_0 = OpConstant %ulong 0 +%ulong_1 = OpConstant %ulong 1 +%ulong_2 = OpConstant %ulong 2 +%ulong_9223372036854775809 = OpConstant %ulong 9223372036854775809 +%ulong_max = OpConstant %ulong 18446744073709551615 %v2int_undef = OpUndef %v2int %v2int_0_0 = OpConstantComposite %v2int %int_0 %int_0 %v2int_1_0 = OpConstantComposite %v2int %int_1 %int_0 @@ -191,18 +225,22 @@ OpName %main "main" %v2int_2_3 = OpConstantComposite %v2int %int_2 %int_3 %v2int_3_2 = OpConstantComposite %v2int %int_3 %int_2 %v2int_4_4 = OpConstantComposite %v2int %int_4 %int_4 +%v2int_min_max = OpConstantComposite %v2int %int_min %int_max %v2bool_null = OpConstantNull %v2bool %v2bool_true_false = OpConstantComposite %v2bool %true %false %v2bool_false_true = OpConstantComposite %v2bool %false %true %struct_v2int_int_int_null = OpConstantNull %struct_v2int_int_int %v2int_null = OpConstantNull %v2int %102 = OpConstantComposite %v2int %103 %103 +%v4int_undef = OpUndef %v4int %v4int_0_0_0_0 = OpConstantComposite %v4int %int_0 %int_0 %int_0 %int_0 +%m2x2int_undef = OpUndef %m2x2int %struct_undef_0_0 = OpConstantComposite %struct_v2int_int_int %v2int_undef %int_0 %int_0 %float_n1 = OpConstant %float -1 %104 = OpConstant %float 0 ; Need a def with an numerical id to define id maps. %float_null = OpConstantNull %float %float_0 = OpConstant %float 0 +%float_n0 = OpConstant %float -0.0 %float_1 = OpConstant %float 1 %float_2 = OpConstant %float 2 %float_3 = OpConstant %float 3 @@ -228,6 +266,7 @@ OpName %main "main" %105 = OpConstant %double 0 ; Need a def with an numerical id to define id maps. %double_null = OpConstantNull %double %double_0 = OpConstant %double 0 +%double_n0 = OpConstant %double -0.0 %double_1 = OpConstant %double 1 %double_2 = OpConstant %double 2 %double_3 = OpConstant %double 3 @@ -250,14 +289,32 @@ OpName %main "main" %v4float_0_0_0_1 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_1 %v4float_0_1_0_0 = OpConstantComposite %v4float %float_0 %float_1 %float_null %float_0 %v4float_1_1_1_1 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%v4float_1_2_3_4 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4 +%v4float_null = OpConstantNull %v4float +%mat4v4float_null = OpConstantComposite %mat4v4float %v4float_null %v4float_null %v4float_null %v4float_null +%mat4v4float_1_2_3_4 = OpConstantComposite %mat4v4float %v4float_1_2_3_4 %v4float_1_2_3_4 %v4float_1_2_3_4 %v4float_1_2_3_4 %107 = OpConstantComposite %v4double %double_0 %double_0 %double_0 %double_0 %v4double_0_0_0_0 = OpConstantComposite %v4double %double_0 %double_0 %double_0 %double_0 %v4double_0_0_0_1 = OpConstantComposite %v4double %double_0 %double_0 %double_0 %double_1 %v4double_0_1_0_0 = OpConstantComposite %v4double %double_0 %double_1 %double_null %double_0 %v4double_1_1_1_1 = OpConstantComposite %v4double %double_1 %double_1 %double_1 %double_1 +%v4double_1_2_3_4 = OpConstantComposite %v4double %double_1 %double_2 %double_3 %double_4 %v4double_1_1_1_0p5 = OpConstantComposite %v4double %double_1 %double_1 %double_1 %double_0p5 %v4double_null = OpConstantNull %v4double +%mat4v4double_null = OpConstantComposite %mat4v4double %v4double_null %v4double_null %v4double_null %v4double_null +%mat4v4double_1_2_3_4 = OpConstantComposite %mat4v4double %v4double_1_2_3_4 %v4double_1_2_3_4 %v4double_1_2_3_4 %v4double_1_2_3_4 %v4float_n1_2_1_3 = OpConstantComposite %v4float %float_n1 %float_2 %float_1 %float_3 +%uint_0x3f800000 = OpConstant %uint 0x3f800000 +%uint_0xbf800000 = OpConstant %uint 0xbf800000 +%v2uint_0x3f800000_0xbf800000 = OpConstantComposite %v2uint %uint_0x3f800000 %uint_0xbf800000 +%long_0xbf8000003f800000 = OpConstant %long 0xbf8000003f800000 +%int_0x3FF00000 = OpConstant %int 0x3FF00000 +%int_0x00000000 = OpConstant %int 0x00000000 +%int_0xC05FD666 = OpConstant %int 0xC05FD666 +%int_0x66666666 = OpConstant %int 0x66666666 +%v4int_0x3FF00000_0x00000000_0xC05FD666_0x66666666 = OpConstantComposite %v4int %int_0x00000000 %int_0x3FF00000 %int_0x66666666 %int_0xC05FD666 +%ushort_0xBC00 = OpConstant %ushort 0xBC00 +%short_0xBC00 = OpConstant %short 0xBC00 )"; return header; @@ -708,7 +765,119 @@ INSTANTIATE_TEST_SUITE_P(TestCase, IntegerInstructionFoldingTest, "%2 = OpExtInst %uint %1 UClamp %uint_2 %undef %uint_1\n" + "OpReturn\n" + "OpFunctionEnd", - 2, 1) + 2, 1), + // Test case 46: Bit-cast int 0 to unsigned int + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %uint %int_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 47: Bit-cast int -24 to unsigned int + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %uint %int_n24\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, static_cast(-24)), + // Test case 48: Bit-cast float 1.0f to unsigned int + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %uint %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, static_cast(0x3f800000)), + // Test case 49: Bit-cast ushort 0xBC00 to ushort + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %ushort %ushort_0xBC00\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0xBC00), + // Test case 50: Bit-cast short 0xBC00 to ushort + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %ushort %short_0xBC00\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0xFFFFBC00), + // Test case 51: Bit-cast half 1 to ushort + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %ushort %half_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x3C00), + // Test case 52: Bit-cast ushort 0xBC00 to short + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %short %ushort_0xBC00\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0xBC00), + // Test case 53: Bit-cast short 0xBC00 to short + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %short %short_0xBC00\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0xFFFFBC00), + // Test case 54: Bit-cast half 1 to short + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %short %half_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x3C00), + // Test case 55: Bit-cast ushort 0xBC00 to half + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %half %ushort_0xBC00\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0xBC00), + // Test case 56: Bit-cast short 0xBC00 to half + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %half %short_0xBC00\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0xFFFFBC00), + // Test case 57: Bit-cast half 1 to half + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %half %half_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x3C00), + // Test case 58: Bit-cast ubyte 1 to byte + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %byte %ubyte_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1), + // Test case 59: Bit-cast byte -1 to ubyte + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %ubyte %byte_n1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0xFFFFFFFF) )); // clang-format on @@ -790,10 +959,126 @@ INSTANTIATE_TEST_SUITE_P(TestCase, IntVectorInstructionFoldingTest, "%2 = OpVectorShuffle %v2int %v2int_null %v2int_2_3 0 4294967295 \n" + "OpReturn\n" + "OpFunctionEnd", - 2, {0,0}) + 2, {0,0}), + // Test case 4: fold bit-cast int -24 to unsigned int + InstructionFoldingCase>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpBitcast %v2uint %v2int_min_max\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {2147483648, 2147483647}) )); // clang-format on +using DoubleVectorInstructionFoldingTest = + ::testing::TestWithParam>>; + +TEST_P(DoubleVectorInstructionFoldingTest, Case) { + const auto& tc = GetParam(); + + // Build module. + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Fold the instruction to test. + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); + + // Make sure the instruction folded as expected. + EXPECT_TRUE(succeeded); + if (succeeded && inst != nullptr) { + EXPECT_EQ(inst->opcode(), SpvOpCopyObject); + inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); + std::vector opcodes = {SpvOpConstantComposite}; + EXPECT_THAT(opcodes, Contains(inst->opcode())); + analysis::ConstantManager* const_mrg = context->get_constant_mgr(); + const analysis::Constant* result = const_mrg->GetConstantFromInst(inst); + EXPECT_NE(result, nullptr); + if (result != nullptr) { + const std::vector& componenets = + result->AsVectorConstant()->GetComponents(); + EXPECT_EQ(componenets.size(), tc.expected_result.size()); + for (size_t i = 0; i < componenets.size(); i++) { + EXPECT_EQ(tc.expected_result[i], componenets[i]->GetDouble()); + } + } + } +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P(TestCase, DoubleVectorInstructionFoldingTest, +::testing::Values( + // Test case 0: bit-cast int {0x3FF00000,0x00000000,0xC05FD666,0x66666666} + // to double vector + InstructionFoldingCase>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %v2double %v4int_0x3FF00000_0x00000000_0xC05FD666_0x66666666\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {1.0,-127.35}), + // Test case 1: OpVectorTimesMatrix Non-Zero Zero {{0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}} {1.0, 2.0, 3.0, 4.0} {0.0, 0.0, 0.0, 0.0} + InstructionFoldingCase>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVectorTimesMatrix %v4double %v4double_1_2_3_4 %mat4v4double_null\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {0.0,0.0,0.0,0.0}), + // Test case 2: OpVectorTimesMatrix Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0} + InstructionFoldingCase>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVectorTimesMatrix %v4double %v4double_null %mat4v4double_1_2_3_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {0.0,0.0,0.0,0.0}), + // Test case 3: OpVectorTimesMatrix Non-Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {1.0, 2.0, 3.0, 4.0} {30.0, 30.0, 30.0, 30.0} + InstructionFoldingCase>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVectorTimesMatrix %v4double %v4double_1_2_3_4 %mat4v4double_1_2_3_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {30.0,30.0,30.0,30.0}), + // Test case 4: OpMatrixTimesVector Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}} {0.0, 0.0, 0.0, 0.0} + InstructionFoldingCase>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpMatrixTimesVector %v4double %mat4v4double_null %v4double_1_2_3_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {0.0,0.0,0.0,0.0}), + // Test case 5: OpMatrixTimesVector Non-Zero Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0} + InstructionFoldingCase>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpMatrixTimesVector %v4double %mat4v4double_1_2_3_4 %v4double_null\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {0.0,0.0,0.0,0.0}), + // Test case 6: OpMatrixTimesVector Non-Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {10.0, 20.0, 30.0, 40.0} + InstructionFoldingCase>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpMatrixTimesVector %v4double %mat4v4double_1_2_3_4 %v4double_1_2_3_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {10.0,20.0,30.0,40.0}) +)); + using FloatVectorInstructionFoldingTest = ::testing::TestWithParam>>; @@ -843,7 +1128,78 @@ INSTANTIATE_TEST_SUITE_P(TestCase, FloatVectorInstructionFoldingTest, "%2 = OpExtInst %v2float %1 FMix %v2float_2_3 %v2float_0_0 %v2float_0p2_0p5\n" + "OpReturn\n" + "OpFunctionEnd", - 2, {1.6f,1.5f}) + 2, {1.6f,1.5f}), + // Test case 1: bit-cast unsigned int vector {0x3f800000, 0xbf800000} to + // float vector + InstructionFoldingCase>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %v2float %v2uint_0x3f800000_0xbf800000\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {1.0f,-1.0f}), + // Test case 2: bit-cast long int 0xbf8000003f800000 to float vector + InstructionFoldingCase>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %v2float %long_0xbf8000003f800000\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {1.0f,-1.0f}), + // Test case 3: OpVectorTimesMatrix Non-Zero Zero {{0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}} {1.0, 2.0, 3.0, 4.0} {0.0, 0.0, 0.0, 0.0} + InstructionFoldingCase>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVectorTimesMatrix %v4float %v4float_1_2_3_4 %mat4v4float_null\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {0.0f,0.0f,0.0f,0.0f}), + // Test case 4: OpVectorTimesMatrix Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0} + InstructionFoldingCase>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVectorTimesMatrix %v4float %v4float_null %mat4v4float_1_2_3_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {0.0f,0.0f,0.0f,0.0f}), + // Test case 5: OpVectorTimesMatrix Non-Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {1.0, 2.0, 3.0, 4.0} {30.0, 30.0, 30.0, 30.0} + InstructionFoldingCase>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVectorTimesMatrix %v4float %v4float_1_2_3_4 %mat4v4float_1_2_3_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {30.0f,30.0f,30.0f,30.0f}), + // Test case 6: OpMatrixTimesVector Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}} {0.0, 0.0, 0.0, 0.0} + InstructionFoldingCase>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpMatrixTimesVector %v4float %mat4v4float_null %v4float_1_2_3_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {0.0f,0.0f,0.0f,0.0f}), + // Test case 7: OpMatrixTimesVector Non-Zero Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0} + InstructionFoldingCase>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpMatrixTimesVector %v4float %mat4v4float_1_2_3_4 %v4float_null\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {0.0f,0.0f,0.0f,0.0f}), + // Test case 8: OpMatrixTimesVector Non-Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {10.0, 20.0, 30.0, 40.0} + InstructionFoldingCase>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpMatrixTimesVector %v4float %mat4v4float_1_2_3_4 %v4float_1_2_3_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {10.0f,20.0f,30.0f,40.0f}) )); // clang-format on using BooleanInstructionFoldingTest = @@ -1854,7 +2210,39 @@ INSTANTIATE_TEST_SUITE_P(FloatConstantFoldingTest, FloatInstructionFoldingTest, "%2 = OpExtInst %float %1 Pow %float_2 %float_3\n" + "OpReturn\n" + "OpFunctionEnd", - 2, 8.0) + 2, 8.0), + // Test case 43: Fold 1.0 / -0.0. + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %float %float_1 %float_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -std::numeric_limits::infinity()), + // Test case 44: Fold -1.0 / -0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %float %float_n1 %float_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits::infinity()), + // Test case 45: Fold 0.0 / 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %float %float_0 %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits::quiet_NaN()), + // Test case 46: Fold 0.0 / -0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %float %float_0 %float_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits::quiet_NaN()) )); // clang-format on @@ -1886,7 +2274,11 @@ TEST_P(DoubleInstructionFoldingTest, Case) { const_mrg->GetConstantFromInst(inst)->AsFloatConstant(); EXPECT_NE(result, nullptr); if (result != nullptr) { - EXPECT_EQ(result->GetDoubleValue(), tc.expected_result); + if (!std::isnan(tc.expected_result)) { + EXPECT_EQ(result->GetDoubleValue(), tc.expected_result); + } else { + EXPECT_TRUE(std::isnan(result->GetDoubleValue())); + } } } } @@ -2089,7 +2481,39 @@ INSTANTIATE_TEST_SUITE_P(DoubleConstantFoldingTest, DoubleInstructionFoldingTest "%2 = OpExtInst %double %1 Pow %double_2 %double_3\n" + "OpReturn\n" + "OpFunctionEnd", - 2, 8.0) + 2, 8.0), + // Test case 23: Fold 1.0 / -0.0. + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %double %double_1 %double_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -std::numeric_limits::infinity()), + // Test case 24: Fold -1.0 / -0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %double %double_n1 %double_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits::infinity()), + // Test case 25: Fold 0.0 / 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %double %double_0 %double_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits::quiet_NaN()), + // Test case 26: Fold 0.0 / -0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %double %double_0 %double_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits::quiet_NaN()) )); // clang-format on @@ -3475,7 +3899,19 @@ INSTANTIATE_TEST_SUITE_P(CompositeExtractFoldingTest, GeneralInstructionFoldingT "%4 = OpCompositeExtract %int %3 2\n" + "OpReturn\n" + "OpFunctionEnd", - 4, INT_0_ID) + 4, INT_0_ID), + // Test case 15: + // Don't fold extract fed by construct with vector result if the index is + // past the last element. + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpCompositeConstruct %v2int %int_0 %int_0\n" + + "%3 = OpCompositeConstruct %v4int %2 %100 %int_0\n" + + "%4 = OpCompositeExtract %int %3 4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, 0) )); INSTANTIATE_TEST_SUITE_P(CompositeConstructFoldingTest, GeneralInstructionFoldingTest, @@ -5458,7 +5894,109 @@ INSTANTIATE_TEST_SUITE_P(MergeMulTest, MatchingInstructionFoldingTest, "%5 = OpFMul %float %4 %2\n" + "OpReturn\n" + "OpFunctionEnd\n", - 5, true) + 5, true), + // Test case 25: fold overflowing signed 32 bit imuls + // (x * 1073741824) * 2 = x * int_min + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32\n" + + "; CHECK: [[int_min:%\\w+]] = OpConstant [[int]] -2147483648\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpIMul [[int]] [[ld]] [[int_min]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpIMul %int %2 %int_1073741824\n" + + "%4 = OpIMul %int %3 %int_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 26: fold overflowing signed 64 bit imuls + // (x * 4611686018427387904) * 2 = x * long_min + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64\n" + + "; CHECK: [[long_min:%\\w+]] = OpConstant [[long]] -9223372036854775808\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpIMul [[long]] [[ld]] [[long_min]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpIMul %long %2 %long_4611686018427387904\n" + + "%4 = OpIMul %long %3 %long_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 27: fold overflowing 32 bit unsigned imuls + // (x * 2147483649) * 2 = x * 2 + InstructionFoldingCase( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_2:%\\w+]] = OpConstant [[uint]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: %4 = OpIMul [[uint]] [[ld]] [[uint_2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%2 = OpLoad %uint %var\n" + + "%3 = OpIMul %uint %2 %uint_2147483649\n" + + "%4 = OpIMul %uint %3 %uint_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 28: fold overflowing 64 bit unsigned imuls + // (x * 9223372036854775809) * 2 = x * 2 + InstructionFoldingCase( + Header() + + "; CHECK: [[ulong:%\\w+]] = OpTypeInt 64 0\n" + + "; CHECK: [[ulong_2:%\\w+]] = OpConstant [[ulong]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[ulong]]\n" + + "; CHECK: %4 = OpIMul [[ulong]] [[ld]] [[ulong_2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_ulong Function\n" + + "%2 = OpLoad %ulong %var\n" + + "%3 = OpIMul %ulong %2 %ulong_9223372036854775809\n" + + "%4 = OpIMul %ulong %3 %ulong_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 29: fold underflowing signed 32 bit imuls + // (x * (-858993459)) * 10 = x * 2 + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32\n" + + "; CHECK: [[int_2:%\\w+]] = OpConstant [[int]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpIMul [[int]] [[ld]] [[int_2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpIMul %int %2 %int_n858993459\n" + + "%4 = OpIMul %int %3 %int_10\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 30: fold underflowing signed 64 bit imuls + // (x * (-3689348814741910323)) * 10 = x * 2 + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64\n" + + "; CHECK: [[long_2:%\\w+]] = OpConstant [[long]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpIMul [[long]] [[ld]] [[long_2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpIMul %long %2 %long_n3689348814741910323\n" + + "%4 = OpIMul %long %3 %long_10\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true) )); INSTANTIATE_TEST_SUITE_P(MergeDivTest, MatchingInstructionFoldingTest, @@ -5618,15 +6156,11 @@ INSTANTIATE_TEST_SUITE_P(MergeDivTest, MatchingInstructionFoldingTest, "OpReturn\n" + "OpFunctionEnd\n", 4, false), - // Test case 11: merge sdiv of snegate - // (-x) / 2 = x / -2 + // Test case 11: Do not merge sdiv of snegate. If %2 is INT_MIN, then the + // sign of %3 will be the same as %2. This cannot be accounted for in OpSDiv. + // Specifically, (-INT_MIN) / 2 != INT_MIN / -2. InstructionFoldingCase( Header() + - "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + - "; CHECK: OpConstant [[int]] -2147483648\n" + - "; CHECK: [[int_n2:%\\w+]] = OpConstant [[int]] -2{{[[:space:]]}}\n" + - "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + - "; CHECK: %4 = OpSDiv [[int]] [[ld]] [[int_n2]]\n" + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + "%var = OpVariable %_ptr_int Function\n" + @@ -5635,16 +6169,12 @@ INSTANTIATE_TEST_SUITE_P(MergeDivTest, MatchingInstructionFoldingTest, "%4 = OpSDiv %int %3 %int_2\n" + "OpReturn\n" + "OpFunctionEnd\n", - 4, true), - // Test case 12: merge sdiv of snegate - // 2 / (-x) = -2 / x + 4, false), + // Test case 12: Do not merge sdiv of snegate. If %2 is INT_MIN, then the + // sign of %3 will be the same as %2. This cannot be accounted for in OpSDiv. + // Specifically, 2 / (-INT_MIN) != -2 / INT_MIN. InstructionFoldingCase( Header() + - "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + - "; CHECK: OpConstant [[int]] -2147483648\n" + - "; CHECK: [[int_n2:%\\w+]] = OpConstant [[int]] -2{{[[:space:]]}}\n" + - "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + - "; CHECK: %4 = OpSDiv [[int]] [[int_n2]] [[ld]]\n" + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + "%var = OpVariable %_ptr_int Function\n" + @@ -5653,7 +6183,7 @@ INSTANTIATE_TEST_SUITE_P(MergeDivTest, MatchingInstructionFoldingTest, "%4 = OpSDiv %int %int_2 %3\n" + "OpReturn\n" + "OpFunctionEnd\n", - 4, true), + 4, false), // Test case 13: Don't merge // (x / {null}) / {null} InstructionFoldingCase( @@ -5704,7 +6234,33 @@ INSTANTIATE_TEST_SUITE_P(MergeDivTest, MatchingInstructionFoldingTest, "%5 = OpFDiv %float %4 %2\n" + "OpReturn\n" + "OpFunctionEnd\n", - 5, true) + 5, true), + // Test case 16: Do not merge udiv of snegate + // (-x) / 2u + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%2 = OpLoad %uint %var\n" + + "%3 = OpSNegate %uint %2\n" + + "%4 = OpUDiv %uint %3 %uint_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, false), + // Test case 17: Do not merge udiv of snegate + // 2u / (-x) + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%2 = OpLoad %uint %var\n" + + "%3 = OpSNegate %uint %2\n" + + "%4 = OpUDiv %uint %uint_2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, false) )); INSTANTIATE_TEST_SUITE_P(MergeAddTest, MatchingInstructionFoldingTest, @@ -5912,6 +6468,108 @@ INSTANTIATE_TEST_SUITE_P(MergeAddTest, MatchingInstructionFoldingTest, "%4 = OpFAdd %float %float_2 %3\n" + "OpReturn\n" + "OpFunctionEnd\n", + 4, true), + // Test case 12: fold overflowing signed 32 bit iadds + // (x + int_max) + 1 = x + int_min + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32\n" + + "; CHECK: [[int_min:%\\w+]] = OpConstant [[int]] -2147483648\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpIAdd [[int]] [[ld]] [[int_min]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpIAdd %int %2 %int_max\n" + + "%4 = OpIAdd %int %3 %int_1\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 13: fold overflowing signed 64 bit iadds + // (x + long_max) + 1 = x + long_min + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64\n" + + "; CHECK: [[long_min:%\\w+]] = OpConstant [[long]] -9223372036854775808\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpIAdd [[long]] [[ld]] [[long_min]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpIAdd %long %2 %long_max\n" + + "%4 = OpIAdd %long %3 %long_1\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 14: fold overflowing 32 bit unsigned iadds + // (x + uint_max) + 2 = x + 1 + InstructionFoldingCase( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: %4 = OpIAdd [[uint]] [[ld]] [[uint_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%2 = OpLoad %uint %var\n" + + "%3 = OpIAdd %uint %2 %uint_max\n" + + "%4 = OpIAdd %uint %3 %uint_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 15: fold overflowing 64 bit unsigned iadds + // (x + ulong_max) + 2 = x + 1 + InstructionFoldingCase( + Header() + + "; CHECK: [[ulong:%\\w+]] = OpTypeInt 64 0\n" + + "; CHECK: [[ulong_1:%\\w+]] = OpConstant [[ulong]] 1\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[ulong]]\n" + + "; CHECK: %4 = OpIAdd [[ulong]] [[ld]] [[ulong_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_ulong Function\n" + + "%2 = OpLoad %ulong %var\n" + + "%3 = OpIAdd %ulong %2 %ulong_max\n" + + "%4 = OpIAdd %ulong %3 %ulong_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 16: fold underflowing signed 32 bit iadds + // (x + int_min) + (-1) = x + int_max + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32\n" + + "; CHECK: [[int_max:%\\w+]] = OpConstant [[int]] 2147483647\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpIAdd [[int]] [[ld]] [[int_max]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpIAdd %int %2 %int_min\n" + + "%4 = OpIAdd %int %3 %int_n1\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 17: fold underflowing signed 64 bit iadds + // (x + long_min) + (-1) = x + long_max + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64\n" + + "; CHECK: [[long_max:%\\w+]] = OpConstant [[long]] 9223372036854775807\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpIAdd [[long]] [[ld]] [[long_max]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpIAdd %long %2 %long_min\n" + + "%4 = OpIAdd %long %3 %long_n1\n" + + "OpReturn\n" + + "OpFunctionEnd\n", 4, true) )); @@ -6280,6 +6938,40 @@ INSTANTIATE_TEST_SUITE_P(MergeSubTest, MatchingInstructionFoldingTest, "%4 = OpISub %int %int_2 %3\n" + "OpReturn\n" + "OpFunctionEnd\n", + 4, true), + // Test case 14: fold overflowing signed 32 bit isubs + // (x - int_max) - 1 = x - int_min + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32\n" + + "; CHECK: [[int_min:%\\w+]] = OpConstant [[int]] -2147483648\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpISub [[int]] [[ld]] [[int_min]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpISub %int %2 %int_max\n" + + "%4 = OpISub %int %3 %int_1\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 15: fold overflowing signed 64 bit isubs + // (x - long_max) - 1 = x - long_min + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64\n" + + "; CHECK: [[long_min:%\\w+]] = OpConstant [[long]] -9223372036854775808\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpISub [[long]] [[ld]] [[long_min]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpISub %long %2 %long_max\n" + + "%4 = OpISub %long %3 %long_1\n" + + "OpReturn\n" + + "OpFunctionEnd\n", 4, true) )); @@ -6391,7 +7083,7 @@ INSTANTIATE_TEST_SUITE_P(SelectFoldingTest, MatchingInstructionFoldingTest, 4, true) )); -INSTANTIATE_TEST_SUITE_P(CompositeExtractMatchingTest, MatchingInstructionFoldingTest, +INSTANTIATE_TEST_SUITE_P(CompositeExtractOrInsertMatchingTest, MatchingInstructionFoldingTest, ::testing::Values( // Test case 0: Extracting from result of consecutive shuffles of differing // size. @@ -6531,7 +7223,145 @@ INSTANTIATE_TEST_SUITE_P(CompositeExtractMatchingTest, MatchingInstructionFoldin "%4 = OpCompositeExtract %int %3 1\n" + "OpReturn\n" + "OpFunctionEnd", - 4, true) + 4, true), + // Test case 8: Inserting every element of a vector turns into a composite construct. + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK-DAG: [[v4:%\\w+]] = OpTypeVector [[int]] 4\n" + + "; CHECK-DAG: [[int1:%\\w+]] = OpConstant [[int]] 1\n" + + "; CHECK-DAG: [[int2:%\\w+]] = OpConstant [[int]] 2\n" + + "; CHECK-DAG: [[int3:%\\w+]] = OpConstant [[int]] 3\n" + + "; CHECK: [[construct:%\\w+]] = OpCompositeConstruct [[v4]] %100 [[int1]] [[int2]] [[int3]]\n" + + "; CHECK: %5 = OpCopyObject [[v4]] [[construct]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpCompositeInsert %v4int %100 %v4int_undef 0\n" + + "%3 = OpCompositeInsert %v4int %int_1 %2 1\n" + + "%4 = OpCompositeInsert %v4int %int_2 %3 2\n" + + "%5 = OpCompositeInsert %v4int %int_3 %4 3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 5, true), + // Test case 9: Inserting every element of a vector turns into a composite construct in a different order. + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK-DAG: [[v4:%\\w+]] = OpTypeVector [[int]] 4\n" + + "; CHECK-DAG: [[int1:%\\w+]] = OpConstant [[int]] 1\n" + + "; CHECK-DAG: [[int2:%\\w+]] = OpConstant [[int]] 2\n" + + "; CHECK-DAG: [[int3:%\\w+]] = OpConstant [[int]] 3\n" + + "; CHECK: [[construct:%\\w+]] = OpCompositeConstruct [[v4]] %100 [[int1]] [[int2]] [[int3]]\n" + + "; CHECK: %5 = OpCopyObject [[v4]] [[construct]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpCompositeInsert %v4int %100 %v4int_undef 0\n" + + "%4 = OpCompositeInsert %v4int %int_2 %2 2\n" + + "%3 = OpCompositeInsert %v4int %int_1 %4 1\n" + + "%5 = OpCompositeInsert %v4int %int_3 %3 3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 5, true), + // Test case 10: Check multiple inserts to the same position are handled correctly. + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK-DAG: [[v4:%\\w+]] = OpTypeVector [[int]] 4\n" + + "; CHECK-DAG: [[int1:%\\w+]] = OpConstant [[int]] 1\n" + + "; CHECK-DAG: [[int2:%\\w+]] = OpConstant [[int]] 2\n" + + "; CHECK-DAG: [[int3:%\\w+]] = OpConstant [[int]] 3\n" + + "; CHECK: [[construct:%\\w+]] = OpCompositeConstruct [[v4]] %100 [[int1]] [[int2]] [[int3]]\n" + + "; CHECK: %6 = OpCopyObject [[v4]] [[construct]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpCompositeInsert %v4int %100 %v4int_undef 0\n" + + "%3 = OpCompositeInsert %v4int %int_2 %2 2\n" + + "%4 = OpCompositeInsert %v4int %int_4 %3 1\n" + + "%5 = OpCompositeInsert %v4int %int_1 %4 1\n" + + "%6 = OpCompositeInsert %v4int %int_3 %5 3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 6, true), + // Test case 11: The last indexes are 0 and 1, but they have different first indexes. This should not be folded. + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpCompositeInsert %m2x2int %100 %m2x2int_undef 0 0\n" + + "%3 = OpCompositeInsert %m2x2int %int_1 %2 1 1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, false), + // Test case 12: Don't fold when there is a partial insertion. + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpCompositeInsert %m2x2int %v2int_1_0 %m2x2int_undef 0\n" + + "%3 = OpCompositeInsert %m2x2int %int_4 %2 0 0\n" + + "%4 = OpCompositeInsert %m2x2int %v2int_2_3 %3 1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, false), + // Test case 13: Insert into a column of a matrix + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK-DAG: [[v2:%\\w+]] = OpTypeVector [[int]] 2\n" + + "; CHECK: [[m2x2:%\\w+]] = OpTypeMatrix [[v2]] 2\n" + + "; CHECK-DAG: [[m2x2_undef:%\\w+]] = OpUndef [[m2x2]]\n" + + "; CHECK-DAG: [[int1:%\\w+]] = OpConstant [[int]] 1\n" + +// We keep this insert in the chain. DeadInsertElimPass should remove it. + "; CHECK: [[insert:%\\w+]] = OpCompositeInsert [[m2x2]] %100 [[m2x2_undef]] 0 0\n" + + "; CHECK: [[construct:%\\w+]] = OpCompositeConstruct [[v2]] %100 [[int1]]\n" + + "; CHECK: %3 = OpCompositeInsert [[m2x2]] [[construct]] [[insert]] 0\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpCompositeInsert %m2x2int %100 %m2x2int_undef 0 0\n" + + "%3 = OpCompositeInsert %m2x2int %int_1 %2 0 1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, true), + // Test case 14: Insert all elements of the matrix. + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK-DAG: [[v2:%\\w+]] = OpTypeVector [[int]] 2\n" + + "; CHECK: [[m2x2:%\\w+]] = OpTypeMatrix [[v2]] 2\n" + + "; CHECK-DAG: [[m2x2_undef:%\\w+]] = OpUndef [[m2x2]]\n" + + "; CHECK-DAG: [[int1:%\\w+]] = OpConstant [[int]] 1\n" + + "; CHECK-DAG: [[int2:%\\w+]] = OpConstant [[int]] 2\n" + + "; CHECK-DAG: [[int3:%\\w+]] = OpConstant [[int]] 3\n" + + "; CHECK: [[c0:%\\w+]] = OpCompositeConstruct [[v2]] %100 [[int1]]\n" + + "; CHECK: [[c1:%\\w+]] = OpCompositeConstruct [[v2]] [[int2]] [[int3]]\n" + + "; CHECK: [[matrix:%\\w+]] = OpCompositeConstruct [[m2x2]] [[c0]] [[c1]]\n" + + "; CHECK: %5 = OpCopyObject [[m2x2]] [[matrix]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpCompositeConstruct %v2int %100 %int_1\n" + + "%3 = OpCompositeInsert %m2x2int %2 %m2x2int_undef 0\n" + + "%4 = OpCompositeInsert %m2x2int %int_2 %3 1 0\n" + + "%5 = OpCompositeInsert %m2x2int %int_3 %4 1 1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 5, true), + // Test case 15: Replace construct with extract when reconstructing a member + // of another object. + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[v2:%\\w+]] = OpTypeVector [[int]] 2\n" + + "; CHECK: [[m2x2:%\\w+]] = OpTypeMatrix [[v2]] 2\n" + + "; CHECK: [[m2x2_undef:%\\w+]] = OpUndef [[m2x2]]\n" + + "; CHECK: %5 = OpCompositeExtract [[v2]] [[m2x2_undef]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%3 = OpCompositeExtract %int %m2x2int_undef 1 0\n" + + "%4 = OpCompositeExtract %int %m2x2int_undef 1 1\n" + + "%5 = OpCompositeConstruct %v2int %3 %4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 5, true) )); INSTANTIATE_TEST_SUITE_P(DotProductMatchingTest, MatchingInstructionFoldingTest, @@ -6616,12 +7446,297 @@ INSTANTIATE_TEST_SUITE_P(DotProductMatchingTest, MatchingInstructionFoldingTest, 3, true) )); +INSTANTIATE_TEST_SUITE_P(VectorShuffleMatchingTest, MatchingInstructionFoldingTest, +::testing::Values( + // Test case 0: Using OpDot to extract last element. + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[v2int:%\\w+]] = OpTypeVector [[int]] 2{{[[:space:]]}}\n" + + "; CHECK: [[null:%\\w+]] = OpConstantNull [[v2int]]\n" + + "; CHECK: OpVectorShuffle\n" + + "; CHECK: %3 = OpVectorShuffle [[v2int]] [[null]] {{%\\w+}} 4294967295 2\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpVectorShuffle %v2int %v2int_null %v2int_2_3 3 0xFFFFFFFF \n" + + "%3 = OpVectorShuffle %v2int %2 %v2int_2_3 1 2 \n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, true) + )); + +INSTANTIATE_TEST_SUITE_P(FmaGenerationMatchingTest, MatchingInstructionFoldingTest, +::testing::Values( + // Test case 0: (x * y) + a = Fma(x, y, a) + InstructionFoldingCase( + Header() + + "; CHECK: [[ext:%\\w+]] = OpExtInstImport \"GLSL.std.450\"\n" + + "; CHECK: OpFunction\n" + + "; CHECK: [[x:%\\w+]] = OpVariable {{%\\w+}} Function\n" + + "; CHECK: [[y:%\\w+]] = OpVariable {{%\\w+}} Function\n" + + "; CHECK: [[a:%\\w+]] = OpVariable {{%\\w+}} Function\n" + + "; CHECK: [[lx:%\\w+]] = OpLoad {{%\\w+}} [[x]]\n" + + "; CHECK: [[ly:%\\w+]] = OpLoad {{%\\w+}} [[y]]\n" + + "; CHECK: [[la:%\\w+]] = OpLoad {{%\\w+}} [[a]]\n" + + "; CHECK: [[fma:%\\w+]] = OpExtInst {{%\\w+}} [[ext]] Fma [[lx]] [[ly]] [[la]]\n" + + "; CHECK: OpStore {{%\\w+}} [[fma]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%x = OpVariable %_ptr_float Function\n" + + "%y = OpVariable %_ptr_float Function\n" + + "%a = OpVariable %_ptr_float Function\n" + + "%lx = OpLoad %float %x\n" + + "%ly = OpLoad %float %y\n" + + "%mul = OpFMul %float %lx %ly\n" + + "%la = OpLoad %float %a\n" + + "%3 = OpFAdd %float %mul %la\n" + + "OpStore %a %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, true), + // Test case 1: a + (x * y) = Fma(x, y, a) + InstructionFoldingCase( + Header() + + "; CHECK: [[ext:%\\w+]] = OpExtInstImport \"GLSL.std.450\"\n" + + "; CHECK: OpFunction\n" + + "; CHECK: [[x:%\\w+]] = OpVariable {{%\\w+}} Function\n" + + "; CHECK: [[y:%\\w+]] = OpVariable {{%\\w+}} Function\n" + + "; CHECK: [[a:%\\w+]] = OpVariable {{%\\w+}} Function\n" + + "; CHECK: [[lx:%\\w+]] = OpLoad {{%\\w+}} [[x]]\n" + + "; CHECK: [[ly:%\\w+]] = OpLoad {{%\\w+}} [[y]]\n" + + "; CHECK: [[la:%\\w+]] = OpLoad {{%\\w+}} [[a]]\n" + + "; CHECK: [[fma:%\\w+]] = OpExtInst {{%\\w+}} [[ext]] Fma [[lx]] [[ly]] [[la]]\n" + + "; CHECK: OpStore {{%\\w+}} [[fma]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%x = OpVariable %_ptr_float Function\n" + + "%y = OpVariable %_ptr_float Function\n" + + "%a = OpVariable %_ptr_float Function\n" + + "%lx = OpLoad %float %x\n" + + "%ly = OpLoad %float %y\n" + + "%mul = OpFMul %float %lx %ly\n" + + "%la = OpLoad %float %a\n" + + "%3 = OpFAdd %float %la %mul\n" + + "OpStore %a %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, true), + // Test case 2: (x * y) + a = Fma(x, y, a) with vectors + InstructionFoldingCase( + Header() + + "; CHECK: [[ext:%\\w+]] = OpExtInstImport \"GLSL.std.450\"\n" + + "; CHECK: OpFunction\n" + + "; CHECK: [[x:%\\w+]] = OpVariable {{%\\w+}} Function\n" + + "; CHECK: [[y:%\\w+]] = OpVariable {{%\\w+}} Function\n" + + "; CHECK: [[a:%\\w+]] = OpVariable {{%\\w+}} Function\n" + + "; CHECK: [[lx:%\\w+]] = OpLoad {{%\\w+}} [[x]]\n" + + "; CHECK: [[ly:%\\w+]] = OpLoad {{%\\w+}} [[y]]\n" + + "; CHECK: [[la:%\\w+]] = OpLoad {{%\\w+}} [[a]]\n" + + "; CHECK: [[fma:%\\w+]] = OpExtInst {{%\\w+}} [[ext]] Fma [[lx]] [[ly]] [[la]]\n" + + "; CHECK: OpStore {{%\\w+}} [[fma]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%x = OpVariable %_ptr_v4float Function\n" + + "%y = OpVariable %_ptr_v4float Function\n" + + "%a = OpVariable %_ptr_v4float Function\n" + + "%lx = OpLoad %v4float %x\n" + + "%ly = OpLoad %v4float %y\n" + + "%mul = OpFMul %v4float %lx %ly\n" + + "%la = OpLoad %v4float %a\n" + + "%3 = OpFAdd %v4float %mul %la\n" + + "OpStore %a %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, true), + // Test case 3: a + (x * y) = Fma(x, y, a) with vectors + InstructionFoldingCase( + Header() + + "; CHECK: [[ext:%\\w+]] = OpExtInstImport \"GLSL.std.450\"\n" + + "; CHECK: OpFunction\n" + + "; CHECK: [[x:%\\w+]] = OpVariable {{%\\w+}} Function\n" + + "; CHECK: [[y:%\\w+]] = OpVariable {{%\\w+}} Function\n" + + "; CHECK: [[a:%\\w+]] = OpVariable {{%\\w+}} Function\n" + + "; CHECK: [[lx:%\\w+]] = OpLoad {{%\\w+}} [[x]]\n" + + "; CHECK: [[ly:%\\w+]] = OpLoad {{%\\w+}} [[y]]\n" + + "; CHECK: [[la:%\\w+]] = OpLoad {{%\\w+}} [[a]]\n" + + "; CHECK: [[fma:%\\w+]] = OpExtInst {{%\\w+}} [[ext]] Fma [[lx]] [[ly]] [[la]]\n" + + "; CHECK: OpStore {{%\\w+}} [[fma]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%x = OpVariable %_ptr_float Function\n" + + "%y = OpVariable %_ptr_float Function\n" + + "%a = OpVariable %_ptr_float Function\n" + + "%lx = OpLoad %float %x\n" + + "%ly = OpLoad %float %y\n" + + "%mul = OpFMul %float %lx %ly\n" + + "%la = OpLoad %float %a\n" + + "%3 = OpFAdd %float %la %mul\n" + + "OpStore %a %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, true), + // Test 4: that the OpExtInstImport instruction is generated if it is missing. + InstructionFoldingCase( + std::string() + + "; CHECK: [[ext:%\\w+]] = OpExtInstImport \"GLSL.std.450\"\n" + + "; CHECK: OpFunction\n" + + "; CHECK: [[x:%\\w+]] = OpVariable {{%\\w+}} Function\n" + + "; CHECK: [[y:%\\w+]] = OpVariable {{%\\w+}} Function\n" + + "; CHECK: [[a:%\\w+]] = OpVariable {{%\\w+}} Function\n" + + "; CHECK: [[lx:%\\w+]] = OpLoad {{%\\w+}} [[x]]\n" + + "; CHECK: [[ly:%\\w+]] = OpLoad {{%\\w+}} [[y]]\n" + + "; CHECK: [[la:%\\w+]] = OpLoad {{%\\w+}} [[a]]\n" + + "; CHECK: [[fma:%\\w+]] = OpExtInst {{%\\w+}} [[ext]] Fma [[lx]] [[ly]] [[la]]\n" + + "; CHECK: OpStore {{%\\w+}} [[fma]]\n" + + "OpCapability Shader\n" + + "OpMemoryModel Logical GLSL450\n" + + "OpEntryPoint Fragment %main \"main\"\n" + + "OpExecutionMode %main OriginUpperLeft\n" + + "OpSource GLSL 140\n" + + "OpName %main \"main\"\n" + + "%void = OpTypeVoid\n" + + "%void_func = OpTypeFunction %void\n" + + "%bool = OpTypeBool\n" + + "%float = OpTypeFloat 32\n" + + "%_ptr_float = OpTypePointer Function %float\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%x = OpVariable %_ptr_float Function\n" + + "%y = OpVariable %_ptr_float Function\n" + + "%a = OpVariable %_ptr_float Function\n" + + "%lx = OpLoad %float %x\n" + + "%ly = OpLoad %float %y\n" + + "%mul = OpFMul %float %lx %ly\n" + + "%la = OpLoad %float %a\n" + + "%3 = OpFAdd %float %mul %la\n" + + "OpStore %a %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, true), + // Test 5: Don't fold if the multiple is marked no contract. + InstructionFoldingCase( + std::string() + + "OpCapability Shader\n" + + "OpMemoryModel Logical GLSL450\n" + + "OpEntryPoint Fragment %main \"main\"\n" + + "OpExecutionMode %main OriginUpperLeft\n" + + "OpSource GLSL 140\n" + + "OpName %main \"main\"\n" + + "OpDecorate %mul NoContraction\n" + + "%void = OpTypeVoid\n" + + "%void_func = OpTypeFunction %void\n" + + "%bool = OpTypeBool\n" + + "%float = OpTypeFloat 32\n" + + "%_ptr_float = OpTypePointer Function %float\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%x = OpVariable %_ptr_float Function\n" + + "%y = OpVariable %_ptr_float Function\n" + + "%a = OpVariable %_ptr_float Function\n" + + "%lx = OpLoad %float %x\n" + + "%ly = OpLoad %float %y\n" + + "%mul = OpFMul %float %lx %ly\n" + + "%la = OpLoad %float %a\n" + + "%3 = OpFAdd %float %mul %la\n" + + "OpStore %a %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, false), + // Test 6: Don't fold if the add is marked no contract. + InstructionFoldingCase( + std::string() + + "OpCapability Shader\n" + + "OpMemoryModel Logical GLSL450\n" + + "OpEntryPoint Fragment %main \"main\"\n" + + "OpExecutionMode %main OriginUpperLeft\n" + + "OpSource GLSL 140\n" + + "OpName %main \"main\"\n" + + "OpDecorate %3 NoContraction\n" + + "%void = OpTypeVoid\n" + + "%void_func = OpTypeFunction %void\n" + + "%bool = OpTypeBool\n" + + "%float = OpTypeFloat 32\n" + + "%_ptr_float = OpTypePointer Function %float\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%x = OpVariable %_ptr_float Function\n" + + "%y = OpVariable %_ptr_float Function\n" + + "%a = OpVariable %_ptr_float Function\n" + + "%lx = OpLoad %float %x\n" + + "%ly = OpLoad %float %y\n" + + "%mul = OpFMul %float %lx %ly\n" + + "%la = OpLoad %float %a\n" + + "%3 = OpFAdd %float %mul %la\n" + + "OpStore %a %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, false), + // Test case 7: (x * y) - a = Fma(x, y, -a) + InstructionFoldingCase( + Header() + + "; CHECK: [[ext:%\\w+]] = OpExtInstImport \"GLSL.std.450\"\n" + + "; CHECK: OpFunction\n" + + "; CHECK: [[x:%\\w+]] = OpVariable {{%\\w+}} Function\n" + + "; CHECK: [[y:%\\w+]] = OpVariable {{%\\w+}} Function\n" + + "; CHECK: [[a:%\\w+]] = OpVariable {{%\\w+}} Function\n" + + "; CHECK: [[lx:%\\w+]] = OpLoad {{%\\w+}} [[x]]\n" + + "; CHECK: [[ly:%\\w+]] = OpLoad {{%\\w+}} [[y]]\n" + + "; CHECK: [[la:%\\w+]] = OpLoad {{%\\w+}} [[a]]\n" + + "; CHECK: [[na:%\\w+]] = OpFNegate {{%\\w+}} [[la]]\n" + + "; CHECK: [[fma:%\\w+]] = OpExtInst {{%\\w+}} [[ext]] Fma [[lx]] [[ly]] [[na]]\n" + + "; CHECK: OpStore {{%\\w+}} [[fma]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%x = OpVariable %_ptr_float Function\n" + + "%y = OpVariable %_ptr_float Function\n" + + "%a = OpVariable %_ptr_float Function\n" + + "%lx = OpLoad %float %x\n" + + "%ly = OpLoad %float %y\n" + + "%mul = OpFMul %float %lx %ly\n" + + "%la = OpLoad %float %a\n" + + "%3 = OpFSub %float %mul %la\n" + + "OpStore %a %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, true), + // Test case 8: a - (x * y) = Fma(-x, y, a) + InstructionFoldingCase( + Header() + + "; CHECK: [[ext:%\\w+]] = OpExtInstImport \"GLSL.std.450\"\n" + + "; CHECK: OpFunction\n" + + "; CHECK: [[x:%\\w+]] = OpVariable {{%\\w+}} Function\n" + + "; CHECK: [[y:%\\w+]] = OpVariable {{%\\w+}} Function\n" + + "; CHECK: [[a:%\\w+]] = OpVariable {{%\\w+}} Function\n" + + "; CHECK: [[lx:%\\w+]] = OpLoad {{%\\w+}} [[x]]\n" + + "; CHECK: [[ly:%\\w+]] = OpLoad {{%\\w+}} [[y]]\n" + + "; CHECK: [[la:%\\w+]] = OpLoad {{%\\w+}} [[a]]\n" + + "; CHECK: [[nx:%\\w+]] = OpFNegate {{%\\w+}} [[lx]]\n" + + "; CHECK: [[fma:%\\w+]] = OpExtInst {{%\\w+}} [[ext]] Fma [[nx]] [[ly]] [[la]]\n" + + "; CHECK: OpStore {{%\\w+}} [[fma]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%x = OpVariable %_ptr_float Function\n" + + "%y = OpVariable %_ptr_float Function\n" + + "%a = OpVariable %_ptr_float Function\n" + + "%lx = OpLoad %float %x\n" + + "%ly = OpLoad %float %y\n" + + "%mul = OpFMul %float %lx %ly\n" + + "%la = OpLoad %float %a\n" + + "%3 = OpFSub %float %la %mul\n" + + "OpStore %a %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, true) +)); + using MatchingInstructionWithNoResultFoldingTest = ::testing::TestWithParam>; // Test folding instructions that do not have a result. The instruction // that will be folded is the last instruction before the return. If there -// are multiple returns, there is not guarentee which one is used. +// are multiple returns, there is not guarantee which one is used. TEST_P(MatchingInstructionWithNoResultFoldingTest, Case) { const auto& tc = GetParam(); @@ -6949,6 +8064,27 @@ INSTANTIATE_TEST_SUITE_P(VectorShuffleMatchingTest, MatchingInstructionWithNoRes "%9 = OpVectorShuffle %v4double %7 %8 2 0 1 4294967295\n" + "OpReturn\n" + "OpFunctionEnd", + 9, true), + // Test case 14: Shuffle with undef literal and change size of first input vector. + InstructionFoldingCase( + Header() + + "; CHECK: [[double:%\\w+]] = OpTypeFloat 64\n" + + "; CHECK: [[v4double:%\\w+]] = OpTypeVector [[double]] 2\n" + + "; CHECK: OpVectorShuffle\n" + + "; CHECK: OpVectorShuffle {{%\\w+}} %5 %7 0 1 4 4294967295\n" + + "; CHECK: OpReturn\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVariable %_ptr_v4double Function\n" + + "%3 = OpVariable %_ptr_v4double Function\n" + + "%4 = OpVariable %_ptr_v4double Function\n" + + "%5 = OpLoad %v4double %2\n" + + "%6 = OpLoad %v4double %3\n" + + "%7 = OpLoad %v4double %4\n" + + "%8 = OpVectorShuffle %v2double %5 %5 0 1\n" + + "%9 = OpVectorShuffle %v4double %8 %7 0 1 2 4294967295\n" + + "OpReturn\n" + + "OpFunctionEnd", 9, true) )); diff --git a/test/opt/freeze_spec_const_test.cpp b/test/opt/freeze_spec_const_test.cpp index e5999cec..ad0fc32e 100644 --- a/test/opt/freeze_spec_const_test.cpp +++ b/test/opt/freeze_spec_const_test.cpp @@ -128,6 +128,51 @@ TEST_F(FreezeSpecConstantValueRemoveDecorationTest, /* skip_nop = */ true); } +TEST_F(FreezeSpecConstantValueRemoveDecorationTest, + RemoveDecorationForLocalSizeIdWithSpecId) { + std::vector text = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint GLCompute %2 \"main\"", + "OpExecutionModeId %2 LocalSizeId %uint_32 %uint_1 %uint_1_0", + "OpSource GLSL 450", + "OpDecorate %3 SpecId 18", + "OpDecorate %5 SpecId 19", + "%void = OpTypeVoid", + "%9 = OpTypeFunction %void", + "%uint = OpTypeInt 32 0", + "%uint_32 = OpSpecConstant %uint 32", + "%uint_1 = OpConstant %uint 1", + "%uint_1_0 = OpSpecConstant %uint 1", + "%2 = OpFunction %void None %9", + "%11 = OpLabel", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + std::string expected_disassembly = SelectiveJoin(text, [](const char* line) { + return std::string(line).find("SpecId") != std::string::npos; + }); + std::vector> replacement_pairs = { + {"%uint_32 = OpSpecConstant %uint 32", "%uint_32 = OpConstant %uint 32"}, + {"%uint_1_0 = OpSpecConstant %uint 1", "%uint_1_0 = OpConstant %uint 1"}, + }; + for (auto& p : replacement_pairs) { + EXPECT_TRUE(FindAndReplace(&expected_disassembly, p.first, p.second)) + << "text:\n" + << expected_disassembly << "\n" + << "find_str:\n" + << p.first << "\n" + << "replace_str:\n" + << p.second << "\n"; + } + SinglePassRunAndCheck(JoinAllInsts(text), + expected_disassembly, + /* skip_nop = */ true); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/function_test.cpp b/test/opt/function_test.cpp index af25bacc..34a03871 100644 --- a/test/opt/function_test.cpp +++ b/test/opt/function_test.cpp @@ -296,6 +296,65 @@ OpFunctionEnd EXPECT_EQ(1, non_semantic_ids.count(8)); } +TEST(FunctionTest, ReorderBlocksinStructuredOrder) { + // The spir-v has the basic block in a random order. We want to reorder them + // in structured order. + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %100 "PSMain" + OpExecutionMode %PSMain OriginUpperLeft + OpSource HLSL 600 + %int = OpTypeInt 32 1 + %void = OpTypeVoid + %19 = OpTypeFunction %void + %bool = OpTypeBool +%undef_bool = OpUndef %bool +%undef_int = OpUndef %int + %100 = OpFunction %void None %19 + %11 = OpLabel + OpSelectionMerge %10 None + OpSwitch %undef_int %3 0 %2 10 %1 + %2 = OpLabel + OpReturn + %7 = OpLabel + OpBranch %8 + %3 = OpLabel + OpBranch %4 + %10 = OpLabel + OpReturn + %9 = OpLabel + OpBranch %10 + %8 = OpLabel + OpBranch %4 + %4 = OpLabel + OpLoopMerge %9 %8 None + OpBranchConditional %undef_bool %5 %9 + %1 = OpLabel + OpReturn + %6 = OpLabel + OpBranch %7 + %5 = OpLabel + OpSelectionMerge %7 None + OpBranchConditional %undef_bool %6 %7 + OpFunctionEnd +)"; + + std::unique_ptr ctx = + spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_TRUE(ctx); + auto* func = spvtest::GetFunction(ctx->module(), 100); + ASSERT_TRUE(func); + func->ReorderBasicBlocksInStructuredOrder(); + + auto first_block = func->begin(); + auto bb = first_block; + for (++bb; bb != func->end(); ++bb) { + EXPECT_EQ(bb->id(), (bb - first_block)); + } +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/graphics_robust_access_test.cpp b/test/opt/graphics_robust_access_test.cpp index 4b2cd447..057b909d 100644 --- a/test/opt/graphics_robust_access_test.cpp +++ b/test/opt/graphics_robust_access_test.cpp @@ -1242,7 +1242,7 @@ TEST_F(GraphicsRobustAccessTest, ACArrayRTArrayStructVectorElem) { ; CHECK-DAG: %int_9 = OpConstant %int 9 ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647 ; CHECK: OpLabel - ; This access chain is manufatured only so we can compute the array length. + ; This access chain is manufactured only so we can compute the array length. ; Note that the %int_9 is already clamped ; CHECK: %[[ssbo_base:\w+]] = )" << ac << R"( %[[ssbo_p]] %var %int_9 @@ -1548,6 +1548,105 @@ OpFunctionEnd true); } +TEST_F(GraphicsRobustAccessTest, ReplaceIndexReportsChanged) { + // A ClusterFuzz generated shader that triggered a + // "Binary size unexpectedly changed despite the optimizer saying there was no + // change" assertion. + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/4166. + std::string shader = R"( +; SPIR-V +; Version: 1.0 +; Generator: Google Shaderc over Glslang; 245 +; Bound: 41 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "else" %gl_GlobalInvocationID + OpExecutionMode %main LocalSize 1 1 3338665985 + OpSource GLSL 450 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %index "index" + OpName %gl_GlobalInvocationID "gl_GlobalInvocationID" + OpName %S "S" + OpMemberName %_struct_24 0 "" + OpMemberName %_struct_24 1 "" + OpName %Dst "Dst" + OpMemberName %Dst 0 "s" + OpName %dst "dst" + OpName %Src "Src" + OpMemberName %Src 0 "s" + OpName %src "src" + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpMemberDecorate %_struct_24 0 Offset 64 + OpMemberDecorate %_struct_24 1 Offset 8 + OpDecorate %_arr__struct_24_uint_1 ArrayStride 16 + OpMemberDecorate %Dst 0 Offset 0 + OpDecorate %Dst BufferBlock + OpDecorate %dst DescriptorSet 0 + OpDecorate %dst Binding 1 + OpDecorate %_arr__struct_24_uint_1_0 ArrayStride 16 + OpMemberDecorate %Src 0 Offset 0 + OpDecorate %Src Block + OpDecorate %src DescriptorSet 0 + OpDecorate %src Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input + %uint_4864 = OpConstant %uint 4864 +%_ptr_Input_uint = OpTypePointer Input %uint + %uint_1 = OpConstant %uint 1 + %bool = OpTypeBool + %v2uint = OpTypeVector %uint 2 + %_struct_24 = OpTypeStruct %_ptr_Input_uint %v2uint +%_arr__struct_24_uint_1 = OpTypeArray %_struct_24 %uint_1 + %Dst = OpTypeStruct %_arr__struct_24_uint_1 +%_ptr_Uniform_Dst = OpTypePointer Uniform %Dst + %dst = OpVariable %_ptr_Uniform_Dst Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_arr__struct_24_uint_1_0 = OpTypeArray %_struct_24 %uint_1 + %Src = OpTypeStruct %_arr__struct_24_uint_1_0 +%_ptr_Uniform_Src = OpTypePointer Uniform %Src + %src = OpVariable %_ptr_Uniform_Src Uniform +%_ptr_Uniform__struct_24 = OpTypePointer Uniform %_struct_24 + %main = OpFunction %void None %3 + %5 = OpLabel + %index = OpVariable %_ptr_Function_uint Function + %14 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_4864 + %15 = OpLoad %uint %14 + OpStore %index %15 + %16 = OpLoad %uint %index + %S = OpUGreaterThanEqual %bool %16 %uint_1 + OpSelectionMerge %21 None + OpBranchConditional %S %20 %21 + %20 = OpLabel + OpReturn + %21 = OpLabel + %31 = OpLoad %uint %index + %36 = OpLoad %uint %index + %38 = OpAccessChain %_ptr_Uniform__struct_24 %src %int_0 %36 + %39 = OpLoad %_struct_24 %38 + %40 = OpAccessChain %_ptr_Uniform__struct_24 %dst %int_0 %31 + OpStore %40 %39 + OpReturn + OpFunctionEnd +)"; + + std::vector optimized_bin; + auto status = spvtools::opt::Pass::Status::Failure; + std::tie(optimized_bin, status) = + SinglePassRunToBinary(shader, false); + // Check whether the pass returns the correct modification indication. + EXPECT_EQ(status, spvtools::opt::Pass::Status::SuccessWithChange); +} + // TODO(dneto): Test access chain index wider than 64 bits? // TODO(dneto): Test struct access chain index wider than 64 bits? // TODO(dneto): OpImageTexelPointer diff --git a/test/opt/if_conversion_test.cpp b/test/opt/if_conversion_test.cpp index dee15c31..dc7f8316 100644 --- a/test/opt/if_conversion_test.cpp +++ b/test/opt/if_conversion_test.cpp @@ -328,6 +328,40 @@ OpFunctionEnd SinglePassRunAndCheck(text, text, true, true); } +TEST_F(IfConversionTest, DontFlatten) { + const std::string text = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %1 "func" %2 +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%v2uint = OpTypeVector %uint 2 +%10 = OpConstantComposite %v2uint %uint_0 %uint_1 +%11 = OpConstantComposite %v2uint %uint_1 %uint_0 +%_ptr_Output_v2uint = OpTypePointer Output %v2uint +%2 = OpVariable %_ptr_Output_v2uint Output +%13 = OpTypeFunction %void +%1 = OpFunction %void None %13 +%14 = OpLabel +OpSelectionMerge %15 DontFlatten +OpBranchConditional %true %16 %17 +%16 = OpLabel +OpBranch %15 +%17 = OpLabel +OpBranch %15 +%15 = OpLabel +%18 = OpPhi %v2uint %10 %16 %11 %17 +OpStore %2 %18 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, true, true); +} + TEST_F(IfConversionTest, LoopUntouched) { const std::string text = R"(OpCapability Shader OpMemoryModel Logical GLSL450 @@ -557,6 +591,33 @@ OpFunctionEnd SinglePassRunAndMatch(text, true); } +TEST_F(IfConversionTest, MultipleEdgesFromSameBlock) { + // If a block has two out going edges that go to the same block, then there + // can be an OpPhi instruction with fewer entries than the number of incoming + // edges. This must be handled. + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +%void = OpTypeVoid +%4 = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%true_0 = OpConstantTrue %bool +%2 = OpFunction %void None %4 +%8 = OpLabel +OpSelectionMerge %9 None +OpBranchConditional %true_0 %9 %9 +%9 = OpLabel +%10 = OpPhi %bool %true %8 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, true, true); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/inline_opaque_test.cpp b/test/opt/inline_opaque_test.cpp index 8cb8925c..e4db4325 100644 --- a/test/opt/inline_opaque_test.cpp +++ b/test/opt/inline_opaque_test.cpp @@ -226,6 +226,115 @@ OpFunctionEnd predefs + before + post_defs, predefs + after + post_defs, true, true); } +TEST_F(InlineOpaqueTest, InlineOpaqueForLinkage) { + const std::string predefs_1 = + R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %S_t "S_t" +OpMemberName %S_t 0 "v0" +OpMemberName %S_t 1 "v1" +OpMemberName %S_t 2 "smp" +OpName %foo_struct_S_t_vf2_vf21_ "foo(struct-S_t-vf2-vf21;" +OpName %s "s" +OpName %outColor "outColor" +OpName %sampler15 "sampler15" +OpName %s0 "s0" +OpName %texCoords "texCoords" +OpName %param "param" +OpDecorate %main LinkageAttributes "main" Export +)"; + + const std::string name = R"(OpName %return_value "return_value" +)"; + + const std::string predefs_2 = R"(OpDecorate %sampler15 DescriptorSet 0 +%void = OpTypeVoid +%13 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%outColor = OpVariable %_ptr_Output_v4float Output +%18 = OpTypeImage %float 2D 0 0 0 1 Unknown +%19 = OpTypeSampledImage %18 +%S_t = OpTypeStruct %v2float %v2float %19 +%_ptr_Function_S_t = OpTypePointer Function %S_t +%21 = OpTypeFunction %void %_ptr_Function_S_t +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 +%_ptr_Function_19 = OpTypePointer Function %19 +%sampler15 = OpVariable %_ptr_UniformConstant_19 UniformConstant +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_2 = OpConstant %int 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Input_v2float = OpTypePointer Input %v2float +%texCoords = OpVariable %_ptr_Input_v2float Input +)"; + + const std::string before = + R"(%main = OpFunction %void None %13 +%29 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%param = OpVariable %_ptr_Function_S_t Function +%30 = OpLoad %v2float %texCoords +%31 = OpAccessChain %_ptr_Function_v2float %s0 %int_0 +OpStore %31 %30 +%32 = OpLoad %19 %sampler15 +%33 = OpAccessChain %_ptr_Function_19 %s0 %int_2 +OpStore %33 %32 +%34 = OpLoad %S_t %s0 +OpStore %param %34 +%return_value = OpFunctionCall %void %foo_struct_S_t_vf2_vf21_ %param +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %13 +%29 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%param = OpVariable %_ptr_Function_S_t Function +%30 = OpLoad %v2float %texCoords +%31 = OpAccessChain %_ptr_Function_v2float %s0 %int_0 +OpStore %31 %30 +%32 = OpLoad %19 %sampler15 +%33 = OpAccessChain %_ptr_Function_19 %s0 %int_2 +OpStore %33 %32 +%34 = OpLoad %S_t %s0 +OpStore %param %34 +%42 = OpAccessChain %_ptr_Function_19 %param %int_2 +%43 = OpLoad %19 %42 +%44 = OpAccessChain %_ptr_Function_v2float %param %int_0 +%45 = OpLoad %v2float %44 +%46 = OpImageSampleImplicitLod %v4float %43 %45 +OpStore %outColor %46 +OpReturn +OpFunctionEnd +)"; + + const std::string post_defs = + R"(%foo_struct_S_t_vf2_vf21_ = OpFunction %void None %21 +%s = OpFunctionParameter %_ptr_Function_S_t +%35 = OpLabel +%36 = OpAccessChain %_ptr_Function_19 %s %int_2 +%37 = OpLoad %19 %36 +%38 = OpAccessChain %_ptr_Function_v2float %s %int_0 +%39 = OpLoad %v2float %38 +%40 = OpImageSampleImplicitLod %v4float %37 %39 +OpStore %outColor %40 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs_1 + name + predefs_2 + before + post_defs, + predefs_1 + predefs_2 + after + post_defs, true, true); +} + TEST_F(InlineOpaqueTest, InlineInNonEntryPointFunction) { // This demonstrates opaque inlining in a function that is not // an entry point function (main2) but is in the call tree of an diff --git a/test/opt/inline_test.cpp b/test/opt/inline_test.cpp index 29399013..f3ff81ce 100644 --- a/test/opt/inline_test.cpp +++ b/test/opt/inline_test.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017 Valve Corporation -// Copyright (c) 2017 LunarG Inc. +// Copyright (c) 2017-2022 Valve Corporation +// Copyright (c) 2017-2022 LunarG Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -1533,9 +1533,11 @@ OpFunctionEnd %9 = OpLabel OpBranch %10 %10 = OpLabel -OpLoopMerge %12 %10 None +OpLoopMerge %12 %15 None OpBranch %14 %14 = OpLabel +OpBranch %15 +%15 = OpLabel OpBranchConditional %true %10 %12 %12 = OpLabel OpReturn @@ -1694,7 +1696,7 @@ OpFunctionEnd OpBranch %13 %13 = OpLabel %14 = OpCopyObject %bool %false -OpLoopMerge %16 %13 None +OpLoopMerge %16 %22 None OpBranch %17 %17 = OpLabel %19 = OpCopyObject %bool %true @@ -1702,6 +1704,8 @@ OpSelectionMerge %20 None OpBranchConditional %true %20 %20 %20 = OpLabel %21 = OpPhi %bool %19 %17 +OpBranch %22 +%22 = OpLabel OpBranchConditional %true %13 %16 %16 = OpLabel OpReturn @@ -2300,7 +2304,6 @@ TEST_F(InlineTest, DontInlineDirectlyRecursiveFunc) { OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %1 "main" OpExecutionMode %1 OriginUpperLeft -OpDecorate %2 DescriptorSet 439418829 %void = OpTypeVoid %4 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -2330,7 +2333,6 @@ TEST_F(InlineTest, DontInlineInDirectlyRecursiveFunc) { OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %1 "main" OpExecutionMode %1 OriginUpperLeft -OpDecorate %2 DescriptorSet 439418829 %void = OpTypeVoid %4 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -2581,6 +2583,132 @@ OpFunctionEnd SinglePassRunAndCheck(before, after, false, true); } +TEST_F(InlineTest, InlineForLinkage) { + const std::string before = + R"(OpCapability SampledBuffer +OpCapability ImageBuffer +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %type_buffer_image "type.buffer.image" +OpName %output "output" +OpName %main "main" +OpName %color "color" +OpName %bb_entry "bb.entry" +OpName %param_var_color "param.var.color" +OpName %fn "fn" +OpName %color_0 "color" +OpName %bb_entry_0 "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +OpDecorate %output DescriptorSet 0 +OpDecorate %output Binding 1 +%float = OpTypeFloat 32 +%float_0_200000003 = OpConstant %float 0.200000003 +%v4float = OpTypeVector %float 4 +%6 = OpConstantComposite %v4float %float_0_200000003 %float_0_200000003 %float_0_200000003 %float_0_200000003 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%type_buffer_image = OpTypeImage %float Buffer 2 0 0 2 Rgba32f +%_ptr_UniformConstant_type_buffer_image = OpTypePointer UniformConstant %type_buffer_image +%_ptr_Function_v4float = OpTypePointer Function %v4float +%11 = OpTypeFunction %float %_ptr_Function_v4float +%_ptr_Function_float = OpTypePointer Function %float +%output = OpVariable %_ptr_UniformConstant_type_buffer_image UniformConstant +%main = OpFunction %float None %11 +%color = OpFunctionParameter %_ptr_Function_v4float +%bb_entry = OpLabel +%param_var_color = OpVariable %_ptr_Function_v4float Function +%16 = OpLoad %v4float %color +OpStore %param_var_color %16 +%17 = OpFunctionCall %float %fn %param_var_color +OpReturnValue %17 +OpFunctionEnd +%fn = OpFunction %float None %11 +%color_0 = OpFunctionParameter %_ptr_Function_v4float +%bb_entry_0 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%22 = OpLoad %v4float %color_0 +OpStore %v %22 +%23 = OpLoad %v4float %v +%24 = OpFMul %v4float %23 %6 +OpStore %v %24 +%26 = OpAccessChain %_ptr_Function_float %v %int_0 +%27 = OpLoad %float %26 +OpReturnValue %27 +OpFunctionEnd + )"; + + const std::string after = + R"(OpCapability SampledBuffer +OpCapability ImageBuffer +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %type_buffer_image "type.buffer.image" +OpName %output "output" +OpName %main "main" +OpName %color "color" +OpName %bb_entry "bb.entry" +OpName %param_var_color "param.var.color" +OpName %fn "fn" +OpName %color_0 "color" +OpName %bb_entry_0 "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +OpDecorate %output DescriptorSet 0 +OpDecorate %output Binding 1 +%float = OpTypeFloat 32 +%float_0_200000003 = OpConstant %float 0.200000003 +%v4float = OpTypeVector %float 4 +%6 = OpConstantComposite %v4float %float_0_200000003 %float_0_200000003 %float_0_200000003 %float_0_200000003 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%type_buffer_image = OpTypeImage %float Buffer 2 0 0 2 Rgba32f +%_ptr_UniformConstant_type_buffer_image = OpTypePointer UniformConstant %type_buffer_image +%_ptr_Function_v4float = OpTypePointer Function %v4float +%11 = OpTypeFunction %float %_ptr_Function_v4float +%_ptr_Function_float = OpTypePointer Function %float +%output = OpVariable %_ptr_UniformConstant_type_buffer_image UniformConstant +%main = OpFunction %float None %11 +%color = OpFunctionParameter %_ptr_Function_v4float +%bb_entry = OpLabel +%28 = OpVariable %_ptr_Function_v4float Function +%29 = OpVariable %_ptr_Function_float Function +%param_var_color = OpVariable %_ptr_Function_v4float Function +%16 = OpLoad %v4float %color +OpStore %param_var_color %16 +%31 = OpLoad %v4float %param_var_color +OpStore %28 %31 +%32 = OpLoad %v4float %28 +%33 = OpFMul %v4float %32 %6 +OpStore %28 %33 +%34 = OpAccessChain %_ptr_Function_float %28 %int_0 +%35 = OpLoad %float %34 +OpStore %29 %35 +%17 = OpLoad %float %29 +OpReturnValue %17 +OpFunctionEnd +%fn = OpFunction %float None %11 +%color_0 = OpFunctionParameter %_ptr_Function_v4float +%bb_entry_0 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%22 = OpLoad %v4float %color_0 +OpStore %v %22 +%23 = OpLoad %v4float %v +%24 = OpFMul %v4float %23 %6 +OpStore %v %24 +%26 = OpAccessChain %_ptr_Function_float %v %int_0 +%27 = OpLoad %float %26 +OpReturnValue %27 +OpFunctionEnd +)"; + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(before, after, false, true); +} + TEST_F(InlineTest, InlineFuncWithOpTerminateRayNotInContinue) { const std::string text = R"( @@ -3259,6 +3387,82 @@ TEST_F(InlineTest, DebugSimple) { SinglePassRunAndMatch(text, true); } +TEST_F(InlineTest, ShaderDebugSimple) { + // Same as DebugSimple but for NonSemantic.Shader.DebugInfo.100. + const std::string text = R"( +; CHECK: [[main_name:%\d+]] = OpString "main" +; CHECK: [[foo_name:%\d+]] = OpString "foo" +; CHECK: [[dbg_main:%\d+]] = OpExtInst %void {{%\d+}} DebugFunction [[main_name]] {{%\d+}} {{%\d+}} %uint_4 %uint_1 {{%\d+}} [[main_name]] %uint_3 %uint_4 +; CHECK: [[dbg_foo:%\d+]] = OpExtInst %void {{%\d+}} DebugFunction [[foo_name]] {{%\d+}} {{%\d+}} %uint_1 %uint_1 {{%\d+}} [[foo_name]] %uint_3 %uint_1 +; CHECK: [[foo_bb:%\d+]] = OpExtInst %void {{%\d+}} DebugLexicalBlock {{%\d+}} %uint_1 %uint_14 [[dbg_foo]] +; CHECK: [[inlined_at:%\d+]] = OpExtInst %void {{%\d+}} DebugInlinedAt %uint_4 [[dbg_main]] +; CHECK: [[main:%\d+]] = OpFunction %void None +; CHECK: {{%\d+}} = OpExtInst %void {{%\d+}} DebugScope [[foo_bb]] [[inlined_at]] +; CHECK: [[foo:%\d+]] = OpFunction %v4float None + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %3 %4 + OpExecutionMode %main OriginUpperLeft + %5 = OpString "ps.hlsl" + OpSource HLSL 600 %5 + %6 = OpString "float" + %main_name = OpString "main" + %foo_name = OpString "foo" + OpDecorate %3 Location 0 + OpDecorate %4 Location 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %uint_4 = OpConstant %uint 4 + %uint_5 = OpConstant %uint 5 + %uint_14 = OpConstant %uint 14 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 + %float_1 = OpConstant %float 1 + %v4float = OpTypeVector %float 4 + %14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %18 = OpTypeFunction %void + %19 = OpTypeFunction %v4float + %3 = OpVariable %_ptr_Input_v4float Input + %4 = OpVariable %_ptr_Output_v4float Output + %20 = OpExtInst %void %1 DebugSource %5 + %21 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %20 %uint_5 + %22 = OpExtInst %void %1 DebugTypeBasic %6 %uint_32 %uint_3 %uint_0 + %23 = OpExtInst %void %1 DebugTypeVector %22 %uint_4 + %24 = OpExtInst %void %1 DebugTypeFunction %uint_3 %23 %23 + %25 = OpExtInst %void %1 DebugTypeFunction %uint_3 %23 + %dbg_main = OpExtInst %void %1 DebugFunction %main_name %24 %20 %uint_4 %uint_1 %21 %main_name %uint_3 %uint_4 + %dbg_foo = OpExtInst %void %1 DebugFunction %foo_name %25 %20 %uint_1 %uint_1 %21 %foo_name %uint_3 %uint_1 + %29 = OpExtInst %void %1 DebugLexicalBlock %20 %uint_1 %uint_14 %dbg_foo + %main = OpFunction %void None %18 + %30 = OpLabel +%dbg_main_def = OpExtInst %void %1 DebugFunctionDefinition %dbg_main %main + %31 = OpExtInst %void %1 DebugScope %dbg_main + %32 = OpFunctionCall %v4float %foo + %33 = OpLoad %v4float %3 + %34 = OpFAdd %v4float %32 %33 + OpStore %4 %34 + OpReturn + OpFunctionEnd + %foo = OpFunction %v4float None %19 + %36 = OpLabel +%dbg_foo_def = OpExtInst %void %1 DebugFunctionDefinition %dbg_foo %foo + %35 = OpExtInst %void %1 DebugScope %dbg_foo + %37 = OpExtInst %void %1 DebugScope %29 + OpReturnValue %14 + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + TEST_F(InlineTest, DebugNested) { // When function main() calls function zoo() and function zoo() calls // function bar() and function bar() calls function foo(), check that @@ -3462,6 +3666,103 @@ float4 main(float4 color : COLOR) : SV_TARGET { SinglePassRunAndMatch(text, true); } +TEST_F(InlineTest, ShaderDebugSimpleHLSLPixelShader) { + // Same as DebugSimpleHLSLPixelShader but for + // NonSemantic.Shader.DebugInfo.100. + const std::string text = R"( +; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} %uint_1 %uint_1 {{%\d+}} {{%\d+}} %uint_3 %uint_1 +; CHECK: [[lex_blk:%\d+]] = OpExtInst %void [[ext]] DebugLexicalBlock {{%\d+}} %uint_1 %uint_47 [[dbg_main]] +; CHECK: %main = OpFunction %void None +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_main]] +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare {{%\d+}} %param_var_color +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[lex_blk]] +; CHECK: {{%\d+}} = OpExtInst %void %1 DebugLine {{%\d+}} %uint_2 %uint_2 %uint_10 %uint_10 +; CHECK: {{%\d+}} = OpLoad %v4float %param_var_color +; CHECK: {{%\d+}} = OpExtInst %void %1 DebugLine {{%\d+}} %uint_2 %uint_2 %uint_3 %uint_3 +; CHECK: OpFunctionEnd +; CHECK: %src_main = OpFunction %v4float None + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET + OpExecutionMode %main OriginUpperLeft + %5 = OpString "ps.hlsl" + OpSource HLSL 600 %5 + %14 = OpString "#line 1 \"ps.hlsl\" +float4 main(float4 color : COLOR) : SV_TARGET { + return color; +} +" + %17 = OpString "float" + %21 = OpString "src.main" + %24 = OpString "color" + OpName %in_var_COLOR "in.var.COLOR" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %main "main" + OpName %param_var_color "param.var.color" + OpName %src_main "src.main" + OpName %color "color" + OpName %bb_entry "bb.entry" + OpDecorate %in_var_COLOR Location 0 + OpDecorate %out_var_SV_TARGET Location 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %uint_4 = OpConstant %uint 4 + %uint_5 = OpConstant %uint 5 + %uint_10 = OpConstant %uint 10 + %uint_20 = OpConstant %uint 20 + %uint_32 = OpConstant %uint 32 + %uint_47 = OpConstant %uint 47 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %27 = OpTypeFunction %void +%_ptr_Function_v4float = OpTypePointer Function %v4float + %33 = OpTypeFunction %v4float %_ptr_Function_v4float +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %13 = OpExtInst %void %1 DebugExpression + %15 = OpExtInst %void %1 DebugSource %5 %14 + %16 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %15 %uint_5 + %18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 %uint_3 %uint_0 + %19 = OpExtInst %void %1 DebugTypeVector %18 %uint_4 + %20 = OpExtInst %void %1 DebugTypeFunction %uint_3 %19 %19 + %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 %uint_1 %uint_1 %16 %21 %uint_3 %uint_1 + %25 = OpExtInst %void %1 DebugLocalVariable %24 %19 %15 %uint_1 %uint_20 %22 %uint_4 %uint_0 + %26 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_1 %uint_47 %22 + %main = OpFunction %void None %27 + %28 = OpLabel +%param_var_color = OpVariable %_ptr_Function_v4float Function + %31 = OpLoad %v4float %in_var_COLOR + OpStore %param_var_color %31 + %32 = OpFunctionCall %v4float %src_main %param_var_color + OpStore %out_var_SV_TARGET %32 + OpReturn + OpFunctionEnd + %src_main = OpFunction %v4float None %33 + %color = OpFunctionParameter %_ptr_Function_v4float + %bb_entry = OpLabel + %140 = OpExtInst %void %1 DebugFunctionDefinition %22 %src_main + %141 = OpExtInst %void %1 DebugLine %5 %uint_1 %uint_1 %uint_1 %uint_1 + %34 = OpExtInst %void %1 DebugScope %22 + %36 = OpExtInst %void %1 DebugDeclare %25 %color %13 + %38 = OpExtInst %void %1 DebugScope %26 + %142 = OpExtInst %void %1 DebugLine %5 %uint_2 %uint_2 %uint_10 %uint_10 + %39 = OpLoad %v4float %color + %143 = OpExtInst %void %1 DebugLine %5 %uint_2 %uint_2 %uint_3 %uint_3 + OpReturnValue %39 + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + TEST_F(InlineTest, DebugDeclareForCalleeFunctionParam) { // Check that InlinePass correctly generates DebugDeclare instructions // for callee function's parameters and maps them to corresponding @@ -3937,6 +4238,190 @@ OpFunctionEnd SinglePassRunAndMatch(text, true); } +TEST_F(InlineTest, CreateConstantForInlinedAt) { + // This shader causes CreateDebugInlinedAt to generate a constant. + // Using the Constant manager would attempt to build the invalidated + // DefUse manager during inlining which could cause an assert because + // the function is in an inconsistent state. This test verifies that + // CreateDebugInlinedAt detects that the DefUse manager is disabled + // and creates a duplicate constant safely without the Constant manager. + // + // int function1() { + // return 1; + // } + // + // void main() { + // function1(); + // } + + const std::string text = R"(OpCapability Shader +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_7_0 = OpConstant %uint 7 +; CHECK: OpExtInst %void %1 DebugInlinedAt %uint_7_0 +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%3 = OpString "parent3.hlsl" +%8 = OpString "int" +%19 = OpString "function1" +%20 = OpString "" +%26 = OpString "main" +OpName %main "main" +OpName %src_main "src.main" +OpName %bb_entry "bb.entry" +OpName %function1 "function1" +OpName %bb_entry_0 "bb.entry" +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%void = OpTypeVoid +%uint_4 = OpConstant %uint 4 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_1 = OpConstant %uint 1 +%uint_5 = OpConstant %uint 5 +%uint_2 = OpConstant %uint 2 +%uint_17 = OpConstant %uint 17 +%uint_6 = OpConstant %uint 6 +%uint_13 = OpConstant %uint 13 +%uint_7 = OpConstant %uint 7 +%31 = OpTypeFunction %void +%42 = OpTypeFunction %int +%10 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 %uint_4 %uint_0 +%13 = OpExtInst %void %1 DebugTypeFunction %uint_3 %10 +%15 = OpExtInst %void %1 DebugSource %3 +%16 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %15 %uint_5 +%21 = OpExtInst %void %1 DebugFunction %19 %13 %15 %uint_2 %uint_1 %16 %20 %uint_3 %uint_2 +%23 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_2 %uint_17 %21 +%25 = OpExtInst %void %1 DebugTypeFunction %uint_3 %void +%27 = OpExtInst %void %1 DebugFunction %26 %25 %15 %uint_6 %uint_1 %16 %20 %uint_3 %uint_6 +%29 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_6 %uint_13 %27 +%main = OpFunction %void None %31 +%32 = OpLabel +%33 = OpFunctionCall %void %src_main +OpLine %3 8 1 +OpReturn +OpFunctionEnd +OpLine %3 6 1 +%src_main = OpFunction %void None %31 +OpNoLine +%bb_entry = OpLabel +%47 = OpExtInst %void %1 DebugScope %27 +%37 = OpExtInst %void %1 DebugFunctionDefinition %27 %src_main +%48 = OpExtInst %void %1 DebugScope %29 +OpLine %3 7 3 +%39 = OpFunctionCall %int %function1 +%49 = OpExtInst %void %1 DebugScope %27 +OpLine %3 8 1 +OpReturn +%50 = OpExtInst %void %1 DebugNoScope +OpFunctionEnd +OpLine %3 2 1 +%function1 = OpFunction %int None %42 +OpNoLine +%bb_entry_0 = OpLabel +%51 = OpExtInst %void %1 DebugScope %21 +%45 = OpExtInst %void %1 DebugFunctionDefinition %21 %function1 +%52 = OpExtInst %void %1 DebugScope %23 +OpLine %3 3 3 +OpReturnValue %int_1 +%53 = OpExtInst %void %1 DebugNoScope +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + +TEST_F(InlineTest, CreateDebugInlinedAtFromDebugLine) { + const std::string text = R"(OpCapability Shader +; CHECK: OpExtInst %void %1 DebugInlinedAt %uint_6 +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%3 = OpString "debuginlinedat.frag" +%8 = OpString "int" +%15 = OpString "int function1() { + return 1; +} + +void main() { + function1(); +} +" +%20 = OpString "function1" +%21 = OpString "" +%26 = OpString "main" +OpName %main "main" +OpName %src_main "src.main" +OpName %bb_entry "bb.entry" +OpName %function1 "function1" +OpName %bb_entry_0 "bb.entry" +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%void = OpTypeVoid +%uint_4 = OpConstant %uint 4 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_1 = OpConstant %uint 1 +%uint_5 = OpConstant %uint 5 +%uint_17 = OpConstant %uint 17 +%uint_13 = OpConstant %uint 13 +%30 = OpTypeFunction %void +%uint_7 = OpConstant %uint 7 +%uint_6 = OpConstant %uint 6 +%uint_2 = OpConstant %uint 2 +%uint_12 = OpConstant %uint 12 +%48 = OpTypeFunction %int +%uint_9 = OpConstant %uint 9 +%10 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 %uint_4 %uint_0 +%13 = OpExtInst %void %1 DebugTypeFunction %uint_3 %10 +%16 = OpExtInst %void %1 DebugSource %3 %15 +%17 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %16 %uint_5 +%22 = OpExtInst %void %1 DebugFunction %20 %13 %16 %uint_1 %uint_1 %17 %21 %uint_3 %uint_1 +%23 = OpExtInst %void %1 DebugLexicalBlock %16 %uint_1 %uint_17 %22 +%25 = OpExtInst %void %1 DebugTypeFunction %uint_3 %void +%27 = OpExtInst %void %1 DebugFunction %26 %25 %16 %uint_5 %uint_1 %17 %21 %uint_3 %uint_5 +%28 = OpExtInst %void %1 DebugLexicalBlock %16 %uint_5 %uint_13 %27 +%main = OpFunction %void None %30 +%31 = OpLabel +%32 = OpFunctionCall %void %src_main +%34 = OpExtInst %void %1 DebugLine %16 %uint_7 %uint_7 %uint_1 %uint_1 +OpReturn +OpFunctionEnd +%src_main = OpFunction %void None %30 +%bb_entry = OpLabel +%37 = OpExtInst %void %1 DebugScope %27 +%38 = OpExtInst %void %1 DebugFunctionDefinition %27 %src_main +%39 = OpExtInst %void %1 DebugScope %28 +%40 = OpExtInst %void %1 DebugLine %16 %uint_6 %uint_6 %uint_2 %uint_12 +%44 = OpFunctionCall %int %function1 +%46 = OpExtInst %void %1 DebugScope %27 +%47 = OpExtInst %void %1 DebugLine %16 %uint_7 %uint_7 %uint_1 %uint_1 +OpReturn +OpFunctionEnd +%function1 = OpFunction %int None %48 +%bb_entry_0 = OpLabel +%50 = OpExtInst %void %1 DebugScope %22 +%51 = OpExtInst %void %1 DebugFunctionDefinition %22 %function1 +%52 = OpExtInst %void %1 DebugScope %23 +%53 = OpExtInst %void %1 DebugLine %16 %uint_2 %uint_2 %uint_2 %uint_9 +OpReturnValue %int_1 +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SinglePassRunAndMatch(text, true); +} + // TODO(greg-lunarg): Add tests to verify handling of these cases: // // Empty modules diff --git a/test/opt/inst_bindless_check_test.cpp b/test/opt/inst_bindless_check_test.cpp index 1a42329b..90f40bc6 100644 --- a/test/opt/inst_bindless_check_test.cpp +++ b/test/opt/inst_bindless_check_test.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017 Valve Corporation -// Copyright (c) 2017 LunarG Inc. +// Copyright (c) 2017-2022 Valve Corporation +// Copyright (c) 2017-2022 LunarG Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -28,6 +28,297 @@ namespace { using InstBindlessTest = PassTest<::testing::Test>; +static const std::string kOutputDecorations = R"( +; CHECK: OpDecorate [[output_buffer_type:%inst_bindless_OutputBuffer]] Block +; CHECK: OpMemberDecorate [[output_buffer_type]] 0 Offset 0 +; CHECK: OpMemberDecorate [[output_buffer_type]] 1 Offset 4 +; CHECK: OpDecorate [[output_buffer_var:%\w+]] DescriptorSet 7 +; CHECK: OpDecorate [[output_buffer_var]] Binding 0 +)"; + +static const std::string kOutputGlobals = R"( +; CHECK: [[output_buffer_type]] = OpTypeStruct %uint %_runtimearr_uint +; CHECK: [[output_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[output_buffer_type]] +; CHECK: [[output_buffer_var]] = OpVariable [[output_ptr_type]] StorageBuffer +)"; + +static const std::string kStreamWrite4Begin = R"( +; CHECK: %inst_bindless_stream_write_4 = OpFunction %void None {{%\w+}} +; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint +; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint +; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint +; CHECK: [[param_4:%\w+]] = OpFunctionParameter %uint +; CHECK: {{%\w+}} = OpLabel +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_0 +; CHECK: {{%\w+}} = OpAtomicIAdd %uint {{%\w+}} %uint_4 %uint_0 %uint_10 +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_10 +; CHECK: {{%\w+}} = OpArrayLength %uint [[output_buffer_var]] 1 +; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}} +; CHECK: OpSelectionMerge {{%\w+}} None +; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +; CHECK: {{%\w+}} = OpLabel +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_0 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} %uint_10 +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} %uint_23 +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_2 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} [[param_1]] +)"; + +static const std::string kStreamWrite4End = R"( +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} [[param_2]] +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_8 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} [[param_3]] +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_9 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} [[param_4]] +; CHECK: OpBranch {{%\w+}} +; CHECK: {{%\w+}} = OpLabel +; CHECK: OpReturn +; CHECK: OpFunctionEnd +)"; + +// clang-format off +static const std::string kStreamWrite4Frag = kStreamWrite4Begin + R"( +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} %uint_4 +; CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +; CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +)" + kStreamWrite4End; + +static const std::string kStreamWrite4Tese = kStreamWrite4Begin + R"( +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} %uint_2 +; CHECK: {{%\w+}} = OpLoad %uint %gl_PrimitiveID +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +; CHECK: {{%\w+}} = OpLoad %v3float %gl_TessCoord +; CHECK: {{%\w+}} = OpBitcast %v3uint {{%\w+}} +; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +)" + kStreamWrite4End; + +static const std::string kStreamWrite4Vert = kStreamWrite4Begin + R"( +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} %uint_0 +; CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +; CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +)" + kStreamWrite4End; + +static const std::string kStreamWrite4Compute = kStreamWrite4Begin + R"( +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} %uint_5 +; CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID +; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +)" + kStreamWrite4End; + +static const std::string kStreamWrite4Ray = kStreamWrite4Begin + R"( +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +; CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}} +; CHECK: {{%\w+}} = OpCompositeExtract %uint %90 0 +; CHECK: {{%\w+}} = OpCompositeExtract %uint %90 1 +; CHECK: {{%\w+}} = OpCompositeExtract %uint %90 2 +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +)" + kStreamWrite4End; +// clang-format on + +static const std::string kStreamWrite5Begin = R"( +; CHECK: %inst_bindless_stream_write_5 = OpFunction %void None {{%\w+}} +; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint +; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint +; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint +; CHECK: [[param_4:%\w+]] = OpFunctionParameter %uint +; CHECK: [[param_5:%\w+]] = OpFunctionParameter %uint +; CHECK: {{%\w+}} = OpLabel +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_0 +; CHECK: {{%\w+}} = OpAtomicIAdd %uint {{%\w+}} %uint_4 %uint_0 %uint_11 +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_11 +; CHECK: {{%\w+}} = OpArrayLength %uint [[output_buffer_var]] 1 +; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}} +; CHECK: OpSelectionMerge {{%\w+}} None +; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +; CHECK: {{%\w+}} = OpLabel +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_0 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} %uint_11 +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} %uint_23 +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_2 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} [[param_1]] +)"; + +static const std::string kStreamWrite5End = R"( +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} [[param_2]] +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_8 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} [[param_3]] +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_9 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} [[param_4]] +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_10 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} [[param_5]] +; CHECK: OpBranch {{%\w+}} +; CHECK: {{%\w+}} = OpLabel +; CHECK: OpReturn +; CHECK: OpFunctionEnd +)"; + +// clang-format off +static const std::string kStreamWrite5Frag = kStreamWrite5Begin + R"( +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} %uint_4 +; CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +; CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +)" + kStreamWrite4End; + +static const std::string kStreamWrite5Vert = kStreamWrite5Begin + R"( +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} %uint_0 +; CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +; CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +)" + kStreamWrite5End; +// clang-format on + +static const std::string kInputDecorations = R"( +; CHECK: OpDecorate [[input_buffer_type:%inst_bindless_InputBuffer]] Block +; CHECK: OpMemberDecorate [[input_buffer_type]] 0 Offset 0 +; CHECK: OpDecorate [[input_buffer_var:%\w+]] DescriptorSet 7 +; CHECK: OpDecorate [[input_buffer_var]] Binding 1 +)"; + +static const std::string kInputGlobals = R"( +; CHECK: [[input_buffer_type]] = OpTypeStruct %_runtimearr_uint +; CHECK: [[input_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[input_buffer_type]] +; CHECK: [[input_buffer_var]] = OpVariable [[input_ptr_type]] StorageBuffer +)"; + +static const std::string kDirectRead2 = R"( +; CHECK: %inst_bindless_direct_read_2 = OpFunction %uint None {{%\w+}} +; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint +; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint +; CHECK: {{%\w+}} = OpLabel +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 [[param_1]] +; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}} +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_2]] +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}} +; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}} +; CHECK: OpReturnValue {{%\w+}} +; CHECK: OpFunctionEnd +)"; + +static const std::string kDirectRead3 = R"( + ;CHECK: %inst_bindless_direct_read_3 = OpFunction %uint None {{%\w+}} +; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint +; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint +; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint + ;CHECK: {{%\w+}} = OpLabel + ;CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 [[param_1]] + ;CHECK: {{%\w+}} = OpLoad %uint {{%\w+}} + ;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_2]] + ;CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}} + ;CHECK: {{%\w+}} = OpLoad %uint {{%\w+}} + ;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_3]] + ;CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}} + ;CHECK: {{%\w+}} = OpLoad %uint {{%\w+}} + ;CHECK: OpReturnValue {{%\w+}} + ;CHECK: OpFunctionEnd +)"; + +static const std::string kDirectRead4 = R"( +; CHECK: %inst_bindless_direct_read_4 = OpFunction %uint None {{%\w+}} +; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint +; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint +; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint +; CHECK: [[param_4:%\w+]] = OpFunctionParameter %uint +; CHECK: {{%\w+}} = OpLabel +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 [[param_1]] +; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}} +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_2]] +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}} +; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}} +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_3]] +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}} +; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}} +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_4]] +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}} +; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}} +; CHECK: OpReturnValue {{%\w+}} +; CHECK: OpFunctionEnd +)"; + TEST_F(InstBindlessTest, NoInstrumentConstIndexInbounds) { // Texture2D g_tColor[128]; // @@ -215,27 +506,20 @@ TEST_F(InstBindlessTest, Simple) { // return ps_output; // } - const std::string entry_before = - R"(OpCapability Shader + const std::string entry = R"( +OpCapability Shader +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord OpExecutionMode %MainPs OriginUpperLeft OpSource HLSL 500 )"; - const std::string entry_after = - R"(OpCapability Shader -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -)"; - - const std::string names_annots = - R"(OpName %MainPs "MainPs" + // clang-format off + const std::string names_annots = R"( +OpName %MainPs "MainPs" OpName %g_tColor "g_tColor" OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" @@ -250,20 +534,13 @@ OpDecorate %PerViewConstantBuffer_t Block OpDecorate %g_sAniso DescriptorSet 0 OpDecorate %i_vTextureCoords Location 0 OpDecorate %_entryPointOutput_vColor Location 0 +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kOutputDecorations + R"( +; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord )"; - const std::string new_annots = - R"(OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_55 Block -OpMemberDecorate %_struct_55 0 Offset 0 -OpMemberDecorate %_struct_55 1 Offset 4 -OpDecorate %57 DescriptorSet 7 -OpDecorate %57 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -)"; - - const std::string consts_types_vars = - R"(%void = OpTypeVoid + const std::string consts_types_vars = R"( +%void = OpTypeVoid %10 = OpTypeFunction %void %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 @@ -289,36 +566,32 @@ OpDecorate %gl_FragCoord BuiltIn FragCoord %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +; CHECK: %uint_0 = OpConstant %uint 0 +; CHECK: %bool = OpTypeBool +; CHECK: %48 = OpTypeFunction %void %uint %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kOutputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +; CHECK: %v4uint = OpTypeVector %uint 4 +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_56 = OpConstant %uint 56 +; CHECK: %103 = OpConstantNull %v4float )"; + // clang-format on - const std::string new_consts_types_vars = - R"(%uint_0 = OpConstant %uint 0 -%bool = OpTypeBool -%48 = OpTypeFunction %void %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_55 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_55 = OpTypePointer StorageBuffer %_struct_55 -%57 = OpVariable %_ptr_StorageBuffer__struct_55 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_1 = OpConstant %uint 1 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_3 = OpConstant %uint 3 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_56 = OpConstant %uint 56 -%103 = OpConstantNull %v4float -)"; - - const std::string func_pt1 = - R"(%MainPs = OpFunction %void None %10 + const std::string main_func = R"( +%MainPs = OpFunction %void None %10 %29 = OpLabel %30 = OpLoad %v2float %i_vTextureCoords %31 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 @@ -327,93 +600,34 @@ OpDecorate %gl_FragCoord BuiltIn FragCoord %34 = OpLoad %16 %33 %35 = OpLoad %24 %g_sAniso %36 = OpSampledImage %26 %34 %35 -)"; - - const std::string func_pt2_before = - R"(%37 = OpImageSampleImplicitLod %v4float %36 %30 +%37 = OpImageSampleImplicitLod %v4float %36 %30 OpStore %_entryPointOutput_vColor %37 +; CHECK-NOT: %37 = OpImageSampleImplicitLod %v4float %36 %30 +; CHECK-NOT: OpStore %_entryPointOutput_vColor %37 +; CHECK: %40 = OpULessThan %bool %32 %uint_128 +; CHECK: OpSelectionMerge %41 None +; CHECK: OpBranchConditional %40 %42 %43 +; CHECK: %42 = OpLabel +; CHECK: %44 = OpLoad %16 %33 +; CHECK: %45 = OpSampledImage %26 %44 %35 +; CHECK: %46 = OpImageSampleImplicitLod %v4float %45 %30 +; CHECK: OpBranch %41 +; CHECK: %43 = OpLabel +; CHECK: %102 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_56 %uint_0 %32 %uint_128 +; CHECK: OpBranch %41 +; CHECK: %41 = OpLabel +; CHECK: %104 = OpPhi %v4float %46 %42 %103 %43 +; CHECK: OpStore %_entryPointOutput_vColor %104 OpReturn OpFunctionEnd )"; - const std::string func_pt2_after = - R"(%40 = OpULessThan %bool %32 %uint_128 -OpSelectionMerge %41 None -OpBranchConditional %40 %42 %43 -%42 = OpLabel -%44 = OpLoad %16 %33 -%45 = OpSampledImage %26 %44 %35 -%46 = OpImageSampleImplicitLod %v4float %45 %30 -OpBranch %41 -%43 = OpLabel -%102 = OpFunctionCall %void %47 %uint_56 %uint_0 %32 %uint_128 -OpBranch %41 -%41 = OpLabel -%104 = OpPhi %v4float %46 %42 %103 %43 -OpStore %_entryPointOutput_vColor %104 -OpReturn -OpFunctionEnd -)"; - - const std::string output_func = - R"(%47 = OpFunction %void None %48 -%49 = OpFunctionParameter %uint -%50 = OpFunctionParameter %uint -%51 = OpFunctionParameter %uint -%52 = OpFunctionParameter %uint -%53 = OpLabel -%59 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_0 -%62 = OpAtomicIAdd %uint %59 %uint_4 %uint_0 %uint_10 -%63 = OpIAdd %uint %62 %uint_10 -%64 = OpArrayLength %uint %57 1 -%65 = OpULessThanEqual %bool %63 %64 -OpSelectionMerge %66 None -OpBranchConditional %65 %67 %66 -%67 = OpLabel -%68 = OpIAdd %uint %62 %uint_0 -%70 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %68 -OpStore %70 %uint_10 -%72 = OpIAdd %uint %62 %uint_1 -%73 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %72 -OpStore %73 %uint_23 -%75 = OpIAdd %uint %62 %uint_2 -%76 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %75 -OpStore %76 %49 -%78 = OpIAdd %uint %62 %uint_3 -%79 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %78 -OpStore %79 %uint_4 -%82 = OpLoad %v4float %gl_FragCoord -%84 = OpBitcast %v4uint %82 -%85 = OpCompositeExtract %uint %84 0 -%86 = OpIAdd %uint %62 %uint_4 -%87 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %86 -OpStore %87 %85 -%88 = OpCompositeExtract %uint %84 1 -%90 = OpIAdd %uint %62 %uint_5 -%91 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %90 -OpStore %91 %88 -%93 = OpIAdd %uint %62 %uint_7 -%94 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %93 -OpStore %94 %50 -%96 = OpIAdd %uint %62 %uint_8 -%97 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %96 -OpStore %97 %51 -%99 = OpIAdd %uint %62 %uint_9 -%100 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %99 -OpStore %100 %52 -OpBranch %66 -%66 = OpLabel -OpReturn -OpFunctionEnd -)"; + const std::string output_func = kStreamWrite4Frag; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - entry_before + names_annots + consts_types_vars + func_pt1 + - func_pt2_before, - entry_after + names_annots + new_annots + consts_types_vars + - new_consts_types_vars + func_pt1 + func_pt2_after + output_func, - true, true, 7u, 23u, false, false, false, false, false); + SinglePassRunAndMatch( + entry + names_annots + consts_types_vars + main_func + output_func, true, + 7u, 23u, false, false, false, false, false); } TEST_F(InstBindlessTest, InstrumentMultipleInstructions) { @@ -447,11 +661,14 @@ TEST_F(InstBindlessTest, InstrumentMultipleInstructions) { // return ps_output; // } - const std::string defs_before = - R"(OpCapability Shader + // clang-format off + const std::string defs = R"( +OpCapability Shader +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord OpExecutionMode %MainPs OriginUpperLeft OpSource HLSL 500 OpName %MainPs "MainPs" @@ -470,6 +687,9 @@ OpDecorate %PerViewConstantBuffer_t Block OpDecorate %g_sAniso DescriptorSet 0 OpDecorate %i_vTextureCoords Location 0 OpDecorate %_entryPointOutput_vColor Location 0 +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kOutputDecorations + R"( +; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %10 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -497,93 +717,32 @@ OpDecorate %_entryPointOutput_vColor Location 0 %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +; CHECK: %uint_0 = OpConstant %uint 0 +; CHECK: %bool = OpTypeBool +; CHECK: %56 = OpTypeFunction %void %uint %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kOutputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +; CHECK: %v4uint = OpTypeVector %uint 4 +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_58 = OpConstant %uint 58 +; CHECK: %111 = OpConstantNull %v4float +; CHECK: %uint_64 = OpConstant %uint 64 )"; + // clang-format on - const std::string defs_after = - R"(OpCapability Shader -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" -OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" -OpName %_ "" -OpName %g_sAniso "g_sAniso" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 3 -OpDecorate %g_tColor Binding 0 -OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 -OpMemberDecorate %PerViewConstantBuffer_t 1 Offset 4 -OpDecorate %PerViewConstantBuffer_t Block -OpDecorate %g_sAniso DescriptorSet 0 -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_63 Block -OpMemberDecorate %_struct_63 0 Offset 0 -OpMemberDecorate %_struct_63 1 Offset 4 -OpDecorate %65 DescriptorSet 7 -OpDecorate %65 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -%void = OpTypeVoid -%10 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%v4float = OpTypeVector %float 4 -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%int_1 = OpConstant %int 1 -%17 = OpTypeImage %float 2D 0 0 0 1 Unknown -%uint = OpTypeInt 32 0 -%uint_128 = OpConstant %uint 128 -%_arr_17_uint_128 = OpTypeArray %17 %uint_128 -%_ptr_UniformConstant__arr_17_uint_128 = OpTypePointer UniformConstant %_arr_17_uint_128 -%g_tColor = OpVariable %_ptr_UniformConstant__arr_17_uint_128 UniformConstant -%PerViewConstantBuffer_t = OpTypeStruct %uint %uint -%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t -%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant -%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint -%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17 -%25 = OpTypeSampler -%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25 -%g_sAniso = OpVariable %_ptr_UniformConstant_25 UniformConstant -%27 = OpTypeSampledImage %17 -%_ptr_Input_v2float = OpTypePointer Input %v2float -%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -%uint_0 = OpConstant %uint 0 -%bool = OpTypeBool -%56 = OpTypeFunction %void %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_63 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63 -%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_1 = OpConstant %uint 1 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_3 = OpConstant %uint 3 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_58 = OpConstant %uint 58 -%111 = OpConstantNull %v4float -%uint_64 = OpConstant %uint 64 -)"; - - const std::string func_before = + const std::string main_func = R"(%MainPs = OpFunction %void None %10 %30 = OpLabel %31 = OpLoad %v2float %i_vTextureCoords @@ -594,6 +753,20 @@ OpDecorate %gl_FragCoord BuiltIn FragCoord %36 = OpLoad %25 %g_sAniso %37 = OpSampledImage %27 %35 %36 %38 = OpImageSampleImplicitLod %v4float %37 %31 +; CHECK-NOT: %38 = OpImageSampleImplicitLod %v4float %37 %31 +; CHECK: %48 = OpULessThan %bool %33 %uint_128 +; CHECK: OpSelectionMerge %49 None +; CHECK: OpBranchConditional %48 %50 %51 +; CHECK: %50 = OpLabel +; CHECK: %52 = OpLoad %17 %34 +; CHECK: %53 = OpSampledImage %27 %52 %36 +; CHECK: %54 = OpImageSampleImplicitLod %v4float %53 %31 +; CHECK: OpBranch %49 +; CHECK: %51 = OpLabel +; CHECK: %110 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_58 %uint_0 %33 %uint_128 +; CHECK: OpBranch %49 +; CHECK: %49 = OpLabel +; CHECK: %112 = OpPhi %v4float %54 %50 %111 %51 %39 = OpAccessChain %_ptr_PushConstant_uint %_ %int_1 %40 = OpLoad %uint %39 %41 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %40 @@ -601,114 +774,33 @@ OpDecorate %gl_FragCoord BuiltIn FragCoord %43 = OpSampledImage %27 %42 %36 %44 = OpImageSampleImplicitLod %v4float %43 %31 %45 = OpFAdd %v4float %38 %44 +; CHECK-NOT: %44 = OpImageSampleImplicitLod %v4float %43 %31 +; CHECK-NOT: %45 = OpFAdd %v4float %38 %44 +; CHECK: %113 = OpULessThan %bool %40 %uint_128 +; CHECK: OpSelectionMerge %114 None +; CHECK: OpBranchConditional %113 %115 %116 +; CHECK: %115 = OpLabel +; CHECK: %117 = OpLoad %17 %41 +; CHECK: %118 = OpSampledImage %27 %117 %36 +; CHECK: %119 = OpImageSampleImplicitLod %v4float %118 %31 +; CHECK: OpBranch %114 +; CHECK: %116 = OpLabel +; CHECK: %121 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_64 %uint_0 %40 %uint_128 +; CHECK: OpBranch %114 +; CHECK: %114 = OpLabel +; CHECK: %122 = OpPhi %v4float %119 %115 %111 %116 +; CHECK: %45 = OpFAdd %v4float %112 %122 OpStore %_entryPointOutput_vColor %45 OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%MainPs = OpFunction %void None %10 -%30 = OpLabel -%31 = OpLoad %v2float %i_vTextureCoords -%32 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 -%33 = OpLoad %uint %32 -%34 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %33 -%35 = OpLoad %17 %34 -%36 = OpLoad %25 %g_sAniso -%37 = OpSampledImage %27 %35 %36 -%48 = OpULessThan %bool %33 %uint_128 -OpSelectionMerge %49 None -OpBranchConditional %48 %50 %51 -%50 = OpLabel -%52 = OpLoad %17 %34 -%53 = OpSampledImage %27 %52 %36 -%54 = OpImageSampleImplicitLod %v4float %53 %31 -OpBranch %49 -%51 = OpLabel -%110 = OpFunctionCall %void %55 %uint_58 %uint_0 %33 %uint_128 -OpBranch %49 -%49 = OpLabel -%112 = OpPhi %v4float %54 %50 %111 %51 -%39 = OpAccessChain %_ptr_PushConstant_uint %_ %int_1 -%40 = OpLoad %uint %39 -%41 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %40 -%42 = OpLoad %17 %41 -%43 = OpSampledImage %27 %42 %36 -%113 = OpULessThan %bool %40 %uint_128 -OpSelectionMerge %114 None -OpBranchConditional %113 %115 %116 -%115 = OpLabel -%117 = OpLoad %17 %41 -%118 = OpSampledImage %27 %117 %36 -%119 = OpImageSampleImplicitLod %v4float %118 %31 -OpBranch %114 -%116 = OpLabel -%121 = OpFunctionCall %void %55 %uint_64 %uint_0 %40 %uint_128 -OpBranch %114 -%114 = OpLabel -%122 = OpPhi %v4float %119 %115 %111 %116 -%45 = OpFAdd %v4float %112 %122 -OpStore %_entryPointOutput_vColor %45 -OpReturn -OpFunctionEnd -)"; - - const std::string output_func = - R"(%55 = OpFunction %void None %56 -%57 = OpFunctionParameter %uint -%58 = OpFunctionParameter %uint -%59 = OpFunctionParameter %uint -%60 = OpFunctionParameter %uint -%61 = OpLabel -%67 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0 -%70 = OpAtomicIAdd %uint %67 %uint_4 %uint_0 %uint_10 -%71 = OpIAdd %uint %70 %uint_10 -%72 = OpArrayLength %uint %65 1 -%73 = OpULessThanEqual %bool %71 %72 -OpSelectionMerge %74 None -OpBranchConditional %73 %75 %74 -%75 = OpLabel -%76 = OpIAdd %uint %70 %uint_0 -%78 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %76 -OpStore %78 %uint_10 -%80 = OpIAdd %uint %70 %uint_1 -%81 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %80 -OpStore %81 %uint_23 -%83 = OpIAdd %uint %70 %uint_2 -%84 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %83 -OpStore %84 %57 -%86 = OpIAdd %uint %70 %uint_3 -%87 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %86 -OpStore %87 %uint_4 -%90 = OpLoad %v4float %gl_FragCoord -%92 = OpBitcast %v4uint %90 -%93 = OpCompositeExtract %uint %92 0 -%94 = OpIAdd %uint %70 %uint_4 -%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94 -OpStore %95 %93 -%96 = OpCompositeExtract %uint %92 1 -%98 = OpIAdd %uint %70 %uint_5 -%99 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %98 -OpStore %99 %96 -%101 = OpIAdd %uint %70 %uint_7 -%102 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %101 -OpStore %102 %58 -%104 = OpIAdd %uint %70 %uint_8 -%105 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %104 -OpStore %105 %59 -%107 = OpIAdd %uint %70 %uint_9 -%108 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %107 -OpStore %108 %60 -OpBranch %74 -%74 = OpLabel -OpReturn -OpFunctionEnd -)"; + const std::string output_func = kStreamWrite4Frag; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + output_func, true, - true, 7u, 23u, false, false, false, false, false); + SinglePassRunAndMatch(defs + main_func + output_func, + true, 7u, 23u, false, false, + false, false, false); } TEST_F(InstBindlessTest, InstrumentOpImage) { @@ -716,12 +808,15 @@ TEST_F(InstBindlessTest, InstrumentOpImage) { // using OpImage. This test was created by editing the SPIR-V // from the Simple test. - const std::string defs_before = - R"(OpCapability Shader + // clang-format off + const std::string defs = R"( +OpCapability Shader OpCapability StorageImageReadWithoutFormat +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord OpExecutionMode %MainPs OriginUpperLeft OpSource HLSL 500 OpName %MainPs "MainPs" @@ -737,6 +832,9 @@ OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 OpDecorate %PerViewConstantBuffer_t Block OpDecorate %i_vTextureCoords Location 0 OpDecorate %_entryPointOutput_vColor Location 0 +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kOutputDecorations + R"( +; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -760,87 +858,32 @@ OpDecorate %_entryPointOutput_vColor Location 0 %i_vTextureCoords = OpVariable %_ptr_Input_v2int Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +; CHECK: uint_0 = OpConstant %uint 0 +; CHECK: bool = OpTypeBool +; CHECK: %86 = OpTypeFunction %void %uint %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kOutputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +; CHECK: %v4uint = OpTypeVector %uint 4 +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_51 = OpConstant %uint 51 +; CHECK: %141 = OpConstantNull %v4float )"; + // clang-format on - const std::string defs_after = - R"(OpCapability Shader -OpCapability StorageImageReadWithoutFormat -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" -OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" -OpName %_ "" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 3 -OpDecorate %g_tColor Binding 0 -OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 -OpDecorate %PerViewConstantBuffer_t Block -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_51 Block -OpMemberDecorate %_struct_51 0 Offset 0 -OpMemberDecorate %_struct_51 1 Offset 4 -OpDecorate %53 DescriptorSet 7 -OpDecorate %53 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -%void = OpTypeVoid -%9 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%int = OpTypeInt 32 1 -%v2int = OpTypeVector %int 2 -%int_0 = OpConstant %int 0 -%15 = OpTypeImage %float 2D 0 0 0 0 Unknown -%uint = OpTypeInt 32 0 -%uint_128 = OpConstant %uint 128 -%18 = OpTypeSampledImage %15 -%_arr_18_uint_128 = OpTypeArray %18 %uint_128 -%_ptr_UniformConstant__arr_18_uint_128 = OpTypePointer UniformConstant %_arr_18_uint_128 -%g_tColor = OpVariable %_ptr_UniformConstant__arr_18_uint_128 UniformConstant -%PerViewConstantBuffer_t = OpTypeStruct %uint -%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t -%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant -%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint -%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 -%_ptr_Input_v2int = OpTypePointer Input %v2int -%i_vTextureCoords = OpVariable %_ptr_Input_v2int Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -%uint_0 = OpConstant %uint 0 -%bool = OpTypeBool -%44 = OpTypeFunction %void %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_51 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_51 = OpTypePointer StorageBuffer %_struct_51 -%53 = OpVariable %_ptr_StorageBuffer__struct_51 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_1 = OpConstant %uint 1 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_3 = OpConstant %uint 3 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_51 = OpConstant %uint 51 -%99 = OpConstantNull %v4float -)"; - - const std::string func_before = - R"(%MainPs = OpFunction %void None %3 + const std::string main_func = R"( +%MainPs = OpFunction %void None %3 %5 = OpLabel %53 = OpLoad %v2int %i_vTextureCoords %63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 @@ -850,93 +893,32 @@ OpDecorate %gl_FragCoord BuiltIn FragCoord %75 = OpImage %20 %66 %71 = OpImageRead %v4float %75 %53 OpStore %_entryPointOutput_vColor %71 +; CHECK-NOT: %71 = OpImageRead %v4float %75 %53 +; CHECK-NOT: OpStore %_entryPointOutput_vColor %71 +; CHECK: %78 = OpULessThan %bool %64 %uint_128 +; CHECK: OpSelectionMerge %79 None +; CHECK: OpBranchConditional %78 %80 %81 +; CHECK: %80 = OpLabel +; CHECK: %82 = OpLoad %39 %65 +; CHECK: %83 = OpImage %20 %82 +; CHECK: %84 = OpImageRead %v4float %83 %53 +; CHECK: OpBranch %79 +; CHECK: %81 = OpLabel +; CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %64 %uint_128 +; CHECK: OpBranch %79 +; CHECK: %79 = OpLabel +; CHECK: %142 = OpPhi %v4float %84 %80 %141 %81 +; CHECK: OpStore %_entryPointOutput_vColor %142 OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%MainPs = OpFunction %void None %9 -%26 = OpLabel -%27 = OpLoad %v2int %i_vTextureCoords -%28 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 -%29 = OpLoad %uint %28 -%30 = OpAccessChain %_ptr_UniformConstant_18 %g_tColor %29 -%31 = OpLoad %18 %30 -%32 = OpImage %15 %31 -%36 = OpULessThan %bool %29 %uint_128 -OpSelectionMerge %37 None -OpBranchConditional %36 %38 %39 -%38 = OpLabel -%40 = OpLoad %18 %30 -%41 = OpImage %15 %40 -%42 = OpImageRead %v4float %41 %27 -OpBranch %37 -%39 = OpLabel -%98 = OpFunctionCall %void %43 %uint_51 %uint_0 %29 %uint_128 -OpBranch %37 -%37 = OpLabel -%100 = OpPhi %v4float %42 %38 %99 %39 -OpStore %_entryPointOutput_vColor %100 -OpReturn -OpFunctionEnd -)"; + const std::string output_func = kStreamWrite4Frag; - const std::string output_func = - R"(%43 = OpFunction %void None %44 -%45 = OpFunctionParameter %uint -%46 = OpFunctionParameter %uint -%47 = OpFunctionParameter %uint -%48 = OpFunctionParameter %uint -%49 = OpLabel -%55 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_0 -%58 = OpAtomicIAdd %uint %55 %uint_4 %uint_0 %uint_10 -%59 = OpIAdd %uint %58 %uint_10 -%60 = OpArrayLength %uint %53 1 -%61 = OpULessThanEqual %bool %59 %60 -OpSelectionMerge %62 None -OpBranchConditional %61 %63 %62 -%63 = OpLabel -%64 = OpIAdd %uint %58 %uint_0 -%66 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %64 -OpStore %66 %uint_10 -%68 = OpIAdd %uint %58 %uint_1 -%69 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %68 -OpStore %69 %uint_23 -%71 = OpIAdd %uint %58 %uint_2 -%72 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %71 -OpStore %72 %45 -%74 = OpIAdd %uint %58 %uint_3 -%75 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %74 -OpStore %75 %uint_4 -%78 = OpLoad %v4float %gl_FragCoord -%80 = OpBitcast %v4uint %78 -%81 = OpCompositeExtract %uint %80 0 -%82 = OpIAdd %uint %58 %uint_4 -%83 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %82 -OpStore %83 %81 -%84 = OpCompositeExtract %uint %80 1 -%86 = OpIAdd %uint %58 %uint_5 -%87 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %86 -OpStore %87 %84 -%89 = OpIAdd %uint %58 %uint_7 -%90 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %89 -OpStore %90 %46 -%92 = OpIAdd %uint %58 %uint_8 -%93 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %92 -OpStore %93 %47 -%95 = OpIAdd %uint %58 %uint_9 -%96 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %95 -OpStore %96 %48 -OpBranch %62 -%62 = OpLabel -OpReturn -OpFunctionEnd -)"; - - // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + output_func, true, - true, 7u, 23u, false, false, false, false, false); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(defs + main_func + output_func, + true, 7u, 23u, false, false, + false, false, false); } TEST_F(InstBindlessTest, InstrumentSampledImage) { @@ -944,11 +926,14 @@ TEST_F(InstBindlessTest, InstrumentSampledImage) { // using sampled image. This test was created by editing the SPIR-V // from the Simple test. - const std::string defs_before = - R"(OpCapability Shader + // clang-format off + const std::string defs = R"( +OpCapability Shader +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord OpExecutionMode %MainPs OriginUpperLeft OpSource HLSL 500 OpName %MainPs "MainPs" @@ -964,6 +949,8 @@ OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 OpDecorate %PerViewConstantBuffer_t Block OpDecorate %i_vTextureCoords Location 0 OpDecorate %_entryPointOutput_vColor Location 0 +)" + kOutputDecorations + R"( +; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -987,86 +974,32 @@ OpDecorate %_entryPointOutput_vColor Location 0 %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +; CHECK: uint_0 = OpConstant %uint 0 +; CHECK: bool = OpTypeBool +; CHECK: %81 = OpTypeFunction %void %uint %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kOutputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +; CHECK: %v4uint = OpTypeVector %uint 4 +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_49 = OpConstant %uint 49 +; CHECK: %136 = OpConstantNull %v4float )"; + // clang-format on - const std::string defs_after = - R"(OpCapability Shader -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" -OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" -OpName %_ "" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 3 -OpDecorate %g_tColor Binding 0 -OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 -OpDecorate %PerViewConstantBuffer_t Block -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_49 Block -OpMemberDecorate %_struct_49 0 Offset 0 -OpMemberDecorate %_struct_49 1 Offset 4 -OpDecorate %51 DescriptorSet 7 -OpDecorate %51 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -%void = OpTypeVoid -%9 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%v4float = OpTypeVector %float 4 -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%15 = OpTypeImage %float 2D 0 0 0 1 Unknown -%uint = OpTypeInt 32 0 -%uint_128 = OpConstant %uint 128 -%18 = OpTypeSampledImage %15 -%_arr_18_uint_128 = OpTypeArray %18 %uint_128 -%_ptr_UniformConstant__arr_18_uint_128 = OpTypePointer UniformConstant %_arr_18_uint_128 -%g_tColor = OpVariable %_ptr_UniformConstant__arr_18_uint_128 UniformConstant -%PerViewConstantBuffer_t = OpTypeStruct %uint -%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t -%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant -%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint -%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 -%_ptr_Input_v2float = OpTypePointer Input %v2float -%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -%uint_0 = OpConstant %uint 0 -%bool = OpTypeBool -%42 = OpTypeFunction %void %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_49 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_49 = OpTypePointer StorageBuffer %_struct_49 -%51 = OpVariable %_ptr_StorageBuffer__struct_49 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_1 = OpConstant %uint 1 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_3 = OpConstant %uint 3 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_49 = OpConstant %uint 49 -%97 = OpConstantNull %v4float -)"; - - const std::string func_before = - R"(%MainPs = OpFunction %void None %3 + const std::string main_func = R"( +%MainPs = OpFunction %void None %3 %5 = OpLabel %53 = OpLoad %v2float %i_vTextureCoords %63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 @@ -1075,91 +1008,31 @@ OpDecorate %gl_FragCoord BuiltIn FragCoord %66 = OpLoad %39 %65 %71 = OpImageSampleImplicitLod %v4float %66 %53 OpStore %_entryPointOutput_vColor %71 +; CHECK-NOT: %71 = OpImageSampleImplicitLod %v4float %66 %53 +; CHECK-NOT: OpStore %_entryPointOutput_vColor %71 +; CHECK: %74 = OpULessThan %bool %64 %uint_128 +; CHECK: OpSelectionMerge %75 None +; CHECK: OpBranchConditional %74 %76 %77 +; CHECK: %76 = OpLabel +; CHECK: %78 = OpLoad %39 %65 +; CHECK: %79 = OpImageSampleImplicitLod %v4float %78 %53 +; CHECK: OpBranch %75 +; CHECK: %77 = OpLabel +; CHECK: %135 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_49 %uint_0 %64 %uint_128 +; CHECK: OpBranch %75 +; CHECK: %75 = OpLabel +; CHECK: %137 = OpPhi %v4float %79 %76 %136 %77 +; CHECK: OpStore %_entryPointOutput_vColor %137 OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%MainPs = OpFunction %void None %9 -%26 = OpLabel -%27 = OpLoad %v2float %i_vTextureCoords -%28 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 -%29 = OpLoad %uint %28 -%30 = OpAccessChain %_ptr_UniformConstant_18 %g_tColor %29 -%31 = OpLoad %18 %30 -%35 = OpULessThan %bool %29 %uint_128 -OpSelectionMerge %36 None -OpBranchConditional %35 %37 %38 -%37 = OpLabel -%39 = OpLoad %18 %30 -%40 = OpImageSampleImplicitLod %v4float %39 %27 -OpBranch %36 -%38 = OpLabel -%96 = OpFunctionCall %void %41 %uint_49 %uint_0 %29 %uint_128 -OpBranch %36 -%36 = OpLabel -%98 = OpPhi %v4float %40 %37 %97 %38 -OpStore %_entryPointOutput_vColor %98 -OpReturn -OpFunctionEnd -)"; + const std::string output_func = kStreamWrite4Frag; - const std::string output_func = - R"(%41 = OpFunction %void None %42 -%43 = OpFunctionParameter %uint -%44 = OpFunctionParameter %uint -%45 = OpFunctionParameter %uint -%46 = OpFunctionParameter %uint -%47 = OpLabel -%53 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_0 -%56 = OpAtomicIAdd %uint %53 %uint_4 %uint_0 %uint_10 -%57 = OpIAdd %uint %56 %uint_10 -%58 = OpArrayLength %uint %51 1 -%59 = OpULessThanEqual %bool %57 %58 -OpSelectionMerge %60 None -OpBranchConditional %59 %61 %60 -%61 = OpLabel -%62 = OpIAdd %uint %56 %uint_0 -%64 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %62 -OpStore %64 %uint_10 -%66 = OpIAdd %uint %56 %uint_1 -%67 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %66 -OpStore %67 %uint_23 -%69 = OpIAdd %uint %56 %uint_2 -%70 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %69 -OpStore %70 %43 -%72 = OpIAdd %uint %56 %uint_3 -%73 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %72 -OpStore %73 %uint_4 -%76 = OpLoad %v4float %gl_FragCoord -%78 = OpBitcast %v4uint %76 -%79 = OpCompositeExtract %uint %78 0 -%80 = OpIAdd %uint %56 %uint_4 -%81 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %80 -OpStore %81 %79 -%82 = OpCompositeExtract %uint %78 1 -%84 = OpIAdd %uint %56 %uint_5 -%85 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %84 -OpStore %85 %82 -%87 = OpIAdd %uint %56 %uint_7 -%88 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %87 -OpStore %88 %44 -%90 = OpIAdd %uint %56 %uint_8 -%91 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %90 -OpStore %91 %45 -%93 = OpIAdd %uint %56 %uint_9 -%94 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %93 -OpStore %94 %46 -OpBranch %60 -%60 = OpLabel -OpReturn -OpFunctionEnd -)"; - - // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + output_func, true, - true, 7u, 23u, false, false, false, false, false); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(defs + main_func + output_func, + true, 7u, 23u, false, false, + false, false, false); } TEST_F(InstBindlessTest, InstrumentImageWrite) { @@ -1167,12 +1040,15 @@ TEST_F(InstBindlessTest, InstrumentImageWrite) { // doing bindless image write. This test was created by editing the SPIR-V // from the Simple test. - const std::string defs_before = - R"(OpCapability Shader + // clang-format off + const std::string defs = R"( +OpCapability Shader OpCapability StorageImageWriteWithoutFormat +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord OpExecutionMode %MainPs OriginUpperLeft OpSource HLSL 500 OpName %MainPs "MainPs" @@ -1188,6 +1064,9 @@ OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 OpDecorate %PerViewConstantBuffer_t Block OpDecorate %i_vTextureCoords Location 0 OpDecorate %_entryPointOutput_vColor Location 0 +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kOutputDecorations + R"( +; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -1212,87 +1091,31 @@ OpDecorate %_entryPointOutput_vColor Location 0 %i_vTextureCoords = OpVariable %_ptr_Input_v2int Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +; CHECK: uint_0 = OpConstant %uint 0 +; CHECK: bool = OpTypeBool +; CHECK: %41 = OpTypeFunction %void %uint %uint %uint %uint +; CHECK: _runtimearr_uint = OpTypeRuntimeArray %uint +)" + kOutputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +; CHECK: %v4uint = OpTypeVector %uint 4 +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_51 = OpConstant %uint 51 )"; + // clang-format on - const std::string defs_after = - R"(OpCapability Shader -OpCapability StorageImageWriteWithoutFormat -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" -OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" -OpName %_ "" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 3 -OpDecorate %g_tColor Binding 0 -OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 -OpDecorate %PerViewConstantBuffer_t Block -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_48 Block -OpMemberDecorate %_struct_48 0 Offset 0 -OpMemberDecorate %_struct_48 1 Offset 4 -OpDecorate %50 DescriptorSet 7 -OpDecorate %50 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -%void = OpTypeVoid -%9 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%v4float = OpTypeVector %float 4 -%int = OpTypeInt 32 1 -%v2int = OpTypeVector %int 2 -%int_0 = OpConstant %int 0 -%16 = OpTypeImage %float 2D 0 0 0 0 Unknown -%uint = OpTypeInt 32 0 -%uint_128 = OpConstant %uint 128 -%19 = OpConstantNull %v4float -%_arr_16_uint_128 = OpTypeArray %16 %uint_128 -%_ptr_UniformConstant__arr_16_uint_128 = OpTypePointer UniformConstant %_arr_16_uint_128 -%g_tColor = OpVariable %_ptr_UniformConstant__arr_16_uint_128 UniformConstant -%PerViewConstantBuffer_t = OpTypeStruct %uint -%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t -%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant -%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint -%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16 -%_ptr_Input_v2int = OpTypePointer Input %v2int -%i_vTextureCoords = OpVariable %_ptr_Input_v2int Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -%uint_0 = OpConstant %uint 0 -%bool = OpTypeBool -%41 = OpTypeFunction %void %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_48 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_48 = OpTypePointer StorageBuffer %_struct_48 -%50 = OpVariable %_ptr_StorageBuffer__struct_48 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_1 = OpConstant %uint 1 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_3 = OpConstant %uint 3 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_51 = OpConstant %uint 51 -)"; - - const std::string func_before = - R"(%MainPs = OpFunction %void None %3 + const std::string main_func = R"( +%MainPs = OpFunction %void None %3 %5 = OpLabel %53 = OpLoad %v2int %i_vTextureCoords %63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 @@ -1301,90 +1124,30 @@ OpDecorate %gl_FragCoord BuiltIn FragCoord %66 = OpLoad %20 %65 OpImageWrite %66 %53 %80 OpStore %_entryPointOutput_vColor %80 +; CHECK-NOT: OpImageWrite %66 %53 %80 +; CHECK-NOT: OpStore %_entryPointOutput_vColor %80 +; CHECK: %35 = OpULessThan %bool %30 %uint_128 +; CHECK: OpSelectionMerge %36 None +; CHECK: OpBranchConditional %35 %37 %38 +; CHECK: %37 = OpLabel +; CHECK: %39 = OpLoad %16 %31 +; CHECK: OpImageWrite %39 %28 %19 +; CHECK: OpBranch %36 +; CHECK: %38 = OpLabel +; CHECK: %95 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %30 %uint_128 +; CHECK: OpBranch %36 +; CHECK: %36 = OpLabel +; CHECK: OpStore %_entryPointOutput_vColor %19 OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%MainPs = OpFunction %void None %9 -%27 = OpLabel -%28 = OpLoad %v2int %i_vTextureCoords -%29 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 -%30 = OpLoad %uint %29 -%31 = OpAccessChain %_ptr_UniformConstant_16 %g_tColor %30 -%32 = OpLoad %16 %31 -%35 = OpULessThan %bool %30 %uint_128 -OpSelectionMerge %36 None -OpBranchConditional %35 %37 %38 -%37 = OpLabel -%39 = OpLoad %16 %31 -OpImageWrite %39 %28 %19 -OpBranch %36 -%38 = OpLabel -%95 = OpFunctionCall %void %40 %uint_51 %uint_0 %30 %uint_128 -OpBranch %36 -%36 = OpLabel -OpStore %_entryPointOutput_vColor %19 -OpReturn -OpFunctionEnd -)"; - - const std::string output_func = - R"(%40 = OpFunction %void None %41 -%42 = OpFunctionParameter %uint -%43 = OpFunctionParameter %uint -%44 = OpFunctionParameter %uint -%45 = OpFunctionParameter %uint -%46 = OpLabel -%52 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_0 -%55 = OpAtomicIAdd %uint %52 %uint_4 %uint_0 %uint_10 -%56 = OpIAdd %uint %55 %uint_10 -%57 = OpArrayLength %uint %50 1 -%58 = OpULessThanEqual %bool %56 %57 -OpSelectionMerge %59 None -OpBranchConditional %58 %60 %59 -%60 = OpLabel -%61 = OpIAdd %uint %55 %uint_0 -%63 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %61 -OpStore %63 %uint_10 -%65 = OpIAdd %uint %55 %uint_1 -%66 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %65 -OpStore %66 %uint_23 -%68 = OpIAdd %uint %55 %uint_2 -%69 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %68 -OpStore %69 %42 -%71 = OpIAdd %uint %55 %uint_3 -%72 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %71 -OpStore %72 %uint_4 -%75 = OpLoad %v4float %gl_FragCoord -%77 = OpBitcast %v4uint %75 -%78 = OpCompositeExtract %uint %77 0 -%79 = OpIAdd %uint %55 %uint_4 -%80 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %79 -OpStore %80 %78 -%81 = OpCompositeExtract %uint %77 1 -%83 = OpIAdd %uint %55 %uint_5 -%84 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %83 -OpStore %84 %81 -%86 = OpIAdd %uint %55 %uint_7 -%87 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %86 -OpStore %87 %43 -%89 = OpIAdd %uint %55 %uint_8 -%90 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %89 -OpStore %90 %44 -%92 = OpIAdd %uint %55 %uint_9 -%93 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %92 -OpStore %93 %45 -OpBranch %59 -%59 = OpLabel -OpReturn -OpFunctionEnd -)"; + const std::string output_func = kStreamWrite4Frag; // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + output_func, true, - true, 7u, 23u, false, false, false, false, false); + SinglePassRunAndMatch(defs + main_func + output_func, + true, 7u, 23u, false, false, + false, false, false); } TEST_F(InstBindlessTest, InstrumentVertexSimple) { @@ -1392,9 +1155,11 @@ TEST_F(InstBindlessTest, InstrumentVertexSimple) { // doing bindless image write. This test was created by editing the SPIR-V // from the Simple test. - const std::string defs_before = - R"(OpCapability Shader + // clang-format off + const std::string defs = R"( +OpCapability Shader OpCapability Sampled1D +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %main "main" %_ %coords2D @@ -1413,6 +1178,10 @@ OpName %foo "foo" OpMemberName %foo 0 "g_idx" OpName %__0 "" OpName %coords2D "coords2D" +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kOutputDecorations + R"( +; CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex +; CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex OpMemberDecorate %gl_PerVertex 0 BuiltIn Position OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance @@ -1455,106 +1224,31 @@ OpDecorate %coords2D Location 0 %v2float = OpTypeVector %float 2 %_ptr_Input_v2float = OpTypePointer Input %v2float %coords2D = OpVariable %_ptr_Input_v2float Input +; CHECK: %uint_0 = OpConstant %uint 0 +; CHECK: %bool = OpTypeBool +; CHECK: %54 = OpTypeFunction %void %uint %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kOutputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %_ptr_Input_uint = OpTypePointer Input %uint +; CHECK: %gl_VertexIndex = OpVariable %_ptr_Input_uint Input +; CHECK: %gl_InstanceIndex = OpVariable %_ptr_Input_uint Input +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_74 = OpConstant %uint 74 +; CHECK: %106 = OpConstantNull %v4float )"; + // clang-format on - const std::string defs_after = - R"(OpCapability Shader -OpCapability Sampled1D -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Vertex %main "main" %_ %coords2D %gl_VertexIndex %gl_InstanceIndex -OpSource GLSL 450 -OpName %main "main" -OpName %lod "lod" -OpName %coords1D "coords1D" -OpName %gl_PerVertex "gl_PerVertex" -OpMemberName %gl_PerVertex 0 "gl_Position" -OpMemberName %gl_PerVertex 1 "gl_PointSize" -OpMemberName %gl_PerVertex 2 "gl_ClipDistance" -OpMemberName %gl_PerVertex 3 "gl_CullDistance" -OpName %_ "" -OpName %texSampler1D "texSampler1D" -OpName %foo "foo" -OpMemberName %foo 0 "g_idx" -OpName %__0 "" -OpName %coords2D "coords2D" -OpMemberDecorate %gl_PerVertex 0 BuiltIn Position -OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize -OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance -OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance -OpDecorate %gl_PerVertex Block -OpDecorate %texSampler1D DescriptorSet 0 -OpDecorate %texSampler1D Binding 3 -OpMemberDecorate %foo 0 Offset 0 -OpDecorate %foo Block -OpDecorate %__0 DescriptorSet 0 -OpDecorate %__0 Binding 5 -OpDecorate %coords2D Location 0 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_61 Block -OpMemberDecorate %_struct_61 0 Offset 0 -OpMemberDecorate %_struct_61 1 Offset 4 -OpDecorate %63 DescriptorSet 7 -OpDecorate %63 Binding 0 -OpDecorate %gl_VertexIndex BuiltIn VertexIndex -OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex -%void = OpTypeVoid -%12 = OpTypeFunction %void -%float = OpTypeFloat 32 -%_ptr_Function_float = OpTypePointer Function %float -%float_3 = OpConstant %float 3 -%float_1_78900003 = OpConstant %float 1.78900003 -%v4float = OpTypeVector %float 4 -%uint = OpTypeInt 32 0 -%uint_1 = OpConstant %uint 1 -%_arr_float_uint_1 = OpTypeArray %float %uint_1 -%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 -%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex -%_ = OpVariable %_ptr_Output_gl_PerVertex Output -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%24 = OpTypeImage %float 1D 0 0 0 1 Unknown -%25 = OpTypeSampledImage %24 -%uint_128 = OpConstant %uint 128 -%_arr_25_uint_128 = OpTypeArray %25 %uint_128 -%_ptr_UniformConstant__arr_25_uint_128 = OpTypePointer UniformConstant %_arr_25_uint_128 -%texSampler1D = OpVariable %_ptr_UniformConstant__arr_25_uint_128 UniformConstant -%foo = OpTypeStruct %int -%_ptr_Uniform_foo = OpTypePointer Uniform %foo -%__0 = OpVariable %_ptr_Uniform_foo Uniform -%_ptr_Uniform_int = OpTypePointer Uniform %int -%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25 -%_ptr_Output_v4float = OpTypePointer Output %v4float -%v2float = OpTypeVector %float 2 -%_ptr_Input_v2float = OpTypePointer Input %v2float -%coords2D = OpVariable %_ptr_Input_v2float Input -%uint_0 = OpConstant %uint 0 -%bool = OpTypeBool -%54 = OpTypeFunction %void %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_61 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_61 = OpTypePointer StorageBuffer %_struct_61 -%63 = OpVariable %_ptr_StorageBuffer__struct_61 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_3 = OpConstant %uint 3 -%_ptr_Input_uint = OpTypePointer Input %uint -%gl_VertexIndex = OpVariable %_ptr_Input_uint Input -%gl_InstanceIndex = OpVariable %_ptr_Input_uint Input -%uint_5 = OpConstant %uint 5 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_74 = OpConstant %uint 74 -%106 = OpConstantNull %v4float -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %3 + const std::string main_func = R"( +%main = OpFunction %void None %3 %5 = OpLabel %lod = OpVariable %_ptr_Function_float Function %coords1D = OpVariable %_ptr_Function_float Function @@ -1569,96 +1263,34 @@ OpStore %coords1D %float_1_78900003 %38 = OpImageSampleExplicitLod %v4float %35 %36 Lod %37 %40 = OpAccessChain %_ptr_Output_v4float %_ %int_0 OpStore %40 %38 +; CHECK-NOT: %38 = OpImageSampleExplicitLod %v4float %35 %36 Lod %37 +; CHECK-NOT: %40 = OpAccessChain %_ptr_Output_v4float %_ %int_0 +; CHECK-NOT: OpStore %40 %38 +; CHECK: %46 = OpULessThan %bool %37 %uint_128 +; CHECK: OpSelectionMerge %47 None +; CHECK: OpBranchConditional %46 %48 %49 +; CHECK: %48 = OpLabel +; CHECK: %50 = OpLoad %25 %38 +; CHECK: %51 = OpImageSampleExplicitLod %v4float %50 %40 Lod %41 +; CHECK: OpBranch %47 +; CHECK: %49 = OpLabel +; CHECK: %52 = OpBitcast %uint %37 +; CHECK: %105 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_74 %uint_0 %52 %uint_128 +; CHECK: OpBranch %47 +; CHECK: %47 = OpLabel +; CHECK: %107 = OpPhi %v4float %51 %48 %106 %49 +; CHECK: %43 = OpAccessChain %_ptr_Output_v4float %_ %int_0 +; CHECK: OpStore %43 %107 OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %12 -%35 = OpLabel -%lod = OpVariable %_ptr_Function_float Function -%coords1D = OpVariable %_ptr_Function_float Function -OpStore %lod %float_3 -OpStore %coords1D %float_1_78900003 -%36 = OpAccessChain %_ptr_Uniform_int %__0 %int_0 -%37 = OpLoad %int %36 -%38 = OpAccessChain %_ptr_UniformConstant_25 %texSampler1D %37 -%39 = OpLoad %25 %38 -%40 = OpLoad %float %coords1D -%41 = OpLoad %float %lod -%46 = OpULessThan %bool %37 %uint_128 -OpSelectionMerge %47 None -OpBranchConditional %46 %48 %49 -%48 = OpLabel -%50 = OpLoad %25 %38 -%51 = OpImageSampleExplicitLod %v4float %50 %40 Lod %41 -OpBranch %47 -%49 = OpLabel -%52 = OpBitcast %uint %37 -%105 = OpFunctionCall %void %53 %uint_74 %uint_0 %52 %uint_128 -OpBranch %47 -%47 = OpLabel -%107 = OpPhi %v4float %51 %48 %106 %49 -%43 = OpAccessChain %_ptr_Output_v4float %_ %int_0 -OpStore %43 %107 -OpReturn -OpFunctionEnd -)"; - - const std::string output_func = - R"(%53 = OpFunction %void None %54 -%55 = OpFunctionParameter %uint -%56 = OpFunctionParameter %uint -%57 = OpFunctionParameter %uint -%58 = OpFunctionParameter %uint -%59 = OpLabel -%65 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_0 -%68 = OpAtomicIAdd %uint %65 %uint_4 %uint_0 %uint_10 -%69 = OpIAdd %uint %68 %uint_10 -%70 = OpArrayLength %uint %63 1 -%71 = OpULessThanEqual %bool %69 %70 -OpSelectionMerge %72 None -OpBranchConditional %71 %73 %72 -%73 = OpLabel -%74 = OpIAdd %uint %68 %uint_0 -%75 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %74 -OpStore %75 %uint_10 -%77 = OpIAdd %uint %68 %uint_1 -%78 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %77 -OpStore %78 %uint_23 -%80 = OpIAdd %uint %68 %uint_2 -%81 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %80 -OpStore %81 %55 -%83 = OpIAdd %uint %68 %uint_3 -%84 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %83 -OpStore %84 %uint_0 -%87 = OpLoad %uint %gl_VertexIndex -%88 = OpIAdd %uint %68 %uint_4 -%89 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %88 -OpStore %89 %87 -%91 = OpLoad %uint %gl_InstanceIndex -%93 = OpIAdd %uint %68 %uint_5 -%94 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %93 -OpStore %94 %91 -%96 = OpIAdd %uint %68 %uint_7 -%97 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %96 -OpStore %97 %56 -%99 = OpIAdd %uint %68 %uint_8 -%100 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %99 -OpStore %100 %57 -%102 = OpIAdd %uint %68 %uint_9 -%103 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %102 -OpStore %103 %58 -OpBranch %72 -%72 = OpLabel -OpReturn -OpFunctionEnd -)"; + const std::string output_func = kStreamWrite4Vert; // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + output_func, true, - true, 7u, 23u, false, false, false, false, false); + SinglePassRunAndMatch(defs + main_func + output_func, + true, 7u, 23u, false, false, + false, false, false); } TEST_F(InstBindlessTest, InstrumentTeseSimple) { @@ -1680,13 +1312,13 @@ TEST_F(InstBindlessTest, InstrumentTeseSimple) { // gl_Position = adds[uniform_index_buffer.index].val; // } // - // clang-format on - const std::string defs_before = - R"(OpCapability Tessellation + const std::string defs = R"( +OpCapability Tessellation %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint TessellationEvaluation %main "main" %_ +; CHECK: OpEntryPoint TessellationEvaluation %main "main" %_ %gl_PrimitiveID %gl_TessCoord OpExecutionMode %main Triangles OpExecutionMode %main SpacingEqual OpExecutionMode %main VertexOrderCw @@ -1718,6 +1350,10 @@ OpMemberDecorate %ufoo 0 Offset 0 OpDecorate %ufoo Block OpDecorate %uniform_index_buffer DescriptorSet 0 OpDecorate %uniform_index_buffer Binding 0 +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kOutputDecorations + R"( +; CHECK: OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId +; CHECK: OpDecorate %gl_TessCoord BuiltIn TessCoord %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -1741,211 +1377,80 @@ OpDecorate %uniform_index_buffer Binding 0 %_ptr_Uniform_uint = OpTypePointer Uniform %uint %_ptr_StorageBuffer_v4float = OpTypePointer StorageBuffer %v4float %_ptr_Output_v4float = OpTypePointer Output %v4float +; CHECK: %uint_0 = OpConstant %uint 0 +; CHECK: %bool = OpTypeBool +; CHECK: %40 = OpTypeFunction %void %uint %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kOutputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %_ptr_Input_uint = OpTypePointer Input %uint +; CHECK: %gl_PrimitiveID = OpVariable %_ptr_Input_uint Input +; CHECK: %v3float = OpTypeVector %float 3 +; CHECK: %_ptr_Input_v3float = OpTypePointer Input %v3float +; CHECK: %gl_TessCoord = OpVariable %_ptr_Input_v3float Input +; CHECK: %v3uint = OpTypeVector %uint 3 +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_6 = OpConstant %uint 6 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_63 = OpConstant %uint 63 +; CHECK: %101 = OpConstantNull %v4float )"; + // clang-format on - const std::string defs_after = - R"(OpCapability Tessellation -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint TessellationEvaluation %main "main" %_ %gl_PrimitiveID %gl_TessCoord -OpExecutionMode %main Triangles -OpExecutionMode %main SpacingEqual -OpExecutionMode %main VertexOrderCw -OpSource GLSL 450 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpName %main "main" -OpName %gl_PerVertex "gl_PerVertex" -OpMemberName %gl_PerVertex 0 "gl_Position" -OpMemberName %gl_PerVertex 1 "gl_PointSize" -OpMemberName %gl_PerVertex 2 "gl_ClipDistance" -OpMemberName %gl_PerVertex 3 "gl_CullDistance" -OpName %_ "" -OpName %bfoo "bfoo" -OpMemberName %bfoo 0 "val" -OpName %adds "adds" -OpName %ufoo "ufoo" -OpMemberName %ufoo 0 "index" -OpName %uniform_index_buffer "uniform_index_buffer" -OpMemberDecorate %gl_PerVertex 0 BuiltIn Position -OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize -OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance -OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance -OpDecorate %gl_PerVertex Block -OpMemberDecorate %bfoo 0 Offset 0 -OpDecorate %bfoo Block -OpDecorate %adds DescriptorSet 0 -OpDecorate %adds Binding 1 -OpMemberDecorate %ufoo 0 Offset 0 -OpDecorate %ufoo Block -OpDecorate %uniform_index_buffer DescriptorSet 0 -OpDecorate %uniform_index_buffer Binding 0 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_47 Block -OpMemberDecorate %_struct_47 0 Offset 0 -OpMemberDecorate %_struct_47 1 Offset 4 -OpDecorate %49 DescriptorSet 7 -OpDecorate %49 Binding 0 -OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId -OpDecorate %gl_TessCoord BuiltIn TessCoord -%void = OpTypeVoid -%10 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%uint = OpTypeInt 32 0 -%uint_1 = OpConstant %uint 1 -%_arr_float_uint_1 = OpTypeArray %float %uint_1 -%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 -%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex -%_ = OpVariable %_ptr_Output_gl_PerVertex Output -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%bfoo = OpTypeStruct %v4float -%uint_11 = OpConstant %uint 11 -%_arr_bfoo_uint_11 = OpTypeArray %bfoo %uint_11 -%_ptr_StorageBuffer__arr_bfoo_uint_11 = OpTypePointer StorageBuffer %_arr_bfoo_uint_11 -%adds = OpVariable %_ptr_StorageBuffer__arr_bfoo_uint_11 StorageBuffer -%ufoo = OpTypeStruct %uint -%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo -%uniform_index_buffer = OpVariable %_ptr_Uniform_ufoo Uniform -%_ptr_Uniform_uint = OpTypePointer Uniform %uint -%_ptr_StorageBuffer_v4float = OpTypePointer StorageBuffer %v4float -%_ptr_Output_v4float = OpTypePointer Output %v4float -%uint_0 = OpConstant %uint 0 -%bool = OpTypeBool -%40 = OpTypeFunction %void %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_47 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_47 = OpTypePointer StorageBuffer %_struct_47 -%49 = OpVariable %_ptr_StorageBuffer__struct_47 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_3 = OpConstant %uint 3 -%_ptr_Input_uint = OpTypePointer Input %uint -%gl_PrimitiveID = OpVariable %_ptr_Input_uint Input -%v3float = OpTypeVector %float 3 -%_ptr_Input_v3float = OpTypePointer Input %v3float -%gl_TessCoord = OpVariable %_ptr_Input_v3float Input -%v3uint = OpTypeVector %uint 3 -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_63 = OpConstant %uint 63 -%101 = OpConstantNull %v4float -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %3 + const std::string main_func = R"( +%main = OpFunction %void None %3 %5 = OpLabel %25 = OpAccessChain %_ptr_Uniform_uint %uniform_index_buffer %int_0 %26 = OpLoad %uint %25 %28 = OpAccessChain %_ptr_StorageBuffer_v4float %adds %26 %int_0 %29 = OpLoad %v4float %28 +; CHECK-NOT: %29 = OpLoad %v4float %28 +; CHECK: %34 = OpULessThan %bool %28 %uint_11 +; CHECK: OpSelectionMerge %35 None +; CHECK: OpBranchConditional %34 %36 %37 +; CHECK: %36 = OpLabel +; CHECK: %38 = OpLoad %v4float %29 +; CHECK: OpBranch %35 +; CHECK: %37 = OpLabel +; CHECK: %100 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_63 %uint_0 %28 %uint_11 +; CHECK: OpBranch %35 +; CHECK: %35 = OpLabel +; CHECK: %102 = OpPhi %v4float %38 %36 %101 %37 %31 = OpAccessChain %_ptr_Output_v4float %_ %int_0 OpStore %31 %29 +; CHECK-NOT: OpStore %31 %29 +; CHECK: OpStore %31 %102 OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %10 -%26 = OpLabel -%27 = OpAccessChain %_ptr_Uniform_uint %uniform_index_buffer %int_0 -%28 = OpLoad %uint %27 -%29 = OpAccessChain %_ptr_StorageBuffer_v4float %adds %28 %int_0 -%34 = OpULessThan %bool %28 %uint_11 -OpSelectionMerge %35 None -OpBranchConditional %34 %36 %37 -%36 = OpLabel -%38 = OpLoad %v4float %29 -OpBranch %35 -%37 = OpLabel -%100 = OpFunctionCall %void %39 %uint_63 %uint_0 %28 %uint_11 -OpBranch %35 -%35 = OpLabel -%102 = OpPhi %v4float %38 %36 %101 %37 -%31 = OpAccessChain %_ptr_Output_v4float %_ %int_0 -OpStore %31 %102 -OpReturn -OpFunctionEnd -)"; - - const std::string output_func = - R"(%39 = OpFunction %void None %40 -%41 = OpFunctionParameter %uint -%42 = OpFunctionParameter %uint -%43 = OpFunctionParameter %uint -%44 = OpFunctionParameter %uint -%45 = OpLabel -%51 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_0 -%54 = OpAtomicIAdd %uint %51 %uint_4 %uint_0 %uint_10 -%55 = OpIAdd %uint %54 %uint_10 -%56 = OpArrayLength %uint %49 1 -%57 = OpULessThanEqual %bool %55 %56 -OpSelectionMerge %58 None -OpBranchConditional %57 %59 %58 -%59 = OpLabel -%60 = OpIAdd %uint %54 %uint_0 -%61 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %60 -OpStore %61 %uint_10 -%63 = OpIAdd %uint %54 %uint_1 -%64 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %63 -OpStore %64 %uint_23 -%66 = OpIAdd %uint %54 %uint_2 -%67 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %66 -OpStore %67 %41 -%69 = OpIAdd %uint %54 %uint_3 -%70 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %69 -OpStore %70 %uint_2 -%73 = OpLoad %uint %gl_PrimitiveID -%74 = OpIAdd %uint %54 %uint_4 -%75 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %74 -OpStore %75 %73 -%79 = OpLoad %v3float %gl_TessCoord -%81 = OpBitcast %v3uint %79 -%82 = OpCompositeExtract %uint %81 0 -%83 = OpCompositeExtract %uint %81 1 -%85 = OpIAdd %uint %54 %uint_5 -%86 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %85 -OpStore %86 %82 -%88 = OpIAdd %uint %54 %uint_6 -%89 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %88 -OpStore %89 %83 -%91 = OpIAdd %uint %54 %uint_7 -%92 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %91 -OpStore %92 %42 -%94 = OpIAdd %uint %54 %uint_8 -%95 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %94 -OpStore %95 %43 -%97 = OpIAdd %uint %54 %uint_9 -%98 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %97 -OpStore %98 %44 -OpBranch %58 -%58 = OpLabel -OpReturn -OpFunctionEnd -)"; + const std::string output_func = kStreamWrite4Tese; // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + output_func, true, - true, 7u, 23u, false, false, false, false, false); + SinglePassRunAndMatch(defs + main_func + output_func, + true, 7u, 23u, false, false, + false, false, false); } TEST_F(InstBindlessTest, MultipleDebugFunctions) { // Same source as Simple, but compiled -g and not optimized, especially not // inlined. The OpSource has had the source extracted for the sake of brevity. - const std::string defs_before = - R"(OpCapability Shader + // clang-format off + const std::string defs = R"( +OpCapability Shader +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %2 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord OpExecutionMode %MainPs OriginUpperLeft %1 = OpString "foo5.frag" OpSource HLSL 500 %1 @@ -1974,6 +1479,9 @@ OpDecorate %g_sAniso DescriptorSet 0 OpDecorate %g_sAniso Binding 1 OpDecorate %i_vTextureCoords Location 0 OpDecorate %_entryPointOutput_vColor Location 0 +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kOutputDecorations + R"( +; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %4 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -2007,109 +1515,32 @@ OpDecorate %_entryPointOutput_vColor Location 0 %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +; CHECK: %uint_0 = OpConstant %uint 0 +; CHECK: %bool = OpTypeBool +; CHECK: %70 = OpTypeFunction %void %uint %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kOutputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +; CHECK: %v4uint = OpTypeVector %uint 4 +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_109 = OpConstant %uint 109 +; CHECK: %125 = OpConstantNull %v4float )"; + // clang-format on - const std::string defs_after = - R"(OpCapability Shader -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord -OpExecutionMode %MainPs OriginUpperLeft -%5 = OpString "foo5.frag" -OpSource HLSL 500 %5 -OpName %MainPs "MainPs" -OpName %PS_INPUT "PS_INPUT" -OpMemberName %PS_INPUT 0 "vTextureCoords" -OpName %PS_OUTPUT "PS_OUTPUT" -OpMemberName %PS_OUTPUT 0 "vColor" -OpName %_MainPs_struct_PS_INPUT_vf21_ "@MainPs(struct-PS_INPUT-vf21;" -OpName %i "i" -OpName %ps_output "ps_output" -OpName %g_tColor "g_tColor" -OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" -OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" -OpName %_ "" -OpName %g_sAniso "g_sAniso" -OpName %i_0 "i" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpName %param "param" -OpDecorate %g_tColor DescriptorSet 0 -OpDecorate %g_tColor Binding 0 -OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 -OpDecorate %PerViewConstantBuffer_t Block -OpDecorate %g_sAniso DescriptorSet 0 -OpDecorate %g_sAniso Binding 1 -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_77 Block -OpMemberDecorate %_struct_77 0 Offset 0 -OpMemberDecorate %_struct_77 1 Offset 4 -OpDecorate %79 DescriptorSet 7 -OpDecorate %79 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -%void = OpTypeVoid -%18 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%PS_INPUT = OpTypeStruct %v2float -%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT -%v4float = OpTypeVector %float 4 -%PS_OUTPUT = OpTypeStruct %v4float -%23 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT -%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%27 = OpTypeImage %float 2D 0 0 0 1 Unknown -%uint = OpTypeInt 32 0 -%uint_128 = OpConstant %uint 128 -%_arr_27_uint_128 = OpTypeArray %27 %uint_128 -%_ptr_UniformConstant__arr_27_uint_128 = OpTypePointer UniformConstant %_arr_27_uint_128 -%g_tColor = OpVariable %_ptr_UniformConstant__arr_27_uint_128 UniformConstant -%PerViewConstantBuffer_t = OpTypeStruct %uint -%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t -%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant -%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint -%_ptr_UniformConstant_27 = OpTypePointer UniformConstant %27 -%35 = OpTypeSampler -%_ptr_UniformConstant_35 = OpTypePointer UniformConstant %35 -%g_sAniso = OpVariable %_ptr_UniformConstant_35 UniformConstant -%37 = OpTypeSampledImage %27 -%_ptr_Function_v2float = OpTypePointer Function %v2float -%_ptr_Function_v4float = OpTypePointer Function %v4float -%_ptr_Input_v2float = OpTypePointer Input %v2float -%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -%uint_0 = OpConstant %uint 0 -%bool = OpTypeBool -%70 = OpTypeFunction %void %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_77 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_77 = OpTypePointer StorageBuffer %_struct_77 -%79 = OpVariable %_ptr_StorageBuffer__struct_77 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_1 = OpConstant %uint 1 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_3 = OpConstant %uint 3 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_109 = OpConstant %uint 109 -%125 = OpConstantNull %v4float -)"; - - const std::string func1_before = - R"(%MainPs = OpFunction %void None %4 + const std::string func1 = R"( +%MainPs = OpFunction %void None %4 %6 = OpLabel %i_0 = OpVariable %_ptr_Function_PS_INPUT Function %param = OpVariable %_ptr_Function_PS_INPUT Function @@ -2126,26 +1557,8 @@ OpReturn OpFunctionEnd )"; - const std::string func1_after = - R"(%MainPs = OpFunction %void None %18 -%42 = OpLabel -%i_0 = OpVariable %_ptr_Function_PS_INPUT Function -%param = OpVariable %_ptr_Function_PS_INPUT Function -OpLine %5 21 0 -%43 = OpLoad %v2float %i_vTextureCoords -%44 = OpAccessChain %_ptr_Function_v2float %i_0 %int_0 -OpStore %44 %43 -%45 = OpLoad %PS_INPUT %i_0 -OpStore %param %45 -%46 = OpFunctionCall %PS_OUTPUT %_MainPs_struct_PS_INPUT_vf21_ %param -%47 = OpCompositeExtract %v4float %46 0 -OpStore %_entryPointOutput_vColor %47 -OpReturn -OpFunctionEnd -)"; - - const std::string func2_before = - R"(%_MainPs_struct_PS_INPUT_vf21_ = OpFunction %PS_OUTPUT None %13 + const std::string func2 = R"( +%_MainPs_struct_PS_INPUT_vf21_ = OpFunction %PS_OUTPUT None %13 %i = OpFunctionParameter %_ptr_Function_PS_INPUT %16 = OpLabel %ps_output = OpVariable %_ptr_Function_PS_OUTPUT Function @@ -2159,6 +1572,24 @@ OpLine %1 24 0 %43 = OpAccessChain %_ptr_Function_v2float %i %int_0 %44 = OpLoad %v2float %43 %45 = OpImageSampleImplicitLod %v4float %41 %44 +; CHECK-NOT: %45 = OpImageSampleImplicitLod %v4float %41 %44 +; CHECK: OpNoLine +; CHECK: %62 = OpULessThan %bool %50 %uint_128 +; CHECK: OpSelectionMerge %63 None +; CHECK: OpBranchConditional %62 %64 %65 +; CHECK: %64 = OpLabel +; CHECK: %66 = OpLoad %27 %51 +; CHECK: %67 = OpSampledImage %37 %66 %53 +; CHECK: OpLine %5 24 0 +; CHECK: %68 = OpImageSampleImplicitLod %v4float %67 %56 +; CHECK: OpNoLine +; CHECK: OpBranch %63 +; CHECK: %65 = OpLabel +; CHECK: %124 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_109 %uint_0 %50 %uint_128 +; CHECK: OpBranch %63 +; CHECK: %63 = OpLabel +; CHECK: %126 = OpPhi %v4float %68 %64 %125 %65 +; CHECK: OpLine %5 24 0 %47 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0 OpStore %47 %45 OpLine %1 25 0 @@ -2167,102 +1598,12 @@ OpReturnValue %48 OpFunctionEnd )"; - const std::string func2_after = - R"(%_MainPs_struct_PS_INPUT_vf21_ = OpFunction %PS_OUTPUT None %23 -%i = OpFunctionParameter %_ptr_Function_PS_INPUT -%48 = OpLabel -%ps_output = OpVariable %_ptr_Function_PS_OUTPUT Function -OpLine %5 24 0 -%49 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 -%50 = OpLoad %uint %49 -%51 = OpAccessChain %_ptr_UniformConstant_27 %g_tColor %50 -%52 = OpLoad %27 %51 -%53 = OpLoad %35 %g_sAniso -%54 = OpSampledImage %37 %52 %53 -%55 = OpAccessChain %_ptr_Function_v2float %i %int_0 -%56 = OpLoad %v2float %55 -OpNoLine -%62 = OpULessThan %bool %50 %uint_128 -OpSelectionMerge %63 None -OpBranchConditional %62 %64 %65 -%64 = OpLabel -%66 = OpLoad %27 %51 -%67 = OpSampledImage %37 %66 %53 -OpLine %5 24 0 -%68 = OpImageSampleImplicitLod %v4float %67 %56 -OpNoLine -OpBranch %63 -%65 = OpLabel -%124 = OpFunctionCall %void %69 %uint_109 %uint_0 %50 %uint_128 -OpBranch %63 -%63 = OpLabel -%126 = OpPhi %v4float %68 %64 %125 %65 -OpLine %5 24 0 -%58 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0 -OpStore %58 %126 -OpLine %5 25 0 -%59 = OpLoad %PS_OUTPUT %ps_output -OpReturnValue %59 -OpFunctionEnd -)"; - - const std::string output_func = - R"(%69 = OpFunction %void None %70 -%71 = OpFunctionParameter %uint -%72 = OpFunctionParameter %uint -%73 = OpFunctionParameter %uint -%74 = OpFunctionParameter %uint -%75 = OpLabel -%81 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_0 -%84 = OpAtomicIAdd %uint %81 %uint_4 %uint_0 %uint_10 -%85 = OpIAdd %uint %84 %uint_10 -%86 = OpArrayLength %uint %79 1 -%87 = OpULessThanEqual %bool %85 %86 -OpSelectionMerge %88 None -OpBranchConditional %87 %89 %88 -%89 = OpLabel -%90 = OpIAdd %uint %84 %uint_0 -%92 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %90 -OpStore %92 %uint_10 -%94 = OpIAdd %uint %84 %uint_1 -%95 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %94 -OpStore %95 %uint_23 -%97 = OpIAdd %uint %84 %uint_2 -%98 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %97 -OpStore %98 %71 -%100 = OpIAdd %uint %84 %uint_3 -%101 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %100 -OpStore %101 %uint_4 -%104 = OpLoad %v4float %gl_FragCoord -%106 = OpBitcast %v4uint %104 -%107 = OpCompositeExtract %uint %106 0 -%108 = OpIAdd %uint %84 %uint_4 -%109 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %108 -OpStore %109 %107 -%110 = OpCompositeExtract %uint %106 1 -%112 = OpIAdd %uint %84 %uint_5 -%113 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %112 -OpStore %113 %110 -%115 = OpIAdd %uint %84 %uint_7 -%116 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %115 -OpStore %116 %72 -%118 = OpIAdd %uint %84 %uint_8 -%119 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %118 -OpStore %119 %73 -%121 = OpIAdd %uint %84 %uint_9 -%122 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %121 -OpStore %122 %74 -OpBranch %88 -%88 = OpLabel -OpReturn -OpFunctionEnd -)"; + const std::string output_func = kStreamWrite4Frag; // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func1_before + func2_before, - defs_after + func1_after + func2_after + output_func, true, true, 7u, 23u, - false, false, false, false, false); + SinglePassRunAndMatch( + defs + func1 + func2 + output_func, true, 7u, 23u, false, false, false, + false, false); } TEST_F(InstBindlessTest, RuntimeArray) { @@ -2270,13 +1611,16 @@ TEST_F(InstBindlessTest, RuntimeArray) { // with runtime descriptor array. This test was created by editing the // SPIR-V from the Simple test. - const std::string defs_before = - R"(OpCapability Shader + // clang-format off + const std::string defs = R"( +OpCapability Shader OpCapability RuntimeDescriptorArray OpExtension "SPV_EXT_descriptor_indexing" +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord OpExecutionMode %MainPs OriginUpperLeft OpSource HLSL 500 OpName %MainPs "MainPs" @@ -2295,6 +1639,9 @@ OpDecorate %g_sAniso DescriptorSet 1 OpDecorate %g_sAniso Binding 0 OpDecorate %i_vTextureCoords Location 0 OpDecorate %_entryPointOutput_vColor Location 0 +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kInputDecorations + kOutputDecorations + R"( +; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -2321,102 +1668,34 @@ OpDecorate %_entryPointOutput_vColor Location 0 %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +; CHECK: %uint_0 = OpConstant %uint 0 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %41 = OpTypeFunction %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kInputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %bool = OpTypeBool +; CHECK: %65 = OpTypeFunction %void %uint %uint %uint %uint +)" + kOutputGlobals + R"( +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +; CHECK: %v4uint = OpTypeVector %uint 4 +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_59 = OpConstant %uint 59 +; CHECK: %116 = OpConstantNull %v4float +; CHECK: %119 = OpTypeFunction %uint %uint %uint %uint %uint )"; + // clang-format on - const std::string defs_after = - R"(OpCapability Shader -OpCapability RuntimeDescriptorArray -OpExtension "SPV_EXT_descriptor_indexing" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" -OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" -OpName %_ "" -OpName %g_sAniso "g_sAniso" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 1 -OpDecorate %g_tColor Binding 2 -OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 -OpDecorate %PerViewConstantBuffer_t Block -OpDecorate %g_sAniso DescriptorSet 1 -OpDecorate %g_sAniso Binding 0 -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_46 Block -OpMemberDecorate %_struct_46 0 Offset 0 -OpDecorate %48 DescriptorSet 7 -OpDecorate %48 Binding 1 -OpDecorate %_struct_71 Block -OpMemberDecorate %_struct_71 0 Offset 0 -OpMemberDecorate %_struct_71 1 Offset 4 -OpDecorate %73 DescriptorSet 7 -OpDecorate %73 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -%void = OpTypeVoid -%10 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%v4float = OpTypeVector %float 4 -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%16 = OpTypeImage %float 2D 0 0 0 1 Unknown -%uint = OpTypeInt 32 0 -%uint_1 = OpConstant %uint 1 -%_runtimearr_16 = OpTypeRuntimeArray %16 -%_ptr_UniformConstant__runtimearr_16 = OpTypePointer UniformConstant %_runtimearr_16 -%g_tColor = OpVariable %_ptr_UniformConstant__runtimearr_16 UniformConstant -%PerViewConstantBuffer_t = OpTypeStruct %uint -%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t -%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant -%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint -%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16 -%24 = OpTypeSampler -%_ptr_UniformConstant_24 = OpTypePointer UniformConstant %24 -%g_sAniso = OpVariable %_ptr_UniformConstant_24 UniformConstant -%26 = OpTypeSampledImage %16 -%_ptr_Input_v2float = OpTypePointer Input %v2float -%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -%uint_0 = OpConstant %uint 0 -%uint_2 = OpConstant %uint 2 -%41 = OpTypeFunction %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_46 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_46 = OpTypePointer StorageBuffer %_struct_46 -%48 = OpVariable %_ptr_StorageBuffer__struct_46 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%bool = OpTypeBool -%65 = OpTypeFunction %void %uint %uint %uint %uint -%_struct_71 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_71 = OpTypePointer StorageBuffer %_struct_71 -%73 = OpVariable %_ptr_StorageBuffer__struct_71 StorageBuffer -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_3 = OpConstant %uint 3 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_59 = OpConstant %uint 59 -%116 = OpConstantNull %v4float -%119 = OpTypeFunction %uint %uint %uint %uint %uint -)"; - - const std::string func_before = - R"(%MainPs = OpFunction %void None %3 + const std::string main_func = R"( +%MainPs = OpFunction %void None %3 %5 = OpLabel %53 = OpLoad %v2float %i_vTextureCoords %63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 @@ -2427,138 +1706,46 @@ OpDecorate %gl_FragCoord BuiltIn FragCoord %68 = OpSampledImage %39 %66 %67 %71 = OpImageSampleImplicitLod %v4float %68 %53 OpStore %_entryPointOutput_vColor %71 +; CHECK-NOT: %71 = OpImageSampleImplicitLod %v4float %68 %53 +; CHECK-NOT: OpStore %_entryPointOutput_vColor %71 +; CHECK: %55 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_2 %uint_2 +; CHECK: %57 = OpULessThan %bool %32 %55 +; CHECK: OpSelectionMerge %58 None +; CHECK: OpBranchConditional %57 %59 %60 +; CHECK: %59 = OpLabel +; CHECK: %61 = OpLoad %16 %33 +; CHECK: %62 = OpSampledImage %26 %61 %35 +; CHECK: %136 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_1 %uint_2 %32 +; CHECK: %137 = OpULessThan %bool %uint_0 %136 +; CHECK: OpSelectionMerge %138 None +; CHECK: OpBranchConditional %137 %139 %140 +; CHECK: %139 = OpLabel +; CHECK: %141 = OpLoad %16 %33 +; CHECK: %142 = OpSampledImage %26 %141 %35 +; CHECK: %143 = OpImageSampleImplicitLod %v4float %142 %30 +; CHECK: OpBranch %138 +; CHECK: %140 = OpLabel +; CHECK: %144 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_59 %uint_1 %32 %uint_0 +; CHECK: OpBranch %138 +; CHECK: %138 = OpLabel +; CHECK: %145 = OpPhi %v4float %143 %139 %116 %140 +; CHECK: OpBranch %58 +; CHECK: %60 = OpLabel +; CHECK: %115 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_59 %uint_0 %32 %55 +; CHECK: OpBranch %58 +; CHECK: %58 = OpLabel +; CHECK: %117 = OpPhi %v4float %145 %138 %116 %60 +; CHECK: OpStore %_entryPointOutput_vColor %117 OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%MainPs = OpFunction %void None %10 -%29 = OpLabel -%30 = OpLoad %v2float %i_vTextureCoords -%31 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 -%32 = OpLoad %uint %31 -%33 = OpAccessChain %_ptr_UniformConstant_16 %g_tColor %32 -%34 = OpLoad %16 %33 -%35 = OpLoad %24 %g_sAniso -%36 = OpSampledImage %26 %34 %35 -%55 = OpFunctionCall %uint %40 %uint_2 %uint_2 -%57 = OpULessThan %bool %32 %55 -OpSelectionMerge %58 None -OpBranchConditional %57 %59 %60 -%59 = OpLabel -%61 = OpLoad %16 %33 -%62 = OpSampledImage %26 %61 %35 -%136 = OpFunctionCall %uint %118 %uint_0 %uint_1 %uint_2 %32 -%137 = OpULessThan %bool %uint_0 %136 -OpSelectionMerge %138 None -OpBranchConditional %137 %139 %140 -%139 = OpLabel -%141 = OpLoad %16 %33 -%142 = OpSampledImage %26 %141 %35 -%143 = OpImageSampleImplicitLod %v4float %142 %30 -OpBranch %138 -%140 = OpLabel -%144 = OpFunctionCall %void %64 %uint_59 %uint_1 %32 %uint_0 -OpBranch %138 -%138 = OpLabel -%145 = OpPhi %v4float %143 %139 %116 %140 -OpBranch %58 -%60 = OpLabel -%115 = OpFunctionCall %void %64 %uint_59 %uint_0 %32 %55 -OpBranch %58 -%58 = OpLabel -%117 = OpPhi %v4float %145 %138 %116 %60 -OpStore %_entryPointOutput_vColor %117 -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%40 = OpFunction %uint None %41 -%42 = OpFunctionParameter %uint -%43 = OpFunctionParameter %uint -%44 = OpLabel -%50 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %42 -%51 = OpLoad %uint %50 -%52 = OpIAdd %uint %51 %43 -%53 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %52 -%54 = OpLoad %uint %53 -OpReturnValue %54 -OpFunctionEnd -%64 = OpFunction %void None %65 -%66 = OpFunctionParameter %uint -%67 = OpFunctionParameter %uint -%68 = OpFunctionParameter %uint -%69 = OpFunctionParameter %uint -%70 = OpLabel -%74 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_0 -%77 = OpAtomicIAdd %uint %74 %uint_4 %uint_0 %uint_10 -%78 = OpIAdd %uint %77 %uint_10 -%79 = OpArrayLength %uint %73 1 -%80 = OpULessThanEqual %bool %78 %79 -OpSelectionMerge %81 None -OpBranchConditional %80 %82 %81 -%82 = OpLabel -%83 = OpIAdd %uint %77 %uint_0 -%84 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %83 -OpStore %84 %uint_10 -%86 = OpIAdd %uint %77 %uint_1 -%87 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %86 -OpStore %87 %uint_23 -%88 = OpIAdd %uint %77 %uint_2 -%89 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %88 -OpStore %89 %66 -%91 = OpIAdd %uint %77 %uint_3 -%92 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %91 -OpStore %92 %uint_4 -%95 = OpLoad %v4float %gl_FragCoord -%97 = OpBitcast %v4uint %95 -%98 = OpCompositeExtract %uint %97 0 -%99 = OpIAdd %uint %77 %uint_4 -%100 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %99 -OpStore %100 %98 -%101 = OpCompositeExtract %uint %97 1 -%103 = OpIAdd %uint %77 %uint_5 -%104 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %103 -OpStore %104 %101 -%106 = OpIAdd %uint %77 %uint_7 -%107 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %106 -OpStore %107 %67 -%109 = OpIAdd %uint %77 %uint_8 -%110 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %109 -OpStore %110 %68 -%112 = OpIAdd %uint %77 %uint_9 -%113 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %112 -OpStore %113 %69 -OpBranch %81 -%81 = OpLabel -OpReturn -OpFunctionEnd -%118 = OpFunction %uint None %119 -%120 = OpFunctionParameter %uint -%121 = OpFunctionParameter %uint -%122 = OpFunctionParameter %uint -%123 = OpFunctionParameter %uint -%124 = OpLabel -%125 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %120 -%126 = OpLoad %uint %125 -%127 = OpIAdd %uint %126 %121 -%128 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %127 -%129 = OpLoad %uint %128 -%130 = OpIAdd %uint %129 %122 -%131 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %130 -%132 = OpLoad %uint %131 -%133 = OpIAdd %uint %132 %123 -%134 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %133 -%135 = OpLoad %uint %134 -OpReturnValue %135 -OpFunctionEnd -)"; + const std::string new_funcs = kDirectRead2 + kStreamWrite4Frag + kDirectRead4; // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, false, false, false); + SinglePassRunAndMatch(defs + main_func + new_funcs, + true, 7u, 23u, true, true, false, + false, false); } TEST_F(InstBindlessTest, InstrumentInitCheckOnScalarDescriptor) { @@ -2569,11 +1756,14 @@ TEST_F(InstBindlessTest, InstrumentInitCheckOnScalarDescriptor) { // does not have the extension enabled because it does not contain a // runtime array. This is the same shader as NoInstrumentNonBindless. - const std::string defs_before = - R"(OpCapability Shader + // clang-format off + const std::string defs = R"( +OpCapability Shader +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord OpExecutionMode %MainPs OriginUpperLeft OpSource HLSL 500 OpName %MainPs "MainPs" @@ -2587,6 +1777,9 @@ OpDecorate %g_sAniso DescriptorSet 0 OpDecorate %g_sAniso Binding 0 OpDecorate %i_vTextureCoords Location 0 OpDecorate %_entryPointOutput_vColor Location 0 +; check: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kInputDecorations + kOutputDecorations + R"( +; check: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %8 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -2603,86 +1796,35 @@ OpDecorate %_entryPointOutput_vColor Location 0 %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +; CHECK: %uint = OpTypeInt 32 0 +; CHECK: %uint_0 = OpConstant %uint 0 +; CHECK: %28 = OpTypeFunction %uint %uint %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kInputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %bool = OpTypeBool +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %61 = OpTypeFunction %void %uint %uint %uint %uint +)" + kOutputGlobals + R"( +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +; CHECK: %v4uint = OpTypeVector %uint 4 +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_39 = OpConstant %uint 39 +; CHECK: %113 = OpConstantNull %v4float )"; + // clang-format on - const std::string defs_after = - R"(OpCapability Shader -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %g_sAniso "g_sAniso" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 0 -OpDecorate %g_tColor Binding 0 -OpDecorate %g_sAniso DescriptorSet 0 -OpDecorate %g_sAniso Binding 0 -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_35 Block -OpMemberDecorate %_struct_35 0 Offset 0 -OpDecorate %37 DescriptorSet 7 -OpDecorate %37 Binding 1 -OpDecorate %_struct_67 Block -OpMemberDecorate %_struct_67 0 Offset 0 -OpMemberDecorate %_struct_67 1 Offset 4 -OpDecorate %69 DescriptorSet 7 -OpDecorate %69 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -%void = OpTypeVoid -%8 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%v4float = OpTypeVector %float 4 -%12 = OpTypeImage %float 2D 0 0 0 1 Unknown -%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 -%g_tColor = OpVariable %_ptr_UniformConstant_12 UniformConstant -%14 = OpTypeSampler -%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14 -%g_sAniso = OpVariable %_ptr_UniformConstant_14 UniformConstant -%16 = OpTypeSampledImage %12 -%_ptr_Input_v2float = OpTypePointer Input %v2float -%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -%uint = OpTypeInt 32 0 -%uint_0 = OpConstant %uint 0 -%28 = OpTypeFunction %uint %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_35 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_35 = OpTypePointer StorageBuffer %_struct_35 -%37 = OpVariable %_ptr_StorageBuffer__struct_35 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%bool = OpTypeBool -%uint_1 = OpConstant %uint 1 -%61 = OpTypeFunction %void %uint %uint %uint %uint -%_struct_67 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_67 = OpTypePointer StorageBuffer %_struct_67 -%69 = OpVariable %_ptr_StorageBuffer__struct_67 StorageBuffer -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_3 = OpConstant %uint 3 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_39 = OpConstant %uint 39 -%113 = OpConstantNull %v4float -)"; - - const std::string func_before = - R"(%MainPs = OpFunction %void None %8 + const std::string main_func = R"( +%MainPs = OpFunction %void None %8 %19 = OpLabel %20 = OpLoad %v2float %i_vTextureCoords %21 = OpLoad %12 %g_tColor @@ -2690,111 +1832,33 @@ OpDecorate %gl_FragCoord BuiltIn FragCoord %23 = OpSampledImage %16 %21 %22 %24 = OpImageSampleImplicitLod %v4float %23 %20 OpStore %_entryPointOutput_vColor %24 +; CHECK-NOT: %24 = OpImageSampleImplicitLod %v4float %23 %20 +; CHECK-NOT: OpStore %_entryPointOutput_vColor %24 +; CHECK: %50 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0 +; CHECK: %52 = OpULessThan %bool %uint_0 %50 +; CHECK: OpSelectionMerge %54 None +; CHECK: OpBranchConditional %52 %55 %56 +; CHECK: %55 = OpLabel +; CHECK: %57 = OpLoad %12 %g_tColor +; CHECK: %58 = OpSampledImage %16 %57 %22 +; CHECK: %59 = OpImageSampleImplicitLod %v4float %58 %20 +; CHECK: OpBranch %54 +; CHECK: %56 = OpLabel +; CHECK: %112 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_39 %uint_1 %uint_0 %uint_0 +; CHECK: OpBranch %54 +; CHECK: %54 = OpLabel +; CHECK: %114 = OpPhi %v4float %59 %55 %113 %56 +; CHECK: OpStore %_entryPointOutput_vColor %114 OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%MainPs = OpFunction %void None %8 -%19 = OpLabel -%20 = OpLoad %v2float %i_vTextureCoords -%21 = OpLoad %12 %g_tColor -%22 = OpLoad %14 %g_sAniso -%23 = OpSampledImage %16 %21 %22 -%50 = OpFunctionCall %uint %27 %uint_0 %uint_0 %uint_0 %uint_0 -%52 = OpULessThan %bool %uint_0 %50 -OpSelectionMerge %54 None -OpBranchConditional %52 %55 %56 -%55 = OpLabel -%57 = OpLoad %12 %g_tColor -%58 = OpSampledImage %16 %57 %22 -%59 = OpImageSampleImplicitLod %v4float %58 %20 -OpBranch %54 -%56 = OpLabel -%112 = OpFunctionCall %void %60 %uint_39 %uint_1 %uint_0 %uint_0 -OpBranch %54 -%54 = OpLabel -%114 = OpPhi %v4float %59 %55 %113 %56 -OpStore %_entryPointOutput_vColor %114 -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%27 = OpFunction %uint None %28 -%29 = OpFunctionParameter %uint -%30 = OpFunctionParameter %uint -%31 = OpFunctionParameter %uint -%32 = OpFunctionParameter %uint -%33 = OpLabel -%39 = OpAccessChain %_ptr_StorageBuffer_uint %37 %uint_0 %29 -%40 = OpLoad %uint %39 -%41 = OpIAdd %uint %40 %30 -%42 = OpAccessChain %_ptr_StorageBuffer_uint %37 %uint_0 %41 -%43 = OpLoad %uint %42 -%44 = OpIAdd %uint %43 %31 -%45 = OpAccessChain %_ptr_StorageBuffer_uint %37 %uint_0 %44 -%46 = OpLoad %uint %45 -%47 = OpIAdd %uint %46 %32 -%48 = OpAccessChain %_ptr_StorageBuffer_uint %37 %uint_0 %47 -%49 = OpLoad %uint %48 -OpReturnValue %49 -OpFunctionEnd -%60 = OpFunction %void None %61 -%62 = OpFunctionParameter %uint -%63 = OpFunctionParameter %uint -%64 = OpFunctionParameter %uint -%65 = OpFunctionParameter %uint -%66 = OpLabel -%70 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_0 -%73 = OpAtomicIAdd %uint %70 %uint_4 %uint_0 %uint_10 -%74 = OpIAdd %uint %73 %uint_10 -%75 = OpArrayLength %uint %69 1 -%76 = OpULessThanEqual %bool %74 %75 -OpSelectionMerge %77 None -OpBranchConditional %76 %78 %77 -%78 = OpLabel -%79 = OpIAdd %uint %73 %uint_0 -%80 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %79 -OpStore %80 %uint_10 -%82 = OpIAdd %uint %73 %uint_1 -%83 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %82 -OpStore %83 %uint_23 -%85 = OpIAdd %uint %73 %uint_2 -%86 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %85 -OpStore %86 %62 -%88 = OpIAdd %uint %73 %uint_3 -%89 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %88 -OpStore %89 %uint_4 -%92 = OpLoad %v4float %gl_FragCoord -%94 = OpBitcast %v4uint %92 -%95 = OpCompositeExtract %uint %94 0 -%96 = OpIAdd %uint %73 %uint_4 -%97 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %96 -OpStore %97 %95 -%98 = OpCompositeExtract %uint %94 1 -%100 = OpIAdd %uint %73 %uint_5 -%101 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %100 -OpStore %101 %98 -%103 = OpIAdd %uint %73 %uint_7 -%104 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %103 -OpStore %104 %63 -%106 = OpIAdd %uint %73 %uint_8 -%107 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %106 -OpStore %107 %64 -%109 = OpIAdd %uint %73 %uint_9 -%110 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %109 -OpStore %110 %65 -OpBranch %77 -%77 = OpLabel -OpReturn -OpFunctionEnd -)"; + const std::string new_funcs = kDirectRead4 + kStreamWrite4Frag; // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, false, false, false); + SinglePassRunAndMatch(defs + main_func + new_funcs, + true, 7u, 23u, true, true, false, + false, false); } TEST_F(InstBindlessTest, SPV14AddToEntryPoint) { @@ -2927,15 +1991,18 @@ TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedUBOArray) { // b = uniformBuffer[nu_ii].a; // } - const std::string defs_before = - R"(OpCapability Shader + // clang-format off + const std::string defs = R"( +OpCapability Shader OpCapability ShaderNonUniform OpCapability RuntimeDescriptorArray OpCapability UniformBufferArrayNonUniformIndexing OpExtension "SPV_EXT_descriptor_indexing" +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %b %nu_ii +; CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord OpExecutionMode %main OriginUpperLeft OpSource GLSL 450 OpSourceExtension "GL_EXT_nonuniform_qualifier" @@ -2955,6 +2022,12 @@ OpDecorate %nu_ii Location 0 OpDecorate %nu_ii NonUniform OpDecorate %16 NonUniform OpDecorate %20 NonUniform +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kInputDecorations + R"( +; CHECK: OpDecorate %130 NonUniform +)" + kOutputDecorations + R"( +; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +; CHECK: OpDecorate %127 NonUniform %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -2969,229 +2042,81 @@ OpDecorate %20 NonUniform %nu_ii = OpVariable %_ptr_Input_int Input %int_0 = OpConstant %int 0 %_ptr_Uniform_float = OpTypePointer Uniform %float +; CHECK: %uint = OpTypeInt 32 0 +; CHECK: %uint_0 = OpConstant %uint 0 +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %26 = OpTypeFunction %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kInputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %bool = OpTypeBool +; CHECK: %49 = OpTypeFunction %void %uint %uint %uint %uint +)" + kOutputGlobals + R"( +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %v4float = OpTypeVector %float 4 +; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +; CHECK: %v4uint = OpTypeVector %uint 4 +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_45 = OpConstant %uint 45 +; CHECK: %101 = OpConstantNull %float +; CHECK: %105 = OpTypeFunction %uint %uint %uint %uint %uint )"; + // clang-format on - const std::string defs_after = - R"(OpCapability Shader -OpCapability ShaderNonUniform -OpCapability RuntimeDescriptorArray -OpCapability UniformBufferArrayNonUniformIndexing -OpExtension "SPV_EXT_descriptor_indexing" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpName %main "main" -OpName %b "b" -OpName %uname "uname" -OpMemberName %uname 0 "a" -OpName %uniformBuffer "uniformBuffer" -OpName %nu_ii "nu_ii" -OpDecorate %b Location 0 -OpMemberDecorate %uname 0 Offset 0 -OpDecorate %uname Block -OpDecorate %uniformBuffer DescriptorSet 0 -OpDecorate %uniformBuffer Binding 3 -OpDecorate %nu_ii Flat -OpDecorate %nu_ii Location 0 -OpDecorate %nu_ii NonUniform -OpDecorate %7 NonUniform -OpDecorate %102 NonUniform -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_31 Block -OpMemberDecorate %_struct_31 0 Offset 0 -OpDecorate %33 DescriptorSet 7 -OpDecorate %33 Binding 1 -OpDecorate %130 NonUniform -OpDecorate %_struct_55 Block -OpMemberDecorate %_struct_55 0 Offset 0 -OpMemberDecorate %_struct_55 1 Offset 4 -OpDecorate %57 DescriptorSet 7 -OpDecorate %57 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -OpDecorate %127 NonUniform -%void = OpTypeVoid -%10 = OpTypeFunction %void -%float = OpTypeFloat 32 -%_ptr_Output_float = OpTypePointer Output %float -%b = OpVariable %_ptr_Output_float Output -%uname = OpTypeStruct %float -%_runtimearr_uname = OpTypeRuntimeArray %uname -%_ptr_Uniform__runtimearr_uname = OpTypePointer Uniform %_runtimearr_uname -%uniformBuffer = OpVariable %_ptr_Uniform__runtimearr_uname Uniform -%int = OpTypeInt 32 1 -%_ptr_Input_int = OpTypePointer Input %int -%nu_ii = OpVariable %_ptr_Input_int Input -%int_0 = OpConstant %int 0 -%_ptr_Uniform_float = OpTypePointer Uniform %float -%uint = OpTypeInt 32 0 -%uint_0 = OpConstant %uint 0 -%uint_1 = OpConstant %uint 1 -%uint_3 = OpConstant %uint 3 -%26 = OpTypeFunction %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_31 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_31 = OpTypePointer StorageBuffer %_struct_31 -%33 = OpVariable %_ptr_StorageBuffer__struct_31 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%bool = OpTypeBool -%49 = OpTypeFunction %void %uint %uint %uint %uint -%_struct_55 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_55 = OpTypePointer StorageBuffer %_struct_55 -%57 = OpVariable %_ptr_StorageBuffer__struct_55 StorageBuffer -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_45 = OpConstant %uint 45 -%101 = OpConstantNull %float -%105 = OpTypeFunction %uint %uint %uint %uint %uint -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %3 + const std::string main_func = R"( +%main = OpFunction %void None %3 %5 = OpLabel %16 = OpLoad %int %nu_ii %19 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %16 %int_0 %20 = OpLoad %float %19 OpStore %b %20 +; CHECK-NOT: %20 = OpLoad %float %19 +; CHECK-NOT: OpStore %b %20 +; CHECK: %40 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_3 +; CHECK: %42 = OpULessThan %bool %7 %40 +; CHECK: OpSelectionMerge %43 None +; CHECK: OpBranchConditional %42 %44 %45 +; CHECK: %44 = OpLabel +; CHECK: %103 = OpBitcast %uint %7 +; CHECK: %122 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_3 %103 +; CHECK: %123 = OpULessThan %bool %uint_0 %122 +; CHECK: OpSelectionMerge %124 None +; CHECK: OpBranchConditional %123 %125 %126 +; CHECK: %125 = OpLabel +; CHECK: %127 = OpLoad %float %20 +; CHECK: OpBranch %124 +; CHECK: %126 = OpLabel +; CHECK: %128 = OpBitcast %uint %7 +; CHECK: %129 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_1 %128 %uint_0 +; CHECK: OpBranch %124 +; CHECK: %124 = OpLabel +; CHECK: %130 = OpPhi %float %127 %125 %101 %126 +; CHECK: OpBranch %43 +; CHECK: %45 = OpLabel +; CHECK: %47 = OpBitcast %uint %7 +; CHECK: %100 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_0 %47 %40 +; CHECK: OpBranch %43 +; CHECK: %43 = OpLabel +; CHECK: %102 = OpPhi %float %130 %124 %101 %45 +; CHECK: OpStore %b %102 OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %10 -%19 = OpLabel -%7 = OpLoad %int %nu_ii -%20 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %7 %int_0 -%40 = OpFunctionCall %uint %25 %uint_1 %uint_3 -%42 = OpULessThan %bool %7 %40 -OpSelectionMerge %43 None -OpBranchConditional %42 %44 %45 -%44 = OpLabel -%103 = OpBitcast %uint %7 -%122 = OpFunctionCall %uint %104 %uint_0 %uint_0 %uint_3 %103 -%123 = OpULessThan %bool %uint_0 %122 -OpSelectionMerge %124 None -OpBranchConditional %123 %125 %126 -%125 = OpLabel -%127 = OpLoad %float %20 -OpBranch %124 -%126 = OpLabel -%128 = OpBitcast %uint %7 -%129 = OpFunctionCall %void %48 %uint_45 %uint_1 %128 %uint_0 -OpBranch %124 -%124 = OpLabel -%130 = OpPhi %float %127 %125 %101 %126 -OpBranch %43 -%45 = OpLabel -%47 = OpBitcast %uint %7 -%100 = OpFunctionCall %void %48 %uint_45 %uint_0 %47 %40 -OpBranch %43 -%43 = OpLabel -%102 = OpPhi %float %130 %124 %101 %45 -OpStore %b %102 -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%25 = OpFunction %uint None %26 -%27 = OpFunctionParameter %uint -%28 = OpFunctionParameter %uint -%29 = OpLabel -%35 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %27 -%36 = OpLoad %uint %35 -%37 = OpIAdd %uint %36 %28 -%38 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %37 -%39 = OpLoad %uint %38 -OpReturnValue %39 -OpFunctionEnd -%48 = OpFunction %void None %49 -%50 = OpFunctionParameter %uint -%51 = OpFunctionParameter %uint -%52 = OpFunctionParameter %uint -%53 = OpFunctionParameter %uint -%54 = OpLabel -%58 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_0 -%61 = OpAtomicIAdd %uint %58 %uint_4 %uint_0 %uint_10 -%62 = OpIAdd %uint %61 %uint_10 -%63 = OpArrayLength %uint %57 1 -%64 = OpULessThanEqual %bool %62 %63 -OpSelectionMerge %65 None -OpBranchConditional %64 %66 %65 -%66 = OpLabel -%67 = OpIAdd %uint %61 %uint_0 -%68 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %67 -OpStore %68 %uint_10 -%70 = OpIAdd %uint %61 %uint_1 -%71 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %70 -OpStore %71 %uint_23 -%73 = OpIAdd %uint %61 %uint_2 -%74 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %73 -OpStore %74 %50 -%75 = OpIAdd %uint %61 %uint_3 -%76 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %75 -OpStore %76 %uint_4 -%80 = OpLoad %v4float %gl_FragCoord -%82 = OpBitcast %v4uint %80 -%83 = OpCompositeExtract %uint %82 0 -%84 = OpIAdd %uint %61 %uint_4 -%85 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %84 -OpStore %85 %83 -%86 = OpCompositeExtract %uint %82 1 -%88 = OpIAdd %uint %61 %uint_5 -%89 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %88 -OpStore %89 %86 -%91 = OpIAdd %uint %61 %uint_7 -%92 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %91 -OpStore %92 %51 -%94 = OpIAdd %uint %61 %uint_8 -%95 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %94 -OpStore %95 %52 -%97 = OpIAdd %uint %61 %uint_9 -%98 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %97 -OpStore %98 %53 -OpBranch %65 -%65 = OpLabel -OpReturn -OpFunctionEnd -%104 = OpFunction %uint None %105 -%106 = OpFunctionParameter %uint -%107 = OpFunctionParameter %uint -%108 = OpFunctionParameter %uint -%109 = OpFunctionParameter %uint -%110 = OpLabel -%111 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %106 -%112 = OpLoad %uint %111 -%113 = OpIAdd %uint %112 %107 -%114 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %113 -%115 = OpLoad %uint %114 -%116 = OpIAdd %uint %115 %108 -%117 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %116 -%118 = OpLoad %uint %117 -%119 = OpIAdd %uint %118 %109 -%120 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %119 -%121 = OpLoad %uint %120 -OpReturnValue %121 -OpFunctionEnd -)"; + const std::string new_funcs = kDirectRead2 + kStreamWrite4Frag + kDirectRead4; // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, false, false, false); + SinglePassRunAndMatch(defs + main_func + new_funcs, + true, 7u, 23u, true, true, false, + false, false); } TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArrayDeprecated) { @@ -3208,15 +2133,18 @@ TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArrayDeprecated) { // b = storageBuffer[nu_ii].b; // } - const std::string defs_before = - R"(OpCapability Shader + // clang-format off + const std::string defs = R"( +OpCapability Shader OpCapability ShaderNonUniform OpCapability RuntimeDescriptorArray OpCapability StorageBufferArrayNonUniformIndexing OpExtension "SPV_EXT_descriptor_indexing" +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %b %nu_ii +; CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord OpExecutionMode %main OriginUpperLeft OpSource GLSL 450 OpSourceExtension "GL_EXT_nonuniform_qualifier" @@ -3236,6 +2164,12 @@ OpDecorate %nu_ii Location 0 OpDecorate %nu_ii NonUniform OpDecorate %16 NonUniform OpDecorate %20 NonUniform +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kInputDecorations + R"( +; CHECK: OpDecorate %130 NonUniform +)" + kOutputDecorations + R"( +; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +; CHECK: OpDecorate %127 NonUniform %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -3250,243 +2184,98 @@ OpDecorate %20 NonUniform %nu_ii = OpVariable %_ptr_Input_int Input %int_0 = OpConstant %int 0 %_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float +; CHECK: %uint = OpTypeInt 32 0 +; CHECK: %uint_0 = OpConstant %uint 0 +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %26 = OpTypeFunction %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kInputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %bool = OpTypeBool +; CHECK: %49 = OpTypeFunction %void %uint %uint %uint %uint +)" + kOutputGlobals + R"( +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %v4float = OpTypeVector %float 4 +; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +; CHECK: %v4uint = OpTypeVector %uint 4 +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_45 = OpConstant %uint 45 +; CHECK: %101 = OpConstantNull %float +; CHECK: %105 = OpTypeFunction %uint %uint %uint %uint %uint )"; + // clang-format on - const std::string defs_after = - R"(OpCapability Shader -OpCapability ShaderNonUniform -OpCapability RuntimeDescriptorArray -OpCapability StorageBufferArrayNonUniformIndexing -OpExtension "SPV_EXT_descriptor_indexing" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpName %main "main" -OpName %b "b" -OpName %bname "bname" -OpMemberName %bname 0 "a" -OpName %storageBuffer "storageBuffer" -OpName %nu_ii "nu_ii" -OpDecorate %b Location 0 -OpMemberDecorate %bname 0 Offset 0 -OpDecorate %bname Block -OpDecorate %storageBuffer DescriptorSet 0 -OpDecorate %storageBuffer Binding 3 -OpDecorate %nu_ii Flat -OpDecorate %nu_ii Location 0 -OpDecorate %nu_ii NonUniform -OpDecorate %7 NonUniform -OpDecorate %102 NonUniform -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_31 Block -OpMemberDecorate %_struct_31 0 Offset 0 -OpDecorate %33 DescriptorSet 7 -OpDecorate %33 Binding 1 -OpDecorate %130 NonUniform -OpDecorate %_struct_55 Block -OpMemberDecorate %_struct_55 0 Offset 0 -OpMemberDecorate %_struct_55 1 Offset 4 -OpDecorate %57 DescriptorSet 7 -OpDecorate %57 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -OpDecorate %127 NonUniform -%void = OpTypeVoid -%10 = OpTypeFunction %void -%float = OpTypeFloat 32 -%_ptr_Output_float = OpTypePointer Output %float -%b = OpVariable %_ptr_Output_float Output -%bname = OpTypeStruct %float -%_runtimearr_bname = OpTypeRuntimeArray %bname -%_ptr_StorageBuffer__runtimearr_bname = OpTypePointer StorageBuffer %_runtimearr_bname -%storageBuffer = OpVariable %_ptr_StorageBuffer__runtimearr_bname StorageBuffer -%int = OpTypeInt 32 1 -%_ptr_Input_int = OpTypePointer Input %int -%nu_ii = OpVariable %_ptr_Input_int Input -%int_0 = OpConstant %int 0 -%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float -%uint = OpTypeInt 32 0 -%uint_0 = OpConstant %uint 0 -%uint_1 = OpConstant %uint 1 -%uint_3 = OpConstant %uint 3 -%26 = OpTypeFunction %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_31 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_31 = OpTypePointer StorageBuffer %_struct_31 -%33 = OpVariable %_ptr_StorageBuffer__struct_31 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%bool = OpTypeBool -%49 = OpTypeFunction %void %uint %uint %uint %uint -%_struct_55 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_55 = OpTypePointer StorageBuffer %_struct_55 -%57 = OpVariable %_ptr_StorageBuffer__struct_55 StorageBuffer -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_45 = OpConstant %uint 45 -%101 = OpConstantNull %float -%105 = OpTypeFunction %uint %uint %uint %uint %uint -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %3 + const std::string main_func = R"( +%main = OpFunction %void None %3 %5 = OpLabel %16 = OpLoad %int %nu_ii %19 = OpAccessChain %_ptr_StorageBuffer_float %storageBuffer %16 %int_0 %20 = OpLoad %float %19 OpStore %b %20 +; CHECK-NOT: %20 = OpLoad %float %19 +; CHECK-NOT: OpStore %b %20 +; CHECK: %40 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_3 +; CHECK: %42 = OpULessThan %bool %7 %40 +; CHECK: OpSelectionMerge %43 None +; CHECK: OpBranchConditional %42 %44 %45 +; CHECK: %44 = OpLabel +; CHECK: %103 = OpBitcast %uint %7 +; CHECK: %122 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_3 %103 +; CHECK: %123 = OpULessThan %bool %uint_0 %122 +; CHECK: OpSelectionMerge %124 None +; CHECK: OpBranchConditional %123 %125 %126 +; CHECK: %125 = OpLabel +; CHECK: %127 = OpLoad %float %20 +; CHECK: OpBranch %124 +; CHECK: %126 = OpLabel +; CHECK: %128 = OpBitcast %uint %7 +; CHECK: %129 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_1 %128 %uint_0 +; CHECK: OpBranch %124 +; CHECK: %124 = OpLabel +; CHECK: %130 = OpPhi %float %127 %125 %101 %126 +; CHECK: OpBranch %43 +; CHECK: %45 = OpLabel +; CHECK: %47 = OpBitcast %uint %7 +; CHECK: %100 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_0 %47 %40 +; CHECK: OpBranch %43 +; CHECK: %43 = OpLabel +; CHECK: %102 = OpPhi %float %130 %124 %101 %45 +; CHECK: OpStore %b %102 OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %10 -%19 = OpLabel -%7 = OpLoad %int %nu_ii -%20 = OpAccessChain %_ptr_StorageBuffer_float %storageBuffer %7 %int_0 -%40 = OpFunctionCall %uint %25 %uint_1 %uint_3 -%42 = OpULessThan %bool %7 %40 -OpSelectionMerge %43 None -OpBranchConditional %42 %44 %45 -%44 = OpLabel -%103 = OpBitcast %uint %7 -%122 = OpFunctionCall %uint %104 %uint_0 %uint_0 %uint_3 %103 -%123 = OpULessThan %bool %uint_0 %122 -OpSelectionMerge %124 None -OpBranchConditional %123 %125 %126 -%125 = OpLabel -%127 = OpLoad %float %20 -OpBranch %124 -%126 = OpLabel -%128 = OpBitcast %uint %7 -%129 = OpFunctionCall %void %48 %uint_45 %uint_1 %128 %uint_0 -OpBranch %124 -%124 = OpLabel -%130 = OpPhi %float %127 %125 %101 %126 -OpBranch %43 -%45 = OpLabel -%47 = OpBitcast %uint %7 -%100 = OpFunctionCall %void %48 %uint_45 %uint_0 %47 %40 -OpBranch %43 -%43 = OpLabel -%102 = OpPhi %float %130 %124 %101 %45 -OpStore %b %102 -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%25 = OpFunction %uint None %26 -%27 = OpFunctionParameter %uint -%28 = OpFunctionParameter %uint -%29 = OpLabel -%35 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %27 -%36 = OpLoad %uint %35 -%37 = OpIAdd %uint %36 %28 -%38 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %37 -%39 = OpLoad %uint %38 -OpReturnValue %39 -OpFunctionEnd -%48 = OpFunction %void None %49 -%50 = OpFunctionParameter %uint -%51 = OpFunctionParameter %uint -%52 = OpFunctionParameter %uint -%53 = OpFunctionParameter %uint -%54 = OpLabel -%58 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_0 -%61 = OpAtomicIAdd %uint %58 %uint_4 %uint_0 %uint_10 -%62 = OpIAdd %uint %61 %uint_10 -%63 = OpArrayLength %uint %57 1 -%64 = OpULessThanEqual %bool %62 %63 -OpSelectionMerge %65 None -OpBranchConditional %64 %66 %65 -%66 = OpLabel -%67 = OpIAdd %uint %61 %uint_0 -%68 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %67 -OpStore %68 %uint_10 -%70 = OpIAdd %uint %61 %uint_1 -%71 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %70 -OpStore %71 %uint_23 -%73 = OpIAdd %uint %61 %uint_2 -%74 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %73 -OpStore %74 %50 -%75 = OpIAdd %uint %61 %uint_3 -%76 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %75 -OpStore %76 %uint_4 -%80 = OpLoad %v4float %gl_FragCoord -%82 = OpBitcast %v4uint %80 -%83 = OpCompositeExtract %uint %82 0 -%84 = OpIAdd %uint %61 %uint_4 -%85 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %84 -OpStore %85 %83 -%86 = OpCompositeExtract %uint %82 1 -%88 = OpIAdd %uint %61 %uint_5 -%89 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %88 -OpStore %89 %86 -%91 = OpIAdd %uint %61 %uint_7 -%92 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %91 -OpStore %92 %51 -%94 = OpIAdd %uint %61 %uint_8 -%95 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %94 -OpStore %95 %52 -%97 = OpIAdd %uint %61 %uint_9 -%98 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %97 -OpStore %98 %53 -OpBranch %65 -%65 = OpLabel -OpReturn -OpFunctionEnd -%104 = OpFunction %uint None %105 -%106 = OpFunctionParameter %uint -%107 = OpFunctionParameter %uint -%108 = OpFunctionParameter %uint -%109 = OpFunctionParameter %uint -%110 = OpLabel -%111 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %106 -%112 = OpLoad %uint %111 -%113 = OpIAdd %uint %112 %107 -%114 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %113 -%115 = OpLoad %uint %114 -%116 = OpIAdd %uint %115 %108 -%117 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %116 -%118 = OpLoad %uint %117 -%119 = OpIAdd %uint %118 %109 -%120 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %119 -%121 = OpLoad %uint %120 -OpReturnValue %121 -OpFunctionEnd -)"; + const std::string new_funcs = kDirectRead2 + kStreamWrite4Frag + kDirectRead4; // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, false, false, false); + SinglePassRunAndMatch(defs + main_func + new_funcs, + true, 7u, 23u, true, true, false, + false, false); } TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArray) { // Same as Deprecated but declaring as StorageBuffer Block - const std::string defs_before = - R"(OpCapability Shader + // clang-format off + const std::string defs = R"( +OpCapability Shader OpCapability ShaderNonUniform OpCapability RuntimeDescriptorArray OpCapability StorageBufferArrayNonUniformIndexing OpExtension "SPV_EXT_descriptor_indexing" +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %b %nu_ii +; CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord OpExecutionMode %main OriginUpperLeft OpSource GLSL 450 OpSourceExtension "GL_EXT_nonuniform_qualifier" @@ -3506,6 +2295,12 @@ OpDecorate %nu_ii Location 0 OpDecorate %nu_ii NonUniform OpDecorate %16 NonUniform OpDecorate %20 NonUniform +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kInputDecorations + R"( +; CHECK: OpDecorate %130 NonUniform +)" + kOutputDecorations + R"( +; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +; CHECK: OpDecorate %127 NonUniform %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -3520,229 +2315,81 @@ OpDecorate %20 NonUniform %nu_ii = OpVariable %_ptr_Input_int Input %int_0 = OpConstant %int 0 %_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float +; CHECK: %uint = OpTypeInt 32 0 +; CHECK: %uint_0 = OpConstant %uint 0 +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %26 = OpTypeFunction %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kInputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %bool = OpTypeBool +; CHECK: %49 = OpTypeFunction %void %uint %uint %uint %uint +)" + kOutputGlobals + R"( +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %v4float = OpTypeVector %float 4 +; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +; CHECK: %v4uint = OpTypeVector %uint 4 +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_45 = OpConstant %uint 45 +; CHECK: %101 = OpConstantNull %float +; CHECK: %105 = OpTypeFunction %uint %uint %uint %uint %uint )"; + // clang-format on - const std::string defs_after = - R"(OpCapability Shader -OpCapability ShaderNonUniform -OpCapability RuntimeDescriptorArray -OpCapability StorageBufferArrayNonUniformIndexing -OpExtension "SPV_EXT_descriptor_indexing" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpName %main "main" -OpName %b "b" -OpName %bname "bname" -OpMemberName %bname 0 "a" -OpName %storageBuffer "storageBuffer" -OpName %nu_ii "nu_ii" -OpDecorate %b Location 0 -OpMemberDecorate %bname 0 Offset 0 -OpDecorate %bname Block -OpDecorate %storageBuffer DescriptorSet 0 -OpDecorate %storageBuffer Binding 3 -OpDecorate %nu_ii Flat -OpDecorate %nu_ii Location 0 -OpDecorate %nu_ii NonUniform -OpDecorate %7 NonUniform -OpDecorate %102 NonUniform -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_31 Block -OpMemberDecorate %_struct_31 0 Offset 0 -OpDecorate %33 DescriptorSet 7 -OpDecorate %33 Binding 1 -OpDecorate %130 NonUniform -OpDecorate %_struct_55 Block -OpMemberDecorate %_struct_55 0 Offset 0 -OpMemberDecorate %_struct_55 1 Offset 4 -OpDecorate %57 DescriptorSet 7 -OpDecorate %57 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -OpDecorate %127 NonUniform -%void = OpTypeVoid -%10 = OpTypeFunction %void -%float = OpTypeFloat 32 -%_ptr_Output_float = OpTypePointer Output %float -%b = OpVariable %_ptr_Output_float Output -%bname = OpTypeStruct %float -%_runtimearr_bname = OpTypeRuntimeArray %bname -%_ptr_StorageBuffer__runtimearr_bname = OpTypePointer StorageBuffer %_runtimearr_bname -%storageBuffer = OpVariable %_ptr_StorageBuffer__runtimearr_bname StorageBuffer -%int = OpTypeInt 32 1 -%_ptr_Input_int = OpTypePointer Input %int -%nu_ii = OpVariable %_ptr_Input_int Input -%int_0 = OpConstant %int 0 -%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float -%uint = OpTypeInt 32 0 -%uint_0 = OpConstant %uint 0 -%uint_1 = OpConstant %uint 1 -%uint_3 = OpConstant %uint 3 -%26 = OpTypeFunction %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_31 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_31 = OpTypePointer StorageBuffer %_struct_31 -%33 = OpVariable %_ptr_StorageBuffer__struct_31 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%bool = OpTypeBool -%49 = OpTypeFunction %void %uint %uint %uint %uint -%_struct_55 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_55 = OpTypePointer StorageBuffer %_struct_55 -%57 = OpVariable %_ptr_StorageBuffer__struct_55 StorageBuffer -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_45 = OpConstant %uint 45 -%101 = OpConstantNull %float -%105 = OpTypeFunction %uint %uint %uint %uint %uint -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %3 + const std::string main_func = R"( +%main = OpFunction %void None %3 %5 = OpLabel %16 = OpLoad %int %nu_ii %19 = OpAccessChain %_ptr_StorageBuffer_float %storageBuffer %16 %int_0 %20 = OpLoad %float %19 OpStore %b %20 +; CHECK-NOT: %20 = OpLoad %float %19 +; CHECK-NOT: OpStore %b %20 +; CHECK: %40 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_3 +; CHECK: %42 = OpULessThan %bool %7 %40 +; CHECK: OpSelectionMerge %43 None +; CHECK: OpBranchConditional %42 %44 %45 +; CHECK: %44 = OpLabel +; CHECK: %103 = OpBitcast %uint %7 +; CHECK: %122 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_3 %103 +; CHECK: %123 = OpULessThan %bool %uint_0 %122 +; CHECK: OpSelectionMerge %124 None +; CHECK: OpBranchConditional %123 %125 %126 +; CHECK: %125 = OpLabel +; CHECK: %127 = OpLoad %float %20 +; CHECK: OpBranch %124 +; CHECK: %126 = OpLabel +; CHECK: %128 = OpBitcast %uint %7 +; CHECK: %129 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_1 %128 %uint_0 +; CHECK: OpBranch %124 +; CHECK: %124 = OpLabel +; CHECK: %130 = OpPhi %float %127 %125 %101 %126 +; CHECK: OpBranch %43 +; CHECK: %45 = OpLabel +; CHECK: %47 = OpBitcast %uint %7 +; CHECK: %100 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_0 %47 %40 +; CHECK: OpBranch %43 +; CHECK: %43 = OpLabel +; CHECK: %102 = OpPhi %float %130 %124 %101 %45 +; CHECK: OpStore %b %102 OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %10 -%19 = OpLabel -%7 = OpLoad %int %nu_ii -%20 = OpAccessChain %_ptr_StorageBuffer_float %storageBuffer %7 %int_0 -%40 = OpFunctionCall %uint %25 %uint_1 %uint_3 -%42 = OpULessThan %bool %7 %40 -OpSelectionMerge %43 None -OpBranchConditional %42 %44 %45 -%44 = OpLabel -%103 = OpBitcast %uint %7 -%122 = OpFunctionCall %uint %104 %uint_0 %uint_0 %uint_3 %103 -%123 = OpULessThan %bool %uint_0 %122 -OpSelectionMerge %124 None -OpBranchConditional %123 %125 %126 -%125 = OpLabel -%127 = OpLoad %float %20 -OpBranch %124 -%126 = OpLabel -%128 = OpBitcast %uint %7 -%129 = OpFunctionCall %void %48 %uint_45 %uint_1 %128 %uint_0 -OpBranch %124 -%124 = OpLabel -%130 = OpPhi %float %127 %125 %101 %126 -OpBranch %43 -%45 = OpLabel -%47 = OpBitcast %uint %7 -%100 = OpFunctionCall %void %48 %uint_45 %uint_0 %47 %40 -OpBranch %43 -%43 = OpLabel -%102 = OpPhi %float %130 %124 %101 %45 -OpStore %b %102 -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%25 = OpFunction %uint None %26 -%27 = OpFunctionParameter %uint -%28 = OpFunctionParameter %uint -%29 = OpLabel -%35 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %27 -%36 = OpLoad %uint %35 -%37 = OpIAdd %uint %36 %28 -%38 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %37 -%39 = OpLoad %uint %38 -OpReturnValue %39 -OpFunctionEnd -%48 = OpFunction %void None %49 -%50 = OpFunctionParameter %uint -%51 = OpFunctionParameter %uint -%52 = OpFunctionParameter %uint -%53 = OpFunctionParameter %uint -%54 = OpLabel -%58 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_0 -%61 = OpAtomicIAdd %uint %58 %uint_4 %uint_0 %uint_10 -%62 = OpIAdd %uint %61 %uint_10 -%63 = OpArrayLength %uint %57 1 -%64 = OpULessThanEqual %bool %62 %63 -OpSelectionMerge %65 None -OpBranchConditional %64 %66 %65 -%66 = OpLabel -%67 = OpIAdd %uint %61 %uint_0 -%68 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %67 -OpStore %68 %uint_10 -%70 = OpIAdd %uint %61 %uint_1 -%71 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %70 -OpStore %71 %uint_23 -%73 = OpIAdd %uint %61 %uint_2 -%74 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %73 -OpStore %74 %50 -%75 = OpIAdd %uint %61 %uint_3 -%76 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %75 -OpStore %76 %uint_4 -%80 = OpLoad %v4float %gl_FragCoord -%82 = OpBitcast %v4uint %80 -%83 = OpCompositeExtract %uint %82 0 -%84 = OpIAdd %uint %61 %uint_4 -%85 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %84 -OpStore %85 %83 -%86 = OpCompositeExtract %uint %82 1 -%88 = OpIAdd %uint %61 %uint_5 -%89 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %88 -OpStore %89 %86 -%91 = OpIAdd %uint %61 %uint_7 -%92 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %91 -OpStore %92 %51 -%94 = OpIAdd %uint %61 %uint_8 -%95 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %94 -OpStore %95 %52 -%97 = OpIAdd %uint %61 %uint_9 -%98 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %97 -OpStore %98 %53 -OpBranch %65 -%65 = OpLabel -OpReturn -OpFunctionEnd -%104 = OpFunction %uint None %105 -%106 = OpFunctionParameter %uint -%107 = OpFunctionParameter %uint -%108 = OpFunctionParameter %uint -%109 = OpFunctionParameter %uint -%110 = OpLabel -%111 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %106 -%112 = OpLoad %uint %111 -%113 = OpIAdd %uint %112 %107 -%114 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %113 -%115 = OpLoad %uint %114 -%116 = OpIAdd %uint %115 %108 -%117 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %116 -%118 = OpLoad %uint %117 -%119 = OpIAdd %uint %118 %109 -%120 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %119 -%121 = OpLoad %uint %120 -OpReturnValue %121 -OpFunctionEnd -)"; + const std::string new_funcs = kDirectRead2 + kStreamWrite4Frag + kDirectRead4; // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, false, false, false); + SinglePassRunAndMatch(defs + main_func + new_funcs, + true, 7u, 23u, true, true, false, + false, false); } TEST_F(InstBindlessTest, InstInitLoadUBOScalar) { @@ -3757,12 +2404,15 @@ TEST_F(InstBindlessTest, InstInitLoadUBOScalar) { // b = uniformBuffer.a; // } - const std::string defs_before = - R"(OpCapability Shader + // clang-format off + const std::string defs = R"( +OpCapability Shader OpExtension "SPV_EXT_descriptor_indexing" +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %b +; CHECK: OpEntryPoint Fragment %main "main" %b %gl_FragCoord OpExecutionMode %main OriginUpperLeft OpSource GLSL 450 OpSourceExtension "GL_EXT_nonuniform_qualifier" @@ -3776,6 +2426,9 @@ OpMemberDecorate %uname 0 Offset 0 OpDecorate %uname Block OpDecorate %uniformBuffer DescriptorSet 0 OpDecorate %uniformBuffer Binding 3 +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kInputDecorations + kOutputDecorations + R"( +; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -3787,187 +2440,68 @@ OpDecorate %uniformBuffer Binding 3 %int = OpTypeInt 32 1 %int_0 = OpConstant %int 0 %_ptr_Uniform_float = OpTypePointer Uniform %float +; CHECK: %int = OpTypeInt 32 1 +; CHECK: %int_0 = OpConstant %int 0 +; CHECK: %_ptr_Uniform_float = OpTypePointer Uniform %float +; CHECK: %uint = OpTypeInt 32 0 +; CHECK: %uint_0 = OpConstant %uint 0 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %21 = OpTypeFunction %uint %uint %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kInputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %bool = OpTypeBool +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %52 = OpTypeFunction %void %uint %uint %uint %uint +)" + kOutputGlobals + R"( +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %v4float = OpTypeVector %float 4 +; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +; CHECK: %v4uint = OpTypeVector %uint 4 +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_32 = OpConstant %uint 32 +; CHECK: %104 = OpConstantNull %float )"; + // clang-format on - const std::string defs_after = - R"(OpCapability Shader -OpExtension "SPV_EXT_descriptor_indexing" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %b %gl_FragCoord -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpName %main "main" -OpName %b "b" -OpName %uname "uname" -OpMemberName %uname 0 "a" -OpName %uniformBuffer "uniformBuffer" -OpDecorate %b Location 0 -OpMemberDecorate %uname 0 Offset 0 -OpDecorate %uname Block -OpDecorate %uniformBuffer DescriptorSet 0 -OpDecorate %uniformBuffer Binding 3 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_28 Block -OpMemberDecorate %_struct_28 0 Offset 0 -OpDecorate %30 DescriptorSet 7 -OpDecorate %30 Binding 1 -OpDecorate %_struct_58 Block -OpMemberDecorate %_struct_58 0 Offset 0 -OpMemberDecorate %_struct_58 1 Offset 4 -OpDecorate %60 DescriptorSet 7 -OpDecorate %60 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -%void = OpTypeVoid -%7 = OpTypeFunction %void -%float = OpTypeFloat 32 -%_ptr_Output_float = OpTypePointer Output %float -%b = OpVariable %_ptr_Output_float Output -%uname = OpTypeStruct %float -%_ptr_Uniform_uname = OpTypePointer Uniform %uname -%uniformBuffer = OpVariable %_ptr_Uniform_uname Uniform -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%_ptr_Uniform_float = OpTypePointer Uniform %float -%uint = OpTypeInt 32 0 -%uint_0 = OpConstant %uint 0 -%uint_3 = OpConstant %uint 3 -%21 = OpTypeFunction %uint %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_28 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_28 = OpTypePointer StorageBuffer %_struct_28 -%30 = OpVariable %_ptr_StorageBuffer__struct_28 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%bool = OpTypeBool -%uint_1 = OpConstant %uint 1 -%52 = OpTypeFunction %void %uint %uint %uint %uint -%_struct_58 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_58 = OpTypePointer StorageBuffer %_struct_58 -%60 = OpVariable %_ptr_StorageBuffer__struct_58 StorageBuffer -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_32 = OpConstant %uint 32 -%104 = OpConstantNull %float -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %3 + const std::string main_func = R"( +%main = OpFunction %void None %3 %5 = OpLabel %15 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %int_0 %16 = OpLoad %float %15 OpStore %b %16 +; CHECK-NOT: %16 = OpLoad %float %15 +; CHECK-NOT: OpStore %b %16 +; CHECK: %43 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_3 %uint_0 +; CHECK: %45 = OpULessThan %bool %uint_0 %43 +; CHECK: OpSelectionMerge %47 None +; CHECK: OpBranchConditional %45 %48 %49 +; CHECK: %48 = OpLabel +; CHECK: %50 = OpLoad %float %15 +; CHECK: OpBranch %47 +; CHECK: %49 = OpLabel +; CHECK: %103 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_32 %uint_1 %uint_0 %uint_0 +; CHECK: OpBranch %47 +; CHECK: %47 = OpLabel +; CHECK: %105 = OpPhi %float %50 %48 %104 %49 +; CHECK: OpStore %b %105 OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %7 -%14 = OpLabel -%15 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %int_0 -%43 = OpFunctionCall %uint %20 %uint_0 %uint_0 %uint_3 %uint_0 -%45 = OpULessThan %bool %uint_0 %43 -OpSelectionMerge %47 None -OpBranchConditional %45 %48 %49 -%48 = OpLabel -%50 = OpLoad %float %15 -OpBranch %47 -%49 = OpLabel -%103 = OpFunctionCall %void %51 %uint_32 %uint_1 %uint_0 %uint_0 -OpBranch %47 -%47 = OpLabel -%105 = OpPhi %float %50 %48 %104 %49 -OpStore %b %105 -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%20 = OpFunction %uint None %21 -%22 = OpFunctionParameter %uint -%23 = OpFunctionParameter %uint -%24 = OpFunctionParameter %uint -%25 = OpFunctionParameter %uint -%26 = OpLabel -%32 = OpAccessChain %_ptr_StorageBuffer_uint %30 %uint_0 %22 -%33 = OpLoad %uint %32 -%34 = OpIAdd %uint %33 %23 -%35 = OpAccessChain %_ptr_StorageBuffer_uint %30 %uint_0 %34 -%36 = OpLoad %uint %35 -%37 = OpIAdd %uint %36 %24 -%38 = OpAccessChain %_ptr_StorageBuffer_uint %30 %uint_0 %37 -%39 = OpLoad %uint %38 -%40 = OpIAdd %uint %39 %25 -%41 = OpAccessChain %_ptr_StorageBuffer_uint %30 %uint_0 %40 -%42 = OpLoad %uint %41 -OpReturnValue %42 -OpFunctionEnd -%51 = OpFunction %void None %52 -%53 = OpFunctionParameter %uint -%54 = OpFunctionParameter %uint -%55 = OpFunctionParameter %uint -%56 = OpFunctionParameter %uint -%57 = OpLabel -%61 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_0 -%64 = OpAtomicIAdd %uint %61 %uint_4 %uint_0 %uint_10 -%65 = OpIAdd %uint %64 %uint_10 -%66 = OpArrayLength %uint %60 1 -%67 = OpULessThanEqual %bool %65 %66 -OpSelectionMerge %68 None -OpBranchConditional %67 %69 %68 -%69 = OpLabel -%70 = OpIAdd %uint %64 %uint_0 -%71 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %70 -OpStore %71 %uint_10 -%73 = OpIAdd %uint %64 %uint_1 -%74 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %73 -OpStore %74 %uint_23 -%76 = OpIAdd %uint %64 %uint_2 -%77 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %76 -OpStore %77 %53 -%78 = OpIAdd %uint %64 %uint_3 -%79 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %78 -OpStore %79 %uint_4 -%83 = OpLoad %v4float %gl_FragCoord -%85 = OpBitcast %v4uint %83 -%86 = OpCompositeExtract %uint %85 0 -%87 = OpIAdd %uint %64 %uint_4 -%88 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %87 -OpStore %88 %86 -%89 = OpCompositeExtract %uint %85 1 -%91 = OpIAdd %uint %64 %uint_5 -%92 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %91 -OpStore %92 %89 -%94 = OpIAdd %uint %64 %uint_7 -%95 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %94 -OpStore %95 %54 -%97 = OpIAdd %uint %64 %uint_8 -%98 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %97 -OpStore %98 %55 -%100 = OpIAdd %uint %64 %uint_9 -%101 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %100 -OpStore %101 %56 -OpBranch %68 -%68 = OpLabel -OpReturn -OpFunctionEnd -)"; + const std::string new_funcs = kDirectRead4 + kStreamWrite4Frag; // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, false, false, false); + SinglePassRunAndMatch(defs + main_func + new_funcs, + true, 7u, 23u, true, true, false, + false, false); } TEST_F(InstBindlessTest, InstBoundsInitStoreUnsizedSSBOArray) { @@ -3984,15 +2518,17 @@ TEST_F(InstBindlessTest, InstBoundsInitStoreUnsizedSSBOArray) { // storageBuffer[nu_ii].b = b; // } - const std::string defs_before = - R"(OpCapability Shader + // clang-format off + const std::string defs = R"(OpCapability Shader OpCapability ShaderNonUniform OpCapability RuntimeDescriptorArray OpCapability StorageBufferArrayNonUniformIndexing OpExtension "SPV_EXT_descriptor_indexing" +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %nu_ii %b +; CHECK: OpEntryPoint Fragment %main "main" %nu_ii %b %gl_FragCoord OpExecutionMode %main OriginUpperLeft OpSource GLSL 450 OpSourceExtension "GL_EXT_nonuniform_qualifier" @@ -4011,6 +2547,9 @@ OpDecorate %nu_ii Location 0 OpDecorate %nu_ii NonUniform OpDecorate %14 NonUniform OpDecorate %b Location 1 +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kInputDecorations + kOutputDecorations + R"( +; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -4025,223 +2564,76 @@ OpDecorate %b Location 1 %_ptr_Input_float = OpTypePointer Input %float %b = OpVariable %_ptr_Input_float Input %_ptr_Uniform_float = OpTypePointer Uniform %float +; CHECK: %uint = OpTypeInt 32 0 +; CHECK: %uint_0 = OpConstant %uint 0 +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %26 = OpTypeFunction %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kInputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %bool = OpTypeBool +; CHECK: %48 = OpTypeFunction %void %uint %uint %uint %uint +)" + kOutputGlobals + R"( +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %v4float = OpTypeVector %float 4 +; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +; CHECK: %v4uint = OpTypeVector %uint 4 +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_45 = OpConstant %uint 45 +; CHECK: %102 = OpTypeFunction %uint %uint %uint %uint %uint )"; + // clang-format on - const std::string defs_after = - R"(OpCapability Shader -OpCapability ShaderNonUniform -OpCapability RuntimeDescriptorArray -OpCapability StorageBufferArrayNonUniformIndexing -OpExtension "SPV_EXT_descriptor_indexing" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %nu_ii %b %gl_FragCoord -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpName %main "main" -OpName %bname "bname" -OpMemberName %bname 0 "b" -OpName %storageBuffer "storageBuffer" -OpName %nu_ii "nu_ii" -OpName %b "b" -OpMemberDecorate %bname 0 Offset 0 -OpDecorate %bname BufferBlock -OpDecorate %storageBuffer DescriptorSet 0 -OpDecorate %storageBuffer Binding 4 -OpDecorate %nu_ii Flat -OpDecorate %nu_ii Location 0 -OpDecorate %nu_ii NonUniform -OpDecorate %7 NonUniform -OpDecorate %b Location 1 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_31 Block -OpMemberDecorate %_struct_31 0 Offset 0 -OpDecorate %33 DescriptorSet 7 -OpDecorate %33 Binding 1 -OpDecorate %_struct_54 Block -OpMemberDecorate %_struct_54 0 Offset 0 -OpMemberDecorate %_struct_54 1 Offset 4 -OpDecorate %56 DescriptorSet 7 -OpDecorate %56 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -%void = OpTypeVoid -%9 = OpTypeFunction %void -%float = OpTypeFloat 32 -%bname = OpTypeStruct %float -%_runtimearr_bname = OpTypeRuntimeArray %bname -%_ptr_Uniform__runtimearr_bname = OpTypePointer Uniform %_runtimearr_bname -%storageBuffer = OpVariable %_ptr_Uniform__runtimearr_bname Uniform -%int = OpTypeInt 32 1 -%_ptr_Input_int = OpTypePointer Input %int -%nu_ii = OpVariable %_ptr_Input_int Input -%int_0 = OpConstant %int 0 -%_ptr_Input_float = OpTypePointer Input %float -%b = OpVariable %_ptr_Input_float Input -%_ptr_Uniform_float = OpTypePointer Uniform %float -%uint = OpTypeInt 32 0 -%uint_0 = OpConstant %uint 0 -%uint_1 = OpConstant %uint 1 -%uint_4 = OpConstant %uint 4 -%26 = OpTypeFunction %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_31 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_31 = OpTypePointer StorageBuffer %_struct_31 -%33 = OpVariable %_ptr_StorageBuffer__struct_31 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%bool = OpTypeBool -%48 = OpTypeFunction %void %uint %uint %uint %uint -%_struct_54 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_54 = OpTypePointer StorageBuffer %_struct_54 -%56 = OpVariable %_ptr_StorageBuffer__struct_54 StorageBuffer -%uint_10 = OpConstant %uint 10 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_3 = OpConstant %uint 3 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_45 = OpConstant %uint 45 -%102 = OpTypeFunction %uint %uint %uint %uint %uint -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %3 + const std::string main_func = R"( +%main = OpFunction %void None %3 %5 = OpLabel %14 = OpLoad %int %nu_ii %18 = OpLoad %float %b %20 = OpAccessChain %_ptr_Uniform_float %storageBuffer %14 %int_0 OpStore %20 %18 +; CHECK-NOT: OpStore %20 %18 +; CHECK: %40 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_4 +; CHECK: %42 = OpULessThan %bool %7 %40 +; CHECK: OpSelectionMerge %43 None +; CHECK: OpBranchConditional %42 %44 %45 +; CHECK: %44 = OpLabel +; CHECK: %100 = OpBitcast %uint %7 +; CHECK: %119 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_4 %100 +; CHECK: %120 = OpULessThan %bool %uint_0 %119 +; CHECK: OpSelectionMerge %121 None +; CHECK: OpBranchConditional %120 %122 %123 +; CHECK: %122 = OpLabel +; CHECK: OpStore %20 %19 +; CHECK: OpBranch %121 +; CHECK: %123 = OpLabel +; CHECK: %124 = OpBitcast %uint %7 +; CHECK: %125 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_1 %124 %uint_0 +; CHECK: OpBranch %121 +; CHECK: %121 = OpLabel +; CHECK: OpBranch %43 +; CHECK: %45 = OpLabel +; CHECK: %46 = OpBitcast %uint %7 +; CHECK: %99 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_0 %46 %40 +; CHECK: OpBranch %43 +; CHECK: %43 = OpLabel OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %9 -%18 = OpLabel -%7 = OpLoad %int %nu_ii -%19 = OpLoad %float %b -%20 = OpAccessChain %_ptr_Uniform_float %storageBuffer %7 %int_0 -%40 = OpFunctionCall %uint %25 %uint_1 %uint_4 -%42 = OpULessThan %bool %7 %40 -OpSelectionMerge %43 None -OpBranchConditional %42 %44 %45 -%44 = OpLabel -%100 = OpBitcast %uint %7 -%119 = OpFunctionCall %uint %101 %uint_0 %uint_0 %uint_4 %100 -%120 = OpULessThan %bool %uint_0 %119 -OpSelectionMerge %121 None -OpBranchConditional %120 %122 %123 -%122 = OpLabel -OpStore %20 %19 -OpBranch %121 -%123 = OpLabel -%124 = OpBitcast %uint %7 -%125 = OpFunctionCall %void %47 %uint_45 %uint_1 %124 %uint_0 -OpBranch %121 -%121 = OpLabel -OpBranch %43 -%45 = OpLabel -%46 = OpBitcast %uint %7 -%99 = OpFunctionCall %void %47 %uint_45 %uint_0 %46 %40 -OpBranch %43 -%43 = OpLabel -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%25 = OpFunction %uint None %26 -%27 = OpFunctionParameter %uint -%28 = OpFunctionParameter %uint -%29 = OpLabel -%35 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %27 -%36 = OpLoad %uint %35 -%37 = OpIAdd %uint %36 %28 -%38 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %37 -%39 = OpLoad %uint %38 -OpReturnValue %39 -OpFunctionEnd -%47 = OpFunction %void None %48 -%49 = OpFunctionParameter %uint -%50 = OpFunctionParameter %uint -%51 = OpFunctionParameter %uint -%52 = OpFunctionParameter %uint -%53 = OpLabel -%57 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_0 -%59 = OpAtomicIAdd %uint %57 %uint_4 %uint_0 %uint_10 -%60 = OpIAdd %uint %59 %uint_10 -%61 = OpArrayLength %uint %56 1 -%62 = OpULessThanEqual %bool %60 %61 -OpSelectionMerge %63 None -OpBranchConditional %62 %64 %63 -%64 = OpLabel -%65 = OpIAdd %uint %59 %uint_0 -%66 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %65 -OpStore %66 %uint_10 -%68 = OpIAdd %uint %59 %uint_1 -%69 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %68 -OpStore %69 %uint_23 -%71 = OpIAdd %uint %59 %uint_2 -%72 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %71 -OpStore %72 %49 -%74 = OpIAdd %uint %59 %uint_3 -%75 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %74 -OpStore %75 %uint_4 -%79 = OpLoad %v4float %gl_FragCoord -%81 = OpBitcast %v4uint %79 -%82 = OpCompositeExtract %uint %81 0 -%83 = OpIAdd %uint %59 %uint_4 -%84 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %83 -OpStore %84 %82 -%85 = OpCompositeExtract %uint %81 1 -%87 = OpIAdd %uint %59 %uint_5 -%88 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %87 -OpStore %88 %85 -%90 = OpIAdd %uint %59 %uint_7 -%91 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %90 -OpStore %91 %50 -%93 = OpIAdd %uint %59 %uint_8 -%94 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %93 -OpStore %94 %51 -%96 = OpIAdd %uint %59 %uint_9 -%97 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %96 -OpStore %97 %52 -OpBranch %63 -%63 = OpLabel -OpReturn -OpFunctionEnd -%101 = OpFunction %uint None %102 -%103 = OpFunctionParameter %uint -%104 = OpFunctionParameter %uint -%105 = OpFunctionParameter %uint -%106 = OpFunctionParameter %uint -%107 = OpLabel -%108 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %103 -%109 = OpLoad %uint %108 -%110 = OpIAdd %uint %109 %104 -%111 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %110 -%112 = OpLoad %uint %111 -%113 = OpIAdd %uint %112 %105 -%114 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %113 -%115 = OpLoad %uint %114 -%116 = OpIAdd %uint %115 %106 -%117 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %116 -%118 = OpLoad %uint %117 -OpReturnValue %118 -OpFunctionEnd -)"; + const std::string new_funcs = kDirectRead2 + kStreamWrite4Frag + kDirectRead4; // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, false, false, false); + SinglePassRunAndMatch(defs + main_func + new_funcs, + true, 7u, 23u, true, true, false, + false, false); } TEST_F(InstBindlessTest, InstBoundsInitLoadSizedUBOArray) { @@ -4258,14 +2650,17 @@ TEST_F(InstBindlessTest, InstBoundsInitLoadSizedUBOArray) { // b = uniformBuffer[nu_ii].a; // } - const std::string defs_before = - R"(OpCapability Shader + // clang-format off + const std::string defs = R"( +OpCapability Shader OpCapability ShaderNonUniform OpCapability UniformBufferArrayNonUniformIndexing OpExtension "SPV_EXT_descriptor_indexing" +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %b %nu_ii +; CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord OpExecutionMode %main OriginUpperLeft OpSource GLSL 450 OpSourceExtension "GL_EXT_nonuniform_qualifier" @@ -4285,6 +2680,11 @@ OpDecorate %nu_ii Location 0 OpDecorate %nu_ii NonUniform OpDecorate %18 NonUniform OpDecorate %22 NonUniform +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kOutputDecorations + R"( +; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +)" + kInputDecorations + R"( +; CHECK: OpDecorate %117 NonUniform %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -4301,216 +2701,77 @@ OpDecorate %22 NonUniform %nu_ii = OpVariable %_ptr_Input_int Input %int_0 = OpConstant %int 0 %_ptr_Uniform_float = OpTypePointer Uniform %float -)"; +; CHECK: %uint_0 = OpConstant %uint 0 +; CHECK: %bool = OpTypeBool +; CHECK: %32 = OpTypeFunction %void %uint %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kOutputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %v4float = OpTypeVector %float 4 +; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +; CHECK: %v4uint = OpTypeVector %uint 4 +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_46 = OpConstant %uint 46 +; CHECK: %88 = OpConstantNull %float +; CHECK: %92 = OpTypeFunction %uint %uint %uint %uint %uint +)" + kInputGlobals; + // clang-format on - const std::string defs_after = - R"(OpCapability Shader -OpCapability ShaderNonUniform -OpCapability UniformBufferArrayNonUniformIndexing -OpExtension "SPV_EXT_descriptor_indexing" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpName %main "main" -OpName %b "b" -OpName %uname "uname" -OpMemberName %uname 0 "a" -OpName %uniformBuffer "uniformBuffer" -OpName %nu_ii "nu_ii" -OpDecorate %b Location 0 -OpMemberDecorate %uname 0 Offset 0 -OpDecorate %uname Block -OpDecorate %uniformBuffer DescriptorSet 0 -OpDecorate %uniformBuffer Binding 3 -OpDecorate %nu_ii Flat -OpDecorate %nu_ii Location 0 -OpDecorate %nu_ii NonUniform -OpDecorate %7 NonUniform -OpDecorate %89 NonUniform -OpDecorate %120 NonUniform -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_39 Block -OpMemberDecorate %_struct_39 0 Offset 0 -OpMemberDecorate %_struct_39 1 Offset 4 -OpDecorate %41 DescriptorSet 7 -OpDecorate %41 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -OpDecorate %_struct_98 Block -OpMemberDecorate %_struct_98 0 Offset 0 -OpDecorate %100 DescriptorSet 7 -OpDecorate %100 Binding 1 -OpDecorate %117 NonUniform -%void = OpTypeVoid -%10 = OpTypeFunction %void -%float = OpTypeFloat 32 -%_ptr_Output_float = OpTypePointer Output %float -%b = OpVariable %_ptr_Output_float Output -%uname = OpTypeStruct %float -%uint = OpTypeInt 32 0 -%uint_128 = OpConstant %uint 128 -%_arr_uname_uint_128 = OpTypeArray %uname %uint_128 -%_ptr_Uniform__arr_uname_uint_128 = OpTypePointer Uniform %_arr_uname_uint_128 -%uniformBuffer = OpVariable %_ptr_Uniform__arr_uname_uint_128 Uniform -%int = OpTypeInt 32 1 -%_ptr_Input_int = OpTypePointer Input %int -%nu_ii = OpVariable %_ptr_Input_int Input -%int_0 = OpConstant %int 0 -%_ptr_Uniform_float = OpTypePointer Uniform %float -%uint_0 = OpConstant %uint 0 -%bool = OpTypeBool -%32 = OpTypeFunction %void %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_39 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39 -%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_1 = OpConstant %uint 1 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_3 = OpConstant %uint 3 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_46 = OpConstant %uint 46 -%88 = OpConstantNull %float -%92 = OpTypeFunction %uint %uint %uint %uint %uint -%_struct_98 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_98 = OpTypePointer StorageBuffer %_struct_98 -%100 = OpVariable %_ptr_StorageBuffer__struct_98 StorageBuffer -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %3 + const std::string main_func = R"( +%main = OpFunction %void None %3 %5 = OpLabel %18 = OpLoad %int %nu_ii %21 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %18 %int_0 %22 = OpLoad %float %21 OpStore %b %22 +; CHECK-NOT: %22 = OpLoad %float %21 +; CHECK-NOT: OpStore %b %22 +; CHECK: %25 = OpULessThan %bool %7 %uint_128 +; CHECK: OpSelectionMerge %26 None +; CHECK: OpBranchConditional %25 %27 %28 +; CHECK: %27 = OpLabel +; CHECK: %90 = OpBitcast %uint %7 +; CHECK: %112 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_3 %90 +; CHECK: %113 = OpULessThan %bool %uint_0 %112 +; CHECK: OpSelectionMerge %114 None +; CHECK: OpBranchConditional %113 %115 %116 +; CHECK: %115 = OpLabel +; CHECK: %117 = OpLoad %float %22 +; CHECK: OpBranch %114 +; CHECK: %116 = OpLabel +; CHECK: %118 = OpBitcast %uint %7 +; CHECK: %119 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_46 %uint_1 %118 %uint_0 +; CHECK: OpBranch %114 +; CHECK: %114 = OpLabel +; CHECK: %120 = OpPhi %float %117 %115 %88 %116 +; CHECK: OpBranch %26 +; CHECK: %28 = OpLabel +; CHECK: %30 = OpBitcast %uint %7 +; CHECK: %87 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_46 %uint_0 %30 %uint_128 +; CHECK: OpBranch %26 +; CHECK: %26 = OpLabel +; CHECK: %89 = OpPhi %float %120 %114 %88 %28 +; CHECK: OpStore %b %89 OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %10 -%21 = OpLabel -%7 = OpLoad %int %nu_ii -%22 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %7 %int_0 -%25 = OpULessThan %bool %7 %uint_128 -OpSelectionMerge %26 None -OpBranchConditional %25 %27 %28 -%27 = OpLabel -%90 = OpBitcast %uint %7 -%112 = OpFunctionCall %uint %91 %uint_0 %uint_0 %uint_3 %90 -%113 = OpULessThan %bool %uint_0 %112 -OpSelectionMerge %114 None -OpBranchConditional %113 %115 %116 -%115 = OpLabel -%117 = OpLoad %float %22 -OpBranch %114 -%116 = OpLabel -%118 = OpBitcast %uint %7 -%119 = OpFunctionCall %void %31 %uint_46 %uint_1 %118 %uint_0 -OpBranch %114 -%114 = OpLabel -%120 = OpPhi %float %117 %115 %88 %116 -OpBranch %26 -%28 = OpLabel -%30 = OpBitcast %uint %7 -%87 = OpFunctionCall %void %31 %uint_46 %uint_0 %30 %uint_128 -OpBranch %26 -%26 = OpLabel -%89 = OpPhi %float %120 %114 %88 %28 -OpStore %b %89 -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%31 = OpFunction %void None %32 -%33 = OpFunctionParameter %uint -%34 = OpFunctionParameter %uint -%35 = OpFunctionParameter %uint -%36 = OpFunctionParameter %uint -%37 = OpLabel -%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 -%46 = OpAtomicIAdd %uint %43 %uint_4 %uint_0 %uint_10 -%47 = OpIAdd %uint %46 %uint_10 -%48 = OpArrayLength %uint %41 1 -%49 = OpULessThanEqual %bool %47 %48 -OpSelectionMerge %50 None -OpBranchConditional %49 %51 %50 -%51 = OpLabel -%52 = OpIAdd %uint %46 %uint_0 -%54 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %52 -OpStore %54 %uint_10 -%56 = OpIAdd %uint %46 %uint_1 -%57 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %56 -OpStore %57 %uint_23 -%59 = OpIAdd %uint %46 %uint_2 -%60 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %59 -OpStore %60 %33 -%62 = OpIAdd %uint %46 %uint_3 -%63 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %62 -OpStore %63 %uint_4 -%67 = OpLoad %v4float %gl_FragCoord -%69 = OpBitcast %v4uint %67 -%70 = OpCompositeExtract %uint %69 0 -%71 = OpIAdd %uint %46 %uint_4 -%72 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %71 -OpStore %72 %70 -%73 = OpCompositeExtract %uint %69 1 -%75 = OpIAdd %uint %46 %uint_5 -%76 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %75 -OpStore %76 %73 -%78 = OpIAdd %uint %46 %uint_7 -%79 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %78 -OpStore %79 %34 -%81 = OpIAdd %uint %46 %uint_8 -%82 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %81 -OpStore %82 %35 -%84 = OpIAdd %uint %46 %uint_9 -%85 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %84 -OpStore %85 %36 -OpBranch %50 -%50 = OpLabel -OpReturn -OpFunctionEnd -%91 = OpFunction %uint None %92 -%93 = OpFunctionParameter %uint -%94 = OpFunctionParameter %uint -%95 = OpFunctionParameter %uint -%96 = OpFunctionParameter %uint -%97 = OpLabel -%101 = OpAccessChain %_ptr_StorageBuffer_uint %100 %uint_0 %93 -%102 = OpLoad %uint %101 -%103 = OpIAdd %uint %102 %94 -%104 = OpAccessChain %_ptr_StorageBuffer_uint %100 %uint_0 %103 -%105 = OpLoad %uint %104 -%106 = OpIAdd %uint %105 %95 -%107 = OpAccessChain %_ptr_StorageBuffer_uint %100 %uint_0 %106 -%108 = OpLoad %uint %107 -%109 = OpIAdd %uint %108 %96 -%110 = OpAccessChain %_ptr_StorageBuffer_uint %100 %uint_0 %109 -%111 = OpLoad %uint %110 -OpReturnValue %111 -OpFunctionEnd -)"; + const std::string new_funcs = kStreamWrite4Frag + kDirectRead4; // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, false, false, false); + SinglePassRunAndMatch(defs + main_func + new_funcs, + true, 7u, 23u, true, true, false, + false, false); } TEST_F(InstBindlessTest, @@ -4532,13 +2793,16 @@ TEST_F(InstBindlessTest, // sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r; // } - const std::string defs_before = - R"(OpCapability Shader + // clang-format off + const std::string defs = R"( +OpCapability Shader OpCapability RuntimeDescriptorArray OpExtension "SPV_EXT_descriptor_indexing" +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" +; CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID OpExecutionMode %main LocalSize 1 1 1 OpSource GLSL 450 OpSourceExtension "GL_EXT_nonuniform_qualifier" @@ -4556,6 +2820,9 @@ OpDecorate %sbo Binding 0 OpDecorate %images DescriptorSet 0 OpDecorate %images Binding 1 OpDecorate %images NonWritable +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kInputDecorations + kOutputDecorations + R"( +; CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId %void = OpTypeVoid %3 = OpTypeFunction %void %uint = OpTypeInt 32 0 @@ -4577,100 +2844,38 @@ OpDecorate %images NonWritable %v4float = OpTypeVector %float 4 %uint_0 = OpConstant %uint 0 %_ptr_Uniform_float = OpTypePointer Uniform %float +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %34 = OpTypeFunction %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kInputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %bool = OpTypeBool +; CHECK: %57 = OpTypeFunction %void %uint %uint %uint %uint +)" + kOutputGlobals + R"( +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %v3uint = OpTypeVector %uint 3 +; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint +; CHECK: %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +; CHECK: %uint_6 = OpConstant %uint 6 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_50 = OpConstant %uint 50 +; CHECK: %112 = OpConstantNull %v4float +; CHECK: %115 = OpTypeFunction %uint %uint %uint %uint %uint +; CHECK: %uint_47 = OpConstant %uint 47 +; CHECK: %140 = OpConstantNull %uint +; CHECK: %uint_53 = OpConstant %uint 53 )"; + // clang-format on - const std::string defs_after = - R"(OpCapability Shader -OpCapability RuntimeDescriptorArray -OpExtension "SPV_EXT_descriptor_indexing" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID -OpExecutionMode %main LocalSize 1 1 1 -OpSource GLSL 450 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpName %main "main" -OpName %Input "Input" -OpMemberName %Input 0 "index" -OpMemberName %Input 1 "red" -OpName %sbo "sbo" -OpName %images "images" -OpMemberDecorate %Input 0 Offset 0 -OpMemberDecorate %Input 1 Offset 4 -OpDecorate %Input BufferBlock -OpDecorate %sbo DescriptorSet 0 -OpDecorate %sbo Binding 0 -OpDecorate %images DescriptorSet 0 -OpDecorate %images Binding 1 -OpDecorate %images NonWritable -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_39 Block -OpMemberDecorate %_struct_39 0 Offset 0 -OpDecorate %41 DescriptorSet 7 -OpDecorate %41 Binding 1 -OpDecorate %_struct_63 Block -OpMemberDecorate %_struct_63 0 Offset 0 -OpMemberDecorate %_struct_63 1 Offset 4 -OpDecorate %65 DescriptorSet 7 -OpDecorate %65 Binding 0 -OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId -%void = OpTypeVoid -%7 = OpTypeFunction %void -%uint = OpTypeInt 32 0 -%float = OpTypeFloat 32 -%Input = OpTypeStruct %uint %float -%_ptr_Uniform_Input = OpTypePointer Uniform %Input -%sbo = OpVariable %_ptr_Uniform_Input Uniform -%int = OpTypeInt 32 1 -%int_1 = OpConstant %int 1 -%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f -%_runtimearr_13 = OpTypeRuntimeArray %13 -%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 -%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant -%int_0 = OpConstant %int 0 -%_ptr_Uniform_uint = OpTypePointer Uniform %uint -%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 -%v2int = OpTypeVector %int 2 -%20 = OpConstantComposite %v2int %int_0 %int_0 -%v4float = OpTypeVector %float 4 -%uint_0 = OpConstant %uint 0 -%_ptr_Uniform_float = OpTypePointer Uniform %float -%uint_1 = OpConstant %uint 1 -%34 = OpTypeFunction %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_39 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39 -%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%bool = OpTypeBool -%57 = OpTypeFunction %void %uint %uint %uint %uint -%_struct_63 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63 -%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_5 = OpConstant %uint 5 -%uint_3 = OpConstant %uint 3 -%v3uint = OpTypeVector %uint 3 -%_ptr_Input_v3uint = OpTypePointer Input %v3uint -%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_50 = OpConstant %uint 50 -%112 = OpConstantNull %v4float -%115 = OpTypeFunction %uint %uint %uint %uint %uint -%uint_47 = OpConstant %uint 47 -%140 = OpConstantNull %uint -%uint_53 = OpConstant %uint 53 -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %3 + const std::string main_func = R"( +%main = OpFunction %void None %3 %5 = OpLabel %19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 %20 = OpLoad %uint %19 @@ -4680,159 +2885,70 @@ OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId %29 = OpCompositeExtract %float %27 0 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 OpStore %31 %29 -OpReturn -OpFunctionEnd -)"; - - const std::string func_after = - R"(%main = OpFunction %void None %7 -%24 = OpLabel -%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 -%132 = OpFunctionCall %uint %114 %uint_0 %uint_0 %uint_0 %uint_0 -%133 = OpULessThan %bool %uint_0 %132 -OpSelectionMerge %134 None -OpBranchConditional %133 %135 %136 -%135 = OpLabel -%137 = OpLoad %uint %25 -OpBranch %134 -%136 = OpLabel -%139 = OpFunctionCall %void %56 %uint_47 %uint_1 %uint_0 %uint_0 -OpBranch %134 -%134 = OpLabel -%141 = OpPhi %uint %137 %135 %140 %136 -%27 = OpAccessChain %_ptr_UniformConstant_13 %images %141 -%28 = OpLoad %13 %27 -%48 = OpFunctionCall %uint %33 %uint_1 %uint_1 -%50 = OpULessThan %bool %141 %48 -OpSelectionMerge %51 None -OpBranchConditional %50 %52 %53 -%52 = OpLabel -%54 = OpLoad %13 %27 -%142 = OpFunctionCall %uint %114 %uint_0 %uint_0 %uint_1 %141 -%143 = OpULessThan %bool %uint_0 %142 -OpSelectionMerge %144 None -OpBranchConditional %143 %145 %146 -%145 = OpLabel -%147 = OpLoad %13 %27 -%148 = OpImageRead %v4float %147 %20 -OpBranch %144 -%146 = OpLabel -%149 = OpFunctionCall %void %56 %uint_50 %uint_1 %141 %uint_0 -OpBranch %144 -%144 = OpLabel -%150 = OpPhi %v4float %148 %145 %112 %146 -OpBranch %51 -%53 = OpLabel -%111 = OpFunctionCall %void %56 %uint_50 %uint_0 %141 %48 -OpBranch %51 -%51 = OpLabel -%113 = OpPhi %v4float %150 %144 %112 %53 -%30 = OpCompositeExtract %float %113 0 -%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 -%151 = OpFunctionCall %uint %114 %uint_0 %uint_0 %uint_0 %uint_0 -%152 = OpULessThan %bool %uint_0 %151 -OpSelectionMerge %153 None -OpBranchConditional %152 %154 %155 -%154 = OpLabel -OpStore %31 %30 -OpBranch %153 -%155 = OpLabel -%157 = OpFunctionCall %void %56 %uint_53 %uint_1 %uint_0 %uint_0 -OpBranch %153 -%153 = OpLabel +; CHECK-NOT: OpStore %31 %29 +; CHECK: %132 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0 +; CHECK: %133 = OpULessThan %bool %uint_0 %132 +; CHECK: OpSelectionMerge %134 None +; CHECK: OpBranchConditional %133 %135 %136 +; CHECK: %135 = OpLabel +; CHECK: %137 = OpLoad %uint %25 +; CHECK: OpBranch %134 +; CHECK: %136 = OpLabel +; CHECK: %139 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_47 %uint_1 %uint_0 %uint_0 +; CHECK: OpBranch %134 +; CHECK: %134 = OpLabel +; CHECK: %141 = OpPhi %uint %137 %135 %140 %136 +; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %141 +; CHECK: %28 = OpLoad %13 %27 +; CHECK: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1 +; CHECK: %50 = OpULessThan %bool %141 %48 +; CHECK: OpSelectionMerge %51 None +; CHECK: OpBranchConditional %50 %52 %53 +; CHECK: %52 = OpLabel +; CHECK: %54 = OpLoad %13 %27 +; CHECK: %142 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %141 +; CHECK: %143 = OpULessThan %bool %uint_0 %142 +; CHECK: OpSelectionMerge %144 None +; CHECK: OpBranchConditional %143 %145 %146 +; CHECK: %145 = OpLabel +; CHECK: %147 = OpLoad %13 %27 +; CHECK: %148 = OpImageRead %v4float %147 %20 +; CHECK: OpBranch %144 +; CHECK: %146 = OpLabel +; CHECK: %149 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_50 %uint_1 %141 %uint_0 +; CHECK: OpBranch %144 +; CHECK: %144 = OpLabel +; CHECK: %150 = OpPhi %v4float %148 %145 %112 %146 +; CHECK: OpBranch %51 +; CHECK: %53 = OpLabel +; CHECK: %111 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_50 %uint_0 %141 %48 +; CHECK: OpBranch %51 +; CHECK: %51 = OpLabel +; CHECK: %113 = OpPhi %v4float %150 %144 %112 %53 +; CHECK: %30 = OpCompositeExtract %float %113 0 +; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +; CHECK: %151 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0 +; CHECK: %152 = OpULessThan %bool %uint_0 %151 +; CHECK: OpSelectionMerge %153 None +; CHECK: OpBranchConditional %152 %154 %155 +; CHECK: %154 = OpLabel +; CHECK: OpStore %31 %30 +; CHECK: OpBranch %153 +; CHECK: %155 = OpLabel +; CHECK: %157 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_53 %uint_1 %uint_0 %uint_0 +; CHECK: OpBranch %153 +; CHECK: %153 = OpLabel OpReturn OpFunctionEnd )"; const std::string new_funcs = - R"(%33 = OpFunction %uint None %34 -%35 = OpFunctionParameter %uint -%36 = OpFunctionParameter %uint -%37 = OpLabel -%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35 -%44 = OpLoad %uint %43 -%45 = OpIAdd %uint %44 %36 -%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45 -%47 = OpLoad %uint %46 -OpReturnValue %47 -OpFunctionEnd -%56 = OpFunction %void None %57 -%58 = OpFunctionParameter %uint -%59 = OpFunctionParameter %uint -%60 = OpFunctionParameter %uint -%61 = OpFunctionParameter %uint -%62 = OpLabel -%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0 -%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10 -%70 = OpIAdd %uint %69 %uint_10 -%71 = OpArrayLength %uint %65 1 -%72 = OpULessThanEqual %bool %70 %71 -OpSelectionMerge %73 None -OpBranchConditional %72 %74 %73 -%74 = OpLabel -%75 = OpIAdd %uint %69 %uint_0 -%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75 -OpStore %76 %uint_10 -%78 = OpIAdd %uint %69 %uint_1 -%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78 -OpStore %79 %uint_23 -%81 = OpIAdd %uint %69 %uint_2 -%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81 -OpStore %82 %58 -%85 = OpIAdd %uint %69 %uint_3 -%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85 -OpStore %86 %uint_5 -%90 = OpLoad %v3uint %gl_GlobalInvocationID -%91 = OpCompositeExtract %uint %90 0 -%92 = OpCompositeExtract %uint %90 1 -%93 = OpCompositeExtract %uint %90 2 -%94 = OpIAdd %uint %69 %uint_4 -%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94 -OpStore %95 %91 -%96 = OpIAdd %uint %69 %uint_5 -%97 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %96 -OpStore %97 %92 -%99 = OpIAdd %uint %69 %uint_6 -%100 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %99 -OpStore %100 %93 -%102 = OpIAdd %uint %69 %uint_7 -%103 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %102 -OpStore %103 %59 -%105 = OpIAdd %uint %69 %uint_8 -%106 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %105 -OpStore %106 %60 -%108 = OpIAdd %uint %69 %uint_9 -%109 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %108 -OpStore %109 %61 -OpBranch %73 -%73 = OpLabel -OpReturn -OpFunctionEnd -%114 = OpFunction %uint None %115 -%116 = OpFunctionParameter %uint -%117 = OpFunctionParameter %uint -%118 = OpFunctionParameter %uint -%119 = OpFunctionParameter %uint -%120 = OpLabel -%121 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %116 -%122 = OpLoad %uint %121 -%123 = OpIAdd %uint %122 %117 -%124 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %123 -%125 = OpLoad %uint %124 -%126 = OpIAdd %uint %125 %118 -%127 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %126 -%128 = OpLoad %uint %127 -%129 = OpIAdd %uint %128 %119 -%130 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %129 -%131 = OpLoad %uint %130 -OpReturnValue %131 -OpFunctionEnd -)"; + kDirectRead2 + kStreamWrite4Compute + kDirectRead4; // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, false, false, false); + SinglePassRunAndMatch(defs + main_func + new_funcs, + true, 7u, 23u, true, true, false, + false, false); } TEST_F(InstBindlessTest, @@ -4853,14 +2969,17 @@ TEST_F(InstBindlessTest, // sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r; // } - const std::string defs_before = - R"(OpCapability RuntimeDescriptorArray + // clang-format off + const std::string defs = R"( +OpCapability RuntimeDescriptorArray OpCapability RayTracingNV OpExtension "SPV_EXT_descriptor_indexing" OpExtension "SPV_NV_ray_tracing" +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint RayGenerationNV %main "main" +; CHECK: OpEntryPoint RayGenerationNV %main "main" %89 OpSource GLSL 460 OpSourceExtension "GL_EXT_nonuniform_qualifier" OpSourceExtension "GL_NV_ray_tracing" @@ -4878,51 +2997,11 @@ OpDecorate %sbo Binding 0 OpDecorate %images DescriptorSet 0 OpDecorate %images Binding 1 OpDecorate %images NonWritable +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kInputDecorations + kOutputDecorations + R"( +; CHECK: OpDecorate %89 BuiltIn LaunchIdNV %void = OpTypeVoid -)"; - - const std::string defs_after = - R"(OpCapability RuntimeDescriptorArray -OpCapability RayTracingNV -OpExtension "SPV_EXT_descriptor_indexing" -OpExtension "SPV_NV_ray_tracing" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint RayGenerationNV %main "main" %89 -OpSource GLSL 460 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpSourceExtension "GL_NV_ray_tracing" -OpName %main "main" -OpName %StorageBuffer "StorageBuffer" -OpMemberName %StorageBuffer 0 "index" -OpMemberName %StorageBuffer 1 "red" -OpName %sbo "sbo" -OpName %images "images" -OpMemberDecorate %StorageBuffer 0 Offset 0 -OpMemberDecorate %StorageBuffer 1 Offset 4 -OpDecorate %StorageBuffer BufferBlock -OpDecorate %sbo DescriptorSet 0 -OpDecorate %sbo Binding 0 -OpDecorate %images DescriptorSet 0 -OpDecorate %images Binding 1 -OpDecorate %images NonWritable -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_39 Block -OpMemberDecorate %_struct_39 0 Offset 0 -OpDecorate %41 DescriptorSet 7 -OpDecorate %41 Binding 1 -OpDecorate %_struct_63 Block -OpMemberDecorate %_struct_63 0 Offset 0 -OpMemberDecorate %_struct_63 1 Offset 4 -OpDecorate %65 DescriptorSet 7 -OpDecorate %65 Binding 0 -OpDecorate %89 BuiltIn LaunchIdNV -%void = OpTypeVoid -)"; - - const std::string func_before = - R"(%3 = OpTypeFunction %void +%3 = OpTypeFunction %void %uint = OpTypeInt 32 0 %float = OpTypeFloat 32 %StorageBuffer = OpTypeStruct %uint %float @@ -4942,6 +3021,38 @@ OpDecorate %89 BuiltIn LaunchIdNV %v4float = OpTypeVector %float 4 %uint_0 = OpConstant %uint 0 %_ptr_Uniform_float = OpTypePointer Uniform %float +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %34 = OpTypeFunction %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kInputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %bool = OpTypeBool +; CHECK: %57 = OpTypeFunction %void %uint %uint %uint %uint +)" + kOutputGlobals + R"( +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %uint_5313 = OpConstant %uint 5313 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %v3uint = OpTypeVector %uint 3 +; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint +; CHECK: %89 = OpVariable %_ptr_Input_v3uint Input +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_6 = OpConstant %uint 6 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_51 = OpConstant %uint 51 +; CHECK: %113 = OpConstantNull %v4float +; CHECK: %116 = OpTypeFunction %uint %uint %uint %uint %uint +; CHECK: %uint_48 = OpConstant %uint 48 +; CHECK: %141 = OpConstantNull %uint +; CHECK: %uint_54 = OpConstant %uint 54 +)"; + // clang-format on + + const std::string main_func = R"( %main = OpFunction %void None %3 %5 = OpLabel %19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 @@ -4952,211 +3063,69 @@ OpDecorate %89 BuiltIn LaunchIdNV %29 = OpCompositeExtract %float %27 0 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 OpStore %31 %29 +; CHECK-NOT: OpStore %31 %29 +; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0 +; CHECK: %134 = OpULessThan %bool %uint_0 %133 +; CHECK: OpSelectionMerge %135 None +; CHECK: OpBranchConditional %134 %136 %137 +; CHECK: %136 = OpLabel +; CHECK: %138 = OpLoad %uint %25 +; CHECK: OpBranch %135 +; CHECK: %137 = OpLabel +; CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0 +; CHECK: OpBranch %135 +; CHECK: %135 = OpLabel +; CHECK: %142 = OpPhi %uint %138 %136 %141 %137 +; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142 +; CHECK: %28 = OpLoad %13 %27 +; CHECK: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1 +; CHECK: %50 = OpULessThan %bool %142 %48 +; CHECK: OpSelectionMerge %51 None +; CHECK: OpBranchConditional %50 %52 %53 +; CHECK: %52 = OpLabel +; CHECK: %54 = OpLoad %13 %27 +; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142 +; CHECK: %144 = OpULessThan %bool %uint_0 %143 +; CHECK: OpSelectionMerge %145 None +; CHECK: OpBranchConditional %144 %146 %147 +; CHECK: %146 = OpLabel +; CHECK: %148 = OpLoad %13 %27 +; CHECK: %149 = OpImageRead %v4float %148 %20 +; CHECK: OpBranch %145 +; CHECK: %147 = OpLabel +; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0 +; CHECK: OpBranch %145 +; CHECK: %145 = OpLabel +; CHECK: %151 = OpPhi %v4float %149 %146 %113 %147 +; CHECK: OpBranch %51 +; CHECK: %53 = OpLabel +; CHECK: %112 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48 +; CHECK: OpBranch %51 +; CHECK: %51 = OpLabel +; CHECK: %114 = OpPhi %v4float %151 %145 %113 %53 +; CHECK: %30 = OpCompositeExtract %float %114 0 +; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +; CHECK: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0 +; CHECK: %153 = OpULessThan %bool %uint_0 %152 +; CHECK: OpSelectionMerge %154 None +; CHECK: OpBranchConditional %153 %155 %156 +; CHECK: %155 = OpLabel +; CHECK: OpStore %31 %30 +; CHECK: OpBranch %154 +; CHECK: %156 = OpLabel +; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0 +; CHECK: OpBranch %154 +; CHECK: %154 = OpLabel OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%7 = OpTypeFunction %void -%uint = OpTypeInt 32 0 -%float = OpTypeFloat 32 -%StorageBuffer = OpTypeStruct %uint %float -%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer -%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform -%int = OpTypeInt 32 1 -%int_1 = OpConstant %int 1 -%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f -%_runtimearr_13 = OpTypeRuntimeArray %13 -%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 -%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant -%int_0 = OpConstant %int 0 -%_ptr_Uniform_uint = OpTypePointer Uniform %uint -%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 -%v2int = OpTypeVector %int 2 -%20 = OpConstantComposite %v2int %int_0 %int_0 -%v4float = OpTypeVector %float 4 -%uint_0 = OpConstant %uint 0 -%_ptr_Uniform_float = OpTypePointer Uniform %float -%uint_1 = OpConstant %uint 1 -%34 = OpTypeFunction %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_39 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39 -%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%bool = OpTypeBool -%57 = OpTypeFunction %void %uint %uint %uint %uint -%_struct_63 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63 -%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_5313 = OpConstant %uint 5313 -%uint_3 = OpConstant %uint 3 -%v3uint = OpTypeVector %uint 3 -%_ptr_Input_v3uint = OpTypePointer Input %v3uint -%89 = OpVariable %_ptr_Input_v3uint Input -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_51 = OpConstant %uint 51 -%113 = OpConstantNull %v4float -%116 = OpTypeFunction %uint %uint %uint %uint %uint -%uint_48 = OpConstant %uint 48 -%141 = OpConstantNull %uint -%uint_54 = OpConstant %uint 54 -%main = OpFunction %void None %7 -%24 = OpLabel -%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 -%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 -%134 = OpULessThan %bool %uint_0 %133 -OpSelectionMerge %135 None -OpBranchConditional %134 %136 %137 -%136 = OpLabel -%138 = OpLoad %uint %25 -OpBranch %135 -%137 = OpLabel -%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0 -OpBranch %135 -%135 = OpLabel -%142 = OpPhi %uint %138 %136 %141 %137 -%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142 -%28 = OpLoad %13 %27 -%48 = OpFunctionCall %uint %33 %uint_1 %uint_1 -%50 = OpULessThan %bool %142 %48 -OpSelectionMerge %51 None -OpBranchConditional %50 %52 %53 -%52 = OpLabel -%54 = OpLoad %13 %27 -%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142 -%144 = OpULessThan %bool %uint_0 %143 -OpSelectionMerge %145 None -OpBranchConditional %144 %146 %147 -%146 = OpLabel -%148 = OpLoad %13 %27 -%149 = OpImageRead %v4float %148 %20 -OpBranch %145 -%147 = OpLabel -%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0 -OpBranch %145 -%145 = OpLabel -%151 = OpPhi %v4float %149 %146 %113 %147 -OpBranch %51 -%53 = OpLabel -%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48 -OpBranch %51 -%51 = OpLabel -%114 = OpPhi %v4float %151 %145 %113 %53 -%30 = OpCompositeExtract %float %114 0 -%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 -%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 -%153 = OpULessThan %bool %uint_0 %152 -OpSelectionMerge %154 None -OpBranchConditional %153 %155 %156 -%155 = OpLabel -OpStore %31 %30 -OpBranch %154 -%156 = OpLabel -%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0 -OpBranch %154 -%154 = OpLabel -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%33 = OpFunction %uint None %34 -%35 = OpFunctionParameter %uint -%36 = OpFunctionParameter %uint -%37 = OpLabel -%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35 -%44 = OpLoad %uint %43 -%45 = OpIAdd %uint %44 %36 -%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45 -%47 = OpLoad %uint %46 -OpReturnValue %47 -OpFunctionEnd -%56 = OpFunction %void None %57 -%58 = OpFunctionParameter %uint -%59 = OpFunctionParameter %uint -%60 = OpFunctionParameter %uint -%61 = OpFunctionParameter %uint -%62 = OpLabel -%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0 -%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10 -%70 = OpIAdd %uint %69 %uint_10 -%71 = OpArrayLength %uint %65 1 -%72 = OpULessThanEqual %bool %70 %71 -OpSelectionMerge %73 None -OpBranchConditional %72 %74 %73 -%74 = OpLabel -%75 = OpIAdd %uint %69 %uint_0 -%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75 -OpStore %76 %uint_10 -%78 = OpIAdd %uint %69 %uint_1 -%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78 -OpStore %79 %uint_23 -%81 = OpIAdd %uint %69 %uint_2 -%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81 -OpStore %82 %58 -%85 = OpIAdd %uint %69 %uint_3 -%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85 -OpStore %86 %uint_5313 -%90 = OpLoad %v3uint %89 -%91 = OpCompositeExtract %uint %90 0 -%92 = OpCompositeExtract %uint %90 1 -%93 = OpCompositeExtract %uint %90 2 -%94 = OpIAdd %uint %69 %uint_4 -%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94 -OpStore %95 %91 -%97 = OpIAdd %uint %69 %uint_5 -%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97 -OpStore %98 %92 -%100 = OpIAdd %uint %69 %uint_6 -%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100 -OpStore %101 %93 -%103 = OpIAdd %uint %69 %uint_7 -%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103 -OpStore %104 %59 -%106 = OpIAdd %uint %69 %uint_8 -%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106 -OpStore %107 %60 -%109 = OpIAdd %uint %69 %uint_9 -%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109 -OpStore %110 %61 -OpBranch %73 -%73 = OpLabel -OpReturn -OpFunctionEnd -%115 = OpFunction %uint None %116 -%117 = OpFunctionParameter %uint -%118 = OpFunctionParameter %uint -%119 = OpFunctionParameter %uint -%120 = OpFunctionParameter %uint -%121 = OpLabel -%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117 -%123 = OpLoad %uint %122 -%124 = OpIAdd %uint %123 %118 -%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124 -%126 = OpLoad %uint %125 -%127 = OpIAdd %uint %126 %119 -%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127 -%129 = OpLoad %uint %128 -%130 = OpIAdd %uint %129 %120 -%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130 -%132 = OpLoad %uint %131 -OpReturnValue %132 -OpFunctionEnd -)"; + const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4; // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, false, false, false); + SinglePassRunAndMatch(defs + main_func + new_funcs, + true, 7u, 23u, true, true, false, + false, false); } TEST_F(InstBindlessTest, @@ -5177,14 +3146,17 @@ TEST_F(InstBindlessTest, // sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r; // } - const std::string defs_before = - R"(OpCapability RuntimeDescriptorArray + // clang-format off + const std::string defs = R"( +OpCapability RuntimeDescriptorArray OpCapability RayTracingNV OpExtension "SPV_EXT_descriptor_indexing" OpExtension "SPV_NV_ray_tracing" +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint IntersectionNV %main "main" +; CHECK: OpEntryPoint IntersectionNV %main "main" %89 OpSource GLSL 460 OpSourceExtension "GL_EXT_nonuniform_qualifier" OpSourceExtension "GL_NV_ray_tracing" @@ -5202,51 +3174,11 @@ OpDecorate %sbo Binding 0 OpDecorate %images DescriptorSet 0 OpDecorate %images Binding 1 OpDecorate %images NonWritable +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kInputDecorations + kOutputDecorations + R"( +; CHECK: OpDecorate %89 BuiltIn LaunchIdNV %void = OpTypeVoid -)"; - - const std::string defs_after = - R"(OpCapability RuntimeDescriptorArray -OpCapability RayTracingNV -OpExtension "SPV_EXT_descriptor_indexing" -OpExtension "SPV_NV_ray_tracing" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint IntersectionNV %main "main" %89 -OpSource GLSL 460 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpSourceExtension "GL_NV_ray_tracing" -OpName %main "main" -OpName %StorageBuffer "StorageBuffer" -OpMemberName %StorageBuffer 0 "index" -OpMemberName %StorageBuffer 1 "red" -OpName %sbo "sbo" -OpName %images "images" -OpMemberDecorate %StorageBuffer 0 Offset 0 -OpMemberDecorate %StorageBuffer 1 Offset 4 -OpDecorate %StorageBuffer BufferBlock -OpDecorate %sbo DescriptorSet 0 -OpDecorate %sbo Binding 0 -OpDecorate %images DescriptorSet 0 -OpDecorate %images Binding 1 -OpDecorate %images NonWritable -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_39 Block -OpMemberDecorate %_struct_39 0 Offset 0 -OpDecorate %41 DescriptorSet 7 -OpDecorate %41 Binding 1 -OpDecorate %_struct_63 Block -OpMemberDecorate %_struct_63 0 Offset 0 -OpMemberDecorate %_struct_63 1 Offset 4 -OpDecorate %65 DescriptorSet 7 -OpDecorate %65 Binding 0 -OpDecorate %89 BuiltIn LaunchIdNV -%void = OpTypeVoid -)"; - - const std::string func_before = - R"(%3 = OpTypeFunction %void +%3 = OpTypeFunction %void %uint = OpTypeInt 32 0 %float = OpTypeFloat 32 %StorageBuffer = OpTypeStruct %uint %float @@ -5266,6 +3198,37 @@ OpDecorate %89 BuiltIn LaunchIdNV %v4float = OpTypeVector %float 4 %uint_0 = OpConstant %uint 0 %_ptr_Uniform_float = OpTypePointer Uniform %float +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %34 = OpTypeFunction %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kInputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %bool = OpTypeBool +)" + kOutputGlobals + R"( +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %uint_5314 = OpConstant %uint 5314 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %v3uint = OpTypeVector %uint 3 +; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint +; CHECK: %89 = OpVariable %_ptr_Input_v3uint Input +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_6 = OpConstant %uint 6 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_51 = OpConstant %uint 51 +; CHECK: %113 = OpConstantNull %v4float +; CHECK: %116 = OpTypeFunction %uint %uint %uint %uint %uint +; CHECK: %uint_48 = OpConstant %uint 48 +; CHECK: %141 = OpConstantNull %uint +; CHECK: %uint_54 = OpConstant %uint 54 +)"; + // clang-format on + + const std::string main_func = R"( %main = OpFunction %void None %3 %5 = OpLabel %19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 @@ -5276,211 +3239,69 @@ OpDecorate %89 BuiltIn LaunchIdNV %29 = OpCompositeExtract %float %27 0 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 OpStore %31 %29 +; CHECK-NOT: OpStore %31 %29 +; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0 +; CHECK: %134 = OpULessThan %bool %uint_0 %133 +; CHECK: OpSelectionMerge %135 None +; CHECK: OpBranchConditional %134 %136 %137 +; CHECK: %136 = OpLabel +; CHECK: %138 = OpLoad %uint %25 +; CHECK: OpBranch %135 +; CHECK: %137 = OpLabel +; CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0 +; CHECK: OpBranch %135 +; CHECK: %135 = OpLabel +; CHECK: %142 = OpPhi %uint %138 %136 %141 %137 +; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142 +; CHECK: %28 = OpLoad %13 %27 +; CHECK: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1 +; CHECK: %50 = OpULessThan %bool %142 %48 +; CHECK: OpSelectionMerge %51 None +; CHECK: OpBranchConditional %50 %52 %53 +; CHECK: %52 = OpLabel +; CHECK: %54 = OpLoad %13 %27 +; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142 +; CHECK: %144 = OpULessThan %bool %uint_0 %143 +; CHECK: OpSelectionMerge %145 None +; CHECK: OpBranchConditional %144 %146 %147 +; CHECK: %146 = OpLabel +; CHECK: %148 = OpLoad %13 %27 +; CHECK: %149 = OpImageRead %v4float %148 %20 +; CHECK: OpBranch %145 +; CHECK: %147 = OpLabel +; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0 +; CHECK: OpBranch %145 +; CHECK: %145 = OpLabel +; CHECK: %151 = OpPhi %v4float %149 %146 %113 %147 +; CHECK: OpBranch %51 +; CHECK: %53 = OpLabel +; CHECK: %112 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48 +; CHECK: OpBranch %51 +; CHECK: %51 = OpLabel +; CHECK: %114 = OpPhi %v4float %151 %145 %113 %53 +; CHECK: %30 = OpCompositeExtract %float %114 0 +; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +; CHECK: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0 +; CHECK: %153 = OpULessThan %bool %uint_0 %152 +; CHECK: OpSelectionMerge %154 None +; CHECK: OpBranchConditional %153 %155 %156 +; CHECK: %155 = OpLabel +; CHECK: OpStore %31 %30 +; CHECK: OpBranch %154 +; CHECK: %156 = OpLabel +; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0 +; CHECK: OpBranch %154 +; CHECK: %154 = OpLabel OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%7 = OpTypeFunction %void -%uint = OpTypeInt 32 0 -%float = OpTypeFloat 32 -%StorageBuffer = OpTypeStruct %uint %float -%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer -%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform -%int = OpTypeInt 32 1 -%int_1 = OpConstant %int 1 -%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f -%_runtimearr_13 = OpTypeRuntimeArray %13 -%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 -%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant -%int_0 = OpConstant %int 0 -%_ptr_Uniform_uint = OpTypePointer Uniform %uint -%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 -%v2int = OpTypeVector %int 2 -%20 = OpConstantComposite %v2int %int_0 %int_0 -%v4float = OpTypeVector %float 4 -%uint_0 = OpConstant %uint 0 -%_ptr_Uniform_float = OpTypePointer Uniform %float -%uint_1 = OpConstant %uint 1 -%34 = OpTypeFunction %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_39 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39 -%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%bool = OpTypeBool -%57 = OpTypeFunction %void %uint %uint %uint %uint -%_struct_63 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63 -%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_5314 = OpConstant %uint 5314 -%uint_3 = OpConstant %uint 3 -%v3uint = OpTypeVector %uint 3 -%_ptr_Input_v3uint = OpTypePointer Input %v3uint -%89 = OpVariable %_ptr_Input_v3uint Input -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_51 = OpConstant %uint 51 -%113 = OpConstantNull %v4float -%116 = OpTypeFunction %uint %uint %uint %uint %uint -%uint_48 = OpConstant %uint 48 -%141 = OpConstantNull %uint -%uint_54 = OpConstant %uint 54 -%main = OpFunction %void None %7 -%24 = OpLabel -%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 -%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 -%134 = OpULessThan %bool %uint_0 %133 -OpSelectionMerge %135 None -OpBranchConditional %134 %136 %137 -%136 = OpLabel -%138 = OpLoad %uint %25 -OpBranch %135 -%137 = OpLabel -%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0 -OpBranch %135 -%135 = OpLabel -%142 = OpPhi %uint %138 %136 %141 %137 -%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142 -%28 = OpLoad %13 %27 -%48 = OpFunctionCall %uint %33 %uint_1 %uint_1 -%50 = OpULessThan %bool %142 %48 -OpSelectionMerge %51 None -OpBranchConditional %50 %52 %53 -%52 = OpLabel -%54 = OpLoad %13 %27 -%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142 -%144 = OpULessThan %bool %uint_0 %143 -OpSelectionMerge %145 None -OpBranchConditional %144 %146 %147 -%146 = OpLabel -%148 = OpLoad %13 %27 -%149 = OpImageRead %v4float %148 %20 -OpBranch %145 -%147 = OpLabel -%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0 -OpBranch %145 -%145 = OpLabel -%151 = OpPhi %v4float %149 %146 %113 %147 -OpBranch %51 -%53 = OpLabel -%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48 -OpBranch %51 -%51 = OpLabel -%114 = OpPhi %v4float %151 %145 %113 %53 -%30 = OpCompositeExtract %float %114 0 -%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 -%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 -%153 = OpULessThan %bool %uint_0 %152 -OpSelectionMerge %154 None -OpBranchConditional %153 %155 %156 -%155 = OpLabel -OpStore %31 %30 -OpBranch %154 -%156 = OpLabel -%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0 -OpBranch %154 -%154 = OpLabel -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%33 = OpFunction %uint None %34 -%35 = OpFunctionParameter %uint -%36 = OpFunctionParameter %uint -%37 = OpLabel -%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35 -%44 = OpLoad %uint %43 -%45 = OpIAdd %uint %44 %36 -%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45 -%47 = OpLoad %uint %46 -OpReturnValue %47 -OpFunctionEnd -%56 = OpFunction %void None %57 -%58 = OpFunctionParameter %uint -%59 = OpFunctionParameter %uint -%60 = OpFunctionParameter %uint -%61 = OpFunctionParameter %uint -%62 = OpLabel -%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0 -%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10 -%70 = OpIAdd %uint %69 %uint_10 -%71 = OpArrayLength %uint %65 1 -%72 = OpULessThanEqual %bool %70 %71 -OpSelectionMerge %73 None -OpBranchConditional %72 %74 %73 -%74 = OpLabel -%75 = OpIAdd %uint %69 %uint_0 -%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75 -OpStore %76 %uint_10 -%78 = OpIAdd %uint %69 %uint_1 -%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78 -OpStore %79 %uint_23 -%81 = OpIAdd %uint %69 %uint_2 -%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81 -OpStore %82 %58 -%85 = OpIAdd %uint %69 %uint_3 -%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85 -OpStore %86 %uint_5314 -%90 = OpLoad %v3uint %89 -%91 = OpCompositeExtract %uint %90 0 -%92 = OpCompositeExtract %uint %90 1 -%93 = OpCompositeExtract %uint %90 2 -%94 = OpIAdd %uint %69 %uint_4 -%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94 -OpStore %95 %91 -%97 = OpIAdd %uint %69 %uint_5 -%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97 -OpStore %98 %92 -%100 = OpIAdd %uint %69 %uint_6 -%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100 -OpStore %101 %93 -%103 = OpIAdd %uint %69 %uint_7 -%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103 -OpStore %104 %59 -%106 = OpIAdd %uint %69 %uint_8 -%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106 -OpStore %107 %60 -%109 = OpIAdd %uint %69 %uint_9 -%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109 -OpStore %110 %61 -OpBranch %73 -%73 = OpLabel -OpReturn -OpFunctionEnd -%115 = OpFunction %uint None %116 -%117 = OpFunctionParameter %uint -%118 = OpFunctionParameter %uint -%119 = OpFunctionParameter %uint -%120 = OpFunctionParameter %uint -%121 = OpLabel -%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117 -%123 = OpLoad %uint %122 -%124 = OpIAdd %uint %123 %118 -%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124 -%126 = OpLoad %uint %125 -%127 = OpIAdd %uint %126 %119 -%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127 -%129 = OpLoad %uint %128 -%130 = OpIAdd %uint %129 %120 -%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130 -%132 = OpLoad %uint %131 -OpReturnValue %132 -OpFunctionEnd -)"; + const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4; // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, false, false, false); + SinglePassRunAndMatch(defs + main_func + new_funcs, + true, 7u, 23u, true, true, false, + false, false); } TEST_F(InstBindlessTest, @@ -5501,14 +3322,17 @@ TEST_F(InstBindlessTest, // sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r; // } - const std::string defs_before = - R"(OpCapability RuntimeDescriptorArray + // clang-format off + const std::string defs = R"( +OpCapability RuntimeDescriptorArray OpCapability RayTracingNV OpExtension "SPV_EXT_descriptor_indexing" OpExtension "SPV_NV_ray_tracing" +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint AnyHitNV %main "main" +; CHECK: OpEntryPoint AnyHitNV %main "main" %89 OpSource GLSL 460 OpSourceExtension "GL_EXT_nonuniform_qualifier" OpSourceExtension "GL_NV_ray_tracing" @@ -5526,51 +3350,11 @@ OpDecorate %sbo Binding 0 OpDecorate %images DescriptorSet 0 OpDecorate %images Binding 1 OpDecorate %images NonWritable +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kInputDecorations + kOutputDecorations + R"( +; CHECK: OpDecorate %89 BuiltIn LaunchIdNV %void = OpTypeVoid -)"; - - const std::string defs_after = - R"(OpCapability RuntimeDescriptorArray -OpCapability RayTracingNV -OpExtension "SPV_EXT_descriptor_indexing" -OpExtension "SPV_NV_ray_tracing" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint AnyHitNV %main "main" %89 -OpSource GLSL 460 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpSourceExtension "GL_NV_ray_tracing" -OpName %main "main" -OpName %StorageBuffer "StorageBuffer" -OpMemberName %StorageBuffer 0 "index" -OpMemberName %StorageBuffer 1 "red" -OpName %sbo "sbo" -OpName %images "images" -OpMemberDecorate %StorageBuffer 0 Offset 0 -OpMemberDecorate %StorageBuffer 1 Offset 4 -OpDecorate %StorageBuffer BufferBlock -OpDecorate %sbo DescriptorSet 0 -OpDecorate %sbo Binding 0 -OpDecorate %images DescriptorSet 0 -OpDecorate %images Binding 1 -OpDecorate %images NonWritable -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_39 Block -OpMemberDecorate %_struct_39 0 Offset 0 -OpDecorate %41 DescriptorSet 7 -OpDecorate %41 Binding 1 -OpDecorate %_struct_63 Block -OpMemberDecorate %_struct_63 0 Offset 0 -OpMemberDecorate %_struct_63 1 Offset 4 -OpDecorate %65 DescriptorSet 7 -OpDecorate %65 Binding 0 -OpDecorate %89 BuiltIn LaunchIdNV -%void = OpTypeVoid -)"; - - const std::string func_before = - R"(%3 = OpTypeFunction %void +%3 = OpTypeFunction %void %uint = OpTypeInt 32 0 %float = OpTypeFloat 32 %StorageBuffer = OpTypeStruct %uint %float @@ -5590,6 +3374,38 @@ OpDecorate %89 BuiltIn LaunchIdNV %v4float = OpTypeVector %float 4 %uint_0 = OpConstant %uint 0 %_ptr_Uniform_float = OpTypePointer Uniform %float +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %34 = OpTypeFunction %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kInputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %bool = OpTypeBool +; CHECK: %57 = OpTypeFunction %void %uint %uint %uint %uint +)" + kOutputGlobals + R"( +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %uint_5315 = OpConstant %uint 5315 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %v3uint = OpTypeVector %uint 3 +; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint +; CHECK: %89 = OpVariable %_ptr_Input_v3uint Input +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_6 = OpConstant %uint 6 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_51 = OpConstant %uint 51 +; CHECK: %113 = OpConstantNull %v4float +; CHECK: %116 = OpTypeFunction %uint %uint %uint %uint %uint +; CHECK: %uint_48 = OpConstant %uint 48 +; CHECK: %141 = OpConstantNull %uint +; CHECK: %uint_54 = OpConstant %uint 54 +)"; + // clang-format on + + const std::string main_func = R"( %main = OpFunction %void None %3 %5 = OpLabel %19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 @@ -5600,211 +3416,69 @@ OpDecorate %89 BuiltIn LaunchIdNV %29 = OpCompositeExtract %float %27 0 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 OpStore %31 %29 +; CHECK-NOT: OpStore %31 %29 +; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0 +; CHECK: %134 = OpULessThan %bool %uint_0 %133 +; CHECK: OpSelectionMerge %135 None +; CHECK: OpBranchConditional %134 %136 %137 +; CHECK: %136 = OpLabel +; CHECK: %138 = OpLoad %uint %25 +; CHECK: OpBranch %135 +; CHECK: %137 = OpLabel +; CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0 +; CHECK: OpBranch %135 +; CHECK: %135 = OpLabel +; CHECK: %142 = OpPhi %uint %138 %136 %141 %137 +; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142 +; CHECK: %28 = OpLoad %13 %27 +; CHECK: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1 +; CHECK: %50 = OpULessThan %bool %142 %48 +; CHECK: OpSelectionMerge %51 None +; CHECK: OpBranchConditional %50 %52 %53 +; CHECK: %52 = OpLabel +; CHECK: %54 = OpLoad %13 %27 +; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142 +; CHECK: %144 = OpULessThan %bool %uint_0 %143 +; CHECK: OpSelectionMerge %145 None +; CHECK: OpBranchConditional %144 %146 %147 +; CHECK: %146 = OpLabel +; CHECK: %148 = OpLoad %13 %27 +; CHECK: %149 = OpImageRead %v4float %148 %20 +; CHECK: OpBranch %145 +; CHECK: %147 = OpLabel +; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0 +; CHECK: OpBranch %145 +; CHECK: %145 = OpLabel +; CHECK: %151 = OpPhi %v4float %149 %146 %113 %147 +; CHECK: OpBranch %51 +; CHECK: %53 = OpLabel +; CHECK: %112 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48 +; CHECK: OpBranch %51 +; CHECK: %51 = OpLabel +; CHECK: %114 = OpPhi %v4float %151 %145 %113 %53 +; CHECK: %30 = OpCompositeExtract %float %114 0 +; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +; CHECK: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0 +; CHECK: %153 = OpULessThan %bool %uint_0 %152 +; CHECK: OpSelectionMerge %154 None +; CHECK: OpBranchConditional %153 %155 %156 +; CHECK: %155 = OpLabel +; CHECK: OpStore %31 %30 +; CHECK: OpBranch %154 +; CHECK: %156 = OpLabel +; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0 +; CHECK: OpBranch %154 +; CHECK: %154 = OpLabel OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%7 = OpTypeFunction %void -%uint = OpTypeInt 32 0 -%float = OpTypeFloat 32 -%StorageBuffer = OpTypeStruct %uint %float -%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer -%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform -%int = OpTypeInt 32 1 -%int_1 = OpConstant %int 1 -%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f -%_runtimearr_13 = OpTypeRuntimeArray %13 -%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 -%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant -%int_0 = OpConstant %int 0 -%_ptr_Uniform_uint = OpTypePointer Uniform %uint -%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 -%v2int = OpTypeVector %int 2 -%20 = OpConstantComposite %v2int %int_0 %int_0 -%v4float = OpTypeVector %float 4 -%uint_0 = OpConstant %uint 0 -%_ptr_Uniform_float = OpTypePointer Uniform %float -%uint_1 = OpConstant %uint 1 -%34 = OpTypeFunction %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_39 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39 -%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%bool = OpTypeBool -%57 = OpTypeFunction %void %uint %uint %uint %uint -%_struct_63 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63 -%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_5315 = OpConstant %uint 5315 -%uint_3 = OpConstant %uint 3 -%v3uint = OpTypeVector %uint 3 -%_ptr_Input_v3uint = OpTypePointer Input %v3uint -%89 = OpVariable %_ptr_Input_v3uint Input -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_51 = OpConstant %uint 51 -%113 = OpConstantNull %v4float -%116 = OpTypeFunction %uint %uint %uint %uint %uint -%uint_48 = OpConstant %uint 48 -%141 = OpConstantNull %uint -%uint_54 = OpConstant %uint 54 -%main = OpFunction %void None %7 -%24 = OpLabel -%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 -%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 -%134 = OpULessThan %bool %uint_0 %133 -OpSelectionMerge %135 None -OpBranchConditional %134 %136 %137 -%136 = OpLabel -%138 = OpLoad %uint %25 -OpBranch %135 -%137 = OpLabel -%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0 -OpBranch %135 -%135 = OpLabel -%142 = OpPhi %uint %138 %136 %141 %137 -%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142 -%28 = OpLoad %13 %27 -%48 = OpFunctionCall %uint %33 %uint_1 %uint_1 -%50 = OpULessThan %bool %142 %48 -OpSelectionMerge %51 None -OpBranchConditional %50 %52 %53 -%52 = OpLabel -%54 = OpLoad %13 %27 -%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142 -%144 = OpULessThan %bool %uint_0 %143 -OpSelectionMerge %145 None -OpBranchConditional %144 %146 %147 -%146 = OpLabel -%148 = OpLoad %13 %27 -%149 = OpImageRead %v4float %148 %20 -OpBranch %145 -%147 = OpLabel -%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0 -OpBranch %145 -%145 = OpLabel -%151 = OpPhi %v4float %149 %146 %113 %147 -OpBranch %51 -%53 = OpLabel -%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48 -OpBranch %51 -%51 = OpLabel -%114 = OpPhi %v4float %151 %145 %113 %53 -%30 = OpCompositeExtract %float %114 0 -%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 -%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 -%153 = OpULessThan %bool %uint_0 %152 -OpSelectionMerge %154 None -OpBranchConditional %153 %155 %156 -%155 = OpLabel -OpStore %31 %30 -OpBranch %154 -%156 = OpLabel -%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0 -OpBranch %154 -%154 = OpLabel -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%33 = OpFunction %uint None %34 -%35 = OpFunctionParameter %uint -%36 = OpFunctionParameter %uint -%37 = OpLabel -%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35 -%44 = OpLoad %uint %43 -%45 = OpIAdd %uint %44 %36 -%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45 -%47 = OpLoad %uint %46 -OpReturnValue %47 -OpFunctionEnd -%56 = OpFunction %void None %57 -%58 = OpFunctionParameter %uint -%59 = OpFunctionParameter %uint -%60 = OpFunctionParameter %uint -%61 = OpFunctionParameter %uint -%62 = OpLabel -%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0 -%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10 -%70 = OpIAdd %uint %69 %uint_10 -%71 = OpArrayLength %uint %65 1 -%72 = OpULessThanEqual %bool %70 %71 -OpSelectionMerge %73 None -OpBranchConditional %72 %74 %73 -%74 = OpLabel -%75 = OpIAdd %uint %69 %uint_0 -%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75 -OpStore %76 %uint_10 -%78 = OpIAdd %uint %69 %uint_1 -%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78 -OpStore %79 %uint_23 -%81 = OpIAdd %uint %69 %uint_2 -%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81 -OpStore %82 %58 -%85 = OpIAdd %uint %69 %uint_3 -%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85 -OpStore %86 %uint_5315 -%90 = OpLoad %v3uint %89 -%91 = OpCompositeExtract %uint %90 0 -%92 = OpCompositeExtract %uint %90 1 -%93 = OpCompositeExtract %uint %90 2 -%94 = OpIAdd %uint %69 %uint_4 -%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94 -OpStore %95 %91 -%97 = OpIAdd %uint %69 %uint_5 -%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97 -OpStore %98 %92 -%100 = OpIAdd %uint %69 %uint_6 -%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100 -OpStore %101 %93 -%103 = OpIAdd %uint %69 %uint_7 -%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103 -OpStore %104 %59 -%106 = OpIAdd %uint %69 %uint_8 -%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106 -OpStore %107 %60 -%109 = OpIAdd %uint %69 %uint_9 -%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109 -OpStore %110 %61 -OpBranch %73 -%73 = OpLabel -OpReturn -OpFunctionEnd -%115 = OpFunction %uint None %116 -%117 = OpFunctionParameter %uint -%118 = OpFunctionParameter %uint -%119 = OpFunctionParameter %uint -%120 = OpFunctionParameter %uint -%121 = OpLabel -%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117 -%123 = OpLoad %uint %122 -%124 = OpIAdd %uint %123 %118 -%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124 -%126 = OpLoad %uint %125 -%127 = OpIAdd %uint %126 %119 -%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127 -%129 = OpLoad %uint %128 -%130 = OpIAdd %uint %129 %120 -%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130 -%132 = OpLoad %uint %131 -OpReturnValue %132 -OpFunctionEnd -)"; + const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4; // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, false, false, false); + SinglePassRunAndMatch(defs + main_func + new_funcs, + true, 7u, 23u, true, true, false, + false, false); } TEST_F(InstBindlessTest, @@ -5825,14 +3499,17 @@ TEST_F(InstBindlessTest, // sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r; // } - const std::string defs_before = - R"(OpCapability RuntimeDescriptorArray + // clang-format off + const std::string defs = R"( +OpCapability RuntimeDescriptorArray OpCapability RayTracingNV OpExtension "SPV_EXT_descriptor_indexing" OpExtension "SPV_NV_ray_tracing" +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint ClosestHitNV %main "main" +; CHECK: OpEntryPoint ClosestHitNV %main "main" %89 OpSource GLSL 460 OpSourceExtension "GL_EXT_nonuniform_qualifier" OpSourceExtension "GL_NV_ray_tracing" @@ -5850,51 +3527,11 @@ OpDecorate %sbo Binding 0 OpDecorate %images DescriptorSet 0 OpDecorate %images Binding 1 OpDecorate %images NonWritable +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kInputDecorations + kOutputDecorations + R"( +; CHECK: OpDecorate %89 BuiltIn LaunchIdNV %void = OpTypeVoid -)"; - - const std::string defs_after = - R"(OpCapability RuntimeDescriptorArray -OpCapability RayTracingNV -OpExtension "SPV_EXT_descriptor_indexing" -OpExtension "SPV_NV_ray_tracing" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint ClosestHitNV %main "main" %89 -OpSource GLSL 460 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpSourceExtension "GL_NV_ray_tracing" -OpName %main "main" -OpName %StorageBuffer "StorageBuffer" -OpMemberName %StorageBuffer 0 "index" -OpMemberName %StorageBuffer 1 "red" -OpName %sbo "sbo" -OpName %images "images" -OpMemberDecorate %StorageBuffer 0 Offset 0 -OpMemberDecorate %StorageBuffer 1 Offset 4 -OpDecorate %StorageBuffer BufferBlock -OpDecorate %sbo DescriptorSet 0 -OpDecorate %sbo Binding 0 -OpDecorate %images DescriptorSet 0 -OpDecorate %images Binding 1 -OpDecorate %images NonWritable -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_39 Block -OpMemberDecorate %_struct_39 0 Offset 0 -OpDecorate %41 DescriptorSet 7 -OpDecorate %41 Binding 1 -OpDecorate %_struct_63 Block -OpMemberDecorate %_struct_63 0 Offset 0 -OpMemberDecorate %_struct_63 1 Offset 4 -OpDecorate %65 DescriptorSet 7 -OpDecorate %65 Binding 0 -OpDecorate %89 BuiltIn LaunchIdNV -%void = OpTypeVoid -)"; - - const std::string func_before = - R"(%3 = OpTypeFunction %void +%3 = OpTypeFunction %void %uint = OpTypeInt 32 0 %float = OpTypeFloat 32 %StorageBuffer = OpTypeStruct %uint %float @@ -5914,6 +3551,38 @@ OpDecorate %89 BuiltIn LaunchIdNV %v4float = OpTypeVector %float 4 %uint_0 = OpConstant %uint 0 %_ptr_Uniform_float = OpTypePointer Uniform %float +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %34 = OpTypeFunction %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kInputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %bool = OpTypeBool +; CHECK: %57 = OpTypeFunction %void %uint %uint %uint %uint +)" + kOutputGlobals + R"( +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %uint_5316 = OpConstant %uint 5316 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %v3uint = OpTypeVector %uint 3 +; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint +; CHECK: %89 = OpVariable %_ptr_Input_v3uint Input +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_6 = OpConstant %uint 6 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_51 = OpConstant %uint 51 +; CHECK: %113 = OpConstantNull %v4float +; CHECK: %116 = OpTypeFunction %uint %uint %uint %uint %uint +; CHECK: %uint_48 = OpConstant %uint 48 +; CHECK: %141 = OpConstantNull %uint +; CHECK: %uint_54 = OpConstant %uint 54 +)"; + // clang-format on + + const std::string main_func = R"( %main = OpFunction %void None %3 %5 = OpLabel %19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 @@ -5924,211 +3593,69 @@ OpDecorate %89 BuiltIn LaunchIdNV %29 = OpCompositeExtract %float %27 0 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 OpStore %31 %29 +; CHECK-NOT: OpStore %31 %29 +; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0 +; CHECK: %134 = OpULessThan %bool %uint_0 %133 +; CHECK: OpSelectionMerge %135 None +; CHECK: OpBranchConditional %134 %136 %137 +; CHECK: %136 = OpLabel +; CHECK: %138 = OpLoad %uint %25 +; CHECK: OpBranch %135 +; CHECK: %137 = OpLabel +; CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0 +; CHECK: OpBranch %135 +; CHECK: %135 = OpLabel +; CHECK: %142 = OpPhi %uint %138 %136 %141 %137 +; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142 +; CHECK: %28 = OpLoad %13 %27 +; CHECK: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1 +; CHECK: %50 = OpULessThan %bool %142 %48 +; CHECK: OpSelectionMerge %51 None +; CHECK: OpBranchConditional %50 %52 %53 +; CHECK: %52 = OpLabel +; CHECK: %54 = OpLoad %13 %27 +; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142 +; CHECK: %144 = OpULessThan %bool %uint_0 %143 +; CHECK: OpSelectionMerge %145 None +; CHECK: OpBranchConditional %144 %146 %147 +; CHECK: %146 = OpLabel +; CHECK: %148 = OpLoad %13 %27 +; CHECK: %149 = OpImageRead %v4float %148 %20 +; CHECK: OpBranch %145 +; CHECK: %147 = OpLabel +; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0 +; CHECK: OpBranch %145 +; CHECK: %145 = OpLabel +; CHECK: %151 = OpPhi %v4float %149 %146 %113 %147 +; CHECK: OpBranch %51 +; CHECK: %53 = OpLabel +; CHECK: %112 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48 +; CHECK: OpBranch %51 +; CHECK: %51 = OpLabel +; CHECK: %114 = OpPhi %v4float %151 %145 %113 %53 +; CHECK: %30 = OpCompositeExtract %float %114 0 +; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +; CHECK: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0 +; CHECK: %153 = OpULessThan %bool %uint_0 %152 +; CHECK: OpSelectionMerge %154 None +; CHECK: OpBranchConditional %153 %155 %156 +; CHECK: %155 = OpLabel +; CHECK: OpStore %31 %30 +; CHECK: OpBranch %154 +; CHECK: %156 = OpLabel +; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0 +; CHECK: OpBranch %154 +; CHECK: %154 = OpLabel OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%7 = OpTypeFunction %void -%uint = OpTypeInt 32 0 -%float = OpTypeFloat 32 -%StorageBuffer = OpTypeStruct %uint %float -%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer -%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform -%int = OpTypeInt 32 1 -%int_1 = OpConstant %int 1 -%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f -%_runtimearr_13 = OpTypeRuntimeArray %13 -%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 -%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant -%int_0 = OpConstant %int 0 -%_ptr_Uniform_uint = OpTypePointer Uniform %uint -%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 -%v2int = OpTypeVector %int 2 -%20 = OpConstantComposite %v2int %int_0 %int_0 -%v4float = OpTypeVector %float 4 -%uint_0 = OpConstant %uint 0 -%_ptr_Uniform_float = OpTypePointer Uniform %float -%uint_1 = OpConstant %uint 1 -%34 = OpTypeFunction %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_39 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39 -%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%bool = OpTypeBool -%57 = OpTypeFunction %void %uint %uint %uint %uint -%_struct_63 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63 -%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_5316 = OpConstant %uint 5316 -%uint_3 = OpConstant %uint 3 -%v3uint = OpTypeVector %uint 3 -%_ptr_Input_v3uint = OpTypePointer Input %v3uint -%89 = OpVariable %_ptr_Input_v3uint Input -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_51 = OpConstant %uint 51 -%113 = OpConstantNull %v4float -%116 = OpTypeFunction %uint %uint %uint %uint %uint -%uint_48 = OpConstant %uint 48 -%141 = OpConstantNull %uint -%uint_54 = OpConstant %uint 54 -%main = OpFunction %void None %7 -%24 = OpLabel -%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 -%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 -%134 = OpULessThan %bool %uint_0 %133 -OpSelectionMerge %135 None -OpBranchConditional %134 %136 %137 -%136 = OpLabel -%138 = OpLoad %uint %25 -OpBranch %135 -%137 = OpLabel -%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0 -OpBranch %135 -%135 = OpLabel -%142 = OpPhi %uint %138 %136 %141 %137 -%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142 -%28 = OpLoad %13 %27 -%48 = OpFunctionCall %uint %33 %uint_1 %uint_1 -%50 = OpULessThan %bool %142 %48 -OpSelectionMerge %51 None -OpBranchConditional %50 %52 %53 -%52 = OpLabel -%54 = OpLoad %13 %27 -%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142 -%144 = OpULessThan %bool %uint_0 %143 -OpSelectionMerge %145 None -OpBranchConditional %144 %146 %147 -%146 = OpLabel -%148 = OpLoad %13 %27 -%149 = OpImageRead %v4float %148 %20 -OpBranch %145 -%147 = OpLabel -%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0 -OpBranch %145 -%145 = OpLabel -%151 = OpPhi %v4float %149 %146 %113 %147 -OpBranch %51 -%53 = OpLabel -%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48 -OpBranch %51 -%51 = OpLabel -%114 = OpPhi %v4float %151 %145 %113 %53 -%30 = OpCompositeExtract %float %114 0 -%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 -%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 -%153 = OpULessThan %bool %uint_0 %152 -OpSelectionMerge %154 None -OpBranchConditional %153 %155 %156 -%155 = OpLabel -OpStore %31 %30 -OpBranch %154 -%156 = OpLabel -%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0 -OpBranch %154 -%154 = OpLabel -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%33 = OpFunction %uint None %34 -%35 = OpFunctionParameter %uint -%36 = OpFunctionParameter %uint -%37 = OpLabel -%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35 -%44 = OpLoad %uint %43 -%45 = OpIAdd %uint %44 %36 -%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45 -%47 = OpLoad %uint %46 -OpReturnValue %47 -OpFunctionEnd -%56 = OpFunction %void None %57 -%58 = OpFunctionParameter %uint -%59 = OpFunctionParameter %uint -%60 = OpFunctionParameter %uint -%61 = OpFunctionParameter %uint -%62 = OpLabel -%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0 -%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10 -%70 = OpIAdd %uint %69 %uint_10 -%71 = OpArrayLength %uint %65 1 -%72 = OpULessThanEqual %bool %70 %71 -OpSelectionMerge %73 None -OpBranchConditional %72 %74 %73 -%74 = OpLabel -%75 = OpIAdd %uint %69 %uint_0 -%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75 -OpStore %76 %uint_10 -%78 = OpIAdd %uint %69 %uint_1 -%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78 -OpStore %79 %uint_23 -%81 = OpIAdd %uint %69 %uint_2 -%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81 -OpStore %82 %58 -%85 = OpIAdd %uint %69 %uint_3 -%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85 -OpStore %86 %uint_5316 -%90 = OpLoad %v3uint %89 -%91 = OpCompositeExtract %uint %90 0 -%92 = OpCompositeExtract %uint %90 1 -%93 = OpCompositeExtract %uint %90 2 -%94 = OpIAdd %uint %69 %uint_4 -%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94 -OpStore %95 %91 -%97 = OpIAdd %uint %69 %uint_5 -%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97 -OpStore %98 %92 -%100 = OpIAdd %uint %69 %uint_6 -%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100 -OpStore %101 %93 -%103 = OpIAdd %uint %69 %uint_7 -%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103 -OpStore %104 %59 -%106 = OpIAdd %uint %69 %uint_8 -%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106 -OpStore %107 %60 -%109 = OpIAdd %uint %69 %uint_9 -%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109 -OpStore %110 %61 -OpBranch %73 -%73 = OpLabel -OpReturn -OpFunctionEnd -%115 = OpFunction %uint None %116 -%117 = OpFunctionParameter %uint -%118 = OpFunctionParameter %uint -%119 = OpFunctionParameter %uint -%120 = OpFunctionParameter %uint -%121 = OpLabel -%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117 -%123 = OpLoad %uint %122 -%124 = OpIAdd %uint %123 %118 -%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124 -%126 = OpLoad %uint %125 -%127 = OpIAdd %uint %126 %119 -%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127 -%129 = OpLoad %uint %128 -%130 = OpIAdd %uint %129 %120 -%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130 -%132 = OpLoad %uint %131 -OpReturnValue %132 -OpFunctionEnd -)"; + const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4; // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, false, false, false); + SinglePassRunAndMatch(defs + main_func + new_funcs, + true, 7u, 23u, true, true, false, + false, false); } TEST_F(InstBindlessTest, @@ -6149,14 +3676,17 @@ TEST_F(InstBindlessTest, // sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r; // } - const std::string defs_before = - R"(OpCapability RuntimeDescriptorArray + // clang-format off + const std::string defs = R"( +OpCapability RuntimeDescriptorArray OpCapability RayTracingNV OpExtension "SPV_EXT_descriptor_indexing" OpExtension "SPV_NV_ray_tracing" +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint MissNV %main "main" +; CHECK: OpEntryPoint MissNV %main "main" %89 OpSource GLSL 460 OpSourceExtension "GL_EXT_nonuniform_qualifier" OpSourceExtension "GL_NV_ray_tracing" @@ -6174,51 +3704,11 @@ OpDecorate %sbo Binding 0 OpDecorate %images DescriptorSet 0 OpDecorate %images Binding 1 OpDecorate %images NonWritable +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kInputDecorations + kOutputDecorations + R"( +; CHECK: OpDecorate %89 BuiltIn LaunchIdNV %void = OpTypeVoid -)"; - - const std::string defs_after = - R"(OpCapability RuntimeDescriptorArray -OpCapability RayTracingNV -OpExtension "SPV_EXT_descriptor_indexing" -OpExtension "SPV_NV_ray_tracing" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint MissNV %main "main" %89 -OpSource GLSL 460 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpSourceExtension "GL_NV_ray_tracing" -OpName %main "main" -OpName %StorageBuffer "StorageBuffer" -OpMemberName %StorageBuffer 0 "index" -OpMemberName %StorageBuffer 1 "red" -OpName %sbo "sbo" -OpName %images "images" -OpMemberDecorate %StorageBuffer 0 Offset 0 -OpMemberDecorate %StorageBuffer 1 Offset 4 -OpDecorate %StorageBuffer BufferBlock -OpDecorate %sbo DescriptorSet 0 -OpDecorate %sbo Binding 0 -OpDecorate %images DescriptorSet 0 -OpDecorate %images Binding 1 -OpDecorate %images NonWritable -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_39 Block -OpMemberDecorate %_struct_39 0 Offset 0 -OpDecorate %41 DescriptorSet 7 -OpDecorate %41 Binding 1 -OpDecorate %_struct_63 Block -OpMemberDecorate %_struct_63 0 Offset 0 -OpMemberDecorate %_struct_63 1 Offset 4 -OpDecorate %65 DescriptorSet 7 -OpDecorate %65 Binding 0 -OpDecorate %89 BuiltIn LaunchIdNV -%void = OpTypeVoid -)"; - - const std::string func_before = - R"(%3 = OpTypeFunction %void +%3 = OpTypeFunction %void %uint = OpTypeInt 32 0 %float = OpTypeFloat 32 %StorageBuffer = OpTypeStruct %uint %float @@ -6238,6 +3728,38 @@ OpDecorate %89 BuiltIn LaunchIdNV %v4float = OpTypeVector %float 4 %uint_0 = OpConstant %uint 0 %_ptr_Uniform_float = OpTypePointer Uniform %float +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %34 = OpTypeFunction %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kInputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %bool = OpTypeBool +; CHECK: %57 = OpTypeFunction %void %uint %uint %uint %uint +)" + kOutputGlobals + R"( +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %uint_5317 = OpConstant %uint 5317 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %v3uint = OpTypeVector %uint 3 +; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint +; CHECK: %89 = OpVariable %_ptr_Input_v3uint Input +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_6 = OpConstant %uint 6 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_51 = OpConstant %uint 51 +; CHECK: %113 = OpConstantNull %v4float +; CHECK: %116 = OpTypeFunction %uint %uint %uint %uint %uint +; CHECK: %uint_48 = OpConstant %uint 48 +; CHECK: %141 = OpConstantNull %uint +; CHECK: %uint_54 = OpConstant %uint 54 +)"; + // clang-format on + + const std::string main_func = R"( %main = OpFunction %void None %3 %5 = OpLabel %19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 @@ -6248,211 +3770,69 @@ OpDecorate %89 BuiltIn LaunchIdNV %29 = OpCompositeExtract %float %27 0 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 OpStore %31 %29 +; CHECK-NOT OpStore %31 %29 +; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0 +; CHECK: %134 = OpULessThan %bool %uint_0 %133 +; CHECK: OpSelectionMerge %135 None +; CHECK: OpBranchConditional %134 %136 %137 +; CHECK: %136 = OpLabel +; CHECK: %138 = OpLoad %uint %25 +; CHECK: OpBranch %135 +; CHECK: %137 = OpLabel +; CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0 +; CHECK: OpBranch %135 +; CHECK: %135 = OpLabel +; CHECK: %142 = OpPhi %uint %138 %136 %141 %137 +; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142 +; CHECK: %28 = OpLoad %13 %27 +; CHECK: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1 +; CHECK: %50 = OpULessThan %bool %142 %48 +; CHECK: OpSelectionMerge %51 None +; CHECK: OpBranchConditional %50 %52 %53 +; CHECK: %52 = OpLabel +; CHECK: %54 = OpLoad %13 %27 +; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142 +; CHECK: %144 = OpULessThan %bool %uint_0 %143 +; CHECK: OpSelectionMerge %145 None +; CHECK: OpBranchConditional %144 %146 %147 +; CHECK: %146 = OpLabel +; CHECK: %148 = OpLoad %13 %27 +; CHECK: %149 = OpImageRead %v4float %148 %20 +; CHECK: OpBranch %145 +; CHECK: %147 = OpLabel +; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0 +; CHECK: OpBranch %145 +; CHECK: %145 = OpLabel +; CHECK: %151 = OpPhi %v4float %149 %146 %113 %147 +; CHECK: OpBranch %51 +; CHECK: %53 = OpLabel +; CHECK: %112 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48 +; CHECK: OpBranch %51 +; CHECK: %51 = OpLabel +; CHECK: %114 = OpPhi %v4float %151 %145 %113 %53 +; CHECK: %30 = OpCompositeExtract %float %114 0 +; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +; CHECK: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0 +; CHECK: %153 = OpULessThan %bool %uint_0 %152 +; CHECK: OpSelectionMerge %154 None +; CHECK: OpBranchConditional %153 %155 %156 +; CHECK: %155 = OpLabel +; CHECK: OpStore %31 %30 +; CHECK: OpBranch %154 +; CHECK: %156 = OpLabel +; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0 +; CHECK: OpBranch %154 +; CHECK: %154 = OpLabel OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%7 = OpTypeFunction %void -%uint = OpTypeInt 32 0 -%float = OpTypeFloat 32 -%StorageBuffer = OpTypeStruct %uint %float -%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer -%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform -%int = OpTypeInt 32 1 -%int_1 = OpConstant %int 1 -%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f -%_runtimearr_13 = OpTypeRuntimeArray %13 -%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 -%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant -%int_0 = OpConstant %int 0 -%_ptr_Uniform_uint = OpTypePointer Uniform %uint -%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 -%v2int = OpTypeVector %int 2 -%20 = OpConstantComposite %v2int %int_0 %int_0 -%v4float = OpTypeVector %float 4 -%uint_0 = OpConstant %uint 0 -%_ptr_Uniform_float = OpTypePointer Uniform %float -%uint_1 = OpConstant %uint 1 -%34 = OpTypeFunction %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_39 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39 -%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%bool = OpTypeBool -%57 = OpTypeFunction %void %uint %uint %uint %uint -%_struct_63 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63 -%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_5317 = OpConstant %uint 5317 -%uint_3 = OpConstant %uint 3 -%v3uint = OpTypeVector %uint 3 -%_ptr_Input_v3uint = OpTypePointer Input %v3uint -%89 = OpVariable %_ptr_Input_v3uint Input -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_51 = OpConstant %uint 51 -%113 = OpConstantNull %v4float -%116 = OpTypeFunction %uint %uint %uint %uint %uint -%uint_48 = OpConstant %uint 48 -%141 = OpConstantNull %uint -%uint_54 = OpConstant %uint 54 -%main = OpFunction %void None %7 -%24 = OpLabel -%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 -%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 -%134 = OpULessThan %bool %uint_0 %133 -OpSelectionMerge %135 None -OpBranchConditional %134 %136 %137 -%136 = OpLabel -%138 = OpLoad %uint %25 -OpBranch %135 -%137 = OpLabel -%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0 -OpBranch %135 -%135 = OpLabel -%142 = OpPhi %uint %138 %136 %141 %137 -%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142 -%28 = OpLoad %13 %27 -%48 = OpFunctionCall %uint %33 %uint_1 %uint_1 -%50 = OpULessThan %bool %142 %48 -OpSelectionMerge %51 None -OpBranchConditional %50 %52 %53 -%52 = OpLabel -%54 = OpLoad %13 %27 -%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142 -%144 = OpULessThan %bool %uint_0 %143 -OpSelectionMerge %145 None -OpBranchConditional %144 %146 %147 -%146 = OpLabel -%148 = OpLoad %13 %27 -%149 = OpImageRead %v4float %148 %20 -OpBranch %145 -%147 = OpLabel -%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0 -OpBranch %145 -%145 = OpLabel -%151 = OpPhi %v4float %149 %146 %113 %147 -OpBranch %51 -%53 = OpLabel -%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48 -OpBranch %51 -%51 = OpLabel -%114 = OpPhi %v4float %151 %145 %113 %53 -%30 = OpCompositeExtract %float %114 0 -%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 -%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 -%153 = OpULessThan %bool %uint_0 %152 -OpSelectionMerge %154 None -OpBranchConditional %153 %155 %156 -%155 = OpLabel -OpStore %31 %30 -OpBranch %154 -%156 = OpLabel -%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0 -OpBranch %154 -%154 = OpLabel -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%33 = OpFunction %uint None %34 -%35 = OpFunctionParameter %uint -%36 = OpFunctionParameter %uint -%37 = OpLabel -%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35 -%44 = OpLoad %uint %43 -%45 = OpIAdd %uint %44 %36 -%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45 -%47 = OpLoad %uint %46 -OpReturnValue %47 -OpFunctionEnd -%56 = OpFunction %void None %57 -%58 = OpFunctionParameter %uint -%59 = OpFunctionParameter %uint -%60 = OpFunctionParameter %uint -%61 = OpFunctionParameter %uint -%62 = OpLabel -%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0 -%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10 -%70 = OpIAdd %uint %69 %uint_10 -%71 = OpArrayLength %uint %65 1 -%72 = OpULessThanEqual %bool %70 %71 -OpSelectionMerge %73 None -OpBranchConditional %72 %74 %73 -%74 = OpLabel -%75 = OpIAdd %uint %69 %uint_0 -%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75 -OpStore %76 %uint_10 -%78 = OpIAdd %uint %69 %uint_1 -%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78 -OpStore %79 %uint_23 -%81 = OpIAdd %uint %69 %uint_2 -%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81 -OpStore %82 %58 -%85 = OpIAdd %uint %69 %uint_3 -%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85 -OpStore %86 %uint_5317 -%90 = OpLoad %v3uint %89 -%91 = OpCompositeExtract %uint %90 0 -%92 = OpCompositeExtract %uint %90 1 -%93 = OpCompositeExtract %uint %90 2 -%94 = OpIAdd %uint %69 %uint_4 -%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94 -OpStore %95 %91 -%97 = OpIAdd %uint %69 %uint_5 -%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97 -OpStore %98 %92 -%100 = OpIAdd %uint %69 %uint_6 -%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100 -OpStore %101 %93 -%103 = OpIAdd %uint %69 %uint_7 -%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103 -OpStore %104 %59 -%106 = OpIAdd %uint %69 %uint_8 -%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106 -OpStore %107 %60 -%109 = OpIAdd %uint %69 %uint_9 -%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109 -OpStore %110 %61 -OpBranch %73 -%73 = OpLabel -OpReturn -OpFunctionEnd -%115 = OpFunction %uint None %116 -%117 = OpFunctionParameter %uint -%118 = OpFunctionParameter %uint -%119 = OpFunctionParameter %uint -%120 = OpFunctionParameter %uint -%121 = OpLabel -%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117 -%123 = OpLoad %uint %122 -%124 = OpIAdd %uint %123 %118 -%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124 -%126 = OpLoad %uint %125 -%127 = OpIAdd %uint %126 %119 -%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127 -%129 = OpLoad %uint %128 -%130 = OpIAdd %uint %129 %120 -%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130 -%132 = OpLoad %uint %131 -OpReturnValue %132 -OpFunctionEnd -)"; + const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4; // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, false, false, false); + SinglePassRunAndMatch(defs + main_func + new_funcs, + true, 7u, 23u, true, true, false, + false, false); } TEST_F(InstBindlessTest, @@ -6473,14 +3853,17 @@ TEST_F(InstBindlessTest, // sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r; // } - const std::string defs_before = - R"(OpCapability RuntimeDescriptorArray + // clang-format off + const std::string defs = R"( +OpCapability RuntimeDescriptorArray OpCapability RayTracingNV OpExtension "SPV_EXT_descriptor_indexing" OpExtension "SPV_NV_ray_tracing" +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint CallableNV %main "main" +; CHECK: OpEntryPoint CallableNV %main "main" %89 OpSource GLSL 460 OpSourceExtension "GL_EXT_nonuniform_qualifier" OpSourceExtension "GL_NV_ray_tracing" @@ -6498,51 +3881,11 @@ OpDecorate %sbo Binding 0 OpDecorate %images DescriptorSet 0 OpDecorate %images Binding 1 OpDecorate %images NonWritable +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kInputDecorations + kOutputDecorations + R"( +; CHECK: OpDecorate %89 BuiltIn LaunchIdNV %void = OpTypeVoid -)"; - - const std::string defs_after = - R"(OpCapability RuntimeDescriptorArray -OpCapability RayTracingNV -OpExtension "SPV_EXT_descriptor_indexing" -OpExtension "SPV_NV_ray_tracing" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint CallableNV %main "main" %89 -OpSource GLSL 460 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpSourceExtension "GL_NV_ray_tracing" -OpName %main "main" -OpName %StorageBuffer "StorageBuffer" -OpMemberName %StorageBuffer 0 "index" -OpMemberName %StorageBuffer 1 "red" -OpName %sbo "sbo" -OpName %images "images" -OpMemberDecorate %StorageBuffer 0 Offset 0 -OpMemberDecorate %StorageBuffer 1 Offset 4 -OpDecorate %StorageBuffer BufferBlock -OpDecorate %sbo DescriptorSet 0 -OpDecorate %sbo Binding 0 -OpDecorate %images DescriptorSet 0 -OpDecorate %images Binding 1 -OpDecorate %images NonWritable -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_39 Block -OpMemberDecorate %_struct_39 0 Offset 0 -OpDecorate %41 DescriptorSet 7 -OpDecorate %41 Binding 1 -OpDecorate %_struct_63 Block -OpMemberDecorate %_struct_63 0 Offset 0 -OpMemberDecorate %_struct_63 1 Offset 4 -OpDecorate %65 DescriptorSet 7 -OpDecorate %65 Binding 0 -OpDecorate %89 BuiltIn LaunchIdNV -%void = OpTypeVoid -)"; - - const std::string func_before = - R"(%3 = OpTypeFunction %void +%3 = OpTypeFunction %void %uint = OpTypeInt 32 0 %float = OpTypeFloat 32 %StorageBuffer = OpTypeStruct %uint %float @@ -6562,6 +3905,38 @@ OpDecorate %89 BuiltIn LaunchIdNV %v4float = OpTypeVector %float 4 %uint_0 = OpConstant %uint 0 %_ptr_Uniform_float = OpTypePointer Uniform %float +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %34 = OpTypeFunction %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kInputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %bool = OpTypeBool +; CHECK: %57 = OpTypeFunction %void %uint %uint %uint %uint +)" + kOutputGlobals + R"( +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %uint_5318 = OpConstant %uint 5318 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %v3uint = OpTypeVector %uint 3 +; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint +; CHECK: %89 = OpVariable %_ptr_Input_v3uint Input +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_6 = OpConstant %uint 6 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_51 = OpConstant %uint 51 +; CHECK: %113 = OpConstantNull %v4float +; CHECK: %116 = OpTypeFunction %uint %uint %uint %uint %uint +; CHECK: %uint_48 = OpConstant %uint 48 +; CHECK: %141 = OpConstantNull %uint +; CHECK: %uint_54 = OpConstant %uint 54 +)"; + // clang-format on + + const std::string main_func = R"( %main = OpFunction %void None %3 %5 = OpLabel %19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 @@ -6572,211 +3947,69 @@ OpDecorate %89 BuiltIn LaunchIdNV %29 = OpCompositeExtract %float %27 0 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 OpStore %31 %29 +; CHECK-NOT: OpStore %31 %29 +; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0 +; CHECK: %134 = OpULessThan %bool %uint_0 %133 +; CHECK: OpSelectionMerge %135 None +; CHECK: OpBranchConditional %134 %136 %137 +; CHECK: %136 = OpLabel +; CHECK: %138 = OpLoad %uint %25 +; CHECK: OpBranch %135 +; CHECK: %137 = OpLabel +; CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0 +; CHECK: OpBranch %135 +; CHECK: %135 = OpLabel +; CHECK: %142 = OpPhi %uint %138 %136 %141 %137 +; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142 +; CHECK: %28 = OpLoad %13 %27 +; CHECK: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1 +; CHECK: %50 = OpULessThan %bool %142 %48 +; CHECK: OpSelectionMerge %51 None +; CHECK: OpBranchConditional %50 %52 %53 +; CHECK: %52 = OpLabel +; CHECK: %54 = OpLoad %13 %27 +; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142 +; CHECK: %144 = OpULessThan %bool %uint_0 %143 +; CHECK: OpSelectionMerge %145 None +; CHECK: OpBranchConditional %144 %146 %147 +; CHECK: %146 = OpLabel +; CHECK: %148 = OpLoad %13 %27 +; CHECK: %149 = OpImageRead %v4float %148 %20 +; CHECK: OpBranch %145 +; CHECK: %147 = OpLabel +; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0 +; CHECK: OpBranch %145 +; CHECK: %145 = OpLabel +; CHECK: %151 = OpPhi %v4float %149 %146 %113 %147 +; CHECK: OpBranch %51 +; CHECK: %53 = OpLabel +; CHECK: %112 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48 +; CHECK: OpBranch %51 +; CHECK: %51 = OpLabel +; CHECK: %114 = OpPhi %v4float %151 %145 %113 %53 +; CHECK: %30 = OpCompositeExtract %float %114 0 +; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +; CHECK: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0 +; CHECK: %153 = OpULessThan %bool %uint_0 %152 +; CHECK: OpSelectionMerge %154 None +; CHECK: OpBranchConditional %153 %155 %156 +; CHECK: %155 = OpLabel +; CHECK: OpStore %31 %30 +; CHECK: OpBranch %154 +; CHECK: %156 = OpLabel +; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0 +; CHECK: OpBranch %154 +; CHECK: %154 = OpLabel OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%7 = OpTypeFunction %void -%uint = OpTypeInt 32 0 -%float = OpTypeFloat 32 -%StorageBuffer = OpTypeStruct %uint %float -%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer -%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform -%int = OpTypeInt 32 1 -%int_1 = OpConstant %int 1 -%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f -%_runtimearr_13 = OpTypeRuntimeArray %13 -%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 -%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant -%int_0 = OpConstant %int 0 -%_ptr_Uniform_uint = OpTypePointer Uniform %uint -%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 -%v2int = OpTypeVector %int 2 -%20 = OpConstantComposite %v2int %int_0 %int_0 -%v4float = OpTypeVector %float 4 -%uint_0 = OpConstant %uint 0 -%_ptr_Uniform_float = OpTypePointer Uniform %float -%uint_1 = OpConstant %uint 1 -%34 = OpTypeFunction %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_39 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39 -%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%bool = OpTypeBool -%57 = OpTypeFunction %void %uint %uint %uint %uint -%_struct_63 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63 -%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_5318 = OpConstant %uint 5318 -%uint_3 = OpConstant %uint 3 -%v3uint = OpTypeVector %uint 3 -%_ptr_Input_v3uint = OpTypePointer Input %v3uint -%89 = OpVariable %_ptr_Input_v3uint Input -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_51 = OpConstant %uint 51 -%113 = OpConstantNull %v4float -%116 = OpTypeFunction %uint %uint %uint %uint %uint -%uint_48 = OpConstant %uint 48 -%141 = OpConstantNull %uint -%uint_54 = OpConstant %uint 54 -%main = OpFunction %void None %7 -%24 = OpLabel -%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 -%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 -%134 = OpULessThan %bool %uint_0 %133 -OpSelectionMerge %135 None -OpBranchConditional %134 %136 %137 -%136 = OpLabel -%138 = OpLoad %uint %25 -OpBranch %135 -%137 = OpLabel -%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0 -OpBranch %135 -%135 = OpLabel -%142 = OpPhi %uint %138 %136 %141 %137 -%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142 -%28 = OpLoad %13 %27 -%48 = OpFunctionCall %uint %33 %uint_1 %uint_1 -%50 = OpULessThan %bool %142 %48 -OpSelectionMerge %51 None -OpBranchConditional %50 %52 %53 -%52 = OpLabel -%54 = OpLoad %13 %27 -%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142 -%144 = OpULessThan %bool %uint_0 %143 -OpSelectionMerge %145 None -OpBranchConditional %144 %146 %147 -%146 = OpLabel -%148 = OpLoad %13 %27 -%149 = OpImageRead %v4float %148 %20 -OpBranch %145 -%147 = OpLabel -%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0 -OpBranch %145 -%145 = OpLabel -%151 = OpPhi %v4float %149 %146 %113 %147 -OpBranch %51 -%53 = OpLabel -%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48 -OpBranch %51 -%51 = OpLabel -%114 = OpPhi %v4float %151 %145 %113 %53 -%30 = OpCompositeExtract %float %114 0 -%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 -%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 -%153 = OpULessThan %bool %uint_0 %152 -OpSelectionMerge %154 None -OpBranchConditional %153 %155 %156 -%155 = OpLabel -OpStore %31 %30 -OpBranch %154 -%156 = OpLabel -%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0 -OpBranch %154 -%154 = OpLabel -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%33 = OpFunction %uint None %34 -%35 = OpFunctionParameter %uint -%36 = OpFunctionParameter %uint -%37 = OpLabel -%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35 -%44 = OpLoad %uint %43 -%45 = OpIAdd %uint %44 %36 -%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45 -%47 = OpLoad %uint %46 -OpReturnValue %47 -OpFunctionEnd -%56 = OpFunction %void None %57 -%58 = OpFunctionParameter %uint -%59 = OpFunctionParameter %uint -%60 = OpFunctionParameter %uint -%61 = OpFunctionParameter %uint -%62 = OpLabel -%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0 -%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10 -%70 = OpIAdd %uint %69 %uint_10 -%71 = OpArrayLength %uint %65 1 -%72 = OpULessThanEqual %bool %70 %71 -OpSelectionMerge %73 None -OpBranchConditional %72 %74 %73 -%74 = OpLabel -%75 = OpIAdd %uint %69 %uint_0 -%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75 -OpStore %76 %uint_10 -%78 = OpIAdd %uint %69 %uint_1 -%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78 -OpStore %79 %uint_23 -%81 = OpIAdd %uint %69 %uint_2 -%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81 -OpStore %82 %58 -%85 = OpIAdd %uint %69 %uint_3 -%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85 -OpStore %86 %uint_5318 -%90 = OpLoad %v3uint %89 -%91 = OpCompositeExtract %uint %90 0 -%92 = OpCompositeExtract %uint %90 1 -%93 = OpCompositeExtract %uint %90 2 -%94 = OpIAdd %uint %69 %uint_4 -%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94 -OpStore %95 %91 -%97 = OpIAdd %uint %69 %uint_5 -%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97 -OpStore %98 %92 -%100 = OpIAdd %uint %69 %uint_6 -%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100 -OpStore %101 %93 -%103 = OpIAdd %uint %69 %uint_7 -%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103 -OpStore %104 %59 -%106 = OpIAdd %uint %69 %uint_8 -%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106 -OpStore %107 %60 -%109 = OpIAdd %uint %69 %uint_9 -%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109 -OpStore %110 %61 -OpBranch %73 -%73 = OpLabel -OpReturn -OpFunctionEnd -%115 = OpFunction %uint None %116 -%117 = OpFunctionParameter %uint -%118 = OpFunctionParameter %uint -%119 = OpFunctionParameter %uint -%120 = OpFunctionParameter %uint -%121 = OpLabel -%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117 -%123 = OpLoad %uint %122 -%124 = OpIAdd %uint %123 %118 -%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124 -%126 = OpLoad %uint %125 -%127 = OpIAdd %uint %126 %119 -%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127 -%129 = OpLoad %uint %128 -%130 = OpIAdd %uint %129 %120 -%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130 -%132 = OpLoad %uint %131 -OpReturnValue %132 -OpFunctionEnd -)"; + const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4; // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, false, false, false); + SinglePassRunAndMatch(defs + main_func + new_funcs, + true, 7u, 23u, true, true, false, + false, false); } TEST_F(InstBindlessTest, InstBoundsInitSameBlockOpReplication) { @@ -6806,16 +4039,17 @@ TEST_F(InstBindlessTest, InstBoundsInitSameBlockOpReplication) { // outColor = vec4(x, y, 0.0, 0.0); // } // - // clang-format on - const std::string defs_before = - R"(OpCapability Shader + const std::string defs = R"( +OpCapability Shader OpCapability ShaderNonUniformEXT OpCapability SampledImageArrayNonUniformIndexingEXT OpExtension "SPV_EXT_descriptor_indexing" +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %inTexcoord %outColor +; CHECK: OpEntryPoint Fragment %main "main" %inTexcoord %outColor %gl_FragCoord OpExecutionMode %main OriginUpperLeft OpSource GLSL 450 OpSourceExtension "GL_EXT_nonuniform_qualifier" @@ -6845,6 +4079,12 @@ OpDecorate %Uniforms Block OpDecorate %uniforms DescriptorSet 0 OpDecorate %uniforms Binding 0 OpDecorate %outColor Location 0 +; CHECK: OpDecorate %63 NonUniform +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kOutputDecorations + R"( +; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +)" + kInputDecorations + R"( +; CHECK: OpDecorate %151 NonUniform %void = OpTypeVoid %3 = OpTypeFunction %void %int = OpTypeInt 32 1 @@ -6876,122 +4116,35 @@ OpDecorate %outColor Location 0 %_ptr_Output_v4float = OpTypePointer Output %v4float %outColor = OpVariable %_ptr_Output_v4float Output %float_0 = OpConstant %float 0 +; CHECK: %bool = OpTypeBool +; CHECK: %68 = OpTypeFunction %void %uint %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kOutputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +; CHECK: %v4uint = OpTypeVector %uint 4 +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_79 = OpConstant %uint 79 +; CHECK: %122 = OpConstantNull %v4float +; CHECK: %126 = OpTypeFunction %uint %uint %uint %uint %uint +)" + kInputGlobals + R"( +; CHECK: %uint_87 = OpConstant %uint 87 +; CHECK: %165 = OpConstantNull %v2float +; CHECK: %uint_89 = OpConstant %uint 89 )"; + // clang-format on - const std::string defs_after = - R"(OpCapability Shader -OpCapability ShaderNonUniform -OpCapability SampledImageArrayNonUniformIndexing -OpExtension "SPV_EXT_descriptor_indexing" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %inTexcoord %outColor %gl_FragCoord -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpName %main "main" -OpName %index "index" -OpName %x "x" -OpName %uniformTexArr "uniformTexArr" -OpName %uniformSampler "uniformSampler" -OpName %inTexcoord "inTexcoord" -OpName %y "y" -OpName %uniformTex "uniformTex" -OpName %Uniforms "Uniforms" -OpMemberName %Uniforms 0 "var0" -OpName %uniforms "uniforms" -OpName %outColor "outColor" -OpDecorate %uniformTexArr DescriptorSet 0 -OpDecorate %uniformTexArr Binding 3 -OpDecorate %19 NonUniform -OpDecorate %22 NonUniform -OpDecorate %uniformSampler DescriptorSet 0 -OpDecorate %uniformSampler Binding 1 -OpDecorate %inTexcoord Location 0 -OpDecorate %uniformTex DescriptorSet 0 -OpDecorate %uniformTex Binding 2 -OpMemberDecorate %Uniforms 0 Offset 0 -OpDecorate %Uniforms Block -OpDecorate %uniforms DescriptorSet 0 -OpDecorate %uniforms Binding 0 -OpDecorate %outColor Location 0 -OpDecorate %63 NonUniform -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_75 Block -OpMemberDecorate %_struct_75 0 Offset 0 -OpMemberDecorate %_struct_75 1 Offset 4 -OpDecorate %77 DescriptorSet 7 -OpDecorate %77 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -OpDecorate %_struct_132 Block -OpMemberDecorate %_struct_132 0 Offset 0 -OpDecorate %134 DescriptorSet 7 -OpDecorate %134 Binding 1 -OpDecorate %151 NonUniform -%void = OpTypeVoid -%3 = OpTypeFunction %void -%int = OpTypeInt 32 1 -%_ptr_Function_int = OpTypePointer Function %int -%int_0 = OpConstant %int 0 -%float = OpTypeFloat 32 -%_ptr_Function_float = OpTypePointer Function %float -%13 = OpTypeImage %float 2D 0 0 0 1 Unknown -%uint = OpTypeInt 32 0 -%uint_8 = OpConstant %uint 8 -%_arr_13_uint_8 = OpTypeArray %13 %uint_8 -%_ptr_UniformConstant__arr_13_uint_8 = OpTypePointer UniformConstant %_arr_13_uint_8 -%uniformTexArr = OpVariable %_ptr_UniformConstant__arr_13_uint_8 UniformConstant -%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 -%23 = OpTypeSampler -%_ptr_UniformConstant_23 = OpTypePointer UniformConstant %23 -%uniformSampler = OpVariable %_ptr_UniformConstant_23 UniformConstant -%27 = OpTypeSampledImage %13 -%v2float = OpTypeVector %float 2 -%_ptr_Input_v2float = OpTypePointer Input %v2float -%inTexcoord = OpVariable %_ptr_Input_v2float Input -%v4float = OpTypeVector %float 4 -%uint_0 = OpConstant %uint 0 -%uniformTex = OpVariable %_ptr_UniformConstant_13 UniformConstant -%Uniforms = OpTypeStruct %v2float -%_ptr_Uniform_Uniforms = OpTypePointer Uniform %Uniforms -%uniforms = OpVariable %_ptr_Uniform_Uniforms Uniform -%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float -%_ptr_Output_v4float = OpTypePointer Output %v4float -%outColor = OpVariable %_ptr_Output_v4float Output -%float_0 = OpConstant %float 0 -%bool = OpTypeBool -%68 = OpTypeFunction %void %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_75 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_75 = OpTypePointer StorageBuffer %_struct_75 -%77 = OpVariable %_ptr_StorageBuffer__struct_75 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_1 = OpConstant %uint 1 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_3 = OpConstant %uint 3 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_7 = OpConstant %uint 7 -%uint_9 = OpConstant %uint 9 -%uint_79 = OpConstant %uint 79 -%122 = OpConstantNull %v4float -%126 = OpTypeFunction %uint %uint %uint %uint %uint -%_struct_132 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_132 = OpTypePointer StorageBuffer %_struct_132 -%134 = OpVariable %_ptr_StorageBuffer__struct_132 StorageBuffer -%uint_87 = OpConstant %uint 87 -%165 = OpConstantNull %v2float -%uint_89 = OpConstant %uint 89 -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %3 + const std::string main_func = R"( +%main = OpFunction %void None %3 %5 = OpLabel %index = OpVariable %_ptr_Function_int Function %x = OpVariable %_ptr_Function_float Function @@ -7015,6 +4168,36 @@ OpStore %x %36 %49 = OpFMul %v2float %42 %48 %50 = OpImageSampleImplicitLod %v4float %41 %49 %51 = OpCompositeExtract %float %50 0 +; CHECK-NOT: %51 = OpCompositeExtract %float %50 0 +; CHECK: %157 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0 +; CHECK: %158 = OpULessThan %bool %uint_0 %157 +; CHECK: OpSelectionMerge %159 None +; CHECK: OpBranchConditional %158 %160 %161 +; CHECK: %160 = OpLabel +; CHECK: %162 = OpLoad %v2float %47 +; CHECK: OpBranch %159 +; CHECK: %161 = OpLabel +; CHECK: %164 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_87 %uint_1 %uint_0 %uint_0 +; CHECK: OpBranch %159 +; CHECK: %159 = OpLabel +; CHECK: %166 = OpPhi %v2float %162 %160 %165 %161 +; CHECK: %49 = OpFMul %v2float %42 %166 +; CHECK: %167 = OpSampledImage %27 %39 %40 +; CHECK: %168 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_2 %uint_0 +; CHECK: %169 = OpULessThan %bool %uint_0 %168 +; CHECK: OpSelectionMerge %170 None +; CHECK: OpBranchConditional %169 %171 %172 +; CHECK: %171 = OpLabel +; CHECK: %173 = OpLoad %13 %uniformTex +; CHECK: %174 = OpSampledImage %27 %173 %40 +; CHECK: %175 = OpImageSampleImplicitLod %v4float %174 %49 +; CHECK: OpBranch %170 +; CHECK: %172 = OpLabel +; CHECK: %177 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_89 %uint_1 %uint_0 %uint_0 +; CHECK: OpBranch %170 +; CHECK: %170 = OpLabel +; CHECK: %178 = OpPhi %v4float %175 %171 %122 %172 +; CHECK: %51 = OpCompositeExtract %float %178 0 OpStore %y %51 %54 = OpLoad %float %x %55 = OpLoad %float %y @@ -7024,168 +4207,12 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %3 -%5 = OpLabel -%index = OpVariable %_ptr_Function_int Function -%x = OpVariable %_ptr_Function_float Function -%y = OpVariable %_ptr_Function_float Function -OpStore %index %int_0 -%19 = OpLoad %int %index -%21 = OpAccessChain %_ptr_UniformConstant_13 %uniformTexArr %19 -%22 = OpLoad %13 %21 -%26 = OpLoad %23 %uniformSampler -%28 = OpSampledImage %27 %22 %26 -%32 = OpLoad %v2float %inTexcoord -%59 = OpULessThan %bool %19 %uint_8 -OpSelectionMerge %60 None -OpBranchConditional %59 %61 %62 -%61 = OpLabel -%63 = OpLoad %13 %21 -%64 = OpSampledImage %27 %63 %26 -%124 = OpBitcast %uint %19 -%146 = OpFunctionCall %uint %125 %uint_0 %uint_0 %uint_3 %124 -%147 = OpULessThan %bool %uint_0 %146 -OpSelectionMerge %148 None -OpBranchConditional %147 %149 %150 -%149 = OpLabel -%151 = OpLoad %13 %21 -%152 = OpSampledImage %27 %151 %26 -%153 = OpImageSampleImplicitLod %v4float %152 %32 -OpBranch %148 -%150 = OpLabel -%154 = OpBitcast %uint %19 -%155 = OpFunctionCall %void %67 %uint_79 %uint_1 %154 %uint_0 -OpBranch %148 -%148 = OpLabel -%156 = OpPhi %v4float %153 %149 %122 %150 -OpBranch %60 -%62 = OpLabel -%66 = OpBitcast %uint %19 -%121 = OpFunctionCall %void %67 %uint_79 %uint_0 %66 %uint_8 -OpBranch %60 -%60 = OpLabel -%123 = OpPhi %v4float %156 %148 %122 %62 -%36 = OpCompositeExtract %float %123 0 -OpStore %x %36 -%39 = OpLoad %13 %uniformTex -%40 = OpLoad %23 %uniformSampler -%41 = OpSampledImage %27 %39 %40 -%42 = OpLoad %v2float %inTexcoord -%47 = OpAccessChain %_ptr_Uniform_v2float %uniforms %int_0 -%157 = OpFunctionCall %uint %125 %uint_0 %uint_0 %uint_0 %uint_0 -%158 = OpULessThan %bool %uint_0 %157 -OpSelectionMerge %159 None -OpBranchConditional %158 %160 %161 -%160 = OpLabel -%162 = OpLoad %v2float %47 -OpBranch %159 -%161 = OpLabel -%164 = OpFunctionCall %void %67 %uint_87 %uint_1 %uint_0 %uint_0 -OpBranch %159 -%159 = OpLabel -%166 = OpPhi %v2float %162 %160 %165 %161 -%49 = OpFMul %v2float %42 %166 -%167 = OpSampledImage %27 %39 %40 -%168 = OpFunctionCall %uint %125 %uint_0 %uint_0 %uint_2 %uint_0 -%169 = OpULessThan %bool %uint_0 %168 -OpSelectionMerge %170 None -OpBranchConditional %169 %171 %172 -%171 = OpLabel -%173 = OpLoad %13 %uniformTex -%174 = OpSampledImage %27 %173 %40 -%175 = OpImageSampleImplicitLod %v4float %174 %49 -OpBranch %170 -%172 = OpLabel -%177 = OpFunctionCall %void %67 %uint_89 %uint_1 %uint_0 %uint_0 -OpBranch %170 -%170 = OpLabel -%178 = OpPhi %v4float %175 %171 %122 %172 -%51 = OpCompositeExtract %float %178 0 -OpStore %y %51 -%54 = OpLoad %float %x -%55 = OpLoad %float %y -%57 = OpCompositeConstruct %v4float %54 %55 %float_0 %float_0 -OpStore %outColor %57 -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%67 = OpFunction %void None %68 -%69 = OpFunctionParameter %uint -%70 = OpFunctionParameter %uint -%71 = OpFunctionParameter %uint -%72 = OpFunctionParameter %uint -%73 = OpLabel -%79 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_0 -%82 = OpAtomicIAdd %uint %79 %uint_4 %uint_0 %uint_10 -%83 = OpIAdd %uint %82 %uint_10 -%84 = OpArrayLength %uint %77 1 -%85 = OpULessThanEqual %bool %83 %84 -OpSelectionMerge %86 None -OpBranchConditional %85 %87 %86 -%87 = OpLabel -%88 = OpIAdd %uint %82 %uint_0 -%90 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %88 -OpStore %90 %uint_10 -%92 = OpIAdd %uint %82 %uint_1 -%93 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %92 -OpStore %93 %uint_23 -%95 = OpIAdd %uint %82 %uint_2 -%96 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %95 -OpStore %96 %69 -%98 = OpIAdd %uint %82 %uint_3 -%99 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %98 -OpStore %99 %uint_4 -%102 = OpLoad %v4float %gl_FragCoord -%104 = OpBitcast %v4uint %102 -%105 = OpCompositeExtract %uint %104 0 -%106 = OpIAdd %uint %82 %uint_4 -%107 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %106 -OpStore %107 %105 -%108 = OpCompositeExtract %uint %104 1 -%110 = OpIAdd %uint %82 %uint_5 -%111 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %110 -OpStore %111 %108 -%113 = OpIAdd %uint %82 %uint_7 -%114 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %113 -OpStore %114 %70 -%115 = OpIAdd %uint %82 %uint_8 -%116 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %115 -OpStore %116 %71 -%118 = OpIAdd %uint %82 %uint_9 -%119 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %118 -OpStore %119 %72 -OpBranch %86 -%86 = OpLabel -OpReturn -OpFunctionEnd -%125 = OpFunction %uint None %126 -%127 = OpFunctionParameter %uint -%128 = OpFunctionParameter %uint -%129 = OpFunctionParameter %uint -%130 = OpFunctionParameter %uint -%131 = OpLabel -%135 = OpAccessChain %_ptr_StorageBuffer_uint %134 %uint_0 %127 -%136 = OpLoad %uint %135 -%137 = OpIAdd %uint %136 %128 -%138 = OpAccessChain %_ptr_StorageBuffer_uint %134 %uint_0 %137 -%139 = OpLoad %uint %138 -%140 = OpIAdd %uint %139 %129 -%141 = OpAccessChain %_ptr_StorageBuffer_uint %134 %uint_0 %140 -%142 = OpLoad %uint %141 -%143 = OpIAdd %uint %142 %130 -%144 = OpAccessChain %_ptr_StorageBuffer_uint %134 %uint_0 %143 -%145 = OpLoad %uint %144 -OpReturnValue %145 -OpFunctionEnd -)"; + const std::string new_funcs = kStreamWrite4Frag + kDirectRead4; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, false, false, false); + SinglePassRunAndMatch(defs + main_func + new_funcs, + true, 7u, 23u, true, true, false, + false, false); } TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) { @@ -7225,13 +4252,14 @@ TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) { // return ps_output; // } + // clang-format off const std::string text = R"( OpCapability Shader ;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor -;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %130 %157 %gl_FragCoord +;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_FragCoord OpExecutionMode %MainPs OriginUpperLeft OpSource HLSL 500 OpName %MainPs "MainPs" @@ -7260,15 +4288,7 @@ TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) { OpDecorate %i_vTextureCoords Location 0 OpDecorate %_entryPointOutput_vColor Location 0 ;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 - ;CHECK: OpDecorate %_struct_128 Block - ;CHECK: OpMemberDecorate %_struct_128 0 Offset 0 - ;CHECK: OpDecorate %130 DescriptorSet 7 - ;CHECK: OpDecorate %130 Binding 1 - ;CHECK: OpDecorate %_struct_155 Block - ;CHECK: OpMemberDecorate %_struct_155 0 Offset 0 - ;CHECK: OpMemberDecorate %_struct_155 1 Offset 4 - ;CHECK: OpDecorate %157 DescriptorSet 7 - ;CHECK: OpDecorate %157 Binding 0 +)" + kInputDecorations + kOutputDecorations + R"( ;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %3 = OpTypeFunction %void @@ -7304,15 +4324,11 @@ TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) { ;CHECK: %uint_1 = OpConstant %uint 1 ;CHECK: %122 = OpTypeFunction %uint %uint %uint %uint ;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint - ;CHECK: %_struct_128 = OpTypeStruct %_runtimearr_uint - ;CHECK: %_ptr_StorageBuffer__struct_128 = OpTypePointer StorageBuffer %_struct_128 - ;CHECK: %130 = OpVariable %_ptr_StorageBuffer__struct_128 StorageBuffer + )" + kInputGlobals + R"( ;CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint ;CHECK: %uint_4 = OpConstant %uint 4 ;CHECK: %148 = OpTypeFunction %void %uint %uint %uint %uint %uint - ;CHECK: %_struct_155 = OpTypeStruct %uint %_runtimearr_uint - ;CHECK: %_ptr_StorageBuffer__struct_155 = OpTypePointer StorageBuffer %_struct_155 - ;CHECK: %157 = OpVariable %_ptr_StorageBuffer__struct_155 StorageBuffer + )" + kOutputGlobals + R"( ;CHECK: %uint_11 = OpConstant %uint 11 ;CHECK: %uint_23 = OpConstant %uint 23 ;CHECK: %uint_2 = OpConstant %uint 2 @@ -7329,7 +4345,7 @@ TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) { ;CHECK: %uint_75 = OpConstant %uint 75 %MainPs = OpFunction %void None %3 %5 = OpLabel - ;CHECK: %140 = OpFunctionCall %uint %121 %uint_1 %uint_1 %uint_0 + ;CHECK: %140 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_1 %uint_0 ;CHECK: OpBranch %117 ;CHECK: %117 = OpLabel ;CHECK: OpBranch %116 @@ -7352,7 +4368,7 @@ TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) { ;CHECK: %146 = OpLoad %v2float %86 ;CHECK: OpBranch %143 ;CHECK: %145 = OpLabel - ;CHECK: %201 = OpFunctionCall %void %147 %uint_71 %uint_4 %uint_0 %119 %140 + ;CHECK: %201 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_71 %uint_4 %uint_0 %119 %140 ;CHECK: OpBranch %143 ;CHECK: %143 = OpLabel ;CHECK: %203 = OpPhi %v2float %146 %144 %202 %145 @@ -7369,7 +4385,7 @@ TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) { ;CHECK: %209 = OpLoad %v2float %89 ;CHECK: OpBranch %206 ;CHECK: %208 = OpLabel - ;CHECK: %211 = OpFunctionCall %void %147 %uint_75 %uint_4 %uint_0 %204 %140 + ;CHECK: %211 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_75 %uint_4 %uint_0 %204 %140 ;CHECK: OpBranch %206 ;CHECK: %206 = OpLabel ;CHECK: %212 = OpPhi %v2float %209 %207 %202 %208 @@ -7386,75 +4402,8 @@ TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) { OpStore %_entryPointOutput_vColor %100 OpReturn OpFunctionEnd - ;CHECK: %121 = OpFunction %uint None %122 - ;CHECK: %123 = OpFunctionParameter %uint - ;CHECK: %124 = OpFunctionParameter %uint - ;CHECK: %125 = OpFunctionParameter %uint - ;CHECK: %126 = OpLabel - ;CHECK: %132 = OpAccessChain %_ptr_StorageBuffer_uint %130 %uint_0 %123 - ;CHECK: %133 = OpLoad %uint %132 - ;CHECK: %134 = OpIAdd %uint %133 %124 - ;CHECK: %135 = OpAccessChain %_ptr_StorageBuffer_uint %130 %uint_0 %134 - ;CHECK: %136 = OpLoad %uint %135 - ;CHECK: %137 = OpIAdd %uint %136 %125 - ;CHECK: %138 = OpAccessChain %_ptr_StorageBuffer_uint %130 %uint_0 %137 - ;CHECK: %139 = OpLoad %uint %138 - ;CHECK: OpReturnValue %139 - ;CHECK: OpFunctionEnd - ;CHECK: %147 = OpFunction %void None %148 - ;CHECK: %149 = OpFunctionParameter %uint - ;CHECK: %150 = OpFunctionParameter %uint - ;CHECK: %151 = OpFunctionParameter %uint - ;CHECK: %152 = OpFunctionParameter %uint - ;CHECK: %153 = OpFunctionParameter %uint - ;CHECK: %154 = OpLabel - ;CHECK: %158 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_0 - ;CHECK: %160 = OpAtomicIAdd %uint %158 %uint_4 %uint_0 %uint_11 - ;CHECK: %161 = OpIAdd %uint %160 %uint_11 - ;CHECK: %162 = OpArrayLength %uint %157 1 - ;CHECK: %163 = OpULessThanEqual %bool %161 %162 - ;CHECK: OpSelectionMerge %164 None - ;CHECK: OpBranchConditional %163 %165 %164 - ;CHECK: %165 = OpLabel - ;CHECK: %166 = OpIAdd %uint %160 %uint_0 - ;CHECK: %167 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %166 - ;CHECK: OpStore %167 %uint_11 - ;CHECK: %169 = OpIAdd %uint %160 %uint_1 - ;CHECK: %170 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %169 - ;CHECK: OpStore %170 %uint_23 - ;CHECK: %172 = OpIAdd %uint %160 %uint_2 - ;CHECK: %173 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %172 - ;CHECK: OpStore %173 %149 - ;CHECK: %175 = OpIAdd %uint %160 %uint_3 - ;CHECK: %176 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %175 - ;CHECK: OpStore %176 %uint_4 - ;CHECK: %179 = OpLoad %v4float %gl_FragCoord - ;CHECK: %181 = OpBitcast %v4uint %179 - ;CHECK: %182 = OpCompositeExtract %uint %181 0 - ;CHECK: %183 = OpIAdd %uint %160 %uint_4 - ;CHECK: %184 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %183 - ;CHECK: OpStore %184 %182 - ;CHECK: %185 = OpCompositeExtract %uint %181 1 - ;CHECK: %187 = OpIAdd %uint %160 %uint_5 - ;CHECK: %188 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %187 - ;CHECK: OpStore %188 %185 - ;CHECK: %189 = OpIAdd %uint %160 %uint_7 - ;CHECK: %190 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %189 - ;CHECK: OpStore %190 %150 - ;CHECK: %192 = OpIAdd %uint %160 %uint_8 - ;CHECK: %193 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %192 - ;CHECK: OpStore %193 %151 - ;CHECK: %195 = OpIAdd %uint %160 %uint_9 - ;CHECK: %196 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %195 - ;CHECK: OpStore %196 %152 - ;CHECK: %198 = OpIAdd %uint %160 %uint_10 - ;CHECK: %199 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %198 - ;CHECK: OpStore %199 %153 - ;CHECK: OpBranch %164 - ;CHECK: %164 = OpLabel - ;CHECK: OpReturn - ;CHECK: OpFunctionEnd - )"; +)" + kDirectRead3 + kStreamWrite5Frag; + // clang-format on SetTargetEnv(SPV_ENV_VULKAN_1_2); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); @@ -7498,6 +4447,7 @@ TEST_F(InstBindlessTest, UniformArrayRefNoDescInit) { // return ps_output; // } + // clang-format off const std::string text = R"( OpCapability Shader ;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" @@ -7540,15 +4490,7 @@ TEST_F(InstBindlessTest, UniformArrayRefNoDescInit) { OpDecorate %i_vTextureCoords Location 0 OpDecorate %_entryPointOutput_vColor Location 0 ;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -;CHECK: OpDecorate %_struct_111 Block -;CHECK: OpMemberDecorate %_struct_111 0 Offset 0 -;CHECK: OpDecorate %113 DescriptorSet 7 -;CHECK: OpDecorate %113 Binding 1 -;CHECK: OpDecorate %_struct_139 Block -;CHECK: OpMemberDecorate %_struct_139 0 Offset 0 -;CHECK: OpMemberDecorate %_struct_139 1 Offset 4 -;CHECK: OpDecorate %141 DescriptorSet 7 -;CHECK: OpDecorate %141 Binding 0 +)" + kInputDecorations + kOutputDecorations + R"( ;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %3 = OpTypeFunction %void @@ -7591,16 +4533,12 @@ TEST_F(InstBindlessTest, UniformArrayRefNoDescInit) { ;CHECK: %uint_1 = OpConstant %uint 1 ;CHECK: %105 = OpTypeFunction %uint %uint %uint %uint ;CHECK:%_runtimearr_uint = OpTypeRuntimeArray %uint -;CHECK:%_struct_111 = OpTypeStruct %_runtimearr_uint -;CHECK:%_ptr_StorageBuffer__struct_111 = OpTypePointer StorageBuffer %_struct_111 -;CHECK: %113 = OpVariable %_ptr_StorageBuffer__struct_111 StorageBuffer +)" + kInputGlobals + R"( ;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint ;CHECK: %bool = OpTypeBool ;CHECK: %uint_4 = OpConstant %uint 4 ;CHECK: %132 = OpTypeFunction %void %uint %uint %uint %uint %uint -;CHECK:%_struct_139 = OpTypeStruct %uint %_runtimearr_uint -;CHECK:%_ptr_StorageBuffer__struct_139 = OpTypePointer StorageBuffer %_struct_139 -;CHECK: %141 = OpVariable %_ptr_StorageBuffer__struct_139 StorageBuffer +)" + kOutputGlobals + R"( ;CHECK: %uint_11 = OpConstant %uint 11 ;CHECK: %uint_23 = OpConstant %uint 23 ;CHECK: %uint_3 = OpConstant %uint 3 @@ -7615,7 +4553,7 @@ TEST_F(InstBindlessTest, UniformArrayRefNoDescInit) { ;CHECK: %185 = OpConstantNull %v2float %MainPs = OpFunction %void None %3 %5 = OpLabel -;CHECK: %123 = OpFunctionCall %uint %104 %uint_1 %uint_2 %uint_0 +;CHECK: %123 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_2 %uint_0 ;CHECK: OpBranch %93 ;CHECK: %93 = OpLabel ;CHECK: OpBranch %92 @@ -7637,7 +4575,7 @@ TEST_F(InstBindlessTest, UniformArrayRefNoDescInit) { ;CHECK: %130 = OpLoad %v2float %81 ;CHECK: OpBranch %127 ;CHECK: %129 = OpLabel -;CHECK: %184 = OpFunctionCall %void %131 %uint_78 %uint_4 %uint_0 %101 %123 +;CHECK: %184 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_78 %uint_4 %uint_0 %101 %123 ;CHECK: OpBranch %127 ;CHECK: %127 = OpLabel ;CHECK: %186 = OpPhi %v2float %130 %128 %185 %129 @@ -7651,75 +4589,8 @@ TEST_F(InstBindlessTest, UniformArrayRefNoDescInit) { OpStore %_entryPointOutput_vColor %91 OpReturn OpFunctionEnd -;CHECK: %104 = OpFunction %uint None %105 -;CHECK: %106 = OpFunctionParameter %uint -;CHECK: %107 = OpFunctionParameter %uint -;CHECK: %108 = OpFunctionParameter %uint -;CHECK: %109 = OpLabel -;CHECK: %115 = OpAccessChain %_ptr_StorageBuffer_uint %113 %uint_0 %106 -;CHECK: %116 = OpLoad %uint %115 -;CHECK: %117 = OpIAdd %uint %116 %107 -;CHECK: %118 = OpAccessChain %_ptr_StorageBuffer_uint %113 %uint_0 %117 -;CHECK: %119 = OpLoad %uint %118 -;CHECK: %120 = OpIAdd %uint %119 %108 -;CHECK: %121 = OpAccessChain %_ptr_StorageBuffer_uint %113 %uint_0 %120 -;CHECK: %122 = OpLoad %uint %121 -;CHECK: OpReturnValue %122 -;CHECK: OpFunctionEnd -;CHECK: %131 = OpFunction %void None %132 -;CHECK: %133 = OpFunctionParameter %uint -;CHECK: %134 = OpFunctionParameter %uint -;CHECK: %135 = OpFunctionParameter %uint -;CHECK: %136 = OpFunctionParameter %uint -;CHECK: %137 = OpFunctionParameter %uint -;CHECK: %138 = OpLabel -;CHECK: %142 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_0 -;CHECK: %144 = OpAtomicIAdd %uint %142 %uint_4 %uint_0 %uint_11 -;CHECK: %145 = OpIAdd %uint %144 %uint_11 -;CHECK: %146 = OpArrayLength %uint %141 1 -;CHECK: %147 = OpULessThanEqual %bool %145 %146 -;CHECK: OpSelectionMerge %148 None -;CHECK: OpBranchConditional %147 %149 %148 -;CHECK: %149 = OpLabel -;CHECK: %150 = OpIAdd %uint %144 %uint_0 -;CHECK: %151 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %150 -;CHECK: OpStore %151 %uint_11 -;CHECK: %153 = OpIAdd %uint %144 %uint_1 -;CHECK: %154 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %153 -;CHECK: OpStore %154 %uint_23 -;CHECK: %155 = OpIAdd %uint %144 %uint_2 -;CHECK: %156 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %155 -;CHECK: OpStore %156 %133 -;CHECK: %158 = OpIAdd %uint %144 %uint_3 -;CHECK: %159 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %158 -;CHECK: OpStore %159 %uint_4 -;CHECK: %162 = OpLoad %v4float %gl_FragCoord -;CHECK: %164 = OpBitcast %v4uint %162 -;CHECK: %165 = OpCompositeExtract %uint %164 0 -;CHECK: %166 = OpIAdd %uint %144 %uint_4 -;CHECK: %167 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %166 -;CHECK: OpStore %167 %165 -;CHECK: %168 = OpCompositeExtract %uint %164 1 -;CHECK: %170 = OpIAdd %uint %144 %uint_5 -;CHECK: %171 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %170 -;CHECK: OpStore %171 %168 -;CHECK: %172 = OpIAdd %uint %144 %uint_7 -;CHECK: %173 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %172 -;CHECK: OpStore %173 %134 -;CHECK: %175 = OpIAdd %uint %144 %uint_8 -;CHECK: %176 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %175 -;CHECK: OpStore %176 %135 -;CHECK: %178 = OpIAdd %uint %144 %uint_9 -;CHECK: %179 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %178 -;CHECK: OpStore %179 %136 -;CHECK: %181 = OpIAdd %uint %144 %uint_10 -;CHECK: %182 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %181 -;CHECK: OpStore %182 %137 -;CHECK: OpBranch %148 -;CHECK: %148 = OpLabel -;CHECK: OpReturn -;CHECK: OpFunctionEnd - )"; +)" + kDirectRead3 + kStreamWrite5Frag; + // clang-format on SetTargetEnv(SPV_ENV_VULKAN_1_2); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); @@ -7733,13 +4604,14 @@ TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) { // // Same source as UniformArrayRefNoDescInit + // clang-format off const std::string text = R"( OpCapability Shader ;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor -;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %113 %144 %gl_FragCoord +;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_FragCoord OpExecutionMode %MainPs OriginUpperLeft OpSource HLSL 500 OpName %MainPs "MainPs" @@ -7776,15 +4648,7 @@ TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) { OpDecorate %i_vTextureCoords Location 0 OpDecorate %_entryPointOutput_vColor Location 0 ;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -;CHECK: OpDecorate %_struct_111 Block -;CHECK: OpMemberDecorate %_struct_111 0 Offset 0 -;CHECK: OpDecorate %113 DescriptorSet 7 -;CHECK: OpDecorate %113 Binding 1 -;CHECK: OpDecorate %_struct_142 Block -;CHECK: OpMemberDecorate %_struct_142 0 Offset 0 -;CHECK: OpMemberDecorate %_struct_142 1 Offset 4 -;CHECK: OpDecorate %144 DescriptorSet 7 -;CHECK: OpDecorate %144 Binding 0 +)" + kInputDecorations + kOutputDecorations + R"( ;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %3 = OpTypeFunction %void @@ -7826,16 +4690,12 @@ TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) { ;CHECK: %uint_2 = OpConstant %uint 2 ;CHECK: %104 = OpTypeFunction %uint %uint %uint %uint %uint ;CHECK:%_runtimearr_uint = OpTypeRuntimeArray %uint -;CHECK:%_struct_111 = OpTypeStruct %_runtimearr_uint -;CHECK:%_ptr_StorageBuffer__struct_111 = OpTypePointer StorageBuffer %_struct_111 -;CHECK: %113 = OpVariable %_ptr_StorageBuffer__struct_111 StorageBuffer +)" + kInputGlobals + R"( ;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint ;CHECK: %bool = OpTypeBool ;CHECK: %uint_4 = OpConstant %uint 4 ;CHECK: %135 = OpTypeFunction %void %uint %uint %uint %uint %uint -;CHECK:%_struct_142 = OpTypeStruct %uint %_runtimearr_uint -;CHECK:%_ptr_StorageBuffer__struct_142 = OpTypePointer StorageBuffer %_struct_142 -;CHECK: %144 = OpVariable %_ptr_StorageBuffer__struct_142 StorageBuffer +)" + kOutputGlobals + R"( ;CHECK: %uint_11 = OpConstant %uint 11 ;CHECK: %uint_1 = OpConstant %uint 1 ;CHECK: %uint_23 = OpConstant %uint 23 @@ -7853,8 +4713,8 @@ TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) { ;CHECK: %201 = OpConstantNull %v4float %MainPs = OpFunction %void None %3 %5 = OpLabel -;CHECK: %126 = OpFunctionCall %uint %103 %uint_0 %uint_0 %uint_2 %uint_0 -;CHECK: %191 = OpFunctionCall %uint %103 %uint_0 %uint_0 %uint_0 %uint_0 +;CHECK: %126 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_2 %uint_0 +;CHECK: %191 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0 ;CHECK: OpBranch %93 ;CHECK: %93 = OpLabel ;CHECK: OpBranch %92 @@ -7878,7 +4738,7 @@ TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) { ;CHECK: %133 = OpLoad %v2float %81 ;CHECK: OpBranch %130 ;CHECK: %132 = OpLabel -;CHECK: %188 = OpFunctionCall %void %134 %uint_78 %uint_4 %uint_0 %101 %126 +;CHECK: %188 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_78 %uint_4 %uint_0 %101 %126 ;CHECK: OpBranch %130 ;CHECK: %130 = OpLabel ;CHECK: %190 = OpPhi %v2float %133 %131 %189 %132 @@ -7899,86 +4759,15 @@ TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) { ;CHECK: %198 = OpImageSampleImplicitLod %v4float %197 %86 ;CHECK: OpBranch %193 ;CHECK: %195 = OpLabel -;CHECK: %200 = OpFunctionCall %void %134 %uint_83 %uint_1 %uint_0 %uint_0 %uint_0 +;CHECK: %200 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_83 %uint_1 %uint_0 %uint_0 %uint_0 ;CHECK: OpBranch %193 ;CHECK: %193 = OpLabel ;CHECK: %202 = OpPhi %v4float %198 %194 %201 %195 ;CHECK: OpStore %_entryPointOutput_vColor %202 OpReturn OpFunctionEnd -;CHECK: %103 = OpFunction %uint None %104 -;CHECK: %105 = OpFunctionParameter %uint -;CHECK: %106 = OpFunctionParameter %uint -;CHECK: %107 = OpFunctionParameter %uint -;CHECK: %108 = OpFunctionParameter %uint -;CHECK: %109 = OpLabel -;CHECK: %115 = OpAccessChain %_ptr_StorageBuffer_uint %113 %uint_0 %105 -;CHECK: %116 = OpLoad %uint %115 -;CHECK: %117 = OpIAdd %uint %116 %106 -;CHECK: %118 = OpAccessChain %_ptr_StorageBuffer_uint %113 %uint_0 %117 -;CHECK: %119 = OpLoad %uint %118 -;CHECK: %120 = OpIAdd %uint %119 %107 -;CHECK: %121 = OpAccessChain %_ptr_StorageBuffer_uint %113 %uint_0 %120 -;CHECK: %122 = OpLoad %uint %121 -;CHECK: %123 = OpIAdd %uint %122 %108 -;CHECK: %124 = OpAccessChain %_ptr_StorageBuffer_uint %113 %uint_0 %123 -;CHECK: %125 = OpLoad %uint %124 -;CHECK: OpReturnValue %125 -;CHECK: OpFunctionEnd -;CHECK: %134 = OpFunction %void None %135 -;CHECK: %136 = OpFunctionParameter %uint -;CHECK: %137 = OpFunctionParameter %uint -;CHECK: %138 = OpFunctionParameter %uint -;CHECK: %139 = OpFunctionParameter %uint -;CHECK: %140 = OpFunctionParameter %uint -;CHECK: %141 = OpLabel -;CHECK: %145 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_0 -;CHECK: %147 = OpAtomicIAdd %uint %145 %uint_4 %uint_0 %uint_11 -;CHECK: %148 = OpIAdd %uint %147 %uint_11 -;CHECK: %149 = OpArrayLength %uint %144 1 -;CHECK: %150 = OpULessThanEqual %bool %148 %149 -;CHECK: OpSelectionMerge %151 None -;CHECK: OpBranchConditional %150 %152 %151 -;CHECK: %152 = OpLabel -;CHECK: %153 = OpIAdd %uint %147 %uint_0 -;CHECK: %155 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %153 -;CHECK: OpStore %155 %uint_11 -;CHECK: %157 = OpIAdd %uint %147 %uint_1 -;CHECK: %158 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %157 -;CHECK: OpStore %158 %uint_23 -;CHECK: %159 = OpIAdd %uint %147 %uint_2 -;CHECK: %160 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %159 -;CHECK: OpStore %160 %136 -;CHECK: %162 = OpIAdd %uint %147 %uint_3 -;CHECK: %163 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %162 -;CHECK: OpStore %163 %uint_4 -;CHECK: %166 = OpLoad %v4float %gl_FragCoord -;CHECK: %168 = OpBitcast %v4uint %166 -;CHECK: %169 = OpCompositeExtract %uint %168 0 -;CHECK: %170 = OpIAdd %uint %147 %uint_4 -;CHECK: %171 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %170 -;CHECK: OpStore %171 %169 -;CHECK: %172 = OpCompositeExtract %uint %168 1 -;CHECK: %174 = OpIAdd %uint %147 %uint_5 -;CHECK: %175 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %174 -;CHECK: OpStore %175 %172 -;CHECK: %176 = OpIAdd %uint %147 %uint_7 -;CHECK: %177 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %176 -;CHECK: OpStore %177 %137 -;CHECK: %179 = OpIAdd %uint %147 %uint_8 -;CHECK: %180 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %179 -;CHECK: OpStore %180 %138 -;CHECK: %182 = OpIAdd %uint %147 %uint_9 -;CHECK: %183 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %182 -;CHECK: OpStore %183 %139 -;CHECK: %185 = OpIAdd %uint %147 %uint_10 -;CHECK: %186 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %185 -;CHECK: OpStore %186 %140 -;CHECK: OpBranch %151 -;CHECK: %151 = OpLabel -;CHECK: OpReturn -;CHECK: OpFunctionEnd - )"; +)" + kDirectRead4 + kStreamWrite5Frag; + // clang-format on SetTargetEnv(SPV_ENV_VULKAN_1_2); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); @@ -7992,6 +4781,7 @@ TEST_F(InstBindlessTest, Descriptor16BitIdxRef) { // // Use Simple source with min16uint g_nDataIdx + // clang-format off const std::string text = R"( OpCapability Shader OpCapability Int16 @@ -8000,7 +4790,7 @@ TEST_F(InstBindlessTest, Descriptor16BitIdxRef) { %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %_ %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor -;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %_ %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %60 %gl_FragCoord %119 +;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %_ %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %inst_bindless_output_buffer %gl_FragCoord %inst_bindless_input_buffer OpExecutionMode %MainPs OriginUpperLeft OpSource HLSL 500 OpName %MainPs "MainPs" @@ -8020,16 +4810,9 @@ TEST_F(InstBindlessTest, Descriptor16BitIdxRef) { OpDecorate %i_vTextureCoords Location 0 OpDecorate %_entryPointOutput_vColor Location 0 ;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -;CHECK: OpDecorate %_struct_58 Block -;CHECK: OpMemberDecorate %_struct_58 0 Offset 0 -;CHECK: OpMemberDecorate %_struct_58 1 Offset 4 -;CHECK: OpDecorate %60 DescriptorSet 7 -;CHECK: OpDecorate %60 Binding 0 +)" + kOutputDecorations + R"( ;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord -;CHECK: OpDecorate %_struct_117 Block -;CHECK: OpMemberDecorate %_struct_117 0 Offset 0 -;CHECK: OpDecorate %119 DescriptorSet 7 -;CHECK: OpDecorate %119 Binding 1 +)" + kInputDecorations + R"( %void = OpTypeVoid %10 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -8061,9 +4844,7 @@ TEST_F(InstBindlessTest, Descriptor16BitIdxRef) { ;CHECK: %bool = OpTypeBool ;CHECK: %51 = OpTypeFunction %void %uint %uint %uint %uint ;CHECK:%_runtimearr_uint = OpTypeRuntimeArray %uint -;CHECK: %_struct_58 = OpTypeStruct %uint %_runtimearr_uint -;CHECK:%_ptr_StorageBuffer__struct_58 = OpTypePointer StorageBuffer %_struct_58 -;CHECK: %60 = OpVariable %_ptr_StorageBuffer__struct_58 StorageBuffer +)" + kOutputGlobals + R"( ;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint ;CHECK: %uint_10 = OpConstant %uint 10 ;CHECK: %uint_4 = OpConstant %uint 4 @@ -8081,9 +4862,7 @@ TEST_F(InstBindlessTest, Descriptor16BitIdxRef) { ;CHECK: %uint_60 = OpConstant %uint 60 ;CHECK: %106 = OpConstantNull %v4float ;CHECK: %111 = OpTypeFunction %uint %uint %uint %uint %uint -;CHECK:%_struct_117 = OpTypeStruct %_runtimearr_uint -;CHECK:%_ptr_StorageBuffer__struct_117 = OpTypePointer StorageBuffer %_struct_117 -;CHECK: %119 = OpVariable %_ptr_StorageBuffer__struct_117 StorageBuffer +)" + kInputGlobals + R"( %MainPs = OpFunction %void None %10 %30 = OpLabel ;CHECK: OpBranch %108 @@ -8109,7 +4888,7 @@ TEST_F(InstBindlessTest, Descriptor16BitIdxRef) { ;CHECK: %47 = OpLoad %16 %34 ;CHECK: %48 = OpSampledImage %27 %47 %36 ;CHECK: %109 = OpUConvert %uint %33 -;CHECK: %131 = OpFunctionCall %uint %110 %uint_0 %uint_0 %uint_0 %109 +;CHECK: %131 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %109 ;CHECK: %132 = OpULessThan %bool %uint_0 %131 ;CHECK: OpSelectionMerge %133 None ;CHECK: OpBranchConditional %132 %134 %135 @@ -8120,88 +4899,21 @@ TEST_F(InstBindlessTest, Descriptor16BitIdxRef) { ;CHECK: OpBranch %133 ;CHECK: %135 = OpLabel ;CHECK: %139 = OpUConvert %uint %33 -;CHECK: %140 = OpFunctionCall %void %50 %uint_60 %uint_1 %139 %uint_0 +;CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_60 %uint_1 %139 %uint_0 ;CHECK: OpBranch %133 ;CHECK: %133 = OpLabel ;CHECK: %141 = OpPhi %v4float %138 %134 %106 %135 ;CHECK: OpBranch %44 ;CHECK: %46 = OpLabel -;CHECK: %105 = OpFunctionCall %void %50 %uint_60 %uint_0 %41 %uint_128 +;CHECK: %105 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_60 %uint_0 %41 %uint_128 ;CHECK: OpBranch %44 ;CHECK: %44 = OpLabel ;CHECK: %107 = OpPhi %v4float %141 %133 %106 %46 ;CHECK: OpStore %_entryPointOutput_vColor %107 OpReturn OpFunctionEnd -;CHECK: %50 = OpFunction %void None %51 -;CHECK: %52 = OpFunctionParameter %uint -;CHECK: %53 = OpFunctionParameter %uint -;CHECK: %54 = OpFunctionParameter %uint -;CHECK: %55 = OpFunctionParameter %uint -;CHECK: %56 = OpLabel -;CHECK: %62 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_0 -;CHECK: %65 = OpAtomicIAdd %uint %62 %uint_4 %uint_0 %uint_10 -;CHECK: %66 = OpIAdd %uint %65 %uint_10 -;CHECK: %67 = OpArrayLength %uint %60 1 -;CHECK: %68 = OpULessThanEqual %bool %66 %67 -;CHECK: OpSelectionMerge %69 None -;CHECK: OpBranchConditional %68 %70 %69 -;CHECK: %70 = OpLabel -;CHECK: %71 = OpIAdd %uint %65 %uint_0 -;CHECK: %73 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %71 -;CHECK: OpStore %73 %uint_10 -;CHECK: %75 = OpIAdd %uint %65 %uint_1 -;CHECK: %76 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %75 -;CHECK: OpStore %76 %uint_23 -;CHECK: %78 = OpIAdd %uint %65 %uint_2 -;CHECK: %79 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %78 -;CHECK: OpStore %79 %52 -;CHECK: %81 = OpIAdd %uint %65 %uint_3 -;CHECK: %82 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %81 -;CHECK: OpStore %82 %uint_4 -;CHECK: %85 = OpLoad %v4float %gl_FragCoord -;CHECK: %87 = OpBitcast %v4uint %85 -;CHECK: %88 = OpCompositeExtract %uint %87 0 -;CHECK: %89 = OpIAdd %uint %65 %uint_4 -;CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %89 -;CHECK: OpStore %90 %88 -;CHECK: %91 = OpCompositeExtract %uint %87 1 -;CHECK: %93 = OpIAdd %uint %65 %uint_5 -;CHECK: %94 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %93 -;CHECK: OpStore %94 %91 -;CHECK: %96 = OpIAdd %uint %65 %uint_7 -;CHECK: %97 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %96 -;CHECK: OpStore %97 %53 -;CHECK: %99 = OpIAdd %uint %65 %uint_8 -;CHECK: %100 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %99 -;CHECK: OpStore %100 %54 -;CHECK: %102 = OpIAdd %uint %65 %uint_9 -;CHECK: %103 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %102 -;CHECK: OpStore %103 %55 -;CHECK: OpBranch %69 -;CHECK: %69 = OpLabel -;CHECK: OpReturn -;CHECK: OpFunctionEnd -;CHECK: %110 = OpFunction %uint None %111 -;CHECK: %112 = OpFunctionParameter %uint -;CHECK: %113 = OpFunctionParameter %uint -;CHECK: %114 = OpFunctionParameter %uint -;CHECK: %115 = OpFunctionParameter %uint -;CHECK: %116 = OpLabel -;CHECK: %120 = OpAccessChain %_ptr_StorageBuffer_uint %119 %uint_0 %112 -;CHECK: %121 = OpLoad %uint %120 -;CHECK: %122 = OpIAdd %uint %121 %113 -;CHECK: %123 = OpAccessChain %_ptr_StorageBuffer_uint %119 %uint_0 %122 -;CHECK: %124 = OpLoad %uint %123 -;CHECK: %125 = OpIAdd %uint %124 %114 -;CHECK: %126 = OpAccessChain %_ptr_StorageBuffer_uint %119 %uint_0 %125 -;CHECK: %127 = OpLoad %uint %126 -;CHECK: %128 = OpIAdd %uint %127 %115 -;CHECK: %129 = OpAccessChain %_ptr_StorageBuffer_uint %119 %uint_0 %128 -;CHECK: %130 = OpLoad %uint %129 -;CHECK: OpReturnValue %130 -;CHECK: OpFunctionEnd - )"; +)" + kStreamWrite4Frag + kDirectRead4; + // clang-format on SetTargetEnv(SPV_ENV_VULKAN_1_2); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); @@ -8245,6 +4957,7 @@ TEST_F(InstBindlessTest, UniformArray16bitIdxRef) { // return ps_output; // } + // clang-format off const std::string text = R"( OpCapability Shader OpCapability Int16 @@ -8253,7 +4966,7 @@ TEST_F(InstBindlessTest, UniformArray16bitIdxRef) { %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor -;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %69 %97 %gl_FragCoord +;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_FragCoord OpExecutionMode %MainPs OriginUpperLeft OpSource HLSL 500 OpName %MainPs "MainPs" @@ -8290,15 +5003,7 @@ TEST_F(InstBindlessTest, UniformArray16bitIdxRef) { OpDecorate %i_vTextureCoords Location 0 OpDecorate %_entryPointOutput_vColor Location 0 ;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -;CHECK: OpDecorate %_struct_67 Block -;CHECK: OpMemberDecorate %_struct_67 0 Offset 0 -;CHECK: OpDecorate %69 DescriptorSet 7 -;CHECK: OpDecorate %69 Binding 1 -;CHECK: OpDecorate %_struct_95 Block -;CHECK: OpMemberDecorate %_struct_95 0 Offset 0 -;CHECK: OpMemberDecorate %_struct_95 1 Offset 4 -;CHECK: OpDecorate %97 DescriptorSet 7 -;CHECK: OpDecorate %97 Binding 0 +)" + kInputDecorations + kOutputDecorations + R"( ;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %14 = OpTypeFunction %void @@ -8341,16 +5046,12 @@ TEST_F(InstBindlessTest, UniformArray16bitIdxRef) { ;CHECK: %uint_1 = OpConstant %uint 1 ;CHECK: %61 = OpTypeFunction %uint %uint %uint %uint ;CHECK:%_runtimearr_uint = OpTypeRuntimeArray %uint -;CHECK: %_struct_67 = OpTypeStruct %_runtimearr_uint -;CHECK:%_ptr_StorageBuffer__struct_67 = OpTypePointer StorageBuffer %_struct_67 -;CHECK: %69 = OpVariable %_ptr_StorageBuffer__struct_67 StorageBuffer +)" + kInputGlobals + R"( ;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint ;CHECK: %bool = OpTypeBool ;CHECK: %uint_4 = OpConstant %uint 4 ;CHECK: %88 = OpTypeFunction %void %uint %uint %uint %uint %uint -;CHECK: %_struct_95 = OpTypeStruct %uint %_runtimearr_uint -;CHECK:%_ptr_StorageBuffer__struct_95 = OpTypePointer StorageBuffer %_struct_95 -;CHECK: %97 = OpVariable %_ptr_StorageBuffer__struct_95 StorageBuffer +)" + kOutputGlobals + R"( ;CHECK: %uint_11 = OpConstant %uint 11 ;CHECK: %uint_23 = OpConstant %uint 23 ;CHECK: %uint_2 = OpConstant %uint 2 @@ -8366,7 +5067,7 @@ TEST_F(InstBindlessTest, UniformArray16bitIdxRef) { ;CHECK: %142 = OpConstantNull %v2float %MainPs = OpFunction %void None %14 %37 = OpLabel -;CHECK: %79 = OpFunctionCall %uint %60 %uint_1 %uint_0 %uint_0 +;CHECK: %79 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_0 %uint_0 ;CHECK: OpBranch %49 ;CHECK: %49 = OpLabel ;CHECK: OpBranch %48 @@ -8391,7 +5092,7 @@ TEST_F(InstBindlessTest, UniformArray16bitIdxRef) { ;CHECK: %86 = OpLoad %v2float %41 ;CHECK: OpBranch %83 ;CHECK: %85 = OpLabel -;CHECK: %141 = OpFunctionCall %void %87 %uint_81 %uint_4 %uint_0 %58 %79 +;CHECK: %141 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_81 %uint_4 %uint_0 %58 %79 ;CHECK: OpBranch %83 ;CHECK: %83 = OpLabel ;CHECK: %143 = OpPhi %v2float %86 %84 %142 %85 @@ -8403,75 +5104,8 @@ TEST_F(InstBindlessTest, UniformArray16bitIdxRef) { OpStore %_entryPointOutput_vColor %47 OpReturn OpFunctionEnd -;CHECK: %60 = OpFunction %uint None %61 -;CHECK: %62 = OpFunctionParameter %uint -;CHECK: %63 = OpFunctionParameter %uint -;CHECK: %64 = OpFunctionParameter %uint -;CHECK: %65 = OpLabel -;CHECK: %71 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_0 %62 -;CHECK: %72 = OpLoad %uint %71 -;CHECK: %73 = OpIAdd %uint %72 %63 -;CHECK: %74 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_0 %73 -;CHECK: %75 = OpLoad %uint %74 -;CHECK: %76 = OpIAdd %uint %75 %64 -;CHECK: %77 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_0 %76 -;CHECK: %78 = OpLoad %uint %77 -;CHECK: OpReturnValue %78 -;CHECK: OpFunctionEnd -;CHECK: %87 = OpFunction %void None %88 -;CHECK: %89 = OpFunctionParameter %uint -;CHECK: %90 = OpFunctionParameter %uint -;CHECK: %91 = OpFunctionParameter %uint -;CHECK: %92 = OpFunctionParameter %uint -;CHECK: %93 = OpFunctionParameter %uint -;CHECK: %94 = OpLabel -;CHECK: %98 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_0 -;CHECK: %100 = OpAtomicIAdd %uint %98 %uint_4 %uint_0 %uint_11 -;CHECK: %101 = OpIAdd %uint %100 %uint_11 -;CHECK: %102 = OpArrayLength %uint %97 1 -;CHECK: %103 = OpULessThanEqual %bool %101 %102 -;CHECK: OpSelectionMerge %104 None -;CHECK: OpBranchConditional %103 %105 %104 -;CHECK: %105 = OpLabel -;CHECK: %106 = OpIAdd %uint %100 %uint_0 -;CHECK: %107 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %106 -;CHECK: OpStore %107 %uint_11 -;CHECK: %109 = OpIAdd %uint %100 %uint_1 -;CHECK: %110 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %109 -;CHECK: OpStore %110 %uint_23 -;CHECK: %112 = OpIAdd %uint %100 %uint_2 -;CHECK: %113 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %112 -;CHECK: OpStore %113 %89 -;CHECK: %115 = OpIAdd %uint %100 %uint_3 -;CHECK: %116 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %115 -;CHECK: OpStore %116 %uint_4 -;CHECK: %119 = OpLoad %v4float %gl_FragCoord -;CHECK: %121 = OpBitcast %v4uint %119 -;CHECK: %122 = OpCompositeExtract %uint %121 0 -;CHECK: %123 = OpIAdd %uint %100 %uint_4 -;CHECK: %124 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %123 -;CHECK: OpStore %124 %122 -;CHECK: %125 = OpCompositeExtract %uint %121 1 -;CHECK: %127 = OpIAdd %uint %100 %uint_5 -;CHECK: %128 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %127 -;CHECK: OpStore %128 %125 -;CHECK: %129 = OpIAdd %uint %100 %uint_7 -;CHECK: %130 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %129 -;CHECK: OpStore %130 %90 -;CHECK: %132 = OpIAdd %uint %100 %uint_8 -;CHECK: %133 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %132 -;CHECK: OpStore %133 %91 -;CHECK: %135 = OpIAdd %uint %100 %uint_9 -;CHECK: %136 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %135 -;CHECK: OpStore %136 %92 -;CHECK: %138 = OpIAdd %uint %100 %uint_10 -;CHECK: %139 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %138 -;CHECK: OpStore %139 %93 -;CHECK: OpBranch %104 -;CHECK: %104 = OpLabel -;CHECK: OpReturn -;CHECK: OpFunctionEnd - )"; + )" + kDirectRead3 + kStreamWrite5Frag; + // clang-format on SetTargetEnv(SPV_ENV_VULKAN_1_2); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); @@ -8498,13 +5132,14 @@ TEST_F(InstBindlessTest, UniformMatrixRefRowMajor) { // v_vtxResult = var[2][1]; // } - const std::string text = R"( + // clang-format off + std::string text = R"( OpCapability Shader ;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position -;CHECK: OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %45 %72 %gl_VertexIndex %gl_InstanceIndex +;CHECK: OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_VertexIndex %gl_InstanceIndex OpSource GLSL 450 OpSourceExtension "GL_EXT_scalar_block_layout" OpName %main "main" @@ -8527,16 +5162,9 @@ TEST_F(InstBindlessTest, UniformMatrixRefRowMajor) { ;CHECK: OpDecorate %116 RelaxedPrecision OpDecorate %a_position Location 0 ;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -;CHECK: OpDecorate %_struct_43 Block -;CHECK: OpMemberDecorate %_struct_43 0 Offset 0 -;CHECK: OpDecorate %45 DescriptorSet 7 -;CHECK: OpDecorate %45 Binding 1 +)" + kInputDecorations + R"( ;CHECK: OpDecorate %61 RelaxedPrecision -;CHECK: OpDecorate %_struct_70 Block -;CHECK: OpMemberDecorate %_struct_70 0 Offset 0 -;CHECK: OpMemberDecorate %_struct_70 1 Offset 4 -;CHECK: OpDecorate %72 DescriptorSet 7 -;CHECK: OpDecorate %72 Binding 0 +)" + kOutputDecorations + R"( ;CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex ;CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex %void = OpTypeVoid @@ -8564,15 +5192,11 @@ TEST_F(InstBindlessTest, UniformMatrixRefRowMajor) { ;CHECK; %uint_3 = OpConstant %uint 3 ;CHECK; %37 = OpTypeFunction %uint %uint %uint %uint ;CHECK;%_runtimearr_uint = OpTypeRuntimeArray %uint -;CHECK; %_struct_43 = OpTypeStruct %_runtimearr_uint -;CHECK;%_ptr_StorageBuffer__struct_43 = OpTypePointer StorageBuffer %_struct_43 -;CHECK; %45 = OpVariable %_ptr_StorageBuffer__struct_43 StorageBuffer +)" + kInputGlobals + R"( ;CHECK;%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint ;CHECK; %bool = OpTypeBool ;CHECK; %63 = OpTypeFunction %void %uint %uint %uint %uint %uint -;CHECK; %_struct_70 = OpTypeStruct %uint %_runtimearr_uint -;CHECK;%_ptr_StorageBuffer__struct_70 = OpTypePointer StorageBuffer %_struct_70 -;CHECK; %72 = OpVariable %_ptr_StorageBuffer__struct_70 StorageBuffer +)" + kOutputGlobals + R"( ;CHECK; %uint_11 = OpConstant %uint 11 ;CHECK; %uint_23 = OpConstant %uint 23 ;CHECK; %uint_2 = OpConstant %uint 2 @@ -8588,7 +5212,7 @@ TEST_F(InstBindlessTest, UniformMatrixRefRowMajor) { ;CHECK; %115 = OpConstantNull %float %main = OpFunction %void None %3 %5 = OpLabel -;CHECK: %55 = OpFunctionCall %uint %36 %uint_1 %uint_0 %uint_0 +;CHECK: %55 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_0 %uint_0 ;CHECK: OpBranch %26 ;CHECK: %26 = OpLabel ;CHECK: OpBranch %25 @@ -8608,7 +5232,7 @@ TEST_F(InstBindlessTest, UniformMatrixRefRowMajor) { ;CHECK: %61 = OpLoad %float %20 ;CHECK: OpBranch %58 ;CHECK: %60 = OpLabel -;CHECK: %114 = OpFunctionCall %void %62 %uint_45 %uint_4 %uint_0 %35 %55 +;CHECK: %114 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_45 %uint_4 %uint_0 %35 %55 ;CHECK: OpBranch %58 ;CHECK: %58 = OpLabel ;CHECK: %116 = OpPhi %float %61 %59 %115 %60 @@ -8617,73 +5241,8 @@ TEST_F(InstBindlessTest, UniformMatrixRefRowMajor) { ;CHECK: OpStore %v_vtxResult %116 OpReturn OpFunctionEnd -;CHECK: %36 = OpFunction %uint None %37 -;CHECK: %38 = OpFunctionParameter %uint -;CHECK: %39 = OpFunctionParameter %uint -;CHECK: %40 = OpFunctionParameter %uint -;CHECK: %41 = OpLabel -;CHECK: %47 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_0 %38 -;CHECK: %48 = OpLoad %uint %47 -;CHECK: %49 = OpIAdd %uint %48 %39 -;CHECK: %50 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_0 %49 -;CHECK: %51 = OpLoad %uint %50 -;CHECK: %52 = OpIAdd %uint %51 %40 -;CHECK: %53 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_0 %52 -;CHECK: %54 = OpLoad %uint %53 -;CHECK: OpReturnValue %54 -;CHECK: OpFunctionEnd -;CHECK: %62 = OpFunction %void None %63 -;CHECK: %64 = OpFunctionParameter %uint -;CHECK: %65 = OpFunctionParameter %uint -;CHECK: %66 = OpFunctionParameter %uint -;CHECK: %67 = OpFunctionParameter %uint -;CHECK: %68 = OpFunctionParameter %uint -;CHECK: %69 = OpLabel -;CHECK: %73 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_0 -;CHECK: %75 = OpAtomicIAdd %uint %73 %uint_4 %uint_0 %uint_11 -;CHECK: %76 = OpIAdd %uint %75 %uint_11 -;CHECK: %77 = OpArrayLength %uint %72 1 -;CHECK: %78 = OpULessThanEqual %bool %76 %77 -;CHECK: OpSelectionMerge %79 None -;CHECK: OpBranchConditional %78 %80 %79 -;CHECK: %80 = OpLabel -;CHECK: %81 = OpIAdd %uint %75 %uint_0 -;CHECK: %82 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %81 -;CHECK: OpStore %82 %uint_11 -;CHECK: %84 = OpIAdd %uint %75 %uint_1 -;CHECK: %85 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %84 -;CHECK: OpStore %85 %uint_23 -;CHECK: %87 = OpIAdd %uint %75 %uint_2 -;CHECK: %88 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %87 -;CHECK: OpStore %88 %64 -;CHECK: %89 = OpIAdd %uint %75 %uint_3 -;CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %89 -;CHECK: OpStore %90 %uint_0 -;CHECK: %93 = OpLoad %uint %gl_VertexIndex -;CHECK: %94 = OpIAdd %uint %75 %uint_4 -;CHECK: %95 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %94 -;CHECK: OpStore %95 %93 -;CHECK: %97 = OpLoad %uint %gl_InstanceIndex -;CHECK: %99 = OpIAdd %uint %75 %uint_5 -;CHECK: %100 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %99 -;CHECK: OpStore %100 %97 -;CHECK: %102 = OpIAdd %uint %75 %uint_7 -;CHECK: %103 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %102 -;CHECK: OpStore %103 %65 -;CHECK: %105 = OpIAdd %uint %75 %uint_8 -;CHECK: %106 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %105 -;CHECK: OpStore %106 %66 -;CHECK: %108 = OpIAdd %uint %75 %uint_9 -;CHECK: %109 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %108 -;CHECK: OpStore %109 %67 -;CHECK: %111 = OpIAdd %uint %75 %uint_10 -;CHECK: %112 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %111 -;CHECK: OpStore %112 %68 -;CHECK: OpBranch %79 -;CHECK: %79 = OpLabel -;CHECK: OpReturn -;CHECK: OpFunctionEnd - )"; + )" + kDirectRead3 + kStreamWrite5Vert; + // clang-format on SetTargetEnv(SPV_ENV_VULKAN_1_2); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); @@ -8710,13 +5269,14 @@ TEST_F(InstBindlessTest, UniformMatrixRefColumnMajor) { // v_vtxResult = var[2][1]; // } + // clang-format off const std::string text = R"( OpCapability Shader ;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position -;CHECK: OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %45 %72 %gl_VertexIndex %gl_InstanceIndex +;CHECK: OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_VertexIndex %gl_InstanceIndex OpSource GLSL 450 OpSourceExtension "GL_EXT_scalar_block_layout" OpName %main "main" @@ -8739,16 +5299,9 @@ TEST_F(InstBindlessTest, UniformMatrixRefColumnMajor) { ;CHECK: OpDecorate %115 RelaxedPrecision OpDecorate %a_position Location 0 ;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -;CHECK: OpDecorate %_struct_43 Block -;CHECK: OpMemberDecorate %_struct_43 0 Offset 0 -;CHECK: OpDecorate %45 DescriptorSet 7 -;CHECK: OpDecorate %45 Binding 1 +)" + kInputDecorations + R"( ;CHECK: OpDecorate %61 RelaxedPrecision -;CHECK: OpDecorate %_struct_70 Block -;CHECK: OpMemberDecorate %_struct_70 0 Offset 0 -;CHECK: OpMemberDecorate %_struct_70 1 Offset 4 -;CHECK: OpDecorate %72 DescriptorSet 7 -;CHECK: OpDecorate %72 Binding 0 +)" + kOutputDecorations + R"( ;CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex ;CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex %void = OpTypeVoid @@ -8776,15 +5329,11 @@ TEST_F(InstBindlessTest, UniformMatrixRefColumnMajor) { ;CHECK: %uint_3 = OpConstant %uint 3 ;CHECK: %37 = OpTypeFunction %uint %uint %uint %uint ;CHECK:%_runtimearr_uint = OpTypeRuntimeArray %uint -;CHECK: %_struct_43 = OpTypeStruct %_runtimearr_uint -;CHECK:%_ptr_StorageBuffer__struct_43 = OpTypePointer StorageBuffer %_struct_43 -;CHECK: %45 = OpVariable %_ptr_StorageBuffer__struct_43 StorageBuffer +)" + kInputGlobals + R"( ;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint ;CHECK: %bool = OpTypeBool ;CHECK: %63 = OpTypeFunction %void %uint %uint %uint %uint %uint -;CHECK: %_struct_70 = OpTypeStruct %uint %_runtimearr_uint -;CHECK:%_ptr_StorageBuffer__struct_70 = OpTypePointer StorageBuffer %_struct_70 -;CHECK: %72 = OpVariable %_ptr_StorageBuffer__struct_70 StorageBuffer +)" + kOutputGlobals + R"( ;CHECK: %uint_11 = OpConstant %uint 11 ;CHECK: %uint_23 = OpConstant %uint 23 ;CHECK: %uint_2 = OpConstant %uint 2 @@ -8799,7 +5348,7 @@ TEST_F(InstBindlessTest, UniformMatrixRefColumnMajor) { ;CHECK: %114 = OpConstantNull %float %main = OpFunction %void None %3 %5 = OpLabel -;CHECK: %55 = OpFunctionCall %uint %36 %uint_1 %uint_0 %uint_0 +;CHECK: %55 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_0 %uint_0 ;CHECK: OpBranch %26 ;CHECK: %26 = OpLabel ;CHECK: OpBranch %25 @@ -8819,7 +5368,7 @@ TEST_F(InstBindlessTest, UniformMatrixRefColumnMajor) { ;CHECK: %61 = OpLoad %float %20 ;CHECK: OpBranch %58 ;CHECK: %60 = OpLabel -;CHECK: %113 = OpFunctionCall %void %62 %uint_45 %uint_4 %uint_0 %35 %55 +;CHECK: %113 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_45 %uint_4 %uint_0 %35 %55 ;CHECK: OpBranch %58 ;CHECK: %58 = OpLabel ;CHECK: %115 = OpPhi %float %61 %59 %114 %60 @@ -8828,75 +5377,11 @@ TEST_F(InstBindlessTest, UniformMatrixRefColumnMajor) { ;CHECK: OpStore %v_vtxResult %115 OpReturn OpFunctionEnd -;CHECK: %36 = OpFunction %uint None %37 -;CHECK: %38 = OpFunctionParameter %uint -;CHECK: %39 = OpFunctionParameter %uint -;CHECK: %40 = OpFunctionParameter %uint -;CHECK: %41 = OpLabel -;CHECK: %47 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_0 %38 -;CHECK: %48 = OpLoad %uint %47 -;CHECK: %49 = OpIAdd %uint %48 %39 -;CHECK: %50 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_0 %49 -;CHECK: %51 = OpLoad %uint %50 -;CHECK: %52 = OpIAdd %uint %51 %40 -;CHECK: %53 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_0 %52 -;CHECK: %54 = OpLoad %uint %53 -;CHECK: OpReturnValue %54 -;CHECK: OpFunctionEnd -;CHECK: %62 = OpFunction %void None %63 -;CHECK: %64 = OpFunctionParameter %uint -;CHECK: %65 = OpFunctionParameter %uint -;CHECK: %66 = OpFunctionParameter %uint -;CHECK: %67 = OpFunctionParameter %uint -;CHECK: %68 = OpFunctionParameter %uint -;CHECK: %69 = OpLabel -;CHECK: %73 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_0 -;CHECK: %75 = OpAtomicIAdd %uint %73 %uint_4 %uint_0 %uint_11 -;CHECK: %76 = OpIAdd %uint %75 %uint_11 -;CHECK: %77 = OpArrayLength %uint %72 1 -;CHECK: %78 = OpULessThanEqual %bool %76 %77 -;CHECK: OpSelectionMerge %79 None -;CHECK: OpBranchConditional %78 %80 %79 -;CHECK: %80 = OpLabel -;CHECK: %81 = OpIAdd %uint %75 %uint_0 -;CHECK: %82 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %81 -;CHECK: OpStore %82 %uint_11 -;CHECK: %84 = OpIAdd %uint %75 %uint_1 -;CHECK: %85 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %84 -;CHECK: OpStore %85 %uint_23 -;CHECK: %87 = OpIAdd %uint %75 %uint_2 -;CHECK: %88 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %87 -;CHECK: OpStore %88 %64 -;CHECK: %89 = OpIAdd %uint %75 %uint_3 -;CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %89 -;CHECK: OpStore %90 %uint_0 -;CHECK: %93 = OpLoad %uint %gl_VertexIndex -;CHECK: %94 = OpIAdd %uint %75 %uint_4 -;CHECK: %95 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %94 -;CHECK: OpStore %95 %93 -;CHECK: %97 = OpLoad %uint %gl_InstanceIndex -;CHECK: %99 = OpIAdd %uint %75 %uint_5 -;CHECK: %100 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %99 -;CHECK: OpStore %100 %97 -;CHECK: %102 = OpIAdd %uint %75 %uint_7 -;CHECK: %103 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %102 -;CHECK: OpStore %103 %65 -;CHECK: %104 = OpIAdd %uint %75 %uint_8 -;CHECK: %105 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %104 -;CHECK: OpStore %105 %66 -;CHECK: %107 = OpIAdd %uint %75 %uint_9 -;CHECK: %108 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %107 -;CHECK: OpStore %108 %67 -;CHECK: %110 = OpIAdd %uint %75 %uint_10 -;CHECK: %111 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %110 -;CHECK: OpStore %111 %68 -;CHECK: OpBranch %79 -;CHECK: %79 = OpLabel -;CHECK: OpReturn -;CHECK: OpFunctionEnd - )"; + )" + kDirectRead3 + kStreamWrite5Vert; + // clang-format on SetTargetEnv(SPV_ENV_VULKAN_1_2); + ValidatorOptions()->uniform_buffer_standard_layout = true; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndMatch(text, true, 7u, 23u, false, false, true, false, true); @@ -8921,13 +5406,14 @@ TEST_F(InstBindlessTest, UniformMatrixVecRefRowMajor) { // v_vtxResult = var[2][3][1]; // } + // clang-format off const std::string text = R"( OpCapability Shader ;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position -;CHECK: OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %54 %81 %gl_VertexIndex %gl_InstanceIndex +;CHECK: OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_VertexIndex %gl_InstanceIndex OpSource GLSL 450 OpSourceExtension "GL_EXT_scalar_block_layout" OpName %main "main" @@ -8951,16 +5437,9 @@ TEST_F(InstBindlessTest, UniformMatrixVecRefRowMajor) { ;CHECK: OpDecorate %125 RelaxedPrecision OpDecorate %a_position Location 0 ;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -;CHECK: OpDecorate %_struct_52 Block -;CHECK: OpMemberDecorate %_struct_52 0 Offset 0 -;CHECK: OpDecorate %54 DescriptorSet 7 -;CHECK: OpDecorate %54 Binding 1 +)" + kInputDecorations + R"( ;CHECK: OpDecorate %70 RelaxedPrecision -;CHECK: OpDecorate %_struct_79 Block -;CHECK: OpMemberDecorate %_struct_79 0 Offset 0 -;CHECK: OpMemberDecorate %_struct_79 1 Offset 4 -;CHECK: OpDecorate %81 DescriptorSet 7 -;CHECK: OpDecorate %81 Binding 0 +)" + kOutputDecorations + R"( ;CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex ;CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex %void = OpTypeVoid @@ -8995,15 +5474,11 @@ TEST_F(InstBindlessTest, UniformMatrixVecRefRowMajor) { ;CHECK: %uint_1 = OpConstant %uint 1 ;CHECK: %46 = OpTypeFunction %uint %uint %uint %uint ;CHECK:%_runtimearr_uint = OpTypeRuntimeArray %uint -;CHECK: %_struct_52 = OpTypeStruct %_runtimearr_uint -;CHECK:%_ptr_StorageBuffer__struct_52 = OpTypePointer StorageBuffer %_struct_52 -;CHECK: %54 = OpVariable %_ptr_StorageBuffer__struct_52 StorageBuffer +)" + kInputGlobals + R"( ;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint ;CHECK: %bool = OpTypeBool ;CHECK: %72 = OpTypeFunction %void %uint %uint %uint %uint %uint -;CHECK: %_struct_79 = OpTypeStruct %uint %_runtimearr_uint -;CHECK:%_ptr_StorageBuffer__struct_79 = OpTypePointer StorageBuffer %_struct_79 -;CHECK: %81 = OpVariable %_ptr_StorageBuffer__struct_79 StorageBuffer +)" + kOutputGlobals + R"( ;CHECK: %uint_11 = OpConstant %uint 11 ;CHECK: %uint_23 = OpConstant %uint 23 ;CHECK: %uint_2 = OpConstant %uint 2 @@ -9019,7 +5494,7 @@ TEST_F(InstBindlessTest, UniformMatrixVecRefRowMajor) { ;CHECK: %124 = OpConstantNull %v2float %main = OpFunction %void None %3 %5 = OpLabel -;CHECK: %64 = OpFunctionCall %uint %45 %uint_1 %uint_0 %uint_0 +;CHECK: %64 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_0 %uint_0 ;CHECK: OpBranch %31 ;CHECK: %31 = OpLabel ;CHECK: OpBranch %30 @@ -9043,80 +5518,15 @@ TEST_F(InstBindlessTest, UniformMatrixVecRefRowMajor) { ;CHECK: %70 = OpLoad %v2float %25 ;CHECK: OpBranch %67 ;CHECK: %69 = OpLabel -;CHECK: %123 = OpFunctionCall %void %71 %uint_51 %uint_4 %uint_0 %43 %64 +;CHECK: %123 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_51 %uint_4 %uint_0 %43 %64 ;CHECK: OpBranch %67 ;CHECK: %67 = OpLabel ;CHECK: %125 = OpPhi %v2float %70 %68 %124 %69 ;CHECK: OpStore %v_vtxResult %125 OpReturn OpFunctionEnd -;CHECK: %45 = OpFunction %uint None %46 -;CHECK: %47 = OpFunctionParameter %uint -;CHECK: %48 = OpFunctionParameter %uint -;CHECK: %49 = OpFunctionParameter %uint -;CHECK: %50 = OpLabel -;CHECK: %56 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_0 %47 -;CHECK: %57 = OpLoad %uint %56 -;CHECK: %58 = OpIAdd %uint %57 %48 -;CHECK: %59 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_0 %58 -;CHECK: %60 = OpLoad %uint %59 -;CHECK: %61 = OpIAdd %uint %60 %49 -;CHECK: %62 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_0 %61 -;CHECK: %63 = OpLoad %uint %62 -;CHECK: OpReturnValue %63 -;CHECK: OpFunctionEnd -;CHECK: %71 = OpFunction %void None %72 -;CHECK: %73 = OpFunctionParameter %uint -;CHECK: %74 = OpFunctionParameter %uint -;CHECK: %75 = OpFunctionParameter %uint -;CHECK: %76 = OpFunctionParameter %uint -;CHECK: %77 = OpFunctionParameter %uint -;CHECK: %78 = OpLabel -;CHECK: %82 = OpAccessChain %_ptr_StorageBuffer_uint %81 %uint_0 -;CHECK: %84 = OpAtomicIAdd %uint %82 %uint_4 %uint_0 %uint_11 -;CHECK: %85 = OpIAdd %uint %84 %uint_11 -;CHECK: %86 = OpArrayLength %uint %81 1 -;CHECK: %87 = OpULessThanEqual %bool %85 %86 -;CHECK: OpSelectionMerge %88 None -;CHECK: OpBranchConditional %87 %89 %88 -;CHECK: %89 = OpLabel -;CHECK: %90 = OpIAdd %uint %84 %uint_0 -;CHECK: %91 = OpAccessChain %_ptr_StorageBuffer_uint %81 %uint_1 %90 -;CHECK: OpStore %91 %uint_11 -;CHECK: %93 = OpIAdd %uint %84 %uint_1 -;CHECK: %94 = OpAccessChain %_ptr_StorageBuffer_uint %81 %uint_1 %93 -;CHECK: OpStore %94 %uint_23 -;CHECK: %96 = OpIAdd %uint %84 %uint_2 -;CHECK: %97 = OpAccessChain %_ptr_StorageBuffer_uint %81 %uint_1 %96 -;CHECK: OpStore %97 %73 -;CHECK: %98 = OpIAdd %uint %84 %uint_3 -;CHECK: %99 = OpAccessChain %_ptr_StorageBuffer_uint %81 %uint_1 %98 -;CHECK: OpStore %99 %uint_0 -;CHECK: %102 = OpLoad %uint %gl_VertexIndex -;CHECK: %103 = OpIAdd %uint %84 %uint_4 -;CHECK: %104 = OpAccessChain %_ptr_StorageBuffer_uint %81 %uint_1 %103 -;CHECK: OpStore %104 %102 -;CHECK: %106 = OpLoad %uint %gl_InstanceIndex -;CHECK: %108 = OpIAdd %uint %84 %uint_5 -;CHECK: %109 = OpAccessChain %_ptr_StorageBuffer_uint %81 %uint_1 %108 -;CHECK: OpStore %109 %106 -;CHECK: %111 = OpIAdd %uint %84 %uint_7 -;CHECK: %112 = OpAccessChain %_ptr_StorageBuffer_uint %81 %uint_1 %111 -;CHECK: OpStore %112 %74 -;CHECK: %114 = OpIAdd %uint %84 %uint_8 -;CHECK: %115 = OpAccessChain %_ptr_StorageBuffer_uint %81 %uint_1 %114 -;CHECK: OpStore %115 %75 -;CHECK: %117 = OpIAdd %uint %84 %uint_9 -;CHECK: %118 = OpAccessChain %_ptr_StorageBuffer_uint %81 %uint_1 %117 -;CHECK: OpStore %118 %76 -;CHECK: %120 = OpIAdd %uint %84 %uint_10 -;CHECK: %121 = OpAccessChain %_ptr_StorageBuffer_uint %81 %uint_1 %120 -;CHECK: OpStore %121 %77 -;CHECK: OpBranch %88 -;CHECK: %88 = OpLabel -;CHECK: OpReturn -;CHECK: OpFunctionEnd - )"; + )" + kDirectRead3 + kStreamWrite5Vert; + // clang-format on SetTargetEnv(SPV_ENV_VULKAN_1_2); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); @@ -9136,6 +5546,7 @@ TEST_F(InstBindlessTest, ImageBufferOOBRead) { // x = imageLoad(s, ii); // } + // clang-format off const std::string text = R"( OpCapability Shader OpCapability ImageBuffer @@ -9157,11 +5568,7 @@ TEST_F(InstBindlessTest, ImageBufferOOBRead) { OpDecorate %ii Flat OpDecorate %ii Location 13 ;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -;CHECK: OpDecorate %_struct_43 Block -;CHECK: OpMemberDecorate %_struct_43 0 Offset 0 -;CHECK: OpMemberDecorate %_struct_43 1 Offset 4 -;CHECK: OpDecorate %45 DescriptorSet 7 -;CHECK: OpDecorate %45 Binding 0 +)" + kOutputDecorations + R"( ;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %3 = OpTypeFunction %void @@ -9181,9 +5588,7 @@ TEST_F(InstBindlessTest, ImageBufferOOBRead) { ;CHECK: %uint_7 = OpConstant %uint 7 ;CHECK: %35 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint -;CHECK: %_struct_43 = OpTypeStruct %uint %_runtimearr_uint -;CHECK: %_ptr_StorageBuffer__struct_43 = OpTypePointer StorageBuffer %_struct_43 -;CHECK: %45 = OpVariable %_ptr_StorageBuffer__struct_43 StorageBuffer +)" + kOutputGlobals + R"( ;CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint ;CHECK: %uint_11 = OpConstant %uint 11 ;CHECK: %uint_4 = OpConstant %uint 4 @@ -9224,67 +5629,15 @@ TEST_F(InstBindlessTest, ImageBufferOOBRead) { ;CHECK: %33 = OpImageRead %v4float %32 %17 ;CHECK: OpBranch %29 ;CHECK: %31 = OpLabel -;CHECK: %92 = OpFunctionCall %void %34 %uint_33 %uint_7 %uint_0 %23 %25 +;CHECK: %92 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_33 %uint_7 %uint_0 %23 %25 ;CHECK: OpBranch %29 ;CHECK: %29 = OpLabel ;CHECK: %94 = OpPhi %v4float %33 %30 %93 %31 ;CHECK: OpStore %x %94 OpReturn OpFunctionEnd -;CHECK: %34 = OpFunction %void None %35 -;CHECK: %36 = OpFunctionParameter %uint -;CHECK: %37 = OpFunctionParameter %uint -;CHECK: %38 = OpFunctionParameter %uint -;CHECK: %39 = OpFunctionParameter %uint -;CHECK: %40 = OpFunctionParameter %uint -;CHECK: %41 = OpLabel -;CHECK: %47 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_0 -;CHECK: %50 = OpAtomicIAdd %uint %47 %uint_4 %uint_0 %uint_11 -;CHECK: %51 = OpIAdd %uint %50 %uint_11 -;CHECK: %52 = OpArrayLength %uint %45 1 -;CHECK: %53 = OpULessThanEqual %bool %51 %52 -;CHECK: OpSelectionMerge %54 None -;CHECK: OpBranchConditional %53 %55 %54 -;CHECK: %55 = OpLabel -;CHECK: %56 = OpIAdd %uint %50 %uint_0 -;CHECK: %58 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %56 -;CHECK: OpStore %58 %uint_11 -;CHECK: %60 = OpIAdd %uint %50 %uint_1 -;CHECK: %61 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %60 -;CHECK: OpStore %61 %uint_23 -;CHECK: %63 = OpIAdd %uint %50 %uint_2 -;CHECK: %64 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %63 -;CHECK: OpStore %64 %36 -;CHECK: %66 = OpIAdd %uint %50 %uint_3 -;CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %66 -;CHECK: OpStore %67 %uint_4 -;CHECK: %70 = OpLoad %v4float %gl_FragCoord -;CHECK: %72 = OpBitcast %v4uint %70 -;CHECK: %73 = OpCompositeExtract %uint %72 0 -;CHECK: %74 = OpIAdd %uint %50 %uint_4 -;CHECK: %75 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %74 -;CHECK: OpStore %75 %73 -;CHECK: %76 = OpCompositeExtract %uint %72 1 -;CHECK: %78 = OpIAdd %uint %50 %uint_5 -;CHECK: %79 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %78 -;CHECK: OpStore %79 %76 -;CHECK: %80 = OpIAdd %uint %50 %uint_7 -;CHECK: %81 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %80 -;CHECK: OpStore %81 %37 -;CHECK: %83 = OpIAdd %uint %50 %uint_8 -;CHECK: %84 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %83 -;CHECK: OpStore %84 %38 -;CHECK: %86 = OpIAdd %uint %50 %uint_9 -;CHECK: %87 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %86 -;CHECK: OpStore %87 %39 -;CHECK: %89 = OpIAdd %uint %50 %uint_10 -;CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %89 -;CHECK: OpStore %90 %40 -;CHECK: OpBranch %54 -;CHECK: %54 = OpLabel -;CHECK: OpReturn -;CHECK: OpFunctionEnd - )"; + )" + kStreamWrite5Frag; + // clang-format on SetTargetEnv(SPV_ENV_VULKAN_1_2); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); @@ -9304,6 +5657,7 @@ TEST_F(InstBindlessTest, ImageBufferOOBWrite) { // imageStore(s, ii, x); // } + // clang-format off const std::string text = R"( OpCapability Shader OpCapability ImageBuffer @@ -9312,7 +5666,7 @@ TEST_F(InstBindlessTest, ImageBufferOOBWrite) { %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %s %ii %x -;CHECK: OpEntryPoint Fragment %main "main" %s %ii %x %44 %gl_FragCoord +;CHECK: OpEntryPoint Fragment %main "main" %s %ii %x %inst_bindless_output_buffer %gl_FragCoord OpExecutionMode %main OriginUpperLeft OpSource GLSL 450 OpName %main "main" @@ -9326,11 +5680,7 @@ TEST_F(InstBindlessTest, ImageBufferOOBWrite) { OpDecorate %ii Location 13 OpDecorate %x Location 11 ;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -;CHECK: OpDecorate %_struct_42 Block -;CHECK: OpMemberDecorate %_struct_42 0 Offset 0 -;CHECK: OpMemberDecorate %_struct_42 1 Offset 4 -;CHECK: OpDecorate %44 DescriptorSet 7 -;CHECK: OpDecorate %44 Binding 0 +)" + kOutputDecorations + R"( ;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %3 = OpTypeFunction %void @@ -9350,9 +5700,7 @@ TEST_F(InstBindlessTest, ImageBufferOOBWrite) { ;CHECK: %uint_7 = OpConstant %uint 7 ;CHECK: %34 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint -;CHECK: %_struct_42 = OpTypeStruct %uint %_runtimearr_uint -;CHECK: %_ptr_StorageBuffer__struct_42 = OpTypePointer StorageBuffer %_struct_42 -;CHECK: %44 = OpVariable %_ptr_StorageBuffer__struct_42 StorageBuffer +)" + kOutputGlobals + R"( ;CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint ;CHECK: %uint_11 = OpConstant %uint 11 ;CHECK: %uint_4 = OpConstant %uint 4 @@ -9391,65 +5739,13 @@ TEST_F(InstBindlessTest, ImageBufferOOBWrite) { ;CHECK: OpImageWrite %32 %14 %18 ;CHECK: OpBranch %29 ;CHECK: %31 = OpLabel -;CHECK: %91 = OpFunctionCall %void %33 %uint_34 %uint_7 %uint_0 %23 %25 +;CHECK: %91 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_34 %uint_7 %uint_0 %23 %25 ;CHECK: OpBranch %29 ;CHECK: %29 = OpLabel OpReturn OpFunctionEnd -;CHECK: %33 = OpFunction %void None %34 -;CHECK: %35 = OpFunctionParameter %uint -;CHECK: %36 = OpFunctionParameter %uint -;CHECK: %37 = OpFunctionParameter %uint -;CHECK: %38 = OpFunctionParameter %uint -;CHECK: %39 = OpFunctionParameter %uint -;CHECK: %40 = OpLabel -;CHECK: %46 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_0 -;CHECK: %49 = OpAtomicIAdd %uint %46 %uint_4 %uint_0 %uint_11 -;CHECK: %50 = OpIAdd %uint %49 %uint_11 -;CHECK: %51 = OpArrayLength %uint %44 1 -;CHECK: %52 = OpULessThanEqual %bool %50 %51 -;CHECK: OpSelectionMerge %53 None -;CHECK: OpBranchConditional %52 %54 %53 -;CHECK: %54 = OpLabel -;CHECK: %55 = OpIAdd %uint %49 %uint_0 -;CHECK: %57 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %55 -;CHECK: OpStore %57 %uint_11 -;CHECK: %59 = OpIAdd %uint %49 %uint_1 -;CHECK: %60 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %59 -;CHECK: OpStore %60 %uint_23 -;CHECK: %62 = OpIAdd %uint %49 %uint_2 -;CHECK: %63 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %62 -;CHECK: OpStore %63 %35 -;CHECK: %65 = OpIAdd %uint %49 %uint_3 -;CHECK: %66 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %65 -;CHECK: OpStore %66 %uint_4 -;CHECK: %69 = OpLoad %v4float %gl_FragCoord -;CHECK: %71 = OpBitcast %v4uint %69 -;CHECK: %72 = OpCompositeExtract %uint %71 0 -;CHECK: %73 = OpIAdd %uint %49 %uint_4 -;CHECK: %74 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %73 -;CHECK: OpStore %74 %72 -;CHECK: %75 = OpCompositeExtract %uint %71 1 -;CHECK: %77 = OpIAdd %uint %49 %uint_5 -;CHECK: %78 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %77 -;CHECK: OpStore %78 %75 -;CHECK: %79 = OpIAdd %uint %49 %uint_7 -;CHECK: %80 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %79 -;CHECK: OpStore %80 %36 -;CHECK: %82 = OpIAdd %uint %49 %uint_8 -;CHECK: %83 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %82 -;CHECK: OpStore %83 %37 -;CHECK: %85 = OpIAdd %uint %49 %uint_9 -;CHECK: %86 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %85 -;CHECK: OpStore %86 %38 -;CHECK: %88 = OpIAdd %uint %49 %uint_10 -;CHECK: %89 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %88 -;CHECK: OpStore %89 %39 -;CHECK: OpBranch %53 -;CHECK: %53 = OpLabel -;CHECK: OpReturn -;CHECK: OpFunctionEnd - )"; + )" + kStreamWrite5Frag; + // clang-format on SetTargetEnv(SPV_ENV_VULKAN_1_2); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); @@ -9469,6 +5765,7 @@ TEST_F(InstBindlessTest, TextureBufferOOBFetch) { // x = texelFetch(s, ii); // } + // clang-format off const std::string text = R"( OpCapability Shader OpCapability SampledBuffer @@ -9477,7 +5774,7 @@ TEST_F(InstBindlessTest, TextureBufferOOBFetch) { %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %x %s %ii -;CHECK: OpEntryPoint Fragment %main "main" %x %s %ii %45 %gl_FragCoord +;CHECK: OpEntryPoint Fragment %main "main" %x %s %ii %inst_bindless_output_buffer %gl_FragCoord OpExecutionMode %main OriginUpperLeft OpSource GLSL 450 OpName %main "main" @@ -9490,11 +5787,7 @@ TEST_F(InstBindlessTest, TextureBufferOOBFetch) { OpDecorate %ii Flat OpDecorate %ii Location 13 ;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -;CHECK: OpDecorate %_struct_43 Block -;CHECK: OpMemberDecorate %_struct_43 0 Offset 0 -;CHECK: OpMemberDecorate %_struct_43 1 Offset 4 -;CHECK: OpDecorate %45 DescriptorSet 7 -;CHECK: OpDecorate %45 Binding 0 +)" + kOutputDecorations + R"( ;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %3 = OpTypeFunction %void @@ -9514,9 +5807,7 @@ TEST_F(InstBindlessTest, TextureBufferOOBFetch) { ;CHECK: %uint_6 = OpConstant %uint 6 ;CHECK: %35 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint -;CHECK: %_struct_43 = OpTypeStruct %uint %_runtimearr_uint -;CHECK: %_ptr_StorageBuffer__struct_43 = OpTypePointer StorageBuffer %_struct_43 -;CHECK: %45 = OpVariable %_ptr_StorageBuffer__struct_43 StorageBuffer +)" + kOutputGlobals + R"( ;CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint ;CHECK: %uint_11 = OpConstant %uint 11 ;CHECK: %uint_4 = OpConstant %uint 4 @@ -9558,67 +5849,15 @@ TEST_F(InstBindlessTest, TextureBufferOOBFetch) { ;CHECK: %33 = OpImageFetch %v4float %32 %17 ;CHECK: OpBranch %29 ;CHECK: %31 = OpLabel -;CHECK: %93 = OpFunctionCall %void %34 %uint_32 %uint_6 %uint_0 %23 %25 +;CHECK: %93 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_32 %uint_6 %uint_0 %23 %25 ;CHECK: OpBranch %29 ;CHECK: %29 = OpLabel ;CHECK: %95 = OpPhi %v4float %33 %30 %94 %31 ;CHECK: OpStore %x %95 OpReturn OpFunctionEnd -;CHECK: %34 = OpFunction %void None %35 -;CHECK: %36 = OpFunctionParameter %uint -;CHECK: %37 = OpFunctionParameter %uint -;CHECK: %38 = OpFunctionParameter %uint -;CHECK: %39 = OpFunctionParameter %uint -;CHECK: %40 = OpFunctionParameter %uint -;CHECK: %41 = OpLabel -;CHECK: %47 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_0 -;CHECK: %50 = OpAtomicIAdd %uint %47 %uint_4 %uint_0 %uint_11 -;CHECK: %51 = OpIAdd %uint %50 %uint_11 -;CHECK: %52 = OpArrayLength %uint %45 1 -;CHECK: %53 = OpULessThanEqual %bool %51 %52 -;CHECK: OpSelectionMerge %54 None -;CHECK: OpBranchConditional %53 %55 %54 -;CHECK: %55 = OpLabel -;CHECK: %56 = OpIAdd %uint %50 %uint_0 -;CHECK: %58 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %56 -;CHECK: OpStore %58 %uint_11 -;CHECK: %60 = OpIAdd %uint %50 %uint_1 -;CHECK: %61 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %60 -;CHECK: OpStore %61 %uint_23 -;CHECK: %63 = OpIAdd %uint %50 %uint_2 -;CHECK: %64 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %63 -;CHECK: OpStore %64 %36 -;CHECK: %66 = OpIAdd %uint %50 %uint_3 -;CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %66 -;CHECK: OpStore %67 %uint_4 -;CHECK: %70 = OpLoad %v4float %gl_FragCoord -;CHECK: %72 = OpBitcast %v4uint %70 -;CHECK: %73 = OpCompositeExtract %uint %72 0 -;CHECK: %74 = OpIAdd %uint %50 %uint_4 -;CHECK: %75 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %74 -;CHECK: OpStore %75 %73 -;CHECK: %76 = OpCompositeExtract %uint %72 1 -;CHECK: %78 = OpIAdd %uint %50 %uint_5 -;CHECK: %79 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %78 -;CHECK: OpStore %79 %76 -;CHECK: %81 = OpIAdd %uint %50 %uint_7 -;CHECK: %82 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %81 -;CHECK: OpStore %82 %37 -;CHECK: %84 = OpIAdd %uint %50 %uint_8 -;CHECK: %85 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %84 -;CHECK: OpStore %85 %38 -;CHECK: %87 = OpIAdd %uint %50 %uint_9 -;CHECK: %88 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %87 -;CHECK: OpStore %88 %39 -;CHECK: %90 = OpIAdd %uint %50 %uint_10 -;CHECK: %91 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %90 -;CHECK: OpStore %91 %40 -;CHECK: OpBranch %54 -;CHECK: %54 = OpLabel -;CHECK: OpReturn -;CHECK: OpFunctionEnd - )"; + )" + kStreamWrite5Frag; + // clang-format on SetTargetEnv(SPV_ENV_VULKAN_1_2); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); @@ -9638,6 +5877,7 @@ TEST_F(InstBindlessTest, SamplerBufferOOBFetch) { // x = texelFetch(s, ii); // } + // clang-format off const std::string text = R"( OpCapability Shader OpCapability SampledBuffer @@ -9646,7 +5886,7 @@ TEST_F(InstBindlessTest, SamplerBufferOOBFetch) { %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %x %s %ii -;CHECK: OpEntryPoint Fragment %main "main" %x %s %ii %48 %gl_FragCoord +;CHECK: OpEntryPoint Fragment %main "main" %x %s %ii %inst_bindless_output_buffer %gl_FragCoord OpExecutionMode %main OriginUpperLeft OpSource GLSL 450 OpName %main "main" @@ -9659,11 +5899,7 @@ TEST_F(InstBindlessTest, SamplerBufferOOBFetch) { OpDecorate %ii Flat OpDecorate %ii Location 13 ;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -;CHECK: OpDecorate %_struct_46 Block -;CHECK: OpMemberDecorate %_struct_46 0 Offset 0 -;CHECK: OpMemberDecorate %_struct_46 1 Offset 4 -;CHECK: OpDecorate %48 DescriptorSet 7 -;CHECK: OpDecorate %48 Binding 0 +)" + kOutputDecorations + R"( ;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %3 = OpTypeFunction %void @@ -9684,9 +5920,7 @@ TEST_F(InstBindlessTest, SamplerBufferOOBFetch) { ;CHECK: %uint_6 = OpConstant %uint 6 ;CHECK: %38 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint -;CHECK: %_struct_46 = OpTypeStruct %uint %_runtimearr_uint -;CHECK: %_ptr_StorageBuffer__struct_46 = OpTypePointer StorageBuffer %_struct_46 -;CHECK: %48 = OpVariable %_ptr_StorageBuffer__struct_46 StorageBuffer +)" + kOutputGlobals + R"( ;CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint ;CHECK: %uint_11 = OpConstant %uint 11 ;CHECK: %uint_4 = OpConstant %uint 4 @@ -9730,67 +5964,15 @@ TEST_F(InstBindlessTest, SamplerBufferOOBFetch) { ;CHECK: %36 = OpImageFetch %v4float %35 %18 ;CHECK: OpBranch %31 ;CHECK: %33 = OpLabel -;CHECK: %96 = OpFunctionCall %void %37 %uint_34 %uint_6 %uint_0 %25 %27 +;CHECK: %96 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_34 %uint_6 %uint_0 %25 %27 ;CHECK: OpBranch %31 ;CHECK: %31 = OpLabel ;CHECK: %98 = OpPhi %v4float %36 %32 %97 %33 ;CHECK: OpStore %x %98 OpReturn OpFunctionEnd -;CHECK: %37 = OpFunction %void None %38 -;CHECK: %39 = OpFunctionParameter %uint -;CHECK: %40 = OpFunctionParameter %uint -;CHECK: %41 = OpFunctionParameter %uint -;CHECK: %42 = OpFunctionParameter %uint -;CHECK: %43 = OpFunctionParameter %uint -;CHECK: %44 = OpLabel -;CHECK: %50 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 -;CHECK: %53 = OpAtomicIAdd %uint %50 %uint_4 %uint_0 %uint_11 -;CHECK: %54 = OpIAdd %uint %53 %uint_11 -;CHECK: %55 = OpArrayLength %uint %48 1 -;CHECK: %56 = OpULessThanEqual %bool %54 %55 -;CHECK: OpSelectionMerge %57 None -;CHECK: OpBranchConditional %56 %58 %57 -;CHECK: %58 = OpLabel -;CHECK: %59 = OpIAdd %uint %53 %uint_0 -;CHECK: %61 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %59 -;CHECK: OpStore %61 %uint_11 -;CHECK: %63 = OpIAdd %uint %53 %uint_1 -;CHECK: %64 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %63 -;CHECK: OpStore %64 %uint_23 -;CHECK: %66 = OpIAdd %uint %53 %uint_2 -;CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %66 -;CHECK: OpStore %67 %39 -;CHECK: %69 = OpIAdd %uint %53 %uint_3 -;CHECK: %70 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %69 -;CHECK: OpStore %70 %uint_4 -;CHECK: %73 = OpLoad %v4float %gl_FragCoord -;CHECK: %75 = OpBitcast %v4uint %73 -;CHECK: %76 = OpCompositeExtract %uint %75 0 -;CHECK: %77 = OpIAdd %uint %53 %uint_4 -;CHECK: %78 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %77 -;CHECK: OpStore %78 %76 -;CHECK: %79 = OpCompositeExtract %uint %75 1 -;CHECK: %81 = OpIAdd %uint %53 %uint_5 -;CHECK: %82 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %81 -;CHECK: OpStore %82 %79 -;CHECK: %84 = OpIAdd %uint %53 %uint_7 -;CHECK: %85 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %84 -;CHECK: OpStore %85 %40 -;CHECK: %87 = OpIAdd %uint %53 %uint_8 -;CHECK: %88 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %87 -;CHECK: OpStore %88 %41 -;CHECK: %90 = OpIAdd %uint %53 %uint_9 -;CHECK: %91 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %90 -;CHECK: OpStore %91 %42 -;CHECK: %93 = OpIAdd %uint %53 %uint_10 -;CHECK: %94 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %93 -;CHECK: OpStore %94 %43 -;CHECK: OpBranch %57 -;CHECK: %57 = OpLabel -;CHECK: OpReturn -;CHECK: OpFunctionEnd - )"; + )" + kStreamWrite5Frag; + // clang-format on SetTargetEnv(SPV_ENV_VULKAN_1_2); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); @@ -9811,6 +5993,7 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) { // x = texelFetch(samplerBuffer(tBuf, s), ii); // } + // clang-format off const std::string text = R"( OpCapability Shader OpCapability SampledBuffer @@ -9819,7 +6002,7 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) { %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %x %tBuf %s %ii -;CHECK: OpEntryPoint Fragment %main "main" %x %tBuf %s %ii %54 %gl_FragCoord +;CHECK: OpEntryPoint Fragment %main "main" %x %tBuf %s %ii %inst_bindless_output_buffer %gl_FragCoord OpExecutionMode %main OriginUpperLeft OpSource GLSL 450 OpName %main "main" @@ -9835,11 +6018,7 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) { OpDecorate %ii Flat OpDecorate %ii Location 13 ;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -;CHECK: OpDecorate %_struct_52 Block -;CHECK: OpMemberDecorate %_struct_52 0 Offset 0 -;CHECK: OpMemberDecorate %_struct_52 1 Offset 4 -;CHECK: OpDecorate %54 DescriptorSet 7 -;CHECK: OpDecorate %54 Binding 0 +)" + kOutputDecorations + R"( ;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %3 = OpTypeFunction %void @@ -9863,9 +6042,7 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) { ;CHECK: %uint_6 = OpConstant %uint 6 ;CHECK: %44 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint -;CHECK: %_struct_52 = OpTypeStruct %uint %_runtimearr_uint -;CHECK: %_ptr_StorageBuffer__struct_52 = OpTypePointer StorageBuffer %_struct_52 -;CHECK: %54 = OpVariable %_ptr_StorageBuffer__struct_52 StorageBuffer +)" + kOutputGlobals + R"( ;CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint ;CHECK: %uint_11 = OpConstant %uint 11 ;CHECK: %uint_4 = OpConstant %uint 4 @@ -9912,67 +6089,15 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) { ;CHECK: %42 = OpImageFetch %v4float %41 %23 ;CHECK: OpBranch %36 ;CHECK: %38 = OpLabel -;CHECK: %102 = OpFunctionCall %void %43 %uint_42 %uint_6 %uint_0 %30 %32 +;CHECK: %102 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_42 %uint_6 %uint_0 %30 %32 ;CHECK: OpBranch %36 ;CHECK: %36 = OpLabel ;CHECK: %104 = OpPhi %v4float %42 %37 %103 %38 ;CHECK: OpStore %x %104 OpReturn OpFunctionEnd -;CHECK: %43 = OpFunction %void None %44 -;CHECK: %45 = OpFunctionParameter %uint -;CHECK: %46 = OpFunctionParameter %uint -;CHECK: %47 = OpFunctionParameter %uint -;CHECK: %48 = OpFunctionParameter %uint -;CHECK: %49 = OpFunctionParameter %uint -;CHECK: %50 = OpLabel -;CHECK: %56 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_0 -;CHECK: %59 = OpAtomicIAdd %uint %56 %uint_4 %uint_0 %uint_11 -;CHECK: %60 = OpIAdd %uint %59 %uint_11 -;CHECK: %61 = OpArrayLength %uint %54 1 -;CHECK: %62 = OpULessThanEqual %bool %60 %61 -;CHECK: OpSelectionMerge %63 None -;CHECK: OpBranchConditional %62 %64 %63 -;CHECK: %64 = OpLabel -;CHECK: %65 = OpIAdd %uint %59 %uint_0 -;CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %65 -;CHECK: OpStore %67 %uint_11 -;CHECK: %69 = OpIAdd %uint %59 %uint_1 -;CHECK: %70 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %69 -;CHECK: OpStore %70 %uint_23 -;CHECK: %72 = OpIAdd %uint %59 %uint_2 -;CHECK: %73 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %72 -;CHECK: OpStore %73 %45 -;CHECK: %75 = OpIAdd %uint %59 %uint_3 -;CHECK: %76 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %75 -;CHECK: OpStore %76 %uint_4 -;CHECK: %79 = OpLoad %v4float %gl_FragCoord -;CHECK: %81 = OpBitcast %v4uint %79 -;CHECK: %82 = OpCompositeExtract %uint %81 0 -;CHECK: %83 = OpIAdd %uint %59 %uint_4 -;CHECK: %84 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %83 -;CHECK: OpStore %84 %82 -;CHECK: %85 = OpCompositeExtract %uint %81 1 -;CHECK: %87 = OpIAdd %uint %59 %uint_5 -;CHECK: %88 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %87 -;CHECK: OpStore %88 %85 -;CHECK: %90 = OpIAdd %uint %59 %uint_7 -;CHECK: %91 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %90 -;CHECK: OpStore %91 %46 -;CHECK: %93 = OpIAdd %uint %59 %uint_8 -;CHECK: %94 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %93 -;CHECK: OpStore %94 %47 -;CHECK: %96 = OpIAdd %uint %59 %uint_9 -;CHECK: %97 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %96 -;CHECK: OpStore %97 %48 -;CHECK: %99 = OpIAdd %uint %59 %uint_10 -;CHECK: %100 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %99 -;CHECK: OpStore %100 %49 -;CHECK: OpBranch %63 -;CHECK: %63 = OpLabel -;CHECK: OpReturn -;CHECK: OpFunctionEnd - )"; + )" + kStreamWrite5Frag; + // clang-format on SetTargetEnv(SPV_ENV_VULKAN_1_2); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); @@ -9984,8 +6109,8 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) { // // Compute shader // Geometry shader -// Tesselation control shader -// Tesselation eval shader +// Tessellation control shader +// Tessellation eval shader // OpImage // SampledImage variable diff --git a/test/opt/inst_buff_addr_check_test.cpp b/test/opt/inst_buff_addr_check_test.cpp index 41ead67a..e095eb77 100644 --- a/test/opt/inst_buff_addr_check_test.cpp +++ b/test/opt/inst_buff_addr_check_test.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2019 Valve Corporation -// Copyright (c) 2019 LunarG Inc. +// Copyright (c) 2019-2022 Valve Corporation +// Copyright (c) 2019-2022 LunarG Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -27,6 +27,148 @@ namespace spvtools { namespace opt { namespace { +static const std::string kOutputDecorations = R"( +; CHECK: OpDecorate [[output_buffer_type:%inst_buff_addr_OutputBuffer]] Block +; CHECK: OpMemberDecorate [[output_buffer_type]] 0 Offset 0 +; CHECK: OpMemberDecorate [[output_buffer_type]] 1 Offset 4 +; CHECK: OpDecorate [[output_buffer_var:%\w+]] DescriptorSet 7 +; CHECK: OpDecorate [[output_buffer_var]] Binding 0 +)"; + +static const std::string kOutputGlobals = R"( +; CHECK: [[output_buffer_type]] = OpTypeStruct %uint %_runtimearr_uint +; CHECK: [[output_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[output_buffer_type]] +; CHECK: [[output_buffer_var]] = OpVariable [[output_ptr_type]] StorageBuffer +)"; + +static const std::string kStreamWrite4Begin = R"( +; CHECK: {{%\w+}} = OpFunction %void None {{%\w+}} +; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint +; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint +; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint +; CHECK: [[param_4:%\w+]] = OpFunctionParameter %uint +; CHECK: {{%\w+}} = OpLabel +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_0 +; CHECK: {{%\w+}} = OpAtomicIAdd %uint {{%\w+}} %uint_4 %uint_0 %uint_10 +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_10 +; CHECK: {{%\w+}} = OpArrayLength %uint [[output_buffer_var]] 1 +; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}} +; CHECK: OpSelectionMerge {{%\w+}} None +; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +; CHECK: {{%\w+}} = OpLabel +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_0 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} %uint_10 +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} %uint_23 +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_2 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} [[param_1]] +)"; + +static const std::string kStreamWrite4End = R"( +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} [[param_2]] +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_8 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} [[param_3]] +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_9 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} [[param_4]] +; CHECK: OpBranch {{%\w+}} +; CHECK: {{%\w+}} = OpLabel +; CHECK: OpReturn +; CHECK: OpFunctionEnd +)"; + +// clang-format off +static const std::string kStreamWrite4Frag = kStreamWrite4Begin + R"( +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} %uint_4 +; CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +; CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +)" + kStreamWrite4End; + +static const std::string kStreamWrite4Compute = kStreamWrite4Begin + R"( +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} %uint_5 +; CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID +; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}} +; CHECK: OpStore {{%\w+}} {{%\w+}} +)" + kStreamWrite4End; +// clang-format on + +static const std::string kInputDecorations = R"( +; CHECK: OpDecorate [[input_buffer_type:%inst_buff_addr_InputBuffer]] Block +; CHECK: OpMemberDecorate [[input_buffer_type]] 0 Offset 0 +; CHECK: OpDecorate [[input_buffer_var:%\w+]] DescriptorSet 7 +; CHECK: OpDecorate [[input_buffer_var]] Binding 2 +)"; + +static const std::string kInputGlobals = R"( +; CHECK: [[input_buffer_type]] = OpTypeStruct %_runtimearr_ulong +; CHECK: [[input_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[input_buffer_type]] +; CHECK: [[input_buffer_var]] = OpVariable [[input_ptr_type]] StorageBuffer +)"; + +static const std::string kSearchAndTest = R"( +; CHECK: {{%\w+}} = OpFunction %bool None {{%\w+}} +; CHECK: [[param_1:%\w+]] = OpFunctionParameter %ulong +; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint +; CHECK: {{%\w+}} = OpLabel +; CHECK: OpBranch {{%\w+}} +; CHECK: {{%\w+}} = OpLabel +; CHECK: {{%\w+}} = OpPhi %uint %uint_1 {{%\w+}} {{%\w+}} {{%\w+}} +; CHECK: OpLoopMerge {{%\w+}} {{%\w+}} None +; CHECK: OpBranch {{%\w+}} +; CHECK: {{%\w+}} = OpLabel +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 {{%\w+}} +; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}} +; CHECK: {{%\w+}} = OpUGreaterThan %bool {{%\w+}} [[param_1]] +; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +; CHECK: {{%\w+}} = OpLabel +; CHECK: {{%\w+}} = OpISub %uint {{%\w+}} %uint_1 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 {{%\w+}} +; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}} +; CHECK: {{%\w+}} = OpISub %ulong [[param_1]] {{%\w+}} +; CHECK: {{%\w+}} = OpUConvert %ulong [[param_2]] +; CHECK: {{%\w+}} = OpIAdd %ulong {{%\w+}} {{%\w+}} +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 %uint_0 +; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}} +; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}} +; CHECK: {{%\w+}} = OpISub %uint {{%\w+}} %uint_1 +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} {{%\w+}} +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 {{%\w+}} +; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}} +; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}} +; CHECK: OpReturnValue {{%\w+}} +; CHECK: OpFunctionEnd +)"; +// clang-format on + using InstBuffAddrTest = PassTest<::testing::Test>; TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferStore) { @@ -49,13 +191,16 @@ TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferStore) { // u_info.data.b = 0xca7; // } - const std::string defs_before = - R"(OpCapability Shader + const std::string defs = R"( +OpCapability Shader OpCapability PhysicalStorageBufferAddresses +; CHECK: OpCapability Int64 OpExtension "SPV_EXT_physical_storage_buffer" +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint GLCompute %main "main" +; CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID OpExecutionMode %main LocalSize 1 1 1 OpSource GLSL 450 OpSourceExtension "GL_EXT_buffer_reference" @@ -67,6 +212,10 @@ OpName %bufStruct "bufStruct" OpMemberName %bufStruct 0 "a" OpMemberName %bufStruct 1 "b" OpName %u_info "u_info" +)"; + + // clang-format off + const std::string decorates = R"( OpMemberDecorate %ufoo 0 Offset 0 OpMemberDecorate %ufoo 1 Offset 8 OpDecorate %ufoo Block @@ -76,6 +225,14 @@ OpMemberDecorate %bufStruct 1 Offset 32 OpDecorate %bufStruct Block OpDecorate %u_info DescriptorSet 0 OpDecorate %u_info Binding 0 +; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8 +)" + kInputDecorations + R"( +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kOutputDecorations + R"( +; CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId +)"; + + const std::string globals = R"( %void = OpTypeVoid %3 = OpTypeFunction %void OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer @@ -93,224 +250,71 @@ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer %int_1 = OpConstant %int 1 %int_3239 = OpConstant %int 3239 %_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int +; CHECK: %ulong = OpTypeInt 64 0 +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %bool = OpTypeBool +; CHECK: %28 = OpTypeFunction %bool %ulong %uint +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %_runtimearr_ulong = OpTypeRuntimeArray %ulong +)" + kInputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong +; CHECK: %uint_0 = OpConstant %uint 0 +; CHECK: %uint_32 = OpConstant %uint 32 +; CHECK: %70 = OpTypeFunction %void %uint %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +)" + kOutputGlobals + R"( +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %uint_10 = OpConstant %uint 10 +; CHECK: %uint_23 = OpConstant %uint 23 +; CHECK: %uint_5 = OpConstant %uint 5 +; CHECK: %uint_3 = OpConstant %uint 3 +; CHECK: %v3uint = OpTypeVector %uint 3 +; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint +; CHECK: %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +; CHECK: %uint_6 = OpConstant %uint 6 +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_8 = OpConstant %uint 8 +; CHECK: %uint_9 = OpConstant %uint 9 +; CHECK: %uint_48 = OpConstant %uint 48 )"; +// clang-format off - const std::string defs_after = - R"(OpCapability Shader -OpCapability PhysicalStorageBufferAddresses -OpCapability Int64 -OpExtension "SPV_EXT_physical_storage_buffer" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel PhysicalStorageBuffer64 GLSL450 -OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID -OpExecutionMode %main LocalSize 1 1 1 -OpSource GLSL 450 -OpSourceExtension "GL_EXT_buffer_reference" -OpName %main "main" -OpName %ufoo "ufoo" -OpMemberName %ufoo 0 "data" -OpMemberName %ufoo 1 "offset" -OpName %bufStruct "bufStruct" -OpMemberName %bufStruct 0 "a" -OpMemberName %bufStruct 1 "b" -OpName %u_info "u_info" -OpMemberDecorate %ufoo 0 Offset 0 -OpMemberDecorate %ufoo 1 Offset 8 -OpDecorate %ufoo Block -OpDecorate %_arr_int_uint_2 ArrayStride 16 -OpMemberDecorate %bufStruct 0 Offset 0 -OpMemberDecorate %bufStruct 1 Offset 32 -OpDecorate %bufStruct Block -OpDecorate %u_info DescriptorSet 0 -OpDecorate %u_info Binding 0 -OpDecorate %_runtimearr_ulong ArrayStride 8 -OpDecorate %_struct_39 Block -OpMemberDecorate %_struct_39 0 Offset 0 -OpDecorate %41 DescriptorSet 7 -OpDecorate %41 Binding 2 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_77 Block -OpMemberDecorate %_struct_77 0 Offset 0 -OpMemberDecorate %_struct_77 1 Offset 4 -OpDecorate %79 DescriptorSet 7 -OpDecorate %79 Binding 0 -OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId -%void = OpTypeVoid -%8 = OpTypeFunction %void -OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer -%uint = OpTypeInt 32 0 -%ufoo = OpTypeStruct %_ptr_PhysicalStorageBuffer_bufStruct %uint -%int = OpTypeInt 32 1 -%uint_2 = OpConstant %uint 2 -%_arr_int_uint_2 = OpTypeArray %int %uint_2 -%bufStruct = OpTypeStruct %_arr_int_uint_2 %int -%_ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer PhysicalStorageBuffer %bufStruct -%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo -%u_info = OpVariable %_ptr_Uniform_ufoo Uniform -%int_0 = OpConstant %int 0 -%_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_bufStruct -%int_1 = OpConstant %int 1 -%int_3239 = OpConstant %int 3239 -%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int -%ulong = OpTypeInt 64 0 -%uint_4 = OpConstant %uint 4 -%bool = OpTypeBool -%28 = OpTypeFunction %bool %ulong %uint -%uint_1 = OpConstant %uint 1 -%_runtimearr_ulong = OpTypeRuntimeArray %ulong -%_struct_39 = OpTypeStruct %_runtimearr_ulong -%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39 -%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer -%_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong -%uint_0 = OpConstant %uint 0 -%uint_32 = OpConstant %uint 32 -%70 = OpTypeFunction %void %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_77 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_77 = OpTypePointer StorageBuffer %_struct_77 -%79 = OpVariable %_ptr_StorageBuffer__struct_77 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%uint_10 = OpConstant %uint 10 -%uint_23 = OpConstant %uint 23 -%uint_5 = OpConstant %uint 5 -%uint_3 = OpConstant %uint 3 -%v3uint = OpTypeVector %uint 3 -%_ptr_Input_v3uint = OpTypePointer Input %v3uint -%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_9 = OpConstant %uint 9 -%uint_48 = OpConstant %uint 48 -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %3 + const std::string main_func = R"( +%main = OpFunction %void None %3 %5 = OpLabel %17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0 %18 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %17 %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %18 %int_1 +; CHECK-NOT: %17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0 +; CHECK-NOT: %18 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %17 +; CHECK-NOT: %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %18 %int_1 +; CHECK: %20 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0 +; CHECK: %21 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %20 +; CHECK: %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %21 %int_1 +; CHECK: %24 = OpConvertPtrToU %ulong %22 +; CHECK: %61 = OpFunctionCall %bool %inst_buff_addr_search_and_test %24 %uint_4 +; CHECK: OpSelectionMerge %62 None +; CHECK: OpBranchConditional %61 %63 %64 +; CHECK: %63 = OpLabel OpStore %22 %int_3239 Aligned 16 +; CHECK: OpStore %22 %int_3239 Aligned 16 +; CHECK: OpBranch %62 +; CHECK: %64 = OpLabel +; CHECK: %65 = OpUConvert %uint %24 +; CHECK: %67 = OpShiftRightLogical %ulong %24 %uint_32 +; CHECK: %68 = OpUConvert %uint %67 +; CHECK: %124 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_48 %uint_2 %65 %68 +; CHECK: OpBranch %62 +; CHECK: %62 = OpLabel OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %8 -%19 = OpLabel -%20 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0 -%21 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %20 -%22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %21 %int_1 -%24 = OpConvertPtrToU %ulong %22 -%61 = OpFunctionCall %bool %26 %24 %uint_4 -OpSelectionMerge %62 None -OpBranchConditional %61 %63 %64 -%63 = OpLabel -OpStore %22 %int_3239 Aligned 16 -OpBranch %62 -%64 = OpLabel -%65 = OpUConvert %uint %24 -%67 = OpShiftRightLogical %ulong %24 %uint_32 -%68 = OpUConvert %uint %67 -%124 = OpFunctionCall %void %69 %uint_48 %uint_2 %65 %68 -OpBranch %62 -%62 = OpLabel -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%26 = OpFunction %bool None %28 -%29 = OpFunctionParameter %ulong -%30 = OpFunctionParameter %uint -%31 = OpLabel -OpBranch %32 -%32 = OpLabel -%34 = OpPhi %uint %uint_1 %31 %35 %33 -OpLoopMerge %37 %33 None -OpBranch %33 -%33 = OpLabel -%35 = OpIAdd %uint %34 %uint_1 -%44 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %35 -%45 = OpLoad %ulong %44 -%46 = OpUGreaterThan %bool %45 %29 -OpBranchConditional %46 %37 %32 -%37 = OpLabel -%47 = OpISub %uint %35 %uint_1 -%48 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %47 -%49 = OpLoad %ulong %48 -%50 = OpISub %ulong %29 %49 -%51 = OpUConvert %ulong %30 -%52 = OpIAdd %ulong %50 %51 -%53 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %uint_0 -%54 = OpLoad %ulong %53 -%55 = OpUConvert %uint %54 -%56 = OpISub %uint %47 %uint_1 -%57 = OpIAdd %uint %56 %55 -%58 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %57 -%59 = OpLoad %ulong %58 -%60 = OpULessThanEqual %bool %52 %59 -OpReturnValue %60 -OpFunctionEnd -%69 = OpFunction %void None %70 -%71 = OpFunctionParameter %uint -%72 = OpFunctionParameter %uint -%73 = OpFunctionParameter %uint -%74 = OpFunctionParameter %uint -%75 = OpLabel -%81 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_0 -%83 = OpAtomicIAdd %uint %81 %uint_4 %uint_0 %uint_10 -%84 = OpIAdd %uint %83 %uint_10 -%85 = OpArrayLength %uint %79 1 -%86 = OpULessThanEqual %bool %84 %85 -OpSelectionMerge %87 None -OpBranchConditional %86 %88 %87 -%88 = OpLabel -%89 = OpIAdd %uint %83 %uint_0 -%90 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %89 -OpStore %90 %uint_10 -%92 = OpIAdd %uint %83 %uint_1 -%93 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %92 -OpStore %93 %uint_23 -%94 = OpIAdd %uint %83 %uint_2 -%95 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %94 -OpStore %95 %71 -%98 = OpIAdd %uint %83 %uint_3 -%99 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %98 -OpStore %99 %uint_5 -%103 = OpLoad %v3uint %gl_GlobalInvocationID -%104 = OpCompositeExtract %uint %103 0 -%105 = OpCompositeExtract %uint %103 1 -%106 = OpCompositeExtract %uint %103 2 -%107 = OpIAdd %uint %83 %uint_4 -%108 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %107 -OpStore %108 %104 -%109 = OpIAdd %uint %83 %uint_5 -%110 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %109 -OpStore %110 %105 -%112 = OpIAdd %uint %83 %uint_6 -%113 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %112 -OpStore %113 %106 -%115 = OpIAdd %uint %83 %uint_7 -%116 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %115 -OpStore %116 %72 -%118 = OpIAdd %uint %83 %uint_8 -%119 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %118 -OpStore %119 %73 -%121 = OpIAdd %uint %83 %uint_9 -%122 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %121 -OpStore %122 %74 -OpBranch %87 -%87 = OpLabel -OpReturn -OpFunctionEnd -)"; + const std::string output_funcs = kSearchAndTest + kStreamWrite4Compute; // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u); + SinglePassRunAndMatch( + defs + decorates + globals + main_func + output_funcs, true, 7u, 23u); } TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferLoadAndStore) { @@ -337,9 +341,10 @@ TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferLoadAndStore) { // b.x = 531; // } - const std::string defs_before = - R"(OpCapability Shader + const std::string defs = R"( +OpCapability Shader OpCapability PhysicalStorageBufferAddresses +; CHECK: OpCapability Int64 OpExtension "SPV_EXT_physical_storage_buffer" OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" @@ -349,12 +354,17 @@ OpExecutionMode %main LocalSize 1 1 1 OpSource GLSL 450 OpSourceExtension "GL_EXT_buffer_reference" OpName %main "main" +; CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID OpName %blockType "blockType" OpMemberName %blockType 0 "x" OpMemberName %blockType 1 "next" OpName %rootBlock "rootBlock" OpMemberName %rootBlock 0 "root" OpName %r "r" +)"; + +// clang-format off + const std::string decorates = R"( OpMemberDecorate %blockType 0 Offset 0 OpMemberDecorate %blockType 1 Offset 8 OpDecorate %blockType Block @@ -362,6 +372,15 @@ OpMemberDecorate %rootBlock 0 Offset 0 OpDecorate %rootBlock Block OpDecorate %r DescriptorSet 0 OpDecorate %r Binding 0 +; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8 +)" + kInputDecorations + R"( +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kOutputDecorations + R"( +; CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId +)"; + // clang-format on + + const std::string globals = R"( %void = OpTypeVoid %3 = OpTypeFunction %void OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_blockType PhysicalStorageBuffer @@ -377,99 +396,10 @@ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_blockType PhysicalStorageBuffer %_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %_ptr_PhysicalStorageBuffer_blockType %int_531 = OpConstant %int 531 %_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int -)"; +)" + kInputGlobals + kOutputGlobals; - const std::string defs_after = - R"(OpCapability Shader -OpCapability PhysicalStorageBufferAddresses -OpCapability Int64 -OpExtension "SPV_EXT_physical_storage_buffer" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel PhysicalStorageBuffer64 GLSL450 -OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID -OpExecutionMode %main LocalSize 1 1 1 -OpSource GLSL 450 -OpSourceExtension "GL_EXT_buffer_reference" -OpName %main "main" -OpName %blockType "blockType" -OpMemberName %blockType 0 "x" -OpMemberName %blockType 1 "next" -OpName %rootBlock "rootBlock" -OpMemberName %rootBlock 0 "root" -OpName %r "r" -OpMemberDecorate %blockType 0 Offset 0 -OpMemberDecorate %blockType 1 Offset 8 -OpDecorate %blockType Block -OpMemberDecorate %rootBlock 0 Offset 0 -OpDecorate %rootBlock Block -OpDecorate %r DescriptorSet 0 -OpDecorate %r Binding 0 -OpDecorate %_runtimearr_ulong ArrayStride 8 -OpDecorate %_struct_45 Block -OpMemberDecorate %_struct_45 0 Offset 0 -OpDecorate %47 DescriptorSet 7 -OpDecorate %47 Binding 2 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_84 Block -OpMemberDecorate %_struct_84 0 Offset 0 -OpMemberDecorate %_struct_84 1 Offset 4 -OpDecorate %86 DescriptorSet 7 -OpDecorate %86 Binding 0 -OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId -%void = OpTypeVoid -%3 = OpTypeFunction %void -OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_blockType PhysicalStorageBuffer -%int = OpTypeInt 32 1 -%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBuffer_blockType -%_ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %blockType -%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBuffer_blockType -%_ptr_StorageBuffer_rootBlock = OpTypePointer StorageBuffer %rootBlock -%r = OpVariable %_ptr_StorageBuffer_rootBlock StorageBuffer -%int_0 = OpConstant %int 0 -%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_blockType -%int_1 = OpConstant %int 1 -%_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %_ptr_PhysicalStorageBuffer_blockType -%int_531 = OpConstant %int 531 -%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int -%uint = OpTypeInt 32 0 -%uint_2 = OpConstant %uint 2 -%ulong = OpTypeInt 64 0 -%uint_8 = OpConstant %uint 8 -%bool = OpTypeBool -%34 = OpTypeFunction %bool %ulong %uint -%uint_1 = OpConstant %uint 1 -%_runtimearr_ulong = OpTypeRuntimeArray %ulong -%_struct_45 = OpTypeStruct %_runtimearr_ulong -%_ptr_StorageBuffer__struct_45 = OpTypePointer StorageBuffer %_struct_45 -%47 = OpVariable %_ptr_StorageBuffer__struct_45 StorageBuffer -%_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong -%uint_0 = OpConstant %uint 0 -%uint_32 = OpConstant %uint 32 -%77 = OpTypeFunction %void %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_84 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_84 = OpTypePointer StorageBuffer %_struct_84 -%86 = OpVariable %_ptr_StorageBuffer__struct_84 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_5 = OpConstant %uint 5 -%uint_3 = OpConstant %uint 3 -%v3uint = OpTypeVector %uint 3 -%_ptr_Input_v3uint = OpTypePointer Input %v3uint -%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_9 = OpConstant %uint 9 -%uint_44 = OpConstant %uint 44 -%132 = OpConstantNull %ulong -%uint_46 = OpConstant %uint 46 -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %3 + const std::string main_func = R"( +%main = OpFunction %void None %3 %5 = OpLabel %16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType %r %int_0 %17 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %16 @@ -477,142 +407,162 @@ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_blockType PhysicalStorageBuffer %22 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8 %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %22 %int_0 OpStore %26 %int_531 Aligned 16 +; CHECK-NOT: %22 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8 +; CHECK-NOT: %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %22 %int_0 +; CHECK: %30 = OpConvertPtrToU %ulong %21 +; CHECK: %67 = OpFunctionCall %bool %inst_buff_addr_search_and_test %30 %uint_8 +; CHECK: OpSelectionMerge %68 None +; CHECK: OpBranchConditional %67 %69 %70 +; CHECK: %69 = OpLabel +; CHECK: %71 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8 +; CHECK: OpBranch %68 +; CHECK: %70 = OpLabel +; CHECK: %72 = OpUConvert %uint %30 +; CHECK: %74 = OpShiftRightLogical %ulong %30 %uint_32 +; CHECK: %75 = OpUConvert %uint %74 +; CHECK: %131 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_44 %uint_2 %72 %75 +; CHECK: %133 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_blockType %132 +; CHECK: OpBranch %68 +; CHECK: %68 = OpLabel +; CHECK: %134 = OpPhi %_ptr_PhysicalStorageBuffer_blockType %71 %69 %133 %70 +; CHECK: %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %134 %int_0 +; CHECK: %135 = OpConvertPtrToU %ulong %26 +; CHECK: %136 = OpFunctionCall %bool %inst_buff_addr_search_and_test %135 %uint_4 +; CHECK: OpSelectionMerge %137 None +; CHECK: OpBranchConditional %136 %138 %139 +; CHECK: %138 = OpLabel +; CHECK: OpStore %26 %int_531 Aligned 16 +; CHECK: OpBranch %137 +; CHECK: %139 = OpLabel +; CHECK: %140 = OpUConvert %uint %135 +; CHECK: %141 = OpShiftRightLogical %ulong %135 %uint_32 +; CHECK: %142 = OpUConvert %uint %141 +; CHECK: %144 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_46 %uint_2 %140 %142 +; CHECK: OpBranch %137 +; CHECK: %137 = OpLabel OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %3 -%5 = OpLabel -%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType %r %int_0 -%17 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %16 -%21 = OpAccessChain %_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType %17 %int_1 -%30 = OpConvertPtrToU %ulong %21 -%67 = OpFunctionCall %bool %32 %30 %uint_8 -OpSelectionMerge %68 None -OpBranchConditional %67 %69 %70 -%69 = OpLabel -%71 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8 -OpBranch %68 -%70 = OpLabel -%72 = OpUConvert %uint %30 -%74 = OpShiftRightLogical %ulong %30 %uint_32 -%75 = OpUConvert %uint %74 -%131 = OpFunctionCall %void %76 %uint_44 %uint_2 %72 %75 -%133 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_blockType %132 -OpBranch %68 -%68 = OpLabel -%134 = OpPhi %_ptr_PhysicalStorageBuffer_blockType %71 %69 %133 %70 -%26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %134 %int_0 -%135 = OpConvertPtrToU %ulong %26 -%136 = OpFunctionCall %bool %32 %135 %uint_4 -OpSelectionMerge %137 None -OpBranchConditional %136 %138 %139 -%138 = OpLabel -OpStore %26 %int_531 Aligned 16 -OpBranch %137 -%139 = OpLabel -%140 = OpUConvert %uint %135 -%141 = OpShiftRightLogical %ulong %135 %uint_32 -%142 = OpUConvert %uint %141 -%144 = OpFunctionCall %void %76 %uint_46 %uint_2 %140 %142 -OpBranch %137 -%137 = OpLabel -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%32 = OpFunction %bool None %34 -%35 = OpFunctionParameter %ulong -%36 = OpFunctionParameter %uint -%37 = OpLabel -OpBranch %38 -%38 = OpLabel -%40 = OpPhi %uint %uint_1 %37 %41 %39 -OpLoopMerge %43 %39 None -OpBranch %39 -%39 = OpLabel -%41 = OpIAdd %uint %40 %uint_1 -%50 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %41 -%51 = OpLoad %ulong %50 -%52 = OpUGreaterThan %bool %51 %35 -OpBranchConditional %52 %43 %38 -%43 = OpLabel -%53 = OpISub %uint %41 %uint_1 -%54 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %53 -%55 = OpLoad %ulong %54 -%56 = OpISub %ulong %35 %55 -%57 = OpUConvert %ulong %36 -%58 = OpIAdd %ulong %56 %57 -%59 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %uint_0 -%60 = OpLoad %ulong %59 -%61 = OpUConvert %uint %60 -%62 = OpISub %uint %53 %uint_1 -%63 = OpIAdd %uint %62 %61 -%64 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %63 -%65 = OpLoad %ulong %64 -%66 = OpULessThanEqual %bool %58 %65 -OpReturnValue %66 -OpFunctionEnd -%76 = OpFunction %void None %77 -%78 = OpFunctionParameter %uint -%79 = OpFunctionParameter %uint -%80 = OpFunctionParameter %uint -%81 = OpFunctionParameter %uint -%82 = OpLabel -%88 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_0 -%91 = OpAtomicIAdd %uint %88 %uint_4 %uint_0 %uint_10 -%92 = OpIAdd %uint %91 %uint_10 -%93 = OpArrayLength %uint %86 1 -%94 = OpULessThanEqual %bool %92 %93 -OpSelectionMerge %95 None -OpBranchConditional %94 %96 %95 -%96 = OpLabel -%97 = OpIAdd %uint %91 %uint_0 -%98 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %97 -OpStore %98 %uint_10 -%100 = OpIAdd %uint %91 %uint_1 -%101 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %100 -OpStore %101 %uint_23 -%102 = OpIAdd %uint %91 %uint_2 -%103 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %102 -OpStore %103 %78 -%106 = OpIAdd %uint %91 %uint_3 -%107 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %106 -OpStore %107 %uint_5 -%111 = OpLoad %v3uint %gl_GlobalInvocationID -%112 = OpCompositeExtract %uint %111 0 -%113 = OpCompositeExtract %uint %111 1 -%114 = OpCompositeExtract %uint %111 2 -%115 = OpIAdd %uint %91 %uint_4 -%116 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %115 -OpStore %116 %112 -%117 = OpIAdd %uint %91 %uint_5 -%118 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %117 -OpStore %118 %113 -%120 = OpIAdd %uint %91 %uint_6 -%121 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %120 -OpStore %121 %114 -%123 = OpIAdd %uint %91 %uint_7 -%124 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %123 -OpStore %124 %79 -%125 = OpIAdd %uint %91 %uint_8 -%126 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %125 -OpStore %126 %80 -%128 = OpIAdd %uint %91 %uint_9 -%129 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %128 -OpStore %129 %81 -OpBranch %95 -%95 = OpLabel -OpReturn -OpFunctionEnd -)"; + const std::string output_funcs = kSearchAndTest + kStreamWrite4Compute; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u); + SinglePassRunAndMatch( + defs + decorates + globals + main_func + output_funcs, true, 7u, 23u); +} + +TEST_F(InstBuffAddrTest, StructLoad) { + // #version 450 + // #extension GL_EXT_buffer_reference : enable + // #extension GL_ARB_gpu_shader_int64 : enable + // struct Test { + // float a; + // }; + // + // layout(buffer_reference, std430, buffer_reference_align = 16) buffer + // TestBuffer { Test test; }; + // + // Test GetTest(uint64_t ptr) { + // return TestBuffer(ptr).test; + // } + // + // void main() { + // GetTest(0xe0000000); + // } + + const std::string defs = + R"( +OpCapability Shader +OpCapability Int64 +OpCapability PhysicalStorageBufferAddresses +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint Fragment %main "main" +; CHECK: OpEntryPoint Fragment %main "main" %inst_buff_addr_input_buffer %inst_buff_addr_output_buffer %gl_FragCoord +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpSourceExtension "GL_ARB_gpu_shader_int64" +OpSourceExtension "GL_EXT_buffer_reference" +OpName %main "main" +OpName %Test "Test" +OpMemberName %Test 0 "a" +OpName %Test_0 "Test" +OpMemberName %Test_0 0 "a" +OpName %TestBuffer "TestBuffer" +OpMemberName %TestBuffer 0 "test" +)"; + + // clang-format off + const std::string decorates = R"( +OpMemberDecorate %Test_0 0 Offset 0 +OpMemberDecorate %TestBuffer 0 Offset 0 +OpDecorate %TestBuffer Block +; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8 +)" + kInputDecorations + R"( +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +)" + kOutputDecorations + R"( +; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +)"; + + const std::string globals = R"( +%void = OpTypeVoid +%3 = OpTypeFunction %void +%ulong = OpTypeInt 64 0 +%float = OpTypeFloat 32 +%Test = OpTypeStruct %float +OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_TestBuffer PhysicalStorageBuffer +%Test_0 = OpTypeStruct %float +%TestBuffer = OpTypeStruct %Test_0 +%_ptr_PhysicalStorageBuffer_TestBuffer = OpTypePointer PhysicalStorageBuffer %TestBuffer +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_PhysicalStorageBuffer_Test_0 = OpTypePointer PhysicalStorageBuffer %Test_0 +%ulong_18446744073172680704 = OpConstant %ulong 18446744073172680704 +; CHECK: %47 = OpTypeFunction %bool %ulong %uint +)" + kInputGlobals + R"( +; CHECK: %90 = OpTypeFunction %void %uint %uint %uint %uint +)" + kOutputGlobals + R"( +; CHECK: %143 = OpConstantNull %Test_0 +)"; + // clang-format on + + const std::string main_func = + R"( +%main = OpFunction %void None %3 +%5 = OpLabel +%37 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_TestBuffer %ulong_18446744073172680704 +%38 = OpAccessChain %_ptr_PhysicalStorageBuffer_Test_0 %37 %int_0 +%39 = OpLoad %Test_0 %38 Aligned 16 +; CHECK-NOT: %39 = OpLoad %Test_0 %38 Aligned 16 +; CHECK: %43 = OpConvertPtrToU %ulong %38 +; CHECK: %80 = OpFunctionCall %bool %inst_buff_addr_search_and_test %43 %uint_4 +; CHECK: OpSelectionMerge %81 None +; CHECK: OpBranchConditional %80 %82 %83 +; CHECK: %82 = OpLabel +; CHECK: %84 = OpLoad %Test_0 %38 Aligned 16 +; CHECK: OpBranch %81 +; CHECK: %83 = OpLabel +; CHECK: %85 = OpUConvert %uint %43 +; CHECK: %87 = OpShiftRightLogical %ulong %43 %uint_32 +; CHECK: %88 = OpUConvert %uint %87 +; CHECK: %142 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_37 %uint_2 %85 %88 +; CHECK: OpBranch %81 +; CHECK: %81 = OpLabel +; CHECK: %144 = OpPhi %Test_0 %84 %82 %143 %83 +%40 = OpCopyLogical %Test %39 +; CHECK-NOT: %40 = OpCopyLogical %Test %39 +; CHECK: %40 = OpCopyLogical %Test %144 +OpReturn +OpFunctionEnd +)"; + + const std::string output_funcs = kSearchAndTest + kStreamWrite4Frag; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch( + defs + decorates + globals + main_func + output_funcs, true); } } // namespace diff --git a/test/opt/inst_debug_printf_test.cpp b/test/opt/inst_debug_printf_test.cpp index 8123ffbb..57e50440 100644 --- a/test/opt/inst_debug_printf_test.cpp +++ b/test/opt/inst_debug_printf_test.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2020 Valve Corporation -// Copyright (c) 2020 LunarG Inc. +// Copyright (c) 2020-2022 Valve Corporation +// Copyright (c) 2020-2022 LunarG Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -26,6 +26,20 @@ namespace spvtools { namespace opt { namespace { +static const std::string kOutputDecorations = R"( +; CHECK: OpDecorate [[output_buffer_type:%inst_printf_OutputBuffer]] Block +; CHECK: OpMemberDecorate [[output_buffer_type]] 0 Offset 0 +; CHECK: OpMemberDecorate [[output_buffer_type]] 1 Offset 4 +; CHECK: OpDecorate [[output_buffer_var:%\w+]] DescriptorSet 7 +; CHECK: OpDecorate [[output_buffer_var]] Binding 3 +)"; + +static const std::string kOutputGlobals = R"( +; CHECK: [[output_buffer_type]] = OpTypeStruct %uint %_runtimearr_uint +; CHECK: [[output_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[output_buffer_type]] +; CHECK: [[output_buffer_var]] = OpVariable [[output_ptr_type]] StorageBuffer +)"; + using InstDebugPrintfTest = PassTest<::testing::Test>; TEST_F(InstDebugPrintfTest, V4Float32) { @@ -65,6 +79,7 @@ OpExecutionMode %2 OriginUpperLeft %5 = OpString "Color is %vn" )"; + // clang-format off const std::string decorates = R"(OpDecorate %6 DescriptorSet 0 OpDecorate %6 Binding 1 @@ -73,11 +88,7 @@ OpDecorate %7 Binding 0 OpDecorate %3 Location 0 OpDecorate %4 Location 0 ; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -; CHECK: OpDecorate %_struct_47 Block -; CHECK: OpMemberDecorate %_struct_47 0 Offset 0 -; CHECK: OpMemberDecorate %_struct_47 1 Offset 4 -; CHECK: OpDecorate %49 DescriptorSet 7 -; CHECK: OpDecorate %49 Binding 3 +)" + kOutputDecorations + R"( ; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord )"; @@ -101,15 +112,14 @@ OpDecorate %4 Location 0 ; CHECK: %uint = OpTypeInt 32 0 ; CHECK: %38 = OpTypeFunction %void %uint %uint %uint %uint %uint %uint ; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint -; CHECK: %_struct_47 = OpTypeStruct %uint %_runtimearr_uint -; CHECK: %_ptr_StorageBuffer__struct_47 = OpTypePointer StorageBuffer %_struct_47 -; CHECK: %49 = OpVariable %_ptr_StorageBuffer__struct_47 StorageBuffer +)" + kOutputGlobals + R"( ; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint ; CHECK: %bool = OpTypeBool ; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float ; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input ; CHECK: %v4uint = OpTypeVector %uint 4 )"; + // clang-format on const std::string main = R"(%2 = OpFunction %void None %9 @@ -129,7 +139,7 @@ OpDecorate %4 Location 0 ; CHECK: %34 = OpBitcast %uint %33 ; CHECK: %35 = OpCompositeExtract %float %25 3 ; CHECK: %36 = OpBitcast %uint %35 -; CHECK: %101 = OpFunctionCall %void %37 %uint_36 %uint_5 %30 %32 %34 %36 +; CHECK: %101 = OpFunctionCall %void %inst_printf_stream_write_6 %uint_36 %uint_5 %30 %32 %34 %36 ; CHECK: OpBranch %102 ; CHECK: %102 = OpLabel OpStore %4 %25 @@ -137,8 +147,8 @@ OpReturn OpFunctionEnd )"; - const std::string output_func = - R"(; CHECK: %37 = OpFunction %void None %38 + const std::string output_func = R"( +; CHECK: %inst_printf_stream_write_6 = OpFunction %void None %38 ; CHECK: %39 = OpFunctionParameter %uint ; CHECK: %40 = OpFunctionParameter %uint ; CHECK: %41 = OpFunctionParameter %uint @@ -146,50 +156,50 @@ OpFunctionEnd ; CHECK: %43 = OpFunctionParameter %uint ; CHECK: %44 = OpFunctionParameter %uint ; CHECK: %45 = OpLabel -; CHECK: %52 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_0 +; CHECK: %52 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_0 ; CHECK: %55 = OpAtomicIAdd %uint %52 %uint_4 %uint_0 %uint_12 ; CHECK: %56 = OpIAdd %uint %55 %uint_12 -; CHECK: %57 = OpArrayLength %uint %49 1 +; CHECK: %57 = OpArrayLength %uint %inst_printf_output_buffer 1 ; CHECK: %59 = OpULessThanEqual %bool %56 %57 ; CHECK: OpSelectionMerge %60 None ; CHECK: OpBranchConditional %59 %61 %60 ; CHECK: %61 = OpLabel ; CHECK: %62 = OpIAdd %uint %55 %uint_0 -; CHECK: %64 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %62 +; CHECK: %64 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %62 ; CHECK: OpStore %64 %uint_12 ; CHECK: %66 = OpIAdd %uint %55 %uint_1 -; CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %66 +; CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %66 ; CHECK: OpStore %67 %uint_23 ; CHECK: %69 = OpIAdd %uint %55 %uint_2 -; CHECK: %70 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %69 +; CHECK: %70 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %69 ; CHECK: OpStore %70 %39 ; CHECK: %72 = OpIAdd %uint %55 %uint_3 -; CHECK: %73 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %72 +; CHECK: %73 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %72 ; CHECK: OpStore %73 %uint_4 ; CHECK: %76 = OpLoad %v4float %gl_FragCoord ; CHECK: %78 = OpBitcast %v4uint %76 ; CHECK: %79 = OpCompositeExtract %uint %78 0 ; CHECK: %80 = OpIAdd %uint %55 %uint_4 -; CHECK: %81 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %80 +; CHECK: %81 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %80 ; CHECK: OpStore %81 %79 ; CHECK: %82 = OpCompositeExtract %uint %78 1 ; CHECK: %83 = OpIAdd %uint %55 %uint_5 -; CHECK: %84 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %83 +; CHECK: %84 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %83 ; CHECK: OpStore %84 %82 ; CHECK: %86 = OpIAdd %uint %55 %uint_7 -; CHECK: %87 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %86 +; CHECK: %87 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %86 ; CHECK: OpStore %87 %40 ; CHECK: %89 = OpIAdd %uint %55 %uint_8 -; CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %89 +; CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %89 ; CHECK: OpStore %90 %41 ; CHECK: %92 = OpIAdd %uint %55 %uint_9 -; CHECK: %93 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %92 +; CHECK: %93 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %92 ; CHECK: OpStore %93 %42 ; CHECK: %95 = OpIAdd %uint %55 %uint_10 -; CHECK: %96 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %95 +; CHECK: %96 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %95 ; CHECK: OpStore %96 %43 ; CHECK: %98 = OpIAdd %uint %55 %uint_11 -; CHECK: %99 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %98 +; CHECK: %99 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %98 ; CHECK: OpStore %99 %44 ; CHECK: OpBranch %60 ; CHECK: %60 = OpLabel @@ -206,8 +216,8 @@ OpFunctionEnd // // Compute shader // Geometry shader -// Tesselation control shader -// Tesselation eval shader +// Tessellation control shader +// Tessellation eval shader // Vertex shader } // namespace diff --git a/test/opt/instruction_test.cpp b/test/opt/instruction_test.cpp index c5b92efb..dd749ab4 100644 --- a/test/opt/instruction_test.cpp +++ b/test/opt/instruction_test.cpp @@ -62,12 +62,6 @@ TEST(InstructionTest, CreateWithOpcodeAndNoOperands) { EXPECT_EQ(inst.end(), inst.begin()); } -TEST(InstructionTest, OperandAsCString) { - Operand::OperandData abcde{0x64636261, 0x65}; - Operand operand(SPV_OPERAND_TYPE_LITERAL_STRING, std::move(abcde)); - EXPECT_STREQ("abcde", operand.AsCString()); -} - TEST(InstructionTest, OperandAsString) { Operand::OperandData abcde{0x64636261, 0x65}; Operand operand(SPV_OPERAND_TYPE_LITERAL_STRING, std::move(abcde)); @@ -1531,6 +1525,45 @@ OpFunctionEnd EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer()); } +TEST_F(DescriptorTypeTest, GetShader100DebugOpcode) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + %2 = OpString "ps.hlsl" + %3 = OpString "#line 1 \"ps.hlsl\"" + %void = OpTypeVoid + %5 = OpExtInst %void %1 DebugExpression + %6 = OpExtInst %void %1 DebugSource %2 %3 +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + Instruction* debug_expression = context->get_def_use_mgr()->GetDef(5); + EXPECT_EQ(debug_expression->GetShader100DebugOpcode(), + NonSemanticShaderDebugInfo100DebugExpression); + Instruction* debug_source = context->get_def_use_mgr()->GetDef(6); + EXPECT_EQ(debug_source->GetShader100DebugOpcode(), + NonSemanticShaderDebugInfo100DebugSource); + + // Test that an opcode larger than the max will return Max. This instruction + // cannot be in the assembly above because the assembler expects the string + // for the opcode, so we cannot use an arbitrary number. However, a binary + // file could have an arbitrary number. + std::unique_ptr past_max(debug_expression->Clone(context.get())); + const uint32_t kExtInstOpcodeInIndex = 1; + uint32_t large_opcode = NonSemanticShaderDebugInfo100InstructionsMax + 2; + past_max->SetInOperand(kExtInstOpcodeInIndex, {large_opcode}); + EXPECT_EQ(past_max->GetShader100DebugOpcode(), + NonSemanticShaderDebugInfo100InstructionsMax); + + // Test that an opcode without a value in the enum, but less than Max returns + // the same value. + uint32_t opcode = NonSemanticShaderDebugInfo100InstructionsMax - 2; + past_max->SetInOperand(kExtInstOpcodeInIndex, {opcode}); + EXPECT_EQ(past_max->GetShader100DebugOpcode(), opcode); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/interface_var_sroa_test.cpp b/test/opt/interface_var_sroa_test.cpp new file mode 100644 index 00000000..77624587 --- /dev/null +++ b/test/opt/interface_var_sroa_test.cpp @@ -0,0 +1,410 @@ +// Copyright (c) 2022 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 + +#include "gmock/gmock.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using InterfaceVariableScalarReplacementTest = PassTest<::testing::Test>; + +TEST_F(InterfaceVariableScalarReplacementTest, + ReplaceInterfaceVarsWithScalars) { + const std::string spirv = R"( + OpCapability Shader + OpCapability Tessellation + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationControl %func "shader" %x %y %z %w %u %v + +; CHECK: OpName [[x:%\w+]] "x" +; CHECK-NOT: OpName {{%\w+}} "x" +; CHECK: OpName [[y:%\w+]] "y" +; CHECK-NOT: OpName {{%\w+}} "y" +; CHECK: OpName [[z0:%\w+]] "z" +; CHECK: OpName [[z1:%\w+]] "z" +; CHECK: OpName [[w0:%\w+]] "w" +; CHECK: OpName [[w1:%\w+]] "w" +; CHECK: OpName [[u0:%\w+]] "u" +; CHECK: OpName [[u1:%\w+]] "u" +; CHECK: OpName [[v0:%\w+]] "v" +; CHECK: OpName [[v1:%\w+]] "v" +; CHECK: OpName [[v2:%\w+]] "v" +; CHECK: OpName [[v3:%\w+]] "v" +; CHECK: OpName [[v4:%\w+]] "v" +; CHECK: OpName [[v5:%\w+]] "v" + OpName %x "x" + OpName %y "y" + OpName %z "z" + OpName %w "w" + OpName %u "u" + OpName %v "v" + +; CHECK-DAG: OpDecorate [[x]] Location 2 +; CHECK-DAG: OpDecorate [[y]] Location 0 +; CHECK-DAG: OpDecorate [[z0]] Location 0 +; CHECK-DAG: OpDecorate [[z0]] Component 0 +; CHECK-DAG: OpDecorate [[z1]] Location 1 +; CHECK-DAG: OpDecorate [[z1]] Component 0 +; CHECK-DAG: OpDecorate [[z0]] Patch +; CHECK-DAG: OpDecorate [[z1]] Patch +; CHECK-DAG: OpDecorate [[w0]] Location 2 +; CHECK-DAG: OpDecorate [[w0]] Component 0 +; CHECK-DAG: OpDecorate [[w1]] Location 3 +; CHECK-DAG: OpDecorate [[w1]] Component 0 +; CHECK-DAG: OpDecorate [[w0]] Patch +; CHECK-DAG: OpDecorate [[w1]] Patch +; CHECK-DAG: OpDecorate [[u0]] Location 3 +; CHECK-DAG: OpDecorate [[u0]] Component 2 +; CHECK-DAG: OpDecorate [[u1]] Location 4 +; CHECK-DAG: OpDecorate [[u1]] Component 2 +; CHECK-DAG: OpDecorate [[v0]] Location 3 +; CHECK-DAG: OpDecorate [[v0]] Component 3 +; CHECK-DAG: OpDecorate [[v1]] Location 4 +; CHECK-DAG: OpDecorate [[v1]] Component 3 +; CHECK-DAG: OpDecorate [[v2]] Location 5 +; CHECK-DAG: OpDecorate [[v2]] Component 3 +; CHECK-DAG: OpDecorate [[v3]] Location 6 +; CHECK-DAG: OpDecorate [[v3]] Component 3 +; CHECK-DAG: OpDecorate [[v4]] Location 7 +; CHECK-DAG: OpDecorate [[v4]] Component 3 +; CHECK-DAG: OpDecorate [[v5]] Location 8 +; CHECK-DAG: OpDecorate [[v5]] Component 3 + OpDecorate %z Patch + OpDecorate %w Patch + OpDecorate %z Location 0 + OpDecorate %x Location 2 + OpDecorate %v Location 3 + OpDecorate %v Component 3 + OpDecorate %y Location 0 + OpDecorate %w Location 2 + OpDecorate %u Location 3 + OpDecorate %u Component 2 + + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %uint_4 = OpConstant %uint 4 +%_arr_uint_uint_2 = OpTypeArray %uint %uint_2 +%_ptr_Output__arr_uint_uint_2 = OpTypePointer Output %_arr_uint_uint_2 +%_ptr_Input__arr_uint_uint_2 = OpTypePointer Input %_arr_uint_uint_2 +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Output_uint = OpTypePointer Output %uint +%_arr_arr_uint_uint_2_3 = OpTypeArray %_arr_uint_uint_2 %uint_3 +%_ptr_Input__arr_arr_uint_uint_2_3 = OpTypePointer Input %_arr_arr_uint_uint_2_3 +%_arr_arr_arr_uint_uint_2_3_4 = OpTypeArray %_arr_arr_uint_uint_2_3 %uint_4 +%_ptr_Output__arr_arr_arr_uint_uint_2_3_4 = OpTypePointer Output %_arr_arr_arr_uint_uint_2_3_4 +%_ptr_Output__arr_arr_uint_uint_2_3 = OpTypePointer Output %_arr_arr_uint_uint_2_3 + %z = OpVariable %_ptr_Output__arr_uint_uint_2 Output + %x = OpVariable %_ptr_Output__arr_uint_uint_2 Output + %y = OpVariable %_ptr_Input__arr_uint_uint_2 Input + %w = OpVariable %_ptr_Input__arr_uint_uint_2 Input + %u = OpVariable %_ptr_Input__arr_arr_uint_uint_2_3 Input + %v = OpVariable %_ptr_Output__arr_arr_arr_uint_uint_2_3_4 Output + +; CHECK-DAG: [[x]] = OpVariable %_ptr_Output__arr_uint_uint_2 Output +; CHECK-DAG: [[y]] = OpVariable %_ptr_Input__arr_uint_uint_2 Input +; CHECK-DAG: [[z0]] = OpVariable %_ptr_Output_uint Output +; CHECK-DAG: [[z1]] = OpVariable %_ptr_Output_uint Output +; CHECK-DAG: [[w0]] = OpVariable %_ptr_Input_uint Input +; CHECK-DAG: [[w1]] = OpVariable %_ptr_Input_uint Input +; CHECK-DAG: [[u0]] = OpVariable %_ptr_Input__arr_uint_uint_3 Input +; CHECK-DAG: [[u1]] = OpVariable %_ptr_Input__arr_uint_uint_3 Input +; CHECK-DAG: [[v0]] = OpVariable %_ptr_Output__arr_uint_uint_4 Output +; CHECK-DAG: [[v1]] = OpVariable %_ptr_Output__arr_uint_uint_4 Output +; CHECK-DAG: [[v2]] = OpVariable %_ptr_Output__arr_uint_uint_4 Output +; CHECK-DAG: [[v3]] = OpVariable %_ptr_Output__arr_uint_uint_4 Output +; CHECK-DAG: [[v4]] = OpVariable %_ptr_Output__arr_uint_uint_4 Output +; CHECK-DAG: [[v5]] = OpVariable %_ptr_Output__arr_uint_uint_4 Output + + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + +; CHECK: [[w0_value:%\w+]] = OpLoad %uint [[w0]] +; CHECK: [[w1_value:%\w+]] = OpLoad %uint [[w1]] +; CHECK: [[w_value:%\w+]] = OpCompositeConstruct %_arr_uint_uint_2 [[w0_value]] [[w1_value]] +; CHECK: [[w0:%\w+]] = OpCompositeExtract %uint [[w_value]] 0 +; CHECK: OpStore [[z0]] [[w0]] +; CHECK: [[w1:%\w+]] = OpCompositeExtract %uint [[w_value]] 1 +; CHECK: OpStore [[z1]] [[w1]] + %w_value = OpLoad %_arr_uint_uint_2 %w + OpStore %z %w_value + +; CHECK: [[u00_ptr:%\w+]] = OpAccessChain %_ptr_Input_uint [[u0]] %uint_0 +; CHECK: [[u00:%\w+]] = OpLoad %uint [[u00_ptr]] +; CHECK: [[u10_ptr:%\w+]] = OpAccessChain %_ptr_Input_uint [[u1]] %uint_0 +; CHECK: [[u10:%\w+]] = OpLoad %uint [[u10_ptr]] +; CHECK: [[u01_ptr:%\w+]] = OpAccessChain %_ptr_Input_uint [[u0]] %uint_1 +; CHECK: [[u01:%\w+]] = OpLoad %uint [[u01_ptr]] +; CHECK: [[u11_ptr:%\w+]] = OpAccessChain %_ptr_Input_uint [[u1]] %uint_1 +; CHECK: [[u11:%\w+]] = OpLoad %uint [[u11_ptr]] +; CHECK: [[u02_ptr:%\w+]] = OpAccessChain %_ptr_Input_uint [[u0]] %uint_2 +; CHECK: [[u02:%\w+]] = OpLoad %uint [[u02_ptr]] +; CHECK: [[u12_ptr:%\w+]] = OpAccessChain %_ptr_Input_uint [[u1]] %uint_2 +; CHECK: [[u12:%\w+]] = OpLoad %uint [[u12_ptr]] + +; CHECK-DAG: [[u0_val:%\w+]] = OpCompositeConstruct %_arr_uint_uint_2 [[u00]] [[u10]] +; CHECK-DAG: [[u1_val:%\w+]] = OpCompositeConstruct %_arr_uint_uint_2 [[u01]] [[u11]] +; CHECK-DAG: [[u2_val:%\w+]] = OpCompositeConstruct %_arr_uint_uint_2 [[u02]] [[u12]] + +; CHECK: [[u_val:%\w+]] = OpCompositeConstruct %_arr__arr_uint_uint_2_uint_3 [[u0_val]] [[u1_val]] [[u2_val]] + +; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_Output_uint [[v0]] %uint_1 +; CHECK: [[val:%\w+]] = OpCompositeExtract %uint [[u_val]] 0 0 +; CHECK: OpStore [[ptr]] [[val]] +; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_Output_uint [[v1]] %uint_1 +; CHECK: [[val:%\w+]] = OpCompositeExtract %uint [[u_val]] 0 1 +; CHECK: OpStore [[ptr]] [[val]] +; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_Output_uint [[v2]] %uint_1 +; CHECK: [[val:%\w+]] = OpCompositeExtract %uint [[u_val]] 1 0 +; CHECK: OpStore [[ptr]] [[val]] +; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_Output_uint [[v3]] %uint_1 +; CHECK: [[val:%\w+]] = OpCompositeExtract %uint [[u_val]] 1 1 +; CHECK: OpStore [[ptr]] [[val]] +; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_Output_uint [[v4]] %uint_1 +; CHECK: [[val:%\w+]] = OpCompositeExtract %uint [[u_val]] 2 0 +; CHECK: OpStore [[ptr]] [[val]] +; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_Output_uint [[v5]] %uint_1 +; CHECK: [[val:%\w+]] = OpCompositeExtract %uint [[u_val]] 2 1 +; CHECK: OpStore [[ptr]] [[val]] + %v_ptr = OpAccessChain %_ptr_Output__arr_arr_uint_uint_2_3 %v %uint_1 + %u_val = OpLoad %_arr_arr_uint_uint_2_3 %u + OpStore %v_ptr %u_val + + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(spirv, true); +} + +TEST_F(InterfaceVariableScalarReplacementTest, + CheckPatchDecorationPreservation) { + // Make sure scalars for the variables with the extra arrayness have the extra + // arrayness after running the pass while others do not have it. + // Only "y" does not have the extra arrayness in the following SPIR-V. + const std::string spirv = R"( + OpCapability Shader + OpCapability Tessellation + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationEvaluation %func "shader" %x %y %z %w + OpDecorate %z Patch + OpDecorate %w Patch + OpDecorate %z Location 0 + OpDecorate %x Location 2 + OpDecorate %y Location 0 + OpDecorate %w Location 1 + OpName %x "x" + OpName %y "y" + OpName %z "z" + OpName %w "w" + + ; CHECK: OpName [[y:%\w+]] "y" + ; CHECK-NOT: OpName {{%\w+}} "y" + ; CHECK-DAG: OpName [[z0:%\w+]] "z" + ; CHECK-DAG: OpName [[z1:%\w+]] "z" + ; CHECK-DAG: OpName [[w0:%\w+]] "w" + ; CHECK-DAG: OpName [[w1:%\w+]] "w" + ; CHECK-DAG: OpName [[x0:%\w+]] "x" + ; CHECK-DAG: OpName [[x1:%\w+]] "x" + + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_uint_uint_2 = OpTypeArray %uint %uint_2 +%_ptr_Output__arr_uint_uint_2 = OpTypePointer Output %_arr_uint_uint_2 +%_ptr_Input__arr_uint_uint_2 = OpTypePointer Input %_arr_uint_uint_2 + %z = OpVariable %_ptr_Output__arr_uint_uint_2 Output + %x = OpVariable %_ptr_Output__arr_uint_uint_2 Output + %y = OpVariable %_ptr_Input__arr_uint_uint_2 Input + %w = OpVariable %_ptr_Input__arr_uint_uint_2 Input + + ; CHECK-DAG: [[y]] = OpVariable %_ptr_Input__arr_uint_uint_2 Input + ; CHECK-DAG: [[z0]] = OpVariable %_ptr_Output_uint Output + ; CHECK-DAG: [[z1]] = OpVariable %_ptr_Output_uint Output + ; CHECK-DAG: [[w0]] = OpVariable %_ptr_Input_uint Input + ; CHECK-DAG: [[w1]] = OpVariable %_ptr_Input_uint Input + ; CHECK-DAG: [[x0]] = OpVariable %_ptr_Output_uint Output + ; CHECK-DAG: [[x1]] = OpVariable %_ptr_Output_uint Output + + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(spirv, true); +} + +TEST_F(InterfaceVariableScalarReplacementTest, + CheckEntryPointInterfaceOperands) { + const std::string spirv = R"( + OpCapability Shader + OpCapability Tessellation + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationEvaluation %tess "tess" %x %y + OpEntryPoint Vertex %vert "vert" %w + OpDecorate %z Location 0 + OpDecorate %x Location 2 + OpDecorate %y Location 0 + OpDecorate %w Location 1 + OpName %x "x" + OpName %y "y" + OpName %z "z" + OpName %w "w" + + ; CHECK: OpName [[y:%\w+]] "y" + ; CHECK-NOT: OpName {{%\w+}} "y" + ; CHECK-DAG: OpName [[x0:%\w+]] "x" + ; CHECK-DAG: OpName [[x1:%\w+]] "x" + ; CHECK-DAG: OpName [[w0:%\w+]] "w" + ; CHECK-DAG: OpName [[w1:%\w+]] "w" + ; CHECK-DAG: OpName [[z:%\w+]] "z" + ; CHECK-NOT: OpName {{%\w+}} "z" + + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_uint_uint_2 = OpTypeArray %uint %uint_2 +%_ptr_Output__arr_uint_uint_2 = OpTypePointer Output %_arr_uint_uint_2 +%_ptr_Input__arr_uint_uint_2 = OpTypePointer Input %_arr_uint_uint_2 + %z = OpVariable %_ptr_Output__arr_uint_uint_2 Output + %x = OpVariable %_ptr_Output__arr_uint_uint_2 Output + %y = OpVariable %_ptr_Input__arr_uint_uint_2 Input + %w = OpVariable %_ptr_Input__arr_uint_uint_2 Input + + ; CHECK-DAG: [[y]] = OpVariable %_ptr_Input__arr_uint_uint_2 Input + ; CHECK-DAG: [[z]] = OpVariable %_ptr_Output__arr_uint_uint_2 Output + ; CHECK-DAG: [[w0]] = OpVariable %_ptr_Input_uint Input + ; CHECK-DAG: [[w1]] = OpVariable %_ptr_Input_uint Input + ; CHECK-DAG: [[x0]] = OpVariable %_ptr_Output_uint Output + ; CHECK-DAG: [[x1]] = OpVariable %_ptr_Output_uint Output + + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %tess = OpFunction %void None %void_f + %bb0 = OpLabel + OpReturn + OpFunctionEnd + %vert = OpFunction %void None %void_f + %bb1 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(spirv, true); +} + +class InterfaceVarSROAErrorTest : public PassTest<::testing::Test> { + public: + InterfaceVarSROAErrorTest() + : consumer_([this](spv_message_level_t level, const char*, + const spv_position_t& position, const char* message) { + if (!error_message_.empty()) error_message_ += "\n"; + switch (level) { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + error_message_ += "ERROR"; + break; + case SPV_MSG_WARNING: + error_message_ += "WARNING"; + break; + case SPV_MSG_INFO: + error_message_ += "INFO"; + break; + case SPV_MSG_DEBUG: + error_message_ += "DEBUG"; + break; + } + error_message_ += + ": " + std::to_string(position.index) + ": " + message; + }) {} + + Pass::Status RunPass(const std::string& text) { + std::unique_ptr context_ = + spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_2, consumer_, text); + if (!context_.get()) return Pass::Status::Failure; + + PassManager manager; + manager.SetMessageConsumer(consumer_); + manager.AddPass(); + + return manager.Run(context_.get()); + } + + std::string GetErrorMessage() const { return error_message_; } + + void TearDown() override { error_message_.clear(); } + + private: + spvtools::MessageConsumer consumer_; + std::string error_message_; +}; + +TEST_F(InterfaceVarSROAErrorTest, CheckConflictOfExtraArraynessBetweenEntries) { + const std::string spirv = R"( + OpCapability Shader + OpCapability Tessellation + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationControl %tess "tess" %x %y %z + OpEntryPoint Vertex %vert "vert" %z %w + OpDecorate %z Location 0 + OpDecorate %x Location 2 + OpDecorate %y Location 0 + OpDecorate %w Location 1 + OpName %x "x" + OpName %y "y" + OpName %z "z" + OpName %w "w" + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_uint_uint_2 = OpTypeArray %uint %uint_2 +%_ptr_Output__arr_uint_uint_2 = OpTypePointer Output %_arr_uint_uint_2 +%_ptr_Input__arr_uint_uint_2 = OpTypePointer Input %_arr_uint_uint_2 + %z = OpVariable %_ptr_Output__arr_uint_uint_2 Output + %x = OpVariable %_ptr_Output__arr_uint_uint_2 Output + %y = OpVariable %_ptr_Input__arr_uint_uint_2 Input + %w = OpVariable %_ptr_Input__arr_uint_uint_2 Input + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %tess = OpFunction %void None %void_f + %bb0 = OpLabel + OpReturn + OpFunctionEnd + %vert = OpFunction %void None %void_f + %bb1 = OpLabel + OpReturn + OpFunctionEnd + )"; + + EXPECT_EQ(RunPass(spirv), Pass::Status::Failure); + const char expected_error[] = + "ERROR: 0: A variable is arrayed for an entry point but it is not " + "arrayed for another entry point\n" + " %z = OpVariable %_ptr_Output__arr_uint_uint_2 Output"; + EXPECT_STREQ(GetErrorMessage().c_str(), expected_error); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/test/opt/ir_context_test.cpp b/test/opt/ir_context_test.cpp index b6866d01..dcae7cf8 100644 --- a/test/opt/ir_context_test.cpp +++ b/test/opt/ir_context_test.cpp @@ -90,6 +90,21 @@ TEST_F(IRContextTest, IndividualValidAfterBuild) { } } +TEST_F(IRContextTest, DontRebuildValidAnalysis) { + std::unique_ptr module(new Module()); + IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module), + spvtools::MessageConsumer()); + + auto* oldCfg = localContext.cfg(); + auto* oldDefUse = localContext.get_def_use_mgr(); + localContext.BuildInvalidAnalyses(IRContext::kAnalysisCFG | + IRContext::kAnalysisDefUse); + auto* newCfg = localContext.cfg(); + auto* newDefUse = localContext.get_def_use_mgr(); + EXPECT_EQ(oldCfg, newCfg); + EXPECT_EQ(oldDefUse, newDefUse); +} + TEST_F(IRContextTest, AllValidAfterBuild) { std::unique_ptr module = MakeUnique(); IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module), @@ -1132,40 +1147,6 @@ OpFunctionEnd)"; 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()->AddDebugValueIfVarDeclIsVisible(dbg_decl, 26, 22, - dbg_decl, nullptr); - EXPECT_EQ(dbg_decl->NextNode()->opcode(), SpvOpReturn); - - // DebugValue should be added because result id '20' is used for DebugDeclare. - ctx->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(dbg_decl, 20, 22, - dbg_decl, nullptr); - 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()->AddDebugValueIfVarDeclIsVisible(dbg_decl, 20, 7, - dbg_decl, nullptr); - 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()->AddDebugValueIfVarDeclIsVisible(dbg_decl, 26, 7, - dbg_decl, nullptr); - dbg_value = dbg_decl->NextNode(); - EXPECT_EQ(dbg_value->GetOpenCL100DebugOpcode(), OpenCLDebugInfo100DebugValue); - EXPECT_EQ(dbg_value->GetSingleWordOperand(kDebugValueOperandValueIndex), 7); } } // namespace diff --git a/test/opt/ir_loader_test.cpp b/test/opt/ir_loader_test.cpp index 475dd235..ccdd032e 100644 --- a/test/opt/ir_loader_test.cpp +++ b/test/opt/ir_loader_test.cpp @@ -105,6 +105,22 @@ TEST(IrBuilder, RoundTripIncompleteFunction) { DoRoundTripCheck("%2 = OpFunction %1 None %3\n"); } +TEST(IrBuilder, RoundTripFunctionPointer) { + DoRoundTripCheck( + "OpCapability Linkage\n" + "OpCapability FunctionPointersINTEL\n" + "OpName %some_function \"some_function\"\n" + "OpName %ptr_to_function \"ptr_to_function\"\n" + "OpDecorate %some_function LinkageAttributes \"some_function\" Import\n" + "%float = OpTypeFloat 32\n" + "%4 = OpTypeFunction %float %float\n" + "%_ptr_Function_4 = OpTypePointer Function %4\n" + "%ptr_to_function = OpConstantFunctionPointerINTEL %_ptr_Function_4 " + "%some_function\n" + "%some_function = OpFunction %float Const %4\n" + "%6 = OpFunctionParameter %float\n" + "OpFunctionEnd\n"); +} TEST(IrBuilder, KeepLineDebugInfo) { // #version 310 es // void main() {} diff --git a/test/opt/local_access_chain_convert_test.cpp b/test/opt/local_access_chain_convert_test.cpp index 6fcf23fb..07fb537c 100644 --- a/test/opt/local_access_chain_convert_test.cpp +++ b/test/opt/local_access_chain_convert_test.cpp @@ -1156,6 +1156,197 @@ TEST_F(LocalAccessChainConvertTest, AccessChainWithNoIndex) { SinglePassRunAndMatch(before, true); } +TEST_F(LocalAccessChainConvertTest, AccessChainWithLongIndex) { + // The access chain take a value that is larger than 32-bit. The index cannot + // be encoded in an OpCompositeExtract, so nothing should be done. + const std::string before = + R"(OpCapability Shader +OpCapability Int64 +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main_0004f4d4_85b2f584" +OpExecutionMode %2 OriginUpperLeft +%ulong = OpTypeInt 64 0 +%ulong_8589934592 = OpConstant %ulong 8589934592 +%ulong_8589934591 = OpConstant %ulong 8589934591 +%_arr_ulong_ulong_8589934592 = OpTypeArray %ulong %ulong_8589934592 +%_ptr_Function__arr_ulong_ulong_8589934592 = OpTypePointer Function %_arr_ulong_ulong_8589934592 +%_ptr_Function_ulong = OpTypePointer Function %ulong +%void = OpTypeVoid +%10 = OpTypeFunction %void +%2 = OpFunction %void None %10 +%11 = OpLabel +%12 = OpVariable %_ptr_Function__arr_ulong_ulong_8589934592 Function +%13 = OpAccessChain %_ptr_Function_ulong %12 %ulong_8589934591 +%14 = OpLoad %ulong %13 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before, before, false, + true); +} + +TEST_F(LocalAccessChainConvertTest, AccessChainWith32BitIndexInLong) { + // The access chain has a value that is 32-bits, but it is stored in a 64-bit + // variable. This access change can be converted to an extract. + const std::string before = + R"( +; CHECK: OpFunction +; CHECK: [[var:%\w+]] = OpVariable +; CHECK: [[ld:%\w+]] = OpLoad {{%\w+}} [[var]] +; CHECK: OpCompositeExtract %ulong [[ld]] 3 + OpCapability Shader + OpCapability Int64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main_0004f4d4_85b2f584" + OpExecutionMode %2 OriginUpperLeft + %ulong = OpTypeInt 64 0 +%ulong_8589934592 = OpConstant %ulong 8589934592 +%ulong_3 = OpConstant %ulong 3 +%_arr_ulong_ulong_8589934592 = OpTypeArray %ulong %ulong_8589934592 +%_ptr_Function__arr_ulong_ulong_8589934592 = OpTypePointer Function %_arr_ulong_ulong_8589934592 +%_ptr_Function_ulong = OpTypePointer Function %ulong + %void = OpTypeVoid + %10 = OpTypeFunction %void + %2 = OpFunction %void None %10 + %11 = OpLabel + %12 = OpVariable %_ptr_Function__arr_ulong_ulong_8589934592 Function + %13 = OpAccessChain %_ptr_Function_ulong %12 %ulong_3 + %14 = OpLoad %ulong %13 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(before, true); +} + +TEST_F(LocalAccessChainConvertTest, AccessChainWithVarIndex) { + // The access chain has a value that is not constant, so there should not be + // any changes. + const std::string before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main_0004f4d4_85b2f584" +OpExecutionMode %2 OriginUpperLeft +%uint = OpTypeInt 32 0 +%uint_5 = OpConstant %uint 5 +%_arr_uint_uint_5 = OpTypeArray %uint %uint_5 +%_ptr_Function__arr_uint_uint_5 = OpTypePointer Function %_arr_uint_uint_5 +%_ptr_Function_uint = OpTypePointer Function %uint +%8 = OpUndef %uint +%void = OpTypeVoid +%10 = OpTypeFunction %void +%2 = OpFunction %void None %10 +%11 = OpLabel +%12 = OpVariable %_ptr_Function__arr_uint_uint_5 Function +%13 = OpAccessChain %_ptr_Function_uint %12 %8 +%14 = OpLoad %uint %13 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before, before, false, + true); +} + +TEST_F(LocalAccessChainConvertTest, OutOfBoundsAccess) { + // The access chain indexes element 12 in an array of size 10. Nothing should + // be done. + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 +OpExecutionMode %2 OriginUpperLeft +%void = OpTypeVoid +%5 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%int_10 = OpConstant %int 10 +%_arr_int_int_10 = OpTypeArray %int %int_10 +%_ptr_Function_int = OpTypePointer Function %int +%int_12 = OpConstant %int 12 +%_ptr_Output_int = OpTypePointer Output %int +%3 = OpVariable %_ptr_Output_int Output +%_ptr_Function__arr_int_int_10 = OpTypePointer Function %_arr_int_int_10 +%2 = OpFunction %void None %5 +%13 = OpLabel +%14 = OpVariable %_ptr_Function__arr_int_int_10 Function +%15 = OpAccessChain %_ptr_Function_int %14 %int_12 +%16 = OpLoad %int %15 +OpStore %3 %16 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, false, + true); +} + +TEST_F(LocalAccessChainConvertTest, OutOfBoundsAccessAtBoundary) { + // The access chain indexes element 10 in an array of size 10. Nothing should + // be done. + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 +OpExecutionMode %2 OriginUpperLeft +%void = OpTypeVoid +%5 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%int_10 = OpConstant %int 10 +%_arr_int_int_10 = OpTypeArray %int %int_10 +%_ptr_Function_int = OpTypePointer Function %int +%_ptr_Output_int = OpTypePointer Output %int +%3 = OpVariable %_ptr_Output_int Output +%_ptr_Function__arr_int_int_10 = OpTypePointer Function %_arr_int_int_10 +%2 = OpFunction %void None %5 +%12 = OpLabel +%13 = OpVariable %_ptr_Function__arr_int_int_10 Function +%14 = OpAccessChain %_ptr_Function_int %13 %int_10 +%15 = OpLoad %int %14 +OpStore %3 %15 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, false, + true); +} + +TEST_F(LocalAccessChainConvertTest, NegativeIndex) { + // The access chain has a negative index and should not be converted because + // the extract instruction cannot hold a negative number. + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +%void = OpTypeVoid +%4 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%uint_3808428041 = OpConstant %uint 3808428041 +%_arr_int_uint_3808428041 = OpTypeArray %int %uint_3808428041 +%_ptr_Function__arr_int_uint_3808428041 = OpTypePointer Function %_arr_int_uint_3808428041 +%_ptr_Function_int = OpTypePointer Function %int +%int_n1272971256 = OpConstant %int -1272971256 +%2 = OpFunction %void None %4 +%12 = OpLabel +%13 = OpVariable %_ptr_Function__arr_int_uint_3808428041 Function +%14 = OpAccessChain %_ptr_Function_int %13 %int_n1272971256 +%15 = OpLoad %int %14 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, false, + true); +} // TODO(greg-lunarg): Add tests to verify handling of these cases: // diff --git a/test/opt/local_single_block_elim.cpp b/test/opt/local_single_block_elim.cpp index 8e1cee61..28b8a07d 100644 --- a/test/opt/local_single_block_elim.cpp +++ b/test/opt/local_single_block_elim.cpp @@ -84,6 +84,56 @@ OpFunctionEnd predefs_before + before, predefs_before + after, true, true); } +TEST_F(LocalSingleBlockLoadStoreElimTest, LSBElimForLinkage) { + const std::string predefs_before = + R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +OpDecorate %main LinkageAttributes "main" Export +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %7 +%13 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%14 = OpLoad %v4float %BaseColor +OpStore %v %14 +%15 = OpLoad %v4float %v +OpStore %gl_FragColor %15 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %7 +%13 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%14 = OpLoad %v4float %BaseColor +OpStore %v %14 +OpStore %gl_FragColor %14 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs_before + before, predefs_before + after, true, true); +} + TEST_F(LocalSingleBlockLoadStoreElimTest, SimpleLoadLoadElim) { // #version 140 // diff --git a/test/opt/local_single_store_elim_test.cpp b/test/opt/local_single_store_elim_test.cpp index c94ff372..8f43a11d 100644 --- a/test/opt/local_single_store_elim_test.cpp +++ b/test/opt/local_single_store_elim_test.cpp @@ -126,6 +126,91 @@ OpFunctionEnd predefs + after, true, true); } +TEST_F(LocalSingleStoreElimTest, LSSElimForLinkage) { + const std::string predefs = + R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %f "f" +OpName %fi "fi" +OpName %gl_FragColor "gl_FragColor" +OpDecorate %main LinkageAttributes "main" Export +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Input_float = OpTypePointer Input %float +%fi = OpVariable %_ptr_Input_float Input +%float_0 = OpConstant %float 0 +%bool = OpTypeBool +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %9 +%19 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%f = OpVariable %_ptr_Function_float Function +%20 = OpLoad %v4float %BaseColor +OpStore %v %20 +%21 = OpLoad %float %fi +OpStore %f %21 +%22 = OpLoad %float %f +%23 = OpFOrdLessThan %bool %22 %float_0 +OpSelectionMerge %24 None +OpBranchConditional %23 %25 %24 +%25 = OpLabel +OpStore %f %float_0 +OpBranch %24 +%24 = OpLabel +%26 = OpLoad %v4float %v +%27 = OpLoad %float %f +%28 = OpCompositeConstruct %v4float %27 %27 %27 %27 +%29 = OpFAdd %v4float %26 %28 +OpStore %gl_FragColor %29 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %9 +%19 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%f = OpVariable %_ptr_Function_float Function +%20 = OpLoad %v4float %BaseColor +OpStore %v %20 +%21 = OpLoad %float %fi +OpStore %f %21 +%22 = OpLoad %float %f +%23 = OpFOrdLessThan %bool %22 %float_0 +OpSelectionMerge %24 None +OpBranchConditional %23 %25 %24 +%25 = OpLabel +OpStore %f %float_0 +OpBranch %24 +%24 = OpLabel +%27 = OpLoad %float %f +%28 = OpCompositeConstruct %v4float %27 %27 %27 %27 +%29 = OpFAdd %v4float %20 %28 +OpStore %gl_FragColor %29 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, + predefs + after, true, true); +} + TEST_F(LocalSingleStoreElimTest, ThreeStores) { // Three stores to multiple loads of v is not optimized. @@ -1409,19 +1494,19 @@ TEST_F(LocalSingleStoreElimTest, AddDebugValueforStoreOutOfDebugDeclareScope) { %56 = OpLoad %v4float %in_var_COLOR ;CHECK: DebugNoScope ;CHECK-NOT: OpLine -;CHECK: [[pos:%\w+]] = OpLoad %v4float %in_var_POSITION -;CHECK: [[color:%\w+]] = OpLoad %v4float %in_var_COLOR - OpLine %7 7 23 OpStore %param_var_color %56 OpNoLine %93 = OpExtInst %void %1 DebugScope %48 %73 = OpExtInst %void %1 DebugDeclare %53 %param_var_pos %52 %74 = OpExtInst %void %1 DebugDeclare %51 %param_var_color %52 +;CHECK: [[pos:%\w+]] = OpLoad %v4float %in_var_POSITION ;CHECK: OpLine [[file:%\w+]] 6 23 -;CHECK-NEXT: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[dbg_pos]] [[pos]] [[empty_expr:%\w+]] +;CHECK: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[dbg_pos]] [[pos]] [[empty_expr:%\w+]] +;CHECK: [[color:%\w+]] = OpLoad %v4float %in_var_COLOR ;CHECK: OpLine [[file]] 7 23 -;CHECK-NEXT: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[dbg_color]] [[color]] [[empty_expr]] +;CHECK: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[dbg_color]] [[color]] [[empty_expr]] +;CHECK: OpLine [[file]] 9 3 %94 = OpExtInst %void %1 DebugScope %49 OpLine %7 9 3 @@ -1444,6 +1529,227 @@ TEST_F(LocalSingleStoreElimTest, AddDebugValueforStoreOutOfDebugDeclareScope) { SinglePassRunAndMatch(text, false); } +TEST_F(LocalSingleStoreElimTest, DebugValuesForAllLocalsAndParams) { + // Texture2D g_tColor; + // + // SamplerState g_sAniso; + // + // struct PS_INPUT + // { + // float2 vTextureCoords : TEXCOORD2 ; + // } ; + // + // struct PS_OUTPUT + // { + // float4 vColor : SV_Target0 ; + // } ; + // + // void do_sample ( in float2 tc, out float4 c ) { + // c = g_tColor . Sample ( g_sAniso , tc ) ; + // } + // + // PS_OUTPUT MainPs ( PS_INPUT i ) + // { + // PS_OUTPUT ps_output ; + // float4 color; + // + // do_sample ( i . vTextureCoords . xy , color ) ; + // ps_output . vColor = color; + // return ps_output ; + // } + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" +;CHECK: [[set:%\w+]] = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPs "MainPs" %in_var_TEXCOORD2 %out_var_SV_Target0 %g_tColor %g_sAniso + OpExecutionMode %MainPs OriginUpperLeft + %7 = OpString "foo2.frag" + %21 = OpString "float" + %27 = OpString "PS_INPUT" + %31 = OpString "vTextureCoords" + %34 = OpString "PS_OUTPUT" + %38 = OpString "vColor" + %40 = OpString "do_sample" + %41 = OpString "" + %45 = OpString "c" + %47 = OpString "tc" + %50 = OpString "MainPs" + %54 = OpString "color" + %56 = OpString "ps_output" + %59 = OpString "i" + %62 = OpString "@type.sampler" + %63 = OpString "type.sampler" + %65 = OpString "g_sAniso" + %67 = OpString "@type.2d.image" + %68 = OpString "type.2d.image" + %70 = OpString "TemplateParam" + %73 = OpString "g_tColor" +;CHECK: [[str_c:%\w+]] = OpString "c" +;CHECK: [[str_tc:%\w+]] = OpString "tc" +;CHECK: [[str_color:%\w+]] = OpString "color" +;CHECK: [[str_ps_output:%\w+]] = OpString "ps_output" +;CHECK: [[str_i:%\w+]] = OpString "i" + 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 %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_64 = OpConstant %uint 64 + %uint_0 = OpConstant %uint 0 + %uint_128 = OpConstant %uint 128 + %75 = OpTypeFunction %void + %PS_INPUT = OpTypeStruct %v2float +%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT + %PS_OUTPUT = OpTypeStruct %v4float + %85 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT +%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Function_v2float = OpTypePointer Function %v2float + %105 = OpTypeFunction %void %_ptr_Function_v2float %_ptr_Function_v4float +%type_sampled_image = OpTypeSampledImage %type_2d_image + %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 + %145 = OpExtInst %void %1 DebugOperation Deref + %61 = OpExtInst %void %1 DebugInfoNone + %58 = OpExtInst %void %1 DebugExpression + %23 = OpExtInst %void %1 DebugTypeBasic %21 %uint_32 Float + %24 = OpExtInst %void %1 DebugTypeVector %23 2 + %25 = OpExtInst %void %1 DebugSource %7 + %26 = OpExtInst %void %1 DebugCompilationUnit 1 4 %25 HLSL + %29 = OpExtInst %void %1 DebugTypeComposite %27 Structure %25 5 8 %26 %27 %uint_64 FlagIsProtected|FlagIsPrivate %30 + %30 = OpExtInst %void %1 DebugTypeMember %31 %24 %25 7 12 %29 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate + %33 = OpExtInst %void %1 DebugTypeVector %23 4 + %36 = OpExtInst %void %1 DebugTypeComposite %34 Structure %25 10 8 %26 %34 %uint_128 FlagIsProtected|FlagIsPrivate %37 + %37 = OpExtInst %void %1 DebugTypeMember %38 %33 %25 12 12 %36 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate + %39 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %void %24 %33 + %42 = OpExtInst %void %1 DebugFunction %40 %39 %25 15 1 %26 %41 FlagIsProtected|FlagIsPrivate 15 %61 + %44 = OpExtInst %void %1 DebugLexicalBlock %25 15 47 %42 + %46 = OpExtInst %void %1 DebugLocalVariable %45 %33 %25 15 43 %42 FlagIsLocal 2 + %48 = OpExtInst %void %1 DebugLocalVariable %47 %24 %25 15 28 %42 FlagIsLocal 1 + %49 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %36 %29 + %51 = OpExtInst %void %1 DebugFunction %50 %49 %25 19 1 %26 %41 FlagIsProtected|FlagIsPrivate 20 %61 + %53 = OpExtInst %void %1 DebugLexicalBlock %25 20 1 %51 + %55 = OpExtInst %void %1 DebugLocalVariable %54 %33 %25 22 12 %53 FlagIsLocal + %57 = OpExtInst %void %1 DebugLocalVariable %56 %36 %25 21 15 %53 FlagIsLocal + %60 = OpExtInst %void %1 DebugLocalVariable %59 %29 %25 19 29 %51 FlagIsLocal 1 + %64 = OpExtInst %void %1 DebugTypeComposite %62 Structure %25 0 0 %26 %63 %61 FlagIsProtected|FlagIsPrivate + %66 = OpExtInst %void %1 DebugGlobalVariable %65 %64 %25 3 14 %26 %65 %g_sAniso FlagIsDefinition + %69 = OpExtInst %void %1 DebugTypeComposite %67 Class %25 0 0 %26 %68 %61 FlagIsProtected|FlagIsPrivate + %71 = OpExtInst %void %1 DebugTypeTemplateParameter %70 %33 %61 %25 0 0 + %72 = OpExtInst %void %1 DebugTypeTemplate %69 %71 + %74 = OpExtInst %void %1 DebugGlobalVariable %73 %72 %25 1 11 %26 %73 %g_tColor FlagIsDefinition + %142 = OpExtInst %void %1 DebugInlinedAt 24 %53 + %144 = OpExtInst %void %1 DebugExpression %145 + %155 = OpExtInst %void %1 DebugExpression %145 +;CHECK: [[var_c:%\w+]] = OpExtInst %void [[set]] DebugLocalVariable [[str_c]] +;CHECK: [[var_tc:%\w+]] = OpExtInst %void [[set]] DebugLocalVariable [[str_tc]] +;CHECK: [[var_color:%\w+]] = OpExtInst %void [[set]] DebugLocalVariable [[str_color]] +;CHECK: [[var_ps_output:%\w+]] = OpExtInst %void [[set]] DebugLocalVariable [[str_ps_output]] +;CHECK: [[var_i:%\w+]] = OpExtInst %void [[set]] DebugLocalVariable [[str_i]] + %MainPs = OpFunction %void None %75 + %76 = OpLabel + %153 = OpVariable %_ptr_Function_v2float Function + %149 = OpVariable %_ptr_Function_v4float Function + %157 = OpExtInst %void %1 DebugScope %53 + %143 = OpVariable %_ptr_Function_v4float Function + %121 = OpVariable %_ptr_Function_v4float Function + %122 = OpVariable %_ptr_Function_v2float Function + %158 = OpExtInst %void %1 DebugScope %51 + OpLine %7 19 29 + %156 = OpExtInst %void %1 DebugValue %60 %153 %155 %int_0 + %159 = OpExtInst %void %1 DebugScope %53 + OpLine %7 21 15 + %146 = OpExtInst %void %1 DebugValue %57 %143 %144 %int_0 + OpNoLine + %160 = OpExtInst %void %1 DebugNoScope + %80 = OpLoad %v2float %in_var_TEXCOORD2 + %81 = OpCompositeConstruct %PS_INPUT %80 + %154 = OpCompositeExtract %v2float %81 0 + OpStore %153 %154 + %161 = OpExtInst %void %1 DebugScope %53 + OpLine %7 22 12 + %127 = OpExtInst %void %1 DebugDeclare %55 %121 %58 + OpLine %7 24 17 + %129 = OpLoad %v2float %153 + OpStore %122 %129 + %162 = OpExtInst %void %1 DebugScope %42 %142 + OpLine %7 15 28 + %135 = OpExtInst %void %1 DebugDeclare %48 %122 %58 + OpLine %7 15 43 + %136 = OpExtInst %void %1 DebugDeclare %46 %121 %58 + %163 = OpExtInst %void %1 DebugScope %44 %142 + OpLine %7 16 9 + %137 = OpLoad %type_2d_image %g_tColor + OpLine %7 16 29 + %138 = OpLoad %type_sampler %g_sAniso + OpLine %7 16 40 + %139 = OpLoad %v2float %122 + OpLine %7 16 9 + %140 = OpSampledImage %type_sampled_image %137 %138 + %141 = OpImageSampleImplicitLod %v4float %140 %139 None + OpLine %7 16 5 + OpStore %121 %141 + %164 = OpExtInst %void %1 DebugScope %53 + OpLine %7 25 26 + %131 = OpLoad %v4float %121 + OpLine %7 25 5 + OpStore %143 %131 + OpLine %7 26 12 + %147 = OpLoad %v4float %143 + %148 = OpCompositeConstruct %PS_OUTPUT %147 + OpLine %7 26 5 + %150 = OpCompositeExtract %v4float %148 0 + OpStore %149 %150 + OpNoLine + %165 = OpExtInst %void %1 DebugNoScope + %151 = OpLoad %v4float %149 + %152 = OpCompositeConstruct %PS_OUTPUT %151 + %84 = OpCompositeExtract %v4float %152 0 + OpStore %out_var_SV_Target0 %84 + OpLine %7 27 1 + OpReturn + OpFunctionEnd +;CHECK: {{%\w+}} = OpExtInst %void [[set]] DebugValue [[var_i]] +;CHECK: {{%\w+}} = OpExtInst %void [[set]] DebugValue [[var_tc]] +;CHECK: {{%\w+}} = OpExtInst %void [[set]] DebugValue [[var_c]] +;CHECK: {{%\w+}} = OpExtInst %void [[set]] DebugValue [[var_color]] +;CHECK: {{%\w+}} = OpExtInst %void [[set]] DebugValue [[var_ps_output]] + )"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, false); +} + // TODO(greg-lunarg): Add tests to verify handling of these cases: // // Other types diff --git a/test/opt/local_ssa_elim_test.cpp b/test/opt/local_ssa_elim_test.cpp index ca9aba33..45006ca6 100644 --- a/test/opt/local_ssa_elim_test.cpp +++ b/test/opt/local_ssa_elim_test.cpp @@ -2165,6 +2165,140 @@ OpFunctionEnd SinglePassRunAndMatch(text, true); } +TEST_F(LocalSSAElimTest, ShaderDebugForLoop) { + 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 +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "NonSemantic.Shader.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_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_3 = OpConstant %uint 3 +%uint_4 = OpConstant %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_10 = OpConstant %uint 10 +%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 %uint_1 %uint_4 %src %uint_5 +%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 %uint_3 %uint_0 +%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_tf %uint_4 +%main_ty = OpExtInst %void %ext DebugTypeFunction %uint_3 %dbg_v4f %dbg_v4f +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src %uint_0 %uint_0 %cu %main_name %uint_3 %uint_10 +%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_v4f %src %uint_0 %uint_0 %dbg_main %uint_4 +%dbg_i = OpExtInst %void %ext DebugLocalVariable %i_name %dbg_v4f %src %uint_0 %uint_0 %dbg_main %uint_4 +%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(text, true); +} + TEST_F(LocalSSAElimTest, AddDebugValueForFunctionParameterWithPhi) { // Test the distribution of DebugValue for a parameter of an inlined function // and the visibility of Phi instruction. The ssa-rewrite pass must add @@ -2844,6 +2978,7 @@ TEST_F(LocalSSAElimTest, DebugValueForReferenceVariableInBB) { ; CHECK: OpExtInst %void [[ext]] DebugScope [[dbg_main]] ; CHECK: OpStore %f %float_0 +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_x]] %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 @@ -4091,6 +4226,1236 @@ TEST_F(LocalSSAElimTest, PointerVariables) { SinglePassRunAndMatch(text, true); } +TEST_F(LocalSSAElimTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} + +TEST_F(LocalSSAElimTest, MissingDebugValue) { + // Make sure DebugValue for final fragcolor assignment is generated. + + const std::string text = + R"( + OpCapability Shader + OpCapability ImageQuery + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "GLSL.std.450" + %2 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_var_TEXCOORD0 %out_var_SV_TARGET %textureposition %samplerposition %textureNormal %samplerNormal %textureAlbedo %samplerAlbedo %textureShadowMap %samplerShadowMap %ubo + OpExecutionMode %main OriginUpperLeft + %15 = OpString "d2.frag" + %55 = OpString "float" + %63 = OpString "// Copyright 2020 Google LLC + +Texture2D textureposition : register(t1); +SamplerState samplerposition : register(s1); +Texture2D textureNormal : register(t2); +SamplerState samplerNormal : register(s2); +Texture2D textureAlbedo : register(t3); +SamplerState samplerAlbedo : register(s3); +// Depth from the light's point of view +//layout (binding = 5) uniform sampler2DShadow samplerShadowMap; +Texture2DArray textureShadowMap : register(t5); +SamplerState samplerShadowMap : register(s5); + +#define LIGHT_COUNT 3 +#define SHADOW_FACTOR 0.25 +#define AMBIENT_LIGHT 0.1 +#define USE_PCF + +struct Light +{ + float4 position; + float4 target; + float4 color; + float4x4 viewMatrix; +}; + +struct UBO +{ + float4 viewPos; + Light lights[LIGHT_COUNT]; + int useShadows; + int displayDebugTarget; +}; + +cbuffer ubo : register(b4) { UBO ubo; } + +float textureProj(float4 P, float layer, float2 offset) +{ + float shadow = 1.0; + float4 shadowCoord = P / P.w; + shadowCoord.xy = shadowCoord.xy * 0.5 + 0.5; + + if (shadowCoord.z > -1.0 && shadowCoord.z < 1.0) + { + float dist = textureShadowMap.Sample(samplerShadowMap, float3(shadowCoord.xy + offset, layer)).r; + if (shadowCoord.w > 0.0 && dist < shadowCoord.z) + { + shadow = SHADOW_FACTOR; + } + } + return shadow; +} + +float filterPCF(float4 sc, float layer) +{ + int2 texDim; int elements; int levels; + textureShadowMap.GetDimensions(0, texDim.x, texDim.y, elements, levels); + float scale = 1.5; + float dx = scale * 1.0 / float(texDim.x); + float dy = scale * 1.0 / float(texDim.y); + + float shadowFactor = 0.0; + int count = 0; + int range = 1; + + for (int x = -range; x <= range; x++) + { + for (int y = -range; y <= range; y++) + { + shadowFactor += textureProj(sc, layer, float2(dx*x, dy*y)); + count++; + } + + } + return shadowFactor / count; +} + +float3 shadow(float3 fragcolor, float3 fragPos) { + for (int i = 0; i < LIGHT_COUNT; ++i) + { + float4 shadowClip = mul(ubo.lights[i].viewMatrix, float4(fragPos.xyz, 1.0)); + + float shadowFactor; + #ifdef USE_PCF + shadowFactor= filterPCF(shadowClip, i); + #else + shadowFactor = textureProj(shadowClip, i, float2(0.0, 0.0)); + #endif + + fragcolor *= shadowFactor; + } + return fragcolor; +} + +float4 main([[vk::location(0)]] float2 inUV : TEXCOORD0) : SV_TARGET +{ + // Get G-Buffer values + float3 fragPos = textureposition.Sample(samplerposition, inUV).rgb; + float3 normal = textureNormal.Sample(samplerNormal, inUV).rgb; + float4 albedo = textureAlbedo.Sample(samplerAlbedo, inUV); + + // Ambient part + float3 fragcolor = albedo.rgb * AMBIENT_LIGHT; + + float3 N = normalize(normal); + + for(int i = 0; i < LIGHT_COUNT; ++i) + { + // Vector to light + float3 L = ubo.lights[i].position.xyz - fragPos; + // Distance from light to fragment position + float dist = length(L); + L = normalize(L); + + // Viewer to fragment + float3 V = ubo.viewPos.xyz - fragPos; + V = normalize(V); + + float lightCosInnerAngle = cos(radians(15.0)); + float lightCosOuterAngle = cos(radians(25.0)); + float lightRange = 100.0; + + // Direction vector from source to target + float3 dir = normalize(ubo.lights[i].position.xyz - ubo.lights[i].target.xyz); + + // Dual cone spot light with smooth transition between inner and outer angle + float cosDir = dot(L, dir); + float spotEffect = smoothstep(lightCosOuterAngle, lightCosInnerAngle, cosDir); + float heightAttenuation = smoothstep(lightRange, 0.0f, dist); + + // Diffuse lighting + float NdotL = max(0.0, dot(N, L)); + float3 diff = NdotL.xxx; + + // Specular lighting + float3 R = reflect(-L, N); + float NdotR = max(0.0, dot(R, V)); + float3 spec = (pow(NdotR, 16.0) * albedo.a * 2.5).xxx; + + fragcolor += float3((diff + spec) * spotEffect * heightAttenuation) * ubo.lights[i].color.rgb * albedo.rgb; + } + + // Shadow calculations in a separate pass + if (ubo.useShadows > 0) + { + fragcolor = shadow(fragcolor, fragPos); + } + + return float4(fragcolor, 1); +} +" + %68 = OpString "textureProj" + %69 = OpString "" + %78 = OpString "dist" + %82 = OpString "shadowCoord" + %85 = OpString "shadow" + %89 = OpString "offset" + %92 = OpString "layer" + %95 = OpString "P" + %99 = OpString "filterPCF" + %108 = OpString "int" + %110 = OpString "y" + %114 = OpString "x" + %118 = OpString "range" + %122 = OpString "count" + %125 = OpString "shadowFactor" + %128 = OpString "dy" + %131 = OpString "dx" + %134 = OpString "scale" + %137 = OpString "levels" + %141 = OpString "elements" + %145 = OpString "texDim" + %150 = OpString "sc" + %162 = OpString "shadowClip" + %166 = OpString "i" + %169 = OpString "fragPos" + %171 = OpString "fragcolor" + %175 = OpString "main" + %184 = OpString "spec" + %187 = OpString "NdotR" + %190 = OpString "R" + %193 = OpString "diff" + %196 = OpString "NdotL" + %199 = OpString "heightAttenuation" + %202 = OpString "spotEffect" + %205 = OpString "cosDir" + %208 = OpString "dir" + %211 = OpString "lightRange" + %214 = OpString "lightCosOuterAngle" + %217 = OpString "lightCosInnerAngle" + %220 = OpString "V" + %225 = OpString "L" + %230 = OpString "N" + %235 = OpString "albedo" + %238 = OpString "normal" + %244 = OpString "inUV" + %246 = OpString "viewPos" + %249 = OpString "position" + %252 = OpString "target" + %254 = OpString "color" + %259 = OpString "viewMatrix" + %263 = OpString "Light" + %267 = OpString "lights" + %271 = OpString "useShadows" + %275 = OpString "displayDebugTarget" + %278 = OpString "UBO" + %282 = OpString "ubo" + %285 = OpString "type.ubo" + %289 = OpString "@type.sampler" + %290 = OpString "type.sampler" + %292 = OpString "samplerShadowMap" + %295 = OpString "@type.2d.image.array" + %296 = OpString "type.2d.image.array" + %298 = OpString "TemplateParam" + %301 = OpString "textureShadowMap" + %304 = OpString "samplerAlbedo" + %306 = OpString "@type.2d.image" + %307 = OpString "type.2d.image" + %311 = OpString "textureAlbedo" + %313 = OpString "samplerNormal" + %315 = OpString "textureNormal" + %317 = OpString "samplerposition" + %319 = OpString "textureposition" + OpName %type_2d_image "type.2d.image" + OpName %textureposition "textureposition" + OpName %type_sampler "type.sampler" + OpName %samplerposition "samplerposition" + OpName %textureNormal "textureNormal" + OpName %samplerNormal "samplerNormal" + OpName %textureAlbedo "textureAlbedo" + OpName %samplerAlbedo "samplerAlbedo" + OpName %type_2d_image_array "type.2d.image.array" + OpName %textureShadowMap "textureShadowMap" + OpName %samplerShadowMap "samplerShadowMap" + OpName %type_ubo "type.ubo" + OpMemberName %type_ubo 0 "ubo" + OpName %UBO "UBO" + OpMemberName %UBO 0 "viewPos" + OpMemberName %UBO 1 "lights" + OpMemberName %UBO 2 "useShadows" + OpMemberName %UBO 3 "displayDebugTarget" + OpName %Light "Light" + OpMemberName %Light 0 "position" + OpMemberName %Light 1 "target" + OpMemberName %Light 2 "color" + OpMemberName %Light 3 "viewMatrix" + OpName %ubo "ubo" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %main "main" + OpName %param_var_inUV "param.var.inUV" + OpName %type_sampled_image "type.sampled.image" + OpName %type_sampled_image_0 "type.sampled.image" + OpDecorate %in_var_TEXCOORD0 Location 0 + OpDecorate %out_var_SV_TARGET Location 0 + OpDecorate %textureposition DescriptorSet 0 + OpDecorate %textureposition Binding 1 + OpDecorate %samplerposition DescriptorSet 0 + OpDecorate %samplerposition Binding 1 + OpDecorate %textureNormal DescriptorSet 0 + OpDecorate %textureNormal Binding 2 + OpDecorate %samplerNormal DescriptorSet 0 + OpDecorate %samplerNormal Binding 2 + OpDecorate %textureAlbedo DescriptorSet 0 + OpDecorate %textureAlbedo Binding 3 + OpDecorate %samplerAlbedo DescriptorSet 0 + OpDecorate %samplerAlbedo Binding 3 + OpDecorate %textureShadowMap DescriptorSet 0 + OpDecorate %textureShadowMap Binding 5 + OpDecorate %samplerShadowMap DescriptorSet 0 + OpDecorate %samplerShadowMap Binding 5 + OpDecorate %ubo DescriptorSet 0 + OpDecorate %ubo Binding 4 + OpMemberDecorate %Light 0 Offset 0 + OpMemberDecorate %Light 1 Offset 16 + OpMemberDecorate %Light 2 Offset 32 + OpMemberDecorate %Light 3 Offset 48 + OpMemberDecorate %Light 3 MatrixStride 16 + OpMemberDecorate %Light 3 RowMajor + OpDecorate %_arr_Light_uint_3 ArrayStride 112 + OpMemberDecorate %UBO 0 Offset 0 + OpMemberDecorate %UBO 1 Offset 16 + OpMemberDecorate %UBO 2 Offset 352 + OpMemberDecorate %UBO 3 Offset 356 + OpMemberDecorate %type_ubo 0 Offset 0 + OpDecorate %type_ubo Block +)" + R"( %float = OpTypeFloat 32 +%float_0_100000001 = OpConstant %float 0.100000001 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_3 = OpConstant %int 3 + %int_1 = OpConstant %int 1 + %float_15 = OpConstant %float 15 + %float_25 = OpConstant %float 25 + %float_100 = OpConstant %float 100 + %float_0 = OpConstant %float 0 + %float_16 = OpConstant %float 16 + %float_2_5 = OpConstant %float 2.5 + %int_2 = OpConstant %int 2 + %float_1 = OpConstant %float 1 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %float_1_5 = OpConstant %float 1.5 + %float_0_5 = OpConstant %float 0.5 + %v2float = OpTypeVector %float 2 + %35 = OpConstantComposite %v2float %float_0_5 %float_0_5 + %float_n1 = OpConstant %float -1 + %float_0_25 = OpConstant %float 0.25 + %uint_32 = OpConstant %uint 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 +%type_2d_image_array = OpTypeImage %float 2D 2 1 0 1 Unknown +%_ptr_UniformConstant_type_2d_image_array = OpTypePointer UniformConstant %type_2d_image_array + %v4float = OpTypeVector %float 4 + %uint_3 = OpConstant %uint 3 +%mat4v4float = OpTypeMatrix %v4float 4 + %Light = OpTypeStruct %v4float %v4float %v4float %mat4v4float +%_arr_Light_uint_3 = OpTypeArray %Light %uint_3 + %UBO = OpTypeStruct %v4float %_arr_Light_uint_3 %int %int + %type_ubo = OpTypeStruct %UBO +%_ptr_Uniform_type_ubo = OpTypePointer Uniform %type_ubo +%_ptr_Input_v2float = OpTypePointer Input %v2float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %uint_4 = OpConstant %uint 4 + %uint_2 = OpConstant %uint 2 + %uint_1 = OpConstant %uint 1 + %uint_5 = OpConstant %uint 5 + %uint_37 = OpConstant %uint 37 + %uint_38 = OpConstant %uint 38 + %uint_44 = OpConstant %uint 44 + %uint_47 = OpConstant %uint 47 + %uint_45 = OpConstant %uint 45 + %uint_9 = OpConstant %uint 9 + %uint_40 = OpConstant %uint 40 + %uint_39 = OpConstant %uint 39 + %uint_8 = OpConstant %uint 8 + %uint_49 = OpConstant %uint 49 + %uint_35 = OpConstant %uint 35 + %uint_26 = OpConstant %uint 26 + %uint_54 = OpConstant %uint 54 + %uint_55 = OpConstant %uint 55 + %uint_67 = OpConstant %uint 67 + %uint_69 = OpConstant %uint 69 + %uint_68 = OpConstant %uint 68 + %uint_12 = OpConstant %uint 12 + %uint_66 = OpConstant %uint 66 + %uint_11 = OpConstant %uint 11 + %uint_64 = OpConstant %uint 64 + %uint_6 = OpConstant %uint 6 + %uint_63 = OpConstant %uint 63 + %uint_62 = OpConstant %uint 62 + %uint_60 = OpConstant %uint 60 + %uint_59 = OpConstant %uint 59 + %uint_58 = OpConstant %uint 58 + %uint_56 = OpConstant %uint 56 + %uint_33 = OpConstant %uint 33 + %uint_19 = OpConstant %uint 19 + %uint_7 = OpConstant %uint 7 + %uint_34 = OpConstant %uint 34 + %uint_24 = OpConstant %uint 24 + %uint_78 = OpConstant %uint 78 + %uint_80 = OpConstant %uint 80 + %uint_83 = OpConstant %uint 83 + %uint_81 = OpConstant %uint 81 + %uint_10 = OpConstant %uint 10 + %uint_79 = OpConstant %uint 79 + %uint_22 = OpConstant %uint 22 + %uint_95 = OpConstant %uint 95 + %uint_96 = OpConstant %uint 96 + %uint_145 = OpConstant %uint 145 + %uint_108 = OpConstant %uint 108 + %uint_138 = OpConstant %uint 138 + %uint_137 = OpConstant %uint 137 + %uint_136 = OpConstant %uint 136 + %uint_133 = OpConstant %uint 133 + %uint_132 = OpConstant %uint 132 + %uint_129 = OpConstant %uint 129 + %uint_128 = OpConstant %uint 128 + %uint_127 = OpConstant %uint 127 + %uint_124 = OpConstant %uint 124 + %uint_121 = OpConstant %uint 121 + %uint_120 = OpConstant %uint 120 + %uint_119 = OpConstant %uint 119 + %uint_116 = OpConstant %uint 116 + %uint_112 = OpConstant %uint 112 + %uint_110 = OpConstant %uint 110 + %uint_107 = OpConstant %uint 107 + %uint_105 = OpConstant %uint 105 + %uint_103 = OpConstant %uint 103 + %uint_100 = OpConstant %uint 100 + %uint_99 = OpConstant %uint 99 + %uint_98 = OpConstant %uint 98 + %uint_29 = OpConstant %uint 29 + %uint_21 = OpConstant %uint 21 + %uint_256 = OpConstant %uint 256 + %uint_23 = OpConstant %uint 23 + %uint_384 = OpConstant %uint 384 + %uint_512 = OpConstant %uint 512 + %uint_896 = OpConstant %uint 896 + %uint_2688 = OpConstant %uint 2688 + %uint_30 = OpConstant %uint 30 + %uint_2816 = OpConstant %uint 2816 + %uint_31 = OpConstant %uint 31 + %uint_2848 = OpConstant %uint 2848 + %uint_2880 = OpConstant %uint 2880 + %uint_27 = OpConstant %uint 27 + %uint_2944 = OpConstant %uint 2944 + %uint_14 = OpConstant %uint 14 + %uint_16 = OpConstant %uint 16 + %321 = OpTypeFunction %void +%_ptr_Function_v2float = OpTypePointer Function %v2float + %uint_150 = OpConstant %uint 150 + %v3float = OpTypeVector %float 3 +%_ptr_Function_v3float = OpTypePointer Function %v3float +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Function_int = OpTypePointer Function %int +%_ptr_Function_float = OpTypePointer Function %float + %uint_42 = OpConstant %uint 42 +%type_sampled_image = OpTypeSampledImage %type_2d_image + %uint_65 = OpConstant %uint 65 + %uint_18 = OpConstant %uint 18 + %uint_13 = OpConstant %uint 13 + %uint_15 = OpConstant %uint 15 + %uint_17 = OpConstant %uint 17 + %bool = OpTypeBool + %uint_25 = OpConstant %uint 25 +%_ptr_Uniform_UBO = OpTypePointer Uniform %UBO +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %uint_28 = OpConstant %uint 28 + %uint_43 = OpConstant %uint 43 + %uint_113 = OpConstant %uint 113 + %uint_117 = OpConstant %uint 117 + %uint_46 = OpConstant %uint 46 + %uint_76 = OpConstant %uint 76 + %uint_53 = OpConstant %uint 53 + %uint_73 = OpConstant %uint 73 + %uint_48 = OpConstant %uint 48 + %uint_140 = OpConstant %uint 140 + %uint_52 = OpConstant %uint 52 + %uint_93 = OpConstant %uint 93 + %uint_87 = OpConstant %uint 87 + %uint_106 = OpConstant %uint 106 + %uint_36 = OpConstant %uint 36 + %uint_144 = OpConstant %uint 144 +%_ptr_Uniform_int = OpTypePointer Uniform %int + %uint_146 = OpConstant %uint 146 + %uint_147 = OpConstant %uint 147 + %uint_149 = OpConstant %uint 149 + %uint_41 = OpConstant %uint 41 +%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float + %uint_77 = OpConstant %uint 77 + %uint_85 = OpConstant %uint 85 + %uint_90 = OpConstant %uint 90 + %uint_92 = OpConstant %uint 92 + %v2int = OpTypeVector %int 2 +%_ptr_Function_v2int = OpTypePointer Function %v2int + %uint_57 = OpConstant %uint 57 + %v3uint = OpTypeVector %uint 3 + %uint_72 = OpConstant %uint 72 + %uint_70 = OpConstant %uint 70 + %uint_50 = OpConstant %uint 50 + %uint_61 = OpConstant %uint 61 + %uint_71 = OpConstant %uint 71 + %uint_75 = OpConstant %uint 75 + %uint_82 = OpConstant %uint 82 +%type_sampled_image_0 = OpTypeSampledImage %type_2d_image_array + %uint_51 = OpConstant %uint 51 +%textureposition = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%samplerposition = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%textureNormal = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%samplerNormal = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%textureAlbedo = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%samplerAlbedo = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%textureShadowMap = OpVariable %_ptr_UniformConstant_type_2d_image_array UniformConstant +%samplerShadowMap = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %ubo = OpVariable %_ptr_Uniform_type_ubo Uniform +%in_var_TEXCOORD0 = OpVariable %_ptr_Input_v2float Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %uint_1792 = OpConstant %uint 1792 + %uint_1869 = OpConstant %uint 1869 + %uint_2060 = OpConstant %uint 2060 + %288 = OpExtInst %void %2 DebugInfoNone + %243 = OpExtInst %void %2 DebugExpression + %57 = OpExtInst %void %2 DebugTypeBasic %55 %uint_32 %uint_3 %uint_0 + %58 = OpExtInst %void %2 DebugTypeVector %57 %uint_4 + %60 = OpExtInst %void %2 DebugTypeVector %57 %uint_2 + %62 = OpExtInst %void %2 DebugTypeFunction %uint_3 %57 %58 %57 %60 + %64 = OpExtInst %void %2 DebugSource %15 %63 + %65 = OpExtInst %void %2 DebugCompilationUnit %uint_1 %uint_4 %64 %uint_5 + %70 = OpExtInst %void %2 DebugFunction %68 %62 %64 %uint_37 %uint_1 %65 %69 %uint_3 %uint_38 + %73 = OpExtInst %void %2 DebugLexicalBlock %64 %uint_38 %uint_1 %70 + %74 = OpExtInst %void %2 DebugLexicalBlock %64 %uint_44 %uint_2 %73 + %76 = OpExtInst %void %2 DebugLexicalBlock %64 %uint_47 %uint_3 %74 + %79 = OpExtInst %void %2 DebugLocalVariable %78 %57 %64 %uint_45 %uint_9 %74 %uint_4 + %83 = OpExtInst %void %2 DebugLocalVariable %82 %58 %64 %uint_40 %uint_9 %73 %uint_4 + %86 = OpExtInst %void %2 DebugLocalVariable %85 %57 %64 %uint_39 %uint_8 %73 %uint_4 + %90 = OpExtInst %void %2 DebugLocalVariable %89 %60 %64 %uint_37 %uint_49 %70 %uint_4 %uint_3 + %93 = OpExtInst %void %2 DebugLocalVariable %92 %57 %64 %uint_37 %uint_35 %70 %uint_4 %uint_2 + %96 = OpExtInst %void %2 DebugLocalVariable %95 %58 %64 %uint_37 %uint_26 %70 %uint_4 %uint_1 + %98 = OpExtInst %void %2 DebugTypeFunction %uint_3 %57 %58 %57 + %100 = OpExtInst %void %2 DebugFunction %99 %98 %64 %uint_54 %uint_1 %65 %69 %uint_3 %uint_55 + %103 = OpExtInst %void %2 DebugLexicalBlock %64 %uint_55 %uint_1 %100 + %104 = OpExtInst %void %2 DebugLexicalBlock %64 %uint_67 %uint_2 %103 + %106 = OpExtInst %void %2 DebugLexicalBlock %64 %uint_69 %uint_3 %104 + %109 = OpExtInst %void %2 DebugTypeBasic %108 %uint_32 %uint_4 %uint_0 + %111 = OpExtInst %void %2 DebugLocalVariable %110 %109 %64 %uint_68 %uint_12 %104 %uint_4 + %115 = OpExtInst %void %2 DebugLocalVariable %114 %109 %64 %uint_66 %uint_11 %103 %uint_4 + %119 = OpExtInst %void %2 DebugLocalVariable %118 %109 %64 %uint_64 %uint_6 %103 %uint_4 + %123 = OpExtInst %void %2 DebugLocalVariable %122 %109 %64 %uint_63 %uint_6 %103 %uint_4 + %126 = OpExtInst %void %2 DebugLocalVariable %125 %57 %64 %uint_62 %uint_8 %103 %uint_4 + %129 = OpExtInst %void %2 DebugLocalVariable %128 %57 %64 %uint_60 %uint_8 %103 %uint_4 + %132 = OpExtInst %void %2 DebugLocalVariable %131 %57 %64 %uint_59 %uint_8 %103 %uint_4 + %135 = OpExtInst %void %2 DebugLocalVariable %134 %57 %64 %uint_58 %uint_8 %103 %uint_4 + %138 = OpExtInst %void %2 DebugLocalVariable %137 %109 %64 %uint_56 %uint_33 %103 %uint_4 + %142 = OpExtInst %void %2 DebugLocalVariable %141 %109 %64 %uint_56 %uint_19 %103 %uint_4 + %144 = OpExtInst %void %2 DebugTypeVector %109 %uint_2 + %146 = OpExtInst %void %2 DebugLocalVariable %145 %144 %64 %uint_56 %uint_7 %103 %uint_4 + %148 = OpExtInst %void %2 DebugLocalVariable %92 %57 %64 %uint_54 %uint_34 %100 %uint_4 %uint_2 + %151 = OpExtInst %void %2 DebugLocalVariable %150 %58 %64 %uint_54 %uint_24 %100 %uint_4 %uint_1 + %153 = OpExtInst %void %2 DebugTypeVector %57 %uint_3 + %154 = OpExtInst %void %2 DebugTypeFunction %uint_3 %153 %153 %153 + %155 = OpExtInst %void %2 DebugFunction %85 %154 %64 %uint_78 %uint_1 %65 %69 %uint_3 %uint_78 + %157 = OpExtInst %void %2 DebugLexicalBlock %64 %uint_78 %uint_49 %155 + %158 = OpExtInst %void %2 DebugLexicalBlock %64 %uint_80 %uint_2 %157 + %160 = OpExtInst %void %2 DebugLocalVariable %125 %57 %64 %uint_83 %uint_9 %158 %uint_4 + %163 = OpExtInst %void %2 DebugLocalVariable %162 %58 %64 %uint_81 %uint_10 %158 %uint_4 + %167 = OpExtInst %void %2 DebugLocalVariable %166 %109 %64 %uint_79 %uint_11 %157 %uint_4 + %170 = OpExtInst %void %2 DebugLocalVariable %169 %153 %64 %uint_78 %uint_40 %155 %uint_4 %uint_2 + %172 = OpExtInst %void %2 DebugLocalVariable %171 %153 %64 %uint_78 %uint_22 %155 %uint_4 %uint_1 + %174 = OpExtInst %void %2 DebugTypeFunction %uint_3 %58 %60 + %176 = OpExtInst %void %2 DebugFunction %175 %174 %64 %uint_95 %uint_1 %65 %69 %uint_3 %uint_96 + %179 = OpExtInst %void %2 DebugLexicalBlock %64 %uint_96 %uint_1 %176 + %180 = OpExtInst %void %2 DebugLexicalBlock %64 %uint_145 %uint_2 %179 + %182 = OpExtInst %void %2 DebugLexicalBlock %64 %uint_108 %uint_2 %179 + %185 = OpExtInst %void %2 DebugLocalVariable %184 %153 %64 %uint_138 %uint_10 %182 %uint_4 + %188 = OpExtInst %void %2 DebugLocalVariable %187 %57 %64 %uint_137 %uint_9 %182 %uint_4 + %191 = OpExtInst %void %2 DebugLocalVariable %190 %153 %64 %uint_136 %uint_10 %182 %uint_4 + %194 = OpExtInst %void %2 DebugLocalVariable %193 %153 %64 %uint_133 %uint_10 %182 %uint_4 + %197 = OpExtInst %void %2 DebugLocalVariable %196 %57 %64 %uint_132 %uint_9 %182 %uint_4 + %200 = OpExtInst %void %2 DebugLocalVariable %199 %57 %64 %uint_129 %uint_9 %182 %uint_4 + %203 = OpExtInst %void %2 DebugLocalVariable %202 %57 %64 %uint_128 %uint_9 %182 %uint_4 + %206 = OpExtInst %void %2 DebugLocalVariable %205 %57 %64 %uint_127 %uint_9 %182 %uint_4 + %209 = OpExtInst %void %2 DebugLocalVariable %208 %153 %64 %uint_124 %uint_10 %182 %uint_4 + %212 = OpExtInst %void %2 DebugLocalVariable %211 %57 %64 %uint_121 %uint_9 %182 %uint_4 + %215 = OpExtInst %void %2 DebugLocalVariable %214 %57 %64 %uint_120 %uint_9 %182 %uint_4 + %218 = OpExtInst %void %2 DebugLocalVariable %217 %57 %64 %uint_119 %uint_9 %182 %uint_4 + %221 = OpExtInst %void %2 DebugLocalVariable %220 %153 %64 %uint_116 %uint_10 %182 %uint_4 + %223 = OpExtInst %void %2 DebugLocalVariable %78 %57 %64 %uint_112 %uint_9 %182 %uint_4 + %226 = OpExtInst %void %2 DebugLocalVariable %225 %153 %64 %uint_110 %uint_10 %182 %uint_4 + %228 = OpExtInst %void %2 DebugLocalVariable %166 %109 %64 %uint_107 %uint_10 %179 %uint_4 + %231 = OpExtInst %void %2 DebugLocalVariable %230 %153 %64 %uint_105 %uint_9 %179 %uint_4 + %233 = OpExtInst %void %2 DebugLocalVariable %171 %153 %64 %uint_103 %uint_9 %179 %uint_4 + %236 = OpExtInst %void %2 DebugLocalVariable %235 %58 %64 %uint_100 %uint_9 %179 %uint_4 + %239 = OpExtInst %void %2 DebugLocalVariable %238 %153 %64 %uint_99 %uint_9 %179 %uint_4 + %241 = OpExtInst %void %2 DebugLocalVariable %169 %153 %64 %uint_98 %uint_9 %179 %uint_4 + %245 = OpExtInst %void %2 DebugLocalVariable %244 %60 %64 %uint_95 %uint_40 %176 %uint_4 %uint_1 +)" + R"( %247 = OpExtInst %void %2 DebugTypeMember %246 %58 %64 %uint_29 %uint_9 %uint_0 %uint_128 %uint_3 + %250 = OpExtInst %void %2 DebugTypeMember %249 %58 %64 %uint_21 %uint_9 %uint_0 %uint_128 %uint_3 + %253 = OpExtInst %void %2 DebugTypeMember %252 %58 %64 %uint_22 %uint_9 %uint_128 %uint_128 %uint_3 + %256 = OpExtInst %void %2 DebugTypeMember %254 %58 %64 %uint_23 %uint_9 %uint_256 %uint_128 %uint_3 + %258 = OpExtInst %void %2 DebugTypeArray %57 %uint_4 %uint_4 + %262 = OpExtInst %void %2 DebugTypeMember %259 %258 %64 %uint_24 %uint_11 %uint_384 %uint_512 %uint_3 + %265 = OpExtInst %void %2 DebugTypeComposite %263 %uint_1 %64 %uint_19 %uint_8 %65 %263 %uint_896 %uint_3 %250 %253 %256 %262 + %266 = OpExtInst %void %2 DebugTypeArray %265 %uint_3 + %269 = OpExtInst %void %2 DebugTypeMember %267 %266 %64 %uint_30 %uint_8 %uint_128 %uint_2688 %uint_3 + %273 = OpExtInst %void %2 DebugTypeMember %271 %109 %64 %uint_31 %uint_6 %uint_2816 %uint_32 %uint_3 + %277 = OpExtInst %void %2 DebugTypeMember %275 %109 %64 %uint_32 %uint_6 %uint_2848 %uint_32 %uint_3 + %280 = OpExtInst %void %2 DebugTypeComposite %278 %uint_1 %64 %uint_27 %uint_8 %65 %278 %uint_2880 %uint_3 %247 %269 %273 %277 + %284 = OpExtInst %void %2 DebugTypeMember %282 %280 %64 %uint_35 %uint_34 %uint_0 %uint_2944 %uint_3 + %286 = OpExtInst %void %2 DebugTypeComposite %285 %uint_1 %64 %uint_35 %uint_9 %65 %285 %uint_2944 %uint_3 %284 + %287 = OpExtInst %void %2 DebugGlobalVariable %282 %286 %64 %uint_35 %uint_9 %65 %282 %ubo %uint_8 + %291 = OpExtInst %void %2 DebugTypeComposite %289 %uint_1 %64 %uint_0 %uint_0 %65 %290 %288 %uint_3 + %293 = OpExtInst %void %2 DebugGlobalVariable %292 %291 %64 %uint_12 %uint_14 %65 %292 %samplerShadowMap %uint_8 + %297 = OpExtInst %void %2 DebugTypeComposite %295 %uint_0 %64 %uint_0 %uint_0 %65 %296 %288 %uint_3 + %299 = OpExtInst %void %2 DebugTypeTemplateParameter %298 %58 %288 %64 %uint_0 %uint_0 + %300 = OpExtInst %void %2 DebugTypeTemplate %297 %299 + %302 = OpExtInst %void %2 DebugGlobalVariable %301 %300 %64 %uint_11 %uint_16 %65 %301 %textureShadowMap %uint_8 + %305 = OpExtInst %void %2 DebugGlobalVariable %304 %291 %64 %uint_8 %uint_14 %65 %304 %samplerAlbedo %uint_8 + %308 = OpExtInst %void %2 DebugTypeComposite %306 %uint_0 %64 %uint_0 %uint_0 %65 %307 %288 %uint_3 + %309 = OpExtInst %void %2 DebugTypeTemplateParameter %298 %58 %288 %64 %uint_0 %uint_0 + %310 = OpExtInst %void %2 DebugTypeTemplate %308 %309 + %312 = OpExtInst %void %2 DebugGlobalVariable %311 %310 %64 %uint_7 %uint_11 %65 %311 %textureAlbedo %uint_8 + %314 = OpExtInst %void %2 DebugGlobalVariable %313 %291 %64 %uint_6 %uint_14 %65 %313 %samplerNormal %uint_8 + %316 = OpExtInst %void %2 DebugGlobalVariable %315 %310 %64 %uint_5 %uint_11 %65 %315 %textureNormal %uint_8 + %318 = OpExtInst %void %2 DebugGlobalVariable %317 %291 %64 %uint_4 %uint_14 %65 %317 %samplerposition %uint_8 + %320 = OpExtInst %void %2 DebugGlobalVariable %319 %310 %64 %uint_3 %uint_11 %65 %319 %textureposition %uint_8 + %1803 = OpExtInst %void %2 DebugInlinedAt %uint_1792 %180 + %1885 = OpExtInst %void %2 DebugInlinedAt %uint_1869 %158 %1803 + %2085 = OpExtInst %void %2 DebugInlinedAt %uint_2060 %106 %1885 +)" + R"( %main = OpFunction %void None %321 + %322 = OpLabel + %2083 = OpVariable %_ptr_Function_float Function + %2086 = OpVariable %_ptr_Function_v4float Function + %1883 = OpVariable %_ptr_Function_v2int Function + %1891 = OpVariable %_ptr_Function_float Function + %1892 = OpVariable %_ptr_Function_int Function + %1894 = OpVariable %_ptr_Function_int Function + %1895 = OpVariable %_ptr_Function_int Function + %1896 = OpVariable %_ptr_Function_v4float Function + %1801 = OpVariable %_ptr_Function_int Function + %1447 = OpVariable %_ptr_Function_v4float Function + %1448 = OpVariable %_ptr_Function_v3float Function + %1450 = OpVariable %_ptr_Function_int Function + %1451 = OpVariable %_ptr_Function_v3float Function + %1453 = OpVariable %_ptr_Function_v3float Function + %1466 = OpVariable %_ptr_Function_v3float Function +%param_var_inUV = OpVariable %_ptr_Function_v2float Function + %325 = OpExtInst %void %2 DebugFunctionDefinition %176 %main + %326 = OpLoad %v2float %in_var_TEXCOORD0 + OpStore %param_var_inUV %326 + %2290 = OpExtInst %void %2 DebugScope %176 + %1620 = OpExtInst %void %2 DebugLine %64 %uint_95 %uint_95 %uint_33 %uint_40 + %1470 = OpExtInst %void %2 DebugDeclare %245 %param_var_inUV %243 + %2291 = OpExtInst %void %2 DebugScope %179 + %1621 = OpExtInst %void %2 DebugLine %64 %uint_98 %uint_98 %uint_19 %uint_19 + %1471 = OpLoad %type_2d_image %textureposition + %1622 = OpExtInst %void %2 DebugLine %64 %uint_98 %uint_98 %uint_42 %uint_42 + %1472 = OpLoad %type_sampler %samplerposition + %1624 = OpExtInst %void %2 DebugLine %64 %uint_98 %uint_98 %uint_19 %uint_63 + %1474 = OpSampledImage %type_sampled_image %1471 %1472 + %1475 = OpImageSampleImplicitLod %v4float %1474 %326 None + %1626 = OpExtInst %void %2 DebugLine %64 %uint_98 %uint_98 %uint_19 %uint_65 + %1476 = OpVectorShuffle %v3float %1475 %1475 0 1 2 + %2241 = OpExtInst %void %2 DebugLine %64 %uint_98 %uint_98 %uint_2 %uint_65 + %2240 = OpExtInst %void %2 DebugValue %241 %1476 %243 + %1629 = OpExtInst %void %2 DebugLine %64 %uint_99 %uint_99 %uint_18 %uint_18 + %1478 = OpLoad %type_2d_image %textureNormal + %1630 = OpExtInst %void %2 DebugLine %64 %uint_99 %uint_99 %uint_39 %uint_39 + %1479 = OpLoad %type_sampler %samplerNormal + %1632 = OpExtInst %void %2 DebugLine %64 %uint_99 %uint_99 %uint_18 %uint_58 + %1481 = OpSampledImage %type_sampled_image %1478 %1479 + %1482 = OpImageSampleImplicitLod %v4float %1481 %326 None + %1634 = OpExtInst %void %2 DebugLine %64 %uint_99 %uint_99 %uint_18 %uint_60 + %1483 = OpVectorShuffle %v3float %1482 %1482 0 1 2 + %2244 = OpExtInst %void %2 DebugLine %64 %uint_99 %uint_99 %uint_2 %uint_60 + %2243 = OpExtInst %void %2 DebugValue %239 %1483 %243 + %1637 = OpExtInst %void %2 DebugLine %64 %uint_100 %uint_100 %uint_18 %uint_18 + %1485 = OpLoad %type_2d_image %textureAlbedo + %1638 = OpExtInst %void %2 DebugLine %64 %uint_100 %uint_100 %uint_39 %uint_39 + %1486 = OpLoad %type_sampler %samplerAlbedo + %1640 = OpExtInst %void %2 DebugLine %64 %uint_100 %uint_100 %uint_18 %uint_58 + %1488 = OpSampledImage %type_sampled_image %1485 %1486 + %1489 = OpImageSampleImplicitLod %v4float %1488 %326 None + %1642 = OpExtInst %void %2 DebugLine %64 %uint_100 %uint_100 %uint_2 %uint_58 + OpStore %1447 %1489 + %1490 = OpExtInst %void %2 DebugDeclare %236 %1447 %243 + %1645 = OpExtInst %void %2 DebugLine %64 %uint_103 %uint_103 %uint_22 %uint_29 + %1492 = OpVectorShuffle %v3float %1489 %1489 0 1 2 + %1646 = OpExtInst %void %2 DebugLine %64 %uint_103 %uint_103 %uint_22 %uint_35 + %1493 = OpVectorTimesScalar %v3float %1492 %float_0_100000001 + %1647 = OpExtInst %void %2 DebugLine %64 %uint_103 %uint_103 %uint_2 %uint_35 + OpStore %1448 %1493 + %1494 = OpExtInst %void %2 DebugDeclare %233 %1448 %243 + %1650 = OpExtInst %void %2 DebugLine %64 %uint_105 %uint_105 %uint_13 %uint_29 + %1496 = OpExtInst %v3float %1 Normalize %1483 + %2247 = OpExtInst %void %2 DebugLine %64 %uint_105 %uint_105 %uint_2 %uint_29 + %2246 = OpExtInst %void %2 DebugValue %231 %1496 %243 + %1653 = OpExtInst %void %2 DebugLine %64 %uint_107 %uint_107 %uint_6 %uint_14 + OpStore %1450 %int_0 + %1498 = OpExtInst %void %2 DebugDeclare %228 %1450 %243 + %1655 = OpExtInst %void %2 DebugLine %64 %uint_107 %uint_107 %uint_6 %uint_15 + OpBranch %1499 + %1499 = OpLabel + %2292 = OpExtInst %void %2 DebugScope %179 + %1656 = OpExtInst %void %2 DebugLine %64 %uint_107 %uint_107 %uint_17 %uint_17 + %1500 = OpLoad %int %1450 + %1657 = OpExtInst %void %2 DebugLine %64 %uint_107 %uint_107 %uint_17 %uint_21 + %1501 = OpSLessThan %bool %1500 %int_3 + %2293 = OpExtInst %void %2 DebugNoScope + OpLoopMerge %1605 %1602 None + OpBranchConditional %1501 %1502 %1605 + %1502 = OpLabel + %2294 = OpExtInst %void %2 DebugScope %182 + %1660 = OpExtInst %void %2 DebugLine %64 %uint_110 %uint_110 %uint_25 %uint_25 + %1503 = OpLoad %int %1450 + %1661 = OpExtInst %void %2 DebugLine %64 %uint_110 %uint_110 %uint_14 %uint_37 + %1504 = OpAccessChain %_ptr_Uniform_UBO %ubo %int_0 + %1505 = OpAccessChain %_ptr_Uniform_v4float %1504 %int_1 %1503 %int_0 + %1663 = OpExtInst %void %2 DebugLine %64 %uint_110 %uint_110 %uint_14 %uint_28 + %1506 = OpLoad %v4float %1505 + %1664 = OpExtInst %void %2 DebugLine %64 %uint_110 %uint_110 %uint_14 %uint_37 + %1507 = OpVectorShuffle %v3float %1506 %1506 0 1 2 + %1666 = OpExtInst %void %2 DebugLine %64 %uint_110 %uint_110 %uint_14 %uint_43 + %1509 = OpFSub %v3float %1507 %1476 + %1667 = OpExtInst %void %2 DebugLine %64 %uint_110 %uint_110 %uint_3 %uint_43 + OpStore %1451 %1509 + %1510 = OpExtInst %void %2 DebugDeclare %226 %1451 %243 + %1670 = OpExtInst %void %2 DebugLine %64 %uint_112 %uint_112 %uint_16 %uint_24 + %1512 = OpExtInst %float %1 Length %1509 + %2250 = OpExtInst %void %2 DebugLine %64 %uint_112 %uint_112 %uint_3 %uint_24 + %2249 = OpExtInst %void %2 DebugValue %223 %1512 %243 + %1674 = OpExtInst %void %2 DebugLine %64 %uint_113 %uint_113 %uint_7 %uint_18 + %1515 = OpExtInst %v3float %1 Normalize %1509 + %1675 = OpExtInst %void %2 DebugLine %64 %uint_113 %uint_113 %uint_3 %uint_18 + OpStore %1451 %1515 + %1676 = OpExtInst %void %2 DebugLine %64 %uint_116 %uint_116 %uint_14 %uint_26 + %1516 = OpAccessChain %_ptr_Uniform_UBO %ubo %int_0 + %1517 = OpAccessChain %_ptr_Uniform_v4float %1516 %int_0 + %1678 = OpExtInst %void %2 DebugLine %64 %uint_116 %uint_116 %uint_14 %uint_18 + %1518 = OpLoad %v4float %1517 + %1679 = OpExtInst %void %2 DebugLine %64 %uint_116 %uint_116 %uint_14 %uint_26 + %1519 = OpVectorShuffle %v3float %1518 %1518 0 1 2 + %1681 = OpExtInst %void %2 DebugLine %64 %uint_116 %uint_116 %uint_14 %uint_32 + %1521 = OpFSub %v3float %1519 %1476 + %1682 = OpExtInst %void %2 DebugLine %64 %uint_116 %uint_116 %uint_3 %uint_32 + OpStore %1453 %1521 + %1522 = OpExtInst %void %2 DebugDeclare %221 %1453 %243 + %1685 = OpExtInst %void %2 DebugLine %64 %uint_117 %uint_117 %uint_7 %uint_18 + %1524 = OpExtInst %v3float %1 Normalize %1521 + %1686 = OpExtInst %void %2 DebugLine %64 %uint_117 %uint_117 %uint_3 %uint_18 + OpStore %1453 %1524 + %1687 = OpExtInst %void %2 DebugLine %64 %uint_119 %uint_119 %uint_34 %uint_46 + %1525 = OpExtInst %float %1 Radians %float_15 + %1688 = OpExtInst %void %2 DebugLine %64 %uint_119 %uint_119 %uint_30 %uint_47 + %1526 = OpExtInst %float %1 Cos %1525 + %2253 = OpExtInst %void %2 DebugLine %64 %uint_119 %uint_119 %uint_3 %uint_47 + %2252 = OpExtInst %void %2 DebugValue %218 %1526 %243 + %1691 = OpExtInst %void %2 DebugLine %64 %uint_120 %uint_120 %uint_34 %uint_46 + %1528 = OpExtInst %float %1 Radians %float_25 + %1692 = OpExtInst %void %2 DebugLine %64 %uint_120 %uint_120 %uint_30 %uint_47 + %1529 = OpExtInst %float %1 Cos %1528 + %2256 = OpExtInst %void %2 DebugLine %64 %uint_120 %uint_120 %uint_3 %uint_47 + %2255 = OpExtInst %void %2 DebugValue %215 %1529 %243 + %2259 = OpExtInst %void %2 DebugLine %64 %uint_121 %uint_121 %uint_3 %uint_22 + %2258 = OpExtInst %void %2 DebugValue %212 %float_100 %243 + %1698 = OpExtInst %void %2 DebugLine %64 %uint_124 %uint_124 %uint_26 %uint_49 + %1533 = OpAccessChain %_ptr_Uniform_UBO %ubo %int_0 + %1534 = OpAccessChain %_ptr_Uniform_v4float %1533 %int_1 %1503 %int_0 + %1700 = OpExtInst %void %2 DebugLine %64 %uint_124 %uint_124 %uint_26 %uint_40 + %1535 = OpLoad %v4float %1534 + %1701 = OpExtInst %void %2 DebugLine %64 %uint_124 %uint_124 %uint_26 %uint_49 + %1536 = OpVectorShuffle %v3float %1535 %1535 0 1 2 + %1703 = OpExtInst %void %2 DebugLine %64 %uint_124 %uint_124 %uint_55 %uint_76 + %1538 = OpAccessChain %_ptr_Uniform_UBO %ubo %int_0 + %1539 = OpAccessChain %_ptr_Uniform_v4float %1538 %int_1 %1503 %int_1 + %1705 = OpExtInst %void %2 DebugLine %64 %uint_124 %uint_124 %uint_55 %uint_69 + %1540 = OpLoad %v4float %1539 + %1706 = OpExtInst %void %2 DebugLine %64 %uint_124 %uint_124 %uint_55 %uint_76 + %1541 = OpVectorShuffle %v3float %1540 %1540 0 1 2 + %1707 = OpExtInst %void %2 DebugLine %64 %uint_124 %uint_124 %uint_26 %uint_76 + %1542 = OpFSub %v3float %1536 %1541 + %1708 = OpExtInst %void %2 DebugLine %64 %uint_124 %uint_124 %uint_16 %uint_79 + %1543 = OpExtInst %v3float %1 Normalize %1542 + %2262 = OpExtInst %void %2 DebugLine %64 %uint_124 %uint_124 %uint_3 %uint_79 + %2261 = OpExtInst %void %2 DebugValue %209 %1543 %243 + %1713 = OpExtInst %void %2 DebugLine %64 %uint_127 %uint_127 %uint_18 %uint_28 + %1547 = OpDot %float %1515 %1543 + %2265 = OpExtInst %void %2 DebugLine %64 %uint_127 %uint_127 %uint_3 %uint_28 + %2264 = OpExtInst %void %2 DebugValue %206 %1547 %243 + %1719 = OpExtInst %void %2 DebugLine %64 %uint_128 %uint_128 %uint_22 %uint_79 + %1552 = OpExtInst %float %1 SmoothStep %1529 %1526 %1547 + %2268 = OpExtInst %void %2 DebugLine %64 %uint_128 %uint_128 %uint_3 %uint_79 + %2267 = OpExtInst %void %2 DebugValue %203 %1552 %243 + %1724 = OpExtInst %void %2 DebugLine %64 %uint_129 %uint_129 %uint_29 %uint_62 + %1556 = OpExtInst %float %1 SmoothStep %float_100 %float_0 %1512 + %2271 = OpExtInst %void %2 DebugLine %64 %uint_129 %uint_129 %uint_3 %uint_62 + %2270 = OpExtInst %void %2 DebugValue %200 %1556 %243 + %1729 = OpExtInst %void %2 DebugLine %64 %uint_132 %uint_132 %uint_26 %uint_34 + %1560 = OpDot %float %1496 %1515 + %1730 = OpExtInst %void %2 DebugLine %64 %uint_132 %uint_132 %uint_17 %uint_35 + %1561 = OpExtInst %float %1 FMax %float_0 %1560 + %2274 = OpExtInst %void %2 DebugLine %64 %uint_132 %uint_132 %uint_3 %uint_35 + %2273 = OpExtInst %void %2 DebugValue %197 %1561 %243 + %1734 = OpExtInst %void %2 DebugLine %64 %uint_133 %uint_133 %uint_17 %uint_23 + %1564 = OpCompositeConstruct %v3float %1561 %1561 %1561 + %2277 = OpExtInst %void %2 DebugLine %64 %uint_133 %uint_133 %uint_3 %uint_23 + %2276 = OpExtInst %void %2 DebugValue %194 %1564 %243 + %1738 = OpExtInst %void %2 DebugLine %64 %uint_136 %uint_136 %uint_22 %uint_23 + %1567 = OpFNegate %v3float %1515 + %1740 = OpExtInst %void %2 DebugLine %64 %uint_136 %uint_136 %uint_14 %uint_27 + %1569 = OpExtInst %v3float %1 Reflect %1567 %1496 + %2280 = OpExtInst %void %2 DebugLine %64 %uint_136 %uint_136 %uint_3 %uint_27 + %2279 = OpExtInst %void %2 DebugValue %191 %1569 %243 + %1745 = OpExtInst %void %2 DebugLine %64 %uint_137 %uint_137 %uint_26 %uint_34 + %1573 = OpDot %float %1569 %1524 + %1746 = OpExtInst %void %2 DebugLine %64 %uint_137 %uint_137 %uint_17 %uint_35 + %1574 = OpExtInst %float %1 FMax %float_0 %1573 + %2283 = OpExtInst %void %2 DebugLine %64 %uint_137 %uint_137 %uint_3 %uint_35 + %2282 = OpExtInst %void %2 DebugValue %188 %1574 %243 + %1750 = OpExtInst %void %2 DebugLine %64 %uint_138 %uint_138 %uint_18 %uint_33 + %1577 = OpExtInst %float %1 Pow %1574 %float_16 + %1751 = OpExtInst %void %2 DebugLine %64 %uint_138 %uint_138 %uint_37 %uint_44 + %1578 = OpAccessChain %_ptr_Function_float %1447 %int_3 + %1579 = OpLoad %float %1578 + %1753 = OpExtInst %void %2 DebugLine %64 %uint_138 %uint_138 %uint_18 %uint_44 + %1580 = OpFMul %float %1577 %1579 + %1754 = OpExtInst %void %2 DebugLine %64 %uint_138 %uint_138 %uint_18 %uint_48 + %1581 = OpFMul %float %1580 %float_2_5 + %1755 = OpExtInst %void %2 DebugLine %64 %uint_138 %uint_138 %uint_17 %uint_53 + %1582 = OpCompositeConstruct %v3float %1581 %1581 %1581 + %2286 = OpExtInst %void %2 DebugLine %64 %uint_138 %uint_138 %uint_3 %uint_53 + %2285 = OpExtInst %void %2 DebugValue %185 %1582 %243 + %1760 = OpExtInst %void %2 DebugLine %64 %uint_140 %uint_140 %uint_24 %uint_31 + %1586 = OpFAdd %v3float %1564 %1582 + %1762 = OpExtInst %void %2 DebugLine %64 %uint_140 %uint_140 %uint_23 %uint_39 + %1588 = OpVectorTimesScalar %v3float %1586 %1552 + %1764 = OpExtInst %void %2 DebugLine %64 %uint_140 %uint_140 %uint_23 %uint_52 + %1590 = OpVectorTimesScalar %v3float %1588 %1556 + %1766 = OpExtInst %void %2 DebugLine %64 %uint_140 %uint_140 %uint_73 %uint_93 + %1592 = OpAccessChain %_ptr_Uniform_UBO %ubo %int_0 + %1593 = OpAccessChain %_ptr_Uniform_v4float %1592 %int_1 %1503 %int_2 + %1768 = OpExtInst %void %2 DebugLine %64 %uint_140 %uint_140 %uint_73 %uint_87 + %1594 = OpLoad %v4float %1593 + %1769 = OpExtInst %void %2 DebugLine %64 %uint_140 %uint_140 %uint_73 %uint_93 + %1595 = OpVectorShuffle %v3float %1594 %1594 0 1 2 + %1770 = OpExtInst %void %2 DebugLine %64 %uint_140 %uint_140 %uint_16 %uint_93 + %1596 = OpFMul %v3float %1590 %1595 + %1772 = OpExtInst %void %2 DebugLine %64 %uint_140 %uint_140 %uint_99 %uint_106 + %1598 = OpVectorShuffle %v3float %1489 %1489 0 1 2 + %1773 = OpExtInst %void %2 DebugLine %64 %uint_140 %uint_140 %uint_16 %uint_106 + %1599 = OpFMul %v3float %1596 %1598 + %1774 = OpExtInst %void %2 DebugLine %64 %uint_140 %uint_140 %uint_3 %uint_3 + %1600 = OpLoad %v3float %1448 + %1775 = OpExtInst %void %2 DebugLine %64 %uint_140 %uint_140 %uint_3 %uint_106 + %1601 = OpFAdd %v3float %1600 %1599 + OpStore %1448 %1601 + %2295 = OpExtInst %void %2 DebugScope %179 + %1777 = OpExtInst %void %2 DebugLine %64 %uint_107 %uint_107 %uint_34 %uint_36 + OpBranch %1602 + %1602 = OpLabel + %2296 = OpExtInst %void %2 DebugScope %179 + %1778 = OpExtInst %void %2 DebugLine %64 %uint_107 %uint_107 %uint_34 %uint_36 + %1603 = OpLoad %int %1450 + %1604 = OpIAdd %int %1603 %int_1 + OpStore %1450 %1604 + OpBranch %1499 + %1605 = OpLabel + %2297 = OpExtInst %void %2 DebugScope %179 + %1782 = OpExtInst %void %2 DebugLine %64 %uint_144 %uint_144 %uint_6 %uint_10 + %1606 = OpAccessChain %_ptr_Uniform_UBO %ubo %int_0 + %1607 = OpAccessChain %_ptr_Uniform_int %1606 %int_2 + %1608 = OpLoad %int %1607 + %1785 = OpExtInst %void %2 DebugLine %64 %uint_144 %uint_144 %uint_6 %uint_23 + %1609 = OpSGreaterThan %bool %1608 %int_0 + %2298 = OpExtInst %void %2 DebugNoScope + OpSelectionMerge %1614 None + OpBranchConditional %1609 %1610 %1614 +)" + R"( %1610 = OpLabel + %2299 = OpExtInst %void %2 DebugScope %180 + %1788 = OpExtInst %void %2 DebugLine %64 %uint_146 %uint_146 %uint_22 %uint_22 + %1611 = OpLoad %v3float %1448 + OpStore %1466 %1611 + %2300 = OpExtInst %void %2 DebugScope %155 %1803 + %1842 = OpExtInst %void %2 DebugLine %64 %uint_78 %uint_78 %uint_15 %uint_22 + %1810 = OpExtInst %void %2 DebugDeclare %172 %1466 %243 + %2301 = OpExtInst %void %2 DebugScope %180 + %2289 = OpExtInst %void %2 DebugLine %64 %uint_146 %uint_146 %uint_33 %uint_33 + %2288 = OpExtInst %void %2 DebugValue %170 %1476 %243 + %2302 = OpExtInst %void %2 DebugScope %157 %1803 + %1844 = OpExtInst %void %2 DebugLine %64 %uint_79 %uint_79 %uint_7 %uint_15 + OpStore %1801 %int_0 + %1813 = OpExtInst %void %2 DebugDeclare %167 %1801 %243 + %1846 = OpExtInst %void %2 DebugLine %64 %uint_79 %uint_79 %uint_7 %uint_16 + OpBranch %1814 + %1814 = OpLabel + %2303 = OpExtInst %void %2 DebugScope %157 %1803 + %1847 = OpExtInst %void %2 DebugLine %64 %uint_79 %uint_79 %uint_18 %uint_18 + %1815 = OpLoad %int %1801 + %1848 = OpExtInst %void %2 DebugLine %64 %uint_79 %uint_79 %uint_18 %uint_22 + %1816 = OpSLessThan %bool %1815 %int_3 + %2304 = OpExtInst %void %2 DebugNoScope + OpLoopMerge %1840 %1837 None + OpBranchConditional %1816 %1817 %1840 + %1817 = OpLabel + %2305 = OpExtInst %void %2 DebugScope %158 %1803 + %1851 = OpExtInst %void %2 DebugLine %64 %uint_81 %uint_81 %uint_38 %uint_38 + %1818 = OpLoad %int %1801 + %1852 = OpExtInst %void %2 DebugLine %64 %uint_81 %uint_81 %uint_27 %uint_41 + %1819 = OpAccessChain %_ptr_Uniform_UBO %ubo %int_0 + %1820 = OpAccessChain %_ptr_Uniform_mat4v4float %1819 %int_1 %1818 %int_3 + %1821 = OpLoad %mat4v4float %1820 + %1856 = OpExtInst %void %2 DebugLine %64 %uint_81 %uint_81 %uint_60 %uint_68 + %1823 = OpCompositeExtract %float %1476 0 + %1824 = OpCompositeExtract %float %1476 1 + %1825 = OpCompositeExtract %float %1476 2 + %1859 = OpExtInst %void %2 DebugLine %64 %uint_81 %uint_81 %uint_53 %uint_76 + %1826 = OpCompositeConstruct %v4float %1823 %1824 %1825 %float_1 + %1860 = OpExtInst %void %2 DebugLine %64 %uint_81 %uint_81 %uint_23 %uint_77 + %1827 = OpVectorTimesMatrix %v4float %1826 %1821 + %2229 = OpExtInst %void %2 DebugLine %64 %uint_81 %uint_81 %uint_3 %uint_77 + %2228 = OpExtInst %void %2 DebugValue %163 %1827 %243 + %1867 = OpExtInst %void %2 DebugLine %64 %uint_85 %uint_85 %uint_40 %uint_40 + %1832 = OpConvertSToF %float %1818 + %2235 = OpExtInst %void %2 DebugLine %64 %uint_85 %uint_85 %uint_28 %uint_28 + %2234 = OpExtInst %void %2 DebugValue %151 %1827 %243 + %2238 = OpExtInst %void %2 DebugLine %64 %uint_85 %uint_85 %uint_40 %uint_40 + %2237 = OpExtInst %void %2 DebugValue %148 %1832 %243 + %2306 = OpExtInst %void %2 DebugScope %103 %1885 + %1983 = OpExtInst %void %2 DebugLine %64 %uint_56 %uint_56 %uint_2 %uint_7 + %1904 = OpExtInst %void %2 DebugDeclare %146 %1883 %243 + %1986 = OpExtInst %void %2 DebugLine %64 %uint_57 %uint_57 %uint_2 %uint_2 + %1907 = OpLoad %type_2d_image_array %textureShadowMap +)" + R"( %1987 = OpExtInst %void %2 DebugLine %64 %uint_57 %uint_57 %uint_2 %uint_72 + %1908 = OpImageQuerySizeLod %v3uint %1907 %uint_0 + %1909 = OpCompositeExtract %uint %1908 0 + %1910 = OpBitcast %int %1909 + %1911 = OpAccessChain %_ptr_Function_int %1883 %int_0 + OpStore %1911 %1910 + %1912 = OpCompositeExtract %uint %1908 1 + %1913 = OpBitcast %int %1912 + %1914 = OpAccessChain %_ptr_Function_int %1883 %int_1 + OpStore %1914 %1913 + %1915 = OpCompositeExtract %uint %1908 2 + %1916 = OpBitcast %int %1915 + %2204 = OpExtInst %void %2 DebugValue %142 %1916 %243 + %1999 = OpExtInst %void %2 DebugLine %64 %uint_57 %uint_57 %uint_19 %uint_19 + %1917 = OpImageQueryLevels %uint %1907 + %2000 = OpExtInst %void %2 DebugLine %64 %uint_57 %uint_57 %uint_2 %uint_72 + %1918 = OpBitcast %int %1917 + %2207 = OpExtInst %void %2 DebugValue %138 %1918 %243 + %2211 = OpExtInst %void %2 DebugLine %64 %uint_58 %uint_58 %uint_2 %uint_16 + %2210 = OpExtInst %void %2 DebugValue %135 %float_1_5 %243 + %2005 = OpExtInst %void %2 DebugLine %64 %uint_59 %uint_59 %uint_13 %uint_21 + %1921 = OpFMul %float %float_1_5 %float_1 + %2006 = OpExtInst %void %2 DebugLine %64 %uint_59 %uint_59 %uint_33 %uint_40 + %1922 = OpAccessChain %_ptr_Function_int %1883 %int_0 + %1923 = OpLoad %int %1922 + %1924 = OpConvertSToF %float %1923 + %2009 = OpExtInst %void %2 DebugLine %64 %uint_59 %uint_59 %uint_13 %uint_41 + %1925 = OpFDiv %float %1921 %1924 + %2214 = OpExtInst %void %2 DebugLine %64 %uint_59 %uint_59 %uint_2 %uint_41 + %2213 = OpExtInst %void %2 DebugValue %132 %1925 %243 + %2013 = OpExtInst %void %2 DebugLine %64 %uint_60 %uint_60 %uint_13 %uint_21 + %1928 = OpFMul %float %float_1_5 %float_1 + %2014 = OpExtInst %void %2 DebugLine %64 %uint_60 %uint_60 %uint_33 %uint_40 + %1929 = OpAccessChain %_ptr_Function_int %1883 %int_1 + %1930 = OpLoad %int %1929 + %1931 = OpConvertSToF %float %1930 + %2017 = OpExtInst %void %2 DebugLine %64 %uint_60 %uint_60 %uint_13 %uint_41 + %1932 = OpFDiv %float %1928 %1931 + %2217 = OpExtInst %void %2 DebugLine %64 %uint_60 %uint_60 %uint_2 %uint_41 + %2216 = OpExtInst %void %2 DebugValue %129 %1932 %243 + %2020 = OpExtInst %void %2 DebugLine %64 %uint_62 %uint_62 %uint_2 %uint_23 + OpStore %1891 %float_0 + %1934 = OpExtInst %void %2 DebugDeclare %126 %1891 %243 + %2022 = OpExtInst %void %2 DebugLine %64 %uint_63 %uint_63 %uint_2 %uint_14 + OpStore %1892 %int_0 + %1935 = OpExtInst %void %2 DebugDeclare %123 %1892 %243 + %2220 = OpExtInst %void %2 DebugLine %64 %uint_64 %uint_64 %uint_2 %uint_14 + %2219 = OpExtInst %void %2 DebugValue %119 %int_1 %243 + %2027 = OpExtInst %void %2 DebugLine %64 %uint_66 %uint_66 %uint_15 %uint_16 + %1938 = OpSNegate %int %int_1 + %2028 = OpExtInst %void %2 DebugLine %64 %uint_66 %uint_66 %uint_7 %uint_16 + OpStore %1894 %1938 + %1939 = OpExtInst %void %2 DebugDeclare %115 %1894 %243 + %2030 = OpExtInst %void %2 DebugLine %64 %uint_66 %uint_66 %uint_7 %uint_21 + OpBranch %1940 + %1940 = OpLabel + %2307 = OpExtInst %void %2 DebugScope %103 %1885 + %2031 = OpExtInst %void %2 DebugLine %64 %uint_66 %uint_66 %uint_23 %uint_23 + %1941 = OpLoad %int %1894 + %2033 = OpExtInst %void %2 DebugLine %64 %uint_66 %uint_66 %uint_23 %uint_28 + %1943 = OpSLessThanEqual %bool %1941 %int_1 + %2308 = OpExtInst %void %2 DebugNoScope + OpLoopMerge %1976 %1973 None + OpBranchConditional %1943 %1944 %1976 + %1944 = OpLabel + %2309 = OpExtInst %void %2 DebugScope %104 %1885 + %2037 = OpExtInst %void %2 DebugLine %64 %uint_68 %uint_68 %uint_16 %uint_17 + %1946 = OpSNegate %int %int_1 + %2038 = OpExtInst %void %2 DebugLine %64 %uint_68 %uint_68 %uint_8 %uint_17 + OpStore %1895 %1946 + %1947 = OpExtInst %void %2 DebugDeclare %111 %1895 %243 + %2040 = OpExtInst %void %2 DebugLine %64 %uint_68 %uint_68 %uint_8 %uint_22 + OpBranch %1948 + %1948 = OpLabel + %2310 = OpExtInst %void %2 DebugScope %104 %1885 + %2041 = OpExtInst %void %2 DebugLine %64 %uint_68 %uint_68 %uint_24 %uint_24 + %1949 = OpLoad %int %1895 + %2043 = OpExtInst %void %2 DebugLine %64 %uint_68 %uint_68 %uint_24 %uint_29 + %1951 = OpSLessThanEqual %bool %1949 %int_1 + %2311 = OpExtInst %void %2 DebugNoScope + OpLoopMerge %1972 %1969 None + OpBranchConditional %1951 %1952 %1972 + %1952 = OpLabel + %2312 = OpExtInst %void %2 DebugScope %106 %1885 + %2047 = OpExtInst %void %2 DebugLine %64 %uint_70 %uint_70 %uint_32 %uint_32 + OpStore %1896 %1827 + %2051 = OpExtInst %void %2 DebugLine %64 %uint_70 %uint_70 %uint_53 %uint_53 + %1956 = OpLoad %int %1894 + %1957 = OpConvertSToF %float %1956 + %2053 = OpExtInst %void %2 DebugLine %64 %uint_70 %uint_70 %uint_50 %uint_53 + %1958 = OpFMul %float %1925 %1957 + %2055 = OpExtInst %void %2 DebugLine %64 %uint_70 %uint_70 %uint_59 %uint_59 + %1960 = OpLoad %int %1895 + %1961 = OpConvertSToF %float %1960 + %2057 = OpExtInst %void %2 DebugLine %64 %uint_70 %uint_70 %uint_56 %uint_59 + %1962 = OpFMul %float %1932 %1961 + %2058 = OpExtInst %void %2 DebugLine %64 %uint_70 %uint_70 %uint_43 %uint_60 + %1963 = OpCompositeConstruct %v2float %1958 %1962 + %2313 = OpExtInst %void %2 DebugScope %70 %2085 + %2141 = OpExtInst %void %2 DebugLine %64 %uint_37 %uint_37 %uint_19 %uint_26 + %2090 = OpExtInst %void %2 DebugDeclare %96 %1896 %243 + %2314 = OpExtInst %void %2 DebugScope %106 %1885 + %2223 = OpExtInst %void %2 DebugLine %64 %uint_70 %uint_70 %uint_36 %uint_36 + %2222 = OpExtInst %void %2 DebugValue %93 %1832 %243 + %2226 = OpExtInst %void %2 DebugLine %64 %uint_70 %uint_70 %uint_43 %uint_60 + %2225 = OpExtInst %void %2 DebugValue %90 %1963 %243 + %2315 = OpExtInst %void %2 DebugScope %73 %2085 + %2144 = OpExtInst %void %2 DebugLine %64 %uint_39 %uint_39 %uint_2 %uint_17 + OpStore %2083 %float_1 + %2094 = OpExtInst %void %2 DebugDeclare %86 %2083 %243 + %2147 = OpExtInst %void %2 DebugLine %64 %uint_40 %uint_40 %uint_27 %uint_29 + %2096 = OpAccessChain %_ptr_Function_float %1896 %int_3 + %2097 = OpLoad %float %2096 + %2098 = OpCompositeConstruct %v4float %2097 %2097 %2097 %2097 + %2150 = OpExtInst %void %2 DebugLine %64 %uint_40 %uint_40 %uint_23 %uint_29 + %2099 = OpFDiv %v4float %1827 %2098 + %2151 = OpExtInst %void %2 DebugLine %64 %uint_40 %uint_40 %uint_2 %uint_29 + OpStore %2086 %2099 + %2100 = OpExtInst %void %2 DebugDeclare %83 %2086 %243 + %2154 = OpExtInst %void %2 DebugLine %64 %uint_41 %uint_41 %uint_19 %uint_31 + %2102 = OpVectorShuffle %v2float %2099 %2099 0 1 + %2155 = OpExtInst %void %2 DebugLine %64 %uint_41 %uint_41 %uint_19 %uint_36 + %2103 = OpVectorTimesScalar %v2float %2102 %float_0_5 + %2156 = OpExtInst %void %2 DebugLine %64 %uint_41 %uint_41 %uint_19 %uint_42 + %2104 = OpFAdd %v2float %2103 %35 + %2158 = OpExtInst %void %2 DebugLine %64 %uint_41 %uint_41 %uint_2 %uint_42 + %2106 = OpVectorShuffle %v4float %2099 %2104 4 5 2 3 + OpStore %2086 %2106 + %2160 = OpExtInst %void %2 DebugLine %64 %uint_43 %uint_43 %uint_6 %uint_18 + %2107 = OpAccessChain %_ptr_Function_float %2086 %int_2 + %2108 = OpLoad %float %2107 + %2162 = OpExtInst %void %2 DebugLine %64 %uint_43 %uint_43 %uint_6 %uint_23 + %2109 = OpFOrdGreaterThan %bool %2108 %float_n1 + %2163 = OpExtInst %void %2 DebugLine %64 %uint_43 %uint_43 %uint_30 %uint_42 + %2110 = OpAccessChain %_ptr_Function_float %2086 %int_2 + %2111 = OpLoad %float %2110 + %2165 = OpExtInst %void %2 DebugLine %64 %uint_43 %uint_43 %uint_30 %uint_46 + %2112 = OpFOrdLessThan %bool %2111 %float_1 + %2166 = OpExtInst %void %2 DebugLine %64 %uint_43 %uint_43 %uint_6 %uint_46 + %2113 = OpLogicalAnd %bool %2109 %2112 + %2316 = OpExtInst %void %2 DebugNoScope + OpSelectionMerge %2139 None + OpBranchConditional %2113 %2114 %2139 + %2114 = OpLabel + %2317 = OpExtInst %void %2 DebugScope %74 %2085 + %2169 = OpExtInst %void %2 DebugLine %64 %uint_45 %uint_45 %uint_16 %uint_16 + %2115 = OpLoad %type_2d_image_array %textureShadowMap + %2170 = OpExtInst %void %2 DebugLine %64 %uint_45 %uint_45 %uint_40 %uint_40 + %2116 = OpLoad %type_sampler %samplerShadowMap + %2171 = OpExtInst %void %2 DebugLine %64 %uint_45 %uint_45 %uint_65 %uint_65 + %2117 = OpLoad %v4float %2086 + %2172 = OpExtInst %void %2 DebugLine %64 %uint_45 %uint_45 %uint_65 %uint_77 + %2118 = OpVectorShuffle %v2float %2117 %2117 0 1 + %2174 = OpExtInst %void %2 DebugLine %64 %uint_45 %uint_45 %uint_65 %uint_82 + %2120 = OpFAdd %v2float %2118 %1963 + %2122 = OpCompositeExtract %float %2120 0 + %2123 = OpCompositeExtract %float %2120 1 + %2178 = OpExtInst %void %2 DebugLine %64 %uint_45 %uint_45 %uint_58 %uint_95 + %2124 = OpCompositeConstruct %v3float %2122 %2123 %1832 + %2179 = OpExtInst %void %2 DebugLine %64 %uint_45 %uint_45 %uint_16 %uint_96 + %2125 = OpSampledImage %type_sampled_image_0 %2115 %2116 + %2126 = OpImageSampleImplicitLod %v4float %2125 %2124 None + %2181 = OpExtInst %void %2 DebugLine %64 %uint_45 %uint_45 %uint_16 %uint_98 + %2127 = OpCompositeExtract %float %2126 0 + %2202 = OpExtInst %void %2 DebugLine %64 %uint_45 %uint_45 %uint_3 %uint_98 + %2201 = OpExtInst %void %2 DebugValue %79 %2127 %243 + %2184 = OpExtInst %void %2 DebugLine %64 %uint_46 %uint_46 %uint_7 %uint_19 + %2129 = OpAccessChain %_ptr_Function_float %2086 %int_3 + %2130 = OpLoad %float %2129 + %2186 = OpExtInst %void %2 DebugLine %64 %uint_46 %uint_46 %uint_7 %uint_23 + %2131 = OpFOrdGreaterThan %bool %2130 %float_0 + %2188 = OpExtInst %void %2 DebugLine %64 %uint_46 %uint_46 %uint_37 %uint_49 + %2133 = OpAccessChain %_ptr_Function_float %2086 %int_2 + %2134 = OpLoad %float %2133 + %2190 = OpExtInst %void %2 DebugLine %64 %uint_46 %uint_46 %uint_30 %uint_49 + %2135 = OpFOrdLessThan %bool %2127 %2134 + %2191 = OpExtInst %void %2 DebugLine %64 %uint_46 %uint_46 %uint_7 %uint_49 + %2136 = OpLogicalAnd %bool %2131 %2135 + %2318 = OpExtInst %void %2 DebugNoScope + OpSelectionMerge %2138 None + OpBranchConditional %2136 %2137 %2138 + %2137 = OpLabel + %2319 = OpExtInst %void %2 DebugScope %76 %2085 + %2194 = OpExtInst %void %2 DebugLine %64 %uint_48 %uint_48 %uint_4 %uint_13 + OpStore %2083 %float_0_25 + %2320 = OpExtInst %void %2 DebugScope %74 %2085 + %2195 = OpExtInst %void %2 DebugLine %64 %uint_49 %uint_49 %uint_3 %uint_3 + OpBranch %2138 + %2138 = OpLabel + %2321 = OpExtInst %void %2 DebugScope %73 %2085 + %2196 = OpExtInst %void %2 DebugLine %64 %uint_50 %uint_50 %uint_2 %uint_2 + OpBranch %2139 + %2139 = OpLabel + %2322 = OpExtInst %void %2 DebugScope %73 %2085 + %2197 = OpExtInst %void %2 DebugLine %64 %uint_51 %uint_51 %uint_9 %uint_9 + %2140 = OpLoad %float %2083 + %2323 = OpExtInst %void %2 DebugScope %106 %1885 + %2061 = OpExtInst %void %2 DebugLine %64 %uint_70 %uint_70 %uint_4 %uint_4 + %1965 = OpLoad %float %1891 + %2062 = OpExtInst %void %2 DebugLine %64 %uint_70 %uint_70 %uint_4 %uint_61 + %1966 = OpFAdd %float %1965 %2140 + OpStore %1891 %1966 + %2064 = OpExtInst %void %2 DebugLine %64 %uint_71 %uint_71 %uint_4 %uint_9 + %1967 = OpLoad %int %1892 + %1968 = OpIAdd %int %1967 %int_1 + OpStore %1892 %1968 + %2324 = OpExtInst %void %2 DebugScope %104 %1885 + %2067 = OpExtInst %void %2 DebugLine %64 %uint_68 %uint_68 %uint_36 %uint_37 + OpBranch %1969 + %1969 = OpLabel + %2325 = OpExtInst %void %2 DebugScope %104 %1885 + %2068 = OpExtInst %void %2 DebugLine %64 %uint_68 %uint_68 %uint_36 %uint_37 + %1970 = OpLoad %int %1895 + %1971 = OpIAdd %int %1970 %int_1 + OpStore %1895 %1971 + OpBranch %1948 + %1972 = OpLabel + %2326 = OpExtInst %void %2 DebugScope %103 %1885 + %2072 = OpExtInst %void %2 DebugLine %64 %uint_66 %uint_66 %uint_35 %uint_36 + OpBranch %1973 + %1973 = OpLabel + %2327 = OpExtInst %void %2 DebugScope %103 %1885 + %2073 = OpExtInst %void %2 DebugLine %64 %uint_66 %uint_66 %uint_35 %uint_36 + %1974 = OpLoad %int %1894 + %1975 = OpIAdd %int %1974 %int_1 + OpStore %1894 %1975 + OpBranch %1940 + %1976 = OpLabel + %2328 = OpExtInst %void %2 DebugScope %103 %1885 + %2077 = OpExtInst %void %2 DebugLine %64 %uint_75 %uint_75 %uint_9 %uint_9 + %1977 = OpLoad %float %1891 + %2078 = OpExtInst %void %2 DebugLine %64 %uint_75 %uint_75 %uint_24 %uint_24 + %1978 = OpLoad %int %1892 + %1979 = OpConvertSToF %float %1978 + %2080 = OpExtInst %void %2 DebugLine %64 %uint_75 %uint_75 %uint_9 %uint_24 + %1980 = OpFDiv %float %1977 %1979 + %2329 = OpExtInst %void %2 DebugScope %158 %1803 + %2232 = OpExtInst %void %2 DebugLine %64 %uint_85 %uint_85 %uint_4 %uint_41 + %2231 = OpExtInst %void %2 DebugValue %160 %1980 %243 + %1872 = OpExtInst %void %2 DebugLine %64 %uint_90 %uint_90 %uint_3 %uint_3 + %1835 = OpLoad %v3float %1466 + %1873 = OpExtInst %void %2 DebugLine %64 %uint_90 %uint_90 %uint_3 %uint_16 + %1836 = OpVectorTimesScalar %v3float %1835 %1980 + OpStore %1466 %1836 + %2330 = OpExtInst %void %2 DebugScope %157 %1803 + %1875 = OpExtInst %void %2 DebugLine %64 %uint_79 %uint_79 %uint_35 %uint_37 + OpBranch %1837 + %1837 = OpLabel + %2331 = OpExtInst %void %2 DebugScope %157 %1803 + %1876 = OpExtInst %void %2 DebugLine %64 %uint_79 %uint_79 %uint_35 %uint_37 + %1838 = OpLoad %int %1801 + %1839 = OpIAdd %int %1838 %int_1 + OpStore %1801 %1839 + OpBranch %1814 + %1840 = OpLabel + %2332 = OpExtInst %void %2 DebugScope %157 %1803 + %1880 = OpExtInst %void %2 DebugLine %64 %uint_92 %uint_92 %uint_9 %uint_9 + %1841 = OpLoad %v3float %1466 + %2333 = OpExtInst %void %2 DebugScope %180 + %1793 = OpExtInst %void %2 DebugLine %64 %uint_146 %uint_146 %uint_3 %uint_40 + OpStore %1448 %1841 + %2334 = OpExtInst %void %2 DebugScope %179 + %1794 = OpExtInst %void %2 DebugLine %64 %uint_147 %uint_147 %uint_2 %uint_2 + OpBranch %1614 + %1614 = OpLabel +;CHECK: %1614 = OpLabel +;CHECK-NEXT: [[phi:%\w+]] = OpPhi +;CHECK-NEXT: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue %233 + %2335 = OpExtInst %void %2 DebugScope %179 + %1795 = OpExtInst %void %2 DebugLine %64 %uint_149 %uint_149 %uint_16 %uint_16 + %1615 = OpLoad %v3float %1448 + %1616 = OpCompositeExtract %float %1615 0 + %1617 = OpCompositeExtract %float %1615 1 + %1618 = OpCompositeExtract %float %1615 2 + %1799 = OpExtInst %void %2 DebugLine %64 %uint_149 %uint_149 %uint_9 %uint_28 + %1619 = OpCompositeConstruct %v4float %1616 %1617 %1618 %float_1 + %2336 = OpExtInst %void %2 DebugNoLine + %2337 = OpExtInst %void %2 DebugNoScope + OpStore %out_var_SV_TARGET %1619 + %329 = OpExtInst %void %2 DebugLine %64 %uint_150 %uint_150 %uint_1 %uint_1 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + // TODO(greg-lunarg): Add tests to verify handling of these cases: // // No optimization in the presence of diff --git a/test/opt/loop_optimizations/loop_descriptions.cpp b/test/opt/loop_optimizations/loop_descriptions.cpp index 4d2f989a..b3f4f440 100644 --- a/test/opt/loop_optimizations/loop_descriptions.cpp +++ b/test/opt/loop_optimizations/loop_descriptions.cpp @@ -298,7 +298,7 @@ TEST_F(PassClassTest, NoLoop) { /* Generated from following GLSL with latch block artificially inserted to be -seperate from continue. +separate from continue. #version 430 void main(void) { float x[10]; diff --git a/test/opt/loop_optimizations/loop_fission.cpp b/test/opt/loop_optimizations/loop_fission.cpp index 55b9c263..bc3ec39b 100644 --- a/test/opt/loop_optimizations/loop_fission.cpp +++ b/test/opt/loop_optimizations/loop_fission.cpp @@ -692,7 +692,7 @@ SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); SinglePassRunAndCheck(source, expected, true); // By passing 1 as argument we are using the constructor which makes the -// critera to split the loop be if the registers in the loop exceede 1. By +// criteria to split the loop be if the registers in the loop exceede 1. By // using this constructor we are also enabling multiple passes (disabled by // default). SinglePassRunAndCheck(source, expected_multiple_passes, true, diff --git a/test/opt/loop_optimizations/unroll_assumptions.cpp b/test/opt/loop_optimizations/unroll_assumptions.cpp index 0f933021..159e4a14 100644 --- a/test/opt/loop_optimizations/unroll_assumptions.cpp +++ b/test/opt/loop_optimizations/unroll_assumptions.cpp @@ -42,6 +42,10 @@ class PartialUnrollerTestPass : public Pass { Status Process() override { bool changed = false; for (Function& f : *context()->module()) { + if (f.IsDeclaration()) { + continue; + } + LoopDescriptor& loop_descriptor = *context()->GetLoopDescriptor(&f); for (auto& loop : loop_descriptor) { LoopUtils loop_utils{context(), &loop}; @@ -1510,6 +1514,33 @@ OpFunctionEnd SinglePassRunAndCheck>(text, text, false); } +TEST_F(PassClassTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); + SinglePassRunAndCheck>(text, text, false); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/loop_optimizations/unroll_simple.cpp b/test/opt/loop_optimizations/unroll_simple.cpp index 016316ae..299fb2d5 100644 --- a/test/opt/loop_optimizations/unroll_simple.cpp +++ b/test/opt/loop_optimizations/unroll_simple.cpp @@ -378,6 +378,185 @@ OpFunctionEnd)"; SinglePassRunAndMatch(text, true); } +TEST_F(PassClassTest, SimpleFullyUnrollWithShaderDebugInstructions) { + // We must preserve the debug information including + // NonSemantic.Shader.DebugInfo.100 instructions and DebugLine instructions. + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 330 +%file_name = OpString "test" +%float_name = OpString "float" +%main_name = OpString "main" +%f_name = OpString "f" +%i_name = OpString "i" +OpName %2 "main" +OpName %5 "x" +OpName %3 "c" +OpDecorate %3 Location 0 +%6 = OpTypeVoid +%7 = OpTypeFunction %6 +%8 = OpTypeInt 32 1 +%9 = OpTypePointer Function %8 +%10 = OpConstant %8 0 +%11 = OpConstant %8 4 +%12 = OpTypeBool +%13 = OpTypeFloat 32 +%14 = OpTypeInt 32 0 +%uint_0 = OpConstant %14 0 +%uint_1 = OpConstant %14 1 +%uint_2 = OpConstant %14 2 +%uint_3 = OpConstant %14 3 +%uint_4 = OpConstant %14 4 +%uint_5 = OpConstant %14 5 +%uint_6 = OpConstant %14 6 +%uint_7 = OpConstant %14 7 +%uint_8 = OpConstant %14 8 +%uint_10 = OpConstant %14 10 +%uint_32 = OpConstant %14 32 +%15 = OpConstant %14 4 +%16 = OpTypeArray %13 %15 +%17 = OpTypePointer Function %16 +%18 = OpConstant %13 1 +%19 = OpTypePointer Function %13 +%20 = OpConstant %8 1 +%21 = OpTypeVector %13 4 +%22 = OpTypePointer Output %21 +%3 = OpVariable %22 Output +%null_expr = OpExtInst %6 %ext DebugExpression +%deref = OpExtInst %6 %ext DebugOperation %uint_0 +%deref_expr = OpExtInst %6 %ext DebugExpression %deref +%src = OpExtInst %6 %ext DebugSource %file_name +%cu = OpExtInst %6 %ext DebugCompilationUnit %uint_1 %uint_4 %src %uint_5 +%dbg_tf = OpExtInst %6 %ext DebugTypeBasic %float_name %uint_32 %uint_3 %uint_0 +%dbg_v4f = OpExtInst %6 %ext DebugTypeVector %dbg_tf %uint_4 +%main_ty = OpExtInst %6 %ext DebugTypeFunction %uint_3 %dbg_v4f %dbg_v4f +%dbg_main = OpExtInst %6 %ext DebugFunction %main_name %main_ty %src %uint_0 %uint_0 %cu %main_name %uint_3 %uint_10 +%bb = OpExtInst %6 %ext DebugLexicalBlock %src %uint_0 %uint_0 %dbg_main +%dbg_f = OpExtInst %6 %ext DebugLocalVariable %f_name %dbg_v4f %src %uint_0 %uint_0 %dbg_main %uint_4 +%dbg_i = OpExtInst %6 %ext DebugLocalVariable %i_name %dbg_v4f %src %uint_1 %uint_0 %bb %uint_4 + +; CHECK: [[f:%\w+]] = OpString "f" +; CHECK: [[i:%\w+]] = OpString "i" +; CHECK: [[int_0:%\w+]] = OpConstant {{%\w+}} 0 + +; CHECK: [[null_expr:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugExpression +; CHECK: [[deref:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugOperation %uint_0 +; CHECK: [[deref_expr:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugExpression [[deref]] +; CHECK: [[dbg_fn:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugFunction +; CHECK: [[dbg_bb:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLexicalBlock +; CHECK: [[dbg_f:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLocalVariable [[f]] {{%\w+}} {{%\w+}} %uint_0 %uint_0 [[dbg_fn]] +; CHECK: [[dbg_i:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLocalVariable [[i]] {{%\w+}} {{%\w+}} %uint_1 %uint_0 [[dbg_bb]] + +%2 = OpFunction %6 None %7 +%23 = OpLabel + +; The first block has DebugDeclare and DebugValue with Deref +; +; CHECK: OpLabel +; CHECK: %x = OpVariable %_ptr_Function__arr_float_uint_4_0 Function +; CHECK: OpBranch +; CHECK: OpLabel +; CHECK: DebugScope [[dbg_fn]] +; CHECK: DebugValue [[dbg_f]] [[int_0]] [[null_expr]] +; CHECK: OpBranch +; CHECK: DebugScope [[dbg_fn]] +; CHECK: DebugLine {{%\w+}} %uint_1 %uint_1 %uint_1 %uint_1 +; CHECK: OpSLessThan +; CHECK: DebugLine {{%\w+}} %uint_2 %uint_2 %uint_0 %uint_0 +; CHECK: OpBranch +; CHECK: OpLabel +; CHECK: DebugScope [[dbg_bb]] +; CHECK: DebugDeclare [[dbg_f]] %x [[null_expr]] +; CHECK: DebugValue [[dbg_i]] %x [[deref_expr]] +; CHECK: DebugLine {{%\w+}} %uint_3 %uint_3 %uint_0 %uint_0 +; +; CHECK: DebugLine {{%\w+}} %uint_6 %uint_6 %uint_0 %uint_0 +; CHECK: [[add:%\w+]] = OpIAdd +; CHECK: DebugValue [[dbg_f]] [[add]] [[null_expr]] +; CHECK: DebugLine {{%\w+}} %uint_7 %uint_7 %uint_0 %uint_0 + +; Other blocks do not have DebugDeclare and DebugValue with Deref +; +; CHECK: DebugScope [[dbg_fn]] +; CHECK: DebugLine {{%\w+}} %uint_1 %uint_1 %uint_1 %uint_1 +; CHECK: OpSLessThan +; CHECK: DebugLine {{%\w+}} %uint_2 %uint_2 %uint_0 %uint_0 +; CHECK: OpBranch +; CHECK: OpLabel +; +; CHECK: DebugScope [[dbg_bb]] +; CHECK-NOT: DebugDeclare [[dbg_f]] %x [[null_expr]] +; CHECK-NOT: DebugValue [[dbg_i]] %x [[deref_expr]] +; CHECK: DebugLine {{%\w+}} %uint_3 %uint_3 %uint_0 %uint_0 +; +; CHECK: DebugLine {{%\w+}} %uint_6 %uint_6 %uint_0 %uint_0 +; CHECK: [[add:%\w+]] = OpIAdd +; CHECK: DebugValue [[dbg_f]] [[add]] [[null_expr]] +; CHECK: DebugLine {{%\w+}} %uint_7 %uint_7 %uint_0 %uint_0 +; +; CHECK-NOT: DebugDeclare [[dbg_f]] %x [[null_expr]] +; CHECK-NOT: DebugValue [[dbg_i]] %x [[deref_expr]] +; CHECK: DebugScope [[dbg_fn]] +; CHECK: DebugLine {{%\w+}} %uint_8 %uint_8 %uint_0 %uint_0 +; CHECK: OpReturn + +%5 = OpVariable %17 Function +OpBranch %24 +%24 = OpLabel +%35 = OpPhi %8 %10 %23 %34 %26 +%s1 = OpExtInst %6 %ext DebugScope %dbg_main +%d10 = OpExtInst %6 %ext DebugLine %file_name %uint_1 %uint_1 %uint_0 %uint_0 +%value0 = OpExtInst %6 %ext DebugValue %dbg_f %35 %null_expr +OpLoopMerge %25 %26 Unroll +OpBranch %27 +%27 = OpLabel +%s2 = OpExtInst %6 %ext DebugScope %dbg_main +%d1 = OpExtInst %6 %ext DebugLine %file_name %uint_1 %uint_1 %uint_1 %uint_1 +%29 = OpSLessThan %12 %35 %11 +%d2 = OpExtInst %6 %ext DebugLine %file_name %uint_2 %uint_2 %uint_0 %uint_0 +OpBranchConditional %29 %30 %25 +%30 = OpLabel +%s3 = OpExtInst %6 %ext DebugScope %bb +%decl0 = OpExtInst %6 %ext DebugDeclare %dbg_f %5 %null_expr +%decl1 = OpExtInst %6 %ext DebugValue %dbg_i %5 %deref_expr +%d3 = OpExtInst %6 %ext DebugLine %file_name %uint_3 %uint_3 %uint_0 %uint_0 +%32 = OpAccessChain %19 %5 %35 +%d4 = OpExtInst %6 %ext DebugLine %file_name %uint_4 %uint_4 %uint_0 %uint_0 +OpStore %32 %18 +%d5 = OpExtInst %6 %ext DebugLine %file_name %uint_5 %uint_5 %uint_0 %uint_0 +OpBranch %26 +%26 = OpLabel +%s4 = OpExtInst %6 %ext DebugScope %dbg_main +%d6 = OpExtInst %6 %ext DebugLine %file_name %uint_6 %uint_6 %uint_0 %uint_0 +%34 = OpIAdd %8 %35 %20 +%value1 = OpExtInst %6 %ext DebugValue %dbg_f %34 %null_expr +%d7 = OpExtInst %6 %ext DebugLine %file_name %uint_7 %uint_7 %uint_0 %uint_0 +OpBranch %24 +%25 = OpLabel +%s5 = OpExtInst %6 %ext DebugScope %dbg_main +%d8 = OpExtInst %6 %ext DebugLine %file_name %uint_8 %uint_8 %uint_0 %uint_0 +OpReturn +OpFunctionEnd)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(text, true); +} + template class PartialUnrollerTestPass : public Pass { public: @@ -707,7 +886,7 @@ OpFunctionEnd LoopUnroller loop_unroller; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); // By unrolling by a factor that doesn't divide evenly into the number of loop - // iterations we perfom an additional transform when partially unrolling to + // iterations we perform an additional transform when partially unrolling to // account for the remainder. SinglePassRunAndCheck>(text, output, false); } @@ -2939,7 +3118,7 @@ OpFunctionEnd /* Generated from following GLSL with latch block artificially inserted to be -seperate from continue. +separate from continue. #version 430 void main(void) { float x[10]; @@ -3401,6 +3580,249 @@ OpFunctionEnd SinglePassRunAndCheck(shader, output, false); } +TEST_F(PassClassTest, UnrollWithPhiReferencesPhi) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %color + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %main "main" + OpName %color "color" + OpDecorate %color Location 0 + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 + %uint_1 = OpConstant %uint 1 + %uint_3 = OpConstant %uint 3 + %void = OpTypeVoid + %11 = OpTypeFunction %void + %bool = OpTypeBool + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %color = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %11 + %15 = OpLabel + OpBranch %16 + %16 = OpLabel + %17 = OpPhi %float %float_0 %15 %18 %19 + %18 = OpPhi %float %float_1 %15 %20 %19 + %21 = OpPhi %uint %uint_1 %15 %22 %19 + %23 = OpULessThanEqual %bool %21 %uint_3 + OpLoopMerge %24 %19 Unroll + OpBranchConditional %23 %25 %24 + %25 = OpLabel + +; First loop iteration +; CHECK: [[next_phi1_0:%\w+]] = OpFSub %float %float_1 %float_0 + +; Second loop iteration +; CHECK: [[next_phi1_1:%\w+]] = OpFSub %float [[next_phi1_0]] %float_1 + +; Third loop iteration +; CHECK: OpFSub %float [[next_phi1_1]] [[next_phi1_0]] + + %20 = OpFSub %float %18 %17 + OpBranch %19 + %19 = OpLabel + %22 = OpIAdd %uint %21 %uint_1 + OpBranch %16 + %24 = OpLabel + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(text, true); +} + +TEST_F(PassClassTest, UnrollWithDoublePhiReferencesPhi) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %color + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %main "main" + OpName %color "color" + OpDecorate %color Location 0 + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 + %uint_1 = OpConstant %uint 1 + %uint_3 = OpConstant %uint 3 + %void = OpTypeVoid + %11 = OpTypeFunction %void + %bool = OpTypeBool + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %color = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %11 + %15 = OpLabel + OpBranch %16 + %16 = OpLabel + %17 = OpPhi %float %float_1 %15 %18 %19 + %18 = OpPhi %float %float_0 %15 %20 %19 + %20 = OpPhi %float %float_1 %15 %21 %19 + %22 = OpPhi %uint %uint_1 %15 %23 %19 + %24 = OpULessThanEqual %bool %22 %uint_3 + OpLoopMerge %25 %19 Unroll + OpBranchConditional %24 %26 %25 + %26 = OpLabel + +; First loop iteration +; CHECK: [[next_phi1_0:%\w+]] = OpFSub %float %float_1 %float_0 +; CHECK: OpFMul %float %float_1 + +; Second loop iteration +; CHECK: [[next_phi1_1:%\w+]] = OpFSub %float [[next_phi1_0]] %float_1 +; CHECK: OpFMul %float %float_0 + +; Third loop iteration +; CHECK: OpFSub %float [[next_phi1_1]] [[next_phi1_0]] +; CHECK: OpFMul %float %float_1 + + %21 = OpFSub %float %20 %18 + %27 = OpFMul %float %17 %21 + OpBranch %19 + %19 = OpLabel + %23 = OpIAdd %uint %22 %uint_1 + OpBranch %16 + %25 = OpLabel + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(text, true); +} + +TEST_F(PassClassTest, PartialUnrollWithPhiReferencesPhi) { + // With LocalMultiStoreElimPass + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %color + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %main "main" + OpName %color "color" + OpDecorate %color Location 0 + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 + %uint_1 = OpConstant %uint 1 + %uint_3 = OpConstant %uint 3 + %void = OpTypeVoid + %11 = OpTypeFunction %void + %bool = OpTypeBool + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %color = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %11 + %15 = OpLabel + OpBranch %16 + %16 = OpLabel + %17 = OpPhi %float %float_0 %15 %18 %19 + %18 = OpPhi %float %float_1 %15 %20 %19 + %21 = OpPhi %uint %uint_1 %15 %22 %19 + %23 = OpULessThanEqual %bool %21 %uint_3 + OpLoopMerge %24 %19 Unroll + OpBranchConditional %23 %25 %24 + %25 = OpLabel + +; CHECK: [[phi0_0:%\w+]] = OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[phi1_0:%\w+]] +; CHECK: [[phi1_0]] = OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[sub:%\w+]] + +; CHECK: [[sub]] = OpFSub {{%\w+}} [[phi1_0]] [[phi0_0]] + +; CHECK: [[phi0_1:%\w+]] = OpPhi {{%\w+}} [[phi0_0]] +; CHECK: [[phi1_1:%\w+]] = OpPhi {{%\w+}} [[phi1_0]] + +; CHECK: [[sub:%\w+]] = OpFSub {{%\w+}} [[phi1_1]] [[phi0_1]] + +; CHECK: OpFSub {{%\w+}} [[sub]] [[phi1_1]] + + %20 = OpFSub %float %18 %17 + OpBranch %19 + %19 = OpLabel + %22 = OpIAdd %uint %21 %uint_1 + OpBranch %16 + %24 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndMatch>(text, true); +} + +TEST_F(PassClassTest, DontUnrollInfiteLoop) { + // This is an infinite loop that because the step is 0. We want to make sure + // the unroller does not try to unroll it. + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +%void = OpTypeVoid +%4 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_50 = OpConstant %int 50 +%bool = OpTypeBool +%int_0_0 = OpConstant %int 0 +%2 = OpFunction %void None %4 +%10 = OpLabel +OpBranch %11 +%11 = OpLabel +%12 = OpPhi %int %int_0 %10 %13 %14 +%15 = OpSLessThan %bool %12 %int_50 +OpLoopMerge %16 %14 Unroll +OpBranchConditional %15 %14 %16 +%14 = OpLabel +%13 = OpIAdd %int %12 %int_0_0 +OpBranch %11 +%16 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/module_test.cpp b/test/opt/module_test.cpp index a3c2eed7..17a13650 100644 --- a/test/opt/module_test.cpp +++ b/test/opt/module_test.cpp @@ -52,7 +52,7 @@ inline std::unique_ptr BuildModule(std::string text) { } TEST(ModuleTest, ComputeIdBound) { - // Emtpy module case. + // Empty module case. EXPECT_EQ(1u, BuildModule("")->module()->ComputeIdBound()); // Sensitive to result id EXPECT_EQ(2u, BuildModule("%void = OpTypeVoid")->module()->ComputeIdBound()); diff --git a/test/opt/optimizer_test.cpp b/test/opt/optimizer_test.cpp index a51638a1..0171c09b 100644 --- a/test/opt/optimizer_test.cpp +++ b/test/opt/optimizer_test.cpp @@ -147,7 +147,7 @@ TEST(Optimizer, CanRegisterPassesFromFlags) { std::vector pass_flags = { "--strip-debug", - "--strip-reflect", + "--strip-nonsemantic", "--set-spec-const-default-value=23:42 21:12", "--if-conversion", "--freeze-spec-const", diff --git a/test/opt/pass_manager_test.cpp b/test/opt/pass_manager_test.cpp index 22d5e22e..4f36d5b2 100644 --- a/test/opt/pass_manager_test.cpp +++ b/test/opt/pass_manager_test.cpp @@ -30,7 +30,7 @@ namespace { using spvtest::GetIdBound; using ::testing::Eq; -// A null pass whose construtors accept arguments +// A null pass whose constructors accept arguments class NullPassWithArgs : public NullPass { public: NullPassWithArgs(uint32_t) {} diff --git a/test/opt/pass_merge_return_test.cpp b/test/opt/pass_merge_return_test.cpp index fd97efab..04bd5d9b 100644 --- a/test/opt/pass_merge_return_test.cpp +++ b/test/opt/pass_merge_return_test.cpp @@ -2231,7 +2231,7 @@ TEST_F(MergeReturnPassTest, ReturnsInSwitch) { TEST_F(MergeReturnPassTest, UnreachableMergeAndContinue) { // Make sure that the pass can handle a single block that is both a merge and - // a continue. + // a continue. Note that this is invalid SPIR-V. const std::string text = R"( OpCapability Shader @@ -2265,7 +2265,7 @@ TEST_F(MergeReturnPassTest, UnreachableMergeAndContinue) { )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - auto result = SinglePassRunAndDisassemble(text, true, true); + auto result = SinglePassRunAndDisassemble(text, true, false); // Not looking for any particular output. Other tests do that. // Just want to make sure the check for unreachable blocks does not emit an @@ -2567,6 +2567,39 @@ TEST_F(MergeReturnPassTest, ChainedPointerUsedAfterLoop) { SinglePassRunAndMatch(before, true); } +TEST_F(MergeReturnPassTest, OverflowTest1) { + const std::string text = + R"( +; CHECK: OpReturn +; CHECK-NOT: OpReturn +; CHECK: OpFunctionEnd + OpCapability ClipDistance + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %void = OpTypeVoid + %6 = OpTypeFunction %void + %2 = OpFunction %void None %6 + %4194303 = OpLabel + OpBranch %18 + %18 = OpLabel + OpLoopMerge %19 %20 None + OpBranch %21 + %21 = OpLabel + OpReturn + %20 = OpLabel + OpBranch %18 + %19 = OpLabel + OpUnreachable + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto result = + SinglePassRunToBinary(text, /* skip_nop = */ true); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/reduce_load_size_test.cpp b/test/opt/reduce_load_size_test.cpp index 7672e8f3..45467506 100644 --- a/test/opt/reduce_load_size_test.cpp +++ b/test/opt/reduce_load_size_test.cpp @@ -17,6 +17,12 @@ #include "test/opt/pass_fixture.h" #include "test/opt/pass_utils.h" +namespace { + +const double kDefaultLoadReductionThreshold = 0.9; + +} // namespace + namespace spvtools { namespace opt { namespace { @@ -104,7 +110,8 @@ TEST_F(ReduceLoadSizeTest, cbuffer_load_extract) { 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(test, false); + SinglePassRunAndMatch(test, false, + kDefaultLoadReductionThreshold); } TEST_F(ReduceLoadSizeTest, cbuffer_load_extract_not_affected_by_debug_instr) { @@ -202,7 +209,8 @@ TEST_F(ReduceLoadSizeTest, cbuffer_load_extract_not_affected_by_debug_instr) { 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(test, false); + SinglePassRunAndMatch(test, false, + kDefaultLoadReductionThreshold); } TEST_F(ReduceLoadSizeTest, cbuffer_load_extract_vector) { @@ -280,7 +288,8 @@ 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); - SinglePassRunAndCheck(test, test, true, false); + SinglePassRunAndCheck(test, test, true, false, + kDefaultLoadReductionThreshold); } TEST_F(ReduceLoadSizeTest, cbuffer_load_5_extract) { @@ -351,7 +360,8 @@ 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); - SinglePassRunAndCheck(test, test, true, false); + SinglePassRunAndCheck(test, test, true, false, + kDefaultLoadReductionThreshold); } TEST_F(ReduceLoadSizeTest, cbuffer_load_fully_used) { @@ -416,7 +426,115 @@ 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); - SinglePassRunAndCheck(test, test, true, false); + SinglePassRunAndCheck(test, test, true, false, + kDefaultLoadReductionThreshold); +} + +TEST_F(ReduceLoadSizeTest, replace_cbuffer_load_fully_used) { + const std::string test = + R"( + OpCapability Shader + OpCapability SampledBuffer + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_Target0 + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_MaterialInstancing_cbuffer "type.MaterialInstancing_cbuffer" + OpMemberName %type_MaterialInstancing_cbuffer 0 "MaterialInstancing_constants" + OpName %MaterialInstancing_Constants "MaterialInstancing_Constants" + OpMemberName %MaterialInstancing_Constants 0 "offset0" + OpMemberName %MaterialInstancing_Constants 1 "params" + OpName %InstancingParams_Constants "InstancingParams_Constants" + OpMemberName %InstancingParams_Constants 0 "offset1" + OpName %MaterialInstancing_cbuffer "MaterialInstancing_cbuffer" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %main "main" + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %MaterialInstancing_cbuffer DescriptorSet 6 + OpDecorate %MaterialInstancing_cbuffer Binding 0 + OpMemberDecorate %InstancingParams_Constants 0 Offset 0 + OpMemberDecorate %MaterialInstancing_Constants 0 Offset 0 + OpMemberDecorate %MaterialInstancing_Constants 1 Offset 16 + OpMemberDecorate %type_MaterialInstancing_cbuffer 0 Offset 0 + OpDecorate %type_MaterialInstancing_cbuffer Block + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %v4int = OpTypeVector %int 4 +%InstancingParams_Constants = OpTypeStruct %v4int +%MaterialInstancing_Constants = OpTypeStruct %v4int %InstancingParams_Constants +%type_MaterialInstancing_cbuffer = OpTypeStruct %MaterialInstancing_Constants +%_ptr_Uniform_type_MaterialInstancing_cbuffer = OpTypePointer Uniform %type_MaterialInstancing_cbuffer +%_ptr_Output_int = OpTypePointer Output %int + %void = OpTypeVoid + %60 = OpTypeFunction %void +%_ptr_Uniform_MaterialInstancing_Constants = OpTypePointer Uniform %MaterialInstancing_Constants +%MaterialInstancing_cbuffer = OpVariable %_ptr_Uniform_type_MaterialInstancing_cbuffer Uniform +%out_var_SV_Target0 = OpVariable %_ptr_Output_int Output + %main = OpFunction %void None %60 + %80 = OpLabel + %131 = OpAccessChain %_ptr_Uniform_MaterialInstancing_Constants %MaterialInstancing_cbuffer %int_0 + %132 = OpLoad %MaterialInstancing_Constants %131 +; CHECK: [[ac1:%\w+]] = OpAccessChain {{%\w+}} %MaterialInstancing_cbuffer %int_0 +; CHECK: [[ac2:%\w+]] = OpAccessChain {{%\w+}} [[ac1]] %uint_0 +; CHECK: OpLoad %v4int [[ac2]] + +; CHECK: [[ac3:%\w+]] = OpAccessChain {{%\w+}} [[ac1]] %uint_1 +; CHECK: [[ac4:%\w+]] = OpAccessChain {{%\w+}} [[ac3]] %uint_0 +; CHECK: OpLoad %v4int [[ac4]] + %134 = OpCompositeExtract %v4int %132 0 + %135 = OpCompositeExtract %InstancingParams_Constants %132 1 + %136 = OpCompositeExtract %v4int %135 0 + %149 = OpCompositeExtract %int %134 0 + %185 = OpCompositeExtract %int %136 0 + %156 = OpIAdd %int %149 %185 + OpStore %out_var_SV_Target0 %156 + 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(test, false, 1.1); +} + +TEST_F(ReduceLoadSizeTest, replace_array_with_spec_constant_size) { + const std::string test = + R"( + OpCapability ClipDistance + OpExtension " " + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 " " + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %6 = OpSpecConstant %uint 538976288 + %_arr_int_6 = OpTypeArray %int %6 + %_struct_8 = OpTypeStruct %_arr_int_6 + %_struct_9 = OpTypeStruct %_struct_8 +%_ptr_Uniform__struct_9 = OpTypePointer Uniform %_struct_9 +; CHECK: [[var:%\w+]] = OpVariable %_ptr_Uniform__struct_9 Uniform + %11 = OpVariable %_ptr_Uniform__struct_9 Uniform + %int_0 = OpConstant %int 0 +%_ptr_Uniform__arr_int_6 = OpTypePointer Uniform %_arr_int_6 + %1 = OpFunction %void None %3 + %14 = OpLabel +; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_Uniform__arr_int_6 [[var]] %int_0 %int_0 +; CHECK: [[new_ac:%\w+]] = OpAccessChain %_ptr_Uniform_int [[ac]] %uint_538976288 +; CHECK: [[ld:%\w+]] = OpLoad %int [[new_ac]] +; CHECK: %18 = OpIAdd %int [[ld]] [[ld]] + %15 = OpAccessChain %_ptr_Uniform__arr_int_6 %11 %int_0 %int_0 + %16 = OpLoad %_arr_int_6 %15 + %17 = OpCompositeExtract %int %16 538976288 + %18 = OpIAdd %int %17 %17 + OpUnreachable + OpFunctionEnd +)"; + + SinglePassRunAndMatch(test, false, + kDefaultLoadReductionThreshold); } } // namespace diff --git a/test/opt/redundancy_elimination_test.cpp b/test/opt/redundancy_elimination_test.cpp index 474f4661..28eda73e 100644 --- a/test/opt/redundancy_elimination_test.cpp +++ b/test/opt/redundancy_elimination_test.cpp @@ -335,6 +335,32 @@ TEST_F(RedundancyEliminationTest, OpenCLDebugInfo100) { SinglePassRunAndMatch(text, false); } +TEST_F(RedundancyEliminationTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} + } // namespace } // namespace opt -} // namespace spvtools +} // namespace spvtools \ No newline at end of file diff --git a/test/opt/relax_float_ops_test.cpp b/test/opt/relax_float_ops_test.cpp index 14cde0b9..b9cb0de0 100644 --- a/test/opt/relax_float_ops_test.cpp +++ b/test/opt/relax_float_ops_test.cpp @@ -137,6 +137,86 @@ OpFunctionEnd true); } +TEST_F(RelaxFloatOpsTest, RelaxFloatOpsForLinkage) { + const std::string defs0 = + R"(OpCapability Shader +OpCapability Linkage +OpCapability Sampled1D +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %g_tTex1df4 "g_tTex1df4" +OpName %g_sSamp "g_sSamp" +OpName %i_Tex0 "i.Tex0" +OpName %i_Tex1 "i.Tex1" +OpName %_entryPointOutput_Color "@entryPointOutput.Color" +OpDecorate %main LinkageAttributes "main" Export +OpDecorate %g_tTex1df4 DescriptorSet 0 +OpDecorate %g_tTex1df4 Binding 0 +OpDecorate %g_sSamp DescriptorSet 0 +OpDecorate %g_sSamp Binding 0 +OpDecorate %i_Tex0 Location 0 +OpDecorate %i_Tex1 Location 1 +OpDecorate %_entryPointOutput_Color Location 0 +)"; + + const std::string defs1 = + R"(%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%17 = OpTypeImage %float 1D 0 0 0 1 Unknown +%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17 +%g_tTex1df4 = OpVariable %_ptr_UniformConstant_17 UniformConstant +%21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 +%g_sSamp = OpVariable %_ptr_UniformConstant_21 UniformConstant +%25 = OpTypeSampledImage %17 +%_ptr_Input_float = OpTypePointer Input %float +%i_Tex0 = OpVariable %_ptr_Input_float Input +%i_Tex1 = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output +%float_0_5 = OpConstant %float 0.5 +%116 = OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0_5 %float_0_5 +)"; + + const std::string relax_decos = + R"(OpDecorate %60 RelaxedPrecision +OpDecorate %63 RelaxedPrecision +OpDecorate %82 RelaxedPrecision +OpDecorate %88 RelaxedPrecision +OpDecorate %91 RelaxedPrecision +OpDecorate %94 RelaxedPrecision +)"; + + const std::string func_orig = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%60 = OpLoad %float %i_Tex0 +%63 = OpLoad %float %i_Tex1 +%77 = OpLoad %17 %g_tTex1df4 +%78 = OpLoad %21 %g_sSamp +%79 = OpSampledImage %25 %77 %78 +%82 = OpImageSampleImplicitLod %v4float %79 %60 +%83 = OpLoad %17 %g_tTex1df4 +%84 = OpLoad %21 %g_sSamp +%85 = OpSampledImage %25 %83 %84 +%88 = OpImageSampleImplicitLod %v4float %85 %63 +%91 = OpFAdd %v4float %82 %88 +%94 = OpFMul %v4float %91 %116 +OpStore %_entryPointOutput_Color %94 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs0 + defs1 + func_orig, defs0 + relax_decos + defs1 + func_orig, true, + true); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/remove_dontinline_test.cpp b/test/opt/remove_dontinline_test.cpp new file mode 100644 index 00000000..c5425e8c --- /dev/null +++ b/test/opt/remove_dontinline_test.cpp @@ -0,0 +1,127 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using StrengthReductionBasicTest = PassTest<::testing::Test>; + +TEST_F(StrengthReductionBasicTest, ClearDontInline) { + const std::vector text = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + "%void = OpTypeVoid", + "%4 = OpTypeFunction %void", +"; CHECK: OpFunction %void None", + "%main = OpFunction %void DontInline %4", + "%8 = OpLabel", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + + SinglePassRunAndMatch(JoinAllInsts(text), true); +} + +TEST_F(StrengthReductionBasicTest, LeaveUnchanged1) { + const std::vector text = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + "%void = OpTypeVoid", + "%4 = OpTypeFunction %void", + "%main = OpFunction %void None %4", + "%8 = OpLabel", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + + EXPECT_EQ(Pass::Status::SuccessWithoutChange, + std::get<1>(SinglePassRunAndDisassemble( + JoinAllInsts(text), false, true))); +} + +TEST_F(StrengthReductionBasicTest, LeaveUnchanged2) { + const std::vector text = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + "%void = OpTypeVoid", + "%4 = OpTypeFunction %void", + "%main = OpFunction %void Inline %4", + "%8 = OpLabel", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + + EXPECT_EQ(Pass::Status::SuccessWithoutChange, + std::get<1>(SinglePassRunAndDisassemble( + JoinAllInsts(text), false, true))); +} + +TEST_F(StrengthReductionBasicTest, ClearMultipleDontInline) { + const std::vector text = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main1 \"main1\"", + "OpEntryPoint Vertex %main2 \"main2\"", + "OpEntryPoint Vertex %main3 \"main3\"", + "OpEntryPoint Vertex %main4 \"main4\"", + "%void = OpTypeVoid", + "%4 = OpTypeFunction %void", + "; CHECK: OpFunction %void None", + "%main1 = OpFunction %void DontInline %4", + "%8 = OpLabel", + "OpReturn", + "OpFunctionEnd", + "; CHECK: OpFunction %void Inline", + "%main2 = OpFunction %void Inline %4", + "%9 = OpLabel", + "OpReturn", + "OpFunctionEnd", + "; CHECK: OpFunction %void Pure", + "%main3 = OpFunction %void DontInline|Pure %4", + "%10 = OpLabel", + "OpReturn", + "OpFunctionEnd", + "; CHECK: OpFunction %void None", + "%main4 = OpFunction %void None %4", + "%11 = OpLabel", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + + SinglePassRunAndMatch(JoinAllInsts(text), true); +} +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/test/opt/remove_unused_interface_variables_test.cpp b/test/opt/remove_unused_interface_variables_test.cpp new file mode 100644 index 00000000..ddf027f1 --- /dev/null +++ b/test/opt/remove_unused_interface_variables_test.cpp @@ -0,0 +1,185 @@ +// Copyright (c) 2021 ZHOU He +// +// 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 "gmock/gmock.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using RemoveUnusedInterfaceVariablesTest = PassTest<::testing::Test>; + +static const std::string expected = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %_Z5func1v "_Z5func1v" %out_var_SV_TARGET +OpEntryPoint Fragment %_Z5func2v "_Z5func2v" %out_var_SV_TARGET_0 +OpExecutionMode %_Z5func1v OriginUpperLeft +OpExecutionMode %_Z5func2v OriginUpperLeft +OpSource HLSL 630 +OpName %type_cba "type.cba" +OpMemberName %type_cba 0 "color" +OpName %cba "cba" +OpName %out_var_SV_TARGET "out.var.SV_TARGET" +OpName %out_var_SV_TARGET_0 "out.var.SV_TARGET" +OpName %_Z5func1v "_Z5func1v" +OpName %_Z5func2v "_Z5func2v" +OpDecorate %out_var_SV_TARGET Location 0 +OpDecorate %out_var_SV_TARGET_0 Location 0 +OpDecorate %cba DescriptorSet 0 +OpDecorate %cba Binding 0 +OpMemberDecorate %type_cba 0 Offset 0 +OpDecorate %type_cba Block +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%type_cba = OpTypeStruct %v4float +%_ptr_Uniform_type_cba = OpTypePointer Uniform %type_cba +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%14 = OpTypeFunction %void +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%cba = OpVariable %_ptr_Uniform_type_cba Uniform +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output +%out_var_SV_TARGET_0 = OpVariable %_ptr_Output_v4float Output +%_Z5func1v = OpFunction %void None %14 +%16 = OpLabel +%17 = OpAccessChain %_ptr_Uniform_v4float %cba %int_0 +%18 = OpLoad %v4float %17 +OpStore %out_var_SV_TARGET %18 +OpReturn +OpFunctionEnd +%_Z5func2v = OpFunction %void None %14 +%19 = OpLabel +%20 = OpAccessChain %_ptr_Uniform_v4float %cba %int_0 +%21 = OpLoad %v4float %20 +OpStore %out_var_SV_TARGET_0 %21 +OpReturn +OpFunctionEnd +)"; + +TEST_F(RemoveUnusedInterfaceVariablesTest, RemoveUnusedVariable) { + const std::string text = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %_Z5func1v "_Z5func1v" %out_var_SV_TARGET %out_var_SV_TARGET_0 +OpEntryPoint Fragment %_Z5func2v "_Z5func2v" %out_var_SV_TARGET %out_var_SV_TARGET_0 +OpExecutionMode %_Z5func1v OriginUpperLeft +OpExecutionMode %_Z5func2v OriginUpperLeft +OpSource HLSL 630 +OpName %type_cba "type.cba" +OpMemberName %type_cba 0 "color" +OpName %cba "cba" +OpName %out_var_SV_TARGET "out.var.SV_TARGET" +OpName %out_var_SV_TARGET_0 "out.var.SV_TARGET" +OpName %_Z5func1v "_Z5func1v" +OpName %_Z5func2v "_Z5func2v" +OpDecorate %out_var_SV_TARGET Location 0 +OpDecorate %out_var_SV_TARGET_0 Location 0 +OpDecorate %cba DescriptorSet 0 +OpDecorate %cba Binding 0 +OpMemberDecorate %type_cba 0 Offset 0 +OpDecorate %type_cba Block +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%type_cba = OpTypeStruct %v4float +%_ptr_Uniform_type_cba = OpTypePointer Uniform %type_cba +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%14 = OpTypeFunction %void +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%cba = OpVariable %_ptr_Uniform_type_cba Uniform +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output +%out_var_SV_TARGET_0 = OpVariable %_ptr_Output_v4float Output +%_Z5func1v = OpFunction %void None %14 +%16 = OpLabel +%17 = OpAccessChain %_ptr_Uniform_v4float %cba %int_0 +%18 = OpLoad %v4float %17 +OpStore %out_var_SV_TARGET %18 +OpReturn +OpFunctionEnd +%_Z5func2v = OpFunction %void None %14 +%19 = OpLabel +%20 = OpAccessChain %_ptr_Uniform_v4float %cba %int_0 +%21 = OpLoad %v4float %20 +OpStore %out_var_SV_TARGET_0 %21 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, expected, + true, true); +} + +TEST_F(RemoveUnusedInterfaceVariablesTest, FixMissingVariable) { + const std::string text = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %_Z5func1v "_Z5func1v" +OpEntryPoint Fragment %_Z5func2v "_Z5func2v" +OpExecutionMode %_Z5func1v OriginUpperLeft +OpExecutionMode %_Z5func2v OriginUpperLeft +OpSource HLSL 630 +OpName %type_cba "type.cba" +OpMemberName %type_cba 0 "color" +OpName %cba "cba" +OpName %out_var_SV_TARGET "out.var.SV_TARGET" +OpName %out_var_SV_TARGET_0 "out.var.SV_TARGET" +OpName %_Z5func1v "_Z5func1v" +OpName %_Z5func2v "_Z5func2v" +OpDecorate %out_var_SV_TARGET Location 0 +OpDecorate %out_var_SV_TARGET_0 Location 0 +OpDecorate %cba DescriptorSet 0 +OpDecorate %cba Binding 0 +OpMemberDecorate %type_cba 0 Offset 0 +OpDecorate %type_cba Block +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%type_cba = OpTypeStruct %v4float +%_ptr_Uniform_type_cba = OpTypePointer Uniform %type_cba +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%14 = OpTypeFunction %void +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%cba = OpVariable %_ptr_Uniform_type_cba Uniform +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output +%out_var_SV_TARGET_0 = OpVariable %_ptr_Output_v4float Output +%_Z5func1v = OpFunction %void None %14 +%16 = OpLabel +%17 = OpAccessChain %_ptr_Uniform_v4float %cba %int_0 +%18 = OpLoad %v4float %17 +OpStore %out_var_SV_TARGET %18 +OpReturn +OpFunctionEnd +%_Z5func2v = OpFunction %void None %14 +%19 = OpLabel +%20 = OpAccessChain %_ptr_Uniform_v4float %cba %int_0 +%21 = OpLoad %v4float %20 +OpStore %out_var_SV_TARGET_0 %21 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, expected, + true, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/test/opt/replace_desc_array_access_using_var_index_test.cpp b/test/opt/replace_desc_array_access_using_var_index_test.cpp new file mode 100644 index 00000000..9ab9eb11 --- /dev/null +++ b/test/opt/replace_desc_array_access_using_var_index_test.cpp @@ -0,0 +1,576 @@ +// Copyright (c) 2021 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 + +#include "gmock/gmock.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ReplaceDescArrayAccessUsingVarIndexTest = PassTest<::testing::Test>; + +TEST_F(ReplaceDescArrayAccessUsingVarIndexTest, + ReplaceAccessChainToTextureArray) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %psmain "psmain" %gl_FragCoord %in_var_INSTANCEID %out_var_SV_TARGET + OpExecutionMode %psmain OriginUpperLeft + OpSource HLSL 600 + OpName %type_sampler "type.sampler" + OpName %Sampler0 "Sampler0" + OpName %type_2d_image "type.2d.image" + OpName %Tex0 "Tex0" + OpName %in_var_INSTANCEID "in.var.INSTANCEID" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %psmain "psmain" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %in_var_INSTANCEID Flat + OpDecorate %in_var_INSTANCEID Location 0 + OpDecorate %out_var_SV_TARGET Location 0 + OpDecorate %Sampler0 DescriptorSet 0 + OpDecorate %Sampler0 Binding 1 + OpDecorate %Tex0 DescriptorSet 0 + OpDecorate %Tex0 Binding 2 + %bool = OpTypeBool +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 0 Unknown +%_arr_type_2d_image_uint_3 = OpTypeArray %type_2d_image %uint_3 +%_ptr_UniformConstant__arr_type_2d_image_uint_3 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_3 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %21 = OpTypeFunction %void +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %v2float = OpTypeVector %float 2 + %v2uint = OpTypeVector %uint 2 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %27 = OpConstantComposite %v2uint %uint_0 %uint_1 +%type_sampled_image = OpTypeSampledImage %type_2d_image + %Sampler0 = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %Tex0 = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_3 UniformConstant +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%in_var_INSTANCEID = OpVariable %_ptr_Input_uint Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %uint_2 = OpConstant %uint 2 + %66 = OpConstantNull %v4float + +; CHECK: [[null_value:%\w+]] = OpConstantNull %v4float + + %psmain = OpFunction %void None %21 + %39 = OpLabel + %29 = OpLoad %v4float %gl_FragCoord + %30 = OpLoad %uint %in_var_INSTANCEID + %37 = OpIEqual %bool %30 %uint_2 + OpSelectionMerge %38 None + OpBranchConditional %37 %28 %40 + +; CHECK: [[var_index:%\w+]] = OpLoad %uint %in_var_INSTANCEID +; CHECK: OpSelectionMerge [[cond_branch_merge:%\w+]] None +; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} [[bb_cond_br:%\w+]] + + %28 = OpLabel + %31 = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %30 + %32 = OpLoad %type_2d_image %31 + OpImageWrite %32 %27 %29 + +; CHECK: OpSelectionMerge [[merge:%\w+]] None +; CHECK: OpSwitch [[var_index]] [[default:%\w+]] 0 [[case0:%\w+]] 1 [[case1:%\w+]] 2 [[case2:%\w+]] +; CHECK: [[case0]] = OpLabel +; CHECK: OpAccessChain +; CHECK: OpLoad +; CHECK: OpImageWrite +; CHECK: OpBranch [[merge]] +; CHECK: [[case1]] = OpLabel +; CHECK: OpAccessChain +; CHECK: OpLoad +; CHECK: OpImageWrite +; CHECK: OpBranch [[merge]] +; CHECK: [[case2]] = OpLabel +; CHECK: OpAccessChain +; CHECK: OpLoad +; CHECK: OpImageWrite +; CHECK: OpBranch [[merge]] +; CHECK: [[default]] = OpLabel +; CHECK: OpBranch [[merge]] +; CHECK: [[merge]] = OpLabel + + %33 = OpLoad %type_sampler %Sampler0 + %34 = OpVectorShuffle %v2float %29 %29 0 1 + %35 = OpSampledImage %type_sampled_image %32 %33 + %36 = OpImageSampleImplicitLod %v4float %35 %34 None + +; CHECK: OpSelectionMerge [[merge:%\w+]] None +; CHECK: OpSwitch [[var_index]] [[default:%\w+]] 0 [[case0:%\w+]] 1 [[case1:%\w+]] 2 [[case2:%\w+]] +; CHECK: [[case0]] = OpLabel +; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_0 +; CHECK: [[sam:%\w+]] = OpLoad %type_sampler %Sampler0 +; CHECK: [[img:%\w+]] = OpLoad %type_2d_image [[ac]] +; CHECK: [[sampledImg:%\w+]] = OpSampledImage %type_sampled_image [[img]] [[sam]] +; CHECK: [[value0:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg]] +; CHECK: OpBranch [[merge]] +; CHECK: [[case1]] = OpLabel +; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_1 +; CHECK: [[sam:%\w+]] = OpLoad %type_sampler %Sampler0 +; CHECK: [[img:%\w+]] = OpLoad %type_2d_image [[ac]] +; CHECK: [[sampledImg:%\w+]] = OpSampledImage %type_sampled_image [[img]] [[sam]] +; CHECK: [[value1:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg]] +; CHECK: OpBranch [[merge]] +; CHECK: [[case2]] = OpLabel +; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_2 +; CHECK: [[sam:%\w+]] = OpLoad %type_sampler %Sampler0 +; CHECK: [[img:%\w+]] = OpLoad %type_2d_image [[ac]] +; CHECK: [[sampledImg:%\w+]] = OpSampledImage %type_sampled_image [[img]] [[sam]] +; CHECK: [[value2:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg]] +; CHECK: OpBranch [[merge]] +; CHECK: [[default]] = OpLabel +; CHECK: OpBranch [[merge]] +; CHECK: [[merge]] = OpLabel +; CHECK: [[phi0:%\w+]] = OpPhi %v4float [[value0]] [[case0]] [[value1]] [[case1]] [[value2]] [[case2]] [[null_value]] [[default]] + + OpBranch %38 + %40 = OpLabel + OpBranch %38 + %38 = OpLabel + %41 = OpPhi %v4float %36 %28 %29 %40 + +; CHECK: OpBranch [[cond_branch_merge]] +; CHECK: [[bb_cond_br]] = OpLabel +; CHECK: OpBranch [[cond_branch_merge]] +; CHECK: [[cond_branch_merge]] = OpLabel +; CHECK: [[phi1:%\w+]] = OpPhi %v4float [[phi0]] [[merge]] {{%\w+}} [[bb_cond_br]] +; CHECK: OpStore {{%\w+}} [[phi1]] + + OpStore %out_var_SV_TARGET %41 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ReplaceDescArrayAccessUsingVarIndexTest, + ReplaceAccessChainToTextureArrayAndSamplerArray) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %psmain "psmain" %gl_FragCoord %in_var_INSTANCEID %out_var_SV_TARGET + OpExecutionMode %psmain OriginUpperLeft + OpSource HLSL 600 + OpName %type_sampler "type.sampler" + OpName %Sampler0 "Sampler0" + OpName %type_2d_image "type.2d.image" + OpName %Tex0 "Tex0" + OpName %in_var_INSTANCEID "in.var.INSTANCEID" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %psmain "psmain" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %in_var_INSTANCEID Flat + OpDecorate %in_var_INSTANCEID Location 0 + OpDecorate %out_var_SV_TARGET Location 0 + OpDecorate %Sampler0 DescriptorSet 0 + OpDecorate %Sampler0 Binding 1 + OpDecorate %Tex0 DescriptorSet 0 + OpDecorate %Tex0 Binding 2 +%type_sampler = OpTypeSampler + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler +%_arr_type_sampler_uint_2 = OpTypeArray %type_sampler %uint_2 +%_ptr_UniformConstant__arr_type_sampler_uint_2 = OpTypePointer UniformConstant %_arr_type_sampler_uint_2 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 0 Unknown +%_arr_type_2d_image_uint_2 = OpTypeArray %type_2d_image %uint_2 +%_ptr_UniformConstant__arr_type_2d_image_uint_2 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_2 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %21 = OpTypeFunction %void +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %v2float = OpTypeVector %float 2 + %v2uint = OpTypeVector %uint 2 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %27 = OpConstantComposite %v2uint %uint_0 %uint_1 +%type_sampled_image = OpTypeSampledImage %type_2d_image + %Sampler0 = OpVariable %_ptr_UniformConstant__arr_type_sampler_uint_2 UniformConstant + %Tex0 = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_2 UniformConstant +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%in_var_INSTANCEID = OpVariable %_ptr_Input_uint Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %66 = OpConstantNull %v4float + %psmain = OpFunction %void None %21 + %28 = OpLabel + %29 = OpLoad %v4float %gl_FragCoord + %30 = OpLoad %uint %in_var_INSTANCEID + %31 = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %30 + %32 = OpLoad %type_2d_image %31 + OpImageWrite %32 %27 %29 + +; CHECK: [[null_value:%\w+]] = OpConstantNull %v4float + +; CHECK: [[var_index:%\w+]] = OpLoad %uint %in_var_INSTANCEID +; CHECK: OpSelectionMerge [[merge:%\w+]] None +; CHECK: OpSwitch [[var_index]] [[default:%\w+]] 0 [[case0:%\w+]] 1 [[case1:%\w+]] +; CHECK: [[case0]] = OpLabel +; CHECK: OpAccessChain +; CHECK: OpLoad +; CHECK: OpImageWrite +; CHECK: OpBranch [[merge]] +; CHECK: [[case1]] = OpLabel +; CHECK: OpAccessChain +; CHECK: OpLoad +; CHECK: OpImageWrite +; CHECK: OpBranch [[merge]] +; CHECK: [[default]] = OpLabel +; CHECK: OpBranch [[merge]] +; CHECK: [[merge]] = OpLabel + + %33 = OpAccessChain %_ptr_UniformConstant_type_sampler %Sampler0 %30 + %37 = OpLoad %type_sampler %33 + %34 = OpVectorShuffle %v2float %29 %29 0 1 + %35 = OpSampledImage %type_sampled_image %32 %37 + %36 = OpImageSampleImplicitLod %v4float %35 %34 None + +; SPIR-V instructions to be replaced (will be killed by ADCE) +; CHECK: OpSelectionMerge +; CHECK: OpSwitch + +; CHECK: OpSelectionMerge [[merge_sampler:%\w+]] None +; CHECK: OpSwitch [[var_index]] [[default_sampler:%\w+]] 0 [[case_sampler0:%\w+]] 1 [[case_sampler1:%\w+]] + +; CHECK: [[case_sampler0]] = OpLabel +; CHECK: OpSelectionMerge [[merge_texture0:%\w+]] None +; CHECK: OpSwitch [[var_index]] [[default_texture:%\w+]] 0 [[case_texture0:%\w+]] 1 [[case_texture1:%\w+]] +; CHECK: [[case_texture0]] = OpLabel +; CHECK: [[pt0:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_0 +; CHECK: [[ps0:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_sampler %Sampler0 %uint_0 +; CHECK: [[s0:%\w+]] = OpLoad %type_sampler [[ps0]] +; CHECK: [[t0:%\w+]] = OpLoad %type_2d_image [[pt0]] +; CHECK: [[sampledImg0:%\w+]] = OpSampledImage %type_sampled_image [[t0]] [[s0]] +; CHECK: [[value0:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg0]] +; CHECK: OpBranch [[merge_texture0]] +; CHECK: [[case_texture1]] = OpLabel +; CHECK: [[pt1:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_1 +; CHECK: [[ps0:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_sampler %Sampler0 %uint_0 +; CHECK: [[s0:%\w+]] = OpLoad %type_sampler [[ps0]] +; CHECK: [[t1:%\w+]] = OpLoad %type_2d_image [[pt1]] +; CHECK: [[sampledImg1:%\w+]] = OpSampledImage %type_sampled_image [[t1]] [[s0]] +; CHECK: [[value1:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg1]] +; CHECK: OpBranch [[merge_texture0]] +; CHECK: [[default_texture]] = OpLabel +; CHECK: OpBranch [[merge_texture0]] +; CHECK: [[merge_texture0]] = OpLabel +; CHECK: [[phi0:%\w+]] = OpPhi %v4float [[value0]] [[case_texture0]] [[value1]] [[case_texture1]] [[null_value]] [[default_texture]] +; CHECK: OpBranch [[merge_sampler]] + +; CHECK: [[case_sampler1]] = OpLabel +; CHECK: OpSelectionMerge [[merge_texture1:%\w+]] None +; CHECK: OpSwitch [[var_index]] [[default_texture:%\w+]] 0 [[case_texture0:%\w+]] 1 [[case_texture1:%\w+]] +; CHECK: [[case_texture0]] = OpLabel +; CHECK: [[pt0:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_0 +; CHECK: [[ps1:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_sampler %Sampler0 %uint_1 +; CHECK: [[s1:%\w+]] = OpLoad %type_sampler [[ps1]] +; CHECK: [[t0:%\w+]] = OpLoad %type_2d_image [[pt0]] +; CHECK: [[sampledImg0:%\w+]] = OpSampledImage %type_sampled_image [[t0]] [[s1]] +; CHECK: [[value0:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg0]] +; CHECK: OpBranch [[merge_texture1]] +; CHECK: [[case_texture1]] = OpLabel +; CHECK: [[pt1:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_1 +; CHECK: [[ps1:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_sampler %Sampler0 %uint_1 +; CHECK: [[s1:%\w+]] = OpLoad %type_sampler [[ps1]] +; CHECK: [[t1:%\w+]] = OpLoad %type_2d_image [[pt1]] +; CHECK: [[sampledImg1:%\w+]] = OpSampledImage %type_sampled_image [[t1]] [[s1]] +; CHECK: [[value1:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg1]] +; CHECK: OpBranch [[merge_texture1]] +; CHECK: [[default_texture]] = OpLabel +; CHECK: OpBranch [[merge_texture1]] +; CHECK: [[merge_texture1]] = OpLabel +; CHECK: [[phi1:%\w+]] = OpPhi %v4float [[value0]] [[case_texture0]] [[value1]] [[case_texture1]] [[null_value]] [[default_texture]] + +; CHECK: [[default_sampler]] = OpLabel +; CHECK: OpBranch [[merge_sampler]] +; CHECK: [[merge_sampler]] = OpLabel +; CHECK: OpPhi %v4float [[phi0]] [[merge_texture0]] [[phi1]] [[merge_texture1]] [[null_value]] [[default_sampler]] +; CHECK: OpStore + + OpStore %out_var_SV_TARGET %36 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ReplaceDescArrayAccessUsingVarIndexTest, + ReplaceAccessChainToTextureArrayWithSingleElement) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %psmain "psmain" %gl_FragCoord %in_var_INSTANCEID %out_var_SV_TARGET + OpExecutionMode %psmain OriginUpperLeft + OpSource HLSL 600 + OpName %type_sampler "type.sampler" + OpName %Sampler0 "Sampler0" + OpName %type_2d_image "type.2d.image" + OpName %Tex0 "Tex0" + OpName %in_var_INSTANCEID "in.var.INSTANCEID" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %psmain "psmain" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %in_var_INSTANCEID Flat + OpDecorate %in_var_INSTANCEID Location 0 + OpDecorate %out_var_SV_TARGET Location 0 + OpDecorate %Sampler0 DescriptorSet 0 + OpDecorate %Sampler0 Binding 1 + OpDecorate %Tex0 DescriptorSet 0 + OpDecorate %Tex0 Binding 2 +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 0 Unknown +%_arr_type_2d_image_uint_1 = OpTypeArray %type_2d_image %uint_1 +%_ptr_UniformConstant__arr_type_2d_image_uint_1 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_1 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %21 = OpTypeFunction %void +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %v2float = OpTypeVector %float 2 + %v2uint = OpTypeVector %uint 2 + %uint_0 = OpConstant %uint 0 + %27 = OpConstantComposite %v2uint %uint_0 %uint_1 +%type_sampled_image = OpTypeSampledImage %type_2d_image + %Sampler0 = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %Tex0 = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_1 UniformConstant +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%in_var_INSTANCEID = OpVariable %_ptr_Input_uint Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %uint_2 = OpConstant %uint 2 + %66 = OpConstantNull %v4float + %psmain = OpFunction %void None %21 + %28 = OpLabel + %29 = OpLoad %v4float %gl_FragCoord + %30 = OpLoad %uint %in_var_INSTANCEID + %31 = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %30 + %32 = OpLoad %type_2d_image %31 + OpImageWrite %32 %27 %29 + +; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_0 +; CHECK-NOT: OpAccessChain +; CHECK-NOT: OpSwitch +; CHECK-NOT: OpPhi + + %33 = OpLoad %type_sampler %Sampler0 + %34 = OpVectorShuffle %v2float %29 %29 0 1 + %35 = OpSampledImage %type_sampled_image %32 %33 + %36 = OpImageSampleImplicitLod %v4float %35 %34 None + + OpStore %out_var_SV_TARGET %36 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ReplaceDescArrayAccessUsingVarIndexTest, ReplaceMultipleAccessChains) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "TestFragment" %2 + OpExecutionMode %1 OriginUpperLeft + OpName %11 "type.ConstantBuffer.TestStruct" + OpMemberName %11 0 "val1" + OpMemberName %11 1 "val2" + OpName %3 "TestResources" + OpName %13 "type.2d.image" + OpName %4 "OutBuffer" + OpName %2 "in.var.SV_INSTANCEID" + OpName %1 "TestFragment" + OpDecorate %2 Flat + OpDecorate %2 Location 0 + OpDecorate %3 DescriptorSet 0 + OpDecorate %3 Binding 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 1 + OpMemberDecorate %11 0 Offset 0 + OpMemberDecorate %11 1 Offset 4 + OpDecorate %11 Block + %9 = OpTypeInt 32 0 + %10 = OpConstant %9 2 + %11 = OpTypeStruct %9 %9 + %8 = OpTypeArray %11 %10 + %7 = OpTypePointer Uniform %8 + %13 = OpTypeImage %9 2D 2 0 0 2 R32ui + %12 = OpTypePointer UniformConstant %13 + %14 = OpTypePointer Input %9 + %15 = OpTypeVoid + %16 = OpTypeFunction %15 + %40 = OpTypeVector %9 2 + %3 = OpVariable %7 Uniform + %4 = OpVariable %12 UniformConstant + %2 = OpVariable %14 Input + %57 = OpTypePointer Uniform %11 + %61 = OpTypePointer Uniform %9 + %62 = OpConstant %9 0 + %1 = OpFunction %15 None %16 + %17 = OpLabel + %20 = OpLoad %9 %2 + %47 = OpAccessChain %57 %3 %20 + %63 = OpAccessChain %61 %47 %62 + %64 = OpLoad %9 %63 + +; CHECK: [[null_value:%\w+]] = OpConstantNull %uint + +; CHECK: [[var_index:%\w+]] = OpLoad %uint %in_var_SV_INSTANCEID +; CHECK: OpSelectionMerge [[merge:%\w+]] None +; CHECK: OpSwitch [[var_index]] [[default:%\w+]] 0 [[case0:%\w+]] 1 [[case1:%\w+]] +; CHECK: [[case0]] = OpLabel +; CHECK: OpAccessChain +; CHECK: OpAccessChain +; CHECK: [[result0:%\w+]] = OpLoad +; CHECK: OpBranch [[merge]] +; CHECK: [[case1]] = OpLabel +; CHECK: OpAccessChain +; CHECK: OpAccessChain +; CHECK: [[result1:%\w+]] = OpLoad +; CHECK: OpBranch [[merge]] +; CHECK: [[default]] = OpLabel +; CHECK: OpBranch [[merge]] +; CHECK: [[merge]] = OpLabel +; CHECK: OpPhi %uint [[result0]] [[case0]] [[result1]] [[case1]] [[null_value]] [[default]] + + %55 = OpCompositeConstruct %40 %20 %20 + %56 = OpLoad %13 %4 + OpImageWrite %56 %55 %64 None + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ReplaceDescArrayAccessUsingVarIndexTest, + ReplaceAccessChainToTextureArrayWithNonUniformIndex) { + const std::string text = R"( + OpCapability Shader + OpCapability ShaderNonUniform + OpCapability SampledImageArrayNonUniformIndexing + OpExtension "SPV_EXT_descriptor_indexing" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %PSMain "PSMain" %in_var_TEXCOORD0 %in_var_MATERIAL_ID %out_var_SV_TARGET + OpExecutionMode %PSMain OriginUpperLeft + OpSource HLSL 610 + OpName %type_sampler "type.sampler" + OpName %sampler_ "sampler_" + OpName %type_2d_image "type.2d.image" + OpName %texture_2d "texture_2d" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %in_var_MATERIAL_ID "in.var.MATERIAL_ID" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %PSMain "PSMain" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %in_var_MATERIAL_ID Flat + OpDecorate %in_var_TEXCOORD0 Location 0 + OpDecorate %in_var_MATERIAL_ID Location 1 + OpDecorate %out_var_SV_TARGET Location 0 + OpDecorate %sampler_ DescriptorSet 1 + OpDecorate %sampler_ Binding 1 + OpDecorate %texture_2d DescriptorSet 0 + OpDecorate %texture_2d Binding 0 + +; CHECK: OpDecorate [[v0:%\w+]] NonUniform +; CHECK: OpDecorate [[v1:%\w+]] NonUniform +; CHECK: OpDecorate [[v2:%\w+]] NonUniform +; CHECK: OpDecorate [[v3:%\w+]] NonUniform + + OpDecorate %10 NonUniform + OpDecorate %11 NonUniform + OpDecorate %12 NonUniform + OpDecorate %13 NonUniform +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_arr_type_2d_image_uint_4 = OpTypeArray %type_2d_image %uint_4 +%_ptr_UniformConstant__arr_type_2d_image_uint_4 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_4 + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%_ptr_Input_uint = OpTypePointer Input %uint + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %26 = OpTypeFunction %void +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampled_image = OpTypeSampledImage %type_2d_image + %sampler_ = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %texture_2d = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_4 UniformConstant +%in_var_TEXCOORD0 = OpVariable %_ptr_Input_v2float Input +%in_var_MATERIAL_ID = OpVariable %_ptr_Input_uint Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + +; CHECK: %uint_0 = OpConstant %uint 0 +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %uint_3 = OpConstant %uint 3 + + %PSMain = OpFunction %void None %26 + %28 = OpLabel + %29 = OpLoad %v2float %in_var_TEXCOORD0 + %30 = OpLoad %uint %in_var_MATERIAL_ID +; CHECK: [[v0]] = OpCopyObject %uint {{%\w+}} + %10 = OpCopyObject %uint %30 +; CHECK: [[v1]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %texture_2d [[v0]] + %11 = OpAccessChain %_ptr_UniformConstant_type_2d_image %texture_2d %10 +; CHECK: [[v2]] = OpLoad %type_2d_image [[v1]] + %12 = OpLoad %type_2d_image %11 + %31 = OpLoad %type_sampler %sampler_ +; CHECK: [[v3]] = OpSampledImage %type_sampled_image [[v2]] {{%\w+}} + %13 = OpSampledImage %type_sampled_image %12 %31 + %32 = OpImageSampleImplicitLod %v4float %13 %29 None + OpStore %out_var_SV_TARGET %32 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/test/opt/scalar_analysis.cpp b/test/opt/scalar_analysis.cpp index 598d8c7b..df2aa8f8 100644 --- a/test/opt/scalar_analysis.cpp +++ b/test/opt/scalar_analysis.cpp @@ -1202,7 +1202,6 @@ TEST_F(ScalarAnalysisTest, InductionWithVariantStep) { EXPECT_EQ(phis.size(), 2u); SENode* phi_node_1 = analysis.AnalyzeInstruction(phis[0]); SENode* phi_node_2 = analysis.AnalyzeInstruction(phis[1]); - phi_node_1->DumpDot(std::cout, true); EXPECT_NE(phi_node_1, nullptr); EXPECT_NE(phi_node_2, nullptr); diff --git a/test/opt/scalar_replacement_test.cpp b/test/opt/scalar_replacement_test.cpp index 8115f5fb..0c97c80b 100644 --- a/test/opt/scalar_replacement_test.cpp +++ b/test/opt/scalar_replacement_test.cpp @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "source/opt/scalar_replacement_pass.h" + #include #include "gmock/gmock.h" @@ -23,6 +25,18 @@ namespace spvtools { namespace opt { namespace { +using ScalarReplacementPassName = ::testing::Test; + +TEST_F(ScalarReplacementPassName, Default) { + auto srp = ScalarReplacementPass(); + EXPECT_STREQ(srp.name(), "scalar-replacement=100"); +} + +TEST_F(ScalarReplacementPassName, Large) { + auto srp = ScalarReplacementPass(0xffffffffu); + EXPECT_STREQ(srp.name(), "scalar-replacement=4294967295"); +} + using ScalarReplacementTest = PassTest<::testing::Test>; TEST_F(ScalarReplacementTest, SimpleStruct) { @@ -470,9 +484,9 @@ TEST_F(ScalarReplacementTest, NonUniformCompositeInitialization) { ; CHECK: [[const_array:%\w+]] = OpConstantComposite [[array]] ; CHECK: [[const_matrix:%\w+]] = OpConstantNull [[matrix]] ; CHECK: [[const_struct1:%\w+]] = OpConstantComposite [[struct1]] -; CHECK: OpConstantNull [[uint]] -; CHECK: OpConstantNull [[vector]] -; CHECK: OpConstantNull [[long]] +; CHECK: OpUndef [[uint]] +; CHECK: OpUndef [[vector]] +; CHECK: OpUndef [[long]] ; CHECK: OpFunction ; CHECK-NOT: OpVariable [[struct2_ptr]] Function ; CHECK: OpVariable [[uint_ptr]] Function @@ -654,11 +668,10 @@ TEST_F(ScalarReplacementTest, ReplaceWholeLoadCopyMemoryAccess) { ; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 ; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[uint]] ; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]] -; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0 -; CHECK: [[null:%\w+]] = OpConstantNull [[uint]] +; CHECK: [[undef:%\w+]] = OpUndef [[uint]] ; CHECK: [[var0:%\w+]] = OpVariable [[uint_ptr]] Function ; CHECK: [[l0:%\w+]] = OpLoad [[uint]] [[var0]] Nontemporal -; CHECK: OpCompositeConstruct [[struct1]] [[l0]] [[null]] +; CHECK: OpCompositeConstruct [[struct1]] [[l0]] [[undef]] ; OpCapability Shader OpCapability Linkage @@ -1267,16 +1280,16 @@ TEST_F(ScalarReplacementTest, ReplaceWholeLoadAndStore) { ; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[uint]] ; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]] ; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0 -; CHECK: [[null:%\w+]] = OpConstantNull [[uint]] +; CHECK: [[undef:%\w+]] = OpUndef [[uint]] ; CHECK: [[var0:%\w+]] = OpVariable [[uint_ptr]] Function ; CHECK: [[var1:%\w+]] = OpVariable [[uint_ptr]] Function ; CHECK-NOT: OpVariable ; CHECK: [[l0:%\w+]] = OpLoad [[uint]] [[var0]] -; CHECK: [[c0:%\w+]] = OpCompositeConstruct [[struct1]] [[l0]] [[null]] +; CHECK: [[c0:%\w+]] = OpCompositeConstruct [[struct1]] [[l0]] [[undef]] ; CHECK: [[e0:%\w+]] = OpCompositeExtract [[uint]] [[c0]] 0 ; CHECK: OpStore [[var1]] [[e0]] ; CHECK: [[l1:%\w+]] = OpLoad [[uint]] [[var1]] -; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[null]] +; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[undef]] ; CHECK: [[e1:%\w+]] = OpCompositeExtract [[uint]] [[c1]] 0 ; OpCapability Shader @@ -1314,7 +1327,7 @@ TEST_F(ScalarReplacementTest, ReplaceWholeLoadAndStore2) { ; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[uint]] ; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]] ; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0 -; CHECK: [[null:%\w+]] = OpConstantNull [[uint]] +; CHECK: [[undef:%\w+]] = OpUndef [[uint]] ; CHECK: [[var1:%\w+]] = OpVariable [[uint_ptr]] Function ; CHECK: [[var0a:%\w+]] = OpVariable [[uint_ptr]] Function ; CHECK: [[var0b:%\w+]] = OpVariable [[uint_ptr]] Function @@ -1325,7 +1338,7 @@ TEST_F(ScalarReplacementTest, ReplaceWholeLoadAndStore2) { ; CHECK: [[e0:%\w+]] = OpCompositeExtract [[uint]] [[c0]] 0 ; CHECK: OpStore [[var1]] [[e0]] ; CHECK: [[l1:%\w+]] = OpLoad [[uint]] [[var1]] -; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[null]] +; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[undef]] ; CHECK: [[e1:%\w+]] = OpCompositeExtract [[uint]] [[c1]] 0 ; OpCapability Shader @@ -1362,14 +1375,14 @@ TEST_F(ScalarReplacementTest, CreateAmbiguousNullConstant1) { ; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[struct_member:%\w+]] ; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]] ; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0 -; CHECK: [[null:%\w+]] = OpConstantNull [[struct_member]] +; CHECK: [[undef:%\w+]] = OpUndef [[struct_member]] ; CHECK: [[var0a:%\w+]] = OpVariable [[uint_ptr]] Function ; CHECK: [[var1:%\w+]] = OpVariable [[uint_ptr]] Function ; CHECK: [[var0b:%\w+]] = OpVariable [[uint_ptr]] Function ; CHECK-NOT: OpVariable ; CHECK: OpStore [[var1]] ; CHECK: [[l1:%\w+]] = OpLoad [[uint]] [[var1]] -; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[null]] +; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[undef]] ; CHECK: [[e1:%\w+]] = OpCompositeExtract [[uint]] [[c1]] 0 ; OpCapability Shader @@ -1444,13 +1457,13 @@ TEST_F(ScalarReplacementTest, CreateAmbiguousNullConstant2) { ; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[struct_member:%\w+]] ; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]] ; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0 -; CHECK: [[null:%\w+]] = OpConstantNull [[struct_member]] +; CHECK: [[undef:%\w+]] = OpUndef [[struct_member]] ; CHECK: [[var0a:%\w+]] = OpVariable [[uint_ptr]] Function ; CHECK: [[var1:%\w+]] = OpVariable [[uint_ptr]] Function ; CHECK: [[var0b:%\w+]] = OpVariable [[uint_ptr]] Function ; CHECK: OpStore [[var1]] ; CHECK: [[l1:%\w+]] = OpLoad [[uint]] [[var1]] -; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[null]] +; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[undef]] ; CHECK: [[e1:%\w+]] = OpCompositeExtract [[uint]] [[c1]] 0 ; OpCapability Shader @@ -1935,12 +1948,12 @@ OpName %6 "simple_struct" ; CHECK: [[dbg_local_var:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable ; CHECK: [[deref_expr:%\w+]] = OpExtInst %void [[ext]] DebugExpression [[deref]] ; CHECK: [[repl3:%\w+]] = OpVariable %_ptr_Function_uint Function -; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr]] %int_3 ; CHECK: [[repl2:%\w+]] = OpVariable %_ptr_Function_uint Function -; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_2 ; CHECK: [[repl1:%\w+]] = OpVariable %_ptr_Function_uint Function -; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1 ; CHECK: [[repl0:%\w+]] = OpVariable %_ptr_Function_uint Function +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr]] %int_3 +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_2 +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1 ; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl0]] [[deref_expr]] %int_0 ; CHECK-NOT: DebugDeclare %decl = OpExtInst %1 %ext DebugDeclare %dbg_foo %14 %null_expr @@ -2058,10 +2071,10 @@ OpName %6 "simple_struct" ; CHECK: [[repl2:%\w+]] = OpVariable %_ptr_Function_float Function %float_1 ; CHECK: [[repl1:%\w+]] = OpVariable %_ptr_Function_uint Function %uint_32 ; CHECK: [[repl3:%\w+]] = OpVariable %_ptr_Function_float Function %float_1 +; CHECK: [[repl0:%\w+]] = OpVariable %_ptr_Function_uint Function %uint_32 ; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr]] %int_2 ; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1 %int_0 ; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_1 %int_1 -; CHECK: [[repl0:%\w+]] = OpVariable %_ptr_Function_uint Function %uint_32 ; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl0]] [[deref_expr]] %int_0 ; CHECK-NOT: DebugDeclare %decl = OpExtInst %1 %ext DebugDeclare %dbg_foo %14 %null_expr @@ -2174,12 +2187,12 @@ OpName %6 "simple_struct" ; CHECK: [[dbg_local_var:%\w+]] = OpExtInst %void [[ext:%\w+]] DebugLocalVariable ; CHECK: [[repl3:%\w+]] = OpVariable %_ptr_Function_uint Function -; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr:%\w+]] %int_3 ; CHECK: [[repl2:%\w+]] = OpVariable %_ptr_Function_uint Function -; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_2 ; CHECK: [[repl1:%\w+]] = OpVariable %_ptr_Function_uint Function -; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1 ; CHECK: [[repl0:%\w+]] = OpVariable %_ptr_Function_uint Function +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr:%\w+]] %int_3 +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_2 +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1 ; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl0]] [[deref_expr]] %int_0 OpBranch %20 @@ -2237,6 +2250,66 @@ OpFunctionEnd SinglePassRunAndMatch(text, false); } +TEST_F(ScalarReplacementTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} + +TEST_F(ScalarReplacementTest, UndefImageMember) { + // Test that scalar replacement creates an undef for a type that cannot have + // and OpConstantNull. + const std::string text = R"( +; CHECK: [[image_type:%\w+]] = OpTypeSampledImage {{%\w+}} +; CHECK: [[struct_type:%\w+]] = OpTypeStruct [[image_type]] +; CHECK: [[undef:%\w+]] = OpUndef [[image_type]] +; CHECK: {{%\w+}} = OpCompositeConstruct [[struct_type]] [[undef]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %void = OpTypeVoid + %4 = OpTypeFunction %void + %float = OpTypeFloat 32 + %6 = OpTypeImage %float 2D 0 0 0 1 Unknown + %7 = OpTypeSampledImage %6 + %_struct_8 = OpTypeStruct %7 + %9 = OpTypeFunction %_struct_8 + %10 = OpUndef %_struct_8 +%_ptr_Function__struct_8 = OpTypePointer Function %_struct_8 + %2 = OpFunction %void None %4 + %11 = OpLabel + %16 = OpVariable %_ptr_Function__struct_8 Function + OpStore %16 %10 + %12 = OpLoad %_struct_8 %16 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/set_spec_const_default_value_test.cpp b/test/opt/set_spec_const_default_value_test.cpp index 5e63862e..10f805b6 100644 --- a/test/opt/set_spec_const_default_value_test.cpp +++ b/test/opt/set_spec_const_default_value_test.cpp @@ -618,7 +618,7 @@ INSTANTIATE_TEST_SUITE_P( {"", SpecIdToValueBitPatternMap{}, ""}, // 1. Empty with non-empty values to set. {"", SpecIdToValueBitPatternMap{{1, {100}}, {2, {200}}}, ""}, - // 2. Baisc bool type. + // 2. Basic bool type. { // code "OpDecorate %1 SpecId 100\n" @@ -935,6 +935,98 @@ INSTANTIATE_TEST_SUITE_P( "%2 = OpSpecConstantTrue %bool\n" "%3 = OpSpecConstantTrue %bool\n", }, + // 19. 16-bit signed int type. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%short = OpTypeInt 16 1\n" + "%1 = OpSpecConstant %short 10\n" + "%2 = OpSpecConstant %short 11\n" + "%3 = OpSpecConstant %short 11\n", + // default values + SpecIdToValueBitPatternMap{ + {100, {32767}}, {101, {0xffff}}, {102, {0xffffffd6}}}, + // expected. These are sign-extended + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%short = OpTypeInt 16 1\n" + "%1 = OpSpecConstant %short 32767\n" + "%2 = OpSpecConstant %short -1\n" + "%3 = OpSpecConstant %short -42\n", + }, + // 20. 16-bit unsigned int type. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%ushort = OpTypeInt 16 0\n" + "%1 = OpSpecConstant %ushort 10\n" + "%2 = OpSpecConstant %ushort 11\n" + "%3 = OpSpecConstant %ushort 11\n", + // default values + SpecIdToValueBitPatternMap{ + {100, {32767}}, {101, {0xffff}}, {102, {0xffffffd6}}}, + // expected. Upper bits are always zero. + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%ushort = OpTypeInt 16 0\n" + "%1 = OpSpecConstant %ushort 32767\n" + "%2 = OpSpecConstant %ushort 65535\n" + "%3 = OpSpecConstant %ushort 65494\n", + }, + // 21. 8-bit signed int type. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%char = OpTypeInt 8 1\n" + "%1 = OpSpecConstant %char 10\n" + "%2 = OpSpecConstant %char 11\n" + "%3 = OpSpecConstant %char 11\n", + // default values + SpecIdToValueBitPatternMap{ + {100, {127}}, {101, {128}}, {102, {0xd6}}}, + // expected. These are sign extended + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%char = OpTypeInt 8 1\n" + "%1 = OpSpecConstant %char 127\n" + "%2 = OpSpecConstant %char -128\n" + "%3 = OpSpecConstant %char -42\n", + }, + // 22. 8-bit unsigned int type. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "OpDecorate %4 SpecId 103\n" + "%uchar = OpTypeInt 8 0\n" + "%1 = OpSpecConstant %uchar 10\n" + "%2 = OpSpecConstant %uchar 11\n" + "%3 = OpSpecConstant %uchar 11\n" + "%4 = OpSpecConstant %uchar 11\n", + // default values + SpecIdToValueBitPatternMap{ + {100, {127}}, {101, {128}}, {102, {256}}, {103, {0xffffffd6}}}, + // expected. Upper bits are always zero. + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "OpDecorate %4 SpecId 103\n" + "%uchar = OpTypeInt 8 0\n" + "%1 = OpSpecConstant %uchar 127\n" + "%2 = OpSpecConstant %uchar 128\n" + "%3 = OpSpecConstant %uchar 0\n" + "%4 = OpSpecConstant %uchar 214\n", + }, })); INSTANTIATE_TEST_SUITE_P( diff --git a/test/opt/simplification_test.cpp b/test/opt/simplification_test.cpp index 7a9696ea..7727f567 100644 --- a/test/opt/simplification_test.cpp +++ b/test/opt/simplification_test.cpp @@ -360,6 +360,31 @@ OpFunctionEnd SinglePassRunAndMatch(spirv, true); } +TEST_F(SimplificationTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/spread_volatile_semantics_test.cpp b/test/opt/spread_volatile_semantics_test.cpp new file mode 100644 index 00000000..dbb889c0 --- /dev/null +++ b/test/opt/spread_volatile_semantics_test.cpp @@ -0,0 +1,1267 @@ +// Copyright (c) 2022 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 + +#include "gmock/gmock.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +struct ExecutionModelAndBuiltIn { + const char* execution_model; + const char* built_in; + const bool use_v4uint; +}; + +using AddVolatileDecorationTest = + PassTest<::testing::TestWithParam>; + +TEST_P(AddVolatileDecorationTest, InMain) { + const auto& tc = GetParam(); + const std::string execution_model(tc.execution_model); + const std::string built_in(tc.built_in); + const std::string var_type = + tc.use_v4uint ? "%_ptr_Input_v4uint" : "%_ptr_Input_uint"; + const std::string var_load_type = tc.use_v4uint ? "%v4uint" : "%uint"; + + const std::string text = + std::string(R"(OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint )") + + execution_model + std::string(R"( %main "main" %var +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %main "main" +OpName %fn "fn" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +)") + std::string(R"( +; CHECK: OpDecorate [[var:%\w+]] BuiltIn )") + + built_in + std::string(R"( +; CHECK: OpDecorate [[var]] Volatile +OpDecorate %var BuiltIn )") + built_in + std::string(R"( +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%v4uint = OpTypeVector %uint 4 +%_ptr_Input_v4uint = OpTypePointer Input %v4uint +%var = OpVariable )") + + var_type + std::string(R"( Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%main = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %19 +%load = OpLoad )") + var_load_type + std::string(R"( %var +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +%32 = OpFunctionCall %void %fn +OpReturn +OpFunctionEnd +%fn = OpFunction %void None %3 +%33 = OpLabel +OpReturn +OpFunctionEnd +)"); + + SinglePassRunAndMatch(text, true); +} + +INSTANTIATE_TEST_SUITE_P( + AddVolatileDecoration, AddVolatileDecorationTest, + ::testing::ValuesIn(std::vector{ + {"RayGenerationKHR", "SubgroupSize", false}, + {"RayGenerationKHR", "SubgroupLocalInvocationId", false}, + {"RayGenerationKHR", "SubgroupEqMask", true}, + {"ClosestHitKHR", "SubgroupLocalInvocationId", true}, + {"IntersectionKHR", "SubgroupEqMask", true}, + {"MissKHR", "SubgroupGeMask", true}, + {"CallableKHR", "SubgroupGtMask", true}, + {"RayGenerationKHR", "SubgroupLeMask", true}, + })); + +using SetLoadVolatileTest = + PassTest<::testing::TestWithParam>; + +TEST_P(SetLoadVolatileTest, InMain) { + const auto& tc = GetParam(); + const std::string execution_model(tc.execution_model); + const std::string built_in(tc.built_in); + + const std::string var_type = + tc.use_v4uint ? "%_ptr_Input_v4uint" : "%_ptr_Input_uint"; + const std::string var_value = tc.use_v4uint ? std::string(R"( +; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_Input_uint [[var]] %int_0 +; CHECK: OpLoad {{%\w+}} [[ptr]] Volatile +%ptr = OpAccessChain %_ptr_Input_uint %var %int_0 +%var_value = OpLoad %uint %ptr)") + : std::string(R"( +; CHECK: OpLoad {{%\w+}} [[var]] Volatile +%var_value = OpLoad %uint %var)"); + + const std::string text = std::string(R"(OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpCapability VulkanMemoryModel +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +OpMemoryModel Logical Vulkan +OpEntryPoint )") + execution_model + + std::string(R"( %main "main" %var +OpName %main "main" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +)") + std::string(R"( +; CHECK: OpDecorate [[var:%\w+]] BuiltIn )") + + built_in + std::string(R"( +OpDecorate %var BuiltIn )") + built_in + + std::string(R"( +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%v4uint = OpTypeVector %uint 4 +%_ptr_Input_v4uint = OpTypePointer Input %v4uint +%var = OpVariable )") + var_type + + std::string(R"( Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%main = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %19 +)") + var_value + std::string(R"( +%test = OpIAdd %uint %var_value %20 +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %test +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd +)"); + + SinglePassRunAndMatch(text, true); +} + +INSTANTIATE_TEST_SUITE_P( + SetLoadVolatile, SetLoadVolatileTest, + ::testing::ValuesIn(std::vector{ + {"RayGenerationKHR", "SubgroupSize", false}, + {"RayGenerationKHR", "SubgroupLocalInvocationId", false}, + {"RayGenerationKHR", "SubgroupEqMask", true}, + {"ClosestHitKHR", "SubgroupLocalInvocationId", true}, + {"IntersectionKHR", "SubgroupEqMask", true}, + {"MissKHR", "SubgroupGeMask", true}, + {"CallableKHR", "SubgroupGtMask", true}, + {"RayGenerationKHR", "SubgroupLeMask", true}, + })); + +using VolatileSpreadTest = PassTest<::testing::Test>; + +TEST_F(VolatileSpreadTest, SpreadVolatileForHelperInvocation) { + const std::string text = + R"( +OpCapability Shader +OpCapability DemoteToHelperInvocation +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var +OpExecutionMode %main OriginUpperLeft + +; CHECK: OpDecorate [[var:%\w+]] BuiltIn HelperInvocation +; CHECK: OpDecorate [[var]] Volatile +OpDecorate %var BuiltIn HelperInvocation + +%bool = OpTypeBool +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%_ptr_Input_bool = OpTypePointer Input %bool +%var = OpVariable %_ptr_Input_bool Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +%load = OpLoad %bool %var +OpDemoteToHelperInvocation +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_6); + SinglePassRunAndMatch(text, true); +} + +TEST_F(VolatileSpreadTest, MultipleExecutionModel) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var +OpEntryPoint GLCompute %compute "Compute" %gl_LocalInvocationIndex +OpExecutionMode %compute LocalSize 16 16 1 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable + +; CHECK: OpEntryPoint RayGenerationNV {{%\w+}} "RayGeneration" [[var:%\w+]] +; CHECK: OpDecorate [[var]] BuiltIn SubgroupSize +; CHECK: OpDecorate [[var]] Volatile +; CHECK-NOT: OpDecorate {{%\w+}} Volatile +OpDecorate %var BuiltIn SubgroupSize + +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%var = OpVariable %_ptr_Input_uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%shared = OpVariable %_ptr_Workgroup_uint Workgroup + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %var +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd + +%compute = OpFunction %void None %3 +%66 = OpLabel +%62 = OpLoad %uint %gl_LocalInvocationIndex +%61 = OpAtomicIAdd %uint %shared %uint_1 %uint_0 %62 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(VolatileSpreadTest, VarUsedInMultipleEntryPoints) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var +OpEntryPoint ClosestHitKHR %ClosestHit "ClosestHit" %var +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %ClosestHit "ClosestHit" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable + +; CHECK: OpEntryPoint RayGenerationNV {{%\w+}} "RayGeneration" [[var:%\w+]] +; CHECK: OpEntryPoint ClosestHitNV {{%\w+}} "ClosestHit" [[var]] +; CHECK: OpDecorate [[var]] BuiltIn SubgroupSize +; CHECK: OpDecorate [[var]] Volatile +; CHECK-NOT: OpDecorate {{%\w+}} Volatile +OpDecorate %var BuiltIn SubgroupSize + +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%var = OpVariable %_ptr_Input_uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%shared = OpVariable %_ptr_Workgroup_uint Workgroup + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %var +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd + +%ClosestHit = OpFunction %void None %3 +%45 = OpLabel +%49 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%40 = OpLoad %uint %var +%42 = OpAccessChain %_ptr_UniformConstant_13 %images %40 +%43 = OpLoad %13 %42 +%47 = OpImageRead %v4float %43 %25 +%59 = OpCompositeExtract %float %47 0 +%51 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %51 %59 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +class VolatileSpreadErrorTest : public PassTest<::testing::Test> { + public: + VolatileSpreadErrorTest() + : consumer_([this](spv_message_level_t level, const char*, + const spv_position_t& position, const char* message) { + if (!error_message_.empty()) error_message_ += "\n"; + switch (level) { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + error_message_ += "ERROR"; + break; + case SPV_MSG_WARNING: + error_message_ += "WARNING"; + break; + case SPV_MSG_INFO: + error_message_ += "INFO"; + break; + case SPV_MSG_DEBUG: + error_message_ += "DEBUG"; + break; + } + error_message_ += + ": " + std::to_string(position.index) + ": " + message; + }) {} + + Pass::Status RunPass(const std::string& text) { + std::unique_ptr context_ = + spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_2, consumer_, text); + if (!context_.get()) return Pass::Status::Failure; + + PassManager manager; + manager.SetMessageConsumer(consumer_); + manager.AddPass(); + + return manager.Run(context_.get()); + } + + std::string GetErrorMessage() const { return error_message_; } + + void TearDown() override { error_message_.clear(); } + + private: + spvtools::MessageConsumer consumer_; + std::string error_message_; +}; + +TEST_F(VolatileSpreadErrorTest, VarUsedInMultipleExecutionModelError) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var +OpEntryPoint GLCompute %compute "Compute" %gl_LocalInvocationIndex %var +OpExecutionMode %compute LocalSize 16 16 1 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +OpDecorate %var BuiltIn SubgroupSize +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%var = OpVariable %_ptr_Input_uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%shared = OpVariable %_ptr_Workgroup_uint Workgroup + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %var +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd + +%compute = OpFunction %void None %3 +%66 = OpLabel +%62 = OpLoad %uint %gl_LocalInvocationIndex +%63 = OpLoad %uint %var +%64 = OpIAdd %uint %62 %63 +%61 = OpAtomicIAdd %uint %shared %uint_1 %uint_0 %64 +OpReturn +OpFunctionEnd +)"; + + EXPECT_EQ(RunPass(text), Pass::Status::Failure); + const char expected_error[] = + "ERROR: 0: Variable is a target for Volatile semantics for an entry " + "point, but it is not for another entry point"; + EXPECT_STREQ(GetErrorMessage().substr(0, sizeof(expected_error) - 1).c_str(), + expected_error); +} + +TEST_F(VolatileSpreadErrorTest, + VarUsedInMultipleReverseOrderExecutionModelError) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %compute "Compute" %gl_LocalInvocationIndex %var +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var +OpExecutionMode %compute LocalSize 16 16 1 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +OpDecorate %var BuiltIn SubgroupSize +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%var = OpVariable %_ptr_Input_uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%shared = OpVariable %_ptr_Workgroup_uint Workgroup + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %var +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd + +%compute = OpFunction %void None %3 +%66 = OpLabel +%62 = OpLoad %uint %gl_LocalInvocationIndex +%63 = OpLoad %uint %var +%64 = OpIAdd %uint %62 %63 +%61 = OpAtomicIAdd %uint %shared %uint_1 %uint_0 %64 +OpReturn +OpFunctionEnd +)"; + + EXPECT_EQ(RunPass(text), Pass::Status::Failure); + const char expected_error[] = + "ERROR: 0: Variable is a target for Volatile semantics for an entry " + "point, but it is not for another entry point"; + EXPECT_STREQ(GetErrorMessage().substr(0, sizeof(expected_error) - 1).c_str(), + expected_error); +} + +TEST_F(VolatileSpreadErrorTest, FunctionNotInlined) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var +OpEntryPoint ClosestHitKHR %ClosestHit "ClosestHit" %var +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %ClosestHit "ClosestHit" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +OpDecorate %var BuiltIn SubgroupSize +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%var = OpVariable %_ptr_Input_uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%shared = OpVariable %_ptr_Workgroup_uint Workgroup + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %19 +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd + +%NotInlined = OpFunction %void None %3 +%32 = OpLabel +OpReturn +OpFunctionEnd + +%ClosestHit = OpFunction %void None %3 +%45 = OpLabel +%49 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%40 = OpLoad %uint %49 +%42 = OpAccessChain %_ptr_UniformConstant_13 %images %40 +%43 = OpLoad %13 %42 +%47 = OpImageRead %v4float %43 %25 +%59 = OpCompositeExtract %float %47 0 +%51 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %51 %59 +OpReturn +OpFunctionEnd +)"; + + EXPECT_EQ(RunPass(text), Pass::Status::SuccessWithoutChange); +} + +TEST_F(VolatileSpreadErrorTest, VarNotUsedInEntryPointForVolatile) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var +OpEntryPoint GLCompute %compute "Compute" %gl_LocalInvocationIndex %var +OpExecutionMode %compute LocalSize 16 16 1 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable + +; CHECK-NOT: OpDecorate {{%\w+}} Volatile + +OpDecorate %var BuiltIn SubgroupSize +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%var = OpVariable %_ptr_Input_uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%shared = OpVariable %_ptr_Workgroup_uint Workgroup + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %19 +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd + +%compute = OpFunction %void None %3 +%66 = OpLabel +%62 = OpLoad %uint %gl_LocalInvocationIndex +%63 = OpLoad %uint %var +%64 = OpIAdd %uint %62 %63 +%61 = OpAtomicIAdd %uint %shared %uint_1 %uint_0 %64 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(VolatileSpreadTest, RecursivelySpreadVolatile) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpCapability VulkanMemoryModel +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical Vulkan +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var0 %var1 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable + +; CHECK: OpDecorate [[var0:%\w+]] BuiltIn SubgroupEqMask +; CHECK: OpDecorate [[var1:%\w+]] BuiltIn SubgroupGeMask +OpDecorate %var0 BuiltIn SubgroupEqMask +OpDecorate %var1 BuiltIn SubgroupGeMask + +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%v4uint = OpTypeVector %uint 4 +%_ptr_Input_v4uint = OpTypePointer Input %v4uint +%_ptr_Input_uint = OpTypePointer Input %uint +%var0 = OpVariable %_ptr_Input_v4uint Input +%var1 = OpVariable %_ptr_Input_v4uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel + +; CHECK: [[ptr0:%\w+]] = OpAccessChain %_ptr_Input_uint [[var0]] %int_0 +; CHECK: OpLoad {{%\w+}} [[ptr0]] Volatile +%19 = OpAccessChain %_ptr_Input_uint %var0 %int_0 +%20 = OpLoad %uint %19 + +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %uint_1 + +; CHECK: OpLoad {{%\w+}} [[ptr0]] Volatile +%24 = OpLoad %uint %19 + +; CHECK: [[var2:%\w+]] = OpCopyObject %_ptr_Input_v4uint [[var0]] +; CHECK: [[ptr2:%\w+]] = OpAccessChain %_ptr_Input_uint [[var2]] %int_1 +; CHECK: OpLoad {{%\w+}} [[ptr2]] Volatile +%18 = OpCopyObject %_ptr_Input_v4uint %var0 +%21 = OpAccessChain %_ptr_Input_uint %18 %int_1 +%26 = OpLoad %uint %21 + +%28 = OpIAdd %uint %24 %26 +%30 = OpConvertUToF %float %28 + +; CHECK: [[ptr1:%\w+]] = OpAccessChain %_ptr_Input_uint [[var1]] %int_1 +; CHECK: OpLoad {{%\w+}} [[ptr1]] Volatile +%32 = OpAccessChain %_ptr_Input_uint %var1 %int_1 +%33 = OpLoad %uint %32 + +%34 = OpConvertUToF %float %33 +%35 = OpFAdd %float %34 %30 +%36 = OpFAdd %float %35 %29 +OpStore %31 %36 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(VolatileSpreadTest, SpreadVolatileOnlyForTargetEntryPoints) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpCapability VulkanMemoryModel +OpCapability VulkanMemoryModelDeviceScopeKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical Vulkan +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var0 %var1 +OpEntryPoint GLCompute %compute "Compute" %var0 %var1 +OpExecutionMode %compute LocalSize 16 16 1 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable + +; CHECK: OpDecorate [[var0:%\w+]] BuiltIn SubgroupEqMask +; CHECK: OpDecorate [[var1:%\w+]] BuiltIn SubgroupGeMask +OpDecorate %var0 BuiltIn SubgroupEqMask +OpDecorate %var1 BuiltIn SubgroupGeMask + +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%v4uint = OpTypeVector %uint 4 +%_ptr_Input_v4uint = OpTypePointer Input %v4uint +%_ptr_Input_uint = OpTypePointer Input %uint +%var0 = OpVariable %_ptr_Input_v4uint Input +%var1 = OpVariable %_ptr_Input_v4uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%shared = OpVariable %_ptr_Workgroup_uint Workgroup + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel + +; CHECK: [[ptr0:%\w+]] = OpAccessChain %_ptr_Input_uint [[var0]] %int_0 +; CHECK: OpLoad {{%\w+}} [[ptr0]] Volatile +%19 = OpAccessChain %_ptr_Input_uint %var0 %int_0 +%20 = OpLoad %uint %19 + +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %uint_1 + +; CHECK: OpLoad {{%\w+}} [[ptr0]] Volatile +%24 = OpLoad %uint %19 + +; CHECK: [[var2:%\w+]] = OpCopyObject %_ptr_Input_v4uint [[var0]] +; CHECK: [[ptr2:%\w+]] = OpAccessChain %_ptr_Input_uint [[var2]] %int_1 +; CHECK: OpLoad {{%\w+}} [[ptr2]] Volatile +%18 = OpCopyObject %_ptr_Input_v4uint %var0 +%21 = OpAccessChain %_ptr_Input_uint %18 %int_1 +%26 = OpLoad %uint %21 + +%28 = OpIAdd %uint %24 %26 +%30 = OpConvertUToF %float %28 + +; CHECK: [[ptr1:%\w+]] = OpAccessChain %_ptr_Input_uint [[var1]] %int_1 +; CHECK: OpLoad {{%\w+}} [[ptr1]] Volatile +%32 = OpAccessChain %_ptr_Input_uint %var1 %int_1 +%33 = OpLoad %uint %32 + +%34 = OpConvertUToF %float %33 +%35 = OpFAdd %float %34 %30 +%36 = OpFAdd %float %35 %29 +OpStore %31 %36 +OpReturn +OpFunctionEnd + +%compute = OpFunction %void None %3 +%66 = OpLabel + +; CHECK-NOT: OpLoad {{%\w+}} {{%\w+}} Volatile +%62 = OpLoad %v4uint %var0 +%63 = OpLoad %v4uint %var1 +%64 = OpIAdd %v4uint %62 %63 +%65 = OpCompositeExtract %uint %64 0 +%61 = OpAtomicIAdd %uint %shared %uint_1 %uint_0 %65 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(VolatileSpreadTest, SkipIfItHasNoExecutionModel) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%4 = OpFunction %2 None %3 +%5 = OpLabel +OpReturn +OpFunctionEnd +)"; + + Pass::Status status; + std::tie(std::ignore, status) = + SinglePassRunToBinary(text, + /* skip_nop = */ false); + EXPECT_EQ(status, Pass::Status::SuccessWithoutChange); +} + +TEST_F(VolatileSpreadTest, NoInlinedfuncCalls) { + const std::string text = R"( +OpCapability RayTracingNV +OpCapability VulkanMemoryModel +OpCapability GroupNonUniform +OpExtension "SPV_NV_ray_tracing" +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical Vulkan +OpEntryPoint RayGenerationNV %main "main" %SubgroupSize +OpSource HLSL 630 +OpName %main "main" +OpName %src_main "src.main" +OpName %bb_entry "bb.entry" +OpName %func0 "func0" +OpName %bb_entry_0 "bb.entry" +OpName %func2 "func2" +OpName %bb_entry_1 "bb.entry" +OpName %param_var_count "param.var.count" +OpName %func1 "func1" +OpName %bb_entry_2 "bb.entry" +OpName %func3 "func3" +OpName %count "count" +OpName %bb_entry_3 "bb.entry" +OpDecorate %SubgroupSize BuiltIn SubgroupSize +%uint = OpTypeInt 32 0 +%_ptr_Input_uint = OpTypePointer Input %uint +%void = OpTypeVoid +%6 = OpTypeFunction %void +%_ptr_Function_uint = OpTypePointer Function %uint +%25 = OpTypeFunction %void %_ptr_Function_uint +%SubgroupSize = OpVariable %_ptr_Input_uint Input +%main = OpFunction %void None %6 +%7 = OpLabel +%8 = OpFunctionCall %void %src_main +OpReturn +OpFunctionEnd +%src_main = OpFunction %void None %6 +%bb_entry = OpLabel +%11 = OpFunctionCall %void %func0 +OpReturn +OpFunctionEnd +%func0 = OpFunction %void DontInline %6 +%bb_entry_0 = OpLabel +%14 = OpFunctionCall %void %func2 +%16 = OpFunctionCall %void %func1 +OpReturn +OpFunctionEnd +%func2 = OpFunction %void DontInline %6 +%bb_entry_1 = OpLabel +%param_var_count = OpVariable %_ptr_Function_uint Function +; CHECK: {{%\w+}} = OpLoad %uint %SubgroupSize Volatile +%21 = OpLoad %uint %SubgroupSize +OpStore %param_var_count %21 +%22 = OpFunctionCall %void %func3 %param_var_count +OpReturn +OpFunctionEnd +%func1 = OpFunction %void DontInline %6 +%bb_entry_2 = OpLabel +OpReturn +OpFunctionEnd +%func3 = OpFunction %void DontInline %25 +%count = OpFunctionParameter %_ptr_Function_uint +%bb_entry_3 = OpLabel +OpReturn +OpFunctionEnd +)"; + SinglePassRunAndMatch(text, true); +} + +TEST_F(VolatileSpreadErrorTest, NoInlinedMultiEntryfuncCalls) { + const std::string text = R"( +OpCapability RayTracingNV +OpCapability SubgroupBallotKHR +OpExtension "SPV_NV_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint RayGenerationNV %main "main" %SubgroupSize +OpEntryPoint GLCompute %main2 "main2" %gl_LocalInvocationIndex %SubgroupSize +OpSource HLSL 630 +OpName %main "main" +OpName %bb_entry "bb.entry" +OpName %main2 "main2" +OpName %bb_entry_0 "bb.entry" +OpName %func "func" +OpName %count "count" +OpName %bb_entry_1 "bb.entry" +OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex +OpDecorate %SubgroupSize BuiltIn SubgroupSize +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Input_uint = OpTypePointer Input %uint +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%void = OpTypeVoid +%12 = OpTypeFunction %void +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Function_v4float = OpTypePointer Function %v4float +%29 = OpTypeFunction %void %_ptr_Function_v4float +%34 = OpTypeFunction %void %_ptr_Function_uint +%SubgroupSize = OpVariable %_ptr_Input_uint Input +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%main = OpFunction %void None %12 +%bb_entry = OpLabel +%20 = OpFunctionCall %void %func +OpReturn +OpFunctionEnd +%main2 = OpFunction %void None %12 +%bb_entry_0 = OpLabel +%33 = OpFunctionCall %void %func +OpReturn +OpFunctionEnd +%func = OpFunction %void DontInline %12 +%bb_entry_1 = OpLabel +%count = OpVariable %_ptr_Function_uint Function +%35 = OpLoad %uint %SubgroupSize +OpStore %count %35 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(RunPass(text), Pass::Status::Failure); + const char expected_error[] = + "ERROR: 0: Variable is a target for Volatile semantics for an entry " + "point, but it is not for another entry point"; + EXPECT_STREQ(GetErrorMessage().substr(0, sizeof(expected_error) - 1).c_str(), + expected_error); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/test/opt/strip_nonsemantic_info_test.cpp b/test/opt/strip_nonsemantic_info_test.cpp new file mode 100644 index 00000000..3aacffa3 --- /dev/null +++ b/test/opt/strip_nonsemantic_info_test.cpp @@ -0,0 +1,296 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "spirv-tools/optimizer.hpp" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using StripNonSemanticInfoTest = PassTest<::testing::Test>; + +// This test acts as an end-to-end code example on how to strip +// reflection info from a SPIR-V module. Use this code pattern +// when you have compiled HLSL code with Glslang or DXC using +// option -fhlsl_functionality1 to insert reflection information, +// but then want to filter out the extra instructions before sending +// it to a driver that does not implement VK_GOOGLE_hlsl_functionality1. +TEST_F(StripNonSemanticInfoTest, StripReflectEnd2EndExample) { + // This is a non-sensical example, but exercises the instructions. + std::string before = R"(OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_decorate_string" +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpMemoryModel Logical Simple +OpDecorateStringGOOGLE %float HlslSemanticGOOGLE "foobar" +OpDecorateStringGOOGLE %void HlslSemanticGOOGLE "my goodness" +%void = OpTypeVoid +%float = OpTypeFloat 32 +)"; + SpirvTools tools(SPV_ENV_UNIVERSAL_1_1); + std::vector binary_in; + tools.Assemble(before, &binary_in); + + // Instantiate the optimizer, and run the strip-nonsemantic-info + // pass over the |binary_in| module, and place the modified module + // into |binary_out|. + spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_1); + optimizer.RegisterPass(spvtools::CreateStripNonSemanticInfoPass()); + std::vector binary_out; + optimizer.Run(binary_in.data(), binary_in.size(), &binary_out); + + // Check results + std::string disassembly; + tools.Disassemble(binary_out.data(), binary_out.size(), &disassembly); + std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical Simple +%void = OpTypeVoid +%float = OpTypeFloat 32 +)"; + EXPECT_THAT(disassembly, testing::Eq(after)); +} + +// This test is functionally the same as the end-to-end test above, +// but uses the test SinglePassRunAndCheck test fixture instead. +TEST_F(StripNonSemanticInfoTest, StripHlslSemantic) { + // This is a non-sensical example, but exercises the instructions. + std::string before = R"(OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_decorate_string" +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpMemoryModel Logical Simple +OpDecorateStringGOOGLE %float HlslSemanticGOOGLE "foobar" +OpDecorateStringGOOGLE %void HlslSemanticGOOGLE "my goodness" +%void = OpTypeVoid +%float = OpTypeFloat 32 +)"; + std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical Simple +%void = OpTypeVoid +%float = OpTypeFloat 32 +)"; + + SinglePassRunAndCheck(before, after, false); +} + +TEST_F(StripNonSemanticInfoTest, StripHlslCounterBuffer) { + std::string before = R"(OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpMemoryModel Logical Simple +OpDecorateId %void HlslCounterBufferGOOGLE %float +%void = OpTypeVoid +%float = OpTypeFloat 32 +)"; + std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical Simple +%void = OpTypeVoid +%float = OpTypeFloat 32 +)"; + + SinglePassRunAndCheck(before, after, false); +} + +TEST_F(StripNonSemanticInfoTest, StripHlslSemanticOnMember) { + // This is a non-sensical example, but exercises the instructions. + std::string before = R"(OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_decorate_string" +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpMemoryModel Logical Simple +OpMemberDecorateStringGOOGLE %struct 0 HlslSemanticGOOGLE "foobar" +%float = OpTypeFloat 32 +%_struct_3 = OpTypeStruct %float +)"; + std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical Simple +%float = OpTypeFloat 32 +%_struct_3 = OpTypeStruct %float +)"; + + SinglePassRunAndCheck(before, after, false); +} + +TEST_F(StripNonSemanticInfoTest, StripNonSemanticImport) { + std::string text = R"( +; CHECK-NOT: OpExtension "SPV_KHR_non_semantic_info" +; CHECK-NOT: OpExtInstImport +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.Test" +OpMemoryModel Logical GLSL450 +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(StripNonSemanticInfoTest, StripNonSemanticGlobal) { + std::string text = R"( +; CHECK-NOT: OpExtInst +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.Test" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%1 = OpExtInst %void %ext 1 +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(StripNonSemanticInfoTest, StripNonSemanticInFunction) { + std::string text = R"( +; CHECK-NOT: OpExtInst +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.Test" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +%1 = OpExtInst %void %ext 1 %foo +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(StripNonSemanticInfoTest, StripNonSemanticAfterFunction) { + std::string text = R"( +; CHECK-NOT: OpExtInst +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.Test" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%1 = OpExtInst %void %ext 1 %foo +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(StripNonSemanticInfoTest, StripNonSemanticBetweenFunctions) { + std::string text = R"( +; CHECK-NOT: OpExtInst +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.Test" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%1 = OpExtInst %void %ext 1 %foo +%bar = OpFunction %void None %void_fn +%bar_entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +// Make sure that strip reflect does not remove the debug info (OpString and +// OpLine). +TEST_F(StripNonSemanticInfoTest, DontStripDebug) { + std::string text = R"(OpCapability Shader +OpMemoryModel Logical Simple +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +%2 = OpString "file" +%void = OpTypeVoid +%4 = OpTypeFunction %void +%1 = OpFunction %void None %4 +%5 = OpLabel +OpLine %2 1 1 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} + +TEST_F(StripNonSemanticInfoTest, RemovedNonSemanticDebugInfo) { + const std::string text = R"( +;CHECK-NOT: OpExtension "SPV_KHR_non_semantic_info +;CHECK-NOT: OpExtInstImport "NonSemantic.Shader.DebugInfo.100 +;CHECK-NOT: OpExtInst %void {{%\w+}} DebugSource +;CHECK-NOT: OpExtInst %void {{%\w+}} DebugLine + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %PSMain "PSMain" %in_var_COLOR %out_var_SV_TARGET + OpExecutionMode %PSMain OriginUpperLeft + %5 = OpString "t.hlsl" + %6 = OpString "float" + %7 = OpString "color" + %8 = OpString "PSInput" + %9 = OpString "PSMain" + %10 = OpString "" + %11 = OpString "input" + OpName %in_var_COLOR "in.var.COLOR" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %PSMain "PSMain" + OpDecorate %in_var_COLOR Location 0 + OpDecorate %out_var_SV_TARGET Location 0 + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %uint_1 = OpConstant %uint 1 + %uint_9 = OpConstant %uint 9 + %21 = OpTypeFunction %void +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %13 = OpExtInst %void %1 DebugSource %5 + %PSMain = OpFunction %void None %21 + %22 = OpLabel + %23 = OpLoad %v4float %in_var_COLOR + OpStore %out_var_SV_TARGET %23 + %24 = OpExtInst %void %1 DebugLine %13 %uint_9 %uint_9 %uint_1 %uint_1 + OpReturn + OpFunctionEnd +)"; + SinglePassRunAndMatch(text, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/test/opt/type_manager_test.cpp b/test/opt/type_manager_test.cpp index fdae2efc..df216bc9 100644 --- a/test/opt/type_manager_test.cpp +++ b/test/opt/type_manager_test.cpp @@ -167,10 +167,25 @@ std::vector> GenerateAllTypes() { types.emplace_back(new NamedBarrier()); types.emplace_back(new AccelerationStructureNV()); types.emplace_back(new CooperativeMatrixNV(f32, 24, 24, 24)); + types.emplace_back(new RayQueryKHR()); return types; } +TEST(TypeManager, GenerateAllTypesGeneratesAllTypes) { + std::set generated_types; + for (auto& type : GenerateAllTypes()) { + generated_types.insert(type->kind()); + } + + std::vector all_types; + for (uint32_t kind = 0; kind != Type::Kind::kLast; ++kind) { + all_types.push_back(static_cast(kind)); + } + + EXPECT_THAT(generated_types, testing::UnorderedElementsAreArray(all_types)); +} + TEST(TypeManager, TypeStrings) { const std::string text = R"( OpDecorate %spec_const_with_id SpecId 99 @@ -1065,6 +1080,7 @@ TEST(TypeManager, GetTypeInstructionAllTypes) { ; CHECK: OpTypeNamedBarrier ; CHECK: OpTypeAccelerationStructureKHR ; CHECK: OpTypeCooperativeMatrixNV [[f32]] [[uint24]] [[uint24]] [[uint24]] +; CHECK: OpTypeRayQueryKHR OpCapability Shader OpCapability Int64 OpCapability Linkage diff --git a/test/opt/types_test.cpp b/test/opt/types_test.cpp index 82e40405..552ad97c 100644 --- a/test/opt/types_test.cpp +++ b/test/opt/types_test.cpp @@ -266,6 +266,67 @@ TEST(Types, AllTypes) { } } +TEST(Types, TestNumberOfComponentsOnArrays) { + Float f32(32); + EXPECT_EQ(f32.NumberOfComponents(), 0); + + Array array_size_42( + &f32, Array::LengthInfo{99u, {Array::LengthInfo::kConstant, 42u}}); + EXPECT_EQ(array_size_42.NumberOfComponents(), 42); + + Array array_size_0xDEADBEEF00C0FFEE( + &f32, Array::LengthInfo{ + 99u, {Array::LengthInfo::kConstant, 0xC0FFEE, 0xDEADBEEF}}); + EXPECT_EQ(array_size_0xDEADBEEF00C0FFEE.NumberOfComponents(), + 0xDEADBEEF00C0FFEEull); + + Array array_size_unknown( + &f32, + Array::LengthInfo{99u, {Array::LengthInfo::kConstantWithSpecId, 10}}); + EXPECT_EQ(array_size_unknown.NumberOfComponents(), UINT64_MAX); + + RuntimeArray runtime_array(&f32); + EXPECT_EQ(runtime_array.NumberOfComponents(), UINT64_MAX); +} + +TEST(Types, TestNumberOfComponentsOnVectors) { + Float f32(32); + EXPECT_EQ(f32.NumberOfComponents(), 0); + + for (uint32_t vector_size = 1; vector_size < 4; ++vector_size) { + Vector vector(&f32, vector_size); + EXPECT_EQ(vector.NumberOfComponents(), vector_size); + } +} + +TEST(Types, TestNumberOfComponentsOnMatrices) { + Float f32(32); + Vector vector(&f32, 2); + + for (uint32_t number_of_columns = 1; number_of_columns < 4; + ++number_of_columns) { + Matrix matrix(&vector, number_of_columns); + EXPECT_EQ(matrix.NumberOfComponents(), number_of_columns); + } +} + +TEST(Types, TestNumberOfComponentsOnStructs) { + Float f32(32); + Vector vector(&f32, 2); + + Struct empty_struct({}); + EXPECT_EQ(empty_struct.NumberOfComponents(), 0); + + Struct struct_f32({&f32}); + EXPECT_EQ(struct_f32.NumberOfComponents(), 1); + + Struct struct_f32_vec({&f32, &vector}); + EXPECT_EQ(struct_f32_vec.NumberOfComponents(), 2); + + Struct struct_100xf32(std::vector(100, &f32)); + EXPECT_EQ(struct_100xf32.NumberOfComponents(), 100); +} + TEST(Types, IntSignedness) { std::vector signednesses = {true, false, false, true}; std::vector> types; diff --git a/test/opt/unify_const_test.cpp b/test/opt/unify_const_test.cpp index 6ed21734..0d7c30b0 100644 --- a/test/opt/unify_const_test.cpp +++ b/test/opt/unify_const_test.cpp @@ -263,7 +263,7 @@ TEST_F(UnifyFrontEndConstantSingleTest, UnifyWithDecorationOnTypes) { // decorated flat struct "%flat_d = OpTypeStruct %int %float", "%_pf_flat_d = OpTypePointer Function %flat_d", - // perserved contants. %flat_1 and %flat_d has same members, but + // preserved constants. %flat_1 and %flat_d has same members, but // their type are different in decorations, so they should not be // used to replace each other. "%int_1 = OpConstant %int 1", @@ -682,7 +682,7 @@ INSTANTIATE_TEST_SUITE_P( // zero-valued composite constant built from zero-valued constant // component. inner_zero should not be replace by null_inner. "%inner_zero = OpConstantComposite %inner_struct %bool_zero %float_zero", - // zero-valued composite contant built from zero-valued constants + // zero-valued composite constant built from zero-valued constants // and null constants. "%outer_zero = OpConstantComposite %outer_struct %inner_zero %int_null %double_null", // outer_struct type null constant, it should not be replaced by @@ -820,7 +820,7 @@ INSTANTIATE_TEST_SUITE_P( { "%spec_signed_add_duplicate = OpSpecConstantOp %int IAdd %spec_signed_1 %spec_signed_2", }, - // use duplicated contants in main + // use duplicated constants in main { "%int_var = OpVariable %_pf_int Function", "OpStore %int_var %spec_signed_add_duplicate", diff --git a/test/opt/upgrade_memory_model_test.cpp b/test/opt/upgrade_memory_model_test.cpp index 7f64ffd7..2cd3c7df 100644 --- a/test/opt/upgrade_memory_model_test.cpp +++ b/test/opt/upgrade_memory_model_test.cpp @@ -404,7 +404,7 @@ OpCapability VariablePointers OpExtension "SPV_KHR_variable_pointers" OpMemoryModel Logical GLSL450 OpDecorate %param Coherent -OpDecorate %param ArrayStride 4 +OpDecorate %ptr_int_StorageBuffer ArrayStride 4 %void = OpTypeVoid %bool = OpTypeBool %int = OpTypeInt 32 0 diff --git a/test/opt/vector_dce_test.cpp b/test/opt/vector_dce_test.cpp index 9bdad375..b14e2256 100644 --- a/test/opt/vector_dce_test.cpp +++ b/test/opt/vector_dce_test.cpp @@ -1351,6 +1351,72 @@ OpFunctionEnd SinglePassRunAndMatch(text, true); } +TEST_F(VectorDCETest, OutOfBoundsExtract) { + // It tests that the vector DCE pass is able to handle an extract with an + // index that is out of bounds. + const std::string text = R"( +; CHECK: [[undef:%\w+]] = OpUndef %v4float +; CHECK: OpCompositeExtract %float [[undef]] 8 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %OutColor + OpExecutionMode %main OriginUpperLeft + OpDecorate %OutColor Location 0 + %void = OpTypeVoid + %10 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_float = OpTypePointer Output %float + %OutColor = OpVariable %_ptr_Output_float Output + %null = OpConstantNull %v4float + %float_1 = OpConstant %float 1 + %main = OpFunction %void None %10 + %28 = OpLabel + %33 = OpCompositeInsert %v4float %float_1 %null 1 + %extract = OpCompositeExtract %float %33 8 + OpStore %OutColor %extract + OpReturn + OpFunctionEnd +)"; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(text, false); +} + +TEST_F(VectorDCETest, OutOfBoundsShuffle) { + // It tests that the vector DCE pass is able to handle a shuffle with an + // index that is out of bounds. + const std::string text = R"( +; CHECK: [[undef:%\w+]] = OpUndef %v4float +; CHECK: OpVectorShuffle %v4float [[undef]] [[undef]] 9 10 11 12 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %OutColor + OpExecutionMode %main OriginUpperLeft + OpDecorate %OutColor Location 0 + %void = OpTypeVoid + %10 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %OutColor = OpVariable %_ptr_Output_v4float Output + %null = OpConstantNull %v4float + %float_1 = OpConstant %float 1 + %main = OpFunction %void None %10 + %28 = OpLabel + %33 = OpCompositeInsert %v4float %float_1 %null 1 + %shuffle = OpVectorShuffle %v4float %33 %33 9 10 11 12 + OpStore %OutColor %shuffle + OpReturn + OpFunctionEnd +)"; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(text, false); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/reduce/CMakeLists.txt b/test/reduce/CMakeLists.txt index 652f0ab5..121cd4f0 100644 --- a/test/reduce/CMakeLists.txt +++ b/test/reduce/CMakeLists.txt @@ -14,6 +14,7 @@ add_spvtools_unittest(TARGET reduce SRCS + conditional_branch_to_simple_conditional_branch_test.cpp merge_blocks_test.cpp operand_to_constant_test.cpp operand_to_undef_test.cpp @@ -26,10 +27,10 @@ add_spvtools_unittest(TARGET reduce remove_selection_test.cpp remove_unused_instruction_test.cpp remove_unused_struct_member_test.cpp + simple_conditional_branch_to_branch_test.cpp + structured_construct_to_block_test.cpp structured_loop_to_selection_test.cpp validation_during_reduction_test.cpp - conditional_branch_to_simple_conditional_branch_test.cpp - simple_conditional_branch_to_branch_test.cpp LIBS SPIRV-Tools-reduce ) diff --git a/test/reduce/merge_blocks_test.cpp b/test/reduce/merge_blocks_test.cpp index 8506ee08..c472301f 100644 --- a/test/reduce/merge_blocks_test.cpp +++ b/test/reduce/merge_blocks_test.cpp @@ -647,6 +647,64 @@ TEST(MergeBlocksReductionPassTest, LoopReturnReverse) { MergeBlocksReductionPassTest_LoopReturn_Helper(true); } +TEST(MergeBlocksReductionPassTest, MergeUnreachable) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %11 = OpTypeBool + %12 = OpConstantFalse %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + %9 = OpLabel + OpBranch %100 + %100 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + const auto ops = + MergeBlocksReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %11 = OpTypeBool + %12 = OpConstantFalse %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after, context.get()); +} + } // namespace } // namespace reduce } // namespace spvtools diff --git a/test/reduce/reduce_test_util.cpp b/test/reduce/reduce_test_util.cpp index 0c234111..42716600 100644 --- a/test/reduce/reduce_test_util.cpp +++ b/test/reduce/reduce_test_util.cpp @@ -21,6 +21,29 @@ namespace spvtools { namespace reduce { +const spvtools::MessageConsumer kConsoleMessageConsumer = + [](spv_message_level_t level, const char*, const spv_position_t& position, + const char* message) -> void { + switch (level) { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + std::cerr << "error: line " << position.index << ": " << message + << std::endl; + break; + case SPV_MSG_WARNING: + std::cout << "warning: line " << position.index << ": " << message + << std::endl; + break; + case SPV_MSG_INFO: + std::cout << "info: line " << position.index << ": " << message + << std::endl; + break; + default: + break; + } +}; + void CheckEqual(const spv_target_env env, const std::vector& expected_binary, const std::vector& actual_binary) { @@ -55,8 +78,9 @@ void CheckEqual(const spv_target_env env, const std::string& expected_text, void CheckValid(spv_target_env env, const opt::IRContext* ir) { std::vector binary; ir->module()->ToBinary(&binary, false); - SpirvTools t(env); - ASSERT_TRUE(t.Validate(binary)); + SpirvTools tools(env); + tools.SetMessageConsumer(kConsoleMessageConsumer); + ASSERT_TRUE(tools.Validate(binary)); } std::string ToString(spv_target_env env, const opt::IRContext* ir) { diff --git a/test/reduce/structured_construct_to_block_test.cpp b/test/reduce/structured_construct_to_block_test.cpp new file mode 100644 index 00000000..95009660 --- /dev/null +++ b/test/reduce/structured_construct_to_block_test.cpp @@ -0,0 +1,245 @@ +// Copyright (c) 2021 Alastair F. Donaldson +// +// 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/reduce/structured_construct_to_block_reduction_opportunity_finder.h" + +#include "source/opt/build_module.h" +#include "source/reduce/reduction_opportunity.h" +#include "test/reduce/reduce_test_util.h" + +namespace spvtools { +namespace reduce { +namespace { + +TEST(StructuredConstructToBlockReductionPassTest, SimpleTest) { + 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 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %10 = OpTypeBool + %11 = OpConstantTrue %10 + %19 = OpConstant %6 3 + %29 = OpConstant %6 1 + %31 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpSelectionMerge %13 None + OpBranchConditional %11 %12 %13 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + OpLoopMerge %16 %17 None + OpBranch %15 + %15 = OpLabel + %18 = OpLoad %6 %8 + %20 = OpSGreaterThan %10 %18 %19 + OpSelectionMerge %22 None + OpBranchConditional %20 %21 %22 + %21 = OpLabel + OpBranch %16 + %22 = OpLabel + OpBranch %17 + %17 = OpLabel + OpBranch %14 + %16 = OpLabel + %24 = OpLoad %6 %8 + OpSelectionMerge %28 None + OpSwitch %24 %27 1 %25 2 %26 + %27 = OpLabel + OpStore %8 %19 + OpBranch %28 + %25 = OpLabel + OpStore %8 %29 + OpBranch %28 + %26 = OpLabel + OpStore %8 %31 + OpBranch %28 + %28 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredConstructToBlockReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(3, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(env, context.get()); + + ASSERT_TRUE(ops[1]->PreconditionHolds()); + ops[1]->TryToApply(); + CheckValid(env, context.get()); + + ASSERT_TRUE(ops[2]->PreconditionHolds()); + ops[2]->TryToApply(); + CheckValid(env, context.get()); + + std::string expected = 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 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %10 = OpTypeBool + %11 = OpConstantTrue %10 + %19 = OpConstant %6 3 + %29 = OpConstant %6 1 + %31 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %13 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + OpBranch %16 + %16 = OpLabel + %24 = OpLoad %6 %8 + OpBranch %28 + %28 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, expected, context.get()); +} + +TEST(StructuredConstructToBlockReductionPassTest, CannotBeRemovedDueToUses) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %100 "temp" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %10 = OpTypeBool + %11 = OpConstantTrue %10 + %19 = OpConstant %6 3 + %29 = OpConstant %6 1 + %31 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpSelectionMerge %13 None + OpBranchConditional %11 %12 %13 + %12 = OpLabel + %100 = OpCopyObject %10 %11 + OpBranch %13 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + OpLoopMerge %16 %17 None + OpBranch %15 + %15 = OpLabel + %18 = OpLoad %6 %8 + %20 = OpSGreaterThan %10 %18 %19 + OpSelectionMerge %22 None + OpBranchConditional %20 %21 %22 + %21 = OpLabel + OpBranch %16 + %22 = OpLabel + OpBranch %17 + %17 = OpLabel + OpBranch %14 + %16 = OpLabel + %101 = OpCopyObject %6 %18 + %24 = OpLoad %6 %8 + OpSelectionMerge %28 None + OpSwitch %24 %27 1 %25 2 %26 + %27 = OpLabel + OpStore %8 %19 + %102 = OpCopyObject %10 %11 + OpBranch %28 + %25 = OpLabel + OpStore %8 %29 + OpBranch %28 + %26 = OpLabel + OpStore %8 %31 + OpBranch %28 + %28 = OpLabel + %103 = OpPhi %10 %102 %27 %11 %25 %11 %26 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredConstructToBlockReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_TRUE(ops.empty()); +} + +TEST(StructuredConstructToBlockReductionPassTest, + CannotBeRemovedDueToOpPhiAtMerge) { + 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 + %10 = OpTypeBool + %11 = OpConstantTrue %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %13 None + OpBranchConditional %11 %12 %13 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + %101 = OpPhi %10 %11 %5 %11 %12 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredConstructToBlockReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_TRUE(ops.empty()); +} + +} // namespace +} // namespace reduce +} // namespace spvtools diff --git a/test/reduce/structured_loop_to_selection_test.cpp b/test/reduce/structured_loop_to_selection_test.cpp index 0cfcfdff..d203f3ea 100644 --- a/test/reduce/structured_loop_to_selection_test.cpp +++ b/test/reduce/structured_loop_to_selection_test.cpp @@ -2957,7 +2957,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, OpLoopMerge %12 %13 None OpBranch %12 %13 = OpLabel - OpBranchConditional %6 %9 %11 + OpBranch %11 %12 = OpLabel OpBranch %10 %10 = OpLabel @@ -2999,7 +2999,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, OpLoopMerge %12 %13 None OpBranch %12 %13 = OpLabel - OpBranchConditional %6 %9 %11 + OpBranch %11 %12 = OpLabel OpBranch %9 %10 = OpLabel @@ -3036,7 +3036,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, OpSelectionMerge %12 None OpBranchConditional %6 %12 %12 %13 = OpLabel - OpBranchConditional %6 %9 %11 + OpBranch %11 %12 = OpLabel OpBranch %9 %10 = OpLabel @@ -3050,8 +3050,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, TEST(StructuredLoopToSelectionReductionPassTest, UnreachableInnerLoopContinueBranchingToOuterLoopMerge2) { - // In this test, the branch to the outer loop merge from the inner loop's - // continue is part of a structured selection. + // In this test, the unreachable continue is composed of multiple blocks. std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -3073,8 +3072,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, OpLoopMerge %12 %13 None OpBranch %12 %13 = OpLabel - OpSelectionMerge %14 None - OpBranchConditional %6 %9 %14 + OpBranch %14 %14 = OpLabel OpBranch %11 %12 = OpLabel @@ -3118,8 +3116,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, OpLoopMerge %12 %13 None OpBranch %12 %13 = OpLabel - OpSelectionMerge %14 None - OpBranchConditional %6 %9 %14 + OpBranch %14 %14 = OpLabel OpBranch %11 %12 = OpLabel @@ -3158,8 +3155,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, OpSelectionMerge %12 None OpBranchConditional %6 %12 %12 %13 = OpLabel - OpSelectionMerge %14 None - OpBranchConditional %6 %9 %14 + OpBranch %14 %14 = OpLabel OpBranch %11 %12 = OpLabel diff --git a/test/target_env_test.cpp b/test/target_env_test.cpp index 4acb8ff2..7917cbfb 100644 --- a/test/target_env_test.cpp +++ b/test/target_env_test.cpp @@ -135,7 +135,8 @@ INSTANTIATE_TEST_SUITE_P( {VK(1, 0), SPV(1, 3), true, SPV_ENV_VULKAN_1_1}, {VK(1, 0), SPV(1, 4), true, SPV_ENV_VULKAN_1_1_SPIRV_1_4}, {VK(1, 0), SPV(1, 5), true, SPV_ENV_VULKAN_1_2}, - {VK(1, 0), SPV(1, 6), false, SPV_ENV_UNIVERSAL_1_0}, + {VK(1, 0), SPV(1, 6), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 0), SPV(1, 7), false, SPV_ENV_UNIVERSAL_1_0}, // Vulkan 1.1 cases {VK(1, 1), SPV(1, 0), true, SPV_ENV_VULKAN_1_1}, {VK(1, 1), SPV(1, 1), true, SPV_ENV_VULKAN_1_1}, @@ -143,7 +144,8 @@ INSTANTIATE_TEST_SUITE_P( {VK(1, 1), SPV(1, 3), true, SPV_ENV_VULKAN_1_1}, {VK(1, 1), SPV(1, 4), true, SPV_ENV_VULKAN_1_1_SPIRV_1_4}, {VK(1, 1), SPV(1, 5), true, SPV_ENV_VULKAN_1_2}, - {VK(1, 1), SPV(1, 6), false, SPV_ENV_UNIVERSAL_1_0}, + {VK(1, 1), SPV(1, 6), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 1), SPV(1, 7), false, SPV_ENV_UNIVERSAL_1_0}, // Vulkan 1.2 cases {VK(1, 2), SPV(1, 0), true, SPV_ENV_VULKAN_1_2}, {VK(1, 2), SPV(1, 1), true, SPV_ENV_VULKAN_1_2}, @@ -151,9 +153,17 @@ INSTANTIATE_TEST_SUITE_P( {VK(1, 2), SPV(1, 3), true, SPV_ENV_VULKAN_1_2}, {VK(1, 2), SPV(1, 4), true, SPV_ENV_VULKAN_1_2}, {VK(1, 2), SPV(1, 5), true, SPV_ENV_VULKAN_1_2}, - {VK(1, 2), SPV(1, 6), false, SPV_ENV_UNIVERSAL_1_0}, + {VK(1, 2), SPV(1, 6), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 2), SPV(1, 7), false, SPV_ENV_UNIVERSAL_1_0}, // Vulkan 1.3 cases - {VK(1, 3), SPV(1, 0), false, SPV_ENV_UNIVERSAL_1_0}, + {VK(1, 3), SPV(1, 0), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 3), SPV(1, 1), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 3), SPV(1, 2), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 3), SPV(1, 3), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 3), SPV(1, 4), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 3), SPV(1, 5), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 3), SPV(1, 6), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 3), SPV(1, 7), false, SPV_ENV_UNIVERSAL_1_0}, // Vulkan 2.0 cases {VK(2, 0), SPV(1, 0), false, SPV_ENV_UNIVERSAL_1_0}, // Vulkan 99.0 cases diff --git a/test/test_fixture.h b/test/test_fixture.h index 0c5bfc9c..029fc854 100644 --- a/test/test_fixture.h +++ b/test/test_fixture.h @@ -15,6 +15,7 @@ #ifndef TEST_TEST_FIXTURE_H_ #define TEST_TEST_FIXTURE_H_ +#include #include #include @@ -91,12 +92,26 @@ class TextToBinaryTestBase : public T { return diagnostic->error; } + // Potentially flip the words in the binary representation to the other + // endianness + template + void MaybeFlipWords(bool flip_words, It begin, It end) { + SCOPED_TRACE(flip_words ? "Flipped Endianness" : "Normal Endianness"); + if (flip_words) { + std::transform(begin, end, begin, [](const uint32_t raw_word) { + return spvFixWord(raw_word, I32_ENDIAN_HOST == I32_ENDIAN_BIG + ? SPV_ENDIANNESS_LITTLE + : SPV_ENDIANNESS_BIG); + }); + } + } + // Encodes SPIR-V text into binary and then decodes the binary using // given options. Returns the decoded text. std::string EncodeAndDecodeSuccessfully( const std::string& txt, uint32_t disassemble_options = SPV_BINARY_TO_TEXT_OPTION_NONE, - spv_target_env env = SPV_ENV_UNIVERSAL_1_0) { + spv_target_env env = SPV_ENV_UNIVERSAL_1_0, bool flip_words = false) { DestroyBinary(); DestroyDiagnostic(); ScopedContext context(env); @@ -110,6 +125,8 @@ class TextToBinaryTestBase : public T { EXPECT_EQ(SPV_SUCCESS, error); if (!binary) return ""; + MaybeFlipWords(flip_words, binary->code, binary->code + binary->wordCount); + spv_text decoded_text; error = spvBinaryToText(context.context, binary->code, binary->wordCount, disassemble_options, &decoded_text, &diagnostic); diff --git a/test/text_advance_test.cpp b/test/text_advance_test.cpp index 9de77a83..0d23ab1c 100644 --- a/test/text_advance_test.cpp +++ b/test/text_advance_test.cpp @@ -130,5 +130,14 @@ TEST(TextAdvance, SkipOverCRLFs) { EXPECT_EQ(2u, pos.line); EXPECT_EQ(4u, pos.index); } + +TEST(TextAdvance, HandleLotsOfWhitespace) { + std::string lots_of_spaces(10000, ' '); + lots_of_spaces += "Word"; + const auto pos = PositionAfterAdvance(lots_of_spaces.c_str()); + EXPECT_EQ(10000u, pos.column); + EXPECT_EQ(0u, pos.line); + EXPECT_EQ(10000u, pos.index); +} } // namespace } // namespace spvtools diff --git a/test/text_to_binary.annotation_test.cpp b/test/text_to_binary.annotation_test.cpp index 61bdf64c..76776de9 100644 --- a/test/text_to_binary.annotation_test.cpp +++ b/test/text_to_binary.annotation_test.cpp @@ -398,7 +398,8 @@ TEST_F(TextToBinaryTest, GroupMemberDecorateGoodTwoTargets) { TEST_F(TextToBinaryTest, GroupMemberDecorateMissingGroupId) { EXPECT_THAT(CompileFailure("OpGroupMemberDecorate"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpGroupMemberDecorate instruction, but " + "found the end of the stream.")); } TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidGroupId) { @@ -413,7 +414,8 @@ TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidTargetId) { TEST_F(TextToBinaryTest, GroupMemberDecorateMissingTargetMemberNumber) { EXPECT_THAT(CompileFailure("OpGroupMemberDecorate %group %id0"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpGroupMemberDecorate instruction, but " + "found the end of the stream.")); } TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidTargetMemberNumber) { @@ -428,7 +430,8 @@ TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidSecondTargetId) { TEST_F(TextToBinaryTest, GroupMemberDecorateMissingSecondTargetMemberNumber) { EXPECT_THAT(CompileFailure("OpGroupMemberDecorate %group %id0 42 %id1"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpGroupMemberDecorate instruction, but " + "found the end of the stream.")); } TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidSecondTargetMemberNumber) { diff --git a/test/text_to_binary.barrier_test.cpp b/test/text_to_binary.barrier_test.cpp index 545d26ff..f1cb4fbe 100644 --- a/test/text_to_binary.barrier_test.cpp +++ b/test/text_to_binary.barrier_test.cpp @@ -44,7 +44,8 @@ TEST_F(OpMemoryBarrier, Good) { TEST_F(OpMemoryBarrier, BadMissingScopeId) { const std::string input = "OpMemoryBarrier\n"; EXPECT_THAT(CompileFailure(input), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpMemoryBarrier instruction, but found " + "the end of the stream.")); } TEST_F(OpMemoryBarrier, BadInvalidScopeId) { @@ -55,7 +56,8 @@ TEST_F(OpMemoryBarrier, BadInvalidScopeId) { TEST_F(OpMemoryBarrier, BadMissingMemorySemanticsId) { const std::string input = "OpMemoryBarrier %scope\n"; EXPECT_THAT(CompileFailure(input), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpMemoryBarrier instruction, but found " + "the end of the stream.")); } TEST_F(OpMemoryBarrier, BadInvalidMemorySemanticsId) { @@ -92,13 +94,16 @@ TEST_F(NamedMemoryBarrierTest, OpcodeAssemblesInV10) { TEST_F(NamedMemoryBarrierTest, ArgumentCount) { EXPECT_THAT(CompileFailure("OpMemoryNamedBarrier", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpMemoryNamedBarrier instruction, but " + "found the end of the stream.")); EXPECT_THAT( CompileFailure("OpMemoryNamedBarrier %bar", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpMemoryNamedBarrier instruction, but found the " + "end of the stream.")); EXPECT_THAT( CompileFailure("OpMemoryNamedBarrier %bar %scope", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpMemoryNamedBarrier instruction, but found the " + "end of the stream.")); EXPECT_THAT( CompiledInstructions("OpMemoryNamedBarrier %bar %scope %semantics", SPV_ENV_UNIVERSAL_1_1), @@ -151,10 +156,12 @@ TEST_F(NamedBarrierInitializeTest, OpcodeAssemblesInV10) { TEST_F(NamedBarrierInitializeTest, ArgumentCount) { EXPECT_THAT( CompileFailure("%bar = OpNamedBarrierInitialize", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpNamedBarrierInitialize instruction, but found " + "the end of the stream.")); EXPECT_THAT(CompileFailure("%bar = OpNamedBarrierInitialize %ype", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpNamedBarrierInitialize instruction, " + "but found the end of the stream.")); EXPECT_THAT( CompiledInstructions("%bar = OpNamedBarrierInitialize %type %count", SPV_ENV_UNIVERSAL_1_1), diff --git a/test/text_to_binary.control_flow_test.cpp b/test/text_to_binary.control_flow_test.cpp index 3e117b8f..472cb6da 100644 --- a/test/text_to_binary.control_flow_test.cpp +++ b/test/text_to_binary.control_flow_test.cpp @@ -163,7 +163,8 @@ TEST_F(TextToBinaryTest, SwitchGoodTwoTargets) { TEST_F(TextToBinaryTest, SwitchBadMissingSelector) { EXPECT_THAT(CompileFailure("OpSwitch"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpSwitch instruction, but found the end " + "of the stream.")); } TEST_F(TextToBinaryTest, SwitchBadInvalidSelector) { @@ -173,7 +174,8 @@ TEST_F(TextToBinaryTest, SwitchBadInvalidSelector) { TEST_F(TextToBinaryTest, SwitchBadMissingDefault) { EXPECT_THAT(CompileFailure("OpSwitch %selector"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpSwitch instruction, but found the end " + "of the stream.")); } TEST_F(TextToBinaryTest, SwitchBadInvalidDefault) { @@ -195,7 +197,8 @@ TEST_F(TextToBinaryTest, SwitchBadMissingTarget) { EXPECT_THAT(CompileFailure("%1 = OpTypeInt 32 0\n" "%2 = OpConstant %1 52\n" "OpSwitch %2 %default 12"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpSwitch instruction, but found the end " + "of the stream.")); } // A test case for an OpSwitch. @@ -379,7 +382,7 @@ INSTANTIATE_TEST_SUITE_P( "OpTypeQueue", "OpTypePipe ReadOnly", - // Skip OpTypeForwardPointer becasuse it doesn't even produce a result + // Skip OpTypeForwardPointer because it doesn't even produce a result // ID. // At least one thing that isn't a type at all diff --git a/test/text_to_binary.device_side_enqueue_test.cpp b/test/text_to_binary.device_side_enqueue_test.cpp index 03d7e741..2f4dd705 100644 --- a/test/text_to_binary.device_side_enqueue_test.cpp +++ b/test/text_to_binary.device_side_enqueue_test.cpp @@ -83,7 +83,8 @@ TEST_F(OpKernelEnqueueBad, MissingLastOperand) { CompileFailure( "%result = OpEnqueueKernel %type %queue %flags %NDRange %num_events" " %wait_events %ret_event %invoke %param %param_size"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpEnqueueKernel instruction, but found the end " + "of the stream.")); } TEST_F(OpKernelEnqueueBad, InvalidLastOperand) { diff --git a/test/text_to_binary.extension_test.cpp b/test/text_to_binary.extension_test.cpp index 8c42f72c..cf4919ac 100644 --- a/test/text_to_binary.extension_test.cpp +++ b/test/text_to_binary.extension_test.cpp @@ -197,7 +197,7 @@ INSTANTIATE_TEST_SUITE_P( SpvBuiltInSubgroupLtMask})}, }))); -// The old builtin names (with KHR suffix) still work in the assmebler, and +// The old builtin names (with KHR suffix) still work in the assembler, and // map to the enums without the KHR. INSTANTIATE_TEST_SUITE_P( SPV_KHR_shader_ballot_vulkan_1_1_alias_check, ExtensionAssemblyTest, @@ -935,6 +935,167 @@ INSTANTIATE_TEST_SUITE_P( MakeVector("SPV_KHR_expect_assume"))}, {"OpAssumeTrueKHR %1\n", MakeInstruction(SpvOpAssumeTrueKHR, {1})}}))); +// SPV_KHR_subgroup_uniform_control_flow + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_subgroup_uniform_control_flow, ExtensionRoundTripTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_3, + SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2), + ValuesIn(std::vector{ + {"OpExtension \"SPV_KHR_subgroup_uniform_control_flow\"\n", + MakeInstruction( + SpvOpExtension, + MakeVector("SPV_KHR_subgroup_uniform_control_flow"))}, + {"OpExecutionMode %1 SubgroupUniformControlFlowKHR\n", + MakeInstruction( + SpvOpExecutionMode, + {1, SpvExecutionModeSubgroupUniformControlFlowKHR})}, + }))); + +// SPV_KHR_integer_dot_product + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_integer_dot_product, ExtensionRoundTripTest, + Combine( + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5, + SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, + SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_3), + ValuesIn(std::vector{ + {"OpExtension \"SPV_KHR_integer_dot_product\"\n", + MakeInstruction(SpvOpExtension, + MakeVector("SPV_KHR_integer_dot_product"))}, + {"OpCapability DotProductInputAll\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityDotProductInputAllKHR})}, + {"OpCapability DotProductInput4x8Bit\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityDotProductInput4x8BitKHR})}, + {"OpCapability DotProductInput4x8BitPacked\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityDotProductInput4x8BitPackedKHR})}, + {"OpCapability DotProduct\n", + MakeInstruction(SpvOpCapability, {SpvCapabilityDotProductKHR})}, + {"%2 = OpSDot %1 %3 %4\n", + MakeInstruction(SpvOpSDotKHR, {1, 2, 3, 4})}, + {"%2 = OpSDot %1 %3 %4 PackedVectorFormat4x8Bit\n", + MakeInstruction( + SpvOpSDotKHR, + {1, 2, 3, 4, + SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, + {"%2 = OpUDot %1 %3 %4\n", + MakeInstruction(SpvOpUDotKHR, {1, 2, 3, 4})}, + {"%2 = OpUDot %1 %3 %4 PackedVectorFormat4x8Bit\n", + MakeInstruction( + SpvOpUDotKHR, + {1, 2, 3, 4, + SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, + {"%2 = OpSUDot %1 %3 %4\n", + MakeInstruction(SpvOpSUDotKHR, {1, 2, 3, 4})}, + {"%2 = OpSUDot %1 %3 %4 PackedVectorFormat4x8Bit\n", + MakeInstruction( + SpvOpSUDotKHR, + {1, 2, 3, 4, + SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, + {"%2 = OpSDotAccSat %1 %3 %4 %5\n", + MakeInstruction(SpvOpSDotAccSatKHR, {1, 2, 3, 4, 5})}, + {"%2 = OpSDotAccSat %1 %3 %4 %5 PackedVectorFormat4x8Bit\n", + MakeInstruction( + SpvOpSDotAccSatKHR, + {1, 2, 3, 4, 5, + SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, + {"%2 = OpUDotAccSat %1 %3 %4 %5\n", + MakeInstruction(SpvOpUDotAccSatKHR, {1, 2, 3, 4, 5})}, + {"%2 = OpUDotAccSat %1 %3 %4 %5 PackedVectorFormat4x8Bit\n", + MakeInstruction( + SpvOpUDotAccSatKHR, + {1, 2, 3, 4, 5, + SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, + {"%2 = OpSUDotAccSat %1 %3 %4 %5\n", + MakeInstruction(SpvOpSUDotAccSatKHR, {1, 2, 3, 4, 5})}, + {"%2 = OpSUDotAccSat %1 %3 %4 %5 PackedVectorFormat4x8Bit\n", + MakeInstruction( + SpvOpSUDotAccSatKHR, + {1, 2, 3, 4, 5, + SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, + }))); + +// SPV_KHR_bit_instructions + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_bit_instructions, ExtensionRoundTripTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5, + SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2), + ValuesIn(std::vector{ + {"OpExtension \"SPV_KHR_bit_instructions\"\n", + MakeInstruction(SpvOpExtension, + MakeVector("SPV_KHR_bit_instructions"))}, + {"OpCapability BitInstructions\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityBitInstructions})}, + }))); + +// SPV_KHR_uniform_group_instructions + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_uniform_group_instructions, ExtensionRoundTripTest, + Combine( + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5, + SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, + SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_3), + ValuesIn(std::vector{ + {"OpExtension \"SPV_KHR_uniform_group_instructions\"\n", + MakeInstruction(SpvOpExtension, + MakeVector("SPV_KHR_uniform_group_instructions"))}, + {"OpCapability GroupUniformArithmeticKHR\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityGroupUniformArithmeticKHR})}, + {"%2 = OpGroupIMulKHR %1 %3 Reduce %4\n", + MakeInstruction(SpvOpGroupIMulKHR, + {1, 2, 3, SpvGroupOperationReduce, 4})}, + {"%2 = OpGroupFMulKHR %1 %3 Reduce %4\n", + MakeInstruction(SpvOpGroupFMulKHR, + {1, 2, 3, SpvGroupOperationReduce, 4})}, + {"%2 = OpGroupBitwiseAndKHR %1 %3 Reduce %4\n", + MakeInstruction(SpvOpGroupBitwiseAndKHR, + {1, 2, 3, SpvGroupOperationReduce, 4})}, + {"%2 = OpGroupBitwiseOrKHR %1 %3 Reduce %4\n", + MakeInstruction(SpvOpGroupBitwiseOrKHR, + {1, 2, 3, SpvGroupOperationReduce, 4})}, + {"%2 = OpGroupBitwiseXorKHR %1 %3 Reduce %4\n", + MakeInstruction(SpvOpGroupBitwiseXorKHR, + {1, 2, 3, SpvGroupOperationReduce, 4})}, + {"%2 = OpGroupLogicalAndKHR %1 %3 Reduce %4\n", + MakeInstruction(SpvOpGroupLogicalAndKHR, + {1, 2, 3, SpvGroupOperationReduce, 4})}, + {"%2 = OpGroupLogicalOrKHR %1 %3 Reduce %4\n", + MakeInstruction(SpvOpGroupLogicalOrKHR, + {1, 2, 3, SpvGroupOperationReduce, 4})}, + {"%2 = OpGroupLogicalXorKHR %1 %3 Reduce %4\n", + MakeInstruction(SpvOpGroupLogicalXorKHR, + {1, 2, 3, SpvGroupOperationReduce, 4})}, + }))); + +// SPV_KHR_subgroup_rotate + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_subgroup_rotate, ExtensionRoundTripTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_6, + SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2, + SPV_ENV_VULKAN_1_3, SPV_ENV_OPENCL_2_1), + ValuesIn(std::vector{ + {"OpExtension \"SPV_KHR_subgroup_rotate\"\n", + MakeInstruction(SpvOpExtension, + MakeVector("SPV_KHR_subgroup_rotate"))}, + {"OpCapability GroupNonUniformRotateKHR\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityGroupNonUniformRotateKHR})}, + {"%2 = OpGroupNonUniformRotateKHR %1 %3 %4 %5\n", + MakeInstruction(SpvOpGroupNonUniformRotateKHR, + {1, 2, 3, 4, 5})}, + {"%2 = OpGroupNonUniformRotateKHR %1 %3 %4 %5 %6\n", + MakeInstruction(SpvOpGroupNonUniformRotateKHR, + {1, 2, 3, 4, 5, 6})}, + }))); } // namespace } // namespace spvtools diff --git a/test/text_to_binary.image_test.cpp b/test/text_to_binary.image_test.cpp index d445369c..8d8ff432 100644 --- a/test/text_to_binary.image_test.cpp +++ b/test/text_to_binary.image_test.cpp @@ -123,7 +123,8 @@ TEST_F(OpImageTest, InvalidTypeOperand) { TEST_F(OpImageTest, MissingSampledImageOperand) { EXPECT_THAT(CompileFailure("%2 = OpImage %1"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpImage instruction, but found the end " + "of the stream.")); } TEST_F(OpImageTest, InvalidSampledImageOperand) { @@ -222,7 +223,8 @@ TEST_F(OpImageSparseReadTest, InvalidTypeOperand) { TEST_F(OpImageSparseReadTest, MissingImageOperand) { EXPECT_THAT(CompileFailure("%2 = OpImageSparseRead %1"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpImageSparseRead instruction, but " + "found the end of the stream.")); } TEST_F(OpImageSparseReadTest, InvalidImageOperand) { @@ -232,7 +234,8 @@ TEST_F(OpImageSparseReadTest, InvalidImageOperand) { TEST_F(OpImageSparseReadTest, MissingCoordinateOperand) { EXPECT_THAT(CompileFailure("%2 = OpImageSparseRead %1 %2"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpImageSparseRead instruction, but " + "found the end of the stream.")); } TEST_F(OpImageSparseReadTest, InvalidCoordinateOperand) { diff --git a/test/text_to_binary.memory_test.cpp b/test/text_to_binary.memory_test.cpp index 7b09ed58..f94c134a 100644 --- a/test/text_to_binary.memory_test.cpp +++ b/test/text_to_binary.memory_test.cpp @@ -166,7 +166,8 @@ TEST_F(MemoryRoundTripTest, OpCopyMemoryNoMemAccessGood) { TEST_F(MemoryRoundTripTest, OpCopyMemoryTooFewArgsBad) { std::string spirv = "OpCopyMemory %1\n"; std::string err = CompileFailure(spirv); - EXPECT_THAT(err, HasSubstr("Expected operand, found end of stream")); + EXPECT_THAT(err, HasSubstr("Expected operand for OpCopyMemory instruction, " + "but found the end of the stream.")); } TEST_F(MemoryRoundTripTest, OpCopyMemoryTooManyArgsBad) { @@ -295,7 +296,8 @@ TEST_F(MemoryRoundTripTest, OpCopyMemorySizedNoMemAccessGood) { TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTooFewArgsBad) { std::string spirv = "OpCopyMemorySized %1 %2\n"; std::string err = CompileFailure(spirv); - EXPECT_THAT(err, HasSubstr("Expected operand, found end of stream")); + EXPECT_THAT(err, HasSubstr("Expected operand for OpCopyMemorySized " + "instruction, but found the end of the stream.")); } TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTooManyArgsBad) { diff --git a/test/text_to_binary.mode_setting_test.cpp b/test/text_to_binary.mode_setting_test.cpp index 8ddf4219..7f15c8b4 100644 --- a/test/text_to_binary.mode_setting_test.cpp +++ b/test/text_to_binary.mode_setting_test.cpp @@ -189,6 +189,7 @@ INSTANTIATE_TEST_SUITE_P( {CASE(OutputTriangleStrip), {}}, {CASE(VecTypeHint), {96}}, {CASE(ContractionOff), {}}, + {CASE(SubgroupUniformControlFlowKHR), {}}, }))); INSTANTIATE_TEST_SUITE_P( @@ -289,7 +290,8 @@ using TextToBinaryCapability = spvtest::TextToBinaryTest; TEST_F(TextToBinaryCapability, BadMissingCapability) { EXPECT_THAT(CompileFailure("OpCapability"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpCapability instruction, but found the " + "end of the stream.")); } TEST_F(TextToBinaryCapability, BadInvalidCapability) { diff --git a/test/text_to_binary.pipe_storage_test.cpp b/test/text_to_binary.pipe_storage_test.cpp index f74dbcfd..955f5ef8 100644 --- a/test/text_to_binary.pipe_storage_test.cpp +++ b/test/text_to_binary.pipe_storage_test.cpp @@ -59,10 +59,12 @@ TEST_F(OpConstantPipeStorageTest, ArgumentCount) { "'OpConstantPipeStorage'.")); EXPECT_THAT( CompileFailure("%1 = OpConstantPipeStorage", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpConstantPipeStorage instruction, but found " + "the end of the stream.")); EXPECT_THAT(CompileFailure("%1 = OpConstantPipeStorage %2 3 4", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpConstantPipeStorage instruction, but " + "found the end of the stream.")); EXPECT_THAT(CompiledInstructions("%1 = OpConstantPipeStorage %2 3 4 5", SPV_ENV_UNIVERSAL_1_1), Eq(MakeInstruction(SpvOpConstantPipeStorage, {1, 2, 3, 4, 5}))); @@ -101,10 +103,12 @@ TEST_F(OpCreatePipeFromPipeStorageTest, ArgumentCount) { "'OpCreatePipeFromPipeStorage'.")); EXPECT_THAT( CompileFailure("%1 = OpCreatePipeFromPipeStorage", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpCreatePipeFromPipeStorage instruction, but " + "found the end of the stream.")); EXPECT_THAT(CompileFailure("%1 = OpCreatePipeFromPipeStorage %2 OpNop", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found next instruction instead.")); + Eq("Expected operand for OpCreatePipeFromPipeStorage " + "instruction, but found the next instruction instead.")); EXPECT_THAT(CompiledInstructions("%1 = OpCreatePipeFromPipeStorage %2 %3", SPV_ENV_UNIVERSAL_1_1), Eq(MakeInstruction(SpvOpCreatePipeFromPipeStorage, {1, 2, 3}))); diff --git a/test/text_to_binary.subgroup_dispatch_test.cpp b/test/text_to_binary.subgroup_dispatch_test.cpp index 967e3c38..8c404457 100644 --- a/test/text_to_binary.subgroup_dispatch_test.cpp +++ b/test/text_to_binary.subgroup_dispatch_test.cpp @@ -46,11 +46,13 @@ TEST_F(OpGetKernelLocalSizeForSubgroupCountTest, ArgumentCount) { "found 'OpGetKernelLocalSizeForSubgroupCount'.")); EXPECT_THAT(CompileFailure("%res = OpGetKernelLocalSizeForSubgroupCount", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpGetKernelLocalSizeForSubgroupCount " + "instruction, but found the end of the stream.")); EXPECT_THAT( CompileFailure("%1 = OpGetKernelLocalSizeForSubgroupCount %2 %3 %4 %5 %6", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpGetKernelLocalSizeForSubgroupCount " + "instruction, but found the end of the stream.")); EXPECT_THAT( CompiledInstructions("%res = OpGetKernelLocalSizeForSubgroupCount %type " "%sgcount %invoke %param %param_size %param_align", @@ -93,10 +95,12 @@ TEST_F(OpGetKernelMaxNumSubgroupsTest, ArgumentCount) { "'OpGetKernelMaxNumSubgroups'.")); EXPECT_THAT(CompileFailure("%res = OpGetKernelMaxNumSubgroups", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpGetKernelMaxNumSubgroups instruction, " + "but found the end of the stream.")); EXPECT_THAT(CompileFailure("%1 = OpGetKernelMaxNumSubgroups %2 %3 %4 %5", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpGetKernelMaxNumSubgroups instruction, " + "but found the end of the stream.")); EXPECT_THAT( CompiledInstructions("%res = OpGetKernelMaxNumSubgroups %type " "%invoke %param %param_size %param_align", diff --git a/test/text_to_binary.type_declaration_test.cpp b/test/text_to_binary.type_declaration_test.cpp index 1589188f..65a23554 100644 --- a/test/text_to_binary.type_declaration_test.cpp +++ b/test/text_to_binary.type_declaration_test.cpp @@ -223,12 +223,14 @@ TEST_F(OpTypeForwardPointerTest, ValidStorageClass) { TEST_F(OpTypeForwardPointerTest, MissingType) { EXPECT_THAT(CompileFailure("OpTypeForwardPointer"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpTypeForwardPointer instruction, but " + "found the end of the stream.")); } TEST_F(OpTypeForwardPointerTest, MissingClass) { EXPECT_THAT(CompileFailure("OpTypeForwardPointer %pt"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpTypeForwardPointer instruction, but " + "found the end of the stream.")); } TEST_F(OpTypeForwardPointerTest, WrongClass) { @@ -252,7 +254,8 @@ TEST_F(OpSizeOfTest, ArgumentCount) { Eq("Expected at the beginning of an instruction, found " "'OpSizeOf'.")); EXPECT_THAT(CompileFailure("%res = OpSizeOf OpNop", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found next instruction instead.")); + Eq("Expected operand for OpSizeOf instruction, but found the " + "next instruction instead.")); EXPECT_THAT( CompiledInstructions("%1 = OpSizeOf %2 %3", SPV_ENV_UNIVERSAL_1_1), Eq(MakeInstruction(SpvOpSizeOf, {1, 2, 3}))); diff --git a/test/text_to_binary_test.cpp b/test/text_to_binary_test.cpp index 57f0a6ce..0b348e87 100644 --- a/test/text_to_binary_test.cpp +++ b/test/text_to_binary_test.cpp @@ -65,7 +65,7 @@ INSTANTIATE_TEST_SUITE_P( {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 1, "NotNaN"}, {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 2, "NotInf"}, {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 3, "NotNaN|NotInf"}, - // Mask experssions are symmetric. + // Mask expressions are symmetric. {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 3, "NotInf|NotNaN"}, // Repeating a value has no effect. {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 3, "NotInf|NotNaN|NotInf"}, @@ -247,12 +247,6 @@ INSTANTIATE_TEST_SUITE_P( {"0x1.804p4", 0x00004e01}, })); -TEST(CreateContext, InvalidEnvironment) { - spv_target_env env; - std::memset(&env, 99, sizeof(env)); - EXPECT_THAT(spvContextCreate(env), IsNull()); -} - TEST(CreateContext, UniversalEnvironment) { auto c = spvContextCreate(SPV_ENV_UNIVERSAL_1_0); EXPECT_THAT(c, NotNull()); diff --git a/test/tools/expect.py b/test/tools/expect.py index 0b51adc9..7351c029 100755 --- a/test/tools/expect.py +++ b/test/tools/expect.py @@ -285,6 +285,21 @@ class ValidObjectFile1_5(ReturnCodeIsZero, CorrectObjectFilePreamble): return True, '' +class ValidObjectFile1_6(ReturnCodeIsZero, CorrectObjectFilePreamble): + """Mixin class for checking that every input file generates a valid SPIR-V 1.6 + object file following the object file naming rule, and there is no output on + stdout/stderr.""" + + def check_object_file_preamble(self, status): + for input_filename in status.input_filenames: + object_filename = get_object_filename(input_filename) + success, message = self.verify_object_file_preamble( + os.path.join(status.directory, object_filename), 0x10600) + if not success: + return False, message + return True, '' + + class ValidObjectFileWithAssemblySubstr(SuccessfulReturn, CorrectObjectFilePreamble): """Mixin class for checking that every input file generates a valid object diff --git a/test/tools/opt/flags.py b/test/tools/opt/flags.py index c79f6807..52a43c51 100644 --- a/test/tools/opt/flags.py +++ b/test/tools/opt/flags.py @@ -34,7 +34,7 @@ def empty_main_assembly(): @inside_spirv_testsuite('SpirvOptBase') -class TestAssemblyFileAsOnlyParameter(expect.ValidObjectFile1_5): +class TestAssemblyFileAsOnlyParameter(expect.ValidObjectFile1_6): """Tests that spirv-opt accepts a SPIR-V object file.""" shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm') @@ -52,7 +52,7 @@ class TestHelpFlag(expect.ReturnCodeIsZero, expect.StdoutMatch): @inside_spirv_testsuite('SpirvOptFlags') -class TestValidPassFlags(expect.ValidObjectFile1_5, +class TestValidPassFlags(expect.ValidObjectFile1_6, expect.ExecutedListOfPasses): """Tests that spirv-opt accepts all valid optimization flags.""" @@ -72,7 +72,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_5, '--private-to-local', '--reduce-load-size', '--redundancy-elimination', '--remove-duplicates', '--replace-invalid-opcode', '--ssa-rewrite', '--scalar-replacement', '--scalar-replacement=42', '--strength-reduction', - '--strip-debug', '--strip-reflect', '--vector-dce', '--workaround-1209', + '--strip-debug', '--strip-nonsemantic', '--vector-dce', '--workaround-1209', '--unify-const', '--graphics-robust-access', '--wrap-opkill', '--amd-ext-to-khr' ] expected_passes = [ @@ -117,7 +117,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_5, 'scalar-replacement=42', 'strength-reduction', 'strip-debug', - 'strip-reflect', + 'strip-nonsemantic', 'vector-dce', 'workaround-1209', 'unify-const', @@ -132,7 +132,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_5, @inside_spirv_testsuite('SpirvOptFlags') -class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_5, +class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_6, expect.ExecutedListOfPasses): """Tests that spirv-opt schedules all the passes triggered by -O.""" @@ -190,7 +190,7 @@ class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_5, @inside_spirv_testsuite('SpirvOptFlags') -class TestSizeOptimizationPasses(expect.ValidObjectFile1_5, +class TestSizeOptimizationPasses(expect.ValidObjectFile1_6, expect.ExecutedListOfPasses): """Tests that spirv-opt schedules all the passes triggered by -Os.""" @@ -237,7 +237,7 @@ class TestSizeOptimizationPasses(expect.ValidObjectFile1_5, @inside_spirv_testsuite('SpirvOptFlags') -class TestLegalizationPasses(expect.ValidObjectFile1_5, +class TestLegalizationPasses(expect.ValidObjectFile1_6, expect.ExecutedListOfPasses): """Tests that spirv-opt schedules all the passes triggered by --legalize-hlsl. """ diff --git a/test/util/CMakeLists.txt b/test/util/CMakeLists.txt index 6679dba7..20038f76 100644 --- a/test/util/CMakeLists.txt +++ b/test/util/CMakeLists.txt @@ -16,6 +16,7 @@ add_spvtools_unittest(TARGET utils SRCS ilist_test.cpp bit_vector_test.cpp bitutils_test.cpp + hash_combine_test.cpp small_vector_test.cpp LIBS SPIRV-Tools-opt ) diff --git a/test/util/hash_combine_test.cpp b/test/util/hash_combine_test.cpp new file mode 100644 index 00000000..9cd1d870 --- /dev/null +++ b/test/util/hash_combine_test.cpp @@ -0,0 +1,43 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "source/util/hash_combine.h" + +namespace spvtools { +namespace utils { +namespace { + +using HashCombineTest = ::testing::Test; + +TEST(HashCombineTest, Identity) { EXPECT_EQ(hash_combine(0), 0); } + +TEST(HashCombineTest, Variadic) { + // Expect manual and variadic template versions be the same. + EXPECT_EQ(hash_combine(hash_combine(hash_combine(0, 1), 2), 3), + hash_combine(0, 1, 2, 3)); +} + +TEST(HashCombineTest, Vector) { + // Expect variadic and vector versions be the same. + EXPECT_EQ(hash_combine(0, std::vector({1, 2, 3})), + hash_combine(0, 1, 2, 3)); +} + +} // namespace +} // namespace utils +} // namespace spvtools diff --git a/test/util/small_vector_test.cpp b/test/util/small_vector_test.cpp index 01d7df18..b8f068eb 100644 --- a/test/util/small_vector_test.cpp +++ b/test/util/small_vector_test.cpp @@ -56,6 +56,18 @@ TEST(SmallVectorTest, Initialize_list2) { } } +TEST(SmallVectorTest, Initialize_list3) { + std::vector result = {0, 1, 2, 3}; + SmallVector vec(result.begin(), result.end()); + + EXPECT_FALSE(vec.empty()); + EXPECT_EQ(vec.size(), 4); + + for (uint32_t i = 0; i < vec.size(); ++i) { + EXPECT_EQ(vec[i], result[i]); + } +} + TEST(SmallVectorTest, Initialize_copy1) { SmallVector vec1 = {0, 1, 2, 3}; SmallVector vec2(vec1); @@ -593,6 +605,68 @@ TEST(SmallVectorTest, Resize6) { EXPECT_EQ(vec, result); } +TEST(SmallVectorTest, Pop_back) { + SmallVector vec = {0, 1, 2, 10, 10, 10}; + SmallVector result = {0, 1, 2}; + + EXPECT_EQ(vec.size(), 6); + vec.pop_back(); + vec.pop_back(); + vec.pop_back(); + EXPECT_EQ(vec.size(), 3); + EXPECT_EQ(vec, result); +} + +TEST(SmallVectorTest, Pop_back_TestDestructor) { + // Tracks number of constructions and destructions to ensure they are called. + struct TracksDtor { + TracksDtor& operator=(TracksDtor&&) = delete; + TracksDtor& operator=(const TracksDtor&) = delete; + + TracksDtor(int& num_ctors, int& num_dtors) + : num_ctors_(num_ctors), num_dtors_(num_dtors) { + num_ctors_++; + } + TracksDtor(const TracksDtor& that) + : TracksDtor(that.num_ctors_, that.num_dtors_) {} + TracksDtor(TracksDtor&& that) + : TracksDtor(that.num_ctors_, that.num_dtors_) {} + ~TracksDtor() { num_dtors_++; } + + int& num_ctors_; + int& num_dtors_; + }; + + constexpr int capacity = 4; + SmallVector vec; + + int num_ctors = 0; + int num_dtors = 0; + + // Make sure it works when staying within the smallvector capacity + for (int i = 0; i < capacity; ++i) { + vec.emplace_back(num_ctors, num_dtors); + } + + EXPECT_EQ(num_ctors, capacity); + while (!vec.empty()) { + vec.pop_back(); + } + + EXPECT_EQ(num_ctors, capacity); + EXPECT_EQ(num_dtors, num_ctors); + + // And when larger than builtin capacity + for (int i = 0; i < capacity * 2; ++i) { + vec.emplace_back(num_ctors, num_dtors); + } + + while (!vec.empty()) { + vec.pop_back(); + } + EXPECT_EQ(num_dtors, num_ctors); +} + } // namespace } // namespace utils } // namespace spvtools diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt index 4aec2497..de89b93f 100644 --- a/test/val/CMakeLists.txt +++ b/test/val/CMakeLists.txt @@ -23,6 +23,7 @@ set(VAL_TEST_COMMON_SRCS add_spvtools_unittest(TARGET val_abcde SRCS val_adjacency_test.cpp + val_annotation_test.cpp val_arithmetics_test.cpp val_atomics_test.cpp val_barriers_test.cpp @@ -35,13 +36,18 @@ add_spvtools_unittest(TARGET val_abcde val_data_test.cpp val_decoration_test.cpp val_derivatives_test.cpp - val_entry_point.cpp + val_entry_point_test.cpp val_explicit_reserved_test.cpp val_extensions_test.cpp - val_extension_spv_khr_expect_assume.cpp - val_extension_spv_khr_linkonce_odr.cpp - val_extension_spv_khr_terminate_invocation.cpp + val_extension_spv_khr_expect_assume_test.cpp + val_extension_spv_khr_linkonce_odr_test.cpp + val_extension_spv_khr_subgroup_uniform_control_flow_test.cpp + val_extension_spv_khr_integer_dot_product_test.cpp + val_extension_spv_khr_bit_instructions_test.cpp + val_extension_spv_khr_terminate_invocation_test.cpp + val_extension_spv_khr_subgroup_rotate_test.cpp val_ext_inst_test.cpp + val_ext_inst_debug_test.cpp ${VAL_TEST_COMMON_SRCS} LIBS ${SPIRV_TOOLS_FULL_VISIBILITY} PCH_FILE pch_test_val @@ -71,6 +77,7 @@ add_spvtools_unittest(TARGET val_fghijklmnop val_literals_test.cpp val_logicals_test.cpp val_memory_test.cpp + val_mesh_shading_test.cpp val_misc_test.cpp val_modes_test.cpp val_non_semantic_test.cpp @@ -82,8 +89,10 @@ add_spvtools_unittest(TARGET val_fghijklmnop PCH_FILE pch_test_val ) -add_spvtools_unittest(TARGET val_stuvw +add_spvtools_unittest(TARGET val_rstuvw SRCS + val_ray_query_test.cpp + val_ray_tracing_test.cpp val_small_type_uses_test.cpp val_ssa_test.cpp val_state_test.cpp diff --git a/test/val/val_adjacency_test.cpp b/test/val/val_adjacency_test.cpp index 29598535..4649948f 100644 --- a/test/val/val_adjacency_test.cpp +++ b/test/val/val_adjacency_test.cpp @@ -54,7 +54,7 @@ OpFunctionEnd CompileSuccessfully(module); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("ID 1[%bool] has not been defined")); + HasSubstr("ID '1[%bool]' has not been defined")); } TEST_F(ValidateAdjacency, OpLoopMergeEndsModuleFail) { diff --git a/test/val/val_annotation_test.cpp b/test/val/val_annotation_test.cpp new file mode 100644 index 00000000..bb30de0a --- /dev/null +++ b/test/val/val_annotation_test.cpp @@ -0,0 +1,955 @@ +// Copyright (c) 2021 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. + +// Validation tests for decorations + +#include +#include + +#include "gmock/gmock.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" +#include "test/val/val_code_generator.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::Combine; +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::Values; + +using DecorationTest = spvtest::ValidateBase; + +TEST_F(DecorationTest, WorkgroupSizeShader) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %ones BuiltIn WorkgroupSize +%int = OpTypeInt 32 0 +%int3 = OpTypeVector %int 3 +%int_1 = OpConstant %int 1 +%ones = OpConstantComposite %int3 %int_1 %int_1 %int_1 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(DecorationTest, WorkgroupSizeKernel) { + const std::string text = R"( +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +OpDecorate %var BuiltIn WorkgroupSize +%int = OpTypeInt 32 0 +%int3 = OpTypeVector %int 3 +%ptr = OpTypePointer Input %int3 +%var = OpVariable %ptr Input +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +using MemberOnlyDecorations = spvtest::ValidateBase; + +TEST_P(MemberOnlyDecorations, MemberDecoration) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpMemberDecorate %struct 0 )" + + deco + R"( +%float = OpTypeFloat 32 +%float2 = OpTypeVector %float 2 +%float2x2 = OpTypeMatrix %float2 2 +%struct = OpTypeStruct %float2x2 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(MemberOnlyDecorations, Decoration) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %struct )" + deco + + R"( +%float = OpTypeFloat 32 +%float2 = OpTypeVector %float 2 +%float2x2 = OpTypeMatrix %float2 2 +%struct = OpTypeStruct %float2x2 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("can only be applied to structure members")); +} + +INSTANTIATE_TEST_SUITE_P(ValidateMemberOnlyDecorations, MemberOnlyDecorations, + Values("RowMajor", "ColMajor", "MatrixStride 16" + // SPIR-V spec bug? + /*,"Offset 0"*/)); + +using NonMemberOnlyDecorations = spvtest::ValidateBase; + +TEST_P(NonMemberOnlyDecorations, MemberDecoration) { + const auto deco = GetParam(); + const auto text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpCapability InputAttachment +OpCapability Addresses +OpCapability PhysicalStorageBufferAddresses +OpCapability ShaderNonUniform +OpExtension "SPV_KHR_no_integer_wrap_decoration" +OpExtension "SPV_KHR_physical_storage_buffer" +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpExtension "SPV_EXT_descriptor_indexing" +OpMemoryModel Logical GLSL450 +OpMemberDecorate %struct 0 )" + + deco + R"( +%float = OpTypeFloat 32 +%float2 = OpTypeVector %float 2 +%float2x2 = OpTypeMatrix %float2 2 +%struct = OpTypeStruct %float2x2 +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("cannot be applied to structure members")); +} + +INSTANTIATE_TEST_SUITE_P( + ValidateNonMemberOnlyDecorations, NonMemberOnlyDecorations, + Values("SpecId 1", "Block", "BufferBlock", "ArrayStride 4", "GLSLShared", + "GLSLPacked", "CPacked", + // TODO: https://github.com/KhronosGroup/glslang/issues/703: + // glslang applies Restrict to structure members. + //"Restrict", + "Aliased", "Constant", "Uniform", "SaturatedConversion", "Index 0", + "Binding 0", "DescriptorSet 0", "FuncParamAttr Zext", + "FPRoundingMode RTE", "FPFastMathMode None", + "LinkageAttributes \"ext\" Import", "NoContraction", + "InputAttachmentIndex 0", "Alignment 4", "MaxByteOffset 4", + "AlignmentId %float", "MaxByteOffsetId %float", "NoSignedWrap", + "NoUnsignedWrap", "NonUniform", "RestrictPointer", "AliasedPointer", + "CounterBuffer %float")); + +using StructDecorations = spvtest::ValidateBase; + +TEST_P(StructDecorations, Struct) { + const std::string deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %struct )" + deco + + R"( +%struct = OpTypeStruct +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(StructDecorations, OtherType) { + const std::string deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %int )" + deco + R"( +%int = OpTypeInt 32 0 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); +} + +TEST_P(StructDecorations, Variable) { + const std::string deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %var )" + deco + R"( +%int = OpTypeInt 32 0 +%ptr = OpTypePointer Private %int +%var = OpVariable %ptr Private +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); +} + +TEST_P(StructDecorations, FunctionParameter) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %func LinkageAttributes "import" Import +OpDecorate %param )" + deco + + R"( +%int = OpTypeInt 32 0 +%void = OpTypeVoid +%fn = OpTypeFunction %void %int +%func = OpFunction %void None %fn +%param = OpFunctionParameter %int +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); +} + +TEST_P(StructDecorations, Constant) { + const std::string deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %int_0 )" + deco + + R"( +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); +} + +INSTANTIATE_TEST_SUITE_P(ValidateStructDecorations, StructDecorations, + Values("Block", "BufferBlock", "GLSLShared", + "GLSLPacked", "CPacked")); + +using ArrayDecorations = spvtest::ValidateBase; + +TEST_P(ArrayDecorations, Array) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %array )" + deco + + R"( +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%array = OpTypeArray %int %int_4 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ArrayDecorations, RuntimeArray) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %array )" + deco + + R"( +%int = OpTypeInt 32 0 +%array = OpTypeRuntimeArray %int +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ArrayDecorations, Pointer) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %ptr )" + deco + R"( +%int = OpTypeInt 32 0 +%ptr = OpTypePointer Workgroup %int +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ArrayDecorations, Struct) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %struct )" + deco + + R"( +%int = OpTypeInt 32 0 +%struct = OpTypeStruct %int +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be an array or pointer type")); +} + +TEST_P(ArrayDecorations, Variable) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %var )" + deco + R"( +%int = OpTypeInt 32 0 +%ptr = OpTypePointer Private %int +%var = OpVariable %ptr Private +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be an array or pointer type")); +} + +TEST_P(ArrayDecorations, FunctionParameter) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %func LinkageAttributes "import" Import +OpDecorate %param )" + deco + + R"( +%int = OpTypeInt 32 0 +%void = OpTypeVoid +%fn = OpTypeFunction %void %int +%func = OpFunction %void None %fn +%param = OpFunctionParameter %int +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be an array or pointer type")); +} + +TEST_P(ArrayDecorations, Constant) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %null )" + deco + + R"( +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%array = OpTypeArray %int %int_4 +%null = OpConstantNull %array +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be an array or pointer type")); +} + +INSTANTIATE_TEST_SUITE_P(ValidateArrayDecorations, ArrayDecorations, + Values("ArrayStride 4")); + +using BuiltInDecorations = spvtest::ValidateBase; + +TEST_P(BuiltInDecorations, Variable) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %var BuiltIn )" + + deco + R"( +%int = OpTypeInt 32 0 +%ptr = OpTypePointer Input %int +%var = OpVariable %ptr Input +)"; + + CompileSuccessfully(text); + if (deco != "WorkgroupSize") { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be a constant for WorkgroupSize")); + } +} + +TEST_P(BuiltInDecorations, IntegerType) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %int BuiltIn )" + + deco + R"( +%int = OpTypeInt 32 0 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("BuiltIns can only target variables, structure members " + "or constants")); +} + +TEST_P(BuiltInDecorations, FunctionParameter) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %func LinkageAttributes "import" Import +OpDecorate %param BuiltIn )" + + deco + R"( +%int = OpTypeInt 32 0 +%void = OpTypeVoid +%fn = OpTypeFunction %void %int +%func = OpFunction %void None %fn +%param = OpFunctionParameter %int +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("BuiltIns can only target variables, structure members " + "or constants")); +} + +TEST_P(BuiltInDecorations, Constant) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %const BuiltIn )" + + deco + R"( +%int = OpTypeInt 32 0 +%int3 = OpTypeVector %int 3 +%int_1 = OpConstant %int 1 +%const = OpConstantComposite %int3 %int_1 %int_1 %int_1 +)"; + + CompileSuccessfully(text); + if (deco == "WorkgroupSize") { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); + } +} + +TEST_P(BuiltInDecorations, SpecConstant) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %const BuiltIn )" + + deco + R"( +%int = OpTypeInt 32 0 +%int3 = OpTypeVector %int 3 +%int_1 = OpConstant %int 1 +%const = OpSpecConstantComposite %int3 %int_1 %int_1 %int_1 +)"; + + CompileSuccessfully(text); + if (deco == "WorkgroupSize") { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); + } +} + +INSTANTIATE_TEST_SUITE_P(ValidateBuiltInDecorations, BuiltInDecorations, + Values("Position", "PointSize", "VertexId", + "InstanceId", "FragCoord", "FrontFacing", + "NumWorkgroups", "WorkgroupSize", + "LocalInvocationId", "GlobalInvocationId")); + +using MemoryObjectDecorations = spvtest::ValidateBase; + +TEST_P(MemoryObjectDecorations, Variable) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpCapability SampleRateShading +OpCapability TransformFeedback +OpCapability GeometryStreams +OpCapability Tessellation +OpCapability PhysicalStorageBufferAddresses +OpExtension "SPV_KHR_physical_storage_buffer" +OpMemoryModel Logical GLSL450 +OpDecorate %var )" + deco + R"( +%float = OpTypeFloat 32 +%ptr = OpTypePointer Input %float +%var = OpVariable %ptr Input +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(MemoryObjectDecorations, FunctionParameterGood) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpCapability SampleRateShading +OpCapability TransformFeedback +OpCapability GeometryStreams +OpCapability Tessellation +OpCapability PhysicalStorageBufferAddresses +OpExtension "SPV_KHR_physical_storage_buffer" +OpMemoryModel Logical GLSL450 +OpDecorate %func LinkageAttributes "import" Import +OpDecorate %param )" + deco + + R"( +%float = OpTypeFloat 32 +%ptr = OpTypePointer Input %float +%void = OpTypeVoid +%fn = OpTypeFunction %void %ptr +%func = OpFunction %void None %fn +%param = OpFunctionParameter %ptr +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(MemoryObjectDecorations, FunctionParameterNotAPointer) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpCapability SampleRateShading +OpCapability TransformFeedback +OpCapability GeometryStreams +OpCapability Tessellation +OpCapability PhysicalStorageBufferAddresses +OpExtension "SPV_KHR_physical_storage_buffer" +OpMemoryModel Logical GLSL450 +OpDecorate %func LinkageAttributes "import" Import +OpDecorate %param )" + deco + + R"( +%float = OpTypeFloat 32 +%void = OpTypeVoid +%fn = OpTypeFunction %void %float +%func = OpFunction %void None %fn +%param = OpFunctionParameter %float +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a pointer type")); +} + +TEST_P(MemoryObjectDecorations, FloatType) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpCapability SampleRateShading +OpCapability TransformFeedback +OpCapability GeometryStreams +OpCapability Tessellation +OpCapability PhysicalStorageBufferAddresses +OpExtension "SPV_KHR_physical_storage_buffer" +OpMemoryModel Logical GLSL450 +OpDecorate %float )" + deco + + R"( +%float = OpTypeFloat 32 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be a memory object declaration")); +} + +TEST_P(MemoryObjectDecorations, Constant) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpCapability SampleRateShading +OpCapability TransformFeedback +OpCapability GeometryStreams +OpCapability Tessellation +OpCapability PhysicalStorageBufferAddresses +OpExtension "SPV_KHR_physical_storage_buffer" +OpMemoryModel Logical GLSL450 +OpDecorate %const )" + deco + + R"( +%float = OpTypeFloat 32 +%const = OpConstant %float 0 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be a memory object declaration")); +} + +// NonWritable and NonReadable are covered by other tests. +INSTANTIATE_TEST_SUITE_P( + ValidateMemoryObjectDecorations, MemoryObjectDecorations, + Values("NoPerspective", "Flat", "Patch", "Centroid", "Component 0", + "Sample", "Restrict", "Aliased", "Volatile", "Coherent", "Stream 0", + "XfbBuffer 1", "XfbStride 1", "AliasedPointer", "RestrictPointer")); + +using VariableDecorations = spvtest::ValidateBase; + +TEST_P(VariableDecorations, Variable) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpCapability InputAttachment +OpMemoryModel Logical GLSL450 +OpDecorate %var )" + deco + R"( +%float = OpTypeFloat 32 +%ptr = OpTypePointer Input %float +%var = OpVariable %ptr Input +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(VariableDecorations, FunctionParameter) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpCapability InputAttachment +OpMemoryModel Logical GLSL450 +OpDecorate %func LinkageAttributes "import" Import +OpDecorate %param )" + deco + + R"( +%float = OpTypeFloat 32 +%void = OpTypeVoid +%fn = OpTypeFunction %void %float +%func = OpFunction %void None %fn +%param = OpFunctionParameter %float +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); +} + +TEST_P(VariableDecorations, FloatType) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpCapability InputAttachment +OpMemoryModel Logical GLSL450 +OpDecorate %float )" + deco + + R"( +%float = OpTypeFloat 32 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); +} + +TEST_P(VariableDecorations, Constant) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpCapability InputAttachment +OpMemoryModel Logical GLSL450 +OpDecorate %const )" + deco + + R"( +%float = OpTypeFloat 32 +%const = OpConstant %float 0 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); +} + +INSTANTIATE_TEST_SUITE_P(ValidateVariableDecorations, VariableDecorations, + Values("Invariant", "Constant", "Location 0", + "Index 0", "Binding 0", "DescriptorSet 0")); + +using VulkanIOStorageClass = + spvtest::ValidateBase>; + +TEST_P(VulkanIOStorageClass, Invalid) { + const auto deco = std::get<0>(GetParam()); + const auto sc = std::get<1>(GetParam()); + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %var )" + deco + R"( 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%ptr = OpTypePointer )" + + sc + + R"( %float +%var = OpVariable %ptr )" + sc + + R"( +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-06672")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("decoration must not be applied to this storage class")); +} + +INSTANTIATE_TEST_SUITE_P(ValidateVulkanIOStorageClass, VulkanIOStorageClass, + Combine(Values("Location", "Component"), + Values("StorageBuffer", "Uniform", + "UniformConstant", "Workgroup", + "Private"))); + +using VulkanResourceStorageClass = + spvtest::ValidateBase>; + +TEST_P(VulkanResourceStorageClass, Invalid) { + const auto deco = std::get<0>(GetParam()); + const auto sc = std::get<1>(GetParam()); + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %var )" + deco + R"( 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%ptr = OpTypePointer )" + + sc + + R"( %float +%var = OpVariable %ptr )" + sc + + R"( +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("VUID-StandaloneSpirv-DescriptorSet-06491")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be in the StorageBuffer, Uniform, or " + "UniformConstant storage class")); +} + +INSTANTIATE_TEST_SUITE_P(ValidateVulkanResourceStorageClass, + VulkanResourceStorageClass, + Combine(Values("DescriptorSet", "Binding"), + Values("Private", "Input", "Output", + "Workgroup"))); + +using VulkanInterpolationStorageClass = spvtest::ValidateBase; + +TEST_P(VulkanInterpolationStorageClass, Input) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability SampleRateShading +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %var )" + deco + R"( +%void = OpTypeVoid +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%ptr = OpTypePointer Input %float +%var = OpVariable %ptr 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_P(VulkanInterpolationStorageClass, Output) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability SampleRateShading +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpDecorate %var )" + deco + R"( +%void = OpTypeVoid +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%ptr = OpTypePointer Output %float +%var = OpVariable %ptr 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_P(VulkanInterpolationStorageClass, Private) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability SampleRateShading +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %var )" + deco + R"( +%void = OpTypeVoid +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%ptr = OpTypePointer Private %float +%var = OpVariable %ptr Private +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("storage class must be Input or Output")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("[VUID-StandaloneSpirv-Flat-04670")); +} + +TEST_P(VulkanInterpolationStorageClass, Uniform) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability SampleRateShading +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %var )" + deco + R"( +OpDecorate %var Binding 0 +OpDecorate %var DescriptorSet 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%ptr = OpTypePointer Uniform %float +%var = OpVariable %ptr Uniform +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("storage class must be Input or Output")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("[VUID-StandaloneSpirv-Flat-04670")); +} + +TEST_P(VulkanInterpolationStorageClass, StorageBuffer) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability SampleRateShading +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %var )" + deco + R"( +OpDecorate %var Binding 0 +OpDecorate %var DescriptorSet 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%ptr = OpTypePointer StorageBuffer %float +%var = OpVariable %ptr StorageBuffer +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("storage class must be Input or Output")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("[VUID-StandaloneSpirv-Flat-04670")); +} + +INSTANTIATE_TEST_SUITE_P(ValidateVulkanInterpolationStorageClass, + VulkanInterpolationStorageClass, + Values("Flat", "NoPerspective", "Centroid", "Sample")); + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/test/val/val_arithmetics_test.cpp b/test/val/val_arithmetics_test.cpp index b82fc97e..631375ef 100644 --- a/test/val/val_arithmetics_test.cpp +++ b/test/val/val_arithmetics_test.cpp @@ -606,8 +606,9 @@ TEST_F(ValidateArithmetics, DotNotVectorTypeOperand1) { CompileSuccessfully(GenerateCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 6[%float] cannot be a " - "type")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand '6[%float]' cannot be a " + "type")); } TEST_F(ValidateArithmetics, DotNotVectorTypeOperand2) { @@ -656,7 +657,7 @@ TEST_F(ValidateArithmetics, DotDifferentVectorSize) { EXPECT_THAT( getDiagnosticString(), HasSubstr( - "Expected operands to have the same number of componenets: Dot")); + "Expected operands to have the same number of components: Dot")); } TEST_F(ValidateArithmetics, VectorTimesScalarSuccess) { @@ -1309,6 +1310,57 @@ TEST_F(ValidateArithmetics, CoopMatDimFail) { HasSubstr("Cooperative matrix 'M' mismatch: CooperativeMatrixMulAddNV")); } +TEST_F(ValidateArithmetics, CoopMatComponentTypeNotScalarNumeric) { + const std::string types = R"( +%bad = OpTypeCooperativeMatrixNV %bool %subgroup %u32_8 %u32_8 +)"; + + CompileSuccessfully(GenerateCoopMatCode(types, "").c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpTypeCooperativeMatrixNV Component Type " + "'4[%bool]' is not a scalar numerical type.")); +} + +TEST_F(ValidateArithmetics, CoopMatScopeNotConstantInt) { + const std::string types = R"( +%bad = OpTypeCooperativeMatrixNV %f16 %f32_1 %u32_8 %u32_8 +)"; + + CompileSuccessfully(GenerateCoopMatCode(types, "").c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpTypeCooperativeMatrixNV Scope '17[%float_1]' is not a " + "constant instruction with scalar integer type.")); +} + +TEST_F(ValidateArithmetics, CoopMatRowsNotConstantInt) { + const std::string types = R"( +%bad = OpTypeCooperativeMatrixNV %f16 %subgroup %f32_1 %u32_8 +)"; + + CompileSuccessfully(GenerateCoopMatCode(types, "").c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpTypeCooperativeMatrixNV Rows '17[%float_1]' is not a " + "constant instruction with scalar integer type.")); +} + +TEST_F(ValidateArithmetics, CoopMatColumnsNotConstantInt) { + const std::string types = R"( +%bad = OpTypeCooperativeMatrixNV %f16 %subgroup %u32_8 %f32_1 +)"; + + CompileSuccessfully(GenerateCoopMatCode(types, "").c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpTypeCooperativeMatrixNV Cols '17[%float_1]' is not a " + "constant instruction with scalar integer type.")); +} + TEST_F(ValidateArithmetics, IAddCarrySuccess) { const std::string body = R"( %val1 = OpIAddCarry %struct_u32_u32 %u32_0 %u32_1 diff --git a/test/val/val_atomics_test.cpp b/test/val/val_atomics_test.cpp index e2ca71fe..b266ad66 100644 --- a/test/val/val_atomics_test.cpp +++ b/test/val/val_atomics_test.cpp @@ -101,7 +101,7 @@ std::string GenerateShaderCode( OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft )"; - const std::string defintions = R"( + const std::string definitions = R"( %u64 = OpTypeInt 64 0 %s64 = OpTypeInt 64 1 @@ -115,19 +115,19 @@ OpExecutionMode %main OriginUpperLeft )"; return GenerateShaderCodeImpl( body, "OpCapability Int64\n" + capabilities_and_extensions, - defintions + extra_defs, - memory_model, execution); + definitions + extra_defs, memory_model, execution); } std::string GenerateShaderComputeCode( const std::string& body, const std::string& capabilities_and_extensions = "", + const std::string& extra_defs = "", const std::string& memory_model = "GLSL450") { const std::string execution = R"( OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 32 1 1 )"; - const std::string defintions = R"( + const std::string definitions = R"( %u64 = OpTypeInt 64 0 %s64 = OpTypeInt 64 1 @@ -140,8 +140,8 @@ OpExecutionMode %main LocalSize 32 1 1 %s64_var = OpVariable %s64_ptr Workgroup )"; return GenerateShaderCodeImpl( - body, "OpCapability Int64\n" + capabilities_and_extensions, defintions, - memory_model, execution); + body, "OpCapability Int64\n" + capabilities_and_extensions, + definitions + extra_defs, memory_model, execution); } std::string GenerateKernelCode( @@ -269,6 +269,21 @@ TEST_F(ValidateAtomics, AtomicLoadInt32VulkanSuccess) { ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } +TEST_F(ValidateAtomics, AtomicLoadVulkanWrongStorageClass) { + const std::string body = R"( +%val1 = OpAtomicLoad %u32 %u32_var %device %relaxed +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04645")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("in Vulkan environment, Workgroup Storage Class is limited to " + "MeshNV, TaskNV, and GLCompute execution model")); +} + TEST_F(ValidateAtomics, AtomicAddIntVulkanWrongType1) { const std::string body = R"( %val1 = OpAtomicIAdd %f32 %f32_var %device %relaxed %f32_1 @@ -303,7 +318,7 @@ TEST_F(ValidateAtomics, AtomicAddFloatVulkan) { EXPECT_THAT( getDiagnosticString(), HasSubstr("Opcode AtomicFAddEXT requires one of these capabilities: " - "AtomicFloat32AddEXT AtomicFloat64AddEXT")); + "AtomicFloat32AddEXT AtomicFloat64AddEXT AtomicFloat16AddEXT")); } TEST_F(ValidateAtomics, AtomicMinFloatVulkan) { @@ -524,6 +539,27 @@ OpExtension "SPV_EXT_shader_atomic_float_min_max" "require the AtomicFloat32MinMaxEXT capability")); } +TEST_F(ValidateAtomics, AtomicAddFloat16VulkanSuccess) { + const std::string defs = R"( +%f16 = OpTypeFloat 16 +%f16_1 = OpConstant %f16 1 +%f16_ptr = OpTypePointer Workgroup %f16 +%f16_var = OpVariable %f16_ptr Workgroup +)"; + const std::string body = R"( +%val1 = OpAtomicFAddEXT %f16 %f16_var %device %relaxed %f16_1 +)"; + const std::string extra = R"( +OpCapability Float16 +OpCapability AtomicFloat16AddEXT +OpExtension "SPV_EXT_shader_atomic_float16_add" +)"; + + CompileSuccessfully(GenerateShaderComputeCode(body, extra, defs), + SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + TEST_F(ValidateAtomics, AtomicAddFloatVulkanSuccess) { const std::string body = R"( %val1 = OpAtomicFAddEXT %f32 %f32_var %device %relaxed %f32_1 @@ -534,7 +570,8 @@ OpCapability AtomicFloat32AddEXT OpExtension "SPV_EXT_shader_atomic_float_add" )"; - CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0); + CompileSuccessfully(GenerateShaderComputeCode(body, extra), + SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } @@ -554,7 +591,8 @@ OpCapability AtomicFloat16MinMaxEXT OpExtension "SPV_EXT_shader_atomic_float_min_max" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, defs), SPV_ENV_VULKAN_1_0); + CompileSuccessfully(GenerateShaderComputeCode(body, extra, defs), + SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } @@ -574,7 +612,8 @@ OpCapability AtomicFloat16MinMaxEXT OpExtension "SPV_EXT_shader_atomic_float_min_max" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, defs), SPV_ENV_VULKAN_1_0); + CompileSuccessfully(GenerateShaderComputeCode(body, extra, defs), + SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } @@ -587,7 +626,8 @@ OpCapability AtomicFloat32MinMaxEXT OpExtension "SPV_EXT_shader_atomic_float_min_max" )"; - CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0); + CompileSuccessfully(GenerateShaderComputeCode(body, extra), + SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } @@ -600,7 +640,8 @@ OpCapability AtomicFloat32MinMaxEXT OpExtension "SPV_EXT_shader_atomic_float_min_max" )"; - CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0); + CompileSuccessfully(GenerateShaderComputeCode(body, extra), + SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } @@ -620,7 +661,8 @@ OpCapability AtomicFloat64MinMaxEXT OpExtension "SPV_EXT_shader_atomic_float_min_max" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, defs), SPV_ENV_VULKAN_1_0); + CompileSuccessfully(GenerateShaderComputeCode(body, extra, defs), + SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } @@ -640,7 +682,8 @@ OpCapability AtomicFloat64MinMaxEXT OpExtension "SPV_EXT_shader_atomic_float_min_max" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, defs), SPV_ENV_VULKAN_1_0); + CompileSuccessfully(GenerateShaderComputeCode(body, extra, defs), + SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } @@ -654,12 +697,27 @@ TEST_F(ValidateAtomics, AtomicLoadFloatVulkan) { ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } -TEST_F(ValidateAtomics, AtomicStoreFloatVulkan) { +TEST_F(ValidateAtomics, AtomicStoreVulkanWrongStorageClass) { const std::string body = R"( OpAtomicStore %f32_var %device %relaxed %f32_1 )"; CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04645")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("in Vulkan environment, Workgroup Storage Class is limited to " + "MeshNV, TaskNV, and GLCompute execution model")); +} + +TEST_F(ValidateAtomics, AtomicStoreFloatVulkan) { + const std::string body = R"( +OpAtomicStore %f32_var %device %relaxed %f32_1 +)"; + + CompileSuccessfully(GenerateShaderComputeCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } @@ -668,7 +726,7 @@ TEST_F(ValidateAtomics, AtomicExchangeFloatVulkan) { %val2 = OpAtomicExchange %f32 %f32_var %device %relaxed %f32_0 )"; - CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + CompileSuccessfully(GenerateShaderComputeCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } @@ -720,8 +778,8 @@ OpAtomicStore %f32_var_function %device %relaxed %f32_1 EXPECT_THAT( getDiagnosticString(), HasSubstr("AtomicStore: Vulkan spec only allows storage classes for " - "atomic to be: Uniform, Workgroup, Image, StorageBuffer, or " - "PhysicalStorageBuffer.")); + "atomic to be: Uniform, Workgroup, Image, StorageBuffer, " + "PhysicalStorageBuffer or TaskPayloadWorkgroupEXT.")); } TEST_F(ValidateAtomics, AtomicStoreFunctionPointerStorageType) { @@ -903,8 +961,9 @@ OpAtomicStore %u64_var %device %relaxed %u64_1 OpAtomicStore %s64_var %device %relaxed %s64_1 )"; - CompileSuccessfully(GenerateShaderCode(body, "OpCapability Int64Atomics\n"), - SPV_ENV_VULKAN_1_0); + CompileSuccessfully( + GenerateShaderComputeCode(body, "OpCapability Int64Atomics\n"), + SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } @@ -941,8 +1000,9 @@ TEST_F(ValidateAtomics, AtomicLoadWrongPointerType) { CompileSuccessfully(GenerateKernelCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Operand 27[%_ptr_Workgroup_float] cannot be a type")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Operand '27[%_ptr_Workgroup_float]' cannot be a type")); } TEST_F(ValidateAtomics, AtomicLoadWrongPointerDataType) { @@ -1007,7 +1067,7 @@ OpAtomicStore %u32_var %device %release %u32_1 OpAtomicStore %u32_var %invocation %relaxed %u32_1 )"; - CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + CompileSuccessfully(GenerateShaderComputeCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } @@ -1214,7 +1274,7 @@ TEST_F(ValidateAtomics, AtomicExchangeWrongPointerType) { CompileSuccessfully(GenerateKernelCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Operand 33[%_ptr_Workgroup_v4float] cannot be a " + HasSubstr("Operand '33[%_ptr_Workgroup_v4float]' cannot be a " "type")); } @@ -1341,7 +1401,7 @@ TEST_F(ValidateAtomics, AtomicCompareExchangeWrongPointerType) { CompileSuccessfully(GenerateKernelCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Operand 33[%_ptr_Workgroup_v4float] cannot be a " + HasSubstr("Operand '33[%_ptr_Workgroup_v4float]' cannot be a " "type")); } @@ -2297,7 +2357,7 @@ OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), + CompileSuccessfully(GenerateShaderComputeCode(body, extra, "", "VulkanKHR"), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); } @@ -2620,6 +2680,39 @@ OpFunctionEnd "CooperativeMatrixNV capability is present")); } +TEST_F(ValidateAtomics, IIncrementBadPointerDataType) { + const std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + %uint = OpTypeInt 32 0 +%_ptr_Input_uint = OpTypePointer Input %uint + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint + %void = OpTypeVoid + %16 = OpTypeFunction %void +%uint_538976288 = OpConstant %uint 538976288 + %int = OpTypeInt 32 1 +%_runtimearr_int = OpTypeRuntimeArray %int + %_struct_5 = OpTypeStruct %_runtimearr_int +%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5 + %3 = OpVariable %_ptr_Input_v3uint Input + %7 = OpVariable %_ptr_Uniform__struct_5 Uniform + %8224 = OpFunction %void None %16 + %65312 = OpLabel + %25 = OpAccessChain %_ptr_Input_uint %3 %uint_538976288 + %26 = OpLoad %uint %25 + %2097184 = OpAtomicIIncrement %int %7 %uint_538976288 %26 + OpUnreachable + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicIIncrement: expected Pointer to point to a " + "value of type Result Type")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_barriers_test.cpp b/test/val/val_barriers_test.cpp index 1178ca02..c86cdc13 100644 --- a/test/val/val_barriers_test.cpp +++ b/test/val/val_barriers_test.cpp @@ -254,8 +254,9 @@ OpControlBarrier %device %device %none ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_2)); EXPECT_THAT( getDiagnosticString(), - HasSubstr("OpControlBarrier requires one of the following Execution " - "Models: TessellationControl, GLCompute or Kernel")); + HasSubstr("OpControlBarrier requires one of the following " + "Execution Models: TessellationControl, GLCompute, Kernel, " + "MeshNV or TaskNV")); } TEST_F(ValidateBarriers, OpControlBarrierExecutionModelFragmentSpirv13) { @@ -360,11 +361,26 @@ OpControlBarrier %subgroup %subgroup %none CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-None-04638")); + AnyVUID("VUID-StandaloneSpirv-SubgroupVoteKHR-06997")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("ControlBarrier: in Vulkan 1.0 environment Memory Scope is " - "limited to Device, Workgroup and Invocation")); + HasSubstr( + "ControlBarrier: in Vulkan 1.0 environment Memory Scope is can not " + "be Subgroup without SubgroupBallotKHR or SubgroupVoteKHR declared")); +} + +TEST_F(ValidateBarriers, OpControlBarrierVulkanMemoryScopeSubgroupVoteKHR) { + const std::string capabilities = R"( +OpCapability SubgroupVoteKHR +OpExtension "SPV_KHR_subgroup_vote" +)"; + const std::string body = R"( +OpControlBarrier %subgroup %subgroup %none +)"; + + CompileSuccessfully(GenerateShaderCode(body, capabilities), + SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1MemoryScopeSubgroup) { @@ -386,8 +402,9 @@ OpControlBarrier %subgroup %cross_device %none EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-StandaloneSpirv-None-04638")); EXPECT_THAT(getDiagnosticString(), - HasSubstr("ControlBarrier: in Vulkan environment, Memory Scope " - "cannot be CrossDevice")); + HasSubstr("ControlBarrier: in Vulkan environment Memory Scope is " + "limited to Device, QueueFamily, Workgroup, " + "ShaderCallKHR, Subgroup, or Invocation")); } TEST_F(ValidateBarriers, @@ -399,10 +416,12 @@ OpControlBarrier %subgroup %workgroup %acquire CompileSuccessfully(GenerateVulkanVertexShaderCode(body), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-None-04639")); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Workgroup Memory Scope is limited to MeshNV, TaskNV, " - "and GLCompute execution model")); + AnyVUID("VUID-StandaloneSpirv-None-07321")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Workgroup Memory Scope is limited to MeshNV, " + "TaskNV, MeshEXT, TaskEXT, TessellationControl, and GLCompute " + "execution model")); } TEST_F(ValidateBarriers, @@ -417,8 +436,8 @@ OpControlBarrier %workgroup %subgroup %acquire AnyVUID("VUID-StandaloneSpirv-None-04637")); EXPECT_THAT(getDiagnosticString(), HasSubstr("in Vulkan environment, Workgroup execution scope is " - "only for TaskNV, MeshNV, TessellationControl, and " - "GLCompute execution models")); + "only for TaskNV, MeshNV, TaskEXT, MeshEXT, " + "TessellationControl, and GLCompute execution models")); } TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupComputeSuccess) { @@ -528,10 +547,11 @@ OpControlBarrier %subgroup %workgroup %acquire_release CompileSuccessfully(GenerateShaderCode(body, "", "Fragment"), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpControlBarrier requires one of the following Execution " - "Models: TessellationControl, GLCompute or Kernel")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpControlBarrier requires one of the following " + "Execution " + "Models: TessellationControl, GLCompute, Kernel, " + "MeshNV or TaskNV")); } TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionVertex1p1) { @@ -572,8 +592,9 @@ OpControlBarrier %subgroup %workgroup %acquire_release ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT( getDiagnosticString(), - HasSubstr("OpControlBarrier requires one of the following Execution " - "Models: TessellationControl, GLCompute or Kernel")); + HasSubstr("OpControlBarrier requires one of the following " + "Execution Models: TessellationControl, GLCompute, Kernel, " + "MeshNV or TaskNV")); } TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionGeometry1p1) { @@ -615,10 +636,11 @@ OpControlBarrier %subgroup %workgroup %acquire_release GenerateShaderCode(body, "OpCapability Geometry\n", "Geometry"), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpControlBarrier requires one of the following Execution " - "Models: TessellationControl, GLCompute or Kernel")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpControlBarrier requires one of the following " + "Execution " + "Models: TessellationControl, GLCompute, Kernel, " + "MeshNV or TaskNV")); } TEST_F(ValidateBarriers, @@ -663,10 +685,11 @@ OpControlBarrier %subgroup %workgroup %acquire_release "TessellationEvaluation"), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpControlBarrier requires one of the following Execution " - "Models: TessellationControl, GLCompute or Kernel")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpControlBarrier requires one of the following " + "Execution " + "Models: TessellationControl, GLCompute, Kernel, " + "MeshNV or TaskNV")); } TEST_F(ValidateBarriers, OpMemoryBarrierSuccess) { @@ -752,11 +775,12 @@ OpMemoryBarrier %subgroup %acquire_release_uniform_workgroup CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-None-04638")); + AnyVUID("VUID-StandaloneSpirv-SubgroupVoteKHR-06997")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("MemoryBarrier: in Vulkan 1.0 environment Memory Scope is " - "limited to Device, Workgroup and Invocation")); + HasSubstr( + "MemoryBarrier: in Vulkan 1.0 environment Memory Scope is can not be " + "Subgroup without SubgroupBallotKHR or SubgroupVoteKHR declared")); } TEST_F(ValidateBarriers, OpMemoryBarrierVulkan1p1MemoryScopeSubgroup) { @@ -947,7 +971,7 @@ OpMemoryBarrier %u32 %u32_0 CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 5[%uint] cannot be a " + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand '5[%uint]' cannot be a " "type")); } diff --git a/test/val/val_bitwise_test.cpp b/test/val/val_bitwise_test.cpp index 1001def8..bebaa84f 100644 --- a/test/val/val_bitwise_test.cpp +++ b/test/val/val_bitwise_test.cpp @@ -340,6 +340,16 @@ TEST_F(ValidateBitwise, OpBitFieldInsertSuccess) { ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateBitwise, OpBitFieldInsertVulkanSuccess) { + const std::string body = R"( +%val1 = OpBitFieldInsert %u32 %u32_1 %u32_2 %s32_1 %s32_2 +%val2 = OpBitFieldInsert %s32vec2 %s32vec2_12 %s32vec2_12 %s32_1 %u32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + TEST_F(ValidateBitwise, OpBitFieldInsertWrongResultType) { const std::string body = R"( %val1 = OpBitFieldInsert %bool %u64_1 %u64_2 %s32_1 %s32_2 @@ -350,7 +360,7 @@ TEST_F(ValidateBitwise, OpBitFieldInsertWrongResultType) { EXPECT_THAT( getDiagnosticString(), HasSubstr( - "Expected int scalar or vector type as Result Type: BitFieldInsert")); + "Expected Base Type to be equal to Result Type: BitFieldInsert")); } TEST_F(ValidateBitwise, OpBitFieldInsertWrongBaseType) { @@ -403,6 +413,20 @@ TEST_F(ValidateBitwise, OpBitFieldInsertCountNotInt) { HasSubstr("Expected Count Type to be int scalar: BitFieldInsert")); } +TEST_F(ValidateBitwise, OpBitFieldInsertNot32Vulkan) { + const std::string body = R"( +%val1 = OpBitFieldInsert %u64 %u64_1 %u64_2 %s32_1 %s32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Base-04781")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected 32-bit int type for Base operand: BitFieldInsert")); +} + TEST_F(ValidateBitwise, OpBitFieldSExtractSuccess) { const std::string body = R"( %val1 = OpBitFieldSExtract %u64 %u64_1 %s32_1 %s32_2 @@ -413,6 +437,16 @@ TEST_F(ValidateBitwise, OpBitFieldSExtractSuccess) { ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateBitwise, OpBitFieldSExtractVulkanSuccess) { + const std::string body = R"( +%val1 = OpBitFieldSExtract %u32 %u32_1 %s32_1 %s32_2 +%val2 = OpBitFieldSExtract %s32vec2 %s32vec2_12 %s32_1 %u32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + TEST_F(ValidateBitwise, OpBitFieldSExtractWrongResultType) { const std::string body = R"( %val1 = OpBitFieldSExtract %bool %u64_1 %s32_1 %s32_2 @@ -420,9 +454,10 @@ TEST_F(ValidateBitwise, OpBitFieldSExtractWrongResultType) { CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected int scalar or vector type as Result Type: " - "BitFieldSExtract")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Base Type to be equal to Result Type: BitFieldSExtract")); } TEST_F(ValidateBitwise, OpBitFieldSExtractWrongBaseType) { @@ -462,6 +497,20 @@ TEST_F(ValidateBitwise, OpBitFieldSExtractCountNotInt) { HasSubstr("Expected Count Type to be int scalar: BitFieldSExtract")); } +TEST_F(ValidateBitwise, OpBitFieldSExtractNot32Vulkan) { + const std::string body = R"( +%val1 = OpBitFieldSExtract %u64 %u64_1 %s32_1 %s32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Base-04781")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected 32-bit int type for Base operand: BitFieldSExtract")); +} + TEST_F(ValidateBitwise, OpBitReverseSuccess) { const std::string body = R"( %val1 = OpBitReverse %u64 %u64_1 @@ -472,6 +521,16 @@ TEST_F(ValidateBitwise, OpBitReverseSuccess) { ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateBitwise, OpBitReverseVulkanSuccess) { + const std::string body = R"( +%val1 = OpBitReverse %u32 %u32_1 +%val2 = OpBitReverse %s32vec2 %s32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + TEST_F(ValidateBitwise, OpBitReverseWrongResultType) { const std::string body = R"( %val1 = OpBitReverse %bool %u64_1 @@ -481,8 +540,7 @@ TEST_F(ValidateBitwise, OpBitReverseWrongResultType) { ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr( - "Expected int scalar or vector type as Result Type: BitReverse")); + HasSubstr("Expected Base Type to be equal to Result Type: BitReverse")); } TEST_F(ValidateBitwise, OpBitReverseWrongBaseType) { @@ -497,16 +555,41 @@ TEST_F(ValidateBitwise, OpBitReverseWrongBaseType) { HasSubstr("Expected Base Type to be equal to Result Type: BitReverse")); } +TEST_F(ValidateBitwise, OpBitReverseNot32Vulkan) { + const std::string body = R"( +%val1 = OpBitReverse %u64 %u64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Base-04781")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected 32-bit int type for Base operand: BitReverse")); +} + TEST_F(ValidateBitwise, OpBitCountSuccess) { const std::string body = R"( %val1 = OpBitCount %s32 %u64_1 %val2 = OpBitCount %u32vec2 %s32vec2_12 +%val3 = OpBitCount %s64 %s64_1 )"; CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateBitwise, OpBitCountVulkanSuccess) { + const std::string body = R"( +%val1 = OpBitCount %s32 %u32_1 +%val2 = OpBitCount %u32vec2 %s32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + TEST_F(ValidateBitwise, OpBitCountWrongResultType) { const std::string body = R"( %val1 = OpBitCount %bool %u64_1 @@ -524,11 +607,14 @@ TEST_F(ValidateBitwise, OpBitCountBaseNotInt) { %val1 = OpBitCount %u32 %f64_1 )"; - CompileSuccessfully(GenerateShaderCode(body).c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Base-04781")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("Expected Base Type to be int scalar or vector: BitCount")); + HasSubstr( + "Expected int scalar or vector type for Base operand: BitCount")); } TEST_F(ValidateBitwise, OpBitCountBaseWrongDimension) { @@ -544,6 +630,19 @@ TEST_F(ValidateBitwise, OpBitCountBaseWrongDimension) { "BitCount")); } +TEST_F(ValidateBitwise, OpBitCountNot32Vulkan) { + const std::string body = R"( +%val1 = OpBitCount %s64 %s64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Base-04781")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected 32-bit int type for Base operand: BitCount")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp index 2116fff2..4f9fc976 100644 --- a/test/val/val_builtins_test.cpp +++ b/test/val/val_builtins_test.cpp @@ -60,8 +60,9 @@ using ValidateVulkanSubgroupBuiltIns = using ValidateVulkanCombineBuiltInExecutionModelDataTypeResult = spvtest::ValidateBase>; -using ValidateVulkanCombineBuiltInArrayedVariable = spvtest::ValidateBase< - std::tuple>; +using ValidateVulkanCombineBuiltInArrayedVariable = + spvtest::ValidateBase>; using ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult = spvtest::ValidateBase< std::tuple(GetParam()); const char* const storage_class = std::get<2>(GetParam()); const char* const data_type = std::get<3>(GetParam()); - const TestResult& test_result = std::get<4>(GetParam()); + const char* const vuid = std::get<4>(GetParam()); + const TestResult& test_result = std::get<5>(GetParam()); CodeGenerator generator = GetArrayedVariableCodeGenerator( built_in, execution_model, storage_class, data_type); @@ -2622,18 +2694,20 @@ TEST_P(ValidateVulkanCombineBuiltInArrayedVariable, Variable) { if (test_result.error_str2) { EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2)); } + if (vuid) { + EXPECT_THAT(getDiagnosticString(), AnyVUID(vuid)); + } } -INSTANTIATE_TEST_SUITE_P(PointSizeArrayedF32TessControl, - ValidateVulkanCombineBuiltInArrayedVariable, - Combine(Values("PointSize"), - Values("TessellationControl"), Values("Input"), - Values("%f32"), Values(TestResult()))); +INSTANTIATE_TEST_SUITE_P( + PointSizeArrayedF32TessControl, ValidateVulkanCombineBuiltInArrayedVariable, + Combine(Values("PointSize"), Values("TessellationControl"), Values("Input"), + Values("%f32"), Values(nullptr), Values(TestResult()))); INSTANTIATE_TEST_SUITE_P( PointSizeArrayedF64TessControl, ValidateVulkanCombineBuiltInArrayedVariable, Combine(Values("PointSize"), Values("TessellationControl"), Values("Input"), - Values("%f64"), + Values("%f64"), Values("VUID-PointSize-PointSize-04317"), Values(TestResult(SPV_ERROR_INVALID_DATA, "needs to be a 32-bit float scalar", "has bit width 64")))); @@ -2641,7 +2715,7 @@ INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P( PointSizeArrayedF32Vertex, ValidateVulkanCombineBuiltInArrayedVariable, Combine(Values("PointSize"), Values("Vertex"), Values("Output"), - Values("%f32"), + Values("%f32"), Values("VUID-PointSize-PointSize-04317"), Values(TestResult(SPV_ERROR_INVALID_DATA, "needs to be a 32-bit float scalar", "is not a float scalar")))); @@ -2650,13 +2724,14 @@ INSTANTIATE_TEST_SUITE_P(PositionArrayedF32Vec4TessControl, ValidateVulkanCombineBuiltInArrayedVariable, Combine(Values("Position"), Values("TessellationControl"), Values("Input"), - Values("%f32vec4"), Values(TestResult()))); + Values("%f32vec4"), Values(nullptr), + Values(TestResult()))); INSTANTIATE_TEST_SUITE_P( PositionArrayedF32Vec3TessControl, ValidateVulkanCombineBuiltInArrayedVariable, Combine(Values("Position"), Values("TessellationControl"), Values("Input"), - Values("%f32vec3"), + Values("%f32vec3"), Values("VUID-Position-Position-04321"), Values(TestResult(SPV_ERROR_INVALID_DATA, "needs to be a 4-component 32-bit float vector", "has 3 components")))); @@ -2664,7 +2739,7 @@ INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P( PositionArrayedF32Vec4Vertex, ValidateVulkanCombineBuiltInArrayedVariable, Combine(Values("Position"), Values("Vertex"), Values("Output"), - Values("%f32vec4"), + Values("%f32vec4"), Values("VUID-Position-Position-04321"), Values(TestResult(SPV_ERROR_INVALID_DATA, "needs to be a 4-component 32-bit float vector", "is not a float vector")))); @@ -2674,13 +2749,15 @@ INSTANTIATE_TEST_SUITE_P( ValidateVulkanCombineBuiltInArrayedVariable, Combine(Values("ClipDistance", "CullDistance"), Values("Geometry", "TessellationControl", "TessellationEvaluation"), - Values("Output"), Values("%f32arr2", "%f32arr4"), + Values("Output"), Values("%f32arr2", "%f32arr4"), Values(nullptr), Values(TestResult()))); INSTANTIATE_TEST_SUITE_P( ClipAndCullDistanceVertexInput, ValidateVulkanCombineBuiltInArrayedVariable, Combine(Values("ClipDistance", "CullDistance"), Values("Fragment"), Values("Input"), Values("%f32arr4"), + Values("VUID-ClipDistance-ClipDistance-04191 " + "VUID-CullDistance-CullDistance-04200"), Values(TestResult(SPV_ERROR_INVALID_DATA, "needs to be a 32-bit float array", "components are not float scalar")))); @@ -2690,6 +2767,8 @@ INSTANTIATE_TEST_SUITE_P( Combine(Values("ClipDistance", "CullDistance"), Values("Geometry", "TessellationControl", "TessellationEvaluation"), Values("Input"), Values("%f32vec2", "%f32vec4"), + Values("VUID-ClipDistance-ClipDistance-04191 " + "VUID-CullDistance-CullDistance-04200"), Values(TestResult(SPV_ERROR_INVALID_DATA, "needs to be a 32-bit float array", "components are not float scalar")))); @@ -2772,6 +2851,61 @@ INSTANTIATE_TEST_SUITE_P( "needs to be a 32-bit int scalar", "has bit width 64")))); +INSTANTIATE_TEST_SUITE_P( + ArmCoreBuiltinsInputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("CoreIDARM", "CoreCountARM", "CoreMaxIDARM", "WarpIDARM", + "WarpMaxIDARM"), + Values("Vertex", "Fragment", "TessellationControl", + "TessellationEvaluation", "Geometry", "GLCompute"), + Values("Input"), Values("%u32"), + Values("OpCapability CoreBuiltinsARM\n"), + Values("OpExtension \"SPV_ARM_core_builtins\"\n"), Values(nullptr), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + ArmCoreBuiltinsNotInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("CoreIDARM", "CoreCountARM", "CoreMaxIDARM", "WarpIDARM", + "WarpMaxIDARM"), + Values("Vertex", "Fragment", "TessellationControl", + "TessellationEvaluation", "Geometry", "GLCompute"), + Values("Output"), Values("%u32"), + Values("OpCapability CoreBuiltinsARM\n"), + Values("OpExtension \"SPV_ARM_core_builtins\"\n"), Values(nullptr), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Input storage class", + "uses storage class Output")))); + +INSTANTIATE_TEST_SUITE_P( + ArmCoreBuiltinsNotIntScalar, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("CoreIDARM", "CoreCountARM", "CoreMaxIDARM", "WarpIDARM", + "WarpMaxIDARM"), + Values("Vertex", "Fragment", "TessellationControl", + "TessellationEvaluation", "Geometry", "GLCompute"), + Values("Input"), Values("%f32", "%u32vec3"), + Values("OpCapability CoreBuiltinsARM\n"), + Values("OpExtension \"SPV_ARM_core_builtins\"\n"), Values(nullptr), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "is not an int scalar")))); + +INSTANTIATE_TEST_SUITE_P( + ArmCoreBuiltinsNotInt32, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("CoreIDARM", "CoreCountARM", "CoreMaxIDARM", "WarpIDARM", + "WarpMaxIDARM"), + Values("Vertex", "Fragment", "TessellationControl", + "TessellationEvaluation", "Geometry", "GLCompute"), + Values("Input"), Values("%u64"), + Values("OpCapability CoreBuiltinsARM\n"), + Values("OpExtension \"SPV_ARM_core_builtins\"\n"), Values(nullptr), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "has bit width 64")))); + CodeGenerator GetWorkgroupSizeSuccessGenerator() { CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); @@ -2830,7 +2964,8 @@ TEST_F(ValidateBuiltIns, VulkanWorkgroupSizeFragment) { ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), HasSubstr("Vulkan spec allows BuiltIn WorkgroupSize to be used " - "only with GLCompute execution model")); + "only with GLCompute, MeshNV, TaskNV, MeshEXT or " + "TaskEXT execution model")); EXPECT_THAT(getDiagnosticString(), HasSubstr("is referencing ID <2> (OpConstantComposite) which is " "decorated with BuiltIn WorkgroupSize in function <1> " @@ -2860,9 +2995,9 @@ OpDecorate %copy BuiltIn WorkgroupSize CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("BuiltIns can only target variables, structs or constants")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("BuiltIns can only target variables, structure " + "members or constants")); } CodeGenerator GetWorkgroupSizeNotVectorGenerator() { @@ -3097,6 +3232,8 @@ TEST_F(ValidateBuiltIns, TwoBuiltInsFirstFails) { CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( +OpDecorate %input_type Block +OpDecorate %output_type Block OpMemberDecorate %input_type 0 BuiltIn FragCoord OpMemberDecorate %output_type 0 BuiltIn Position )"; @@ -3137,6 +3274,8 @@ TEST_F(ValidateBuiltIns, TwoBuiltInsSecondFails) { CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( +OpDecorate %input_type Block +OpDecorate %output_type Block OpMemberDecorate %input_type 0 BuiltIn Position OpMemberDecorate %output_type 0 BuiltIn FragCoord )"; @@ -3200,6 +3339,7 @@ OpStore %position %f32vec4_0123 TEST_F(ValidateBuiltIns, FragmentPositionTwoEntryPoints) { CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( +OpDecorate %output_type Block OpMemberDecorate %output_type 0 BuiltIn Position )"; @@ -3251,6 +3391,7 @@ CodeGenerator GetNoDepthReplacingGenerator() { CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( +OpDecorate %output_type Block OpMemberDecorate %output_type 0 BuiltIn FragDepth )"; @@ -3281,7 +3422,7 @@ OpReturn OpFunctionEnd )"; - generator.add_at_the_end_ = function_body; + generator.add_at_the_end_ = function_body; return generator; } @@ -3302,6 +3443,7 @@ CodeGenerator GetOneMainHasDepthReplacingOtherHasntGenerator() { CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( +OpDecorate %output_type Block OpMemberDecorate %output_type 0 BuiltIn FragDepth )"; @@ -3343,7 +3485,7 @@ OpReturn OpFunctionEnd )"; - generator.add_at_the_end_ = function_body; + generator.add_at_the_end_ = function_body; return generator; } @@ -3361,7 +3503,6 @@ TEST_F(ValidateBuiltIns, HasSubstr("VUID-FragDepth-FragDepth-04216")); } - TEST_F(ValidateBuiltIns, AllowInstanceIdWithIntersectionShader) { CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.capabilities_ += R"( @@ -3373,6 +3514,7 @@ OpExtension "SPV_NV_ray_tracing" )"; generator.before_types_ = R"( +OpDecorate %input_type Block OpMemberDecorate %input_type 0 BuiltIn InstanceId )"; @@ -3481,35 +3623,6 @@ OpDecorate %gl_ViewportIndex PerPrimitiveNV EXPECT_THAT(getDiagnosticString(), HasSubstr("is not an int scalar")); } -TEST_F(ValidateBuiltIns, GetUnderlyingTypeNoAssert) { - std::string spirv = R"( - OpCapability Shader - OpMemoryModel Logical GLSL450 - 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 - %v4float = OpTypeVector %float 4 - %gl_PointCoord = OpTypeStruct %v4float - %_ptr_Input_v4float = OpTypePointer Input %v4float - %_ptr_Output_v4float = OpTypePointer Output %v4float - %12 = OpVariable %_ptr_Input_v4float Input - %17 = OpVariable %_ptr_Output_v4float Output - %4 = OpFunction %void None %3 - %15 = OpLabel - OpReturn - OpFunctionEnd)"; - CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("did not find an member index to get underlying data " - "type")); -} - TEST_P(ValidateVulkanSubgroupBuiltIns, InMain) { const char* const built_in = std::get<0>(GetParam()); const char* const execution_model = std::get<1>(GetParam()); @@ -3637,6 +3750,7 @@ OpCapability GroupNonUniformBallot OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %foo "foo" OpExecutionMode %foo LocalSize 1 1 1 +OpDecorate %struct Block OpMemberDecorate %struct 0 BuiltIn SubgroupEqMask %void = OpTypeVoid %int = OpTypeInt 32 0 @@ -3691,6 +3805,7 @@ OpCapability GroupNonUniform OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %foo "foo" OpExecutionMode %foo LocalSize 1 1 1 +OpDecorate %struct Block OpMemberDecorate %struct 0 BuiltIn SubgroupSize %void = OpTypeVoid %int = OpTypeInt 32 0 @@ -3711,13 +3826,13 @@ OpFunctionEnd INSTANTIATE_TEST_SUITE_P( SubgroupNumAndIdNotCompute, ValidateVulkanSubgroupBuiltIns, - Combine( - Values("SubgroupId", "NumSubgroups"), Values("Vertex"), Values("Input"), - Values("%u32"), - Values("VUID-SubgroupId-SubgroupId-04367 " - "VUID-NumSubgroups-NumSubgroups-04293"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "to be used only with GLCompute execution model")))); + Combine(Values("SubgroupId", "NumSubgroups"), Values("Vertex"), + Values("Input"), Values("%u32"), + Values("VUID-SubgroupId-SubgroupId-04367 " + "VUID-NumSubgroups-NumSubgroups-04293"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with GLCompute, MeshNV, " + "TaskNV, MeshEXT or TaskEXT execution model")))); INSTANTIATE_TEST_SUITE_P( SubgroupNumAndIdNotU32, ValidateVulkanSubgroupBuiltIns, @@ -3751,6 +3866,7 @@ OpCapability GroupNonUniform OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %foo "foo" OpExecutionMode %foo LocalSize 1 1 1 +OpDecorate %struct Block OpMemberDecorate %struct 0 BuiltIn SubgroupId %void = OpTypeVoid %int = OpTypeInt 32 0 @@ -3780,9 +3896,9 @@ OpDecorate %void BuiltIn Position CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("BuiltIns can only target variables, structs or constants")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("BuiltIns can only target variables, structure members " + "or constants")); } TEST_F(ValidateBuiltIns, TargetIsVariable) { @@ -3800,47 +3916,6 @@ OpDecorate %wg_var BuiltIn Position EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateBuiltIns, TargetIsStruct) { - const std::string text = R"( -OpCapability Shader -OpCapability Linkage -OpMemoryModel Logical GLSL450 -OpDecorate %struct BuiltIn Position -%struct = OpTypeStruct -)"; - - CompileSuccessfully(text); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateBuiltIns, TargetIsConstant) { - const std::string text = R"( -OpCapability Shader -OpCapability Linkage -OpMemoryModel Logical GLSL450 -OpDecorate %int0 BuiltIn Position -%int = OpTypeInt 32 0 -%int0 = OpConstant %int 0 -)"; - - CompileSuccessfully(text); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateBuiltIns, TargetIsSpecConstant) { - const std::string text = R"( -OpCapability Shader -OpCapability Linkage -OpMemoryModel Logical GLSL450 -OpDecorate %int0 BuiltIn Position -%int = OpTypeInt 32 0 -%int0 = OpSpecConstant %int 0 -)"; - - CompileSuccessfully(text); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - INSTANTIATE_TEST_SUITE_P( PrimitiveShadingRateOutputSuccess, ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, @@ -4121,6 +4196,71 @@ INSTANTIATE_TEST_SUITE_P( "According to the Vulkan spec BuiltIn FullyCoveredEXT variable " "needs to be a bool scalar.")))); +INSTANTIATE_TEST_SUITE_P( + BaryCoordNotFragment, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine( + Values("BaryCoordKHR", "BaryCoordNoPerspKHR"), Values("Vertex"), + Values("Input"), Values("%f32vec3"), + Values("OpCapability FragmentBarycentricKHR\n"), + Values("OpExtension \"SPV_KHR_fragment_shader_barycentric\"\n"), + Values("VUID-BaryCoordKHR-BaryCoordKHR-04154 " + "VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04160 "), + Values(TestResult(SPV_ERROR_INVALID_DATA, "Vulkan spec allows BuiltIn", + "to be used only with Fragment execution model")))); + +INSTANTIATE_TEST_SUITE_P( + BaryCoordNotInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("BaryCoordKHR", "BaryCoordNoPerspKHR"), Values("Fragment"), + Values("Output"), Values("%f32vec3"), + Values("OpCapability FragmentBarycentricKHR\n"), + Values("OpExtension \"SPV_KHR_fragment_shader_barycentric\"\n"), + Values("VUID-BaryCoordKHR-BaryCoordKHR-04155 " + "VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04161 "), + Values(TestResult( + SPV_ERROR_INVALID_DATA, "Vulkan spec allows BuiltIn", + "to be only used for variables with Input storage class")))); + +INSTANTIATE_TEST_SUITE_P( + BaryCoordNotFloatVector, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine( + Values("BaryCoordKHR", "BaryCoordNoPerspKHR"), Values("Fragment"), + Values("Output"), Values("%f32arr3", "%u32vec4"), + Values("OpCapability FragmentBarycentricKHR\n"), + Values("OpExtension \"SPV_KHR_fragment_shader_barycentric\"\n"), + Values("VUID-BaryCoordKHR-BaryCoordKHR-04156 " + "VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04162 "), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 3-component 32-bit float vector")))); + +INSTANTIATE_TEST_SUITE_P( + BaryCoordNotFloatVec3, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine( + Values("BaryCoordKHR", "BaryCoordNoPerspKHR"), Values("Fragment"), + Values("Output"), Values("%f32vec2"), + Values("OpCapability FragmentBarycentricKHR\n"), + Values("OpExtension \"SPV_KHR_fragment_shader_barycentric\"\n"), + Values("VUID-BaryCoordKHR-BaryCoordKHR-04156 " + "VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04162 "), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 3-component 32-bit float vector")))); + +INSTANTIATE_TEST_SUITE_P( + BaryCoordNotF32Vec3, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine( + Values("BaryCoordKHR", "BaryCoordNoPerspKHR"), Values("Fragment"), + Values("Output"), Values("%f64vec3"), + Values("OpCapability FragmentBarycentricKHR\n"), + Values("OpExtension \"SPV_KHR_fragment_shader_barycentric\"\n"), + Values("VUID-BaryCoordKHR-BaryCoordKHR-04156 " + "VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04162 "), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 3-component 32-bit float vector")))); + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_capability_test.cpp b/test/val/val_capability_test.cpp index 82f8d381..0d84caa1 100644 --- a/test/val/val_capability_test.cpp +++ b/test/val/val_capability_test.cpp @@ -1205,8 +1205,10 @@ INSTANTIATE_TEST_SUITE_P(Decoration, ValidateCapability, Values( std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt RelaxedPrecision\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var RelaxedPrecision\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Private %intt\n" + "%var = OpVariable %ptr Private\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + // Block applies to struct type. @@ -1224,93 +1226,125 @@ std::make_pair(std::string(kOpenCLMemoryModel) + ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt RowMajor\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpMemberDecorate %structt 0 RowMajor\n" + "%floatt = OpTypeFloat 32\n" + "%float2 = OpTypeVector %floatt 2\n" + "%mat2x2 = OpTypeMatrix %float2 2\n" + "%structt = OpTypeStruct %mat2x2\n" + std::string(kVoidFVoid), MatrixDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt ColMajor\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpMemberDecorate %structt 0 ColMajor\n" + "%floatt = OpTypeFloat 32\n" + "%float2 = OpTypeVector %floatt 2\n" + "%mat2x2 = OpTypeMatrix %float2 2\n" + "%structt = OpTypeStruct %mat2x2\n" + std::string(kVoidFVoid), MatrixDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt ArrayStride 1\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %array ArrayStride 4\n" + "%intt = OpTypeInt 32 0\n" + "%array = OpTypeRuntimeArray %intt\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt MatrixStride 1\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpMemberDecorate %structt 0 MatrixStride 8\n" + "%floatt = OpTypeFloat 32\n" + "%float2 = OpTypeVector %floatt 2\n" + "%mat2x2 = OpTypeMatrix %float2 2\n" + "%structt = OpTypeStruct %mat2x2\n" + std::string(kVoidFVoid), MatrixDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt GLSLShared\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %struct GLSLShared\n" + "%struct = OpTypeStruct\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt GLSLPacked\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %struct GLSLPacked\n" + "%struct = OpTypeStruct\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" - "OpDecorate %intt CPacked\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %struct CPacked\n" + "%struct = OpTypeStruct\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt NoPerspective\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var NoPerspective\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Flat\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Flat\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Patch\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Patch\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), TessellationDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Centroid\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Centroid\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Sample\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Sample\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), std::vector{"SampleRateShading"}), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Invariant\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Invariant\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Restrict\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Restrict\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Aliased\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Aliased\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Volatile\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Volatile\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" - "OpDecorate %intt Constant\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Constant\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Coherent\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Coherent\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + // NonWritable must target something valid, such as a storage image. @@ -1324,8 +1358,12 @@ std::make_pair(std::string(kOpenCLMemoryModel) + AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt NonReadable\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var NonReadable " + "%float = OpTypeFloat 32 " + "%imstor = OpTypeImage %float 2D 0 0 0 2 Unknown " + "%ptr = OpTypePointer UniformConstant %imstor " + "%var = OpVariable %ptr UniformConstant " + + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + // Uniform must target a non-void value. @@ -1342,8 +1380,10 @@ std::make_pair(std::string(kGLSL450MemoryModel) + KernelDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Stream 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Stream 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Output %intt\n" + "%var = OpVariable %ptr Output\n" + std::string(kVoidFVoid), std::vector{"GeometryStreams"}), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" @@ -1360,33 +1400,44 @@ std::make_pair(std::string(kOpenCLMemoryModel) + ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Index 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Index 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Binding 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Binding 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Uniform %intt\n" + "%var = OpVariable %ptr Uniform\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt DescriptorSet 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var DescriptorSet 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Uniform %intt\n" + "%var = OpVariable %ptr Uniform\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Offset 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpMemberDecorate %structt 0 Offset 0\n" + "%intt = OpTypeInt 32 0\n" + "%structt = OpTypeStruct %intt\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt XfbBuffer 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var XfbBuffer 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Uniform %intt\n" + "%var = OpVariable %ptr Uniform\n" + std::string(kVoidFVoid), std::vector{"TransformFeedback"}), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt XfbStride 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var XfbStride 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Uniform %intt\n" + "%var = OpVariable %ptr Uniform\n" + std::string(kVoidFVoid), std::vector{"TransformFeedback"}), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" @@ -1410,8 +1461,10 @@ std::make_pair(std::string(kOpenCLMemoryModel) + ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt InputAttachmentIndex 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var InputAttachmentIndex 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer UniformConstant %intt\n" + "%var = OpVariable %ptr UniformConstant\n" + std::string(kVoidFVoid), std::vector{"InputAttachment"}), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" @@ -1471,264 +1524,300 @@ INSTANTIATE_TEST_SUITE_P(BuiltIn, ValidateCapability, Values( std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn Position\n" + "OpDecorate %var BuiltIn Position\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), // Just mentioning PointSize, ClipDistance, or CullDistance as a BuiltIn does // not trigger the requirement for the associated capability. // See https://github.com/KhronosGroup/SPIRV-Tools/issues/365 std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn PointSize\n" + "OpDecorate %var BuiltIn PointSize\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn ClipDistance\n" + "OpDecorate %var BuiltIn ClipDistance\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn CullDistance\n" + "OpDecorate %var BuiltIn CullDistance\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn VertexId\n" + "OpDecorate %var BuiltIn VertexId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn InstanceId\n" + "OpDecorate %var BuiltIn InstanceId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn PrimitiveId\n" + "OpDecorate %var BuiltIn PrimitiveId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), GeometryTessellationDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn InvocationId\n" + "OpDecorate %var BuiltIn InvocationId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), GeometryTessellationDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn Layer\n" + "OpDecorate %var BuiltIn Layer\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), GeometryDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn ViewportIndex\n" + "OpDecorate %var BuiltIn ViewportIndex\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), std::vector{"MultiViewport"}), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn TessLevelOuter\n" + "OpDecorate %var BuiltIn TessLevelOuter\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), TessellationDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn TessLevelInner\n" + "OpDecorate %var BuiltIn TessLevelInner\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), TessellationDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn TessCoord\n" + "OpDecorate %var BuiltIn TessCoord\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), TessellationDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn PatchVertices\n" + "OpDecorate %var BuiltIn PatchVertices\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), TessellationDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn FragCoord\n" + "OpDecorate %var BuiltIn FragCoord\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn PointCoord\n" + "OpDecorate %var BuiltIn PointCoord\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn FrontFacing\n" + "OpDecorate %var BuiltIn FrontFacing\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn SampleId\n" + "OpDecorate %var BuiltIn SampleId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), std::vector{"SampleRateShading"}), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn SamplePosition\n" + "OpDecorate %var BuiltIn SamplePosition\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), std::vector{"SampleRateShading"}), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn SampleMask\n" + "OpDecorate %var BuiltIn SampleMask\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn FragDepth\n" + "OpDecorate %var BuiltIn FragDepth\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn HelperInvocation\n" + "OpDecorate %var BuiltIn HelperInvocation\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn VertexIndex\n" + "OpDecorate %var BuiltIn VertexIndex\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn InstanceIndex\n" + "OpDecorate %var BuiltIn InstanceIndex\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn NumWorkgroups\n" + "OpDecorate %var BuiltIn NumWorkgroups\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn WorkgroupSize\n" + "OpDecorate %var BuiltIn WorkgroupId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn WorkgroupId\n" + "OpDecorate %var BuiltIn LocalInvocationId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn LocalInvocationId\n" + "OpDecorate %var BuiltIn GlobalInvocationId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn GlobalInvocationId\n" + "OpDecorate %var BuiltIn LocalInvocationIndex\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), - AllCapabilities()), -std::make_pair(std::string(kOpenCLMemoryModel) + - "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn LocalInvocationIndex\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn WorkDim\n" + "OpDecorate %var BuiltIn WorkDim\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn GlobalSize\n" + "OpDecorate %var BuiltIn GlobalSize\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn EnqueuedWorkgroupSize\n" + "OpDecorate %var BuiltIn EnqueuedWorkgroupSize\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn GlobalOffset\n" + "OpDecorate %var BuiltIn GlobalOffset\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn GlobalLinearId\n" + "OpDecorate %var BuiltIn GlobalLinearId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn SubgroupSize\n" + "OpDecorate %var BuiltIn SubgroupSize\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelAndGroupNonUniformDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn SubgroupMaxSize\n" + "OpDecorate %var BuiltIn SubgroupMaxSize\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn NumSubgroups\n" + "OpDecorate %var BuiltIn NumSubgroups\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelAndGroupNonUniformDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn NumEnqueuedSubgroups\n" + "OpDecorate %var BuiltIn NumEnqueuedSubgroups\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn SubgroupId\n" + "OpDecorate %var BuiltIn SubgroupId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelAndGroupNonUniformDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn SubgroupLocalInvocationId\n" + "OpDecorate %var BuiltIn SubgroupLocalInvocationId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelAndGroupNonUniformDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn VertexIndex\n" + "OpDecorate %var BuiltIn VertexIndex\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn InstanceIndex\n" + "OpDecorate %var BuiltIn InstanceIndex\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()) ))); @@ -1742,11 +1831,11 @@ INSTANTIATE_TEST_SUITE_P(BuiltIn, ValidateCapabilityVulkan10, ValuesIn(AllSpirV10Capabilities()), Values( std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" - "OpMemberDecorate %block 0 BuiltIn PointSize\n" - "%f32 = OpTypeFloat 32\n" - "%block = OpTypeStruct %f32\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn PointSize\n" + "%float = OpTypeFloat 32\n" + "%ptr_output_float = OpTypePointer Output %float\n" + "%var = OpVariable %ptr_output_float Output\n" + std::string(kVoidFVoid), // Capabilities which should succeed. AllVulkan10Capabilities()), std::make_pair(std::string(kGLSL450MemoryModel) + @@ -1775,22 +1864,31 @@ INSTANTIATE_TEST_SUITE_P(BuiltIn, ValidateCapabilityOpenGL40, ValuesIn(AllSpirV10Capabilities()), Values( std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn PointSize\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn PointSize\n" + "%float = OpTypeFloat 32\n" + "%ptr_output_float = OpTypePointer Output %float\n" + "%var = OpVariable %ptr_output_float Output\n" + std::string(kVoidFVoid), AllSpirV10Capabilities()), std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn ClipDistance\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn ClipDistance\n" + "%float = OpTypeFloat 32\n" + "%int = OpTypeInt 32 0\n" + "%int_1 = OpConstant %int 1\n" + "%array = OpTypeArray %float %int_1\n" + "%ptr = OpTypePointer Output %array\n" + "%var = OpVariable %ptr Output\n" + std::string(kVoidFVoid), AllSpirV10Capabilities()), std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn CullDistance\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn CullDistance\n" + "%float = OpTypeFloat 32\n" + "%int = OpTypeInt 32 0\n" + "%int_1 = OpConstant %int 1\n" + "%array = OpTypeArray %float %int_1\n" + "%ptr = OpTypePointer Output %array\n" + "%var = OpVariable %ptr Output\n" + std::string(kVoidFVoid), AllSpirV10Capabilities()) ))); @@ -1800,16 +1898,21 @@ INSTANTIATE_TEST_SUITE_P(Capabilities, ValidateCapabilityVulkan11, ValuesIn(AllCapabilities()), Values( std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn PointSize\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn PointSize\n" + "%float = OpTypeFloat 32\n" + "%ptr_output_float = OpTypePointer Output %float\n" + "%var = OpVariable %ptr_output_float Output\n" + std::string(kVoidFVoid), AllVulkan11Capabilities()), std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn CullDistance\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn CullDistance\n" + "%float = OpTypeFloat 32\n" + "%int = OpTypeInt 32 0\n" + "%int_1 = OpConstant %int 1\n" + "%array = OpTypeArray %float %int_1\n" + "%ptr = OpTypePointer Output %array\n" + "%var = OpVariable %ptr Output\n" + std::string(kVoidFVoid), AllVulkan11Capabilities()) ))); @@ -1819,16 +1922,21 @@ INSTANTIATE_TEST_SUITE_P(Capabilities, ValidateCapabilityVulkan12, ValuesIn(AllSpirV15Capabilities()), Values( std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn PointSize\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn PointSize\n" + "%float = OpTypeFloat 32\n" + "%ptr_output_float = OpTypePointer Output %float\n" + "%var = OpVariable %ptr_output_float Output\n" + std::string(kVoidFVoid), AllVulkan12Capabilities()), std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn CullDistance\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn CullDistance\n" + "%float = OpTypeFloat 32\n" + "%int = OpTypeInt 32 0\n" + "%int_1 = OpConstant %int 1\n" + "%array = OpTypeArray %float %int_1\n" + "%ptr = OpTypePointer Output %array\n" + "%var = OpVariable %ptr Output\n" + std::string(kVoidFVoid), AllVulkan12Capabilities()) ))); @@ -2810,8 +2918,8 @@ std::vector CapVersionCases1_5() { "StorageTexelBufferArrayNonUniformIndexing", "SPV_EXT_descriptor_indexing"), // SPV_EXT_physical_storage_buffer - IN15("PhysicalStorageBufferAddressesEXT", - "PhysicalStorageBufferAddresses", "SPV_EXT_physical_storage_buffer"), + IN15("PhysicalStorageBufferAddresses", "PhysicalStorageBufferAddresses", + "SPV_EXT_physical_storage_buffer"), // SPV_KHR_vulkan_memory_model IN15("VulkanMemoryModelKHR", "VulkanMemoryModel", "SPV_KHR_vulkan_memory_model"), diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp index 9715a5a2..a4d14441 100644 --- a/test/val/val_cfg_test.cpp +++ b/test/val/val_cfg_test.cpp @@ -65,7 +65,7 @@ class Block { /// Creates a Block with a given label /// /// @param[in]: label the label id of the block - /// @param[in]: type the branch instruciton that ends the block + /// @param[in]: type the branch instruction that ends the block explicit Block(std::string label, SpvOp type = SpvOpBranch) : label_(label), body_(), type_(type), successors_() {} @@ -386,8 +386,8 @@ TEST_P(ValidateCFG, BlockAppearsBeforeDominatorBad) { CompileSuccessfully(str); ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - MatchesRegex("Block .\\[%cont\\] appears in the binary " - "before its dominator .\\[%branch\\]\n" + MatchesRegex("Block '.\\[%cont\\]' appears in the binary " + "before its dominator '.\\[%branch\\]'\n" " %branch = OpLabel\n")); } @@ -419,7 +419,7 @@ TEST_P(ValidateCFG, MergeBlockTargetedByMultipleHeaderBlocksBad) { if (is_shader) { ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - MatchesRegex("Block .\\[%merge\\] is already a merge block " + MatchesRegex("Block '.\\[%merge\\]' is already a merge block " "for another header\n" " %Main = OpFunction %void None %9\n")); } else { @@ -455,7 +455,7 @@ TEST_P(ValidateCFG, MergeBlockTargetedByMultipleHeaderBlocksSelectionBad) { if (is_shader) { ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - MatchesRegex("Block .\\[%merge\\] is already a merge block " + MatchesRegex("Block '.\\[%merge\\]' is already a merge block " "for another header\n" " %Main = OpFunction %void None %9\n")); } else { @@ -480,8 +480,8 @@ TEST_P(ValidateCFG, BranchTargetFirstBlockBadSinceEntryBlock) { CompileSuccessfully(str); ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - MatchesRegex("First block .\\[%entry\\] of function " - ".\\[%Main\\] is targeted by block .\\[%bad\\]\n" + MatchesRegex("First block '.\\[%entry\\]' of function " + "'.\\[%Main\\]' is targeted by block '.\\[%bad\\]'\n" " %Main = OpFunction %void None %10\n")); } @@ -529,10 +529,11 @@ TEST_P(ValidateCFG, BranchConditionalTrueTargetFirstBlockBad) { CompileSuccessfully(str); ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - MatchesRegex("First block .\\[%entry\\] of function .\\[%Main\\] " - "is targeted by block .\\[%bad\\]\n" - " %Main = OpFunction %void None %10\n")); + EXPECT_THAT( + getDiagnosticString(), + MatchesRegex("First block '.\\[%entry\\]' of function '.\\[%Main\\]' " + "is targeted by block '.\\[%bad\\]'\n" + " %Main = OpFunction %void None %10\n")); } TEST_P(ValidateCFG, BranchConditionalFalseTargetFirstBlockBad) { @@ -558,10 +559,11 @@ TEST_P(ValidateCFG, BranchConditionalFalseTargetFirstBlockBad) { CompileSuccessfully(str); ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - MatchesRegex("First block .\\[%entry\\] of function .\\[%Main\\] " - "is targeted by block .\\[%bad\\]\n" - " %Main = OpFunction %void None %10\n")); + EXPECT_THAT( + getDiagnosticString(), + MatchesRegex("First block '.\\[%entry\\]' of function '.\\[%Main\\]' " + "is targeted by block '.\\[%bad\\]'\n" + " %Main = OpFunction %void None %10\n")); } TEST_P(ValidateCFG, SwitchTargetFirstBlockBad) { @@ -594,10 +596,11 @@ TEST_P(ValidateCFG, SwitchTargetFirstBlockBad) { CompileSuccessfully(str); ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - MatchesRegex("First block .\\[%entry\\] of function .\\[%Main\\] " - "is targeted by block .\\[%bad\\]\n" - " %Main = OpFunction %void None %10\n")); + EXPECT_THAT( + getDiagnosticString(), + MatchesRegex("First block '.\\[%entry\\]' of function '.\\[%Main\\]' " + "is targeted by block '.\\[%bad\\]'\n" + " %Main = OpFunction %void None %10\n")); } TEST_P(ValidateCFG, BranchToBlockInOtherFunctionBad) { @@ -630,46 +633,11 @@ TEST_P(ValidateCFG, BranchToBlockInOtherFunctionBad) { CompileSuccessfully(str); ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - MatchesRegex("Block\\(s\\) \\{.\\[%middle2\\]\\} are referenced but not " - "defined in function .\\[%Main\\]\n" - " %Main = OpFunction %void None %9\n")); -} - -TEST_P(ValidateCFG, HeaderDoesntDominatesMergeBad) { - bool is_shader = GetParam() == SpvCapabilityShader; - Block entry("entry"); - Block head("head", SpvOpBranchConditional); - Block f("f"); - Block merge("merge", SpvOpReturn); - - head.SetBody("%cond = OpSLessThan %boolt %one %two\n"); - - if (is_shader) head.AppendBody("OpSelectionMerge %merge None\n"); - - std::string str = GetDefaultHeader(GetParam()) + - nameOps("head", "merge", std::make_pair("func", "Main")) + - types_consts() + - "%func = OpFunction %voidt None %funct\n"; - - str += entry >> merge; - str += head >> std::vector({merge, f}); - str += f >> merge; - str += merge; - str += "OpFunctionEnd\n"; - - CompileSuccessfully(str); - if (is_shader) { - ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - MatchesRegex("The selection construct with the selection header " - ".\\[%head\\] does not dominate the merge block " - ".\\[%merge\\]\n %merge = OpLabel\n")); - } else { - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); - } + EXPECT_THAT(getDiagnosticString(), + MatchesRegex( + "Block\\(s\\) \\{'.\\[%middle2\\]'\\} are referenced but not " + "defined in function '.\\[%Main\\]'\n" + " %Main = OpFunction %void None %9\n")); } TEST_P(ValidateCFG, HeaderDoesntStrictlyDominateMergeBad) { @@ -697,9 +665,11 @@ TEST_P(ValidateCFG, HeaderDoesntStrictlyDominateMergeBad) { ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - MatchesRegex("The selection construct with the selection header " - ".\\[%head\\] does not strictly dominate the merge block " - ".\\[%head\\]\n %head = OpLabel\n")); + MatchesRegex( + "The selection construct with the selection header " + "'.\\[%head\\]' does not strictly structurally dominate the " + "merge block " + "'.\\[%head\\]'\n %head = OpLabel\n")); } else { ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << str; } @@ -907,16 +877,7 @@ std::string GetUnreachableContinueUnreachableLoopInst(SpvCapability cap) { TEST_P(ValidateCFG, UnreachableContinueUnreachableLoopInst) { CompileSuccessfully(GetUnreachableContinueUnreachableLoopInst(GetParam())); - if (GetParam() == SpvCapabilityShader) { - // Shader causes additional structured CFG checks that cause a failure. - ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Back-edges (1[%branch] -> 3[%target]) can only be " - "formed between a block and a loop header.")); - - } else { - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); - } + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } std::string GetUnreachableMergeWithComplexBody(SpvCapability cap) { @@ -1070,12 +1031,10 @@ std::string GetUnreachableContinueWithBranchUse(SpvCapability cap) { std::string header = GetDefaultHeader(cap); Block entry("entry"); - Block foo("foo", SpvOpBranch); Block branch("branch", SpvOpBranch); Block merge("merge", SpvOpReturn); Block target("target", SpvOpBranch); - foo >> target; target >> branch; entry.AppendBody("%placeholder = OpVariable %intptrt Function\n"); @@ -1092,7 +1051,6 @@ std::string GetUnreachableContinueWithBranchUse(SpvCapability cap) { str += branch >> std::vector({merge}); str += merge; str += target; - str += foo; str += "OpFunctionEnd\n"; return str; @@ -1156,6 +1114,7 @@ std::string GetUnreachableMergeAndContinue(SpvCapability cap) { Block body("body", SpvOpBranchConditional); Block t("t", SpvOpReturn); Block f("f", SpvOpReturn); + Block pre_target("pre_target", SpvOpBranch); target >> branch; body.SetBody("%cond = OpSLessThan %boolt %one %two\n"); @@ -1163,10 +1122,10 @@ std::string GetUnreachableMergeAndContinue(SpvCapability cap) { std::string str = header; if (cap == SpvCapabilityShader) { branch.AppendBody("OpLoopMerge %merge %target None\n"); - body.AppendBody("OpSelectionMerge %target None\n"); + body.AppendBody("OpSelectionMerge %pre_target None\n"); } - str += nameOps("branch", "merge", "target", "body", "t", "f", + str += nameOps("branch", "merge", "pre_target", "target", "body", "t", "f", std::make_pair("func", "Main")); str += types_consts(); str += "%func = OpFunction %voidt None %funct\n"; @@ -1176,6 +1135,7 @@ std::string GetUnreachableMergeAndContinue(SpvCapability cap) { str += t; str += f; str += merge; + str += pre_target >> target; str += target; str += "OpFunctionEnd\n"; @@ -1296,9 +1256,10 @@ TEST_P(ValidateCFG, NestedLoops) { loop2.SetBody("OpLoopMerge %loop2_merge %loop2 None\n"); } - std::string str = GetDefaultHeader(GetParam()) + - nameOps("loop2", "loop2_merge") + types_consts() + - "%func = OpFunction %voidt None %funct\n"; + std::string str = + GetDefaultHeader(GetParam()) + + nameOps("loop1", "loop1_cont_break_block", "loop2", "loop2_merge") + + types_consts() + "%func = OpFunction %voidt None %funct\n"; str += entry >> loop1; str += loop1 >> loop1_cont_break_block; @@ -1389,11 +1350,13 @@ TEST_P(ValidateCFG, BackEdgeBlockDoesntPostDominateContinueTargetBad) { CompileSuccessfully(str); if (GetParam() == SpvCapabilityShader) { ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - MatchesRegex("The continue construct with the continue target " - ".\\[%loop1_cont\\] is not post dominated by the " - "back-edge block .\\[%be_block\\]\n" - " %be_block = OpLabel\n")); + EXPECT_THAT( + getDiagnosticString(), + MatchesRegex( + "The continue construct with the continue target " + "'.\\[%loop1_cont\\]' is not structurally post dominated by the " + "back-edge block '.\\[%be_block\\]'\n" + " %be_block = OpLabel\n")); } else { ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -1426,7 +1389,7 @@ TEST_P(ValidateCFG, BranchingToNonLoopHeaderBlockBad) { ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - MatchesRegex("Back-edges \\(.\\[%f\\] -> .\\[%split\\]\\) can only " + MatchesRegex("Back-edges \\('.\\[%f\\]' -> '.\\[%split\\]'\\) can only " "be formed between a block and a loop header.\n" " %f = OpLabel\n")); } else { @@ -1458,7 +1421,7 @@ TEST_P(ValidateCFG, BranchingToSameNonLoopHeaderBlockBad) { EXPECT_THAT( getDiagnosticString(), MatchesRegex( - "Back-edges \\(.\\[%split\\] -> .\\[%split\\]\\) can only be " + "Back-edges \\('.\\[%split\\]' -> '.\\[%split\\]'\\) can only be " "formed between a block and a loop header.\n %split = OpLabel\n")); } else { ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); @@ -1493,7 +1456,7 @@ TEST_P(ValidateCFG, MultipleBackEdgeBlocksToLoopHeaderBad) { EXPECT_THAT( getDiagnosticString(), MatchesRegex( - "Loop header .\\[%loop\\] is targeted by 2 back-edge blocks but " + "Loop header '.\\[%loop\\]' is targeted by 2 back-edge blocks but " "the standard requires exactly one\n %loop = OpLabel\n")) << str; } else { @@ -1528,11 +1491,13 @@ TEST_P(ValidateCFG, ContinueTargetMustBePostDominatedByBackEdge) { CompileSuccessfully(str); if (is_shader) { ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - MatchesRegex("The continue construct with the continue target " - ".\\[%cheader\\] is not post dominated by the " - "back-edge block .\\[%be_block\\]\n" - " %be_block = OpLabel\n")); + EXPECT_THAT( + getDiagnosticString(), + MatchesRegex( + "The continue construct with the continue target " + "'.\\[%cheader\\]' is not structurally post dominated by the " + "back-edge block '.\\[%be_block\\]'\n" + " %be_block = OpLabel\n")); } else { ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -1561,11 +1526,12 @@ TEST_P(ValidateCFG, BranchOutOfConstructToMergeBad) { CompileSuccessfully(str); if (is_shader) { ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - MatchesRegex("The continue construct with the continue target " - ".\\[%loop\\] is not post dominated by the " - "back-edge block .\\[%cont\\]\n" - " %cont = OpLabel\n")) + EXPECT_THAT( + getDiagnosticString(), + MatchesRegex("The continue construct with the continue target " + "'.\\[%loop\\]' is not structurally post dominated by the " + "back-edge block '.\\[%cont\\]'\n" + " %cont = OpLabel\n")) << str; } else { ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); @@ -1597,11 +1563,12 @@ TEST_P(ValidateCFG, BranchOutOfConstructBad) { CompileSuccessfully(str); if (is_shader) { ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - MatchesRegex("The continue construct with the continue target " - ".\\[%loop\\] is not post dominated by the " - "back-edge block .\\[%cont\\]\n" - " %cont = OpLabel\n")); + EXPECT_THAT( + getDiagnosticString(), + MatchesRegex("The continue construct with the continue target " + "'.\\[%loop\\]' is not structurally post dominated by the " + "back-edge block '.\\[%cont\\]'\n" + " %cont = OpLabel\n")); } else { ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -1674,7 +1641,7 @@ TEST_F(ValidateCFG, LoopWithZeroBackEdgesBad) { ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - MatchesRegex("Loop header .\\[%loop\\] is targeted by " + MatchesRegex("Loop header '.\\[%loop\\]' is targeted by " "0 back-edge blocks but the standard requires exactly " "one\n %loop = OpLabel\n")); } @@ -1786,9 +1753,10 @@ TEST_P(ValidateCFG, ContinueTargetCanBeMergeBlockForNestedStructure) { EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("Header block 3[%if_head] is contained in the loop construct " - "headed " - "by 2[%loop], but its merge block 5[%if_merge] is not")); + HasSubstr( + "Header block '3[%if_head]' is contained in the loop construct " + "headed " + "by '2[%loop]', but its merge block '5[%if_merge]' is not")); } else { EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); } @@ -1824,40 +1792,6 @@ TEST_P(ValidateCFG, SingleLatchBlockMultipleBranchesToLoopHeader) { << str << getDiagnosticString(); } -TEST_P(ValidateCFG, SingleLatchBlockHeaderContinueTargetIsItselfGood) { - // This test case ensures we don't count a Continue Target from a loop - // header to itself as a self-loop when computing back edges. - // Also, it detects that there is an edge from %latch to the pseudo-exit - // node, rather than from %loop. In particular, it detects that we - // have used the *reverse* textual order of blocks when computing - // predecessor traversal roots. - bool is_shader = GetParam() == SpvCapabilityShader; - Block entry("entry"); - Block loop("loop"); - Block latch("latch"); - Block merge("merge", SpvOpReturn); - - entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); - if (is_shader) { - loop.SetBody("OpLoopMerge %merge %loop None\n"); - } - - std::string str = GetDefaultHeader(GetParam()) + - nameOps("entry", "loop", "latch", "merge") + - types_consts() + - "%func = OpFunction %voidt None %funct\n"; - - str += entry >> loop; - str += loop >> latch; - str += latch >> loop; - str += merge; - str += "OpFunctionEnd"; - - CompileSuccessfully(str); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) - << str << getDiagnosticString(); -} - // Unit test to check the case where a basic block is the entry block of 2 // different constructs. In this case, the basic block is the entry block of a // continue construct as well as a selection construct. See issue# 517 for more @@ -2027,8 +1961,9 @@ OpFunctionEnd EXPECT_THAT( getDiagnosticString(), HasSubstr( - "Case construct that targets 10[%10] has branches to multiple other " - "case construct targets 12[%12] and 11[%11]\n %10 = OpLabel")); + "Case construct that targets '10[%10]' has branches to multiple " + "other " + "case construct targets '12[%12]' and '11[%11]'\n %10 = OpLabel")); } TEST_F(ValidateCFG, MultipleFallThroughToDefault) { @@ -2062,7 +1997,7 @@ OpFunctionEnd EXPECT_THAT( getDiagnosticString(), HasSubstr("Multiple case constructs have branches to the case construct " - "that targets 10[%10]\n %10 = OpLabel")); + "that targets '10[%10]'\n %10 = OpLabel")); } TEST_F(ValidateCFG, MultipleFallThroughToNonDefault) { @@ -2096,7 +2031,7 @@ OpFunctionEnd EXPECT_THAT( getDiagnosticString(), HasSubstr("Multiple case constructs have branches to the case construct " - "that targets 12[%12]\n %12 = OpLabel")); + "that targets '12[%12]'\n %12 = OpLabel")); } TEST_F(ValidateCFG, DuplicateTargetWithFallThrough) { @@ -2157,10 +2092,11 @@ OpFunctionEnd ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("Case construct that targets 12[%12] has branches to the case " - "construct that targets 11[%11], but does not immediately " - "precede it in the OpSwitch's target list\n" - " OpSwitch %uint_0 %10 0 %11 1 %12")); + HasSubstr( + "Case construct that targets '12[%12]' has branches to the case " + "construct that targets '11[%11]', but does not immediately " + "precede it in the OpSwitch's target list\n" + " OpSwitch %uint_0 %10 0 %11 1 %12")); } TEST_F(ValidateCFG, WrongOperandListThroughDefault) { @@ -2193,10 +2129,11 @@ OpFunctionEnd ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("Case construct that targets 12[%12] has branches to the case " - "construct that targets 11[%11], but does not immediately " - "precede it in the OpSwitch's target list\n" - " OpSwitch %uint_0 %10 0 %11 1 %12")); + HasSubstr( + "Case construct that targets '12[%12]' has branches to the case " + "construct that targets '11[%11]', but does not immediately " + "precede it in the OpSwitch's target list\n" + " OpSwitch %uint_0 %10 0 %11 1 %12")); } TEST_F(ValidateCFG, WrongOperandListNotLast) { @@ -2231,10 +2168,11 @@ OpFunctionEnd ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("Case construct that targets 12[%12] has branches to the case " - "construct that targets 11[%11], but does not immediately " - "precede it in the OpSwitch's target list\n" - " OpSwitch %uint_0 %10 0 %11 1 %12 2 %13")); + HasSubstr( + "Case construct that targets '12[%12]' has branches to the case " + "construct that targets '11[%11]', but does not immediately " + "precede it in the OpSwitch's target list\n" + " OpSwitch %uint_0 %10 0 %11 1 %12 2 %13")); } TEST_F(ValidateCFG, GoodUnreachableSwitch) { @@ -2298,11 +2236,12 @@ OpFunctionEnd CompileSuccessfully(text); ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Case construct that targets 8[%8] has invalid branch " - "to block 10[%10] (not another case construct, " - "corresponding merge, outer loop merge or outer loop " - "continue)")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Case construct that targets '8[%8]' has invalid branch " + "to block '10[%10]' (not another case construct, " + "corresponding merge, outer loop merge or outer loop " + "continue)")); } TEST_F(ValidateCFG, GoodCaseExitsToOuterConstructs) { @@ -2369,8 +2308,8 @@ OpFunctionEnd EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("Case construct that targets 1[%default] has branches to the " - "case construct that targets 2[%other], but does not " + HasSubstr("Case construct that targets '1[%default]' has branches to the " + "case construct that targets '2[%other]', but does not " "immediately precede it in the OpSwitch's target list")); } @@ -2402,8 +2341,8 @@ OpFunctionEnd EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("Case construct that targets 2[%other] has branches to the " - "case construct that targets 1[%default], but does not " + HasSubstr("Case construct that targets '2[%other]' has branches to the " + "case construct that targets '1[%default]', but does not " "immediately precede it in the OpSwitch's target list")); } @@ -2872,8 +2811,8 @@ OpFunctionEnd CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("block 9 branches to the loop construct, but not " - "to the loop header 7")); + HasSubstr("Back-edges ('10[%10]' -> '9[%9]') can only be formed " + "between a block and a loop header")); } TEST_F(ValidateCFG, LoopMergeMergeBlockNotLabel) { @@ -2898,7 +2837,7 @@ OpFunctionEnd CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Merge Block 1[%undef] must be an OpLabel")); + HasSubstr("Merge Block '1[%undef]' must be an OpLabel")); } TEST_F(ValidateCFG, LoopMergeContinueTargetNotLabel) { @@ -2923,7 +2862,7 @@ OpFunctionEnd CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Continue Target 1[%undef] must be an OpLabel")); + HasSubstr("Continue Target '1[%undef]' must be an OpLabel")); } TEST_F(ValidateCFG, LoopMergeMergeBlockContinueTargetSameLabel) { @@ -3167,9 +3106,10 @@ OpFunctionEnd CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("block 10[%10] exits the selection headed by " - "8[%8], but not via a structured exit")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("block '10[%10]' exits the selection headed by " + "'8[%8]', but not via a structured exit")); } TEST_F(ValidateCFG, InvalidLoopExit) { @@ -3203,8 +3143,8 @@ OpFunctionEnd CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("block 11[%11] exits the loop headed by " - "8[%8], but not via a structured exit")); + HasSubstr("block '11[%11]' exits the loop headed by " + "'8[%8]', but not via a structured exit")); } TEST_F(ValidateCFG, InvalidContinueExit) { @@ -3237,9 +3177,10 @@ OpFunctionEnd CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("block 11[%11] exits the continue headed by " - "10[%10], but not via a structured exit")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("block '11[%11]' exits the continue headed by " + "'10[%10]', but not via a structured exit")); } TEST_F(ValidateCFG, InvalidSelectionExitBackedge) { @@ -3275,9 +3216,11 @@ OpFunctionEnd CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("block 13[%13] exits the selection headed by " - "9[%9], but not via a structured exit")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "The continue construct with the continue target '9[%9]' is not " + "structurally post dominated by the back-edge block '13[%13]'")); } TEST_F(ValidateCFG, BreakFromSwitch) { @@ -3340,9 +3283,10 @@ OpFunctionEnd CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("block 12[%12] exits the selection headed by " - "10[%10], but not via a structured exit")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("block '12[%12]' exits the selection headed by " + "'10[%10]', but not via a structured exit")); } TEST_F(ValidateCFG, BreakToOuterSwitch) { @@ -3379,9 +3323,10 @@ OpFunctionEnd CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("block 14[%14] exits the selection headed by " - "10[%10], but not via a structured exit")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("block '14[%14]' exits the selection headed by " + "'10[%10]', but not via a structured exit")); } TEST_F(ValidateCFG, BreakToOuterLoop) { @@ -3423,8 +3368,8 @@ OpFunctionEnd CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("block 15[%15] exits the loop headed by " - "10[%10], but not via a structured exit")); + HasSubstr("block '15[%15]' exits the loop headed by " + "'10[%10]', but not via a structured exit")); } TEST_F(ValidateCFG, ContinueFromNestedSelection) { @@ -3523,7 +3468,7 @@ OpFunctionEnd EXPECT_THAT( getDiagnosticString(), HasSubstr( - "OpSwitch must be preceeded by an OpSelectionMerge instruction")); + "OpSwitch must be preceded by an OpSelectionMerge instruction")); } TEST_F(ValidateCFG, MissingMergeSwitchBad2) { @@ -3550,7 +3495,7 @@ OpFunctionEnd EXPECT_THAT( getDiagnosticString(), HasSubstr( - "OpSwitch must be preceeded by an OpSelectionMerge instruction")); + "OpSwitch must be preceded by an OpSelectionMerge instruction")); } TEST_F(ValidateCFG, MissingMergeOneBranchToMergeGood) { @@ -3622,7 +3567,7 @@ OpFunctionEnd EXPECT_THAT( getDiagnosticString(), HasSubstr( - "OpSwitch must be preceeded by an OpSelectionMerge instruction")); + "OpSwitch must be preceded by an OpSelectionMerge instruction")); } TEST_F(ValidateCFG, MissingMergeOneUnseenTargetSwitchBad) { @@ -3654,7 +3599,7 @@ OpFunctionEnd EXPECT_THAT( getDiagnosticString(), HasSubstr( - "OpSwitch must be preceeded by an OpSelectionMerge instruction")); + "OpSwitch must be preceded by an OpSelectionMerge instruction")); } TEST_F(ValidateCFG, MissingMergeLoopBreakGood) { @@ -3871,9 +3816,9 @@ OpFunctionEnd EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr( - "Header block 3[%body] is contained in the loop construct headed by " - "1[%loop], but its merge block 2[%continue] is not")); + HasSubstr("Header block '3[%body]' is contained in the loop construct " + "headed by " + "'1[%loop]', but its merge block '2[%continue]' is not")); } TEST_F(ValidateCFG, ContinueCannotBeLoopMergeTarget) { @@ -3908,9 +3853,9 @@ OpFunctionEnd EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr( - "Header block 3[%inner] is contained in the loop construct headed by " - "1[%loop], but its merge block 2[%continue] is not")); + HasSubstr("Header block '3[%inner]' is contained in the loop construct " + "headed by " + "'1[%loop]', but its merge block '2[%continue]' is not")); } TEST_F(ValidateCFG, ExitFromConstructWhoseHeaderIsAMerge) { @@ -4229,6 +4174,68 @@ OpFunctionEnd ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateCFG, StructuredSelections_RegisterBothTrueAndFalse) { + // In this test, we try to make a case where the false branches + // to %20 and %60 from blocks %10 and %50 must be registered + // during the validity check for sturctured selections. + // However, an error is caught earlier in the flow, that the + // branches from %100 to %20 and %60 violate dominance. + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical Simple + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + + %bool = OpTypeBool + %cond = OpUndef %bool + + %main = OpFunction %void None %void_fn + + %1 = OpLabel + OpSelectionMerge %999 None + OpBranchConditional %cond %10 %100 + + %10 = OpLabel + OpSelectionMerge %30 None ; force registration of %30 + OpBranchConditional %cond %30 %20 ; %20 should be registered too + + %20 = OpLabel + OpBranch %30 + + %30 = OpLabel ; merge for first if + OpBranch %50 + + + %50 = OpLabel + OpSelectionMerge %70 None ; force registration of %70 + OpBranchConditional %cond %70 %60 ; %60 should be registered + + %60 = OpLabel + OpBranch %70 + + %70 = OpLabel ; merge for second if + OpBranch %999 + + %100 = OpLabel + OpBranchConditional %cond %20 %60 ; should require a merge + + %999 = OpLabel + OpReturn + + OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The selection construct with the selection header " + "'8[%8]' does not structurally dominate the merge " + "block '10[%10]'\n")); +} + TEST_F(ValidateCFG, UnreachableIsStaticallyReachable) { const std::string text = R"( OpCapability Shader @@ -4364,6 +4371,236 @@ TEST_F(ValidateCFG, PhiOnVoid) { HasSubstr("OpPhi must not have void result type")); } +TEST_F(ValidateCFG, InvalidExitSingleBlockLoop) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %5 "BAD" +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%fn = OpFunction %void None %void_fn +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +OpLoopMerge %3 %4 None +OpBranchConditional %undef %3 %5 +%5 = OpLabel +OpLoopMerge %6 %5 None +OpBranchConditional %undef %5 %4 +%6 = OpLabel +OpReturn +%4 = OpLabel +OpBranch %2 +%3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("block '1[%BAD]' exits the continue headed by " + "'1[%BAD]', but not via a structured exit")); +} + +TEST_F(ValidateCFG, SwitchSelectorNotAnInt) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float_1 = OpConstant %float 1 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %default None +OpSwitch %float_1 %default +%default = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Selector type must be OpTypeInt")); +} + +TEST_F(ValidateCFG, SwitchDefaultNotALabel) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %default None +OpSwitch %int_1 %int_1 +%default = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Default must be an OpLabel instruction")); +} + +TEST_F(ValidateCFG, BlockDepthRecursion) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +OpLoopMerge %3 %4 None +OpBranchConditional %undef %3 %4 +%4 = OpLabel +OpBranch %2 +%3 = OpLabel +OpBranch %5 +%5 = OpLabel +OpSelectionMerge %2 None +OpBranchConditional %undef %6 %7 +%6 = OpLabel +OpReturn +%7 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); +} + +TEST_F(ValidateCFG, BadStructuredExitBackwardsMerge) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +OpLoopMerge %4 %5 None +OpBranchConditional %undef %4 %6 +%6 = OpLabel +OpSelectionMerge %7 None +OpBranchConditional %undef %8 %9 +%7 = OpLabel +OpReturn +%8 = OpLabel +OpBranch %5 +%9 = OpLabel +OpSelectionMerge %6 None +OpBranchConditional %undef %5 %5 +%5 = OpLabel +OpBranch %2 +%4 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); +} + +TEST_F(ValidateCFG, BranchConditionalDifferentTargetsPre1p6) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranchConditional %undef %target %target +%target = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); +} + +TEST_F(ValidateCFG, BranchConditionalDifferentTargetsPost1p6) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranchConditional %undef %target %target +%target = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_6); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In SPIR-V 1.6 or later, True Label and False Label " + "must be different labels")); +} + +TEST_F(ValidateCFG, BadBackEdgeUnreachableContinue) { + 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 +OpLoopMerge %6 %7 None +OpBranch %8 +%8 = OpLabel +OpBranch %5 +%7 = OpLabel +OpUnreachable +%6 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("The continue construct with the continue target '7[%7]' " + "does not structurally dominate the back-edge block '8[%8]'")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_composites_test.cpp b/test/val/val_composites_test.cpp index bf7caa9f..0fd1ed65 100644 --- a/test/val/val_composites_test.cpp +++ b/test/val/val_composites_test.cpp @@ -86,10 +86,10 @@ OpCapability Float64 %f32mat23_121212 = OpConstantComposite %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12 %f32vec2arr3 = OpTypeArray %f32vec2 %u32_3 -%f32vec2rarr = OpTypeRuntimeArray %f32vec2 +%f32vec2arr2 = OpTypeArray %f32vec2 %u32_2 %f32u32struct = OpTypeStruct %f32 %u32 -%big_struct = OpTypeStruct %f32 %f32vec4 %f32mat23 %f32vec2arr3 %f32vec2rarr %f32u32struct +%big_struct = OpTypeStruct %f32 %f32vec4 %f32mat23 %f32vec2arr3 %f32vec2arr2 %f32u32struct %ptr_big_struct = OpTypePointer Uniform %big_struct %var_big_struct = OpVariable %ptr_big_struct Uniform @@ -150,7 +150,6 @@ OpMemoryModel Logical GLSL450 ; uniform blockName { ; S s; ; bool cond; -; RunTimeArray arr; ; } %f32arr = OpTypeRuntimeArray %float @@ -161,7 +160,7 @@ OpMemoryModel Logical GLSL450 %_ptr_Function_vec4 = OpTypePointer Function %v4float %_ptr_Uniform_vec4 = OpTypePointer Uniform %v4float %struct_s = OpTypeStruct %int %array5_vec4 %int %array5_mat4x3 -%struct_blockName = OpTypeStruct %struct_s %int %f32arr +%struct_blockName = OpTypeStruct %struct_s %int %_ptr_Uniform_blockName = OpTypePointer Uniform %struct_blockName %_ptr_Uniform_struct_s = OpTypePointer Uniform %struct_s %_ptr_Uniform_array5_mat4x3 = OpTypePointer Uniform %array5_mat4x3 @@ -323,8 +322,9 @@ TEST_F(ValidateComposites, CompositeConstructVectorWrongConsituent1) { CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 5[%float] cannot be a " - "type")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand '5[%float]' cannot be a " + "type")); } TEST_F(ValidateComposites, CompositeConstructVectorWrongConsituent2) { @@ -541,7 +541,7 @@ TEST_F(ValidateComposites, CopyObjectResultTypeNotType) { CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("ID 19[%float_0] is not a type id")); + HasSubstr("ID '19[%float_0]' is not a type id")); } TEST_F(ValidateComposites, CopyObjectWrongOperandType) { @@ -644,8 +644,8 @@ TEST_F(ValidateComposites, CompositeExtractSuccess) { %val12 = OpCompositeExtract %f32 %struct 2 2 1 %val13 = OpCompositeExtract %f32vec2 %struct 3 2 %val14 = OpCompositeExtract %f32 %struct 3 2 1 -%val15 = OpCompositeExtract %f32vec2 %struct 4 100 -%val16 = OpCompositeExtract %f32 %struct 4 1000 1 +%val15 = OpCompositeExtract %f32vec2 %struct 4 1 +%val16 = OpCompositeExtract %f32 %struct 4 0 1 %val17 = OpCompositeExtract %f32 %struct 5 0 %val18 = OpCompositeExtract %u32 %struct 5 1 )"; @@ -661,7 +661,7 @@ TEST_F(ValidateComposites, CompositeExtractNotObject) { CompileSuccessfully(GenerateShaderCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 11[%v4float] cannot " + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand '11[%v4float]' cannot " "be a type")); } @@ -868,8 +868,8 @@ TEST_F(ValidateComposites, CompositeInsertSuccess) { %val12 = OpCompositeInsert %big_struct %f32_3 %struct 2 2 1 %val13 = OpCompositeInsert %big_struct %f32vec2_01 %struct 3 2 %val14 = OpCompositeInsert %big_struct %f32_3 %struct 3 2 1 -%val15 = OpCompositeInsert %big_struct %f32vec2_01 %struct 4 100 -%val16 = OpCompositeInsert %big_struct %f32_3 %struct 4 1000 1 +%val15 = OpCompositeInsert %big_struct %f32vec2_01 %struct 4 1 +%val16 = OpCompositeInsert %big_struct %f32_3 %struct 4 0 1 %val17 = OpCompositeInsert %big_struct %f32_3 %struct 5 0 %val18 = OpCompositeInsert %big_struct %u32_3 %struct 5 1 )"; @@ -1382,8 +1382,8 @@ TEST_F(ValidateComposites, CompositeExtractStructIndexOutOfBoundBad) { EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Index is out of bounds, can not find index 3 in the " - "structure '25'. This structure has 3 members. " - "Largest valid index is 2.")); + "structure '25'. This structure has 2 members. " + "Largest valid index is 1.")); } // Invalid. Index into a struct is larger than the number of struct members. @@ -1403,8 +1403,8 @@ TEST_F(ValidateComposites, CompositeInsertStructIndexOutOfBoundBad) { EXPECT_THAT( getDiagnosticString(), HasSubstr("Index is out of bounds, can not find index 3 in the structure " - " '25'. This structure has 3 members. Largest valid index " - "is 2.")); + " '25'. This structure has 2 members. Largest valid index " + "is 1.")); } // #1403: Ensure that the default spec constant value is not used to check the diff --git a/test/val/val_conversion_test.cpp b/test/val/val_conversion_test.cpp index b9802ece..1f8c4265 100644 --- a/test/val/val_conversion_test.cpp +++ b/test/val/val_conversion_test.cpp @@ -813,8 +813,9 @@ TEST_F(ValidateConversion, PtrCastToGenericWrongInputType) { CompileSuccessfully(GenerateKernelCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 4[%float] cannot be a " - "type")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand '4[%float]' cannot be a " + "type")); } TEST_F(ValidateConversion, PtrCastToGenericWrongInputStorageClass) { @@ -1258,8 +1259,9 @@ TEST_F(ValidateConversion, BitcastInputHasNoType) { CompileSuccessfully(GenerateKernelCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 4[%float] cannot be a " - "type")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand '4[%float]' cannot be a " + "type")); } TEST_F(ValidateConversion, BitcastWrongResultType) { @@ -1458,7 +1460,7 @@ OpFunctionEnd CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 1[%uint] cannot be a " + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand '1[%uint]' cannot be a " "type")); } @@ -1557,7 +1559,7 @@ OpExtension "SPV_EXT_physical_storage_buffer" OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft -OpDecorate %val1 RestrictPointerEXT +OpDecorate %val1 RestrictPointer %uint64 = OpTypeInt 64 0 %u64_1 = OpConstant %uint64 1 %ptr = OpTypePointer PhysicalStorageBuffer %uint64 @@ -1615,7 +1617,7 @@ OpExtension "SPV_EXT_physical_storage_buffer" OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft -OpDecorate %val1 RestrictPointerEXT +OpDecorate %val1 RestrictPointer %uint32 = OpTypeInt 32 0 %uint64 = OpTypeInt 64 0 %ptr = OpTypePointer PhysicalStorageBuffer %uint64 @@ -1641,6 +1643,131 @@ OpFunctionEnd "integer type to have a 64-bit width for Vulkan environment.")); } +TEST_F(ValidateConversion, ConvertUToAccelerationStructureU32Vec2) { + const std::string extensions = R"( +OpCapability RayQueryKHR +OpExtension "SPV_KHR_ray_query" +)"; + const std::string types = R"( +%u32vec2ptr_func = OpTypePointer Function %u32vec2 +%typeAS = OpTypeAccelerationStructureKHR +)"; + const std::string body = R"( +%asHandle = OpVariable %u32vec2ptr_func Function +%load = OpLoad %u32vec2 %asHandle +%val = OpConvertUToAccelerationStructureKHR %typeAS %load +)"; + + CompileSuccessfully(GenerateShaderCode(body, extensions, "", types).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, ConvertUToAccelerationStructureSuccessU64) { + const std::string extensions = R"( +OpCapability RayQueryKHR +OpExtension "SPV_KHR_ray_query" +)"; + const std::string types = R"( +%u64_func = OpTypePointer Function %u64 +%typeAS = OpTypeAccelerationStructureKHR +)"; + const std::string body = R"( +%asHandle = OpVariable %u64_func Function +%load = OpLoad %u64 %asHandle +%val = OpConvertUToAccelerationStructureKHR %typeAS %load +)"; + + CompileSuccessfully(GenerateShaderCode(body, extensions, "", types).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, ConvertUToAccelerationStructureResult) { + const std::string extensions = R"( +OpCapability RayQueryKHR +OpExtension "SPV_KHR_ray_query" +)"; + const std::string types = R"( +%u32vec2ptr_func = OpTypePointer Function %u32vec2 +%typeRQ = OpTypeRayQueryKHR +)"; + const std::string body = R"( +%asHandle = OpVariable %u32vec2ptr_func Function +%load = OpLoad %u32vec2 %asHandle +%val = OpConvertUToAccelerationStructureKHR %typeRQ %load +)"; + + CompileSuccessfully(GenerateShaderCode(body, extensions, "", types).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be a Acceleration Structure")); +} + +TEST_F(ValidateConversion, ConvertUToAccelerationStructureU32) { + const std::string extensions = R"( +OpCapability RayQueryKHR +OpExtension "SPV_KHR_ray_query" +)"; + const std::string types = R"( +%u32ptr_func = OpTypePointer Function %u32 +%typeAS = OpTypeAccelerationStructureKHR +)"; + const std::string body = R"( +%asHandle = OpVariable %u32ptr_func Function +%load = OpLoad %u32 %asHandle +%val = OpConvertUToAccelerationStructureKHR %typeAS %load +)"; + + CompileSuccessfully(GenerateShaderCode(body, extensions, "", types).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected 64-bit uint scalar or 2-component 32-bit " + "uint vector as input")); +} + +TEST_F(ValidateConversion, ConvertUToAccelerationStructureS64) { + const std::string extensions = R"( +OpCapability RayQueryKHR +OpExtension "SPV_KHR_ray_query" +)"; + const std::string types = R"( +%s64ptr_func = OpTypePointer Function %s64 +%typeAS = OpTypeAccelerationStructureKHR +)"; + const std::string body = R"( +%asHandle = OpVariable %s64ptr_func Function +%load = OpLoad %s64 %asHandle +%val = OpConvertUToAccelerationStructureKHR %typeAS %load +)"; + + CompileSuccessfully(GenerateShaderCode(body, extensions, "", types).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected 64-bit uint scalar or 2-component 32-bit " + "uint vector as input")); +} + +TEST_F(ValidateConversion, ConvertUToAccelerationStructureS32Vec2) { + const std::string extensions = R"( +OpCapability RayQueryKHR +OpExtension "SPV_KHR_ray_query" +)"; + const std::string types = R"( +%s32vec2ptr_func = OpTypePointer Function %s32vec2 +%typeAS = OpTypeAccelerationStructureKHR +)"; + const std::string body = R"( +%asHandle = OpVariable %s32vec2ptr_func Function +%load = OpLoad %s32vec2 %asHandle +%val = OpConvertUToAccelerationStructureKHR %typeAS %load +)"; + + CompileSuccessfully(GenerateShaderCode(body, extensions, "", types).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected 64-bit uint scalar or 2-component 32-bit " + "uint vector as input")); +} + using ValidateSmallConversions = spvtest::ValidateBase; CodeGenerator GetSmallConversionsCodeGenerator() { diff --git a/test/val/val_data_test.cpp b/test/val/val_data_test.cpp index 1d4c0e04..6a7f243f 100644 --- a/test/val/val_data_test.cpp +++ b/test/val/val_data_test.cpp @@ -387,7 +387,7 @@ TEST_F(ValidateData, ids_should_be_validated_before_data) { CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Operand 3[%3] requires a previous definition")); + HasSubstr("Operand '3[%3]' requires a previous definition")); } TEST_F(ValidateData, matrix_bad_column_type) { @@ -549,7 +549,7 @@ TEST_F(ValidateData, missing_forward_pointer_decl) { CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Operand 3[%3] requires a previous definition")); + HasSubstr("Operand '3[%3]' requires a previous definition")); } TEST_F(ValidateData, missing_forward_pointer_decl_self_reference) { @@ -561,7 +561,7 @@ TEST_F(ValidateData, missing_forward_pointer_decl_self_reference) { ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("Operand 2[%_struct_2] requires a previous definition")); + HasSubstr("Operand '2[%_struct_2]' requires a previous definition")); } TEST_F(ValidateData, forward_pointer_missing_definition) { @@ -766,6 +766,8 @@ TEST_F(ValidateData, vulkan_RTA_not_at_end_of_struct) { CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT(getDiagnosticString(), HasSubstr("In Vulkan, OpTypeRuntimeArray must only be used for " "the last member of an OpTypeStruct\n %_struct_3 = " @@ -822,7 +824,7 @@ OpMemoryModel Logical GLSL450 CompileSuccessfully(test, SPV_ENV_UNIVERSAL_1_5); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Operand 3[%_ptr_PhysicalStorageBuffer__struct_1] " + HasSubstr("Operand '3[%_ptr_PhysicalStorageBuffer__struct_1]' " "requires a previous definition")); } diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp index 096fd172..28ee970a 100644 --- a/test/val/val_decoration_test.cpp +++ b/test/val/val_decoration_test.cpp @@ -42,6 +42,7 @@ struct TestResult { }; using ValidateDecorations = spvtest::ValidateBase; +using ValidateDecorationString = spvtest::ValidateBase; using ValidateVulkanCombineDecorationResult = spvtest::ValidateBase>; @@ -50,20 +51,20 @@ TEST_F(ValidateDecorations, ValidateOpDecorateRegistration) { OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 - OpDecorate %1 ArrayStride 4 - OpDecorate %1 RelaxedPrecision + OpDecorate %1 Location 4 + OpDecorate %1 Centroid %2 = OpTypeFloat 32 - %1 = OpTypeRuntimeArray %2 + %3 = OpTypePointer Output %2 + %1 = OpVariable %3 Output ; Since %1 is used first in Decoration, it gets id 1. )"; const uint32_t id = 1; CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); // Must have 2 decorations. - EXPECT_THAT( - vstate_->id_decorations(id), - Eq(std::vector{Decoration(SpvDecorationArrayStride, {4}), - Decoration(SpvDecorationRelaxedPrecision)})); + EXPECT_THAT(vstate_->id_decorations(id), + Eq(std::set{Decoration(SpvDecorationLocation, {4}), + Decoration(SpvDecorationCentroid)})); } TEST_F(ValidateDecorations, ValidateOpMemberDecorateRegistration) { @@ -88,15 +89,15 @@ TEST_F(ValidateDecorations, ValidateOpMemberDecorateRegistration) { const uint32_t arr_id = 1; EXPECT_THAT( vstate_->id_decorations(arr_id), - Eq(std::vector{Decoration(SpvDecorationArrayStride, {4})})); + Eq(std::set{Decoration(SpvDecorationArrayStride, {4})})); // The struct must have 3 decorations. const uint32_t struct_id = 2; EXPECT_THAT( vstate_->id_decorations(struct_id), - Eq(std::vector{Decoration(SpvDecorationNonReadable, {}, 2), - Decoration(SpvDecorationOffset, {2}, 2), - Decoration(SpvDecorationBufferBlock)})); + Eq(std::set{Decoration(SpvDecorationNonReadable, {}, 2), + Decoration(SpvDecorationOffset, {2}, 2), + Decoration(SpvDecorationBufferBlock)})); } TEST_F(ValidateDecorations, ValidateOpMemberDecorateOutOfBound) { @@ -119,7 +120,7 @@ TEST_F(ValidateDecorations, ValidateOpMemberDecorateOutOfBound) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Index 1 provided in OpMemberDecorate for struct " - "2[%_struct_2] is out of bounds. The structure has 1 " + "'2[%_struct_2]' is out of bounds. The structure has 1 " "members. Largest valid index is 0.")); } @@ -151,9 +152,9 @@ TEST_F(ValidateDecorations, ValidateGroupDecorateRegistration) { // Decoration group has 3 decorations. auto expected_decorations = - std::vector{Decoration(SpvDecorationDescriptorSet, {0}), - Decoration(SpvDecorationRelaxedPrecision), - Decoration(SpvDecorationRestrict)}; + std::set{Decoration(SpvDecorationDescriptorSet, {0}), + Decoration(SpvDecorationRelaxedPrecision), + Decoration(SpvDecorationRestrict)}; // Decoration group is applied to id 1, 2, 3, and 4. Note that id 1 (which is // the decoration group id) also has all the decorations. @@ -181,7 +182,7 @@ TEST_F(ValidateDecorations, ValidateGroupMemberDecorateRegistration) { EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); // Decoration group has 1 decoration. auto expected_decorations = - std::vector{Decoration(SpvDecorationOffset, {3}, 3)}; + std::set{Decoration(SpvDecorationOffset, {3}, 3)}; // Decoration group is applied to id 2, 3, and 4. EXPECT_THAT(vstate_->id_decorations(2), Eq(expected_decorations)); @@ -226,6 +227,7 @@ TEST_F(ValidateDecorations, StructAllMembersHaveBuiltInDecorationsGood) { OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 + OpDecorate %_struct_1 Block OpMemberDecorate %_struct_1 0 BuiltIn Position OpMemberDecorate %_struct_1 1 BuiltIn Position OpMemberDecorate %_struct_1 2 BuiltIn Position @@ -243,6 +245,7 @@ TEST_F(ValidateDecorations, MixedBuiltInDecorationsBad) { OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 + OpDecorate %_struct_1 Block OpMemberDecorate %_struct_1 0 BuiltIn Position OpMemberDecorate %_struct_1 1 BuiltIn Position %float = OpTypeFloat 32 @@ -265,6 +268,7 @@ TEST_F(ValidateDecorations, StructContainsBuiltInStructBad) { OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 + OpDecorate %_struct_1 Block OpMemberDecorate %_struct_1 0 BuiltIn Position OpMemberDecorate %_struct_1 1 BuiltIn Position OpMemberDecorate %_struct_1 2 BuiltIn Position @@ -276,12 +280,13 @@ TEST_F(ValidateDecorations, StructContainsBuiltInStructBad) { )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Structure 1[%_struct_1] contains members with " - "BuiltIn decoration. Therefore this structure may not " - "be contained as a member of another structure type. " - "Structure 4[%_struct_4] contains structure " - "1[%_struct_1].")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Structure '1[%_struct_1]' contains members with " + "BuiltIn decoration. Therefore this structure may not " + "be contained as a member of another structure type. " + "Structure '4[%_struct_4]' contains structure " + "'1[%_struct_1]'.")); } TEST_F(ValidateDecorations, StructContainsNonBuiltInStructGood) { @@ -305,6 +310,8 @@ TEST_F(ValidateDecorations, MultipleBuiltInObjectsConsumedByOpEntryPointBad) { OpEntryPoint Geometry %main "main" %in_1 %in_2 OpExecutionMode %main InputPoints OpExecutionMode %main OutputPoints + OpDecorate %struct_1 Block + OpDecorate %struct_2 Block OpMemberDecorate %struct_1 0 BuiltIn InvocationId OpMemberDecorate %struct_2 0 BuiltIn Position %int = OpTypeInt 32 1 @@ -339,6 +346,8 @@ TEST_F(ValidateDecorations, OpEntryPoint Geometry %main "main" %in_1 %out_1 OpExecutionMode %main InputPoints OpExecutionMode %main OutputPoints + OpDecorate %struct_1 Block + OpDecorate %struct_2 Block OpMemberDecorate %struct_1 0 BuiltIn InvocationId OpMemberDecorate %struct_2 0 BuiltIn Position %int = OpTypeInt 32 1 @@ -559,6 +568,8 @@ OpFunctionEnd CompileSuccessfully(spirv, env); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04915")); EXPECT_THAT(getDiagnosticString(), HasSubstr("A BuiltIn variable (id 2) cannot have any Location or " "Component decorations")); @@ -593,11 +604,355 @@ OpFunctionEnd CompileSuccessfully(spirv, env); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04915")); EXPECT_THAT(getDiagnosticString(), HasSubstr("A BuiltIn variable (id 2) cannot have any Location or " "Component decorations")); } +TEST_F(ValidateDecorations, LocationDecorationOnNumericTypeBad) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord + OpExecutionMode %main OriginUpperLeft + OpDecorate %fragCoord Location 0 + OpDecorate %v4float Location 1 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output +%non_interface = OpVariable %ptr_v4float Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Location decoration on target '3[%v4float]' must " + "be a variable")); +} + +TEST_F(ValidateDecorations, LocationDecorationOnStructBad) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord + OpExecutionMode %main OriginUpperLeft + OpDecorate %fragCoord Location 0 + OpDecorate %struct Location 1 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %struct = OpTypeStruct %float + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output +%non_interface = OpVariable %ptr_v4float Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Location decoration on target '3[%_struct_3]' " + "must be a variable")); +} + +TEST_F(ValidateDecorations, + LocationDecorationUnusedNonInterfaceVariableVulkan_Ignored) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %fragCoord Location 0 + OpDecorate %non_interface Location 1 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output +%non_interface = OpVariable %ptr_v4float Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_EQ(getDiagnosticString(), ""); +} + +TEST_F(ValidateDecorations, + LocationDecorationNonInterfaceStructVulkan_Ignored) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord + OpExecutionMode %main OriginUpperLeft + OpDecorate %fragCoord Location 0 + OpMemberDecorate %block 0 Location 2 + OpMemberDecorate %block 0 Component 1 + OpDecorate %block Block + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %vec3 = OpTypeVector %float 3 +%outvar_ptr = OpTypePointer Output %vec3 + %fragCoord = OpVariable %outvar_ptr Output + %block = OpTypeStruct %vec3 + %invar_ptr = OpTypePointer Input %block +%non_interface = OpVariable %invar_ptr Input + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_EQ(getDiagnosticString(), ""); +} + +TEST_F(ValidateDecorations, LocationDecorationNonInterfaceStructVulkanGood) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord %interface + OpExecutionMode %main OriginUpperLeft + OpDecorate %fragCoord Location 0 + OpMemberDecorate %block 0 Location 2 + OpMemberDecorate %block 0 Component 1 + OpDecorate %block Block + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %vec3 = OpTypeVector %float 3 +%outvar_ptr = OpTypePointer Output %vec3 + %fragCoord = OpVariable %outvar_ptr Output + %block = OpTypeStruct %vec3 + %invar_ptr = OpTypePointer Input %block + %interface = OpVariable %invar_ptr Input ;; this variable is unused. Ignore it + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); +} + +TEST_F(ValidateDecorations, LocationDecorationVariableNonStructVulkanBad) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord %nonblock_var + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %fragCoord Location 0 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output +%nonblock_var = OpVariable %ptr_v4float Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04916")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable must be decorated with a location")); +} + +TEST_F(ValidateDecorations, LocationDecorationVariableStructNoBlockVulkanBad) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord %block_var + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %fragCoord Location 0 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output + %block = OpTypeStruct %v4float + %block_ptr = OpTypePointer Output %block + %block_var = OpVariable %block_ptr Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04917")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable must be decorated with a location")); +} + +TEST_F(ValidateDecorations, LocationDecorationVariableNoBlockVulkanGood) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord %block_var + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %fragCoord Location 0 + OpDecorate %block_var Location 1 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output + %block = OpTypeStruct %v4float + %block_ptr = OpTypePointer Output %block + %block_var = OpVariable %block_ptr Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, LocationDecorationVariableExtraMemeberVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord %block_var + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %fragCoord Location 0 + OpDecorate %block Block + OpDecorate %block_var Location 1 + OpMemberDecorate %block 0 Location 1 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output + %block = OpTypeStruct %v4float + %block_ptr = OpTypePointer Output %block + %block_var = OpVariable %block_ptr Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd + +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04918")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Members cannot be assigned a location")); +} + +TEST_F(ValidateDecorations, LocationDecorationVariableMissingMemeberVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord %block_var + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %fragCoord Location 0 + OpDecorate %block Block + OpMemberDecorate %block 0 Location 1 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output + %block = OpTypeStruct %v4float %v4float + %block_ptr = OpTypePointer Output %block + %block_var = OpVariable %block_ptr Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04919")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Member index 1 is missing a location assignment")); +} + +TEST_F(ValidateDecorations, LocationDecorationVariableOnlyMemeberVulkanGood) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord %block_var + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %fragCoord Location 0 + OpDecorate %block Block + OpMemberDecorate %block 0 Location 1 + OpMemberDecorate %block 1 Location 4 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output + %block = OpTypeStruct %v4float %v4float + %block_ptr = OpTypePointer Output %block + %block_var = OpVariable %block_ptr Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + // #version 440 // #extension GL_EXT_nonuniform_qualifier : enable // layout(binding = 1) uniform sampler2D s2d[]; @@ -687,8 +1042,7 @@ TEST_F(ValidateDecorations, BlockDecoratingArrayBad) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Block decoration on a non-struct type")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); } TEST_F(ValidateDecorations, BlockDecoratingIntBad) { @@ -713,8 +1067,7 @@ TEST_F(ValidateDecorations, BlockDecoratingIntBad) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Block decoration on a non-struct type")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); } TEST_F(ValidateDecorations, BlockMissingOffsetBad) { @@ -2335,6 +2688,8 @@ TEST_F(ValidateDecorations, VulkanBufferBlockOnStorageBufferBad) { CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PushConstant-06675")); EXPECT_THAT(getDiagnosticString(), HasSubstr("In Vulkan, BufferBlock is disallowed on variables in " "the StorageBuffer storage class")); @@ -2529,9 +2884,11 @@ TEST_F(ValidateDecorations, VulkanPushConstantMissingBlockBad) { CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PushConstant-06675")); EXPECT_THAT(getDiagnosticString(), HasSubstr("PushConstant id '2' is missing Block decoration.\n" - "From Vulkan spec, section 14.5.1:\n" + "From Vulkan spec:\n" "Such variables must be identified with a Block " "decoration")); } @@ -2682,11 +3039,13 @@ TEST_F(ValidateDecorations, VulkanMultiplePushConstantsSingleEntryPointBad) { CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpEntryPoint-06674")); EXPECT_THAT( getDiagnosticString(), HasSubstr( "Entry point id '1' uses more than one PushConstant interface.\n" - "From Vulkan spec, section 14.5.1:\n" + "From Vulkan spec:\n" "There must be no more than one push constant block " "statically used per shader entry point.")); } @@ -2793,11 +3152,13 @@ TEST_F(ValidateDecorations, CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpEntryPoint-06674")); EXPECT_THAT( getDiagnosticString(), HasSubstr( "Entry point id '1' uses more than one PushConstant interface.\n" - "From Vulkan spec, section 14.5.1:\n" + "From Vulkan spec:\n" "There must be no more than one push constant block " "statically used per shader entry point.")); } @@ -2834,9 +3195,11 @@ TEST_F(ValidateDecorations, VulkanUniformMissingDescriptorSetBad) { CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677")); EXPECT_THAT(getDiagnosticString(), HasSubstr("Uniform id '3' is missing DescriptorSet decoration.\n" - "From Vulkan spec, section 14.5.2:\n" + "From Vulkan spec:\n" "These variables must have DescriptorSet and Binding " "decorations specified")); } @@ -2873,9 +3236,11 @@ TEST_F(ValidateDecorations, VulkanUniformMissingBindingBad) { CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677")); EXPECT_THAT(getDiagnosticString(), HasSubstr("Uniform id '3' is missing Binding decoration.\n" - "From Vulkan spec, section 14.5.2:\n" + "From Vulkan spec:\n" "These variables must have DescriptorSet and Binding " "decorations specified")); } @@ -2905,10 +3270,12 @@ TEST_F(ValidateDecorations, VulkanUniformConstantMissingDescriptorSetBad) { CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677")); EXPECT_THAT( getDiagnosticString(), HasSubstr("UniformConstant id '2' is missing DescriptorSet decoration.\n" - "From Vulkan spec, section 14.5.2:\n" + "From Vulkan spec:\n" "These variables must have DescriptorSet and Binding " "decorations specified")); } @@ -2938,10 +3305,12 @@ TEST_F(ValidateDecorations, VulkanUniformConstantMissingBindingBad) { CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677")); EXPECT_THAT( getDiagnosticString(), HasSubstr("UniformConstant id '2' is missing Binding decoration.\n" - "From Vulkan spec, section 14.5.2:\n" + "From Vulkan spec:\n" "These variables must have DescriptorSet and Binding " "decorations specified")); } @@ -2978,10 +3347,12 @@ TEST_F(ValidateDecorations, VulkanStorageBufferMissingDescriptorSetBad) { CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677")); EXPECT_THAT( getDiagnosticString(), HasSubstr("StorageBuffer id '3' is missing DescriptorSet decoration.\n" - "From Vulkan spec, section 14.5.2:\n" + "From Vulkan spec:\n" "These variables must have DescriptorSet and Binding " "decorations specified")); } @@ -3018,9 +3389,11 @@ TEST_F(ValidateDecorations, VulkanStorageBufferMissingBindingBad) { CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677")); EXPECT_THAT(getDiagnosticString(), HasSubstr("StorageBuffer id '3' is missing Binding decoration.\n" - "From Vulkan spec, section 14.5.2:\n" + "From Vulkan spec:\n" "These variables must have DescriptorSet and Binding " "decorations specified")); } @@ -3063,10 +3436,12 @@ TEST_F(ValidateDecorations, CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677")); EXPECT_THAT( getDiagnosticString(), HasSubstr("StorageBuffer id '3' is missing DescriptorSet decoration.\n" - "From Vulkan spec, section 14.5.2:\n" + "From Vulkan spec:\n" "These variables must have DescriptorSet and Binding " "decorations specified")); } @@ -4313,7 +4688,7 @@ OpDecorate %1 Coherent CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Coherent decoration targeting 1[%1] is " + HasSubstr("Coherent decoration targeting '1[%1]' is " "banned when using the Vulkan memory model.")); } @@ -4333,8 +4708,9 @@ OpMemberDecorate %1 0 Coherent EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), - HasSubstr("Coherent decoration targeting 1[%_struct_1] (member index 0) " - "is banned when using the Vulkan memory model.")); + HasSubstr( + "Coherent decoration targeting '1[%_struct_1]' (member index 0) " + "is banned when using the Vulkan memory model.")); } TEST_F(ValidateDecorations, VulkanMemoryModelNoVolatile) { @@ -4354,7 +4730,7 @@ OpDecorate %1 Volatile CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Volatile decoration targeting 1[%1] is banned when " + HasSubstr("Volatile decoration targeting '1[%1]' is banned when " "using the Vulkan memory model.")); } @@ -4373,7 +4749,7 @@ OpMemberDecorate %1 1 Volatile CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Volatile decoration targeting 1[%_struct_1] (member " + HasSubstr("Volatile decoration targeting '1[%_struct_1]' (member " "index 1) is banned when using the Vulkan memory " "model.")); } @@ -5592,16 +5968,16 @@ OpFunctionEnd TEST_F(ValidateDecorations, PSBAliasedRestrictPointerSuccess) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft -OpDecorate %val1 RestrictPointerEXT +OpDecorate %val1 RestrictPointer %uint64 = OpTypeInt 64 0 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %pptr_f = OpTypePointer Function %ptr %void = OpTypeVoid %voidfn = OpTypeFunction %void @@ -5618,15 +5994,15 @@ OpFunctionEnd TEST_F(ValidateDecorations, PSBAliasedRestrictPointerMissing) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft %uint64 = OpTypeInt 64 0 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %pptr_f = OpTypePointer Function %ptr %void = OpTypeVoid %voidfn = OpTypeFunction %void @@ -5640,23 +6016,23 @@ OpFunctionEnd CompileSuccessfully(body.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected AliasedPointerEXT or RestrictPointerEXT for " - "PhysicalStorageBufferEXT pointer")); + HasSubstr("expected AliasedPointer or RestrictPointer for " + "PhysicalStorageBuffer pointer")); } TEST_F(ValidateDecorations, PSBAliasedRestrictPointerBoth) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft -OpDecorate %val1 RestrictPointerEXT -OpDecorate %val1 AliasedPointerEXT +OpDecorate %val1 RestrictPointer +OpDecorate %val1 AliasedPointer %uint64 = OpTypeInt 64 0 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %pptr_f = OpTypePointer Function %ptr %void = OpTypeVoid %voidfn = OpTypeFunction %void @@ -5669,24 +6045,23 @@ OpFunctionEnd CompileSuccessfully(body.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("can't specify both AliasedPointerEXT and RestrictPointerEXT " - "for PhysicalStorageBufferEXT pointer")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("can't specify both AliasedPointer and RestrictPointer " + "for PhysicalStorageBuffer pointer")); } TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamSuccess) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpDecorate %fparam Restrict %uint64 = OpTypeInt 64 0 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %void = OpTypeVoid %voidfn = OpTypeFunction %void %fnptr = OpTypeFunction %void %ptr @@ -5707,15 +6082,15 @@ OpFunctionEnd TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamMissing) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft %uint64 = OpTypeInt 64 0 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %void = OpTypeVoid %voidfn = OpTypeFunction %void %fnptr = OpTypeFunction %void %ptr @@ -5734,22 +6109,22 @@ OpFunctionEnd ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected Aliased or Restrict for " - "PhysicalStorageBufferEXT pointer")); + "PhysicalStorageBuffer pointer")); } TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamBoth) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpDecorate %fparam Restrict OpDecorate %fparam Aliased %uint64 = OpTypeInt 64 0 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %void = OpTypeVoid %voidfn = OpTypeFunction %void %fnptr = OpTypeFunction %void %ptr @@ -5768,12 +6143,12 @@ OpFunctionEnd ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("can't specify both Aliased and Restrict for " - "PhysicalStorageBufferEXT pointer")); + "PhysicalStorageBuffer pointer")); } TEST_F(ValidateDecorations, PSBFPRoundingModeSuccess) { std::string spirv = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Shader OpCapability Linkage OpCapability StorageBuffer16BitAccess @@ -5781,14 +6156,14 @@ OpExtension "SPV_EXT_physical_storage_buffer" OpExtension "SPV_KHR_storage_buffer_storage_class" OpExtension "SPV_KHR_variable_pointers" OpExtension "SPV_KHR_16bit_storage" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint GLCompute %main "main" OpDecorate %_ FPRoundingMode RTE -OpDecorate %half_ptr_var AliasedPointerEXT +OpDecorate %half_ptr_var AliasedPointer %half = OpTypeFloat 16 %float = OpTypeFloat 32 %float_1_25 = OpConstant %float 1.25 -%half_ptr = OpTypePointer PhysicalStorageBufferEXT %half +%half_ptr = OpTypePointer PhysicalStorageBuffer %half %half_pptr_f = OpTypePointer Function %half_ptr %void = OpTypeVoid %func = OpTypeFunction %void @@ -6129,9 +6504,7 @@ TEST_F(ValidateDecorations, NonWritableLabelTargetBad) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target of NonWritable decoration must be a " - "memory object declaration (a variable or a function " - "parameter)\n %label = OpLabel")); + HasSubstr("must be a memory object declaration")); } TEST_F(ValidateDecorations, NonWritableTypeTargetBad) { @@ -6140,9 +6513,7 @@ TEST_F(ValidateDecorations, NonWritableTypeTargetBad) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target of NonWritable decoration must be a " - "memory object declaration (a variable or a function " - "parameter)\n %void = OpTypeVoid")); + HasSubstr("must be a memory object declaration")); } TEST_F(ValidateDecorations, NonWritableValueTargetBad) { @@ -6151,9 +6522,7 @@ TEST_F(ValidateDecorations, NonWritableValueTargetBad) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target of NonWritable decoration must be a " - "memory object declaration (a variable or a function " - "parameter)\n %float_0 = OpConstant %float 0")); + HasSubstr("must be a memory object declaration")); } TEST_F(ValidateDecorations, NonWritableValueParamBad) { @@ -6161,10 +6530,7 @@ TEST_F(ValidateDecorations, NonWritableValueParamBad) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target of NonWritable decoration is invalid: must " - "point to a storage image, uniform block, or storage " - "buffer\n %param_f = OpFunctionParameter %float")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a pointer type")); } TEST_F(ValidateDecorations, NonWritablePointerParamButWrongTypeBad) { @@ -6467,8 +6833,7 @@ OpFunctionEnd CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target of Component decoration must be " - "a memory object declaration")); + HasSubstr("must be a memory object declaration")); } TEST_F(ValidateDecorations, ComponentDecorationBadStorageClass) { @@ -6767,8 +7132,8 @@ TEST_F(ValidateDecorations, ComponentDecorationFunctionParameter) { )"; CompileSuccessfully(spirv); - EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); - EXPECT_THAT(getDiagnosticString(), Eq("")); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a pointer type")); } TEST_F(ValidateDecorations, VulkanStorageBufferBlock) { @@ -6818,7 +7183,9 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables " + AnyVUID("VUID-StandaloneSpirv-PushConstant-06675")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("From Vulkan spec:\nSuch variables " "must be identified with a Block decoration")); } @@ -6846,7 +7213,9 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables " + AnyVUID("VUID-StandaloneSpirv-PushConstant-06675")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("From Vulkan spec:\nSuch variables " "must be identified with a Block decoration")); } @@ -6875,7 +7244,9 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables " + AnyVUID("VUID-StandaloneSpirv-PushConstant-06675")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("From Vulkan spec:\nSuch variables " "must be identified with a Block decoration")); } @@ -6947,10 +7318,11 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables must be " - "identified with a Block or BufferBlock decoration")); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Uniform-06676")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("From Vulkan spec:\nSuch variables must be " + "identified with a Block or BufferBlock decoration")); } TEST_F(ValidateDecorations, VulkanUniformArrayMissingBlock) { @@ -6975,10 +7347,11 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables must be " - "identified with a Block or BufferBlock decoration")); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Uniform-06676")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("From Vulkan spec:\nSuch variables must be " + "identified with a Block or BufferBlock decoration")); } TEST_F(ValidateDecorations, VulkanUniformRuntimeArrayMissingBlock) { @@ -7004,10 +7377,11 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables must be " - "identified with a Block or BufferBlock decoration")); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Uniform-06676")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("From Vulkan spec:\nSuch variables must be " + "identified with a Block or BufferBlock decoration")); } TEST_F(ValidateDecorations, VulkanArrayStrideZero) { @@ -7164,9 +7538,7 @@ OpDecorate %struct Location 0 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")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); } TEST_F(ValidateDecorations, LocationFloatBad) { @@ -7180,9 +7552,7 @@ OpDecorate %float Location 0 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")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); } TEST_F(ValidateDecorations, WorkgroupSingleBlockVariable) { @@ -7571,9 +7941,7 @@ TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableNotAStruct) { CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Block decoration on a non-struct type")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); } TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableMissingLayout) { @@ -7648,6 +8016,996 @@ TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableBadLayout) { "member 0 at offset 1 is not aligned to 4")); } +TEST_F(ValidateDecorations, BadMatrixStrideUniform) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 0 MatrixStride 3 +OpMemberDecorate %block 0 ColMajor +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float4 = OpTypeVector %float 4 +%matrix4x4 = OpTypeMatrix %float4 4 +%block = OpTypeStruct %matrix4x4 +%block_ptr = OpTypePointer Uniform %block +%var = OpVariable %block_ptr Uniform +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 2 decorated as Block for variable in Uniform storage " + "class must follow standard uniform buffer layout rules: member 0 is " + "a matrix with stride 3 not satisfying alignment to 16")); +} + +TEST_F(ValidateDecorations, BadMatrixStrideStorageBuffer) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 0 MatrixStride 3 +OpMemberDecorate %block 0 ColMajor +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float4 = OpTypeVector %float 4 +%matrix4x4 = OpTypeMatrix %float4 4 +%block = OpTypeStruct %matrix4x4 +%block_ptr = OpTypePointer StorageBuffer %block +%var = OpVariable %block_ptr StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 2 decorated as Block for variable in StorageBuffer " + "storage class must follow standard storage buffer layout rules: " + "member 0 is a matrix with stride 3 not satisfying alignment to 16")); +} + +TEST_F(ValidateDecorations, BadMatrixStridePushConstant) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 0 MatrixStride 3 +OpMemberDecorate %block 0 ColMajor +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float4 = OpTypeVector %float 4 +%matrix4x4 = OpTypeMatrix %float4 4 +%block = OpTypeStruct %matrix4x4 +%block_ptr = OpTypePointer PushConstant %block +%var = OpVariable %block_ptr PushConstant +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 2 decorated as Block for variable in PushConstant " + "storage class must follow standard storage buffer layout rules: " + "member 0 is a matrix with stride 3 not satisfying alignment to 16")); +} + +TEST_F(ValidateDecorations, BadMatrixStrideStorageBufferScalarLayout) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 0 MatrixStride 3 +OpMemberDecorate %block 0 RowMajor +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float4 = OpTypeVector %float 4 +%matrix4x4 = OpTypeMatrix %float4 4 +%block = OpTypeStruct %matrix4x4 +%block_ptr = OpTypePointer StorageBuffer %block +%var = OpVariable %block_ptr StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + options_->scalar_block_layout = true; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 2 decorated as Block for variable in StorageBuffer " + "storage class must follow scalar storage buffer layout rules: " + "member 0 is a matrix with stride 3 not satisfying alignment to 4")); +} + +TEST_F(ValidateDecorations, MissingOffsetStructNestedInArray) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %array ArrayStride 4 +OpDecorate %outer Block +OpMemberDecorate %outer 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%inner = OpTypeStruct %int +%array = OpTypeArray %inner %int_4 +%outer = OpTypeStruct %array +%ptr_ssbo_outer = OpTypePointer StorageBuffer %outer +%var = OpVariable %ptr_ssbo_outer StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Structure id 3 decorated as Block must be explicitly " + "laid out with Offset decorations")); +} + +TEST_F(ValidateDecorations, AllOnesOffset) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +OpDecorate %outer Block +OpMemberDecorate %outer 0 Offset 0 +OpMemberDecorate %struct 0 Offset 4294967295 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%struct = OpTypeStruct %int +%outer = OpTypeStruct %struct +%ptr = OpTypePointer Uniform %outer +%var = OpVariable %ptr Uniform +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("decorated as Block must be explicitly laid out with " + "Offset decorations")); +} + +TEST_F(ValidateDecorations, PerVertexVulkanGood) { + const std::string spirv = R"( + OpCapability Shader + OpCapability FragmentBarycentricKHR + OpExtension "SPV_KHR_fragment_shader_barycentric" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vertexIDs + OpExecutionMode %main OriginUpperLeft + OpDecorate %vertexIDs Location 0 + OpDecorate %vertexIDs PerVertexKHR + %void = OpTypeVoid + %func = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 +%ptrFloat = OpTypePointer Input %float + %uint_3 = OpConstant %uint 3 +%floatArray = OpTypeArray %float %uint_3 +%ptrFloatArray = OpTypePointer Input %floatArray + %vertexIDs = OpVariable %ptrFloatArray Input + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %main = OpFunction %void None %func + %label = OpLabel + %access = OpAccessChain %ptrFloat %vertexIDs %int_0 + %load = OpLoad %float %access + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateDecorations, PerVertexVulkanOutput) { + const std::string spirv = R"( + OpCapability Shader + OpCapability FragmentBarycentricKHR + OpExtension "SPV_KHR_fragment_shader_barycentric" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vertexIDs + OpExecutionMode %main OriginUpperLeft + OpDecorate %vertexIDs Location 0 + OpDecorate %vertexIDs PerVertexKHR + %void = OpTypeVoid + %func = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 +%ptrFloat = OpTypePointer Output %float + %uint_3 = OpConstant %uint 3 +%floatArray = OpTypeArray %float %uint_3 +%ptrFloatArray = OpTypePointer Output %floatArray + %vertexIDs = OpVariable %ptrFloatArray Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %main = OpFunction %void None %func + %label = OpLabel + %access = OpAccessChain %ptrFloat %vertexIDs %int_0 + %load = OpLoad %float %access + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PerVertexKHR-06777")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("storage class must be Input")); +} + +TEST_F(ValidateDecorations, PerVertexVulkanNonFragment) { + const std::string spirv = R"( + OpCapability Shader + OpCapability FragmentBarycentricKHR + OpExtension "SPV_KHR_fragment_shader_barycentric" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %vertexIDs + OpDecorate %vertexIDs Location 0 + OpDecorate %vertexIDs PerVertexKHR + %void = OpTypeVoid + %func = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 +%ptrFloat = OpTypePointer Input %float + %uint_3 = OpConstant %uint 3 +%floatArray = OpTypeArray %float %uint_3 +%ptrFloatArray = OpTypePointer Input %floatArray + %vertexIDs = OpVariable %ptrFloatArray Input + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %main = OpFunction %void None %func + %label = OpLabel + %access = OpAccessChain %ptrFloat %vertexIDs %int_0 + %load = OpLoad %float %access + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PerVertexKHR-06777")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "PerVertexKHR can only be applied to Fragment Execution Models")); +} + +TEST_F(ValidateDecorations, PerVertexVulkanNonArray) { + const std::string spirv = R"( + OpCapability Shader + OpCapability FragmentBarycentricKHR + OpExtension "SPV_KHR_fragment_shader_barycentric" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vertexIDs + OpExecutionMode %main OriginUpperLeft + OpDecorate %vertexIDs Location 0 + OpDecorate %vertexIDs PerVertexKHR + %void = OpTypeVoid + %func = OpTypeFunction %void + %float = OpTypeFloat 32 + %ptrFloat = OpTypePointer Input %float + %vertexIDs = OpVariable %ptrFloat Input + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %main = OpFunction %void None %func + %label = OpLabel + %load = OpLoad %float %vertexIDs + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Input-06778")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("PerVertexKHR must be declared as arrays")); +} + +TEST_F(ValidateDecorations, RelaxedPrecisionDecorationOnNumericTypeBad) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpDecorate %float RelaxedPrecision + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("RelaxPrecision decoration cannot be applied to a type")); +} + +TEST_F(ValidateDecorations, RelaxedPrecisionDecorationOnStructMember) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpMemberDecorate %struct 0 RelaxedPrecision + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %struct = OpTypeStruct %float + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); +} + +TEST_F(ValidateDecorations, VulkanFlatMultipleInterfaceGood) { + std::string spirv = R"( + OpCapability Shader + OpCapability Geometry + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %layer %gl_Layer + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %layer Location 0 + OpDecorate %gl_Layer Flat + OpDecorate %gl_Layer BuiltIn Layer + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Output_int = OpTypePointer Output %int + %layer = OpVariable %_ptr_Output_int Output +%_ptr_Input_int = OpTypePointer Input %int + %gl_Layer = OpVariable %_ptr_Input_int Input + %main = OpFunction %void None %3 + %5 = OpLabel + %11 = OpLoad %int %gl_Layer + OpStore %layer %11 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateDecorations, VulkanFlatMultipleInterfaceBad) { + std::string spirv = R"( + OpCapability Shader + OpCapability Geometry + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %layer %gl_Layer + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %layer Location 0 + OpDecorate %gl_Layer BuiltIn Layer + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Output_int = OpTypePointer Output %int + %layer = OpVariable %_ptr_Output_int Output +%_ptr_Input_int = OpTypePointer Input %int + %gl_Layer = OpVariable %_ptr_Input_int Input + %main = OpFunction %void None %3 + %5 = OpLabel + %11 = OpLoad %int %gl_Layer + OpStore %layer %11 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Flat-04744")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Fragment OpEntryPoint operand 4 with Input interfaces with integer " + "or float type must have a Flat decoration for Entry Point id 2.")); +} + +TEST_F(ValidateDecorations, VulkanNoFlatFloat32) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %in Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Input_float = OpTypePointer Input %float + %in = OpVariable %_ptr_Input_float Input + %main = OpFunction %void None %3 + %5 = OpLabel + %b = OpVariable %_ptr_Function_float Function + %11 = OpLoad %float %in + OpStore %b %11 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateDecorations, VulkanNoFlatFloat64) { + std::string spirv = R"( + OpCapability Shader + OpCapability Float64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %in Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %double = OpTypeFloat 64 +%_ptr_Function_double = OpTypePointer Function %double +%_ptr_Input_double = OpTypePointer Input %double + %in = OpVariable %_ptr_Input_double Input + %main = OpFunction %void None %3 + %5 = OpLabel + %b = OpVariable %_ptr_Function_double Function + %11 = OpLoad %double %in + OpStore %b %11 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Flat-04744")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Fragment OpEntryPoint operand 3 with Input interfaces with integer " + "or float type must have a Flat decoration for Entry Point id 2.")); +} + +TEST_F(ValidateDecorations, VulkanNoFlatVectorFloat64) { + std::string spirv = R"( + OpCapability Shader + OpCapability Float64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %in Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %double = OpTypeFloat 64 + %v2double = OpTypeVector %double 2 +%_ptr_Function_v2double = OpTypePointer Function %v2double +%_ptr_Input_v2double = OpTypePointer Input %v2double + %in = OpVariable %_ptr_Input_v2double Input + %main = OpFunction %void None %3 + %5 = OpLabel + %b = OpVariable %_ptr_Function_v2double Function + %11 = OpLoad %v2double %in + OpStore %b %11 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateDecorations, VulkanNoFlatIntVector) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %in Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 +%_ptr_Function_v2int = OpTypePointer Function %v2int +%_ptr_Input_v2int = OpTypePointer Input %v2int + %in = OpVariable %_ptr_Input_v2int Input + %main = OpFunction %void None %3 + %5 = OpLabel + %b = OpVariable %_ptr_Function_v2int Function + %12 = OpLoad %v2int %in + OpStore %b %12 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Flat-04744")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Fragment OpEntryPoint operand 3 with Input interfaces with integer " + "or float type must have a Flat decoration for Entry Point id 2.")); +} + +TEST_P(ValidateDecorationString, VulkanOutputInvalidInterface) { + const std::string decoration = GetParam(); + std::stringstream ss; + ss << R"( + OpCapability Shader + OpCapability SampleRateShading + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %out )" + << decoration << R"( + OpDecorate %out Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Output_int = OpTypePointer Output %int + %out = OpVariable %_ptr_Output_int Output + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %out %int_1 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(ss.str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Flat-06201")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpEntryPoint interfaces variable must not be fragment execution " + "model with an output storage class for Entry Point id 2.")); +} + +TEST_P(ValidateDecorationString, VulkanVertexInputInvalidInterface) { + const std::string decoration = GetParam(); + std::stringstream ss; + ss << R"( + OpCapability Shader + OpCapability SampleRateShading + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %out %in + OpSource GLSL 450 + OpDecorate %in )" + << decoration << R"( + OpDecorate %out Location 0 + OpDecorate %in Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Output_int = OpTypePointer Output %int + %out = OpVariable %_ptr_Output_int Output +%_ptr_Input_int = OpTypePointer Input %int + %in = OpVariable %_ptr_Input_int Input + %main = OpFunction %void None %3 + %5 = OpLabel + %11 = OpLoad %int %in + OpStore %out %11 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(ss.str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Flat-06202")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpEntryPoint interfaces variable must not be vertex execution " + "model with an input storage class for Entry Point id 2.")); +} + +INSTANTIATE_TEST_SUITE_P(FragmentInputInterface, ValidateDecorationString, + ::testing::Values("Flat", "NoPerspective", "Sample", + "Centroid")); + +TEST_F(ValidateDecorations, NVBindlessSamplerArrayInBlock) { + const std::string spirv = R"( + OpCapability Shader + OpCapability BindlessTextureNV + OpExtension "SPV_NV_bindless_texture" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpSamplerImageAddressingModeNV 64 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %UBO "UBO" + OpMemberName %UBO 0 "uboSampler" + OpName %_ "" + OpDecorate %array ArrayStride 16 + OpMemberDecorate %UBO 0 Offset 0 + OpDecorate %UBO Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 2 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %7 = OpTypeImage %float 2D 0 0 0 1 Unknown + %8 = OpTypeSampledImage %7 + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %array = OpTypeArray %8 %uint_3 + %UBO = OpTypeStruct %array + %pointer = OpTypePointer Uniform %UBO + %_ = OpVariable %pointer Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateDecorations, Std140ColMajorMat2x2) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 0 ColMajor +OpMemberDecorate %block 0 MatrixStride 8 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%float2 = OpTypeVector %float 2 +%matrix = OpTypeMatrix %float2 2 +%block = OpTypeStruct %matrix +%ptr_block = OpTypePointer Uniform %block +%var = OpVariable %ptr_block Uniform +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "member 0 is a matrix with stride 8 not satisfying alignment to 16")); +} + +TEST_F(ValidateDecorations, Std140RowMajorMat2x2) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 0 RowMajor +OpMemberDecorate %block 0 MatrixStride 8 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%float2 = OpTypeVector %float 2 +%matrix = OpTypeMatrix %float2 2 +%block = OpTypeStruct %matrix +%ptr_block = OpTypePointer Uniform %block +%var = OpVariable %ptr_block Uniform +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "member 0 is a matrix with stride 8 not satisfying alignment to 16")); +} + +TEST_F(ValidateDecorations, Std140ColMajorMat4x2) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 0 ColMajor +OpMemberDecorate %block 0 MatrixStride 8 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%float2 = OpTypeVector %float 2 +%matrix = OpTypeMatrix %float2 4 +%block = OpTypeStruct %matrix +%ptr_block = OpTypePointer Uniform %block +%var = OpVariable %ptr_block Uniform +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "member 0 is a matrix with stride 8 not satisfying alignment to 16")); +} + +TEST_F(ValidateDecorations, Std140ColMajorMat2x3) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 0 ColMajor +OpMemberDecorate %block 0 MatrixStride 12 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%float3 = OpTypeVector %float 3 +%matrix = OpTypeMatrix %float3 2 +%block = OpTypeStruct %matrix +%ptr_block = OpTypePointer Uniform %block +%var = OpVariable %ptr_block Uniform +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("member 0 is a matrix with stride 12 not satisfying " + "alignment to 16")); +} + +TEST_F(ValidateDecorations, MatrixMissingMajornessUniform) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 0 MatrixStride 16 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%float2 = OpTypeVector %float 2 +%matrix = OpTypeMatrix %float2 2 +%block = OpTypeStruct %matrix +%ptr_block = OpTypePointer Uniform %block +%var = OpVariable %ptr_block Uniform +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "must be explicitly laid out with RowMajor or ColMajor decorations")); +} + +TEST_F(ValidateDecorations, MatrixMissingMajornessStorageBuffer) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 0 MatrixStride 16 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%float2 = OpTypeVector %float 2 +%matrix = OpTypeMatrix %float2 2 +%block = OpTypeStruct %matrix +%ptr_block = OpTypePointer StorageBuffer %block +%var = OpVariable %ptr_block StorageBuffer +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "must be explicitly laid out with RowMajor or ColMajor decorations")); +} + +TEST_F(ValidateDecorations, MatrixMissingMajornessPushConstant) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 0 MatrixStride 16 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%float2 = OpTypeVector %float 2 +%matrix = OpTypeMatrix %float2 2 +%block = OpTypeStruct %matrix +%ptr_block = OpTypePointer PushConstant %block +%var = OpVariable %ptr_block PushConstant +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "must be explicitly laid out with RowMajor or ColMajor decorations")); +} + +TEST_F(ValidateDecorations, StructWithRowAndColMajor) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 0 MatrixStride 16 +OpMemberDecorate %block 0 ColMajor +OpMemberDecorate %block 1 Offset 32 +OpMemberDecorate %block 1 MatrixStride 16 +OpMemberDecorate %block 1 RowMajor +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%float2 = OpTypeVector %float 2 +%matrix = OpTypeMatrix %float2 2 +%block = OpTypeStruct %matrix %matrix +%ptr_block = OpTypePointer PushConstant %block +%var = OpVariable %ptr_block PushConstant +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_derivatives_test.cpp b/test/val/val_derivatives_test.cpp index 606abb93..e605f3a0 100644 --- a/test/val/val_derivatives_test.cpp +++ b/test/val/val_derivatives_test.cpp @@ -130,7 +130,7 @@ TEST_F(ValidateDerivatives, OpDPdxWrongResultType) { CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 10[%v4float] cannot " + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand '10[%v4float]' cannot " "be a type")); } @@ -160,6 +160,31 @@ TEST_F(ValidateDerivatives, OpDPdxWrongExecutionModel) { "execution model: DPdx")); } +TEST_F(ValidateDerivatives, NoExecutionModeGLCompute) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float4 = OpTypeVector %float 4 +%undef = OpUndef %float4 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%derivative = OpDPdy %float4 %undef +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Derivative instructions require " + "DerivativeGroupQuadsNV or DerivativeGroupLinearNV " + "execution mode for GLCompute execution model")); +} + using ValidateHalfDerivatives = spvtest::ValidateBase; TEST_P(ValidateHalfDerivatives, ScalarFailure) { diff --git a/test/val/val_entry_point_test.cpp b/test/val/val_entry_point_test.cpp new file mode 100644 index 00000000..f28cf5d1 --- /dev/null +++ b/test/val/val_entry_point_test.cpp @@ -0,0 +1,76 @@ +// Copyright (c) 2019 Samsung Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace { + +using ::testing::Eq; +using ::testing::HasSubstr; + +using ValidateEntryPoints = spvtest::ValidateBase; + +TEST_F(ValidateEntryPoints, DuplicateEntryPoints) { + const std::string body = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %3 "foo" +OpEntryPoint GLCompute %4 "foo" +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%20 = OpLabel +OpReturn +OpFunctionEnd +%4 = OpFunction %1 None %2 +%21 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry points cannot share the same name")); +} + +TEST_F(ValidateEntryPoints, UniqueEntryPoints) { + const std::string body = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %3 "foo" +OpEntryPoint GLCompute %4 "foo2" +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%20 = OpLabel +OpReturn +OpFunctionEnd +%4 = OpFunction %1 None %2 +%21 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +} // namespace +} // namespace spvtools diff --git a/test/val/val_ext_inst_debug_test.cpp b/test/val/val_ext_inst_debug_test.cpp new file mode 100644 index 00000000..554e78b0 --- /dev/null +++ b/test/val/val_ext_inst_debug_test.cpp @@ -0,0 +1,5480 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests validation rules of GLSL.450.std and OpenCL.std extended instructions. +// Doesn't test OpenCL.std vector size 2, 3, 4, 8 or 16 rules (not supported +// by standard SPIR-V). + +#include +#include +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::Not; + +using ValidateOldDebugInfo = spvtest::ValidateBase; +using ValidateOpenCL100DebugInfo = spvtest::ValidateBase; +using ValidateXDebugInfo = spvtest::ValidateBase; +using ValidateLocalDebugInfoOutOfFunction = spvtest::ValidateBase; +using ValidateOpenCL100DebugInfoDebugTypedef = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugTypedef = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugTypeEnum = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugTypeEnum = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugTypeComposite = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugTypeComposite = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugTypeMember = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugTypeMember = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugTypeInheritance = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugFunction = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugFunction = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugFunctionDeclaration = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugFunctionDeclaration = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugLexicalBlock = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugLexicalBlock = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugLocalVariable = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugLocalVariable = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugGlobalVariable = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugGlobalVariable = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugDeclare = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugDeclare = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugValue = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugValue = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfo = spvtest::ValidateBase; + +std::string GenerateShaderCodeForDebugInfo( + const std::string& op_string_instructions, + const std::string& op_const_instructions, + const std::string& debug_instructions_before_main, const std::string& body, + const std::string& capabilities_and_extensions = "", + const std::string& execution_model = "Fragment") { + std::ostringstream ss; + ss << R"( +OpCapability Shader +OpCapability Float16 +OpCapability Float64 +OpCapability Int16 +OpCapability Int64 +)"; + + ss << capabilities_and_extensions; + ss << "%extinst = OpExtInstImport \"GLSL.std.450\"\n"; + ss << "OpMemoryModel Logical GLSL450\n"; + ss << "OpEntryPoint " << execution_model << " %main \"main\"" + << " %f32_output" + << " %f32vec2_output" + << " %u32_output" + << " %u32vec2_output" + << " %u64_output" + << " %f32_input" + << " %f32vec2_input" + << " %u32_input" + << " %u32vec2_input" + << " %u64_input" + << "\n"; + if (execution_model == "Fragment") { + ss << "OpExecutionMode %main OriginUpperLeft\n"; + } + + ss << op_string_instructions; + + ss << R"( +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f16 = OpTypeFloat 16 +%f32 = OpTypeFloat 32 +%f64 = OpTypeFloat 64 +%u32 = OpTypeInt 32 0 +%s32 = OpTypeInt 32 1 +%u64 = OpTypeInt 64 0 +%s64 = OpTypeInt 64 1 +%u16 = OpTypeInt 16 0 +%s16 = OpTypeInt 16 1 +%f32vec2 = OpTypeVector %f32 2 +%f32vec3 = OpTypeVector %f32 3 +%f32vec4 = OpTypeVector %f32 4 +%f64vec2 = OpTypeVector %f64 2 +%f64vec3 = OpTypeVector %f64 3 +%f64vec4 = OpTypeVector %f64 4 +%u32vec2 = OpTypeVector %u32 2 +%u32vec3 = OpTypeVector %u32 3 +%s32vec2 = OpTypeVector %s32 2 +%u32vec4 = OpTypeVector %u32 4 +%s32vec4 = OpTypeVector %s32 4 +%u64vec2 = OpTypeVector %u64 2 +%s64vec2 = OpTypeVector %s64 2 +%f64mat22 = OpTypeMatrix %f64vec2 2 +%f32mat22 = OpTypeMatrix %f32vec2 2 +%f32mat23 = OpTypeMatrix %f32vec2 3 +%f32mat32 = OpTypeMatrix %f32vec3 2 +%f32mat33 = OpTypeMatrix %f32vec3 3 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%f32_2 = OpConstant %f32 2 +%f32_3 = OpConstant %f32 3 +%f32_4 = OpConstant %f32 4 +%f32_h = OpConstant %f32 0.5 +%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 +%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 +%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2 +%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3 +%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 +%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4 + +%f64_0 = OpConstant %f64 0 +%f64_1 = OpConstant %f64 1 +%f64_2 = OpConstant %f64 2 +%f64_3 = OpConstant %f64 3 +%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1 +%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2 +%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3 + +%f16_0 = OpConstant %f16 0 +%f16_1 = OpConstant %f16 1 +%f16_h = OpConstant %f16 0.5 + +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_2 = OpConstant %u32 2 +%u32_3 = OpConstant %u32 3 + +%s32_0 = OpConstant %s32 0 +%s32_1 = OpConstant %s32 1 +%s32_2 = OpConstant %s32 2 +%s32_3 = OpConstant %s32 3 + +%u64_0 = OpConstant %u64 0 +%u64_1 = OpConstant %u64 1 +%u64_2 = OpConstant %u64 2 +%u64_3 = OpConstant %u64 3 + +%s64_0 = OpConstant %s64 0 +%s64_1 = OpConstant %s64 1 +%s64_2 = OpConstant %s64 2 +%s64_3 = OpConstant %s64 3 +)"; + + ss << op_const_instructions; + + ss << R"( +%s32vec2_01 = OpConstantComposite %s32vec2 %s32_0 %s32_1 +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 + +%s32vec2_12 = OpConstantComposite %s32vec2 %s32_1 %s32_2 +%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 + +%s32vec4_0123 = OpConstantComposite %s32vec4 %s32_0 %s32_1 %s32_2 %s32_3 +%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 + +%s64vec2_01 = OpConstantComposite %s64vec2 %s64_0 %s64_1 +%u64vec2_01 = OpConstantComposite %u64vec2 %u64_0 %u64_1 + +%f32mat22_1212 = OpConstantComposite %f32mat22 %f32vec2_12 %f32vec2_12 +%f32mat23_121212 = OpConstantComposite %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12 + +%f32_ptr_output = OpTypePointer Output %f32 +%f32vec2_ptr_output = OpTypePointer Output %f32vec2 + +%u32_ptr_output = OpTypePointer Output %u32 +%u32vec2_ptr_output = OpTypePointer Output %u32vec2 + +%u64_ptr_output = OpTypePointer Output %u64 + +%f32_output = OpVariable %f32_ptr_output Output +%f32vec2_output = OpVariable %f32vec2_ptr_output Output + +%u32_output = OpVariable %u32_ptr_output Output +%u32vec2_output = OpVariable %u32vec2_ptr_output Output + +%u64_output = OpVariable %u64_ptr_output Output + +%f32_ptr_input = OpTypePointer Input %f32 +%f32vec2_ptr_input = OpTypePointer Input %f32vec2 + +%u32_ptr_input = OpTypePointer Input %u32 +%u32vec2_ptr_input = OpTypePointer Input %u32vec2 + +%u64_ptr_input = OpTypePointer Input %u64 + +%f32_ptr_function = OpTypePointer Function %f32 + +%f32_input = OpVariable %f32_ptr_input Input +%f32vec2_input = OpVariable %f32vec2_ptr_input Input + +%u32_input = OpVariable %u32_ptr_input Input +%u32vec2_input = OpVariable %u32vec2_ptr_input Input + +%u64_input = OpVariable %u64_ptr_input Input + +%u32_ptr_function = OpTypePointer Function %u32 + +%struct_f16_u16 = OpTypeStruct %f16 %u16 +%struct_f32_f32 = OpTypeStruct %f32 %f32 +%struct_f32_f32_f32 = OpTypeStruct %f32 %f32 %f32 +%struct_f32_u32 = OpTypeStruct %f32 %u32 +%struct_f32_u32_f32 = OpTypeStruct %f32 %u32 %f32 +%struct_u32_f32 = OpTypeStruct %u32 %f32 +%struct_u32_u32 = OpTypeStruct %u32 %u32 +%struct_f32_f64 = OpTypeStruct %f32 %f64 +%struct_f32vec2_f32vec2 = OpTypeStruct %f32vec2 %f32vec2 +%struct_f32vec2_u32vec2 = OpTypeStruct %f32vec2 %u32vec2 +)"; + + ss << debug_instructions_before_main; + + ss << R"( +%main = OpFunction %void None %func +%main_entry = OpLabel +)"; + + ss << body; + + ss << R"( +OpReturn +OpFunctionEnd)"; + + return ss.str(); +} + +TEST_F(ValidateOldDebugInfo, UseDebugInstructionOutOfFunction) { + const std::string src = R"( +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%cu = OpExtInst %void %DbgExt DebugCompilationUnit %code 1 1 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "DebugInfo" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, UseDebugInstructionOutOfFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugSourceInFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", "", dbg_inst, + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Debug info extension instructions other than DebugScope, " + "DebugNoScope, DebugDeclare, DebugValue must appear between " + "section 9 (types, constants, global variables) and section 10 " + "(function declarations)")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugSourceInFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", "", dbg_inst, + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Debug info extension instructions other than DebugScope, " + "DebugNoScope, DebugDeclare, DebugValue must appear between " + "section 9 (types, constants, global variables) and section 10 " + "(function declarations)")); +} + +TEST_P(ValidateLocalDebugInfoOutOfFunction, OpenCLDebugInfo100DebugScope) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%int_name = OpString "int" +%foo_name = OpString "foo" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %u32_0 Signed +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %int_info %dbg_src 1 1 %main_info FlagIsLocal +%expr = OpExtInst %void %DbgExt DebugExpression +)"; + + const std::string body = R"( +%foo = OpVariable %u32_ptr_function Function +%foo_val = OpLoad %u32 %foo +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header + GetParam(), body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DebugScope, DebugNoScope, DebugDeclare, DebugValue " + "of debug info extension must appear in a function " + "body")); +} + +TEST_P(ValidateLocalDebugInfoOutOfFunction, VulkanDebugInfo100DebugScope) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%int_name = OpString "int" +%foo_name = OpString "foo" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %u32_0 %u32_1 %u32_0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_1 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %int_info %dbg_src %u32_1 %u32_1 %main_info %u32_4 +%expr = OpExtInst %void %DbgExt DebugExpression +)"; + + const std::string body = R"( +%foo = OpVariable %u32_ptr_function Function +%main_def = OpExtInst %void %DbgExt DebugFunctionDefinition %main_info %main +%foo_val = OpLoad %u32 %foo +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header + GetParam(), body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DebugScope, DebugNoScope, DebugDeclare, DebugValue " + "of debug info extension must appear in a function " + "body")); +} + +INSTANTIATE_TEST_SUITE_P( + AllLocalDebugInfo, ValidateLocalDebugInfoOutOfFunction, + ::testing::ValuesIn(std::vector{ + "%main_scope = OpExtInst %void %DbgExt DebugScope %main_info", + "%no_scope = OpExtInst %void %DbgExt DebugNoScope", + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionForwardReference) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionMissingOpFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +)"; + + const std::string dbg_inst_header = R"( +%dbgNone = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %dbgNone +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugScopeBeforeOpVariableInFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info +%foo = OpVariable %f32_ptr_function Function +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeSizeDebugInfoNone) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%ty_name = OpString "struct VS_OUTPUT" +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeForwardReference) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; + float4 color : COLOR; +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_color_name = OpString "color : COLOR" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %VS_OUTPUT_color_info +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic +%VS_OUTPUT_color_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_color_name %v4float_info %dbg_src 3 3 %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeMissingReference) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; + float4 color : COLOR; +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_color_name = OpString "color : COLOR" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %VS_OUTPUT_color_info +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("forward referenced IDs have not been defined")); +} + +TEST_P(ValidateXDebugInfo, DebugSourceWrongResultType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %bool %DbgExt DebugSource %src %code +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + GetParam(), "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected result type must be a result id of " + "OpTypeVoid")); +} + +TEST_P(ValidateXDebugInfo, DebugSourceFailFile) { + const std::string src = R"( +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %DbgExt %code +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + GetParam(), "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand File must be a result id of " + "OpString")); +} + +TEST_P(ValidateXDebugInfo, DebugSourceFailSource) { + const std::string src = R"( +%src = OpString "simple.hlsl" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %DbgExt +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + GetParam(), "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Text must be a result id of " + "OpString")); +} + +TEST_P(ValidateXDebugInfo, DebugSourceNoText) { + const std::string src = R"( +%src = OpString "simple.hlsl" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + GetParam(), "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +INSTANTIATE_TEST_SUITE_P(OpenCLAndVkDebugInfo100, ValidateXDebugInfo, + ::testing::ValuesIn(std::vector{ + R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)", + R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)", + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugCompilationUnit) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugCompilationUnitFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %src HLSL +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Source must be a result id of " + "DebugSource")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugCompilationUnitFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %src %u32_5 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, dbg_inst, + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Source must be a result id of " + "DebugSource")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeBasicFailName) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %int_32 %int_32 Float +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Name must be a result id of " + "OpString")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeBasicFailName) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %u32_32 %u32_32 %u32_3 %u32_0 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Name must be a result id of " + "OpString")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeBasicFailSize) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %float_name Float +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Size must be a result id of " + "OpConstant")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeBasicFailSize) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %float_name %u32_3 %u32_0 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Size must be a result id of " + "OpConstant")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypePointer) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%pfloat_info = OpExtInst %void %DbgExt DebugTypePointer %float_info Function FlagIsLocal +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypePointerFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%pfloat_info = OpExtInst %void %DbgExt DebugTypePointer %dbg_src Function FlagIsLocal +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeQualifier) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %float_info ConstType +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeQualifierFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %comp_unit ConstType +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} +TEST_F(ValidateVulkan100DebugInfo, DebugTypeQualifier) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %float_info %u32_0 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeQualifierFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %comp_unit %u32_0 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArray) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %int_32 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayWithVariableSize) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%int_name = OpString "int" +%main_name = OpString "main" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%uint_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %int_32 Unsigned +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %uint_info %dbg_src 1 1 %main_info FlagIsLocal +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailBaseType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %comp_unit %int_32 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type is not a valid debug " + "type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCount) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %float_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCountFloat) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %f32_4 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCountZero) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %u32_0 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailVariableSizeTypeFloat) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%main_name = OpString "main" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 1 %main_info FlagIsLocal +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeArray) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %u32_32 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayWithVariableSize) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%uint_name = OpString "uint" +%main_name = OpString "main" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_6 = OpConstant %u32 6 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%uint_info = OpExtInst %void %DbgExt DebugTypeBasic %uint_name %u32_32 %u32_6 %u32_0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %uint_info %dbg_src %u32_1 %u32_1 %main_info %u32_4 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_info +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailBaseType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %comp_unit %u32_32 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type is not a valid debug " + "type")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailComponentCount) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %float_info +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailComponentCountFloat) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %f32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayComponentCountZero) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %u32_0 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailVariableSizeTypeFloat) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%main_name = OpString "main" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_6 = OpConstant %u32 6 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src %u32_1 %u32_1 %main_info %u32_4 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_info +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVector) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 4 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFailComponentZero) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 0 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFailComponentFive) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 5 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeVector) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeVectorFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src %u32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeVectorFailComponentZero) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_0 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be positive " + "integer less than or equal to 4")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeVectorFailComponentFive) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_5 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be positive " + "integer less than or equal to 4")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeMatrix) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +%true = OpConstantTrue %bool +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%mfloat_info = OpExtInst %void %DbgExt DebugTypeMatrix %vfloat_info %u32_4 %true +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeMatrixFailVectorTypeType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +%true = OpConstantTrue %bool +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%mfloat_info = OpExtInst %void %DbgExt DebugTypeMatrix %dbg_src %u32_4 %true +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Vector Type must be a result id of " + "DebugTypeVector")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeMatrixFailVectorCountType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +%true = OpConstantTrue %bool +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%mfloat_info = OpExtInst %void %DbgExt DebugTypeMatrix %vfloat_info %dbg_src %true +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Vector Count must be a result id of " + "32-bit unsigned OpConstant")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeMatrixFailVectorCountZero) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +%true = OpConstantTrue %bool +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%mfloat_info = OpExtInst %void %DbgExt DebugTypeMatrix %vfloat_info %u32_0 %true +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Vector Count must be positive " + "integer less than or equal to 4")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeMatrixFailVectorCountFive) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +%true = OpConstantTrue %bool +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%mfloat_info = OpExtInst %void %DbgExt DebugTypeMatrix %vfloat_info %u32_5 %true +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Vector Count must be positive " + "integer less than or equal to 4")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypedef) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugTypedef %foo_name %float_info %dbg_src 1 1 %comp_unit +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugTypedef, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugTypedef )"; + ss << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second + + " must be a result id of ")); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypedef, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%dbg_src %float_info %dbg_src 1 1 %comp_unit)", + "Name"), + std::make_pair(R"(%foo_name %dbg_src %dbg_src 1 1 %comp_unit)", + "Base Type"), + std::make_pair(R"(%foo_name %float_info %comp_unit 1 1 %comp_unit)", + "Source"), + std::make_pair(R"(%foo_name %float_info %dbg_src 1 1 %dbg_src)", + "Parent"), + })); + +TEST_F(ValidateVulkan100DebugInfo, DebugTypedef) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo_info = OpExtInst %void %DbgExt DebugTypedef %foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugTypedef, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo_info = OpExtInst %void %DbgExt DebugTypedef )"; + ss << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second + + " must be a result id of ")); +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugTypedef, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src %float_info %dbg_src %u32_1 %u32_1 %comp_unit)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src %u32_1 %u32_1 %comp_unit)", + "Base Type"), + std::make_pair( + R"(%foo_name %float_info %comp_unit %u32_1 %u32_1 %comp_unit)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %dbg_src)", + "Parent"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%main_type_info1 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_type_info2 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info +%main_type_info3 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info %float_info +%main_type_info4 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void %float_info %float_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunctionFailReturn) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %dbg_src %float_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("expected operand Return Type is not a valid debug type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunctionFailParam) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info %void +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("expected operand Parameter Types is not a valid debug type")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeFunctionAndParams) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%main_type_info1 = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_type_info2 = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %float_info +%main_type_info3 = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %float_info %float_info +%main_type_info4 = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void %float_info %float_info +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeFunctionFailReturn) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %dbg_src %float_info +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("expected operand Return Type is not a valid debug type")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeFunctionFailParam) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %float_info %void +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("expected operand Parameter Types is not a valid debug type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeEnum) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%none = OpExtInst %void %DbgExt DebugInfoNone +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info1 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name %u32_1 %foo_name +%foo_info2 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name %u32_1 %foo_name +%foo_info3 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugTypeEnum, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugTypeEnum )"; + ss << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeEnum, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)", + "Underlying Types"), + std::make_pair( + R"(%foo_name %float_info %comp_unit 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 1 %dbg_src %int_32 FlagIsPublic %u32_0 %foo_name)", + "Parent"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %void FlagIsPublic %u32_0 %foo_name)", + "Size"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %u32_0 FlagIsPublic %u32_0 %foo_name)", + "Size"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %foo_name %foo_name)", + "Value"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %u32_1)", + "Name"), + })); + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeEnum) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%none = OpExtInst %void %DbgExt DebugInfoNone +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo_info1 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %foo_name %u32_1 %foo_name +%foo_info2 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %foo_name %u32_1 %foo_name +%foo_info3 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugTypeEnum, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo_info = OpExtInst %void %DbgExt DebugTypeEnum )"; + ss << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugTypeEnum, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src %float_info %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %foo_name)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %foo_name)", + "Underlying Types"), + std::make_pair( + R"(%foo_name %float_info %comp_unit %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %foo_name)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %dbg_src %u32_32 %u32_3 %u32_0 %foo_name)", + "Parent"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit %void %u32_3 %u32_0 %foo_name)", + "Size"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit %u32_0 %u32_3 %u32_0 %foo_name)", + "Size"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %foo_name %foo_name)", + "Value"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %u32_1)", + "Name"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeFunctionAndInheritance) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +struct foo : VS_OUTPUT { +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%foo_name = OpString "foo" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main +%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic +%child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugTypeComposite, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +struct foo : VS_OUTPUT { +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%foo_name = OpString "foo" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite )"; + ss << param.first; + ss << R"( +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main +%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic +%child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second + " must be ")); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeComposite, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", + "Name"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %comp_unit 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", + "Source"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %dbg_src %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", + "Parent"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %int_128 %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", + "Linkage Name"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %dbg_src FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", + "Size"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %dbg_src %main_info %child)", + "Members"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %dbg_src %child)", + "Members"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %dbg_src)", + "Members"), + })); + +TEST_P(ValidateOpenCL100DebugInfoDebugTypeMember, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float pos : SV_POSITION; +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_32 FlagIsPublic %VS_OUTPUT_pos_info +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember )"; + ss << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + if (!param.second.empty()) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second + + " must be a result id of ")); + } +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeMember, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src %float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)", + "Name"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %dbg_src %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)", + ""), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %float_info 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)", + "Source"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %float_info %u32_0 %int_32 FlagIsPublic)", + "Parent"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %VS_OUTPUT_info %void %int_32 FlagIsPublic)", + "Offset"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %void FlagIsPublic)", + "Size"), + })); + +TEST_P(ValidateOpenCL100DebugInfoDebugTypeInheritance, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT {}; +struct foo : VS_OUTPUT {}; +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%foo_name = OpString "foo" +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_name %u32_0 FlagIsPublic %child +%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic +%bar_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Union %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic +%child = OpExtInst %void %DbgExt DebugTypeInheritance )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeInheritance, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%dbg_src %VS_OUTPUT_info %u32_0 %u32_0 FlagIsPublic)", + "Child must be a result id of"), + std::make_pair(R"(%foo_info %dbg_src %u32_0 %u32_0 FlagIsPublic)", + "Parent must be a result id of"), + std::make_pair( + R"(%bar_info %VS_OUTPUT_info %u32_0 %u32_0 FlagIsPublic)", + "Child must be class or struct debug type"), + std::make_pair(R"(%foo_info %bar_info %u32_0 %u32_0 FlagIsPublic)", + "Parent must be class or struct debug type"), + std::make_pair(R"(%foo_info %VS_OUTPUT_info %void %u32_0 FlagIsPublic)", + "Offset"), + std::make_pair(R"(%foo_info %VS_OUTPUT_info %u32_0 %void FlagIsPublic)", + "Size"), + })); + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeComposite) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +struct foo : VS_OUTPUT { +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%foo_name = OpString "foo" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +%u32_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src %u32_2 %u32_3 %u32_0 %u32_128 %u32_3 +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %u32_128 %u32_3 %VS_OUTPUT_pos_info +%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %foo_name %u32_0 %u32_3 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugTypeComposite, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +struct foo : VS_OUTPUT { +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%foo_name = OpString "foo" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +%u32_128 = OpConstant %u32 128 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src %u32_2 %u32_3 %u32_0 %u32_128 %u32_3 +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite )"; + ss << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second + " must be ")); +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugTypeComposite, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %u32_128 %u32_3 %VS_OUTPUT_pos_info)", + "Name"), + std::make_pair( + R"(%VS_OUTPUT_name %u32_1 %comp_unit %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %u32_128 %u32_3 %VS_OUTPUT_pos_info)", + "Source"), + std::make_pair( + R"(%VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %dbg_src %VS_OUTPUT_linkage_name %u32_128 %u32_3 %VS_OUTPUT_pos_info)", + "Parent"), + std::make_pair( + R"(%VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %u32_128 %u32_128 %u32_3 %VS_OUTPUT_pos_info)", + "Linkage Name"), + std::make_pair( + R"(%VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %dbg_src %u32_3 %VS_OUTPUT_pos_info)", + "Size"), + std::make_pair( + R"(%VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %u32_128 %dbg_src %VS_OUTPUT_pos_info)", + "Flags"), + std::make_pair( + R"(%VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %u32_128 %u32_3 %dbg_src)", + "Members"), + })); + +TEST_P(ValidateVulkan100DebugInfoDebugTypeMember, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float pos : SV_POSITION; +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +%u32_128 = OpConstant %u32 128 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember )"; + ss << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + if (!param.second.empty()) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second + + " must be a result id of ")); + } +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugTypeMember, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src %float_info %dbg_src %u32_2 %u32_3 %u32_0 %u32_32 %u32_3)", + "Name"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %dbg_src %dbg_src %u32_2 %u32_3 %u32_0 %u32_32 %u32_3)", + ""), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %float_info %u32_2 %u32_3 %u32_0 %u32_32 %u32_3)", + "Source"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %dbg_src %u32_2 %u32_3 %void %u32_32 %u32_3)", + "Offset"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %dbg_src %u32_2 %u32_3 %u32_0 %void %u32_3)", + "Size"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %dbg_src %u32_2 %u32_3 %u32_0 %u32_32 %void)", + "Flags"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionDeclaration) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +main() {} +" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugFunction, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +main() {} +" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic +%main_info = OpExtInst %void %DbgExt DebugFunction )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugFunction, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)", + "Name"), + std::make_pair( + R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)", + "Type"), + std::make_pair( + R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)", + "Source"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_linkage_name FlagIsPublic 13 %main)", + "Parent"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic 13 %main)", + "Linkage Name"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %void)", + "Function"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main %dbg_src)", + "Declaration"), + })); + +TEST_P(ValidateOpenCL100DebugInfoDebugFunctionDeclaration, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +main() {} +" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, + ValidateOpenCL100DebugInfoDebugFunctionDeclaration, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic)", + "Name"), + std::make_pair( + R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic)", + "Type"), + std::make_pair( + R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_linkage_name FlagIsPublic)", + "Source"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_linkage_name FlagIsPublic)", + "Parent"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic)", + "Linkage Name"), + })); + +TEST_F(ValidateVulkan100DebugInfo, DebugFunctionDeclaration) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +main() {} +" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_12 = OpConstant %u32 12 +%u32_13 = OpConstant %u32 13 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_13 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugFunction, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +main() {} +" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_12 = OpConstant %u32 12 +%u32_13 = OpConstant %u32 13 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 +%main_info = OpExtInst %void %DbgExt DebugFunction )" + << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugFunction, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%u32_0 %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_13)", + "Name"), + std::make_pair( + R"(%main_name %dbg_src %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_13)", + "Type"), + std::make_pair( + R"(%main_name %main_type_info %comp_unit %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_13)", + "Source"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %dbg_src %main_linkage_name %u32_3 %u32_13)", + "Parent"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %void %u32_3 %u32_13)", + "Linkage Name"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_13 %dbg_src)", + "Declaration"), + })); + +TEST_P(ValidateVulkan100DebugInfoDebugFunctionDeclaration, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +main() {} +" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_12 = OpConstant %u32 12 +%u32_13 = OpConstant %u32 13 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration )" + << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, + ValidateVulkan100DebugInfoDebugFunctionDeclaration, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%u32_0 %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3)", + "Name"), + std::make_pair( + R"(%main_name %dbg_src %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3)", + "Type"), + std::make_pair( + R"(%main_name %main_type_info %comp_unit %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3)", + "Source"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %dbg_src %main_linkage_name %u32_3)", + "Parent"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %void %u32_3)", + "Linkage Name"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugLexicalBlock) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_block = OpExtInst %void %DbgExt DebugLexicalBlock %dbg_src 1 1 %comp_unit %main_name)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugLexicalBlock, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_block = OpExtInst %void %DbgExt DebugLexicalBlock )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugLexicalBlock, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%comp_unit 1 1 %comp_unit %main_name)", "Source"), + std::make_pair(R"(%dbg_src 1 1 %dbg_src %main_name)", "Parent"), + std::make_pair(R"(%dbg_src 1 1 %comp_unit %void)", "Name"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugScopeFailScope) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %dbg_src +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugScopeFailInlinedAt) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %comp_unit %dbg_src +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined At")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugLexicalBlock) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_block = OpExtInst %void %DbgExt DebugLexicalBlock %dbg_src %u32_1 %u32_1 %comp_unit %main_name +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugLexicalBlock, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_block = OpExtInst %void %DbgExt DebugLexicalBlock )" + << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugLexicalBlock, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%comp_unit %u32_1 %u32_1 %comp_unit %main_name)", + "Source"), + std::make_pair(R"(%dbg_src %u32_1 %u32_1 %dbg_src %main_name)", + "Parent"), + std::make_pair(R"(%dbg_src %u32_1 %u32_1 %comp_unit %void)", "Name"), + })); + +TEST_F(ValidateVulkan100DebugInfo, DebugScopeFailScope) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %dbg_src +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugScopeFailInlinedAt) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %comp_unit %dbg_src +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined At")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugLocalVariable) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugLocalVariable, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo = OpExtInst %void %DbgExt DebugLocalVariable )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugLocalVariable, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%void %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src 1 10 %comp_unit FlagIsLocal 0)", + "Type"), + std::make_pair( + R"(%foo_name %float_info %comp_unit 1 10 %comp_unit FlagIsLocal 0)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 10 %dbg_src FlagIsLocal 0)", + "Parent"), + })); + +TEST_F(ValidateVulkan100DebugInfo, DebugLocalVariable) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_10 = OpConstant %u32 10 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugLocalVariable, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_10 = OpConstant %u32 10 +%u32_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo = OpExtInst %void %DbgExt DebugLocalVariable )" + << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugLocalVariable, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%void %float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_3 %u32_0)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src %u32_1 %u32_10 %comp_unit %u32_3 %u32_0)", + "Type"), + std::make_pair( + R"(%foo_name %float_info %comp_unit %u32_1 %u32_10 %comp_unit %u32_3 %u32_0)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_1 %u32_10 %dbg_src %u32_3 %u32_0)", + "Parent"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugDeclare) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 +)"; + + const std::string body = R"( +%foo = OpVariable %f32_ptr_function Function +%decl = OpExtInst %void %DbgExt DebugDeclare %foo_info %foo %null_expr +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugDeclareParam) { + CompileSuccessfully(R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %in_var_COLOR + %4 = OpString "test.hlsl" + OpSource HLSL 620 %4 "#line 1 \"test.hlsl\" +void main(float foo:COLOR) {} +" + %11 = OpString "#line 1 \"test.hlsl\" +void main(float foo:COLOR) {} +" + %14 = OpString "float" + %17 = OpString "src.main" + %20 = OpString "foo" + OpName %in_var_COLOR "in.var.COLOR" + OpName %main "main" + OpName %param_var_foo "param.var.foo" + OpName %src_main "src.main" + OpName %foo "foo" + OpName %bb_entry "bb.entry" + 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 + %23 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float + %29 = OpTypeFunction %void %_ptr_Function_float + OpLine %4 1 21 +%in_var_COLOR = OpVariable %_ptr_Input_float Input + %10 = OpExtInst %void %1 DebugExpression + %12 = OpExtInst %void %1 DebugSource %4 %11 + %13 = OpExtInst %void %1 DebugCompilationUnit 1 4 %12 HLSL + %15 = OpExtInst %void %1 DebugTypeBasic %14 %uint_32 Float + %16 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %void %15 + %18 = OpExtInst %void %1 DebugFunction %17 %16 %12 1 1 %13 %17 FlagIsProtected|FlagIsPrivate 1 %src_main + %21 = OpExtInst %void %1 DebugLocalVariable %20 %15 %12 1 17 %18 FlagIsLocal 0 + %22 = OpExtInst %void %1 DebugLexicalBlock %12 1 28 %18 + OpLine %4 1 1 + %main = OpFunction %void None %23 + %24 = OpLabel + OpLine %4 1 17 +%param_var_foo = OpVariable %_ptr_Function_float Function + %27 = OpLoad %float %in_var_COLOR + OpLine %4 1 1 + %28 = OpFunctionCall %void %src_main %param_var_foo + OpReturn + OpFunctionEnd + %src_main = OpFunction %void None %29 + OpLine %4 1 17 + %foo = OpFunctionParameter %_ptr_Function_float + %31 = OpExtInst %void %1 DebugDeclare %21 %foo %10 + %bb_entry = OpLabel + OpLine %4 1 29 + OpReturn + OpFunctionEnd +)"); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugDeclare, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%foo = OpVariable %f32_ptr_function Function +%decl = OpExtInst %void %DbgExt DebugDeclare )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, ss.str(), extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugDeclare, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%dbg_src %foo %null_expr)", "Local Variable"), + std::make_pair(R"(%foo_info %void %null_expr)", "Variable"), + std::make_pair(R"(%foo_info %foo %dbg_src)", "Expression"), + })); + +TEST_F(ValidateVulkan100DebugInfo, DebugDeclare) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_10 = OpConstant %u32 10 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + const std::string body = R"( +%foo = OpVariable %f32_ptr_function Function +%decl = OpExtInst %void %DbgExt DebugDeclare %foo_info %foo %null_expr +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugDeclareParam) { + CompileSuccessfully(R"( + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %in_var_COLOR + %4 = OpString "test.hlsl" + OpSource HLSL 620 %4 "#line 1 \"test.hlsl\" +void main(float foo:COLOR) {} +" + %11 = OpString "#line 1 \"test.hlsl\" +void main(float foo:COLOR) {} +" + %14 = OpString "float" + %17 = OpString "src.main" + %20 = OpString "foo" + OpName %in_var_COLOR "in.var.COLOR" + OpName %main "main" + OpName %param_var_foo "param.var.foo" + OpName %src_main "src.main" + OpName %foo "foo" + OpName %bb_entry "bb.entry" + OpDecorate %in_var_COLOR Location 0 + %uint = OpTypeInt 32 0 + %u32_0 = OpConstant %uint 0 + %u32_1 = OpConstant %uint 1 + %u32_2 = OpConstant %uint 2 + %u32_3 = OpConstant %uint 3 + %u32_4 = OpConstant %uint 4 + %u32_5 = OpConstant %uint 5 + %u32_10 = OpConstant %uint 10 + %u32_17 = OpConstant %uint 17 + %u32_28 = OpConstant %uint 28 + %u32_32 = OpConstant %uint 32 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 +%_ptr_Input_float = OpTypePointer Input %float + %void = OpTypeVoid + %23 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float + %29 = OpTypeFunction %void %_ptr_Function_float + OpLine %4 1 21 +%in_var_COLOR = OpVariable %_ptr_Input_float Input + %10 = OpExtInst %void %1 DebugExpression + %12 = OpExtInst %void %1 DebugSource %4 %11 + %13 = OpExtInst %void %1 DebugCompilationUnit %u32_1 %u32_4 %12 %u32_5 + %15 = OpExtInst %void %1 DebugTypeBasic %14 %uint_32 %u32_3 %u32_0 + %16 = OpExtInst %void %1 DebugTypeFunction %u32_3 %void %15 + %18 = OpExtInst %void %1 DebugFunction %17 %16 %12 %u32_1 %u32_1 %13 %17 %u32_3 %u32_1 + %21 = OpExtInst %void %1 DebugLocalVariable %20 %15 %12 %u32_1 %u32_17 %18 %u32_4 %u32_0 + %22 = OpExtInst %void %1 DebugLexicalBlock %12 %u32_1 %u32_28 %18 + %main = OpFunction %void None %23 + %24 = OpLabel +%param_var_foo = OpVariable %_ptr_Function_float Function + %27 = OpLoad %float %in_var_COLOR + %28 = OpFunctionCall %void %src_main %param_var_foo + OpReturn + OpFunctionEnd + %src_main = OpFunction %void None %29 + %foo = OpFunctionParameter %_ptr_Function_float + %31 = OpExtInst %void %1 DebugDeclare %21 %foo %10 + %bb_entry = OpLabel + OpReturn + OpFunctionEnd +)"); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugDeclare, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_10 = OpConstant %u32 10 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%foo = OpVariable %f32_ptr_function Function +%decl = OpExtInst %void %DbgExt DebugDeclare )" + << param.first; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, ss.str(), extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugDeclare, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%dbg_src %foo %null_expr)", "Local Variable"), + std::make_pair(R"(%foo_info %void %null_expr)", "Variable"), + std::make_pair(R"(%foo_info %foo %dbg_src)", "Expression"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugExpression) { + const std::string dbg_inst_header = R"( +%op0 = OpExtInst %void %DbgExt DebugOperation Deref +%op1 = OpExtInst %void %DbgExt DebugOperation Plus +%null_expr = OpExtInst %void %DbgExt DebugExpression %op0 %op1 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugExpressionFail) { + const std::string dbg_inst_header = R"( +%op = OpExtInst %void %DbgExt DebugOperation Deref +%null_expr = OpExtInst %void %DbgExt DebugExpression %op %void +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "expected operand Operation must be a result id of DebugOperation")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugExpression) { + const std::string dbg_inst_header = R"( +%op0 = OpExtInst %void %DbgExt DebugOperation %u32_0 +%op1 = OpExtInst %void %DbgExt DebugOperation %u32_1 +%null_expr = OpExtInst %void %DbgExt DebugExpression %op0 %op1 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugExpressionFail) { + const std::string dbg_inst_header = R"( +%op = OpExtInst %void %DbgExt DebugOperation %u32_0 +%null_expr = OpExtInst %void %DbgExt DebugExpression %op %void +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "expected operand Operation must be a result id of DebugOperation")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplate) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateUsedForVariableType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %temp %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%main_name = OpString "main" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %param %param +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %main_info %param +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFailTarget) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%main_name = OpString "main" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %float_info %param +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Target must be DebugTypeComposite or " + "DebugFunction")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFailParam) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%main_name = OpString "main" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %param %param +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %main_info %float_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "expected operand Parameters must be DebugTypeTemplateParameter or " + "DebugTypeTemplateTemplateParameter")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeTemplate) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %ty_name %dbg_none %u32_3 +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src %u32_0 %u32_0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeTemplateUsedForVariableType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %ty_name %dbg_none %u32_3 +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src %u32_0 %u32_0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %temp %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeTemplateFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%main_name = OpString "main" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src %u32_0 %u32_0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %param %param +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %main_info %param +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeTemplateFailTarget) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%main_name = OpString "main" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src %u32_0 %u32_0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %float_info %param +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Target must be DebugTypeComposite or " + "DebugFunction")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeTemplateFailParam) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%main_name = OpString "main" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %ty_name %dbg_none %u32_3 +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src %u32_0 %u32_0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %float_info +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "expected operand Parameters must be DebugTypeTemplateParameter or " + "DebugTypeTemplateTemplateParameter")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariable) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariableStaticMember) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Class %dbg_src 0 0 %comp_unit %foo_name %int_32 FlagIsPublic %a +%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src 0 0 %t %u32_0 %int_32 FlagIsPublic +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate %a +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariableDebugInfoNone) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbgNone = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %dbgNone FlagIsProtected|FlagIsPrivate +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariableConst) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %int_32 FlagIsProtected|FlagIsPrivate +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugGlobalVariable, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo = OpExtInst %void %DbgExt DebugGlobalVariable )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugGlobalVariable, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%void %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", + "Type"), + std::make_pair( + R"(%foo_name %float_info %comp_unit 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 0 0 %dbg_src %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", + "Scope"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 0 0 %comp_unit %void %f32_input FlagIsProtected|FlagIsPrivate)", + "Linkage Name"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %void FlagIsProtected|FlagIsPrivate)", + "Variable"), + })); + +TEST_F(ValidateVulkan100DebugInfo, DebugGlobalVariable) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugGlobalVariableStaticMember) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src %u32_0 %u32_0 %u32_0 %u32_32 %u32_3 +%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name %u32_1 %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %u32_32 %u32_3 %a +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %t %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugGlobalVariableDebugInfoNone) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbgNone = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %dbgNone %u32_3 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugGlobalVariableConst) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %u32_32 %u32_3 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugGlobalVariable, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo = OpExtInst %void %DbgExt DebugGlobalVariable )" + << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateVulkan100DebugInfoDebugGlobalVariable, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%void %float_info %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3)", + "Type"), + std::make_pair( + R"(%foo_name %float_info %comp_unit %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_0 %u32_0 %dbg_src %foo_name %f32_input %u32_3)", + "Scope"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_0 %u32_0 %comp_unit %void %f32_input %u32_3)", + "Linkage Name"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %void %u32_3)", + "Variable"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAt) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main +%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info +%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info %inlined_at +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAtFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main +%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info +%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %inlined_at +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAtFail2) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main +%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info +%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info %main_info +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugInlinedAt) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1 +%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %main_info +%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %main_info %inlined_at +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugInlinedAtFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1 +%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %main_info +%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %inlined_at %inlined_at +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugInlinedAtFail2) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1 +%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %main_info +%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %main_info %main_info +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugValue) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_3 = OpConstant %u32 3 +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 +)"; + + const std::string body = R"( +%value = OpExtInst %void %DbgExt DebugValue %foo_info %int_32 %null_expr %int_3 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugValueWithVariableIndex) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%int_name = OpString "int" +%foo_name = OpString "foo" +%len_name = OpString "length" +)"; + + const std::string size_const = R"( +%int_3 = OpConstant %u32 3 +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %int_32 Signed +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src 1 10 %comp_unit FlagIsLocal +%len_info = OpExtInst %void %DbgExt DebugLocalVariable %len_name %int_info %dbg_src 0 0 %comp_unit FlagIsLocal +)"; + + const std::string body = R"( +%value = OpExtInst %void %DbgExt DebugValue %foo_info %int_32 %null_expr %len_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugValue, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%decl = OpExtInst %void %DbgExt DebugValue )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, ss.str(), extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugValue, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%dbg_src %int_32 %null_expr)", "Local Variable"), + std::make_pair(R"(%foo_info %int_32 %dbg_src)", "Expression"), + std::make_pair(R"(%foo_info %int_32 %null_expr %dbg_src)", "Indexes"), + })); + +TEST_F(ValidateVulkan100DebugInfo, DebugValue) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_10 = OpConstant %u32 10 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + const std::string body = R"( +%value = OpExtInst %void %DbgExt DebugValue %foo_info %u32_32 %null_expr %u32_3 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugValueWithVariableIndex) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%int_name = OpString "int" +%foo_name = OpString "foo" +%len_name = OpString "length" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_10 = OpConstant %u32 10 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %u32_32 %u32_4 %u32_0 +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4 %u32_0 +%len_info = OpExtInst %void %DbgExt DebugLocalVariable %len_name %int_info %dbg_src %u32_0 %u32_0 %comp_unit %u32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + const std::string body = R"( +%value = OpExtInst %void %DbgExt DebugValue %foo_info %u32_32 %null_expr %len_info +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugValue, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_10 = OpConstant %u32 10 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4 %u32_0 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%decl = OpExtInst %void %DbgExt DebugValue )" + << param.first; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, ss.str(), extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateVulkan100DebugInfoDebugValue, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%dbg_src %u32_32 %null_expr %u32_3)", + "Local Variable"), + std::make_pair(R"(%foo_info %u32_32 %dbg_src %u32_3)", "Expression"), + std::make_pair(R"(%foo_info %u32_32 %null_expr %dbg_src)", "Indexes"), + })); + +TEST_F(ValidateVulkan100DebugInfo, VulkanDebugInfoSample) { + std::ostringstream ss; + ss << R"( + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %id_1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %id_MainPs "MainPs" %id_in_var_TEXCOORD2 %id_out_var_SV_Target0 + OpExecutionMode %id_MainPs OriginUpperLeft + %id_7 = OpString "foo.frag" + %id_27 = OpString "float" + %id_32 = OpString "vColor" + %id_36 = OpString "PS_OUTPUT" + %id_42 = OpString "vTextureCoords" + %id_46 = OpString "PS_INPUT" + %id_49 = OpString "MainPs" + %id_50 = OpString "" + %id_55 = OpString "ps_output" + %id_59 = OpString "i" + %id_63 = OpString "@type.sampler" + %id_64 = OpString "type.sampler" + %id_66 = OpString "g_sAniso" + %id_69 = OpString "@type.2d.image" + %id_70 = OpString "type.2d.image" + %id_72 = OpString "TemplateParam" + %id_75 = OpString "g_tColor" + OpName %id_type_2d_image "type.2d.image" + OpName %id_g_tColor "g_tColor" + OpName %id_type_sampler "type.sampler" + OpName %id_g_sAniso "g_sAniso" + OpName %id_in_var_TEXCOORD2 "in.var.TEXCOORD2" + OpName %id_out_var_SV_Target0 "out.var.SV_Target0" + OpName %id_MainPs "MainPs" + OpName %id_PS_INPUT "PS_INPUT" + OpMemberName %id_PS_INPUT 0 "vTextureCoords" + OpName %id_param_var_i "param.var.i" + OpName %id_PS_OUTPUT "PS_OUTPUT" + OpMemberName %id_PS_OUTPUT 0 "vColor" + OpName %id_src_MainPs "src.MainPs" + OpName %id_i "i" + OpName %id_bb_entry "bb.entry" + OpName %id_ps_output "ps_output" + OpName %id_type_sampled_image "type.sampled.image" + OpDecorate %id_in_var_TEXCOORD2 Location 0 + OpDecorate %id_out_var_SV_Target0 Location 0 + OpDecorate %id_g_tColor DescriptorSet 0 + OpDecorate %id_g_tColor Binding 0 + OpDecorate %id_g_sAniso DescriptorSet 0 + OpDecorate %id_g_sAniso Binding 1 + %id_int = OpTypeInt 32 1 + %id_int_0 = OpConstant %id_int 0 + %id_uint = OpTypeInt 32 0 + %id_uint_32 = OpConstant %id_uint 32 + %id_float = OpTypeFloat 32 +%id_type_2d_image = OpTypeImage %id_float 2D 2 0 0 1 Unknown +%id__ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %id_type_2d_image +%id_type_sampler = OpTypeSampler +%id__ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %id_type_sampler + %id_v2float = OpTypeVector %id_float 2 +%id__ptr_Input_v2float = OpTypePointer Input %id_v2float + %id_v4float = OpTypeVector %id_float 4 +%id__ptr_Output_v4float = OpTypePointer Output %id_v4float + %id_void = OpTypeVoid + %id_uint_1 = OpConstant %id_uint 1 + %id_uint_4 = OpConstant %id_uint 4 + %id_uint_5 = OpConstant %id_uint 5 + %id_uint_3 = OpConstant %id_uint 3 + %id_uint_0 = OpConstant %id_uint 0 + %id_uint_128 = OpConstant %id_uint 128 + %id_uint_12 = OpConstant %id_uint 12 + %id_uint_10 = OpConstant %id_uint 10 + %id_uint_8 = OpConstant %id_uint 8 + %id_uint_2 = OpConstant %id_uint 2 + %id_uint_64 = OpConstant %id_uint 64 + %id_uint_7 = OpConstant %id_uint 7 + %id_uint_15 = OpConstant %id_uint 15 + %id_uint_16 = OpConstant %id_uint 16 + %id_uint_17 = OpConstant %id_uint 17 + %id_uint_29 = OpConstant %id_uint 29 + %id_uint_14 = OpConstant %id_uint 14 + %id_uint_11 = OpConstant %id_uint 11 + %id_78 = OpTypeFunction %id_void + %id_PS_INPUT = OpTypeStruct %id_v2float +%id__ptr_Function_PS_INPUT = OpTypePointer Function %id_PS_INPUT + %id_PS_OUTPUT = OpTypeStruct %id_v4float + %id_89 = OpTypeFunction %id_PS_OUTPUT %id__ptr_Function_PS_INPUT +%id__ptr_Function_PS_OUTPUT = OpTypePointer Function %id_PS_OUTPUT + %id_uint_20 = OpConstant %id_uint 20 + %id_uint_19 = OpConstant %id_uint 19 + %id_uint_26 = OpConstant %id_uint 26 + %id_uint_46 = OpConstant %id_uint 46 +%id__ptr_Function_v2float = OpTypePointer Function %id_v2float + %id_uint_57 = OpConstant %id_uint 57 + %id_uint_78 = OpConstant %id_uint 78 +%id_type_sampled_image = OpTypeSampledImage %id_type_2d_image + %id_uint_81 = OpConstant %id_uint 81 +%id__ptr_Function_v4float = OpTypePointer Function %id_v4float + %id_g_tColor = OpVariable %id__ptr_UniformConstant_type_2d_image UniformConstant + %id_g_sAniso = OpVariable %id__ptr_UniformConstant_type_sampler UniformConstant +%id_in_var_TEXCOORD2 = OpVariable %id__ptr_Input_v2float Input +%id_out_var_SV_Target0 = OpVariable %id__ptr_Output_v4float Output + %id_22 = OpExtInst %id_void %id_1 DebugSource %id_7 + %id_23 = OpExtInst %id_void %id_1 DebugCompilationUnit %id_uint_1 %id_uint_4 %id_22 %id_uint_5 + %id_28 = OpExtInst %id_void %id_1 DebugTypeBasic %id_27 %id_uint_32 %id_uint_3 %id_uint_0 + %id_31 = OpExtInst %id_void %id_1 DebugTypeVector %id_28 %id_uint_4 + %id_34 = OpExtInst %id_void %id_1 DebugTypeMember %id_32 %id_31 %id_22 %id_uint_12 %id_uint_12 %id_uint_0 %id_uint_128 %id_uint_3 + %id_37 = OpExtInst %id_void %id_1 DebugTypeComposite %id_36 %id_uint_1 %id_22 %id_uint_10 %id_uint_8 %id_23 %id_36 %id_uint_128 %id_uint_3 %id_34 + %id_40 = OpExtInst %id_void %id_1 DebugTypeVector %id_28 %id_uint_2 + %id_44 = OpExtInst %id_void %id_1 DebugTypeMember %id_42 %id_40 %id_22 %id_uint_7 %id_uint_12 %id_uint_0 %id_uint_64 %id_uint_3 + %id_47 = OpExtInst %id_void %id_1 DebugTypeComposite %id_46 %id_uint_1 %id_22 %id_uint_5 %id_uint_8 %id_23 %id_46 %id_uint_64 %id_uint_3 %id_44 + %id_48 = OpExtInst %id_void %id_1 DebugTypeFunction %id_uint_3 %id_37 %id_47 + %id_51 = OpExtInst %id_void %id_1 DebugFunction %id_49 %id_48 %id_22 %id_uint_15 %id_uint_1 %id_23 %id_50 %id_uint_3 %id_uint_16 + %id_54 = OpExtInst %id_void %id_1 DebugLexicalBlock %id_22 %id_uint_16 %id_uint_1 %id_51 + %id_56 = OpExtInst %id_void %id_1 DebugLocalVariable %id_55 %id_37 %id_22 %id_uint_17 %id_uint_15 %id_54 %id_uint_4 + %id_58 = OpExtInst %id_void %id_1 DebugExpression + %id_60 = OpExtInst %id_void %id_1 DebugLocalVariable %id_59 %id_47 %id_22 %id_uint_15 %id_uint_29 %id_51 %id_uint_4 %id_uint_1 + %id_62 = OpExtInst %id_void %id_1 DebugInfoNone + %id_65 = OpExtInst %id_void %id_1 DebugTypeComposite %id_63 %id_uint_1 %id_22 %id_uint_0 %id_uint_0 %id_23 %id_64 %id_62 %id_uint_3 + %id_67 = OpExtInst %id_void %id_1 DebugGlobalVariable %id_66 %id_65 %id_22 %id_uint_3 %id_uint_14 %id_23 %id_66 %id_g_sAniso %id_uint_8 + %id_71 = OpExtInst %id_void %id_1 DebugTypeComposite %id_69 %id_uint_0 %id_22 %id_uint_0 %id_uint_0 %id_23 %id_70 %id_62 %id_uint_3 + %id_73 = OpExtInst %id_void %id_1 DebugTypeTemplateParameter %id_72 %id_31 %id_62 %id_22 %id_uint_0 %id_uint_0 + %id_74 = OpExtInst %id_void %id_1 DebugTypeTemplate %id_71 %id_73 + %id_76 = OpExtInst %id_void %id_1 DebugGlobalVariable %id_75 %id_74 %id_22 %id_uint_1 %id_uint_11 %id_23 %id_75 %id_g_tColor %id_uint_8 + %id_MainPs = OpFunction %id_void None %id_78 + %id_79 = OpLabel +%id_param_var_i = OpVariable %id__ptr_Function_PS_INPUT Function + %id_83 = OpLoad %id_v2float %id_in_var_TEXCOORD2 + %id_84 = OpCompositeConstruct %id_PS_INPUT %id_83 + OpStore %id_param_var_i %id_84 + %id_86 = OpFunctionCall %id_PS_OUTPUT %id_src_MainPs %id_param_var_i + %id_88 = OpCompositeExtract %id_v4float %id_86 0 + OpStore %id_out_var_SV_Target0 %id_88 + OpReturn + OpFunctionEnd + %id_src_MainPs = OpFunction %id_PS_OUTPUT None %id_89 + %id_i = OpFunctionParameter %id__ptr_Function_PS_INPUT + %id_bb_entry = OpLabel + %id_ps_output = OpVariable %id__ptr_Function_PS_OUTPUT Function + %id_94 = OpExtInst %id_void %id_1 DebugScope %id_51 + %id_97 = OpExtInst %id_void %id_1 DebugDeclare %id_60 %id_i %id_58 + %id_99 = OpExtInst %id_void %id_1 DebugFunctionDefinition %id_51 %id_src_MainPs + %id_100 = OpExtInst %id_void %id_1 DebugScope %id_54 + %id_102 = OpExtInst %id_void %id_1 DebugDeclare %id_56 %id_ps_output %id_58 + %id_106 = OpLoad %id_type_2d_image %id_g_tColor + %id_109 = OpLoad %id_type_sampler %id_g_sAniso + %id_114 = OpAccessChain %id__ptr_Function_v2float %id_i %id_int_0 + %id_115 = OpLoad %id_v2float %id_114 + %id_119 = OpSampledImage %id_type_sampled_image %id_106 %id_109 + %id_120 = OpImageSampleImplicitLod %id_v4float %id_119 %id_115 None + %id_123 = OpAccessChain %id__ptr_Function_v4float %id_ps_output %id_int_0 + OpStore %id_123 %id_120 + %id_125 = OpLoad %id_PS_OUTPUT %id_ps_output + OpReturnValue %id_125 + OpFunctionEnd +)"; + + CompileSuccessfully(ss.str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/test/val/val_ext_inst_test.cpp b/test/val/val_ext_inst_test.cpp index b73ec341..e685acde 100644 --- a/test/val/val_ext_inst_test.cpp +++ b/test/val/val_ext_inst_test.cpp @@ -33,33 +33,6 @@ using ::testing::HasSubstr; using ::testing::Not; using ValidateExtInst = spvtest::ValidateBase; -using ValidateOldDebugInfo = spvtest::ValidateBase; -using ValidateOpenCL100DebugInfo = spvtest::ValidateBase; -using ValidateLocalDebugInfoOutOfFunction = spvtest::ValidateBase; -using ValidateOpenCL100DebugInfoDebugTypedef = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugTypeEnum = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugTypeComposite = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugTypeMember = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugTypeInheritance = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugFunction = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugFunctionDeclaration = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugLexicalBlock = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugLocalVariable = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugGlobalVariable = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugDeclare = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugValue = - spvtest::ValidateBase>; using ValidateGlslStd450SqrtLike = spvtest::ValidateBase; using ValidateGlslStd450FMinLike = spvtest::ValidateBase; using ValidateGlslStd450FClampLike = spvtest::ValidateBase; @@ -488,1685 +461,6 @@ OpFunctionEnd)"; return ss.str(); } -std::string GenerateShaderCodeForDebugInfo( - const std::string& op_string_instructions, - const std::string& op_const_instructions, - const std::string& debug_instructions_before_main, const std::string& body, - const std::string& capabilities_and_extensions = "", - const std::string& execution_model = "Fragment") { - std::ostringstream ss; - ss << R"( -OpCapability Shader -OpCapability Float16 -OpCapability Float64 -OpCapability Int16 -OpCapability Int64 -)"; - - ss << capabilities_and_extensions; - ss << "%extinst = OpExtInstImport \"GLSL.std.450\"\n"; - ss << "OpMemoryModel Logical GLSL450\n"; - ss << "OpEntryPoint " << execution_model << " %main \"main\"" - << " %f32_output" - << " %f32vec2_output" - << " %u32_output" - << " %u32vec2_output" - << " %u64_output" - << " %f32_input" - << " %f32vec2_input" - << " %u32_input" - << " %u32vec2_input" - << " %u64_input" - << "\n"; - if (execution_model == "Fragment") { - ss << "OpExecutionMode %main OriginUpperLeft\n"; - } - - ss << op_string_instructions; - - ss << R"( -%void = OpTypeVoid -%func = OpTypeFunction %void -%bool = OpTypeBool -%f16 = OpTypeFloat 16 -%f32 = OpTypeFloat 32 -%f64 = OpTypeFloat 64 -%u32 = OpTypeInt 32 0 -%s32 = OpTypeInt 32 1 -%u64 = OpTypeInt 64 0 -%s64 = OpTypeInt 64 1 -%u16 = OpTypeInt 16 0 -%s16 = OpTypeInt 16 1 -%f32vec2 = OpTypeVector %f32 2 -%f32vec3 = OpTypeVector %f32 3 -%f32vec4 = OpTypeVector %f32 4 -%f64vec2 = OpTypeVector %f64 2 -%f64vec3 = OpTypeVector %f64 3 -%f64vec4 = OpTypeVector %f64 4 -%u32vec2 = OpTypeVector %u32 2 -%u32vec3 = OpTypeVector %u32 3 -%s32vec2 = OpTypeVector %s32 2 -%u32vec4 = OpTypeVector %u32 4 -%s32vec4 = OpTypeVector %s32 4 -%u64vec2 = OpTypeVector %u64 2 -%s64vec2 = OpTypeVector %s64 2 -%f64mat22 = OpTypeMatrix %f64vec2 2 -%f32mat22 = OpTypeMatrix %f32vec2 2 -%f32mat23 = OpTypeMatrix %f32vec2 3 -%f32mat32 = OpTypeMatrix %f32vec3 2 -%f32mat33 = OpTypeMatrix %f32vec3 3 - -%f32_0 = OpConstant %f32 0 -%f32_1 = OpConstant %f32 1 -%f32_2 = OpConstant %f32 2 -%f32_3 = OpConstant %f32 3 -%f32_4 = OpConstant %f32 4 -%f32_h = OpConstant %f32 0.5 -%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 -%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 -%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2 -%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3 -%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 -%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4 - -%f64_0 = OpConstant %f64 0 -%f64_1 = OpConstant %f64 1 -%f64_2 = OpConstant %f64 2 -%f64_3 = OpConstant %f64 3 -%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1 -%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2 -%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3 - -%f16_0 = OpConstant %f16 0 -%f16_1 = OpConstant %f16 1 -%f16_h = OpConstant %f16 0.5 - -%u32_0 = OpConstant %u32 0 -%u32_1 = OpConstant %u32 1 -%u32_2 = OpConstant %u32 2 -%u32_3 = OpConstant %u32 3 - -%s32_0 = OpConstant %s32 0 -%s32_1 = OpConstant %s32 1 -%s32_2 = OpConstant %s32 2 -%s32_3 = OpConstant %s32 3 - -%u64_0 = OpConstant %u64 0 -%u64_1 = OpConstant %u64 1 -%u64_2 = OpConstant %u64 2 -%u64_3 = OpConstant %u64 3 - -%s64_0 = OpConstant %s64 0 -%s64_1 = OpConstant %s64 1 -%s64_2 = OpConstant %s64 2 -%s64_3 = OpConstant %s64 3 -)"; - - ss << op_const_instructions; - - ss << R"( -%s32vec2_01 = OpConstantComposite %s32vec2 %s32_0 %s32_1 -%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 - -%s32vec2_12 = OpConstantComposite %s32vec2 %s32_1 %s32_2 -%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 - -%s32vec4_0123 = OpConstantComposite %s32vec4 %s32_0 %s32_1 %s32_2 %s32_3 -%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 - -%s64vec2_01 = OpConstantComposite %s64vec2 %s64_0 %s64_1 -%u64vec2_01 = OpConstantComposite %u64vec2 %u64_0 %u64_1 - -%f32mat22_1212 = OpConstantComposite %f32mat22 %f32vec2_12 %f32vec2_12 -%f32mat23_121212 = OpConstantComposite %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12 - -%f32_ptr_output = OpTypePointer Output %f32 -%f32vec2_ptr_output = OpTypePointer Output %f32vec2 - -%u32_ptr_output = OpTypePointer Output %u32 -%u32vec2_ptr_output = OpTypePointer Output %u32vec2 - -%u64_ptr_output = OpTypePointer Output %u64 - -%f32_output = OpVariable %f32_ptr_output Output -%f32vec2_output = OpVariable %f32vec2_ptr_output Output - -%u32_output = OpVariable %u32_ptr_output Output -%u32vec2_output = OpVariable %u32vec2_ptr_output Output - -%u64_output = OpVariable %u64_ptr_output Output - -%f32_ptr_input = OpTypePointer Input %f32 -%f32vec2_ptr_input = OpTypePointer Input %f32vec2 - -%u32_ptr_input = OpTypePointer Input %u32 -%u32vec2_ptr_input = OpTypePointer Input %u32vec2 - -%u64_ptr_input = OpTypePointer Input %u64 - -%f32_ptr_function = OpTypePointer Function %f32 - -%f32_input = OpVariable %f32_ptr_input Input -%f32vec2_input = OpVariable %f32vec2_ptr_input Input - -%u32_input = OpVariable %u32_ptr_input Input -%u32vec2_input = OpVariable %u32vec2_ptr_input Input - -%u64_input = OpVariable %u64_ptr_input Input - -%u32_ptr_function = OpTypePointer Function %u32 - -%struct_f16_u16 = OpTypeStruct %f16 %u16 -%struct_f32_f32 = OpTypeStruct %f32 %f32 -%struct_f32_f32_f32 = OpTypeStruct %f32 %f32 %f32 -%struct_f32_u32 = OpTypeStruct %f32 %u32 -%struct_f32_u32_f32 = OpTypeStruct %f32 %u32 %f32 -%struct_u32_f32 = OpTypeStruct %u32 %f32 -%struct_u32_u32 = OpTypeStruct %u32 %u32 -%struct_f32_f64 = OpTypeStruct %f32 %f64 -%struct_f32vec2_f32vec2 = OpTypeStruct %f32vec2 %f32vec2 -%struct_f32vec2_u32vec2 = OpTypeStruct %f32vec2 %u32vec2 -)"; - - ss << debug_instructions_before_main; - - ss << R"( -%main = OpFunction %void None %func -%main_entry = OpLabel -)"; - - ss << body; - - ss << R"( -OpReturn -OpFunctionEnd)"; - - return ss.str(); -} - -TEST_F(ValidateOldDebugInfo, UseDebugInstructionOutOfFunction) { - const std::string src = R"( -%code = OpString "main() {}" -)"; - - const std::string dbg_inst = R"( -%cu = OpExtInst %void %DbgExt DebugCompilationUnit %code 1 1 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "DebugInfo" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", - extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, UseDebugInstructionOutOfFunction) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -)"; - - const std::string dbg_inst = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", - extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugSourceInFunction) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -)"; - - const std::string dbg_inst = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", "", dbg_inst, - extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Debug info extension instructions other than DebugScope, " - "DebugNoScope, DebugDeclare, DebugValue must appear between " - "section 9 (types, constants, global variables) and section 10 " - "(function declarations)")); -} - -TEST_P(ValidateLocalDebugInfoOutOfFunction, OpenCLDebugInfo100DebugScope) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() {}" -%void_name = OpString "void" -%main_name = OpString "main" -%main_linkage_name = OpString "v_main" -%int_name = OpString "int" -%foo_name = OpString "foo" -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %u32_0 Signed -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main -%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %int_info %dbg_src 1 1 %main_info FlagIsLocal -%expr = OpExtInst %void %DbgExt DebugExpression -)"; - - const std::string body = R"( -%foo = OpVariable %u32_ptr_function Function -%foo_val = OpLoad %u32 %foo -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header + GetParam(), body, extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("DebugScope, DebugNoScope, DebugDeclare, DebugValue " - "of debug info extension must appear in a function " - "body")); -} - -INSTANTIATE_TEST_SUITE_P( - AllLocalDebugInfo, ValidateLocalDebugInfoOutOfFunction, - ::testing::ValuesIn(std::vector{ - "%main_scope = OpExtInst %void %DbgExt DebugScope %main_info", - "%no_scope = OpExtInst %void %DbgExt DebugNoScope", - })); - -TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionForwardReference) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() {}" -%void_name = OpString "void" -%main_name = OpString "main" -%main_linkage_name = OpString "v_main" -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main -)"; - - const std::string body = R"( -%main_scope = OpExtInst %void %DbgExt DebugScope %main_info -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionMissingOpFunction) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() {}" -%void_name = OpString "void" -%main_name = OpString "main" -%main_linkage_name = OpString "v_main" -)"; - - const std::string dbg_inst_header = R"( -%dbgNone = OpExtInst %void %DbgExt DebugInfoNone -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %dbgNone -)"; - - const std::string body = R"( -%main_scope = OpExtInst %void %DbgExt DebugScope %main_info -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugScopeBeforeOpVariableInFunction) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float4 main(float arg) { - float foo; - return float4(0, 0, 0, 0); -} -" -%float_name = OpString "float" -%main_name = OpString "main" -%main_linkage_name = OpString "v4f_main_f" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main -)"; - - const std::string body = R"( -%main_scope = OpExtInst %void %DbgExt DebugScope %main_info -%foo = OpVariable %f32_ptr_function Function -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeSizeDebugInfoNone) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "OpaqueType foo; -main() {} -" -%ty_name = OpString "struct VS_OUTPUT" -)"; - - const std::string dbg_inst_header = R"( -%dbg_none = OpExtInst %void %DbgExt DebugInfoNone -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header, - "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeForwardReference) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "struct VS_OUTPUT { - float4 pos : SV_POSITION; - float4 color : COLOR; -}; -main() {} -" -%VS_OUTPUT_name = OpString "struct VS_OUTPUT" -%float_name = OpString "float" -%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" -%VS_OUTPUT_color_name = OpString "color : COLOR" -%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %VS_OUTPUT_color_info -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 -%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic -%VS_OUTPUT_color_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_color_name %v4float_info %dbg_src 3 3 %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeMissingReference) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "struct VS_OUTPUT { - float4 pos : SV_POSITION; - float4 color : COLOR; -}; -main() {} -" -%VS_OUTPUT_name = OpString "struct VS_OUTPUT" -%float_name = OpString "float" -%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" -%VS_OUTPUT_color_name = OpString "color : COLOR" -%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %VS_OUTPUT_color_info -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 -%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("forward referenced IDs have not been defined")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugInstructionWrongResultType) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -)"; - - const std::string dbg_inst = R"( -%dbg_src = OpExtInst %bool %DbgExt DebugSource %src %code -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", - extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected result type must be a result id of " - "OpTypeVoid")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugCompilationUnit) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -)"; - - const std::string dbg_inst = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", - extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugCompilationUnitFail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -)"; - - const std::string dbg_inst = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %src HLSL -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", - extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Source must be a result id of " - "DebugSource")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugSourceFailFile) { - const std::string src = R"( -%code = OpString "main() {}" -)"; - - const std::string dbg_inst = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %DbgExt %code -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", - extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand File must be a result id of " - "OpString")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugSourceFailSource) { - const std::string src = R"( -%src = OpString "simple.hlsl" -)"; - - const std::string dbg_inst = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %DbgExt -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", - extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Text must be a result id of " - "OpString")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugSourceNoText) { - const std::string src = R"( -%src = OpString "simple.hlsl" -)"; - - const std::string dbg_inst = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", - extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeBasicFailName) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float4 main(float arg) { - float foo; - return float4(0, 0, 0, 0); -} -" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %int_32 %int_32 Float -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Name must be a result id of " - "OpString")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeBasicFailSize) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float4 main(float arg) { - float foo; - return float4(0, 0, 0, 0); -} -" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %float_name Float -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Size must be a result id of " - "OpConstant")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypePointer) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float4 main(float arg) { - float foo; - return float4(0, 0, 0, 0); -} -" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%pfloat_info = OpExtInst %void %DbgExt DebugTypePointer %float_info Function FlagIsLocal -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypePointerFail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float4 main(float arg) { - float foo; - return float4(0, 0, 0, 0); -} -" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%pfloat_info = OpExtInst %void %DbgExt DebugTypePointer %dbg_src Function FlagIsLocal -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Base Type must be a result id of " - "DebugTypeBasic")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeQualifier) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float4 main(float arg) { - float foo; - return float4(0, 0, 0, 0); -} -" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %float_info ConstType -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeQualifierFail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float4 main(float arg) { - float foo; - return float4(0, 0, 0, 0); -} -" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %comp_unit ConstType -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Base Type must be a result id of " - "DebugTypeBasic")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArray) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %int_32 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayWithVariableSize) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -%int_name = OpString "int" -%main_name = OpString "main" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%uint_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %int_32 Unsigned -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main -%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %uint_info %dbg_src 1 1 %main_info FlagIsLocal -%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_info -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailBaseType) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %comp_unit %int_32 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Base Type is not a valid debug " - "type")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCount) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %float_info -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Component Count must be OpConstant with a 32- or " - "64-bits integer scalar type or DebugGlobalVariable or " - "DebugLocalVariable with a 32- or 64-bits unsigned " - "integer scalar type")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCountFloat) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %f32_4 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Component Count must be OpConstant with a 32- or " - "64-bits integer scalar type or DebugGlobalVariable or " - "DebugLocalVariable with a 32- or 64-bits unsigned " - "integer scalar type")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCountZero) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %u32_0 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Component Count must be OpConstant with a 32- or " - "64-bits integer scalar type or DebugGlobalVariable or " - "DebugLocalVariable with a 32- or 64-bits unsigned " - "integer scalar type")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailVariableSizeTypeFloat) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -%main_name = OpString "main" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main -%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 1 %main_info FlagIsLocal -%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_info -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Component Count must be OpConstant with a 32- or " - "64-bits integer scalar type or DebugGlobalVariable or " - "DebugLocalVariable with a 32- or 64-bits unsigned " - "integer scalar type")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVector) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 4 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Base Type must be a result id of " - "DebugTypeBasic")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFailComponentZero) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 0 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Base Type must be a result id of " - "DebugTypeBasic")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFailComponentFive) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 5 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Base Type must be a result id of " - "DebugTypeBasic")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypedef) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo_info = OpExtInst %void %DbgExt DebugTypedef %foo_name %float_info %dbg_src 1 1 %comp_unit -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_P(ValidateOpenCL100DebugInfoDebugTypedef, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo_info = OpExtInst %void %DbgExt DebugTypedef )"; - ss << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), - "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second + - " must be a result id of ")); -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypedef, - ::testing::ValuesIn(std::vector>{ - std::make_pair(R"(%dbg_src %float_info %dbg_src 1 1 %comp_unit)", - "Name"), - std::make_pair(R"(%foo_name %dbg_src %dbg_src 1 1 %comp_unit)", - "Base Type"), - std::make_pair(R"(%foo_name %float_info %comp_unit 1 1 %comp_unit)", - "Source"), - std::make_pair(R"(%foo_name %float_info %dbg_src 1 1 %dbg_src)", - "Parent"), - })); - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunction) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%main_name = OpString "main" -%main_linkage_name = OpString "v_main" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%main_type_info1 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_type_info2 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info -%main_type_info3 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info %float_info -%main_type_info4 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void %float_info %float_info -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunctionFailReturn) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%main_name = OpString "main" -%main_linkage_name = OpString "v_main" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %dbg_src %float_info -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("expected operand Return Type is not a valid debug type")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunctionFailParam) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%main_name = OpString "main" -%main_linkage_name = OpString "v_main" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info %void -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("expected operand Parameter Types is not a valid debug type")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeEnum) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%none = OpExtInst %void %DbgExt DebugInfoNone -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo_info1 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name %u32_1 %foo_name -%foo_info2 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name %u32_1 %foo_name -%foo_info3 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_P(ValidateOpenCL100DebugInfoDebugTypeEnum, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo_info = OpExtInst %void %DbgExt DebugTypeEnum )"; - ss << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), - "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second)); -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeEnum, - ::testing::ValuesIn(std::vector>{ - std::make_pair( - R"(%dbg_src %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)", - "Name"), - std::make_pair( - R"(%foo_name %dbg_src %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)", - "Underlying Types"), - std::make_pair( - R"(%foo_name %float_info %comp_unit 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)", - "Source"), - std::make_pair( - R"(%foo_name %float_info %dbg_src 1 1 %dbg_src %int_32 FlagIsPublic %u32_0 %foo_name)", - "Parent"), - std::make_pair( - R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %void FlagIsPublic %u32_0 %foo_name)", - "Size"), - std::make_pair( - R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %u32_0 FlagIsPublic %u32_0 %foo_name)", - "Size"), - std::make_pair( - R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %foo_name %foo_name)", - "Value"), - std::make_pair( - R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %u32_1)", - "Name"), - })); - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeFunctionAndInheritance) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "struct VS_OUTPUT { - float4 pos : SV_POSITION; -}; -struct foo : VS_OUTPUT { -}; -main() {} -" -%VS_OUTPUT_name = OpString "struct VS_OUTPUT" -%float_name = OpString "float" -%foo_name = OpString "foo" -%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" -%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" -%main_name = OpString "main" -%main_linkage_name = OpString "v4f_main_f" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 -%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main -%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic -%child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_P(ValidateOpenCL100DebugInfoDebugTypeComposite, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "struct VS_OUTPUT { - float4 pos : SV_POSITION; -}; -struct foo : VS_OUTPUT { -}; -main() {} -" -%VS_OUTPUT_name = OpString "struct VS_OUTPUT" -%float_name = OpString "float" -%foo_name = OpString "foo" -%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" -%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" -%main_name = OpString "main" -%main_linkage_name = OpString "v4f_main_f" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite )"; - ss << param.first; - ss << R"( -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 -%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main -%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic -%child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), - "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second + " must be ")); -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeComposite, - ::testing::ValuesIn(std::vector>{ - std::make_pair( - R"(%dbg_src Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", - "Name"), - std::make_pair( - R"(%VS_OUTPUT_name Structure %comp_unit 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", - "Source"), - std::make_pair( - R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %dbg_src %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", - "Parent"), - std::make_pair( - R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %int_128 %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", - "Linkage Name"), - std::make_pair( - R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %dbg_src FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", - "Size"), - std::make_pair( - R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %dbg_src %main_info %child)", - "Members"), - std::make_pair( - R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %dbg_src %child)", - "Members"), - std::make_pair( - R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %dbg_src)", - "Members"), - })); - -TEST_P(ValidateOpenCL100DebugInfoDebugTypeMember, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "struct VS_OUTPUT { - float pos : SV_POSITION; -}; -main() {} -" -%VS_OUTPUT_name = OpString "struct VS_OUTPUT" -%float_name = OpString "float" -%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" -%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_32 FlagIsPublic %VS_OUTPUT_pos_info -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember )"; - ss << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), - "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - if (!param.second.empty()) { - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second + - " must be a result id of ")); - } -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeMember, - ::testing::ValuesIn(std::vector>{ - std::make_pair( - R"(%dbg_src %float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)", - "Name"), - std::make_pair( - R"(%VS_OUTPUT_pos_name %dbg_src %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)", - ""), - std::make_pair( - R"(%VS_OUTPUT_pos_name %float_info %float_info 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)", - "Source"), - std::make_pair( - R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %float_info %u32_0 %int_32 FlagIsPublic)", - "Parent"), - std::make_pair( - R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %VS_OUTPUT_info %void %int_32 FlagIsPublic)", - "Offset"), - std::make_pair( - R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %void FlagIsPublic)", - "Size"), - })); - -TEST_P(ValidateOpenCL100DebugInfoDebugTypeInheritance, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "struct VS_OUTPUT {}; -struct foo : VS_OUTPUT {}; -" -%VS_OUTPUT_name = OpString "struct VS_OUTPUT" -%foo_name = OpString "foo" -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_name %u32_0 FlagIsPublic %child -%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic -%bar_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Union %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic -%child = OpExtInst %void %DbgExt DebugTypeInheritance )" - << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", - extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second)); -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeInheritance, - ::testing::ValuesIn(std::vector>{ - std::make_pair(R"(%dbg_src %VS_OUTPUT_info %u32_0 %u32_0 FlagIsPublic)", - "Child must be a result id of"), - std::make_pair(R"(%foo_info %dbg_src %u32_0 %u32_0 FlagIsPublic)", - "Parent must be a result id of"), - std::make_pair( - R"(%bar_info %VS_OUTPUT_info %u32_0 %u32_0 FlagIsPublic)", - "Child must be class or struct debug type"), - std::make_pair(R"(%foo_info %bar_info %u32_0 %u32_0 FlagIsPublic)", - "Parent must be class or struct debug type"), - std::make_pair(R"(%foo_info %VS_OUTPUT_info %void %u32_0 FlagIsPublic)", - "Offset"), - std::make_pair(R"(%foo_info %VS_OUTPUT_info %u32_0 %void FlagIsPublic)", - "Size"), - })); TEST_P(ValidateGlslStd450SqrtLike, Success) { const std::string ext_inst_name = GetParam(); std::ostringstream ss; @@ -2178,1086 +472,6 @@ TEST_P(ValidateGlslStd450SqrtLike, Success) { ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionDeclaration) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "struct VS_OUTPUT { - float4 pos : SV_POSITION; -}; -main() {} -" -%main_name = OpString "main" -%main_linkage_name = OpString "v4f_main_f" -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header, - "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_P(ValidateOpenCL100DebugInfoDebugFunction, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "struct VS_OUTPUT { - float4 pos : SV_POSITION; -}; -main() {} -" -%main_name = OpString "main" -%main_linkage_name = OpString "v4f_main_f" -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic -%main_info = OpExtInst %void %DbgExt DebugFunction )" - << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", - extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second)); -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugFunction, - ::testing::ValuesIn(std::vector>{ - std::make_pair( - R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)", - "Name"), - std::make_pair( - R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)", - "Type"), - std::make_pair( - R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)", - "Source"), - std::make_pair( - R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_linkage_name FlagIsPublic 13 %main)", - "Parent"), - std::make_pair( - R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic 13 %main)", - "Linkage Name"), - std::make_pair( - R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %void)", - "Function"), - std::make_pair( - R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main %dbg_src)", - "Declaration"), - })); - -TEST_P(ValidateOpenCL100DebugInfoDebugFunctionDeclaration, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "struct VS_OUTPUT { - float4 pos : SV_POSITION; -}; -main() {} -" -%main_name = OpString "main" -%main_linkage_name = OpString "v4f_main_f" -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration )" - << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", - extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second)); -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, - ValidateOpenCL100DebugInfoDebugFunctionDeclaration, - ::testing::ValuesIn(std::vector>{ - std::make_pair( - R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic)", - "Name"), - std::make_pair( - R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic)", - "Type"), - std::make_pair( - R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_linkage_name FlagIsPublic)", - "Source"), - std::make_pair( - R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_linkage_name FlagIsPublic)", - "Parent"), - std::make_pair( - R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic)", - "Linkage Name"), - })); - -TEST_F(ValidateOpenCL100DebugInfo, DebugLexicalBlock) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%main_name = OpString "main" -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%main_block = OpExtInst %void %DbgExt DebugLexicalBlock %dbg_src 1 1 %comp_unit %main_name)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header, - "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_P(ValidateOpenCL100DebugInfoDebugLexicalBlock, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%main_name = OpString "main" -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%main_block = OpExtInst %void %DbgExt DebugLexicalBlock )" - << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", - extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second)); -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugLexicalBlock, - ::testing::ValuesIn(std::vector>{ - std::make_pair(R"(%comp_unit 1 1 %comp_unit %main_name)", "Source"), - std::make_pair(R"(%dbg_src 1 1 %dbg_src %main_name)", "Parent"), - std::make_pair(R"(%dbg_src 1 1 %comp_unit %void)", "Name"), - })); - -TEST_F(ValidateOpenCL100DebugInfo, DebugScopeFailScope) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() {}" -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -)"; - - const std::string body = R"( -%main_scope = OpExtInst %void %DbgExt DebugScope %dbg_src -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugScopeFailInlinedAt) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() {}" -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -)"; - - const std::string body = R"( -%main_scope = OpExtInst %void %DbgExt DebugScope %comp_unit %dbg_src -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined At")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugLocalVariable) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() { float foo; }" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_P(ValidateOpenCL100DebugInfoDebugLocalVariable, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() { float foo; }" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo = OpExtInst %void %DbgExt DebugLocalVariable )" - << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), - "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second)); -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugLocalVariable, - ::testing::ValuesIn(std::vector>{ - std::make_pair( - R"(%void %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0)", - "Name"), - std::make_pair( - R"(%foo_name %dbg_src %dbg_src 1 10 %comp_unit FlagIsLocal 0)", - "Type"), - std::make_pair( - R"(%foo_name %float_info %comp_unit 1 10 %comp_unit FlagIsLocal 0)", - "Source"), - std::make_pair( - R"(%foo_name %float_info %dbg_src 1 10 %dbg_src FlagIsLocal 0)", - "Parent"), - })); - -TEST_F(ValidateOpenCL100DebugInfo, DebugDeclare) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() { float foo; }" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%null_expr = OpExtInst %void %DbgExt DebugExpression -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 -)"; - - const std::string body = R"( -%foo = OpVariable %f32_ptr_function Function -%decl = OpExtInst %void %DbgExt DebugDeclare %foo_info %foo %null_expr -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugDeclareParam) { - CompileSuccessfully(R"( - OpCapability Shader - %1 = OpExtInstImport "OpenCL.DebugInfo.100" - OpMemoryModel Logical GLSL450 - OpEntryPoint Vertex %main "main" %in_var_COLOR - %4 = OpString "test.hlsl" - OpSource HLSL 620 %4 "#line 1 \"test.hlsl\" -void main(float foo:COLOR) {} -" - %11 = OpString "#line 1 \"test.hlsl\" -void main(float foo:COLOR) {} -" - %14 = OpString "float" - %17 = OpString "src.main" - %20 = OpString "foo" - OpName %in_var_COLOR "in.var.COLOR" - OpName %main "main" - OpName %param_var_foo "param.var.foo" - OpName %src_main "src.main" - OpName %foo "foo" - OpName %bb_entry "bb.entry" - 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 - %23 = OpTypeFunction %void -%_ptr_Function_float = OpTypePointer Function %float - %29 = OpTypeFunction %void %_ptr_Function_float - OpLine %4 1 21 -%in_var_COLOR = OpVariable %_ptr_Input_float Input - %10 = OpExtInst %void %1 DebugExpression - %12 = OpExtInst %void %1 DebugSource %4 %11 - %13 = OpExtInst %void %1 DebugCompilationUnit 1 4 %12 HLSL - %15 = OpExtInst %void %1 DebugTypeBasic %14 %uint_32 Float - %16 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %void %15 - %18 = OpExtInst %void %1 DebugFunction %17 %16 %12 1 1 %13 %17 FlagIsProtected|FlagIsPrivate 1 %src_main - %21 = OpExtInst %void %1 DebugLocalVariable %20 %15 %12 1 17 %18 FlagIsLocal 0 - %22 = OpExtInst %void %1 DebugLexicalBlock %12 1 28 %18 - OpLine %4 1 1 - %main = OpFunction %void None %23 - %24 = OpLabel - OpLine %4 1 17 -%param_var_foo = OpVariable %_ptr_Function_float Function - %27 = OpLoad %float %in_var_COLOR - OpLine %4 1 1 - %28 = OpFunctionCall %void %src_main %param_var_foo - OpReturn - OpFunctionEnd - %src_main = OpFunction %void None %29 - OpLine %4 1 17 - %foo = OpFunctionParameter %_ptr_Function_float - %31 = OpExtInst %void %1 DebugDeclare %21 %foo %10 - %bb_entry = OpLabel - OpLine %4 1 29 - OpReturn - OpFunctionEnd -)"); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_P(ValidateOpenCL100DebugInfoDebugDeclare, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() { float foo; }" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%null_expr = OpExtInst %void %DbgExt DebugExpression -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%foo = OpVariable %f32_ptr_function Function -%decl = OpExtInst %void %DbgExt DebugDeclare )" - << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, ss.str(), extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second)); -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugDeclare, - ::testing::ValuesIn(std::vector>{ - std::make_pair(R"(%dbg_src %foo %null_expr)", "Local Variable"), - std::make_pair(R"(%foo_info %void %null_expr)", "Variable"), - std::make_pair(R"(%foo_info %foo %dbg_src)", "Expression"), - })); - -TEST_F(ValidateOpenCL100DebugInfo, DebugExpression) { - const std::string dbg_inst_header = R"( -%op0 = OpExtInst %void %DbgExt DebugOperation Deref -%op1 = OpExtInst %void %DbgExt DebugOperation Plus -%null_expr = OpExtInst %void %DbgExt DebugExpression %op0 %op1 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header, - "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugExpressionFail) { - const std::string dbg_inst_header = R"( -%op = OpExtInst %void %DbgExt DebugOperation Deref -%null_expr = OpExtInst %void %DbgExt DebugExpression %op %void -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header, - "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "expected operand Operation must be a result id of DebugOperation")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplate) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "OpaqueType foo; -main() {} -" -%float_name = OpString "float" -%ty_name = OpString "Texture" -%t_name = OpString "T" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const std::string dbg_inst_header = R"( -%dbg_none = OpExtInst %void %DbgExt DebugInfoNone -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic -%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 -%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateUsedForVariableType) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "OpaqueType foo; -main() {} -" -%float_name = OpString "float" -%ty_name = OpString "Texture" -%t_name = OpString "T" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const std::string dbg_inst_header = R"( -%dbg_none = OpExtInst %void %DbgExt DebugInfoNone -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic -%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 -%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param -%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %temp %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFunction) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "OpaqueType foo; -main() {} -" -%float_name = OpString "float" -%ty_name = OpString "Texture" -%t_name = OpString "T" -%main_name = OpString "main" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const std::string dbg_inst_header = R"( -%dbg_none = OpExtInst %void %DbgExt DebugInfoNone -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %param %param -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main -%temp = OpExtInst %void %DbgExt DebugTypeTemplate %main_info %param -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFailTarget) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "OpaqueType foo; -main() {} -" -%float_name = OpString "float" -%ty_name = OpString "Texture" -%t_name = OpString "T" -%main_name = OpString "main" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const std::string dbg_inst_header = R"( -%dbg_none = OpExtInst %void %DbgExt DebugInfoNone -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 -%temp = OpExtInst %void %DbgExt DebugTypeTemplate %float_info %param -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Target must be DebugTypeComposite or " - "DebugFunction")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFailParam) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "OpaqueType foo; -main() {} -" -%float_name = OpString "float" -%ty_name = OpString "Texture" -%t_name = OpString "T" -%main_name = OpString "main" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const std::string dbg_inst_header = R"( -%dbg_none = OpExtInst %void %DbgExt DebugInfoNone -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %param %param -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main -%temp = OpExtInst %void %DbgExt DebugTypeTemplate %main_info %float_info -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "expected operand Parameters must be DebugTypeTemplateParameter or " - "DebugTypeTemplateTemplateParameter")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariable) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float foo; void main() {}" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariableStaticMember) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float foo; void main() {}" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Class %dbg_src 0 0 %comp_unit %foo_name %int_32 FlagIsPublic %a -%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src 0 0 %t %u32_0 %int_32 FlagIsPublic -%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate %a -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariableDebugInfoNone) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float foo; void main() {}" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbgNone = OpExtInst %void %DbgExt DebugInfoNone -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %dbgNone FlagIsProtected|FlagIsPrivate -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariableConst) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float foo; void main() {}" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %int_32 FlagIsProtected|FlagIsPrivate -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_P(ValidateOpenCL100DebugInfoDebugGlobalVariable, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float foo; void main() {}" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo = OpExtInst %void %DbgExt DebugGlobalVariable )" - << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), - "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second)); -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugGlobalVariable, - ::testing::ValuesIn(std::vector>{ - std::make_pair( - R"(%void %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", - "Name"), - std::make_pair( - R"(%foo_name %dbg_src %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", - "Type"), - std::make_pair( - R"(%foo_name %float_info %comp_unit 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", - "Source"), - std::make_pair( - R"(%foo_name %float_info %dbg_src 0 0 %dbg_src %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", - "Scope"), - std::make_pair( - R"(%foo_name %float_info %dbg_src 0 0 %comp_unit %void %f32_input FlagIsProtected|FlagIsPrivate)", - "Linkage Name"), - std::make_pair( - R"(%foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %void FlagIsProtected|FlagIsPrivate)", - "Variable"), - })); - -TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAt) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() {}" -%void_name = OpString "void" -%main_name = OpString "main" -%main_linkage_name = OpString "v_main" -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main -%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info -%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info %inlined_at -)"; - - const std::string body = R"( -%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAtFail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() {}" -%void_name = OpString "void" -%main_name = OpString "main" -%main_linkage_name = OpString "v_main" -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main -%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info -%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %inlined_at -)"; - - const std::string body = R"( -%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAtFail2) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() {}" -%void_name = OpString "void" -%main_name = OpString "main" -%main_linkage_name = OpString "v_main" -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main -%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info -%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info %main_info -)"; - - const std::string body = R"( -%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugValue) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() { float foo; }" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_3 = OpConstant %u32 3 -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%null_expr = OpExtInst %void %DbgExt DebugExpression -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 -%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 -)"; - - const std::string body = R"( -%value = OpExtInst %void %DbgExt DebugValue %foo_info %int_32 %null_expr %int_3 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugValueWithVariableIndex) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() { float foo; }" -%float_name = OpString "float" -%int_name = OpString "int" -%foo_name = OpString "foo" -%len_name = OpString "length" -)"; - - const std::string size_const = R"( -%int_3 = OpConstant %u32 3 -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%null_expr = OpExtInst %void %DbgExt DebugExpression -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %int_32 Signed -%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 -%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src 1 10 %comp_unit FlagIsLocal -%len_info = OpExtInst %void %DbgExt DebugLocalVariable %len_name %int_info %dbg_src 0 0 %comp_unit FlagIsLocal -)"; - - const std::string body = R"( -%value = OpExtInst %void %DbgExt DebugValue %foo_info %int_32 %null_expr %len_info -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_P(ValidateOpenCL100DebugInfoDebugValue, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() { float foo; }" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%null_expr = OpExtInst %void %DbgExt DebugExpression -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%decl = OpExtInst %void %DbgExt DebugValue )" - << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, ss.str(), extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second)); -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugValue, - ::testing::ValuesIn(std::vector>{ - std::make_pair(R"(%dbg_src %int_32 %null_expr)", "Local Variable"), - std::make_pair(R"(%foo_info %int_32 %dbg_src)", "Expression"), - std::make_pair(R"(%foo_info %int_32 %null_expr %dbg_src)", "Indexes"), - })); - TEST_P(ValidateGlslStd450SqrtLike, IntResultType) { const std::string ext_inst_name = GetParam(); const std::string body = @@ -3507,6 +721,19 @@ TEST_P(ValidateGlslStd450SAbsLike, WrongBitWidthOperand) { "Result Type")); } +TEST_P(ValidateGlslStd450SAbsLike, TypelessOperand) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %s64 %extinst " + ext_inst_name + " %main_entry\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to be int scalars or vectors")); +} + INSTANTIATE_TEST_SUITE_P(AllSAbsLike, ValidateGlslStd450SAbsLike, ::testing::ValuesIn(std::vector{ "SAbs", @@ -3656,6 +883,19 @@ TEST_P(ValidateGlslStd450UMinLike, WrongBitWidthOperand2) { "Result Type")); } +TEST_P(ValidateGlslStd450UMinLike, TypelessOperand) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %s64 %extinst " + ext_inst_name + + " %s64_0 %main_entry\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to be int scalars or vectors")); +} + INSTANTIATE_TEST_SUITE_P(AllUMinLike, ValidateGlslStd450UMinLike, ::testing::ValuesIn(std::vector{ "UMin", @@ -3819,6 +1059,19 @@ TEST_P(ValidateGlslStd450UClampLike, WrongBitWidthOperand3) { "Result Type")); } +TEST_P(ValidateGlslStd450UClampLike, TypelessOperand) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %s64 %extinst " + ext_inst_name + + " %main_entry %s64_0 %s64_0\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to be int scalars or vectors")); +} + INSTANTIATE_TEST_SUITE_P(AllUClampLike, ValidateGlslStd450UClampLike, ::testing::ValuesIn(std::vector{ "UClamp", @@ -4327,6 +1580,19 @@ TEST_F(ValidateExtInst, GlslStd450LdexpExpWrongSize) { "number as Result Type")); } +TEST_F(ValidateExtInst, GlslStd450LdexpExpNoType) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Ldexp %f32_1 %main_entry +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Ldexp: " + "expected operand Exp to be a 32-bit int scalar " + "or vector type")); +} + TEST_F(ValidateExtInst, GlslStd450FrexpStructSuccess) { const std::string body = R"( %val1 = OpExtInst %struct_f32_u32 %extinst FrexpStruct %f32_h @@ -5552,7 +2818,8 @@ TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetInternalInvalidDataF32) { "expected Interpolant to be a pointer")); } -TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetInternalInvalidDataF32Vec2) { +TEST_F(ValidateExtInst, + GlslStd450InterpolateAtOffsetInternalInvalidDataF32Vec2) { const std::string body = R"( %ld2 = OpLoad %f32vec2 %f32vec2_input %val2 = OpExtInst %f32vec2 %extinst InterpolateAtOffset %ld2 %f32vec2_01 @@ -7054,7 +4321,7 @@ TEST_P(ValidateOpenCLStdVStoreHalfLike, PNotPointer) { CompileSuccessfully(GenerateKernelCode(ss.str())); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Operand 89[%_ptr_Workgroup_half] cannot be a type")); + HasSubstr("Operand '89[%_ptr_Workgroup_half]' cannot be a type")); } TEST_P(ValidateOpenCLStdVStoreHalfLike, ConstPointer) { @@ -7226,7 +4493,7 @@ TEST_P(ValidateOpenCLStdVLoadHalfLike, PNotPointer) { CompileSuccessfully(GenerateKernelCode(ss.str())); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Operand 89[%_ptr_Workgroup_half] cannot be a type")); + HasSubstr("Operand '89[%_ptr_Workgroup_half]' cannot be a type")); } TEST_P(ValidateOpenCLStdVLoadHalfLike, OffsetWrongStorageType) { @@ -7397,9 +4664,10 @@ TEST_F(ValidateExtInst, VLoadNPNotPointer) { CompileSuccessfully(GenerateKernelCode(ss.str())); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Operand 120[%_ptr_UniformConstant_float] cannot be a " - "type")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Operand '120[%_ptr_UniformConstant_float]' cannot be a " + "type")); } TEST_F(ValidateExtInst, VLoadNWrongStorageClass) { @@ -7512,7 +4780,7 @@ TEST_F(ValidateExtInst, VLoadHalfPNotPointer) { CompileSuccessfully(GenerateKernelCode(ss.str())); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Operand 114[%_ptr_UniformConstant_half] cannot be a " + HasSubstr("Operand '114[%_ptr_UniformConstant_half]' cannot be a " "type")); } @@ -7666,7 +4934,7 @@ TEST_F(ValidateExtInst, VStoreNPNotPointer) { CompileSuccessfully(GenerateKernelCode(ss.str())); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Operand 127[%_ptr_Generic_float] cannot be a type")); + HasSubstr("Operand '127[%_ptr_Generic_float]' cannot be a type")); } TEST_F(ValidateExtInst, VStoreNWrongStorageClass) { @@ -7981,9 +5249,10 @@ TEST_F(ValidateExtInst, OpenCLStdPrintfFormatNotPointer) { CompileSuccessfully(GenerateKernelCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Operand 137[%_ptr_UniformConstant_uchar] cannot be a " - "type")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Operand '137[%_ptr_UniformConstant_uchar]' cannot be a " + "type")); } TEST_F(ValidateExtInst, OpenCLStdPrintfFormatNotUniformConstStorageClass) { @@ -8075,7 +5344,7 @@ TEST_F(ValidateExtInst, OpenCLStdPrefetchPtrNotPointer) { CompileSuccessfully(GenerateKernelCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Operand 99[%_ptr_CrossWorkgroup_uint] cannot be a " + HasSubstr("Operand '99[%_ptr_CrossWorkgroup_uint]' cannot be a " "type")); } @@ -8778,6 +6047,18 @@ OpMemoryModel Logical GLSL450 "declared without SPV_KHR_non_semantic_info")); } +TEST_F(ValidateClspvReflection, DoesNotRequiresNonSemanticExtensionPost1p6) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "NonSemantic.ClspvReflection.1" +OpMemoryModel Logical GLSL450 +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_6); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); +} + TEST_F(ValidateClspvReflection, MissingVersion) { const std::string text = R"( OpCapability Shader diff --git a/test/val/val_extension_spv_khr_bit_instructions_test.cpp b/test/val/val_extension_spv_khr_bit_instructions_test.cpp new file mode 100644 index 00000000..0e926716 --- /dev/null +++ b/test/val/val_extension_spv_khr_bit_instructions_test.cpp @@ -0,0 +1,117 @@ +// Copyright (c) 2021 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests for OpExtension validator rules. + +#include +#include + +#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 ValidateSpvKHRBitInstructions = spvtest::ValidateBase; + +TEST_F(ValidateSpvKHRBitInstructions, Valid) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability BitInstructions + OpExtension "SPV_KHR_bit_instructions" + OpMemoryModel Physical32 OpenCL + OpEntryPoint Kernel %main "main" + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + %u32 = OpTypeInt 32 0 + %u32_1 = OpConstant %u32 1 + + %main = OpFunction %void None %void_fn + %entry = OpLabel + %unused = OpBitReverse %u32 %u32_1 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSpvKHRBitInstructions, RequiresExtension) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability BitInstructions + OpMemoryModel Physical32 OpenCL + OpEntryPoint Kernel %main "main" + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + %u32 = OpTypeInt 32 0 + %u32_1 = OpConstant %u32 1 + + %main = OpFunction %void None %void_fn + %entry = OpLabel + %unused = OpBitReverse %u32 %u32_1 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("1st operand of Capability: operand BitInstructions(6025) " + "requires one of these extensions: SPV_KHR_bit_instructions")); +} + +TEST_F(ValidateSpvKHRBitInstructions, RequiresCapability) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpExtension "SPV_KHR_bit_instructions" + OpMemoryModel Physical32 OpenCL + OpEntryPoint Kernel %main "main" + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + %u32 = OpTypeInt 32 0 + %u32_1 = OpConstant %u32 1 + + %main = OpFunction %void None %void_fn + %entry = OpLabel + %unused = OpBitReverse %u32 %u32_1 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode BitReverse requires one of these capabilities: " + "Shader BitInstructions")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/test/val/val_extension_spv_khr_expect_assume_test.cpp b/test/val/val_extension_spv_khr_expect_assume_test.cpp new file mode 100644 index 00000000..6ece15d1 --- /dev/null +++ b/test/val/val_extension_spv_khr_expect_assume_test.cpp @@ -0,0 +1,310 @@ +// Copyright (c) 2020 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests for OpExtension validator rules. + +#include +#include + +#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 ValidateSpvExpectAssumeKHR = spvtest::ValidateBase; + +TEST_F(ValidateSpvExpectAssumeKHR, Valid) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpCapability ExpectAssumeKHR + OpExtension "SPV_KHR_expect_assume" + OpMemoryModel Physical32 OpenCL + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + + %bool = OpTypeBool + %true = OpConstantTrue %bool + %undef = OpUndef %bool + + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + + %v2bool = OpTypeVector %bool 2 + %v2uint = OpTypeVector %uint 2 + + %null_v2bool = OpConstantNull %v2bool + %null_v2uint = OpConstantNull %v2uint + + %main = OpFunction %void None %voidfn + %entry = OpLabel + OpAssumeTrueKHR %true + OpAssumeTrueKHR %undef ; probably undefined behaviour + %bool_val = OpExpectKHR %bool %true %true + %uint_val = OpExpectKHR %uint %uint_1 %uint_2 ; a bad expectation + %v2bool_val = OpExpectKHR %v2bool %null_v2bool %null_v2bool + %v2uint_val = OpExpectKHR %v2uint %null_v2uint %null_v2uint + OpReturn + OpFunctionEnd + +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSpvExpectAssumeKHR, RequiresExtension) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpCapability ExpectAssumeKHR + OpMemoryModel Physical32 OpenCL + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + + %bool = OpTypeBool + %true = OpConstantTrue %bool + %undef = OpUndef %bool + + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + + %main = OpFunction %void None %voidfn + %entry = OpLabel + OpAssumeTrueKHR %true + OpAssumeTrueKHR %undef ; probably undefined behaviour + %val = OpExpectKHR %uint %uint_1 %uint_2 ; a bad expectation + OpReturn + OpFunctionEnd + +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Capability: operand ExpectAssumeKHR(5629) requires " + "one of these extensions: SPV_KHR_expect_assume")); +} + +TEST_F(ValidateSpvExpectAssumeKHR, + AssumeTrueKHR_RequiresExpectAssumeCapability) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpExtension "SPV_KHR_expect_assume" + OpMemoryModel Physical32 OpenCL + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + + %bool = OpTypeBool + %true = OpConstantTrue %bool + %undef = OpUndef %bool + + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + + %main = OpFunction %void None %voidfn + %entry = OpLabel + OpAssumeTrueKHR %true + OpAssumeTrueKHR %undef ; probably undefined behaviour + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode AssumeTrueKHR requires one of these " + "capabilities: ExpectAssumeKHR \n" + " OpAssumeTrueKHR %true\n")); +} + +TEST_F(ValidateSpvExpectAssumeKHR, AssumeTrueKHR_OperandMustBeBool) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpCapability ExpectAssumeKHR + OpExtension "SPV_KHR_expect_assume" + OpMemoryModel Physical32 OpenCL + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + + %bool = OpTypeBool + %true = OpConstantTrue %bool + %undef = OpUndef %bool + + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + + %main = OpFunction %void None %voidfn + %entry = OpLabel + OpAssumeTrueKHR %uint_1 ; bad type + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Value operand of OpAssumeTrueKHR must be a boolean scalar\n" + " OpAssumeTrueKHR %uint_1\n")); +} + +TEST_F(ValidateSpvExpectAssumeKHR, ExpectKHR_RequiresExpectAssumeCapability) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpExtension "SPV_KHR_expect_assume" + OpMemoryModel Physical32 OpenCL + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + + %bool = OpTypeBool + %true = OpConstantTrue %bool + %undef = OpUndef %bool + + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + + %main = OpFunction %void None %voidfn + %entry = OpLabel + %val = OpExpectKHR %uint %uint_1 %uint_2 ; a bad expectation + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode ExpectKHR requires one of these capabilities: " + "ExpectAssumeKHR \n" + " %11 = OpExpectKHR %uint %uint_1 %uint_2\n")); +} + +TEST_F(ValidateSpvExpectAssumeKHR, ExpectKHR_ResultMustBeBoolOrIntScalar) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpCapability ExpectAssumeKHR + OpExtension "SPV_KHR_expect_assume" + OpMemoryModel Physical32 OpenCL + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + + %float = OpTypeFloat 32 + + %float_0 = OpConstant %float 0 + + %main = OpFunction %void None %voidfn + %entry = OpLabel + %val = OpExpectKHR %float %float_0 %float_0 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result of OpExpectKHR must be a scalar or vector of " + "integer type or boolean type\n" + " %7 = OpExpectKHR %float %float_0 %float_0\n")); +} + +TEST_F(ValidateSpvExpectAssumeKHR, ExpectKHR_Value0MustMatchResultType) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpCapability ExpectAssumeKHR + OpExtension "SPV_KHR_expect_assume" + OpMemoryModel Physical32 OpenCL + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + + %main = OpFunction %void None %voidfn + %entry = OpLabel + %val = OpExpectKHR %uint %float_0 %float_0 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Type of Value operand of OpExpectKHR does not match " + "the result type \n" + " %8 = OpExpectKHR %uint %float_0 %float_0\n")); +} + +TEST_F(ValidateSpvExpectAssumeKHR, ExpectKHR_Value1MustMatchResultType) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpCapability ExpectAssumeKHR + OpExtension "SPV_KHR_expect_assume" + OpMemoryModel Physical32 OpenCL + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + + %main = OpFunction %void None %voidfn + %entry = OpLabel + %val = OpExpectKHR %uint %uint_0 %float_0 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Type of ExpectedValue operand of OpExpectKHR does not " + "match the result type \n" + " %9 = OpExpectKHR %uint %uint_0 %float_0\n")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/test/val/val_extension_spv_khr_integer_dot_product_test.cpp b/test/val/val_extension_spv_khr_integer_dot_product_test.cpp new file mode 100644 index 00000000..e0e6896c --- /dev/null +++ b/test/val/val_extension_spv_khr_integer_dot_product_test.cpp @@ -0,0 +1,1330 @@ +// Copyright (c) 2020 Google Inc. +// Copyright (c) 2021 Arm Ltd. +// +// 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 +#include +#include +#include + +#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; + +struct Case { + std::vector caps; + std::string inst; + std::string result_type; + std::string op0_type; + std::string op1_type; + std::string acc_type; // can be empty + bool packed; + std::string expected_error; // empty for no error. +}; + +inline std::ostream& operator<<(std::ostream& out, Case c) { + out << "\nSPV_KHR_integer_dot_product Case{{"; + bool first = true; + for (const auto& cap : c.caps) { + if (!first) { + out << " "; + } + first = false; + out << cap; + } + out << "} "; + out << c.inst << " "; + out << c.result_type << " "; + out << c.op0_type << " "; + out << c.op1_type << " "; + out << "'" << c.acc_type << "' "; + out << (c.packed ? "packed " : "unpacked "); + out << "err'" << c.expected_error << "'"; + return out; +} + +std::string AssemblyForCase(const Case& c) { + std::ostringstream ss; + ss << "OpCapability Shader\n"; + for (auto& cap : c.caps) { + ss << "OpCapability " << cap << "\n"; + } + ss << R"( + OpExtension "SPV_KHR_integer_dot_product" + OpMemoryModel Logical Simple + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + + %v2uint = OpTypeVector %uint 2 + %v3uint = OpTypeVector %uint 3 + %v4uint = OpTypeVector %uint 4 + %v2int = OpTypeVector %int 2 + %v3int = OpTypeVector %int 3 + %v4int = OpTypeVector %int 4 + + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + + %v2uint_0 = OpConstantComposite %v2uint %uint_0 %uint_0 + %v2uint_1 = OpConstantComposite %v2uint %uint_1 %uint_1 + %v3uint_0 = OpConstantComposite %v3uint %uint_0 %uint_0 %uint_0 + %v3uint_1 = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1 + %v4uint_0 = OpConstantComposite %v4uint %uint_0 %uint_0 %uint_0 %uint_0 + %v4uint_1 = OpConstantComposite %v4uint %uint_1 %uint_1 %uint_1 %uint_1 + + %v2int_0 = OpConstantComposite %v2int %int_0 %int_0 + %v2int_1 = OpConstantComposite %v2int %int_1 %int_1 + %v3int_0 = OpConstantComposite %v3int %int_0 %int_0 %int_0 + %v3int_1 = OpConstantComposite %v3int %int_1 %int_1 %int_1 + %v4int_0 = OpConstantComposite %v4int %int_0 %int_0 %int_0 %int_0 + %v4int_1 = OpConstantComposite %v4int %int_1 %int_1 %int_1 %int_1 +)"; + + bool use8bit = false; + for (auto& cap : c.caps) { + if (cap == "DotProductInput4x8BitKHR") { + use8bit = true; + } + if (cap == "Int8") { + use8bit = true; + } + } + if (use8bit) { + ss << R"( + %uchar = OpTypeInt 8 0 + %char = OpTypeInt 8 1 + + %v4uchar = OpTypeVector %uchar 4 + %v4char = OpTypeVector %char 4 + + %uchar_0 = OpConstant %uchar 0 + %uchar_1 = OpConstant %uchar 1 + %char_0 = OpConstant %char 0 + %char_1 = OpConstant %char 1 + + %v4uchar_0 = OpConstantComposite %v4uchar %uchar_0 %uint_0 %uchar_0 %uchar_0 + %v4uchar_1 = OpConstantComposite %v4uchar %uchar_1 %uchar_1 %uchar_1 %uchar_1 + %v4char_0 = OpConstantComposite %v4char %char_0 %char_0 %char_0 %char_0 + %v4char_1 = OpConstantComposite %v4char %char_1 %char_1 %char_1 %char_1 + + )"; + } + + ss << R"( + + %main = OpFunction %void None %voidfn + %entry = OpLabel + %result = )" + << c.inst << " " << c.result_type << " "; + ss << c.op0_type << "_0 "; + ss << c.op1_type << "_1 "; + if (!c.acc_type.empty()) { + ss << c.acc_type << "_0 "; + } + if (c.packed) { + ss << "PackedVectorFormat4x8BitKHR"; + } + ss << "\nOpReturn\nOpFunctionEnd\n\n"; + return ss.str(); +} + +using ValidateSpvKHRIntegerDotProduct = spvtest::ValidateBase; + +TEST_P(ValidateSpvKHRIntegerDotProduct, Valid) { + const auto& c = GetParam(); + const auto& assembly = AssemblyForCase(c); + CompileSuccessfully(assembly); + if (c.expected_error.empty()) { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); + } else { + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(c.expected_error)); + } +} + +// UDot +INSTANTIATE_TEST_SUITE_P( + Valid_UDot, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpUDotKHR", + "%uint", + "%v2uint", + "%v2uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpUDotKHR", + "%uint", + "%v3uint", + "%v3uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpUDotKHR", + "%uint", + "%v4uint", + "%v4uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpUDotKHR", + "%uchar", // match width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpUDotKHR", + "%uint", // wider width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpUDotKHR", + "%uchar", // match width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpUDotKHR", + "%uint", // wider width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpUDotKHR", + "%uchar", // matches packed component type + "%uint", + "%uint", + "", + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpUDotKHR", + "%uint", + "%uint", + "%uint", + "", + true, + ""})); + +// SDot result signed args signed signed +INSTANTIATE_TEST_SUITE_P( + Valid_SDot_signed_signed_signed, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotKHR", + "%int", + "%v2int", + "%v2int", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotKHR", + "%int", + "%v3int", + "%v3int", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotKHR", + "%int", + "%v4int", + "%v4int", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotKHR", + "%char", // match width + "%v4char", + "%v4char", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotKHR", + "%int", // wider width + "%v4char", + "%v4char", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotKHR", + "%char", // match width + "%v4char", + "%v4char", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotKHR", + "%int", // wider width + "%v4char", + "%v4char", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSDotKHR", + "%char", // matches packed component type + "%int", + "%int", + "", + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSDotKHR", + "%int", + "%int", + "%int", + "", + true, + ""})); + +// SDot result unsigned args signed unsigned +INSTANTIATE_TEST_SUITE_P( + Valid_SDot_unsigned_signed_unsigned, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotKHR", + "%uint", + "%v2int", + "%v2uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotKHR", + "%uint", + "%v3int", + "%v3uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotKHR", + "%uint", + "%v4int", + "%v4uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotKHR", + "%uchar", // match width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotKHR", + "%uint", // wider width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotKHR", + "%uchar", // match width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotKHR", + "%uint", // wider width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSDotKHR", + "%uchar", // matches packed component type + "%int", + "%uint", + "", + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSDotKHR", + "%uint", + "%int", + "%uint", + "", + true, + ""})); + +// SDot result signed args signed unsigned +INSTANTIATE_TEST_SUITE_P( + Valid_SDot_signed_signed_unsigned, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotKHR", + "%int", + "%v2int", + "%v2uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotKHR", + "%int", + "%v3int", + "%v3uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotKHR", + "%int", + "%v4int", + "%v4uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotKHR", + "%char", // match width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotKHR", + "%int", // wider width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotKHR", + "%char", // match width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotKHR", + "%int", // wider width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSDotKHR", + "%char", // matches packed component type + "%int", + "%uint", + "", + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSDotKHR", + "%int", + "%int", + "%uint", + "", + true, + ""})); + +// SUDot result signed args unsigned unsigned +INSTANTIATE_TEST_SUITE_P( + Valid_SUDot_signed_unsigned_unsigned, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotKHR", + "%int", + "%v2uint", + "%v2uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotKHR", + "%int", + "%v3uint", + "%v3uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotKHR", + "%int", + "%v4uint", + "%v4uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotKHR", + "%char", // match width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotKHR", + "%int", // wider width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotKHR", + "%char", // match width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotKHR", + "%int", // wider width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSUDotKHR", + "%char", // matches packed component type + "%uint", + "%uint", + "", + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSUDotKHR", + "%int", + "%uint", + "%uint", + "", + true, + ""})); + +// SUDot result signed args signed unsigned +INSTANTIATE_TEST_SUITE_P( + Valid_SUDot_signed_signed_unsigned, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotKHR", + "%int", + "%v2int", + "%v2uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotKHR", + "%int", + "%v3int", + "%v3uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotKHR", + "%int", + "%v4int", + "%v4uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotKHR", + "%char", // match width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotKHR", + "%int", // wider width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotKHR", + "%char", // match width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotKHR", + "%int", // wider width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSUDotKHR", + "%char", // matches packed component type + "%int", + "%uint", + "", + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSUDotKHR", + "%int", + "%int", + "%uint", + "", + true, + ""})); + +// SUDot result unsigned args unsigned unsigned +INSTANTIATE_TEST_SUITE_P( + Valid_SUDot_unsigned_unsigned_unsigned, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotKHR", + "%uint", + "%v2uint", + "%v2uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotKHR", + "%uint", + "%v3uint", + "%v3uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotKHR", + "%uint", + "%v4uint", + "%v4uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotKHR", + "%uchar", // match width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotKHR", + "%uint", // wider width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotKHR", + "%uchar", // match width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotKHR", + "%uint", // wider width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSUDotKHR", + "%uchar", // matches packed component type + "%uint", + "%uint", + "", + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSUDotKHR", + "%uint", + "%uint", + "%uint", + "", + true, + ""})); + +// UDotAccSat +INSTANTIATE_TEST_SUITE_P( + Valid_UDotAccSat, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpUDotAccSatKHR", + "%uint", + "%v2uint", + "%v2uint", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpUDotAccSatKHR", + "%uint", + "%v3uint", + "%v3uint", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpUDotAccSatKHR", + "%uint", + "%v4uint", + "%v4uint", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpUDotAccSatKHR", + "%uchar", // match width + "%v4uchar", + "%v4uchar", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpUDotAccSatKHR", + "%uint", // wider width + "%v4uchar", + "%v4uchar", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpUDotAccSatKHR", + "%uchar", // match width + "%v4uchar", + "%v4uchar", + "%uchar", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpUDotAccSatKHR", + "%uint", // wider width + "%v4uchar", + "%v4uchar", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpUDotAccSatKHR", + "%uchar", // matches packed component type + "%uint", + "%uint", + "%uchar", + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpUDotAccSatKHR", + "%uint", + "%uint", + "%uint", + "%uint", + true, + ""})); + +// SDotAccSat result signed args signed signed +INSTANTIATE_TEST_SUITE_P( + Valid_SDotAccSat_signed_signed_signed, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotAccSatKHR", + "%int", + "%v2int", + "%v2int", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotAccSatKHR", + "%int", + "%v3int", + "%v3int", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotAccSatKHR", + "%int", + "%v4int", + "%v4int", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotAccSatKHR", + "%char", // match width + "%v4char", + "%v4char", + "%char", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotAccSatKHR", + "%int", // wider width + "%v4char", + "%v4char", + "%int", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotAccSatKHR", + "%char", // match width + "%v4char", + "%v4char", + "%char", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotAccSatKHR", + "%int", // wider width + "%v4char", + "%v4char", + "%int", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSDotAccSatKHR", + "%char", // matches packed component type + "%int", + "%int", + "%char", // matches packed component type + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSDotAccSatKHR", + "%int", + "%int", + "%int", + "%int", + true, + ""})); + +// SDotAccSat result unsigned args signed unsigned +INSTANTIATE_TEST_SUITE_P( + Valid_SDotAccSat_unsigned_signed_unsigned, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotAccSatKHR", + "%uint", + "%v2int", + "%v2uint", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotAccSatKHR", + "%uint", + "%v3int", + "%v3uint", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotAccSatKHR", + "%uint", + "%v4int", + "%v4uint", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotAccSatKHR", + "%uchar", // match width + "%v4char", + "%v4uchar", + "%uchar", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotAccSatKHR", + "%uint", // wider width + "%v4char", + "%v4uchar", + "%uint", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotAccSatKHR", + "%uchar", // match width + "%v4char", + "%v4uchar", + "%uchar", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotAccSatKHR", + "%uint", // wider width + "%v4char", + "%v4uchar", + "%uint", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSDotAccSatKHR", + "%uchar", // matches packed component type + "%int", + "%uint", + "%uchar", // matches packed component type + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSDotAccSatKHR", + "%uint", + "%int", + "%uint", + "%uint", + true, + ""})); + +// SDotAccSat result signed args signed unsigned +INSTANTIATE_TEST_SUITE_P( + Valid_SDotAccSat_signed_signed_unsigned, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotAccSatKHR", + "%int", + "%v2int", + "%v2uint", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotAccSatKHR", + "%int", + "%v3int", + "%v3uint", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotAccSatKHR", + "%int", + "%v4int", + "%v4uint", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotAccSatKHR", + "%char", // match width + "%v4char", + "%v4uchar", + "%char", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotAccSatKHR", + "%int", // wider width + "%v4char", + "%v4uchar", + "%int", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotAccSatKHR", + "%char", // match width + "%v4char", + "%v4uchar", + "%char", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotAccSatKHR", + "%int", // wider width + "%v4char", + "%v4uchar", + "%int", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSDotAccSatKHR", + "%char", // matches packed component type + "%int", + "%uint", + "%char", // matches packed component type + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSDotAccSatKHR", + "%int", + "%int", + "%uint", + "%int", + true, + ""})); + +// SUDotAccSat result signed args unsigned unsigned +INSTANTIATE_TEST_SUITE_P( + Valid_SUDotAccSat_signed_unsigned_unsigned, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotAccSatKHR", + "%int", + "%v2uint", + "%v2uint", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotAccSatKHR", + "%int", + "%v3uint", + "%v3uint", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotAccSatKHR", + "%int", + "%v4uint", + "%v4uint", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotAccSatKHR", + "%char", // match width + "%v4uchar", + "%v4uchar", + "%char", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotAccSatKHR", + "%int", // wider width + "%v4uchar", + "%v4uchar", + "%int", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotAccSatKHR", + "%char", // match width + "%v4uchar", + "%v4uchar", + "%char", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotAccSatKHR", + "%int", // wider width + "%v4uchar", + "%v4uchar", + "%int", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSUDotAccSatKHR", + "%char", // matches packed component type + "%uint", + "%uint", + "%char", // matches packed component type + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSUDotAccSatKHR", + "%int", + "%uint", + "%uint", + "%int", + true, + ""})); + +// SUDotAccSat result signed args signed unsigned +INSTANTIATE_TEST_SUITE_P( + Valid_SUDotAccSat_signed_signed_unsigned, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotAccSatKHR", + "%int", + "%v2int", + "%v2uint", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotAccSatKHR", + "%int", + "%v3int", + "%v3uint", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotAccSatKHR", + "%int", + "%v4int", + "%v4uint", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotAccSatKHR", + "%char", // match width + "%v4char", + "%v4uchar", + "%char", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotAccSatKHR", + "%int", // wider width + "%v4char", + "%v4uchar", + "%int", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotAccSatKHR", + "%char", // match width + "%v4char", + "%v4uchar", + "%char", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotAccSatKHR", + "%int", // wider width + "%v4char", + "%v4uchar", + "%int", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSUDotAccSatKHR", + "%char", // matches packed component type + "%int", + "%uint", + "%char", // matches packed component type + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSUDotAccSatKHR", + "%int", + "%int", + "%uint", + "%int", + true, + ""})); + +// SUDotAccSat result unsigned args unsigned unsigned +INSTANTIATE_TEST_SUITE_P( + Valid_SUDotAccSat_unsigned_unsigned_unsigned, + ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotAccSatKHR", + "%uint", + "%v2uint", + "%v2uint", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotAccSatKHR", + "%uint", + "%v3uint", + "%v3uint", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotAccSatKHR", + "%uint", + "%v4uint", + "%v4uint", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotAccSatKHR", + "%uchar", // match width + "%v4uchar", + "%v4uchar", + "%uchar", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotAccSatKHR", + "%uint", // wider width + "%v4uchar", + "%v4uchar", + "%uint", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotAccSatKHR", + "%uchar", // match width + "%v4uchar", + "%v4uchar", + "%uchar", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotAccSatKHR", + "%uint", // wider width + "%v4uchar", + "%v4uchar", + "%uint", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSUDotAccSatKHR", + "%uchar", // matches packed component type + "%uint", + "%uint", + "%uchar", // matches packed component type + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSUDotAccSatKHR", + "%uint", + "%uint", + "%uint", + "%uint", + true, + ""})); + +using ValidateSpvKHRIntegerDotProductSimple = ::testing::Test; + +TEST(ValidateSpvKHRIntegerDotProductSimple, DISABLED_RequiresExtension) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, DISABLED_Invalid_ResultTooNarrow) { + // Test across all the instructions. + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_UDot_OperandTypesMatch) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_SDot_OperandTypesMatchExceptSignedness) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_SUDot_OperandTypesMatchExceptSignedness) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_UDotAccSat_OperandTypesMatch) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_SDotAccSat_OperandTypesMatchExceptSignedness) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_SUDotAccSat_OperandTypesMatchExceptSignedness) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_UDot_RequiresUnsigned) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_SUDot_RequiresUnsignedSecondArg) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_UDotAccSat_RequiresUnsigned) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_SUDotAccSat_RequiresUnsignedSecondArg) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_VectorOperandsDisallowPackedFormat) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_ScalarOperandsRequirePackedFormat) { + FAIL(); +} + +// TODO(dneto): Test valid cases with other scalar integer types +// TODO(dneto): Test valid cases of length-8 vectors +// TODO(dneto): Test valid cases of length-16 vectors + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/test/val/val_extension_spv_khr_linkonce_odr_test.cpp b/test/val/val_extension_spv_khr_linkonce_odr_test.cpp new file mode 100644 index 00000000..ac15558b --- /dev/null +++ b/test/val/val_extension_spv_khr_linkonce_odr_test.cpp @@ -0,0 +1,100 @@ +// Copyright (c) 2020 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests for OpExtension validator rules. + +#include +#include + +#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 ValidateSpvKHRLinkOnceODR = spvtest::ValidateBase; + +TEST_F(ValidateSpvKHRLinkOnceODR, Valid) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpExtension "SPV_KHR_linkonce_odr" + OpMemoryModel Physical32 OpenCL + OpDecorate %var LinkageAttributes "foobar" LinkOnceODR + + %uint = OpTypeInt 32 0 + %ptr = OpTypePointer CrossWorkgroup %uint + %var = OpVariable %ptr CrossWorkgroup + +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSpvKHRLinkOnceODR, RequiresExtension) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Physical32 OpenCL + OpDecorate %var LinkageAttributes "foobar" LinkOnceODR + + %uint = OpTypeInt 32 0 + %ptr = OpTypePointer CrossWorkgroup %uint + %var = OpVariable %ptr CrossWorkgroup +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("4th operand of Decorate: operand LinkOnceODR(2) requires one " + "of these extensions: SPV_KHR_linkonce_odr \n" + " OpDecorate %1 LinkageAttributes \"foobar\" LinkOnceODR\n")); +} + +TEST_F(ValidateSpvKHRLinkOnceODR, RequiresLinkageCapability) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpExtension "SPV_KHR_linkonce_odr" + OpMemoryModel Physical32 OpenCL + OpDecorate %var LinkageAttributes "foobar" LinkOnceODR + + %uint = OpTypeInt 32 0 + %ptr = OpTypePointer CrossWorkgroup %uint + %var = OpVariable %ptr CrossWorkgroup +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Operand 2 of Decorate requires one of these capabilities: Linkage \n" + " OpDecorate %1 LinkageAttributes \"foobar\" LinkOnceODR")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/test/val/val_extension_spv_khr_subgroup_rotate_test.cpp b/test/val/val_extension_spv_khr_subgroup_rotate_test.cpp new file mode 100644 index 00000000..4f156e8b --- /dev/null +++ b/test/val/val_extension_spv_khr_subgroup_rotate_test.cpp @@ -0,0 +1,352 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Values; +using ::testing::ValuesIn; + +struct Case { + std::vector caps; + bool shader; + std::string result_type; + std::string scope; + std::string delta; + std::string cluster_size; + std::string expected_error; // empty for no error. +}; + +inline std::ostream& operator<<(std::ostream& out, Case c) { + out << "\nSPV_KHR_subgroup_rotate Case{{"; + for (auto& cap : c.caps) { + out << cap; + } + out << "} "; + out << (c.shader ? "shader " : "kernel "); + out << c.result_type + " "; + out << c.scope + " "; + out << c.delta + " "; + out << c.cluster_size + " "; + out << "err'" << c.expected_error << "'"; + out << "}"; + return out; +} + +std::string AssemblyForCase(const Case& c) { + std::ostringstream ss; + + if (c.shader) { + ss << "OpCapability Shader\n"; + } else { + ss << "OpCapability Kernel\n"; + ss << "OpCapability Addresses\n"; + } + for (auto& cap : c.caps) { + ss << "OpCapability " << cap << "\n"; + } + ss << "OpExtension \"SPV_KHR_subgroup_rotate\"\n"; + + if (c.shader) { + ss << "OpMemoryModel Logical GLSL450\n"; + ss << "OpEntryPoint GLCompute %main \"main\"\n"; + } else { + ss << "OpMemoryModel Physical32 OpenCL\n"; + ss << "OpEntryPoint Kernel %main \"main\"\n"; + } + + ss << R"( + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + %u32 = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %ptr = OpTypePointer Function %u32 + )"; + + if (c.shader) { + ss << "%i32 = OpTypeInt 32 1\n"; + } + + ss << R"( + %u32_0 = OpConstant %u32 0 + %u32_1 = OpConstant %u32 1 + %u32_15 = OpConstant %u32 15 + %u32_16 = OpConstant %u32 16 + %u32_undef = OpUndef %u32 + %u32_spec_1 = OpSpecConstant %u32 1 + %u32_spec_16 = OpSpecConstant %u32 16 + %f32_1 = OpConstant %float 1.0 + %subgroup = OpConstant %u32 3 + %workgroup = OpConstant %u32 2 + %invalid_scope = OpConstant %u32 1 + %val = OpConstant %u32 42 + )"; + + if (c.shader) { + ss << "%i32_1 = OpConstant %i32 1\n"; + } + + ss << R"( + %main = OpFunction %void None %void_fn + %entry = OpLabel + )"; + + ss << "%unused = OpGroupNonUniformRotateKHR "; + ss << c.result_type + " "; + ss << c.scope; + ss << " %val "; + ss << c.delta; + ss << " " + c.cluster_size; + ss << "\n"; + + ss << R"( + OpReturn + OpFunctionEnd + )"; + + return ss.str(); +} + +using ValidateSpvKHRSubgroupRotate = spvtest::ValidateBase; + +TEST_P(ValidateSpvKHRSubgroupRotate, Base) { + const auto& c = GetParam(); + const auto& assembly = AssemblyForCase(c); + CompileSuccessfully(assembly); + if (c.expected_error.empty()) { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); + } else { + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(c.expected_error)); + } +} + +INSTANTIATE_TEST_SUITE_P( + Valid, ValidateSpvKHRSubgroupRotate, + ::testing::Values( + Case{ + {"GroupNonUniformRotateKHR"}, false, "%u32", "%subgroup", "%u32_1"}, + Case{{"GroupNonUniformRotateKHR"}, true, "%u32", "%subgroup", "%u32_1"}, + Case{{"GroupNonUniformRotateKHR"}, + false, + "%u32", + "%subgroup", + "%u32_1", + "%u32_16"}, + Case{{"GroupNonUniformRotateKHR"}, + true, + "%u32", + "%subgroup", + "%u32_1", + "%u32_16"}, + Case{{"GroupNonUniformRotateKHR"}, + false, + "%u32", + "%subgroup", + "%u32_spec_1", + "%u32_16"}, + Case{{"GroupNonUniformRotateKHR"}, + true, + "%u32", + "%subgroup", + "%u32_1", + "%u32_spec_16"}, + Case{{"GroupNonUniformRotateKHR"}, + false, + "%u32", + "%workgroup", + "%u32_1"}, + Case{ + {"GroupNonUniformRotateKHR"}, true, "%u32", "%workgroup", "%u32_1"}, + Case{{"GroupNonUniformRotateKHR"}, + false, + "%u32", + "%workgroup", + "%u32_spec_1"}, + Case{{"GroupNonUniformRotateKHR"}, + true, + "%u32", + "%workgroup", + "%u32_spec_1"})); + +INSTANTIATE_TEST_SUITE_P( + RequiresCapability, ValidateSpvKHRSubgroupRotate, + ::testing::Values(Case{{}, + false, + "%u32", + "%subgroup", + "%u32_1", + "", + "Opcode GroupNonUniformRotateKHR requires one of " + "these capabilities: " + "GroupNonUniformRotateKHR"}, + Case{{}, + true, + "%u32", + "%subgroup", + "%u32_1", + "", + "Opcode GroupNonUniformRotateKHR requires one of " + "these capabilities: " + "GroupNonUniformRotateKHR"})); + +TEST_F(ValidateSpvKHRSubgroupRotate, RequiresExtension) { + const std::string str = R"( + OpCapability GroupNonUniformRotateKHR +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "1st operand of Capability: operand GroupNonUniformRotateKHR(6026) " + "requires one of these extensions: SPV_KHR_subgroup_rotate")); +} + +INSTANTIATE_TEST_SUITE_P( + InvalidExecutionScope, ValidateSpvKHRSubgroupRotate, + ::testing::Values( + Case{{"GroupNonUniformRotateKHR"}, + false, + "%u32", + "%invalid_scope", + "%u32_1", + "", + "Execution scope is limited to Subgroup or Workgroup"}, + Case{{"GroupNonUniformRotateKHR"}, + true, + "%u32", + "%invalid_scope", + "%u32_1", + "", + "Execution scope is limited to Subgroup or Workgroup"})); + +INSTANTIATE_TEST_SUITE_P( + InvalidResultType, ValidateSpvKHRSubgroupRotate, + ::testing::Values(Case{{"GroupNonUniformRotateKHR"}, + false, + "%ptr", + "%subgroup", + "%u32_1", + "", + "Expected Result Type to be a scalar or vector of " + "floating-point, integer or boolean type"}, + Case{{"GroupNonUniformRotateKHR"}, + true, + "%ptr", + "%subgroup", + "%u32_1", + "", + "Expected Result Type to be a scalar or vector of " + "floating-point, integer or boolean type"})); + +INSTANTIATE_TEST_SUITE_P( + MismatchedResultAndValueTypes, ValidateSpvKHRSubgroupRotate, + ::testing::Values( + Case{{"GroupNonUniformRotateKHR"}, + false, + "%float", + "%subgroup", + "%u32_1", + "", + "Result Type must be the same as the type of Value"}, + Case{{"GroupNonUniformRotateKHR"}, + true, + "%float", + "%subgroup", + "%u32_1", + "", + "Result Type must be the same as the type of Value"})); + +INSTANTIATE_TEST_SUITE_P( + InvalidDelta, ValidateSpvKHRSubgroupRotate, + ::testing::Values(Case{{"GroupNonUniformRotateKHR"}, + false, + "%u32", + "%subgroup", + "%f32_1", + "", + "Delta must be a scalar of integer type, whose " + "Signedness operand is 0"}, + Case{{"GroupNonUniformRotateKHR"}, + true, + "%u32", + "%subgroup", + "%f32_1", + "", + "Delta must be a scalar of integer type, whose " + "Signedness operand is 0"}, + Case{{"GroupNonUniformRotateKHR"}, + true, + "%u32", + "%subgroup", + "%i32_1", + "", + "Delta must be a scalar of integer type, whose " + "Signedness operand is 0"})); + +INSTANTIATE_TEST_SUITE_P( + InvalidClusterSize, ValidateSpvKHRSubgroupRotate, + ::testing::Values( + Case{{"GroupNonUniformRotateKHR"}, + false, + "%u32", + "%subgroup", + "%u32_1", + "%f32_1", + "ClusterSize must be a scalar of integer type, whose Signedness " + "operand is 0"}, + Case{{"GroupNonUniformRotateKHR"}, + true, + "%u32", + "%subgroup", + "%u32_1", + "%i32_1", + "ClusterSize must be a scalar of integer type, whose Signedness " + "operand is 0"}, + Case{{"GroupNonUniformRotateKHR"}, + true, + "%u32", + "%subgroup", + "%u32_1", + "%u32_0", + "Behavior is undefined unless ClusterSize is at least 1 and a " + "power of 2"}, + Case{{"GroupNonUniformRotateKHR"}, + true, + "%u32", + "%subgroup", + "%u32_1", + "%u32_15", + "Behavior is undefined unless ClusterSize is at least 1 and a " + "power of 2"}, + Case{{"GroupNonUniformRotateKHR"}, + true, + "%u32", + "%subgroup", + "%u32_1", + "%u32_undef", + "ClusterSize must come from a constant instruction"})); + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/test/val/val_extension_spv_khr_subgroup_uniform_control_flow_test.cpp b/test/val/val_extension_spv_khr_subgroup_uniform_control_flow_test.cpp new file mode 100644 index 00000000..f528cb9e --- /dev/null +++ b/test/val/val_extension_spv_khr_subgroup_uniform_control_flow_test.cpp @@ -0,0 +1,110 @@ +// Copyright (c) 2021 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests for OpExtension validator rules. + +#include +#include + +#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 ValidateSpvKHRSubgroupUniformControlFlow = spvtest::ValidateBase; + +TEST_F(ValidateSpvKHRSubgroupUniformControlFlow, Valid) { + const std::string str = R"( + OpCapability Shader + OpExtension "SPV_KHR_subgroup_uniform_control_flow" + OpMemoryModel Logical Simple + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpExecutionMode %main SubgroupUniformControlFlowKHR + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + + %main = OpFunction %void None %void_fn + %entry = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSpvKHRSubgroupUniformControlFlow, RequiresExtension) { + const std::string str = R"( + OpCapability Shader + OpMemoryModel Logical Simple + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpExecutionMode %main SubgroupUniformControlFlowKHR + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + + %main = OpFunction %void None %void_fn + %entry = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("2nd operand of ExecutionMode: operand " + "SubgroupUniformControlFlowKHR(4421) " + "requires one of these extensions: " + "SPV_KHR_subgroup_uniform_control_flow")); +} + +TEST_F(ValidateSpvKHRSubgroupUniformControlFlow, RequiresShaderCapability) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpExtension "SPV_KHR_subgroup_uniform_control_flow" + OpMemoryModel Physical32 OpenCL + OpEntryPoint Kernel %main "main" + OpExecutionMode %main SubgroupUniformControlFlowKHR + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + + %main = OpFunction %void None %void_fn + %entry = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand 2 of ExecutionMode requires one of these " + "capabilities: Shader")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/test/val/val_extension_spv_khr_terminate_invocation_test.cpp b/test/val/val_extension_spv_khr_terminate_invocation_test.cpp new file mode 100644 index 00000000..8d924149 --- /dev/null +++ b/test/val/val_extension_spv_khr_terminate_invocation_test.cpp @@ -0,0 +1,171 @@ +// 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 +#include + +#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; + +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, RequiresExtensionPre1p6) { + 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 SPIR-V version 1.6 at minimum or one " + "of the following extensions: SPV_KHR_terminate_invocation")); +} + +TEST_F(ValidateSpvKHRTerminateInvocation, RequiresNoExtensionPost1p6) { + 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(), SPV_ENV_UNIVERSAL_1_6); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); +} + +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/test/val/val_fixtures.h b/test/val/val_fixtures.h index acbe0e57..98d8d32a 100644 --- a/test/val/val_fixtures.h +++ b/test/val/val_fixtures.h @@ -40,8 +40,10 @@ class ValidateBase : public ::testing::Test, // Assembles the given SPIR-V text, checks that it fails to assemble, // and returns resulting diagnostic. No internal state is updated. + // Setting the desired_result to SPV_SUCCESS is used to allow all results std::string CompileFailure(std::string code, - spv_target_env env = SPV_ENV_UNIVERSAL_1_0); + spv_target_env env = SPV_ENV_UNIVERSAL_1_0, + spv_result_t desired_result = SPV_SUCCESS); // Checks that 'code' is valid SPIR-V text representation and stores the // binary version for further method calls. @@ -108,11 +110,17 @@ void ValidateBase::TearDown() { template std::string ValidateBase::CompileFailure(std::string code, - spv_target_env env) { + spv_target_env env, + spv_result_t desired_result) { spv_diagnostic diagnostic = nullptr; - EXPECT_NE(SPV_SUCCESS, - spvTextToBinary(ScopedContext(env).context, code.c_str(), - code.size(), &binary_, &diagnostic)); + spv_result_t actual_result = + spvTextToBinary(ScopedContext(env).context, code.c_str(), code.size(), + &binary_, &diagnostic); + EXPECT_NE(SPV_SUCCESS, actual_result); + // optional check for exact result + if (desired_result != SPV_SUCCESS) { + EXPECT_EQ(actual_result, desired_result); + } std::string result(diagnostic->error); spvDiagnosticDestroy(diagnostic); return result; diff --git a/test/val/val_function_test.cpp b/test/val/val_function_test.cpp index af0199a6..e7d5cd7e 100644 --- a/test/val/val_function_test.cpp +++ b/test/val/val_function_test.cpp @@ -181,13 +181,14 @@ TEST_P(ValidateFunctionCall, VariableNoVariablePointers) { } else { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); if (storage_class == "StorageBuffer") { - EXPECT_THAT(getDiagnosticString(), - HasSubstr("StorageBuffer pointer operand 1[%var] requires a " - "variable pointers capability")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("StorageBuffer pointer operand '1[%var]' requires a " + "variable pointers capability")); } else { EXPECT_THAT( getDiagnosticString(), - HasSubstr("Invalid storage class for pointer operand 1[%var]")); + HasSubstr("Invalid storage class for pointer operand '1[%var]'")); } } } @@ -211,8 +212,9 @@ TEST_P(ValidateFunctionCall, VariableVariablePointersStorageClass) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } else { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Invalid storage class for pointer operand 1[%var]")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid storage class for pointer operand '1[%var]'")); } } @@ -235,8 +237,9 @@ TEST_P(ValidateFunctionCall, VariableVariablePointers) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } else { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Invalid storage class for pointer operand 1[%var]")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid storage class for pointer operand '1[%var]'")); } } @@ -258,11 +261,12 @@ TEST_P(ValidateFunctionCall, ParameterNoVariablePointers) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); if (storage_class == "StorageBuffer") { EXPECT_THAT(getDiagnosticString(), - HasSubstr("StorageBuffer pointer operand 1[%p] requires a " + HasSubstr("StorageBuffer pointer operand '1[%p]' requires a " "variable pointers capability")); } else { - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Invalid storage class for pointer operand 1[%p]")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid storage class for pointer operand '1[%p]'")); } } } @@ -287,7 +291,7 @@ TEST_P(ValidateFunctionCall, ParameterVariablePointersStorageBuffer) { } else { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Invalid storage class for pointer operand 1[%p]")); + HasSubstr("Invalid storage class for pointer operand '1[%p]'")); } } @@ -311,7 +315,7 @@ TEST_P(ValidateFunctionCall, ParameterVariablePointers) { } else { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Invalid storage class for pointer operand 1[%p]")); + HasSubstr("Invalid storage class for pointer operand '1[%p]'")); } } @@ -334,16 +338,17 @@ TEST_P(ValidateFunctionCall, NonMemoryObjectDeclarationNoVariablePointers) { EXPECT_THAT( getDiagnosticString(), HasSubstr( - "Pointer operand 2[%gep] must be a memory object declaration")); + "Pointer operand '2[%gep]' must be a memory object declaration")); } else { if (storage_class == "StorageBuffer") { - EXPECT_THAT(getDiagnosticString(), - HasSubstr("StorageBuffer pointer operand 2[%gep] requires a " - "variable pointers capability")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("StorageBuffer pointer operand '2[%gep]' requires a " + "variable pointers capability")); } else if (storage_class != "UniformConstant") { EXPECT_THAT( getDiagnosticString(), - HasSubstr("Invalid storage class for pointer operand 2[%gep]")); + HasSubstr("Invalid storage class for pointer operand '2[%gep]'")); } } } @@ -373,11 +378,11 @@ TEST_P(ValidateFunctionCall, EXPECT_THAT( getDiagnosticString(), HasSubstr( - "Pointer operand 2[%gep] must be a memory object declaration")); + "Pointer operand '2[%gep]' must be a memory object declaration")); } else { EXPECT_THAT( getDiagnosticString(), - HasSubstr("Invalid storage class for pointer operand 2[%gep]")); + HasSubstr("Invalid storage class for pointer operand '2[%gep]'")); } } } @@ -407,11 +412,11 @@ TEST_P(ValidateFunctionCall, NonMemoryObjectDeclarationVariablePointers) { EXPECT_THAT( getDiagnosticString(), HasSubstr( - "Pointer operand 2[%gep] must be a memory object declaration")); + "Pointer operand '2[%gep]' must be a memory object declaration")); } else { EXPECT_THAT( getDiagnosticString(), - HasSubstr("Invalid storage class for pointer operand 2[%gep]")); + HasSubstr("Invalid storage class for pointer operand '2[%gep]'")); } } } diff --git a/test/val/val_id_test.cpp b/test/val/val_id_test.cpp index dd4c952c..dd040b33 100644 --- a/test/val/val_id_test.cpp +++ b/test/val/val_id_test.cpp @@ -35,7 +35,15 @@ using spvtest::ScopedContext; using ::testing::HasSubstr; using ::testing::ValuesIn; -using ValidateIdWithMessage = spvtest::ValidateBase; +class ValidateIdWithMessage : public spvtest::ValidateBase { + public: + ValidateIdWithMessage() { + const bool use_friendly_names = GetParam(); + spvValidatorOptionsSetFriendlyNames(options_, use_friendly_names); + } + + std::string make_message(const char* msg); +}; std::string kOpCapabilitySetupWithoutVector16 = R"( OpCapability Shader @@ -177,9 +185,61 @@ std::string BranchConditionalTail = R"( OpFunctionEnd )"; +// Transform an expected validation message to either use friendly names (as +// provided in the message) or replace the friendly names by the corresponding +// id. The same flag used to configure the validator to output friendly names +// or not is used here. +std::string ValidateIdWithMessage::make_message(const char* msg) { + const bool use_friendly_names = GetParam(); + if (use_friendly_names) { + return msg; + } + + std::string message(msg); + std::ostringstream result; + + size_t next = 0; + while (next < message.size()) { + // Parse 'num[%name]' + size_t open_quote = message.find('\'', next); + + // Copy up to the first quote + result.write(msg + next, open_quote - next); + if (open_quote == std::string::npos) { + break; + } + // Handle apostrophes + if (!isdigit(message[open_quote + 1])) { + result << '\''; + next = open_quote + 1; + continue; + } + + size_t open_bracket = message.find('[', open_quote + 1); + assert(open_bracket != std::string::npos); + + size_t close_bracket = message.find(']', open_bracket + 1); + assert(close_bracket != std::string::npos); + + size_t close_quote = close_bracket + 1; + assert(close_quote < message.size() && message[close_quote] == '\''); + + // Change to 'num[%num]' because friendly names are not being used. + result.write(msg + open_quote, open_bracket - open_quote + 1); + result << '%'; + result.write(msg + open_quote + 1, open_bracket - open_quote - 1); + result << "]'"; + + // Continue to the next id, or end of string. + next = close_quote + 1; + } + + return result.str(); +} + // TODO: OpUndef -TEST_F(ValidateIdWithMessage, OpName) { +TEST_P(ValidateIdWithMessage, OpName) { std::string spirv = kGLSL450MemoryModel + R"( OpName %2 "name" %1 = OpTypeInt 32 0 @@ -189,7 +249,7 @@ TEST_F(ValidateIdWithMessage, OpName) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpMemberNameGood) { +TEST_P(ValidateIdWithMessage, OpMemberNameGood) { std::string spirv = kGLSL450MemoryModel + R"( OpMemberName %2 0 "foo" %1 = OpTypeInt 32 0 @@ -197,30 +257,30 @@ TEST_F(ValidateIdWithMessage, OpMemberNameGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpMemberNameTypeBad) { +TEST_P(ValidateIdWithMessage, OpMemberNameTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( OpMemberName %1 0 "foo" %1 = OpTypeInt 32 0)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpMemberName Type '1[%uint]' is not a struct type.")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "OpMemberName Type '1[%uint]' is not a struct type."))); } -TEST_F(ValidateIdWithMessage, OpMemberNameMemberBad) { +TEST_P(ValidateIdWithMessage, OpMemberNameMemberBad) { std::string spirv = kGLSL450MemoryModel + R"( OpMemberName %1 1 "foo" %2 = OpTypeInt 32 0 %1 = OpTypeStruct %2)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpMemberName Member '1[%_struct_1]' index is larger " - "than Type '1[%_struct_1]'s member count.")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "OpMemberName Member '1[%_struct_1]' index is larger " + "than Type '1[%_struct_1]'s member count."))); } -TEST_F(ValidateIdWithMessage, OpLineGood) { +TEST_P(ValidateIdWithMessage, OpLineGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpString "/path/to/source.file" OpLine %1 0 0 @@ -231,7 +291,7 @@ TEST_F(ValidateIdWithMessage, OpLineGood) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpLineFileBad) { +TEST_P(ValidateIdWithMessage, OpLineFileBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 OpLine %1 0 0 @@ -239,10 +299,11 @@ TEST_F(ValidateIdWithMessage, OpLineFileBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpLine Target '1[%uint]' is not an OpString.")); + HasSubstr(make_message( + "OpLine Target '1[%uint]' is not an OpString."))); } -TEST_F(ValidateIdWithMessage, OpDecorateGood) { +TEST_P(ValidateIdWithMessage, OpDecorateGood) { std::string spirv = kGLSL450MemoryModel + R"( OpDecorate %2 GLSLShared %1 = OpTypeInt 64 0 @@ -250,16 +311,17 @@ TEST_F(ValidateIdWithMessage, OpDecorateGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpDecorateBad) { +TEST_P(ValidateIdWithMessage, OpDecorateBad) { std::string spirv = kGLSL450MemoryModel + R"( OpDecorate %1 GLSLShared)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("forward referenced IDs have not been defined")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("forward referenced IDs have not been defined"))); } -TEST_F(ValidateIdWithMessage, OpMemberDecorateGood) { +TEST_P(ValidateIdWithMessage, OpMemberDecorateGood) { std::string spirv = kGLSL450MemoryModel + R"( OpMemberDecorate %2 0 RelaxedPrecision %1 = OpTypeInt 32 0 @@ -267,17 +329,18 @@ TEST_F(ValidateIdWithMessage, OpMemberDecorateGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpMemberDecorateBad) { +TEST_P(ValidateIdWithMessage, OpMemberDecorateBad) { std::string spirv = kGLSL450MemoryModel + R"( OpMemberDecorate %1 0 RelaxedPrecision %1 = OpTypeInt 32 0)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpMemberDecorate Structure type '1[%uint]' is " - "not a struct type.")); + HasSubstr(make_message( + "OpMemberDecorate Structure type '1[%uint]' is " + "not a struct type."))); } -TEST_F(ValidateIdWithMessage, OpMemberDecorateMemberBad) { +TEST_P(ValidateIdWithMessage, OpMemberDecorateMemberBad) { std::string spirv = kGLSL450MemoryModel + R"( OpMemberDecorate %1 3 RelaxedPrecision %int = OpTypeInt 32 0 @@ -285,12 +348,13 @@ TEST_F(ValidateIdWithMessage, OpMemberDecorateMemberBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Index 3 provided in OpMemberDecorate for struct " - "1[%_struct_1] is out of bounds. The structure has 2 " - "members. Largest valid index is 1.")); + HasSubstr(make_message( + "Index 3 provided in OpMemberDecorate for struct " + "'1[%_struct_1]' is out of bounds. The structure has 2 " + "members. Largest valid index is 1."))); } -TEST_F(ValidateIdWithMessage, OpGroupDecorateGood) { +TEST_P(ValidateIdWithMessage, OpGroupDecorateGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpDecorationGroup OpDecorate %1 RelaxedPrecision @@ -302,7 +366,7 @@ TEST_F(ValidateIdWithMessage, OpGroupDecorateGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpDecorationGroupBad) { +TEST_P(ValidateIdWithMessage, OpDecorationGroupBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpDecorationGroup OpDecorate %1 RelaxedPrecision @@ -312,11 +376,12 @@ TEST_F(ValidateIdWithMessage, OpDecorationGroupBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Result id of OpDecorationGroup can only " - "be targeted by OpName, OpGroupDecorate, " - "OpDecorate, OpDecorateId, and OpGroupMemberDecorate")); + HasSubstr(make_message( + "Result id of OpDecorationGroup can only " + "be targeted by OpName, OpGroupDecorate, " + "OpDecorate, OpDecorateId, and OpGroupMemberDecorate"))); } -TEST_F(ValidateIdWithMessage, OpGroupDecorateDecorationGroupBad) { +TEST_P(ValidateIdWithMessage, OpGroupDecorateDecorationGroupBad) { std::string spirv = R"( OpCapability Shader OpCapability Linkage @@ -328,10 +393,11 @@ TEST_F(ValidateIdWithMessage, OpGroupDecorateDecorationGroupBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpGroupDecorate Decoration group '1[%1]' is not " - "a decoration group.")); + HasSubstr(make_message( + "OpGroupDecorate Decoration group '1[%1]' is not " + "a decoration group."))); } -TEST_F(ValidateIdWithMessage, OpGroupDecorateTargetBad) { +TEST_P(ValidateIdWithMessage, OpGroupDecorateTargetBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpDecorationGroup OpDecorate %1 RelaxedPrecision @@ -340,10 +406,11 @@ TEST_F(ValidateIdWithMessage, OpGroupDecorateTargetBad) { %2 = OpTypeInt 32 0)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("forward referenced IDs have not been defined")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("forward referenced IDs have not been defined"))); } -TEST_F(ValidateIdWithMessage, OpGroupMemberDecorateDecorationGroupBad) { +TEST_P(ValidateIdWithMessage, OpGroupMemberDecorateDecorationGroupBad) { std::string spirv = R"( OpCapability Shader OpCapability Linkage @@ -354,10 +421,11 @@ TEST_F(ValidateIdWithMessage, OpGroupMemberDecorateDecorationGroupBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpGroupMemberDecorate Decoration group '1[%1]' " - "is not a decoration group.")); + HasSubstr(make_message( + "OpGroupMemberDecorate Decoration group '1[%1]' " + "is not a decoration group."))); } -TEST_F(ValidateIdWithMessage, OpGroupMemberDecorateIdNotStructBad) { +TEST_P(ValidateIdWithMessage, OpGroupMemberDecorateIdNotStructBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpDecorationGroup OpGroupMemberDecorate %1 %2 0 @@ -365,10 +433,11 @@ TEST_F(ValidateIdWithMessage, OpGroupMemberDecorateIdNotStructBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpGroupMemberDecorate Structure type '2[%uint]' " - "is not a struct type.")); + HasSubstr(make_message( + "OpGroupMemberDecorate Structure type '2[%uint]' " + "is not a struct type."))); } -TEST_F(ValidateIdWithMessage, OpGroupMemberDecorateIndexOutOfBoundBad) { +TEST_P(ValidateIdWithMessage, OpGroupMemberDecorateIndexOutOfBoundBad) { std::string spirv = kGLSL450MemoryModel + R"( OpDecorate %1 Offset 0 %1 = OpDecorationGroup @@ -379,14 +448,15 @@ TEST_F(ValidateIdWithMessage, OpGroupMemberDecorateIndexOutOfBoundBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Index 3 provided in OpGroupMemberDecorate for struct " - " 2[%_struct_2] is out of bounds. The structure " - "has 3 members. Largest valid index is 2.")); + HasSubstr(make_message( + "Index 3 provided in OpGroupMemberDecorate for struct " + " '2[%_struct_2]' is out of bounds. The structure " + "has 3 members. Largest valid index is 2."))); } // TODO: OpExtInst -TEST_F(ValidateIdWithMessage, OpEntryPointGood) { +TEST_P(ValidateIdWithMessage, OpEntryPointGood) { std::string spirv = kGLSL450MemoryModel + R"( OpEntryPoint GLCompute %3 "" %1 = OpTypeVoid @@ -399,17 +469,18 @@ TEST_F(ValidateIdWithMessage, OpEntryPointGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpEntryPointFunctionBad) { +TEST_P(ValidateIdWithMessage, OpEntryPointFunctionBad) { std::string spirv = kGLSL450MemoryModel + R"( OpEntryPoint GLCompute %1 "" %1 = OpTypeVoid)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpEntryPoint Entry Point '1[%void]' is not a " - "function.")); + HasSubstr(make_message( + "OpEntryPoint Entry Point '1[%void]' is not a " + "function."))); } -TEST_F(ValidateIdWithMessage, OpEntryPointParameterCountBad) { +TEST_P(ValidateIdWithMessage, OpEntryPointParameterCountBad) { std::string spirv = kGLSL450MemoryModel + R"( OpEntryPoint GLCompute %1 "" %2 = OpTypeVoid @@ -420,11 +491,12 @@ TEST_F(ValidateIdWithMessage, OpEntryPointParameterCountBad) { OpFunctionEnd)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpEntryPoint Entry Point '1[%1]'s function " - "parameter count is not zero")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("OpEntryPoint Entry Point '1[%1]'s function " + "parameter count is not zero"))); } -TEST_F(ValidateIdWithMessage, OpEntryPointReturnTypeBad) { +TEST_P(ValidateIdWithMessage, OpEntryPointReturnTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( OpEntryPoint GLCompute %1 "" %2 = OpTypeInt 32 0 @@ -436,11 +508,12 @@ TEST_F(ValidateIdWithMessage, OpEntryPointReturnTypeBad) { OpFunctionEnd)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpEntryPoint Entry Point '1[%1]'s function " - "return type is not void.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("OpEntryPoint Entry Point '1[%1]'s function " + "return type is not void."))); } -TEST_F(ValidateIdWithMessage, OpEntryPointParameterCountBadInVulkan) { +TEST_P(ValidateIdWithMessage, OpEntryPointParameterCountBadInVulkan) { std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 @@ -455,11 +528,12 @@ TEST_F(ValidateIdWithMessage, OpEntryPointParameterCountBadInVulkan) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-StandaloneSpirv-None-04633")); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpEntryPoint Entry Point '1[%1]'s function " - "parameter count is not zero")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("OpEntryPoint Entry Point '1[%1]'s function " + "parameter count is not zero"))); } -TEST_F(ValidateIdWithMessage, OpEntryPointReturnTypeBadInVulkan) { +TEST_P(ValidateIdWithMessage, OpEntryPointReturnTypeBadInVulkan) { std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 @@ -475,12 +549,13 @@ TEST_F(ValidateIdWithMessage, OpEntryPointReturnTypeBadInVulkan) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-StandaloneSpirv-None-04633")); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpEntryPoint Entry Point '1[%1]'s function " - "return type is not void.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("OpEntryPoint Entry Point '1[%1]'s function " + "return type is not void."))); } -TEST_F(ValidateIdWithMessage, OpEntryPointInterfaceIsNotVariableTypeBad) { +TEST_P(ValidateIdWithMessage, OpEntryPointInterfaceIsNotVariableTypeBad) { std::string spirv = R"( OpCapability Shader OpCapability Geometry @@ -502,11 +577,12 @@ TEST_F(ValidateIdWithMessage, OpEntryPointInterfaceIsNotVariableTypeBad) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Interfaces passed to OpEntryPoint must be of type " - "OpTypeVariable. Found OpTypePointer.")); + HasSubstr(make_message( + "Interfaces passed to OpEntryPoint must be of type " + "OpTypeVariable. Found OpTypePointer."))); } -TEST_F(ValidateIdWithMessage, OpEntryPointInterfaceStorageClassBad) { +TEST_P(ValidateIdWithMessage, OpEntryPointInterfaceStorageClassBad) { std::string spirv = R"( OpCapability Shader OpCapability Geometry @@ -529,12 +605,13 @@ TEST_F(ValidateIdWithMessage, OpEntryPointInterfaceStorageClassBad) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpEntryPoint interfaces must be OpVariables with " - "Storage Class of Input(1) or Output(3). Found Storage " - "Class 2 for Entry Point id 1.")); + HasSubstr(make_message( + "OpEntryPoint interfaces must be OpVariables with " + "Storage Class of Input(1) or Output(3). Found Storage " + "Class 2 for Entry Point id 1."))); } -TEST_F(ValidateIdWithMessage, OpExecutionModeGood) { +TEST_P(ValidateIdWithMessage, OpExecutionModeGood) { std::string spirv = kGLSL450MemoryModel + R"( OpEntryPoint GLCompute %3 "" OpExecutionMode %3 LocalSize 1 1 1 @@ -548,7 +625,7 @@ TEST_F(ValidateIdWithMessage, OpExecutionModeGood) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpExecutionModeEntryPointMissing) { +TEST_P(ValidateIdWithMessage, OpExecutionModeEntryPointMissing) { std::string spirv = kGLSL450MemoryModel + R"( OpExecutionMode %3 LocalSize 1 1 1 %1 = OpTypeVoid @@ -560,11 +637,12 @@ TEST_F(ValidateIdWithMessage, OpExecutionModeEntryPointMissing) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpExecutionMode Entry Point '1[%1]' is not the " - "Entry Point operand of an OpEntryPoint.")); + HasSubstr(make_message( + "OpExecutionMode Entry Point '1[%1]' is not the " + "Entry Point operand of an OpEntryPoint."))); } -TEST_F(ValidateIdWithMessage, OpExecutionModeEntryPointBad) { +TEST_P(ValidateIdWithMessage, OpExecutionModeEntryPointBad) { std::string spirv = kGLSL450MemoryModel + R"( OpEntryPoint GLCompute %3 "" %a OpExecutionMode %a LocalSize 1 1 1 @@ -579,11 +657,12 @@ TEST_F(ValidateIdWithMessage, OpExecutionModeEntryPointBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpExecutionMode Entry Point '2[%2]' is not the " - "Entry Point operand of an OpEntryPoint.")); + HasSubstr(make_message( + "OpExecutionMode Entry Point '2[%2]' is not the " + "Entry Point operand of an OpEntryPoint."))); } -TEST_F(ValidateIdWithMessage, OpTypeVectorFloat) { +TEST_P(ValidateIdWithMessage, OpTypeVectorFloat) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 4)"; @@ -591,7 +670,7 @@ TEST_F(ValidateIdWithMessage, OpTypeVectorFloat) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpTypeVectorInt) { +TEST_P(ValidateIdWithMessage, OpTypeVectorInt) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpTypeVector %1 4)"; @@ -599,7 +678,7 @@ TEST_F(ValidateIdWithMessage, OpTypeVectorInt) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpTypeVectorUInt) { +TEST_P(ValidateIdWithMessage, OpTypeVectorUInt) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 64 0 %2 = OpTypeVector %1 4)"; @@ -607,7 +686,7 @@ TEST_F(ValidateIdWithMessage, OpTypeVectorUInt) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpTypeVectorBool) { +TEST_P(ValidateIdWithMessage, OpTypeVectorBool) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeBool %2 = OpTypeVector %1 4)"; @@ -615,20 +694,20 @@ TEST_F(ValidateIdWithMessage, OpTypeVectorBool) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpTypeVectorComponentTypeBad) { +TEST_P(ValidateIdWithMessage, OpTypeVectorComponentTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypePointer UniformConstant %1 %3 = OpTypeVector %2 4)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpTypeVector Component Type " - "'2[%_ptr_UniformConstant_float]' is not a scalar type.")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "OpTypeVector Component Type " + "'2[%_ptr_UniformConstant_float]' is not a scalar type."))); } -TEST_F(ValidateIdWithMessage, OpTypeVectorColumnCountLessThanTwoBad) { +TEST_P(ValidateIdWithMessage, OpTypeVectorColumnCountLessThanTwoBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 1)"; @@ -636,11 +715,12 @@ TEST_F(ValidateIdWithMessage, OpTypeVectorColumnCountLessThanTwoBad) { EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("Illegal number of components (1) for TypeVector\n %v1float = " - "OpTypeVector %float 1\n")); + HasSubstr(make_message( + "Illegal number of components (1) for TypeVector\n %v1float = " + "OpTypeVector %float 1\n"))); } -TEST_F(ValidateIdWithMessage, OpTypeVectorColumnCountGreaterThanFourBad) { +TEST_P(ValidateIdWithMessage, OpTypeVectorColumnCountGreaterThanFourBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 5)"; @@ -648,24 +728,25 @@ TEST_F(ValidateIdWithMessage, OpTypeVectorColumnCountGreaterThanFourBad) { EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("Illegal number of components (5) for TypeVector\n %v5float = " - "OpTypeVector %float 5\n")); + HasSubstr(make_message( + "Illegal number of components (5) for TypeVector\n %v5float = " + "OpTypeVector %float 5\n"))); } -TEST_F(ValidateIdWithMessage, OpTypeVectorColumnCountEightWithoutVector16Bad) { +TEST_P(ValidateIdWithMessage, OpTypeVectorColumnCountEightWithoutVector16Bad) { std::string spirv = kGLSL450MemoryModelWithoutVector16 + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 8)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Having 8 components for TypeVector requires the Vector16 " - "capability\n %v8float = OpTypeVector %float 8\n")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "Having 8 components for TypeVector requires the Vector16 " + "capability\n %v8float = OpTypeVector %float 8\n"))); } -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, OpTypeVectorColumnCountSixteenWithoutVector16Bad) { std::string spirv = kGLSL450MemoryModelWithoutVector16 + R"( %1 = OpTypeFloat 32 @@ -673,13 +754,13 @@ TEST_F(ValidateIdWithMessage, CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Having 16 components for TypeVector requires the Vector16 " - "capability\n %v16float = OpTypeVector %float 16\n")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "Having 16 components for TypeVector requires the Vector16 " + "capability\n %v16float = OpTypeVector %float 16\n"))); } -TEST_F(ValidateIdWithMessage, OpTypeVectorColumnCountOfEightWithVector16Good) { +TEST_P(ValidateIdWithMessage, OpTypeVectorColumnCountOfEightWithVector16Good) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 8)"; @@ -687,7 +768,7 @@ TEST_F(ValidateIdWithMessage, OpTypeVectorColumnCountOfEightWithVector16Good) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, OpTypeVectorColumnCountOfSixteenWithVector16Good) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 @@ -696,7 +777,7 @@ TEST_F(ValidateIdWithMessage, EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpTypeMatrixGood) { +TEST_P(ValidateIdWithMessage, OpTypeMatrixGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 2 @@ -705,32 +786,32 @@ TEST_F(ValidateIdWithMessage, OpTypeMatrixGood) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpTypeMatrixColumnTypeNonVectorBad) { +TEST_P(ValidateIdWithMessage, OpTypeMatrixColumnTypeNonVectorBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeMatrix %1 3)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("olumns in a matrix must be of type vector.\n %mat3float = " - "OpTypeMatrix %float 3\n")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "olumns in a matrix must be of type vector.\n %mat3float = " + "OpTypeMatrix %float 3\n"))); } -TEST_F(ValidateIdWithMessage, OpTypeMatrixVectorTypeNonFloatBad) { +TEST_P(ValidateIdWithMessage, OpTypeMatrixVectorTypeNonFloatBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 16 0 %2 = OpTypeVector %1 2 %3 = OpTypeMatrix %2 2)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Matrix types can only be parameterized with floating-point " - "types.\n %mat2v2ushort = OpTypeMatrix %v2ushort 2\n")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "Matrix types can only be parameterized with floating-point " + "types.\n %mat2v2ushort = OpTypeMatrix %v2ushort 2\n"))); } -TEST_F(ValidateIdWithMessage, OpTypeMatrixColumnCountLessThanTwoBad) { +TEST_P(ValidateIdWithMessage, OpTypeMatrixColumnCountLessThanTwoBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 2 @@ -739,11 +820,12 @@ TEST_F(ValidateIdWithMessage, OpTypeMatrixColumnCountLessThanTwoBad) { EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("Matrix types can only be parameterized as having only 2, 3, " - "or 4 columns.\n %mat1v2float = OpTypeMatrix %v2float 1\n")); + HasSubstr(make_message( + "Matrix types can only be parameterized as having only 2, 3, " + "or 4 columns.\n %mat1v2float = OpTypeMatrix %v2float 1\n"))); } -TEST_F(ValidateIdWithMessage, OpTypeMatrixColumnCountGreaterThanFourBad) { +TEST_P(ValidateIdWithMessage, OpTypeMatrixColumnCountGreaterThanFourBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 2 @@ -752,11 +834,12 @@ TEST_F(ValidateIdWithMessage, OpTypeMatrixColumnCountGreaterThanFourBad) { EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("Matrix types can only be parameterized as having only 2, 3, " - "or 4 columns.\n %mat8v2float = OpTypeMatrix %v2float 8\n")); + HasSubstr(make_message( + "Matrix types can only be parameterized as having only 2, 3, " + "or 4 columns.\n %mat8v2float = OpTypeMatrix %v2float 8\n"))); } -TEST_F(ValidateIdWithMessage, OpTypeSamplerGood) { +TEST_P(ValidateIdWithMessage, OpTypeSamplerGood) { // In Rev31, OpTypeSampler takes no arguments. std::string spirv = kGLSL450MemoryModel + R"( %s = OpTypeSampler)"; @@ -764,7 +847,7 @@ TEST_F(ValidateIdWithMessage, OpTypeSamplerGood) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpTypeArrayGood) { +TEST_P(ValidateIdWithMessage, OpTypeArrayGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpConstant %1 1 @@ -773,7 +856,7 @@ TEST_F(ValidateIdWithMessage, OpTypeArrayGood) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpTypeArrayElementTypeBad) { +TEST_P(ValidateIdWithMessage, OpTypeArrayElementTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpConstant %1 1 @@ -781,8 +864,9 @@ TEST_F(ValidateIdWithMessage, OpTypeArrayElementTypeBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpTypeArray Element Type '2[%uint_1]' is not a " - "type.")); + HasSubstr(make_message( + "OpTypeArray Element Type '2[%uint_1]' is not a " + "type."))); } // Signed or unsigned. @@ -962,20 +1046,19 @@ TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding1InVulkan) { INSTANTIATE_TEST_SUITE_P(Widths, OpTypeArrayLengthTest, ValuesIn(std::vector{16, 32, 64})); -TEST_F(ValidateIdWithMessage, OpTypeArrayLengthNull) { +TEST_P(ValidateIdWithMessage, OpTypeArrayLengthNull) { std::string spirv = kGLSL450MemoryModel + R"( %i32 = OpTypeInt 32 0 %len = OpConstantNull %i32 %ary = OpTypeArray %i32 %len)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "OpTypeArray Length '2[%2]' default value must be at least 1.")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message("OpTypeArray Length '2[%2]' default " + "value must be at least 1."))); } -TEST_F(ValidateIdWithMessage, OpTypeArrayLengthSpecConst) { +TEST_P(ValidateIdWithMessage, OpTypeArrayLengthSpecConst) { std::string spirv = kGLSL450MemoryModel + R"( %i32 = OpTypeInt 32 0 %len = OpSpecConstant %i32 2 @@ -984,7 +1067,7 @@ TEST_F(ValidateIdWithMessage, OpTypeArrayLengthSpecConst) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpTypeArrayLengthSpecConstOp) { +TEST_P(ValidateIdWithMessage, OpTypeArrayLengthSpecConstOp) { std::string spirv = kGLSL450MemoryModel + R"( %i32 = OpTypeInt 32 0 %c1 = OpConstant %i32 1 @@ -995,29 +1078,29 @@ TEST_F(ValidateIdWithMessage, OpTypeArrayLengthSpecConstOp) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpTypeRuntimeArrayGood) { +TEST_P(ValidateIdWithMessage, OpTypeRuntimeArrayGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpTypeRuntimeArray %1)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpTypeRuntimeArrayBad) { +TEST_P(ValidateIdWithMessage, OpTypeRuntimeArrayBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpConstant %1 0 %3 = OpTypeRuntimeArray %2)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpTypeRuntimeArray Element Type '2[%uint_0]' is not a " - "type.")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "OpTypeRuntimeArray Element Type '2[%uint_0]' is not a " + "type."))); } // TODO: Object of this type can only be created with OpVariable using the -// Unifrom Storage Class +// Uniform Storage Class -TEST_F(ValidateIdWithMessage, OpTypeStructGood) { +TEST_P(ValidateIdWithMessage, OpTypeStructGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpTypeFloat 64 @@ -1026,7 +1109,7 @@ TEST_F(ValidateIdWithMessage, OpTypeStructGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpTypeStructMemberTypeBad) { +TEST_P(ValidateIdWithMessage, OpTypeStructMemberTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpTypeFloat 64 @@ -1035,11 +1118,12 @@ TEST_F(ValidateIdWithMessage, OpTypeStructMemberTypeBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpTypeStruct Member Type '3[%double_0]' is not " - "a type.")); + HasSubstr(make_message( + "OpTypeStruct Member Type '3[%double_0]' is not " + "a type."))); } -TEST_F(ValidateIdWithMessage, OpTypeStructOpaqueTypeBad) { +TEST_P(ValidateIdWithMessage, OpTypeStructOpaqueTypeBad) { std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 @@ -1057,37 +1141,39 @@ TEST_F(ValidateIdWithMessage, OpTypeStructOpaqueTypeBad) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-StandaloneSpirv-None-04667")); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpTypeStruct must not contain an opaque type")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("OpTypeStruct must not contain an opaque type"))); } -TEST_F(ValidateIdWithMessage, OpTypePointerGood) { +TEST_P(ValidateIdWithMessage, OpTypePointerGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpTypePointer Input %1)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpTypePointerBad) { +TEST_P(ValidateIdWithMessage, OpTypePointerBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpConstant %1 0 %3 = OpTypePointer Input %2)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpTypePointer Type '2[%uint_0]' is not a " - "type.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("OpTypePointer Type '2[%uint_0]' is not a " + "type."))); } -TEST_F(ValidateIdWithMessage, OpTypeFunctionGood) { +TEST_P(ValidateIdWithMessage, OpTypeFunctionGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeFunction %1)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpTypeFunctionReturnTypeBad) { +TEST_P(ValidateIdWithMessage, OpTypeFunctionReturnTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpConstant %1 0 @@ -1095,10 +1181,11 @@ TEST_F(ValidateIdWithMessage, OpTypeFunctionReturnTypeBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpTypeFunction Return Type '2[%uint_0]' is not " - "a type.")); + HasSubstr(make_message( + "OpTypeFunction Return Type '2[%uint_0]' is not " + "a type."))); } -TEST_F(ValidateIdWithMessage, OpTypeFunctionParameterBad) { +TEST_P(ValidateIdWithMessage, OpTypeFunctionParameterBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -1106,13 +1193,13 @@ TEST_F(ValidateIdWithMessage, OpTypeFunctionParameterBad) { %4 = OpTypeFunction %1 %2 %3)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpTypeFunction Parameter Type '3[%uint_0]' is not a " - "type.")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "OpTypeFunction Parameter Type '3[%uint_0]' is not a " + "type."))); } -TEST_F(ValidateIdWithMessage, OpTypeFunctionParameterTypeVoidBad) { +TEST_P(ValidateIdWithMessage, OpTypeFunctionParameterTypeVoidBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -1120,11 +1207,12 @@ TEST_F(ValidateIdWithMessage, OpTypeFunctionParameterTypeVoidBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpTypeFunction Parameter Type '1[%void]' cannot " - "be OpTypeVoid.")); + HasSubstr(make_message( + "OpTypeFunction Parameter Type '1[%void]' cannot " + "be OpTypeVoid."))); } -TEST_F(ValidateIdWithMessage, OpTypePipeGood) { +TEST_P(ValidateIdWithMessage, OpTypePipeGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 16 @@ -1133,33 +1221,33 @@ TEST_F(ValidateIdWithMessage, OpTypePipeGood) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpConstantTrueGood) { +TEST_P(ValidateIdWithMessage, OpConstantTrueGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeBool %2 = OpConstantTrue %1)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpConstantTrueBad) { +TEST_P(ValidateIdWithMessage, OpConstantTrueBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpConstantTrue %1)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpConstantTrue Result Type '1[%void]' is not a boolean " - "type.")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "OpConstantTrue Result Type '1[%void]' is not a boolean " + "type."))); } -TEST_F(ValidateIdWithMessage, OpConstantFalseGood) { +TEST_P(ValidateIdWithMessage, OpConstantFalseGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeBool %2 = OpConstantTrue %1)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpConstantFalseBad) { +TEST_P(ValidateIdWithMessage, OpConstantFalseBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpConstantFalse %1)"; @@ -1167,18 +1255,19 @@ TEST_F(ValidateIdWithMessage, OpConstantFalseBad) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("OpConstantFalse Result Type '1[%void]' is not a boolean " - "type.")); + HasSubstr(make_message( + "OpConstantFalse Result Type '1[%void]' is not a boolean " + "type."))); } -TEST_F(ValidateIdWithMessage, OpConstantGood) { +TEST_P(ValidateIdWithMessage, OpConstantGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpConstant %1 1)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpConstantBad) { +TEST_P(ValidateIdWithMessage, OpConstantBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpConstant !1 !0)"; @@ -1189,7 +1278,7 @@ TEST_F(ValidateIdWithMessage, OpConstantBad) { EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpConstantCompositeVectorGood) { +TEST_P(ValidateIdWithMessage, OpConstantCompositeVectorGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 4 @@ -1198,7 +1287,7 @@ TEST_F(ValidateIdWithMessage, OpConstantCompositeVectorGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpConstantCompositeVectorWithUndefGood) { +TEST_P(ValidateIdWithMessage, OpConstantCompositeVectorWithUndefGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 4 @@ -1208,7 +1297,7 @@ TEST_F(ValidateIdWithMessage, OpConstantCompositeVectorWithUndefGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpConstantCompositeVectorResultTypeBad) { +TEST_P(ValidateIdWithMessage, OpConstantCompositeVectorResultTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 4 @@ -1216,12 +1305,12 @@ TEST_F(ValidateIdWithMessage, OpConstantCompositeVectorResultTypeBad) { %4 = OpConstantComposite %1 %3 %3 %3 %3)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpConstantComposite Result Type '1[%float]' is not a " - "composite type.")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "OpConstantComposite Result Type '1[%float]' is not a " + "composite type."))); } -TEST_F(ValidateIdWithMessage, OpConstantCompositeVectorConstituentTypeBad) { +TEST_P(ValidateIdWithMessage, OpConstantCompositeVectorConstituentTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 4 @@ -1231,13 +1320,13 @@ TEST_F(ValidateIdWithMessage, OpConstantCompositeVectorConstituentTypeBad) { %6 = OpConstantComposite %2 %3 %5 %3 %3)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpConstantComposite Constituent '5[%uint_42]'s type " - "does not match Result Type '2[%v4float]'s vector " - "element type.")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "OpConstantComposite Constituent '5[%uint_42]'s type " + "does not match Result Type '2[%v4float]'s vector " + "element type."))); } -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, OpConstantCompositeVectorConstituentUndefTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 @@ -1250,10 +1339,11 @@ TEST_F(ValidateIdWithMessage, EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("OpConstantComposite Constituent '5[%5]'s type does not " - "match Result Type '2[%v4float]'s vector element type.")); + HasSubstr(make_message( + "OpConstantComposite Constituent '5[%5]'s type does not " + "match Result Type '2[%v4float]'s vector element type."))); } -TEST_F(ValidateIdWithMessage, OpConstantCompositeMatrixGood) { +TEST_P(ValidateIdWithMessage, OpConstantCompositeMatrixGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 4 @@ -1268,7 +1358,7 @@ TEST_F(ValidateIdWithMessage, OpConstantCompositeMatrixGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpConstantCompositeMatrixUndefGood) { +TEST_P(ValidateIdWithMessage, OpConstantCompositeMatrixUndefGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 4 @@ -1283,7 +1373,7 @@ TEST_F(ValidateIdWithMessage, OpConstantCompositeMatrixUndefGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpConstantCompositeMatrixConstituentTypeBad) { +TEST_P(ValidateIdWithMessage, OpConstantCompositeMatrixConstituentTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 4 @@ -1299,11 +1389,12 @@ TEST_F(ValidateIdWithMessage, OpConstantCompositeMatrixConstituentTypeBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpConstantComposite Constituent '10[%10]' vector " - "component count does not match Result Type " - "'4[%mat4v4float]'s vector component count.")); + HasSubstr(make_message( + "OpConstantComposite Constituent '10[%10]' vector " + "component count does not match Result Type " + "'4[%mat4v4float]'s vector component count."))); } -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, OpConstantCompositeMatrixConstituentUndefTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 @@ -1320,11 +1411,12 @@ TEST_F(ValidateIdWithMessage, CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpConstantComposite Constituent '10[%10]' vector " - "component count does not match Result Type " - "'4[%mat4v4float]'s vector component count.")); + HasSubstr(make_message( + "OpConstantComposite Constituent '10[%10]' vector " + "component count does not match Result Type " + "'4[%mat4v4float]'s vector component count."))); } -TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayGood) { +TEST_P(ValidateIdWithMessage, OpConstantCompositeArrayGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpConstant %1 4 @@ -1333,7 +1425,7 @@ TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayWithUndefGood) { +TEST_P(ValidateIdWithMessage, OpConstantCompositeArrayWithUndefGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpConstant %1 4 @@ -1344,7 +1436,7 @@ TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayWithUndefGood) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayConstConstituentTypeBad) { +TEST_P(ValidateIdWithMessage, OpConstantCompositeArrayConstConstituentTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpConstant %1 4 @@ -1352,10 +1444,11 @@ TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayConstConstituentTypeBad) { %4 = OpConstantComposite %3 %2 %2 %2 %1)"; // Uses a type as operand CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 1[%uint] cannot be a " - "type")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message("Operand '1[%uint]' cannot be a " + "type"))); } -TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayConstConstituentBad) { +TEST_P(ValidateIdWithMessage, OpConstantCompositeArrayConstConstituentBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpConstant %1 4 @@ -1366,10 +1459,11 @@ TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayConstConstituentBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpConstantComposite Constituent '5[%5]' is not a " - "constant or undef.")); + HasSubstr(make_message( + "OpConstantComposite Constituent '5[%5]' is not a " + "constant or undef."))); } -TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayConstituentTypeBad) { +TEST_P(ValidateIdWithMessage, OpConstantCompositeArrayConstituentTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpConstant %1 4 @@ -1380,12 +1474,13 @@ TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayConstituentTypeBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpConstantComposite Constituent " - "'5[%float_3_1400001]'s type does not match Result " - "Type '3[%_arr_uint_uint_4]'s array element " - "type.")); + HasSubstr(make_message( + "OpConstantComposite Constituent " + "'5[%float_3_1400001]'s type does not match Result " + "Type '3[%_arr_uint_uint_4]'s array element " + "type."))); } -TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayConstituentUndefTypeBad) { +TEST_P(ValidateIdWithMessage, OpConstantCompositeArrayConstituentUndefTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpConstant %1 4 @@ -1395,13 +1490,14 @@ TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayConstituentUndefTypeBad) { %4 = OpConstantComposite %3 %2 %2 %2 %6)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpConstantComposite Constituent " - "'5[%5]'s type does not match Result " - "Type '3[%_arr_uint_uint_4]'s array element " - "type.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("OpConstantComposite Constituent " + "'5[%5]'s type does not match Result " + "Type '3[%_arr_uint_uint_4]'s array element " + "type."))); } -TEST_F(ValidateIdWithMessage, OpConstantCompositeStructGood) { +TEST_P(ValidateIdWithMessage, OpConstantCompositeStructGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpTypeInt 64 0 @@ -1412,7 +1508,7 @@ TEST_F(ValidateIdWithMessage, OpConstantCompositeStructGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpConstantCompositeStructUndefGood) { +TEST_P(ValidateIdWithMessage, OpConstantCompositeStructUndefGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpTypeInt 64 0 @@ -1423,7 +1519,7 @@ TEST_F(ValidateIdWithMessage, OpConstantCompositeStructUndefGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpConstantCompositeStructMemberTypeBad) { +TEST_P(ValidateIdWithMessage, OpConstantCompositeStructMemberTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpTypeInt 64 0 @@ -1434,12 +1530,13 @@ TEST_F(ValidateIdWithMessage, OpConstantCompositeStructMemberTypeBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpConstantComposite Constituent " - "'5[%ulong_4300000000]' type does not match the " - "Result Type '3[%_struct_3]'s member type.")); + HasSubstr(make_message( + "OpConstantComposite Constituent " + "'5[%ulong_4300000000]' type does not match the " + "Result Type '3[%_struct_3]'s member type."))); } -TEST_F(ValidateIdWithMessage, OpConstantCompositeStructMemberUndefTypeBad) { +TEST_P(ValidateIdWithMessage, OpConstantCompositeStructMemberUndefTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpTypeInt 64 0 @@ -1450,12 +1547,13 @@ TEST_F(ValidateIdWithMessage, OpConstantCompositeStructMemberUndefTypeBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpConstantComposite Constituent '5[%5]' type " - "does not match the Result Type '3[%_struct_3]'s " - "member type.")); + HasSubstr(make_message( + "OpConstantComposite Constituent '5[%5]' type " + "does not match the Result Type '3[%_struct_3]'s " + "member type."))); } -TEST_F(ValidateIdWithMessage, OpConstantSamplerGood) { +TEST_P(ValidateIdWithMessage, OpConstantSamplerGood) { std::string spirv = kGLSL450MemoryModel + R"( %float = OpTypeFloat 32 %samplerType = OpTypeSampler @@ -1463,7 +1561,7 @@ TEST_F(ValidateIdWithMessage, OpConstantSamplerGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpConstantSamplerResultTypeBad) { +TEST_P(ValidateIdWithMessage, OpConstantSamplerResultTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpConstantSampler %1 Clamp 0 Nearest)"; @@ -1471,12 +1569,12 @@ TEST_F(ValidateIdWithMessage, OpConstantSamplerResultTypeBad) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr( + HasSubstr(make_message( "OpConstantSampler Result Type '1[%float]' is not a sampler " - "type.")); + "type."))); } -TEST_F(ValidateIdWithMessage, OpConstantNullGood) { +TEST_P(ValidateIdWithMessage, OpConstantNullGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeBool %2 = OpConstantNull %1 @@ -1512,7 +1610,7 @@ TEST_F(ValidateIdWithMessage, OpConstantNullGood) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpConstantNullBasicBad) { +TEST_P(ValidateIdWithMessage, OpConstantNullBasicBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpConstantNull %1)"; @@ -1520,11 +1618,12 @@ TEST_F(ValidateIdWithMessage, OpConstantNullBasicBad) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("OpConstantNull Result Type '1[%void]' cannot have a null " - "value.")); + HasSubstr(make_message( + "OpConstantNull Result Type '1[%void]' cannot have a null " + "value."))); } -TEST_F(ValidateIdWithMessage, OpConstantNullArrayBad) { +TEST_P(ValidateIdWithMessage, OpConstantNullArrayBad) { std::string spirv = kGLSL450MemoryModel + R"( %2 = OpTypeInt 32 0 %3 = OpTypeSampler @@ -1535,24 +1634,25 @@ TEST_F(ValidateIdWithMessage, OpConstantNullArrayBad) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr( + HasSubstr(make_message( "OpConstantNull Result Type '4[%_arr_2_uint_4]' cannot have a " - "null value.")); + "null value."))); } -TEST_F(ValidateIdWithMessage, OpConstantNullStructBad) { +TEST_P(ValidateIdWithMessage, OpConstantNullStructBad) { std::string spirv = kGLSL450MemoryModel + R"( %2 = OpTypeSampler %3 = OpTypeStruct %2 %2 %4 = OpConstantNull %3)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpConstantNull Result Type '2[%_struct_2]' " - "cannot have a null value.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("OpConstantNull Result Type '2[%_struct_2]' " + "cannot have a null value."))); } -TEST_F(ValidateIdWithMessage, OpConstantNullRuntimeArrayBad) { +TEST_P(ValidateIdWithMessage, OpConstantNullRuntimeArrayBad) { std::string spirv = kGLSL450MemoryModel + R"( %bool = OpTypeBool %array = OpTypeRuntimeArray %bool @@ -1561,56 +1661,57 @@ TEST_F(ValidateIdWithMessage, OpConstantNullRuntimeArrayBad) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr( + HasSubstr(make_message( "OpConstantNull Result Type '2[%_runtimearr_bool]' cannot have " - "a null value.")); + "a null value."))); } -TEST_F(ValidateIdWithMessage, OpSpecConstantTrueGood) { +TEST_P(ValidateIdWithMessage, OpSpecConstantTrueGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeBool %2 = OpSpecConstantTrue %1)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpSpecConstantTrueBad) { +TEST_P(ValidateIdWithMessage, OpSpecConstantTrueBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpSpecConstantTrue %1)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpSpecConstantTrue Result Type '1[%void]' is not " - "a boolean type")); + HasSubstr(make_message( + "OpSpecConstantTrue Result Type '1[%void]' is not " + "a boolean type"))); } -TEST_F(ValidateIdWithMessage, OpSpecConstantFalseGood) { +TEST_P(ValidateIdWithMessage, OpSpecConstantFalseGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeBool %2 = OpSpecConstantFalse %1)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpSpecConstantFalseBad) { +TEST_P(ValidateIdWithMessage, OpSpecConstantFalseBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpSpecConstantFalse %1)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpSpecConstantFalse Result Type '1[%void]' is not " - "a boolean type")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "OpSpecConstantFalse Result Type '1[%void]' is not " + "a boolean type"))); } -TEST_F(ValidateIdWithMessage, OpSpecConstantGood) { +TEST_P(ValidateIdWithMessage, OpSpecConstantGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpSpecConstant %1 42)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpSpecConstantBad) { +TEST_P(ValidateIdWithMessage, OpSpecConstantBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpSpecConstant !1 !4)"; @@ -1619,12 +1720,13 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantBad) { // change over time, but this must always fail. CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Type Id 1 is not a scalar numeric type")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("Type Id 1 is not a scalar numeric type"))); } // Valid: SpecConstantComposite specializes to a vector. -TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeVectorGood) { +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeVectorGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 4 @@ -1636,7 +1738,7 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeVectorGood) { } // Valid: Vector of floats and Undefs. -TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeVectorWithUndefGood) { +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeVectorWithUndefGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 4 @@ -1649,7 +1751,7 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeVectorWithUndefGood) { } // Invalid: result type is float. -TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeVectorResultTypeBad) { +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeVectorResultTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 4 @@ -1657,11 +1759,12 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeVectorResultTypeBad) { %4 = OpSpecConstantComposite %1 %3 %3 %3 %3)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("is not a composite type")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message("is not a composite type"))); } // Invalid: Vector contains a mix of Int and Float. -TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeVectorConstituentTypeBad) { +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeVectorConstituentTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 4 @@ -1672,13 +1775,14 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeVectorConstituentTypeBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpSpecConstantComposite Constituent " - "'5[%uint_42]'s type does not match Result Type " - "'2[%v4float]'s vector element type.")); + HasSubstr(make_message( + "OpSpecConstantComposite Constituent " + "'5[%uint_42]'s type does not match Result Type " + "'2[%v4float]'s vector element type."))); } // Invalid: Constituent is not a constant -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeVectorConstituentNotConstantBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 @@ -1691,12 +1795,13 @@ TEST_F(ValidateIdWithMessage, CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpSpecConstantComposite Constituent '6[%6]' is " - "not a constant or undef.")); + HasSubstr(make_message( + "OpSpecConstantComposite Constituent '6[%6]' is " + "not a constant or undef."))); } // Invalid: Vector contains a mix of Undef-int and Float. -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeVectorConstituentUndefTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 @@ -1708,13 +1813,14 @@ TEST_F(ValidateIdWithMessage, CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpSpecConstantComposite Constituent '5[%5]'s " - "type does not match Result Type '2[%v4float]'s " - "vector element type.")); + HasSubstr(make_message( + "OpSpecConstantComposite Constituent '5[%5]'s " + "type does not match Result Type '2[%v4float]'s " + "vector element type."))); } // Invalid: Vector expects 3 components, but 4 specified. -TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeVectorNumComponentsBad) { +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeVectorNumComponentsBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 3 @@ -1724,13 +1830,14 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeVectorNumComponentsBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpSpecConstantComposite Constituent count does " - "not match Result Type '2[%v3float]'s vector " - "component count.")); + HasSubstr(make_message( + "OpSpecConstantComposite Constituent count does " + "not match Result Type '2[%v3float]'s vector " + "component count."))); } // Valid: 4x4 matrix of floats -TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixGood) { +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeMatrixGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 4 @@ -1747,7 +1854,7 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixGood) { } // Valid: Matrix in which one column is Undef -TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixUndefGood) { +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeMatrixUndefGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 4 @@ -1764,7 +1871,7 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixUndefGood) { } // Invalid: Matrix in which the sizes of column vectors are not equal. -TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixConstituentTypeBad) { +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeMatrixConstituentTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 4 @@ -1780,13 +1887,14 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixConstituentTypeBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpSpecConstantComposite Constituent '10[%10]' " - "vector component count does not match Result Type " - " '4[%mat4v4float]'s vector component count.")); + HasSubstr(make_message( + "OpSpecConstantComposite Constituent '10[%10]' " + "vector component count does not match Result Type " + " '4[%mat4v4float]'s vector component count."))); } // Invalid: Matrix type expects 4 columns but only 3 specified. -TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixNumColsBad) { +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeMatrixNumColsBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 4 @@ -1799,15 +1907,15 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixNumColsBad) { %10 = OpSpecConstantComposite %3 %6 %7 %8)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpSpecConstantComposite Constituent count does " - "not match Result Type '3[%mat4v4float]'s matrix column " - "count.")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "OpSpecConstantComposite Constituent count does " + "not match Result Type '3[%mat4v4float]'s matrix column " + "count."))); } // Invalid: Composite contains a non-const/undef component -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeMatrixConstituentNotConstBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 @@ -1821,12 +1929,13 @@ TEST_F(ValidateIdWithMessage, CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpSpecConstantComposite Constituent '7[%7]' is " - "not a constant or undef.")); + HasSubstr(make_message( + "OpSpecConstantComposite Constituent '7[%7]' is " + "not a constant or undef."))); } // Invalid: Composite contains a column that is *not* a vector (it's an array) -TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixColTypeBad) { +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeMatrixColTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeInt 32 0 @@ -1841,13 +1950,14 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixColTypeBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpSpecConstantComposite Constituent '8[%8]' type " - "does not match Result Type '7[%mat4v4float]'s " - "matrix column type.")); + HasSubstr(make_message( + "OpSpecConstantComposite Constituent '8[%8]' type " + "does not match Result Type '7[%mat4v4float]'s " + "matrix column type."))); } // Invalid: Matrix with an Undef column of the wrong size. -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeMatrixConstituentUndefTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 @@ -1864,13 +1974,14 @@ TEST_F(ValidateIdWithMessage, CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpSpecConstantComposite Constituent '10[%10]' " - "vector component count does not match Result Type " - " '4[%mat4v4float]'s vector component count.")); + HasSubstr(make_message( + "OpSpecConstantComposite Constituent '10[%10]' " + "vector component count does not match Result Type " + " '4[%mat4v4float]'s vector component count."))); } // Invalid: Matrix in which some columns are Int and some are Float. -TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixColumnTypeBad) { +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeMatrixColumnTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpTypeFloat 32 @@ -1885,13 +1996,14 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixColumnTypeBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpSpecConstantComposite Constituent '8[%8]' " - "component type does not match Result Type " - "'5[%mat2v2float]'s matrix column component type.")); + HasSubstr(make_message( + "OpSpecConstantComposite Constituent '8[%8]' " + "component type does not match Result Type " + "'5[%mat2v2float]'s matrix column component type."))); } // Valid: Array of integers -TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeArrayGood) { +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeArrayGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpSpecConstant %1 4 @@ -1905,7 +2017,7 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeArrayGood) { } // Invalid: Expecting an array of 4 components, but 3 specified. -TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeArrayNumComponentsBad) { +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeArrayNumComponentsBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpConstant %1 4 @@ -1914,13 +2026,14 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeArrayNumComponentsBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpSpecConstantComposite Constituent count does not " - "match Result Type '3[%_arr_uint_uint_4]'s array " - "length.")); + HasSubstr(make_message( + "OpSpecConstantComposite Constituent count does not " + "match Result Type '3[%_arr_uint_uint_4]'s array " + "length."))); } // Valid: Array of Integers and Undef-int -TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeArrayWithUndefGood) { +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeArrayWithUndefGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpSpecConstant %1 4 @@ -1932,7 +2045,7 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeArrayWithUndefGood) { } // Invalid: Array uses a type as operand. -TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeArrayConstConstituentBad) { +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeArrayConstConstituentBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpConstant %1 4 @@ -1943,12 +2056,13 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeArrayConstConstituentBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpSpecConstantComposite Constituent '5[%5]' is " - "not a constant or undef.")); + HasSubstr(make_message( + "OpSpecConstantComposite Constituent '5[%5]' is " + "not a constant or undef."))); } // Invalid: Array has a mix of Int and Float components. -TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeArrayConstituentTypeBad) { +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeArrayConstituentTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpConstant %1 4 @@ -1959,13 +2073,14 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeArrayConstituentTypeBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpSpecConstantComposite Constituent '5[%5]'s " - "type does not match Result Type " - "'3[%_arr_uint_uint_4]'s array element type.")); + HasSubstr(make_message( + "OpSpecConstantComposite Constituent '5[%5]'s " + "type does not match Result Type " + "'3[%_arr_uint_uint_4]'s array element type."))); } // Invalid: Array has a mix of Int and Undef-float. -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeArrayConstituentUndefTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 @@ -1977,13 +2092,14 @@ TEST_F(ValidateIdWithMessage, CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpSpecConstantComposite Constituent '5[%5]'s " - "type does not match Result Type " - "'3[%_arr_uint_2]'s array element type.")); + HasSubstr(make_message( + "OpSpecConstantComposite Constituent '5[%5]'s " + "type does not match Result Type " + "'3[%_arr_uint_2]'s array element type."))); } // Valid: Struct of {Int32,Int32,Int64}. -TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeStructGood) { +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeStructGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpTypeInt 64 0 @@ -1996,7 +2112,7 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeStructGood) { } // Invalid: missing one int32 struct member. -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeStructMissingComponentBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 @@ -2006,14 +2122,15 @@ TEST_F(ValidateIdWithMessage, %6 = OpSpecConstantComposite %3 %4 %5)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpSpecConstantComposite Constituent " - "'2[%_struct_2]' count does not match Result Type " - " '2[%_struct_2]'s struct member count.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("OpSpecConstantComposite Constituent " + "'2[%_struct_2]' count does not match Result Type " + " '2[%_struct_2]'s struct member count."))); } // Valid: Struct uses Undef-int64. -TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeStructUndefGood) { +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeStructUndefGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpTypeInt 64 0 @@ -2026,7 +2143,7 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeStructUndefGood) { } // Invalid: Composite contains non-const/undef component. -TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeStructNonConstBad) { +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeStructNonConstBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpTypeInt 64 0 @@ -2039,13 +2156,14 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeStructNonConstBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpSpecConstantComposite Constituent '7[%7]' is " - "not a constant or undef.")); + HasSubstr(make_message( + "OpSpecConstantComposite Constituent '7[%7]' is " + "not a constant or undef."))); } // Invalid: Struct component type does not match expected specialization type. // Second component was expected to be Int32, but got Int64. -TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeStructMemberTypeBad) { +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeStructMemberTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpTypeInt 64 0 @@ -2056,13 +2174,14 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeStructMemberTypeBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpSpecConstantComposite Constituent '5[%5]' type " - "does not match the Result Type '3[%_struct_3]'s " - "member type.")); + HasSubstr(make_message( + "OpSpecConstantComposite Constituent '5[%5]' type " + "does not match the Result Type '3[%_struct_3]'s " + "member type."))); } // Invalid: Undef-int64 used when Int32 was expected. -TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeStructMemberUndefTypeBad) { +TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeStructMemberUndefTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpTypeInt 64 0 @@ -2073,14 +2192,15 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeStructMemberUndefTypeBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpSpecConstantComposite Constituent '5[%5]' type " - "does not match the Result Type '3[%_struct_3]'s " - "member type.")); + HasSubstr(make_message( + "OpSpecConstantComposite Constituent '5[%5]' type " + "does not match the Result Type '3[%_struct_3]'s " + "member type."))); } // TODO: OpSpecConstantOp -TEST_F(ValidateIdWithMessage, OpVariableGood) { +TEST_P(ValidateIdWithMessage, OpVariableGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpTypePointer Input %1 @@ -2088,16 +2208,16 @@ TEST_F(ValidateIdWithMessage, OpVariableGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpVariableInitializerConstantGood) { +TEST_P(ValidateIdWithMessage, OpVariableInitializerConstantGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 -%2 = OpTypePointer Input %1 +%2 = OpTypePointer Output %1 %3 = OpConstant %1 42 -%4 = OpVariable %2 Input %3)"; +%4 = OpVariable %2 Output %3)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpVariableInitializerGlobalVariableGood) { +TEST_P(ValidateIdWithMessage, OpVariableInitializerGlobalVariableGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpTypePointer Uniform %1 @@ -2109,29 +2229,30 @@ TEST_F(ValidateIdWithMessage, OpVariableInitializerGlobalVariableGood) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } // TODO: Positive test OpVariable with OpConstantNull of OpTypePointer -TEST_F(ValidateIdWithMessage, OpVariableResultTypeBad) { +TEST_P(ValidateIdWithMessage, OpVariableResultTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpVariable %1 Input)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpVariable Result Type '1[%uint]' is not a pointer " - "type.")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "OpVariable Result Type '1[%uint]' is not a pointer " + "type."))); } -TEST_F(ValidateIdWithMessage, OpVariableInitializerIsTypeBad) { +TEST_P(ValidateIdWithMessage, OpVariableInitializerIsTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpTypePointer Input %1 %3 = OpVariable %2 Input %2)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 2[%_ptr_Input_uint] " - "cannot be a type")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message("Operand '2[%_ptr_Input_uint]' " + "cannot be a type"))); } -TEST_F(ValidateIdWithMessage, OpVariableInitializerIsFunctionVarBad) { +TEST_P(ValidateIdWithMessage, OpVariableInitializerIsFunctionVarBad) { std::string spirv = kGLSL450MemoryModel + R"( %int = OpTypeInt 32 0 %ptrint = OpTypePointer Function %int @@ -2148,11 +2269,12 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpVariable Initializer '8[%8]' is not a constant " - "or module-scope variable")); + HasSubstr(make_message( + "OpVariable Initializer '8[%8]' is not a constant " + "or module-scope variable"))); } -TEST_F(ValidateIdWithMessage, OpVariableInitializerIsModuleVarGood) { +TEST_P(ValidateIdWithMessage, OpVariableInitializerIsModuleVarGood) { std::string spirv = kGLSL450MemoryModel + R"( %int = OpTypeInt 32 0 %ptrint = OpTypePointer Uniform %int @@ -2170,7 +2292,7 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpVariableContainsBoolBad) { +TEST_P(ValidateIdWithMessage, OpVariableContainsBoolBad) { std::string spirv = kGLSL450MemoryModel + R"( %bool = OpTypeBool %int = OpTypeInt 32 0 @@ -2187,14 +2309,36 @@ OpFunctionEnd )"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("If OpTypeBool is stored in conjunction with OpVariable" - ", it can only be used with non-externally visible " - "shader Storage Classes: Workgroup, CrossWorkgroup, " - "Private, and Function")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message( + "If OpTypeBool is stored in conjunction with OpVariable, it can only " + "be used with non-externally visible shader Storage Classes: " + "Workgroup, CrossWorkgroup, Private, Function, Input, Output, " + "RayPayloadKHR, IncomingRayPayloadKHR, HitAttributeKHR, " + "CallableDataKHR, or IncomingCallableDataKHR"))); } -TEST_F(ValidateIdWithMessage, OpVariableContainsBoolPointerGood) { +TEST_P(ValidateIdWithMessage, OpVariableContainsBoolPrivateGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%block = OpTypeStruct %bool %int +%_ptr_Private_block = OpTypePointer Private %block +%var = OpVariable %_ptr_Private_block Private +%void = OpTypeVoid +%fnty = OpTypeFunction %void +%main = OpFunction %void None %fnty +%entry = OpLabel +%load = OpLoad %block %var +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateIdWithMessage, OpVariableContainsBoolPointerGood) { std::string spirv = kGLSL450MemoryModel + R"( %bool = OpTypeBool %boolptr = OpTypePointer Uniform %bool @@ -2214,7 +2358,7 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpVariableContainsBuiltinBoolGood) { +TEST_P(ValidateIdWithMessage, OpVariableContainsBuiltinBoolGood) { std::string spirv = kGLSL450MemoryModel + R"( OpMemberDecorate %input 0 BuiltIn FrontFacing %bool = OpTypeBool @@ -2233,7 +2377,59 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpVariableContainsRayPayloadBoolGood) { +TEST_P(ValidateIdWithMessage, OpVariableContainsNoBuiltinBoolBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%bool = OpTypeBool +%input = OpTypeStruct %bool +%_ptr_input = OpTypePointer Input %input +%var = OpVariable %_ptr_input Input +%void = OpTypeVoid +%fnty = OpTypeFunction %void +%main = OpFunction %void None %fnty +%entry = OpLabel +%load = OpLoad %input %var +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message( + "If OpTypeBool is stored in conjunction with OpVariable using Input " + "or Output Storage Classes it requires a BuiltIn decoration"))); +} + +TEST_P(ValidateIdWithMessage, OpVariableContainsNoBuiltinBoolBadVulkan) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var +OpExecutionMode %main OriginUpperLeft +%bool = OpTypeBool +%input = OpTypeStruct %bool +%_ptr_input = OpTypePointer Input %input +%var = OpVariable %_ptr_input Input +%void = OpTypeVoid +%fnty = OpTypeFunction %void +%main = OpFunction %void None %fnty +%entry = OpLabel +%load = OpLoad %input %var +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Input-07290")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message( + "If OpTypeBool is stored in conjunction with OpVariable using Input " + "or Output Storage Classes it requires a BuiltIn decoration"))); +} + +TEST_P(ValidateIdWithMessage, OpVariableContainsRayPayloadBoolGood) { std::string spirv = R"( OpCapability RayTracingNV OpCapability Shader @@ -2256,7 +2452,7 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpVariablePointerNoVariablePointersBad) { +TEST_P(ValidateIdWithMessage, OpVariablePointerNoVariablePointersBad) { const std::string spirv = R"( OpCapability Shader OpCapability Linkage @@ -2277,11 +2473,11 @@ OpFunctionEnd EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr( - "In Logical addressing, variables may not allocate a pointer type")); + HasSubstr(make_message( + "In Logical addressing, variables may not allocate a pointer type"))); } -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, OpVariablePointerNoVariablePointersRelaxedLogicalGood) { const std::string spirv = R"( OpCapability Shader @@ -2305,7 +2501,7 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpFunctionWithNonMemoryObject) { +TEST_P(ValidateIdWithMessage, OpFunctionWithNonMemoryObject) { // DXC generates code that looks like when given something like: // T t; // t.s.fn_1(); @@ -2346,7 +2542,7 @@ TEST_F(ValidateIdWithMessage, OpFunctionWithNonMemoryObject) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, OpVariablePointerVariablePointersStorageBufferGood) { const std::string spirv = R"( OpCapability Shader @@ -2370,7 +2566,7 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpVariablePointerVariablePointersGood) { +TEST_P(ValidateIdWithMessage, OpVariablePointerVariablePointersGood) { const std::string spirv = R"( OpCapability Shader OpCapability Linkage @@ -2393,7 +2589,7 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpVariablePointerVariablePointersBad) { +TEST_P(ValidateIdWithMessage, OpVariablePointerVariablePointersBad) { const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers @@ -2409,12 +2605,13 @@ OpMemoryModel Logical GLSL450 CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("In Logical addressing with variable pointers, " - "variables that allocate pointers must be in Function " - "or Private storage classes")); + HasSubstr(make_message( + "In Logical addressing with variable pointers, " + "variables that allocate pointers must be in Function " + "or Private storage classes"))); } -TEST_F(ValidateIdWithMessage, OpLoadGood) { +TEST_P(ValidateIdWithMessage, OpLoadGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -2487,7 +2684,7 @@ void createVariablePointerSpirvProgram(std::ostringstream* spirv, // With the VariablePointer Capability, OpLoad should allow loading a // VaiablePointer. In this test the variable pointer is obtained by an OpSelect -TEST_F(ValidateIdWithMessage, OpLoadVarPtrOpSelectGood) { +TEST_P(ValidateIdWithMessage, OpLoadVarPtrOpSelectGood) { std::string result_strategy = R"( %isneg = OpSLessThan %bool %i %zero %varptr = OpSelect %f32ptr %isneg %ptr1 %ptr2 @@ -2506,7 +2703,7 @@ TEST_F(ValidateIdWithMessage, OpLoadVarPtrOpSelectGood) { // through a variable pointer. // Disabled since using OpSelect with pointers without VariablePointers will // fail LogicalsPass. -TEST_F(ValidateIdWithMessage, DISABLED_OpLoadVarPtrOpSelectBad) { +TEST_P(ValidateIdWithMessage, DISABLED_OpLoadVarPtrOpSelectBad) { std::string result_strategy = R"( %isneg = OpSLessThan %bool %i %zero %varptr = OpSelect %f32ptr %isneg %ptr1 %ptr2 @@ -2519,12 +2716,13 @@ TEST_F(ValidateIdWithMessage, DISABLED_OpLoadVarPtrOpSelectBad) { false /* Use Helper Function? */); CompileSuccessfully(spirv.str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("is not a logical pointer.")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message("is not a logical pointer."))); } // With the VariablePointer Capability, OpLoad should allow loading a // VaiablePointer. In this test the variable pointer is obtained by an OpPhi -TEST_F(ValidateIdWithMessage, OpLoadVarPtrOpPhiGood) { +TEST_P(ValidateIdWithMessage, OpLoadVarPtrOpPhiGood) { std::string result_strategy = R"( %is_neg = OpSLessThan %bool %i %zero OpSelectionMerge %end_label None @@ -2548,7 +2746,7 @@ TEST_F(ValidateIdWithMessage, OpLoadVarPtrOpPhiGood) { // Without the VariablePointers Capability, OpPhi can have a pointer result // type. -TEST_F(ValidateIdWithMessage, OpPhiBad) { +TEST_P(ValidateIdWithMessage, OpPhiBad) { std::string result_strategy = R"( %is_neg = OpSLessThan %bool %i %zero OpSelectionMerge %end_label None @@ -2569,14 +2767,15 @@ TEST_F(ValidateIdWithMessage, OpPhiBad) { CompileSuccessfully(spirv.str()); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Using pointers with OpPhi requires capability " - "VariablePointers or VariablePointersStorageBuffer")); + HasSubstr(make_message( + "Using pointers with OpPhi requires capability " + "VariablePointers or VariablePointersStorageBuffer"))); } // With the VariablePointer Capability, OpLoad should allow loading through a // VaiablePointer. In this test the variable pointer is obtained from an // OpFunctionCall (return value from a function) -TEST_F(ValidateIdWithMessage, OpLoadVarPtrOpFunctionCallGood) { +TEST_P(ValidateIdWithMessage, OpLoadVarPtrOpFunctionCallGood) { std::ostringstream spirv; std::string result_strategy = R"( %isneg = OpSLessThan %bool %i %zero @@ -2591,7 +2790,7 @@ TEST_F(ValidateIdWithMessage, OpLoadVarPtrOpFunctionCallGood) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpLoadResultTypeBad) { +TEST_P(ValidateIdWithMessage, OpLoadResultTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -2606,13 +2805,14 @@ TEST_F(ValidateIdWithMessage, OpLoadResultTypeBad) { )"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpLoad Result Type " - "'3[%_ptr_UniformConstant_uint]' does not match " - "Pointer '5[%5]'s type.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("OpLoad Result Type " + "'3[%_ptr_UniformConstant_uint]' does not match " + "Pointer '5[%5]'s type."))); } -TEST_F(ValidateIdWithMessage, OpLoadPointerBad) { +TEST_P(ValidateIdWithMessage, OpLoadPointerBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -2628,12 +2828,13 @@ TEST_F(ValidateIdWithMessage, OpLoadPointerBad) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); // Prove that SSA checks trigger for a bad Id value. // The next test case show the not-a-logical-pointer case. - EXPECT_THAT(getDiagnosticString(), HasSubstr("ID 8[%8] has not been " - "defined")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message("ID '8[%8]' has not been " + "defined"))); } // Disabled as bitcasting type to object is now not valid. -TEST_F(ValidateIdWithMessage, DISABLED_OpLoadLogicalPointerBad) { +TEST_P(ValidateIdWithMessage, DISABLED_OpLoadLogicalPointerBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -2655,10 +2856,11 @@ TEST_F(ValidateIdWithMessage, DISABLED_OpLoadLogicalPointerBad) { // I don't know if it's possible to generate a bad case // if/when the validator is complete. EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpLoad Pointer '9' is not a logical pointer.")); + HasSubstr(make_message( + "OpLoad Pointer '9' is not a logical pointer."))); } -TEST_F(ValidateIdWithMessage, OpStoreGood) { +TEST_P(ValidateIdWithMessage, OpStoreGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -2674,7 +2876,7 @@ TEST_F(ValidateIdWithMessage, OpStoreGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpStorePointerBad) { +TEST_P(ValidateIdWithMessage, OpStorePointerBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -2691,12 +2893,13 @@ TEST_F(ValidateIdWithMessage, OpStorePointerBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpStore Pointer '7[%uint_0]' is not a logical " - "pointer.")); + HasSubstr(make_message( + "OpStore Pointer '7[%uint_0]' is not a logical " + "pointer."))); } // Disabled as bitcasting type to object is now not valid. -TEST_F(ValidateIdWithMessage, DISABLED_OpStoreLogicalPointerBad) { +TEST_P(ValidateIdWithMessage, DISABLED_OpStoreLogicalPointerBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -2715,14 +2918,15 @@ TEST_F(ValidateIdWithMessage, DISABLED_OpStoreLogicalPointerBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpStore Pointer '10' is not a logical pointer.")); + HasSubstr(make_message( + "OpStore Pointer '10' is not a logical pointer."))); } // Without the VariablePointer Capability, OpStore should may not store // through a variable pointer. // Disabled since using OpSelect with pointers without VariablePointers will // fail LogicalsPass. -TEST_F(ValidateIdWithMessage, DISABLED_OpStoreVarPtrBad) { +TEST_P(ValidateIdWithMessage, DISABLED_OpStoreVarPtrBad) { std::string result_strategy = R"( %isneg = OpSLessThan %bool %i %zero %varptr = OpSelect %f32ptr %isneg %ptr1 %ptr2 @@ -2735,12 +2939,13 @@ TEST_F(ValidateIdWithMessage, DISABLED_OpStoreVarPtrBad) { false /* Use Helper Function? */); CompileSuccessfully(spirv.str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("is not a logical pointer.")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message("is not a logical pointer."))); } // With the VariablePointer Capability, OpStore should allow storing through a // variable pointer. -TEST_F(ValidateIdWithMessage, OpStoreVarPtrGood) { +TEST_P(ValidateIdWithMessage, OpStoreVarPtrGood) { std::string result_strategy = R"( %isneg = OpSLessThan %bool %i %zero %varptr = OpSelect %f32ptr %isneg %ptr1 %ptr2 @@ -2755,7 +2960,7 @@ TEST_F(ValidateIdWithMessage, OpStoreVarPtrGood) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpStoreObjectGood) { +TEST_P(ValidateIdWithMessage, OpStoreObjectGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -2775,10 +2980,11 @@ TEST_F(ValidateIdWithMessage, OpStoreObjectGood) { OpFunctionEnd)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpStore Object '9[%9]'s type is void.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("OpStore Object '9[%9]'s type is void."))); } -TEST_F(ValidateIdWithMessage, OpStoreTypeBad) { +TEST_P(ValidateIdWithMessage, OpStoreTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -2795,8 +3001,9 @@ TEST_F(ValidateIdWithMessage, OpStoreTypeBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpStore Pointer '7[%7]'s type does not match " - "Object '6[%float_3_1400001]'s type.")); + HasSubstr(make_message( + "OpStore Pointer '7[%7]'s type does not match " + "Object '6[%float_3_1400001]'s type."))); } // The next series of test check test a relaxation of the rules for stores to @@ -2805,7 +3012,7 @@ TEST_F(ValidateIdWithMessage, OpStoreTypeBad) { // TODO: Add tests for layout compatible arrays and matricies when the validator // relaxes the rules for them as well. Also need test to check for layout // decorations specific to those types. -TEST_F(ValidateIdWithMessage, OpStoreTypeBadStruct) { +TEST_P(ValidateIdWithMessage, OpStoreTypeBadStruct) { std::string spirv = kGLSL450MemoryModel + R"( OpMemberDecorate %1 0 Offset 0 OpMemberDecorate %1 1 Offset 4 @@ -2828,13 +3035,14 @@ TEST_F(ValidateIdWithMessage, OpStoreTypeBadStruct) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpStore Pointer '8[%8]'s type does not match " - "Object '11[%11]'s type.")); + HasSubstr(make_message( + "OpStore Pointer '8[%8]'s type does not match " + "Object '11[%11]'s type."))); } // Same code as the last test. The difference is that we relax the rule. // Because the structs %3 and %5 are defined the same way. -TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedStruct) { +TEST_P(ValidateIdWithMessage, OpStoreTypeRelaxedStruct) { std::string spirv = kGLSL450MemoryModel + R"( OpMemberDecorate %1 0 Offset 0 OpMemberDecorate %1 1 Offset 4 @@ -2859,9 +3067,9 @@ TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedStruct) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -// Same code as the last test excect for an extra decoration on one of the +// Same code as the last test except for an extra decoration on one of the // members. With the relaxed rules, the code is still valid. -TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedStructWithExtraDecoration) { +TEST_P(ValidateIdWithMessage, OpStoreTypeRelaxedStructWithExtraDecoration) { std::string spirv = kGLSL450MemoryModel + R"( OpMemberDecorate %1 0 Offset 0 OpMemberDecorate %1 1 Offset 4 @@ -2889,7 +3097,7 @@ TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedStructWithExtraDecoration) { // This test check that we recursively traverse the struct to check if they are // interchangable. -TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedNestedStruct) { +TEST_P(ValidateIdWithMessage, OpStoreTypeRelaxedNestedStruct) { std::string spirv = kGLSL450MemoryModel + R"( OpMemberDecorate %1 0 Offset 0 OpMemberDecorate %1 1 Offset 4 @@ -2925,7 +3133,7 @@ TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedNestedStruct) { // This test check that the even with the relaxed rules an error is identified // if the members of the struct are in a different order. -TEST_F(ValidateIdWithMessage, OpStoreTypeBadRelaxedStruct1) { +TEST_P(ValidateIdWithMessage, OpStoreTypeBadRelaxedStruct1) { std::string spirv = kGLSL450MemoryModel + R"( OpMemberDecorate %1 0 Offset 0 OpMemberDecorate %1 1 Offset 4 @@ -2959,13 +3167,14 @@ TEST_F(ValidateIdWithMessage, OpStoreTypeBadRelaxedStruct1) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("OpStore Pointer '13[%13]'s layout does not match Object " - " '16[%16]'s layout.")); + HasSubstr(make_message( + "OpStore Pointer '13[%13]'s layout does not match Object " + " '16[%16]'s layout."))); } // This test check that the even with the relaxed rules an error is identified // if the members of the struct are at different offsets. -TEST_F(ValidateIdWithMessage, OpStoreTypeBadRelaxedStruct2) { +TEST_P(ValidateIdWithMessage, OpStoreTypeBadRelaxedStruct2) { std::string spirv = kGLSL450MemoryModel + R"( OpMemberDecorate %1 0 Offset 4 OpMemberDecorate %1 1 Offset 0 @@ -2999,11 +3208,12 @@ TEST_F(ValidateIdWithMessage, OpStoreTypeBadRelaxedStruct2) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("OpStore Pointer '13[%13]'s layout does not match Object " - " '16[%16]'s layout.")); + HasSubstr(make_message( + "OpStore Pointer '13[%13]'s layout does not match Object " + " '16[%16]'s layout."))); } -TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedLogicalPointerReturnPointer) { +TEST_P(ValidateIdWithMessage, OpStoreTypeRelaxedLogicalPointerReturnPointer) { const std::string spirv = R"( OpCapability Shader OpCapability Linkage @@ -3022,7 +3232,7 @@ TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedLogicalPointerReturnPointer) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedLogicalPointerAllocPointer) { +TEST_P(ValidateIdWithMessage, OpStoreTypeRelaxedLogicalPointerAllocPointer) { const std::string spirv = R"( OpCapability Shader OpCapability Linkage @@ -3045,7 +3255,7 @@ TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedLogicalPointerAllocPointer) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpStoreVoid) { +TEST_P(ValidateIdWithMessage, OpStoreVoid) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -3060,11 +3270,12 @@ TEST_F(ValidateIdWithMessage, OpStoreVoid) { OpFunctionEnd)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpStore Object '8[%8]'s type is void.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("OpStore Object '8[%8]'s type is void."))); } -TEST_F(ValidateIdWithMessage, OpStoreLabel) { +TEST_P(ValidateIdWithMessage, OpStoreLabel) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -3079,12 +3290,12 @@ TEST_F(ValidateIdWithMessage, OpStoreLabel) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Operand 7[%7] requires a type")); + HasSubstr(make_message("Operand '7[%7]' requires a type"))); } // TODO: enable when this bug is fixed: // https://cvs.khronos.org/bugzilla/show_bug.cgi?id=15404 -TEST_F(ValidateIdWithMessage, DISABLED_OpStoreFunction) { +TEST_P(ValidateIdWithMessage, DISABLED_OpStoreFunction) { std::string spirv = kGLSL450MemoryModel + R"( %2 = OpTypeInt 32 0 %3 = OpTypePointer UniformConstant %2 @@ -3100,7 +3311,7 @@ TEST_F(ValidateIdWithMessage, DISABLED_OpStoreFunction) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpStoreBuiltin) { +TEST_P(ValidateIdWithMessage, OpStoreBuiltin) { std::string spirv = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -3135,10 +3346,11 @@ TEST_F(ValidateIdWithMessage, OpStoreBuiltin) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("storage class is read-only")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message("storage class is read-only"))); } -TEST_F(ValidateIdWithMessage, OpCopyMemoryGood) { +TEST_P(ValidateIdWithMessage, OpCopyMemoryGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -3158,7 +3370,7 @@ TEST_F(ValidateIdWithMessage, OpCopyMemoryGood) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpCopyMemoryNonPointerTarget) { +TEST_P(ValidateIdWithMessage, OpCopyMemoryNonPointerTarget) { const std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -3175,11 +3387,12 @@ OpFunctionEnd CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target operand '6[%6]' is not a pointer.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("Target operand '6[%6]' is not a pointer."))); } -TEST_F(ValidateIdWithMessage, OpCopyMemoryNonPointerSource) { +TEST_P(ValidateIdWithMessage, OpCopyMemoryNonPointerSource) { const std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -3196,11 +3409,12 @@ OpFunctionEnd CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Source operand '6[%6]' is not a pointer.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("Source operand '6[%6]' is not a pointer."))); } -TEST_F(ValidateIdWithMessage, OpCopyMemoryBad) { +TEST_P(ValidateIdWithMessage, OpCopyMemoryBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -3220,11 +3434,11 @@ TEST_F(ValidateIdWithMessage, OpCopyMemoryBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target '5[%5]'s type does not match " - "Source '2[%uint]'s type.")); + HasSubstr(make_message("Target '5[%5]'s type does not match " + "Source '2[%uint]'s type."))); } -TEST_F(ValidateIdWithMessage, OpCopyMemoryVoidTarget) { +TEST_P(ValidateIdWithMessage, OpCopyMemoryVoidTarget) { const std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -3242,12 +3456,13 @@ OpFunctionEnd CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target operand '7[%7]' cannot be a void " - "pointer.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("Target operand '7[%7]' cannot be a void " + "pointer."))); } -TEST_F(ValidateIdWithMessage, OpCopyMemoryVoidSource) { +TEST_P(ValidateIdWithMessage, OpCopyMemoryVoidSource) { const std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -3265,12 +3480,13 @@ OpFunctionEnd CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Source operand '7[%7]' cannot be a void " - "pointer.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("Source operand '7[%7]' cannot be a void " + "pointer."))); } -TEST_F(ValidateIdWithMessage, OpCopyMemorySizedGood) { +TEST_P(ValidateIdWithMessage, OpCopyMemorySizedGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -3288,7 +3504,7 @@ TEST_F(ValidateIdWithMessage, OpCopyMemorySizedGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpCopyMemorySizedTargetBad) { +TEST_P(ValidateIdWithMessage, OpCopyMemorySizedTargetBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -3305,9 +3521,10 @@ TEST_F(ValidateIdWithMessage, OpCopyMemorySizedTargetBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target operand '5[%uint_4]' is not a pointer.")); + HasSubstr(make_message( + "Target operand '5[%uint_4]' is not a pointer."))); } -TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSourceBad) { +TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSourceBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -3324,9 +3541,10 @@ TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSourceBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Source operand '5[%uint_4]' is not a pointer.")); + HasSubstr(make_message( + "Source operand '5[%uint_4]' is not a pointer."))); } -TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSizeBad) { +TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -3343,11 +3561,11 @@ TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSizeBad) { OpFunctionEnd)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Size operand '6[%6]' must be a scalar integer type.")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "Size operand '6[%6]' must be a scalar integer type."))); } -TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSizeTypeBad) { +TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -3366,13 +3584,13 @@ TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSizeTypeBad) { OpFunctionEnd)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Size operand '9[%float_1]' must be a scalar integer " - "type.")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "Size operand '9[%float_1]' must be a scalar integer " + "type."))); } -TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantNull) { +TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantNull) { const std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -3392,12 +3610,13 @@ OpFunctionEnd CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Size operand '3[%3]' cannot be a constant " - "zero.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("Size operand '3[%3]' cannot be a constant " + "zero."))); } -TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantZero) { +TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantZero) { const std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -3418,11 +3637,12 @@ OpFunctionEnd CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Size operand '3[%uint_0]' cannot be a constant " - "zero.")); + HasSubstr(make_message( + "Size operand '3[%uint_0]' cannot be a constant " + "zero."))); } -TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantZero64) { +TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantZero64) { const std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 64 0 @@ -3443,11 +3663,12 @@ OpFunctionEnd CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Size operand '3[%ulong_0]' cannot be a constant " - "zero.")); + HasSubstr(make_message( + "Size operand '3[%ulong_0]' cannot be a constant " + "zero."))); } -TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantNegative) { +TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantNegative) { const std::string spirv = kNoKernelGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 1 @@ -3467,13 +3688,13 @@ OpFunctionEnd CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Size operand '3[%int_n1]' cannot have the sign bit set " - "to 1.")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "Size operand '3[%int_n1]' cannot have the sign bit set " + "to 1."))); } -TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantNegative64) { +TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantNegative64) { const std::string spirv = kNoKernelGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 64 1 @@ -3495,11 +3716,12 @@ OpFunctionEnd EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("Size operand '3[%long_n1]' cannot have the sign bit set " - "to 1.")); + HasSubstr(make_message( + "Size operand '3[%long_n1]' cannot have the sign bit set " + "to 1."))); } -TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSizeUnsignedNegative) { +TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeUnsignedNegative) { const std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -3521,7 +3743,7 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSizeUnsignedNegative64) { +TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeUnsignedNegative64) { const std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 64 0 @@ -3656,7 +3878,7 @@ OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 1[%void] cannot be a " + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand '1[%void]' cannot be a " "type")); } @@ -3675,7 +3897,7 @@ OpFunctionEnd CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Operand 8[%_ptr_Private_float] cannot be a type")); + HasSubstr("Operand '8[%_ptr_Private_float]' cannot be a type")); } // Invalid: The storage class of Base and Result do not match. @@ -4100,7 +4322,7 @@ INSTANTIATE_TEST_SUITE_P( // TODO: OpImagePointer // TODO: OpGenericPtrMemSemantics -TEST_F(ValidateIdWithMessage, OpFunctionGood) { +TEST_P(ValidateIdWithMessage, OpFunctionGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -4112,7 +4334,7 @@ TEST_F(ValidateIdWithMessage, OpFunctionGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpFunctionResultTypeBad) { +TEST_P(ValidateIdWithMessage, OpFunctionResultTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -4124,12 +4346,13 @@ TEST_F(ValidateIdWithMessage, OpFunctionResultTypeBad) { OpFunctionEnd)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpFunction Result Type '2[%uint]' does not " - "match the Function Type's return type " - "'1[%void]'.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("OpFunction Result Type '2[%uint]' does not " + "match the Function Type's return type " + "'1[%void]'."))); } -TEST_F(ValidateIdWithMessage, OpReturnValueTypeBad) { +TEST_P(ValidateIdWithMessage, OpReturnValueTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 %2 = OpTypeFloat 32 @@ -4142,10 +4365,11 @@ TEST_F(ValidateIdWithMessage, OpReturnValueTypeBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpReturnValue Value '3[%float_0]'s type does " - "not match OpFunction's return type.")); + HasSubstr(make_message( + "OpReturnValue Value '3[%float_0]'s type does " + "not match OpFunction's return type."))); } -TEST_F(ValidateIdWithMessage, OpFunctionFunctionTypeBad) { +TEST_P(ValidateIdWithMessage, OpFunctionFunctionTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -4155,13 +4379,13 @@ TEST_F(ValidateIdWithMessage, OpFunctionFunctionTypeBad) { OpFunctionEnd)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpFunction Function Type '2[%uint]' is not a function " - "type.")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "OpFunction Function Type '2[%uint]' is not a function " + "type."))); } -TEST_F(ValidateIdWithMessage, OpFunctionUseBad) { +TEST_P(ValidateIdWithMessage, OpFunctionUseBad) { const std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 %2 = OpTypeFunction %1 @@ -4173,11 +4397,12 @@ OpFunctionEnd CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Invalid use of function result id 3[%3].")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("Invalid use of function result id '3[%3]'."))); } -TEST_F(ValidateIdWithMessage, OpFunctionParameterGood) { +TEST_P(ValidateIdWithMessage, OpFunctionParameterGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -4190,7 +4415,7 @@ TEST_F(ValidateIdWithMessage, OpFunctionParameterGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpFunctionParameterMultipleGood) { +TEST_P(ValidateIdWithMessage, OpFunctionParameterMultipleGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -4204,7 +4429,7 @@ TEST_F(ValidateIdWithMessage, OpFunctionParameterMultipleGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpFunctionParameterResultTypeBad) { +TEST_P(ValidateIdWithMessage, OpFunctionParameterResultTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -4218,11 +4443,12 @@ TEST_F(ValidateIdWithMessage, OpFunctionParameterResultTypeBad) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("OpFunctionParameter Result Type '1[%void]' does not " - "match the OpTypeFunction parameter type of the same index.")); + HasSubstr(make_message( + "OpFunctionParameter Result Type '1[%void]' does not " + "match the OpTypeFunction parameter type of the same index."))); } -TEST_F(ValidateIdWithMessage, OpFunctionCallGood) { +TEST_P(ValidateIdWithMessage, OpFunctionCallGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -4244,7 +4470,7 @@ TEST_F(ValidateIdWithMessage, OpFunctionCallGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpFunctionCallResultTypeBad) { +TEST_P(ValidateIdWithMessage, OpFunctionCallResultTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -4266,12 +4492,13 @@ TEST_F(ValidateIdWithMessage, OpFunctionCallResultTypeBad) { OpFunctionEnd)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpFunctionCall Result Type '1[%void]'s type " - "does not match Function '2[%uint]'s return " - "type.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("OpFunctionCall Result Type '1[%void]'s type " + "does not match Function '2[%uint]'s return " + "type."))); } -TEST_F(ValidateIdWithMessage, OpFunctionCallFunctionBad) { +TEST_P(ValidateIdWithMessage, OpFunctionCallFunctionBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -4287,10 +4514,11 @@ TEST_F(ValidateIdWithMessage, OpFunctionCallFunctionBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpFunctionCall Function '5[%uint_42]' is not a " - "function.")); + HasSubstr(make_message( + "OpFunctionCall Function '5[%uint_42]' is not a " + "function."))); } -TEST_F(ValidateIdWithMessage, OpFunctionCallArgumentTypeBad) { +TEST_P(ValidateIdWithMessage, OpFunctionCallArgumentTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -4316,14 +4544,15 @@ TEST_F(ValidateIdWithMessage, OpFunctionCallArgumentTypeBad) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpFunctionCall Argument '7[%float_3_1400001]'s " - "type does not match Function '2[%uint]'s " - "parameter type.")); + HasSubstr(make_message( + "OpFunctionCall Argument '7[%float_3_1400001]'s " + "type does not match Function '2[%uint]'s " + "parameter type."))); } // Valid: OpSampledImage result is used in the same block by // OpImageSampleImplictLod -TEST_F(ValidateIdWithMessage, OpSampledImageGood) { +TEST_P(ValidateIdWithMessage, OpSampledImageGood) { std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"( %smpld_img = OpSampledImage %sampled_image_type %image_inst %sampler_inst %si_lod = OpImageSampleImplicitLod %v4float %smpld_img %const_vec_1_1 @@ -4335,7 +4564,7 @@ TEST_F(ValidateIdWithMessage, OpSampledImageGood) { // Invalid: OpSampledImage result is defined in one block and used in a // different block. -TEST_F(ValidateIdWithMessage, OpSampledImageUsedInDifferentBlockBad) { +TEST_P(ValidateIdWithMessage, OpSampledImageUsedInDifferentBlockBad) { std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"( %smpld_img = OpSampledImage %sampled_image_type %image_inst %sampler_inst OpBranch %label_2 @@ -4347,10 +4576,11 @@ OpFunctionEnd)"; EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("All OpSampledImage instructions must be in the same block in " - "which their Result are consumed. OpSampledImage Result " - "Type '23[%23]' has a consumer in a different basic " - "block. The consumer instruction is '25[%25]'.")); + HasSubstr(make_message( + "All OpSampledImage instructions must be in the same block in " + "which their Result are consumed. OpSampledImage Result " + "Type '23[%23]' has a consumer in a different basic " + "block. The consumer instruction is '25[%25]'."))); } // Invalid: OpSampledImage result is used by OpSelect @@ -4361,7 +4591,7 @@ OpFunctionEnd)"; // updated, the error message for this test may change. // // Disabled since OpSelect catches this now. -TEST_F(ValidateIdWithMessage, DISABLED_OpSampledImageUsedInOpSelectBad) { +TEST_P(ValidateIdWithMessage, DISABLED_OpSampledImageUsedInOpSelectBad) { std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"( %smpld_img = OpSampledImage %sampled_image_type %image_inst %sampler_inst %select_img = OpSelect %sampled_image_type %spec_true %smpld_img %smpld_img @@ -4370,12 +4600,13 @@ OpFunctionEnd)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Result from OpSampledImage instruction must not " - "appear as operands of OpSelect. Found result " - "'23' as an operand of '24'.")); + HasSubstr(make_message( + "Result from OpSampledImage instruction must not " + "appear as operands of OpSelect. Found result " + "'23' as an operand of '24'."))); } -TEST_F(ValidateIdWithMessage, OpCopyObjectSampledImageGood) { +TEST_P(ValidateIdWithMessage, OpCopyObjectSampledImageGood) { std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"( %smpld_img = OpSampledImage %sampled_image_type %image_inst %sampler_inst %smpld_img2 = OpCopyObject %sampled_image_type %smpld_img @@ -4388,7 +4619,7 @@ OpFunctionEnd)"; // Valid: Get a float in a matrix using CompositeExtract. // Valid: Insert float into a matrix using CompositeInsert. -TEST_F(ValidateIdWithMessage, CompositeExtractInsertGood) { +TEST_P(ValidateIdWithMessage, CompositeExtractInsertGood) { std::ostringstream spirv; spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup << std::endl; spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl; @@ -4404,7 +4635,7 @@ TEST_F(ValidateIdWithMessage, CompositeExtractInsertGood) { } #if 0 -TEST_F(ValidateIdWithMessage, OpFunctionCallArgumentCountBar) { +TEST_P(ValidateIdWithMessage, OpFunctionCallArgumentCountBar) { const char *spirv = R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -4471,7 +4702,7 @@ TEST_F(ValidateIdWithMessage, OpFunctionCallArgumentCountBar) { // TODO: OpVectorExtractDynamic // TODO: OpVectorInsertDynamic -TEST_F(ValidateIdWithMessage, OpVectorShuffleIntGood) { +TEST_P(ValidateIdWithMessage, OpVectorShuffleIntGood) { std::string spirv = kGLSL450MemoryModel + R"( %int = OpTypeInt 32 0 %ivec3 = OpTypeVector %int 3 @@ -4494,7 +4725,7 @@ TEST_F(ValidateIdWithMessage, OpVectorShuffleIntGood) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpVectorShuffleFloatGood) { +TEST_P(ValidateIdWithMessage, OpVectorShuffleFloatGood) { std::string spirv = kGLSL450MemoryModel + R"( %float = OpTypeFloat 32 %vec2 = OpTypeVector %float 2 @@ -4520,7 +4751,7 @@ TEST_F(ValidateIdWithMessage, OpVectorShuffleFloatGood) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpVectorShuffleScalarResultType) { +TEST_P(ValidateIdWithMessage, OpVectorShuffleScalarResultType) { std::string spirv = kGLSL450MemoryModel + R"( %float = OpTypeFloat 32 %vec2 = OpTypeVector %float 2 @@ -4538,12 +4769,12 @@ TEST_F(ValidateIdWithMessage, OpVectorShuffleScalarResultType) { OpFunctionEnd)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Result Type of OpVectorShuffle must be OpTypeVector.")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "Result Type of OpVectorShuffle must be OpTypeVector."))); } -TEST_F(ValidateIdWithMessage, OpVectorShuffleComponentCount) { +TEST_P(ValidateIdWithMessage, OpVectorShuffleComponentCount) { std::string spirv = kGLSL450MemoryModel + R"( %int = OpTypeInt 32 0 %ivec3 = OpTypeVector %int 3 @@ -4562,13 +4793,13 @@ TEST_F(ValidateIdWithMessage, OpVectorShuffleComponentCount) { OpFunctionEnd)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpVectorShuffle component literals count does not match " - "Result Type '2[%v3uint]'s vector component count.")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "OpVectorShuffle component literals count does not match " + "Result Type '2[%v3uint]'s vector component count."))); } -TEST_F(ValidateIdWithMessage, OpVectorShuffleVector1Type) { +TEST_P(ValidateIdWithMessage, OpVectorShuffleVector1Type) { std::string spirv = kGLSL450MemoryModel + R"( %int = OpTypeInt 32 0 %ivec2 = OpTypeVector %int 2 @@ -4585,11 +4816,12 @@ TEST_F(ValidateIdWithMessage, OpVectorShuffleVector1Type) { OpFunctionEnd)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("The type of Vector 1 must be OpTypeVector.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("The type of Vector 1 must be OpTypeVector."))); } -TEST_F(ValidateIdWithMessage, OpVectorShuffleVector2Type) { +TEST_P(ValidateIdWithMessage, OpVectorShuffleVector2Type) { std::string spirv = kGLSL450MemoryModel + R"( %int = OpTypeInt 32 0 %ivec2 = OpTypeVector %int 2 @@ -4607,11 +4839,12 @@ TEST_F(ValidateIdWithMessage, OpVectorShuffleVector2Type) { OpFunctionEnd)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("The type of Vector 2 must be OpTypeVector.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("The type of Vector 2 must be OpTypeVector."))); } -TEST_F(ValidateIdWithMessage, OpVectorShuffleVector1ComponentType) { +TEST_P(ValidateIdWithMessage, OpVectorShuffleVector1ComponentType) { std::string spirv = kGLSL450MemoryModel + R"( %int = OpTypeInt 32 0 %ivec3 = OpTypeVector %int 3 @@ -4640,11 +4873,12 @@ TEST_F(ValidateIdWithMessage, OpVectorShuffleVector1ComponentType) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("The Component Type of Vector 1 must be the same as " - "ResultType.")); + HasSubstr(make_message( + "The Component Type of Vector 1 must be the same as " + "ResultType."))); } -TEST_F(ValidateIdWithMessage, OpVectorShuffleVector2ComponentType) { +TEST_P(ValidateIdWithMessage, OpVectorShuffleVector2ComponentType) { std::string spirv = kGLSL450MemoryModel + R"( %int = OpTypeInt 32 0 %ivec3 = OpTypeVector %int 3 @@ -4673,11 +4907,12 @@ TEST_F(ValidateIdWithMessage, OpVectorShuffleVector2ComponentType) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("The Component Type of Vector 2 must be the same as " - "ResultType.")); + HasSubstr(make_message( + "The Component Type of Vector 2 must be the same as " + "ResultType."))); } -TEST_F(ValidateIdWithMessage, OpVectorShuffleLiterals) { +TEST_P(ValidateIdWithMessage, OpVectorShuffleLiterals) { std::string spirv = kGLSL450MemoryModel + R"( %float = OpTypeFloat 32 %vec2 = OpTypeVector %float 2 @@ -4703,9 +4938,9 @@ TEST_F(ValidateIdWithMessage, OpVectorShuffleLiterals) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr( + HasSubstr(make_message( "Component index 8 is out of bounds for combined (Vector1 + Vector2) " - "size of 5.")); + "size of 5."))); } // TODO: OpCompositeConstruct @@ -4792,7 +5027,7 @@ TEST_F(ValidateIdWithMessage, OpVectorShuffleLiterals) { // TODO: OpSelectionMerge // TODO: OpBranch -TEST_F(ValidateIdWithMessage, OpPhiNotAType) { +TEST_P(ValidateIdWithMessage, OpPhiNotAType) { std::string spirv = kOpenCLMemoryModel32 + R"( %2 = OpTypeBool %3 = OpConstantTrue %2 @@ -4809,11 +5044,12 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("ID 3[%true] is not a type " - "id")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message("ID '3[%true]' is not a type " + "id"))); } -TEST_F(ValidateIdWithMessage, OpPhiSamePredecessor) { +TEST_P(ValidateIdWithMessage, OpPhiSamePredecessor) { std::string spirv = kOpenCLMemoryModel32 + R"( %2 = OpTypeBool %3 = OpConstantTrue %2 @@ -4832,7 +5068,7 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpPhiOddArgumentNumber) { +TEST_P(ValidateIdWithMessage, OpPhiOddArgumentNumber) { std::string spirv = kOpenCLMemoryModel32 + R"( %2 = OpTypeBool %3 = OpConstantTrue %2 @@ -4849,12 +5085,13 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpPhi does not have an equal number of incoming " - "values and basic blocks.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("OpPhi does not have an equal number of incoming " + "values and basic blocks."))); } -TEST_F(ValidateIdWithMessage, OpPhiTooFewPredecessors) { +TEST_P(ValidateIdWithMessage, OpPhiTooFewPredecessors) { std::string spirv = kOpenCLMemoryModel32 + R"( %2 = OpTypeBool %3 = OpConstantTrue %2 @@ -4872,11 +5109,12 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpPhi's number of incoming blocks (0) does not match " - "block's predecessor count (1).")); + HasSubstr(make_message( + "OpPhi's number of incoming blocks (0) does not match " + "block's predecessor count (1)."))); } -TEST_F(ValidateIdWithMessage, OpPhiTooManyPredecessors) { +TEST_P(ValidateIdWithMessage, OpPhiTooManyPredecessors) { std::string spirv = kOpenCLMemoryModel32 + R"( %2 = OpTypeBool %3 = OpConstantTrue %2 @@ -4896,11 +5134,12 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpPhi's number of incoming blocks (2) does not match " - "block's predecessor count (1).")); + HasSubstr(make_message( + "OpPhi's number of incoming blocks (2) does not match " + "block's predecessor count (1)."))); } -TEST_F(ValidateIdWithMessage, OpPhiMismatchedTypes) { +TEST_P(ValidateIdWithMessage, OpPhiMismatchedTypes) { std::string spirv = kOpenCLMemoryModel32 + R"( %2 = OpTypeBool %3 = OpConstantTrue %2 @@ -4922,12 +5161,13 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpPhi's result type 2[%bool] does not match " - "incoming value 6[%uint_0] type " - "5[%uint].")); + HasSubstr(make_message( + "OpPhi's result type '2[%bool]' does not match " + "incoming value '6[%uint_0]' type " + "'5[%uint]'."))); } -TEST_F(ValidateIdWithMessage, OpPhiPredecessorNotABlock) { +TEST_P(ValidateIdWithMessage, OpPhiPredecessorNotABlock) { std::string spirv = kOpenCLMemoryModel32 + R"( %2 = OpTypeBool %3 = OpConstantTrue %2 @@ -4949,11 +5189,12 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpPhi's incoming basic block 3[%true] is not an " - "OpLabel.")); + HasSubstr(make_message( + "OpPhi's incoming basic block '3[%true]' is not an " + "OpLabel."))); } -TEST_F(ValidateIdWithMessage, OpPhiNotAPredecessor) { +TEST_P(ValidateIdWithMessage, OpPhiNotAPredecessor) { std::string spirv = kOpenCLMemoryModel32 + R"( %2 = OpTypeBool %3 = OpConstantTrue %2 @@ -4975,11 +5216,12 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpPhi's incoming basic block 9[%9] is not a " - "predecessor of 8[%8].")); + HasSubstr(make_message( + "OpPhi's incoming basic block '9[%9]' is not a " + "predecessor of '8[%8]'."))); } -TEST_F(ValidateIdWithMessage, OpBranchConditionalGood) { +TEST_P(ValidateIdWithMessage, OpBranchConditionalGood) { std::string spirv = BranchConditionalSetup + R"( %branch_cond = OpINotEqual %bool %i0 %i1 OpSelectionMerge %end None @@ -4990,7 +5232,7 @@ TEST_F(ValidateIdWithMessage, OpBranchConditionalGood) { EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); } -TEST_F(ValidateIdWithMessage, OpBranchConditionalWithWeightsGood) { +TEST_P(ValidateIdWithMessage, OpBranchConditionalWithWeightsGood) { std::string spirv = BranchConditionalSetup + R"( %branch_cond = OpINotEqual %bool %i0 %i1 OpSelectionMerge %end None @@ -5001,7 +5243,7 @@ TEST_F(ValidateIdWithMessage, OpBranchConditionalWithWeightsGood) { EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); } -TEST_F(ValidateIdWithMessage, OpBranchConditional_CondIsScalarInt) { +TEST_P(ValidateIdWithMessage, OpBranchConditional_CondIsScalarInt) { std::string spirv = BranchConditionalSetup + R"( OpSelectionMerge %end None OpBranchConditional %i0 %target_t %target_f @@ -5011,11 +5253,11 @@ TEST_F(ValidateIdWithMessage, OpBranchConditional_CondIsScalarInt) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr( - "Condition operand for OpBranchConditional must be of boolean type")); + HasSubstr(make_message("Condition operand for OpBranchConditional must " + "be of boolean type"))); } -TEST_F(ValidateIdWithMessage, OpBranchConditional_TrueTargetIsNotLabel) { +TEST_P(ValidateIdWithMessage, OpBranchConditional_TrueTargetIsNotLabel) { std::string spirv = BranchConditionalSetup + R"( OpSelectionMerge %end None OpBranchConditional %true %i0 %target_f @@ -5024,11 +5266,12 @@ TEST_F(ValidateIdWithMessage, OpBranchConditional_TrueTargetIsNotLabel) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("The 'True Label' operand for OpBranchConditional must " - "be the ID of an OpLabel instruction")); + HasSubstr(make_message( + "The 'True Label' operand for OpBranchConditional must " + "be the ID of an OpLabel instruction"))); } -TEST_F(ValidateIdWithMessage, OpBranchConditional_FalseTargetIsNotLabel) { +TEST_P(ValidateIdWithMessage, OpBranchConditional_FalseTargetIsNotLabel) { std::string spirv = BranchConditionalSetup + R"( OpSelectionMerge %end None OpBranchConditional %true %target_t %i0 @@ -5037,11 +5280,12 @@ TEST_F(ValidateIdWithMessage, OpBranchConditional_FalseTargetIsNotLabel) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("The 'False Label' operand for OpBranchConditional " - "must be the ID of an OpLabel instruction")); + HasSubstr(make_message( + "The 'False Label' operand for OpBranchConditional " + "must be the ID of an OpLabel instruction"))); } -TEST_F(ValidateIdWithMessage, OpBranchConditional_NotEnoughWeights) { +TEST_P(ValidateIdWithMessage, OpBranchConditional_NotEnoughWeights) { std::string spirv = BranchConditionalSetup + R"( %branch_cond = OpINotEqual %bool %i0 %i1 OpSelectionMerge %end None @@ -5050,12 +5294,12 @@ TEST_F(ValidateIdWithMessage, OpBranchConditional_NotEnoughWeights) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpBranchConditional requires either 3 or 5 parameters")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "OpBranchConditional requires either 3 or 5 parameters"))); } -TEST_F(ValidateIdWithMessage, OpBranchConditional_TooManyWeights) { +TEST_P(ValidateIdWithMessage, OpBranchConditional_TooManyWeights) { std::string spirv = BranchConditionalSetup + R"( %branch_cond = OpINotEqual %bool %i0 %i1 OpSelectionMerge %end None @@ -5064,25 +5308,26 @@ TEST_F(ValidateIdWithMessage, OpBranchConditional_TooManyWeights) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpBranchConditional requires either 3 or 5 parameters")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "OpBranchConditional requires either 3 or 5 parameters"))); } -TEST_F(ValidateIdWithMessage, OpBranchConditional_ConditionIsAType) { +TEST_P(ValidateIdWithMessage, OpBranchConditional_ConditionIsAType) { std::string spirv = BranchConditionalSetup + R"( OpBranchConditional %bool %target_t %target_f )" + BranchConditionalTail; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 3[%bool] cannot be a " - "type")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message("Operand '3[%bool]' cannot be a " + "type"))); } // TODO: OpSwitch -TEST_F(ValidateIdWithMessage, OpReturnValueConstantGood) { +TEST_P(ValidateIdWithMessage, OpReturnValueConstantGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -5096,7 +5341,7 @@ TEST_F(ValidateIdWithMessage, OpReturnValueConstantGood) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpReturnValueVariableGood) { +TEST_P(ValidateIdWithMessage, OpReturnValueVariableGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 ;10 @@ -5113,7 +5358,7 @@ TEST_F(ValidateIdWithMessage, OpReturnValueVariableGood) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpReturnValueExpressionGood) { +TEST_P(ValidateIdWithMessage, OpReturnValueExpressionGood) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -5128,7 +5373,7 @@ TEST_F(ValidateIdWithMessage, OpReturnValueExpressionGood) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpReturnValueIsType) { +TEST_P(ValidateIdWithMessage, OpReturnValueIsType) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -5139,11 +5384,12 @@ TEST_F(ValidateIdWithMessage, OpReturnValueIsType) { OpFunctionEnd)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 1[%void] cannot be a " - "type")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message("Operand '1[%void]' cannot be a " + "type"))); } -TEST_F(ValidateIdWithMessage, OpReturnValueIsLabel) { +TEST_P(ValidateIdWithMessage, OpReturnValueIsLabel) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -5155,10 +5401,10 @@ TEST_F(ValidateIdWithMessage, OpReturnValueIsLabel) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Operand 5[%5] requires a type")); + HasSubstr(make_message("Operand '5[%5]' requires a type"))); } -TEST_F(ValidateIdWithMessage, OpReturnValueIsVoid) { +TEST_P(ValidateIdWithMessage, OpReturnValueIsVoid) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -5170,13 +5416,13 @@ TEST_F(ValidateIdWithMessage, OpReturnValueIsVoid) { OpFunctionEnd)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpReturnValue value's type '1[%void]' is missing or " - "void.")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "OpReturnValue value's type '1[%void]' is missing or " + "void."))); } -TEST_F(ValidateIdWithMessage, OpReturnValueIsVariableInPhysical) { +TEST_P(ValidateIdWithMessage, OpReturnValueIsVariableInPhysical) { // It's valid to return a pointer in a physical addressing model. std::string spirv = kOpCapabilitySetup + R"( OpMemoryModel Physical32 OpenCL @@ -5193,7 +5439,7 @@ TEST_F(ValidateIdWithMessage, OpReturnValueIsVariableInPhysical) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpReturnValueIsVariableInLogical) { +TEST_P(ValidateIdWithMessage, OpReturnValueIsVariableInLogical) { // It's invalid to return a pointer in a physical addressing model. std::string spirv = kOpCapabilitySetup + R"( OpMemoryModel Logical GLSL450 @@ -5208,15 +5454,16 @@ TEST_F(ValidateIdWithMessage, OpReturnValueIsVariableInLogical) { OpFunctionEnd)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpReturnValue value's type " - "'3[%_ptr_Function_uint]' is a pointer, which is " - "invalid in the Logical addressing model.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("OpReturnValue value's type " + "'3[%_ptr_Function_uint]' is a pointer, which is " + "invalid in the Logical addressing model."))); } // With the VariablePointer Capability, the return value of a function is // allowed to be a pointer. -TEST_F(ValidateIdWithMessage, OpReturnValueVarPtrGood) { +TEST_P(ValidateIdWithMessage, OpReturnValueVarPtrGood) { std::ostringstream spirv; createVariablePointerSpirvProgram(&spirv, "" /* Instructions to add to "main" */, @@ -5230,7 +5477,7 @@ TEST_F(ValidateIdWithMessage, OpReturnValueVarPtrGood) { // *not* allowed to be a pointer. // Disabled since using OpSelect with pointers without VariablePointers will // fail LogicalsPass. -TEST_F(ValidateIdWithMessage, DISABLED_OpReturnValueVarPtrBad) { +TEST_P(ValidateIdWithMessage, DISABLED_OpReturnValueVarPtrBad) { std::ostringstream spirv; createVariablePointerSpirvProgram(&spirv, "" /* Instructions to add to "main" */, @@ -5239,13 +5486,14 @@ TEST_F(ValidateIdWithMessage, DISABLED_OpReturnValueVarPtrBad) { CompileSuccessfully(spirv.str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpReturnValue value's type '7' is a pointer, " - "which is invalid in the Logical addressing model.")); + HasSubstr(make_message( + "OpReturnValue value's type '7' is a pointer, " + "which is invalid in the Logical addressing model."))); } // TODO: enable when this bug is fixed: // https://cvs.khronos.org/bugzilla/show_bug.cgi?id=15404 -TEST_F(ValidateIdWithMessage, DISABLED_OpReturnValueIsFunction) { +TEST_P(ValidateIdWithMessage, DISABLED_OpReturnValueIsFunction) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeInt 32 0 @@ -5258,17 +5506,18 @@ TEST_F(ValidateIdWithMessage, DISABLED_OpReturnValueIsFunction) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, UndefinedTypeId) { +TEST_P(ValidateIdWithMessage, UndefinedTypeId) { std::string spirv = kGLSL450MemoryModel + R"( %s = OpTypeStruct %i32 )"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Operand 2[%2] requires a previous definition")); + HasSubstr(make_message( + "Operand '2[%2]' requires a previous definition"))); } -TEST_F(ValidateIdWithMessage, UndefinedIdScope) { +TEST_P(ValidateIdWithMessage, UndefinedIdScope) { std::string spirv = kGLSL450MemoryModel + R"( %u32 = OpTypeInt 32 0 %memsem = OpConstant %u32 0 @@ -5282,11 +5531,12 @@ TEST_F(ValidateIdWithMessage, UndefinedIdScope) { )"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("ID 7[%7] has not been " - "defined")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message("ID '7[%7]' has not been " + "defined"))); } -TEST_F(ValidateIdWithMessage, UndefinedIdMemSem) { +TEST_P(ValidateIdWithMessage, UndefinedIdMemSem) { std::string spirv = kGLSL450MemoryModel + R"( %u32 = OpTypeInt 32 0 %scope = OpConstant %u32 0 @@ -5300,11 +5550,12 @@ TEST_F(ValidateIdWithMessage, UndefinedIdMemSem) { )"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("ID 7[%7] has not been " - "defined")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message("ID '7[%7]' has not been " + "defined"))); } -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, KernelOpEntryPointAndOpInBoundsPtrAccessChainGood) { std::string spirv = kOpenCLMemoryModel32 + R"( OpEntryPoint Kernel %2 "simple_kernel" @@ -5337,7 +5588,7 @@ TEST_F(ValidateIdWithMessage, EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpPtrAccessChainGood) { +TEST_P(ValidateIdWithMessage, OpPtrAccessChainGood) { std::string spirv = kOpenCLMemoryModel64 + R"( OpEntryPoint Kernel %2 "another_kernel" OpSource OpenCL_C 200000 @@ -5372,7 +5623,7 @@ TEST_F(ValidateIdWithMessage, OpPtrAccessChainGood) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, StgBufOpPtrAccessChainGood) { +TEST_P(ValidateIdWithMessage, StgBufOpPtrAccessChainGood) { std::string spirv = R"( OpCapability Shader OpCapability Linkage @@ -5399,7 +5650,7 @@ TEST_F(ValidateIdWithMessage, StgBufOpPtrAccessChainGood) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpLoadBitcastPointerGood) { +TEST_P(ValidateIdWithMessage, OpLoadBitcastPointerGood) { std::string spirv = kOpenCLMemoryModel64 + R"( %2 = OpTypeVoid %3 = OpTypeInt 32 0 @@ -5417,7 +5668,7 @@ TEST_F(ValidateIdWithMessage, OpLoadBitcastPointerGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpLoadBitcastNonPointerBad) { +TEST_P(ValidateIdWithMessage, OpLoadBitcastNonPointerBad) { std::string spirv = kOpenCLMemoryModel64 + R"( %2 = OpTypeVoid %3 = OpTypeInt 32 0 @@ -5434,12 +5685,12 @@ TEST_F(ValidateIdWithMessage, OpLoadBitcastNonPointerBad) { OpFunctionEnd)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpLoad type for pointer '11[%11]' is not a pointer " - "type.")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "OpLoad type for pointer '11[%11]' is not a pointer " + "type."))); } -TEST_F(ValidateIdWithMessage, OpStoreBitcastPointerGood) { +TEST_P(ValidateIdWithMessage, OpStoreBitcastPointerGood) { std::string spirv = kOpenCLMemoryModel64 + R"( %2 = OpTypeVoid %3 = OpTypeInt 32 0 @@ -5458,7 +5709,7 @@ TEST_F(ValidateIdWithMessage, OpStoreBitcastPointerGood) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateIdWithMessage, OpStoreBitcastNonPointerBad) { +TEST_P(ValidateIdWithMessage, OpStoreBitcastNonPointerBad) { std::string spirv = kOpenCLMemoryModel64 + R"( %2 = OpTypeVoid %3 = OpTypeInt 32 0 @@ -5475,15 +5726,15 @@ TEST_F(ValidateIdWithMessage, OpStoreBitcastNonPointerBad) { OpFunctionEnd)"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpStore type for pointer '11[%11]' is not a pointer " - "type.")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "OpStore type for pointer '11[%11]' is not a pointer " + "type."))); } // Result resulting from an instruction within a function may not be used // outside that function. -TEST_F(ValidateIdWithMessage, ResultIdUsedOutsideOfFunctionBad) { +TEST_P(ValidateIdWithMessage, ResultIdUsedOutsideOfFunctionBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -5502,14 +5753,13 @@ OpFunctionEnd )"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "ID 7[%7] defined in block 6[%6] does not dominate its use in block " - "9[%9]")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message("ID '7[%7]' defined in block '6[%6]' does " + "not dominate its use in block " + "'9[%9]'"))); } -TEST_F(ValidateIdWithMessage, SpecIdTargetNotSpecializationConstant) { +TEST_P(ValidateIdWithMessage, SpecIdTargetNotSpecializationConstant) { std::string spirv = kGLSL450MemoryModel + R"( OpDecorate %1 SpecId 200 %void = OpTypeVoid @@ -5523,13 +5773,14 @@ OpFunctionEnd )"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpDecorate SpecId decoration target " - "'1[%uint_3]' is not a scalar specialization " - "constant.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("SpecId decoration on target " + "'1[%uint_3]' must be a scalar specialization " + "constant"))); } -TEST_F(ValidateIdWithMessage, SpecIdTargetOpSpecConstantOpBad) { +TEST_P(ValidateIdWithMessage, SpecIdTargetOpSpecConstantOpBad) { std::string spirv = kGLSL450MemoryModel + R"( OpDecorate %1 SpecId 200 %void = OpTypeVoid @@ -5545,12 +5796,13 @@ OpFunctionEnd )"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpDecorate SpecId decoration target '1[%1]' is " - "not a scalar specialization constant.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("SpecId decoration on target '1[%1]' " + "must be a scalar specialization constant"))); } -TEST_F(ValidateIdWithMessage, SpecIdTargetOpSpecConstantCompositeBad) { +TEST_P(ValidateIdWithMessage, SpecIdTargetOpSpecConstantCompositeBad) { std::string spirv = kGLSL450MemoryModel + R"( OpDecorate %1 SpecId 200 %void = OpTypeVoid @@ -5565,12 +5817,13 @@ OpFunctionEnd )"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpDecorate SpecId decoration target '1[%1]' is " - "not a scalar specialization constant.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("SpecId decoration on target '1[%1]' " + "must be a scalar specialization constant"))); } -TEST_F(ValidateIdWithMessage, SpecIdTargetGood) { +TEST_P(ValidateIdWithMessage, SpecIdTargetGood) { std::string spirv = kGLSL450MemoryModel + R"( OpDecorate %3 SpecId 200 OpDecorate %4 SpecId 201 @@ -5591,7 +5844,7 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); } -TEST_F(ValidateIdWithMessage, CorrectErrorForShuffle) { +TEST_P(ValidateIdWithMessage, CorrectErrorForShuffle) { std::string spirv = kGLSL450MemoryModel + R"( %uint = OpTypeInt 32 0 %float = OpTypeFloat 32 @@ -5612,13 +5865,13 @@ TEST_F(ValidateIdWithMessage, CorrectErrorForShuffle) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr( + HasSubstr(make_message( "Component index 4 is out of bounds for combined (Vector1 + Vector2) " - "size of 4.")); + "size of 4."))); EXPECT_EQ(25, getErrorPosition().index); } -TEST_F(ValidateIdWithMessage, VoidStructMember) { +TEST_P(ValidateIdWithMessage, VoidStructMember) { const std::string spirv = kGLSL450MemoryModel + R"( %void = OpTypeVoid %struct = OpTypeStruct %void @@ -5626,11 +5879,12 @@ TEST_F(ValidateIdWithMessage, VoidStructMember) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Structures cannot contain a void type.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("Structures cannot contain a void type."))); } -TEST_F(ValidateIdWithMessage, TypeFunctionBadUse) { +TEST_P(ValidateIdWithMessage, TypeFunctionBadUse) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -5643,10 +5897,11 @@ TEST_F(ValidateIdWithMessage, TypeFunctionBadUse) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Invalid use of function type result id 2[%2].")); + HasSubstr(make_message( + "Invalid use of function type result id '2[%2]'."))); } -TEST_F(ValidateIdWithMessage, BadTypeId) { +TEST_P(ValidateIdWithMessage, BadTypeId) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -5661,11 +5916,12 @@ TEST_F(ValidateIdWithMessage, BadTypeId) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("ID 4[%float_0] is not a type " - "id")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message("ID '4[%float_0]' is not a type " + "id"))); } -TEST_F(ValidateIdWithMessage, VulkanMemoryModelLoadMakePointerVisibleGood) { +TEST_P(ValidateIdWithMessage, VulkanMemoryModelLoadMakePointerVisibleGood) { std::string spirv = R"( OpCapability Shader OpCapability VulkanMemoryModelKHR @@ -5689,7 +5945,7 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, VulkanMemoryModelLoadMakePointerVisibleMissingNonPrivatePointer) { std::string spirv = R"( OpCapability Shader @@ -5712,12 +5968,13 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("NonPrivatePointerKHR must be specified if " - "MakePointerVisibleKHR is specified.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("NonPrivatePointerKHR must be specified if " + "MakePointerVisibleKHR is specified."))); } -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, VulkanMemoryModelLoadNonPrivatePointerBadStorageClass) { std::string spirv = R"( OpCapability Shader @@ -5741,12 +5998,13 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("NonPrivatePointerKHR requires a pointer in Uniform, " - "Workgroup, CrossWorkgroup, Generic, Image or " - "StorageBuffer storage classes.")); + HasSubstr(make_message( + "NonPrivatePointerKHR requires a pointer in Uniform, " + "Workgroup, CrossWorkgroup, Generic, Image or " + "StorageBuffer storage classes."))); } -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, VulkanMemoryModelLoadMakePointerAvailableCannotBeUsed) { std::string spirv = R"( OpCapability Shader @@ -5770,10 +6028,11 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("MakePointerAvailableKHR cannot be used with OpLoad")); + HasSubstr(make_message( + "MakePointerAvailableKHR cannot be used with OpLoad"))); } -TEST_F(ValidateIdWithMessage, VulkanMemoryModelStoreMakePointerAvailableGood) { +TEST_P(ValidateIdWithMessage, VulkanMemoryModelStoreMakePointerAvailableGood) { std::string spirv = R"( OpCapability Shader OpCapability VulkanMemoryModelKHR @@ -5797,7 +6056,7 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, VulkanMemoryModelStoreMakePointerAvailableMissingNonPrivatePointer) { std::string spirv = R"( OpCapability Shader @@ -5820,12 +6079,13 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("NonPrivatePointerKHR must be specified if " - "MakePointerAvailableKHR is specified.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("NonPrivatePointerKHR must be specified if " + "MakePointerAvailableKHR is specified."))); } -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, VulkanMemoryModelStoreNonPrivatePointerBadStorageClass) { std::string spirv = R"( OpCapability Shader @@ -5849,12 +6109,13 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("NonPrivatePointerKHR requires a pointer in Uniform, " - "Workgroup, CrossWorkgroup, Generic, Image or " - "StorageBuffer storage classes.")); + HasSubstr(make_message( + "NonPrivatePointerKHR requires a pointer in Uniform, " + "Workgroup, CrossWorkgroup, Generic, Image or " + "StorageBuffer storage classes."))); } -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, VulkanMemoryModelStoreMakePointerVisibleCannotBeUsed) { std::string spirv = R"( OpCapability Shader @@ -5878,10 +6139,11 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("MakePointerVisibleKHR cannot be used with OpStore.")); + HasSubstr(make_message( + "MakePointerVisibleKHR cannot be used with OpStore."))); } -TEST_F(ValidateIdWithMessage, VulkanMemoryModelCopyMemoryAvailable) { +TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemoryAvailable) { std::string spirv = R"( OpCapability Shader OpCapability Linkage @@ -5908,7 +6170,7 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } -TEST_F(ValidateIdWithMessage, VulkanMemoryModelCopyMemoryVisible) { +TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemoryVisible) { std::string spirv = R"( OpCapability Shader OpCapability Linkage @@ -5935,7 +6197,7 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } -TEST_F(ValidateIdWithMessage, VulkanMemoryModelCopyMemoryAvailableAndVisible) { +TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemoryAvailableAndVisible) { std::string spirv = R"( OpCapability Shader OpCapability Linkage @@ -5962,7 +6224,7 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemoryAvailableMissingNonPrivatePointer) { std::string spirv = R"( OpCapability Shader @@ -5988,12 +6250,13 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("NonPrivatePointerKHR must be specified if " - "MakePointerAvailableKHR is specified.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("NonPrivatePointerKHR must be specified if " + "MakePointerAvailableKHR is specified."))); } -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemoryVisibleMissingNonPrivatePointer) { std::string spirv = R"( OpCapability Shader @@ -6019,12 +6282,13 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("NonPrivatePointerKHR must be specified if " - "MakePointerVisibleKHR is specified.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("NonPrivatePointerKHR must be specified if " + "MakePointerVisibleKHR is specified."))); } -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemoryAvailableBadStorageClass) { std::string spirv = R"( OpCapability Shader @@ -6051,12 +6315,13 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("NonPrivatePointerKHR requires a pointer in Uniform, " - "Workgroup, CrossWorkgroup, Generic, Image or " - "StorageBuffer storage classes.")); + HasSubstr(make_message( + "NonPrivatePointerKHR requires a pointer in Uniform, " + "Workgroup, CrossWorkgroup, Generic, Image or " + "StorageBuffer storage classes."))); } -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemoryVisibleBadStorageClass) { std::string spirv = R"( OpCapability Shader @@ -6083,12 +6348,13 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("NonPrivatePointerKHR requires a pointer in Uniform, " - "Workgroup, CrossWorkgroup, Generic, Image or " - "StorageBuffer storage classes.")); + HasSubstr(make_message( + "NonPrivatePointerKHR requires a pointer in Uniform, " + "Workgroup, CrossWorkgroup, Generic, Image or " + "StorageBuffer storage classes."))); } -TEST_F(ValidateIdWithMessage, VulkanMemoryModelCopyMemorySizedAvailable) { +TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemorySizedAvailable) { std::string spirv = R"( OpCapability Shader OpCapability Linkage @@ -6116,7 +6382,7 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } -TEST_F(ValidateIdWithMessage, VulkanMemoryModelCopyMemorySizedVisible) { +TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemorySizedVisible) { std::string spirv = R"( OpCapability Shader OpCapability Linkage @@ -6144,7 +6410,7 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemorySizedAvailableAndVisible) { std::string spirv = R"( OpCapability Shader @@ -6173,7 +6439,7 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemorySizedAvailableMissingNonPrivatePointer) { std::string spirv = R"( OpCapability Shader @@ -6200,12 +6466,13 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("NonPrivatePointerKHR must be specified if " - "MakePointerAvailableKHR is specified.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("NonPrivatePointerKHR must be specified if " + "MakePointerAvailableKHR is specified."))); } -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemorySizedVisibleMissingNonPrivatePointer) { std::string spirv = R"( OpCapability Shader @@ -6232,12 +6499,13 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("NonPrivatePointerKHR must be specified if " - "MakePointerVisibleKHR is specified.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(make_message("NonPrivatePointerKHR must be specified if " + "MakePointerVisibleKHR is specified."))); } -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemorySizedAvailableBadStorageClass) { std::string spirv = R"( OpCapability Shader @@ -6265,12 +6533,13 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("NonPrivatePointerKHR requires a pointer in Uniform, " - "Workgroup, CrossWorkgroup, Generic, Image or " - "StorageBuffer storage classes.")); + HasSubstr(make_message( + "NonPrivatePointerKHR requires a pointer in Uniform, " + "Workgroup, CrossWorkgroup, Generic, Image or " + "StorageBuffer storage classes."))); } -TEST_F(ValidateIdWithMessage, +TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemorySizedVisibleBadStorageClass) { std::string spirv = R"( OpCapability Shader @@ -6298,12 +6567,13 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("NonPrivatePointerKHR requires a pointer in Uniform, " - "Workgroup, CrossWorkgroup, Generic, Image or " - "StorageBuffer storage classes.")); + HasSubstr(make_message( + "NonPrivatePointerKHR requires a pointer in Uniform, " + "Workgroup, CrossWorkgroup, Generic, Image or " + "StorageBuffer storage classes."))); } -TEST_F(ValidateIdWithMessage, IdDefInUnreachableBlock1) { +TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock1) { const std::string spirv = kNoKernelGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -6325,11 +6595,12 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("ID 8[%8] defined in block 7[%7] does not dominate its " - "use in block 10[%10]\n %10 = OpLabel")); + HasSubstr(make_message( + "ID '8[%8]' defined in block '7[%7]' does not dominate its " + "use in block '10[%10]'\n %10 = OpLabel"))); } -TEST_F(ValidateIdWithMessage, IdDefInUnreachableBlock2) { +TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock2) { const std::string spirv = kNoKernelGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -6351,11 +6622,12 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("ID 8[%8] defined in block 7[%7] does not dominate its " - "use in block 10[%10]\n %10 = OpLabel")); + HasSubstr(make_message( + "ID '8[%8]' defined in block '7[%7]' does not dominate its " + "use in block '10[%10]'\n %10 = OpLabel"))); } -TEST_F(ValidateIdWithMessage, IdDefInUnreachableBlock3) { +TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock3) { const std::string spirv = kNoKernelGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -6377,11 +6649,12 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("ID 8[%8] defined in block 7[%7] does not dominate its " - "use in block 10[%10]\n %10 = OpLabel")); + HasSubstr(make_message( + "ID '8[%8]' defined in block '7[%7]' does not dominate its " + "use in block '10[%10]'\n %10 = OpLabel"))); } -TEST_F(ValidateIdWithMessage, IdDefInUnreachableBlock4) { +TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock4) { const std::string spirv = kNoKernelGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -6401,7 +6674,7 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } -TEST_F(ValidateIdWithMessage, IdDefInUnreachableBlock5) { +TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock5) { const std::string spirv = kNoKernelGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -6423,7 +6696,7 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } -TEST_F(ValidateIdWithMessage, IdDefInUnreachableBlock6) { +TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock6) { const std::string spirv = kNoKernelGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -6444,11 +6717,12 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("ID 9[%9] defined in block 8[%8] does not dominate its " - "use in block 7[%7]\n %7 = OpLabel")); + HasSubstr(make_message( + "ID '9[%9]' defined in block '8[%8]' does not dominate its " + "use in block '7[%7]'\n %7 = OpLabel"))); } -TEST_F(ValidateIdWithMessage, ReachableDefUnreachableUse) { +TEST_P(ValidateIdWithMessage, ReachableDefUnreachableUse) { const std::string spirv = kNoKernelGLSL450MemoryModel + R"( %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -6468,7 +6742,7 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } -TEST_F(ValidateIdWithMessage, UnreachableDefUsedInPhi) { +TEST_P(ValidateIdWithMessage, UnreachableDefUsedInPhi) { const std::string spirv = kNoKernelGLSL450MemoryModel + R"( %void = OpTypeVoid %3 = OpTypeFunction %void @@ -6496,12 +6770,13 @@ TEST_F(ValidateIdWithMessage, UnreachableDefUsedInPhi) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), - HasSubstr("In OpPhi instruction 14[%14], ID 13[%13] definition does not " - "dominate its parent 7[%7]\n %14 = OpPhi %float %11 %10 %13 " - "%7")); + HasSubstr(make_message( + "In OpPhi instruction '14[%14]', ID '13[%13]' definition does not " + "dominate its parent '7[%7]'\n %14 = OpPhi %float %11 %10 %13 " + "%7"))); } -TEST_F(ValidateIdWithMessage, OpTypeForwardPointerNotAPointerType) { +TEST_P(ValidateIdWithMessage, OpTypeForwardPointerNotAPointerType) { std::string spirv = R"( OpCapability GenericPointer OpCapability VariablePointersStorageBuffer @@ -6520,11 +6795,12 @@ TEST_F(ValidateIdWithMessage, OpTypeForwardPointerNotAPointerType) { CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Pointer type in OpTypeForwardPointer is not a pointer " - "type.\n OpTypeForwardPointer %void CrossWorkgroup")); + HasSubstr(make_message( + "Pointer type in OpTypeForwardPointer is not a pointer " + "type.\n OpTypeForwardPointer %void CrossWorkgroup"))); } -TEST_F(ValidateIdWithMessage, OpTypeForwardPointerWrongStorageClass) { +TEST_P(ValidateIdWithMessage, OpTypeForwardPointerWrongStorageClass) { std::string spirv = R"( OpCapability GenericPointer OpCapability VariablePointersStorageBuffer @@ -6544,14 +6820,14 @@ TEST_F(ValidateIdWithMessage, OpTypeForwardPointerWrongStorageClass) { CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Storage class in OpTypeForwardPointer does not match the " - "pointer definition.\n OpTypeForwardPointer " - "%_ptr_Function_int CrossWorkgroup")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "Storage class in OpTypeForwardPointer does not match the " + "pointer definition.\n OpTypeForwardPointer " + "%_ptr_Function_int CrossWorkgroup"))); } -TEST_F(ValidateIdWithMessage, MissingForwardPointer) { +TEST_P(ValidateIdWithMessage, MissingForwardPointer) { const std::string spirv = R"( OpCapability Linkage OpCapability Shader @@ -6564,12 +6840,42 @@ TEST_F(ValidateIdWithMessage, MissingForwardPointer) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "Operand 3[%_ptr_Uniform__struct_2] requires a previous definition")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message("Operand '3[%_ptr_Uniform__struct_2]' " + "requires a previous definition"))); } +TEST_P(ValidateIdWithMessage, NVBindlessSamplerInStruct) { + std::string spirv = R"( + OpCapability Shader + OpCapability BindlessTextureNV + OpExtension "SPV_NV_bindless_texture" + OpMemoryModel Logical GLSL450 + OpSamplerImageAddressingModeNV 64 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %7 = OpTypeImage %float 2D 0 0 0 1 Unknown + %8 = OpTypeSampledImage %7 + %9 = OpTypeImage %float 2D 0 0 0 2 Rgba32f + %10 = OpTypeSampler + %UBO = OpTypeStruct %8 %9 %10 +%_ptr_Uniform_UBO = OpTypePointer Uniform %UBO + %_ = OpVariable %_ptr_Uniform_UBO Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +INSTANTIATE_TEST_SUITE_P(, ValidateIdWithMessage, ::testing::Bool()); + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp index 1d3b1a7d..a97ef7cb 100644 --- a/test/val/val_image_test.cpp +++ b/test/val/val_image_test.cpp @@ -61,8 +61,11 @@ OpCapability ImageBuffer // In 1.4, the entry point must list all module-scope variables used. Just // list all of them. - std::string interface_vars = (env != SPV_ENV_UNIVERSAL_1_4) ? "" : - R"( + // + // For Vulkan, anything Location decoration needs to be an interface variable + std::string interface_vars = + (env != SPV_ENV_UNIVERSAL_1_4) ? "%input_flat_u32" : + R"( %uniform_image_f32_1d_0001 %uniform_image_f32_1d_0002_rgba32f %uniform_image_f32_2d_0001 @@ -146,6 +149,7 @@ OpDecorate %input_flat_u32 Location 0 %u32vec4 = OpTypeVector %u32 4 %s32vec4 = OpTypeVector %s32 4 %f32vec4 = OpTypeVector %f32 4 +%boolvec4 = OpTypeVector %bool 4 %f32_0 = OpConstant %f32 0 %f32_1 = OpConstant %f32 1 @@ -172,6 +176,8 @@ OpDecorate %input_flat_u32 Location 0 %u64_0 = OpConstant %u64 0 %u64_1 = OpConstant %u64 1 +%bool_t = OpConstantTrue %bool + %u32vec2arr4 = OpTypeArray %u32vec2 %u32_4 %u32vec2arr3 = OpTypeArray %u32vec2 %u32_3 %u32arr4 = OpTypeArray %u32 %u32_4 @@ -214,6 +220,8 @@ OpDecorate %input_flat_u32 Location 0 %f32vec4_0000 = OpConstantComposite %f32vec4 %f32_0 %f32_0 %f32_0 %f32_0 +%boolvec4_tttt = OpConstantComposite %boolvec4 %bool_t %bool_t %bool_t %bool_t + %const_offsets = OpConstantComposite %u32vec2arr4 %u32vec2_01 %u32vec2_12 %u32vec2_01 %u32vec2_12 %const_offsets3x2 = OpConstantComposite %u32vec2arr3 %u32vec2_01 %u32vec2_12 %u32vec2_01 %const_offsets4xu = OpConstantComposite %u32arr4 %u32_0 %u32_0 %u32_0 %u32_0 @@ -243,6 +251,11 @@ OpDecorate %input_flat_u32 Location 0 %uniform_image_u32_2d_0001 = OpVariable %ptr_image_u32_2d_0001 UniformConstant %type_sampled_image_u32_2d_0001 = OpTypeSampledImage %type_image_u32_2d_0001 +%type_image_u32_3d_0001 = OpTypeImage %u32 3D 0 0 0 1 Unknown +%ptr_image_u32_3d_0001 = OpTypePointer UniformConstant %type_image_u32_3d_0001 +%uniform_image_u32_3d_0001 = OpVariable %ptr_image_u32_3d_0001 UniformConstant +%type_sampled_image_u32_3d_0001 = OpTypeSampledImage %type_image_u32_3d_0001 + %type_image_u32_2d_0002 = OpTypeImage %u32 2D 0 0 0 2 Unknown %ptr_image_u32_2d_0002 = OpTypePointer UniformConstant %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 = OpVariable %ptr_image_u32_2d_0002 UniformConstant @@ -269,6 +282,11 @@ OpDecorate %input_flat_u32 Location 0 %uniform_image_f32_3d_0111 = OpVariable %ptr_image_f32_3d_0111 UniformConstant %type_sampled_image_f32_3d_0111 = OpTypeSampledImage %type_image_f32_3d_0111 +%type_image_f32_3d_0001 = OpTypeImage %f32 3D 0 0 0 1 Unknown +%ptr_image_f32_3d_0001 = OpTypePointer UniformConstant %type_image_f32_3d_0001 +%uniform_image_f32_3d_0001 = OpVariable %ptr_image_f32_3d_0001 UniformConstant +%type_sampled_image_f32_3d_0001 = OpTypeSampledImage %type_image_f32_3d_0001 + %type_image_f32_cube_0101 = OpTypeImage %f32 Cube 0 1 0 1 Unknown %ptr_image_f32_cube_0101 = OpTypePointer UniformConstant %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 = OpVariable %ptr_image_f32_cube_0101 UniformConstant @@ -735,6 +753,34 @@ TEST_F(ValidateImage, TypeImageWrongSampledForSubpassData) { HasSubstr("Dim SubpassData requires Sampled to be 2")); } +TEST_F(ValidateImage, TypeImageWrongSampledForSubpassDataVulkan) { + const std::string code = GetShaderHeader("OpCapability InputAttachment\n") + + R"( +%img_type = OpTypeImage %f32 SubpassData 0 0 0 1 Unknown +)" + TrivialMain(); + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeImage-06214")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Dim SubpassData requires Sampled to be 2")); +} + +TEST_F(ValidateImage, TypeImageWrongArrayForSubpassDataVulkan) { + const std::string code = GetShaderHeader("OpCapability InputAttachment\n") + + R"( +%img_type = OpTypeImage %f32 SubpassData 0 1 0 2 Unknown +)" + TrivialMain(); + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeImage-06214")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Dim SubpassData requires Arrayed to be 0")); +} + TEST_F(ValidateImage, TypeImage_OpenCL_Sampled0_OK) { const std::string code = GetKernelHeader() + R"( %img_type = OpTypeImage %void 2D 0 0 0 0 Unknown ReadOnly @@ -1002,6 +1048,26 @@ TEST_F(ValidateImage, SampledImageNotSampler) { HasSubstr("Expected Sampler to be of type OpTypeSampler")); } +TEST_F(ValidateImage, SampledImageIsStorage) { + const std::string declarations = R"( +%type_sampled_image_f32_2d_0002 = OpTypeSampledImage %type_image_f32_2d_0002 +)"; + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0002 %img %sampler +)"; + + CompileSuccessfully(GenerateShaderCode(body, "", "Fragment", "", + SPV_ENV_UNIVERSAL_1_0, "GLSL450", + declarations) + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sampled image type requires an image type with " + "\"Sampled\" operand set to 0 or 1")); +} + TEST_F(ValidateImage, ImageTexelPointerSuccess) { const std::string body = R"( %texel_ptr = OpImageTexelPointer %ptr_Image_u32 %private_image_u32_buffer_0002_r32ui %u32_0 %u32_0 @@ -1059,8 +1125,9 @@ TEST_F(ValidateImage, ImageTexelPointerImageNotResultTypePointer) { CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 136[%136] cannot be a " - "type")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand '148[%148]' cannot be a " + "type")); } TEST_F(ValidateImage, ImageTexelPointerImageNotImage) { @@ -1791,6 +1858,20 @@ TEST_F(ValidateImage, SampleImplicitLodVulkanOffsetWrongSize) { "OpImage*Gather operations")); } +TEST_F(ValidateImage, SampleImplicitLodVulkanOffsetWrongBeforeLegalization) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 Offset %s32vec2_01 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "", "Fragment", "", SPV_ENV_VULKAN_1_0).c_str()); + getValidatorOptions()->before_hlsl_legalization = true; + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + TEST_F(ValidateImage, SampleImplicitLodMoreThanOneOffset) { const std::string body = R"( %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 @@ -1801,9 +1882,10 @@ TEST_F(ValidateImage, SampleImplicitLodMoreThanOneOffset) { CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Image Operands Offset, ConstOffset, ConstOffsets " - "cannot be used together")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Image Operands Offset, ConstOffset, ConstOffsets, Offsets " + "cannot be used together")); } TEST_F(ValidateImage, SampleImplicitLodVulkanMoreThanOneOffset) { @@ -1819,9 +1901,10 @@ TEST_F(ValidateImage, SampleImplicitLodVulkanMoreThanOneOffset) { ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-StandaloneSpirv-Offset-04662")); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Image Operands Offset, ConstOffset, ConstOffsets " - "cannot be used together")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Image Operands Offset, ConstOffset, ConstOffsets, Offsets " + "cannot be used together")); } TEST_F(ValidateImage, SampleImplicitLodMinLodWrongType) { @@ -2274,6 +2357,24 @@ TEST_F(ValidateImage, SampleDrefImplicitLodWrongDrefType) { HasSubstr("Expected Dref to be of 32-bit float type")); } +TEST_F(ValidateImage, SampleDrefImplicitLodWrongDimVulkan) { + const std::string body = R"( +%img = OpLoad %type_image_u32_3d_0001 %uniform_image_u32_3d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_u32_3d_0001 %img %sampler +%res1 = OpImageSampleDrefImplicitLod %u32 %simg %f32vec3_hhh %f32_1 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "", "Fragment", "", SPV_ENV_VULKAN_1_0).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpImage-04777")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In Vulkan, OpImage*Dref* instructions must not use " + "images with a 3D Dim")); +} + TEST_F(ValidateImage, SampleDrefExplicitLodSuccess) { const std::string body = R"( %img = OpLoad %type_image_s32_3d_0001 %uniform_image_s32_3d_0001 @@ -3115,7 +3216,7 @@ TEST_F(ValidateImage, GatherConstOffsetsArrayNotVector) { CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Image Operand ConstOffsets array componenets " + HasSubstr("Expected Image Operand ConstOffsets array components " "to be int vectors of size 2")); } @@ -3130,7 +3231,7 @@ TEST_F(ValidateImage, GatherConstOffsetsArrayVectorWrongSize) { CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Image Operand ConstOffsets array componenets " + HasSubstr("Expected Image Operand ConstOffsets array components " "to be int vectors of size 2")); } @@ -3230,6 +3331,23 @@ TEST_F(ValidateImage, DrefGatherWrongDrefType) { HasSubstr("Expected Dref to be of 32-bit float type")); } +TEST_F(ValidateImage, DrefGatherWrongDimVulkan) { + const std::string body = R"( +%img = OpLoad %type_image_f32_3d_0001 %uniform_image_f32_3d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_3d_0001 %img %sampler +%res1 = OpImageDrefGather %f32vec4 %simg %f32vec4_0000 %f32_0_5 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "", "Fragment", "", SPV_ENV_VULKAN_1_0).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpImage-04777")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'Dim' to be 2D, Cube, or Rect")); +} + TEST_F(ValidateImage, ReadSuccess1) { const std::string body = R"( %img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 @@ -3374,6 +3492,8 @@ TEST_F(ValidateImage, ReadWrongNumComponentsResultType_Vulkan) { GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_VULKAN_1_0) .c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Result-04780")); EXPECT_THAT(getDiagnosticString(), HasSubstr("Expected Result Type to have 4 components")); } @@ -3632,6 +3752,17 @@ OpImageWrite %img %u32_1 %u32vec4_0123 "but given only 1")); } +TEST_F(ValidateImage, WriteTexelScalarSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 +OpImageWrite %img %u32vec2_01 %u32_2 +)"; + + const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + TEST_F(ValidateImage, WriteTexelWrongType) { const std::string body = R"( %img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 @@ -3645,17 +3776,17 @@ OpImageWrite %img %u32vec2_01 %img HasSubstr("Expected Texel to be int or float vector or scalar")); } -TEST_F(ValidateImage, DISABLED_WriteTexelNotVector4) { +TEST_F(ValidateImage, WriteTexelNonNumericalType) { const std::string body = R"( %img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 -OpImageWrite %img %u32vec2_01 %u32vec3_012 +OpImageWrite %img %u32vec2_01 %boolvec4_tttt )"; const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n"; CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Texel to have 4 components")); + HasSubstr("Expected Texel to be int or float vector or scalar")); } TEST_F(ValidateImage, WriteTexelWrongComponentType) { @@ -5446,7 +5577,8 @@ TEST_F(ValidateImage, SignExtendV13Bad) { )"; EXPECT_THAT(CompileFailure(GenerateShaderCode(body, "", "Fragment", "", - SPV_ENV_UNIVERSAL_1_3)), + SPV_ENV_UNIVERSAL_1_3), + SPV_ENV_UNIVERSAL_1_3, SPV_ERROR_WRONG_VERSION), HasSubstr("Invalid image operand 'SignExtend'")); } @@ -5457,7 +5589,8 @@ TEST_F(ValidateImage, ZeroExtendV13Bad) { )"; EXPECT_THAT(CompileFailure(GenerateShaderCode(body, "", "Fragment", "", - SPV_ENV_UNIVERSAL_1_3)), + SPV_ENV_UNIVERSAL_1_3), + SPV_ENV_UNIVERSAL_1_3, SPV_ERROR_WRONG_VERSION), HasSubstr("Invalid image operand 'ZeroExtend'")); } @@ -6045,6 +6178,225 @@ TEST_F(ValidateImage, ImageTexelPointerRgba16fVulkan) { "R32f, R32i, or R32ui for Vulkan environment")); } +TEST_F(ValidateImage, ImageExecutionModeLimitationNoMode) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %2 " " %4 +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%12 = OpTypeImage %float 2D 0 0 0 1 Rgba8ui +%13 = OpTypeSampledImage %12 +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%5 = OpVariable %_ptr_UniformConstant_13 UniformConstant +%_ptr_Input_v4float = OpTypePointer Input %v4float +%4 = OpVariable %_ptr_Input_v4float Input +%v2float = OpTypeVector %float 2 +%float_1_35631564en19 = OpConstant %float 1.35631564e-19 +%2 = OpFunction %void None %8 +%8224 = OpLabel +%6 = OpLoad %13 %5 +%19 = OpLoad %v4float %4 +%20 = OpVectorShuffle %v2float %19 %19 0 1 +%21 = OpVectorTimesScalar %v2float %20 %float_1_35631564en19 +%65312 = OpImageSampleImplicitLod %v4float %6 %21 +OpUnreachable +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ImplicitLod instructions require " + "DerivativeGroupQuadsNV or DerivativeGroupLinearNV " + "execution mode for GLCompute execution model")); +} + +TEST_F(ValidateImage, TypeSampledImageNotBufferPost1p6) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpCapability SampledBuffer +OpMemoryModel Logical GLSL450 +%float = OpTypeFloat 32 +%image = OpTypeImage %float Buffer 0 0 0 1 Unknown +%sampled = OpTypeSampledImage %image +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_6); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In SPIR-V 1.6 or later, sampled image dimension must " + "not be Buffer")); +} + +TEST_F(ValidateImage, NonTemporalImage) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 " " %4 %5 +OpExecutionMode %2 OriginUpperLeft +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%12 = OpTypeImage %float 2D 0 0 0 1 Rgba8ui +%13 = OpTypeSampledImage %12 +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%5 = OpVariable %_ptr_UniformConstant_13 UniformConstant +%_ptr_Input_v4float = OpTypePointer Input %v4float +%4 = OpVariable %_ptr_Input_v4float Input +%v2float = OpTypeVector %float 2 +%float_1_35631564en19 = OpConstant %float 1.35631564e-19 +%2 = OpFunction %void None %8 +%8224 = OpLabel +%6 = OpLoad %13 %5 +%19 = OpLoad %v4float %4 +%20 = OpVectorShuffle %v2float %19 %19 0 1 +%21 = OpVectorTimesScalar %v2float %20 %float_1_35631564en19 +%65312 = OpImageSampleImplicitLod %v4float %6 %21 Nontemporal +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_6); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); +} + +TEST_F(ValidateImage, NVBindlessSamplerBuiltins) { + const std::string text = R"( + OpCapability Shader + OpCapability Int64 + OpCapability Image1D + OpCapability BindlessTextureNV + OpExtension "SPV_NV_bindless_texture" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpSamplerImageAddressingModeNV 64 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %s2D "s2D" + OpName %textureHandle "textureHandle" + OpName %i1D "i1D" + OpName %s "s" + OpName %temp "temp" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %7 = OpTypeImage %float 2D 0 0 0 1 Unknown + %8 = OpTypeSampledImage %7 +%_ptr_Function_8 = OpTypePointer Function %8 + %ulong = OpTypeInt 64 0 +%_ptr_Private_ulong = OpTypePointer Private %ulong +%textureHandle = OpVariable %_ptr_Private_ulong Private + %16 = OpTypeImage %float 1D 0 0 0 2 Rgba32f +%_ptr_Function_16 = OpTypePointer Function %16 + %21 = OpTypeSampler +%_ptr_Function_21 = OpTypePointer Function %21 +%_ptr_Function_ulong = OpTypePointer Function %ulong + %main = OpFunction %void None %3 + %5 = OpLabel + %s2D = OpVariable %_ptr_Function_8 Function + %i1D = OpVariable %_ptr_Function_16 Function + %s = OpVariable %_ptr_Function_21 Function + %temp = OpVariable %_ptr_Function_ulong Function + %14 = OpLoad %ulong %textureHandle + %15 = OpConvertUToSampledImageNV %8 %14 + OpStore %s2D %15 + %19 = OpLoad %ulong %textureHandle + %20 = OpConvertUToImageNV %16 %19 + OpStore %i1D %20 + %24 = OpLoad %ulong %textureHandle + %25 = OpConvertUToSamplerNV %21 %24 + OpStore %s %25 + %28 = OpLoad %8 %s2D + %29 = OpConvertSampledImageToUNV %ulong %28 + OpStore %temp %29 + %30 = OpLoad %16 %i1D + %31 = OpConvertImageToUNV %ulong %30 + OpStore %temp %31 + %32 = OpLoad %21 %s + %33 = OpConvertSamplerToUNV %ulong %32 + OpStore %temp %33 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateImage, NVBindlessAddressingMode64) { + std::string text = R"( + OpCapability Shader + OpCapability BindlessTextureNV + OpExtension "SPV_NV_bindless_texture" + OpMemoryModel Logical GLSL450 + OpSamplerImageAddressingModeNV 64 + OpEntryPoint GLCompute %func "main" +%voidt = OpTypeVoid +%uintt = OpTypeInt 32 0 +%funct = OpTypeFunction %voidt +%func = OpFunction %voidt None %funct +%entry = OpLabel +%udef = OpUndef %uintt + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateImage, NVBindlessAddressingMode32) { + std::string text = R"( + OpCapability Shader + OpCapability BindlessTextureNV + OpExtension "SPV_NV_bindless_texture" + OpMemoryModel Logical GLSL450 + OpSamplerImageAddressingModeNV 32 + OpEntryPoint GLCompute %func "main" +%voidt = OpTypeVoid +%uintt = OpTypeInt 32 0 +%funct = OpTypeFunction %voidt +%func = OpFunction %voidt None %funct +%entry = OpLabel +%udef = OpUndef %uintt + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateImage, NVBindlessInvalidAddressingMode) { + std::string text = R"( + OpCapability Shader + OpCapability BindlessTextureNV + OpExtension "SPV_NV_bindless_texture" + OpMemoryModel Logical GLSL450 + OpSamplerImageAddressingModeNV 0 + OpEntryPoint GLCompute %func "main" +%voidt = OpTypeVoid +%uintt = OpTypeInt 32 0 +%funct = OpTypeFunction %voidt +%func = OpFunction %voidt None %funct +%entry = OpLabel +%udef = OpUndef %uintt + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpSamplerImageAddressingModeNV bitwidth should be 64 or 32")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_interfaces_test.cpp b/test/val/val_interfaces_test.cpp index 6869e794..22a0e7c3 100644 --- a/test/val/val_interfaces_test.cpp +++ b/test/val/val_interfaces_test.cpp @@ -217,7 +217,7 @@ OpFunctionEnd EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); EXPECT_THAT( getDiagnosticString(), - HasSubstr("Non-unique OpEntryPoint interface 2[%var] is disallowed")); + HasSubstr("Non-unique OpEntryPoint interface '2[%var]' is disallowed")); } TEST_F(ValidateInterfacesTest, MissingGlobalVarSPV1p3) { @@ -447,6 +447,8 @@ OpFunctionEnd CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04918")); EXPECT_THAT(getDiagnosticString(), HasSubstr("Members cannot be assigned a location")); } @@ -475,6 +477,8 @@ OpFunctionEnd CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04918")); EXPECT_THAT(getDiagnosticString(), HasSubstr("Members cannot be assigned a location")); } @@ -707,7 +711,9 @@ OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %var1 %var2 OpExecutionMode %main OriginUpperLeft OpDecorate %var1 Location 0 +OpDecorate %var1 Flat OpDecorate %var2 Location 1 +OpDecorate %var2 Flat %void = OpTypeVoid %void_fn = OpTypeFunction %void %float = OpTypeFloat 32 @@ -1267,10 +1273,9 @@ 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")); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be in the Output storage class")); } TEST_F(ValidateInterfacesTest, VulkanLocationsArrayWithComponent) { @@ -1410,6 +1415,129 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); } +TEST_F(ValidateInterfacesTest, VulkanLocationArrayWithComponent1) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in +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 +%int = OpTypeInt 32 0 +%int_2 = OpConstant %int 2 +%float_arr = OpTypeArray %float %int_2 +%struct = OpTypeStruct %float_arr %float_arr +%ptr = OpTypePointer Input %struct +%in = OpVariable %ptr 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, VulkanLocationArrayWithComponent2) { + const std::string text = R"( +OpCapability Shader +OpCapability Float64 +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in +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 +%double = OpTypeFloat 64 +%int = OpTypeInt 32 0 +%int_2 = OpConstant %int 2 +%double_arr = OpTypeArray %double %int_2 +%struct = OpTypeStruct %float %double_arr +%ptr = OpTypePointer Input %struct +%in = OpVariable %ptr 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, DuplicateInterfaceVariableSuccess) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in %out %in +OpExecutionMode %main OriginUpperLeft +OpDecorate %in Location 0 +OpDecorate %out Location 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%in_ptr = OpTypePointer Input %float +%out_ptr = OpTypePointer Output %float +%in = OpVariable %in_ptr Input +%out = OpVariable %out_ptr Output +%void_fn = OpTypeFunction %void +%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, StructWithBuiltinsMissingBlock_Bad) { + // See https://github.com/KhronosGroup/SPIRV-Registry/issues/134 + // + // When a shader input or output is a struct that does not have Block, + // then it must have a Location. + // But BuiltIns must not have locations. + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in +OpExecutionMode %main OriginUpperLeft +; %struct needs a Block decoration +OpMemberDecorate %struct 0 BuiltIn Position +%void = OpTypeVoid +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%struct = OpTypeStruct %v4float +%in_ptr = OpTypePointer Input %struct +%in = OpVariable %in_ptr Input +%void_fn = OpTypeFunction %void +%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(), + AnyVUID("VUID-StandaloneSpirv-Location-04919")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Interface struct has no Block decoration but has BuiltIn members.")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_layout_test.cpp b/test/val/val_layout_test.cpp index d34c97f9..8cca96f5 100644 --- a/test/val/val_layout_test.cpp +++ b/test/val/val_layout_test.cpp @@ -538,7 +538,6 @@ TEST_F(ValidateLayout, ModuleProcessedInvalidIn10) { OpMemoryModel Logical GLSL450 OpName %void "void" OpModuleProcessed "this is ok in 1.1 and later" - OpDecorate %void Volatile ; bogus, but makes the example short %void = OpTypeVoid )"; @@ -558,7 +557,6 @@ TEST_F(ValidateLayout, ModuleProcessedValidIn11) { OpMemoryModel Logical GLSL450 OpName %void "void" OpModuleProcessed "this is ok in 1.1 and later" - OpDecorate %void Volatile ; bogus, but makes the example short %void = OpTypeVoid )"; @@ -669,6 +667,98 @@ TEST_F(ValidateLayout, ModuleProcessedInvalidInBasicBlock) { // TODO(umar): Test optional instructions +TEST_F(ValidateLayout, ValidNVBindlessTexturelayout) { + std::string str = R"( + OpCapability Shader + OpCapability BindlessTextureNV + OpExtension "SPV_NV_bindless_texture" + OpMemoryModel Logical GLSL450 + OpSamplerImageAddressingModeNV 64 + OpEntryPoint GLCompute %func "main" +%voidt = OpTypeVoid +%uintt = OpTypeInt 32 0 +%funct = OpTypeFunction %voidt +%func = OpFunction %voidt None %funct +%entry = OpLabel +%udef = OpUndef %uintt + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLayout, InvalidValidNVBindlessTexturelayout) { + std::string str = R"( + OpCapability Shader + OpCapability BindlessTextureNV + OpExtension "SPV_NV_bindless_texture" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %func "main" + OpSamplerImageAddressingModeNV 64 +%voidt = OpTypeVoid +%uintt = OpTypeInt 32 0 +%funct = OpTypeFunction %voidt +%func = OpFunction %voidt None %funct +%entry = OpLabel +%udef = OpUndef %uintt + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "SamplerImageAddressingModeNV is in an invalid layout section")); +} + +TEST_F(ValidateLayout, MissingNVBindlessAddressModeFromLayout) { + std::string str = R"( + OpCapability Shader + OpCapability BindlessTextureNV + OpExtension "SPV_NV_bindless_texture" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %func "main" +%voidt = OpTypeVoid +%uintt = OpTypeInt 32 0 +%funct = OpTypeFunction %voidt +%func = OpFunction %voidt None %funct +%entry = OpLabel +%udef = OpUndef %uintt + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Missing required OpSamplerImageAddressingModeNV instruction")); +} + +TEST_F(ValidateLayout, NVBindlessAddressModeFromLayoutSpecifiedTwice) { + std::string str = R"( + OpCapability Shader + OpCapability BindlessTextureNV + OpExtension "SPV_NV_bindless_texture" + OpMemoryModel Logical GLSL450 + OpSamplerImageAddressingModeNV 64 + OpSamplerImageAddressingModeNV 64 +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpSamplerImageAddressingModeNV should only be provided once")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_limits_test.cpp b/test/val/val_limits_test.cpp index 8fb80a46..364d514e 100644 --- a/test/val/val_limits_test.cpp +++ b/test/val/val_limits_test.cpp @@ -750,7 +750,7 @@ TEST_F(ValidateLimits, CustomizedControlFlowDepthBad) { } // Valid. The purpose here is to test the CFG depth calculation code when a loop -// continue target is the loop iteself. It also exercises the case where a loop +// continue target is the loop itself. It also exercises the case where a loop // is unreachable. TEST_F(ValidateLimits, ControlFlowNoEntryToLoopGood) { std::string str = header + R"( diff --git a/test/val/val_literals_test.cpp b/test/val/val_literals_test.cpp index 6eadf321..7c9aad67 100644 --- a/test/val/val_literals_test.cpp +++ b/test/val/val_literals_test.cpp @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Validation tests for ilegal literals +// Validation tests for illegal literals #include #include diff --git a/test/val/val_logicals_test.cpp b/test/val/val_logicals_test.cpp index b57c7433..c1406728 100644 --- a/test/val/val_logicals_test.cpp +++ b/test/val/val_logicals_test.cpp @@ -1053,18 +1053,18 @@ TEST_F(ValidateLogicals, OpSGreaterThanDifferentBitWidth) { TEST_F(ValidateLogicals, PSBSelectSuccess) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft -OpDecorate %val1 AliasedPointerEXT +OpDecorate %val1 AliasedPointer %uint64 = OpTypeInt 64 0 %bool = OpTypeBool %true = OpConstantTrue %bool -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %pptr_f = OpTypePointer Function %ptr %void = OpTypeVoid %voidfn = OpTypeFunction %void @@ -1159,6 +1159,61 @@ OpFunctionEnd "condition to be equal: Select")); } +TEST_F(ValidateLogicals, SelectNVBindlessSamplers) { + const std::string spirv = R"( + OpCapability Shader + OpCapability BindlessTextureNV + OpExtension "SPV_NV_bindless_texture" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpSamplerImageAddressingModeNV 64 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_NV_bindless_texture" + OpName %main "main" + OpName %s2D "s2D" + OpName %pickhandle "pickhandle" + OpName %Sampler1 "Sampler1" + OpName %Sampler2 "Sampler2" + OpDecorate %pickhandle Flat + OpDecorate %Sampler1 DescriptorSet 0 + OpDecorate %Sampler1 Binding 0 + OpDecorate %Sampler1 BindlessSamplerNV + OpDecorate %Sampler2 DescriptorSet 0 + OpDecorate %Sampler2 Binding 1 + OpDecorate %Sampler2 BindlessSamplerNV + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %7 = OpTypeImage %float 2D 0 0 0 1 Unknown + %8 = OpTypeSampledImage %7 +%_ptr_Function_8 = OpTypePointer Function %8 + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %bool = OpTypeBool +%_ptr_UniformConstant_8 = OpTypePointer UniformConstant %8 + %Sampler1 = OpVariable %_ptr_UniformConstant_8 UniformConstant + %Sampler2 = OpVariable %_ptr_UniformConstant_8 UniformConstant + %main = OpFunction %void None %3 + %5 = OpLabel + %s2D = OpVariable %_ptr_Function_8 Function + %pickhandle = OpVariable %_ptr_Function_int Function + %14 = OpLoad %int %pickhandle + %17 = OpIEqual %bool %14 %int_0 + %20 = OpLoad %8 %Sampler1 + %22 = OpLoad %8 %Sampler2 + %23 = OpSelect %8 %17 %20 %22 + OpStore %s2D %23 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp index 9799b404..4299eda9 100644 --- a/test/val/val_memory_test.cpp +++ b/test/val/val_memory_test.cpp @@ -203,9 +203,11 @@ TEST_F(ValidateMemory, VulkanUniformOnIntBad) { )"; CompileSuccessfully(src, SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Uniform-06807")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("From Vulkan spec, section 14.5.2:\n" + HasSubstr("From Vulkan spec:\n" "Variables identified with the Uniform storage class are used " "to access transparent buffer backed resources. Such variables " "must be typed as OpTypeStruct, or an array of this type")); @@ -277,9 +279,11 @@ TEST_F(ValidateMemory, VulkanUniformOnRuntimeArrayOfArrayBad) { )"; CompileSuccessfully(src, SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Uniform-06807")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("From Vulkan spec, section 14.5.2:\n" + HasSubstr("From Vulkan spec:\n" "Variables identified with the Uniform storage class are used " "to access transparent buffer backed resources. Such variables " "must be typed as OpTypeStruct, or an array of this type")); @@ -318,9 +322,11 @@ TEST_F(ValidateMemory, VulkanUniformOnArrayOfArrayBad) { )"; CompileSuccessfully(src, SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Uniform-06807")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("From Vulkan spec, section 14.5.2:\n" + HasSubstr("From Vulkan spec:\n" "Variables identified with the Uniform storage class are used " "to access transparent buffer backed resources. Such variables " "must be typed as OpTypeStruct, or an array of this type")); @@ -466,6 +472,55 @@ OpFunctionEnd "= OpVariable %_ptr_Input_float Input %float_1\n")); } +TEST_F(ValidateMemory, UniversalInitializerWithDisallowedStorageClassesBad) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%float = OpTypeFloat 32 +%float_ptr = OpTypePointer Input %float +%init_val = OpConstant %float 1.0 +%1 = OpVariable %float_ptr Input %init_val +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpVariable, '5[%5]', initializer are not allowed for Input")); +} + +TEST_F(ValidateMemory, InitializerWithTaskPayloadWorkgroupEXT) { + std::string spirv = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint TaskEXT %main "main" %payload + %void = OpTypeVoid + %func = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_TaskPayloadWorkgroupEXT = OpTypePointer TaskPayloadWorkgroupEXT %uint + %uint_1 = OpConstant %uint 1 + %payload = OpVariable %_ptr_TaskPayloadWorkgroupEXT TaskPayloadWorkgroupEXT %uint_1 + %main = OpFunction %void None %func + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpVariable, '2[%2]', initializer are not allowed " + "for TaskPayloadWorkgroupEXT")); +} + TEST_F(ValidateMemory, ArrayLenCorrectResultType) { std::string spirv = R"( OpCapability Shader @@ -638,7 +693,7 @@ TEST_F(ValidateMemory, ArrayLenInputNotStruct) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("The Struture's type in OpArrayLength '11[%11]' " + HasSubstr("The Structure's type in OpArrayLength '11[%11]' " "must be a pointer to an OpTypeStruct.")); } @@ -668,7 +723,7 @@ TEST_F(ValidateMemory, ArrayLenInputLastMemberNoRTA) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("The Struture's last member in OpArrayLength '11[%11]' " + HasSubstr("The Structure's last member in OpArrayLength '11[%11]' " "must be an OpTypeRuntimeArray.\n %11 = OpArrayLength %uint " "%10 0\n")); } @@ -699,7 +754,7 @@ TEST_F(ValidateMemory, ArrayLenInputLastMemberNoRTA2) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("The Struture's last member in OpArrayLength '11[%11]' " + HasSubstr("The Structure's last member in OpArrayLength '11[%11]' " "must be an OpTypeRuntimeArray.\n %11 = OpArrayLength %uint " "%10 1\n")); } @@ -746,7 +801,7 @@ TEST_F(ValidateMemory, ArrayLenIndexNotPointerToStruct) { %float = OpTypeFloat 32 %uint = OpTypeInt 32 0 %_runtimearr_float = OpTypeRuntimeArray %float - %_struct_7 = OpTypeStruct %float %_runtimearr_float + %_struct_7 = OpTypeStruct %float %_ptr_Function__struct_7 = OpTypePointer Function %_struct_7 %1 = OpFunction %void None %3 %9 = OpLabel @@ -763,7 +818,7 @@ TEST_F(ValidateMemory, ArrayLenIndexNotPointerToStruct) { EXPECT_THAT( getDiagnosticString(), HasSubstr( - "The Struture's type in OpArrayLength '12[%12]' must be a " + "The Structure's type in OpArrayLength '12[%12]' must be a " "pointer to an OpTypeStruct.\n %12 = OpArrayLength %uint %11 0\n")); } @@ -787,8 +842,9 @@ TEST_F(ValidateMemory, ArrayLenPointerIsAType) { CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 4[%float] cannot be a " - "type")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand '4[%float]' cannot be a " + "type")); } TEST_F(ValidateMemory, PushConstantNotStructGood) { @@ -834,10 +890,48 @@ TEST_F(ValidateMemory, VulkanPushConstantNotStructBad) { CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("PushConstant OpVariable '6[%6]' has illegal " - "type.\nFrom Vulkan spec, section 14.5.1:\n" - "Such variables must be typed as OpTypeStruct, " - "or an array of this type")); + AnyVUID("VUID-StandaloneSpirv-PushConstant-06808")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("PushConstant OpVariable '6[%6]' has illegal " + "type.\nFrom Vulkan spec, Push Constant Interface section:\n" + "Such variables must be typed as OpTypeStruct")); +} + +TEST_F(ValidateMemory, VulkanPushConstantArrayOfStructBad) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %struct Block + OpMemberDecorate %struct 0 Offset 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %int = OpTypeInt 32 0 + %int_1 = OpConstant %int 1 + %struct = OpTypeStruct %float + %array = OpTypeArray %struct %int_1 + %ptr = OpTypePointer PushConstant %array + %pc = OpVariable %ptr PushConstant + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PushConstant-06808")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("PushConstant OpVariable '10[%10]' has illegal " + "type.\nFrom Vulkan spec, Push Constant Interface section:\n" + "Such variables must be typed as OpTypeStruct")); } TEST_F(ValidateMemory, VulkanPushConstant) { @@ -1558,21 +1652,21 @@ OpFunctionEnd CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Operand 1[%incorrect] requires a type")); + HasSubstr("Operand '1[%incorrect]' requires a type")); } TEST_F(ValidateMemory, PSBLoadAlignedSuccess) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft -OpDecorate %val1 AliasedPointerEXT +OpDecorate %val1 AliasedPointer %uint64 = OpTypeInt 64 0 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %pptr_f = OpTypePointer Function %ptr %void = OpTypeVoid %voidfn = OpTypeFunction %void @@ -1585,22 +1679,22 @@ OpReturn OpFunctionEnd )"; - CompileSuccessfully(body.c_str()); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); } TEST_F(ValidateMemory, PSBLoadAlignedMissing) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft -OpDecorate %val1 AliasedPointerEXT +OpDecorate %val1 AliasedPointer %uint64 = OpTypeInt 64 0 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %pptr_f = OpTypePointer Function %ptr %void = OpTypeVoid %voidfn = OpTypeFunction %void @@ -1613,27 +1707,61 @@ OpReturn OpFunctionEnd )"; - CompileSuccessfully(body.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708")); EXPECT_THAT( getDiagnosticString(), - HasSubstr( - "Memory accesses with PhysicalStorageBufferEXT must use Aligned")); + HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned")); +} + +TEST_F(ValidateMemory, PSBLoadAlignedMissingWithOtherOperand) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddresses +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %val1 AliasedPointer +%uint64 = OpTypeInt 64 0 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 +%pptr_f = OpTypePointer Function %ptr +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %pptr_f Function +%val2 = OpLoad %ptr %val1 +%val3 = OpLoad %uint64 %val2 Volatile +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned")); } TEST_F(ValidateMemory, PSBStoreAlignedSuccess) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft -OpDecorate %val1 AliasedPointerEXT +OpDecorate %val1 AliasedPointer %uint64 = OpTypeInt 64 0 %u64_1 = OpConstant %uint64 1 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %pptr_f = OpTypePointer Function %ptr %void = OpTypeVoid %voidfn = OpTypeFunction %void @@ -1646,23 +1774,23 @@ OpReturn OpFunctionEnd )"; - CompileSuccessfully(body.c_str()); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); } TEST_F(ValidateMemory, PSBStoreAlignedMissing) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft -OpDecorate %val1 AliasedPointerEXT +OpDecorate %val1 AliasedPointer %uint64 = OpTypeInt 64 0 %u64_1 = OpConstant %uint64 1 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %pptr_f = OpTypePointer Function %ptr %void = OpTypeVoid %voidfn = OpTypeFunction %void @@ -1675,27 +1803,168 @@ OpReturn OpFunctionEnd )"; - CompileSuccessfully(body.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708")); EXPECT_THAT( getDiagnosticString(), - HasSubstr( - "Memory accesses with PhysicalStorageBufferEXT must use Aligned")); + HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned")); +} + +TEST_F(ValidateMemory, PSBCopyMemoryAlignedSuccess) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddresses +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %val1 AliasedPointer +%int = OpTypeInt 32 0 +%uint64 = OpTypeInt 64 0 +%u64_1 = OpConstant %uint64 1 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 +%pptr_f = OpTypePointer Function %ptr +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %pptr_f Function +%val2 = OpLoad %ptr %val1 +%val3 = OpLoad %ptr %val1 +OpCopyMemory %val2 %val3 Aligned 4 +OpCopyMemory %val3 %val2 Aligned 4 Aligned 4 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateMemory, PSBCopyMemoryAlignedMissingTarget) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddresses +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %val1 AliasedPointer +%int = OpTypeInt 32 0 +%uint64 = OpTypeInt 64 0 +%u64_1 = OpConstant %uint64 1 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 +%pptr_f = OpTypePointer Function %ptr +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %pptr_f Function +%val2 = OpLoad %ptr %val1 +%val3 = OpLoad %ptr %val1 +OpCopyMemory %val2 %val3 Volatile Aligned 4 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned")); +} + +TEST_F(ValidateMemory, PSBCopyMemoryAlignedMissingSource) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddresses +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %val1 AliasedPointer +%int = OpTypeInt 32 0 +%uint64 = OpTypeInt 64 0 +%u64_1 = OpConstant %uint64 1 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 +%pptr_f = OpTypePointer Function %ptr +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %pptr_f Function +%val2 = OpLoad %ptr %val1 +%val3 = OpLoad %ptr %val1 +OpCopyMemory %val2 %val3 Aligned 4 Volatile +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned")); +} + +TEST_F(ValidateMemory, PSBCopyMemoryAlignedMissingBoth) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddresses +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %val1 AliasedPointer +%int = OpTypeInt 32 0 +%uint64 = OpTypeInt 64 0 +%u64_1 = OpConstant %uint64 1 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 +%pptr_f = OpTypePointer Function %ptr +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %pptr_f Function +%val2 = OpLoad %ptr %val1 +%val3 = OpLoad %ptr %val1 +OpCopyMemory %val2 %val3 Volatile +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned")); } TEST_F(ValidateMemory, PSBVariable) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft -OpDecorate %val1 AliasedPointerEXT +OpDecorate %val1 AliasedPointer %uint64 = OpTypeInt 64 0 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 -%val1 = OpVariable %ptr PhysicalStorageBufferEXT +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 +%val1 = OpVariable %ptr PhysicalStorageBuffer %void = OpTypeVoid %voidfn = OpTypeFunction %void %main = OpFunction %void None %voidfn @@ -1708,7 +1977,7 @@ OpFunctionEnd EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("PhysicalStorageBufferEXT must not be used with OpVariable")); + HasSubstr("PhysicalStorageBuffer must not be used with OpVariable")); } std::string GenCoopMatLoadStoreShader(const std::string& storeMemoryAccess, @@ -2097,6 +2366,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), HasSubstr( @@ -2162,6 +2433,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), HasSubstr("For Vulkan with RuntimeDescriptorArrayEXT, a variable " @@ -2217,11 +2490,14 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), HasSubstr( "For Vulkan, OpTypeStruct variables containing OpTypeRuntimeArray " - "must have storage class of StorageBuffer or Uniform.\n %6 = " + "must have storage class of StorageBuffer, PhysicalStorageBuffer, or " + "Uniform.\n %6 = " "OpVariable %_ptr_Workgroup__struct_4 Workgroup\n")); } @@ -2246,10 +2522,13 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT(getDiagnosticString(), HasSubstr("For Vulkan, an OpTypeStruct variable containing an " "OpTypeRuntimeArray must be decorated with Block if it " - "has storage class StorageBuffer.\n %6 = OpVariable " + "has storage class StorageBuffer or " + "PhysicalStorageBuffer.\n %6 = OpVariable " "%_ptr_StorageBuffer__struct_4 StorageBuffer\n")); } @@ -2300,6 +2579,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT(getDiagnosticString(), HasSubstr("For Vulkan, an OpTypeStruct variable containing an " "OpTypeRuntimeArray must be decorated with BufferBlock " @@ -2328,6 +2609,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), HasSubstr( @@ -2344,11 +2627,12 @@ OpExtension "SPV_EXT_descriptor_indexing" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %func "func" OpExecutionMode %func OriginUpperLeft -OpDecorate %array_t Block +OpDecorate %struct Block %uint_t = OpTypeInt 32 0 %inner_array_t = OpTypeRuntimeArray %uint_t %array_t = OpTypeRuntimeArray %inner_array_t -%array_ptr = OpTypePointer StorageBuffer %array_t +%struct = OpTypeStruct %array_t +%array_ptr = OpTypePointer StorageBuffer %struct %2 = OpVariable %array_ptr StorageBuffer %void = OpTypeVoid %func_t = OpTypeFunction %void @@ -2360,6 +2644,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), HasSubstr( @@ -2422,6 +2708,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), HasSubstr( @@ -2458,6 +2746,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), HasSubstr( @@ -2489,6 +2779,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), HasSubstr("OpTypeArray Element Type '5[%_runtimearr_4]' is not " @@ -2504,13 +2796,14 @@ OpExtension "SPV_EXT_descriptor_indexing" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %func "func" OpExecutionMode %func OriginUpperLeft -OpDecorate %array_t Block +OpDecorate %struct Block %uint_t = OpTypeInt 32 0 %dim = OpConstant %uint_t 1 %sampler_t = OpTypeSampler %inner_array_t = OpTypeRuntimeArray %uint_t %array_t = OpTypeRuntimeArray %inner_array_t -%array_ptr = OpTypePointer StorageBuffer %array_t +%struct = OpTypeStruct %array_t +%array_ptr = OpTypePointer StorageBuffer %struct %2 = OpVariable %array_ptr StorageBuffer %void = OpTypeVoid %func_t = OpTypeFunction %void @@ -2522,6 +2815,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), HasSubstr( @@ -2556,6 +2851,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), HasSubstr( @@ -2593,6 +2890,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), HasSubstr( @@ -3013,8 +3312,8 @@ OpFunctionEnd EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Instruction cannot be used without a variable " - "pointers capability")); + HasSubstr("Instruction cannot for logical addressing model be " + "used without a variable pointers capability")); } } @@ -3251,6 +3550,8 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Uniform-06925")); EXPECT_THAT( getDiagnosticString(), HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks")); @@ -3292,6 +3593,8 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Uniform-06925")); EXPECT_THAT( getDiagnosticString(), HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks")); @@ -3364,6 +3667,8 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Uniform-06925")); EXPECT_THAT( getDiagnosticString(), HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks")); @@ -3408,6 +3713,8 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Uniform-06925")); EXPECT_THAT( getDiagnosticString(), HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks")); @@ -3448,6 +3755,8 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Uniform-06925")); EXPECT_THAT( getDiagnosticString(), HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks")); @@ -3973,9 +4282,11 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Uniform-06807")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("From Vulkan spec, section 14.5.2:\nVariables identified with " + HasSubstr("From Vulkan spec:\nVariables identified with " "the StorageBuffer storage class are used to access " "transparent buffer backed resources. Such variables must be " "typed as OpTypeStruct, or an array of this type")); @@ -4004,9 +4315,11 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Uniform-06807")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("From Vulkan spec, section 14.5.2:\nVariables identified with " + HasSubstr("From Vulkan spec:\nVariables identified with " "the StorageBuffer storage class are used to access " "transparent buffer backed resources. Such variables must be " "typed as OpTypeStruct, or an array of this type")); @@ -4034,9 +4347,11 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Uniform-06807")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("From Vulkan spec, section 14.5.2:\nVariables identified with " + HasSubstr("From Vulkan spec:\nVariables identified with " "the StorageBuffer storage class are used to access " "transparent buffer backed resources. Such variables must be " "typed as OpTypeStruct, or an array of this type")); @@ -4046,7 +4361,7 @@ TEST_F(ValidateMemory, VulkanInvariantOutputSuccess) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 -OpEntryPoint Vertex %main "main" +OpEntryPoint Vertex %main "main" %var OpDecorate %var Location 0 OpDecorate %var Invariant %void = OpTypeVoid @@ -4068,7 +4383,7 @@ TEST_F(ValidateMemory, VulkanInvariantInputStructSuccess) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" +OpEntryPoint Fragment %main "main" %var OpExecutionMode %main OriginUpperLeft OpDecorate %var Location 0 OpMemberDecorate %struct 1 Invariant @@ -4257,8 +4572,10 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Variable initializers in Workgroup storage class are " - "limited to OpConstantNull")); + AnyVUID(" VUID-StandaloneSpirv-OpVariable-04734")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpVariable, '5[%5]', initializers are limited to " + "OpConstantNull in Workgroup storage class")); } TEST_F(ValidateMemory, VulkanInitializerWithWorkgroupStorageClassGood) { @@ -4284,6 +4601,117 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } +TEST_F(ValidateMemory, LoadRuntimeArray) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%rta = OpTypeRuntimeArray %int +%block = OpTypeStruct %rta +%ptr_rta = OpTypePointer StorageBuffer %rta +%ptr_block = OpTypePointer StorageBuffer %block +%var = OpVariable %ptr_block StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = OpAccessChain %ptr_rta %var %int_0 +%ld = OpLoad %rta %gep +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Cannot load a runtime-sized array")); +} + +TEST_F(ValidateMemory, LoadRuntimeArrayInStruct) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%rta = OpTypeRuntimeArray %int +%block = OpTypeStruct %rta +%ptr_rta = OpTypePointer StorageBuffer %rta +%ptr_block = OpTypePointer StorageBuffer %block +%var = OpVariable %ptr_block StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%ld = OpLoad %block %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Cannot load a runtime-sized array")); +} + +TEST_F(ValidateMemory, LoadRuntimeArrayInArray) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%rta = OpTypeRuntimeArray %int +%block = OpTypeStruct %rta +%array = OpTypeArray %block %int_4 +%ptr_rta = OpTypePointer StorageBuffer %rta +%ptr_block = OpTypePointer StorageBuffer %block +%ptr_array = OpTypePointer StorageBuffer %array +%var = OpVariable %ptr_array StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%ld = OpLoad %array %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Cannot load a runtime-sized array")); +} + +TEST_F(ValidateMemory, Pre1p4WorkgroupMemoryBadLayoutOk) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +%void = OpTypeVoid +%bool = OpTypeBool +%struct = OpTypeStruct %bool +%ptr = OpTypePointer Workgroup %struct +%var = OpVariable %ptr Workgroup +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_mesh_shading_test.cpp b/test/val/val_mesh_shading_test.cpp new file mode 100644 index 00000000..ce6999dc --- /dev/null +++ b/test/val/val_mesh_shading_test.cpp @@ -0,0 +1,604 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests instructions from SPV_EXT_mesh_shader + +#include +#include + +#include "gmock/gmock.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Values; + +using ValidateMeshShading = spvtest::ValidateBase; + +TEST_F(ValidateMeshShading, EmitMeshTasksEXTNotLastInstructionUniversal) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint TaskEXT %main "main" %p + OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1 + %void = OpTypeVoid + %func = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %float = OpTypeFloat 32 + %arr_float = OpTypeArray %float %uint_1 + %Payload = OpTypeStruct %arr_float +%ptr_Payload = OpTypePointer TaskPayloadWorkgroupEXT %Payload + %p = OpVariable %ptr_Payload TaskPayloadWorkgroupEXT + %main = OpFunction %void None %func + %label1 = OpLabel + OpEmitMeshTasksEXT %uint_1 %uint_1 %uint_1 %p + OpBranch %label2 + %label2 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Branch must appear in a block")); +} + +TEST_F(ValidateMeshShading, EmitMeshTasksEXTNotLastInstructionVulkan) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint TaskEXT %main "main" %p + OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1 + %void = OpTypeVoid + %func = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %float = OpTypeFloat 32 + %arr_float = OpTypeArray %float %uint_1 + %Payload = OpTypeStruct %arr_float +%ptr_Payload = OpTypePointer TaskPayloadWorkgroupEXT %Payload + %p = OpVariable %ptr_Payload TaskPayloadWorkgroupEXT + %main = OpFunction %void None %func + %label1 = OpLabel + OpEmitMeshTasksEXT %uint_1 %uint_1 %uint_1 %p + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Return must appear in a block")); +} + +TEST_F(ValidateMeshShading, BasicTaskSuccess) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint TaskEXT %main "main" + %void = OpTypeVoid + %func = OpTypeFunction %void + %main = OpFunction %void None %func + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); +} + +TEST_F(ValidateMeshShading, BasicMeshSuccess) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" + OpExecutionMode %main OutputVertices 1 + OpExecutionMode %main OutputPrimitivesEXT 1 + OpExecutionMode %main OutputTrianglesEXT + %void = OpTypeVoid + %func = OpTypeFunction %void + %main = OpFunction %void None %func + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); +} + +TEST_F(ValidateMeshShading, VulkanBasicMeshAndTaskSuccess) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpExtension "SPV_NV_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %mainMesh "mainMesh" + OpEntryPoint TaskEXT %mainTask "mainTask" + OpExecutionMode %mainMesh OutputVertices 1 + OpExecutionMode %mainMesh OutputPrimitivesEXT 1 + OpExecutionMode %mainMesh OutputTrianglesEXT + %void = OpTypeVoid + %func = OpTypeFunction %void + %mainMesh = OpFunction %void None %func + %labelMesh = OpLabel + OpReturn + OpFunctionEnd + %mainTask = OpFunction %void None %func + %labelTask = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateMeshShading, VulkanBasicMeshAndTaskBad) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpCapability MeshShadingNV + OpExtension "SPV_EXT_mesh_shader" + OpExtension "SPV_NV_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %mainMesh "mainMesh" + OpEntryPoint TaskNV %mainTask "mainTask" + OpExecutionMode %mainMesh OutputVertices 1 + OpExecutionMode %mainMesh OutputPrimitivesEXT 1 + OpExecutionMode %mainMesh OutputTrianglesEXT + %void = OpTypeVoid + %func = OpTypeFunction %void + %mainMesh = OpFunction %void None %func + %labelMesh = OpLabel + OpReturn + OpFunctionEnd + %mainTask = OpFunction %void None %func + %labelTask = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-MeshEXT-07102")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Module can't mix MeshEXT/TaskEXT with MeshNV/TaskNV " + "Execution Model.")); +} + +TEST_F(ValidateMeshShading, MeshMissingOutputVertices) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" + OpExecutionMode %main OutputPrimitivesEXT 1 + OpExecutionMode %main OutputTrianglesEXT + %void = OpTypeVoid + %func = OpTypeFunction %void + %main = OpFunction %void None %func + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("MeshEXT execution model entry points must specify both " + "OutputPrimitivesEXT and OutputVertices Execution Modes.")); +} + +TEST_F(ValidateMeshShading, MeshMissingOutputPrimitivesEXT) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" + OpExecutionMode %main OutputVertices 1 + OpExecutionMode %main OutputTrianglesEXT + %void = OpTypeVoid + %func = OpTypeFunction %void + %main = OpFunction %void None %func + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("MeshEXT execution model entry points must specify both " + "OutputPrimitivesEXT and OutputVertices Execution Modes.")); +} + +TEST_F(ValidateMeshShading, MeshMissingOutputType) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" + OpExecutionMode %main OutputVertices 1 + OpExecutionMode %main OutputPrimitivesEXT 1 + %void = OpTypeVoid + %func = OpTypeFunction %void + %main = OpFunction %void None %func + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("MeshEXT execution model entry points must specify " + "exactly one of OutputPoints, OutputLinesEXT, or " + "OutputTrianglesEXT Execution Modes.")); +} + +TEST_F(ValidateMeshShading, MeshMultipleOutputType) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" + OpExecutionMode %main OutputVertices 1 + OpExecutionMode %main OutputPrimitivesEXT 1 + OpExecutionMode %main OutputLinesEXT + OpExecutionMode %main OutputTrianglesEXT + %void = OpTypeVoid + %func = OpTypeFunction %void + %main = OpFunction %void None %func + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("MeshEXT execution model entry points must specify " + "exactly one of OutputPoints, OutputLinesEXT, or " + "OutputTrianglesEXT Execution Modes.")); +} + +TEST_F(ValidateMeshShading, BadExecutionModelOutputLinesEXT) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main OutputLinesEXT + %void = OpTypeVoid + %func = OpTypeFunction %void + %main = OpFunction %void None %func + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Execution mode can only be used with the MeshEXT or " + "MeshNV execution model.")); +} + +TEST_F(ValidateMeshShading, BadExecutionModelOutputTrianglesEXT) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main OutputTrianglesEXT + %void = OpTypeVoid + %func = OpTypeFunction %void + %main = OpFunction %void None %func + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Execution mode can only be used with the MeshEXT or " + "MeshNV execution model.")); +} + +TEST_F(ValidateMeshShading, BadExecutionModelOutputPrimitivesEXT) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main OutputPrimitivesEXT 1 + %void = OpTypeVoid + %func = OpTypeFunction %void + %main = OpFunction %void None %func + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Execution mode can only be used with the MeshEXT or " + "MeshNV execution model.")); +} + +TEST_F(ValidateMeshShading, OpEmitMeshTasksBadGroupCountSignedInt) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint TaskEXT %main "main" + %void = OpTypeVoid + %func = OpTypeFunction %void + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %int_1 = OpConstant %int 1 + %uint_1 = OpConstant %uint 1 + %main = OpFunction %void None %func + %label = OpLabel + OpEmitMeshTasksEXT %int_1 %uint_1 %uint_1 + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Group Count X must be a 32-bit unsigned int scalar")); +} + +TEST_F(ValidateMeshShading, OpEmitMeshTasksBadGroupCountVector) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint TaskEXT %main "main" + %void = OpTypeVoid + %func = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 +%_ptr_v2uint = OpTypePointer Function %v2uint + %uint_1 = OpConstant %uint 1 + %composite = OpConstantComposite %v2uint %uint_1 %uint_1 + %_ptr_uint = OpTypePointer Function %uint + %main = OpFunction %void None %func + %label = OpLabel + %x = OpVariable %_ptr_v2uint Function + OpStore %x %composite + %13 = OpAccessChain %_ptr_uint %x %uint_1 + %14 = OpLoad %uint %13 + OpEmitMeshTasksEXT %14 %composite %uint_1 + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Group Count Y must be a 32-bit unsigned int scalar")); +} + +TEST_F(ValidateMeshShading, OpEmitMeshTasksBadPayload) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint TaskEXT %main "main" %payload + %void = OpTypeVoid + %func = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %task = OpTypeStruct %uint +%_ptr_Uniform = OpTypePointer Uniform %task + %payload = OpVariable %_ptr_Uniform Uniform + %uint_1 = OpConstant %uint 1 + %main = OpFunction %void None %func + %label = OpLabel + OpEmitMeshTasksEXT %uint_1 %uint_1 %uint_1 %payload + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Payload OpVariable must have a storage class of " + "TaskPayloadWorkgroupEXT")); +} + +TEST_F(ValidateMeshShading, TaskPayloadWorkgroupBadExecutionModel) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %payload + %void = OpTypeVoid + %func = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_TaskPayloadWorkgroupEXT = OpTypePointer TaskPayloadWorkgroupEXT %uint + %payload = OpVariable %_ptr_TaskPayloadWorkgroupEXT TaskPayloadWorkgroupEXT + %main = OpFunction %void None %func + %label = OpLabel + %load = OpLoad %uint %payload + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("TaskPayloadWorkgroupEXT Storage Class is limited to " + "TaskEXT and MeshKHR execution model")); +} + +TEST_F(ValidateMeshShading, OpSetMeshOutputsBadVertexCount) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" + OpExecutionMode %main OutputVertices 1 + OpExecutionMode %main OutputPrimitivesNV 1 + OpExecutionMode %main OutputTrianglesNV + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %_ptr_int = OpTypePointer Function %int + %int_1 = OpConstant %int 1 + %uint_1 = OpConstant %uint 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %x = OpVariable %_ptr_int Function + OpStore %x %int_1 + %load = OpLoad %int %x + OpSetMeshOutputsEXT %load %uint_1 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Vertex Count must be a 32-bit unsigned int scalar")); +} + +TEST_F(ValidateMeshShading, OpSetMeshOutputsBadPrimitiveCount) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" + OpExecutionMode %main OutputVertices 1 + OpExecutionMode %main OutputPrimitivesNV 1 + OpExecutionMode %main OutputTrianglesNV + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %int_1 = OpConstant %int 1 + %uint_1 = OpConstant %uint 1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpSetMeshOutputsEXT %uint_1 %int_1 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Primitive Count must be a 32-bit unsigned int scalar")); +} + +TEST_F(ValidateMeshShading, OpSetMeshOutputsBadExecutionModel) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint TaskEXT %main "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpSetMeshOutputsEXT %uint_1 %uint_1 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpSetMeshOutputsEXT requires MeshEXT execution model")); +} + +TEST_F(ValidateMeshShading, OpSetMeshOutputsZeroSuccess) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" + OpExecutionMode %main OutputVertices 1 + OpExecutionMode %main OutputPrimitivesNV 1 + OpExecutionMode %main OutputTrianglesNV + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %main = OpFunction %void None %3 + %5 = OpLabel + OpSetMeshOutputsEXT %uint_0 %uint_0 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); +} + +TEST_F(ValidateMeshShading, OpEmitMeshTasksZeroSuccess) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint TaskEXT %main "main" + %void = OpTypeVoid + %func = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 1 + %main = OpFunction %void None %func + %label = OpLabel + OpEmitMeshTasksEXT %uint_0 %uint_0 %uint_0 + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/test/val/val_modes_test.cpp b/test/val/val_modes_test.cpp index 99f5c9cf..689f0baa 100644 --- a/test/val/val_modes_test.cpp +++ b/test/val/val_modes_test.cpp @@ -63,12 +63,13 @@ OpEntryPoint GLCompute %main "main" CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-LocalSize-04683")); + AnyVUID("VUID-StandaloneSpirv-LocalSize-06426")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("In the Vulkan environment, GLCompute execution model entry " - "points require either the LocalSize execution mode or an " - "object decorated with WorkgroupSize must be specified.")); + HasSubstr( + "In the Vulkan environment, GLCompute execution model entry " + "points require either the LocalSize or LocalSizeId execution mode " + "or an object decorated with WorkgroupSize must be specified.")); } TEST_F(ValidateMode, GLComputeNoModeVulkanWorkgroupSize) { @@ -101,6 +102,40 @@ OpExecutionMode %main LocalSize 1 1 1 EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); } +TEST_F(ValidateMode, GLComputeVulkanLocalSizeIdBad) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_1; // need SPIR-V 1.2 + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("LocalSizeId mode is not allowed by the current environment.")); +} + +TEST_F(ValidateMode, GLComputeVulkanLocalSizeIdGood) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_1; // need SPIR-V 1.2 + CompileSuccessfully(spirv, env); + spvValidatorOptionsSetAllowLocalSizeId(getValidatorOptions(), true); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); +} + TEST_F(ValidateMode, FragmentOriginLowerLeftVulkan) { const std::string spirv = R"( OpCapability Shader @@ -672,7 +707,7 @@ INSTANTIATE_TEST_SUITE_P(ValidateModeKernelOnlyGoodSpv13, ValidateModeExecution, Values("Kernel"), Values("LocalSizeHint 1 1 1", "VecTypeHint 4", "ContractionOff", - "LocalSizeHintId %int1"), + "LocalSizeHintId %int1 %int1 %int1"), Values(SPV_ENV_UNIVERSAL_1_3))); INSTANTIATE_TEST_SUITE_P( @@ -684,7 +719,7 @@ INSTANTIATE_TEST_SUITE_P( Values("Geometry", "TessellationControl", "TessellationEvaluation", "GLCompute", "Vertex", "Fragment"), Values("LocalSizeHint 1 1 1", "VecTypeHint 4", "ContractionOff", - "LocalSizeHintId %int1"), + "LocalSizeHintId %int1 %int1 %int1"), Values(SPV_ENV_UNIVERSAL_1_3))); INSTANTIATE_TEST_SUITE_P( @@ -863,7 +898,7 @@ OpCapability Kernel OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Kernel %main "main" -OpExecutionMode %main LocalSizeHintId %int_1 +OpExecutionMode %main LocalSizeHintId %int_1 %int_1 %int_1 %int = OpTypeInt 32 0 %int_1 = OpConstant %int 1 )" + kVoidFunction; @@ -882,7 +917,7 @@ OpCapability Kernel OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Kernel %main "main" -OpExecutionModeId %main LocalSizeHintId %int_1 +OpExecutionModeId %main LocalSizeHintId %int_1 %int_1 %int_1 %int = OpTypeInt 32 0 %int_1 = OpConstant %int 1 )" + kVoidFunction; @@ -898,7 +933,7 @@ OpCapability Kernel OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %main "main" -OpExecutionModeId %main LocalSizeHintId %int_1 +OpExecutionModeId %main LocalSizeHintId %int_1 %int_1 %int_1 %int = OpTypeInt 32 0 %int_ptr = OpTypePointer Private %int %int_1 = OpVariable %int_ptr Private @@ -1066,6 +1101,89 @@ OpFunctionEnd EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); } + +TEST_F(ValidateMode, FragmentShaderStencilRefFrontTooManyModesBad) { + const std::string spirv = R"( +OpCapability Shader +OpCapability StencilExportEXT +OpExtension "SPV_AMD_shader_early_and_late_fragment_tests" +OpExtension "SPV_EXT_shader_stencil_export" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpExecutionMode %main EarlyAndLateFragmentTestsAMD +OpExecutionMode %main StencilRefLessFrontAMD +OpExecutionMode %main StencilRefGreaterFrontAMD +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Fragment execution model entry points can specify at most " + "one of StencilRefUnchangedFrontAMD, " + "StencilRefLessFrontAMD or StencilRefGreaterFrontAMD " + "execution modes.")); +} + +TEST_F(ValidateMode, FragmentShaderStencilRefBackTooManyModesBad) { + const std::string spirv = R"( +OpCapability Shader +OpCapability StencilExportEXT +OpExtension "SPV_AMD_shader_early_and_late_fragment_tests" +OpExtension "SPV_EXT_shader_stencil_export" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpExecutionMode %main EarlyAndLateFragmentTestsAMD +OpExecutionMode %main StencilRefLessBackAMD +OpExecutionMode %main StencilRefGreaterBackAMD +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Fragment execution model entry points can specify at most " + "one of StencilRefUnchangedBackAMD, " + "StencilRefLessBackAMD or StencilRefGreaterBackAMD " + "execution modes.")); +} + +TEST_F(ValidateMode, FragmentShaderStencilRefFrontGood) { + const std::string spirv = R"( +OpCapability Shader +OpCapability StencilExportEXT +OpExtension "SPV_AMD_shader_early_and_late_fragment_tests" +OpExtension "SPV_EXT_shader_stencil_export" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpExecutionMode %main EarlyAndLateFragmentTestsAMD +OpExecutionMode %main StencilRefLessFrontAMD +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateMode, FragmentShaderStencilRefBackGood) { + const std::string spirv = R"( +OpCapability Shader +OpCapability StencilExportEXT +OpExtension "SPV_AMD_shader_early_and_late_fragment_tests" +OpExtension "SPV_EXT_shader_stencil_export" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpExecutionMode %main EarlyAndLateFragmentTestsAMD +OpExecutionMode %main StencilRefLessBackAMD +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); +} + TEST_F(ValidateMode, FragmentShaderDemoteVertexBad) { const std::string spirv = R"( OpCapability Shader @@ -1143,6 +1261,26 @@ OpFunctionEnd HasSubstr("Expected bool scalar type as Result Type")); } +TEST_F(ValidateMode, LocalSizeIdVulkan1p3DoesNotRequireOption) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_non_semantic_test.cpp b/test/val/val_non_semantic_test.cpp index b80bb1ac..85362fef 100644 --- a/test/val/val_non_semantic_test.cpp +++ b/test/val/val_non_semantic_test.cpp @@ -105,7 +105,7 @@ INSTANTIATE_TEST_SUITE_P(OnlyOpExtension, ValidateNonSemanticGenerated, Values(""), Values(TestResult()))); INSTANTIATE_TEST_SUITE_P( - MissingOpExtension, ValidateNonSemanticGenerated, + MissingOpExtensionPre1p6, ValidateNonSemanticGenerated, Combine(Values(false), Values(true), Values(""), Values(""), Values(TestResult( SPV_ERROR_INVALID_DATA, @@ -187,7 +187,27 @@ OpEntryPoint Vertex %main "main" // there's no specific error for using an OpExtInst too early, it requires a // type so by definition any use of a type in it will be an undefined ID EXPECT_THAT(getDiagnosticString(), - HasSubstr("ID 2[%2] has not been defined")); + HasSubstr("ID '2[%2]' has not been defined")); +} + +TEST_F(ValidateNonSemanticString, MissingOpExtensionPost1p6) { + const std::string spirv = R"( +OpCapability Shader +%extinst = OpExtInstImport "NonSemantic.Testing.Set" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%test = OpExtInst %void %extinst 3 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_6); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); } } // namespace diff --git a/test/val/val_ray_query_test.cpp b/test/val/val_ray_query_test.cpp new file mode 100644 index 00000000..e0eb0675 --- /dev/null +++ b/test/val/val_ray_query_test.cpp @@ -0,0 +1,631 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests ray query instructions from SPV_KHR_ray_query. + +#include +#include + +#include "gmock/gmock.h" +#include "spirv-tools/libspirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Values; + +using ValidateRayQuery = spvtest::ValidateBase; + +std::string GenerateShaderCode( + const std::string& body, + const std::string& capabilities_and_extensions = "", + const std::string& declarations = "") { + std::ostringstream ss; + ss << R"( +OpCapability Shader +OpCapability Int64 +OpCapability Float64 +OpCapability RayQueryKHR +OpExtension "SPV_KHR_ray_query" +)"; + + ss << capabilities_and_extensions; + + ss << R"( +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 + +OpDecorate %top_level_as DescriptorSet 0 +OpDecorate %top_level_as Binding 0 + +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%f64 = OpTypeFloat 64 +%u32 = OpTypeInt 32 0 +%s32 = OpTypeInt 32 1 +%u64 = OpTypeInt 64 0 +%s64 = OpTypeInt 64 1 +%type_rq = OpTypeRayQueryKHR +%type_as = OpTypeAccelerationStructureKHR + +%s32vec2 = OpTypeVector %s32 2 +%u32vec2 = OpTypeVector %u32 2 +%f32vec2 = OpTypeVector %f32 2 +%u32vec3 = OpTypeVector %u32 3 +%s32vec3 = OpTypeVector %s32 3 +%f32vec3 = OpTypeVector %f32 3 +%u32vec4 = OpTypeVector %u32 4 +%s32vec4 = OpTypeVector %s32 4 +%f32vec4 = OpTypeVector %f32 4 + +%mat4x3 = OpTypeMatrix %f32vec3 4 + +%f32_0 = OpConstant %f32 0 +%f64_0 = OpConstant %f64 0 +%s32_0 = OpConstant %s32 0 +%u32_0 = OpConstant %u32 0 +%u64_0 = OpConstant %u64 0 + +%u32vec3_0 = OpConstantComposite %u32vec3 %u32_0 %u32_0 %u32_0 +%f32vec3_0 = OpConstantComposite %f32vec3 %f32_0 %f32_0 %f32_0 +%f32vec4_0 = OpConstantComposite %f32vec4 %f32_0 %f32_0 %f32_0 %f32_0 + +%ptr_rq = OpTypePointer Private %type_rq +%ray_query = OpVariable %ptr_rq Private + +%ptr_as = OpTypePointer UniformConstant %type_as +%top_level_as = OpVariable %ptr_as UniformConstant + +%ptr_function_u32 = OpTypePointer Function %u32 +%ptr_function_f32 = OpTypePointer Function %f32 +%ptr_function_f32vec3 = OpTypePointer Function %f32vec3 +)"; + + ss << declarations; + + ss << R"( +%main = OpFunction %void None %func +%main_entry = OpLabel +)"; + + ss << body; + + ss << R"( +OpReturn +OpFunctionEnd)"; + return ss.str(); +} + +std::string RayQueryResult(std::string opcode) { + if (opcode.compare("OpRayQueryProceedKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionTypeKHR") == 0 || + opcode.compare("OpRayQueryGetRayTMinKHR") == 0 || + opcode.compare("OpRayQueryGetRayFlagsKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionTKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionInstanceCustomIndexKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionInstanceIdKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionInstanceShaderBindingTableRecord" + "OffsetKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionGeometryIndexKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionPrimitiveIndexKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionBarycentricsKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionFrontFaceKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionCandidateAABBOpaqueKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionObjectRayDirectionKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionObjectRayOriginKHR") == 0 || + opcode.compare("OpRayQueryGetWorldRayDirectionKHR") == 0 || + opcode.compare("OpRayQueryGetWorldRayOriginKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionObjectToWorldKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionWorldToObjectKHR") == 0) { + return "%result ="; + } + return ""; +} + +std::string RayQueryResultType(std::string opcode, bool valid) { + if (opcode.compare("OpRayQueryGetIntersectionTypeKHR") == 0 || + opcode.compare("OpRayQueryGetRayFlagsKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionInstanceCustomIndexKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionInstanceIdKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionInstanceShaderBindingTableRecord" + "OffsetKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionGeometryIndexKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionPrimitiveIndexKHR") == 0) { + return valid ? "%u32" : "%f64"; + } + + if (opcode.compare("OpRayQueryGetRayTMinKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionTKHR") == 0) { + return valid ? "%f32" : "%f64"; + } + + if (opcode.compare("OpRayQueryGetIntersectionBarycentricsKHR") == 0) { + return valid ? "%f32vec2" : "%f64"; + } + + if (opcode.compare("OpRayQueryGetIntersectionObjectRayDirectionKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionObjectRayOriginKHR") == 0 || + opcode.compare("OpRayQueryGetWorldRayDirectionKHR") == 0 || + opcode.compare("OpRayQueryGetWorldRayOriginKHR") == 0) { + return valid ? "%f32vec3" : "%f64"; + } + + if (opcode.compare("OpRayQueryProceedKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionFrontFaceKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionCandidateAABBOpaqueKHR") == 0) { + return valid ? "%bool" : "%f64"; + } + + if (opcode.compare("OpRayQueryGetIntersectionObjectToWorldKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionWorldToObjectKHR") == 0) { + return valid ? "%mat4x3" : "%f64"; + } + return ""; +} + +std::string RayQueryIntersection(std::string opcode, bool valid) { + if (opcode.compare("OpRayQueryGetIntersectionTypeKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionTKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionInstanceCustomIndexKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionInstanceIdKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionInstanceShaderBindingTableRecord" + "OffsetKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionGeometryIndexKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionPrimitiveIndexKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionBarycentricsKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionFrontFaceKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionObjectRayDirectionKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionObjectRayOriginKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionObjectToWorldKHR") == 0 || + opcode.compare("OpRayQueryGetIntersectionWorldToObjectKHR") == 0) { + return valid ? "%s32_0" : "%f32_0"; + } + return ""; +} + +using RayQueryCommon = spvtest::ValidateBase; + +TEST_P(RayQueryCommon, Success) { + std::string opcode = GetParam(); + std::ostringstream ss; + ss << RayQueryResult(opcode); + ss << " " << opcode << " "; + ss << RayQueryResultType(opcode, true); + ss << " %ray_query "; + ss << RayQueryIntersection(opcode, true); + CompileSuccessfully(GenerateShaderCode(ss.str()).c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(RayQueryCommon, BadQuery) { + std::string opcode = GetParam(); + std::ostringstream ss; + ss << RayQueryResult(opcode); + ss << " " << opcode << " "; + ss << RayQueryResultType(opcode, true); + ss << " %top_level_as "; + ss << RayQueryIntersection(opcode, true); + CompileSuccessfully(GenerateShaderCode(ss.str()).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Ray Query must be a pointer to OpTypeRayQueryKHR")); +} + +TEST_P(RayQueryCommon, BadResult) { + std::string opcode = GetParam(); + std::string result_type = RayQueryResultType(opcode, false); + if (!result_type.empty()) { + std::ostringstream ss; + ss << RayQueryResult(opcode); + ss << " " << opcode << " "; + ss << result_type; + ss << " %ray_query "; + ss << RayQueryIntersection(opcode, true); + CompileSuccessfully(GenerateShaderCode(ss.str()).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + + std::string correct_result_type = RayQueryResultType(opcode, true); + if (correct_result_type.compare("%u32") == 0) { + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("expected Result Type to be 32-bit int scalar type")); + } else if (correct_result_type.compare("%f32") == 0) { + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("expected Result Type to be 32-bit float scalar type")); + } else if (correct_result_type.compare("%f32vec2") == 0) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected Result Type to be 32-bit float " + "2-component vector type")); + } else if (correct_result_type.compare("%f32vec3") == 0) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected Result Type to be 32-bit float " + "3-component vector type")); + } else if (correct_result_type.compare("%bool") == 0) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected Result Type to be bool scalar type")); + } else if (correct_result_type.compare("%mat4x3") == 0) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected matrix type as Result Type")); + } + } +} + +TEST_P(RayQueryCommon, BadIntersection) { + std::string opcode = GetParam(); + std::string intersection = RayQueryIntersection(opcode, false); + if (!intersection.empty()) { + std::ostringstream ss; + ss << RayQueryResult(opcode); + ss << " " << opcode << " "; + ss << RayQueryResultType(opcode, true); + ss << " %ray_query "; + ss << intersection; + CompileSuccessfully(GenerateShaderCode(ss.str()).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "expected Intersection ID to be a constant 32-bit int scalar")); + } +} + +INSTANTIATE_TEST_SUITE_P( + ValidateRayQueryCommon, RayQueryCommon, + Values("OpRayQueryTerminateKHR", "OpRayQueryConfirmIntersectionKHR", + "OpRayQueryProceedKHR", "OpRayQueryGetIntersectionTypeKHR", + "OpRayQueryGetRayTMinKHR", "OpRayQueryGetRayFlagsKHR", + "OpRayQueryGetWorldRayDirectionKHR", + "OpRayQueryGetWorldRayOriginKHR", "OpRayQueryGetIntersectionTKHR", + "OpRayQueryGetIntersectionInstanceCustomIndexKHR", + "OpRayQueryGetIntersectionInstanceIdKHR", + "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR", + "OpRayQueryGetIntersectionGeometryIndexKHR", + "OpRayQueryGetIntersectionPrimitiveIndexKHR", + "OpRayQueryGetIntersectionBarycentricsKHR", + "OpRayQueryGetIntersectionFrontFaceKHR", + "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR", + "OpRayQueryGetIntersectionObjectRayDirectionKHR", + "OpRayQueryGetIntersectionObjectRayOriginKHR", + "OpRayQueryGetIntersectionObjectToWorldKHR", + "OpRayQueryGetIntersectionWorldToObjectKHR")); + +// tests various Intersection operand types +TEST_F(ValidateRayQuery, IntersectionSuccess) { + const std::string body = R"( +%result_1 = OpRayQueryGetIntersectionFrontFaceKHR %bool %ray_query %s32_0 +%result_2 = OpRayQueryGetIntersectionFrontFaceKHR %bool %ray_query %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateRayQuery, IntersectionVector) { + const std::string body = R"( +%result = OpRayQueryGetIntersectionFrontFaceKHR %bool %ray_query %u32vec3_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("expected Intersection ID to be a constant 32-bit int scalar")); +} + +TEST_F(ValidateRayQuery, IntersectionNonConstantVariable) { + const std::string body = R"( +%var = OpVariable %ptr_function_u32 Function +%result = OpRayQueryGetIntersectionFrontFaceKHR %bool %ray_query %var +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("expected Intersection ID to be a constant 32-bit int scalar")); +} + +TEST_F(ValidateRayQuery, IntersectionNonConstantLoad) { + const std::string body = R"( +%var = OpVariable %ptr_function_u32 Function +%load = OpLoad %u32 %var +%result = OpRayQueryGetIntersectionFrontFaceKHR %bool %ray_query %load +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("expected Intersection ID to be a constant 32-bit int scalar")); +} + +TEST_F(ValidateRayQuery, InitializeSuccess) { + const std::string body = R"( +%var_u32 = OpVariable %ptr_function_u32 Function +%var_f32 = OpVariable %ptr_function_f32 Function +%var_f32vec3 = OpVariable %ptr_function_f32vec3 Function + +%as = OpLoad %type_as %top_level_as +OpRayQueryInitializeKHR %ray_query %as %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0 + +%_u32 = OpLoad %u32 %var_u32 +%_f32 = OpLoad %f32 %var_f32 +%_f32vec3 = OpLoad %f32vec3 %var_f32vec3 +OpRayQueryInitializeKHR %ray_query %as %_u32 %_u32 %_f32vec3 %_f32 %_f32vec3 %_f32 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateRayQuery, InitializeFunctionSuccess) { + const std::string declaration = R"( +%rq_ptr = OpTypePointer Private %type_rq +%rq_func_type = OpTypeFunction %void %rq_ptr +%rq_var_1 = OpVariable %rq_ptr Private +%rq_var_2 = OpVariable %rq_ptr Private +)"; + + const std::string body = R"( +%fcall_1 = OpFunctionCall %void %rq_func %rq_var_1 +%as_1 = OpLoad %type_as %top_level_as +OpRayQueryInitializeKHR %rq_var_1 %as_1 %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0 +%fcall_2 = OpFunctionCall %void %rq_func %rq_var_2 +OpReturn +OpFunctionEnd +%rq_func = OpFunction %void None %rq_func_type +%rq_param = OpFunctionParameter %rq_ptr +%label = OpLabel +%as_2 = OpLoad %type_as %top_level_as +OpRayQueryInitializeKHR %rq_param %as_2 %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body, "", declaration).c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateRayQuery, InitializeBadRayQuery) { + const std::string body = R"( +%load = OpLoad %type_as %top_level_as +OpRayQueryInitializeKHR %top_level_as %load %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Ray Query must be a pointer to OpTypeRayQueryKHR")); +} + +TEST_F(ValidateRayQuery, InitializeBadAS) { + const std::string body = R"( +OpRayQueryInitializeKHR %ray_query %ray_query %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Acceleration Structure to be of type " + "OpTypeAccelerationStructureKHR")); +} + +TEST_F(ValidateRayQuery, InitializeBadRayFlags64) { + const std::string body = R"( +%load = OpLoad %type_as %top_level_as +OpRayQueryInitializeKHR %ray_query %load %u64_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Ray Flags must be a 32-bit int scalar")); +} + +TEST_F(ValidateRayQuery, InitializeBadRayFlagsVector) { + const std::string body = R"( +%load = OpLoad %type_as %top_level_as +OpRayQueryInitializeKHR %ray_query %load %u32vec2 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand '15[%v2uint]' cannot be a type")); +} + +TEST_F(ValidateRayQuery, InitializeBadCullMask) { + const std::string body = R"( +%load = OpLoad %type_as %top_level_as +OpRayQueryInitializeKHR %ray_query %load %u32_0 %f32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Cull Mask must be a 32-bit int scalar")); +} + +TEST_F(ValidateRayQuery, InitializeBadRayOriginVec4) { + const std::string body = R"( +%load = OpLoad %type_as %top_level_as +OpRayQueryInitializeKHR %ray_query %load %u32_0 %u32_0 %f32vec4_0 %f32_0 %f32vec3_0 %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Ray Origin must be a 32-bit float 3-component vector")); +} + +TEST_F(ValidateRayQuery, InitializeBadRayOriginFloat) { + const std::string body = R"( +%var_f32 = OpVariable %ptr_function_f32 Function +%_f32 = OpLoad %f32 %var_f32 +%load = OpLoad %type_as %top_level_as +OpRayQueryInitializeKHR %ray_query %load %u32_0 %u32_0 %_f32 %f32_0 %f32vec3_0 %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Ray Origin must be a 32-bit float 3-component vector")); +} + +TEST_F(ValidateRayQuery, InitializeBadRayOriginInt) { + const std::string body = R"( +%load = OpLoad %type_as %top_level_as +OpRayQueryInitializeKHR %ray_query %load %u32_0 %u32_0 %u32vec3_0 %f32_0 %f32vec3_0 %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Ray Origin must be a 32-bit float 3-component vector")); +} + +TEST_F(ValidateRayQuery, InitializeBadRayTMin) { + const std::string body = R"( +%load = OpLoad %type_as %top_level_as +OpRayQueryInitializeKHR %ray_query %load %u32_0 %u32_0 %f32vec3_0 %u32_0 %f32vec3_0 %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Ray TMin must be a 32-bit float scalar")); +} + +TEST_F(ValidateRayQuery, InitializeBadRayDirection) { + const std::string body = R"( +%load = OpLoad %type_as %top_level_as +OpRayQueryInitializeKHR %ray_query %load %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec4_0 %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Ray Direction must be a 32-bit float 3-component vector")); +} + +TEST_F(ValidateRayQuery, InitializeBadRayTMax) { + const std::string body = R"( +%load = OpLoad %type_as %top_level_as +OpRayQueryInitializeKHR %ray_query %load %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f64_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Ray TMax must be a 32-bit float scalar")); +} + +TEST_F(ValidateRayQuery, GenerateIntersectionSuccess) { + const std::string body = R"( +%var = OpVariable %ptr_function_f32 Function +%load = OpLoad %f32 %var +OpRayQueryGenerateIntersectionKHR %ray_query %f32_0 +OpRayQueryGenerateIntersectionKHR %ray_query %load +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateRayQuery, GenerateIntersectionBadRayQuery) { + const std::string body = R"( +OpRayQueryGenerateIntersectionKHR %top_level_as %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Ray Query must be a pointer to OpTypeRayQueryKHR")); +} + +TEST_F(ValidateRayQuery, GenerateIntersectionBadHitT) { + const std::string body = R"( +OpRayQueryGenerateIntersectionKHR %ray_query %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Hit T must be a 32-bit float scalar")); +} + +TEST_F(ValidateRayQuery, RayQueryArraySuccess) { + // This shader is slightly different to the ones above, so it doesn't reuse + // the shader code generator. + const std::string shader = R"( + OpCapability Shader + OpCapability RayQueryKHR + OpExtension "SPV_KHR_ray_query" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 460 + OpDecorate %topLevelAS DescriptorSet 0 + OpDecorate %topLevelAS Binding 0 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %func = OpTypeFunction %void + %ray_query = OpTypeRayQueryKHR + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %ray_query_array = OpTypeArray %ray_query %uint_2 +%ptr_ray_query_array = OpTypePointer Private %ray_query_array + %rayQueries = OpVariable %ptr_ray_query_array Private + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %ptr_ray_query = OpTypePointer Private %ray_query + %accel_struct = OpTypeAccelerationStructureKHR + %ptr_accel_struct = OpTypePointer UniformConstant %accel_struct + %topLevelAS = OpVariable %ptr_accel_struct UniformConstant + %uint_0 = OpConstant %uint 0 + %uint_255 = OpConstant %uint 255 + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %float_0 = OpConstant %float 0 + %vec3_zero = OpConstantComposite %v3float %float_0 %float_0 %float_0 + %float_1 = OpConstant %float 1 + %vec3_xy_0_z_1 = OpConstantComposite %v3float %float_0 %float_0 %float_1 + %float_10 = OpConstant %float 10 + %v3uint = OpTypeVector %uint 3 + %uint_1 = OpConstant %uint 1 + %gl_WorkGroupSize = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1 + %main = OpFunction %void None %func + %main_label = OpLabel + %first_ray_query = OpAccessChain %ptr_ray_query %rayQueries %int_0 + %topLevelAS_val = OpLoad %accel_struct %topLevelAS + OpRayQueryInitializeKHR %first_ray_query %topLevelAS_val %uint_0 %uint_255 %vec3_zero %float_0 %vec3_xy_0_z_1 %float_10 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(shader); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/test/val/val_ray_tracing_test.cpp b/test/val/val_ray_tracing_test.cpp new file mode 100644 index 00000000..58b9356c --- /dev/null +++ b/test/val/val_ray_tracing_test.cpp @@ -0,0 +1,583 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests ray tracing instructions from SPV_KHR_ray_tracing. + +#include +#include + +#include "gmock/gmock.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Values; + +using ValidateRayTracing = spvtest::ValidateBase; + +TEST_F(ValidateRayTracing, IgnoreIntersectionSuccess) { + const std::string body = R"( +OpCapability RayTracingKHR +OpExtension "SPV_KHR_ray_tracing" +OpMemoryModel Logical GLSL450 +OpEntryPoint AnyHitKHR %main "main" +OpName %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%main = OpFunction %void None %func +%label = OpLabel +OpIgnoreIntersectionKHR +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateRayTracing, IgnoreIntersectionExecutionModel) { + const std::string body = R"( +OpCapability RayTracingKHR +OpExtension "SPV_KHR_ray_tracing" +OpMemoryModel Logical GLSL450 +OpEntryPoint CallableKHR %main "main" +OpName %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%main = OpFunction %void None %func +%label = OpLabel +OpIgnoreIntersectionKHR +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpIgnoreIntersectionKHR requires AnyHitKHR execution model")); +} + +TEST_F(ValidateRayTracing, TerminateRaySuccess) { + const std::string body = R"( +OpCapability RayTracingKHR +OpExtension "SPV_KHR_ray_tracing" +OpMemoryModel Logical GLSL450 +OpEntryPoint AnyHitKHR %main "main" +OpName %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%main = OpFunction %void None %func +%label = OpLabel +OpIgnoreIntersectionKHR +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateRayTracing, TerminateRayExecutionModel) { + const std::string body = R"( +OpCapability RayTracingKHR +OpExtension "SPV_KHR_ray_tracing" +OpMemoryModel Logical GLSL450 +OpEntryPoint MissKHR %main "main" +OpName %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%main = OpFunction %void None %func +%label = OpLabel +OpTerminateRayKHR +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpTerminateRayKHR requires AnyHitKHR execution model")); +} + +TEST_F(ValidateRayTracing, ReportIntersectionRaySuccess) { + const std::string body = R"( +OpCapability RayTracingKHR +OpExtension "SPV_KHR_ray_tracing" +OpMemoryModel Logical GLSL450 +OpEntryPoint IntersectionKHR %main "main" +OpName %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%float = OpTypeFloat 32 +%float_1 = OpConstant %float 1 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%bool = OpTypeBool +%main = OpFunction %void None %func +%label = OpLabel +%report = OpReportIntersectionKHR %bool %float_1 %uint_1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateRayTracing, ReportIntersectionExecutionModel) { + const std::string body = R"( +OpCapability RayTracingKHR +OpExtension "SPV_KHR_ray_tracing" +OpMemoryModel Logical GLSL450 +OpEntryPoint MissKHR %main "main" +OpName %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%float = OpTypeFloat 32 +%float_1 = OpConstant %float 1 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%bool = OpTypeBool +%main = OpFunction %void None %func +%label = OpLabel +%report = OpReportIntersectionKHR %bool %float_1 %uint_1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpReportIntersectionKHR requires IntersectionKHR execution model")); +} + +TEST_F(ValidateRayTracing, ReportIntersectionReturnType) { + const std::string body = R"( +OpCapability RayTracingKHR +OpExtension "SPV_KHR_ray_tracing" +OpMemoryModel Logical GLSL450 +OpEntryPoint IntersectionKHR %main "main" +OpName %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%float = OpTypeFloat 32 +%float_1 = OpConstant %float 1 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%main = OpFunction %void None %func +%label = OpLabel +%report = OpReportIntersectionKHR %uint %float_1 %uint_1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected Result Type to be bool scalar type")); +} + +TEST_F(ValidateRayTracing, ReportIntersectionHit) { + const std::string body = R"( +OpCapability RayTracingKHR +OpCapability Float64 +OpExtension "SPV_KHR_ray_tracing" +OpMemoryModel Logical GLSL450 +OpEntryPoint IntersectionKHR %main "main" +OpName %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%float64 = OpTypeFloat 64 +%float64_1 = OpConstant %float64 1 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%bool = OpTypeBool +%main = OpFunction %void None %func +%label = OpLabel +%report = OpReportIntersectionKHR %bool %float64_1 %uint_1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Hit must be a 32-bit int scalar")); +} + +TEST_F(ValidateRayTracing, ReportIntersectionHitKind) { + const std::string body = R"( +OpCapability RayTracingKHR +OpExtension "SPV_KHR_ray_tracing" +OpMemoryModel Logical GLSL450 +OpEntryPoint IntersectionKHR %main "main" +OpName %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%float = OpTypeFloat 32 +%float_1 = OpConstant %float 1 +%sint = OpTypeInt 32 1 +%sint_1 = OpConstant %sint 1 +%bool = OpTypeBool +%main = OpFunction %void None %func +%label = OpLabel +%report = OpReportIntersectionKHR %bool %float_1 %sint_1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Hit Kind must be a 32-bit unsigned int scalar")); +} + +TEST_F(ValidateRayTracing, ExecuteCallableSuccess) { + const std::string body = R"( +OpCapability RayTracingKHR +OpExtension "SPV_KHR_ray_tracing" +OpMemoryModel Logical GLSL450 +OpEntryPoint CallableKHR %main "main" +OpName %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%data_ptr = OpTypePointer CallableDataKHR %int +%data = OpVariable %data_ptr CallableDataKHR +%inData_ptr = OpTypePointer IncomingCallableDataKHR %int +%inData = OpVariable %inData_ptr IncomingCallableDataKHR +%main = OpFunction %void None %func +%label = OpLabel +OpExecuteCallableKHR %uint_0 %data +OpExecuteCallableKHR %uint_0 %inData +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateRayTracing, ExecuteCallableExecutionModel) { + const std::string body = R"( +OpCapability RayTracingKHR +OpExtension "SPV_KHR_ray_tracing" +OpMemoryModel Logical GLSL450 +OpEntryPoint AnyHitKHR %main "main" +OpName %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%data_ptr = OpTypePointer CallableDataKHR %int +%data = OpVariable %data_ptr CallableDataKHR +%inData_ptr = OpTypePointer IncomingCallableDataKHR %int +%inData = OpVariable %inData_ptr IncomingCallableDataKHR +%main = OpFunction %void None %func +%label = OpLabel +OpExecuteCallableKHR %uint_0 %data +OpExecuteCallableKHR %uint_0 %inData +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpExecuteCallableKHR requires RayGenerationKHR, " + "ClosestHitKHR, MissKHR and CallableKHR execution models")); +} + +TEST_F(ValidateRayTracing, ExecuteCallableStorageClass) { + const std::string body = R"( +OpCapability RayTracingKHR +OpExtension "SPV_KHR_ray_tracing" +OpMemoryModel Logical GLSL450 +OpEntryPoint RayGenerationKHR %main "main" +OpName %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%data_ptr = OpTypePointer RayPayloadKHR %int +%data = OpVariable %data_ptr RayPayloadKHR +%main = OpFunction %void None %func +%label = OpLabel +OpExecuteCallableKHR %uint_0 %data +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Callable Data must have storage class CallableDataKHR " + "or IncomingCallableDataKHR")); +} + +TEST_F(ValidateRayTracing, ExecuteCallableSbtIndex) { + const std::string body = R"( +OpCapability RayTracingKHR +OpExtension "SPV_KHR_ray_tracing" +OpMemoryModel Logical GLSL450 +OpEntryPoint CallableKHR %main "main" +OpName %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%int_1 = OpConstant %int 1 +%data_ptr = OpTypePointer CallableDataKHR %int +%data = OpVariable %data_ptr CallableDataKHR +%main = OpFunction %void None %func +%label = OpLabel +OpExecuteCallableKHR %int_1 %data +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SBT Index must be a 32-bit unsigned int scalar")); +} + +std::string GenerateRayTraceCode( + const std::string& body, + const std::string execution_model = "RayGenerationKHR") { + std::ostringstream ss; + ss << R"( +OpCapability RayTracingKHR +OpCapability Float64 +OpExtension "SPV_KHR_ray_tracing" +OpMemoryModel Logical GLSL450 +OpEntryPoint )" + << execution_model << R"( %main "main" +OpDecorate %top_level_as DescriptorSet 0 +OpDecorate %top_level_as Binding 0 +%void = OpTypeVoid +%func = OpTypeFunction %void +%type_as = OpTypeAccelerationStructureKHR +%as_uc_ptr = OpTypePointer UniformConstant %type_as +%top_level_as = OpVariable %as_uc_ptr UniformConstant +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%float = OpTypeFloat 32 +%float64 = OpTypeFloat 64 +%f32vec3 = OpTypeVector %float 3 +%f32vec4 = OpTypeVector %float 4 +%float_0 = OpConstant %float 0 +%float64_0 = OpConstant %float64 0 +%v3composite = OpConstantComposite %f32vec3 %float_0 %float_0 %float_0 +%v4composite = OpConstantComposite %f32vec4 %float_0 %float_0 %float_0 %float_0 +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%payload_ptr = OpTypePointer RayPayloadKHR %int +%payload = OpVariable %payload_ptr RayPayloadKHR +%callable_ptr = OpTypePointer CallableDataKHR %int +%callable = OpVariable %callable_ptr CallableDataKHR +%ptr_uint = OpTypePointer Private %uint +%var_uint = OpVariable %ptr_uint Private +%ptr_float = OpTypePointer Private %float +%var_float = OpVariable %ptr_float Private +%ptr_f32vec3 = OpTypePointer Private %f32vec3 +%var_f32vec3 = OpVariable %ptr_f32vec3 Private +%main = OpFunction %void None %func +%label = OpLabel +)"; + + ss << body; + + ss << R"( +OpReturn +OpFunctionEnd)"; + return ss.str(); +} + +TEST_F(ValidateRayTracing, TraceRaySuccess) { + const std::string body = R"( +%as = OpLoad %type_as %top_level_as +OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float_0 %payload + +%_uint = OpLoad %uint %var_uint +%_float = OpLoad %float %var_float +%_f32vec3 = OpLoad %f32vec3 %var_f32vec3 +OpTraceRayKHR %as %_uint %_uint %_uint %_uint %_uint %_f32vec3 %_float %_f32vec3 %_float %payload +)"; + + CompileSuccessfully(GenerateRayTraceCode(body).c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateRayTracing, TraceRayExecutionModel) { + const std::string body = R"( +%as = OpLoad %type_as %top_level_as +OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float_0 %payload +)"; + + CompileSuccessfully(GenerateRayTraceCode(body, "CallableKHR").c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpTraceRayKHR requires RayGenerationKHR, " + "ClosestHitKHR and MissKHR execution models")); +} + +TEST_F(ValidateRayTracing, TraceRayAccelerationStructure) { + const std::string body = R"( +%_uint = OpLoad %uint %var_uint +OpTraceRayKHR %_uint %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float_0 %payload +)"; + + CompileSuccessfully(GenerateRayTraceCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Acceleration Structure to be of type " + "OpTypeAccelerationStructureKHR")); +} + +TEST_F(ValidateRayTracing, TraceRayRayFlags) { + const std::string body = R"( +%as = OpLoad %type_as %top_level_as +OpTraceRayKHR %as %float_0 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float_0 %payload +)"; + + CompileSuccessfully(GenerateRayTraceCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Ray Flags must be a 32-bit int scalar")); +} + +TEST_F(ValidateRayTracing, TraceRayCullMask) { + const std::string body = R"( +%as = OpLoad %type_as %top_level_as +OpTraceRayKHR %as %uint_1 %float_0 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float_0 %payload +)"; + + CompileSuccessfully(GenerateRayTraceCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Cull Mask must be a 32-bit int scalar")); +} + +TEST_F(ValidateRayTracing, TraceRaySbtOffest) { + const std::string body = R"( +%as = OpLoad %type_as %top_level_as +OpTraceRayKHR %as %uint_1 %uint_1 %float_0 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float_0 %payload +)"; + + CompileSuccessfully(GenerateRayTraceCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SBT Offset must be a 32-bit int scalar")); +} + +TEST_F(ValidateRayTracing, TraceRaySbtStride) { + const std::string body = R"( +%as = OpLoad %type_as %top_level_as +OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %float_0 %uint_1 %v3composite %float_0 %v3composite %float_0 %payload +)"; + + CompileSuccessfully(GenerateRayTraceCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SBT Stride must be a 32-bit int scalar")); +} + +TEST_F(ValidateRayTracing, TraceRayMissIndex) { + const std::string body = R"( +%as = OpLoad %type_as %top_level_as +OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %float_0 %v3composite %float_0 %v3composite %float_0 %payload +)"; + + CompileSuccessfully(GenerateRayTraceCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Miss Index must be a 32-bit int scalar")); +} + +TEST_F(ValidateRayTracing, TraceRayRayOrigin) { + const std::string body = R"( +%as = OpLoad %type_as %top_level_as +OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %float_0 %float_0 %v3composite %float_0 %payload +)"; + + CompileSuccessfully(GenerateRayTraceCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Ray Origin must be a 32-bit float 3-component vector")); +} + +TEST_F(ValidateRayTracing, TraceRayRayTMin) { + const std::string body = R"( +%as = OpLoad %type_as %top_level_as +OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %uint_1 %v3composite %float_0 %payload +)"; + + CompileSuccessfully(GenerateRayTraceCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Ray TMin must be a 32-bit float scalar")); +} + +TEST_F(ValidateRayTracing, TraceRayRayDirection) { + const std::string body = R"( +%as = OpLoad %type_as %top_level_as +OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v4composite %float_0 %payload +)"; + + CompileSuccessfully(GenerateRayTraceCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Ray Direction must be a 32-bit float 3-component vector")); +} + +TEST_F(ValidateRayTracing, TraceRayRayTMax) { + const std::string body = R"( +%as = OpLoad %type_as %top_level_as +OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float64_0 %payload +)"; + + CompileSuccessfully(GenerateRayTraceCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Ray TMax must be a 32-bit float scalar")); +} + +TEST_F(ValidateRayTracing, TraceRayPayload) { + const std::string body = R"( +%as = OpLoad %type_as %top_level_as +OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float_0 %callable +)"; + + CompileSuccessfully(GenerateRayTraceCode(body).c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Payload must have storage class RayPayloadKHR or " + "IncomingRayPayloadKHR")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/test/val/val_ssa_test.cpp b/test/val/val_ssa_test.cpp index 035c710a..c22f4ada 100644 --- a/test/val/val_ssa_test.cpp +++ b/test/val/val_ssa_test.cpp @@ -118,7 +118,7 @@ TEST_F(ValidateSSA, DominateUsageWithinBlockBad) { CompileSuccessfully(str); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - MatchesRegex("ID .\\[%bad\\] has not been defined\n" + MatchesRegex("ID '.\\[%bad\\]' has not been defined\n" " %8 = OpIAdd %uint %uint_1 %bad\n")); } @@ -141,7 +141,7 @@ TEST_F(ValidateSSA, DominateUsageSameInstructionBad) { CompileSuccessfully(str); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - MatchesRegex("ID .\\[%sum\\] has not been defined\n" + MatchesRegex("ID '.\\[%sum\\]' has not been defined\n" " %sum = OpIAdd %uint %uint_1 %sum\n")); } @@ -204,7 +204,7 @@ TEST_F(ValidateSSA, ForwardMemberNameMissingTargetBad) { ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("The following forward referenced IDs have not been " - "defined:\n2[%2]")); + "defined:\n'2[%2]'")); } TEST_F(ValidateSSA, ForwardDecorateGood) { @@ -1126,8 +1126,8 @@ TEST_F(ValidateSSA, IdDoesNotDominateItsUseBad) { ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - MatchesRegex("ID .\\[%eleven\\] defined in block .\\[%true_block\\] " - "does not dominate its use in block .\\[%false_block\\]\n" + MatchesRegex("ID '.\\[%eleven\\]' defined in block '.\\[%true_block\\]' " + "does not dominate its use in block '.\\[%false_block\\]'\n" " %false_block = OpLabel\n")); } @@ -1187,7 +1187,7 @@ TEST_F(ValidateSSA, CompileSuccessfully(str); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - MatchesRegex("ID .\\[%inew\\] has not been defined\n" + MatchesRegex("ID '.\\[%inew\\]' has not been defined\n" " %19 = OpIAdd %uint %inew %uint_1\n")); } @@ -1268,12 +1268,12 @@ TEST_F(ValidateSSA, PhiVariableDefNotDominatedByParentBlockBad) { CompileSuccessfully(str); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - MatchesRegex("In OpPhi instruction .\\[%phi\\], ID .\\[%true_copy\\] " - "definition does not dominate its parent .\\[%if_false\\]\n" - " %phi = OpPhi %bool %true_copy %if_false %false_copy " - "%if_true\n")); + EXPECT_THAT(getDiagnosticString(), + MatchesRegex( + "In OpPhi instruction '.\\[%phi\\]', ID '.\\[%true_copy\\]' " + "definition does not dominate its parent '.\\[%if_false\\]'\n" + " %phi = OpPhi %bool %true_copy %if_false %false_copy " + "%if_true\n")); } TEST_F(ValidateSSA, PhiVariableDefDominatesButNotDefinedInParentBlock) { @@ -1396,11 +1396,11 @@ TEST_F(ValidateSSA, UseFunctionParameterFromOtherFunctionBad) { CompileSuccessfully(str); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - MatchesRegex("ID .\\[%first\\] used in function .\\[%func2\\] is used " - "outside of it's defining function .\\[%func\\]\n" - " %func = OpFunction %void None %14\n")); + EXPECT_THAT(getDiagnosticString(), + MatchesRegex( + "ID '.\\[%first\\]' used in function '.\\[%func2\\]' is used " + "outside of it's defining function '.\\[%func\\]'\n" + " %func = OpFunction %void None %14\n")); } TEST_F(ValidateSSA, TypeForwardPointerForwardReference) { diff --git a/test/val/val_storage_test.cpp b/test/val/val_storage_test.cpp index e6f98bff..8693e803 100644 --- a/test/val/val_storage_test.cpp +++ b/test/val/val_storage_test.cpp @@ -30,6 +30,7 @@ using ::testing::Values; using ValidateStorage = spvtest::ValidateBase; using ValidateStorageClass = spvtest::ValidateBase>; +using ValidateStorageExecutionModel = spvtest::ValidateBase; TEST_F(ValidateStorage, FunctionStorageInsideFunction) { char str[] = R"( @@ -250,6 +251,315 @@ TEST_F(ValidateStorage, RelaxedLogicalPointerFunctionParamBad) { HasSubstr("OpFunctionCall Argument '")); } +std::string GenerateExecutionModelCode(const std::string& execution_model, + const std::string& storage_class, + bool store) { + const std::string mode = (execution_model.compare("GLCompute") == 0) + ? "OpExecutionMode %func LocalSize 1 1 1" + : ""; + const std::string operation = + (store) ? "OpStore %var %int0" : "%load = OpLoad %intt %var"; + std::ostringstream ss; + ss << R"( + OpCapability Shader + OpCapability RayTracingKHR + OpExtension "SPV_KHR_ray_tracing" + OpMemoryModel Logical GLSL450 + OpEntryPoint )" + << execution_model << R"( %func "func" %var + )" << mode << R"( + OpDecorate %var Location 0 +%intt = OpTypeInt 32 0 +%int0 = OpConstant %intt 0 +%voidt = OpTypeVoid +%vfunct = OpTypeFunction %voidt +%ptr = OpTypePointer )" + << storage_class << R"( %intt +%var = OpVariable %ptr )" << storage_class << R"( +%func = OpFunction %voidt None %vfunct +%funcl = OpLabel + )" << operation << R"( + OpReturn + OpFunctionEnd +)"; + + return ss.str(); +} + +TEST_P(ValidateStorageExecutionModel, VulkanOutsideStoreFailure) { + std::string execution_model = GetParam(); + CompileSuccessfully( + GenerateExecutionModelCode(execution_model, "Output", true).c_str(), + SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04644")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("in Vulkan environment, Output Storage Class must not be used " + "in GLCompute, RayGenerationKHR, IntersectionKHR, AnyHitKHR, " + "ClosestHitKHR, MissKHR, or CallableKHR execution models")); +} + +TEST_P(ValidateStorageExecutionModel, CallableDataStore) { + std::string execution_model = GetParam(); + CompileSuccessfully( + GenerateExecutionModelCode(execution_model, "CallableDataKHR", true) + .c_str(), + SPV_ENV_VULKAN_1_2); + if (execution_model.compare("RayGenerationKHR") == 0 || + execution_model.compare("ClosestHitKHR") == 0 || + execution_model.compare("CallableKHR") == 0 || + execution_model.compare("MissKHR") == 0) { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + } else { + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-CallableDataKHR-04704")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "CallableDataKHR Storage Class is limited to RayGenerationKHR, " + "ClosestHitKHR, CallableKHR, and MissKHR execution model")); + } +} + +TEST_P(ValidateStorageExecutionModel, CallableDataLoad) { + std::string execution_model = GetParam(); + CompileSuccessfully( + GenerateExecutionModelCode(execution_model, "CallableDataKHR", false) + .c_str(), + SPV_ENV_VULKAN_1_2); + if (execution_model.compare("RayGenerationKHR") == 0 || + execution_model.compare("ClosestHitKHR") == 0 || + execution_model.compare("CallableKHR") == 0 || + execution_model.compare("MissKHR") == 0) { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + } else { + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-CallableDataKHR-04704")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "CallableDataKHR Storage Class is limited to RayGenerationKHR, " + "ClosestHitKHR, CallableKHR, and MissKHR execution model")); + } +} + +TEST_P(ValidateStorageExecutionModel, IncomingCallableDataStore) { + std::string execution_model = GetParam(); + CompileSuccessfully(GenerateExecutionModelCode( + execution_model, "IncomingCallableDataKHR", true) + .c_str(), + SPV_ENV_VULKAN_1_2); + if (execution_model.compare("CallableKHR") == 0) { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + } else { + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-IncomingCallableDataKHR-04705")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("IncomingCallableDataKHR Storage Class is limited to " + "CallableKHR execution model")); + } +} + +TEST_P(ValidateStorageExecutionModel, IncomingCallableDataLoad) { + std::string execution_model = GetParam(); + CompileSuccessfully(GenerateExecutionModelCode( + execution_model, "IncomingCallableDataKHR", false) + .c_str(), + SPV_ENV_VULKAN_1_2); + if (execution_model.compare("CallableKHR") == 0) { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + } else { + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-IncomingCallableDataKHR-04705")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("IncomingCallableDataKHR Storage Class is limited to " + "CallableKHR execution model")); + } +} + +TEST_P(ValidateStorageExecutionModel, RayPayloadStore) { + std::string execution_model = GetParam(); + CompileSuccessfully( + GenerateExecutionModelCode(execution_model, "RayPayloadKHR", true) + .c_str(), + SPV_ENV_VULKAN_1_2); + if (execution_model.compare("RayGenerationKHR") == 0 || + execution_model.compare("ClosestHitKHR") == 0 || + execution_model.compare("MissKHR") == 0) { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + } else { + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-RayPayloadKHR-04698")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("RayPayloadKHR Storage Class is limited to RayGenerationKHR, " + "ClosestHitKHR, and MissKHR execution model")); + } +} + +TEST_P(ValidateStorageExecutionModel, RayPayloadLoad) { + std::string execution_model = GetParam(); + CompileSuccessfully( + GenerateExecutionModelCode(execution_model, "RayPayloadKHR", false) + .c_str(), + SPV_ENV_VULKAN_1_2); + if (execution_model.compare("RayGenerationKHR") == 0 || + execution_model.compare("ClosestHitKHR") == 0 || + execution_model.compare("MissKHR") == 0) { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + } else { + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-RayPayloadKHR-04698")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("RayPayloadKHR Storage Class is limited to RayGenerationKHR, " + "ClosestHitKHR, and MissKHR execution model")); + } +} + +TEST_P(ValidateStorageExecutionModel, HitAttributeStore) { + std::string execution_model = GetParam(); + CompileSuccessfully( + GenerateExecutionModelCode(execution_model, "HitAttributeKHR", true) + .c_str(), + SPV_ENV_VULKAN_1_2); + if (execution_model.compare("IntersectionKHR") == 0) { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + } else if (execution_model.compare("AnyHitKHR") == 0 || + execution_model.compare("ClosestHitKHR") == 0) { + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-HitAttributeKHR-04703")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("HitAttributeKHR Storage Class variables are read " + "only with AnyHitKHR and ClosestHitKHR")); + } else { + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-HitAttributeKHR-04701")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "HitAttributeKHR Storage Class is limited to IntersectionKHR, " + "AnyHitKHR, sand ClosestHitKHR execution model")); + } +} + +TEST_P(ValidateStorageExecutionModel, HitAttributeLoad) { + std::string execution_model = GetParam(); + CompileSuccessfully( + GenerateExecutionModelCode(execution_model, "HitAttributeKHR", false) + .c_str(), + SPV_ENV_VULKAN_1_2); + if (execution_model.compare("IntersectionKHR") == 0 || + execution_model.compare("AnyHitKHR") == 0 || + execution_model.compare("ClosestHitKHR") == 0) { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + } else { + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-HitAttributeKHR-04701")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "HitAttributeKHR Storage Class is limited to IntersectionKHR, " + "AnyHitKHR, sand ClosestHitKHR execution model")); + } +} + +TEST_P(ValidateStorageExecutionModel, IncomingRayPayloadStore) { + std::string execution_model = GetParam(); + CompileSuccessfully( + GenerateExecutionModelCode(execution_model, "IncomingRayPayloadKHR", true) + .c_str(), + SPV_ENV_VULKAN_1_2); + if (execution_model.compare("AnyHitKHR") == 0 || + execution_model.compare("ClosestHitKHR") == 0 || + execution_model.compare("MissKHR") == 0) { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + } else { + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-IncomingRayPayloadKHR-04699")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("IncomingRayPayloadKHR Storage Class is limited to " + "AnyHitKHR, ClosestHitKHR, and MissKHR execution model")); + } +} + +TEST_P(ValidateStorageExecutionModel, IncomingRayPayloadLoad) { + std::string execution_model = GetParam(); + CompileSuccessfully(GenerateExecutionModelCode(execution_model, + "IncomingRayPayloadKHR", false) + .c_str(), + SPV_ENV_VULKAN_1_2); + if (execution_model.compare("AnyHitKHR") == 0 || + execution_model.compare("ClosestHitKHR") == 0 || + execution_model.compare("MissKHR") == 0) { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + } else { + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-IncomingRayPayloadKHR-04699")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("IncomingRayPayloadKHR Storage Class is limited to " + "AnyHitKHR, ClosestHitKHR, and MissKHR execution model")); + } +} + +TEST_P(ValidateStorageExecutionModel, ShaderRecordBufferStore) { + std::string execution_model = GetParam(); + CompileSuccessfully( + GenerateExecutionModelCode(execution_model, "ShaderRecordBufferKHR", true) + .c_str(), + SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("ShaderRecordBufferKHR Storage Class variables are read only")); +} + +TEST_P(ValidateStorageExecutionModel, ShaderRecordBufferLoad) { + std::string execution_model = GetParam(); + CompileSuccessfully(GenerateExecutionModelCode(execution_model, + "ShaderRecordBufferKHR", false) + .c_str(), + SPV_ENV_VULKAN_1_2); + if (execution_model.compare("RayGenerationKHR") == 0 || + execution_model.compare("IntersectionKHR") == 0 || + execution_model.compare("AnyHitKHR") == 0 || + execution_model.compare("ClosestHitKHR") == 0 || + execution_model.compare("CallableKHR") == 0 || + execution_model.compare("MissKHR") == 0) { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + } else { + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-ShaderRecordBufferKHR-07119")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("ShaderRecordBufferKHR Storage Class is limited to " + "RayGenerationKHR, IntersectionKHR, AnyHitKHR, " + "ClosestHitKHR, CallableKHR, and MissKHR execution model")); + } +} + +INSTANTIATE_TEST_SUITE_P(MatrixExecutionModel, ValidateStorageExecutionModel, + ::testing::Values("RayGenerationKHR", + "IntersectionKHR", "AnyHitKHR", + "ClosestHitKHR", "MissKHR", + "CallableKHR", "GLCompute")); + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_version_test.cpp b/test/val/val_version_test.cpp index 98565ddb..6b7c4fe8 100644 --- a/test/val/val_version_test.cpp +++ b/test/val/val_version_test.cpp @@ -74,6 +74,12 @@ std::string version(spv_target_env env) { case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_VULKAN_1_1_SPIRV_1_4: return "1.4"; + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_VULKAN_1_2: + return "1.5"; + case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: + return "1.6"; default: return "0"; } @@ -103,8 +109,14 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_0, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_1_SPIRV_1_4,vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_0, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_2, vulkan_spirv, true), @@ -115,8 +127,14 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), @@ -127,8 +145,14 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), @@ -139,13 +163,73 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), - std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_5, vulkan_spirv, false) + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), + + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), + + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), + + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_5, vulkan_spirv, false) ) ); @@ -156,27 +240,91 @@ INSTANTIATE_TEST_SUITE_P(Vulkan, ValidateVersion, std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_0, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_0, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_2, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_5, vulkan_spirv, true), - std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), - std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true) + + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), + + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), + + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_VULKAN_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_OPENGL_4_5, vulkan_spirv, false) ) ); @@ -187,6 +335,9 @@ INSTANTIATE_TEST_SUITE_P(OpenCL, ValidateVersion, std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_4, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_5, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_6, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_2_0, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_2_1, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_2_2, opencl_spirv, true), @@ -199,6 +350,9 @@ INSTANTIATE_TEST_SUITE_P(OpenCL, ValidateVersion, std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_4, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_5, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_6, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_2_0, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_2_1, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_2_2, opencl_spirv, true), @@ -211,6 +365,9 @@ INSTANTIATE_TEST_SUITE_P(OpenCL, ValidateVersion, std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, false), std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_4, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_5, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_6, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_OPENCL_2_0, opencl_spirv, false), std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_OPENCL_2_1, opencl_spirv, false), std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_OPENCL_2_2, opencl_spirv, true), @@ -223,6 +380,9 @@ INSTANTIATE_TEST_SUITE_P(OpenCL, ValidateVersion, std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_4, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_5, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_6, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_2_0, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_2_1, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_2_2, opencl_spirv, true), diff --git a/test/wasm/test.js b/test/wasm/test.js new file mode 100644 index 00000000..7f0d8f3c --- /dev/null +++ b/test/wasm/test.js @@ -0,0 +1,64 @@ +// Copyright (c) 2020 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +const spirvTools = require("../../out/web/spirv-tools"); +const fs = require("fs"); +const util = require("util"); +const readFile = util.promisify(fs.readFile); + +const SPV_PATH = "./test/fuzzers/corpora/spv/simple.spv"; + +const test = async () => { + const spv = await spirvTools(); + + // disassemble from file + const buffer = await readFile(SPV_PATH); + const disFileResult = spv.dis( + buffer, + spv.SPV_ENV_UNIVERSAL_1_3, + spv.SPV_BINARY_TO_TEXT_OPTION_INDENT | + spv.SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | + spv.SPV_BINARY_TO_TEXT_OPTION_COLOR + ); + console.log("dis from file:\n", disFileResult); + + // assemble + const source = ` + OpCapability Linkage + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpSource GLSL 450 + OpDecorate %spec SpecId 1 + %int = OpTypeInt 32 1 + %spec = OpSpecConstant %int 0 + %const = OpConstant %int 42`; + const asResult = spv.as( + source, + spv.SPV_ENV_UNIVERSAL_1_3, + spv.SPV_TEXT_TO_BINARY_OPTION_NONE + ); + console.log(`as returned ${asResult.byteLength} bytes`); + + // re-disassemble + const disResult = spv.dis( + asResult, + spv.SPV_ENV_UNIVERSAL_1_3, + spv.SPV_BINARY_TO_TEXT_OPTION_INDENT | + spv.SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | + spv.SPV_BINARY_TO_TEXT_OPTION_COLOR + ); + console.log("dis:\n", disResult); +}; + +test(); diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 124a3328..86d0bc46 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -41,13 +41,15 @@ endfunction() if (NOT ${SPIRV_SKIP_EXECUTABLES}) add_spvtools_tool(TARGET spirv-as SRCS as/as.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_tool(TARGET spirv-diff SRCS diff/diff.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-diff SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) add_spvtools_tool(TARGET spirv-dis SRCS dis/dis.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) add_spvtools_tool(TARGET spirv-val SRCS val/val.cpp util/cli_consumer.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) add_spvtools_tool(TARGET spirv-opt SRCS opt/opt.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) - if (NOT DEFINED IOS_PLATFORM) # iOS does not allow std::system calls which spirv-reduce requires + if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL "iOS")) # iOS does not allow std::system calls which spirv-reduce requires add_spvtools_tool(TARGET spirv-reduce SRCS reduce/reduce.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-reduce ${SPIRV_TOOLS_FULL_VISIBILITY}) endif() add_spvtools_tool(TARGET spirv-link SRCS link/linker.cpp LIBS SPIRV-Tools-link ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_tool(TARGET spirv-lint SRCS lint/lint.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-lint SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) add_spvtools_tool(TARGET spirv-cfg SRCS cfg/cfg.cpp cfg/bin_to_dot.h @@ -56,8 +58,8 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES}) target_include_directories(spirv-cfg PRIVATE ${spirv-tools_SOURCE_DIR} ${SPIRV_HEADER_INCLUDE_DIR}) set(SPIRV_INSTALL_TARGETS spirv-as spirv-dis spirv-val spirv-opt - spirv-cfg spirv-link) - if(NOT DEFINED IOS_PLATFORM) + spirv-cfg spirv-link spirv-lint) + if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL "iOS")) set(SPIRV_INSTALL_TARGETS ${SPIRV_INSTALL_TARGETS} spirv-reduce) endif() @@ -67,9 +69,6 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES}) endif(SPIRV_BUILD_FUZZER) if(ENABLE_SPIRV_TOOLS_INSTALL) - install(TARGETS ${SPIRV_INSTALL_TARGETS} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(TARGETS ${SPIRV_INSTALL_TARGETS} DESTINATION ${CMAKE_INSTALL_BINDIR}) endif(ENABLE_SPIRV_TOOLS_INSTALL) endif() diff --git a/tools/as/as.cpp b/tools/as/as.cpp index c8a44456..506b0585 100644 --- a/tools/as/as.cpp +++ b/tools/as/as.cpp @@ -48,7 +48,7 @@ Options: argv0, argv0, target_env_list.c_str()); } -static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; +static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; int main(int argc, char** argv) { const char* inFile = nullptr; diff --git a/tools/cfg/bin_to_dot.cpp b/tools/cfg/bin_to_dot.cpp index 2561eea4..72e7693a 100644 --- a/tools/cfg/bin_to_dot.cpp +++ b/tools/cfg/bin_to_dot.cpp @@ -57,13 +57,13 @@ class DotConverter { // Ends processing for the current block, emitting its dot code. void FlushBlock(const std::vector& successors); - // The ID of the current functio, or 0 if outside of a function. + // The ID of the current function, or 0 if outside of a function. uint32_t current_function_id_ = 0; // The ID of the current basic block, or 0 if outside of a block. uint32_t current_block_id_ = 0; - // Have we completed processing for the entry block to this fuction? + // Have we completed processing for the entry block to this function? bool seen_function_entry_block_ = false; // The Id of the merge block for this block if it exists, or 0 otherwise. diff --git a/tools/cfg/bin_to_dot.h b/tools/cfg/bin_to_dot.h index 4de2e07f..a61c9759 100644 --- a/tools/cfg/bin_to_dot.h +++ b/tools/cfg/bin_to_dot.h @@ -20,7 +20,7 @@ #include "spirv-tools/libspirv.h" // Dumps the control flow graph for the given module to the output stream. -// Returns SPV_SUCCESS on succes. +// Returns SPV_SUCCESS on success. spv_result_t BinaryToDot(const spv_const_context context, const uint32_t* words, size_t num_words, std::iostream* out, spv_diagnostic* diagnostic); diff --git a/tools/cfg/cfg.cpp b/tools/cfg/cfg.cpp index a93488d5..5380c21e 100644 --- a/tools/cfg/cfg.cpp +++ b/tools/cfg/cfg.cpp @@ -44,7 +44,7 @@ Options: argv0, argv0); } -static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; +static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; int main(int argc, char** argv) { const char* inFile = nullptr; @@ -69,16 +69,16 @@ int main(int argc, char** argv) { if (0 == strcmp(argv[argi], "--help")) { print_usage(argv[0]); return 0; - } else if (0 == strcmp(argv[argi], "--version")) { + } + if (0 == strcmp(argv[argi], "--version")) { printf("%s EXPERIMENTAL\n", spvSoftwareVersionDetailsString()); printf("Target: %s\n", spvTargetEnvDescription(kDefaultEnvironment)); return 0; - } else { - print_usage(argv[0]); - return 1; } - } break; + print_usage(argv[0]); + return 1; + } case 0: { // Setting a filename of "-" to indicate stdin. if (!inFile) { diff --git a/tools/diff/diff.cpp b/tools/diff/diff.cpp new file mode 100644 index 00000000..d3cad04b --- /dev/null +++ b/tools/diff/diff.cpp @@ -0,0 +1,201 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +#include +#endif + +#include "source/diff/diff.h" + +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" +#include "spirv-tools/libspirv.hpp" +#include "tools/io.h" +#include "tools/util/cli_consumer.h" + +static void print_usage(char* argv0) { + printf(R"(%s - Compare two SPIR-V files + +Usage: %s + +The SPIR-V binary is read from and . If either +file ends in .spvasm, the SPIR-V is read as text and disassembled. + +The contents of the SPIR-V modules are analyzed and a diff is produced showing a +logical transformation from src to dst, in src's id-space. + + -h, --help Print this help. + --version Display diff version information. + + --color Force color output. The default when printing to a terminal. + Overrides a previous --no-color option. + --no-color Don't print in color. Overrides a previous --color option. + The default when output goes to something other than a + terminal (e.g. a pipe, or a shell redirection). + + --no-indent Don't indent instructions. + + --no-header Don't output the header as leading comments. + + --with-id-map Also output the mapping between src and dst outputs. + + --ignore-set-binding + Don't use set/binding decorations for variable matching. + --ignore-location + Don't use location decorations for variable matching. +)", + argv0, argv0); +} + +static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; + +static bool is_assembly(const char* path) { + const char* suffix = strrchr(path, '.'); + if (suffix == nullptr) { + return false; + } + + return strcmp(suffix, ".spvasm") == 0; +} + +static std::unique_ptr load_module(const char* path) { + if (is_assembly(path)) { + std::vector contents; + if (!ReadTextFile(path, &contents)) return {}; + + return spvtools::BuildModule( + kDefaultEnvironment, spvtools::utils::CLIMessageConsumer, + std::string(contents.begin(), contents.end()), + spvtools::SpirvTools::kDefaultAssembleOption | + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + } + + std::vector contents; + if (!ReadBinaryFile(path, &contents)) return {}; + + return spvtools::BuildModule(kDefaultEnvironment, + spvtools::utils::CLIMessageConsumer, + contents.data(), contents.size()); +} + +int main(int argc, char** argv) { + const char* src_file = nullptr; + const char* dst_file = nullptr; + bool color_is_possible = +#if SPIRV_COLOR_TERMINAL + true; +#else + false; +#endif + bool force_color = false; + bool force_no_color = false; + bool allow_indent = true; + bool no_header = false; + bool dump_id_map = false; + bool ignore_set_binding = false; + bool ignore_location = false; + + for (int argi = 1; argi < argc; ++argi) { + if ('-' == argv[argi][0]) { + switch (argv[argi][1]) { + case 'h': + print_usage(argv[0]); + return 0; + case '-': { + // Long options + if (strcmp(argv[argi], "--no-color") == 0) { + force_no_color = true; + force_color = false; + } else if (strcmp(argv[argi], "--color") == 0) { + force_no_color = false; + force_color = true; + } else if (strcmp(argv[argi], "--no-indent") == 0) { + allow_indent = false; + } else if (strcmp(argv[argi], "--no-header") == 0) { + no_header = true; + } else if (strcmp(argv[argi], "--with-id-map") == 0) { + dump_id_map = true; + } else if (strcmp(argv[argi], "--ignore-set-binding") == 0) { + ignore_set_binding = true; + } else if (strcmp(argv[argi], "--ignore-location") == 0) { + ignore_location = true; + } else if (strcmp(argv[argi], "--help") == 0) { + print_usage(argv[0]); + return 0; + } else if (strcmp(argv[argi], "--version") == 0) { + printf("%s\n", spvSoftwareVersionDetailsString()); + printf("Target: %s\n", + spvTargetEnvDescription(kDefaultEnvironment)); + return 0; + } else { + print_usage(argv[0]); + return 1; + } + } break; + default: + print_usage(argv[0]); + return 1; + } + } else { + if (src_file == nullptr) { + src_file = argv[argi]; + } else if (dst_file == nullptr) { + dst_file = argv[argi]; + } else { + fprintf(stderr, "error: More than two input files specified\n"); + return 1; + } + } + } + + if (src_file == nullptr || dst_file == nullptr) { + print_usage(argv[0]); + return 1; + } + + spvtools::diff::Options options; + + if (allow_indent) options.indent = true; + if (no_header) options.no_header = true; + if (dump_id_map) options.dump_id_map = true; + if (ignore_set_binding) options.ignore_set_binding = true; + if (ignore_location) options.ignore_location = true; + + if (color_is_possible && !force_no_color) { + bool output_is_tty = true; +#if defined(_POSIX_VERSION) + output_is_tty = isatty(fileno(stdout)); +#endif + if (output_is_tty || force_color) { + options.color_output = true; + } + } + + std::unique_ptr src = load_module(src_file); + std::unique_ptr dst = load_module(dst_file); + + if (!src) { + fprintf(stderr, "error: Loading src file\n"); + } + if (!dst) { + fprintf(stderr, "error: Loading dst file\n"); + } + if (!src || !dst) { + return 1; + } + + spvtools::diff::Diff(src.get(), dst.get(), std::cout, options); + + return 0; +} diff --git a/tools/fuzz/fuzz.cpp b/tools/fuzz/fuzz.cpp index 422bea53..ca6633a6 100644 --- a/tools/fuzz/fuzz.cpp +++ b/tools/fuzz/fuzz.cpp @@ -630,7 +630,7 @@ bool Fuzz(const spv_target_env& target_env, std::move(ir_context), std::move(transformation_context), std::move(fuzzer_context), message_consumer, donor_suppliers, fuzzer_options->all_passes_enabled, repeated_pass_strategy, - fuzzer_options->fuzzer_pass_validation_enabled, validator_options); + fuzzer_options->fuzzer_pass_validation_enabled, validator_options, false); auto fuzz_result = fuzzer.Run(0); if (fuzz_result.status == spvtools::fuzz::Fuzzer::Status::kFuzzerPassLedToInvalidModule) { @@ -683,7 +683,7 @@ void DumpTransformationsJson( json_options.add_whitespace = true; auto json_generation_status = google::protobuf::util::MessageToJsonString( transformations, &json_string, json_options); - if (json_generation_status == google::protobuf::util::Status::OK) { + if (json_generation_status.ok()) { std::ofstream transformations_json_file(filename); transformations_json_file << json_string; transformations_json_file.close(); @@ -734,9 +734,9 @@ int main(int argc, const char** argv) { std::string facts_json_string((std::istreambuf_iterator(facts_input)), std::istreambuf_iterator()); facts_input.close(); - if (google::protobuf::util::Status::OK != - google::protobuf::util::JsonStringToMessage(facts_json_string, - &initial_facts)) { + if (!google::protobuf::util::JsonStringToMessage(facts_json_string, + &initial_facts) + .ok()) { spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error reading facts data"); return 1; } @@ -816,7 +816,7 @@ int main(int argc, const char** argv) { json_options.add_whitespace = true; auto json_generation_status = google::protobuf::util::MessageToJsonString( transformations_applied, &json_string, json_options); - if (json_generation_status != google::protobuf::util::Status::OK) { + if (!json_generation_status.ok()) { spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error writing out transformations in JSON format"); return 1; diff --git a/tools/io.h b/tools/io.h index aff9eabd..9dc834ed 100644 --- a/tools/io.h +++ b/tools/io.h @@ -26,9 +26,15 @@ #define SET_STDIN_TO_BINARY_MODE() _setmode(_fileno(stdin), O_BINARY); #define SET_STDIN_TO_TEXT_MODE() _setmode(_fileno(stdin), O_TEXT); +#define SET_STDOUT_TO_BINARY_MODE() _setmode(_fileno(stdout), O_BINARY); +#define SET_STDOUT_TO_TEXT_MODE() _setmode(_fileno(stdout), O_TEXT); +#define SET_STDOUT_MODE(mode) _setmode(_fileno(stdout), mode); #else #define SET_STDIN_TO_BINARY_MODE() #define SET_STDIN_TO_TEXT_MODE() +#define SET_STDOUT_TO_BINARY_MODE() 0 +#define SET_STDOUT_TO_TEXT_MODE() 0 +#define SET_STDOUT_MODE(mode) #endif // Appends the contents of the |file| to |data|, assuming each element in the @@ -89,7 +95,7 @@ bool ReadBinaryFile(const char* filename, std::vector* data) { ReadFile(fp, data); bool succeeded = WasFileCorrectlyRead(fp, filename); - if (use_file) fclose(fp); + if (use_file && fp) fclose(fp); return succeeded; } @@ -111,10 +117,48 @@ bool ReadTextFile(const char* filename, std::vector* data) { ReadFile(fp, data); bool succeeded = WasFileCorrectlyRead(fp, filename); - if (use_file) fclose(fp); + if (use_file && fp) fclose(fp); return succeeded; } +namespace { +// A class to create and manage a file for outputting data. +class OutputFile { + public: + // Opens |filename| in the given mode. If |filename| is nullptr, the empty + // string or "-", stdout will be set to the given mode. + OutputFile(const char* filename, const char* mode) { + const bool use_stdout = + !filename || (filename[0] == '-' && filename[1] == '\0'); + if (use_stdout) { + if (strchr(mode, 'b')) { + old_mode_ = SET_STDOUT_TO_BINARY_MODE(); + } else { + old_mode_ = SET_STDOUT_TO_TEXT_MODE(); + } + fp_ = stdout; + } else { + fp_ = fopen(filename, mode); + } + } + + ~OutputFile() { + if (fp_ == stdout) { + SET_STDOUT_MODE(old_mode_); + } else if (fp_ != nullptr) { + fclose(fp_); + } + } + + // Returns a file handle to the file. + FILE* GetFileHandle() const { return fp_; } + + private: + FILE* fp_; + int old_mode_; +}; +} // namespace + // Writes the given |data| into the file named as |filename| using the given // |mode|, assuming |data| is an array of |count| elements of type |T|. If // |filename| is nullptr or "-", writes to standard output. If any error occurs, @@ -122,20 +166,19 @@ bool ReadTextFile(const char* filename, std::vector* data) { template bool WriteFile(const char* filename, const char* mode, const T* data, size_t count) { - const bool use_stdout = - !filename || (filename[0] == '-' && filename[1] == '\0'); - if (FILE* fp = (use_stdout ? stdout : fopen(filename, mode))) { - size_t written = fwrite(data, sizeof(T), count, fp); - if (count != written) { - fprintf(stderr, "error: could not write to file '%s'\n", filename); - if (!use_stdout) fclose(fp); - return false; - } - if (!use_stdout) fclose(fp); - } else { + OutputFile file(filename, mode); + FILE* fp = file.GetFileHandle(); + if (fp == nullptr) { fprintf(stderr, "error: could not open file '%s'\n", filename); return false; } + + size_t written = fwrite(data, sizeof(T), count, fp); + if (count != written) { + fprintf(stderr, "error: could not write to file '%s'\n", filename); + return false; + } + return true; } diff --git a/tools/link/linker.cpp b/tools/link/linker.cpp index 359e8030..bdddeb89 100644 --- a/tools/link/linker.cpp +++ b/tools/link/linker.cpp @@ -25,7 +25,7 @@ namespace { -const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; +const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; void print_usage(const char* program) { std::string target_env_list = spvTargetEnvList(16, 80); @@ -49,15 +49,18 @@ Options (in lexicographical order): --create-library Link the binaries into a library, keeping all exported symbols. -h, --help - Print this help. + Print this help. --target-env - Set the target environment. Without this flag the target - environment defaults to spv1.5. must be one of - {%s} + Set the environment used for interpreting the inputs. Without + this option the environment defaults to spv1.6. must be + one of {%s}. + NOTE: The SPIR-V version used by the linked binary module + depends only on the version of the inputs, and is not affected + by this option. --verify-ids Verify that IDs in the resulting modules are truly unique. --version - Display linker version information + Display linker version information. )", program, program, target_env_list.c_str()); } @@ -160,10 +163,11 @@ int main(int argc, char** argv) { std::vector linkingResult; spv_result_t status = Link(context, contents, &linkingResult, options); + if (status != SPV_SUCCESS && status != SPV_WARNING) return 1; if (!WriteFile(outFile, "wb", linkingResult.data(), linkingResult.size())) return 1; - return status == SPV_SUCCESS ? 0 : 1; + return 0; } diff --git a/tools/lint/lint.cpp b/tools/lint/lint.cpp new file mode 100644 index 00000000..d37df830 --- /dev/null +++ b/tools/lint/lint.cpp @@ -0,0 +1,75 @@ +// Copyright (c) 2021 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 + +#include "source/opt/log.h" +#include "spirv-tools/linter.hpp" +#include "tools/io.h" +#include "tools/util/cli_consumer.h" + +const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; + +namespace { +// Status and actions to perform after parsing command-line arguments. +enum LintActions { LINT_CONTINUE, LINT_STOP }; + +struct LintStatus { + LintActions action; + int code; +}; + +// Parses command-line flags. |argc| contains the number of command-line flags. +// |argv| points to an array of strings holding the flags. +// +// On return, this function stores the name of the input program in |in_file|. +// The return value indicates whether optimization should continue and a status +// code indicating an error or success. +LintStatus ParseFlags(int argc, const char** argv, const char** in_file) { + // TODO (dongja): actually parse flags, etc. + if (argc != 2) { + spvtools::Error(spvtools::utils::CLIMessageConsumer, nullptr, {}, + "expected exactly one argument: in_file"); + return {LINT_STOP, 1}; + } + + *in_file = argv[1]; + + return {LINT_CONTINUE, 0}; +} +} // namespace + +int main(int argc, const char** argv) { + const char* in_file = nullptr; + + spv_target_env target_env = kDefaultEnvironment; + + spvtools::Linter linter(target_env); + linter.SetMessageConsumer(spvtools::utils::CLIMessageConsumer); + + LintStatus status = ParseFlags(argc, argv, &in_file); + + if (status.action == LINT_STOP) { + return status.code; + } + + std::vector binary; + if (!ReadBinaryFile(in_file, &binary)) { + return 1; + } + + bool ok = linter.Run(binary.data(), binary.size()); + + return ok ? 0 : 1; +} diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index b9339abe..ce2103ca 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -44,7 +44,7 @@ struct OptStatus { // initialization and setup. Note that |source| and |position| are irrelevant // here because we are still not processing a SPIR-V input file. void opt_diagnostic(spv_message_level_t level, const char* /*source*/, - const spv_position_t& /*positon*/, const char* message) { + const spv_position_t& /*position*/, const char* message) { if (level == SPV_MSG_ERROR) { fprintf(stderr, "error: "); } @@ -59,7 +59,7 @@ std::string GetListOfPassesAsString(const spvtools::Optimizer& optimizer) { return ss.str(); } -const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; +const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; std::string GetLegalizationPasses() { spvtools::Optimizer optimizer(kDefaultEnvironment); @@ -143,16 +143,34 @@ Options (in lexicographical order):)", does not support RelaxedPrecision or ignores it. This pass also removes all RelaxedPrecision decorations.)"); printf(R"( + --convert-to-sampled-image ": ..." + convert images and/or samplers with the given pairs of descriptor + set and binding to sampled images. If a pair of an image and a + sampler have the same pair of descriptor set and binding that is + one of the given pairs, they will be converted to a sampled + image. In addition, if only an image or a sampler has the + descriptor set and binding that is one of the given pairs, it + will be converted to a sampled image.)"); + printf(R"( --copy-propagate-arrays Does propagation of memory references when an array is a copy of another. It will only propagate an array if the source is never written to, and the only store to the target is the copy.)"); printf(R"( - --decompose-initialized-variables - Decomposes initialized variable declarations into a declaration - followed by a store of the initial value. This is done to work - around known issues with some Vulkan drivers for initialize - variables.)"); + --replace-desc-array-access-using-var-index + Replaces accesses to descriptor arrays based on a variable index + with a switch that has a case for every possible value of the + index.)"); + printf(R"( + --spread-volatile-semantics + Spread Volatile semantics to variables with SMIDNV, WarpIDNV, + SubgroupSize, SubgroupLocalInvocationId, SubgroupEqMask, + SubgroupGeMask, SubgroupGtMask, SubgroupLeMask, or SubgroupLtMask + BuiltIn decorations or OpLoad for them when the shader model is + ray generation, closest hit, miss, intersection, or callable. + For the SPIR-V version is 1.6 or above, it also spreads Volatile + semantics to a variable with HelperInvocation BuiltIn decoration + in the fragement shader.)"); printf(R"( --descriptor-scalar-replacement Replaces every array variable |desc| that has a DescriptorSet @@ -184,6 +202,10 @@ Options (in lexicographical order):)", unused stores to vector components, that are not removed by aggressive dead code elimination.)"); printf(R"( + --eliminate-dead-input-components + Deletes unused components from input variables. Currently + deletes trailing unused elements from input arrays.)"); + printf(R"( --eliminate-dead-variables Deletes module scope variables that are not referenced.)"); printf(R"( @@ -209,6 +231,10 @@ Options (in lexicographical order):)", loads and stores. Performed only on entry point call tree functions.)"); printf(R"( + --fix-func-call-param + fix non memory argument for the function call, replace + accesschain pointer argument with a variable.)"); + printf(R"( --flatten-decorations Replace decoration groups with repeated OpDecorate and OpMemberDecorate instructions.)"); @@ -378,9 +404,12 @@ Options (in lexicographical order):)", Change the scope of private variables that are used in a single function to that function.)"); printf(R"( - --reduce-load-size + --reduce-load-size[=] Replaces loads of composite objects where not every component is - used by loads of just the elements that are used.)"); + used by loads of just the elements that are used. If the ratio + of the used components of the load is less than the , + we replace the load. is a double type number. If + it is bigger than 1.0, we always replaces the load.)"); printf(R"( --redundancy-elimination Looks for instructions in the same function that compute the @@ -406,6 +435,12 @@ Options (in lexicographical order):)", Removes duplicate types, decorations, capabilities and extension instructions.)"); printf(R"( + --remove-unused-interface-variables + Removes variables referenced on the |OpEntryPoint| instruction + that are not referenced in the entry point function or any function + in its call tree. Note that this could cause the shader interface + to no longer match other shader stages.)"); + printf(R"( --replace-invalid-opcode Replaces instructions whose opcode is valid for shader modules, but not for the current shader stage. To have an effect, all @@ -450,16 +485,16 @@ Options (in lexicographical order):)", --strength-reduction Replaces instructions with equivalent and less expensive ones.)"); printf(R"( - --strip-atomic-counter-memory - Removes AtomicCountMemory bit from memory semantics values.)"); - printf(R"( --strip-debug Remove all debug instructions.)"); printf(R"( + --strip-nonsemantic + Remove all reflection and nonsemantic information.)"); + printf(R"( --strip-reflect - Remove all reflection information. For now, this covers - reflection information defined by SPV_GOOGLE_hlsl_functionality1 - and SPV_KHR_non_semantic_info)"); + DEPRECATED. Remove all reflection information. For now, this + covers reflection information defined by + SPV_GOOGLE_hlsl_functionality1 and SPV_KHR_non_semantic_info)"); printf(R"( --target-env= Set the target environment. Without this flag the target diff --git a/tools/reduce/reduce.cpp b/tools/reduce/reduce.cpp index 4447b356..37600543 100644 --- a/tools/reduce/reduce.cpp +++ b/tools/reduce/reduce.cpp @@ -262,7 +262,7 @@ void DumpShader(spvtools::opt::IRContext* context, const char* filename) { DumpShader(binary, filename); } -const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; +const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; int main(int argc, const char** argv) { std::string in_binary_file; diff --git a/tools/sva/README.md b/tools/sva/README.md index d80b4d2c..cd3d13c9 100644 --- a/tools/sva/README.md +++ b/tools/sva/README.md @@ -1,6 +1,6 @@ # SVA -SPIR-V Assember for WebGPU. The SPIR-V Assembler is a JavaScript library to +SPIR-V Assembler for WebGPU. The SPIR-V Assembler is a JavaScript library to convert SPIR-V assembly (as produced by spirv-dis in SPIR-V Tools) into a SPIR-V binary. The assembler assumes it is generating WebGPU SPIR-V and thus has the following limitations. diff --git a/tools/sva/yarn.lock b/tools/sva/yarn.lock index c46b9017..e7b735e0 100644 --- a/tools/sva/yarn.lock +++ b/tools/sva/yarn.lock @@ -694,9 +694,9 @@ get-stream@^4.0.0: pump "^3.0.0" glob-parent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954" - integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg== + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" @@ -938,9 +938,9 @@ locate-path@^3.0.0: path-exists "^3.0.0" lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14: - version "4.17.19" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" - integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@2.2.0: version "2.2.0" @@ -1260,9 +1260,9 @@ path-to-regexp@2.2.1: integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ== pathval@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" - integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA= + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== prelude-ls@~1.1.2: version "1.1.2" diff --git a/tools/val/val.cpp b/tools/val/val.cpp index 21a7d8f4..880ce46b 100644 --- a/tools/val/val.cpp +++ b/tools/val/val.cpp @@ -64,6 +64,8 @@ Options: --relax-struct-store Allow store from one struct type to a different type with compatible layout and members. + --allow-localsizeid Allow use of the LocalSizeId decoration where it would otherwise not + be allowed by the target environment. --before-hlsl-legalization Allows code patterns that are intended to be fixed by spirv-opt's legalization passes. --version Display validator version information. @@ -75,7 +77,7 @@ Options: int main(int argc, char** argv) { const char* inFile = nullptr; - spv_target_env target_env = SPV_ENV_UNIVERSAL_1_5; + spv_target_env target_env = SPV_ENV_UNIVERSAL_1_6; spvtools::ValidatorOptions options; bool continue_processing = true; int return_code = 0; @@ -109,17 +111,20 @@ int main(int argc, char** argv) { printf("%s\n", spvSoftwareVersionDetailsString()); printf( "Targets:\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n " - "%s\n", + "%s\n %s\n %s\n %s\n", spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_0), spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1), spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_2), spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_3), spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_4), spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_5), + spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_6), spvTargetEnvDescription(SPV_ENV_OPENCL_2_2), spvTargetEnvDescription(SPV_ENV_VULKAN_1_0), spvTargetEnvDescription(SPV_ENV_VULKAN_1_1), - spvTargetEnvDescription(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + spvTargetEnvDescription(SPV_ENV_VULKAN_1_1_SPIRV_1_4), + spvTargetEnvDescription(SPV_ENV_VULKAN_1_2), + spvTargetEnvDescription(SPV_ENV_VULKAN_1_3)); continue_processing = false; return_code = 0; } else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) { @@ -153,6 +158,8 @@ int main(int argc, char** argv) { options.SetWorkgroupScalarBlockLayout(true); } else if (0 == strcmp(cur_arg, "--skip-block-layout")) { options.SetSkipBlockLayout(true); + } else if (0 == strcmp(cur_arg, "--allow-localsizeid")) { + options.SetAllowLocalSizeId(true); } else if (0 == strcmp(cur_arg, "--relax-struct-store")) { options.SetRelaxStructStore(true); } else if (0 == cur_arg[1]) { diff --git a/utils/check_copyright.py b/utils/check_copyright.py index 1bc39f67..aa647af5 100755 --- a/utils/check_copyright.py +++ b/utils/check_copyright.py @@ -14,6 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """Checks for copyright notices in all the files that need them under the + current directory. Optionally insert them. When inserting, replaces an MIT or Khronos free use license with Apache 2. """ @@ -37,12 +38,32 @@ AUTHORS = ['The Khronos Group Inc.', 'Vasyl Teliman', 'Advanced Micro Devices, Inc.', 'Stefano Milizia', - 'Alastair F. Donaldson'] -CURRENT_YEAR='2020' + 'Alastair F. Donaldson', + 'Mostafa Ashraf', + 'Shiyu Liu', + 'ZHOU He'] +CURRENT_YEAR = 2022 -YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020|2021)' -COPYRIGHT_RE = re.compile( - 'Copyright \(c\) {} ({})'.format(YEARS, '|'.join(AUTHORS))) +FIRST_YEAR = 2014 +FINAL_YEAR = CURRENT_YEAR + 5 +# A regular expression to match the valid years in the copyright information. +YEAR_REGEX = '(' + '|'.join( + str(year) for year in range(FIRST_YEAR, FINAL_YEAR + 1)) + ')' + +# A regular expression to make a range of years in the form -. +YEAR_RANGE_REGEX = '(' +for year1 in range(FIRST_YEAR, FINAL_YEAR + 1): + for year2 in range(year1 + 1, FINAL_YEAR + 1): + YEAR_RANGE_REGEX += str(year1) + '-' + str(year2) + '|' +YEAR_RANGE_REGEX = YEAR_RANGE_REGEX[:-1] + ')' + +# In the copyright info, the year can be a single year or a range. This is a +# regex to make sure it matches one of them. +YEAR_OR_RANGE_REGEX = '(' + YEAR_REGEX + '|' + YEAR_RANGE_REGEX + ')' + +# The final regular expression to match a valid copyright line. +COPYRIGHT_RE = re.compile('Copyright \(c\) {} ({})'.format( + YEAR_OR_RANGE_REGEX, '|'.join(AUTHORS))) MIT_BEGIN_RE = re.compile('Permission is hereby granted, ' 'free of charge, to any person obtaining a') @@ -129,7 +150,7 @@ def insert_copyright(author, glob, comment_prefix): update_file = False for line in fileinput.input(file, inplace=1): emit = True - if state is 0: + if state == 0: if COPYRIGHT_RE.search(line): state = 1 elif skip(line): @@ -140,7 +161,7 @@ def insert_copyright(author, glob, comment_prefix): sys.stdout.write(licensed) # Assume there isn't a previous license notice. state = 1 - elif state is 1: + elif state == 1: if MIT_BEGIN_RE.search(line): state = 2 emit = False @@ -148,7 +169,7 @@ def insert_copyright(author, glob, comment_prefix): # Assume an Apache license is preceded by a copyright # notice. So just emit it like the rest of the file. state = 9 - elif state is 2: + elif state == 2: # Replace the MIT license with Apache 2 emit = False if MIT_END_RE.search(line): diff --git a/utils/check_symbol_exports.py b/utils/check_symbol_exports.py index bcd77da6..7795d72b 100755 --- a/utils/check_symbol_exports.py +++ b/utils/check_symbol_exports.py @@ -12,7 +12,13 @@ # 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. -"""Checks names of global exports from a library.""" +"""Ensures that all externally visible functions in the library have an appropriate name + +Appropriate function names are: + - names starting with spv, + - anything in a namespace, + - functions added by the protobuf compiler, + - and weak definitions of new and delete.""" import os.path import re @@ -46,14 +52,16 @@ def check_library(library): exports are namespaced or begin with spv (in either C or C++ styles) then return 0. Otherwise emit a message and return 1.""" - # The pattern for a global symbol record - symbol_pattern = re.compile(r'^[0-aA-Fa-f]+ g *F \.text.*[0-9A-Fa-f]+ +(.*)') + # The pattern for an externally visible symbol record + symbol_pattern = re.compile(r'^[0-aA-Fa-f]+ +([wg]) *F \.text.*[0-9A-Fa-f]+ +(.*)') # Ok patterns are as follows, assuming Itanium name mangling: # spv[A-Z] : extern "C" symbol starting with spv # _ZN : something in a namespace + # _ZSt : something in the standard namespace + # _ZZN : something in a local scope and namespace # _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_])') + symbol_ok_pattern = re.compile(r'^(spv[A-Z]|_ZN|_ZSt|_ZZN|_Z[0-9]+spv[A-Z_])') # In addition, the following pattern allowlists global functions that are added # by the protobuf compiler: @@ -61,18 +69,31 @@ def check_library(library): # - InitDefaults_spvtoolsfuzz_2eproto() symbol_allowlist_pattern = re.compile(r'_Z[0-9]+(InitDefaults|AddDescriptors)_spvtoolsfuzz_2eprotov') + symbol_is_new_or_delete = re.compile(r'^(_Zna|_Znw|_Zdl|_Zda)') + # Compilaion for Arm has various thunks for constructors, destructors, vtables. + # They are weak. + symbol_is_thunk = re.compile(r'^_ZT') + + # This occurs in NDK builds. + symbol_is_hidden = re.compile(r'^\.hidden ') + seen = set() result = 0 for line in command_output(['objdump', '-t', library], '.').split('\n'): match = symbol_pattern.search(line) if match: - symbol = match.group(1) + linkage = match.group(1) + symbol = match.group(2) if symbol not in seen: seen.add(symbol) #print("look at '{}'".format(symbol)) - if not (symbol_allowlist_pattern.match(symbol) or symbol_ok_pattern.match(symbol)): - print('{}: error: Unescaped exported symbol: {}'.format(PROG, symbol)) - result = 1 + if not (symbol_is_new_or_delete.match(symbol) and linkage == 'w'): + if not (symbol_is_thunk.match(symbol) and linkage == 'w'): + if not (symbol_allowlist_pattern.match(symbol) or + symbol_ok_pattern.match(symbol) or + symbol_is_hidden.match(symbol)): + print('{}: error: Unescaped exported symbol: {}'.format(PROG, symbol)) + result = 1 return result diff --git a/utils/generate_grammar_tables.py b/utils/generate_grammar_tables.py index c4ed180a..74aa2829 100755 --- a/utils/generate_grammar_tables.py +++ b/utils/generate_grammar_tables.py @@ -23,7 +23,7 @@ import re PYGEN_VARIABLE_PREFIX = 'pygen_variable' # Extensions to recognize, but which don't necessarily come from the SPIR-V -# core or KHR grammar files. Get this list from the SPIR-V registery web page. +# core or KHR grammar files. Get this list from the SPIR-V registry web page. # NOTE: Only put things on this list if it is not in those grammar files. EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS = """ SPV_AMD_gcn_shader @@ -523,17 +523,17 @@ def generate_operand_kind_table(enums): enums = [generate_enum_operand_kind(e, exts) for e in enums] exts_arrays = generate_extension_arrays(exts) - # We have three operand kinds that requires their optional counterpart to + # We have a few operand kinds that require their optional counterpart to # exist in the operand info table. - three_optional_enums = ['ImageOperands', 'AccessQualifier', 'MemoryAccess'] - three_optional_enums = [e for e in enums if e[0] in three_optional_enums] - enums.extend(three_optional_enums) + optional_enums = ['ImageOperands', 'AccessQualifier', 'MemoryAccess', 'PackedVectorFormat'] + optional_enums = [e for e in enums if e[0] in optional_enums] + enums.extend(optional_enums) enum_kinds, enum_names, enum_entries = zip(*enums) - # Mark the last three as optional ones. - enum_quantifiers = [''] * (len(enums) - 3) + ['?'] * 3 + # Mark the last few as optional ones. + enum_quantifiers = [''] * (len(enums) - len(optional_enums)) + ['?'] * len(optional_enums) # And we don't want redefinition of them. - enum_entries = enum_entries[:-3] + enum_entries = enum_entries[:-len(optional_enums)] enum_kinds = [convert_operand_kind(e) for e in zip(enum_kinds, enum_quantifiers)] table_entries = zip(enum_kinds, enum_names, enum_names) diff --git a/utils/git-sync-deps b/utils/git-sync-deps index eecfbe93..7a7e606f 100755 --- a/utils/git-sync-deps +++ b/utils/git-sync-deps @@ -168,7 +168,7 @@ def git_checkout_to_directory(git, repo, checkoutable, directory, verbose): with open(os.devnull, 'w') as devnull: # If this fails, we will fetch before trying again. Don't spam user - # with error infomation. + # with error information. if 0 == subprocess.call([git, 'checkout', '--quiet', checkoutable], cwd=directory, stderr=devnull): # if this succeeds, skip slow `git fetch`. diff --git a/utils/roll_deps.sh b/utils/roll_deps.sh index 7ecfdd3b..20c061fd 100755 --- a/utils/roll_deps.sh +++ b/utils/roll_deps.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2019 Google Inc. +# Copyright (c) 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,14 +15,17 @@ # 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. +# Depends on roll-dep from depot_tools +# (https://chromium.googlesource.com/chromium/tools/depot_tools) being in PATH. + +set -eo pipefail effcee_dir="external/effcee/" effcee_trunk="origin/main" googletest_dir="external/googletest/" -googletest_trunk="origin/master" +googletest_trunk="origin/main" re2_dir="external/re2/" -re2_trunk="origin/master" +re2_trunk="origin/main" spirv_headers_dir="external/spirv-headers/" spirv_headers_trunk="origin/master" @@ -36,11 +39,15 @@ if [[ $(git diff --stat) != '' ]]; then exit 1 fi +echo "*** Ignore messages about running 'git cl upload' ***" + old_head=$(git rev-parse HEAD) +set +e 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}" + diff --git a/utils/update_build_version.py b/utils/update_build_version.py index 321de74b..2a1ca600 100755 --- a/utils/update_build_version.py +++ b/utils/update_build_version.py @@ -17,16 +17,16 @@ # Updates an output file with version info unless the new content is the same # as the existing content. # -# Args: +# Args: # # The output file will contain a line of text consisting of two C source syntax # string literals separated by a comma: -# - The software version deduced from the CHANGES file in the given directory. +# - The software version deduced from the given CHANGES file. # - A longer string with the project name, the software version number, and -# git commit information for the directory. The commit information -# is the output of "git describe" if that succeeds, or "git rev-parse HEAD" -# if that succeeds, or otherwise a message containing the phrase -# "unknown hash". +# git commit information for the CHANGES file's directory. The commit +# information is the output of "git describe" if that succeeds, or "git +# rev-parse HEAD" if that succeeds, or otherwise a message containing the +# phrase "unknown hash". # The string contents are escaped as necessary. import datetime @@ -73,9 +73,8 @@ def command_output(cmd, directory): return stdout -def deduce_software_version(directory): - """Returns a software version number parsed from the CHANGES file - in the given directory. +def deduce_software_version(changes_file): + """Returns a software version number parsed from the given CHANGES file. The CHANGES file describes most recent versions first. """ @@ -85,7 +84,6 @@ def deduce_software_version(directory): # unexpected carriage returns on a linefeed-only system such as # Linux. pattern = re.compile(r'^(v\d+\.\d+(-dev)?) \d\d\d\d-\d\d-\d\d\s*$') - changes_file = os.path.join(directory, 'CHANGES') with open(changes_file, mode='r') as f: for line in f.readlines(): match = pattern.match(line) @@ -125,16 +123,17 @@ def describe(directory): def main(): if len(sys.argv) != 3: - print('usage: {} '.format(sys.argv[0])) + print('usage: {} '.format(sys.argv[0])) sys.exit(1) output_file = sys.argv[2] mkdir_p(os.path.dirname(output_file)) software_version = deduce_software_version(sys.argv[1]) + directory = os.path.dirname(sys.argv[1]) new_content = '"{}", "SPIRV-Tools {} {}"\n'.format( software_version, software_version, - describe(sys.argv[1]).replace('"', '\\"')) + describe(directory).replace('"', '\\"')) if os.path.isfile(output_file): with open(output_file, 'r') as f: diff --git a/utils/vscode/install.sh b/utils/vscode/install.sh index 01fc9140..de54c623 100755 --- a/utils/vscode/install.sh +++ b/utils/vscode/install.sh @@ -18,15 +18,17 @@ set -e # Fail on any error. EXT_PATH=~/.vscode/extensions/google.spirvls-0.0.1 ROOT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -go run ${ROOT_PATH}/src/tools/gen-grammar.go --cache ${ROOT_PATH}/cache --template ${ROOT_PATH}/spirv.json.tmpl --out ${ROOT_PATH}/spirv.json -go run ${ROOT_PATH}/src/tools/gen-grammar.go --cache ${ROOT_PATH}/cache --template ${ROOT_PATH}/src/schema/schema.go.tmpl --out ${ROOT_PATH}/src/schema/schema.go +pushd ${ROOT_PATH} + go run ./src/tools/gen-grammar.go --cache ./cache --template ./spirv.json.tmpl --out ./spirv.json + go run ./src/tools/gen-grammar.go --cache ./cache --template ./src/schema/schema.go.tmpl --out ./src/schema/schema.go -mkdir -p ${EXT_PATH} -cp ${ROOT_PATH}/extension.js ${EXT_PATH} -cp ${ROOT_PATH}/package.json ${EXT_PATH} -cp ${ROOT_PATH}/spirv.json ${EXT_PATH} + mkdir -p ${EXT_PATH} + cp ./extension.js ${EXT_PATH} + cp ./package.json ${EXT_PATH} + cp ./spirv.json ${EXT_PATH} -go build -o ${EXT_PATH}/langsvr ${ROOT_PATH}/src/langsvr.go + go build -o ${EXT_PATH}/langsvr ./src/langsvr.go +popd cd ${EXT_PATH} npm install diff --git a/utils/vscode/spirv.json b/utils/vscode/spirv.json index 29b679c3..2e88296e 100644 --- a/utils/vscode/spirv.json +++ b/utils/vscode/spirv.json @@ -1,7 +1,7 @@ { "scopeName": "source.spirv", "name": "SPIR-V", - "comment": "Generated by gen-grammar.go --template=../../spirv.json.tmpl --out=../../spirv.json. Do not modify this file directly.", + "comment": "Generated by gen-grammar.go --template=./spirv.json.tmpl --out=./spirv.json. Do not modify this file directly.", "patterns": [ { "include": "#BitEnum_ImageOperands" }, { "include": "#BitEnum_FPFastMathMode" }, @@ -12,7 +12,6 @@ { "include": "#BitEnum_MemoryAccess" }, { "include": "#BitEnum_KernelProfilingInfo" }, { "include": "#BitEnum_RayFlags" }, - { "include": "#BitEnum_FragmentShadingRate" }, { "include": "#ValueEnum_SourceLanguage" }, { "include": "#ValueEnum_ExecutionModel" }, { "include": "#ValueEnum_AddressingModel" }, @@ -26,8 +25,6 @@ { "include": "#ValueEnum_ImageChannelOrder" }, { "include": "#ValueEnum_ImageChannelDataType" }, { "include": "#ValueEnum_FPRoundingMode" }, - { "include": "#ValueEnum_FPDenormMode" }, - { "include": "#ValueEnum_FPOperationMode" }, { "include": "#ValueEnum_LinkageType" }, { "include": "#ValueEnum_AccessQualifier" }, { "include": "#ValueEnum_FunctionParameterAttribute" }, @@ -60,7 +57,7 @@ "name": "keyword.spirv" }, "BitEnum_FPFastMathMode": { - "match": "\\b(None|NotNaN|NotInf|NSZ|AllowRecip|Fast|AllowContractFastINTEL|AllowReassocINTEL)\\b", + "match": "\\b(None|NotNaN|NotInf|NSZ|AllowRecip|Fast)\\b", "name": "keyword.spirv" }, "BitEnum_SelectionControl": { @@ -68,7 +65,7 @@ "name": "keyword.spirv" }, "BitEnum_LoopControl": { - "match": "\\b(None|Unroll|DontUnroll|DependencyInfinite|DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount|InitiationIntervalINTEL|MaxConcurrencyINTEL|DependencyArrayINTEL|PipelineEnableINTEL|LoopCoalesceINTEL|MaxInterleavingINTEL|SpeculatedIterationsINTEL|NoFusionINTEL)\\b", + "match": "\\b(None|Unroll|DontUnroll|DependencyInfinite|DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount)\\b", "name": "keyword.spirv" }, "BitEnum_FunctionControl": { @@ -91,10 +88,6 @@ "match": "\\b(NoneKHR|OpaqueKHR|NoOpaqueKHR|TerminateOnFirstHitKHR|SkipClosestHitShaderKHR|CullBackFacingTrianglesKHR|CullFrontFacingTrianglesKHR|CullOpaqueKHR|CullNoOpaqueKHR|SkipTrianglesKHR|SkipAABBsKHR)\\b", "name": "keyword.spirv" }, - "BitEnum_FragmentShadingRate": { - "match": "\\b(Vertical2Pixels|Vertical4Pixels|Horizontal2Pixels|Horizontal4Pixels)\\b", - "name": "keyword.spirv" - }, "ValueEnum_SourceLanguage": { "match": "\\b(Unknown|ESSL|GLSL|OpenCL_C|OpenCL_CPP|HLSL)\\b", "name": "keyword.spirv" @@ -112,11 +105,11 @@ "name": "keyword.spirv" }, "ValueEnum_ExecutionMode": { - "match": "\\b(Invocations|SpacingEqual|SpacingFractionalEven|SpacingFractionalOdd|VertexOrderCw|VertexOrderCcw|PixelCenterInteger|OriginUpperLeft|OriginLowerLeft|EarlyFragmentTests|PointMode|Xfb|DepthReplacing|DepthGreater|DepthLess|DepthUnchanged|LocalSize|LocalSizeHint|InputPoints|InputLines|InputLinesAdjacency|Triangles|InputTrianglesAdjacency|Quads|Isolines|OutputVertices|OutputPoints|OutputLineStrip|OutputTriangleStrip|VecTypeHint|ContractionOff|Initializer|Finalizer|SubgroupSize|SubgroupsPerWorkgroup|SubgroupsPerWorkgroupId|LocalSizeId|LocalSizeHintId|PostDepthCoverage|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|StencilRefReplacingEXT|OutputLinesNV|OutputPrimitivesNV|DerivativeGroupQuadsNV|DerivativeGroupLinearNV|OutputTrianglesNV|PixelInterlockOrderedEXT|PixelInterlockUnorderedEXT|SampleInterlockOrderedEXT|SampleInterlockUnorderedEXT|ShadingRateInterlockOrderedEXT|ShadingRateInterlockUnorderedEXT|SharedLocalMemorySizeINTEL|RoundingModeRTPINTEL|RoundingModeRTNINTEL|FloatingPointModeALTINTEL|FloatingPointModeIEEEINTEL|MaxWorkgroupSizeINTEL|MaxWorkDimINTEL|NoGlobalOffsetINTEL|NumSIMDWorkitemsINTEL|SchedulerTargetFmaxMhzINTEL)\\b", + "match": "\\b(Invocations|SpacingEqual|SpacingFractionalEven|SpacingFractionalOdd|VertexOrderCw|VertexOrderCcw|PixelCenterInteger|OriginUpperLeft|OriginLowerLeft|EarlyFragmentTests|PointMode|Xfb|DepthReplacing|DepthGreater|DepthLess|DepthUnchanged|LocalSize|LocalSizeHint|InputPoints|InputLines|InputLinesAdjacency|Triangles|InputTrianglesAdjacency|Quads|Isolines|OutputVertices|OutputPoints|OutputLineStrip|OutputTriangleStrip|VecTypeHint|ContractionOff|Initializer|Finalizer|SubgroupSize|SubgroupsPerWorkgroup|SubgroupsPerWorkgroupId|LocalSizeId|LocalSizeHintId|PostDepthCoverage|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|StencilRefReplacingEXT|OutputLinesNV|OutputPrimitivesNV|DerivativeGroupQuadsNV|DerivativeGroupLinearNV|OutputTrianglesNV|PixelInterlockOrderedEXT|PixelInterlockUnorderedEXT|SampleInterlockOrderedEXT|SampleInterlockUnorderedEXT|ShadingRateInterlockOrderedEXT|ShadingRateInterlockUnorderedEXT)\\b", "name": "keyword.spirv" }, "ValueEnum_StorageClass": { - "match": "\\b(UniformConstant|Input|Uniform|Output|Workgroup|CrossWorkgroup|Private|Function|Generic|PushConstant|AtomicCounter|Image|StorageBuffer|CallableDataNV|CallableDataKHR|IncomingCallableDataNV|IncomingCallableDataKHR|RayPayloadNV|RayPayloadKHR|HitAttributeNV|HitAttributeKHR|IncomingRayPayloadNV|IncomingRayPayloadKHR|ShaderRecordBufferNV|ShaderRecordBufferKHR|PhysicalStorageBuffer|PhysicalStorageBufferEXT|CodeSectionINTEL|DeviceOnlyINTEL|HostOnlyINTEL)\\b", + "match": "\\b(UniformConstant|Input|Uniform|Output|Workgroup|CrossWorkgroup|Private|Function|Generic|PushConstant|AtomicCounter|Image|StorageBuffer|CallableDataNV|CallableDataKHR|IncomingCallableDataNV|IncomingCallableDataKHR|RayPayloadNV|RayPayloadKHR|HitAttributeNV|HitAttributeKHR|IncomingRayPayloadNV|IncomingRayPayloadKHR|ShaderRecordBufferNV|ShaderRecordBufferKHR|PhysicalStorageBuffer|PhysicalStorageBufferEXT)\\b", "name": "keyword.spirv" }, "ValueEnum_Dim": { @@ -132,7 +125,7 @@ "name": "keyword.spirv" }, "ValueEnum_ImageFormat": { - "match": "\\b(Unknown|Rgba32f|Rgba16f|R32f|Rgba8|Rgba8Snorm|Rg32f|Rg16f|R11fG11fB10f|R16f|Rgba16|Rgb10A2|Rg16|Rg8|R16|R8|Rgba16Snorm|Rg16Snorm|Rg8Snorm|R16Snorm|R8Snorm|Rgba32i|Rgba16i|Rgba8i|R32i|Rg32i|Rg16i|Rg8i|R16i|R8i|Rgba32ui|Rgba16ui|Rgba8ui|R32ui|Rgb10a2ui|Rg32ui|Rg16ui|Rg8ui|R16ui|R8ui|R64ui|R64i)\\b", + "match": "\\b(Unknown|Rgba32f|Rgba16f|R32f|Rgba8|Rgba8Snorm|Rg32f|Rg16f|R11fG11fB10f|R16f|Rgba16|Rgb10A2|Rg16|Rg8|R16|R8|Rgba16Snorm|Rg16Snorm|Rg8Snorm|R16Snorm|R8Snorm|Rgba32i|Rgba16i|Rgba8i|R32i|Rg32i|Rg16i|Rg8i|R16i|R8i|Rgba32ui|Rgba16ui|Rgba8ui|R32ui|Rgb10a2ui|Rg32ui|Rg16ui|Rg8ui|R16ui|R8ui)\\b", "name": "keyword.spirv" }, "ValueEnum_ImageChannelOrder": { @@ -147,14 +140,6 @@ "match": "\\b(RTE|RTZ|RTP|RTN)\\b", "name": "keyword.spirv" }, - "ValueEnum_FPDenormMode": { - "match": "\\b(Preserve|FlushToZero)\\b", - "name": "keyword.spirv" - }, - "ValueEnum_FPOperationMode": { - "match": "\\b(IEEE|ALT)\\b", - "name": "keyword.spirv" - }, "ValueEnum_LinkageType": { "match": "\\b(Export|Import)\\b", "name": "keyword.spirv" @@ -168,11 +153,11 @@ "name": "keyword.spirv" }, "ValueEnum_Decoration": { - "match": "\\b(RelaxedPrecision|SpecId|Block|BufferBlock|RowMajor|ColMajor|ArrayStride|MatrixStride|GLSLShared|GLSLPacked|CPacked|BuiltIn|NoPerspective|Flat|Patch|Centroid|Sample|Invariant|Restrict|Aliased|Volatile|Constant|Coherent|NonWritable|NonReadable|Uniform|UniformId|SaturatedConversion|Stream|Location|Component|Index|Binding|DescriptorSet|Offset|XfbBuffer|XfbStride|FuncParamAttr|FPRoundingMode|FPFastMathMode|LinkageAttributes|NoContraction|InputAttachmentIndex|Alignment|MaxByteOffset|AlignmentId|MaxByteOffsetId|NoSignedWrap|NoUnsignedWrap|ExplicitInterpAMD|OverrideCoverageNV|PassthroughNV|ViewportRelativeNV|SecondaryViewportRelativeNV|PerPrimitiveNV|PerViewNV|PerTaskNV|PerVertexNV|NonUniform|NonUniformEXT|RestrictPointer|RestrictPointerEXT|AliasedPointer|AliasedPointerEXT|SIMTCallINTEL|ReferencedIndirectlyINTEL|ClobberINTEL|SideEffectsINTEL|VectorComputeVariableINTEL|FuncParamIOKindINTEL|VectorComputeFunctionINTEL|StackCallINTEL|GlobalVariableOffsetINTEL|CounterBuffer|HlslCounterBufferGOOGLE|UserSemantic|HlslSemanticGOOGLE|UserTypeGOOGLE|FunctionRoundingModeINTEL|FunctionDenormModeINTEL|RegisterINTEL|MemoryINTEL|NumbanksINTEL|BankwidthINTEL|MaxPrivateCopiesINTEL|SinglepumpINTEL|DoublepumpINTEL|MaxReplicatesINTEL|SimpleDualPortINTEL|MergeINTEL|BankBitsINTEL|ForcePow2DepthINTEL|BurstCoalesceINTEL|CacheSizeINTEL|DontStaticallyCoalesceINTEL|PrefetchINTEL|StallEnableINTEL|FuseLoopsInFunctionINTEL|BufferLocationINTEL|IOPipeStorageINTEL|FunctionFloatingPointModeINTEL|SingleElementVectorINTEL|VectorComputeCallableFunctionINTEL)\\b", + "match": "\\b(RelaxedPrecision|SpecId|Block|BufferBlock|RowMajor|ColMajor|ArrayStride|MatrixStride|GLSLShared|GLSLPacked|CPacked|BuiltIn|NoPerspective|Flat|Patch|Centroid|Sample|Invariant|Restrict|Aliased|Volatile|Constant|Coherent|NonWritable|NonReadable|Uniform|UniformId|SaturatedConversion|Stream|Location|Component|Index|Binding|DescriptorSet|Offset|XfbBuffer|XfbStride|FuncParamAttr|FPRoundingMode|FPFastMathMode|LinkageAttributes|NoContraction|InputAttachmentIndex|Alignment|MaxByteOffset|AlignmentId|MaxByteOffsetId|NoSignedWrap|NoUnsignedWrap|ExplicitInterpAMD|OverrideCoverageNV|PassthroughNV|ViewportRelativeNV|SecondaryViewportRelativeNV|PerPrimitiveNV|PerViewNV|PerTaskNV|PerVertexNV|NonUniform|NonUniformEXT|RestrictPointer|RestrictPointerEXT|AliasedPointer|AliasedPointerEXT|CounterBuffer|HlslCounterBufferGOOGLE|UserSemantic|HlslSemanticGOOGLE|UserTypeGOOGLE)\\b", "name": "keyword.spirv" }, "ValueEnum_BuiltIn": { - "match": "\\b(Position|PointSize|ClipDistance|CullDistance|VertexId|InstanceId|PrimitiveId|InvocationId|Layer|ViewportIndex|TessLevelOuter|TessLevelInner|TessCoord|PatchVertices|FragCoord|PointCoord|FrontFacing|SampleId|SamplePosition|SampleMask|FragDepth|HelperInvocation|NumWorkgroups|WorkgroupSize|WorkgroupId|LocalInvocationId|GlobalInvocationId|LocalInvocationIndex|WorkDim|GlobalSize|EnqueuedWorkgroupSize|GlobalOffset|GlobalLinearId|SubgroupSize|SubgroupMaxSize|NumSubgroups|NumEnqueuedSubgroups|SubgroupId|SubgroupLocalInvocationId|VertexIndex|InstanceIndex|SubgroupEqMask|SubgroupEqMaskKHR|SubgroupGeMask|SubgroupGeMaskKHR|SubgroupGtMask|SubgroupGtMaskKHR|SubgroupLeMask|SubgroupLeMaskKHR|SubgroupLtMask|SubgroupLtMaskKHR|BaseVertex|BaseInstance|DrawIndex|PrimitiveShadingRateKHR|DeviceIndex|ViewIndex|ShadingRateKHR|BaryCoordNoPerspAMD|BaryCoordNoPerspCentroidAMD|BaryCoordNoPerspSampleAMD|BaryCoordSmoothAMD|BaryCoordSmoothCentroidAMD|BaryCoordSmoothSampleAMD|BaryCoordPullModelAMD|FragStencilRefEXT|ViewportMaskNV|SecondaryPositionNV|SecondaryViewportMaskNV|PositionPerViewNV|ViewportMaskPerViewNV|FullyCoveredEXT|TaskCountNV|PrimitiveCountNV|PrimitiveIndicesNV|ClipDistancePerViewNV|CullDistancePerViewNV|LayerPerViewNV|MeshViewCountNV|MeshViewIndicesNV|BaryCoordNV|BaryCoordNoPerspNV|FragSizeEXT|FragmentSizeNV|FragInvocationCountEXT|InvocationsPerPixelNV|LaunchIdNV|LaunchIdKHR|LaunchSizeNV|LaunchSizeKHR|WorldRayOriginNV|WorldRayOriginKHR|WorldRayDirectionNV|WorldRayDirectionKHR|ObjectRayOriginNV|ObjectRayOriginKHR|ObjectRayDirectionNV|ObjectRayDirectionKHR|RayTminNV|RayTminKHR|RayTmaxNV|RayTmaxKHR|InstanceCustomIndexNV|InstanceCustomIndexKHR|ObjectToWorldNV|ObjectToWorldKHR|WorldToObjectNV|WorldToObjectKHR|HitTNV|HitKindNV|HitKindKHR|IncomingRayFlagsNV|IncomingRayFlagsKHR|RayGeometryIndexKHR|WarpsPerSMNV|SMCountNV|WarpIDNV|SMIDNV)\\b", + "match": "\\b(Position|PointSize|ClipDistance|CullDistance|VertexId|InstanceId|PrimitiveId|InvocationId|Layer|ViewportIndex|TessLevelOuter|TessLevelInner|TessCoord|PatchVertices|FragCoord|PointCoord|FrontFacing|SampleId|SamplePosition|SampleMask|FragDepth|HelperInvocation|NumWorkgroups|WorkgroupSize|WorkgroupId|LocalInvocationId|GlobalInvocationId|LocalInvocationIndex|WorkDim|GlobalSize|EnqueuedWorkgroupSize|GlobalOffset|GlobalLinearId|SubgroupSize|SubgroupMaxSize|NumSubgroups|NumEnqueuedSubgroups|SubgroupId|SubgroupLocalInvocationId|VertexIndex|InstanceIndex|SubgroupEqMask|SubgroupGeMask|SubgroupGtMask|SubgroupLeMask|SubgroupLtMask|SubgroupEqMaskKHR|SubgroupGeMaskKHR|SubgroupGtMaskKHR|SubgroupLeMaskKHR|SubgroupLtMaskKHR|BaseVertex|BaseInstance|DrawIndex|DeviceIndex|ViewIndex|BaryCoordNoPerspAMD|BaryCoordNoPerspCentroidAMD|BaryCoordNoPerspSampleAMD|BaryCoordSmoothAMD|BaryCoordSmoothCentroidAMD|BaryCoordSmoothSampleAMD|BaryCoordPullModelAMD|FragStencilRefEXT|ViewportMaskNV|SecondaryPositionNV|SecondaryViewportMaskNV|PositionPerViewNV|ViewportMaskPerViewNV|FullyCoveredEXT|TaskCountNV|PrimitiveCountNV|PrimitiveIndicesNV|ClipDistancePerViewNV|CullDistancePerViewNV|LayerPerViewNV|MeshViewCountNV|MeshViewIndicesNV|BaryCoordNV|BaryCoordNoPerspNV|FragSizeEXT|FragmentSizeNV|FragInvocationCountEXT|InvocationsPerPixelNV|LaunchIdNV|LaunchIdKHR|LaunchSizeNV|LaunchSizeKHR|WorldRayOriginNV|WorldRayOriginKHR|WorldRayDirectionNV|WorldRayDirectionKHR|ObjectRayOriginNV|ObjectRayOriginKHR|ObjectRayDirectionNV|ObjectRayDirectionKHR|RayTminNV|RayTminKHR|RayTmaxNV|RayTmaxKHR|InstanceCustomIndexNV|InstanceCustomIndexKHR|ObjectToWorldNV|ObjectToWorldKHR|WorldToObjectNV|WorldToObjectKHR|HitTNV|HitTKHR|HitKindNV|HitKindKHR|IncomingRayFlagsNV|IncomingRayFlagsKHR|RayGeometryIndexKHR|WarpsPerSMNV|SMCountNV|WarpIDNV|SMIDNV)\\b", "name": "keyword.spirv" }, "ValueEnum_Scope": { @@ -188,7 +173,7 @@ "name": "keyword.spirv" }, "ValueEnum_Capability": { - "match": "\\b(Matrix|Shader|Geometry|Tessellation|Addresses|Linkage|Kernel|Vector16|Float16Buffer|Float16|Float64|Int64|Int64Atomics|ImageBasic|ImageReadWrite|ImageMipmap|Pipes|Groups|DeviceEnqueue|LiteralSampler|AtomicStorage|Int16|TessellationPointSize|GeometryPointSize|ImageGatherExtended|StorageImageMultisample|UniformBufferArrayDynamicIndexing|SampledImageArrayDynamicIndexing|StorageBufferArrayDynamicIndexing|StorageImageArrayDynamicIndexing|ClipDistance|CullDistance|ImageCubeArray|SampleRateShading|ImageRect|SampledRect|GenericPointer|Int8|InputAttachment|SparseResidency|MinLod|Sampled1D|Image1D|SampledCubeArray|SampledBuffer|ImageBuffer|ImageMSArray|StorageImageExtendedFormats|ImageQuery|DerivativeControl|InterpolationFunction|TransformFeedback|GeometryStreams|StorageImageReadWithoutFormat|StorageImageWriteWithoutFormat|MultiViewport|SubgroupDispatch|NamedBarrier|PipeStorage|GroupNonUniform|GroupNonUniformVote|GroupNonUniformArithmetic|GroupNonUniformBallot|GroupNonUniformShuffle|GroupNonUniformShuffleRelative|GroupNonUniformClustered|GroupNonUniformQuad|ShaderLayer|ShaderViewportIndex|FragmentShadingRateKHR|SubgroupBallotKHR|DrawParameters|WorkgroupMemoryExplicitLayoutKHR|WorkgroupMemoryExplicitLayout8BitAccessKHR|WorkgroupMemoryExplicitLayout16BitAccessKHR|SubgroupVoteKHR|StorageBuffer16BitAccess|StorageUniformBufferBlock16|UniformAndStorageBuffer16BitAccess|StorageUniform16|StoragePushConstant16|StorageInputOutput16|DeviceGroup|MultiView|VariablePointersStorageBuffer|VariablePointers|AtomicStorageOps|SampleMaskPostDepthCoverage|StorageBuffer8BitAccess|UniformAndStorageBuffer8BitAccess|StoragePushConstant8|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|RayQueryProvisionalKHR|RayQueryKHR|RayTraversalPrimitiveCullingKHR|RayTracingKHR|Float16ImageAMD|ImageGatherBiasLodAMD|FragmentMaskAMD|StencilExportEXT|ImageReadWriteLodAMD|Int64ImageEXT|ShaderClockKHR|SampleMaskOverrideCoverageNV|GeometryShaderPassthroughNV|ShaderViewportIndexLayerEXT|ShaderViewportIndexLayerNV|ShaderViewportMaskNV|ShaderStereoViewNV|PerViewAttributesNV|FragmentFullyCoveredEXT|MeshShadingNV|ImageFootprintNV|FragmentBarycentricNV|ComputeDerivativeGroupQuadsNV|FragmentDensityEXT|ShadingRateNV|GroupNonUniformPartitionedNV|ShaderNonUniform|ShaderNonUniformEXT|RuntimeDescriptorArray|RuntimeDescriptorArrayEXT|InputAttachmentArrayDynamicIndexing|InputAttachmentArrayDynamicIndexingEXT|UniformTexelBufferArrayDynamicIndexing|UniformTexelBufferArrayDynamicIndexingEXT|StorageTexelBufferArrayDynamicIndexing|StorageTexelBufferArrayDynamicIndexingEXT|UniformBufferArrayNonUniformIndexing|UniformBufferArrayNonUniformIndexingEXT|SampledImageArrayNonUniformIndexing|SampledImageArrayNonUniformIndexingEXT|StorageBufferArrayNonUniformIndexing|StorageBufferArrayNonUniformIndexingEXT|StorageImageArrayNonUniformIndexing|StorageImageArrayNonUniformIndexingEXT|InputAttachmentArrayNonUniformIndexing|InputAttachmentArrayNonUniformIndexingEXT|UniformTexelBufferArrayNonUniformIndexing|UniformTexelBufferArrayNonUniformIndexingEXT|StorageTexelBufferArrayNonUniformIndexing|StorageTexelBufferArrayNonUniformIndexingEXT|RayTracingNV|VulkanMemoryModel|VulkanMemoryModelKHR|VulkanMemoryModelDeviceScope|VulkanMemoryModelDeviceScopeKHR|PhysicalStorageBufferAddresses|PhysicalStorageBufferAddressesEXT|ComputeDerivativeGroupLinearNV|RayTracingProvisionalKHR|CooperativeMatrixNV|FragmentShaderSampleInterlockEXT|FragmentShaderShadingRateInterlockEXT|ShaderSMBuiltinsNV|FragmentShaderPixelInterlockEXT|DemoteToHelperInvocationEXT|SubgroupShuffleINTEL|SubgroupBufferBlockIOINTEL|SubgroupImageBlockIOINTEL|SubgroupImageMediaBlockIOINTEL|RoundToInfinityINTEL|FloatingPointModeINTEL|IntegerFunctions2INTEL|FunctionPointersINTEL|IndirectReferencesINTEL|AsmINTEL|AtomicFloat32MinMaxEXT|AtomicFloat64MinMaxEXT|AtomicFloat16MinMaxEXT|VectorComputeINTEL|VectorAnyINTEL|SubgroupAvcMotionEstimationINTEL|SubgroupAvcMotionEstimationIntraINTEL|SubgroupAvcMotionEstimationChromaINTEL|VariableLengthArrayINTEL|FunctionFloatControlINTEL|FPGAMemoryAttributesINTEL|FPFastMathModeINTEL|ArbitraryPrecisionIntegersINTEL|UnstructuredLoopControlsINTEL|FPGALoopControlsINTEL|KernelAttributesINTEL|FPGAKernelAttributesINTEL|FPGAMemoryAccessesINTEL|FPGAClusterAttributesINTEL|LoopFuseINTEL|FPGABufferLocationINTEL|USMStorageClassesINTEL|IOPipesINTEL|BlockingPipesINTEL|FPGARegINTEL|AtomicFloat32AddEXT|AtomicFloat64AddEXT|LongConstantCompositeINTEL)\\b", + "match": "\\b(Matrix|Shader|Geometry|Tessellation|Addresses|Linkage|Kernel|Vector16|Float16Buffer|Float16|Float64|Int64|Int64Atomics|ImageBasic|ImageReadWrite|ImageMipmap|Pipes|Groups|DeviceEnqueue|LiteralSampler|AtomicStorage|Int16|TessellationPointSize|GeometryPointSize|ImageGatherExtended|StorageImageMultisample|UniformBufferArrayDynamicIndexing|SampledImageArrayDynamicIndexing|StorageBufferArrayDynamicIndexing|StorageImageArrayDynamicIndexing|ClipDistance|CullDistance|ImageCubeArray|SampleRateShading|ImageRect|SampledRect|GenericPointer|Int8|InputAttachment|SparseResidency|MinLod|Sampled1D|Image1D|SampledCubeArray|SampledBuffer|ImageBuffer|ImageMSArray|StorageImageExtendedFormats|ImageQuery|DerivativeControl|InterpolationFunction|TransformFeedback|GeometryStreams|StorageImageReadWithoutFormat|StorageImageWriteWithoutFormat|MultiViewport|SubgroupDispatch|NamedBarrier|PipeStorage|GroupNonUniform|GroupNonUniformVote|GroupNonUniformArithmetic|GroupNonUniformBallot|GroupNonUniformShuffle|GroupNonUniformShuffleRelative|GroupNonUniformClustered|GroupNonUniformQuad|ShaderLayer|ShaderViewportIndex|SubgroupBallotKHR|DrawParameters|SubgroupVoteKHR|StorageBuffer16BitAccess|StorageUniformBufferBlock16|UniformAndStorageBuffer16BitAccess|StorageUniform16|StoragePushConstant16|StorageInputOutput16|DeviceGroup|MultiView|VariablePointersStorageBuffer|VariablePointers|AtomicStorageOps|SampleMaskPostDepthCoverage|StorageBuffer8BitAccess|UniformAndStorageBuffer8BitAccess|StoragePushConstant8|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|RayQueryProvisionalKHR|RayTraversalPrimitiveCullingProvisionalKHR|Float16ImageAMD|ImageGatherBiasLodAMD|FragmentMaskAMD|StencilExportEXT|ImageReadWriteLodAMD|ShaderClockKHR|SampleMaskOverrideCoverageNV|GeometryShaderPassthroughNV|ShaderViewportIndexLayerEXT|ShaderViewportIndexLayerNV|ShaderViewportMaskNV|ShaderStereoViewNV|PerViewAttributesNV|FragmentFullyCoveredEXT|MeshShadingNV|ImageFootprintNV|FragmentBarycentricNV|ComputeDerivativeGroupQuadsNV|FragmentDensityEXT|ShadingRateNV|GroupNonUniformPartitionedNV|ShaderNonUniform|ShaderNonUniformEXT|RuntimeDescriptorArray|RuntimeDescriptorArrayEXT|InputAttachmentArrayDynamicIndexing|InputAttachmentArrayDynamicIndexingEXT|UniformTexelBufferArrayDynamicIndexing|UniformTexelBufferArrayDynamicIndexingEXT|StorageTexelBufferArrayDynamicIndexing|StorageTexelBufferArrayDynamicIndexingEXT|UniformBufferArrayNonUniformIndexing|UniformBufferArrayNonUniformIndexingEXT|SampledImageArrayNonUniformIndexing|SampledImageArrayNonUniformIndexingEXT|StorageBufferArrayNonUniformIndexing|StorageBufferArrayNonUniformIndexingEXT|StorageImageArrayNonUniformIndexing|StorageImageArrayNonUniformIndexingEXT|InputAttachmentArrayNonUniformIndexing|InputAttachmentArrayNonUniformIndexingEXT|UniformTexelBufferArrayNonUniformIndexing|UniformTexelBufferArrayNonUniformIndexingEXT|StorageTexelBufferArrayNonUniformIndexing|StorageTexelBufferArrayNonUniformIndexingEXT|RayTracingNV|VulkanMemoryModel|VulkanMemoryModelKHR|VulkanMemoryModelDeviceScope|VulkanMemoryModelDeviceScopeKHR|PhysicalStorageBufferAddresses|PhysicalStorageBufferAddressesEXT|ComputeDerivativeGroupLinearNV|RayTracingProvisionalKHR|CooperativeMatrixNV|FragmentShaderSampleInterlockEXT|FragmentShaderShadingRateInterlockEXT|ShaderSMBuiltinsNV|FragmentShaderPixelInterlockEXT|DemoteToHelperInvocationEXT|SubgroupShuffleINTEL|SubgroupBufferBlockIOINTEL|SubgroupImageBlockIOINTEL|SubgroupImageMediaBlockIOINTEL|IntegerFunctions2INTEL|SubgroupAvcMotionEstimationINTEL|SubgroupAvcMotionEstimationIntraINTEL|SubgroupAvcMotionEstimationChromaINTEL)\\b", "name": "keyword.spirv" }, "ValueEnum_RayQueryIntersection": { @@ -204,7 +189,7 @@ "name": "keyword.spirv" }, "BitEnum_DebugInfoFlags": { - "match": "\\b(None|FlagIsProtected|FlagIsPrivate|FlagIsPublic|FlagIsLocal|FlagIsDefinition|FlagFwdDecl|FlagArtificial|FlagExplicit|FlagPrototyped|FlagObjectPointer|FlagStaticMember|FlagIndirectVariable|FlagLValueReference|FlagRValueReference|FlagIsOptimized|FlagIsEnumClass|FlagTypePassByValue|FlagTypePassByReference)\\b", + "match": "\\b(FlagIsProtected|FlagIsPrivate|FlagIsPublic|FlagIsLocal|FlagIsDefinition|FlagFwdDecl|FlagArtificial|FlagExplicit|FlagPrototyped|FlagObjectPointer|FlagStaticMember|FlagIndirectVariable|FlagLValueReference|FlagRValueReference|FlagIsOptimized|FlagIsEnumClass|FlagTypePassByValue|FlagTypePassByReference)\\b", "name": "keyword.spirv" }, "ValueEnum_DebugBaseTypeAttributeEncoding": { diff --git a/utils/vscode/src/langsvr.go b/utils/vscode/src/langsvr.go index 4ed441cb..b76e35f7 100644 --- a/utils/vscode/src/langsvr.go +++ b/utils/vscode/src/langsvr.go @@ -412,13 +412,15 @@ func (s *server) Formatting(ctx context.Context, p *lsp.DocumentFormattingParams } } - // Every good file ends with a new line. - sb.WriteString("\n") + formatted := sb.String() + + // Every good file ends with a single new line. + formatted = strings.TrimRight(formatted, "\n") + "\n" return []lsp.TextEdit{ - lsp.TextEdit{ + { Range: rangeToLSP(f.fullRange), - NewText: sb.String(), + NewText: formatted, }, }, nil } diff --git a/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go b/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go index b8436d27..44dd2205 100644 --- a/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go +++ b/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go @@ -48,7 +48,7 @@ const ( requestDone ) -// Request is sent to a server to represent a Call or Notify operaton. +// Request is sent to a server to represent a Call or Notify operation. type Request struct { conn *Conn cancel context.CancelFunc diff --git a/utils/vscode/src/lsp/jsonrpc2/wire.go b/utils/vscode/src/lsp/jsonrpc2/wire.go index 3e31c340..fed9a25b 100644 --- a/utils/vscode/src/lsp/jsonrpc2/wire.go +++ b/utils/vscode/src/lsp/jsonrpc2/wire.go @@ -44,7 +44,7 @@ const ( CodeServerOverloaded = -32000 ) -// WireRequest is sent to a server to represent a Call or Notify operaton. +// WireRequest is sent to a server to represent a Call or Notify operation. type WireRequest struct { // VersionTag is always encoded as the string "2.0" VersionTag VersionTag `json:"jsonrpc"` diff --git a/utils/vscode/src/lsp/protocol/tsprotocol.go b/utils/vscode/src/lsp/protocol/tsprotocol.go index 50543fc8..e0a35946 100644 --- a/utils/vscode/src/lsp/protocol/tsprotocol.go +++ b/utils/vscode/src/lsp/protocol/tsprotocol.go @@ -143,7 +143,7 @@ type WorkspaceFoldersServerCapabilities struct { * change notifications. * * If a strings is provided the string is treated as a ID - * under which the notification is registed on the client + * under which the notification is registered on the client * side. The ID can be used to unregister for these events * using the `client/unregisterCapability` request. */ @@ -162,7 +162,7 @@ type WorkspaceFolder struct { /*Name defined: * The name of the workspace folder. Used to refer to this - * workspace folder in thge user interface. + * workspace folder in the user interface. */ Name string `json:"name"` } @@ -1129,7 +1129,7 @@ type ServerCapabilities struct { * change notifications. * * If a strings is provided the string is treated as a ID - * under which the notification is registed on the client + * under which the notification is registered on the client * side. The ID can be used to unregister for these events * using the `client/unregisterCapability` request. */ @@ -1803,7 +1803,7 @@ type CompletionOptions struct { /*AllCommitCharacters defined: * The list of all possible characters that commit a completion. This field can be used - * if clients don't support individual commmit characters per completion item. See + * if clients don't support individual commit characters per completion item. See * `ClientCapabilities.textDocument.completion.completionItem.commitCharactersSupport` * * @since 3.2.0 @@ -2844,7 +2844,7 @@ type LocationLink struct { /*TargetSelectionRange defined: * The range that should be selected and revealed when this link is being followed, e.g the name of a function. - * Must be contained by the the `targetRange`. See also `DocumentSymbol#range` + * Must be contained by the `targetRange`. See also `DocumentSymbol#range` */ TargetSelectionRange Range `json:"targetSelectionRange"` } @@ -2881,7 +2881,7 @@ type Color struct { type ColorInformation struct { /*Range defined: - * The range in the document where this color appers. + * The range in the document where this color appears. */ Range Range `json:"range"` @@ -3627,14 +3627,14 @@ type DocumentSymbol struct { /*Range defined: * The range enclosing this symbol not including leading/trailing whitespace but everything else - * like comments. This information is typically used to determine if the the clients cursor is + * like comments. This information is typically used to determine if the clients cursor is * inside the symbol to reveal in the symbol in the UI. */ Range Range `json:"range"` /*SelectionRange defined: * The range that should be selected and revealed when this symbol is being picked, e.g the name of a function. - * Must be contained by the the `range`. + * Must be contained by the `range`. */ SelectionRange Range `json:"selectionRange"` @@ -3652,7 +3652,7 @@ type CodeActionContext struct { /*Diagnostics defined: * An array of diagnostics known on the client side overlapping the range provided to the - * `textDocument/codeAction` request. They are provied so that the server knows which + * `textDocument/codeAction` request. They are provided so that the server knows which * errors are currently presented to the user for the given range. There is no guarantee * that these accurately reflect the error state of the resource. The primary parameter * to compute code actions is the provided range. @@ -4081,13 +4081,13 @@ const ( /*TextOnlyTransactional defined: * If the workspace edit contains only textual file changes they are executed transactional. * If resource changes (create, rename or delete file) are part of the change the failure - * handling startegy is abort. + * handling strategy is abort. */ TextOnlyTransactional FailureHandlingKind = "textOnlyTransactional" /*Undo defined: * The client tries to undo the operations already executed. But there is no - * guaruntee that this is succeeding. + * guarantee that this is succeeding. */ Undo FailureHandlingKind = "undo" diff --git a/utils/vscode/src/parser/parser.go b/utils/vscode/src/parser/parser.go index cc6f3332..4c0fa8f7 100644 --- a/utils/vscode/src/parser/parser.go +++ b/utils/vscode/src/parser/parser.go @@ -798,7 +798,7 @@ type Identifier struct { References []*Token // all the places the identifier was referenced } -// Severity is an enumerator of diagnositc seeverities +// Severity is an enumerator of diagnostic severities type Severity int // Severity levels diff --git a/utils/vscode/src/schema/schema.go b/utils/vscode/src/schema/schema.go index 86d856fd..ed02de4e 100755 --- a/utils/vscode/src/schema/schema.go +++ b/utils/vscode/src/schema/schema.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Generated by gen-grammar.go --template=../schema/schema.go.tmpl --out=../schema/schema.go +// Generated by gen-grammar.go --template=./src/schema/schema.go.tmpl --out=./src/schema/schema.go // Do not modify this file directly. package schema @@ -447,25 +447,12 @@ var ( "OpPtrEqual": OpPtrEqual, "OpPtrNotEqual": OpPtrNotEqual, "OpPtrDiff": OpPtrDiff, - "OpTerminateInvocation": OpTerminateInvocation, "OpSubgroupBallotKHR": OpSubgroupBallotKHR, "OpSubgroupFirstInvocationKHR": OpSubgroupFirstInvocationKHR, "OpSubgroupAllKHR": OpSubgroupAllKHR, "OpSubgroupAnyKHR": OpSubgroupAnyKHR, "OpSubgroupAllEqualKHR": OpSubgroupAllEqualKHR, "OpSubgroupReadInvocationKHR": OpSubgroupReadInvocationKHR, - "OpTraceRayKHR": OpTraceRayKHR, - "OpExecuteCallableKHR": OpExecuteCallableKHR, - "OpConvertUToAccelerationStructureKHR": OpConvertUToAccelerationStructureKHR, - "OpIgnoreIntersectionKHR": OpIgnoreIntersectionKHR, - "OpTerminateRayKHR": OpTerminateRayKHR, - "OpTypeRayQueryKHR": OpTypeRayQueryKHR, - "OpRayQueryInitializeKHR": OpRayQueryInitializeKHR, - "OpRayQueryTerminateKHR": OpRayQueryTerminateKHR, - "OpRayQueryGenerateIntersectionKHR": OpRayQueryGenerateIntersectionKHR, - "OpRayQueryConfirmIntersectionKHR": OpRayQueryConfirmIntersectionKHR, - "OpRayQueryProceedKHR": OpRayQueryProceedKHR, - "OpRayQueryGetIntersectionTypeKHR": OpRayQueryGetIntersectionTypeKHR, "OpGroupIAddNonUniformAMD": OpGroupIAddNonUniformAMD, "OpGroupFAddNonUniformAMD": OpGroupFAddNonUniformAMD, "OpGroupFMinNonUniformAMD": OpGroupFMinNonUniformAMD, @@ -483,11 +470,39 @@ var ( "OpReportIntersectionNV": OpReportIntersectionNV, "OpReportIntersectionKHR": OpReportIntersectionKHR, "OpIgnoreIntersectionNV": OpIgnoreIntersectionNV, + "OpIgnoreIntersectionKHR": OpIgnoreIntersectionKHR, "OpTerminateRayNV": OpTerminateRayNV, + "OpTerminateRayKHR": OpTerminateRayKHR, "OpTraceNV": OpTraceNV, + "OpTraceRayKHR": OpTraceRayKHR, "OpTypeAccelerationStructureNV": OpTypeAccelerationStructureNV, "OpTypeAccelerationStructureKHR": OpTypeAccelerationStructureKHR, + "OpTypeRayQueryProvisionalKHR": OpTypeRayQueryProvisionalKHR, + "OpRayQueryInitializeKHR": OpRayQueryInitializeKHR, + "OpRayQueryTerminateKHR": OpRayQueryTerminateKHR, + "OpRayQueryGenerateIntersectionKHR": OpRayQueryGenerateIntersectionKHR, + "OpRayQueryConfirmIntersectionKHR": OpRayQueryConfirmIntersectionKHR, + "OpRayQueryProceedKHR": OpRayQueryProceedKHR, + "OpRayQueryGetIntersectionTypeKHR": OpRayQueryGetIntersectionTypeKHR, + "OpRayQueryGetRayTMinKHR": OpRayQueryGetRayTMinKHR, + "OpRayQueryGetRayFlagsKHR": OpRayQueryGetRayFlagsKHR, + "OpRayQueryGetIntersectionTKHR": OpRayQueryGetIntersectionTKHR, + "OpRayQueryGetIntersectionInstanceCustomIndexKHR": OpRayQueryGetIntersectionInstanceCustomIndexKHR, + "OpRayQueryGetIntersectionInstanceIdKHR": OpRayQueryGetIntersectionInstanceIdKHR, + "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR": OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR, + "OpRayQueryGetIntersectionGeometryIndexKHR": OpRayQueryGetIntersectionGeometryIndexKHR, + "OpRayQueryGetIntersectionPrimitiveIndexKHR": OpRayQueryGetIntersectionPrimitiveIndexKHR, + "OpRayQueryGetIntersectionBarycentricsKHR": OpRayQueryGetIntersectionBarycentricsKHR, + "OpRayQueryGetIntersectionFrontFaceKHR": OpRayQueryGetIntersectionFrontFaceKHR, + "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR": OpRayQueryGetIntersectionCandidateAABBOpaqueKHR, + "OpRayQueryGetIntersectionObjectRayDirectionKHR": OpRayQueryGetIntersectionObjectRayDirectionKHR, + "OpRayQueryGetIntersectionObjectRayOriginKHR": OpRayQueryGetIntersectionObjectRayOriginKHR, + "OpRayQueryGetWorldRayDirectionKHR": OpRayQueryGetWorldRayDirectionKHR, + "OpRayQueryGetWorldRayOriginKHR": OpRayQueryGetWorldRayOriginKHR, + "OpRayQueryGetIntersectionObjectToWorldKHR": OpRayQueryGetIntersectionObjectToWorldKHR, + "OpRayQueryGetIntersectionWorldToObjectKHR": OpRayQueryGetIntersectionWorldToObjectKHR, "OpExecuteCallableNV": OpExecuteCallableNV, + "OpExecuteCallableKHR": OpExecuteCallableKHR, "OpTypeCooperativeMatrixNV": OpTypeCooperativeMatrixNV, "OpCooperativeMatrixLoadNV": OpCooperativeMatrixLoadNV, "OpCooperativeMatrixStoreNV": OpCooperativeMatrixStoreNV, @@ -521,13 +536,6 @@ var ( "OpUSubSatINTEL": OpUSubSatINTEL, "OpIMul32x16INTEL": OpIMul32x16INTEL, "OpUMul32x16INTEL": OpUMul32x16INTEL, - "OpConstFunctionPointerINTEL": OpConstFunctionPointerINTEL, - "OpFunctionPointerCallINTEL": OpFunctionPointerCallINTEL, - "OpAsmTargetINTEL": OpAsmTargetINTEL, - "OpAsmINTEL": OpAsmINTEL, - "OpAsmCallINTEL": OpAsmCallINTEL, - "OpAtomicFMinEXT": OpAtomicFMinEXT, - "OpAtomicFMaxEXT": OpAtomicFMaxEXT, "OpDecorateString": OpDecorateString, "OpDecorateStringGOOGLE": OpDecorateStringGOOGLE, "OpMemberDecorateString": OpMemberDecorateString, @@ -650,37 +658,6 @@ var ( "OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL": OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL, "OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL": OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL, "OpSubgroupAvcSicGetInterRawSadsINTEL": OpSubgroupAvcSicGetInterRawSadsINTEL, - "OpVariableLengthArrayINTEL": OpVariableLengthArrayINTEL, - "OpSaveMemoryINTEL": OpSaveMemoryINTEL, - "OpRestoreMemoryINTEL": OpRestoreMemoryINTEL, - "OpLoopControlINTEL": OpLoopControlINTEL, - "OpPtrCastToCrossWorkgroupINTEL": OpPtrCastToCrossWorkgroupINTEL, - "OpCrossWorkgroupCastToPtrINTEL": OpCrossWorkgroupCastToPtrINTEL, - "OpReadPipeBlockingINTEL": OpReadPipeBlockingINTEL, - "OpWritePipeBlockingINTEL": OpWritePipeBlockingINTEL, - "OpFPGARegINTEL": OpFPGARegINTEL, - "OpRayQueryGetRayTMinKHR": OpRayQueryGetRayTMinKHR, - "OpRayQueryGetRayFlagsKHR": OpRayQueryGetRayFlagsKHR, - "OpRayQueryGetIntersectionTKHR": OpRayQueryGetIntersectionTKHR, - "OpRayQueryGetIntersectionInstanceCustomIndexKHR": OpRayQueryGetIntersectionInstanceCustomIndexKHR, - "OpRayQueryGetIntersectionInstanceIdKHR": OpRayQueryGetIntersectionInstanceIdKHR, - "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR": OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR, - "OpRayQueryGetIntersectionGeometryIndexKHR": OpRayQueryGetIntersectionGeometryIndexKHR, - "OpRayQueryGetIntersectionPrimitiveIndexKHR": OpRayQueryGetIntersectionPrimitiveIndexKHR, - "OpRayQueryGetIntersectionBarycentricsKHR": OpRayQueryGetIntersectionBarycentricsKHR, - "OpRayQueryGetIntersectionFrontFaceKHR": OpRayQueryGetIntersectionFrontFaceKHR, - "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR": OpRayQueryGetIntersectionCandidateAABBOpaqueKHR, - "OpRayQueryGetIntersectionObjectRayDirectionKHR": OpRayQueryGetIntersectionObjectRayDirectionKHR, - "OpRayQueryGetIntersectionObjectRayOriginKHR": OpRayQueryGetIntersectionObjectRayOriginKHR, - "OpRayQueryGetWorldRayDirectionKHR": OpRayQueryGetWorldRayDirectionKHR, - "OpRayQueryGetWorldRayOriginKHR": OpRayQueryGetWorldRayOriginKHR, - "OpRayQueryGetIntersectionObjectToWorldKHR": OpRayQueryGetIntersectionObjectToWorldKHR, - "OpRayQueryGetIntersectionWorldToObjectKHR": OpRayQueryGetIntersectionWorldToObjectKHR, - "OpAtomicFAddEXT": OpAtomicFAddEXT, - "OpTypeBufferSurfaceINTEL": OpTypeBufferSurfaceINTEL, - "OpTypeStructContinuedINTEL": OpTypeStructContinuedINTEL, - "OpConstantCompositeContinuedINTEL": OpConstantCompositeContinuedINTEL, - "OpSpecConstantCompositeContinuedINTEL": OpSpecConstantCompositeContinuedINTEL, } // ExtOpcodes is a map of extension name to Opcode description list. @@ -10045,13 +10022,6 @@ var ( }, }, } - OpTerminateInvocation = &Opcode { - Opname: "OpTerminateInvocation", - Class: "Control-Flow", - Opcode: 4416, - Operands: []Operand { - }, - } OpSubgroupBallotKHR = &Opcode { Opname: "OpSubgroupBallotKHR", Class: "Group", @@ -10189,270 +10159,6 @@ var ( }, }, } - OpTraceRayKHR = &Opcode { - Opname: "OpTraceRayKHR", - Class: "Reserved", - Opcode: 4445, - Operands: []Operand { - Operand { - Kind: OperandKindIdRef, - Name: "'Accel'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Ray Flags'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Cull Mask'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'SBT Offset'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'SBT Stride'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Miss Index'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Ray Origin'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Ray Tmin'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Ray Direction'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Ray Tmax'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Payload'", - Quantifier: "", - }, - }, - } - OpExecuteCallableKHR = &Opcode { - Opname: "OpExecuteCallableKHR", - Class: "Reserved", - Opcode: 4446, - Operands: []Operand { - Operand { - Kind: OperandKindIdRef, - Name: "'SBT Index'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Callable Data'", - Quantifier: "", - }, - }, - } - OpConvertUToAccelerationStructureKHR = &Opcode { - Opname: "OpConvertUToAccelerationStructureKHR", - Class: "Reserved", - Opcode: 4447, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Accel'", - Quantifier: "", - }, - }, - } - OpIgnoreIntersectionKHR = &Opcode { - Opname: "OpIgnoreIntersectionKHR", - Class: "Reserved", - Opcode: 4448, - Operands: []Operand { - }, - } - OpTerminateRayKHR = &Opcode { - Opname: "OpTerminateRayKHR", - Class: "Reserved", - Opcode: 4449, - Operands: []Operand { - }, - } - OpTypeRayQueryKHR = &Opcode { - Opname: "OpTypeRayQueryKHR", - Class: "Reserved", - Opcode: 4472, - Operands: []Operand { - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - }, - } - OpRayQueryInitializeKHR = &Opcode { - Opname: "OpRayQueryInitializeKHR", - Class: "Reserved", - Opcode: 4473, - Operands: []Operand { - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Accel'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayFlags'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'CullMask'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayOrigin'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayTMin'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayDirection'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayTMax'", - Quantifier: "", - }, - }, - } - OpRayQueryTerminateKHR = &Opcode { - Opname: "OpRayQueryTerminateKHR", - Class: "Reserved", - Opcode: 4474, - Operands: []Operand { - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - }, - } - OpRayQueryGenerateIntersectionKHR = &Opcode { - Opname: "OpRayQueryGenerateIntersectionKHR", - Class: "Reserved", - Opcode: 4475, - Operands: []Operand { - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'HitT'", - Quantifier: "", - }, - }, - } - OpRayQueryConfirmIntersectionKHR = &Opcode { - Opname: "OpRayQueryConfirmIntersectionKHR", - Class: "Reserved", - Opcode: 4476, - Operands: []Operand { - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - }, - } - OpRayQueryProceedKHR = &Opcode { - Opname: "OpRayQueryProceedKHR", - Class: "Reserved", - Opcode: 4477, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionTypeKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionTypeKHR", - Class: "Reserved", - Opcode: 4479, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } OpGroupIAddNonUniformAMD = &Opcode { Opname: "OpGroupIAddNonUniformAMD", Class: "Group", @@ -10932,6 +10638,13 @@ var ( Operands: []Operand { }, } + OpIgnoreIntersectionKHR = &Opcode { + Opname: "OpIgnoreIntersectionKHR", + Class: "Reserved", + Opcode: 5335, + Operands: []Operand { + }, + } OpTerminateRayNV = &Opcode { Opname: "OpTerminateRayNV", Class: "Reserved", @@ -10939,6 +10652,13 @@ var ( Operands: []Operand { }, } + OpTerminateRayKHR = &Opcode { + Opname: "OpTerminateRayKHR", + Class: "Reserved", + Opcode: 5336, + Operands: []Operand { + }, + } OpTraceNV = &Opcode { Opname: "OpTraceNV", Class: "Reserved", @@ -11001,6 +10721,68 @@ var ( }, }, } + OpTraceRayKHR = &Opcode { + Opname: "OpTraceRayKHR", + Class: "Reserved", + Opcode: 5337, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Accel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Cull Mask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Miss Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Tmin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Tmax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'PayloadId'", + Quantifier: "", + }, + }, + } OpTypeAccelerationStructureNV = &Opcode { Opname: "OpTypeAccelerationStructureNV", Class: "Reserved", @@ -11025,6 +10807,589 @@ var ( }, }, } + OpTypeRayQueryProvisionalKHR = &Opcode { + Opname: "OpTypeRayQueryProvisionalKHR", + Class: "Reserved", + Opcode: 4472, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpRayQueryInitializeKHR = &Opcode { + Opname: "OpRayQueryInitializeKHR", + Class: "Reserved", + Opcode: 4473, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Accel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayFlags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'CullMask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayOrigin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayTMin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayDirection'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayTMax'", + Quantifier: "", + }, + }, + } + OpRayQueryTerminateKHR = &Opcode { + Opname: "OpRayQueryTerminateKHR", + Class: "Reserved", + Opcode: 4474, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGenerateIntersectionKHR = &Opcode { + Opname: "OpRayQueryGenerateIntersectionKHR", + Class: "Reserved", + Opcode: 4475, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'HitT'", + Quantifier: "", + }, + }, + } + OpRayQueryConfirmIntersectionKHR = &Opcode { + Opname: "OpRayQueryConfirmIntersectionKHR", + Class: "Reserved", + Opcode: 4476, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryProceedKHR = &Opcode { + Opname: "OpRayQueryProceedKHR", + Class: "Reserved", + Opcode: 4477, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionTypeKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionTypeKHR", + Class: "Reserved", + Opcode: 4479, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetRayTMinKHR = &Opcode { + Opname: "OpRayQueryGetRayTMinKHR", + Class: "Reserved", + Opcode: 6016, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetRayFlagsKHR = &Opcode { + Opname: "OpRayQueryGetRayFlagsKHR", + Class: "Reserved", + Opcode: 6017, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionTKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionTKHR", + Class: "Reserved", + Opcode: 6018, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionInstanceCustomIndexKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionInstanceCustomIndexKHR", + Class: "Reserved", + Opcode: 6019, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionInstanceIdKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionInstanceIdKHR", + Class: "Reserved", + Opcode: 6020, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR", + Class: "Reserved", + Opcode: 6021, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionGeometryIndexKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionGeometryIndexKHR", + Class: "Reserved", + Opcode: 6022, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionPrimitiveIndexKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionPrimitiveIndexKHR", + Class: "Reserved", + Opcode: 6023, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionBarycentricsKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionBarycentricsKHR", + Class: "Reserved", + Opcode: 6024, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionFrontFaceKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionFrontFaceKHR", + Class: "Reserved", + Opcode: 6025, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR", + Class: "Reserved", + Opcode: 6026, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionObjectRayDirectionKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionObjectRayDirectionKHR", + Class: "Reserved", + Opcode: 6027, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionObjectRayOriginKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionObjectRayOriginKHR", + Class: "Reserved", + Opcode: 6028, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetWorldRayDirectionKHR = &Opcode { + Opname: "OpRayQueryGetWorldRayDirectionKHR", + Class: "Reserved", + Opcode: 6029, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetWorldRayOriginKHR = &Opcode { + Opname: "OpRayQueryGetWorldRayOriginKHR", + Class: "Reserved", + Opcode: 6030, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionObjectToWorldKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionObjectToWorldKHR", + Class: "Reserved", + Opcode: 6031, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionWorldToObjectKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionWorldToObjectKHR", + Class: "Reserved", + Opcode: 6032, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } OpExecuteCallableNV = &Opcode { Opname: "OpExecuteCallableNV", Class: "Reserved", @@ -11042,6 +11407,23 @@ var ( }, }, } + OpExecuteCallableKHR = &Opcode { + Opname: "OpExecuteCallableKHR", + Class: "Reserved", + Opcode: 5344, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Callable DataId'", + Quantifier: "", + }, + }, + } OpTypeCooperativeMatrixNV = &Opcode { Opname: "OpTypeCooperativeMatrixNV", Class: "Reserved", @@ -11878,210 +12260,6 @@ var ( }, }, } - OpConstFunctionPointerINTEL = &Opcode { - Opname: "OpConstFunctionPointerINTEL", - Class: "@exclude", - Opcode: 5600, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Function'", - Quantifier: "", - }, - }, - } - OpFunctionPointerCallINTEL = &Opcode { - Opname: "OpFunctionPointerCallINTEL", - Class: "@exclude", - Opcode: 5601, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Operand 1'", - Quantifier: "*", - }, - }, - } - OpAsmTargetINTEL = &Opcode { - Opname: "OpAsmTargetINTEL", - Class: "@exclude", - Opcode: 5609, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindLiteralString, - Name: "'Asm target'", - Quantifier: "", - }, - }, - } - OpAsmINTEL = &Opcode { - Opname: "OpAsmINTEL", - Class: "@exclude", - Opcode: 5610, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Asm type'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Target'", - Quantifier: "", - }, - Operand { - Kind: OperandKindLiteralString, - Name: "'Asm instructions'", - Quantifier: "", - }, - Operand { - Kind: OperandKindLiteralString, - Name: "'Constraints'", - Quantifier: "", - }, - }, - } - OpAsmCallINTEL = &Opcode { - Opname: "OpAsmCallINTEL", - Class: "@exclude", - Opcode: 5611, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Asm'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Argument 0'", - Quantifier: "*", - }, - }, - } - OpAtomicFMinEXT = &Opcode { - Opname: "OpAtomicFMinEXT", - Class: "Atomic", - Opcode: 5614, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Pointer'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdScope, - Name: "'Memory'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdMemorySemantics, - Name: "'Semantics'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Value'", - Quantifier: "", - }, - }, - } - OpAtomicFMaxEXT = &Opcode { - Opname: "OpAtomicFMaxEXT", - Class: "Atomic", - Opcode: 5615, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Pointer'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdScope, - Name: "'Memory'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdMemorySemantics, - Name: "'Semantics'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Value'", - Quantifier: "", - }, - }, - } OpDecorateString = &Opcode { Opname: "OpDecorateString", Class: "Annotation", @@ -15261,713 +15439,6 @@ var ( }, }, } - OpVariableLengthArrayINTEL = &Opcode { - Opname: "OpVariableLengthArrayINTEL", - Class: "@exclude", - Opcode: 5818, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Lenght'", - Quantifier: "", - }, - }, - } - OpSaveMemoryINTEL = &Opcode { - Opname: "OpSaveMemoryINTEL", - Class: "@exclude", - Opcode: 5819, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - }, - } - OpRestoreMemoryINTEL = &Opcode { - Opname: "OpRestoreMemoryINTEL", - Class: "@exclude", - Opcode: 5820, - Operands: []Operand { - Operand { - Kind: OperandKindIdRef, - Name: "'Ptr'", - Quantifier: "", - }, - }, - } - OpLoopControlINTEL = &Opcode { - Opname: "OpLoopControlINTEL", - Class: "Reserved", - Opcode: 5887, - Operands: []Operand { - Operand { - Kind: OperandKindLiteralInteger, - Name: "'Loop Control Parameters'", - Quantifier: "*", - }, - }, - } - OpPtrCastToCrossWorkgroupINTEL = &Opcode { - Opname: "OpPtrCastToCrossWorkgroupINTEL", - Class: "@exclude", - Opcode: 5934, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Pointer'", - Quantifier: "", - }, - }, - } - OpCrossWorkgroupCastToPtrINTEL = &Opcode { - Opname: "OpCrossWorkgroupCastToPtrINTEL", - Class: "@exclude", - Opcode: 5938, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Pointer'", - Quantifier: "", - }, - }, - } - OpReadPipeBlockingINTEL = &Opcode { - Opname: "OpReadPipeBlockingINTEL", - Class: "Pipe", - Opcode: 5946, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Packet Size'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Packet Alignment'", - Quantifier: "", - }, - }, - } - OpWritePipeBlockingINTEL = &Opcode { - Opname: "OpWritePipeBlockingINTEL", - Class: "Pipe", - Opcode: 5947, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Packet Size'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Packet Alignment'", - Quantifier: "", - }, - }, - } - OpFPGARegINTEL = &Opcode { - Opname: "OpFPGARegINTEL", - Class: "Reserved", - Opcode: 5949, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Result'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Input'", - Quantifier: "", - }, - }, - } - OpRayQueryGetRayTMinKHR = &Opcode { - Opname: "OpRayQueryGetRayTMinKHR", - Class: "Reserved", - Opcode: 6016, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - }, - } - OpRayQueryGetRayFlagsKHR = &Opcode { - Opname: "OpRayQueryGetRayFlagsKHR", - Class: "Reserved", - Opcode: 6017, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionTKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionTKHR", - Class: "Reserved", - Opcode: 6018, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionInstanceCustomIndexKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionInstanceCustomIndexKHR", - Class: "Reserved", - Opcode: 6019, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionInstanceIdKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionInstanceIdKHR", - Class: "Reserved", - Opcode: 6020, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR", - Class: "Reserved", - Opcode: 6021, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionGeometryIndexKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionGeometryIndexKHR", - Class: "Reserved", - Opcode: 6022, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionPrimitiveIndexKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionPrimitiveIndexKHR", - Class: "Reserved", - Opcode: 6023, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionBarycentricsKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionBarycentricsKHR", - Class: "Reserved", - Opcode: 6024, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionFrontFaceKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionFrontFaceKHR", - Class: "Reserved", - Opcode: 6025, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR", - Class: "Reserved", - Opcode: 6026, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionObjectRayDirectionKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionObjectRayDirectionKHR", - Class: "Reserved", - Opcode: 6027, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionObjectRayOriginKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionObjectRayOriginKHR", - Class: "Reserved", - Opcode: 6028, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpRayQueryGetWorldRayDirectionKHR = &Opcode { - Opname: "OpRayQueryGetWorldRayDirectionKHR", - Class: "Reserved", - Opcode: 6029, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - }, - } - OpRayQueryGetWorldRayOriginKHR = &Opcode { - Opname: "OpRayQueryGetWorldRayOriginKHR", - Class: "Reserved", - Opcode: 6030, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionObjectToWorldKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionObjectToWorldKHR", - Class: "Reserved", - Opcode: 6031, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionWorldToObjectKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionWorldToObjectKHR", - Class: "Reserved", - Opcode: 6032, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpAtomicFAddEXT = &Opcode { - Opname: "OpAtomicFAddEXT", - Class: "Atomic", - Opcode: 6035, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Pointer'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdScope, - Name: "'Memory'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdMemorySemantics, - Name: "'Semantics'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Value'", - Quantifier: "", - }, - }, - } - OpTypeBufferSurfaceINTEL = &Opcode { - Opname: "OpTypeBufferSurfaceINTEL", - Class: "Type-Declaration", - Opcode: 6086, - Operands: []Operand { - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - }, - } - OpTypeStructContinuedINTEL = &Opcode { - Opname: "OpTypeStructContinuedINTEL", - Class: "Type-Declaration", - Opcode: 6090, - Operands: []Operand { - Operand { - Kind: OperandKindIdRef, - Name: "'Member 0 type', + 'member 1 type', + ...", - Quantifier: "*", - }, - }, - } - OpConstantCompositeContinuedINTEL = &Opcode { - Opname: "OpConstantCompositeContinuedINTEL", - Class: "Constant-Creation", - Opcode: 6091, - Operands: []Operand { - Operand { - Kind: OperandKindIdRef, - Name: "'Constituents'", - Quantifier: "*", - }, - }, - } - OpSpecConstantCompositeContinuedINTEL = &Opcode { - Opname: "OpSpecConstantCompositeContinuedINTEL", - Class: "Constant-Creation", - Opcode: 6092, - Operands: []Operand { - Operand { - Kind: OperandKindIdRef, - Name: "'Constituents'", - Quantifier: "*", - }, - }, - } GLSLStd450_Round = &Opcode { Opname: "Round", @@ -20317,20 +19788,6 @@ var ( Parameters: []Parameter{}, Version: "", }, - Enumerant{ - Enumerant: "AllowContractFastINTEL", - Value: 0x10000, - Capabilities: []string{"FPFastMathModeINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "AllowReassocINTEL", - Value: 0x20000, - Capabilities: []string{"FPFastMathModeINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, }, Bases: []*OperandKind {}, } @@ -20436,62 +19893,6 @@ var ( Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, Version: "1.4", }, - Enumerant{ - Enumerant: "InitiationIntervalINTEL", - Value: 0x10000, - Capabilities: []string{"FPGALoopControlsINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, - Version: "None", - }, - Enumerant{ - Enumerant: "MaxConcurrencyINTEL", - Value: 0x20000, - Capabilities: []string{"FPGALoopControlsINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, - Version: "None", - }, - Enumerant{ - Enumerant: "DependencyArrayINTEL", - Value: 0x40000, - Capabilities: []string{"FPGALoopControlsINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, - Version: "None", - }, - Enumerant{ - Enumerant: "PipelineEnableINTEL", - Value: 0x80000, - Capabilities: []string{"FPGALoopControlsINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, - Version: "None", - }, - Enumerant{ - Enumerant: "LoopCoalesceINTEL", - Value: 0x100000, - Capabilities: []string{"FPGALoopControlsINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, - Version: "None", - }, - Enumerant{ - Enumerant: "MaxInterleavingINTEL", - Value: 0x200000, - Capabilities: []string{"FPGALoopControlsINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, - Version: "None", - }, - Enumerant{ - Enumerant: "SpeculatedIterationsINTEL", - Value: 0x400000, - Capabilities: []string{"FPGALoopControlsINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, - Version: "None", - }, - Enumerant{ - Enumerant: "NoFusionINTEL", - Value: 0x800000, - Capabilities: []string{"FPGALoopControlsINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, - Version: "None", - }, }, Bases: []*OperandKind {}, } @@ -20782,114 +20183,79 @@ var ( Enumerant{ Enumerant: "NoneKHR", Value: 0x0000, - Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ Enumerant: "OpaqueKHR", Value: 0x0001, - Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ Enumerant: "NoOpaqueKHR", Value: 0x0002, - Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ Enumerant: "TerminateOnFirstHitKHR", Value: 0x0004, - Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ Enumerant: "SkipClosestHitShaderKHR", Value: 0x0008, - Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ Enumerant: "CullBackFacingTrianglesKHR", Value: 0x0010, - Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ Enumerant: "CullFrontFacingTrianglesKHR", Value: 0x0020, - Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ Enumerant: "CullOpaqueKHR", Value: 0x0040, - Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ Enumerant: "CullNoOpaqueKHR", Value: 0x0080, - Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ Enumerant: "SkipTrianglesKHR", Value: 0x0100, - Capabilities: []string{"RayTraversalPrimitiveCullingKHR",}, + Capabilities: []string{"RayTraversalPrimitiveCullingProvisionalKHR",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ Enumerant: "SkipAABBsKHR", Value: 0x0200, - Capabilities: []string{"RayTraversalPrimitiveCullingKHR",}, + Capabilities: []string{"RayTraversalPrimitiveCullingProvisionalKHR",}, Parameters: []Parameter{}, - Version: "None", - }, - }, - Bases: []*OperandKind {}, - } - OperandKindFragmentShadingRate = &OperandKind { - Kind: "FragmentShadingRate", - Category: "BitEnum", - Enumerants: []Enumerant { - Enumerant{ - Enumerant: "Vertical2Pixels", - Value: 0x0001, - Capabilities: []string{"FragmentShadingRateKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "Vertical4Pixels", - Value: 0x0002, - Capabilities: []string{"FragmentShadingRateKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "Horizontal2Pixels", - Value: 0x0004, - Capabilities: []string{"FragmentShadingRateKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "Horizontal4Pixels", - Value: 0x0008, - Capabilities: []string{"FragmentShadingRateKHR",}, - Parameters: []Parameter{}, - Version: "None", + Version: "", }, }, Bases: []*OperandKind {}, @@ -21013,84 +20379,84 @@ var ( Enumerant{ Enumerant: "RayGenerationNV", Value: 5313, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "RayGenerationKHR", Value: 5313, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "IntersectionNV", Value: 5314, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "IntersectionKHR", Value: 5314, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "AnyHitNV", Value: 5315, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "AnyHitKHR", Value: 5315, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ClosestHitNV", Value: 5316, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ClosestHitKHR", Value: 5316, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "MissNV", Value: 5317, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "MissKHR", Value: 5317, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "CallableNV", Value: 5318, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "CallableKHR", Value: 5318, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, @@ -21577,76 +20943,6 @@ var ( Parameters: []Parameter{}, Version: "None", }, - Enumerant{ - Enumerant: "SharedLocalMemorySizeINTEL", - Value: 5618, - Capabilities: []string{"VectorComputeINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'Size'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "RoundingModeRTPINTEL", - Value: 5620, - Capabilities: []string{"RoundToInfinityINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "RoundingModeRTNINTEL", - Value: 5621, - Capabilities: []string{"RoundToInfinityINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "FloatingPointModeALTINTEL", - Value: 5622, - Capabilities: []string{"RoundToInfinityINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "FloatingPointModeIEEEINTEL", - Value: 5623, - Capabilities: []string{"RoundToInfinityINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "MaxWorkgroupSizeINTEL", - Value: 5893, - Capabilities: []string{"KernelAttributesINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'max_x_size'"},{OperandKindLiteralInteger, "'max_y_size'"},{OperandKindLiteralInteger, "'max_z_size'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "MaxWorkDimINTEL", - Value: 5894, - Capabilities: []string{"KernelAttributesINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'max_dimensions'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "NoGlobalOffsetINTEL", - Value: 5895, - Capabilities: []string{"KernelAttributesINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "NumSIMDWorkitemsINTEL", - Value: 5896, - Capabilities: []string{"FPGAKernelAttributesINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'vector_width'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "SchedulerTargetFmaxMhzINTEL", - Value: 5903, - Capabilities: []string{"FPGAKernelAttributesINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'target_fmax'"},}, - Version: "None", - }, }, Bases: []*OperandKind {}, } @@ -21748,84 +21044,84 @@ var ( Enumerant{ Enumerant: "CallableDataNV", Value: 5328, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "CallableDataKHR", Value: 5328, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "IncomingCallableDataNV", Value: 5329, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "IncomingCallableDataKHR", Value: 5329, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "RayPayloadNV", Value: 5338, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "RayPayloadKHR", Value: 5338, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "HitAttributeNV", Value: 5339, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "HitAttributeKHR", Value: 5339, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "IncomingRayPayloadNV", Value: 5342, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "IncomingRayPayloadKHR", Value: 5342, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ShaderRecordBufferNV", Value: 5343, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ShaderRecordBufferKHR", Value: 5343, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, @@ -21843,27 +21139,6 @@ var ( Parameters: []Parameter{}, Version: "1.5", }, - Enumerant{ - Enumerant: "CodeSectionINTEL", - Value: 5605, - Capabilities: []string{"FunctionPointersINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "DeviceOnlyINTEL", - Value: 5936, - Capabilities: []string{"USMStorageClassesINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "HostOnlyINTEL", - Value: 5937, - Capabilities: []string{"USMStorageClassesINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, }, Bases: []*OperandKind {}, } @@ -22270,20 +21545,6 @@ var ( Parameters: []Parameter{}, Version: "", }, - Enumerant{ - Enumerant: "R64ui", - Value: 40, - Capabilities: []string{"Int64ImageEXT",}, - Parameters: []Parameter{}, - Version: "", - }, - Enumerant{ - Enumerant: "R64i", - Value: 41, - Capabilities: []string{"Int64ImageEXT",}, - Parameters: []Parameter{}, - Version: "", - }, }, Bases: []*OperandKind {}, } @@ -22595,48 +21856,6 @@ var ( }, Bases: []*OperandKind {}, } - OperandKindFPDenormMode = &OperandKind { - Kind: "FPDenormMode", - Category: "ValueEnum", - Enumerants: []Enumerant { - Enumerant{ - Enumerant: "Preserve", - Value: 0, - Capabilities: []string{"FunctionFloatControlINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "FlushToZero", - Value: 1, - Capabilities: []string{"FunctionFloatControlINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - }, - Bases: []*OperandKind {}, - } - OperandKindFPOperationMode = &OperandKind { - Kind: "FPOperationMode", - Category: "ValueEnum", - Enumerants: []Enumerant { - Enumerant{ - Enumerant: "IEEE", - Value: 0, - Capabilities: []string{"FunctionFloatControlINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "ALT", - Value: 1, - Capabilities: []string{"FunctionFloatControlINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - }, - Bases: []*OperandKind {}, - } OperandKindLinkageType = &OperandKind { Kind: "LinkageType", Category: "ValueEnum", @@ -23201,69 +22420,6 @@ var ( Parameters: []Parameter{}, Version: "1.5", }, - Enumerant{ - Enumerant: "SIMTCallINTEL", - Value: 5599, - Capabilities: []string{"VectorComputeINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'N'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "ReferencedIndirectlyINTEL", - Value: 5602, - Capabilities: []string{"IndirectReferencesINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "ClobberINTEL", - Value: 5607, - Capabilities: []string{"AsmINTEL",}, - Parameters: []Parameter{{OperandKindLiteralString, "'Register'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "SideEffectsINTEL", - Value: 5608, - Capabilities: []string{"AsmINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "VectorComputeVariableINTEL", - Value: 5624, - Capabilities: []string{"VectorComputeINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "FuncParamIOKindINTEL", - Value: 5625, - Capabilities: []string{"VectorComputeINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'Kind'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "VectorComputeFunctionINTEL", - Value: 5626, - Capabilities: []string{"VectorComputeINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "StackCallINTEL", - Value: 5627, - Capabilities: []string{"VectorComputeINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "GlobalVariableOffsetINTEL", - Value: 5628, - Capabilities: []string{"VectorComputeINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'Offset'"},}, - Version: "None", - }, Enumerant{ Enumerant: "CounterBuffer", Value: 5634, @@ -23299,181 +22455,6 @@ var ( Parameters: []Parameter{{OperandKindLiteralString, "'User Type'"},}, Version: "None", }, - Enumerant{ - Enumerant: "FunctionRoundingModeINTEL", - Value: 5822, - Capabilities: []string{"FunctionFloatControlINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},{OperandKindFPRoundingMode, "'FP Rounding Mode'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "FunctionDenormModeINTEL", - Value: 5823, - Capabilities: []string{"FunctionFloatControlINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},{OperandKindFPDenormMode, "'FP Denorm Mode'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "RegisterINTEL", - Value: 5825, - Capabilities: []string{"FPGAMemoryAttributesINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "MemoryINTEL", - Value: 5826, - Capabilities: []string{"FPGAMemoryAttributesINTEL",}, - Parameters: []Parameter{{OperandKindLiteralString, "'Memory Type'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "NumbanksINTEL", - Value: 5827, - Capabilities: []string{"FPGAMemoryAttributesINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'Banks'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "BankwidthINTEL", - Value: 5828, - Capabilities: []string{"FPGAMemoryAttributesINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'Bank Width'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "MaxPrivateCopiesINTEL", - Value: 5829, - Capabilities: []string{"FPGAMemoryAttributesINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'Maximum Copies'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "SinglepumpINTEL", - Value: 5830, - Capabilities: []string{"FPGAMemoryAttributesINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "DoublepumpINTEL", - Value: 5831, - Capabilities: []string{"FPGAMemoryAttributesINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "MaxReplicatesINTEL", - Value: 5832, - Capabilities: []string{"FPGAMemoryAttributesINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'Maximum Replicates'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "SimpleDualPortINTEL", - Value: 5833, - Capabilities: []string{"FPGAMemoryAttributesINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "MergeINTEL", - Value: 5834, - Capabilities: []string{"FPGAMemoryAttributesINTEL",}, - Parameters: []Parameter{{OperandKindLiteralString, "'Merge Key'"},{OperandKindLiteralString, "'Merge Type'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "BankBitsINTEL", - Value: 5835, - Capabilities: []string{"FPGAMemoryAttributesINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'Bank Bits'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "ForcePow2DepthINTEL", - Value: 5836, - Capabilities: []string{"FPGAMemoryAttributesINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'Force Key'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "BurstCoalesceINTEL", - Value: 5899, - Capabilities: []string{"FPGAMemoryAccessesINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "CacheSizeINTEL", - Value: 5900, - Capabilities: []string{"FPGAMemoryAccessesINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'Cache Size in bytes'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "DontStaticallyCoalesceINTEL", - Value: 5901, - Capabilities: []string{"FPGAMemoryAccessesINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "PrefetchINTEL", - Value: 5902, - Capabilities: []string{"FPGAMemoryAccessesINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'Prefetcher Size in bytes'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "StallEnableINTEL", - Value: 5905, - Capabilities: []string{"FPGAClusterAttributesINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "FuseLoopsInFunctionINTEL", - Value: 5907, - Capabilities: []string{"LoopFuseINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "BufferLocationINTEL", - Value: 5921, - Capabilities: []string{"FPGABufferLocationINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'Buffer Location ID'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "IOPipeStorageINTEL", - Value: 5944, - Capabilities: []string{"IOPipesINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'IO Pipe ID'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "FunctionFloatingPointModeINTEL", - Value: 6080, - Capabilities: []string{"FunctionFloatControlINTEL",}, - Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},{OperandKindFPOperationMode, "'FP Operation Mode'"},}, - Version: "None", - }, - Enumerant{ - Enumerant: "SingleElementVectorINTEL", - Value: 6085, - Capabilities: []string{"VectorComputeINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "VectorComputeCallableFunctionINTEL", - Value: 6087, - Capabilities: []string{"VectorComputeINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, }, Bases: []*OperandKind {}, } @@ -23526,7 +22507,7 @@ var ( Enumerant{ Enumerant: "PrimitiveId", Value: 7, - Capabilities: []string{"Geometry","Tessellation","RayTracingNV","RayTracingKHR","MeshShadingNV",}, + Capabilities: []string{"Geometry","Tessellation","RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "", }, @@ -23540,14 +22521,14 @@ var ( Enumerant{ Enumerant: "Layer", Value: 9, - Capabilities: []string{"Geometry","ShaderLayer","ShaderViewportIndexLayerEXT","MeshShadingNV",}, + Capabilities: []string{"Geometry","ShaderLayer","ShaderViewportIndexLayerEXT",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ Enumerant: "ViewportIndex", Value: 10, - Capabilities: []string{"MultiViewport","ShaderViewportIndex","ShaderViewportIndexLayerEXT","MeshShadingNV",}, + Capabilities: []string{"MultiViewport","ShaderViewportIndex","ShaderViewportIndexLayerEXT",}, Parameters: []Parameter{}, Version: "", }, @@ -23775,13 +22756,6 @@ var ( Parameters: []Parameter{}, Version: "1.3", }, - Enumerant{ - Enumerant: "SubgroupEqMaskKHR", - Value: 4416, - Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, - Parameters: []Parameter{}, - Version: "1.3", - }, Enumerant{ Enumerant: "SubgroupGeMask", Value: 4417, @@ -23789,13 +22763,6 @@ var ( Parameters: []Parameter{}, Version: "1.3", }, - Enumerant{ - Enumerant: "SubgroupGeMaskKHR", - Value: 4417, - Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, - Parameters: []Parameter{}, - Version: "1.3", - }, Enumerant{ Enumerant: "SubgroupGtMask", Value: 4418, @@ -23803,13 +22770,6 @@ var ( Parameters: []Parameter{}, Version: "1.3", }, - Enumerant{ - Enumerant: "SubgroupGtMaskKHR", - Value: 4418, - Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, - Parameters: []Parameter{}, - Version: "1.3", - }, Enumerant{ Enumerant: "SubgroupLeMask", Value: 4419, @@ -23817,13 +22777,6 @@ var ( Parameters: []Parameter{}, Version: "1.3", }, - Enumerant{ - Enumerant: "SubgroupLeMaskKHR", - Value: 4419, - Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, - Parameters: []Parameter{}, - Version: "1.3", - }, Enumerant{ Enumerant: "SubgroupLtMask", Value: 4420, @@ -23831,6 +22784,34 @@ var ( Parameters: []Parameter{}, Version: "1.3", }, + Enumerant{ + Enumerant: "SubgroupEqMaskKHR", + Value: 4416, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "SubgroupGeMaskKHR", + Value: 4417, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "SubgroupGtMaskKHR", + Value: 4418, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "SubgroupLeMaskKHR", + Value: 4419, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "1.3", + }, Enumerant{ Enumerant: "SubgroupLtMaskKHR", Value: 4420, @@ -23859,13 +22840,6 @@ var ( Parameters: []Parameter{}, Version: "1.3", }, - Enumerant{ - Enumerant: "PrimitiveShadingRateKHR", - Value: 4432, - Capabilities: []string{"FragmentShadingRateKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, Enumerant{ Enumerant: "DeviceIndex", Value: 4438, @@ -23880,13 +22854,6 @@ var ( Parameters: []Parameter{}, Version: "1.3", }, - Enumerant{ - Enumerant: "ShadingRateKHR", - Value: 4444, - Capabilities: []string{"FragmentShadingRateKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, Enumerant{ Enumerant: "BaryCoordNoPerspAMD", Value: 4992, @@ -24086,196 +23053,203 @@ var ( Enumerant{ Enumerant: "LaunchIdNV", Value: 5319, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "LaunchIdKHR", Value: 5319, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "LaunchSizeNV", Value: 5320, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "LaunchSizeKHR", Value: 5320, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "WorldRayOriginNV", Value: 5321, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "WorldRayOriginKHR", Value: 5321, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "WorldRayDirectionNV", Value: 5322, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "WorldRayDirectionKHR", Value: 5322, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ObjectRayOriginNV", Value: 5323, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ObjectRayOriginKHR", Value: 5323, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ObjectRayDirectionNV", Value: 5324, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ObjectRayDirectionKHR", Value: 5324, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "RayTminNV", Value: 5325, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "RayTminKHR", Value: 5325, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "RayTmaxNV", Value: 5326, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "RayTmaxKHR", Value: 5326, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "InstanceCustomIndexNV", Value: 5327, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "InstanceCustomIndexKHR", Value: 5327, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ObjectToWorldNV", Value: 5330, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ObjectToWorldKHR", Value: 5330, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "WorldToObjectNV", Value: 5331, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "WorldToObjectKHR", Value: 5331, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "HitTNV", Value: 5332, - Capabilities: []string{"RayTracingNV",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HitTKHR", + Value: 5332, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "HitKindNV", Value: 5333, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "HitKindKHR", Value: 5333, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "IncomingRayFlagsNV", Value: 5351, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "IncomingRayFlagsKHR", Value: 5351, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "RayGeometryIndexKHR", Value: 5352, - Capabilities: []string{"RayTracingKHR",}, + Capabilities: []string{"RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, @@ -24366,9 +23340,9 @@ var ( Enumerant{ Enumerant: "ShaderCallKHR", Value: 6, - Capabilities: []string{"RayTracingKHR",}, + Capabilities: []string{"RayTracingProvisionalKHR",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, }, Bases: []*OperandKind {}, @@ -24944,13 +23918,6 @@ var ( Parameters: []Parameter{}, Version: "1.5", }, - Enumerant{ - Enumerant: "FragmentShadingRateKHR", - Value: 4422, - Capabilities: []string{"Shader",}, - Parameters: []Parameter{}, - Version: "None", - }, Enumerant{ Enumerant: "SubgroupBallotKHR", Value: 4423, @@ -24965,27 +23932,6 @@ var ( Parameters: []Parameter{}, Version: "1.3", }, - Enumerant{ - Enumerant: "WorkgroupMemoryExplicitLayoutKHR", - Value: 4428, - Capabilities: []string{"Shader",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "WorkgroupMemoryExplicitLayout8BitAccessKHR", - Value: 4429, - Capabilities: []string{"WorkgroupMemoryExplicitLayoutKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "WorkgroupMemoryExplicitLayout16BitAccessKHR", - Value: 4430, - Capabilities: []string{"Shader",}, - Parameters: []Parameter{}, - Version: "None", - }, Enumerant{ Enumerant: "SubgroupVoteKHR", Value: 4431, @@ -25141,23 +24087,9 @@ var ( Version: "None", }, Enumerant{ - Enumerant: "RayQueryKHR", - Value: 4472, - Capabilities: []string{"Shader",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "RayTraversalPrimitiveCullingKHR", + Enumerant: "RayTraversalPrimitiveCullingProvisionalKHR", Value: 4478, - Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "RayTracingKHR", - Value: 4479, - Capabilities: []string{"Shader",}, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, @@ -25196,13 +24128,6 @@ var ( Parameters: []Parameter{}, Version: "None", }, - Enumerant{ - Enumerant: "Int64ImageEXT", - Value: 5016, - Capabilities: []string{"Shader",}, - Parameters: []Parameter{}, - Version: "None", - }, Enumerant{ Enumerant: "ShaderClockKHR", Value: 5055, @@ -25616,20 +24541,6 @@ var ( Parameters: []Parameter{}, Version: "None", }, - Enumerant{ - Enumerant: "RoundToInfinityINTEL", - Value: 5582, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "FloatingPointModeINTEL", - Value: 5583, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, Enumerant{ Enumerant: "IntegerFunctions2INTEL", Value: 5584, @@ -25637,62 +24548,6 @@ var ( Parameters: []Parameter{}, Version: "None", }, - Enumerant{ - Enumerant: "FunctionPointersINTEL", - Value: 5603, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "IndirectReferencesINTEL", - Value: 5604, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "AsmINTEL", - Value: 5606, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "AtomicFloat32MinMaxEXT", - Value: 5612, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "AtomicFloat64MinMaxEXT", - Value: 5613, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "AtomicFloat16MinMaxEXT", - Value: 5616, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "VectorComputeINTEL", - Value: 5617, - Capabilities: []string{"VectorAnyINTEL",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "VectorAnyINTEL", - Value: 5619, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, Enumerant{ Enumerant: "SubgroupAvcMotionEstimationINTEL", Value: 5696, @@ -25714,146 +24569,6 @@ var ( Parameters: []Parameter{}, Version: "None", }, - Enumerant{ - Enumerant: "VariableLengthArrayINTEL", - Value: 5817, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "FunctionFloatControlINTEL", - Value: 5821, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "FPGAMemoryAttributesINTEL", - Value: 5824, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "FPFastMathModeINTEL", - Value: 5837, - Capabilities: []string{"Kernel",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "ArbitraryPrecisionIntegersINTEL", - Value: 5844, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "UnstructuredLoopControlsINTEL", - Value: 5886, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "FPGALoopControlsINTEL", - Value: 5888, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "KernelAttributesINTEL", - Value: 5892, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "FPGAKernelAttributesINTEL", - Value: 5897, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "FPGAMemoryAccessesINTEL", - Value: 5898, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "FPGAClusterAttributesINTEL", - Value: 5904, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "LoopFuseINTEL", - Value: 5906, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "FPGABufferLocationINTEL", - Value: 5920, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "USMStorageClassesINTEL", - Value: 5935, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "IOPipesINTEL", - Value: 5943, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "BlockingPipesINTEL", - Value: 5945, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "FPGARegINTEL", - Value: 5948, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "AtomicFloat32AddEXT", - Value: 6033, - Capabilities: []string{"Shader",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "AtomicFloat64AddEXT", - Value: 6034, - Capabilities: []string{"Shader",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "LongConstantCompositeINTEL", - Value: 6089, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, }, Bases: []*OperandKind {}, } @@ -25864,16 +24579,16 @@ var ( Enumerant{ Enumerant: "RayQueryCandidateIntersectionKHR", Value: 0, - Capabilities: []string{"RayQueryKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ Enumerant: "RayQueryCommittedIntersectionKHR", Value: 1, - Capabilities: []string{"RayQueryKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, }, Bases: []*OperandKind {}, @@ -25885,23 +24600,23 @@ var ( Enumerant{ Enumerant: "RayQueryCommittedIntersectionNoneKHR", Value: 0, - Capabilities: []string{"RayQueryKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ Enumerant: "RayQueryCommittedIntersectionTriangleKHR", Value: 1, - Capabilities: []string{"RayQueryKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ Enumerant: "RayQueryCommittedIntersectionGeneratedKHR", Value: 2, - Capabilities: []string{"RayQueryKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, }, Bases: []*OperandKind {}, @@ -25913,16 +24628,16 @@ var ( Enumerant{ Enumerant: "RayQueryCandidateIntersectionTriangleKHR", Value: 0, - Capabilities: []string{"RayQueryKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ Enumerant: "RayQueryCandidateIntersectionAABBKHR", Value: 1, - Capabilities: []string{"RayQueryKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, }, Bases: []*OperandKind {}, @@ -26022,13 +24737,6 @@ var ( Kind: "DebugInfoFlags", Category: "BitEnum", Enumerants: []Enumerant { - Enumerant{ - Enumerant: "None", - Value: 0x0000, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "", - }, Enumerant{ Enumerant: "FlagIsProtected", Value: 0x01,