Squashed 'third_party/SPIRV-Tools/' changes from c3f22f7cb..8aa423930
8aa423930 Avoid pessimizing std::move (#3124)
fad92f1e7 Fix typo in validation message (#3122)
578c5ac13 Change default version for CreatInstBindlessCheckPass to 2 (#3119)
9215c1b7d Fix convert-relax-to-half invalid code (#3099) (#3106)
64f36ea52 Support OpenCL.DebugInfo.100 extended instruction set (#3080)
e01bc6d4e spirv-fuzz: Always add new globals to entry point interfaces (#3113)
dcb7169bb spirv-fuzz: Transformation to add a new function to a module (#3114)
2e41d5ece spirv-fuzz: Avoid passing access chains as parameters (#3112)
e70b009b0 Add support for SPV_KHR_non_semantic_info (#3110)
38d7fbaad spirv-fuzz: Transformations to add types, constants and variables (#3101)
fccbc00ac Make Instrumentation format version 2 the default (Step 1) (#3096)
96354f504 spirv-fuzz: Fuzzer pass to merge blocks (#3097)
5c019b592 Start SPIRV-Tools v2020.1
c413b982c Finalize SPIRV-Tools v2019.5
2afbe9051 Update CHANGES
00ca4e5bd Don't crash when folding construct of empty struct (#3092)
0a2b38d08 spirv-fuzz: function outlining fuzzer pass (#3078)
983b5b4fc spirv-fuzz: Use validator to check break/continue dominance conditions (#3089)
e82a42860 WebGPU: Array size at most max signed int + 1 (#3077)
0a5d99d02 Permit the debug instructions in WebGPU SPIR-V - remove from the optimizer (#3083)
af7410597 graphics robust access: use signed clamp (#3073)
3ed458604 Folding: perform add and sub on mismatched integer types (#3084)
47f3eb426 spirv-fuzz: Fix invalid tests (#3079)
b334829a9 Validate nested constructs (#3068)
52e9cc930 spirv-fuzz: Improve debugging facilities (#3074)
54385458c Handle unreachable block when computing register pressure (#3070)
868ca3954 Improve RegisterSizePasses (#3059)
f31f26f73 utils/vscode: Add install.bat (#3071)
03957e8a9 build: cmake: Add support for Fuchsia. (#3062)
a62012ced Add test with explicit example of stripping reflection info (#3064)
8312c523e Permit the debug instructions in WebGPU SPIR-V (#3063)
85f3e93d1 Respect CMAKE_INSTALL_LIBDIR in installed CMake files (#3054)
45dde9ad6 Add missing dealloc (#3061)
2ee9aaa28 Initialize binary for use as guard later (#3058)
0391d0823 Handle OpPhi with no in operands in value numbering (#3056)
ca703c887 Kill the id-to-func map after wrap-opkill (#3055)
57b4cb40b Convert stderr and stdout in status to strings on assignment (#3049)
git-subtree-dir: third_party/SPIRV-Tools
git-subtree-split: 8aa423930db37e37086665efcc55944d577c06e5
diff --git a/Android.mk b/Android.mk
index cb7062c..7794c76 100644
--- a/Android.mk
+++ b/Android.mk
@@ -178,73 +178,43 @@
# Locations of grammar files.
#
-# TODO(dneto): Build a single set of tables that embeds versioning differences on
-# a per-item basis. That must happen before SPIR-V 1.4, etc.
-# https://github.com/KhronosGroup/SPIRV-Tools/issues/1195
-SPV_CORE10_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/1.0/spirv.core.grammar.json
-SPV_CORE11_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/1.1/spirv.core.grammar.json
-SPV_CORE12_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/1.2/spirv.core.grammar.json
SPV_COREUNIFIED1_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/spirv.core.grammar.json
-SPV_CORELATEST_GRAMMAR=$(SPV_COREUNIFIED1_GRAMMAR)
SPV_GLSL_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/1.2/extinst.glsl.std.450.grammar.json
SPV_OPENCL_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/1.2/extinst.opencl.std.100.grammar.json
# TODO(dneto): I expect the DebugInfo grammar file to eventually migrate to SPIRV-Headers
SPV_DEBUGINFO_GRAMMAR=$(LOCAL_PATH)/source/extinst.debuginfo.grammar.json
+SPV_CLDEBUGINFO100_GRAMMAR=$(LOCAL_PATH)/source/extinst.opencl.debuginfo.100.grammar.json
define gen_spvtools_grammar_tables
-$(call generate-file-dir,$(1)/core.insts-1.0.inc)
-$(1)/core.insts-1.0.inc $(1)/operand.kinds-1.0.inc $(1)/glsl.std.450.insts.inc $(1)/opencl.std.insts.inc: \
+$(call generate-file-dir,$(1)/core.insts-unified1.inc)
+$(1)/core.insts-unified1.inc $(1)/operand.kinds-unified1.inc \
+$(1)/glsl.std.450.insts.inc \
+$(1)/opencl.std.insts.inc \
+: \
$(LOCAL_PATH)/utils/generate_grammar_tables.py \
- $(SPV_CORE10_GRAMMAR) \
+ $(SPV_COREUNIFIED1_GRAMMAR) \
$(SPV_GLSL_GRAMMAR) \
- $(SPV_OPENCL_GRAMMAR) \
- $(SPV_DEBUGINFO_GRAMMAR)
+ $(SPV_OpenCL_GRAMMAR) \
+ $(SPV_DEBUGINFO_GRAMMAR) \
+ $(SPV_CLDEBUGINFO100_GRAMMAR)
@$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \
- --spirv-core-grammar=$(SPV_CORE10_GRAMMAR) \
+ --spirv-core-grammar=$(SPV_COREUNIFIED1_GRAMMAR) \
--extinst-glsl-grammar=$(SPV_GLSL_GRAMMAR) \
--extinst-opencl-grammar=$(SPV_OPENCL_GRAMMAR) \
--extinst-debuginfo-grammar=$(SPV_DEBUGINFO_GRAMMAR) \
- --core-insts-output=$(1)/core.insts-1.0.inc \
+ --extinst-cldebuginfo100-grammar=$(SPV_CLDEBUGINFO100_GRAMMAR) \
+ --core-insts-output=$(1)/core.insts-unified1.inc \
--glsl-insts-output=$(1)/glsl.std.450.insts.inc \
--opencl-insts-output=$(1)/opencl.std.insts.inc \
- --operand-kinds-output=$(1)/operand.kinds-1.0.inc
- @echo "[$(TARGET_ARCH_ABI)] Grammar v1.0 : instructions & operands <= grammar JSON files"
-$(1)/core.insts-1.1.inc $(1)/operand.kinds-1.1.inc: \
- $(LOCAL_PATH)/utils/generate_grammar_tables.py \
- $(SPV_CORE11_GRAMMAR) \
- $(SPV_DEBUGINFO_GRAMMAR)
- @$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \
- --spirv-core-grammar=$(SPV_CORE11_GRAMMAR) \
- --extinst-debuginfo-grammar=$(SPV_DEBUGINFO_GRAMMAR) \
- --core-insts-output=$(1)/core.insts-1.1.inc \
- --operand-kinds-output=$(1)/operand.kinds-1.1.inc
- @echo "[$(TARGET_ARCH_ABI)] Grammar v1.1 : instructions & operands <= grammar JSON files"
-$(1)/core.insts-1.2.inc $(1)/operand.kinds-1.2.inc: \
- $(LOCAL_PATH)/utils/generate_grammar_tables.py \
- $(SPV_CORE12_GRAMMAR) \
- $(SPV_DEBUGINFO_GRAMMAR)
- @$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \
- --spirv-core-grammar=$(SPV_CORE12_GRAMMAR) \
- --extinst-debuginfo-grammar=$(SPV_DEBUGINFO_GRAMMAR) \
- --core-insts-output=$(1)/core.insts-1.2.inc \
- --operand-kinds-output=$(1)/operand.kinds-1.2.inc
- @echo "[$(TARGET_ARCH_ABI)] Grammar v1.2 : instructions & operands <= grammar JSON files"
-$(1)/core.insts-unified1.inc $(1)/operand.kinds-unified1.inc: \
- $(LOCAL_PATH)/utils/generate_grammar_tables.py \
- $(SPV_COREUNIFIED1_GRAMMAR) \
- $(SPV_DEBUGINFO_GRAMMAR)
- @$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \
- --spirv-core-grammar=$(SPV_COREUNIFIED1_GRAMMAR) \
- --extinst-debuginfo-grammar=$(SPV_DEBUGINFO_GRAMMAR) \
- --core-insts-output=$(1)/core.insts-unified1.inc \
--operand-kinds-output=$(1)/operand.kinds-unified1.inc
- @echo "[$(TARGET_ARCH_ABI)] Grammar v1.3 (from unified1) : instructions & operands <= grammar JSON files"
-$(LOCAL_PATH)/source/opcode.cpp: $(1)/core.insts-1.0.inc $(1)/core.insts-1.1.inc $(1)/core.insts-1.2.inc $(1)/core.insts-unified1.inc
-$(LOCAL_PATH)/source/operand.cpp: $(1)/operand.kinds-1.0.inc $(1)/operand.kinds-1.1.inc $(1)/operand.kinds-1.2.inc $(1)/operand.kinds-unified1.inc
+ @echo "[$(TARGET_ARCH_ABI)] Grammar (from unified1) : instructions & operands <= grammar JSON files"
+$(LOCAL_PATH)/source/opcode.cpp: $(1)/core.insts-unified1.inc
+$(LOCAL_PATH)/source/operand.cpp: $(1)/operand.kinds-unified1.inc
$(LOCAL_PATH)/source/ext_inst.cpp: \
$(1)/glsl.std.450.insts.inc \
$(1)/opencl.std.insts.inc \
$(1)/debuginfo.insts.inc \
+ $(1)/opencl.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 \
@@ -271,8 +241,9 @@
@echo "[$(TARGET_ARCH_ABI)] Generate language specific header for $(2): headers <= grammar"
$(LOCAL_PATH)/source/ext_inst.cpp: $(1)/$(2).h
endef
-# We generate language-specific headers for DebugInfo
+# 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)))
define gen_spvtools_vendor_tables
@@ -282,22 +253,28 @@
$(LOCAL_PATH)/source/extinst.$(2).grammar.json
@$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \
--extinst-vendor-grammar=$(LOCAL_PATH)/source/extinst.$(2).grammar.json \
- --vendor-insts-output=$(1)/$(2).insts.inc
+ --vendor-insts-output=$(1)/$(2).insts.inc \
+ --vendor-operand-kind-prefix=$(3)
@echo "[$(TARGET_ARCH_ABI)] Vendor extended instruction set: $(2) tables <= grammar"
$(LOCAL_PATH)/source/ext_inst.cpp: $(1)/$(2).insts.inc
endef
-# Vendor extended instruction sets, with grammars from SPIRV-Tools source tree.
-SPV_NONSTANDARD_EXTINST_GRAMMARS=$(foreach F,$(wildcard $(LOCAL_PATH)/source/extinst.*.grammar.json),$(patsubst extinst.%.grammar.json,%,$(notdir $F)))
-$(foreach E,$(SPV_NONSTANDARD_EXTINST_GRAMMARS),$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),$E)))
+# 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),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,""))
+$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-shader-trinary-minmax,""))
define gen_spvtools_enum_string_mapping
$(call generate-file-dir,$(1)/extension_enum.inc.inc)
$(1)/extension_enum.inc $(1)/enum_string_mapping.inc: \
$(LOCAL_PATH)/utils/generate_grammar_tables.py \
- $(SPV_CORELATEST_GRAMMAR)
+ $(SPV_COREUNIFIED1_GRAMMAR)
@$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \
- --spirv-core-grammar=$(SPV_CORELATEST_GRAMMAR) \
+ --spirv-core-grammar=$(SPV_COREUNIFIED1_GRAMMAR) \
--extinst-debuginfo-grammar=$(SPV_DEBUGINFO_GRAMMAR) \
+ --extinst-cldebuginfo100-grammar=$(SPV_CLDEBUGINFO100_GRAMMAR) \
--extension-enum-output=$(1)/extension_enum.inc \
--enum-string-mapping-output=$(1)/enum_string_mapping.inc
@echo "[$(TARGET_ARCH_ABI)] Generate enum<->string mapping <= grammar JSON files"
diff --git a/BUILD.bazel b/BUILD.bazel
index dc1ca60..3046781 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -2,6 +2,7 @@
":build_defs.bzl",
"COMMON_COPTS",
"DEBUGINFO_GRAMMAR_JSON_FILE",
+ "CLDEBUGINFO100_GRAMMAR_JSON_FILE",
"TEST_COPTS",
"base_test",
"generate_core_tables",
@@ -38,12 +39,6 @@
srcs = ["utils/generate_language_headers.py"],
)
-generate_core_tables("1.0")
-
-generate_core_tables("1.1")
-
-generate_core_tables("1.2")
-
generate_core_tables("unified1")
generate_enum_string_mapping("unified1")
@@ -62,8 +57,12 @@
generate_vendor_tables("debuginfo")
+generate_vendor_tables("opencl.debuginfo.100", "CLDEBUG100_")
+
generate_extinst_lang_headers("DebugInfo", DEBUGINFO_GRAMMAR_JSON_FILE)
+generate_extinst_lang_headers("OpenCLDebugInfo100", CLDEBUGINFO100_GRAMMAR_JSON_FILE)
+
py_binary(
name = "generate_registry_tables",
srcs = ["utils/generate_registry_tables.py"],
@@ -96,16 +95,15 @@
name = "generated_headers",
hdrs = [
":gen_build_version",
- ":gen_core_tables_1.0",
- ":gen_core_tables_1.1",
- ":gen_core_tables_1.2",
":gen_core_tables_unified1",
":gen_enum_string_mapping",
":gen_extinst_lang_headers_DebugInfo",
+ ":gen_extinst_lang_headers_OpenCLDebugInfo100",
":gen_glsl_tables_unified1",
":gen_opencl_tables_unified1",
":gen_registry_tables",
":gen_vendor_tables_debuginfo",
+ ":gen_vendor_tables_opencl_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",
diff --git a/BUILD.gn b/BUILD.gn
index 5c8eb9a..3c85c4e 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -32,7 +32,8 @@
"${spirv_headers}/include/spirv/$version/spirv.core.grammar.json"
core_insts_file = "${target_gen_dir}/core.insts-$version.inc"
operand_kinds_file = "${target_gen_dir}/operand.kinds-$version.inc"
- extinst_file = "source/extinst.debuginfo.grammar.json"
+ debuginfo_insts_file = "source/extinst.debuginfo.grammar.json"
+ cldebuginfo100_insts_file = "source/extinst.opencl.debuginfo.100.grammar.json"
sources = [
core_json_file,
@@ -47,7 +48,9 @@
"--core-insts-output",
rebase_path(core_insts_file, root_build_dir),
"--extinst-debuginfo-grammar",
- rebase_path(extinst_file, root_build_dir),
+ rebase_path(debuginfo_insts_file, root_build_dir),
+ "--extinst-cldebuginfo100-grammar",
+ rebase_path(cldebuginfo100_insts_file, root_build_dir),
"--operand-kinds-output",
rebase_path(operand_kinds_file, root_build_dir),
]
@@ -64,7 +67,8 @@
core_json_file =
"${spirv_headers}/include/spirv/$version/spirv.core.grammar.json"
- debug_insts_file = "source/extinst.debuginfo.grammar.json"
+ debuginfo_insts_file = "source/extinst.debuginfo.grammar.json"
+ cldebuginfo100_insts_file = "source/extinst.opencl.debuginfo.100.grammar.json"
extension_enum_file = "${target_gen_dir}/extension_enum.inc"
extension_map_file = "${target_gen_dir}/enum_string_mapping.inc"
@@ -72,7 +76,9 @@
"--spirv-core-grammar",
rebase_path(core_json_file, root_build_dir),
"--extinst-debuginfo-grammar",
- rebase_path(debug_insts_file, root_build_dir),
+ rebase_path(debuginfo_insts_file, root_build_dir),
+ "--extinst-cldebuginfo100-grammar",
+ rebase_path(cldebuginfo100_insts_file, root_build_dir),
"--extension-enum-output",
rebase_path(extension_enum_file, root_build_dir),
"--enum-string-mapping-output",
@@ -100,7 +106,6 @@
"${spirv_headers}/include/spirv/$version/spirv.core.grammar.json"
glsl_json_file = "${spirv_headers}/include/spirv/${version}/extinst.glsl.std.450.grammar.json"
glsl_insts_file = "${target_gen_dir}/glsl.std.450.insts.inc"
- debug_insts_file = "source/extinst.debuginfo.grammar.json"
args = [
"--spirv-core-grammar",
@@ -109,8 +114,6 @@
rebase_path(glsl_json_file, root_build_dir),
"--glsl-insts-output",
rebase_path(glsl_insts_file, root_build_dir),
- "--extinst-debuginfo-grammar",
- rebase_path(debug_insts_file, root_build_dir),
]
inputs = [
core_json_file,
@@ -134,7 +137,6 @@
"${spirv_headers}/include/spirv/$version/spirv.core.grammar.json"
opengl_json_file = "${spirv_headers}/include/spirv/${version}/extinst.opencl.std.100.grammar.json"
opencl_insts_file = "${target_gen_dir}/opencl.std.insts.inc"
- debug_insts_file = "source/extinst.debuginfo.grammar.json"
args = [
"--spirv-core-grammar",
@@ -143,8 +145,6 @@
rebase_path(opengl_json_file, root_build_dir),
"--opencl-insts-output",
rebase_path(opencl_insts_file, root_build_dir),
- "--extinst-debuginfo-grammar",
- rebase_path(debug_insts_file, root_build_dir),
]
inputs = [
core_json_file,
@@ -164,13 +164,12 @@
name = invoker.name
extinst_output_base = "${target_gen_dir}/${name}"
- debug_insts_file = "source/extinst.debuginfo.grammar.json"
args = [
"--extinst-name",
"${name}",
"--extinst-grammar",
- rebase_path(debug_insts_file, root_build_dir),
+ rebase_path(invoker.grammar_file, root_build_dir),
"--extinst-output-base",
rebase_path(extinst_output_base, root_build_dir),
]
@@ -198,6 +197,8 @@
rebase_path(extinst_vendor_grammar, root_build_dir),
"--vendor-insts-output",
rebase_path(extinst_file, root_build_dir),
+ "--vendor-operand-kind-prefix",
+ invoker.operand_kind_prefix
]
inputs = [
extinst_vendor_grammar,
@@ -256,21 +257,28 @@
spvtools_opencl_tables("opencl1-0") {
version = "1.0"
}
-spvtools_language_header("unified1") {
+spvtools_language_header("debuginfo") {
name = "DebugInfo"
+ grammar_file = "source/extinst.debuginfo.grammar.json"
+}
+spvtools_language_header("cldebuginfo100") {
+ name = "OpenCLDebugInfo100"
+ grammar_file = "source/extinst.opencl.debuginfo.100.grammar.json"
}
spvtools_vendor_tables = [
- "spv-amd-shader-explicit-vertex-parameter",
- "spv-amd-shader-trinary-minmax",
- "spv-amd-gcn-shader",
- "spv-amd-shader-ballot",
- "debuginfo",
+ ["spv-amd-shader-explicit-vertex-parameter", ""],
+ ["spv-amd-shader-trinary-minmax", ""],
+ ["spv-amd-gcn-shader", ""],
+ ["spv-amd-shader-ballot", ""],
+ ["debuginfo", ""],
+ ["opencl.debuginfo.100", "CLDEBUG100_"],
]
-foreach(table, spvtools_vendor_tables) {
+foreach(table_def, spvtools_vendor_tables) {
spvtools_vendor_table(table) {
- name = table
+ name = table_def[0]
+ operand_kind_prefix = table_def[1]
}
}
@@ -320,10 +328,12 @@
":spvtools_core_tables_unified1",
":spvtools_generators_inc",
":spvtools_glsl_tables_glsl1-0",
- ":spvtools_language_header_unified1",
+ ":spvtools_language_header_debuginfo",
+ ":spvtools_language_header_cldebuginfo100",
":spvtools_opencl_tables_opencl1-0",
]
- foreach(target_name, spvtools_vendor_tables) {
+ foreach(table_def, spvtools_vendor_tables) {
+ target_name = table_def[0]
deps += [ ":spvtools_vendor_tables_$target_name" ]
}
@@ -818,6 +828,7 @@
"test/ext_inst.debuginfo_test.cpp",
"test/ext_inst.glsl_test.cpp",
"test/ext_inst.opencl_test.cpp",
+ "test/ext_inst.cldebug100_test.cpp",
"test/fix_word_test.cpp",
"test/generator_magic_number_test.cpp",
"test/hex_float_test.cpp",
@@ -864,7 +875,8 @@
deps = [
":spvtools",
- ":spvtools_language_header_unified1",
+ ":spvtools_language_header_debuginfo",
+ ":spvtools_language_header_cldebuginfo100",
":spvtools_val",
"//testing/gmock",
"//testing/gtest",
diff --git a/CHANGES b/CHANGES
index 5263f12..2f2968e 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,9 @@
Revision history for SPIRV-Tools
-v2019.5-dev 2019-10-21
+v2020.1-dev 2019-12-11
+ - Start v2020.1-dev
+
+v2019.5 2019-12-11
- General:
- Export SPIRV-Tools targets on installation
- SPIRV-Tools support for SPIR-V 1.5 (#2865)
@@ -12,6 +15,12 @@
- Improved CMake install step. (#2963)
- Add fuzzer for spirv-dis call path (#2977)
- Ensure timestamp does not vary with timezone. (#2982)
+ - Add a vscode extension for SPIR-V disassembly files (#2987)
+ - Add iOS as a supported platform (#3001)
+ - utils/vscode: Add SPIR-V language server support
+ - Respect CMAKE_INSTALL_LIBDIR in installed CMake files (#3054)
+ - Permit the debug instructions in WebGPU SPIR-V (#3063)
+ - Add support for Fuchsia. (#3062)
- Optimizer
- Add descriptor array scalar replacement (#2742)
- Add pass to wrap OpKill in a function call (#2790)
@@ -24,6 +33,10 @@
- Better handling of OpKill in continues (#2842,#2922,#2933)
- Enable OpTypeCooperativeMatrix specialization (#2927)
- Support constant-folding UConvert and SConvert (#2960)
+ - Update Offset to ConstOffset bitmask if operand is constant. (#3024)
+ - Improve RegisterSizePasses (#3059)
+ - Folding: perform add and sub on mismatched integer types (#3084)
+ - Graphics robust access: use signed clamp (#3073)
Fixes:
- Instrument: Fix version 2 output record write for tess eval shaders. (#2782)
- Instrument: Add support for Buffer Device Address extension (#2792)
@@ -37,10 +50,12 @@
- Adding valilidation checks for OpEntryPoint duplicate names and execution mode (#2862)
- Relaxed bitcast with pointers (#2878)
- Validate physical storage buffer restrictions (#2930)
- - Add SPV_KHR_shader_clock validation (#2879)
+ - Add SPV_KHR_shader_clock validation (#2879, #3013)
- Validate that selections are structured (#2962)
- Disallow use of OpCompositeExtract/OpCompositeInsert with no indices (#2980)
- Check that derivatives operate on 32-bit values (#2983)
+ - Validate array stride does not cause overlap (#3028)
+ - Validate nested constructs (#3068)
Fixes:
- Fix validation of constant matrices (#2794)
- Update "remquor" validation
@@ -63,6 +78,12 @@
- Add fuzzer pass to change loop controls (#2949)
- Add fuzzer pass to change function controls (#2951)
- Add fuzzer pass to add NoContraction decorations (#2950)
+ - Add missing functionality for matrix composites (#2974)
+ - Fuzzer pass to adjust memory access operands (#2968)
+ - Transformation to extract from a composite object (#2991)
+ - Vector shuffle transformation (#3015)
+ - Improve debugging facilities (#3074)
+ - Function outlining fuzzer pass (#3078)
v2019.4 2019-08-08
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 19108f3..6ed56a8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -29,7 +29,6 @@
set(SPIRV_TOOLS "SPIRV-Tools")
include(GNUInstallDirs)
-include(cmake/setup_build.cmake)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_CXX_STANDARD 11)
@@ -54,6 +53,8 @@
set(SPIRV_TIMER_ENABLED ${SPIRV_ALLOW_TIMERS})
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD")
add_definitions(-DSPIRV_FREEBSD)
+elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Fuchsia")
+ add_definitions(-DSPIRV_FUCHSIA)
else()
message(FATAL_ERROR "Your platform '${CMAKE_SYSTEM_NAME}' is not supported!")
endif()
@@ -215,7 +216,7 @@
endmacro()
else()
macro(spvtools_config_package_dir TARGET PATH)
- set(${PATH} lib/cmake/${TARGET})
+ set(${PATH} ${CMAKE_INSTALL_LIBDIR}/cmake/${TARGET})
endmacro()
endif()
diff --git a/build_defs.bzl b/build_defs.bzl
index 483fd2a..5d913a1 100644
--- a/build_defs.bzl
+++ b/build_defs.bzl
@@ -40,6 +40,7 @@
})
DEBUGINFO_GRAMMAR_JSON_FILE = "source/extinst.debuginfo.grammar.json"
+CLDEBUGINFO100_GRAMMAR_JSON_FILE = "source/extinst.opencl.debuginfo.100.grammar.json"
def generate_core_tables(version = None):
if not version:
@@ -47,6 +48,7 @@
grammars = [
"@spirv_headers//:spirv_core_grammar_" + version,
DEBUGINFO_GRAMMAR_JSON_FILE,
+ CLDEBUGINFO100_GRAMMAR_JSON_FILE,
]
outs = [
"core.insts-{}.inc".format(version),
@@ -61,8 +63,9 @@
"$(location :generate_grammar_tables) " +
"--spirv-core-grammar=$(location {0}) " +
"--extinst-debuginfo-grammar=$(location {1}) " +
- "--core-insts-output=$(location {2}) " +
- "--operand-kinds-output=$(location {3})"
+ "--extinst-cldebuginfo100-grammar=$(location {2}) " +
+ "--core-insts-output=$(location {3}) " +
+ "--operand-kinds-output=$(location {4})"
).format(*fmtargs),
tools = [":generate_grammar_tables"],
visibility = ["//visibility:private"],
@@ -74,6 +77,7 @@
grammars = [
"@spirv_headers//:spirv_core_grammar_" + version,
DEBUGINFO_GRAMMAR_JSON_FILE,
+ CLDEBUGINFO100_GRAMMAR_JSON_FILE,
]
outs = [
"extension_enum.inc",
@@ -88,8 +92,9 @@
"$(location :generate_grammar_tables) " +
"--spirv-core-grammar=$(location {0}) " +
"--extinst-debuginfo-grammar=$(location {1}) " +
- "--extension-enum-output=$(location {2}) " +
- "--enum-string-mapping-output=$(location {3})"
+ "--extinst-cldebuginfo100-grammar=$(location {2}) " +
+ "--extension-enum-output=$(location {3}) " +
+ "--enum-string-mapping-output=$(location {4})"
).format(*fmtargs),
tools = [":generate_grammar_tables"],
visibility = ["//visibility:private"],
@@ -137,13 +142,14 @@
visibility = ["//visibility:private"],
)
-def generate_vendor_tables(extension = None):
+def generate_vendor_tables(extension, operand_kind_prefix = ""):
if not extension:
fail("Must specify extension", "extension")
- extension_rule = extension.replace("-", "_")
+ extension_rule = extension.replace("-", "_").replace(".", "_")
grammars = ["source/extinst.{}.grammar.json".format(extension)]
outs = ["{}.insts.inc".format(extension)]
- fmtargs = grammars + outs
+ prefices = [operand_kind_prefix]
+ fmtargs = grammars + outs + prefices
native.genrule(
name = "gen_vendor_tables_" + extension_rule,
srcs = grammars,
@@ -151,7 +157,8 @@
cmd = (
"$(location :generate_grammar_tables) " +
"--extinst-vendor-grammar=$(location {0}) " +
- "--vendor-insts-output=$(location {1})"
+ "--vendor-insts-output=$(location {1}) " +
+ "--vendor-operand-kind-prefix={2}"
).format(*fmtargs),
tools = [":generate_grammar_tables"],
visibility = ["//visibility:private"],
diff --git a/cmake/setup_build.cmake b/cmake/setup_build.cmake
deleted file mode 100644
index 6ba4c53..0000000
--- a/cmake/setup_build.cmake
+++ /dev/null
@@ -1,20 +0,0 @@
-# Find nosetests; see spirv_add_nosetests() for opting in to nosetests in a
-# specific directory.
-find_program(NOSETESTS_EXE NAMES nosetests PATHS $ENV{PYTHON_PACKAGE_PATH})
-if (NOT NOSETESTS_EXE)
- message(STATUS "SPIRV-Tools: nosetests was not found - python support code will not be tested")
-else()
- message(STATUS "SPIRV-Tools: nosetests found - python support code will be tested")
-endif()
-
-# Run nosetests on file ${PREFIX}_nosetest.py. Nosetests will look for classes
-# and functions whose names start with "nosetest". The test name will be
-# ${PREFIX}_nosetests.
-function(spirv_add_nosetests PREFIX)
- if(NOT "${SPIRV_SKIP_TESTS}" AND NOSETESTS_EXE)
- add_test(
- NAME ${PREFIX}_nosetests
- COMMAND ${NOSETESTS_EXE} -m "^[Nn]ose[Tt]est" -v
- ${CMAKE_CURRENT_SOURCE_DIR}/${PREFIX}_nosetest.py)
- endif()
-endfunction()
diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt
index 3190f4b..8bde13c 100644
--- a/external/CMakeLists.txt
+++ b/external/CMakeLists.txt
@@ -108,6 +108,9 @@
set(protobuf_BUILD_TESTS OFF CACHE BOOL "Disable protobuf tests")
set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "Do not build protobuf static runtime")
if (IS_DIRECTORY ${PROTOBUF_DIR})
+ if (${CMAKE_CXX_COMPILER_ID} MATCHES Clang)
+ add_definitions(-Wno-inconsistent-missing-override)
+ endif()
add_subdirectory(${PROTOBUF_DIR} EXCLUDE_FROM_ALL)
else()
message(FATAL_ERROR
diff --git a/include/spirv-tools/instrument.hpp b/include/spirv-tools/instrument.hpp
index 681d008..2dcb333 100644
--- a/include/spirv-tools/instrument.hpp
+++ b/include/spirv-tools/instrument.hpp
@@ -35,10 +35,9 @@
// generated by InstrumentPass::GenDebugStreamWrite. This method is utilized
// by InstBindlessCheckPass.
//
-// kInst2* values support version 2 of the output record format. These should
-// be used if available and version 2 is enabled. Version 1 is DEPRECATED.
-// Specifically, version 1 uses two words for the stage-specific section of
-// the output record; version 2 uses three words.
+// kInst2* values support version 2 of the output record format and were used
+// for the transition to this format. These values have now been transferred
+// to the original kInst* values. The kInst2* values are therefore DEPRECATED.
//
// The first member of the debug output buffer contains the next available word
// in the data stream to be written. Shaders will atomically read and update
@@ -124,7 +123,7 @@
static const int kInstRayTracingOutLaunchIdZ = kInstCommonOutCnt + 2;
// Size of Common and Stage-specific Members
-static const int kInstStageOutCnt = kInstCommonOutCnt + 2;
+static const int kInstStageOutCnt = kInstCommonOutCnt + 3;
static const int kInst2StageOutCnt = kInstCommonOutCnt + 3;
// Validation Error Code Offset
@@ -160,6 +159,10 @@
// A buffer address unalloc error will output the 64-bit pointer in
// two 32-bit pieces, lower bits first.
+static const int kInstBuffAddrUnallocOutDescPtrLo = kInstStageOutCnt + 1;
+static const int kInstBuffAddrUnallocOutDescPtrHi = kInstStageOutCnt + 2;
+static const int kInstBuffAddrUnallocOutCnt = kInstStageOutCnt + 3;
+
static const int kInst2BuffAddrUnallocOutDescPtrLo = kInst2StageOutCnt + 1;
static const int kInst2BuffAddrUnallocOutDescPtrHi = kInst2StageOutCnt + 2;
static const int kInst2BuffAddrUnallocOutCnt = kInst2StageOutCnt + 3;
diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h
index dd2526b..5dcb81a 100644
--- a/include/spirv-tools/libspirv.h
+++ b/include/spirv-tools/libspirv.h
@@ -225,13 +225,23 @@
// A sequence of zero or more pairs of (Id, Literal integer)
LAST_VARIABLE(SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER),
- // The following are concrete enum types.
+ // The following are concrete enum types from the DebugInfo extended
+ // instruction set.
SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS, // DebugInfo Sec 3.2. A mask.
SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING, // DebugInfo Sec 3.3
SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE, // DebugInfo Sec 3.4
SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER, // DebugInfo Sec 3.5
SPV_OPERAND_TYPE_DEBUG_OPERATION, // DebugInfo Sec 3.6
+ // The following are concrete enum types from the OpenCL.DebugInfo.100
+ // extended instruction set.
+ SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS, // Sec 3.2. A Mask
+ SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING, // Sec 3.3
+ SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE, // Sec 3.4
+ SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER, // Sec 3.5
+ SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION, // Sec 3.6
+ SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY, // Sec 3.7
+
// This is a sentinel value, and does not represent an operand type.
// It should come last.
SPV_OPERAND_TYPE_NUM_OPERAND_TYPES,
@@ -248,6 +258,12 @@
SPV_EXT_INST_TYPE_SPV_AMD_GCN_SHADER,
SPV_EXT_INST_TYPE_SPV_AMD_SHADER_BALLOT,
SPV_EXT_INST_TYPE_DEBUGINFO,
+ SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
+
+ // Multiple distinct extended instruction set types could return this
+ // value, if they are prefixed with NonSemantic. and are otherwise
+ // unrecognised
+ SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN,
SPV_FORCE_32_BIT_ENUM(spv_ext_inst_type_t)
} spv_ext_inst_type_t;
@@ -627,6 +643,11 @@
SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit(
spv_fuzzer_options options, uint32_t shrinker_step_limit);
+// Enables running the validator after every pass is applied during a fuzzing
+// run.
+SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableFuzzerPassValidation(
+ spv_fuzzer_options options);
+
// Encodes the given SPIR-V assembly text to its binary representation. The
// length parameter specifies the number of bytes for text. Encoded binary will
// be stored into *binary. Any error will be written into *diagnostic if
diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp
index b09fdd4..ceadef8 100644
--- a/include/spirv-tools/libspirv.hpp
+++ b/include/spirv-tools/libspirv.hpp
@@ -229,6 +229,11 @@
spvFuzzerOptionsSetShrinkerStepLimit(options_, shrinker_step_limit);
}
+ // See spvFuzzerOptionsEnableFuzzerPassValidation.
+ void enable_fuzzer_pass_validation() {
+ spvFuzzerOptionsEnableFuzzerPassValidation(options_);
+ }
+
private:
spv_fuzzer_options options_;
};
diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp
index 509051d..ba8cbaf 100644
--- a/include/spirv-tools/optimizer.hpp
+++ b/include/spirv-tools/optimizer.hpp
@@ -685,9 +685,9 @@
// any resulting half precision values back to float32 as needed. No variables
// are changed. No image operations are changed.
//
-// Best if run late since it will generate better code with unneeded function
-// scope loads and stores and composite inserts and extracts removed. Also best
-// if followed by instruction simplification, redundancy elimination and DCE.
+// Best if run after function scope store/load and composite operation
+// eliminations are run. Also best if followed by instruction simplification,
+// redundancy elimination and DCE.
Optimizer::PassToken CreateConvertRelaxedToHalfPass();
// Create relax float ops pass.
@@ -748,7 +748,7 @@
// |version| specifies the buffer record format.
Optimizer::PassToken CreateInstBindlessCheckPass(
uint32_t desc_set, uint32_t shader_id, bool input_length_enable = false,
- bool input_init_enable = false, uint32_t version = 1);
+ bool input_init_enable = false, uint32_t version = 2);
// Create a pass to instrument physical buffer address checking
// This pass instruments all physical buffer address references to check that
@@ -827,6 +827,16 @@
// of range. (The module is already invalid if that is the case.)
// - TODO(dneto): The OpImageTexelPointer coordinate component is not 32-bits
// wide.
+//
+// NOTE: Access chain indices are always treated as signed integers. So
+// if an array has a fixed size of more than 2^31 elements, then elements
+// from 2^31 and above are never accessible with a 32-bit index,
+// signed or unsigned. For this case, this pass will clamp the index
+// between 0 and at 2^31-1, inclusive.
+// Similarly, if an array has more then 2^15 element and is accessed with
+// a 16-bit index, then elements from 2^15 and above are not accessible.
+// In this case, the pass will clamp the index between 0 and 2^15-1
+// inclusive.
Optimizer::PassToken CreateGraphicsRobustAccessPass();
// Create descriptor scalar replacement pass.
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index f3b5942..4e7e10c 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -20,6 +20,7 @@
# For now, assume the DebugInfo grammar file is in the current directory.
# It might migrate to SPIRV-Headers.
set(DEBUGINFO_GRAMMAR_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/extinst.debuginfo.grammar.json")
+set(CLDEBUGINFO100_GRAMMAR_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/extinst.opencl.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
@@ -33,9 +34,13 @@
COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT}
--spirv-core-grammar=${GRAMMAR_JSON_FILE}
--extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE}
+ --extinst-cldebuginfo100-grammar=${CLDEBUGINFO100_GRAMMAR_JSON_FILE}
--core-insts-output=${GRAMMAR_INSTS_INC_FILE}
--operand-kinds-output=${GRAMMAR_KINDS_INC_FILE}
- DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${GRAMMAR_JSON_FILE} ${DEBUGINFO_GRAMMAR_JSON_FILE}
+ DEPENDS ${GRAMMAR_PROCESSING_SCRIPT}
+ ${GRAMMAR_JSON_FILE}
+ ${DEBUGINFO_GRAMMAR_JSON_FILE}
+ ${CLDEBUGINFO100_GRAMMAR_JSON_FILE}
COMMENT "Generate info tables for SPIR-V v${CONFIG_VERSION} core instructions and operands.")
list(APPEND OPCODE_CPP_DEPENDS ${GRAMMAR_INSTS_INC_FILE})
list(APPEND OPERAND_CPP_DEPENDS ${GRAMMAR_KINDS_INC_FILE})
@@ -50,9 +55,13 @@
COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT}
--spirv-core-grammar=${GRAMMAR_JSON_FILE}
--extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE}
+ --extinst-cldebuginfo100-grammar=${CLDEBUGINFO100_GRAMMAR_JSON_FILE}
--extension-enum-output=${GRAMMAR_EXTENSION_ENUM_INC_FILE}
--enum-string-mapping-output=${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE}
- DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${GRAMMAR_JSON_FILE} ${DEBUGINFO_GRAMMAR_JSON_FILE}
+ DEPENDS ${GRAMMAR_PROCESSING_SCRIPT}
+ ${GRAMMAR_JSON_FILE}
+ ${DEBUGINFO_GRAMMAR_JSON_FILE}
+ ${CLDEBUGINFO100_GRAMMAR_JSON_FILE}
COMMENT "Generate enum-string mapping for SPIR-V v${CONFIG_VERSION}.")
list(APPEND EXTENSION_H_DEPENDS ${GRAMMAR_EXTENSION_ENUM_INC_FILE})
list(APPEND ENUM_STRING_MAPPING_CPP_DEPENDS ${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE})
@@ -101,13 +110,14 @@
list(APPEND EXTINST_CPP_DEPENDS ${GRAMMAR_INC_FILE})
endmacro(spvtools_opencl_tables)
-macro(spvtools_vendor_tables VENDOR_TABLE SHORT_NAME)
+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-tools_SOURCE_DIR}/source/extinst.${VENDOR_TABLE}.grammar.json")
add_custom_command(OUTPUT ${INSTS_FILE}
COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT}
--extinst-vendor-grammar=${GRAMMAR_FILE}
--vendor-insts-output=${INSTS_FILE}
+ --vendor-operand-kind-prefix=${OPERAND_KIND_PREFIX}
DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${GRAMMAR_FILE}
COMMENT "Generate extended instruction tables for ${VENDOR_TABLE}.")
add_custom_target(spv-tools-${SHORT_NAME} DEPENDS ${INSTS_FILE})
@@ -134,12 +144,14 @@
spvtools_enum_string_mapping("unified1")
spvtools_opencl_tables("unified1")
spvtools_glsl_tables("unified1")
-spvtools_vendor_tables("spv-amd-shader-explicit-vertex-parameter" "spv-amd-sevp")
-spvtools_vendor_tables("spv-amd-shader-trinary-minmax" "spv-amd-stm")
-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("spv-amd-shader-explicit-vertex-parameter" "spv-amd-sevp" "")
+spvtools_vendor_tables("spv-amd-shader-trinary-minmax" "spv-amd-stm" "")
+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_extinst_lang_headers("DebugInfo" ${DEBUGINFO_GRAMMAR_JSON_FILE})
+spvtools_extinst_lang_headers("OpenCLDebugInfo100" ${CLDEBUGINFO100_GRAMMAR_JSON_FILE})
spvtools_vimsyntax("unified1" "1.0")
add_custom_target(spirv-tools-vimsyntax DEPENDS ${VIMSYNTAX_FILE})
diff --git a/source/binary.cpp b/source/binary.cpp
index 1d31283..0463061 100644
--- a/source/binary.cpp
+++ b/source/binary.cpp
@@ -477,9 +477,22 @@
assert(SpvOpExtInst == opcode);
assert(inst->ext_inst_type != SPV_EXT_INST_TYPE_NONE);
spv_ext_inst_desc ext_inst;
- if (grammar_.lookupExtInst(inst->ext_inst_type, word, &ext_inst))
- return diagnostic() << "Invalid extended instruction number: " << word;
- spvPushOperandTypes(ext_inst->operandTypes, expected_operands);
+ if (grammar_.lookupExtInst(inst->ext_inst_type, word, &ext_inst) ==
+ SPV_SUCCESS) {
+ // if we know about this ext inst, push the expected operands
+ spvPushOperandTypes(ext_inst->operandTypes, expected_operands);
+ } else {
+ // if we don't know this extended instruction and the set isn't
+ // non-semantic, we cannot process further
+ if (!spvExtInstIsNonSemantic(inst->ext_inst_type)) {
+ return diagnostic()
+ << "Invalid extended instruction number: " << word;
+ } else {
+ // for non-semantic instruction sets, we know the form of all such
+ // extended instructions contains a series of IDs as parameters
+ expected_operands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID);
+ }
+ }
} break;
case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: {
@@ -623,7 +636,12 @@
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_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: {
// A single word that is a plain enum value.
// Map an optional operand type to its corresponding concrete type.
@@ -647,6 +665,7 @@
case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
case SPV_OPERAND_TYPE_SELECTION_CONTROL:
+ case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS:
case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: {
// This operand is a mask.
diff --git a/source/disassemble.cpp b/source/disassemble.cpp
index c116f50..4b3972b 100644
--- a/source/disassemble.cpp
+++ b/source/disassemble.cpp
@@ -217,10 +217,18 @@
break;
case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: {
spv_ext_inst_desc ext_inst;
- if (grammar_.lookupExtInst(inst.ext_inst_type, word, &ext_inst))
- assert(false && "should have caught this earlier");
SetRed();
- stream_ << ext_inst->name;
+ 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;
@@ -272,7 +280,12 @@
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_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: {
spv_operand_desc entry;
if (grammar_.lookupOperand(operand.type, word, &entry))
assert(false && "should have caught this earlier");
@@ -285,6 +298,7 @@
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:
diff --git a/source/ext_inst.cpp b/source/ext_inst.cpp
index 0499e23..e332f0d 100644
--- a/source/ext_inst.cpp
+++ b/source/ext_inst.cpp
@@ -28,6 +28,7 @@
#include "debuginfo.insts.inc"
#include "glsl.std.450.insts.inc"
+#include "opencl.debuginfo.100.insts.inc"
#include "opencl.std.insts.inc"
#include "spirv-tools/libspirv.h"
@@ -51,6 +52,8 @@
ARRAY_SIZE(spv_amd_shader_ballot_entries), spv_amd_shader_ballot_entries},
{SPV_EXT_INST_TYPE_DEBUGINFO, ARRAY_SIZE(debuginfo_entries),
debuginfo_entries},
+ {SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
+ ARRAY_SIZE(opencl_debuginfo_100_entries), opencl_debuginfo_100_entries},
};
static const spv_ext_inst_table_t kTable_1_0 = {ARRAY_SIZE(kGroups_1_0),
@@ -116,9 +119,24 @@
if (!strcmp("DebugInfo", name)) {
return SPV_EXT_INST_TYPE_DEBUGINFO;
}
+ if (!strcmp("OpenCL.DebugInfo.100", name)) {
+ return SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100;
+ }
+ // ensure to add any known non-semantic extended instruction sets
+ // above this point, and update spvExtInstIsNonSemantic()
+ if (!strncmp("NonSemantic.", name, 12)) {
+ return SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN;
+ }
return SPV_EXT_INST_TYPE_NONE;
}
+bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type) {
+ if (type == SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN) {
+ return true;
+ }
+ return false;
+}
+
spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table,
const spv_ext_inst_type_t type,
const char* name,
diff --git a/source/ext_inst.h b/source/ext_inst.h
index a821cc2..b42d82b 100644
--- a/source/ext_inst.h
+++ b/source/ext_inst.h
@@ -21,6 +21,9 @@
// Gets the type of the extended instruction set with the specified name.
spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name);
+// Returns true if the extended instruction set is non-semantic
+bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type);
+
// Finds the named extented 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.
diff --git a/source/extinst.opencl.debuginfo.100.grammar.json b/source/extinst.opencl.debuginfo.100.grammar.json
new file mode 100644
index 0000000..08062be
--- /dev/null
+++ b/source/extinst.opencl.debuginfo.100.grammar.json
@@ -0,0 +1,632 @@
+{
+ "copyright" : [
+ "Copyright (c) 2018 The Khronos Group Inc.",
+ "",
+ "Permission is hereby granted, free of charge, to any person obtaining a copy",
+ "of this software and/or associated documentation files (the \"Materials\"),",
+ "to deal in the Materials without restriction, including without limitation",
+ "the rights to use, copy, modify, merge, publish, distribute, sublicense,",
+ "and/or sell copies of the Materials, and to permit persons to whom the",
+ "Materials are furnished to do so, subject to the following conditions:",
+ "",
+ "The above copyright notice and this permission notice shall be included in",
+ "all copies or substantial portions of the Materials.",
+ "",
+ "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS",
+ "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND",
+ "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ",
+ "",
+ "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS",
+ "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
+ "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL",
+ "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
+ "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING",
+ "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS",
+ "IN THE MATERIALS."
+ ],
+ "version" : 200,
+ "revision" : 2,
+ "instructions" : [
+ {
+ "opname" : "DebugInfoNone",
+ "opcode" : 0
+ },
+ {
+ "opname" : "DebugCompilationUnit",
+ "opcode" : 1,
+ "operands" : [
+ { "kind" : "LiteralInteger", "name" : "'Version'" },
+ { "kind" : "LiteralInteger", "name" : "'DWARF Version'" },
+ { "kind" : "IdRef", "name" : "'Source'" },
+ { "kind" : "SourceLanguage", "name" : "'Language'" }
+ ]
+ },
+ {
+ "opname" : "DebugTypeBasic",
+ "opcode" : 2,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Name'" },
+ { "kind" : "IdRef", "name" : "'Size'" },
+ { "kind" : "DebugBaseTypeAttributeEncoding", "name" : "'Encoding'" }
+ ]
+ },
+ {
+ "opname" : "DebugTypePointer",
+ "opcode" : 3,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Base Type'" },
+ { "kind" : "StorageClass", "name" : "'Storage Class'" },
+ { "kind" : "DebugInfoFlags", "name" : "'Flags'" }
+ ]
+ },
+ {
+ "opname" : "DebugTypeQualifier",
+ "opcode" : 4,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Base Type'" },
+ { "kind" : "DebugTypeQualifier", "name" : "'Type Qualifier'" }
+ ]
+ },
+ {
+ "opname" : "DebugTypeArray",
+ "opcode" : 5,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Base Type'" },
+ { "kind" : "IdRef", "name" : "'Component Counts'", "quantifier" : "*" }
+ ]
+ },
+ {
+ "opname" : "DebugTypeVector",
+ "opcode" : 6,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Base Type'" },
+ { "kind" : "LiteralInteger", "name" : "'Component Count'" }
+ ]
+ },
+ {
+ "opname" : "DebugTypedef",
+ "opcode" : 7,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Name'" },
+ { "kind" : "IdRef", "name" : "'Base Type'" },
+ { "kind" : "IdRef", "name" : "'Source'" },
+ { "kind" : "LiteralInteger", "name" : "'Line'" },
+ { "kind" : "LiteralInteger", "name" : "'Column'" },
+ { "kind" : "IdRef", "name" : "'Parent'" }
+ ]
+ },
+ {
+ "opname" : "DebugTypeFunction",
+ "opcode" : 8,
+ "operands" : [
+ { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+ { "kind" : "IdRef", "name" : "'Return Type'" },
+ { "kind" : "IdRef", "name" : "'Parameter Types'", "quantifier" : "*" }
+ ]
+ },
+ {
+ "opname" : "DebugTypeEnum",
+ "opcode" : 9,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Name'" },
+ { "kind" : "IdRef", "name" : "'Underlying Type'" },
+ { "kind" : "IdRef", "name" : "'Source'" },
+ { "kind" : "LiteralInteger", "name" : "'Line'" },
+ { "kind" : "LiteralInteger", "name" : "'Column'" },
+ { "kind" : "IdRef", "name" : "'Parent'" },
+ { "kind" : "IdRef", "name" : "'Size'" },
+ { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+ { "kind" : "PairIdRefIdRef", "name" : "'Value, Name, Value, Name, ...'", "quantifier" : "*" }
+ ]
+ },
+ {
+ "opname" : "DebugTypeComposite",
+ "opcode" : 10,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Name'" },
+ { "kind" : "DebugCompositeType", "name" : "'Tag'" },
+ { "kind" : "IdRef", "name" : "'Source'" },
+ { "kind" : "LiteralInteger", "name" : "'Line'" },
+ { "kind" : "LiteralInteger", "name" : "'Column'" },
+ { "kind" : "IdRef", "name" : "'Parent'" },
+ { "kind" : "IdRef", "name" : "'Linkage Name'" },
+ { "kind" : "IdRef", "name" : "'Size'" },
+ { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+ { "kind" : "IdRef", "name" : "'Members'", "quantifier" : "*" }
+ ]
+ },
+ {
+ "opname" : "DebugTypeMember",
+ "opcode" : 11,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Name'" },
+ { "kind" : "IdRef", "name" : "'Type'" },
+ { "kind" : "IdRef", "name" : "'Source'" },
+ { "kind" : "LiteralInteger", "name" : "'Line'" },
+ { "kind" : "LiteralInteger", "name" : "'Column'" },
+ { "kind" : "IdRef", "name" : "'Parent'" },
+ { "kind" : "IdRef", "name" : "'Offset'" },
+ { "kind" : "IdRef", "name" : "'Size'" },
+ { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+ { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" }
+ ]
+ },
+ {
+ "opname" : "DebugTypeInheritance",
+ "opcode" : 12,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Child'" },
+ { "kind" : "IdRef", "name" : "'Parent'" },
+ { "kind" : "IdRef", "name" : "'Offset'" },
+ { "kind" : "IdRef", "name" : "'Size'" },
+ { "kind" : "DebugInfoFlags", "name" : "'Flags'" }
+ ]
+ },
+ {
+ "opname" : "DebugTypePtrToMember",
+ "opcode" : 13,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Member Type'" },
+ { "kind" : "IdRef", "name" : "'Parent'" }
+ ]
+ },
+ {
+ "opname" : "DebugTypeTemplate",
+ "opcode" : 14,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Target'" },
+ { "kind" : "IdRef", "name" : "'Parameters'", "quantifier" : "*" }
+ ]
+ },
+ {
+ "opname" : "DebugTypeTemplateParameter",
+ "opcode" : 15,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Name'" },
+ { "kind" : "IdRef", "name" : "'Actual Type'" },
+ { "kind" : "IdRef", "name" : "'Value'" },
+ { "kind" : "IdRef", "name" : "'Source'" },
+ { "kind" : "LiteralInteger", "name" : "'Line'" },
+ { "kind" : "LiteralInteger", "name" : "'Column'" }
+ ]
+ },
+ {
+ "opname" : "DebugTypeTemplateTemplateParameter",
+ "opcode" : 16,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Name'" },
+ { "kind" : "IdRef", "name" : "'Template Name'" },
+ { "kind" : "IdRef", "name" : "'Source'" },
+ { "kind" : "LiteralInteger", "name" : "'Line'" },
+ { "kind" : "LiteralInteger", "name" : "'Column'" }
+ ]
+ },
+ {
+ "opname" : "DebugTypeTemplateParameterPack",
+ "opcode" : 17,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Name'" },
+ { "kind" : "IdRef", "name" : "'Source'" },
+ { "kind" : "LiteralInteger", "name" : "'Line'" },
+ { "kind" : "LiteralInteger", "name" : "'Column'" },
+ { "kind" : "IdRef", "name" : "'Template Parameters'", "quantifier" : "*" }
+ ]
+ },
+ {
+ "opname" : "DebugGlobalVariable",
+ "opcode" : 18,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Name'" },
+ { "kind" : "IdRef", "name" : "'Type'" },
+ { "kind" : "IdRef", "name" : "'Source'" },
+ { "kind" : "LiteralInteger", "name" : "'Line'" },
+ { "kind" : "LiteralInteger", "name" : "'Column'" },
+ { "kind" : "IdRef", "name" : "'Parent'" },
+ { "kind" : "IdRef", "name" : "'Linkage Name'" },
+ { "kind" : "IdRef", "name" : "'Variable'" },
+ { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+ { "kind" : "IdRef", "name" : "'Static Member Declaration'", "quantifier" : "?" }
+ ]
+ },
+ {
+ "opname" : "DebugFunctionDeclaration",
+ "opcode" : 19,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Name'" },
+ { "kind" : "IdRef", "name" : "'Type'" },
+ { "kind" : "IdRef", "name" : "'Source'" },
+ { "kind" : "LiteralInteger", "name" : "'Line'" },
+ { "kind" : "LiteralInteger", "name" : "'Column'" },
+ { "kind" : "IdRef", "name" : "'Parent'" },
+ { "kind" : "IdRef", "name" : "'Linkage Name'" },
+ { "kind" : "DebugInfoFlags", "name" : "'Flags'" }
+ ]
+ },
+ {
+ "opname" : "DebugFunction",
+ "opcode" : 20,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Name'" },
+ { "kind" : "IdRef", "name" : "'Type'" },
+ { "kind" : "IdRef", "name" : "'Source'" },
+ { "kind" : "LiteralInteger", "name" : "'Line'" },
+ { "kind" : "LiteralInteger", "name" : "'Column'" },
+ { "kind" : "IdRef", "name" : "'Parent'" },
+ { "kind" : "IdRef", "name" : "'Linkage Name'" },
+ { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+ { "kind" : "LiteralInteger", "name" : "'Scope Line'" },
+ { "kind" : "IdRef", "name" : "'Function'" },
+ { "kind" : "IdRef", "name" : "'Declaration'", "quantifier" : "?" }
+ ]
+ },
+ {
+ "opname" : "DebugLexicalBlock",
+ "opcode" : 21,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Source'" },
+ { "kind" : "LiteralInteger", "name" : "'Line'" },
+ { "kind" : "LiteralInteger", "name" : "'Column'" },
+ { "kind" : "IdRef", "name" : "'Parent'" },
+ { "kind" : "IdRef", "name" : "'Name'", "quantifier" : "?" }
+ ]
+ },
+ {
+ "opname" : "DebugLexicalBlockDiscriminator",
+ "opcode" : 22,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Source'" },
+ { "kind" : "LiteralInteger", "name" : "'Discriminator'" },
+ { "kind" : "IdRef", "name" : "'Parent'" }
+ ]
+ },
+ {
+ "opname" : "DebugScope",
+ "opcode" : 23,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Scope'" },
+ { "kind" : "IdRef", "name" : "'Inlined At'", "quantifier" : "?" }
+ ]
+ },
+ {
+ "opname" : "DebugNoScope",
+ "opcode" : 24
+ },
+ {
+ "opname" : "DebugInlinedAt",
+ "opcode" : 25,
+ "operands" : [
+ { "kind" : "LiteralInteger", "name" : "'Line'" },
+ { "kind" : "IdRef", "name" : "'Scope'" },
+ { "kind" : "IdRef", "name" : "'Inlined'", "quantifier" : "?" }
+ ]
+ },
+ {
+ "opname" : "DebugLocalVariable",
+ "opcode" : 26,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Name'" },
+ { "kind" : "IdRef", "name" : "'Type'" },
+ { "kind" : "IdRef", "name" : "'Source'" },
+ { "kind" : "LiteralInteger", "name" : "'Line'" },
+ { "kind" : "LiteralInteger", "name" : "'Column'" },
+ { "kind" : "IdRef", "name" : "'Parent'" },
+ { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+ { "kind" : "LiteralInteger", "name" : "'Arg Number'", "quantifier" : "?" }
+ ]
+ },
+ {
+ "opname" : "DebugInlinedVariable",
+ "opcode" : 27,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Variable'" },
+ { "kind" : "IdRef", "name" : "'Inlined'" }
+ ]
+ },
+ {
+ "opname" : "DebugDeclare",
+ "opcode" : 28,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Local Variable'" },
+ { "kind" : "IdRef", "name" : "'Variable'" },
+ { "kind" : "IdRef", "name" : "'Expression'" }
+ ]
+ },
+ {
+ "opname" : "DebugValue",
+ "opcode" : 29,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Local Variable'" },
+ { "kind" : "IdRef", "name" : "'Value'" },
+ { "kind" : "IdRef", "name" : "'Expression'" },
+ { "kind" : "IdRef", "name" : "'Indexes'", "quantifier" : "*" }
+ ]
+ },
+ {
+ "opname" : "DebugOperation",
+ "opcode" : 30,
+ "operands" : [
+ { "kind" : "DebugOperation", "name" : "'OpCode'" },
+ { "kind" : "LiteralInteger", "name" : "'Operands ...'", "quantifier" : "*" }
+ ]
+ },
+ {
+ "opname" : "DebugExpression",
+ "opcode" : 31,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Operands ...'", "quantifier" : "*" }
+ ]
+ },
+ {
+ "opname" : "DebugMacroDef",
+ "opcode" : 32,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Source'" },
+ { "kind" : "LiteralInteger", "name" : "'Line'" },
+ { "kind" : "IdRef", "name" : "'Name'" },
+ { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" }
+ ]
+ },
+ {
+ "opname" : "DebugMacroUndef",
+ "opcode" : 33,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Source'" },
+ { "kind" : "LiteralInteger", "name" : "'Line'" },
+ { "kind" : "IdRef", "name" : "'Macro'" }
+ ]
+ },
+ {
+ "opname" : "DebugImportedEntity",
+ "opcode" : 34,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'Name'" },
+ { "kind" : "DebugImportedEntity", "name" : "'Tag'" },
+ { "kind" : "IdRef", "name" : "'Source'" },
+ { "kind" : "IdRef", "name" : "'Entity'" },
+ { "kind" : "LiteralInteger", "name" : "'Line'" },
+ { "kind" : "LiteralInteger", "name" : "'Column'" },
+ { "kind" : "IdRef", "name" : "'Parent'" }
+ ]
+ },
+ {
+ "opname" : "DebugSource",
+ "opcode" : 35,
+ "operands" : [
+ { "kind" : "IdRef", "name" : "'File'" },
+ { "kind" : "IdRef", "name" : "'Text'", "quantifier" : "?" }
+ ]
+ }
+ ],
+ "operand_kinds" : [
+ {
+ "category" : "BitEnum",
+ "kind" : "DebugInfoFlags",
+ "enumerants" : [
+ {
+ "enumerant" : "FlagIsProtected",
+ "value" : "0x01"
+ },
+ {
+ "enumerant" : "FlagIsPrivate",
+ "value" : "0x02"
+ },
+ {
+ "enumerant" : "FlagIsPublic",
+ "value" : "0x03"
+ },
+ {
+ "enumerant" : "FlagIsLocal",
+ "value" : "0x04"
+ },
+ {
+ "enumerant" : "FlagIsDefinition",
+ "value" : "0x08"
+ },
+ {
+ "enumerant" : "FlagFwdDecl",
+ "value" : "0x10"
+ },
+ {
+ "enumerant" : "FlagArtificial",
+ "value" : "0x20"
+ },
+ {
+ "enumerant" : "FlagExplicit",
+ "value" : "0x40"
+ },
+ {
+ "enumerant" : "FlagPrototyped",
+ "value" : "0x80"
+ },
+ {
+ "enumerant" : "FlagObjectPointer",
+ "value" : "0x100"
+ },
+ {
+ "enumerant" : "FlagStaticMember",
+ "value" : "0x200"
+ },
+ {
+ "enumerant" : "FlagIndirectVariable",
+ "value" : "0x400"
+ },
+ {
+ "enumerant" : "FlagLValueReference",
+ "value" : "0x800"
+ },
+ {
+ "enumerant" : "FlagRValueReference",
+ "value" : "0x1000"
+ },
+ {
+ "enumerant" : "FlagIsOptimized",
+ "value" : "0x2000"
+ },
+ {
+ "enumerant" : "FlagIsEnumClass",
+ "value" : "0x4000"
+ },
+ {
+ "enumerant" : "FlagTypePassByValue",
+ "value" : "0x8000"
+ },
+ {
+ "enumerant" : "FlagTypePassByReference",
+ "value" : "0x10000"
+ }
+ ]
+ },
+ {
+ "category" : "ValueEnum",
+ "kind" : "DebugBaseTypeAttributeEncoding",
+ "enumerants" : [
+ {
+ "enumerant" : "Unspecified",
+ "value" : "0"
+ },
+ {
+ "enumerant" : "Address",
+ "value" : "1"
+ },
+ {
+ "enumerant" : "Boolean",
+ "value" : "2"
+ },
+ {
+ "enumerant" : "Float",
+ "value" : "3"
+ },
+ {
+ "enumerant" : "Signed",
+ "value" : "4"
+ },
+ {
+ "enumerant" : "SignedChar",
+ "value" : "5"
+ },
+ {
+ "enumerant" : "Unsigned",
+ "value" : "6"
+ },
+ {
+ "enumerant" : "UnsignedChar",
+ "value" : "7"
+ }
+ ]
+ },
+ {
+ "category" : "ValueEnum",
+ "kind" : "DebugCompositeType",
+ "enumerants" : [
+ {
+ "enumerant" : "Class",
+ "value" : "0"
+ },
+ {
+ "enumerant" : "Structure",
+ "value" : "1"
+ },
+ {
+ "enumerant" : "Union",
+ "value" : "2"
+ }
+ ]
+ },
+ {
+ "category" : "ValueEnum",
+ "kind" : "DebugTypeQualifier",
+ "enumerants" : [
+ {
+ "enumerant" : "ConstType",
+ "value" : "0"
+ },
+ {
+ "enumerant" : "VolatileType",
+ "value" : "1"
+ },
+ {
+ "enumerant" : "RestrictType",
+ "value" : "2"
+ },
+ {
+ "enumerant" : "AtomicType",
+ "value" : "3"
+ }
+ ]
+ },
+ {
+ "category" : "ValueEnum",
+ "kind" : "DebugOperation",
+ "enumerants" : [
+ {
+ "enumerant" : "Deref",
+ "value" : "0"
+ },
+ {
+ "enumerant" : "Plus",
+ "value" : "1"
+ },
+ {
+ "enumerant" : "Minus",
+ "value" : "2"
+ },
+ {
+ "enumerant" : "PlusUconst",
+ "value" : "3",
+ "parameters" : [
+ { "kind" : "LiteralInteger" }
+ ]
+ },
+ {
+ "enumerant" : "BitPiece",
+ "value" : "4",
+ "parameters" : [
+ { "kind" : "LiteralInteger" },
+ { "kind" : "LiteralInteger" }
+ ]
+ },
+ {
+ "enumerant" : "Swap",
+ "value" : "5"
+ },
+ {
+ "enumerant" : "Xderef",
+ "value" : "6"
+ },
+ {
+ "enumerant" : "StackValue",
+ "value" : "7"
+ },
+ {
+ "enumerant" : "Constu",
+ "value" : "8",
+ "parameters" : [
+ { "kind" : "LiteralInteger" }
+ ]
+ },
+ {
+ "enumerant" : "Fragment",
+ "value" : "9",
+ "parameters" : [
+ { "kind" : "LiteralInteger" },
+ { "kind" : "LiteralInteger" }
+ ]
+ }
+ ]
+ },
+ {
+ "category" : "ValueEnum",
+ "kind" : "DebugImportedEntity",
+ "enumerants" : [
+ {
+ "enumerant" : "ImportedModule",
+ "value" : "0"
+ },
+ {
+ "enumerant" : "ImportedDeclaration",
+ "value" : "1"
+ }
+ ]
+ }
+ ]
+}
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index 97f8976..bc7d453 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -47,12 +47,15 @@
fuzzer_pass_apply_id_synonyms.h
fuzzer_pass_construct_composites.h
fuzzer_pass_copy_objects.h
+ fuzzer_pass_merge_blocks.h
fuzzer_pass_obfuscate_constants.h
+ fuzzer_pass_outline_functions.h
fuzzer_pass_permute_blocks.h
fuzzer_pass_split_blocks.h
fuzzer_util.h
id_use_descriptor.h
instruction_descriptor.h
+ instruction_message.h
protobufs/spirvfuzz_protobufs.h
pseudo_random_generator.h
random_generator.h
@@ -60,18 +63,29 @@
shrinker.h
transformation.h
transformation_add_constant_boolean.h
+ transformation_add_constant_composite.h
transformation_add_constant_scalar.h
transformation_add_dead_break.h
transformation_add_dead_continue.h
+ transformation_add_function.h
+ transformation_add_global_undef.h
+ transformation_add_global_variable.h
transformation_add_no_contraction_decoration.h
+ transformation_add_type_array.h
transformation_add_type_boolean.h
transformation_add_type_float.h
+ transformation_add_type_function.h
transformation_add_type_int.h
+ transformation_add_type_matrix.h
transformation_add_type_pointer.h
+ transformation_add_type_struct.h
+ transformation_add_type_vector.h
transformation_composite_construct.h
transformation_composite_extract.h
transformation_copy_object.h
+ transformation_merge_blocks.h
transformation_move_block_down.h
+ transformation_outline_function.h
transformation_replace_boolean_constant_with_constant_binary.h
transformation_replace_constant_with_uniform.h
transformation_replace_id_with_synonym.h
@@ -101,30 +115,44 @@
fuzzer_pass_apply_id_synonyms.cpp
fuzzer_pass_construct_composites.cpp
fuzzer_pass_copy_objects.cpp
+ fuzzer_pass_merge_blocks.cpp
fuzzer_pass_obfuscate_constants.cpp
+ fuzzer_pass_outline_functions.cpp
fuzzer_pass_permute_blocks.cpp
fuzzer_pass_split_blocks.cpp
fuzzer_util.cpp
id_use_descriptor.cpp
instruction_descriptor.cpp
+ instruction_message.cpp
pseudo_random_generator.cpp
random_generator.cpp
replayer.cpp
shrinker.cpp
transformation.cpp
transformation_add_constant_boolean.cpp
+ transformation_add_constant_composite.cpp
transformation_add_constant_scalar.cpp
transformation_add_dead_break.cpp
transformation_add_dead_continue.cpp
+ transformation_add_function.cpp
+ transformation_add_global_undef.cpp
+ transformation_add_global_variable.cpp
transformation_add_no_contraction_decoration.cpp
+ transformation_add_type_array.cpp
transformation_add_type_boolean.cpp
transformation_add_type_float.cpp
+ transformation_add_type_function.cpp
transformation_add_type_int.cpp
+ transformation_add_type_matrix.cpp
transformation_add_type_pointer.cpp
+ transformation_add_type_struct.cpp
+ transformation_add_type_vector.cpp
transformation_composite_construct.cpp
transformation_composite_extract.cpp
transformation_copy_object.cpp
+ transformation_merge_blocks.cpp
transformation_move_block_down.cpp
+ transformation_outline_function.cpp
transformation_replace_boolean_constant_with_constant_binary.cpp
transformation_replace_constant_with_uniform.cpp
transformation_replace_id_with_synonym.cpp
diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp
index 01b4258..95913d0 100644
--- a/source/fuzz/fuzzer.cpp
+++ b/source/fuzz/fuzzer.cpp
@@ -31,7 +31,9 @@
#include "source/fuzz/fuzzer_pass_apply_id_synonyms.h"
#include "source/fuzz/fuzzer_pass_construct_composites.h"
#include "source/fuzz/fuzzer_pass_copy_objects.h"
+#include "source/fuzz/fuzzer_pass_merge_blocks.h"
#include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
+#include "source/fuzz/fuzzer_pass_outline_functions.h"
#include "source/fuzz/fuzzer_pass_permute_blocks.h"
#include "source/fuzz/fuzzer_pass_split_blocks.h"
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
@@ -65,13 +67,26 @@
} // namespace
struct Fuzzer::Impl {
- explicit Impl(spv_target_env env) : target_env(env) {}
+ explicit Impl(spv_target_env env, uint32_t random_seed,
+ bool validate_after_each_pass)
+ : target_env(env),
+ seed(random_seed),
+ validate_after_each_fuzzer_pass(validate_after_each_pass) {}
- const spv_target_env target_env; // Target environment.
- MessageConsumer consumer; // Message consumer.
+ bool ApplyPassAndCheckValidity(FuzzerPass* pass,
+ const opt::IRContext& ir_context,
+ const spvtools::SpirvTools& tools) const;
+
+ const spv_target_env target_env; // Target environment.
+ const uint32_t seed; // Seed for random number generator.
+ bool validate_after_each_fuzzer_pass; // Determines whether the validator
+ // should be invoked after every fuzzer pass.
+ MessageConsumer consumer; // Message consumer.
};
-Fuzzer::Fuzzer(spv_target_env env) : impl_(MakeUnique<Impl>(env)) {}
+Fuzzer::Fuzzer(spv_target_env env, uint32_t seed,
+ bool validate_after_each_fuzzer_pass)
+ : impl_(MakeUnique<Impl>(env, seed, validate_after_each_fuzzer_pass)) {}
Fuzzer::~Fuzzer() = default;
@@ -79,10 +94,27 @@
impl_->consumer = std::move(c);
}
+bool Fuzzer::Impl::ApplyPassAndCheckValidity(
+ FuzzerPass* pass, const opt::IRContext& ir_context,
+ const spvtools::SpirvTools& tools) const {
+ pass->Apply();
+ if (validate_after_each_fuzzer_pass) {
+ std::vector<uint32_t> binary_to_validate;
+ ir_context.module()->ToBinary(&binary_to_validate, false);
+ if (!tools.Validate(&binary_to_validate[0], binary_to_validate.size())) {
+ consumer(SPV_MSG_INFO, nullptr, {},
+ "Binary became invalid during fuzzing (set a breakpoint to "
+ "inspect); stopping.");
+ return false;
+ }
+ }
+ return true;
+}
+
Fuzzer::FuzzerResultStatus Fuzzer::Run(
const std::vector<uint32_t>& binary_in,
const protobufs::FactSequence& initial_facts,
- spv_const_fuzzer_options options, std::vector<uint32_t>* binary_out,
+ std::vector<uint32_t>* binary_out,
protobufs::TransformationSequence* transformation_sequence_out) const {
// Check compatibility between the library version being linked with and the
// header files being used.
@@ -108,10 +140,8 @@
impl_->target_env, impl_->consumer, binary_in.data(), binary_in.size());
assert(ir_context);
- // Make a PRNG, either from a given seed or from a random device.
- PseudoRandomGenerator random_generator(
- options->has_random_seed ? options->random_seed
- : static_cast<uint32_t>(std::random_device()()));
+ // Make a PRNG from the seed passed to the fuzzer on creation.
+ PseudoRandomGenerator random_generator(impl_->seed);
// The fuzzer will introduce new ids into the module. The module's id bound
// gives the smallest id that can be used for this purpose. We add an offset
@@ -128,9 +158,13 @@
// Add some essential ingredients to the module if they are not already
// present, such as boolean constants.
- FuzzerPassAddUsefulConstructs(ir_context.get(), &fact_manager,
- &fuzzer_context, transformation_sequence_out)
- .Apply();
+ FuzzerPassAddUsefulConstructs add_useful_constructs(
+ ir_context.get(), &fact_manager, &fuzzer_context,
+ transformation_sequence_out);
+ if (!impl_->ApplyPassAndCheckValidity(&add_useful_constructs, *ir_context,
+ tools)) {
+ return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
+ }
// Apply some semantics-preserving passes.
std::vector<std::unique_ptr<FuzzerPass>> passes;
@@ -150,9 +184,15 @@
MaybeAddPass<FuzzerPassCopyObjects>(&passes, ir_context.get(),
&fact_manager, &fuzzer_context,
transformation_sequence_out);
+ MaybeAddPass<FuzzerPassMergeBlocks>(&passes, ir_context.get(),
+ &fact_manager, &fuzzer_context,
+ transformation_sequence_out);
MaybeAddPass<FuzzerPassObfuscateConstants>(&passes, ir_context.get(),
&fact_manager, &fuzzer_context,
transformation_sequence_out);
+ MaybeAddPass<FuzzerPassOutlineFunctions>(&passes, ir_context.get(),
+ &fact_manager, &fuzzer_context,
+ transformation_sequence_out);
MaybeAddPass<FuzzerPassPermuteBlocks>(&passes, ir_context.get(),
&fact_manager, &fuzzer_context,
transformation_sequence_out);
@@ -168,7 +208,11 @@
(is_first ||
fuzzer_context.ChoosePercentage(kChanceOfApplyingAnotherPass))) {
is_first = false;
- passes[fuzzer_context.RandomIndex(passes)]->Apply();
+ if (!impl_->ApplyPassAndCheckValidity(
+ passes[fuzzer_context.RandomIndex(passes)].get(), *ir_context,
+ tools)) {
+ return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
+ }
}
// Now apply some passes that it does not make sense to apply repeatedly,
@@ -186,15 +230,13 @@
MaybeAddPass<FuzzerPassAdjustSelectionControls>(
&final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
transformation_sequence_out);
+ MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
+ &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
+ transformation_sequence_out);
for (auto& pass : final_passes) {
- pass->Apply();
- }
-
- if (fuzzer_context.ChooseEven()) {
- FuzzerPassAddNoContractionDecorations(ir_context.get(), &fact_manager,
- &fuzzer_context,
- transformation_sequence_out)
- .Apply();
+ if (!impl_->ApplyPassAndCheckValidity(pass.get(), *ir_context, tools)) {
+ return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
+ }
}
// Encode the module as a binary.
diff --git a/source/fuzz/fuzzer.h b/source/fuzz/fuzzer.h
index a257c2a..c1d2dee 100644
--- a/source/fuzz/fuzzer.h
+++ b/source/fuzz/fuzzer.h
@@ -32,11 +32,16 @@
enum class FuzzerResultStatus {
kComplete,
kFailedToCreateSpirvToolsInterface,
+ kFuzzerPassLedToInvalidModule,
kInitialBinaryInvalid,
};
- // Constructs a fuzzer from the given target environment.
- explicit Fuzzer(spv_target_env env);
+ // Constructs a fuzzer from the given target environment |env|. |seed| is a
+ // seed for pseudo-random number generation.
+ // |validate_after_each_fuzzer_pass| controls whether the validator will be
+ // invoked after every fuzzer pass is applied.
+ explicit Fuzzer(spv_target_env env, uint32_t seed,
+ bool validate_after_each_fuzzer_pass);
// Disables copy/move constructor/assignment operations.
Fuzzer(const Fuzzer&) = delete;
@@ -51,14 +56,13 @@
void SetMessageConsumer(MessageConsumer consumer);
// Transforms |binary_in| to |binary_out| by running a number of randomized
- // fuzzer passes, controlled via |options|. Initial facts about the input
- // binary and the context in which it will execute are provided via
- // |initial_facts|. The transformation sequence that was applied is returned
- // via |transformation_sequence_out|.
+ // fuzzer passes. Initial facts about the input binary and the context in
+ // which it will execute are provided via |initial_facts|. The transformation
+ // sequence that was applied is returned via |transformation_sequence_out|.
FuzzerResultStatus Run(
const std::vector<uint32_t>& binary_in,
const protobufs::FactSequence& initial_facts,
- spv_const_fuzzer_options options, std::vector<uint32_t>* binary_out,
+ std::vector<uint32_t>* binary_out,
protobufs::TransformationSequence* transformation_sequence_out) const;
private:
diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp
index 356cb35..98585d9 100644
--- a/source/fuzz/fuzzer_context.cpp
+++ b/source/fuzz/fuzzer_context.cpp
@@ -34,10 +34,12 @@
90};
const std::pair<uint32_t, uint32_t> kChanceOfAdjustingSelectionControl = {20,
90};
-const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfConstructingComposite = {20, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfMergingBlocks = {20, 95};
const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfOutliningFunction = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
@@ -81,10 +83,13 @@
chance_of_constructing_composite_ =
ChooseBetweenMinAndMax(kChanceOfConstructingComposite);
chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject);
+ chance_of_merging_blocks_ = ChooseBetweenMinAndMax(kChanceOfMergingBlocks);
chance_of_moving_block_down_ =
ChooseBetweenMinAndMax(kChanceOfMovingBlockDown);
chance_of_obfuscating_constant_ =
ChooseBetweenMinAndMax(kChanceOfObfuscatingConstant);
+ chance_of_outlining_function_ =
+ ChooseBetweenMinAndMax(kChanceOfOutliningFunction);
chance_of_replacing_id_with_synonym_ =
ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h
index c8242e6..619c131 100644
--- a/source/fuzz/fuzzer_context.h
+++ b/source/fuzz/fuzzer_context.h
@@ -81,10 +81,14 @@
return chance_of_constructing_composite_;
}
uint32_t GetChanceOfCopyingObject() { return chance_of_copying_object_; }
+ uint32_t GetChanceOfMergingBlocks() { return chance_of_merging_blocks_; }
uint32_t GetChanceOfMovingBlockDown() { return chance_of_moving_block_down_; }
uint32_t GetChanceOfObfuscatingConstant() {
return chance_of_obfuscating_constant_;
}
+ uint32_t GetChanceOfOutliningFunction() {
+ return chance_of_outlining_function_;
+ }
uint32_t GetChanceOfReplacingIdWithSynonym() {
return chance_of_replacing_id_with_synonym_;
}
@@ -119,8 +123,10 @@
uint32_t chance_of_adjusting_selection_control_;
uint32_t chance_of_constructing_composite_;
uint32_t chance_of_copying_object_;
+ uint32_t chance_of_merging_blocks_;
uint32_t chance_of_moving_block_down_;
uint32_t chance_of_obfuscating_constant_;
+ uint32_t chance_of_outlining_function_;
uint32_t chance_of_replacing_id_with_synonym_;
uint32_t chance_of_splitting_block_;
diff --git a/source/fuzz/fuzzer_pass_construct_composites.cpp b/source/fuzz/fuzzer_pass_construct_composites.cpp
index 9eb5631..ff0adab 100644
--- a/source/fuzz/fuzzer_pass_construct_composites.cpp
+++ b/source/fuzz/fuzzer_pass_construct_composites.cpp
@@ -148,7 +148,6 @@
transformation.Apply(GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation() =
transformation.ToMessage();
- // Indicate that one instruction was added.
});
}
diff --git a/source/fuzz/fuzzer_pass_merge_blocks.cpp b/source/fuzz/fuzzer_pass_merge_blocks.cpp
new file mode 100644
index 0000000..ca1bfb3
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_merge_blocks.cpp
@@ -0,0 +1,65 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_merge_blocks.h"
+
+#include <vector>
+
+#include "source/fuzz/transformation_merge_blocks.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassMergeBlocks::FuzzerPassMergeBlocks(
+ opt::IRContext* ir_context, FactManager* fact_manager,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassMergeBlocks::~FuzzerPassMergeBlocks() = default;
+
+void FuzzerPassMergeBlocks::Apply() {
+ // First we populate a sequence of transformations that we might consider
+ // applying.
+ std::vector<TransformationMergeBlocks> potential_transformations;
+ // We do this by considering every block of every function.
+ for (auto& function : *GetIRContext()->module()) {
+ for (auto& block : function) {
+ // We probabilistically decide to ignore some blocks.
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfMergingBlocks())) {
+ continue;
+ }
+ // For other blocks, we add a transformation to merge the block into its
+ // predecessor if that transformation would be applicable.
+ TransformationMergeBlocks transformation(block.id());
+ if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
+ potential_transformations.push_back(transformation);
+ }
+ }
+ }
+
+ while (!potential_transformations.empty()) {
+ uint32_t index = GetFuzzerContext()->RandomIndex(potential_transformations);
+ auto transformation = potential_transformations.at(index);
+ potential_transformations.erase(potential_transformations.begin() + index);
+ if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
+ transformation.Apply(GetIRContext(), GetFactManager());
+ *GetTransformations()->add_transformation() = transformation.ToMessage();
+ }
+ }
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_merge_blocks.h b/source/fuzz/fuzzer_pass_merge_blocks.h
new file mode 100644
index 0000000..457e591
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_merge_blocks.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_MERGE_BLOCKS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_MERGE_BLOCKS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A fuzzer pass for merging blocks in the module.
+class FuzzerPassMergeBlocks : public FuzzerPass {
+ public:
+ FuzzerPassMergeBlocks(opt::IRContext* ir_context, FactManager* fact_manager,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassMergeBlocks();
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_MERGE_BLOCKS_H_
diff --git a/source/fuzz/fuzzer_pass_outline_functions.cpp b/source/fuzz/fuzzer_pass_outline_functions.cpp
new file mode 100644
index 0000000..d59c195
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_outline_functions.cpp
@@ -0,0 +1,99 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_outline_functions.h"
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_outline_function.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassOutlineFunctions::FuzzerPassOutlineFunctions(
+ opt::IRContext* ir_context, FactManager* fact_manager,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassOutlineFunctions::~FuzzerPassOutlineFunctions() = default;
+
+void FuzzerPassOutlineFunctions::Apply() {
+ std::vector<opt::Function*> original_functions;
+ for (auto& function : *GetIRContext()->module()) {
+ original_functions.push_back(&function);
+ }
+ for (auto& function : original_functions) {
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfOutliningFunction())) {
+ continue;
+ }
+ std::vector<opt::BasicBlock*> blocks;
+ for (auto& block : *function) {
+ blocks.push_back(&block);
+ }
+ auto entry_block = blocks[GetFuzzerContext()->RandomIndex(blocks)];
+ auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(function);
+ auto postdominator_analysis =
+ GetIRContext()->GetPostDominatorAnalysis(function);
+ std::vector<opt::BasicBlock*> candidate_exit_blocks;
+ for (auto postdominates_entry_block = entry_block;
+ postdominates_entry_block != nullptr;
+ postdominates_entry_block = postdominator_analysis->ImmediateDominator(
+ postdominates_entry_block)) {
+ if (dominator_analysis->Dominates(entry_block,
+ postdominates_entry_block)) {
+ candidate_exit_blocks.push_back(postdominates_entry_block);
+ }
+ }
+ if (candidate_exit_blocks.empty()) {
+ continue;
+ }
+ auto exit_block = candidate_exit_blocks[GetFuzzerContext()->RandomIndex(
+ candidate_exit_blocks)];
+
+ auto region_blocks = TransformationOutlineFunction::GetRegionBlocks(
+ GetIRContext(), entry_block, exit_block);
+ std::map<uint32_t, uint32_t> input_id_to_fresh_id;
+ for (auto id : TransformationOutlineFunction::GetRegionInputIds(
+ GetIRContext(), region_blocks, exit_block)) {
+ input_id_to_fresh_id[id] = GetFuzzerContext()->GetFreshId();
+ }
+ std::map<uint32_t, uint32_t> output_id_to_fresh_id;
+ for (auto id : TransformationOutlineFunction::GetRegionOutputIds(
+ GetIRContext(), region_blocks, exit_block)) {
+ output_id_to_fresh_id[id] = GetFuzzerContext()->GetFreshId();
+ }
+ TransformationOutlineFunction transformation(
+ entry_block->id(), exit_block->id(),
+ /*new_function_struct_return_type_id*/
+ GetFuzzerContext()->GetFreshId(),
+ /*new_function_type_id*/ GetFuzzerContext()->GetFreshId(),
+ /*new_function_id*/ GetFuzzerContext()->GetFreshId(),
+ /*new_function_region_entry_block*/
+ GetFuzzerContext()->GetFreshId(),
+ /*new_caller_result_id*/ GetFuzzerContext()->GetFreshId(),
+ /*new_callee_result_id*/ GetFuzzerContext()->GetFreshId(),
+ /*input_id_to_fresh_id*/ std::move(input_id_to_fresh_id),
+ /*output_id_to_fresh_id*/ std::move(output_id_to_fresh_id));
+ if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
+ transformation.Apply(GetIRContext(), GetFactManager());
+ *GetTransformations()->add_transformation() = transformation.ToMessage();
+ }
+ }
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_outline_functions.h b/source/fuzz/fuzzer_pass_outline_functions.h
new file mode 100644
index 0000000..5448e7d
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_outline_functions.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_OUTLINE_FUNCTIONS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_OUTLINE_FUNCTIONS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A fuzzer pass for outlining single-entry single-exit regions of a control
+// flow graph into their own functions.
+class FuzzerPassOutlineFunctions : public FuzzerPass {
+ public:
+ FuzzerPassOutlineFunctions(
+ opt::IRContext* ir_context, FactManager* fact_manager,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassOutlineFunctions();
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_OUTLINE_FUNCTIONS_H_
diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp
index 4654682..1c39da0 100644
--- a/source/fuzz/fuzzer_util.cpp
+++ b/source/fuzz/fuzzer_util.cpp
@@ -14,6 +14,8 @@
#include "source/fuzz/fuzzer_util.h"
+#include "source/opt/build_module.h"
+
namespace spvtools {
namespace fuzz {
@@ -180,108 +182,6 @@
return block->end();
}
-bool NewEdgeRespectsUseDefDominance(opt::IRContext* context,
- opt::BasicBlock* bb_from,
- opt::BasicBlock* bb_to) {
- assert(bb_from->terminator()->opcode() == SpvOpBranch);
-
- // If there is *already* an edge from |bb_from| to |bb_to|, then adding
- // another edge is fine from a dominance point of view.
- if (bb_from->terminator()->GetSingleWordInOperand(0) == bb_to->id()) {
- return true;
- }
-
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2919): the
- // solution below to determining whether a new edge respects dominance
- // rules is incomplete. Test
- // TransformationAddDeadContinueTest::DISABLED_Miscellaneous6 exposes the
- // problem. In practice, this limitation does not bite too often, and the
- // worst it does is leads to SPIR-V that spirv-val rejects.
-
- // Let us assume that the module being manipulated is valid according to the
- // rules of the SPIR-V language.
- //
- // Suppose that some block Y is dominated by |bb_to| (which includes the case
- // where Y = |bb_to|).
- //
- // Suppose that Y uses an id i that is defined in some other block X.
- //
- // Because the module is valid, X must dominate Y. We are concerned about
- // whether an edge from |bb_from| to |bb_to| could *stop* X from dominating
- // Y.
- //
- // Because |bb_to| dominates Y, a new edge from |bb_from| to |bb_to| can
- // only affect whether X dominates Y if X dominates |bb_to|.
- //
- // So let us assume that X does dominate |bb_to|, so that we have:
- //
- // (X defines i) dominates |bb_to| dominates (Y uses i)
- //
- // The new edge from |bb_from| to |bb_to| will stop the definition of i in X
- // from dominating the use of i in Y exactly when the new edge will stop X
- // from dominating |bb_to|.
- //
- // Now, the block X that we are worried about cannot dominate |bb_from|,
- // because in that case X would still dominate |bb_to| after we add an edge
- // from |bb_from| to |bb_to|.
- //
- // Also, it cannot be that X = |bb_to|, because nothing can stop a block
- // from dominating itself.
- //
- // So we are looking for a block X such that:
- //
- // - X strictly dominates |bb_to|
- // - X does not dominate |bb_from|
- // - X defines an id i
- // - i is used in some block Y
- // - |bb_to| dominates Y
-
- // Walk the dominator tree backwards, starting from the immediate dominator
- // of |bb_to|. We can stop when we find a block that also dominates
- // |bb_from|.
- auto dominator_analysis = context->GetDominatorAnalysis(bb_from->GetParent());
- for (auto dominator = dominator_analysis->ImmediateDominator(bb_to);
- dominator != nullptr &&
- !dominator_analysis->Dominates(dominator, bb_from);
- dominator = dominator_analysis->ImmediateDominator(dominator)) {
- // |dominator| is a candidate for block X in the above description.
- // We now look through the instructions for a candidate instruction i.
- for (auto& inst : *dominator) {
- // Consider all the uses of this instruction.
- if (!context->get_def_use_mgr()->WhileEachUse(
- &inst,
- [bb_to, context, dominator_analysis](
- opt::Instruction* user, uint32_t operand_index) -> bool {
- // If this use is in an OpPhi, we need to check that dominance
- // of the relevant *parent* block is not spoiled. Otherwise we
- // need to check that dominance of the block containing the use
- // is not spoiled.
- opt::BasicBlock* use_block_or_phi_parent =
- user->opcode() == SpvOpPhi
- ? context->cfg()->block(
- user->GetSingleWordOperand(operand_index + 1))
- : context->get_instr_block(user);
-
- // There might not be any relevant block, e.g. if the use is in
- // a decoration; in this case the new edge is unproblematic.
- if (use_block_or_phi_parent == nullptr) {
- return true;
- }
-
- // With reference to the above discussion,
- // |use_block_or_phi_parent| is a candidate for the block Y.
- // If |bb_to| dominates this block, the new edge would be
- // problematic.
- return !dominator_analysis->Dominates(bb_to,
- use_block_or_phi_parent);
- })) {
- return false;
- }
- }
- }
- return true;
-}
-
bool BlockIsReachableInItsFunction(opt::IRContext* context,
opt::BasicBlock* bb) {
auto enclosing_function = bb->GetParent();
@@ -399,6 +299,24 @@
return array_length_constant->GetU32();
}
+bool IsValid(opt::IRContext* context) {
+ std::vector<uint32_t> binary;
+ context->module()->ToBinary(&binary, false);
+ return SpirvTools(context->grammar().target_env()).Validate(binary);
+}
+
+std::unique_ptr<opt::IRContext> CloneIRContext(opt::IRContext* context) {
+ std::vector<uint32_t> binary;
+ context->module()->ToBinary(&binary, false);
+ return BuildModule(context->grammar().target_env(), nullptr, binary.data(),
+ binary.size());
+}
+
+bool IsNonFunctionTypeId(opt::IRContext* ir_context, uint32_t id) {
+ auto type = ir_context->get_type_mgr()->GetType(id);
+ return type && !type->AsFunction();
+}
+
} // namespace fuzzerutil
} // namespace fuzz
diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h
index 1569df0..af3eb1b 100644
--- a/source/fuzz/fuzzer_util.h
+++ b/source/fuzz/fuzzer_util.h
@@ -69,14 +69,6 @@
opt::BasicBlock::iterator GetIteratorForInstruction(
opt::BasicBlock* block, const opt::Instruction* inst);
-// The function determines whether adding an edge from |bb_from| to |bb_to| -
-// is legitimate with respect to the SPIR-V rule that a definition must
-// dominate all of its uses. This is because adding such an edge can change
-// dominance in the control flow graph, potentially making the module invalid.
-bool NewEdgeRespectsUseDefDominance(opt::IRContext* context,
- opt::BasicBlock* bb_from,
- opt::BasicBlock* bb_to);
-
// 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,
@@ -117,6 +109,17 @@
uint32_t GetArraySize(const opt::Instruction& array_type_instruction,
opt::IRContext* context);
+// Returns true if and only if |context| is valid, according to the validator.
+bool IsValid(opt::IRContext* context);
+
+// Returns a clone of |context|, by writing |context| to a binary and then
+// parsing it again.
+std::unique_ptr<opt::IRContext> CloneIRContext(opt::IRContext* context);
+
+// Returns true if and only if |id| is the id of a type that is not a function
+// type.
+bool IsNonFunctionTypeId(opt::IRContext* ir_context, uint32_t id);
+
} // namespace fuzzerutil
} // namespace fuzz
diff --git a/source/fuzz/instruction_message.cpp b/source/fuzz/instruction_message.cpp
new file mode 100644
index 0000000..b217a21
--- /dev/null
+++ b/source/fuzz/instruction_message.cpp
@@ -0,0 +1,69 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/instruction_message.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+protobufs::Instruction MakeInstructionMessage(
+ SpvOp opcode, uint32_t result_type_id, uint32_t result_id,
+ const std::vector<std::pair<uint32_t, std::vector<uint32_t>>>&
+ input_operands) {
+ protobufs::Instruction result;
+ result.set_opcode(opcode);
+ result.set_result_type_id(result_type_id);
+ result.set_result_id(result_id);
+ for (auto& operand : input_operands) {
+ auto operand_message = result.add_input_operand();
+ operand_message->set_operand_type(operand.first);
+ for (auto operand_word : operand.second) {
+ operand_message->add_operand_data(operand_word);
+ }
+ }
+ return result;
+}
+
+std::unique_ptr<opt::Instruction> InstructionFromMessage(
+ opt::IRContext* ir_context,
+ const protobufs::Instruction& instruction_message) {
+ // First, update the module's id bound with respect to the new instruction,
+ // if it has a result id.
+ if (instruction_message.result_id()) {
+ fuzzerutil::UpdateModuleIdBound(ir_context,
+ instruction_message.result_id());
+ }
+ // Now create a sequence of input operands from the input operand data in the
+ // protobuf message.
+ opt::Instruction::OperandList in_operands;
+ for (auto& operand_message : instruction_message.input_operand()) {
+ opt::Operand::OperandData operand_data;
+ for (auto& word : operand_message.operand_data()) {
+ operand_data.push_back(word);
+ }
+ in_operands.push_back(
+ {static_cast<spv_operand_type_t>(operand_message.operand_type()),
+ operand_data});
+ }
+ // Create and return the instruction.
+ return MakeUnique<opt::Instruction>(
+ ir_context, static_cast<SpvOp>(instruction_message.opcode()),
+ instruction_message.result_type_id(), instruction_message.result_id(),
+ in_operands);
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/instruction_message.h b/source/fuzz/instruction_message.h
new file mode 100644
index 0000000..ed339aa
--- /dev/null
+++ b/source/fuzz/instruction_message.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_INSTRUCTION_MESSAGE_H_
+#define SOURCE_FUZZ_INSTRUCTION_MESSAGE_H_
+
+#include <memory>
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/opt/instruction.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Creates an Instruction protobuf message from its component parts.
+protobufs::Instruction MakeInstructionMessage(
+ SpvOp opcode, uint32_t result_type_id, uint32_t result_id,
+ const std::vector<std::pair<uint32_t, std::vector<uint32_t>>>&
+ input_operands);
+
+// Creates and returns an opt::Instruction from protobuf message
+// |instruction_message|, relative to |ir_context|. In the process, the module
+// id bound associated with |ir_context| is updated to be at least as large as
+// the result id (if any) associated with the new instruction.
+std::unique_ptr<opt::Instruction> InstructionFromMessage(
+ opt::IRContext* ir_context,
+ const protobufs::Instruction& instruction_message);
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_INSTRUCTION_MESSAGE_H_
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index b33c2e5..dbd1fb8 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -21,6 +21,16 @@
package spvtools.fuzz.protobufs;
+message UInt32Pair {
+
+ // A pair of uint32s; useful for defining mappings.
+
+ uint32 first = 1;
+
+ uint32 second = 2;
+
+}
+
message InstructionDescriptor {
// Describes an instruction in some block of a function with respect to a
@@ -116,6 +126,37 @@
}
+message InstructionOperand {
+
+ // Represents an operand to a SPIR-V instruction.
+
+ // The type of the operand.
+ uint32 operand_type = 1;
+
+ // The data associated with the operand. For most operands (e.g. ids,
+ // storage classes and literals) this will be a single word.
+ repeated uint32 operand_data = 2;
+
+}
+
+message Instruction {
+
+ // Represents a SPIR-V instruction.
+
+ // The instruction's opcode (e.g. OpLabel).
+ uint32 opcode = 1;
+
+ // The id of the instruction's result type; 0 if there is no result type.
+ uint32 result_type_id = 2;
+
+ // The id of the instruction's result; 0 if there is no result.
+ uint32 result_id = 3;
+
+ // Zero or more input operands.
+ repeated InstructionOperand input_operand = 4;
+
+}
+
message FactSequence {
repeated Fact fact = 1;
}
@@ -190,6 +231,17 @@
TransformationSetMemoryOperandsMask set_memory_operands_mask = 20;
TransformationCompositeExtract composite_extract = 21;
TransformationVectorShuffle vector_shuffle = 22;
+ TransformationOutlineFunction outline_function = 23;
+ TransformationMergeBlocks merge_blocks = 24;
+ TransformationAddTypeVector add_type_vector = 25;
+ TransformationAddTypeArray add_type_array = 26;
+ TransformationAddTypeMatrix add_type_matrix = 27;
+ TransformationAddTypeStruct add_type_struct = 28;
+ TransformationAddTypeFunction add_type_function = 29;
+ TransformationAddConstantComposite add_constant_composite = 30;
+ TransformationAddGlobalVariable add_global_variable = 31;
+ TransformationAddGlobalUndef add_global_undef = 32;
+ TransformationAddFunction add_function = 33;
// Add additional option using the next available number.
}
}
@@ -206,6 +258,21 @@
}
+message TransformationAddConstantComposite {
+
+ // Adds a constant of the given composite type to the module.
+
+ // Fresh id for the composite
+ uint32 fresh_id = 1;
+
+ // A composite type id
+ uint32 type_id = 2;
+
+ // Constituent ids for the composite
+ repeated uint32 constituent_id = 3;
+
+}
+
message TransformationAddConstantScalar {
// Adds a constant of the given scalar type
@@ -262,6 +329,43 @@
}
+message TransformationAddFunction {
+
+ // Adds a SPIR-V function to the module.
+
+ // The series of instructions that comprise the function.
+ repeated Instruction instruction = 1;
+
+}
+
+message TransformationAddGlobalUndef {
+
+ // Adds an undefined value of a given type to the module at global scope.
+
+ // Fresh id for the undefined value
+ uint32 fresh_id = 1;
+
+ // The type of the undefined value
+ uint32 type_id = 2;
+
+}
+
+message TransformationAddGlobalVariable {
+
+ // Adds a global variable of the given type to the module, with Private
+ // storage class and optionally with an initializer.
+
+ // Fresh id for the global variable
+ uint32 fresh_id = 1;
+
+ // The type of the global variable
+ uint32 type_id = 2;
+
+ // Optional initializer; 0 if there is no initializer
+ uint32 initializer_id = 3;
+
+}
+
message TransformationAddNoContractionDecoration {
// Applies OpDecorate NoContraction to the given result id
@@ -271,6 +375,21 @@
}
+message TransformationAddTypeArray {
+
+ // Adds an array type of the given element type and size to the module
+
+ // Fresh id for the array type
+ uint32 fresh_id = 1;
+
+ // The array's element type
+ uint32 element_type_id = 2;
+
+ // The array's size
+ uint32 size_id = 3;
+
+}
+
message TransformationAddTypeBoolean {
// Adds OpTypeBool to the module
@@ -292,6 +411,21 @@
}
+message TransformationAddTypeFunction {
+
+ // Adds a function type to the module
+
+ // Fresh id for the function type
+ uint32 fresh_id = 1;
+
+ // The function's return type
+ uint32 return_type_id = 2;
+
+ // The function's argument types
+ repeated uint32 argument_type_id = 3;
+
+}
+
message TransformationAddTypeInt {
// Adds OpTypeInt to the module with the given width and signedness
@@ -307,6 +441,22 @@
}
+message TransformationAddTypeMatrix {
+
+ // Adds a matrix type to the module
+
+ // Fresh id for the matrix type
+ uint32 fresh_id = 1;
+
+ // The matrix's column type, which must be a floating-point vector (as per
+ // the "data rules" in the SPIR-V specification).
+ uint32 column_type_id = 2;
+
+ // The matrix's column count
+ uint32 column_count = 3;
+
+}
+
message TransformationAddTypePointer {
// Adds OpTypePointer to the module, with the given storage class and base
@@ -323,6 +473,33 @@
}
+message TransformationAddTypeStruct {
+
+ // Adds a struct type to the module
+
+ // Fresh id for the struct type
+ uint32 fresh_id = 1;
+
+ // The struct's member types
+ repeated uint32 member_type_id = 3;
+
+}
+
+message TransformationAddTypeVector {
+
+ // Adds a vector type to the module
+
+ // Fresh id for the vector type
+ uint32 fresh_id = 1;
+
+ // The vector's component type
+ uint32 component_type_id = 2;
+
+ // The vector's component count
+ uint32 component_count = 3;
+
+}
+
message TransformationCompositeConstruct {
// A transformation that introduces an OpCompositeConstruct instruction to
@@ -380,6 +557,16 @@
}
+message TransformationMergeBlocks {
+
+ // A transformation that merges a block with its predecessor.
+
+ // The id of the block that is to be merged with its predecessor; the merged
+ // block will have the *predecessor's* id.
+ uint32 block_id = 1;
+
+}
+
message TransformationMoveBlockDown {
// A transformation that moves a basic block to be one position lower in
@@ -389,6 +576,53 @@
uint32 block_id = 1;
}
+message TransformationOutlineFunction {
+
+ // A transformation that outlines a single-entry single-exit region of a
+ // control flow graph into a separate function, and replaces the region with
+ // a call to that function.
+
+ // Id of the entry block of the single-entry single-exit region to be outlined
+ uint32 entry_block = 1;
+
+ // Id of the exit block of the single-entry single-exit region to be outlined
+ uint32 exit_block = 2;
+
+ // Id of a struct that will store the return values of the new function
+ uint32 new_function_struct_return_type_id = 3;
+
+ // A fresh id for the type of the outlined function
+ uint32 new_function_type_id = 4;
+
+ // A fresh id for the outlined function itself
+ uint32 new_function_id = 5;
+
+ // A fresh id to represent the block in the outlined function that represents
+ // the first block of the outlined region.
+ uint32 new_function_region_entry_block = 6;
+
+ // A fresh id for the result of the OpFunctionCall instruction that will call
+ // the outlined function
+ uint32 new_caller_result_id = 7;
+
+ // A fresh id to capture the return value of the outlined function - the
+ // argument to OpReturn
+ uint32 new_callee_result_id = 8;
+
+ // Ids defined outside the region and used inside the region will become
+ // parameters to the outlined function. This is a mapping from used ids to
+ // fresh parameter ids.
+ repeated UInt32Pair input_id_to_fresh_id = 9;
+
+ // Ids defined inside the region and used outside the region will become
+ // fresh ids defined by the outlined function, which get copied into the
+ // function's struct return value and then copied into their destination ids
+ // by the caller. This is a mapping from original ids to corresponding fresh
+ // ids.
+ repeated UInt32Pair output_id_to_fresh_id = 10;
+
+}
+
message TransformationReplaceBooleanConstantWithConstantBinary {
// A transformation to capture replacing a use of a boolean constant with
diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp
index d8fc92f..1489f85 100644
--- a/source/fuzz/transformation.cpp
+++ b/source/fuzz/transformation.cpp
@@ -17,18 +17,29 @@
#include <cassert>
#include "source/fuzz/transformation_add_constant_boolean.h"
+#include "source/fuzz/transformation_add_constant_composite.h"
#include "source/fuzz/transformation_add_constant_scalar.h"
#include "source/fuzz/transformation_add_dead_break.h"
#include "source/fuzz/transformation_add_dead_continue.h"
+#include "source/fuzz/transformation_add_function.h"
+#include "source/fuzz/transformation_add_global_undef.h"
+#include "source/fuzz/transformation_add_global_variable.h"
#include "source/fuzz/transformation_add_no_contraction_decoration.h"
+#include "source/fuzz/transformation_add_type_array.h"
#include "source/fuzz/transformation_add_type_boolean.h"
#include "source/fuzz/transformation_add_type_float.h"
+#include "source/fuzz/transformation_add_type_function.h"
#include "source/fuzz/transformation_add_type_int.h"
+#include "source/fuzz/transformation_add_type_matrix.h"
#include "source/fuzz/transformation_add_type_pointer.h"
+#include "source/fuzz/transformation_add_type_struct.h"
+#include "source/fuzz/transformation_add_type_vector.h"
#include "source/fuzz/transformation_composite_construct.h"
#include "source/fuzz/transformation_composite_extract.h"
#include "source/fuzz/transformation_copy_object.h"
+#include "source/fuzz/transformation_merge_blocks.h"
#include "source/fuzz/transformation_move_block_down.h"
+#include "source/fuzz/transformation_outline_function.h"
#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
#include "source/fuzz/transformation_replace_id_with_synonym.h"
@@ -51,6 +62,9 @@
case protobufs::Transformation::TransformationCase::kAddConstantBoolean:
return MakeUnique<TransformationAddConstantBoolean>(
message.add_constant_boolean());
+ case protobufs::Transformation::TransformationCase::kAddConstantComposite:
+ return MakeUnique<TransformationAddConstantComposite>(
+ message.add_constant_composite());
case protobufs::Transformation::TransformationCase::kAddConstantScalar:
return MakeUnique<TransformationAddConstantScalar>(
message.add_constant_scalar());
@@ -59,20 +73,39 @@
case protobufs::Transformation::TransformationCase::kAddDeadContinue:
return MakeUnique<TransformationAddDeadContinue>(
message.add_dead_continue());
+ case protobufs::Transformation::TransformationCase::kAddFunction:
+ return MakeUnique<TransformationAddFunction>(message.add_function());
+ case protobufs::Transformation::TransformationCase::kAddGlobalUndef:
+ return MakeUnique<TransformationAddGlobalUndef>(
+ message.add_global_undef());
+ case protobufs::Transformation::TransformationCase::kAddGlobalVariable:
+ return MakeUnique<TransformationAddGlobalVariable>(
+ message.add_global_variable());
case protobufs::Transformation::TransformationCase::
kAddNoContractionDecoration:
return MakeUnique<TransformationAddNoContractionDecoration>(
message.add_no_contraction_decoration());
+ case protobufs::Transformation::TransformationCase::kAddTypeArray:
+ return MakeUnique<TransformationAddTypeArray>(message.add_type_array());
case protobufs::Transformation::TransformationCase::kAddTypeBoolean:
return MakeUnique<TransformationAddTypeBoolean>(
message.add_type_boolean());
case protobufs::Transformation::TransformationCase::kAddTypeFloat:
return MakeUnique<TransformationAddTypeFloat>(message.add_type_float());
+ case protobufs::Transformation::TransformationCase::kAddTypeFunction:
+ return MakeUnique<TransformationAddTypeFunction>(
+ message.add_type_function());
case protobufs::Transformation::TransformationCase::kAddTypeInt:
return MakeUnique<TransformationAddTypeInt>(message.add_type_int());
+ case protobufs::Transformation::TransformationCase::kAddTypeMatrix:
+ return MakeUnique<TransformationAddTypeMatrix>(message.add_type_matrix());
case protobufs::Transformation::TransformationCase::kAddTypePointer:
return MakeUnique<TransformationAddTypePointer>(
message.add_type_pointer());
+ case protobufs::Transformation::TransformationCase::kAddTypeStruct:
+ return MakeUnique<TransformationAddTypeStruct>(message.add_type_struct());
+ case protobufs::Transformation::TransformationCase::kAddTypeVector:
+ return MakeUnique<TransformationAddTypeVector>(message.add_type_vector());
case protobufs::Transformation::TransformationCase::kCompositeConstruct:
return MakeUnique<TransformationCompositeConstruct>(
message.composite_construct());
@@ -81,8 +114,13 @@
message.composite_extract());
case protobufs::Transformation::TransformationCase::kCopyObject:
return MakeUnique<TransformationCopyObject>(message.copy_object());
+ case protobufs::Transformation::TransformationCase::kMergeBlocks:
+ return MakeUnique<TransformationMergeBlocks>(message.merge_blocks());
case protobufs::Transformation::TransformationCase::kMoveBlockDown:
return MakeUnique<TransformationMoveBlockDown>(message.move_block_down());
+ case protobufs::Transformation::TransformationCase::kOutlineFunction:
+ return MakeUnique<TransformationOutlineFunction>(
+ message.outline_function());
case protobufs::Transformation::TransformationCase::
kReplaceBooleanConstantWithConstantBinary:
return MakeUnique<TransformationReplaceBooleanConstantWithConstantBinary>(
diff --git a/source/fuzz/transformation_add_constant_composite.cpp b/source/fuzz/transformation_add_constant_composite.cpp
new file mode 100644
index 0000000..7ba1ea4
--- /dev/null
+++ b/source/fuzz/transformation_add_constant_composite.cpp
@@ -0,0 +1,130 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_constant_composite.h"
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddConstantComposite::TransformationAddConstantComposite(
+ const spvtools::fuzz::protobufs::TransformationAddConstantComposite&
+ message)
+ : message_(message) {}
+
+TransformationAddConstantComposite::TransformationAddConstantComposite(
+ uint32_t fresh_id, uint32_t type_id,
+ const std::vector<uint32_t>& constituent_ids) {
+ message_.set_fresh_id(fresh_id);
+ message_.set_type_id(type_id);
+ for (auto constituent_id : constituent_ids) {
+ message_.add_constituent_id(constituent_id);
+ }
+}
+
+bool TransformationAddConstantComposite::IsApplicable(
+ opt::IRContext* context,
+ const spvtools::fuzz::FactManager& /*unused*/) const {
+ // Check that the given id is fresh.
+ if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+ return false;
+ }
+ // Check that the composite type id is an instruction id.
+ auto composite_type_instruction =
+ context->get_def_use_mgr()->GetDef(message_.type_id());
+ if (!composite_type_instruction) {
+ return false;
+ }
+ // Gather up the operands for the composite constant, in the process checking
+ // whether the given type really defines a composite.
+ std::vector<uint32_t> constituent_type_ids;
+ switch (composite_type_instruction->opcode()) {
+ case SpvOpTypeArray:
+ for (uint32_t index = 0;
+ index <
+ fuzzerutil::GetArraySize(*composite_type_instruction, context);
+ index++) {
+ constituent_type_ids.push_back(
+ composite_type_instruction->GetSingleWordInOperand(0));
+ }
+ break;
+ case SpvOpTypeMatrix:
+ case SpvOpTypeVector:
+ for (uint32_t index = 0;
+ index < composite_type_instruction->GetSingleWordInOperand(1);
+ index++) {
+ constituent_type_ids.push_back(
+ composite_type_instruction->GetSingleWordInOperand(0));
+ }
+ break;
+ case SpvOpTypeStruct:
+ composite_type_instruction->ForEachInOperand(
+ [&constituent_type_ids](const uint32_t* member_type_id) {
+ constituent_type_ids.push_back(*member_type_id);
+ });
+ break;
+ default:
+ // Not a composite type.
+ return false;
+ }
+
+ // Check that the number of provided operands matches the number of
+ // constituents required by the type.
+ if (constituent_type_ids.size() !=
+ static_cast<uint32_t>(message_.constituent_id().size())) {
+ return false;
+ }
+
+ // Check that every provided operand refers to an instruction of the
+ // corresponding constituent type.
+ for (uint32_t index = 0; index < constituent_type_ids.size(); index++) {
+ auto constituent_instruction =
+ context->get_def_use_mgr()->GetDef(message_.constituent_id(index));
+ if (!constituent_instruction) {
+ return false;
+ }
+ if (constituent_instruction->type_id() != constituent_type_ids.at(index)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void TransformationAddConstantComposite::Apply(
+ opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+ opt::Instruction::OperandList in_operands;
+ for (auto constituent_id : message_.constituent_id()) {
+ in_operands.push_back({SPV_OPERAND_TYPE_ID, {constituent_id}});
+ }
+ context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+ context, SpvOpConstantComposite, message_.type_id(), message_.fresh_id(),
+ in_operands));
+ fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+ // We have added an instruction to the module, so need to be careful about the
+ // validity of existing analyses.
+ context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddConstantComposite::ToMessage()
+ const {
+ protobufs::Transformation result;
+ *result.mutable_add_constant_composite() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_add_constant_composite.h b/source/fuzz/transformation_add_constant_composite.h
new file mode 100644
index 0000000..9a824a0
--- /dev/null
+++ b/source/fuzz/transformation_add_constant_composite.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_COMPOSITE_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_COMPOSITE_H_
+
+#include <vector>
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddConstantComposite : public Transformation {
+ public:
+ explicit TransformationAddConstantComposite(
+ const protobufs::TransformationAddConstantComposite& message);
+
+ TransformationAddConstantComposite(
+ uint32_t fresh_id, uint32_t type_id,
+ const std::vector<uint32_t>& constituent_ids);
+
+ // - |message_.fresh_id| must be a fresh id
+ // - |message_.type_id| must be the id of a composite type
+ // - |message_.constituent_id| must refer to ids that match the constituent
+ // types of this composite type
+ bool IsApplicable(opt::IRContext* context,
+ const FactManager& fact_manager) const override;
+
+ // Adds an OpConstantComposite instruction defining a constant of type
+ // |message_.type_id|, using |message_.constituent_id| as constituents, with
+ // result id |message_.fresh_id|.
+ void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationAddConstantComposite message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_COMPOSITE_H_
diff --git a/source/fuzz/transformation_add_dead_break.cpp b/source/fuzz/transformation_add_dead_break.cpp
index b244cf4..a37100b 100644
--- a/source/fuzz/transformation_add_dead_break.cpp
+++ b/source/fuzz/transformation_add_dead_break.cpp
@@ -175,18 +175,23 @@
return false;
}
- // Check that adding the break would not violate the property that a
- // definition must dominate all of its uses.
- return fuzzerutil::NewEdgeRespectsUseDefDominance(context, bb_from, bb_to);
+ // Adding the dead break is only valid if SPIR-V rules related to dominance
+ // hold. Rather than checking these rules explicitly, we defer to the
+ // validator. We make a clone of the module, apply the transformation to the
+ // clone, and check whether the transformed clone is valid.
+ //
+ // In principle some of the above checks could be removed, with more reliance
+ // being places on the validator. This should be revisited if we are sure
+ // the validator is complete with respect to checking structured control flow
+ // rules.
+ auto cloned_context = fuzzerutil::CloneIRContext(context);
+ ApplyImpl(cloned_context.get());
+ return fuzzerutil::IsValid(cloned_context.get());
}
void TransformationAddDeadBreak::Apply(opt::IRContext* context,
FactManager* /*unused*/) const {
- fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
- context, context->cfg()->block(message_.from_block()),
- context->cfg()->block(message_.to_block()),
- message_.break_condition_value(), message_.phi_id());
-
+ ApplyImpl(context);
// Invalidate all analyses
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
}
@@ -197,5 +202,13 @@
return result;
}
+void TransformationAddDeadBreak::ApplyImpl(
+ spvtools::opt::IRContext* context) const {
+ fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
+ context, context->cfg()->block(message_.from_block()),
+ context->cfg()->block(message_.to_block()),
+ message_.break_condition_value(), message_.phi_id());
+}
+
} // namespace fuzz
} // namespace spvtools
diff --git a/source/fuzz/transformation_add_dead_break.h b/source/fuzz/transformation_add_dead_break.h
index 10d2cec..81a2c99 100644
--- a/source/fuzz/transformation_add_dead_break.h
+++ b/source/fuzz/transformation_add_dead_break.h
@@ -67,6 +67,14 @@
bool AddingBreakRespectsStructuredControlFlow(opt::IRContext* context,
opt::BasicBlock* bb_from) const;
+ // Used by 'Apply' to actually apply the transformation to the module of
+ // interest, and by 'IsApplicable' to do a dry-run of the transformation on a
+ // cloned module, in order to check that the transformation leads to a valid
+ // module. This is only invoked by 'IsApplicable' after certain basic
+ // applicability checks have been made, ensuring that the invocation of this
+ // method is legal.
+ void ApplyImpl(opt::IRContext* context) const;
+
protobufs::TransformationAddDeadBreak message_;
};
diff --git a/source/fuzz/transformation_add_dead_continue.cpp b/source/fuzz/transformation_add_dead_continue.cpp
index e644b88..0aacc5b 100644
--- a/source/fuzz/transformation_add_dead_continue.cpp
+++ b/source/fuzz/transformation_add_dead_continue.cpp
@@ -106,34 +106,30 @@
return false;
}
- // Check that adding the continue would not violate the property that a
- // definition must dominate all of its uses.
- if (!fuzzerutil::NewEdgeRespectsUseDefDominance(
- context, bb_from, context->cfg()->block(continue_block))) {
+ // Check whether the data passed to extend OpPhi instructions is appropriate.
+ if (!fuzzerutil::PhiIdsOkForNewEdge(context, bb_from,
+ context->cfg()->block(continue_block),
+ message_.phi_id())) {
return false;
}
- // The transformation is good if and only if the given phi ids are sufficient
- // to extend relevant OpPhi instructions in the continue block.
- return fuzzerutil::PhiIdsOkForNewEdge(context, bb_from,
- context->cfg()->block(continue_block),
- message_.phi_id());
+ // Adding the dead break is only valid if SPIR-V rules related to dominance
+ // hold. Rather than checking these rules explicitly, we defer to the
+ // validator. We make a clone of the module, apply the transformation to the
+ // clone, and check whether the transformed clone is valid.
+ //
+ // In principle some of the above checks could be removed, with more reliance
+ // being places on the validator. This should be revisited if we are sure
+ // the validator is complete with respect to checking structured control flow
+ // rules.
+ auto cloned_context = fuzzerutil::CloneIRContext(context);
+ ApplyImpl(cloned_context.get());
+ return fuzzerutil::IsValid(cloned_context.get());
}
void TransformationAddDeadContinue::Apply(opt::IRContext* context,
FactManager* /*unused*/) const {
- auto bb_from = context->cfg()->block(message_.from_block());
- auto continue_block =
- bb_from->IsLoopHeader()
- ? bb_from->ContinueBlockId()
- : context->GetStructuredCFGAnalysis()->LoopContinueBlock(
- message_.from_block());
- assert(continue_block &&
- "Precondition for this transformation requires that "
- "message_.from_block must be in a loop.");
- fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
- context, bb_from, context->cfg()->block(continue_block),
- message_.continue_condition_value(), message_.phi_id());
+ ApplyImpl(context);
// Invalidate all analyses
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
}
@@ -144,5 +140,19 @@
return result;
}
+void TransformationAddDeadContinue::ApplyImpl(
+ spvtools::opt::IRContext* context) const {
+ auto bb_from = context->cfg()->block(message_.from_block());
+ auto continue_block =
+ bb_from->IsLoopHeader()
+ ? bb_from->ContinueBlockId()
+ : context->GetStructuredCFGAnalysis()->LoopContinueBlock(
+ message_.from_block());
+ assert(continue_block && "message_.from_block must be in a loop.");
+ fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
+ context, bb_from, context->cfg()->block(continue_block),
+ message_.continue_condition_value(), message_.phi_id());
+}
+
} // namespace fuzz
} // namespace spvtools
diff --git a/source/fuzz/transformation_add_dead_continue.h b/source/fuzz/transformation_add_dead_continue.h
index df6bb4c..86b4c93 100644
--- a/source/fuzz/transformation_add_dead_continue.h
+++ b/source/fuzz/transformation_add_dead_continue.h
@@ -64,6 +64,14 @@
protobufs::Transformation ToMessage() const override;
private:
+ // Used by 'Apply' to actually apply the transformation to the module of
+ // interest, and by 'IsApplicable' to do a dry-run of the transformation on a
+ // cloned module, in order to check that the transformation leads to a valid
+ // module. This is only invoked by 'IsApplicable' after certain basic
+ // applicability checks have been made, ensuring that the invocation of this
+ // method is legal.
+ void ApplyImpl(opt::IRContext* context) const;
+
protobufs::TransformationAddDeadContinue message_;
};
diff --git a/source/fuzz/transformation_add_function.cpp b/source/fuzz/transformation_add_function.cpp
new file mode 100644
index 0000000..5e53961
--- /dev/null
+++ b/source/fuzz/transformation_add_function.cpp
@@ -0,0 +1,156 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_function.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_message.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddFunction::TransformationAddFunction(
+ const spvtools::fuzz::protobufs::TransformationAddFunction& message)
+ : message_(message) {}
+
+TransformationAddFunction::TransformationAddFunction(
+ const std::vector<protobufs::Instruction>& instructions) {
+ for (auto& instruction : instructions) {
+ *message_.add_instruction() = instruction;
+ }
+}
+
+bool TransformationAddFunction::IsApplicable(
+ opt::IRContext* context,
+ const spvtools::fuzz::FactManager& /*unused*/) const {
+ // Because checking all the conditions for a function to be valid is a big
+ // job that the SPIR-V validator can already do, a "try it and see" approach
+ // is taken here.
+
+ // We first clone the current module, so that we can try adding the new
+ // function without risking wrecking |context|.
+ auto cloned_module = fuzzerutil::CloneIRContext(context);
+
+ // We try to add a function to the cloned module, which may fail if
+ // |message_.instruction| is not sufficiently well-formed.
+ if (!TryToAddFunction(cloned_module.get())) {
+ return false;
+ }
+ // Having managed to add the new function to the cloned module, we ascertain
+ // whether the cloned module is still valid. If it is, the transformation is
+ // applicable.
+ return fuzzerutil::IsValid(cloned_module.get());
+}
+
+void TransformationAddFunction::Apply(
+ opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+ auto success = TryToAddFunction(context);
+ assert(success && "The function should be successfully added.");
+ (void)(success); // Keep release builds happy (otherwise they may complain
+ // that |success| is not used).
+ context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddFunction::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_add_function() = message_;
+ return result;
+}
+
+bool TransformationAddFunction::TryToAddFunction(
+ opt::IRContext* context) const {
+ // This function returns false if |message_.instruction| was not well-formed
+ // enough to actually create a function and add it to |context|.
+
+ // A function must have at least some instructions.
+ if (message_.instruction().empty()) {
+ return false;
+ }
+
+ // A function must start with OpFunction.
+ auto function_begin = message_.instruction(0);
+ if (function_begin.opcode() != SpvOpFunction) {
+ return false;
+ }
+
+ // Make a function, headed by the OpFunction instruction.
+ std::unique_ptr<opt::Function> new_function = MakeUnique<opt::Function>(
+ InstructionFromMessage(context, function_begin));
+
+ // Keeps track of which instruction protobuf message we are currently
+ // considering.
+ uint32_t instruction_index = 1;
+ const auto num_instructions =
+ static_cast<uint32_t>(message_.instruction().size());
+
+ // Iterate through all function parameter instructions, adding parameters to
+ // the new function.
+ while (instruction_index < num_instructions &&
+ message_.instruction(instruction_index).opcode() ==
+ SpvOpFunctionParameter) {
+ new_function->AddParameter(InstructionFromMessage(
+ context, message_.instruction(instruction_index)));
+ instruction_index++;
+ }
+
+ // After the parameters, there needs to be a label.
+ if (instruction_index == num_instructions ||
+ message_.instruction(instruction_index).opcode() != SpvOpLabel) {
+ return false;
+ }
+
+ // Iterate through the instructions block by block until the end of the
+ // function is reached.
+ while (instruction_index < num_instructions &&
+ message_.instruction(instruction_index).opcode() != SpvOpFunctionEnd) {
+ // Invariant: we should always be at a label instruction at this point.
+ assert(message_.instruction(instruction_index).opcode() == SpvOpLabel);
+
+ // Make a basic block using the label instruction, with the new function
+ // as its parent.
+ std::unique_ptr<opt::BasicBlock> block =
+ MakeUnique<opt::BasicBlock>(InstructionFromMessage(
+ context, message_.instruction(instruction_index)));
+ block->SetParent(new_function.get());
+
+ // Consider successive instructions until we hit another label or the end
+ // of the function, adding each such instruction to the block.
+ instruction_index++;
+ while (instruction_index < num_instructions &&
+ message_.instruction(instruction_index).opcode() !=
+ SpvOpFunctionEnd &&
+ message_.instruction(instruction_index).opcode() != SpvOpLabel) {
+ block->AddInstruction(InstructionFromMessage(
+ context, message_.instruction(instruction_index)));
+ instruction_index++;
+ }
+ // Add the block to the new function.
+ new_function->AddBasicBlock(std::move(block));
+ }
+ // Having considered all the blocks, we should be at the last instruction and
+ // it needs to be OpFunctionEnd.
+ if (instruction_index != num_instructions - 1 ||
+ message_.instruction(instruction_index).opcode() != SpvOpFunctionEnd) {
+ return false;
+ }
+ // Set the function's final instruction, add the function to the module and
+ // report success.
+ new_function->SetFunctionEnd(
+ InstructionFromMessage(context, message_.instruction(instruction_index)));
+ context->AddFunction(std::move(new_function));
+ return true;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_add_function.h b/source/fuzz/transformation_add_function.h
new file mode 100644
index 0000000..fee2732
--- /dev/null
+++ b/source/fuzz/transformation_add_function.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddFunction : public Transformation {
+ public:
+ explicit TransformationAddFunction(
+ const protobufs::TransformationAddFunction& message);
+
+ explicit TransformationAddFunction(
+ const std::vector<protobufs::Instruction>& instructions);
+
+ // - |message_.instruction| must correspond to a sufficiently well-formed
+ // sequence of instructions that a function can be created from them
+ // - Adding the created function to the module must lead to a valid module.
+ bool IsApplicable(opt::IRContext* context,
+ const FactManager& fact_manager) const override;
+
+ // Adds the function defined by |message_.instruction| to the module
+ void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ // Attempts to create a function from the series of instructions in
+ // |message_.instruction| and add it to |context|. Returns false if this is
+ // not possible due to the messages not respecting the basic structure of a
+ // function, e.g. if there is no OpFunction instruction or no blocks; in this
+ // case |context| is left in an indeterminate state.
+ //
+ // Otherwise returns true. Whether |context| is valid after addition of the
+ // function depends on the contents of |message_.instruction|.
+ //
+ // Intended usage:
+ // - Perform a dry run of this method on a clone of a module, and use
+ // the validator to check whether the resulting module is valid. Working
+ // on a clone means it does not matter if the function fails to be cleanly
+ // added, or leads to an invalid module.
+ // - If the dry run succeeds, run the method on the real module of interest,
+ // to add the function.
+ bool TryToAddFunction(opt::IRContext* context) const;
+
+ protobufs::TransformationAddFunction message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_
diff --git a/source/fuzz/transformation_add_global_undef.cpp b/source/fuzz/transformation_add_global_undef.cpp
new file mode 100644
index 0000000..f9585b3
--- /dev/null
+++ b/source/fuzz/transformation_add_global_undef.cpp
@@ -0,0 +1,62 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_global_undef.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddGlobalUndef::TransformationAddGlobalUndef(
+ const spvtools::fuzz::protobufs::TransformationAddGlobalUndef& message)
+ : message_(message) {}
+
+TransformationAddGlobalUndef::TransformationAddGlobalUndef(uint32_t fresh_id,
+ uint32_t type_id) {
+ message_.set_fresh_id(fresh_id);
+ message_.set_type_id(type_id);
+}
+
+bool TransformationAddGlobalUndef::IsApplicable(
+ opt::IRContext* context,
+ const spvtools::fuzz::FactManager& /*unused*/) const {
+ // A fresh id is required.
+ if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+ return false;
+ }
+ auto type = context->get_type_mgr()->GetType(message_.type_id());
+ // The type must exist, and must not be a function type.
+ return type && !type->AsFunction();
+}
+
+void TransformationAddGlobalUndef::Apply(
+ opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+ context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+ context, SpvOpUndef, message_.type_id(), message_.fresh_id(),
+ opt::Instruction::OperandList()));
+ fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+ // We have added an instruction to the module, so need to be careful about the
+ // validity of existing analyses.
+ context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddGlobalUndef::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_add_global_undef() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_add_global_undef.h b/source/fuzz/transformation_add_global_undef.h
new file mode 100644
index 0000000..550d9f6
--- /dev/null
+++ b/source/fuzz/transformation_add_global_undef.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddGlobalUndef : public Transformation {
+ public:
+ explicit TransformationAddGlobalUndef(
+ const protobufs::TransformationAddGlobalUndef& message);
+
+ 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
+ bool IsApplicable(opt::IRContext* context,
+ const FactManager& fact_manager) const override;
+
+ // Adds an OpUndef instruction to the module, with |message_.type_id| as its
+ // type. The instruction has result id |message_.fresh_id|.
+ void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationAddGlobalUndef message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_
diff --git a/source/fuzz/transformation_add_global_variable.cpp b/source/fuzz/transformation_add_global_variable.cpp
new file mode 100644
index 0000000..cea268c
--- /dev/null
+++ b/source/fuzz/transformation_add_global_variable.cpp
@@ -0,0 +1,131 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_global_variable.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddGlobalVariable::TransformationAddGlobalVariable(
+ const spvtools::fuzz::protobufs::TransformationAddGlobalVariable& message)
+ : message_(message) {}
+
+TransformationAddGlobalVariable::TransformationAddGlobalVariable(
+ uint32_t fresh_id, uint32_t type_id, uint32_t initializer_id) {
+ message_.set_fresh_id(fresh_id);
+ message_.set_type_id(type_id);
+ message_.set_initializer_id(initializer_id);
+}
+
+bool TransformationAddGlobalVariable::IsApplicable(
+ opt::IRContext* context,
+ const spvtools::fuzz::FactManager& /*unused*/) const {
+ // The result id must be fresh.
+ if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+ return false;
+ }
+ // The type id must correspond to a type.
+ auto type = context->get_type_mgr()->GetType(message_.type_id());
+ if (!type) {
+ return false;
+ }
+ // That type must be a pointer type ...
+ auto pointer_type = type->AsPointer();
+ if (!pointer_type) {
+ return false;
+ }
+ // ... with Private storage class.
+ if (pointer_type->storage_class() != SpvStorageClassPrivate) {
+ return false;
+ }
+ if (message_.initializer_id()) {
+ // The initializer id must be the id of a constant. Check this with the
+ // constant manager.
+ auto constant_id = context->get_constant_mgr()->GetConstantsFromIds(
+ {message_.initializer_id()});
+ if (constant_id.empty()) {
+ return false;
+ }
+ assert(constant_id.size() == 1 &&
+ "We asked for the constant associated with a single id; we should "
+ "get a single constant.");
+ // The type of the constant must match the pointee type of the pointer.
+ if (pointer_type->pointee_type() != constant_id[0]->type()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void TransformationAddGlobalVariable::Apply(
+ opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+ opt::Instruction::OperandList input_operands;
+ input_operands.push_back(
+ {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassPrivate}});
+ if (message_.initializer_id()) {
+ input_operands.push_back(
+ {SPV_OPERAND_TYPE_ID, {message_.initializer_id()}});
+ }
+ context->module()->AddGlobalValue(
+ MakeUnique<opt::Instruction>(context, SpvOpVariable, message_.type_id(),
+ message_.fresh_id(), input_operands));
+ fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+
+ if (PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(context)) {
+ // Conservatively add this global to the interface of every entry point in
+ // the module. This means that the global is available for other
+ // transformations to use.
+ //
+ // A downside of this is that the global will be in the interface even if it
+ // ends up never being used.
+ //
+ // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3111) revisit
+ // this if a more thorough approach to entry point interfaces is taken.
+ for (auto& entry_point : context->module()->entry_points()) {
+ entry_point.AddOperand({SPV_OPERAND_TYPE_ID, {message_.fresh_id()}});
+ }
+ }
+
+ // We have added an instruction to the module, so need to be careful about the
+ // validity of existing analyses.
+ context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddGlobalVariable::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_add_global_variable() = message_;
+ return result;
+}
+
+bool TransformationAddGlobalVariable::
+ PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(
+ opt::IRContext* context) {
+ // TODO(afd): We capture the universal environments for which this requirement
+ // holds. The check should be refined on demand for other target
+ // environments.
+ switch (context->grammar().target_env()) {
+ case SPV_ENV_UNIVERSAL_1_0:
+ case SPV_ENV_UNIVERSAL_1_1:
+ case SPV_ENV_UNIVERSAL_1_2:
+ case SPV_ENV_UNIVERSAL_1_3:
+ return false;
+ default:
+ return true;
+ }
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_add_global_variable.h b/source/fuzz/transformation_add_global_variable.h
new file mode 100644
index 0000000..ca63e68
--- /dev/null
+++ b/source/fuzz/transformation_add_global_variable.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddGlobalVariable : public Transformation {
+ public:
+ explicit TransformationAddGlobalVariable(
+ const protobufs::TransformationAddGlobalVariable& message);
+
+ TransformationAddGlobalVariable(uint32_t fresh_id, uint32_t type_id,
+ uint32_t initializer_id);
+
+ // - |message_.fresh_id| must be fresh
+ // - |message_.type_id| must be the id of a pointer type with Private storage
+ // class
+ // - |message_.initializer_id| must either be 0 or the id of a constant whose
+ // type is the pointee type of |message_.type_id|
+ bool IsApplicable(opt::IRContext* context,
+ const FactManager& fact_manager) const override;
+
+ // Adds a global variable with Private storage class to the module, with type
+ // |message_.type_id| and either no initializer or |message_.initializer_id|
+ // as an initializer, depending on whether |message_.initializer_id| is 0.
+ // The global variable has result id |message_.fresh_id|.
+ void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ static bool PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(
+ opt::IRContext* context);
+
+ protobufs::TransformationAddGlobalVariable message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_
diff --git a/source/fuzz/transformation_add_type_array.cpp b/source/fuzz/transformation_add_type_array.cpp
new file mode 100644
index 0000000..2074e98
--- /dev/null
+++ b/source/fuzz/transformation_add_type_array.cpp
@@ -0,0 +1,88 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_type_array.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddTypeArray::TransformationAddTypeArray(
+ const spvtools::fuzz::protobufs::TransformationAddTypeArray& message)
+ : message_(message) {}
+
+TransformationAddTypeArray::TransformationAddTypeArray(uint32_t fresh_id,
+ uint32_t element_type_id,
+ uint32_t size_id) {
+ message_.set_fresh_id(fresh_id);
+ message_.set_element_type_id(element_type_id);
+ message_.set_size_id(size_id);
+}
+
+bool TransformationAddTypeArray::IsApplicable(
+ opt::IRContext* context,
+ const spvtools::fuzz::FactManager& /*unused*/) const {
+ // A fresh id is required.
+ if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+ return false;
+ }
+ auto element_type =
+ context->get_type_mgr()->GetType(message_.element_type_id());
+ if (!element_type || element_type->AsFunction()) {
+ // The element type id either does not refer to a type, or refers to a
+ // function type; both are illegal.
+ return false;
+ }
+ auto constant =
+ context->get_constant_mgr()->GetConstantsFromIds({message_.size_id()});
+ if (constant.empty()) {
+ // The size id does not refer to a constant.
+ return false;
+ }
+ assert(constant.size() == 1 &&
+ "Only one constant id was provided, so only one constant should have "
+ "been returned");
+
+ auto int_constant = constant[0]->AsIntConstant();
+ if (!int_constant) {
+ // The size constant is not an integer.
+ return false;
+ }
+ // We require that the size constant be a 32-bit value that is positive when
+ // interpreted as being signed.
+ return int_constant->words().size() == 1 && int_constant->GetS32() >= 1;
+}
+
+void TransformationAddTypeArray::Apply(
+ opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+ opt::Instruction::OperandList in_operands;
+ in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.element_type_id()}});
+ in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.size_id()}});
+ context->module()->AddType(MakeUnique<opt::Instruction>(
+ context, SpvOpTypeArray, 0, message_.fresh_id(), in_operands));
+ fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+ // We have added an instruction to the module, so need to be careful about the
+ // validity of existing analyses.
+ context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddTypeArray::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_add_type_array() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_add_type_array.h b/source/fuzz/transformation_add_type_array.h
new file mode 100644
index 0000000..b6e0718
--- /dev/null
+++ b/source/fuzz/transformation_add_type_array.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_ARRAY_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_ARRAY_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddTypeArray : public Transformation {
+ public:
+ explicit TransformationAddTypeArray(
+ const protobufs::TransformationAddTypeArray& message);
+
+ TransformationAddTypeArray(uint32_t fresh_id, uint32_t element_type_id,
+ uint32_t size_id);
+
+ // - |message_.fresh_id| must be fresh
+ // - |message_.element_type_id| must be the id of a non-function type
+ // - |message_.size_id| must be the id of a 32-bit integer constant that is
+ // positive when interpreted as signed.
+ bool IsApplicable(opt::IRContext* context,
+ const FactManager& fact_manager) const override;
+
+ // Adds an OpTypeArray instruction to the module, with element type given by
+ // |message_.element_type_id| and size given by |message_.size_id|. The
+ // result id of the instruction is |message_.fresh_id|.
+ void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationAddTypeArray message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_ARRAY_H_
diff --git a/source/fuzz/transformation_add_type_function.cpp b/source/fuzz/transformation_add_type_function.cpp
new file mode 100644
index 0000000..4b6717b
--- /dev/null
+++ b/source/fuzz/transformation_add_type_function.cpp
@@ -0,0 +1,113 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_type_function.h"
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddTypeFunction::TransformationAddTypeFunction(
+ const spvtools::fuzz::protobufs::TransformationAddTypeFunction& message)
+ : message_(message) {}
+
+TransformationAddTypeFunction::TransformationAddTypeFunction(
+ uint32_t fresh_id, uint32_t return_type_id,
+ const std::vector<uint32_t>& argument_type_ids) {
+ message_.set_fresh_id(fresh_id);
+ message_.set_return_type_id(return_type_id);
+ for (auto id : argument_type_ids) {
+ message_.add_argument_type_id(id);
+ }
+}
+
+bool TransformationAddTypeFunction::IsApplicable(
+ opt::IRContext* context,
+ const spvtools::fuzz::FactManager& /*unused*/) const {
+ // The result id must be fresh.
+ if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+ return false;
+ }
+ // The return and argument types must be type ids but not not be function
+ // type ids.
+ if (!fuzzerutil::IsNonFunctionTypeId(context, message_.return_type_id())) {
+ return false;
+ }
+ for (auto argument_type_id : message_.argument_type_id()) {
+ if (!fuzzerutil::IsNonFunctionTypeId(context, argument_type_id)) {
+ return false;
+ }
+ }
+ // Check whether there is already an OpTypeFunction definition that uses
+ // exactly the same return and argument type ids. (Note that the type manager
+ // does not allow us to check this, as it does not distinguish between
+ // function types with different but isomorphic pointer argument types.)
+ for (auto& inst : context->module()->types_values()) {
+ if (inst.opcode() != SpvOpTypeFunction) {
+ // Consider only OpTypeFunction instructions.
+ continue;
+ }
+ if (inst.GetSingleWordInOperand(0) != message_.return_type_id()) {
+ // Different return types - cannot be the same.
+ continue;
+ }
+ if (inst.NumInOperands() !=
+ 1 + static_cast<uint32_t>(message_.argument_type_id().size())) {
+ // Different numbers of arguments - cannot be the same.
+ continue;
+ }
+ bool found_argument_mismatch = false;
+ for (uint32_t index = 1; index < inst.NumInOperands(); index++) {
+ if (message_.argument_type_id(index - 1) !=
+ inst.GetSingleWordInOperand(index)) {
+ // Argument mismatch - cannot be the same.
+ found_argument_mismatch = true;
+ break;
+ }
+ }
+ if (found_argument_mismatch) {
+ continue;
+ }
+ // Everything matches - the type is already declared.
+ return false;
+ }
+ return true;
+}
+
+void TransformationAddTypeFunction::Apply(
+ opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+ opt::Instruction::OperandList in_operands;
+ in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.return_type_id()}});
+ for (auto argument_type_id : message_.argument_type_id()) {
+ in_operands.push_back({SPV_OPERAND_TYPE_ID, {argument_type_id}});
+ }
+ context->module()->AddType(MakeUnique<opt::Instruction>(
+ context, SpvOpTypeFunction, 0, message_.fresh_id(), in_operands));
+ fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+ // We have added an instruction to the module, so need to be careful about the
+ // validity of existing analyses.
+ context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddTypeFunction::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_add_type_function() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_add_type_function.h b/source/fuzz/transformation_add_type_function.h
new file mode 100644
index 0000000..2b59661
--- /dev/null
+++ b/source/fuzz/transformation_add_type_function.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FUNCTION_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FUNCTION_H_
+
+#include <vector>
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddTypeFunction : public Transformation {
+ public:
+ explicit TransformationAddTypeFunction(
+ const protobufs::TransformationAddTypeFunction& message);
+
+ TransformationAddTypeFunction(uint32_t fresh_id, uint32_t return_type_id,
+ const std::vector<uint32_t>& argument_type_ids);
+
+ // - |message_.fresh_id| must not be used by the module
+ // - |message_.return_type_id| and each element of |message_.argument_type_id|
+ // must be the ids of non-function types
+ // - The module must not contain an OpTypeFunction instruction defining a
+ // function type with the signature provided by teh given return and
+ // argument types
+ bool IsApplicable(opt::IRContext* context,
+ const FactManager& fact_manager) const override;
+
+ // Adds an OpTypeFunction instruction to the module, with signature given by
+ // |message_.return_type_id| and |message_.argument_type_id|. The result id
+ // for the instruction is |message_.fresh_id|.
+ void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationAddTypeFunction message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FUNCTION_H_
diff --git a/source/fuzz/transformation_add_type_matrix.cpp b/source/fuzz/transformation_add_type_matrix.cpp
new file mode 100644
index 0000000..07ab705
--- /dev/null
+++ b/source/fuzz/transformation_add_type_matrix.cpp
@@ -0,0 +1,71 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_type_matrix.h"
+
+#include "fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddTypeMatrix::TransformationAddTypeMatrix(
+ const spvtools::fuzz::protobufs::TransformationAddTypeMatrix& message)
+ : message_(message) {}
+
+TransformationAddTypeMatrix::TransformationAddTypeMatrix(
+ uint32_t fresh_id, uint32_t column_type_id, uint32_t column_count) {
+ message_.set_fresh_id(fresh_id);
+ message_.set_column_type_id(column_type_id);
+ message_.set_column_count(column_count);
+}
+
+bool TransformationAddTypeMatrix::IsApplicable(
+ opt::IRContext* context,
+ const spvtools::fuzz::FactManager& /*unused*/) const {
+ // The result id must be fresh.
+ if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+ return false;
+ }
+ // The column type must be a floating-point vector.
+ auto column_type =
+ context->get_type_mgr()->GetType(message_.column_type_id());
+ if (!column_type) {
+ return false;
+ }
+ return column_type->AsVector() &&
+ column_type->AsVector()->element_type()->AsFloat();
+}
+
+void TransformationAddTypeMatrix::Apply(
+ opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+ opt::Instruction::OperandList in_operands;
+ in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.column_type_id()}});
+ in_operands.push_back(
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.column_count()}});
+ context->module()->AddType(MakeUnique<opt::Instruction>(
+ context, SpvOpTypeMatrix, 0, message_.fresh_id(), in_operands));
+ fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+ // We have added an instruction to the module, so need to be careful about the
+ // validity of existing analyses.
+ context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddTypeMatrix::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_add_type_matrix() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_add_type_matrix.h b/source/fuzz/transformation_add_type_matrix.h
new file mode 100644
index 0000000..ee3caf7
--- /dev/null
+++ b/source/fuzz/transformation_add_type_matrix.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_MATRIX_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_MATRIX_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddTypeMatrix : public Transformation {
+ public:
+ explicit TransformationAddTypeMatrix(
+ const protobufs::TransformationAddTypeMatrix& message);
+
+ TransformationAddTypeMatrix(uint32_t fresh_id, uint32_t base_type_id,
+ uint32_t size);
+
+ // - |message_.fresh_id| must be a fresh id
+ // - |message_.column_type_id| must be the id of a floating-point vector type
+ bool IsApplicable(opt::IRContext* context,
+ const FactManager& fact_manager) const override;
+
+ // Adds an OpTypeMatrix instruction to the module, with column type
+ // |message_.column_type_id| and |message_.column_count| columns, with result
+ // id |message_.fresh_id|.
+ void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationAddTypeMatrix message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_MATRIX_H_
diff --git a/source/fuzz/transformation_add_type_struct.cpp b/source/fuzz/transformation_add_type_struct.cpp
new file mode 100644
index 0000000..1ae8372
--- /dev/null
+++ b/source/fuzz/transformation_add_type_struct.cpp
@@ -0,0 +1,73 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_type_struct.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddTypeStruct::TransformationAddTypeStruct(
+ const spvtools::fuzz::protobufs::TransformationAddTypeStruct& message)
+ : message_(message) {}
+
+TransformationAddTypeStruct::TransformationAddTypeStruct(
+ uint32_t fresh_id, const std::vector<uint32_t>& member_type_ids) {
+ message_.set_fresh_id(fresh_id);
+ for (auto member_type_id : member_type_ids) {
+ message_.add_member_type_id(member_type_id);
+ }
+}
+
+bool TransformationAddTypeStruct::IsApplicable(
+ opt::IRContext* context,
+ const spvtools::fuzz::FactManager& /*unused*/) const {
+ // A fresh id is required.
+ if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+ return false;
+ }
+ for (auto member_type : message_.member_type_id()) {
+ auto type = context->get_type_mgr()->GetType(member_type);
+ if (!type || type->AsFunction()) {
+ // The member type id either does not refer to a type, or refers to a
+ // function type; both are illegal.
+ return false;
+ }
+ }
+ return true;
+}
+
+void TransformationAddTypeStruct::Apply(
+ opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+ opt::Instruction::OperandList in_operands;
+ for (auto member_type : message_.member_type_id()) {
+ in_operands.push_back({SPV_OPERAND_TYPE_ID, {member_type}});
+ }
+ context->module()->AddType(MakeUnique<opt::Instruction>(
+ context, SpvOpTypeStruct, 0, message_.fresh_id(), in_operands));
+ fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+ // We have added an instruction to the module, so need to be careful about the
+ // validity of existing analyses.
+ context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddTypeStruct::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_add_type_struct() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_add_type_struct.h b/source/fuzz/transformation_add_type_struct.h
new file mode 100644
index 0000000..edf3ec6
--- /dev/null
+++ b/source/fuzz/transformation_add_type_struct.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_STRUCT_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_STRUCT_H_
+
+#include <vector>
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddTypeStruct : public Transformation {
+ public:
+ explicit TransformationAddTypeStruct(
+ const protobufs::TransformationAddTypeStruct& message);
+
+ TransformationAddTypeStruct(uint32_t fresh_id,
+ const std::vector<uint32_t>& component_type_ids);
+
+ // - |message_.fresh_id| must be a fresh id
+ // - |message_.member_type_id| must be a sequence of non-function type ids
+ bool IsApplicable(opt::IRContext* context,
+ const FactManager& fact_manager) const override;
+
+ // Adds an OpTypeStruct instruction whose field types are given by
+ // |message_.member_type_id|, with result id |message_.fresh_id|.
+ void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationAddTypeStruct message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_STRUCT_H_
diff --git a/source/fuzz/transformation_add_type_vector.cpp b/source/fuzz/transformation_add_type_vector.cpp
new file mode 100644
index 0000000..3fdf50b
--- /dev/null
+++ b/source/fuzz/transformation_add_type_vector.cpp
@@ -0,0 +1,69 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_type_vector.h"
+
+#include "fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddTypeVector::TransformationAddTypeVector(
+ const spvtools::fuzz::protobufs::TransformationAddTypeVector& message)
+ : message_(message) {}
+
+TransformationAddTypeVector::TransformationAddTypeVector(
+ uint32_t fresh_id, uint32_t component_type_id, uint32_t component_count) {
+ message_.set_fresh_id(fresh_id);
+ message_.set_component_type_id(component_type_id);
+ message_.set_component_count(component_count);
+}
+
+bool TransformationAddTypeVector::IsApplicable(
+ opt::IRContext* context,
+ const spvtools::fuzz::FactManager& /*unused*/) const {
+ if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+ return false;
+ }
+ auto component_type =
+ context->get_type_mgr()->GetType(message_.component_type_id());
+ if (!component_type) {
+ return false;
+ }
+ return component_type->AsBool() || component_type->AsFloat() ||
+ component_type->AsInteger();
+}
+
+void TransformationAddTypeVector::Apply(
+ opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+ opt::Instruction::OperandList in_operands;
+ in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.component_type_id()}});
+ in_operands.push_back(
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.component_count()}});
+ context->module()->AddType(MakeUnique<opt::Instruction>(
+ context, SpvOpTypeVector, 0, message_.fresh_id(), in_operands));
+ fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+ // We have added an instruction to the module, so need to be careful about the
+ // validity of existing analyses.
+ context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddTypeVector::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_add_type_vector() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_add_type_vector.h b/source/fuzz/transformation_add_type_vector.h
new file mode 100644
index 0000000..7b50f6a
--- /dev/null
+++ b/source/fuzz/transformation_add_type_vector.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_VECTOR_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_VECTOR_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddTypeVector : public Transformation {
+ public:
+ explicit TransformationAddTypeVector(
+ const protobufs::TransformationAddTypeVector& message);
+
+ TransformationAddTypeVector(uint32_t fresh_id, uint32_t base_type_id,
+ uint32_t size);
+
+ // - |message_.fresh_id| must be a fresh id
+ // - |message_.component_type_id| must be the id of a scalar type
+ bool IsApplicable(opt::IRContext* context,
+ const FactManager& fact_manager) const override;
+
+ // Adds an OpTypeVector instruction to the module, with component type
+ // |message_.component_type_id| and |message_.component_count| components,
+ // with result id |message_.fresh_id|.
+ void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationAddTypeVector message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_VECTOR_H_
diff --git a/source/fuzz/transformation_merge_blocks.cpp b/source/fuzz/transformation_merge_blocks.cpp
new file mode 100644
index 0000000..316e80d
--- /dev/null
+++ b/source/fuzz/transformation_merge_blocks.cpp
@@ -0,0 +1,81 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_merge_blocks.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/opt/block_merge_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationMergeBlocks::TransformationMergeBlocks(
+ const spvtools::fuzz::protobufs::TransformationMergeBlocks& message)
+ : message_(message) {}
+
+TransformationMergeBlocks::TransformationMergeBlocks(uint32_t block_id) {
+ message_.set_block_id(block_id);
+}
+
+bool TransformationMergeBlocks::IsApplicable(
+ opt::IRContext* context,
+ const spvtools::fuzz::FactManager& /*unused*/) const {
+ auto second_block = fuzzerutil::MaybeFindBlock(context, message_.block_id());
+ // The given block must exist.
+ if (!second_block) {
+ return false;
+ }
+ // The block must have just one predecessor.
+ auto predecessors = context->cfg()->preds(second_block->id());
+ if (predecessors.size() != 1) {
+ return false;
+ }
+ auto first_block = context->cfg()->block(predecessors.at(0));
+
+ return opt::blockmergeutil::CanMergeWithSuccessor(context, first_block);
+}
+
+void TransformationMergeBlocks::Apply(
+ opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+ auto second_block = fuzzerutil::MaybeFindBlock(context, message_.block_id());
+ auto first_block =
+ context->cfg()->block(context->cfg()->preds(second_block->id()).at(0));
+
+ auto function = first_block->GetParent();
+ // We need an iterator pointing to the predecessor, hence the loop.
+ for (auto bi = function->begin(); bi != function->end(); ++bi) {
+ if (bi->id() == first_block->id()) {
+ assert(opt::blockmergeutil::CanMergeWithSuccessor(context, &*bi) &&
+ "Because 'Apply' should only be invoked if 'IsApplicable' holds, "
+ "it must be possible to merge |bi| with its successor.");
+ opt::blockmergeutil::MergeWithSuccessor(context, function, bi);
+ // Invalidate all analyses, since we have changed the module
+ // significantly.
+ context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+ return;
+ }
+ }
+ assert(false &&
+ "Control should not reach here - we should always find the desired "
+ "block");
+}
+
+protobufs::Transformation TransformationMergeBlocks::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_merge_blocks() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_merge_blocks.h b/source/fuzz/transformation_merge_blocks.h
new file mode 100644
index 0000000..86216db
--- /dev/null
+++ b/source/fuzz/transformation_merge_blocks.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_MERGE_BLOCKS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_MERGE_BLOCKS_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationMergeBlocks : public Transformation {
+ public:
+ explicit TransformationMergeBlocks(
+ const protobufs::TransformationMergeBlocks& message);
+
+ TransformationMergeBlocks(uint32_t block_id);
+
+ // - |message_.block_id| must be the id of a block, b
+ // - 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
+ // valid module
+ bool IsApplicable(opt::IRContext* context,
+ const FactManager& fact_manager) const override;
+
+ // The contents of b are merged into a, and a's terminator is replaced with
+ // the terminator of b. Block b is removed from the module.
+ void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationMergeBlocks message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_MERGE_BLOCKS_H_
diff --git a/source/fuzz/transformation_outline_function.cpp b/source/fuzz/transformation_outline_function.cpp
new file mode 100644
index 0000000..1d1d48e
--- /dev/null
+++ b/source/fuzz/transformation_outline_function.cpp
@@ -0,0 +1,938 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_outline_function.h"
+
+#include <set>
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+namespace {
+
+std::map<uint32_t, uint32_t> PairSequenceToMap(
+ const google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>&
+ pair_sequence) {
+ std::map<uint32_t, uint32_t> result;
+ for (auto& pair : pair_sequence) {
+ result[pair.first()] = pair.second();
+ }
+ return result;
+}
+
+} // namespace
+
+TransformationOutlineFunction::TransformationOutlineFunction(
+ const spvtools::fuzz::protobufs::TransformationOutlineFunction& message)
+ : message_(message) {}
+
+TransformationOutlineFunction::TransformationOutlineFunction(
+ uint32_t entry_block, uint32_t exit_block,
+ uint32_t new_function_struct_return_type_id, uint32_t new_function_type_id,
+ uint32_t new_function_id, uint32_t new_function_region_entry_block,
+ uint32_t new_caller_result_id, uint32_t new_callee_result_id,
+ std::map<uint32_t, uint32_t>&& input_id_to_fresh_id,
+ std::map<uint32_t, uint32_t>&& output_id_to_fresh_id) {
+ message_.set_entry_block(entry_block);
+ message_.set_exit_block(exit_block);
+ message_.set_new_function_struct_return_type_id(
+ new_function_struct_return_type_id);
+ message_.set_new_function_type_id(new_function_type_id);
+ message_.set_new_function_id(new_function_id);
+ message_.set_new_function_region_entry_block(new_function_region_entry_block);
+ message_.set_new_caller_result_id(new_caller_result_id);
+ message_.set_new_callee_result_id(new_callee_result_id);
+ for (auto& entry : input_id_to_fresh_id) {
+ protobufs::UInt32Pair pair;
+ pair.set_first(entry.first);
+ pair.set_second(entry.second);
+ *message_.add_input_id_to_fresh_id() = pair;
+ }
+ for (auto& entry : output_id_to_fresh_id) {
+ protobufs::UInt32Pair pair;
+ pair.set_first(entry.first);
+ pair.set_second(entry.second);
+ *message_.add_output_id_to_fresh_id() = pair;
+ }
+}
+
+bool TransformationOutlineFunction::IsApplicable(
+ opt::IRContext* context,
+ const spvtools::fuzz::FactManager& /*unused*/) const {
+ std::set<uint32_t> ids_used_by_this_transformation;
+
+ // The various new ids used by the transformation must be fresh and distinct.
+
+ if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+ message_.new_function_struct_return_type_id(), context,
+ &ids_used_by_this_transformation)) {
+ return false;
+ }
+
+ if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+ message_.new_function_type_id(), context,
+ &ids_used_by_this_transformation)) {
+ return false;
+ }
+
+ if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+ message_.new_function_id(), context,
+ &ids_used_by_this_transformation)) {
+ return false;
+ }
+
+ if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+ message_.new_function_region_entry_block(), context,
+ &ids_used_by_this_transformation)) {
+ return false;
+ }
+
+ if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+ message_.new_caller_result_id(), context,
+ &ids_used_by_this_transformation)) {
+ return false;
+ }
+
+ if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+ message_.new_callee_result_id(), context,
+ &ids_used_by_this_transformation)) {
+ return false;
+ }
+
+ for (auto& pair : message_.input_id_to_fresh_id()) {
+ if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+ pair.second(), context, &ids_used_by_this_transformation)) {
+ return false;
+ }
+ }
+
+ for (auto& pair : message_.output_id_to_fresh_id()) {
+ if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+ pair.second(), context, &ids_used_by_this_transformation)) {
+ return false;
+ }
+ }
+
+ // The entry and exit block ids must indeed refer to blocks.
+ for (auto block_id : {message_.entry_block(), message_.exit_block()}) {
+ auto block_label = context->get_def_use_mgr()->GetDef(block_id);
+ if (!block_label || block_label->opcode() != SpvOpLabel) {
+ return false;
+ }
+ }
+
+ auto entry_block = context->cfg()->block(message_.entry_block());
+ auto exit_block = context->cfg()->block(message_.exit_block());
+
+ // The entry block cannot start with OpVariable - this would mean that
+ // outlining would remove a variable from the function containing the region
+ // being outlined.
+ if (entry_block->begin()->opcode() == SpvOpVariable) {
+ return false;
+ }
+
+ // For simplicity, we do not allow the entry block to be a loop header.
+ if (entry_block->GetLoopMergeInst()) {
+ return false;
+ }
+
+ // For simplicity, we do not allow the exit block to be a merge block or
+ // continue target.
+ bool exit_block_is_merge_or_continue = false;
+ context->get_def_use_mgr()->WhileEachUse(
+ exit_block->id(),
+ [&exit_block_is_merge_or_continue](
+ const opt::Instruction* use_instruction,
+ uint32_t /*unused*/) -> bool {
+ switch (use_instruction->opcode()) {
+ case SpvOpLoopMerge:
+ case SpvOpSelectionMerge:
+ exit_block_is_merge_or_continue = true;
+ return false;
+ default:
+ return true;
+ }
+ });
+ if (exit_block_is_merge_or_continue) {
+ return false;
+ }
+
+ // The entry block cannot start with OpPhi. This is to keep the
+ // transformation logic simple. (Another transformation to split the OpPhis
+ // from a block could be applied to avoid this scenario.)
+ if (entry_block->begin()->opcode() == SpvOpPhi) {
+ return false;
+ }
+
+ // The block must be in the same function.
+ if (entry_block->GetParent() != exit_block->GetParent()) {
+ return false;
+ }
+
+ // The entry block must dominate the exit block.
+ auto dominator_analysis =
+ context->GetDominatorAnalysis(entry_block->GetParent());
+ if (!dominator_analysis->Dominates(entry_block, exit_block)) {
+ return false;
+ }
+
+ // The exit block must post-dominate the entry block.
+ auto postdominator_analysis =
+ context->GetPostDominatorAnalysis(entry_block->GetParent());
+ if (!postdominator_analysis->Dominates(exit_block, entry_block)) {
+ return false;
+ }
+
+ // Find all the blocks dominated by |message_.entry_block| and post-dominated
+ // by |message_.exit_block|.
+ auto region_set = GetRegionBlocks(
+ context, entry_block = context->cfg()->block(message_.entry_block()),
+ exit_block = context->cfg()->block(message_.exit_block()));
+
+ // Check whether |region_set| really is a single-entry single-exit region, and
+ // also check whether structured control flow constructs and their merge
+ // and continue constructs are either wholly in or wholly out of the region -
+ // e.g. avoid the situation where the region contains the head of a loop but
+ // not the loop's continue construct.
+ //
+ // This is achieved by going through every block in the function that contains
+ // the region.
+ for (auto& block : *entry_block->GetParent()) {
+ if (&block == exit_block) {
+ // It is OK (and typically expected) for the exit block of the region to
+ // have successors outside the region. It is also OK for the exit block
+ // to head a structured control flow construct - the block containing the
+ // call to the outlined function will end up heading this construct if
+ // outlining takes place.
+ continue;
+ }
+
+ if (region_set.count(&block) != 0) {
+ // The block is in the region and is not the region's exit block. Let's
+ // see whether all of the block's successors are in the region. If they
+ // are not, the region is not single-entry single-exit.
+ bool all_successors_in_region = true;
+ block.WhileEachSuccessorLabel([&all_successors_in_region, context,
+ ®ion_set](uint32_t successor) -> bool {
+ if (region_set.count(context->cfg()->block(successor)) == 0) {
+ all_successors_in_region = false;
+ return false;
+ }
+ return true;
+ });
+ if (!all_successors_in_region) {
+ return false;
+ }
+ }
+
+ if (auto merge = block.GetMergeInst()) {
+ // The block is a loop or selection header -- the header and its
+ // associated merge block had better both be in the region or both be
+ // outside the region.
+ auto merge_block = context->cfg()->block(merge->GetSingleWordOperand(0));
+ if (region_set.count(&block) != region_set.count(merge_block)) {
+ return false;
+ }
+ }
+
+ if (auto loop_merge = block.GetLoopMergeInst()) {
+ // Similar to the above, but for the continue target of a loop.
+ auto continue_target =
+ context->cfg()->block(loop_merge->GetSingleWordOperand(1));
+ if (continue_target != exit_block &&
+ region_set.count(&block) != region_set.count(continue_target)) {
+ return false;
+ }
+ }
+ }
+
+ // For each region input id, i.e. every id defined outside the region but
+ // used inside the region, ...
+ std::map<uint32_t, uint32_t> input_id_to_fresh_id_map =
+ PairSequenceToMap(message_.input_id_to_fresh_id());
+ for (auto id : GetRegionInputIds(context, region_set, exit_block)) {
+ // There needs to be a corresponding fresh id to be used as a function
+ // parameter.
+ if (input_id_to_fresh_id_map.count(id) == 0) {
+ return false;
+ }
+ // Furthermore, no region input id is allowed to be the result of an access
+ // chain. This is because region input ids will become function parameters,
+ // and it is not legal to pass an access chain as a function parameter.
+ if (context->get_def_use_mgr()->GetDef(id)->opcode() == SpvOpAccessChain) {
+ return false;
+ }
+ }
+
+ // For each region output id -- i.e. every id defined inside the region but
+ // used outside the region -- there needs to be a corresponding fresh id that
+ // can hold the value for this id computed in the outlined function.
+ std::map<uint32_t, uint32_t> output_id_to_fresh_id_map =
+ PairSequenceToMap(message_.output_id_to_fresh_id());
+ for (auto id : GetRegionOutputIds(context, region_set, exit_block)) {
+ if (output_id_to_fresh_id_map.count(id) == 0) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void TransformationOutlineFunction::Apply(
+ opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+ // The entry block for the region before outlining.
+ auto original_region_entry_block =
+ context->cfg()->block(message_.entry_block());
+
+ // The exit block for the region before outlining.
+ auto original_region_exit_block =
+ context->cfg()->block(message_.exit_block());
+
+ // The single-entry single-exit region defined by |message_.entry_block| and
+ // |message_.exit_block|.
+ std::set<opt::BasicBlock*> region_blocks = GetRegionBlocks(
+ context, original_region_entry_block, original_region_exit_block);
+
+ // Input and output ids for the region being outlined.
+ std::vector<uint32_t> region_input_ids =
+ GetRegionInputIds(context, region_blocks, original_region_exit_block);
+ std::vector<uint32_t> region_output_ids =
+ GetRegionOutputIds(context, region_blocks, original_region_exit_block);
+
+ // Maps from input and output ids to fresh ids.
+ std::map<uint32_t, uint32_t> input_id_to_fresh_id_map =
+ PairSequenceToMap(message_.input_id_to_fresh_id());
+ std::map<uint32_t, uint32_t> output_id_to_fresh_id_map =
+ PairSequenceToMap(message_.output_id_to_fresh_id());
+
+ UpdateModuleIdBoundForFreshIds(context, input_id_to_fresh_id_map,
+ output_id_to_fresh_id_map);
+
+ // Construct a map that associates each output id with its type id.
+ std::map<uint32_t, uint32_t> output_id_to_type_id;
+ for (uint32_t output_id : region_output_ids) {
+ output_id_to_type_id[output_id] =
+ context->get_def_use_mgr()->GetDef(output_id)->type_id();
+ }
+
+ // The region will be collapsed to a single block that calls a function
+ // containing the outlined region. This block needs to end with whatever
+ // the exit block of the region ended with before outlining. We thus clone
+ // the terminator of the region's exit block, and the merge instruction for
+ // the block if there is one, so that we can append them to the end of the
+ // collapsed block later.
+ std::unique_ptr<opt::Instruction> cloned_exit_block_terminator =
+ std::unique_ptr<opt::Instruction>(
+ original_region_exit_block->terminator()->Clone(context));
+ std::unique_ptr<opt::Instruction> cloned_exit_block_merge =
+ original_region_exit_block->GetMergeInst()
+ ? std::unique_ptr<opt::Instruction>(
+ original_region_exit_block->GetMergeInst()->Clone(context))
+ : nullptr;
+
+ // Make a function prototype for the outlined function, which involves
+ // figuring out its required type.
+ std::unique_ptr<opt::Function> outlined_function = PrepareFunctionPrototype(
+ context, region_input_ids, region_output_ids, input_id_to_fresh_id_map);
+
+ // Adapt the region to be outlined so that its input ids are replaced with the
+ // ids of the outlined function's input parameters, and so that output ids
+ // are similarly remapped.
+ RemapInputAndOutputIdsInRegion(
+ context, *original_region_exit_block, region_blocks, region_input_ids,
+ region_output_ids, input_id_to_fresh_id_map, output_id_to_fresh_id_map);
+
+ // Fill out the body of the outlined function according to the region that is
+ // being outlined.
+ PopulateOutlinedFunction(context, *original_region_entry_block,
+ *original_region_exit_block, region_blocks,
+ region_output_ids, output_id_to_fresh_id_map,
+ outlined_function.get());
+
+ // Collapse the region that has been outlined into a function down to a single
+ // block that calls said function.
+ ShrinkOriginalRegion(
+ context, region_blocks, region_input_ids, region_output_ids,
+ output_id_to_type_id, outlined_function->type_id(),
+ std::move(cloned_exit_block_merge),
+ std::move(cloned_exit_block_terminator), original_region_entry_block);
+
+ // Add the outlined function to the module.
+ context->module()->AddFunction(std::move(outlined_function));
+
+ // Major surgery has been conducted on the module, so invalidate all analyses.
+ context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationOutlineFunction::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_outline_function() = message_;
+ return result;
+}
+
+bool TransformationOutlineFunction::
+ CheckIdIsFreshAndNotUsedByThisTransformation(
+ uint32_t id, opt::IRContext* context,
+ std::set<uint32_t>* ids_used_by_this_transformation) const {
+ if (!fuzzerutil::IsFreshId(context, id)) {
+ return false;
+ }
+ if (ids_used_by_this_transformation->count(id) != 0) {
+ return false;
+ }
+ ids_used_by_this_transformation->insert(id);
+ return true;
+}
+
+std::vector<uint32_t> TransformationOutlineFunction::GetRegionInputIds(
+ opt::IRContext* context, const std::set<opt::BasicBlock*>& region_set,
+ opt::BasicBlock* region_exit_block) {
+ std::vector<uint32_t> result;
+
+ auto enclosing_function = region_exit_block->GetParent();
+
+ // Consider each parameter of the function containing the region.
+ enclosing_function->ForEachParam([context, ®ion_set, &result](
+ opt::Instruction* function_parameter) {
+ // Consider every use of the parameter.
+ context->get_def_use_mgr()->WhileEachUse(
+ function_parameter, [context, function_parameter, ®ion_set, &result](
+ opt::Instruction* use, uint32_t /*unused*/) {
+ // Get the block, if any, in which the parameter is used.
+ auto use_block = context->get_instr_block(use);
+ // If the use is in a block that lies within the region, the
+ // parameter is an input id for the region.
+ if (use_block && region_set.count(use_block) != 0) {
+ result.push_back(function_parameter->result_id());
+ return false;
+ }
+ return true;
+ });
+ });
+
+ // Consider all definitions in the function that might turn out to be input
+ // ids.
+ for (auto& block : *enclosing_function) {
+ std::vector<opt::Instruction*> candidate_input_ids_for_block;
+ if (region_set.count(&block) == 0) {
+ // All instructions in blocks outside the region are candidate's for
+ // generating input ids.
+ for (auto& inst : block) {
+ candidate_input_ids_for_block.push_back(&inst);
+ }
+ } else {
+ // Blocks in the region cannot generate input ids.
+ continue;
+ }
+
+ // Consider each candidate input id to check whether it is used in the
+ // region.
+ for (auto& inst : candidate_input_ids_for_block) {
+ context->get_def_use_mgr()->WhileEachUse(
+ inst,
+ [context, &inst, region_exit_block, ®ion_set, &result](
+ opt::Instruction* use, uint32_t /*unused*/) -> bool {
+
+ // Find the block in which this id use occurs, recording the id as
+ // an input id if the block is outside the region, with some
+ // exceptions detailed below.
+ auto use_block = context->get_instr_block(use);
+
+ if (!use_block) {
+ // There might be no containing block, e.g. if the use is in a
+ // decoration.
+ return true;
+ }
+
+ if (region_set.count(use_block) == 0) {
+ // The use is not in the region: this does not make it an input
+ // id.
+ return true;
+ }
+
+ if (use_block == region_exit_block && use->IsBlockTerminator()) {
+ // We do not regard uses in the exit block terminator as input
+ // ids, as this terminator does not get outlined.
+ return true;
+ }
+
+ result.push_back(inst->result_id());
+ return false;
+ });
+ }
+ }
+ return result;
+}
+
+std::vector<uint32_t> TransformationOutlineFunction::GetRegionOutputIds(
+ opt::IRContext* context, const std::set<opt::BasicBlock*>& region_set,
+ opt::BasicBlock* region_exit_block) {
+ std::vector<uint32_t> result;
+
+ // Consider each block in the function containing the region.
+ for (auto& block : *region_exit_block->GetParent()) {
+ if (region_set.count(&block) == 0) {
+ // Skip blocks that are not in the region.
+ continue;
+ }
+ // Consider each use of each instruction defined in the block.
+ for (auto& inst : block) {
+ context->get_def_use_mgr()->WhileEachUse(
+ &inst,
+ [®ion_set, context, &inst, region_exit_block, &result](
+ opt::Instruction* use, uint32_t /*unused*/) -> bool {
+
+ // Find the block in which this id use occurs, recording the id as
+ // an output id if the block is outside the region, with some
+ // exceptions detailed below.
+ auto use_block = context->get_instr_block(use);
+
+ if (!use_block) {
+ // There might be no containing block, e.g. if the use is in a
+ // decoration.
+ return true;
+ }
+
+ if (region_set.count(use_block) != 0) {
+ // The use is in the region.
+ if (use_block != region_exit_block || !use->IsBlockTerminator()) {
+ // Furthermore, the use is not in the terminator of the region's
+ // exit block.
+ return true;
+ }
+ }
+
+ result.push_back(inst.result_id());
+ return false;
+ });
+ }
+ }
+ return result;
+}
+
+std::set<opt::BasicBlock*> TransformationOutlineFunction::GetRegionBlocks(
+ opt::IRContext* context, opt::BasicBlock* entry_block,
+ opt::BasicBlock* exit_block) {
+ auto enclosing_function = entry_block->GetParent();
+ auto dominator_analysis = context->GetDominatorAnalysis(enclosing_function);
+ auto postdominator_analysis =
+ context->GetPostDominatorAnalysis(enclosing_function);
+
+ std::set<opt::BasicBlock*> result;
+ for (auto& block : *enclosing_function) {
+ if (dominator_analysis->Dominates(entry_block, &block) &&
+ postdominator_analysis->Dominates(exit_block, &block)) {
+ result.insert(&block);
+ }
+ }
+ return result;
+}
+
+std::unique_ptr<opt::Function>
+TransformationOutlineFunction::PrepareFunctionPrototype(
+ opt::IRContext* context, const std::vector<uint32_t>& region_input_ids,
+ const std::vector<uint32_t>& region_output_ids,
+ const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map) const {
+ uint32_t return_type_id = 0;
+ uint32_t function_type_id = 0;
+
+ // First, try to find an existing function type that is suitable. This is
+ // only possible if the region generates no output ids; if it generates output
+ // ids we are going to make a new struct for those, and since that struct does
+ // not exist there cannot already be a function type with this struct as its
+ // return type.
+ if (region_output_ids.empty()) {
+ opt::analysis::Void void_type;
+ return_type_id = context->get_type_mgr()->GetId(&void_type);
+ std::vector<const opt::analysis::Type*> argument_types;
+ for (auto id : region_input_ids) {
+ argument_types.push_back(context->get_type_mgr()->GetType(
+ context->get_def_use_mgr()->GetDef(id)->type_id()));
+ }
+ opt::analysis::Function function_type(&void_type, argument_types);
+ function_type_id = context->get_type_mgr()->GetId(&function_type);
+ }
+
+ // If no existing function type was found, we need to create one.
+ if (function_type_id == 0) {
+ assert(
+ ((return_type_id == 0) == !region_output_ids.empty()) &&
+ "We should only have set the return type if there are no output ids.");
+ // If the region generates output ids, we need to make a struct with one
+ // field per output id.
+ if (!region_output_ids.empty()) {
+ opt::Instruction::OperandList struct_member_types;
+ for (uint32_t output_id : region_output_ids) {
+ auto output_id_type =
+ context->get_def_use_mgr()->GetDef(output_id)->type_id();
+ struct_member_types.push_back({SPV_OPERAND_TYPE_ID, {output_id_type}});
+ }
+ // Add a new struct type to the module.
+ context->module()->AddType(MakeUnique<opt::Instruction>(
+ context, SpvOpTypeStruct, 0,
+ message_.new_function_struct_return_type_id(),
+ std::move(struct_member_types)));
+ // The return type for the function is the newly-created struct.
+ return_type_id = message_.new_function_struct_return_type_id();
+ }
+ assert(
+ return_type_id != 0 &&
+ "We should either have a void return type, or have created a struct.");
+
+ // The region's input ids dictate the parameter types to the function.
+ opt::Instruction::OperandList function_type_operands;
+ function_type_operands.push_back({SPV_OPERAND_TYPE_ID, {return_type_id}});
+ for (auto id : region_input_ids) {
+ function_type_operands.push_back(
+ {SPV_OPERAND_TYPE_ID,
+ {context->get_def_use_mgr()->GetDef(id)->type_id()}});
+ }
+ // Add a new function type to the module, and record that this is the type
+ // id for the new function.
+ context->module()->AddType(MakeUnique<opt::Instruction>(
+ context, SpvOpTypeFunction, 0, message_.new_function_type_id(),
+ function_type_operands));
+ function_type_id = message_.new_function_type_id();
+ }
+
+ // Create a new function with |message_.new_function_id| as the function id,
+ // and the return type and function type prepared above.
+ std::unique_ptr<opt::Function> outlined_function =
+ MakeUnique<opt::Function>(MakeUnique<opt::Instruction>(
+ context, SpvOpFunction, return_type_id, message_.new_function_id(),
+ opt::Instruction::OperandList(
+ {{spv_operand_type_t ::SPV_OPERAND_TYPE_LITERAL_INTEGER,
+ {SpvFunctionControlMaskNone}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+ {function_type_id}}})));
+
+ // Add one parameter to the function for each input id, using the fresh ids
+ // provided in |input_id_to_fresh_id_map|.
+ for (auto id : region_input_ids) {
+ outlined_function->AddParameter(MakeUnique<opt::Instruction>(
+ context, SpvOpFunctionParameter,
+ context->get_def_use_mgr()->GetDef(id)->type_id(),
+ input_id_to_fresh_id_map.at(id), opt::Instruction::OperandList()));
+ }
+
+ return outlined_function;
+}
+
+void TransformationOutlineFunction::UpdateModuleIdBoundForFreshIds(
+ opt::IRContext* context,
+ const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
+ const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map) const {
+ // Enlarge the module's id bound as needed to accommodate the various fresh
+ // ids associated with the transformation.
+ fuzzerutil::UpdateModuleIdBound(
+ context, message_.new_function_struct_return_type_id());
+ fuzzerutil::UpdateModuleIdBound(context, message_.new_function_type_id());
+ fuzzerutil::UpdateModuleIdBound(context, message_.new_function_id());
+ fuzzerutil::UpdateModuleIdBound(context,
+ message_.new_function_region_entry_block());
+ fuzzerutil::UpdateModuleIdBound(context, message_.new_caller_result_id());
+ fuzzerutil::UpdateModuleIdBound(context, message_.new_callee_result_id());
+
+ for (auto& entry : input_id_to_fresh_id_map) {
+ fuzzerutil::UpdateModuleIdBound(context, entry.second);
+ }
+
+ for (auto& entry : output_id_to_fresh_id_map) {
+ fuzzerutil::UpdateModuleIdBound(context, entry.second);
+ }
+}
+
+void TransformationOutlineFunction::RemapInputAndOutputIdsInRegion(
+ opt::IRContext* context, const opt::BasicBlock& original_region_exit_block,
+ const std::set<opt::BasicBlock*>& region_blocks,
+ const std::vector<uint32_t>& region_input_ids,
+ const std::vector<uint32_t>& region_output_ids,
+ const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
+ const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map) const {
+ // Change all uses of input ids inside the region to the corresponding fresh
+ // ids that will ultimately be parameters of the outlined function.
+ // This is done by considering each region input id in turn.
+ for (uint32_t id : region_input_ids) {
+ // We then consider each use of the input id.
+ context->get_def_use_mgr()->ForEachUse(
+ id, [context, id, &input_id_to_fresh_id_map, region_blocks](
+ opt::Instruction* use, uint32_t operand_index) {
+ // Find the block in which this use of the input id occurs.
+ opt::BasicBlock* use_block = context->get_instr_block(use);
+ // We want to rewrite the use id if its block occurs in the outlined
+ // region.
+ if (region_blocks.count(use_block) != 0) {
+ // Rewrite this use of the input id.
+ use->SetOperand(operand_index, {input_id_to_fresh_id_map.at(id)});
+ }
+ });
+ }
+
+ // Change each definition of a region output id to define the corresponding
+ // fresh ids that will store intermediate value for the output ids. Also
+ // change all uses of the output id located in the outlined region.
+ // This is done by considering each region output id in turn.
+ for (uint32_t id : region_output_ids) {
+ // First consider each use of the output id and update the relevant uses.
+ context->get_def_use_mgr()->ForEachUse(
+ id,
+ [context, &original_region_exit_block, id, &output_id_to_fresh_id_map,
+ region_blocks](opt::Instruction* use, uint32_t operand_index) {
+ // Find the block in which this use of the output id occurs.
+ auto use_block = context->get_instr_block(use);
+ // We want to rewrite the use id if its block occurs in the outlined
+ // region, with one exception: the terminator of the exit block of
+ // the region is going to remain in the original function, so if the
+ // use appears in such a terminator instruction we leave it alone.
+ if (
+ // The block is in the region ...
+ region_blocks.count(use_block) != 0 &&
+ // ... and the use is not in the terminator instruction of the
+ // region's exit block.
+ !(use_block == &original_region_exit_block &&
+ use->IsBlockTerminator())) {
+ // Rewrite this use of the output id.
+ use->SetOperand(operand_index, {output_id_to_fresh_id_map.at(id)});
+ }
+ });
+
+ // Now change the instruction that defines the output id so that it instead
+ // defines the corresponding fresh id. We do this after changing all the
+ // uses so that the definition of the original id is still registered when
+ // we analyse its uses.
+ context->get_def_use_mgr()->GetDef(id)->SetResultId(
+ output_id_to_fresh_id_map.at(id));
+ }
+}
+
+void TransformationOutlineFunction::PopulateOutlinedFunction(
+ opt::IRContext* context, const opt::BasicBlock& original_region_entry_block,
+ const opt::BasicBlock& original_region_exit_block,
+ const std::set<opt::BasicBlock*>& region_blocks,
+ const std::vector<uint32_t>& region_output_ids,
+ const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map,
+ opt::Function* outlined_function) const {
+ // When we create the exit block for the outlined region, we use this pointer
+ // to track of it so that we can manipulate it later.
+ opt::BasicBlock* outlined_region_exit_block = nullptr;
+
+ // The region entry block in the new function is identical to the entry block
+ // of the region being outlined, except that it has
+ // |message_.new_function_region_entry_block| as its id.
+ std::unique_ptr<opt::BasicBlock> outlined_region_entry_block =
+ MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
+ context, SpvOpLabel, 0, message_.new_function_region_entry_block(),
+ opt::Instruction::OperandList()));
+ outlined_region_entry_block->SetParent(outlined_function);
+ if (&original_region_entry_block == &original_region_exit_block) {
+ outlined_region_exit_block = outlined_region_entry_block.get();
+ }
+
+ for (auto& inst : original_region_entry_block) {
+ outlined_region_entry_block->AddInstruction(
+ std::unique_ptr<opt::Instruction>(inst.Clone(context)));
+ }
+ outlined_function->AddBasicBlock(std::move(outlined_region_entry_block));
+
+ // We now go through the single-entry single-exit region defined by the entry
+ // and exit blocks, adding clones of all blocks to the new function.
+
+ // Consider every block in the enclosing function.
+ auto enclosing_function = original_region_entry_block.GetParent();
+ for (auto block_it = enclosing_function->begin();
+ block_it != enclosing_function->end();) {
+ // Skip the region's entry block - we already dealt with it above.
+ if (region_blocks.count(&*block_it) == 0 ||
+ &*block_it == &original_region_entry_block) {
+ ++block_it;
+ continue;
+ }
+ // Clone the block so that it can be added to the new function.
+ auto cloned_block =
+ std::unique_ptr<opt::BasicBlock>(block_it->Clone(context));
+
+ // If this is the region's exit block, then the cloned block is the outlined
+ // region's exit block.
+ if (&*block_it == &original_region_exit_block) {
+ assert(outlined_region_exit_block == nullptr &&
+ "We should not yet have encountered the exit block.");
+ outlined_region_exit_block = cloned_block.get();
+ }
+
+ cloned_block->SetParent(outlined_function);
+
+ // Redirect any OpPhi operands whose predecessors are the original region
+ // entry block to become the new function entry block.
+ cloned_block->ForEachPhiInst([this](opt::Instruction* phi_inst) {
+ for (uint32_t predecessor_index = 1;
+ predecessor_index < phi_inst->NumInOperands();
+ predecessor_index += 2) {
+ if (phi_inst->GetSingleWordInOperand(predecessor_index) ==
+ message_.entry_block()) {
+ phi_inst->SetInOperand(predecessor_index,
+ {message_.new_function_region_entry_block()});
+ }
+ }
+ });
+
+ outlined_function->AddBasicBlock(std::move(cloned_block));
+ block_it = block_it.Erase();
+ }
+ assert(outlined_region_exit_block != nullptr &&
+ "We should have encountered the region's exit block when iterating "
+ "through the function");
+
+ // We now need to adapt the exit block for the region - in the new function -
+ // so that it ends with a return.
+
+ // We first eliminate the merge instruction (if any) and the terminator for
+ // the cloned exit block.
+ for (auto inst_it = outlined_region_exit_block->begin();
+ inst_it != outlined_region_exit_block->end();) {
+ if (inst_it->opcode() == SpvOpLoopMerge ||
+ inst_it->opcode() == SpvOpSelectionMerge) {
+ inst_it = inst_it.Erase();
+ } else if (inst_it->IsBlockTerminator()) {
+ inst_it = inst_it.Erase();
+ } else {
+ ++inst_it;
+ }
+ }
+
+ // We now add either OpReturn or OpReturnValue as the cloned exit block's
+ // terminator.
+ if (region_output_ids.empty()) {
+ // The case where there are no region output ids is simple: we just add
+ // OpReturn.
+ outlined_region_exit_block->AddInstruction(MakeUnique<opt::Instruction>(
+ context, SpvOpReturn, 0, 0, opt::Instruction::OperandList()));
+ } else {
+ // In the case where there are output ids, we add an OpCompositeConstruct
+ // instruction to pack all the output values into a struct, and then an
+ // OpReturnValue instruction to return this struct.
+ opt::Instruction::OperandList struct_member_operands;
+ for (uint32_t id : region_output_ids) {
+ struct_member_operands.push_back(
+ {SPV_OPERAND_TYPE_ID, {output_id_to_fresh_id_map.at(id)}});
+ }
+ outlined_region_exit_block->AddInstruction(MakeUnique<opt::Instruction>(
+ context, SpvOpCompositeConstruct,
+ message_.new_function_struct_return_type_id(),
+ message_.new_callee_result_id(), struct_member_operands));
+ outlined_region_exit_block->AddInstruction(MakeUnique<opt::Instruction>(
+ context, SpvOpReturnValue, 0, 0,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {message_.new_callee_result_id()}}})));
+ }
+
+ outlined_function->SetFunctionEnd(MakeUnique<opt::Instruction>(
+ context, SpvOpFunctionEnd, 0, 0, opt::Instruction::OperandList()));
+}
+
+void TransformationOutlineFunction::ShrinkOriginalRegion(
+ opt::IRContext* context, std::set<opt::BasicBlock*>& region_blocks,
+ const std::vector<uint32_t>& region_input_ids,
+ const std::vector<uint32_t>& region_output_ids,
+ const std::map<uint32_t, uint32_t>& output_id_to_type_id,
+ uint32_t return_type_id,
+ std::unique_ptr<opt::Instruction> cloned_exit_block_merge,
+ std::unique_ptr<opt::Instruction> cloned_exit_block_terminator,
+ opt::BasicBlock* original_region_entry_block) const {
+ // Erase all blocks from the original function that are in the outlined
+ // region, except for the region's entry block.
+ //
+ // In the process, identify all references to the exit block of the region,
+ // as merge blocks, continue targets, or OpPhi predecessors, and rewrite them
+ // to refer to the region entry block (the single block to which we are
+ // shrinking the region).
+ auto enclosing_function = original_region_entry_block->GetParent();
+ for (auto block_it = enclosing_function->begin();
+ block_it != enclosing_function->end();) {
+ if (&*block_it == original_region_entry_block) {
+ ++block_it;
+ } else if (region_blocks.count(&*block_it) == 0) {
+ // The block is not in the region. Check whether it has the last block
+ // of the region as an OpPhi predecessor, and if so change the
+ // predecessor to be the first block of the region (i.e. the block
+ // containing the call to what was outlined).
+ assert(block_it->MergeBlockIdIfAny() != message_.exit_block() &&
+ "Outlined region must not end with a merge block");
+ assert(block_it->ContinueBlockIdIfAny() != message_.exit_block() &&
+ "Outlined region must not end with a continue target");
+ block_it->ForEachPhiInst([this](opt::Instruction* phi_inst) {
+ for (uint32_t predecessor_index = 1;
+ predecessor_index < phi_inst->NumInOperands();
+ predecessor_index += 2) {
+ if (phi_inst->GetSingleWordInOperand(predecessor_index) ==
+ message_.exit_block()) {
+ phi_inst->SetInOperand(predecessor_index, {message_.entry_block()});
+ }
+ }
+ });
+ ++block_it;
+ } else {
+ // The block is in the region and is not the region's entry block: kill
+ // it.
+ block_it = block_it.Erase();
+ }
+ }
+
+ // Now erase all instructions from the region's entry block, as they have
+ // been outlined.
+ for (auto inst_it = original_region_entry_block->begin();
+ inst_it != original_region_entry_block->end();) {
+ inst_it = inst_it.Erase();
+ }
+
+ // Now we add a call to the outlined function to the region's entry block.
+ opt::Instruction::OperandList function_call_operands;
+ function_call_operands.push_back(
+ {SPV_OPERAND_TYPE_ID, {message_.new_function_id()}});
+ // The function parameters are the region input ids.
+ for (auto input_id : region_input_ids) {
+ function_call_operands.push_back({SPV_OPERAND_TYPE_ID, {input_id}});
+ }
+
+ original_region_entry_block->AddInstruction(MakeUnique<opt::Instruction>(
+ context, SpvOpFunctionCall, return_type_id,
+ message_.new_caller_result_id(), function_call_operands));
+
+ // If there are output ids, the function call will return a struct. For each
+ // output id, we add an extract operation to pull the appropriate struct
+ // member out into an output id.
+ for (uint32_t index = 0; index < region_output_ids.size(); ++index) {
+ uint32_t output_id = region_output_ids[index];
+ original_region_entry_block->AddInstruction(MakeUnique<opt::Instruction>(
+ context, SpvOpCompositeExtract, output_id_to_type_id.at(output_id),
+ output_id,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {message_.new_caller_result_id()}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}}})));
+ }
+
+ // Finally, we terminate the block with the merge instruction (if any) that
+ // used to belong to the region's exit block, and the terminator that used
+ // to belong to the region's exit block.
+ if (cloned_exit_block_merge != nullptr) {
+ original_region_entry_block->AddInstruction(
+ std::move(cloned_exit_block_merge));
+ }
+ original_region_entry_block->AddInstruction(
+ std::move(cloned_exit_block_terminator));
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_outline_function.h b/source/fuzz/transformation_outline_function.h
new file mode 100644
index 0000000..784499d
--- /dev/null
+++ b/source/fuzz/transformation_outline_function.h
@@ -0,0 +1,222 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_OUTLINE_FUNCTION_H_
+#define SOURCE_FUZZ_TRANSFORMATION_OUTLINE_FUNCTION_H_
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationOutlineFunction : public Transformation {
+ public:
+ explicit TransformationOutlineFunction(
+ const protobufs::TransformationOutlineFunction& message);
+
+ TransformationOutlineFunction(
+ uint32_t entry_block, uint32_t exit_block,
+ uint32_t new_function_struct_return_type_id,
+ uint32_t new_function_type_id, uint32_t new_function_id,
+ uint32_t new_function_region_entry_block, uint32_t new_caller_result_id,
+ uint32_t new_callee_result_id,
+ std::map<uint32_t, uint32_t>&& input_id_to_fresh_id,
+ std::map<uint32_t, uint32_t>&& output_id_to_fresh_id);
+
+ // - All the fresh ids occurring in the transformation must be distinct and
+ // fresh
+ // - |message_.entry_block| and |message_.exit_block| must form a single-entry
+ // single-exit control flow graph region
+ // - |message_.entry_block| must not start with OpVariable
+ // - |message_.entry_block| must not be a loop header
+ // - |message_.exit_block| must not be a merge block or the continue target
+ // of a loop
+ // - A structured control flow construct must lie either completely within the
+ // region or completely outside it
+ // - |message.entry_block| must not start with OpPhi; this is to keep the
+ // transformation simple - another transformation should be used to split
+ // a desired entry block that starts with OpPhi if needed
+ // - |message_.input_id_to_fresh_id| must contain an entry for every id
+ // defined outside the region but used in the region
+ // - |message_.output_id_to_fresh_id| must contain an entry for every id
+ // defined in the region but used outside the region
+ bool IsApplicable(opt::IRContext* context,
+ const FactManager& fact_manager) const override;
+
+ // - A new function with id |message_.new_function_id| is added to the module.
+ // - If the region generates output ids, the return type of this function is
+ // a new struct type with one field per output id, and with type id
+ // |message_.new_function_struct_return_type|, otherwise the function return
+ // types is void and |message_.new_function_struct_return_type| is not used.
+ // - If the region generates input ids, the new function has one parameter per
+ // input id. Fresh ids for these parameters are provided by
+ // |message_.input_id_to_fresh_id|.
+ // - Unless the type required for the new function is already known,
+ // |message_.new_function_type_id| is used as the type id for a new function
+ // type, and the new function uses this type.
+ // - The new function starts with a dummy block with id
+ // |message_.new_function_first_block|, which jumps straight to a successor
+ // block, to avoid violating rules on what the first block in a function may
+ // look like.
+ // - The outlined region is replaced with a single block, with the same id
+ // as |message_.entry_block|, and which calls the new function, passing the
+ // region's input ids as parameters. The result is stored in
+ // |message_.new_caller_result_id|, which has type
+ // |message_.new_function_struct_return_type| (unless there are
+ // no output ids, in which case the return type is void). The components
+ // of this returned struct are then copied out into the region's output ids.
+ // The block ends with the merge instruction (if any) and terminator of
+ // |message_.exit_block|.
+ // - The body of the new function is identical to the outlined region, except
+ // that (a) the region's entry block has id
+ // |message_.new_function_region_entry_block|, (b) input id uses are
+ // replaced with parameter accesses, (c) and definitions of output ids are
+ // replaced with definitions of corresponding fresh ids provided by
+ // |message_.output_id_to_fresh_id|, and (d) the block of the function
+ // ends by returning a composite of type
+ // |message_.new_function_struct_return_type| comprised of all the fresh
+ // output ids (unless the return type is void, in which case no value is
+ // returned.
+ void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ // Returns the set of blocks dominated by |entry_block| and post-dominated
+ // by |exit_block|.
+ static std::set<opt::BasicBlock*> GetRegionBlocks(
+ opt::IRContext* context, opt::BasicBlock* entry_block,
+ opt::BasicBlock* exit_block);
+
+ // Yields ids that are used in |region_set| and that are either parameters
+ // to the function containing |region_set|, or are defined by blocks of this
+ // function that are outside |region_set|.
+ //
+ // Special cases: OpPhi instructions in |region_entry_block| and the
+ // terminator of |region_exit_block| do not get outlined, therefore
+ // - id uses in OpPhi instructions in |region_entry_block| are ignored
+ // - id uses in the terminator instruction of |region_exit_block| are ignored
+ static std::vector<uint32_t> GetRegionInputIds(
+ opt::IRContext* context, const std::set<opt::BasicBlock*>& region_set,
+ opt::BasicBlock* region_exit_block);
+
+ // Yields all ids that are defined in |region_set| and used outside
+ // |region_set|.
+ //
+ // Special cases: for similar reasons as for |GetRegionInputIds|,
+ // - ids defined in the region and used in the terminator of
+ // |region_exit_block| count as output ids
+ static std::vector<uint32_t> GetRegionOutputIds(
+ opt::IRContext* context, const std::set<opt::BasicBlock*>& region_set,
+ opt::BasicBlock* region_exit_block);
+
+ private:
+ // A helper method for the applicability check. Returns true if and only if
+ // |id| is (a) a fresh id for the module, and (b) an id that has not
+ // previously been subject to this check. We use this to check whether the
+ // ids given for the transformation are not only fresh but also different from
+ // one another.
+ bool CheckIdIsFreshAndNotUsedByThisTransformation(
+ uint32_t id, opt::IRContext* context,
+ std::set<uint32_t>* ids_used_by_this_transformation) const;
+
+ // Ensures that the module's id bound is at least the maximum of any fresh id
+ // associated with the transformation.
+ void UpdateModuleIdBoundForFreshIds(
+ opt::IRContext* context,
+ const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
+ const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map) const;
+
+ // Uses |input_id_to_fresh_id_map| and |output_id_to_fresh_id_map| to convert,
+ // in the region to be outlined, all the input ids in |region_input_ids| and
+ // the output ids in |region_output_ids| to their fresh counterparts.
+ // Parameters |region_blocks| provides access to the blocks that must be
+ // modified, and |original_region_exit_block| allows for some special cases
+ // where ids should not be remapped.
+ void RemapInputAndOutputIdsInRegion(
+ opt::IRContext* context,
+ const opt::BasicBlock& original_region_exit_block,
+ const std::set<opt::BasicBlock*>& region_blocks,
+ const std::vector<uint32_t>& region_input_ids,
+ const std::vector<uint32_t>& region_output_ids,
+ const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
+ const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map) const;
+
+ // Produce a Function object that has the right function type and parameter
+ // declarations. The function argument types and parameter ids are dictated
+ // by |region_input_ids| and |input_id_to_fresh_id_map|. The function return
+ // type is dictated by |region_output_ids|.
+ //
+ // A new struct type to represent the function return type, and a new function
+ // type for the function, will be added to the module (unless suitable types
+ // are already present).
+ std::unique_ptr<opt::Function> PrepareFunctionPrototype(
+ opt::IRContext* context, const std::vector<uint32_t>& region_input_ids,
+ const std::vector<uint32_t>& region_output_ids,
+ const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map) const;
+
+ // Creates the body of the outlined function by cloning blocks from the
+ // original region, given by |region_blocks|, adapting the cloned version
+ // of |original_region_exit_block| so that it returns something appropriate,
+ // and patching up branches to |original_region_entry_block| to refer to its
+ // clone. Parameters |region_output_ids| and |output_id_to_fresh_id_map| are
+ // used to determine what the function should return.
+ void PopulateOutlinedFunction(
+ opt::IRContext* context,
+ const opt::BasicBlock& original_region_entry_block,
+ const opt::BasicBlock& original_region_exit_block,
+ const std::set<opt::BasicBlock*>& region_blocks,
+ const std::vector<uint32_t>& region_output_ids,
+ const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map,
+ opt::Function* outlined_function) const;
+
+ // Shrinks the outlined region, given by |region_blocks|, down to the single
+ // block |original_region_entry_block|. This block is itself shrunk to just
+ // contain:
+ // - any OpPhi instructions that were originally present
+ // - a call to the outlined function, with parameters provided by
+ // |region_input_ids|
+ // - instructions to route components of the call's return value into
+ // |region_output_ids|
+ // - The merge instruction (if any) and terminator of the original region's
+ // exit block, given by |cloned_exit_block_merge| and
+ // |cloned_exit_block_terminator|
+ // Parameters |output_id_to_type_id| and |return_type_id| provide the
+ // provide types for the region's output ids, and the return type of the
+ // outlined function: as the module is in an inconsistent state when this
+ // function is called, this information cannot be gotten from the def-use
+ // manager.
+ void ShrinkOriginalRegion(
+ opt::IRContext* context, std::set<opt::BasicBlock*>& region_blocks,
+ const std::vector<uint32_t>& region_input_ids,
+ const std::vector<uint32_t>& region_output_ids,
+ const std::map<uint32_t, uint32_t>& output_id_to_type_id,
+ uint32_t return_type_id,
+ std::unique_ptr<opt::Instruction> cloned_exit_block_merge,
+ std::unique_ptr<opt::Instruction> cloned_exit_block_terminator,
+ opt::BasicBlock* original_region_entry_block) const;
+
+ protobufs::TransformationOutlineFunction message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_OUTLINE_FUNCTION_H_
diff --git a/source/operand.cpp b/source/operand.cpp
index cd26a3d..39d17a6 100644
--- a/source/operand.cpp
+++ b/source/operand.cpp
@@ -29,6 +29,7 @@
// per-item basis. https://github.com/KhronosGroup/SPIRV-Tools/issues/1195
#include "operand.kinds-unified1.inc"
+#include "spirv-tools/libspirv.h"
static const spv_operand_table_t kOperandTable = {
ARRAY_SIZE(pygen_variable_OperandInfoTable),
@@ -228,6 +229,18 @@
return "debug type qualifier";
case SPV_OPERAND_TYPE_DEBUG_OPERATION:
return "debug operation";
+ case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS:
+ return "OpenCL.DebugInfo.100 debug info flags";
+ case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
+ return "OpenCL.DebugInfo.100 debug base type encoding";
+ case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE:
+ return "OpenCL.DebugInfo.100 debug composite type";
+ case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER:
+ return "OpenCL.DebugInfo.100 debug type qualifier";
+ case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION:
+ return "OpenCL.DebugInfo.100 debug operation";
+ case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY:
+ return "OpenCL.DebugInfo.100 debug imported entity";
// The next values are for values returned from an instruction, not actually
// an operand. So the specific strings don't matter. But let's add them
@@ -312,6 +325,11 @@
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:
return true;
default:
break;
@@ -328,6 +346,7 @@
case SPV_OPERAND_TYPE_FUNCTION_CONTROL:
case SPV_OPERAND_TYPE_MEMORY_ACCESS:
case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS:
+ case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS:
return true;
default:
break;
diff --git a/source/opt/block_merge_util.cpp b/source/opt/block_merge_util.cpp
index 107723d..263a069 100644
--- a/source/opt/block_merge_util.cpp
+++ b/source/opt/block_merge_util.cpp
@@ -49,6 +49,18 @@
return IsMerge(context, block->id());
}
+// Returns true if |id| is the continue target of a merge instruction.
+bool IsContinue(IRContext* context, uint32_t id) {
+ return !context->get_def_use_mgr()->WhileEachUse(
+ id, [](Instruction* user, uint32_t index) {
+ SpvOp op = user->opcode();
+ if (op == SpvOpLoopMerge && index == 1u) {
+ return false;
+ }
+ return true;
+ });
+}
+
// Removes any OpPhi instructions in |block|, which should have exactly one
// predecessor, replacing uses of OpPhi ids with the ids associated with the
// predecessor.
@@ -86,6 +98,11 @@
return false;
}
+ if (pred_is_merge && IsContinue(context, lab_id)) {
+ // Cannot merge a continue target with a merge block.
+ return false;
+ }
+
// Don't bother trying to merge unreachable blocks.
if (auto dominators = context->GetDominatorAnalysis(block->GetParent())) {
if (!dominators->IsReachable(block)) return false;
diff --git a/source/opt/convert_to_half_pass.cpp b/source/opt/convert_to_half_pass.cpp
index 4c02c73..5022e1b 100644
--- a/source/opt/convert_to_half_pass.cpp
+++ b/source/opt/convert_to_half_pass.cpp
@@ -42,7 +42,7 @@
return Pass::IsFloat(ty_id, width);
}
-bool ConvertToHalfPass::IsRelaxed(Instruction* inst) {
+bool ConvertToHalfPass::IsDecoratedRelaxed(Instruction* inst) {
uint32_t r_id = inst->result_id();
for (auto r_inst : get_decoration_mgr()->GetDecorationsFor(r_id, false))
if (r_inst->opcode() == SpvOpDecorate &&
@@ -51,6 +51,12 @@
return false;
}
+bool ConvertToHalfPass::IsRelaxed(uint32_t id) {
+ return relaxed_ids_set_.count(id) > 0;
+}
+
+void ConvertToHalfPass::AddRelaxed(uint32_t id) { relaxed_ids_set_.insert(id); }
+
analysis::Type* ConvertToHalfPass::FloatScalarType(uint32_t width) {
analysis::Float float_ty(width);
return context()->get_type_mgr()->GetRegisteredType(&float_ty);
@@ -87,16 +93,19 @@
}
void ConvertToHalfPass::GenConvert(uint32_t* val_idp, uint32_t width,
- InstructionBuilder* builder) {
+ Instruction* inst) {
Instruction* val_inst = get_def_use_mgr()->GetDef(*val_idp);
uint32_t ty_id = val_inst->type_id();
uint32_t nty_id = EquivFloatTypeId(ty_id, width);
if (nty_id == ty_id) return;
Instruction* cvt_inst;
+ InstructionBuilder builder(
+ context(), inst,
+ IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
if (val_inst->opcode() == SpvOpUndef)
- cvt_inst = builder->AddNullaryOp(nty_id, SpvOpUndef);
+ cvt_inst = builder.AddNullaryOp(nty_id, SpvOpUndef);
else
- cvt_inst = builder->AddUnaryOp(nty_id, SpvOpFConvert, *val_idp);
+ cvt_inst = builder.AddUnaryOp(nty_id, SpvOpFConvert, *val_idp);
*val_idp = cvt_inst->result_id();
}
@@ -153,17 +162,15 @@
bool modified = false;
// Convert all float32 based operands to float16 equivalent and change
// instruction type to float16 equivalent.
- InstructionBuilder builder(
- context(), inst,
- IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
- inst->ForEachInId([&builder, &modified, this](uint32_t* idp) {
+ inst->ForEachInId([&inst, &modified, this](uint32_t* idp) {
Instruction* op_inst = get_def_use_mgr()->GetDef(*idp);
if (!IsFloat(op_inst, 32)) return;
- GenConvert(idp, 16, &builder);
+ GenConvert(idp, 16, inst);
modified = true;
});
if (IsFloat(inst, 32)) {
inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
+ converted_ids_.insert(inst->result_id());
modified = true;
}
if (modified) get_def_use_mgr()->AnalyzeInstUse(inst);
@@ -171,23 +178,10 @@
}
bool ConvertToHalfPass::ProcessPhi(Instruction* inst) {
- // Skip if not float32
- if (!IsFloat(inst, 32)) return false;
- // Skip if no relaxed operands.
- bool relaxed_found = false;
- uint32_t ocnt = 0;
- inst->ForEachInId([&ocnt, &relaxed_found, this](uint32_t* idp) {
- if (ocnt % 2 == 0) {
- Instruction* val_inst = get_def_use_mgr()->GetDef(*idp);
- if (IsRelaxed(val_inst)) relaxed_found = true;
- }
- ++ocnt;
- });
- if (!relaxed_found) return false;
// Add float16 converts of any float32 operands and change type
// of phi to float16 equivalent. Operand converts need to be added to
// preceeding blocks.
- ocnt = 0;
+ uint32_t ocnt = 0;
uint32_t* prev_idp;
inst->ForEachInId([&ocnt, &prev_idp, this](uint32_t* idp) {
if (ocnt % 2 == 0) {
@@ -203,65 +197,32 @@
insert_before->opcode() != SpvOpLoopMerge)
++insert_before;
}
- InstructionBuilder builder(context(), &*insert_before,
- IRContext::kAnalysisDefUse |
- IRContext::kAnalysisInstrToBlockMapping);
- GenConvert(prev_idp, 16, &builder);
+ GenConvert(prev_idp, 16, &*insert_before);
}
}
++ocnt;
});
inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
get_def_use_mgr()->AnalyzeInstUse(inst);
+ converted_ids_.insert(inst->result_id());
return true;
}
-bool ConvertToHalfPass::ProcessExtract(Instruction* inst) {
- bool modified = false;
- uint32_t comp_id = inst->GetSingleWordInOperand(0);
- Instruction* comp_inst = get_def_use_mgr()->GetDef(comp_id);
- // If extract is relaxed float32 based type and the composite is a relaxed
- // float32 based type, convert it to float16 equivalent. This is slightly
- // aggressive and pushes any likely conversion to apply to the whole
- // composite rather than apply to each extracted component later. This
- // can be a win if the platform can convert the entire composite in the same
- // time as one component. It risks converting components that may not be
- // used, although empirical data on a large set of real-world shaders seems
- // to suggest this is not common and the composite convert is the best choice.
- if (IsFloat(inst, 32) && IsRelaxed(inst) && IsFloat(comp_inst, 32) &&
- IsRelaxed(comp_inst)) {
- InstructionBuilder builder(
- context(), inst,
- IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
- GenConvert(&comp_id, 16, &builder);
- inst->SetInOperand(0, {comp_id});
- comp_inst = get_def_use_mgr()->GetDef(comp_id);
- modified = true;
- }
- // If the composite is a float16 based type, make sure the type of the
- // extract agrees.
- if (IsFloat(comp_inst, 16) && !IsFloat(inst, 16)) {
- inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
- modified = true;
- }
- if (modified) get_def_use_mgr()->AnalyzeInstUse(inst);
- return modified;
-}
-
bool ConvertToHalfPass::ProcessConvert(Instruction* inst) {
// If float32 and relaxed, change to float16 convert
- if (IsFloat(inst, 32) && IsRelaxed(inst)) {
+ if (IsFloat(inst, 32) && IsRelaxed(inst->result_id())) {
inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
get_def_use_mgr()->AnalyzeInstUse(inst);
+ converted_ids_.insert(inst->result_id());
}
- // If operand and result types are the same, replace result with operand
- // and change convert to copy to keep validator happy; DCE will clean it up
+ // If operand and result types are the same, change FConvert to CopyObject to
+ // keep validator happy; simplification and DCE will clean it up
+ // One way this can happen is if an FConvert generated during this pass
+ // (likely by ProcessPhi) is later encountered here and its operand has been
+ // changed to half.
uint32_t val_id = inst->GetSingleWordInOperand(0);
Instruction* val_inst = get_def_use_mgr()->GetDef(val_id);
- if (inst->type_id() == val_inst->type_id()) {
- context()->ReplaceAllUsesWith(inst->result_id(), val_id);
- inst->SetOpcode(SpvOpCopyObject);
- }
+ if (inst->type_id() == val_inst->type_id()) inst->SetOpcode(SpvOpCopyObject);
return true; // modified
}
@@ -270,12 +231,8 @@
// If image reference, only need to convert dref args back to float32
if (dref_image_ops_.count(inst->opcode()) != 0) {
uint32_t dref_id = inst->GetSingleWordInOperand(kImageSampleDrefIdInIdx);
- Instruction* dref_inst = get_def_use_mgr()->GetDef(dref_id);
- if (IsFloat(dref_inst, 16) && IsRelaxed(dref_inst)) {
- InstructionBuilder builder(
- context(), inst,
- IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
- GenConvert(&dref_id, 32, &builder);
+ if (converted_ids_.count(dref_id) > 0) {
+ GenConvert(&dref_id, 32, inst);
inst->SetInOperand(kImageSampleDrefIdInIdx, {dref_id});
get_def_use_mgr()->AnalyzeInstUse(inst);
modified = true;
@@ -288,32 +245,24 @@
bool modified = false;
// If non-relaxed instruction has changed operands, need to convert
// them back to float32
- InstructionBuilder builder(
- context(), inst,
- IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
- inst->ForEachInId([&builder, &modified, this](uint32_t* idp) {
- Instruction* op_inst = get_def_use_mgr()->GetDef(*idp);
- if (!IsFloat(op_inst, 16)) return;
- if (!IsRelaxed(op_inst)) return;
+ inst->ForEachInId([&inst, &modified, this](uint32_t* idp) {
+ if (converted_ids_.count(*idp) == 0) return;
uint32_t old_id = *idp;
- GenConvert(idp, 32, &builder);
+ GenConvert(idp, 32, inst);
if (*idp != old_id) modified = true;
});
if (modified) get_def_use_mgr()->AnalyzeInstUse(inst);
return modified;
}
-bool ConvertToHalfPass::GenHalfCode(Instruction* inst) {
+bool ConvertToHalfPass::GenHalfInst(Instruction* inst) {
bool modified = false;
// Remember id for later deletion of RelaxedPrecision decoration
- bool inst_relaxed = IsRelaxed(inst);
- if (inst_relaxed) relaxed_ids_.push_back(inst->result_id());
+ bool inst_relaxed = IsRelaxed(inst->result_id());
if (IsArithmetic(inst) && inst_relaxed)
modified = GenHalfArith(inst);
- else if (inst->opcode() == SpvOpPhi)
+ else if (inst->opcode() == SpvOpPhi && inst_relaxed)
modified = ProcessPhi(inst);
- else if (inst->opcode() == SpvOpCompositeExtract)
- modified = ProcessExtract(inst);
else if (inst->opcode() == SpvOpFConvert)
modified = ProcessConvert(inst);
else if (image_ops_.count(inst->opcode()) != 0)
@@ -323,13 +272,62 @@
return modified;
}
+bool ConvertToHalfPass::CloseRelaxInst(Instruction* inst) {
+ if (inst->result_id() == 0) return false;
+ if (IsRelaxed(inst->result_id())) return false;
+ if (!IsFloat(inst, 32)) return false;
+ if (IsDecoratedRelaxed(inst)) {
+ AddRelaxed(inst->result_id());
+ return true;
+ }
+ if (closure_ops_.count(inst->opcode()) == 0) return false;
+ // Can relax if all float operands are relaxed
+ bool relax = true;
+ inst->ForEachInId([&relax, this](uint32_t* idp) {
+ Instruction* op_inst = get_def_use_mgr()->GetDef(*idp);
+ if (!IsFloat(op_inst, 32)) return;
+ if (!IsRelaxed(*idp)) relax = false;
+ });
+ if (relax) {
+ AddRelaxed(inst->result_id());
+ return true;
+ }
+ // Can relax if all uses are relaxed
+ relax = true;
+ get_def_use_mgr()->ForEachUser(inst, [&relax, this](Instruction* uinst) {
+ if (uinst->result_id() == 0 || !IsFloat(uinst, 32) ||
+ (!IsDecoratedRelaxed(uinst) && !IsRelaxed(uinst->result_id()))) {
+ relax = false;
+ return;
+ }
+ });
+ if (relax) {
+ AddRelaxed(inst->result_id());
+ return true;
+ }
+ return false;
+}
+
bool ConvertToHalfPass::ProcessFunction(Function* func) {
+ // Do a closure of Relaxed on composite and phi instructions
+ bool changed = true;
+ while (changed) {
+ changed = false;
+ cfg()->ForEachBlockInReversePostOrder(
+ func->entry().get(), [&changed, this](BasicBlock* bb) {
+ for (auto ii = bb->begin(); ii != bb->end(); ++ii)
+ changed |= CloseRelaxInst(&*ii);
+ });
+ }
+ // Do convert of relaxed instructions to half precision
bool modified = false;
cfg()->ForEachBlockInReversePostOrder(
func->entry().get(), [&modified, this](BasicBlock* bb) {
for (auto ii = bb->begin(); ii != bb->end(); ++ii)
- modified |= GenHalfCode(&*ii);
+ modified |= GenHalfInst(&*ii);
});
+ // Replace invalid converts of matrix into equivalent vector extracts,
+ // converts and finally a composite construct
cfg()->ForEachBlockInReversePostOrder(
func->entry().get(), [&modified, this](BasicBlock* bb) {
for (auto ii = bb->begin(); ii != bb->end(); ++ii)
@@ -346,7 +344,7 @@
// If modified, make sure module has Float16 capability
if (modified) context()->AddCapability(SpvCapabilityFloat16);
// Remove all RelaxedPrecision decorations from instructions and globals
- for (auto c_id : relaxed_ids_) RemoveRelaxedDecoration(c_id);
+ for (auto c_id : relaxed_ids_set_) RemoveRelaxedDecoration(c_id);
for (auto& val : get_module()->types_values()) {
uint32_t v_id = val.result_id();
if (v_id != 0) RemoveRelaxedDecoration(v_id);
@@ -366,6 +364,7 @@
SpvOpVectorShuffle,
SpvOpCompositeConstruct,
SpvOpCompositeInsert,
+ SpvOpCompositeExtract,
SpvOpCopyObject,
SpvOpTranspose,
SpvOpConvertSToF,
@@ -453,7 +452,19 @@
SpvOpImageSparseSampleProjDrefExplicitLod,
SpvOpImageSparseDrefGather,
};
- relaxed_ids_.clear();
+ closure_ops_ = {
+ SpvOpVectorExtractDynamic,
+ SpvOpVectorInsertDynamic,
+ SpvOpVectorShuffle,
+ SpvOpCompositeConstruct,
+ SpvOpCompositeInsert,
+ SpvOpCompositeExtract,
+ SpvOpCopyObject,
+ SpvOpTranspose,
+ SpvOpPhi,
+ };
+ relaxed_ids_set_.clear();
+ converted_ids_.clear();
}
} // namespace opt
diff --git a/source/opt/convert_to_half_pass.h b/source/opt/convert_to_half_pass.h
index 5225848..143aebf 100644
--- a/source/opt/convert_to_half_pass.h
+++ b/source/opt/convert_to_half_pass.h
@@ -38,7 +38,8 @@
const char* name() const override { return "convert-to-half-pass"; }
private:
- // Return true if |inst| is an arithmetic op that can be of type float16
+ // Return true if |inst| is an arithmetic, composite or phi op that can be
+ // of type float16
bool IsArithmetic(Instruction* inst);
// Return true if |inst| returns scalar, vector or matrix type with base
@@ -46,7 +47,13 @@
bool IsFloat(Instruction* inst, uint32_t width);
// Return true if |inst| is decorated with RelaxedPrecision
- bool IsRelaxed(Instruction* inst);
+ bool IsDecoratedRelaxed(Instruction* inst);
+
+ // Return true if |id| has been added to the relaxed id set
+ bool IsRelaxed(uint32_t id);
+
+ // Add |id| to the relaxed id set
+ void AddRelaxed(uint32_t id);
// Return type id for float with |width|
analysis::Type* FloatScalarType(uint32_t width);
@@ -64,19 +71,23 @@
// Append instructions to builder to convert value |*val_idp| to type
// |ty_id| but with |width|. Set |*val_idp| to the new id.
- void GenConvert(uint32_t* val_idp, uint32_t width,
- InstructionBuilder* builder);
+ void GenConvert(uint32_t* val_idp, uint32_t width, Instruction* inst);
// Remove RelaxedPrecision decoration of |id|.
void RemoveRelaxedDecoration(uint32_t id);
+ // Add |inst| to relaxed instruction set if warranted. Specifically, if
+ // it is float32 and either decorated relaxed or a composite or phi
+ // instruction where all operands are relaxed or all uses are relaxed.
+ bool CloseRelaxInst(Instruction* inst);
+
// If |inst| is an arithmetic, phi, extract or convert instruction of float32
// base type and decorated with RelaxedPrecision, change it to the equivalent
// float16 based type instruction. Specifically, insert instructions to
// convert all operands to float16 (if needed) and change its type to the
// equivalent float16 type. Otherwise, insert instructions to convert its
// operands back to their original types, if needed.
- bool GenHalfCode(Instruction* inst);
+ bool GenHalfInst(Instruction* inst);
// Gen code for relaxed arithmetic |inst|
bool GenHalfArith(Instruction* inst);
@@ -84,9 +95,6 @@
// Gen code for relaxed phi |inst|
bool ProcessPhi(Instruction* inst);
- // Gen code for relaxed extract |inst|
- bool ProcessExtract(Instruction* inst);
-
// Gen code for relaxed convert |inst|
bool ProcessConvert(Instruction* inst);
@@ -98,11 +106,11 @@
// If |inst| is an FConvert of a matrix type, decompose it to a series
// of vector extracts, converts and inserts into an Undef. These are
- // generated by GenHalfCode because they are easier to manipulate, but are
+ // generated by GenHalfInst because they are easier to manipulate, but are
// invalid so we need to clean them up.
bool MatConvertCleanup(Instruction* inst);
- // Call GenHalfCode on every instruction in |func|.
+ // Call GenHalfInst on every instruction in |func|.
// If code is generated for an instruction, replace the instruction
// with the new instructions that are generated.
bool ProcessFunction(Function* func);
@@ -124,8 +132,14 @@
// Set of dref sample operations
std::unordered_set<uint32_t> dref_image_ops_;
+ // Set of dref sample operations
+ std::unordered_set<uint32_t> closure_ops_;
+
+ // Set of ids of all relaxed instructions
+ std::unordered_set<uint32_t> relaxed_ids_set_;
+
// Ids of all converted instructions
- std::vector<uint32_t> relaxed_ids_;
+ std::unordered_set<uint32_t> converted_ids_;
};
} // namespace opt
diff --git a/source/opt/folding_rules.cpp b/source/opt/folding_rules.cpp
index de740ca..1c8cdc8 100644
--- a/source/opt/folding_rules.cpp
+++ b/source/opt/folding_rules.cpp
@@ -553,7 +553,6 @@
const analysis::Constant* input1,
const analysis::Constant* input2) {
assert(input1 && input2);
- assert(input1->type() == input2->type());
const analysis::Type* type = input1->type();
std::vector<uint32_t> words;
if (const analysis::Vector* vector_type = type->AsVector()) {
@@ -1502,60 +1501,64 @@
};
}
-FoldingRule CompositeExtractFeedingConstruct() {
- // If the OpCompositeConstruct is simply putting back together elements that
- // where extracted from the same souce, we can simlpy reuse the source.
- //
- // This is a common code pattern because of the way that scalar replacement
- // works.
- return [](IRContext* context, Instruction* inst,
- const std::vector<const analysis::Constant*>&) {
- assert(inst->opcode() == SpvOpCompositeConstruct &&
- "Wrong opcode. Should be OpCompositeConstruct.");
- analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
- uint32_t original_id = 0;
+// If the OpCompositeConstruct is simply putting back together elements that
+// where extracted from the same source, we can simply reuse the source.
+//
+// This is a common code pattern because of the way that scalar replacement
+// works.
+bool CompositeExtractFeedingConstruct(
+ IRContext* context, Instruction* inst,
+ const std::vector<const analysis::Constant*>&) {
+ assert(inst->opcode() == SpvOpCompositeConstruct &&
+ "Wrong opcode. Should be OpCompositeConstruct.");
+ analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+ uint32_t original_id = 0;
- // Check each element to make sure they are:
- // - extractions
- // - extracting the same position they are inserting
- // - all extract from the same id.
- for (uint32_t i = 0; i < inst->NumInOperands(); ++i) {
- uint32_t element_id = inst->GetSingleWordInOperand(i);
- Instruction* element_inst = def_use_mgr->GetDef(element_id);
+ if (inst->NumInOperands() == 0) {
+ // The struct being constructed has no members.
+ return false;
+ }
- if (element_inst->opcode() != SpvOpCompositeExtract) {
- return false;
- }
+ // Check each element to make sure they are:
+ // - extractions
+ // - extracting the same position they are inserting
+ // - all extract from the same id.
+ 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 (element_inst->NumInOperands() != 2) {
- return false;
- }
-
- if (element_inst->GetSingleWordInOperand(1) != i) {
- return false;
- }
-
- if (i == 0) {
- original_id =
- element_inst->GetSingleWordInOperand(kExtractCompositeIdInIdx);
- } else if (original_id != element_inst->GetSingleWordInOperand(
- kExtractCompositeIdInIdx)) {
- return false;
- }
- }
-
- // 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()) {
+ if (element_inst->opcode() != SpvOpCompositeExtract) {
return false;
}
- // Simplify by using the original object.
- inst->SetOpcode(SpvOpCopyObject);
- inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {original_id}}});
- return true;
- };
+ if (element_inst->NumInOperands() != 2) {
+ return false;
+ }
+
+ if (element_inst->GetSingleWordInOperand(1) != i) {
+ return false;
+ }
+
+ if (i == 0) {
+ original_id =
+ element_inst->GetSingleWordInOperand(kExtractCompositeIdInIdx);
+ } else if (original_id !=
+ element_inst->GetSingleWordInOperand(kExtractCompositeIdInIdx)) {
+ return false;
+ }
+ }
+
+ // 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()) {
+ return false;
+ }
+
+ // Simplify by using the original object.
+ inst->SetOpcode(SpvOpCopyObject);
+ inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {original_id}}});
+ return true;
}
FoldingRule InsertFeedingExtract() {
@@ -2420,7 +2423,7 @@
// 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_[SpvOpCompositeConstruct].push_back(CompositeExtractFeedingConstruct());
+ rules_[SpvOpCompositeConstruct].push_back(CompositeExtractFeedingConstruct);
rules_[SpvOpCompositeExtract].push_back(InsertFeedingExtract());
rules_[SpvOpCompositeExtract].push_back(CompositeConstructFeedingExtract());
diff --git a/source/opt/graphics_robust_access_pass.cpp b/source/opt/graphics_robust_access_pass.cpp
index e309d3a..22c979c 100644
--- a/source/opt/graphics_robust_access_pass.cpp
+++ b/source/opt/graphics_robust_access_pass.cpp
@@ -121,6 +121,10 @@
// the result of an OpArrayLength instruction acting on the pointer of
// the containing structure as noted above.
//
+// - Access chain indices are always treated as signed, so:
+// - Clamp the upper bound at the signed integer maximum.
+// - Use SClamp for all clamping.
+//
// - TODO(dneto): OpImageTexelPointer:
// - Clamp coordinate to the image size returned by OpImageQuerySize
// - If multi-sampled, clamp the sample index to the count returned by
@@ -141,6 +145,7 @@
#include <cstring>
#include <functional>
#include <initializer_list>
+#include <limits>
#include <utility>
#include "constants.h"
@@ -246,6 +251,7 @@
}
for (auto* inst : access_chains) {
ClampIndicesForAccessChain(inst);
+ if (module_status_.failed) return module_status_.modified;
}
for (auto* inst : image_texel_pointers) {
@@ -261,6 +267,8 @@
auto* constant_mgr = context()->get_constant_mgr();
auto* def_use_mgr = context()->get_def_use_mgr();
auto* type_mgr = context()->get_type_mgr();
+ const bool have_int64_cap =
+ context()->get_feature_mgr()->HasCapability(SpvCapabilityInt64);
// Replaces one of the OpAccessChain index operands with a new value.
// Updates def-use analysis.
@@ -268,37 +276,66 @@
Instruction* new_value) {
inst.SetOperand(operand_index, {new_value->result_id()});
def_use_mgr->AnalyzeInstUse(&inst);
+ return SPV_SUCCESS;
};
// Replaces one of the OpAccesssChain index operands with a clamped value.
// Replace the operand at |operand_index| with the value computed from
- // unsigned_clamp(%old_value, %min_value, %max_value). It also analyzes
+ // signed_clamp(%old_value, %min_value, %max_value). It also analyzes
// the new instruction and records that them module is modified.
- auto clamp_index = [&inst, this, &replace_index](
+ // Assumes %min_value is signed-less-or-equal than %max_value. (All callees
+ // use 0 for %min_value).
+ auto clamp_index = [&inst, type_mgr, this, &replace_index](
uint32_t operand_index, Instruction* old_value,
Instruction* min_value, Instruction* max_value) {
- auto* clamp_inst = MakeClampInst(old_value, min_value, max_value, &inst);
- replace_index(operand_index, clamp_inst);
+ auto* clamp_inst =
+ MakeSClampInst(*type_mgr, old_value, min_value, max_value, &inst);
+ return replace_index(operand_index, clamp_inst);
};
// Ensures the specified index of access chain |inst| has a value that is
// at most |count| - 1. If the index is already a constant value less than
// |count| then no change is made.
- auto clamp_to_literal_count = [&inst, this, &constant_mgr, &type_mgr,
- &replace_index, &clamp_index](
- uint32_t operand_index, uint64_t count) {
+ auto clamp_to_literal_count =
+ [&inst, this, &constant_mgr, &type_mgr, have_int64_cap, &replace_index,
+ &clamp_index](uint32_t operand_index, uint64_t count) -> spv_result_t {
Instruction* index_inst =
this->GetDef(inst.GetSingleWordOperand(operand_index));
const auto* index_type =
type_mgr->GetType(index_inst->type_id())->AsInteger();
assert(index_type);
+ const auto index_width = index_type->width();
+
if (count <= 1) {
// Replace the index with 0.
- replace_index(operand_index, GetValueForType(0, index_type));
- return;
+ return replace_index(operand_index, GetValueForType(0, index_type));
}
- const auto index_width = index_type->width();
+ uint64_t maxval = count - 1;
+
+ // Compute the bit width of a viable type to hold |maxval|.
+ // Look for a bit width, up to 64 bits wide, to fit maxval.
+ uint32_t maxval_width = index_width;
+ while ((maxval_width < 64) && (0 != (maxval >> maxval_width))) {
+ maxval_width *= 2;
+ }
+ // Determine the type for |maxval|.
+ analysis::Integer signed_type_for_query(maxval_width, true);
+ auto* maxval_type =
+ type_mgr->GetRegisteredType(&signed_type_for_query)->AsInteger();
+ // Access chain indices are treated as signed, so limit the maximum value
+ // of the index so it will always be positive for a signed clamp operation.
+ maxval = std::min(maxval, ((uint64_t(1) << (maxval_width - 1)) - 1));
+
+ if (index_width > 64) {
+ return this->Fail() << "Can't handle indices wider than 64 bits, found "
+ "constant index with "
+ << index_width << " bits as index number "
+ << operand_index << " of access chain "
+ << inst.PrettyPrint();
+ }
+
+ // Split into two cases: the current index is a constant, or not.
// If the index is a constant then |index_constant| will not be a null
// pointer. (If index is an |OpConstantNull| then it |index_constant| will
@@ -313,55 +350,62 @@
value = int64_t(int_index_constant->GetS32BitValue());
} else if (index_width <= 64) {
value = int_index_constant->GetS64BitValue();
- } else {
- this->Fail() << "Can't handle indices wider than 64 bits, found "
- "constant index with "
- << index_type->width() << "bits";
- return;
}
if (value < 0) {
- replace_index(operand_index, GetValueForType(0, index_type));
- } else if (uint64_t(value) < count) {
+ return replace_index(operand_index, GetValueForType(0, index_type));
+ } else if (uint64_t(value) <= maxval) {
// Nothing to do.
- return;
+ return SPV_SUCCESS;
} else {
- // Replace with count - 1.
+ // Replace with maxval.
assert(count > 0); // Already took care of this case above.
- replace_index(operand_index, GetValueForType(count - 1, index_type));
+ return replace_index(operand_index,
+ GetValueForType(maxval, maxval_type));
}
} else {
// Generate a clamp instruction.
-
- // Compute the bit width of a viable type to hold (count-1).
- const auto maxval = count - 1;
- const auto* maxval_type = index_type;
- // Look for a bit width, up to 64 bits wide, to fit maxval.
- uint32_t maxval_width = index_width;
- while ((maxval_width < 64) && (0 != (maxval >> maxval_width))) {
- maxval_width *= 2;
+ assert(maxval >= 1);
+ assert(index_width <= 64); // Otherwise, already returned above.
+ if (index_width >= 64 && !have_int64_cap) {
+ // An inconsistent module.
+ return Fail() << "Access chain index is wider than 64 bits, but Int64 "
+ "is not declared: "
+ << index_inst->PrettyPrint();
}
// Widen the index value if necessary
if (maxval_width > index_width) {
- // Find the wider type. We only need this case if a constant (array)
- // bound is too big. This never requires us to *add* a capability
- // declaration for Int64 because the existence of the array bound would
- // already have required that declaration.
+ // Find the wider type. We only need this case if a constant array
+ // bound is too big.
+
+ // From how we calculated maxval_width, widening won't require adding
+ // the Int64 capability.
+ assert(have_int64_cap || maxval_width <= 32);
+ if (!have_int64_cap && maxval_width >= 64) {
+ // Be defensive, but this shouldn't happen.
+ return this->Fail()
+ << "Clamping index would require adding Int64 capability. "
+ << "Can't clamp 32-bit index " << operand_index
+ << " of access chain " << inst.PrettyPrint();
+ }
index_inst = WidenInteger(index_type->IsSigned(), maxval_width,
index_inst, &inst);
- maxval_type = type_mgr->GetType(index_inst->type_id())->AsInteger();
}
+
// Finally, clamp the index.
- clamp_index(operand_index, index_inst, GetValueForType(0, maxval_type),
- GetValueForType(maxval, maxval_type));
+ return clamp_index(operand_index, index_inst,
+ GetValueForType(0, maxval_type),
+ GetValueForType(maxval, maxval_type));
}
+ return SPV_SUCCESS;
};
// Ensures the specified index of access chain |inst| has a value that is at
// most the value of |count_inst| minus 1, where |count_inst| is treated as an
- // unsigned integer.
+ // unsigned integer. This can log a failure.
auto clamp_to_count = [&inst, this, &constant_mgr, &clamp_to_literal_count,
- &clamp_index, &type_mgr](uint32_t operand_index,
- Instruction* count_inst) {
+ &clamp_index,
+ &type_mgr](uint32_t operand_index,
+ Instruction* count_inst) -> spv_result_t {
Instruction* index_inst =
this->GetDef(inst.GetSingleWordOperand(operand_index));
const auto* index_type =
@@ -378,12 +422,11 @@
} else if (width <= 64) {
value = count_constant->AsIntConstant()->GetU64BitValue();
} else {
- this->Fail() << "Can't handle indices wider than 64 bits, found "
- "constant index with "
- << index_type->width() << "bits";
- return;
+ return this->Fail() << "Can't handle indices wider than 64 bits, found "
+ "constant index with "
+ << index_type->width() << "bits";
}
- clamp_to_literal_count(operand_index, value);
+ return clamp_to_literal_count(operand_index, value);
} else {
// Widen them to the same width.
const auto index_width = index_type->width();
@@ -406,9 +449,21 @@
&inst, SpvOpISub, type_mgr->GetId(wider_type), TakeNextId(),
{{SPV_OPERAND_TYPE_ID, {count_inst->result_id()}},
{SPV_OPERAND_TYPE_ID, {one->result_id()}}});
- clamp_index(operand_index, index_inst, GetValueForType(0, wider_type),
- count_minus_1);
+ auto* zero = GetValueForType(0, wider_type);
+ // Make sure we clamp to an upper bound that is at most the signed max
+ // for the target type.
+ const uint64_t max_signed_value =
+ ((uint64_t(1) << (target_width - 1)) - 1);
+ // Use unsigned-min to ensure that the result is always non-negative.
+ // That ensures we satisfy the invariant for SClamp, where the "min"
+ // argument we give it (zero), is no larger than the third argument.
+ auto* upper_bound =
+ MakeUMinInst(*type_mgr, count_minus_1,
+ GetValueForType(max_signed_value, wider_type), &inst);
+ // Now clamp the index to this upper bound.
+ return clamp_index(operand_index, index_inst, zero, upper_bound);
}
+ return SPV_SUCCESS;
};
const Instruction* base_inst = GetDef(inst.GetSingleWordInOperand(0));
@@ -483,6 +538,7 @@
return;
}
clamp_to_count(idx, array_len);
+ if (module_status_.failed) return;
pointee_type = GetDef(pointee_type->GetSingleWordOperand(1));
} break;
@@ -557,22 +613,51 @@
return conversion;
}
-Instruction* GraphicsRobustAccessPass::MakeClampInst(Instruction* x,
- Instruction* min,
- Instruction* max,
- Instruction* where) {
+Instruction* GraphicsRobustAccessPass::MakeUMinInst(
+ const analysis::TypeManager& tm, Instruction* x, Instruction* y,
+ Instruction* where) {
+ // Get IDs of instructions we'll be referencing. Evaluate them before calling
+ // the function so we force a deterministic ordering in case both of them need
+ // to take a new ID.
+ const uint32_t glsl_insts_id = GetGlslInsts();
+ uint32_t smin_id = TakeNextId();
+ const auto xwidth = tm.GetType(x->type_id())->AsInteger()->width();
+ const auto ywidth = tm.GetType(y->type_id())->AsInteger()->width();
+ assert(xwidth == ywidth);
+ (void)xwidth;
+ (void)ywidth;
+ auto* smin_inst = InsertInst(
+ where, SpvOpExtInst, x->type_id(), smin_id,
+ {
+ {SPV_OPERAND_TYPE_ID, {glsl_insts_id}},
+ {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {GLSLstd450UMin}},
+ {SPV_OPERAND_TYPE_ID, {x->result_id()}},
+ {SPV_OPERAND_TYPE_ID, {y->result_id()}},
+ });
+ return smin_inst;
+}
+
+Instruction* GraphicsRobustAccessPass::MakeSClampInst(
+ const analysis::TypeManager& tm, Instruction* x, Instruction* min,
+ Instruction* max, Instruction* where) {
// Get IDs of instructions we'll be referencing. Evaluate them before calling
// the function so we force a deterministic ordering in case both of them need
// to take a new ID.
const uint32_t glsl_insts_id = GetGlslInsts();
uint32_t clamp_id = TakeNextId();
- assert(x->type_id() == min->type_id());
- assert(x->type_id() == max->type_id());
+ const auto xwidth = tm.GetType(x->type_id())->AsInteger()->width();
+ const auto minwidth = tm.GetType(min->type_id())->AsInteger()->width();
+ const auto maxwidth = tm.GetType(max->type_id())->AsInteger()->width();
+ assert(xwidth == minwidth);
+ assert(xwidth == maxwidth);
+ (void)xwidth;
+ (void)minwidth;
+ (void)maxwidth;
auto* clamp_inst = InsertInst(
where, SpvOpExtInst, x->type_id(), clamp_id,
{
{SPV_OPERAND_TYPE_ID, {glsl_insts_id}},
- {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {GLSLstd450UClamp}},
+ {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {GLSLstd450SClamp}},
{SPV_OPERAND_TYPE_ID, {x->result_id()}},
{SPV_OPERAND_TYPE_ID, {min->result_id()}},
{SPV_OPERAND_TYPE_ID, {max->result_id()}},
@@ -716,6 +801,7 @@
spv_result_t GraphicsRobustAccessPass::ClampCoordinateForImageTexelPointer(
opt::Instruction* image_texel_pointer) {
// TODO(dneto): Write tests for this code.
+ // TODO(dneto): Use signed-clamp
return SPV_SUCCESS;
// Example:
@@ -916,9 +1002,9 @@
{constant_mgr->GetDefiningInstruction(coordinate_1)->result_id()}}});
// Clamp the coordinate
- auto* clamp_coord =
- MakeClampInst(coord, constant_mgr->GetDefiningInstruction(coordinate_0),
- query_max_including_faces, image_texel_pointer);
+ auto* clamp_coord = MakeSClampInst(
+ *type_mgr, coord, constant_mgr->GetDefiningInstruction(coordinate_0),
+ query_max_including_faces, image_texel_pointer);
image_texel_pointer->SetInOperand(1, {clamp_coord->result_id()});
// Clamp the sample index
@@ -936,8 +1022,8 @@
{{SPV_OPERAND_TYPE_ID, {query_samples_id}},
{SPV_OPERAND_TYPE_ID, {component_1_id}}});
- auto* clamp_samples = MakeClampInst(
- samples, constant_mgr->GetDefiningInstruction(coordinate_0),
+ auto* clamp_samples = MakeSClampInst(
+ *type_mgr, samples, constant_mgr->GetDefiningInstruction(coordinate_0),
max_samples, image_texel_pointer);
image_texel_pointer->SetInOperand(2, {clamp_samples->result_id()});
diff --git a/source/opt/graphics_robust_access_pass.h b/source/opt/graphics_robust_access_pass.h
index b21154e..6fc692c 100644
--- a/source/opt/graphics_robust_access_pass.h
+++ b/source/opt/graphics_robust_access_pass.h
@@ -18,13 +18,13 @@
#include <map>
#include <unordered_map>
-#include "source/diagnostic.h"
-
#include "constants.h"
#include "def_use_manager.h"
#include "instruction.h"
#include "module.h"
#include "pass.h"
+#include "source/diagnostic.h"
+#include "type_manager.h"
namespace spvtools {
namespace opt {
@@ -49,7 +49,8 @@
// consumer.
spvtools::DiagnosticStream Fail();
- // Returns SPV_SUCCESS if this pass can correctly process the module.
+ // Returns SPV_SUCCESS if this pass can correctly process the module,
+ // as far as we can tell from capabilities and the memory model.
// Otherwise logs a message and returns a failure code.
spv_result_t IsCompatibleModule();
@@ -59,12 +60,12 @@
spv_result_t ProcessCurrentModule();
// Process the given function. Updates the state value |_|. Returns true
- // if the module was modified.
+ // if the module was modified. This can log a failure.
bool ProcessAFunction(opt::Function*);
// Clamps indices in the OpAccessChain or OpInBoundsAccessChain instruction
// |access_chain|. Inserts instructions before the given instruction. Updates
- // analyses and records that the module is modified.
+ // analyses and records that the module is modified. This can log a failure.
void ClampIndicesForAccessChain(Instruction* access_chain);
// Returns the id of the instruction importing the "GLSL.std.450" extended
@@ -85,17 +86,29 @@
Instruction* WidenInteger(bool sign_extend, uint32_t bit_width,
Instruction* value, Instruction* before_inst);
- // Returns a new instruction that invokes the UClamp GLSL.std.450 extended
+ // Returns a new instruction that invokes the UMin GLSL.std.450 extended
+ // instruction with the two given operands. That is, the result of the
+ // instruction is:
+ // - |x| if |x| is unsigned-less than |y|
+ // - |y| otherwise
+ // We assume that |x| and |y| are scalar integer types with the same
+ // width. The instruction is inserted before |where|.
+ opt::Instruction* MakeUMinInst(const analysis::TypeManager& tm,
+ Instruction* x, Instruction* y,
+ Instruction* where);
+
+ // Returns a new instruction that invokes the SClamp GLSL.std.450 extended
// instruction with the three given operands. That is, the result of the
// instruction is:
- // - |min| if |x| is unsigned-less than |min|
- // - |max| if |x| is unsigned-more than |max|
+ // - |min| if |x| is signed-less than |min|
+ // - |max| if |x| is signed-more than |max|
// - |x| otherwise.
- // We assume that |min| is unsigned-less-or-equal to |max|, and that the
+ // We assume that |min| is signed-less-or-equal to |max|, and that the
// operands all have the same scalar integer type. The instruction is
// inserted before |where|.
- opt::Instruction* MakeClampInst(Instruction* x, Instruction* min,
- Instruction* max, Instruction* where);
+ opt::Instruction* MakeSClampInst(const analysis::TypeManager& tm,
+ Instruction* x, Instruction* min,
+ 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
diff --git a/source/opt/inline_pass.cpp b/source/opt/inline_pass.cpp
index 36991ec..3c874a7 100644
--- a/source/opt/inline_pass.cpp
+++ b/source/opt/inline_pass.cpp
@@ -27,7 +27,6 @@
static const int kSpvFunctionCallFunctionId = 2;
static const int kSpvFunctionCallArgumentId = 3;
static const int kSpvReturnValueId = 0;
-static const int kSpvLoopMergeContinueTargetIdInIdx = 1;
namespace spvtools {
namespace opt {
@@ -285,19 +284,14 @@
if (rid != 0) callee_result_ids.insert(rid);
});
- // If the caller is in a single-block loop, and the callee has multiple
- // blocks, then the normal inlining logic will place the OpLoopMerge in
- // the last of several blocks in the loop. Instead, it should be placed
- // at the end of the first block. First determine if the caller is in a
- // single block loop. We'll wait to move the OpLoopMerge until the end
- // of the regular inlining logic, and only if necessary.
- bool caller_is_single_block_loop = false;
+ // If the caller is a loop header and the callee has multiple blocks, then the
+ // normal inlining logic will place the OpLoopMerge in the last of several
+ // blocks in the loop. Instead, it should be placed at the end of the first
+ // block. We'll wait to move the OpLoopMerge until the end of the regular
+ // inlining logic, and only if necessary.
bool caller_is_loop_header = false;
- if (auto* loop_merge = call_block_itr->GetLoopMergeInst()) {
+ if (call_block_itr->GetLoopMergeInst()) {
caller_is_loop_header = true;
- caller_is_single_block_loop =
- call_block_itr->id() ==
- loop_merge->GetSingleWordInOperand(kSpvLoopMergeContinueTargetIdInIdx);
}
bool callee_begins_with_structured_header =
@@ -611,10 +605,6 @@
--loop_merge_itr;
assert(loop_merge_itr->opcode() == SpvOpLoopMerge);
std::unique_ptr<Instruction> cp_inst(loop_merge_itr->Clone(context()));
- if (caller_is_single_block_loop) {
- // Also, update its continue target to point to the last block.
- cp_inst->SetInOperand(kSpvLoopMergeContinueTargetIdInIdx, {last->id()});
- }
first->tail().InsertBefore(std::move(cp_inst));
// Remove the loop merge from the last block.
diff --git a/source/opt/inst_bindless_check_pass.h b/source/opt/inst_bindless_check_pass.h
index 51d7712..447871b 100644
--- a/source/opt/inst_bindless_check_pass.h
+++ b/source/opt/inst_bindless_check_pass.h
@@ -28,18 +28,19 @@
// external design may change as the layer evolves.
class InstBindlessCheckPass : public InstrumentPass {
public:
- // For test harness only
- InstBindlessCheckPass()
- : InstrumentPass(7, 23, kInstValidationIdBindless, 1),
- input_length_enabled_(true),
- input_init_enabled_(true) {}
- // For all other interfaces
+ // Deprecated interface
InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id,
bool input_length_enable, bool input_init_enable,
uint32_t version)
: InstrumentPass(desc_set, shader_id, kInstValidationIdBindless, version),
input_length_enabled_(input_length_enable),
input_init_enabled_(input_init_enable) {}
+ // Preferred Interface
+ InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id,
+ bool input_length_enable, bool input_init_enable)
+ : InstrumentPass(desc_set, shader_id, kInstValidationIdBindless),
+ input_length_enabled_(input_length_enable),
+ input_init_enabled_(input_init_enable) {}
~InstBindlessCheckPass() override = default;
diff --git a/source/opt/inst_buff_addr_check_pass.h b/source/opt/inst_buff_addr_check_pass.h
index 9ad3528..67ffcc3 100644
--- a/source/opt/inst_buff_addr_check_pass.h
+++ b/source/opt/inst_buff_addr_check_pass.h
@@ -28,13 +28,13 @@
// external design of this class may change as the layer evolves.
class InstBuffAddrCheckPass : public InstrumentPass {
public:
- // For test harness only
- InstBuffAddrCheckPass()
- : InstrumentPass(7, 23, kInstValidationIdBuffAddr, 1) {}
- // For all other interfaces
+ // Deprecated interface
InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id, uint32_t version)
: InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr,
version) {}
+ // Preferred interface
+ InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id)
+ : InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr) {}
~InstBuffAddrCheckPass() override = default;
diff --git a/source/opt/instrument_pass.cpp b/source/opt/instrument_pass.cpp
index 2a3e7f0..dfcd164 100644
--- a/source/opt/instrument_pass.cpp
+++ b/source/opt/instrument_pass.cpp
@@ -185,32 +185,12 @@
GetUintId(), SpvOpCompositeExtract, load_id, 1);
Instruction* z_inst = builder->AddIdLiteralOp(
GetUintId(), SpvOpCompositeExtract, load_id, 2);
- if (version_ == 1) {
- // For version 1 format, as a stopgap, pack uvec3 into first word:
- // x << 21 | y << 10 | z. Second word is unused. (DEPRECATED)
- Instruction* x_shft_inst = builder->AddBinaryOp(
- GetUintId(), SpvOpShiftLeftLogical, x_inst->result_id(),
- builder->GetUintConstantId(21));
- Instruction* y_shft_inst = builder->AddBinaryOp(
- GetUintId(), SpvOpShiftLeftLogical, y_inst->result_id(),
- builder->GetUintConstantId(10));
- Instruction* x_or_y_inst = builder->AddBinaryOp(
- GetUintId(), SpvOpBitwiseOr, x_shft_inst->result_id(),
- y_shft_inst->result_id());
- Instruction* x_or_y_or_z_inst =
- builder->AddBinaryOp(GetUintId(), SpvOpBitwiseOr,
- x_or_y_inst->result_id(), z_inst->result_id());
- GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationId,
- x_or_y_or_z_inst->result_id(), builder);
- } else {
- // For version 2 format, write all three words
- GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdX,
- x_inst->result_id(), builder);
- GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdY,
- y_inst->result_id(), builder);
- GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdZ,
- z_inst->result_id(), builder);
- }
+ GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdX,
+ x_inst->result_id(), builder);
+ GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdY,
+ y_inst->result_id(), builder);
+ GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdZ,
+ z_inst->result_id(), builder);
} break;
case SpvExecutionModelGeometry: {
// Load and store PrimitiveId and InvocationId.
@@ -231,30 +211,23 @@
kInstTessCtlOutPrimitiveId, base_offset_id, builder);
} break;
case SpvExecutionModelTessellationEvaluation: {
- if (version_ == 1) {
- // For format version 1, load and store InvocationId.
- GenBuiltinOutputCode(
- context()->GetBuiltinInputVarId(SpvBuiltInInvocationId),
- kInstTessOutInvocationId, base_offset_id, builder);
- } else {
- // For format version 2, load and store PrimitiveId and TessCoord.uv
- GenBuiltinOutputCode(
- context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId),
- kInstTessEvalOutPrimitiveId, base_offset_id, builder);
- uint32_t load_id = GenVarLoad(
- context()->GetBuiltinInputVarId(SpvBuiltInTessCoord), builder);
- Instruction* uvec3_cast_inst =
- builder->AddUnaryOp(GetVec3UintId(), SpvOpBitcast, load_id);
- uint32_t uvec3_cast_id = uvec3_cast_inst->result_id();
- Instruction* u_inst = builder->AddIdLiteralOp(
- GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 0);
- Instruction* v_inst = builder->AddIdLiteralOp(
- GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 1);
- GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordU,
- u_inst->result_id(), builder);
- GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordV,
- v_inst->result_id(), builder);
- }
+ // Load and store PrimitiveId and TessCoord.uv
+ GenBuiltinOutputCode(
+ context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId),
+ kInstTessEvalOutPrimitiveId, base_offset_id, builder);
+ uint32_t load_id = GenVarLoad(
+ context()->GetBuiltinInputVarId(SpvBuiltInTessCoord), builder);
+ Instruction* uvec3_cast_inst =
+ builder->AddUnaryOp(GetVec3UintId(), SpvOpBitcast, load_id);
+ uint32_t uvec3_cast_id = uvec3_cast_inst->result_id();
+ Instruction* u_inst = builder->AddIdLiteralOp(
+ GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 0);
+ Instruction* v_inst = builder->AddIdLiteralOp(
+ GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 1);
+ GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordU,
+ u_inst->result_id(), builder);
+ GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordV,
+ v_inst->result_id(), builder);
} break;
case SpvExecutionModelFragment: {
// Load FragCoord and convert to Uint
@@ -671,8 +644,7 @@
context(), &*new_blk_ptr,
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
// Gen test if debug output buffer size will not be exceeded.
- uint32_t val_spec_offset =
- (version_ == 1) ? kInstStageOutCnt : kInst2StageOutCnt;
+ uint32_t val_spec_offset = kInstStageOutCnt;
uint32_t obuf_record_sz = val_spec_offset + val_spec_param_cnt;
uint32_t buf_id = GetOutputBufferId();
uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
@@ -892,6 +864,14 @@
}
bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) {
+ // Check that format version 2 requested
+ if (version_ != 2u) {
+ if (consumer()) {
+ std::string message = "Unsupported instrumentation format requested";
+ consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str());
+ }
+ return false;
+ }
// Make sure all entry points have the same execution model. Do not
// instrument if they do not.
// TODO(greg-lunarg): Handle mixed stages. Technically, a shader module
@@ -905,12 +885,17 @@
for (auto& e : get_module()->entry_points()) {
if (ecnt == 0)
stage = e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx);
- else if (e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx) != stage)
+ else if (e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx) !=
+ stage) {
+ if (consumer()) {
+ std::string message = "Mixed stage shader module not supported";
+ consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str());
+ }
return false;
+ }
++ecnt;
}
- // Only supporting vertex, fragment and compute shaders at the moment.
- // TODO(greg-lunarg): Handle all stages.
+ // Check for supported stages
if (stage != SpvExecutionModelVertex && stage != SpvExecutionModelFragment &&
stage != SpvExecutionModelGeometry &&
stage != SpvExecutionModelGLCompute &&
@@ -920,8 +905,14 @@
stage != SpvExecutionModelIntersectionNV &&
stage != SpvExecutionModelAnyHitNV &&
stage != SpvExecutionModelClosestHitNV &&
- stage != SpvExecutionModelMissNV && stage != SpvExecutionModelCallableNV)
+ stage != SpvExecutionModelMissNV &&
+ stage != SpvExecutionModelCallableNV) {
+ if (consumer()) {
+ std::string message = "Stage not supported by instrumentation";
+ consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str());
+ }
return false;
+ }
// Add together the roots of all entry points
std::queue<uint32_t> roots;
for (auto& e : get_module()->entry_points()) {
diff --git a/source/opt/instrument_pass.h b/source/opt/instrument_pass.h
index ead3b73..02568fb 100644
--- a/source/opt/instrument_pass.h
+++ b/source/opt/instrument_pass.h
@@ -81,7 +81,16 @@
protected:
// Create instrumentation pass for |validation_id| which utilizes descriptor
// set |desc_set| for debug input and output buffers and writes |shader_id|
- // into debug output records with format |version|.
+ // into debug output records.
+ InstrumentPass(uint32_t desc_set, uint32_t shader_id, uint32_t validation_id)
+ : Pass(),
+ desc_set_(desc_set),
+ shader_id_(shader_id),
+ validation_id_(validation_id),
+ version_(2u) {}
+ // Create instrumentation pass for |validation_id| which utilizes descriptor
+ // set |desc_set| for debug input and output buffers and writes |shader_id|
+ // into debug output records with format |version|. Deprecated.
InstrumentPass(uint32_t desc_set, uint32_t shader_id, uint32_t validation_id,
uint32_t version)
: Pass(),
diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp
index d940180..7bca29b 100644
--- a/source/opt/ir_context.cpp
+++ b/source/opt/ir_context.cpp
@@ -273,6 +273,14 @@
}
}
+ if (AreAnalysesValid(kAnalysisIdToFuncMapping)) {
+ for (auto& fn : *module_) {
+ if (id_to_func_[fn.result_id()] != &fn) {
+ return false;
+ }
+ }
+ }
+
if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) {
for (auto& func : *module()) {
for (auto& block : func) {
@@ -818,6 +826,7 @@
roots->pop();
if (done.insert(fi).second) {
Function* fn = GetFunction(fi);
+ assert(fn && "Trying to process a function that does not exist.");
modified = pfn(fn) || modified;
AddCalls(fn, roots);
}
diff --git a/source/opt/ir_loader.cpp b/source/opt/ir_loader.cpp
index c68b3e2..e21e680 100644
--- a/source/opt/ir_loader.cpp
+++ b/source/opt/ir_loader.cpp
@@ -16,6 +16,7 @@
#include <utility>
+#include "source/ext_inst.h"
#include "source/opt/log.h"
#include "source/opt/reflect.h"
#include "source/util/make_unique.h"
@@ -113,11 +114,14 @@
} else if (IsTypeInst(opcode)) {
module_->AddType(std::move(spv_inst));
} else if (IsConstantInst(opcode) || opcode == SpvOpVariable ||
- opcode == SpvOpUndef) {
+ opcode == SpvOpUndef ||
+ (opcode == SpvOpExtInst &&
+ spvExtInstIsNonSemantic(inst->ext_inst_type))) {
module_->AddGlobalValue(std::move(spv_inst));
} else {
Errorf(consumer_, src, loc,
- "Unhandled inst type (opcode: %d) found outside function definition.",
+ "Unhandled inst type (opcode: %d) found outside function "
+ "definition.",
opcode);
return false;
}
diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp
index ece1abe..241aa75 100644
--- a/source/opt/optimizer.cpp
+++ b/source/opt/optimizer.cpp
@@ -198,33 +198,39 @@
.RegisterPass(CreateDeadBranchElimPass())
.RegisterPass(CreateMergeReturnPass())
.RegisterPass(CreateInlineExhaustivePass())
- .RegisterPass(CreateAggressiveDCEPass())
+ .RegisterPass(CreateEliminateDeadFunctionsPass())
.RegisterPass(CreatePrivateToLocalPass())
- .RegisterPass(CreateScalarReplacementPass())
- .RegisterPass(CreateLocalAccessChainConvertPass())
- .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
- .RegisterPass(CreateLocalSingleStoreElimPass())
- .RegisterPass(CreateAggressiveDCEPass())
- .RegisterPass(CreateSimplificationPass())
- .RegisterPass(CreateDeadInsertElimPass())
+ .RegisterPass(CreateScalarReplacementPass(0))
.RegisterPass(CreateLocalMultiStoreElimPass())
- .RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateCCPPass())
+ .RegisterPass(CreateLoopUnrollPass(true))
+ .RegisterPass(CreateDeadBranchElimPass())
+ .RegisterPass(CreateSimplificationPass())
+ .RegisterPass(CreateScalarReplacementPass(0))
+ .RegisterPass(CreateLocalSingleStoreElimPass())
+ .RegisterPass(CreateIfConversionPass())
+ .RegisterPass(CreateSimplificationPass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateDeadBranchElimPass())
- .RegisterPass(CreateIfConversionPass())
- .RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateBlockMergePass())
- .RegisterPass(CreateSimplificationPass())
+ .RegisterPass(CreateLocalAccessChainConvertPass())
+ .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
+ .RegisterPass(CreateAggressiveDCEPass())
+ .RegisterPass(CreateCopyPropagateArraysPass())
+ .RegisterPass(CreateVectorDCEPass())
.RegisterPass(CreateDeadInsertElimPass())
+ .RegisterPass(CreateEliminateDeadMembersPass())
+ .RegisterPass(CreateLocalSingleStoreElimPass())
+ .RegisterPass(CreateBlockMergePass())
+ .RegisterPass(CreateLocalMultiStoreElimPass())
.RegisterPass(CreateRedundancyEliminationPass())
- .RegisterPass(CreateCFGCleanupPass())
- .RegisterPass(CreateAggressiveDCEPass());
+ .RegisterPass(CreateSimplificationPass())
+ .RegisterPass(CreateAggressiveDCEPass())
+ .RegisterPass(CreateCFGCleanupPass());
}
Optimizer& Optimizer::RegisterVulkanToWebGPUPasses() {
- return RegisterPass(CreateStripDebugInfoPass())
- .RegisterPass(CreateStripAtomicCounterMemoryPass())
+ return RegisterPass(CreateStripAtomicCounterMemoryPass())
.RegisterPass(CreateGenerateWebGPUInitializersPass())
.RegisterPass(CreateLegalizeVectorShufflePass())
.RegisterPass(CreateSplitInvalidUnreachablePass())
diff --git a/source/opt/register_pressure.cpp b/source/opt/register_pressure.cpp
index 34dac1d..cb24674 100644
--- a/source/opt/register_pressure.cpp
+++ b/source/opt/register_pressure.cpp
@@ -78,9 +78,16 @@
// - Second, walk loop forest to propagate registers crossing back-edges
// (add iterative values into the liveness set).
void Compute() {
- cfg_.ForEachBlockInPostOrder(&*function_->begin(), [this](BasicBlock* bb) {
- ComputePartialLiveness(bb);
- });
+ for (BasicBlock& start_bb : *function_) {
+ if (reg_pressure_->Get(start_bb.id()) != nullptr) {
+ continue;
+ }
+ cfg_.ForEachBlockInPostOrder(&start_bb, [this](BasicBlock* bb) {
+ if (reg_pressure_->Get(bb->id()) == nullptr) {
+ ComputePartialLiveness(bb);
+ }
+ });
+ }
DoLoopLivenessUnification();
EvaluateRegisterRequirements();
}
diff --git a/source/opt/strip_debug_info_pass.cpp b/source/opt/strip_debug_info_pass.cpp
index 9e7fad0..936c966 100644
--- a/source/opt/strip_debug_info_pass.cpp
+++ b/source/opt/strip_debug_info_pass.cpp
@@ -19,12 +19,60 @@
namespace opt {
Pass::Status StripDebugInfoPass::Process() {
- bool modified = !context()->debugs1().empty() ||
- !context()->debugs2().empty() ||
- !context()->debugs3().empty();
+ bool uses_non_semantic_info = false;
+ for (auto& inst : context()->module()->extensions()) {
+ const char* ext_name =
+ reinterpret_cast<const char*>(&inst.GetInOperand(0).words[0]);
+ if (0 == std::strcmp(ext_name, "SPV_KHR_non_semantic_info")) {
+ uses_non_semantic_info = true;
+ }
+ }
std::vector<Instruction*> to_kill;
- for (auto& dbg : context()->debugs1()) to_kill.push_back(&dbg);
+
+ // if we use non-semantic info, it may reference OpString. Do a more
+ // expensive pass checking the uses of the OpString to see if any are
+ // OpExtInst on a non-semantic instruction set. If we're not using the
+ // extension then we can do a simpler pass and kill all debug1 instructions
+ if (uses_non_semantic_info) {
+ for (auto& inst : context()->module()->debugs1()) {
+ switch (inst.opcode()) {
+ case SpvOpString: {
+ analysis::DefUseManager* def_use = context()->get_def_use_mgr();
+
+ // see if this string is used anywhere by a non-semantic instruction
+ bool no_nonsemantic_use =
+ def_use->WhileEachUser(&inst, [def_use](Instruction* use) {
+ if (use->opcode() == SpvOpExtInst) {
+ auto ext_inst_set =
+ def_use->GetDef(use->GetSingleWordInOperand(0u));
+ const char* extension_name = reinterpret_cast<const char*>(
+ &ext_inst_set->GetInOperand(0).words[0]);
+ if (0 == std::strncmp(extension_name, "NonSemantic.", 12)) {
+ // found a non-semantic use, return false as we cannot
+ // remove this OpString
+ return false;
+ }
+ }
+
+ // other instructions can't be a non-semantic use
+ return true;
+ });
+
+ if (no_nonsemantic_use) to_kill.push_back(&inst);
+
+ break;
+ }
+
+ default:
+ to_kill.push_back(&inst);
+ break;
+ }
+ }
+ } else {
+ for (auto& dbg : context()->debugs1()) to_kill.push_back(&dbg);
+ }
+
for (auto& dbg : context()->debugs2()) to_kill.push_back(&dbg);
for (auto& dbg : context()->debugs3()) to_kill.push_back(&dbg);
@@ -38,8 +86,11 @@
return false;
});
+ bool modified = !to_kill.empty();
+
for (auto* inst : to_kill) context()->KillInst(inst);
+ // clear OpLine information
context()->module()->ForEachInst([&modified](Instruction* inst) {
modified |= !inst->dbg_line_insts().empty();
inst->dbg_line_insts().clear();
diff --git a/source/opt/strip_reflect_info_pass.cpp b/source/opt/strip_reflect_info_pass.cpp
index 984073f..c231ead 100644
--- a/source/opt/strip_reflect_info_pass.cpp
+++ b/source/opt/strip_reflect_info_pass.cpp
@@ -67,9 +67,54 @@
} else if (!other_uses_for_decorate_string &&
0 == std::strcmp(ext_name, "SPV_GOOGLE_decorate_string")) {
to_remove.push_back(&inst);
+ } else if (0 == std::strcmp(ext_name, "SPV_KHR_non_semantic_info")) {
+ to_remove.push_back(&inst);
}
}
+ // clear all debug data now if it hasn't been cleared already, to remove any
+ // remaining OpString that may have been referenced by non-semantic extinsts
+ for (auto& dbg : context()->debugs1()) to_remove.push_back(&dbg);
+ for (auto& dbg : context()->debugs2()) to_remove.push_back(&dbg);
+ for (auto& dbg : context()->debugs3()) to_remove.push_back(&dbg);
+
+ // remove any extended inst imports that are non semantic
+ std::unordered_set<uint32_t> 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 char* extension_name =
+ reinterpret_cast<const char*>(&inst.GetInOperand(0).words[0]);
+ if (0 == std::strncmp(extension_name, "NonSemantic.", 12)) {
+ 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);
+ }
+ }
+ });
+ }
+
+ // OpName must come first, since they may refer to other debug instructions.
+ // If they are after the instructions that refer to, then they will be killed
+ // when that instruction is killed, which will lead to a double kill.
+ std::sort(to_remove.begin(), to_remove.end(),
+ [](Instruction* lhs, Instruction* rhs) -> bool {
+ if (lhs->opcode() == SpvOpName && rhs->opcode() != SpvOpName)
+ return true;
+ return false;
+ });
+
for (auto* inst : to_remove) {
modified = true;
context()->KillInst(inst);
diff --git a/source/opt/value_number_table.cpp b/source/opt/value_number_table.cpp
index 8df34ef..82549a6 100644
--- a/source/opt/value_number_table.cpp
+++ b/source/opt/value_number_table.cpp
@@ -93,7 +93,7 @@
// Phi nodes are a type of copy. If all of the inputs have the same value
// number, then we can assign the result of the phi the same value number.
- if (inst->opcode() == SpvOpPhi &&
+ if (inst->opcode() == SpvOpPhi && inst->NumInOperands() > 0 &&
dec_mgr->HaveTheSameDecorations(inst->result_id(),
inst->GetSingleWordInOperand(0))) {
value = GetValueNumber(inst->GetSingleWordInOperand(0));
diff --git a/source/opt/wrap_opkill.h b/source/opt/wrap_opkill.h
index 87a5d69..09f2dfa 100644
--- a/source/opt/wrap_opkill.h
+++ b/source/opt/wrap_opkill.h
@@ -34,8 +34,7 @@
IRContext::kAnalysisInstrToBlockMapping |
IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators |
IRContext::kAnalysisNameMap | IRContext::kAnalysisBuiltinVarId |
- IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisConstants |
- IRContext::kAnalysisTypes;
+ IRContext::kAnalysisConstants | IRContext::kAnalysisTypes;
}
private:
diff --git a/source/print.cpp b/source/print.cpp
index 1d9829d..128587a 100644
--- a/source/print.cpp
+++ b/source/print.cpp
@@ -15,7 +15,8 @@
#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_IOS) || defined(SPIRV_FREEBSD) || \
+ defined(SPIRV_EMSCRIPTEN) || defined(SPIRV_FUCHSIA)
namespace spvtools {
clr::reset::operator const char*() { return "\x1b[0m"; }
diff --git a/source/spirv_fuzzer_options.cpp b/source/spirv_fuzzer_options.cpp
index ab8903e..b407f14 100644
--- a/source/spirv_fuzzer_options.cpp
+++ b/source/spirv_fuzzer_options.cpp
@@ -23,7 +23,8 @@
: has_random_seed(false),
random_seed(0),
replay_validation_enabled(false),
- shrinker_step_limit(kDefaultStepLimit) {}
+ shrinker_step_limit(kDefaultStepLimit),
+ fuzzer_pass_validation_enabled(false) {}
SPIRV_TOOLS_EXPORT spv_fuzzer_options spvFuzzerOptionsCreate() {
return new spv_fuzzer_options_t();
@@ -48,3 +49,8 @@
spv_fuzzer_options options, uint32_t shrinker_step_limit) {
options->shrinker_step_limit = shrinker_step_limit;
}
+
+SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableFuzzerPassValidation(
+ spv_fuzzer_options options) {
+ options->fuzzer_pass_validation_enabled = true;
+}
diff --git a/source/spirv_fuzzer_options.h b/source/spirv_fuzzer_options.h
index 7bb16c7..143f77f 100644
--- a/source/spirv_fuzzer_options.h
+++ b/source/spirv_fuzzer_options.h
@@ -34,6 +34,9 @@
// See spvFuzzerOptionsSetShrinkerStepLimit.
uint32_t shrinker_step_limit;
+
+ // See spvFuzzerOptionsValidateAfterEveryPass.
+ bool fuzzer_pass_validation_enabled;
};
#endif // SOURCE_SPIRV_FUZZER_OPTIONS_H_
diff --git a/source/text.cpp b/source/text.cpp
index d88d4f7..88a8e8f 100644
--- a/source/text.cpp
+++ b/source/text.cpp
@@ -242,14 +242,37 @@
// The assembler accepts the symbolic name for an extended instruction,
// and emits its corresponding number.
spv_ext_inst_desc extInst;
- if (grammar.lookupExtInst(pInst->extInstType, textValue, &extInst)) {
- return context->diagnostic()
- << "Invalid extended instruction name '" << textValue << "'.";
- }
- spvInstructionAddWord(pInst, extInst->ext_inst);
+ if (grammar.lookupExtInst(pInst->extInstType, textValue, &extInst) ==
+ SPV_SUCCESS) {
+ // if we know about this extended instruction, push the numeric value
+ spvInstructionAddWord(pInst, extInst->ext_inst);
- // Prepare to parse the operands for the extended instructions.
- spvPushOperandTypes(extInst->operandTypes, pExpectedOperands);
+ // Prepare to parse the operands for the extended instructions.
+ spvPushOperandTypes(extInst->operandTypes, pExpectedOperands);
+ } else {
+ // if we don't know this extended instruction and the set isn't
+ // non-semantic, we cannot process further
+ if (!spvExtInstIsNonSemantic(pInst->extInstType)) {
+ return context->diagnostic()
+ << "Invalid extended instruction name '" << textValue << "'.";
+ } else {
+ // for non-semantic instruction sets, as long as the text name is an
+ // integer value we can encode it since we know the form of all such
+ // extended instructions
+ spv_literal_t extInstValue;
+ if (spvTextToLiteral(textValue, &extInstValue) ||
+ extInstValue.type != SPV_LITERAL_TYPE_UINT_32) {
+ return context->diagnostic()
+ << "Couldn't translate unknown extended instruction name '"
+ << textValue << "' to unsigned integer.";
+ }
+
+ spvInstructionAddWord(pInst, extInstValue.value.u32);
+
+ // opcode contains an unknown number of IDs.
+ pExpectedOperands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID);
+ }
+ }
} break;
case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: {
@@ -377,7 +400,8 @@
case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
case SPV_OPERAND_TYPE_SELECTION_CONTROL:
- case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: {
+ 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)
diff --git a/source/val/basic_block.h b/source/val/basic_block.h
index efbd243..876105c 100644
--- a/source/val/basic_block.h
+++ b/source/val/basic_block.h
@@ -15,8 +15,8 @@
#ifndef SOURCE_VAL_BASIC_BLOCK_H_
#define SOURCE_VAL_BASIC_BLOCK_H_
-#include <cstdint>
#include <bitset>
+#include <cstdint>
#include <functional>
#include <memory>
#include <vector>
@@ -28,7 +28,7 @@
enum BlockType : uint32_t {
kBlockTypeUndefined,
- kBlockTypeHeader,
+ kBlockTypeSelection,
kBlockTypeLoop,
kBlockTypeMerge,
kBlockTypeBreak,
diff --git a/source/val/function.cpp b/source/val/function.cpp
index 876a608..0281770 100644
--- a/source/val/function.cpp
+++ b/source/val/function.cpp
@@ -14,9 +14,8 @@
#include "source/val/function.h"
-#include <cassert>
-
#include <algorithm>
+#include <cassert>
#include <sstream>
#include <unordered_map>
#include <unordered_set>
@@ -99,7 +98,7 @@
spv_result_t Function::RegisterSelectionMerge(uint32_t merge_id) {
RegisterBlock(merge_id, false);
BasicBlock& merge_block = blocks_.at(merge_id);
- current_block_->set_type(kBlockTypeHeader);
+ current_block_->set_type(kBlockTypeSelection);
merge_block.set_type(kBlockTypeMerge);
merge_block_header_[&merge_block] = current_block_;
@@ -344,7 +343,7 @@
BasicBlock* header = merge_block_header_[bb];
assert(header);
block_depth_[bb] = GetBlockDepth(header);
- } else if (bb_dom->is_type(kBlockTypeHeader) ||
+ } else if (bb_dom->is_type(kBlockTypeSelection) ||
bb_dom->is_type(kBlockTypeLoop)) {
// The dominator of the given block is a header block. So, the nesting
// depth of this block is: 1 + nesting depth of the header.
diff --git a/source/val/instruction.h b/source/val/instruction.h
index 1fa855f..e30bfbc 100644
--- a/source/val/instruction.h
+++ b/source/val/instruction.h
@@ -21,6 +21,7 @@
#include <utility>
#include <vector>
+#include "source/ext_inst.h"
#include "source/table.h"
#include "spirv-tools/libspirv.h"
@@ -85,6 +86,11 @@
return inst_.ext_inst_type;
}
+ bool IsNonSemantic() const {
+ return opcode() == SpvOp::SpvOpExtInst &&
+ spvExtInstIsNonSemantic(inst_.ext_inst_type);
+ }
+
// Casts the words belonging to the operand under |index| to |T| and returns.
template <typename T>
T GetOperandAs(size_t index) const {
diff --git a/source/val/validate_annotation.cpp b/source/val/validate_annotation.cpp
index 2695280..df38f1b 100644
--- a/source/val/validate_annotation.cpp
+++ b/source/val/validate_annotation.cpp
@@ -286,7 +286,8 @@
auto use = pair.first;
if (use->opcode() != SpvOpDecorate && use->opcode() != SpvOpGroupDecorate &&
use->opcode() != SpvOpGroupMemberDecorate &&
- use->opcode() != SpvOpName && use->opcode() != SpvOpDecorateId) {
+ use->opcode() != SpvOpName && use->opcode() != SpvOpDecorateId &&
+ !use->IsNonSemantic()) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Result id of OpDecorationGroup can only "
<< "be targeted by OpName, OpGroupDecorate, "
diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp
index 4801fc5..43a6af7 100644
--- a/source/val/validate_cfg.cpp
+++ b/source/val/validate_cfg.cpp
@@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "source/val/validate.h"
-
#include <algorithm>
#include <cassert>
#include <functional>
@@ -34,6 +32,7 @@
#include "source/val/basic_block.h"
#include "source/val/construct.h"
#include "source/val/function.h"
+#include "source/val/validate.h"
#include "source/val/validation_state.h"
namespace spvtools {
@@ -755,6 +754,26 @@
<< header_name << " <ID> " << header->id();
}
}
+
+ if (block->is_type(BlockType::kBlockTypeSelection) ||
+ block->is_type(BlockType::kBlockTypeLoop)) {
+ size_t index = (block->terminator() - &_.ordered_instructions()[0]) - 1;
+ const auto& merge_inst = _.ordered_instructions()[index];
+ if (merge_inst.opcode() == SpvOpSelectionMerge ||
+ merge_inst.opcode() == SpvOpLoopMerge) {
+ uint32_t merge_id = merge_inst.GetOperandAs<uint32_t>(0);
+ auto merge_block = function->GetBlock(merge_id).first;
+ if (merge_block->reachable() &&
+ !construct_blocks.count(merge_block)) {
+ return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
+ << "Header block " << _.getIdName(block->id())
+ << " is contained in the " << construct_name
+ << " construct headed by " << _.getIdName(header->id())
+ << ", but its merge block " << _.getIdName(merge_id)
+ << " is not";
+ }
+ }
+ }
}
// Checks rules for case constructs.
diff --git a/source/val/validate_debug.cpp b/source/val/validate_debug.cpp
index b49890f..0a25d8a 100644
--- a/source/val/validate_debug.cpp
+++ b/source/val/validate_debug.cpp
@@ -56,13 +56,6 @@
} // namespace
spv_result_t DebugPass(ValidationState_t& _, const Instruction* inst) {
- if (spvIsWebGPUEnv(_.context()->target_env) &&
- spvOpcodeIsDebug(inst->opcode())) {
- return _.diag(SPV_ERROR_INVALID_BINARY, inst)
- << "Debugging instructions are not allowed in the WebGPU execution "
- << "environment.";
- }
-
switch (inst->opcode()) {
case SpvOpMemberName:
if (auto error = ValidateMemberName(_, inst)) return error;
diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp
index d513a25..3b44833 100644
--- a/source/val/validate_decorations.cpp
+++ b/source/val/validate_decorations.cpp
@@ -1275,6 +1275,7 @@
const auto store = use.first;
if (store->opcode() == SpvOpFConvert) continue;
if (spvOpcodeIsDebug(store->opcode())) continue;
+ if (store->IsNonSemantic()) continue;
if (spvOpcodeIsDecoration(store->opcode())) continue;
if (store->opcode() != SpvOpStore) {
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp
index 1a64605..070cc4c 100644
--- a/source/val/validate_extensions.cpp
+++ b/source/val/validate_extensions.cpp
@@ -61,8 +61,8 @@
spv_result_t ValidateExtInstImport(ValidationState_t& _,
const Instruction* inst) {
+ const auto name_id = 1;
if (spvIsWebGPUEnv(_.context()->target_env)) {
- const auto name_id = 1;
const std::string name(reinterpret_cast<const char*>(
inst->words().data() + inst->operands()[name_id].offset));
if (name != "GLSL.std.450") {
@@ -72,6 +72,16 @@
}
}
+ if (!_.HasExtension(kSPV_KHR_non_semantic_info)) {
+ const std::string name(reinterpret_cast<const char*>(
+ inst->words().data() + inst->operands()[name_id].offset));
+ if (name.find("NonSemantic.") == 0) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "NonSemantic extended instruction sets cannot be declared "
+ "without SPV_KHR_non_semantic_info.";
+ }
+ }
+
return SPV_SUCCESS;
}
diff --git a/source/val/validate_function.cpp b/source/val/validate_function.cpp
index b983194..f995ab3 100644
--- a/source/val/validate_function.cpp
+++ b/source/val/validate_function.cpp
@@ -87,7 +87,8 @@
for (auto& pair : inst->uses()) {
const auto* use = pair.first;
if (std::find(acceptable.begin(), acceptable.end(), use->opcode()) ==
- acceptable.end()) {
+ acceptable.end() &&
+ !use->IsNonSemantic()) {
return _.diag(SPV_ERROR_INVALID_ID, use)
<< "Invalid use of function result id " << _.getIdName(inst->id())
<< ".";
diff --git a/source/val/validate_id.cpp b/source/val/validate_id.cpp
index d80e1b8..7406330 100644
--- a/source/val/validate_id.cpp
+++ b/source/val/validate_id.cpp
@@ -167,7 +167,8 @@
const auto opcode = inst->opcode();
if (spvOpcodeGeneratesType(def->opcode()) &&
!spvOpcodeGeneratesType(opcode) && !spvOpcodeIsDebug(opcode) &&
- !spvOpcodeIsDecoration(opcode) && opcode != SpvOpFunction &&
+ !inst->IsNonSemantic() && !spvOpcodeIsDecoration(opcode) &&
+ opcode != SpvOpFunction &&
opcode != SpvOpCooperativeMatrixLengthNV &&
!(opcode == SpvOpSpecConstantOp &&
inst->word(3) == SpvOpCooperativeMatrixLengthNV)) {
@@ -175,7 +176,7 @@
<< "Operand " << _.getIdName(operand_word)
<< " cannot be a type";
} else if (def->type_id() == 0 && !spvOpcodeGeneratesType(opcode) &&
- !spvOpcodeIsDebug(opcode) &&
+ !spvOpcodeIsDebug(opcode) && !inst->IsNonSemantic() &&
!spvOpcodeIsDecoration(opcode) &&
!spvOpcodeIsBranch(opcode) && opcode != SpvOpPhi &&
opcode != SpvOpExtInst && opcode != SpvOpExtInstImport &&
@@ -187,6 +188,11 @@
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Operand " << _.getIdName(operand_word)
<< " requires a type";
+ } else if (def->IsNonSemantic() && !inst->IsNonSemantic()) {
+ return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << "Operand " << _.getIdName(operand_word)
+ << " in semantic instruction cannot be a non-semantic "
+ "instruction";
} else {
ret = SPV_SUCCESS;
}
diff --git a/source/val/validate_layout.cpp b/source/val/validate_layout.cpp
index 53c2835..259befe 100644
--- a/source/val/validate_layout.cpp
+++ b/source/val/validate_layout.cpp
@@ -34,6 +34,30 @@
// checked.
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 {
+ // otherwise they must be used in a block
+ if (_.current_layout_section() < kLayoutFunctionDefinitions) {
+ return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+ << spvOpcodeString(opcode) << " must appear in a block";
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
while (_.IsOpcodeInCurrentLayoutSection(opcode) == false) {
_.ProgressToNextLayoutSectionOrder();
@@ -144,6 +168,29 @@
}
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 {
+ // otherwise they must be used in a block
+ if (_.in_block() == false) {
+ return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+ << spvOpcodeString(opcode) << " must appear in a block";
+ }
+ }
+ break;
+
default:
if (_.current_layout_section() == kLayoutFunctionDeclarations &&
_.in_function_body()) {
diff --git a/source/val/validate_type.cpp b/source/val/validate_type.cpp
index 4d673b4..5924c69 100644
--- a/source/val/validate_type.cpp
+++ b/source/val/validate_type.cpp
@@ -19,29 +19,40 @@
#include "source/val/instruction.h"
#include "source/val/validate.h"
#include "source/val/validation_state.h"
+#include "spirv/unified1/spirv.h"
namespace spvtools {
namespace val {
namespace {
-// True if the integer constant is > 0. |const_words| are words of the
-// constant-defining instruction (either OpConstant or
-// OpSpecConstant). typeWords are the words of the constant's-type-defining
-// OpTypeInt.
-bool AboveZero(const std::vector<uint32_t>& const_words,
- const std::vector<uint32_t>& type_words) {
- const uint32_t width = type_words[2];
- const bool is_signed = type_words[3] > 0;
+// Returns, as an int64_t, the literal value from an OpConstant or the
+// default value of an OpSpecConstant, assuming it is an integral type.
+// For signed integers, relies the rule that literal value is sign extended
+// to fill out to word granularity. Assumes that the constant value
+// has
+int64_t ConstantLiteralAsInt64(uint32_t width,
+ const std::vector<uint32_t>& const_words) {
const uint32_t lo_word = const_words[3];
- if (width > 32) {
- // The spec currently doesn't allow integers wider than 64 bits.
- const uint32_t hi_word = const_words[4]; // Must exist, per spec.
- if (is_signed && (hi_word >> 31)) return false;
- return (lo_word | hi_word) > 0;
- } else {
- if (is_signed && (lo_word >> 31)) return false;
- return lo_word > 0;
- }
+ if (width <= 32) return int32_t(lo_word);
+ assert(width <= 64);
+ assert(const_words.size() > 4);
+ const uint32_t hi_word = const_words[4]; // Must exist, per spec.
+ return static_cast<int64_t>(uint64_t(lo_word) | uint64_t(hi_word) << 32);
+}
+
+// Returns, as an uint64_t, the literal value from an OpConstant or the
+// default value of an OpSpecConstant, assuming it is an integral type.
+// For signed integers, relies the rule that literal value is sign extended
+// to fill out to word granularity. Assumes that the constant value
+// has
+int64_t ConstantLiteralAsUint64(uint32_t width,
+ const std::vector<uint32_t>& const_words) {
+ const uint32_t lo_word = const_words[3];
+ if (width <= 32) return lo_word;
+ assert(width <= 64);
+ assert(const_words.size() > 4);
+ const uint32_t hi_word = const_words[4]; // Must exist, per spec.
+ return (uint64_t(lo_word) | uint64_t(hi_word) << 32);
}
// Validates that type declarations are unique, unless multiple declarations
@@ -258,14 +269,33 @@
switch (length->opcode()) {
case SpvOpSpecConstant:
- case SpvOpConstant:
- if (AboveZero(length->words(), const_result_type->words())) break;
- // Else fall through!
- case SpvOpConstantNull: {
+ case SpvOpConstant: {
+ auto& type_words = const_result_type->words();
+ const bool is_signed = type_words[3] > 0;
+ const uint32_t width = type_words[2];
+ const int64_t ivalue = ConstantLiteralAsInt64(width, length->words());
+ if (ivalue == 0 || (ivalue < 0 && is_signed)) {
+ return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << "OpTypeArray Length <id> '" << _.getIdName(length_id)
+ << "' default value must be at least 1: found " << ivalue;
+ }
+ if (spvIsWebGPUEnv(_.context()->target_env)) {
+ // WebGPU has maximum integer width of 32 bits, and max array size
+ // is one more than the max signed integer representation.
+ const uint64_t max_permitted = (uint64_t(1) << 31);
+ const uint64_t uvalue = ConstantLiteralAsUint64(width, length->words());
+ if (uvalue > max_permitted) {
+ return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << "OpTypeArray Length <id> '" << _.getIdName(length_id)
+ << "' size exceeds max value " << max_permitted
+ << " permitted by WebGPU: got " << uvalue;
+ }
+ }
+ } break;
+ case SpvOpConstantNull:
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpTypeArray Length <id> '" << _.getIdName(length_id)
<< "' default value must be at least 1.";
- }
case SpvOpSpecConstantOp:
// Assume it's OK, rather than try to evaluate the operation.
break;
@@ -506,7 +536,7 @@
for (auto& pair : inst->uses()) {
const auto* use = pair.first;
if (use->opcode() != SpvOpFunction && !spvOpcodeIsDebug(use->opcode()) &&
- !spvOpcodeIsDecoration(use->opcode())) {
+ !use->IsNonSemantic() && !spvOpcodeIsDecoration(use->opcode())) {
return _.diag(SPV_ERROR_INVALID_ID, use)
<< "Invalid use of function type result id "
<< _.getIdName(inst->id()) << ".";
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index 20eaf88..51aebbe 100644
--- a/source/val/validation_state.cpp
+++ b/source/val/validation_state.cpp
@@ -93,6 +93,9 @@
case SpvOpLine:
case SpvOpNoLine:
case SpvOpUndef:
+ // SpvOpExtInst is only allowed here for certain extended instruction
+ // sets. This will be checked separately
+ case SpvOpExtInst:
out = true;
break;
default: break;
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 3dca430..70999f9 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -100,8 +100,10 @@
diagnostic_test.cpp
enum_string_mapping_test.cpp
enum_set_test.cpp
+ ext_inst.cldebug100_test.cpp
ext_inst.debuginfo_test.cpp
ext_inst.glsl_test.cpp
+ ext_inst.non_semantic_test.cpp
ext_inst.opencl_test.cpp
fix_word_test.cpp
generator_magic_number_test.cpp
diff --git a/test/ext_inst.cldebug100_test.cpp b/test/ext_inst.cldebug100_test.cpp
new file mode 100644
index 0000000..4f1e106
--- /dev/null
+++ b/test/ext_inst.cldebug100_test.cpp
@@ -0,0 +1,1070 @@
+// Copyright (c) 2017-2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+#include <vector>
+
+#include "OpenCLDebugInfo100.h"
+#include "gmock/gmock.h"
+#include "source/util/string_utils.h"
+#include "spirv/unified1/spirv.h"
+#include "test/test_fixture.h"
+#include "test/unit_spirv.h"
+
+// This file tests the correctness of encoding and decoding of instructions
+// involving the OpenCL.DebugInfo.100 extended instruction set.
+// Validation is not checked here.
+
+namespace spvtools {
+namespace {
+
+using spvtest::Concatenate;
+using spvtest::MakeInstruction;
+using testing::Eq;
+using utils::MakeVector;
+
+// Test values of enums vs. what is written in the spec.
+
+TEST(ExtInstCLDebugInfo, InstructionValues) {
+ EXPECT_EQ(0, OpenCLDebugInfo100DebugInfoNone);
+ EXPECT_EQ(1, OpenCLDebugInfo100DebugCompilationUnit);
+ EXPECT_EQ(2, OpenCLDebugInfo100DebugTypeBasic);
+ EXPECT_EQ(3, OpenCLDebugInfo100DebugTypePointer);
+ EXPECT_EQ(4, OpenCLDebugInfo100DebugTypeQualifier);
+ EXPECT_EQ(5, OpenCLDebugInfo100DebugTypeArray);
+ EXPECT_EQ(6, OpenCLDebugInfo100DebugTypeVector);
+ EXPECT_EQ(7, OpenCLDebugInfo100DebugTypedef);
+ EXPECT_EQ(8, OpenCLDebugInfo100DebugTypeFunction);
+ EXPECT_EQ(9, OpenCLDebugInfo100DebugTypeEnum);
+ EXPECT_EQ(10, OpenCLDebugInfo100DebugTypeComposite);
+ EXPECT_EQ(11, OpenCLDebugInfo100DebugTypeMember);
+ EXPECT_EQ(12, OpenCLDebugInfo100DebugTypeInheritance);
+ EXPECT_EQ(13, OpenCLDebugInfo100DebugTypePtrToMember);
+ EXPECT_EQ(14, OpenCLDebugInfo100DebugTypeTemplate);
+ EXPECT_EQ(15, OpenCLDebugInfo100DebugTypeTemplateParameter);
+ EXPECT_EQ(16, OpenCLDebugInfo100DebugTypeTemplateTemplateParameter);
+ EXPECT_EQ(17, OpenCLDebugInfo100DebugTypeTemplateParameterPack);
+ EXPECT_EQ(18, OpenCLDebugInfo100DebugGlobalVariable);
+ EXPECT_EQ(19, OpenCLDebugInfo100DebugFunctionDeclaration);
+ EXPECT_EQ(20, OpenCLDebugInfo100DebugFunction);
+ EXPECT_EQ(21, OpenCLDebugInfo100DebugLexicalBlock);
+ EXPECT_EQ(22, OpenCLDebugInfo100DebugLexicalBlockDiscriminator);
+ EXPECT_EQ(23, OpenCLDebugInfo100DebugScope);
+ EXPECT_EQ(24, OpenCLDebugInfo100DebugNoScope);
+ EXPECT_EQ(25, OpenCLDebugInfo100DebugInlinedAt);
+ EXPECT_EQ(26, OpenCLDebugInfo100DebugLocalVariable);
+ EXPECT_EQ(27, OpenCLDebugInfo100DebugInlinedVariable);
+ EXPECT_EQ(28, OpenCLDebugInfo100DebugDeclare);
+ EXPECT_EQ(29, OpenCLDebugInfo100DebugValue);
+ EXPECT_EQ(30, OpenCLDebugInfo100DebugOperation);
+ EXPECT_EQ(31, OpenCLDebugInfo100DebugExpression);
+ EXPECT_EQ(32, OpenCLDebugInfo100DebugMacroDef);
+ EXPECT_EQ(33, OpenCLDebugInfo100DebugMacroUndef);
+ EXPECT_EQ(34, OpenCLDebugInfo100DebugImportedEntity);
+ EXPECT_EQ(35, OpenCLDebugInfo100DebugSource);
+}
+
+TEST(ExtInstCLDebugInfo, InfoFlagValues) {
+ EXPECT_EQ(1 << 0, OpenCLDebugInfo100FlagIsProtected);
+ EXPECT_EQ(1 << 1, OpenCLDebugInfo100FlagIsPrivate);
+ EXPECT_EQ(((1 << 0) | (1 << 1)), OpenCLDebugInfo100FlagIsPublic);
+ EXPECT_EQ(1 << 2, OpenCLDebugInfo100FlagIsLocal);
+ EXPECT_EQ(1 << 3, OpenCLDebugInfo100FlagIsDefinition);
+ EXPECT_EQ(1 << 4, OpenCLDebugInfo100FlagFwdDecl);
+ EXPECT_EQ(1 << 5, OpenCLDebugInfo100FlagArtificial);
+ EXPECT_EQ(1 << 6, OpenCLDebugInfo100FlagExplicit);
+ EXPECT_EQ(1 << 7, OpenCLDebugInfo100FlagPrototyped);
+ EXPECT_EQ(1 << 8, OpenCLDebugInfo100FlagObjectPointer);
+ EXPECT_EQ(1 << 9, OpenCLDebugInfo100FlagStaticMember);
+ EXPECT_EQ(1 << 10, OpenCLDebugInfo100FlagIndirectVariable);
+ EXPECT_EQ(1 << 11, OpenCLDebugInfo100FlagLValueReference);
+ EXPECT_EQ(1 << 12, OpenCLDebugInfo100FlagRValueReference);
+ EXPECT_EQ(1 << 13, OpenCLDebugInfo100FlagIsOptimized);
+ EXPECT_EQ(1 << 14, OpenCLDebugInfo100FlagIsEnumClass);
+ EXPECT_EQ(1 << 15, OpenCLDebugInfo100FlagTypePassByValue);
+ EXPECT_EQ(1 << 16, OpenCLDebugInfo100FlagTypePassByReference);
+}
+
+TEST(ExtInstCLDebugInfo, BaseTypeAttributeEndodingValues) {
+ EXPECT_EQ(0, OpenCLDebugInfo100Unspecified);
+ EXPECT_EQ(1, OpenCLDebugInfo100Address);
+ EXPECT_EQ(2, OpenCLDebugInfo100Boolean);
+ EXPECT_EQ(3, OpenCLDebugInfo100Float);
+ EXPECT_EQ(4, OpenCLDebugInfo100Signed);
+ EXPECT_EQ(5, OpenCLDebugInfo100SignedChar);
+ EXPECT_EQ(6, OpenCLDebugInfo100Unsigned);
+ EXPECT_EQ(7, OpenCLDebugInfo100UnsignedChar);
+}
+
+TEST(ExtInstCLDebugInfo, CompositeTypeValues) {
+ EXPECT_EQ(0, OpenCLDebugInfo100Class);
+ EXPECT_EQ(1, OpenCLDebugInfo100Structure);
+ EXPECT_EQ(2, OpenCLDebugInfo100Union);
+}
+
+TEST(ExtInstCLDebugInfo, TypeQualifierValues) {
+ EXPECT_EQ(0, OpenCLDebugInfo100ConstType);
+ EXPECT_EQ(1, OpenCLDebugInfo100VolatileType);
+ EXPECT_EQ(2, OpenCLDebugInfo100RestrictType);
+ EXPECT_EQ(3, OpenCLDebugInfo100AtomicType);
+}
+
+TEST(ExtInstCLDebugInfo, DebugOperationValues) {
+ EXPECT_EQ(0, OpenCLDebugInfo100Deref);
+ EXPECT_EQ(1, OpenCLDebugInfo100Plus);
+ EXPECT_EQ(2, OpenCLDebugInfo100Minus);
+ EXPECT_EQ(3, OpenCLDebugInfo100PlusUconst);
+ EXPECT_EQ(4, OpenCLDebugInfo100BitPiece);
+ EXPECT_EQ(5, OpenCLDebugInfo100Swap);
+ EXPECT_EQ(6, OpenCLDebugInfo100Xderef);
+ EXPECT_EQ(7, OpenCLDebugInfo100StackValue);
+ EXPECT_EQ(8, OpenCLDebugInfo100Constu);
+ EXPECT_EQ(9, OpenCLDebugInfo100Fragment);
+}
+
+TEST(ExtInstCLDebugInfo, ImportedEntityValues) {
+ EXPECT_EQ(0, OpenCLDebugInfo100ImportedModule);
+ EXPECT_EQ(1, OpenCLDebugInfo100ImportedDeclaration);
+}
+
+// Test round trip through assembler and disassembler.
+
+struct InstructionCase {
+ uint32_t opcode;
+ std::string name;
+ std::string operands;
+ std::vector<uint32_t> expected_operands;
+};
+
+using ExtInstCLDebugInfo100RoundTripTest =
+ spvtest::TextToBinaryTestBase<::testing::TestWithParam<InstructionCase>>;
+using ExtInstCLDebugInfo100RoundTripTestExplicit = spvtest::TextToBinaryTest;
+
+TEST_P(ExtInstCLDebugInfo100RoundTripTest, ParameterizedExtInst) {
+ const std::string input =
+ "%1 = OpExtInstImport \"OpenCL.DebugInfo.100\"\n"
+ "%3 = OpExtInst %2 %1 " +
+ GetParam().name + GetParam().operands + "\n";
+ // First make sure it assembles correctly.
+ std::cout << input << std::endl;
+ EXPECT_THAT(CompiledInstructions(input),
+ Eq(Concatenate(
+ {MakeInstruction(SpvOpExtInstImport, {1},
+ MakeVector("OpenCL.DebugInfo.100")),
+ MakeInstruction(SpvOpExtInst, {2, 3, 1, GetParam().opcode},
+ GetParam().expected_operands)})))
+ << input;
+ // Now check the round trip through the disassembler.
+ EXPECT_THAT(EncodeAndDecodeSuccessfully(input), input) << input;
+}
+
+#define EPREFIX "Debug"
+
+#define CASE_0(Enum) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, "", {} \
+ }
+
+#define CASE_ILL(Enum, L0, L1) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 " #L0 " " #L1, { \
+ 4, L0, L1 \
+ } \
+ }
+
+#define CASE_IL(Enum, L0) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 " #L0, { \
+ 4, L0 \
+ } \
+ }
+
+#define CASE_I(Enum) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4", { 4 } \
+ }
+
+#define CASE_II(Enum) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 %5", { 4, 5 } \
+ }
+
+#define CASE_III(Enum) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 %5 %6", { \
+ 4, 5, 6 \
+ } \
+ }
+
+#define CASE_IIII(Enum) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 %5 %6 %7", { \
+ 4, 5, 6, 7 \
+ } \
+ }
+
+#define CASE_IIIII(Enum) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 %5 %6 %7 %8", \
+ { \
+ 4, 5, 6, 7, 8 \
+ } \
+ }
+
+#define CASE_IIIIII(Enum) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 %5 %6 %7 %8 %9", { \
+ 4, 5, 6, 7, 8, 9 \
+ } \
+ }
+
+#define CASE_IIIIIII(Enum) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 %5 %6 %7 %8 %9 %10", { \
+ 4, 5, 6, 7, 8, 9, 10 \
+ } \
+ }
+
+#define CASE_IIILLI(Enum, L0, L1) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 %5 %6 " #L0 " " #L1 " %7", { \
+ 4, 5, 6, L0, L1, 7 \
+ } \
+ }
+
+#define CASE_IIILLIF(Enum, L0, L1, Fstr, Fnum) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 %5 %6 " #L0 " " #L1 " %7 " Fstr, { \
+ 4, 5, 6, L0, L1, 7, Fnum \
+ } \
+ }
+
+#define CASE_IIILLIFL(Enum, L0, L1, Fstr, Fnum, L2) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 %5 %6 " #L0 " " #L1 " %7 " Fstr " " #L2, { \
+ 4, 5, 6, L0, L1, 7, Fnum, L2 \
+ } \
+ }
+
+#define CASE_IIILLIL(Enum, L0, L1, L2) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 %5 %6 " #L0 " " #L1 " %7 " #L2, { \
+ 4, 5, 6, L0, L1, 7, L2 \
+ } \
+ }
+
+#define CASE_IE(Enum, E0) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 " #E0, { \
+ 4, uint32_t(OpenCLDebugInfo100##E0) \
+ } \
+ }
+
+#define CASE_IEIILLI(Enum, E0, L1, L2) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 " #E0 " %5 %6 " #L1 " " #L2 " %7", { \
+ 4, uint32_t(OpenCLDebugInfo100##E0), 5, 6, L1, L2, 7 \
+ } \
+ }
+
+#define CASE_IIE(Enum, E0) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 %5 " #E0, { \
+ 4, 5, uint32_t(OpenCLDebugInfo100##E0) \
+ } \
+ }
+
+#define CASE_ISF(Enum, S0, Fstr, Fnum) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 " #S0 " " Fstr, { \
+ 4, uint32_t(SpvStorageClass##S0), Fnum \
+ } \
+ }
+
+#define CASE_LII(Enum, L0) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " " #L0 " %4 %5", \
+ { \
+ L0, 4, 5 \
+ } \
+ }
+
+#define CASE_LLIe(Enum, L0, L1, RawEnumName, RawEnumValue) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " " #L0 " " #L1 " %4 " RawEnumName, { \
+ L0, L1, 4, RawEnumValue \
+ } \
+ }
+
+#define CASE_ILI(Enum, L0) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 " #L0 " %5", \
+ { \
+ 4, L0, 5 \
+ } \
+ }
+
+#define CASE_ILII(Enum, L0) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 " #L0 " %5 %6", { \
+ 4, L0, 5, 6 \
+ } \
+ }
+
+#define CASE_ILLII(Enum, L0, L1) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 " #L0 " " #L1 " %5 %6", { \
+ 4, L0, L1, 5, 6 \
+ } \
+ }
+
+#define CASE_IIILLIIF(Enum, L0, L1, Fstr, Fnum) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr, { \
+ 4, 5, 6, L0, L1, 7, 8, Fnum \
+ } \
+ }
+
+#define CASE_IIILLIIFII(Enum, L0, L1, Fstr, Fnum) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " %9 %10", { \
+ 4, 5, 6, L0, L1, 7, 8, Fnum, 9, 10 \
+ } \
+ }
+
+#define CASE_IIILLIIFIIII(Enum, L0, L1, Fstr, Fnum) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " %9 %10 %11 %12", { \
+ 4, 5, 6, L0, L1, 7, 8, Fnum, 9, 10, 11, 12 \
+ } \
+ }
+
+#define CASE_IIILLIIFIIIIII(Enum, L0, L1, Fstr, Fnum) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " %9 %10 %11 %12 %13 %14", { \
+ 4, 5, 6, L0, L1, 7, 8, Fnum, 9, 10, 11, 12, 13, 14 \
+ } \
+ }
+
+#define CASE_IEILLIIIF(Enum, E0, L0, L1, Fstr, Fnum) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 %8 " Fstr, { \
+ 4, uint32_t(OpenCLDebugInfo100##E0), 5, L0, L1, 6, 7, 8, Fnum \
+ } \
+ }
+
+#define CASE_IEILLIIIFI(Enum, E0, L0, L1, Fstr, Fnum) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 %8 " Fstr " %9", { \
+ 4, uint32_t(OpenCLDebugInfo100##E0), 5, L0, L1, 6, 7, 8, Fnum, 9 \
+ } \
+ }
+
+#define CASE_IEILLIIIFII(Enum, E0, L0, L1, Fstr, Fnum) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 %8 " Fstr " %9 %10", { \
+ 4, uint32_t(OpenCLDebugInfo100##E0), 5, L0, L1, 6, 7, 8, Fnum, 9, 10 \
+ } \
+ }
+
+#define CASE_IEILLIIIFIII(Enum, E0, L0, L1, Fstr, Fnum) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 %8 " Fstr " %9 %10 %11", { \
+ 4, uint32_t(OpenCLDebugInfo100##E0), 5, L0, L1, 6, 7, 8, Fnum, 9, 10, 11 \
+ } \
+ }
+
+#define CASE_IEILLIIIFIIII(Enum, E0, L0, L1, Fstr, Fnum) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 %8 " Fstr " %9 %10 %11 %12", { \
+ 4, uint32_t(OpenCLDebugInfo100##E0), 5, L0, L1, 6, 7, 8, Fnum, 9, 10, \
+ 11, 12 \
+ } \
+ }
+
+#define CASE_IIILLIIIF(Enum, L0, L1, Fstr, Fnum) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 %5 %6 " #L0 " " #L1 " %7 %8 %9 " Fstr, { \
+ 4, 5, 6, L0, L1, 7, 8, 9, Fnum \
+ } \
+ }
+
+#define CASE_IIILLIIIFI(Enum, L0, L1, Fstr, Fnum) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 %5 %6 " #L0 " " #L1 " %7 %8 %9 " Fstr " %10", { \
+ 4, 5, 6, L0, L1, 7, 8, 9, Fnum, 10 \
+ } \
+ }
+
+#define CASE_IIIIF(Enum, Fstr, Fnum) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 %5 %6 %7 " Fstr, { \
+ 4, 5, 6, 7, Fnum \
+ } \
+ }
+
+#define CASE_IIILL(Enum, L0, L1) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 %5 %6 " #L0 " " #L1, { \
+ 4, 5, 6, L0, L1 \
+ } \
+ }
+
+#define CASE_IIIILL(Enum, L0, L1) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 %5 %6 %7 " #L0 " " #L1, { \
+ 4, 5, 6, 7, L0, L1 \
+ } \
+ }
+
+#define CASE_IILLI(Enum, L0, L1) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 %5 " #L0 " " #L1 " %6", { \
+ 4, 5, L0, L1, 6 \
+ } \
+ }
+
+#define CASE_IILLII(Enum, L0, L1) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 %5 " #L0 " " #L1 " %6 %7", { \
+ 4, 5, L0, L1, 6, 7 \
+ } \
+ }
+
+#define CASE_IILLIII(Enum, L0, L1) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 %5 " #L0 " " #L1 " %6 %7 %8", { \
+ 4, 5, L0, L1, 6, 7, 8 \
+ } \
+ }
+
+#define CASE_IILLIIII(Enum, L0, L1) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 %5 " #L0 " " #L1 " %6 %7 %8 %9", { \
+ 4, 5, L0, L1, 6, 7, 8, 9 \
+ } \
+ }
+
+#define CASE_IIILLIIFLI(Enum, L0, L1, Fstr, Fnum, L2) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " " #L2 " %9", { \
+ 4, 5, 6, L0, L1, 7, 8, Fnum, L2, 9 \
+ } \
+ }
+
+#define CASE_IIILLIIFLII(Enum, L0, L1, Fstr, Fnum, L2) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " " #L2 " %9 %10", { \
+ 4, 5, 6, L0, L1, 7, 8, Fnum, L2, 9, 10 \
+ } \
+ }
+
+#define CASE_E(Enum, E0) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " " #E0, { \
+ uint32_t(OpenCLDebugInfo100##E0) \
+ } \
+ }
+
+#define CASE_EI(Enum, E0) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " " #E0 " %4", { \
+ uint32_t(OpenCLDebugInfo100##E0), 4 \
+ } \
+ }
+
+#define CASE_EII(Enum, E0) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " " #E0 " %4 %5", \
+ { \
+ uint32_t(OpenCLDebugInfo100##E0), 4, 5 \
+ } \
+ }
+
+#define CASE_EIII(Enum, E0) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " " #E0 " %4 %5 %6", { \
+ uint32_t(OpenCLDebugInfo100##E0), 4, 5, 6 \
+ } \
+ }
+
+#define CASE_EIIII(Enum, E0) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " " #E0 " %4 %5 %6 %7", { \
+ uint32_t(OpenCLDebugInfo100##E0), 4, 5, 6, 7 \
+ } \
+ }
+
+#define CASE_EIIIII(Enum, E0) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " " #E0 " %4 %5 %6 %7 %8", { \
+ uint32_t(OpenCLDebugInfo100##E0), 4, 5, 6, 7, 8 \
+ } \
+ }
+
+#define CASE_EL(Enum, E0, L0) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " " #E0 " " #L0, { \
+ uint32_t(OpenCLDebugInfo100##E0), L0 \
+ } \
+ }
+
+#define CASE_ELL(Enum, E0, L0, L1) \
+ { \
+ uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+ " " #E0 " " #L0 " " #L1, { \
+ uint32_t(OpenCLDebugInfo100##E0), L0, L1 \
+ } \
+ }
+
+// OpenCL.DebugInfo.100 4.1 Missing Debugging Information
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugInfoNone,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_0(InfoNone), // enum value 0
+ })));
+
+// OpenCL.DebugInfo.100 4.2 Compilation Unit
+INSTANTIATE_TEST_SUITE_P(
+ OpenCLDebugInfo100DebugCompilationUnit, ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_LLIe(CompilationUnit, 100, 42, "HLSL", SpvSourceLanguageHLSL),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(
+ OpenCLDebugInfo100DebugSource, ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ // TODO(dneto): Should this be a list of sourc texts,
+ // to accomodate length limits?
+ CASE_I(Source),
+ CASE_II(Source),
+ })));
+
+// OpenCL.DebugInfo.100 4.3 Type instructions
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeBasic,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_IIE(TypeBasic, Unspecified),
+ CASE_IIE(TypeBasic, Address),
+ CASE_IIE(TypeBasic, Boolean),
+ CASE_IIE(TypeBasic, Float),
+ CASE_IIE(TypeBasic, Signed),
+ CASE_IIE(TypeBasic, SignedChar),
+ CASE_IIE(TypeBasic, Unsigned),
+ CASE_IIE(TypeBasic, UnsignedChar),
+ })));
+
+// The FlagIsPublic is value is (1 << 0) | (1 << 2) which is the same
+// as the bitwise-OR of FlagIsProtected and FlagIsPrivate.
+// The disassembler will emit the compound expression instead.
+// There is no simple fix for this. This enum is not really a mask
+// for the bottom two bits.
+TEST_F(ExtInstCLDebugInfo100RoundTripTestExplicit, FlagIsPublic) {
+ const std::string prefix =
+ "%1 = OpExtInstImport \"DebugInfo\"\n"
+ "%3 = OpExtInst %2 %1 DebugTypePointer %4 Private ";
+ const std::string input = prefix + "FlagIsPublic\n";
+ const std::string expected = prefix + "FlagIsProtected|FlagIsPrivate\n";
+ // First make sure it assembles correctly.
+ EXPECT_THAT(
+ CompiledInstructions(input),
+ Eq(Concatenate(
+ {MakeInstruction(SpvOpExtInstImport, {1}, MakeVector("DebugInfo")),
+ MakeInstruction(SpvOpExtInst,
+ {2, 3, 1, OpenCLDebugInfo100DebugTypePointer, 4,
+ uint32_t(SpvStorageClassPrivate),
+ OpenCLDebugInfo100FlagIsPublic})})))
+ << input;
+ // Now check the round trip through the disassembler.
+ EXPECT_THAT(EncodeAndDecodeSuccessfully(input), Eq(expected)) << input;
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ OpenCLDebugInfo100DebugTypePointer, ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+
+ //// Use each flag independently.
+ CASE_ISF(TypePointer, Private, "FlagIsProtected",
+ uint32_t(OpenCLDebugInfo100FlagIsProtected)),
+ CASE_ISF(TypePointer, Private, "FlagIsPrivate",
+ uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+
+ // FlagIsPublic is tested above.
+
+ CASE_ISF(TypePointer, Private, "FlagIsLocal",
+ uint32_t(OpenCLDebugInfo100FlagIsLocal)),
+ CASE_ISF(TypePointer, Private, "FlagIsDefinition",
+ uint32_t(OpenCLDebugInfo100FlagIsDefinition)),
+ CASE_ISF(TypePointer, Private, "FlagFwdDecl",
+ uint32_t(OpenCLDebugInfo100FlagFwdDecl)),
+ CASE_ISF(TypePointer, Private, "FlagArtificial",
+ uint32_t(OpenCLDebugInfo100FlagArtificial)),
+ CASE_ISF(TypePointer, Private, "FlagExplicit",
+ uint32_t(OpenCLDebugInfo100FlagExplicit)),
+ CASE_ISF(TypePointer, Private, "FlagPrototyped",
+ uint32_t(OpenCLDebugInfo100FlagPrototyped)),
+ CASE_ISF(TypePointer, Private, "FlagObjectPointer",
+ uint32_t(OpenCLDebugInfo100FlagObjectPointer)),
+ CASE_ISF(TypePointer, Private, "FlagStaticMember",
+ uint32_t(OpenCLDebugInfo100FlagStaticMember)),
+ CASE_ISF(TypePointer, Private, "FlagIndirectVariable",
+ uint32_t(OpenCLDebugInfo100FlagIndirectVariable)),
+ CASE_ISF(TypePointer, Private, "FlagLValueReference",
+ uint32_t(OpenCLDebugInfo100FlagLValueReference)),
+ CASE_ISF(TypePointer, Private, "FlagIsOptimized",
+ uint32_t(OpenCLDebugInfo100FlagIsOptimized)),
+ CASE_ISF(TypePointer, Private, "FlagIsEnumClass",
+ uint32_t(OpenCLDebugInfo100FlagIsEnumClass)),
+ CASE_ISF(TypePointer, Private, "FlagTypePassByValue",
+ uint32_t(OpenCLDebugInfo100FlagTypePassByValue)),
+ CASE_ISF(TypePointer, Private, "FlagTypePassByReference",
+ uint32_t(OpenCLDebugInfo100FlagTypePassByReference)),
+
+ //// Use flags in combination, and try different storage classes.
+ CASE_ISF(TypePointer, Function, "FlagIsProtected|FlagIsPrivate",
+ uint32_t(OpenCLDebugInfo100FlagIsProtected) |
+ uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+ CASE_ISF(
+ TypePointer, Workgroup,
+ "FlagIsPrivate|FlagFwdDecl|FlagIndirectVariable|FlagIsOptimized",
+ uint32_t(OpenCLDebugInfo100FlagIsPrivate) |
+ uint32_t(OpenCLDebugInfo100FlagFwdDecl) |
+ uint32_t(OpenCLDebugInfo100FlagIndirectVariable) |
+ uint32_t(OpenCLDebugInfo100FlagIsOptimized)),
+
+ })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeQualifier,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_IE(TypeQualifier, ConstType),
+ CASE_IE(TypeQualifier, VolatileType),
+ CASE_IE(TypeQualifier, RestrictType),
+ CASE_IE(TypeQualifier, AtomicType),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeArray,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_II(TypeArray),
+ CASE_III(TypeArray),
+ CASE_IIII(TypeArray),
+ CASE_IIIII(TypeArray),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeVector,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_IL(TypeVector, 2),
+ CASE_IL(TypeVector, 3),
+ CASE_IL(TypeVector, 4),
+ CASE_IL(TypeVector, 16),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypedef,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_IIILLI(Typedef, 12, 13),
+ CASE_IIILLI(Typedef, 14, 99),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeFunction,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_EI(TypeFunction, FlagIsProtected),
+ CASE_EII(TypeFunction, FlagIsDefinition),
+ CASE_EIII(TypeFunction, FlagArtificial),
+ CASE_EIIII(TypeFunction, FlagExplicit),
+ CASE_EIIIII(TypeFunction, FlagIsPrivate),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(
+ OpenCLDebugInfo100DebugTypeEnum, ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_IIILLIIFII(
+ TypeEnum, 12, 13,
+ "FlagIsPrivate|FlagFwdDecl|FlagIndirectVariable|FlagIsOptimized",
+ uint32_t(OpenCLDebugInfo100FlagIsPrivate) |
+ uint32_t(OpenCLDebugInfo100FlagFwdDecl) |
+ uint32_t(OpenCLDebugInfo100FlagIndirectVariable) |
+ uint32_t(OpenCLDebugInfo100FlagIsOptimized)),
+ CASE_IIILLIIFIIII(TypeEnum, 17, 18, "FlagStaticMember",
+ uint32_t(OpenCLDebugInfo100FlagStaticMember)),
+ CASE_IIILLIIFIIIIII(TypeEnum, 99, 1, "FlagStaticMember",
+ uint32_t(OpenCLDebugInfo100FlagStaticMember)),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(
+ OpenCLDebugInfo100DebugTypeComposite, ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_IEILLIIIF(
+ TypeComposite, Class, 12, 13,
+ "FlagIsPrivate|FlagFwdDecl|FlagIndirectVariable|FlagIsOptimized",
+ uint32_t(OpenCLDebugInfo100FlagIsPrivate) |
+ uint32_t(OpenCLDebugInfo100FlagFwdDecl) |
+ uint32_t(OpenCLDebugInfo100FlagIndirectVariable) |
+ uint32_t(OpenCLDebugInfo100FlagIsOptimized)),
+ // Cover all tag values: Class, Structure, Union
+ CASE_IEILLIIIF(TypeComposite, Class, 12, 13, "FlagIsPrivate",
+ uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+ CASE_IEILLIIIF(TypeComposite, Structure, 12, 13, "FlagIsPrivate",
+ uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+ CASE_IEILLIIIF(TypeComposite, Union, 12, 13, "FlagIsPrivate",
+ uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+ // Now add members
+ CASE_IEILLIIIFI(TypeComposite, Class, 9, 10, "FlagIsPrivate",
+ uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+ CASE_IEILLIIIFII(TypeComposite, Class, 9, 10, "FlagIsPrivate",
+ uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+ CASE_IEILLIIIFIII(TypeComposite, Class, 9, 10, "FlagIsPrivate",
+ uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+ CASE_IEILLIIIFIIII(TypeComposite, Class, 9, 10, "FlagIsPrivate",
+ uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(
+ OpenCLDebugInfo100DebugTypeMember, ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_IIILLIIIF(TypeMember, 12, 13, "FlagIsPrivate",
+ uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+ CASE_IIILLIIIF(TypeMember, 99, 100, "FlagIsPrivate|FlagFwdDecl",
+ uint32_t(OpenCLDebugInfo100FlagIsPrivate) |
+ uint32_t(OpenCLDebugInfo100FlagFwdDecl)),
+ // Add the optional Id argument.
+ CASE_IIILLIIIFI(TypeMember, 12, 13, "FlagIsPrivate",
+ uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(
+ OpenCLDebugInfo100DebugTypeInheritance, ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_IIIIF(TypeInheritance, "FlagIsPrivate",
+ uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+ CASE_IIIIF(TypeInheritance, "FlagIsPrivate|FlagFwdDecl",
+ uint32_t(OpenCLDebugInfo100FlagIsPrivate) |
+ uint32_t(OpenCLDebugInfo100FlagFwdDecl)),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypePtrToMember,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_II(TypePtrToMember),
+ })));
+
+// OpenCL.DebugInfo.100 4.4 Templates
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeTemplate,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_II(TypeTemplate),
+ CASE_III(TypeTemplate),
+ CASE_IIII(TypeTemplate),
+ CASE_IIIII(TypeTemplate),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeTemplateParameter,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_IIIILL(TypeTemplateParameter, 1, 2),
+ CASE_IIIILL(TypeTemplateParameter, 99, 102),
+ CASE_IIIILL(TypeTemplateParameter, 10, 7),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeTemplateTemplateParameter,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_IIILL(TypeTemplateTemplateParameter, 1, 2),
+ CASE_IIILL(TypeTemplateTemplateParameter, 99, 102),
+ CASE_IIILL(TypeTemplateTemplateParameter, 10, 7),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeTemplateParameterPack,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_IILLI(TypeTemplateParameterPack, 1, 2),
+ CASE_IILLII(TypeTemplateParameterPack, 99, 102),
+ CASE_IILLIII(TypeTemplateParameterPack, 10, 7),
+ CASE_IILLIIII(TypeTemplateParameterPack, 10, 7),
+ })));
+
+// OpenCL.DebugInfo.100 4.5 Global Variables
+
+INSTANTIATE_TEST_SUITE_P(
+ OpenCLDebugInfo100DebugGlobalVariable, ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_IIILLIIIF(GlobalVariable, 1, 2, "FlagIsOptimized",
+ uint32_t(OpenCLDebugInfo100FlagIsOptimized)),
+ CASE_IIILLIIIF(GlobalVariable, 42, 43, "FlagIsOptimized",
+ uint32_t(OpenCLDebugInfo100FlagIsOptimized)),
+ CASE_IIILLIIIFI(GlobalVariable, 1, 2, "FlagIsOptimized",
+ uint32_t(OpenCLDebugInfo100FlagIsOptimized)),
+ CASE_IIILLIIIFI(GlobalVariable, 42, 43, "FlagIsOptimized",
+ uint32_t(OpenCLDebugInfo100FlagIsOptimized)),
+ })));
+
+// OpenCL.DebugInfo.100 4.6 Functions
+
+INSTANTIATE_TEST_SUITE_P(
+ OpenCLDebugInfo100DebugFunctionDeclaration,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_IIILLIIF(FunctionDeclaration, 1, 2, "FlagIsOptimized",
+ uint32_t(OpenCLDebugInfo100FlagIsOptimized)),
+ CASE_IIILLIIF(FunctionDeclaration, 42, 43, "FlagFwdDecl",
+ uint32_t(OpenCLDebugInfo100FlagFwdDecl)),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(
+ OpenCLDebugInfo100DebugFunction, ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_IIILLIIFLI(Function, 1, 2, "FlagIsOptimized",
+ uint32_t(OpenCLDebugInfo100FlagIsOptimized), 3),
+ CASE_IIILLIIFLI(Function, 42, 43, "FlagFwdDecl",
+ uint32_t(OpenCLDebugInfo100FlagFwdDecl), 44),
+ // Add the optional declaration Id.
+ CASE_IIILLIIFLII(Function, 1, 2, "FlagIsOptimized",
+ uint32_t(OpenCLDebugInfo100FlagIsOptimized), 3),
+ CASE_IIILLIIFLII(Function, 42, 43, "FlagFwdDecl",
+ uint32_t(OpenCLDebugInfo100FlagFwdDecl), 44),
+ })));
+
+// OpenCL.DebugInfo.100 4.7 Local Information
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugLexicalBlock,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_ILLII(LexicalBlock, 1, 2),
+ CASE_ILLII(LexicalBlock, 42, 43),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugLexicalBlockDiscriminator,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_ILI(LexicalBlockDiscriminator, 1),
+ CASE_ILI(LexicalBlockDiscriminator, 42),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugScope,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_I(Scope),
+ CASE_II(Scope),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugNoScope,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_0(NoScope),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugInlinedAt,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_LII(InlinedAt, 1),
+ CASE_LII(InlinedAt, 42),
+ })));
+
+// OpenCL.DebugInfo.100 4.8 Local Variables
+
+INSTANTIATE_TEST_SUITE_P(
+ OpenCLDebugInfo100DebugLocalVariable, ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_IIILLIF(LocalVariable, 1, 2, "FlagIsPrivate",
+ OpenCLDebugInfo100FlagIsPrivate),
+ CASE_IIILLIF(LocalVariable, 4, 5, "FlagIsProtected",
+ OpenCLDebugInfo100FlagIsProtected),
+ CASE_IIILLIFL(LocalVariable, 9, 99, "FlagIsProtected",
+ OpenCLDebugInfo100FlagIsProtected, 195),
+ CASE_IIILLIFL(LocalVariable, 19, 199, "FlagIsPrivate",
+ OpenCLDebugInfo100FlagIsPrivate, 195),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugInlinedVariable,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_II(InlinedVariable),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugDebugDeclare,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_III(Declare),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(
+ OpenCLDebugInfo100DebugDebugValue, ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_IIII(Value),
+ CASE_IIIII(Value),
+ CASE_IIIIII(Value),
+ // Test up to 3 id parameters. We can always try more.
+ CASE_IIIIIII(Value),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugDebugOperation,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_E(Operation, Deref),
+ CASE_E(Operation, Plus),
+ CASE_E(Operation, Minus),
+ CASE_EL(Operation, PlusUconst, 1),
+ CASE_EL(Operation, PlusUconst, 42),
+ CASE_ELL(Operation, BitPiece, 1, 2),
+ CASE_ELL(Operation, BitPiece, 4, 5),
+ CASE_E(Operation, Swap),
+ CASE_E(Operation, Xderef),
+ CASE_E(Operation, StackValue),
+ CASE_EL(Operation, Constu, 1),
+ CASE_EL(Operation, Constu, 42),
+ CASE_ELL(Operation, Fragment, 100, 200),
+ CASE_ELL(Operation, Fragment, 8, 9),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugDebugExpression,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_0(Expression),
+ CASE_I(Expression),
+ CASE_II(Expression),
+ CASE_III(Expression),
+ CASE_IIII(Expression),
+ CASE_IIIII(Expression),
+ CASE_IIIIII(Expression),
+ CASE_IIIIIII(Expression),
+ })));
+
+// OpenCL.DebugInfo.100 4.9 Macros
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugMacroDef,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_ILI(MacroDef, 1),
+ CASE_ILI(MacroDef, 42),
+ CASE_ILII(MacroDef, 1),
+ CASE_ILII(MacroDef, 42),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugMacroUndef,
+ ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ CASE_ILI(MacroUndef, 1),
+ CASE_ILI(MacroUndef, 42),
+ })));
+
+// OpenCL.DebugInfo.100 4.10 Imported Entities
+
+INSTANTIATE_TEST_SUITE_P(
+ OpenCLDebugInfo100DebugImportedEntity, ExtInstCLDebugInfo100RoundTripTest,
+ ::testing::ValuesIn(std::vector<InstructionCase>({
+ // ID Name
+ // Literal Tag
+ // ID Source
+ // ID Entity
+ // Literal Number Line
+ // Literal Number Column
+ // ID Parent
+ CASE_IEIILLI(ImportedEntity, ImportedModule, 67, 68),
+ CASE_IEIILLI(ImportedEntity, ImportedDeclaration, 42, 43),
+ })));
+
+#undef EPREFIX
+#undef CASE_0
+#undef CASE_ILL
+#undef CASE_IL
+#undef CASE_I
+#undef CASE_II
+#undef CASE_III
+#undef CASE_IIII
+#undef CASE_IIIII
+#undef CASE_IIIIII
+#undef CASE_IIIIIII
+#undef CASE_IIILLI
+#undef CASE_IIILLIL
+#undef CASE_IE
+#undef CASE_IEIILLI
+#undef CASE_IIE
+#undef CASE_ISF
+#undef CASE_LII
+#undef CASE_LLIe
+#undef CASE_ILI
+#undef CASE_ILII
+#undef CASE_ILLII
+#undef CASE_IIILLIF
+#undef CASE_IIILLIFL
+#undef CASE_IIILLIIF
+#undef CASE_IIILLIIFII
+#undef CASE_IIILLIIFIIII
+#undef CASE_IIILLIIFIIIIII
+#undef CASE_IEILLIIIF
+#undef CASE_IEILLIIIFI
+#undef CASE_IEILLIIIFII
+#undef CASE_IEILLIIIFIII
+#undef CASE_IEILLIIIFIIII
+#undef CASE_IIILLIIIF
+#undef CASE_IIILLIIIFI
+#undef CASE_IIIIF
+#undef CASE_IIILL
+#undef CASE_IIIILL
+#undef CASE_IILLI
+#undef CASE_IILLII
+#undef CASE_IILLIII
+#undef CASE_IILLIIII
+#undef CASE_IIILLIIFLI
+#undef CASE_IIILLIIFLII
+#undef CASE_E
+#undef CASE_EI
+#undef CASE_EII
+#undef CASE_EIII
+#undef CASE_EIIII
+#undef CASE_EIIIII
+#undef CASE_EL
+#undef CASE_ELL
+
+} // namespace
+} // namespace spvtools
diff --git a/test/ext_inst.non_semantic_test.cpp b/test/ext_inst.non_semantic_test.cpp
new file mode 100644
index 0000000..870684e
--- /dev/null
+++ b/test/ext_inst.non_semantic_test.cpp
@@ -0,0 +1,90 @@
+// Copyright (c) 2015-2016 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.
+
+// Assembler tests for non-semantic extended instructions
+
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "test/test_fixture.h"
+#include "test/unit_spirv.h"
+
+using ::testing::Eq;
+
+namespace spvtools {
+namespace {
+
+using NonSemanticRoundTripTest = RoundTripTest;
+using NonSemanticTextToBinaryTest = spvtest::TextToBinaryTest;
+
+TEST_F(NonSemanticRoundTripTest, NonSemanticInsts) {
+ std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Testing.ExtInst"
+%2 = OpTypeVoid
+%3 = OpExtInst %2 %1 132384681 %2
+%4 = OpTypeInt 32 0
+%5 = OpConstant %4 123
+%6 = OpString "Test string"
+%7 = OpExtInst %4 %1 82198732 %5 %6
+%8 = OpExtInstImport "NonSemantic.Testing.AnotherUnknownExtInstSet"
+%9 = OpExtInst %4 %8 613874321 %7 %5 %6
+)";
+ std::string disassembly = EncodeAndDecodeSuccessfully(
+ spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_0);
+ EXPECT_THAT(disassembly, Eq(spirv));
+}
+
+TEST_F(NonSemanticTextToBinaryTest, InvalidExtInstSetName) {
+ std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic_Testing_ExtInst"
+)";
+
+ EXPECT_THAT(
+ CompileFailure(spirv),
+ Eq("Invalid extended instruction import 'NonSemantic_Testing_ExtInst'"));
+}
+
+TEST_F(NonSemanticTextToBinaryTest, NonSemanticIntParameter) {
+ std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Testing.ExtInst"
+%2 = OpTypeVoid
+%3 = OpExtInst %2 %1 1 99999
+)";
+
+ EXPECT_THAT(CompileFailure(spirv), Eq("Expected id to start with %."));
+}
+
+TEST_F(NonSemanticTextToBinaryTest, NonSemanticFloatParameter) {
+ std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Testing.ExtInst"
+%2 = OpTypeVoid
+%3 = OpExtInst %2 %1 1 3.141592
+)";
+
+ EXPECT_THAT(CompileFailure(spirv), Eq("Expected id to start with %."));
+}
+
+TEST_F(NonSemanticTextToBinaryTest, NonSemanticStringParameter) {
+ std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Testing.ExtInst"
+%2 = OpTypeVoid
+%3 = OpExtInst %2 %1 1 "foobar"
+)";
+
+ EXPECT_THAT(CompileFailure(spirv), Eq("Expected id to start with %."));
+}
+
+} // namespace
+} // namespace spvtools
diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt
index b38f35e..fb9e964 100644
--- a/test/fuzz/CMakeLists.txt
+++ b/test/fuzz/CMakeLists.txt
@@ -24,18 +24,28 @@
fuzzer_pass_add_useful_constructs_test.cpp
instruction_descriptor_test.cpp
transformation_add_constant_boolean_test.cpp
+ transformation_add_constant_composite_test.cpp
transformation_add_constant_scalar_test.cpp
transformation_add_dead_break_test.cpp
transformation_add_dead_continue_test.cpp
+ transformation_add_global_undef_test.cpp
+ transformation_add_global_variable_test.cpp
transformation_add_no_contraction_decoration_test.cpp
+ transformation_add_type_array_test.cpp
transformation_add_type_boolean_test.cpp
transformation_add_type_float_test.cpp
+ transformation_add_type_function_test.cpp
transformation_add_type_int_test.cpp
+ transformation_add_type_matrix_test.cpp
transformation_add_type_pointer_test.cpp
+ transformation_add_type_struct_test.cpp
+ transformation_add_type_vector_test.cpp
transformation_composite_construct_test.cpp
transformation_composite_extract_test.cpp
transformation_copy_object_test.cpp
+ transformation_merge_blocks_test.cpp
transformation_move_block_down_test.cpp
+ transformation_outline_function_test.cpp
transformation_replace_boolean_constant_with_constant_binary_test.cpp
transformation_replace_constant_with_uniform_test.cpp
transformation_replace_id_with_synonym_test.cpp
diff --git a/test/fuzz/fuzz_test_util.cpp b/test/fuzz/fuzz_test_util.cpp
index bc6d4ee..1d15ad6 100644
--- a/test/fuzz/fuzz_test_util.cpp
+++ b/test/fuzz/fuzz_test_util.cpp
@@ -14,6 +14,7 @@
#include "test/fuzz/fuzz_test_util.h"
+#include <fstream>
#include <iostream>
#include "tools/io.h"
@@ -105,5 +106,20 @@
}
}
+void DumpTransformationsJson(
+ const protobufs::TransformationSequence& transformations,
+ const char* filename) {
+ std::string json_string;
+ auto json_options = google::protobuf::util::JsonOptions();
+ 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) {
+ std::ofstream transformations_json_file(filename);
+ transformations_json_file << json_string;
+ transformations_json_file.close();
+ }
+}
+
} // namespace fuzz
} // namespace spvtools
diff --git a/test/fuzz/fuzz_test_util.h b/test/fuzz/fuzz_test_util.h
index 93f37e5..9e08bf6 100644
--- a/test/fuzz/fuzz_test_util.h
+++ b/test/fuzz/fuzz_test_util.h
@@ -19,6 +19,7 @@
#include <vector>
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/opt/build_module.h"
#include "source/opt/ir_context.h"
#include "spirv-tools/libspirv.h"
@@ -100,6 +101,12 @@
// Dumps |binary| to file |filename|. Useful for interactive debugging.
void DumpShader(const std::vector<uint32_t>& binary, const char* filename);
+// Dumps |transformations| to file |filename| in JSON format. Useful for
+// interactive debugging.
+void DumpTransformationsJson(
+ const protobufs::TransformationSequence& transformations,
+ const char* filename);
+
} // namespace fuzz
} // namespace spvtools
diff --git a/test/fuzz/fuzzer_replayer_test.cpp b/test/fuzz/fuzzer_replayer_test.cpp
index 22a26fc..fa1f096 100644
--- a/test/fuzz/fuzzer_replayer_test.cpp
+++ b/test/fuzz/fuzzer_replayer_test.cpp
@@ -62,13 +62,11 @@
for (uint32_t seed = initial_seed; seed < initial_seed + num_runs; seed++) {
std::vector<uint32_t> fuzzer_binary_out;
protobufs::TransformationSequence fuzzer_transformation_sequence_out;
- spvtools::FuzzerOptions fuzzer_options;
- spvFuzzerOptionsSetRandomSeed(fuzzer_options, seed);
- Fuzzer fuzzer(env);
+ Fuzzer fuzzer(env, seed, true);
fuzzer.SetMessageConsumer(kSilentConsumer);
auto fuzzer_result_status =
- fuzzer.Run(binary_in, initial_facts, fuzzer_options, &fuzzer_binary_out,
+ fuzzer.Run(binary_in, initial_facts, &fuzzer_binary_out,
&fuzzer_transformation_sequence_out);
ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result_status);
ASSERT_TRUE(t.Validate(fuzzer_binary_out));
@@ -421,8 +419,8 @@
%86 = OpLabel
%184 = OpPhi %26 %27 %78 %126 %89
%92 = OpSLessThan %8 %184 %56
- OpLoopMerge %88 %89 None
- OpBranchConditional %92 %87 %88
+ OpLoopMerge %1000 %89 None
+ OpBranchConditional %92 %87 %1000
%87 = OpLabel
%95 = OpIAdd %26 %183 %74
%96 = OpSLessThan %8 %184 %95
@@ -464,6 +462,8 @@
%89 = OpLabel
%126 = OpIAdd %26 %184 %74
OpBranch %86
+ %1000 = OpLabel
+ OpBranch %88
%88 = OpLabel
%128 = OpIAdd %26 %183 %74
OpBranch %77
@@ -724,7 +724,6 @@
%37 = OpSDiv %6 %302 %35
%38 = OpIMul %6 %35 %37
%40 = OpIEqual %17 %38 %302
- OpSelectionMerge %42 None
OpBranchConditional %40 %41 %42
%41 = OpLabel
%50 = OpConvertSToF %20 %302
@@ -752,7 +751,6 @@
OpBranch %59
%75 = OpLabel
%78 = OpSGreaterThan %17 %304 %9
- OpSelectionMerge %80 None
OpBranchConditional %78 %79 %80
%79 = OpLabel
%83 = OpISub %6 %304 %54
diff --git a/test/fuzz/fuzzer_shrinker_test.cpp b/test/fuzz/fuzzer_shrinker_test.cpp
index 6485070..9af863e 100644
--- a/test/fuzz/fuzzer_shrinker_test.cpp
+++ b/test/fuzz/fuzzer_shrinker_test.cpp
@@ -165,12 +165,10 @@
// Run the fuzzer and check that it successfully yields a valid binary.
std::vector<uint32_t> fuzzer_binary_out;
protobufs::TransformationSequence fuzzer_transformation_sequence_out;
- spvtools::FuzzerOptions fuzzer_options;
- spvFuzzerOptionsSetRandomSeed(fuzzer_options, seed);
- Fuzzer fuzzer(env);
+ Fuzzer fuzzer(env, seed, true);
fuzzer.SetMessageConsumer(kSilentConsumer);
auto fuzzer_result_status =
- fuzzer.Run(binary_in, initial_facts, fuzzer_options, &fuzzer_binary_out,
+ fuzzer.Run(binary_in, initial_facts, &fuzzer_binary_out,
&fuzzer_transformation_sequence_out);
ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result_status);
ASSERT_TRUE(t.Validate(fuzzer_binary_out));
@@ -530,8 +528,8 @@
%86 = OpLabel
%184 = OpPhi %26 %27 %78 %126 %89
%92 = OpSLessThan %8 %184 %56
- OpLoopMerge %88 %89 None
- OpBranchConditional %92 %87 %88
+ OpLoopMerge %1000 %89 None
+ OpBranchConditional %92 %87 %1000
%87 = OpLabel
%95 = OpIAdd %26 %183 %74
%96 = OpSLessThan %8 %184 %95
@@ -573,6 +571,8 @@
%89 = OpLabel
%126 = OpIAdd %26 %184 %74
OpBranch %86
+ %1000 = OpLabel
+ OpBranch %88
%88 = OpLabel
%128 = OpIAdd %26 %183 %74
OpBranch %77
@@ -831,7 +831,6 @@
%37 = OpSDiv %6 %302 %35
%38 = OpIMul %6 %35 %37
%40 = OpIEqual %17 %38 %302
- OpSelectionMerge %42 None
OpBranchConditional %40 %41 %42
%41 = OpLabel
%50 = OpConvertSToF %20 %302
@@ -859,7 +858,6 @@
OpBranch %59
%75 = OpLabel
%78 = OpSGreaterThan %17 %304 %9
- OpSelectionMerge %80 None
OpBranchConditional %78 %79 %80
%79 = OpLabel
%83 = OpISub %6 %304 %54
diff --git a/test/fuzz/transformation_add_constant_composite_test.cpp b/test/fuzz/transformation_add_constant_composite_test.cpp
new file mode 100644
index 0000000..5ce171b
--- /dev/null
+++ b/test/fuzz/transformation_add_constant_composite_test.cpp
@@ -0,0 +1,158 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_constant_composite.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddConstantCompositeTest, BasicTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 2
+ %8 = OpTypeMatrix %7 3
+ %11 = OpConstant %6 0
+ %12 = OpConstant %6 1
+ %14 = OpConstant %6 2
+ %15 = OpConstant %6 3
+ %17 = OpConstant %6 4
+ %18 = OpConstant %6 5
+ %21 = OpTypeInt 32 1
+ %22 = OpTypeInt 32 0
+ %23 = OpConstant %22 3
+ %24 = OpTypeArray %21 %23
+ %25 = OpTypeBool
+ %26 = OpTypeStruct %24 %25
+ %29 = OpConstant %21 1
+ %30 = OpConstant %21 2
+ %31 = OpConstant %21 3
+ %33 = OpConstantFalse %25
+ %35 = OpTypeVector %6 3
+ %38 = OpConstant %6 6
+ %39 = OpConstant %6 7
+ %40 = OpConstant %6 8
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ // Too few ids
+ ASSERT_FALSE(TransformationAddConstantComposite(103, 8, {100, 101})
+ .IsApplicable(context.get(), fact_manager));
+ // Too many ids
+ ASSERT_FALSE(TransformationAddConstantComposite(101, 7, {14, 15, 14})
+ .IsApplicable(context.get(), fact_manager));
+ // Id already in use
+ ASSERT_FALSE(TransformationAddConstantComposite(40, 7, {11, 12})
+ .IsApplicable(context.get(), fact_manager));
+ // %39 is not a type
+ ASSERT_FALSE(TransformationAddConstantComposite(100, 39, {11, 12})
+ .IsApplicable(context.get(), fact_manager));
+
+ TransformationAddConstantComposite transformations[] = {
+ // %100 = OpConstantComposite %7 %11 %12
+ TransformationAddConstantComposite(100, 7, {11, 12}),
+
+ // %101 = OpConstantComposite %7 %14 %15
+ TransformationAddConstantComposite(101, 7, {14, 15}),
+
+ // %102 = OpConstantComposite %7 %17 %18
+ TransformationAddConstantComposite(102, 7, {17, 18}),
+
+ // %103 = OpConstantComposite %8 %100 %101 %102
+ TransformationAddConstantComposite(103, 8, {100, 101, 102}),
+
+ // %104 = OpConstantComposite %24 %29 %30 %31
+ TransformationAddConstantComposite(104, 24, {29, 30, 31}),
+
+ // %105 = OpConstantComposite %26 %104 %33
+ TransformationAddConstantComposite(105, 26, {104, 33}),
+
+ // %106 = OpConstantComposite %35 %38 %39 %40
+ TransformationAddConstantComposite(106, 35, {38, 39, 40})};
+
+ for (auto& transformation : transformations) {
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ }
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 2
+ %8 = OpTypeMatrix %7 3
+ %11 = OpConstant %6 0
+ %12 = OpConstant %6 1
+ %14 = OpConstant %6 2
+ %15 = OpConstant %6 3
+ %17 = OpConstant %6 4
+ %18 = OpConstant %6 5
+ %21 = OpTypeInt 32 1
+ %22 = OpTypeInt 32 0
+ %23 = OpConstant %22 3
+ %24 = OpTypeArray %21 %23
+ %25 = OpTypeBool
+ %26 = OpTypeStruct %24 %25
+ %29 = OpConstant %21 1
+ %30 = OpConstant %21 2
+ %31 = OpConstant %21 3
+ %33 = OpConstantFalse %25
+ %35 = OpTypeVector %6 3
+ %38 = OpConstant %6 6
+ %39 = OpConstant %6 7
+ %40 = OpConstant %6 8
+ %100 = OpConstantComposite %7 %11 %12
+ %101 = OpConstantComposite %7 %14 %15
+ %102 = OpConstantComposite %7 %17 %18
+ %103 = OpConstantComposite %8 %100 %101 %102
+ %104 = OpConstantComposite %24 %29 %30 %31
+ %105 = OpConstantComposite %26 %104 %33
+ %106 = OpConstantComposite %35 %38 %39 %40
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/test/fuzz/transformation_add_dead_continue_test.cpp b/test/fuzz/transformation_add_dead_continue_test.cpp
index 8173e72..ff93da8 100644
--- a/test/fuzz/transformation_add_dead_continue_test.cpp
+++ b/test/fuzz/transformation_add_dead_continue_test.cpp
@@ -209,117 +209,6 @@
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
-TEST(TransformationAddDeadContinueTest,
- DoNotAllowContinueToMergeBlockOfAnotherLoop) {
- // A loop header must dominate its merge block if that merge block is
- // reachable. We are thus not allowed to add a dead continue that would result
- // in violation of this property. This test checks for such a scenario.
-
- std::string shader = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %4 "main" %16 %139
- OpExecutionMode %4 OriginUpperLeft
- OpSource ESSL 310
- %2 = OpTypeVoid
- %3 = OpTypeFunction %2
- %6 = OpTypeFloat 32
- %7 = OpTypePointer Function %6
- %8 = OpTypeBool
- %14 = OpTypeVector %6 4
- %15 = OpTypePointer Input %14
- %16 = OpVariable %15 Input
- %138 = OpTypePointer Output %14
- %139 = OpVariable %138 Output
- %400 = OpConstantTrue %8
- %4 = OpFunction %2 None %3
- %5 = OpLabel
- OpBranch %500
- %500 = OpLabel
- OpLoopMerge %501 %502 None
- OpBranch %503 ; We are not allowed to change this to OpBranchConditional %400 %503 %502
- %503 = OpLabel
- OpLoopMerge %502 %504 None
- OpBranchConditional %400 %505 %504
- %505 = OpLabel
- OpBranch %502
- %504 = OpLabel
- OpBranch %503
- %502 = OpLabel
- OpBranchConditional %400 %501 %500
- %501 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- const auto env = SPV_ENV_UNIVERSAL_1_3;
- const auto consumer = nullptr;
- const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
- ASSERT_TRUE(IsValid(env, context.get()));
- FactManager fact_manager;
-
- ASSERT_FALSE(TransformationAddDeadContinue(500, true, {})
- .IsApplicable(context.get(), fact_manager));
- ASSERT_FALSE(TransformationAddDeadContinue(500, false, {})
- .IsApplicable(context.get(), fact_manager));
-}
-
-TEST(TransformationAddDeadContinueTest, DoNotAllowContinueToSelectionMerge) {
- // A selection header must dominate its merge block if that merge block is
- // reachable. We are thus not allowed to add a dead continue that would result
- // in violation of this property. This test checks for such a scenario.
-
- std::string shader = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %4 "main" %16 %139
- OpExecutionMode %4 OriginUpperLeft
- OpSource ESSL 310
- %2 = OpTypeVoid
- %3 = OpTypeFunction %2
- %6 = OpTypeFloat 32
- %7 = OpTypePointer Function %6
- %8 = OpTypeBool
- %14 = OpTypeVector %6 4
- %15 = OpTypePointer Input %14
- %16 = OpVariable %15 Input
- %138 = OpTypePointer Output %14
- %139 = OpVariable %138 Output
- %400 = OpConstantTrue %8
- %4 = OpFunction %2 None %3
- %5 = OpLabel
- OpBranch %500
- %500 = OpLabel
- OpLoopMerge %501 %502 None
- OpBranch %503 ; We are not allowed to change this to OpBranchConditional %400 %503 %502
- %503 = OpLabel
- OpSelectionMerge %502 None
- OpBranchConditional %400 %505 %504
- %505 = OpLabel
- OpBranch %502
- %504 = OpLabel
- OpBranch %502
- %502 = OpLabel
- OpBranchConditional %400 %501 %500
- %501 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- const auto env = SPV_ENV_UNIVERSAL_1_3;
- const auto consumer = nullptr;
- const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
- ASSERT_TRUE(IsValid(env, context.get()));
- FactManager fact_manager;
-
- ASSERT_FALSE(TransformationAddDeadContinue(500, true, {})
- .IsApplicable(context.get(), fact_manager));
- ASSERT_FALSE(TransformationAddDeadContinue(500, false, {})
- .IsApplicable(context.get(), fact_manager));
-}
-
TEST(TransformationAddDeadContinueTest, LoopNest) {
// Checks some allowed and disallowed scenarios for a nest of loops, including
// continuing a loop from an if or switch.
@@ -1420,7 +1309,6 @@
OpLoopMerge %1557 %1570 None
OpBranchConditional %395 %1562 %1557
%1562 = OpLabel
- OpSelectionMerge %1570 None
OpBranchConditional %395 %1571 %1570
%1571 = OpLabel
OpBranch %1557
@@ -1626,11 +1514,8 @@
ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
}
-TEST(TransformationAddDeadContinueTest, DISABLED_Miscellaneous6) {
- // A miscellaneous test that exposing a known bug in spirv-fuzz.
-
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2919): re-enable
- // this test as an when the known issue is fixed.
+TEST(TransformationAddDeadContinueTest, Miscellaneous6) {
+ // A miscellaneous test that exposed a bug in spirv-fuzz.
std::string shader = R"(
OpCapability Shader
@@ -1648,7 +1533,7 @@
OpBranch %10
%10 = OpLabel
OpLoopMerge %13 %12 None
- OpBranchConditional %9 %13 %11
+ OpBranch %11
%11 = OpLabel
%20 = OpCopyObject %6 %9
OpBranch %12
diff --git a/test/fuzz/transformation_add_function_test.cpp b/test/fuzz/transformation_add_function_test.cpp
new file mode 100644
index 0000000..66130be
--- /dev/null
+++ b/test/fuzz/transformation_add_function_test.cpp
@@ -0,0 +1,447 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_function.h"
+#include "source/fuzz/instruction_message.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddFunctionTest, BasicTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %8 = OpTypeFloat 32
+ %9 = OpTypePointer Function %8
+ %10 = OpTypeFunction %8 %7 %9
+ %18 = OpConstant %8 0
+ %20 = OpConstant %6 0
+ %28 = OpTypeBool
+ %37 = OpConstant %6 1
+ %42 = OpTypePointer Private %8
+ %43 = OpVariable %42 Private
+ %47 = OpConstant %8 1
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationAddFunction transformation1(std::vector<protobufs::Instruction>(
+ {MakeInstructionMessage(
+ SpvOpFunction, 8, 13,
+ {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
+ {SPV_OPERAND_TYPE_ID, {10}}}),
+ MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {}),
+ MakeInstructionMessage(SpvOpFunctionParameter, 9, 12, {}),
+ MakeInstructionMessage(SpvOpLabel, 0, 14, {}),
+ MakeInstructionMessage(
+ SpvOpVariable, 9, 17,
+ {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
+ MakeInstructionMessage(
+ SpvOpVariable, 7, 19,
+ {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
+ MakeInstructionMessage(
+ SpvOpStore, 0, 0,
+ {{SPV_OPERAND_TYPE_ID, {17}}, {SPV_OPERAND_TYPE_ID, {18}}}),
+ MakeInstructionMessage(
+ SpvOpStore, 0, 0,
+ {{SPV_OPERAND_TYPE_ID, {19}}, {SPV_OPERAND_TYPE_ID, {20}}}),
+ MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {21}}}),
+ MakeInstructionMessage(SpvOpLabel, 0, 21, {}),
+ MakeInstructionMessage(
+ SpvOpLoopMerge, 0, 0,
+ {{SPV_OPERAND_TYPE_ID, {23}},
+ {SPV_OPERAND_TYPE_ID, {24}},
+ {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}),
+ MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {25}}}),
+ MakeInstructionMessage(SpvOpLabel, 0, 25, {}),
+ MakeInstructionMessage(SpvOpLoad, 6, 26, {{SPV_OPERAND_TYPE_ID, {19}}}),
+ MakeInstructionMessage(SpvOpLoad, 6, 27, {{SPV_OPERAND_TYPE_ID, {11}}}),
+ MakeInstructionMessage(
+ SpvOpSLessThan, 28, 29,
+ {{SPV_OPERAND_TYPE_ID, {26}}, {SPV_OPERAND_TYPE_ID, {27}}}),
+ MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
+ {{SPV_OPERAND_TYPE_ID, {29}},
+ {SPV_OPERAND_TYPE_ID, {22}},
+ {SPV_OPERAND_TYPE_ID, {23}}}),
+ MakeInstructionMessage(SpvOpLabel, 0, 22, {}),
+ MakeInstructionMessage(SpvOpLoad, 8, 30, {{SPV_OPERAND_TYPE_ID, {12}}}),
+ MakeInstructionMessage(SpvOpLoad, 6, 31, {{SPV_OPERAND_TYPE_ID, {19}}}),
+ MakeInstructionMessage(SpvOpConvertSToF, 8, 32,
+ {{SPV_OPERAND_TYPE_ID, {31}}}),
+ MakeInstructionMessage(
+ SpvOpFMul, 8, 33,
+ {{SPV_OPERAND_TYPE_ID, {30}}, {SPV_OPERAND_TYPE_ID, {32}}}),
+ MakeInstructionMessage(SpvOpLoad, 8, 34, {{SPV_OPERAND_TYPE_ID, {17}}}),
+ MakeInstructionMessage(
+ SpvOpFAdd, 8, 35,
+ {{SPV_OPERAND_TYPE_ID, {34}}, {SPV_OPERAND_TYPE_ID, {33}}}),
+ MakeInstructionMessage(
+ SpvOpStore, 0, 0,
+ {{SPV_OPERAND_TYPE_ID, {17}}, {SPV_OPERAND_TYPE_ID, {35}}}),
+ MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {24}}}),
+ MakeInstructionMessage(SpvOpLabel, 0, 24, {}),
+ MakeInstructionMessage(SpvOpLoad, 6, 36, {{SPV_OPERAND_TYPE_ID, {19}}}),
+ MakeInstructionMessage(
+ SpvOpIAdd, 6, 38,
+ {{SPV_OPERAND_TYPE_ID, {36}}, {SPV_OPERAND_TYPE_ID, {37}}}),
+ MakeInstructionMessage(
+ SpvOpStore, 0, 0,
+ {{SPV_OPERAND_TYPE_ID, {19}}, {SPV_OPERAND_TYPE_ID, {38}}}),
+ MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {21}}}),
+ MakeInstructionMessage(SpvOpLabel, 0, 23, {}),
+ MakeInstructionMessage(SpvOpLoad, 8, 39, {{SPV_OPERAND_TYPE_ID, {17}}}),
+ MakeInstructionMessage(SpvOpReturnValue, 0, 0,
+ {{SPV_OPERAND_TYPE_ID, {39}}}),
+ MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})}));
+
+ ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
+ transformation1.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation1 = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %8 = OpTypeFloat 32
+ %9 = OpTypePointer Function %8
+ %10 = OpTypeFunction %8 %7 %9
+ %18 = OpConstant %8 0
+ %20 = OpConstant %6 0
+ %28 = OpTypeBool
+ %37 = OpConstant %6 1
+ %42 = OpTypePointer Private %8
+ %43 = OpVariable %42 Private
+ %47 = OpConstant %8 1
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %13 = OpFunction %8 None %10
+ %11 = OpFunctionParameter %7
+ %12 = OpFunctionParameter %9
+ %14 = OpLabel
+ %17 = OpVariable %9 Function
+ %19 = OpVariable %7 Function
+ OpStore %17 %18
+ OpStore %19 %20
+ OpBranch %21
+ %21 = OpLabel
+ OpLoopMerge %23 %24 None
+ OpBranch %25
+ %25 = OpLabel
+ %26 = OpLoad %6 %19
+ %27 = OpLoad %6 %11
+ %29 = OpSLessThan %28 %26 %27
+ OpBranchConditional %29 %22 %23
+ %22 = OpLabel
+ %30 = OpLoad %8 %12
+ %31 = OpLoad %6 %19
+ %32 = OpConvertSToF %8 %31
+ %33 = OpFMul %8 %30 %32
+ %34 = OpLoad %8 %17
+ %35 = OpFAdd %8 %34 %33
+ OpStore %17 %35
+ OpBranch %24
+ %24 = OpLabel
+ %36 = OpLoad %6 %19
+ %38 = OpIAdd %6 %36 %37
+ OpStore %19 %38
+ OpBranch %21
+ %23 = OpLabel
+ %39 = OpLoad %8 %17
+ OpReturnValue %39
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation1, context.get()));
+
+ TransformationAddFunction transformation2(std::vector<protobufs::Instruction>(
+ {MakeInstructionMessage(
+ SpvOpFunction, 2, 15,
+ {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
+ {SPV_OPERAND_TYPE_ID, {3}}}),
+ MakeInstructionMessage(SpvOpLabel, 0, 16, {}),
+ MakeInstructionMessage(
+ SpvOpVariable, 7, 44,
+ {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
+ MakeInstructionMessage(
+ SpvOpVariable, 9, 45,
+ {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
+ MakeInstructionMessage(
+ SpvOpVariable, 7, 48,
+ {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
+ MakeInstructionMessage(
+ SpvOpVariable, 9, 49,
+ {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
+ MakeInstructionMessage(
+ SpvOpStore, 0, 0,
+ {{SPV_OPERAND_TYPE_ID, {44}}, {SPV_OPERAND_TYPE_ID, {20}}}),
+ MakeInstructionMessage(
+ SpvOpStore, 0, 0,
+ {{SPV_OPERAND_TYPE_ID, {45}}, {SPV_OPERAND_TYPE_ID, {18}}}),
+ MakeInstructionMessage(SpvOpFunctionCall, 8, 46,
+ {{SPV_OPERAND_TYPE_ID, {13}},
+ {SPV_OPERAND_TYPE_ID, {44}},
+ {SPV_OPERAND_TYPE_ID, {45}}}),
+ MakeInstructionMessage(
+ SpvOpStore, 0, 0,
+ {{SPV_OPERAND_TYPE_ID, {48}}, {SPV_OPERAND_TYPE_ID, {37}}}),
+ MakeInstructionMessage(
+ SpvOpStore, 0, 0,
+ {{SPV_OPERAND_TYPE_ID, {49}}, {SPV_OPERAND_TYPE_ID, {47}}}),
+ MakeInstructionMessage(SpvOpFunctionCall, 8, 50,
+ {{SPV_OPERAND_TYPE_ID, {13}},
+ {SPV_OPERAND_TYPE_ID, {48}},
+ {SPV_OPERAND_TYPE_ID, {49}}}),
+ MakeInstructionMessage(
+ SpvOpFAdd, 8, 51,
+ {{SPV_OPERAND_TYPE_ID, {46}}, {SPV_OPERAND_TYPE_ID, {50}}}),
+ MakeInstructionMessage(
+ SpvOpStore, 0, 0,
+ {{SPV_OPERAND_TYPE_ID, {43}}, {SPV_OPERAND_TYPE_ID, {51}}}),
+ MakeInstructionMessage(SpvOpReturn, 0, 0, {}),
+ MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})}));
+
+ ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
+ transformation2.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation2 = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %8 = OpTypeFloat 32
+ %9 = OpTypePointer Function %8
+ %10 = OpTypeFunction %8 %7 %9
+ %18 = OpConstant %8 0
+ %20 = OpConstant %6 0
+ %28 = OpTypeBool
+ %37 = OpConstant %6 1
+ %42 = OpTypePointer Private %8
+ %43 = OpVariable %42 Private
+ %47 = OpConstant %8 1
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %13 = OpFunction %8 None %10
+ %11 = OpFunctionParameter %7
+ %12 = OpFunctionParameter %9
+ %14 = OpLabel
+ %17 = OpVariable %9 Function
+ %19 = OpVariable %7 Function
+ OpStore %17 %18
+ OpStore %19 %20
+ OpBranch %21
+ %21 = OpLabel
+ OpLoopMerge %23 %24 None
+ OpBranch %25
+ %25 = OpLabel
+ %26 = OpLoad %6 %19
+ %27 = OpLoad %6 %11
+ %29 = OpSLessThan %28 %26 %27
+ OpBranchConditional %29 %22 %23
+ %22 = OpLabel
+ %30 = OpLoad %8 %12
+ %31 = OpLoad %6 %19
+ %32 = OpConvertSToF %8 %31
+ %33 = OpFMul %8 %30 %32
+ %34 = OpLoad %8 %17
+ %35 = OpFAdd %8 %34 %33
+ OpStore %17 %35
+ OpBranch %24
+ %24 = OpLabel
+ %36 = OpLoad %6 %19
+ %38 = OpIAdd %6 %36 %37
+ OpStore %19 %38
+ OpBranch %21
+ %23 = OpLabel
+ %39 = OpLoad %8 %17
+ OpReturnValue %39
+ OpFunctionEnd
+ %15 = OpFunction %2 None %3
+ %16 = OpLabel
+ %44 = OpVariable %7 Function
+ %45 = OpVariable %9 Function
+ %48 = OpVariable %7 Function
+ %49 = OpVariable %9 Function
+ OpStore %44 %20
+ OpStore %45 %18
+ %46 = OpFunctionCall %8 %13 %44 %45
+ OpStore %48 %37
+ OpStore %49 %47
+ %50 = OpFunctionCall %8 %13 %48 %49
+ %51 = OpFAdd %8 %46 %50
+ OpStore %43 %51
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation2, context.get()));
+}
+
+TEST(TransformationAddFunctionTest, InapplicableTransformations) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %8 = OpTypeFloat 32
+ %9 = OpTypePointer Function %8
+ %10 = OpTypeFunction %8 %7 %9
+ %18 = OpConstant %8 0
+ %20 = OpConstant %6 0
+ %28 = OpTypeBool
+ %37 = OpConstant %6 1
+ %42 = OpTypePointer Private %8
+ %43 = OpVariable %42 Private
+ %47 = OpConstant %8 1
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %13 = OpFunction %8 None %10
+ %11 = OpFunctionParameter %7
+ %12 = OpFunctionParameter %9
+ %14 = OpLabel
+ %17 = OpVariable %9 Function
+ %19 = OpVariable %7 Function
+ OpStore %17 %18
+ OpStore %19 %20
+ OpBranch %21
+ %21 = OpLabel
+ OpLoopMerge %23 %24 None
+ OpBranch %25
+ %25 = OpLabel
+ %26 = OpLoad %6 %19
+ %27 = OpLoad %6 %11
+ %29 = OpSLessThan %28 %26 %27
+ OpBranchConditional %29 %22 %23
+ %22 = OpLabel
+ %30 = OpLoad %8 %12
+ %31 = OpLoad %6 %19
+ %32 = OpConvertSToF %8 %31
+ %33 = OpFMul %8 %30 %32
+ %34 = OpLoad %8 %17
+ %35 = OpFAdd %8 %34 %33
+ OpStore %17 %35
+ OpBranch %24
+ %24 = OpLabel
+ %36 = OpLoad %6 %19
+ %38 = OpIAdd %6 %36 %37
+ OpStore %19 %38
+ OpBranch %21
+ %23 = OpLabel
+ %39 = OpLoad %8 %17
+ OpReturnValue %39
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ // No instructions
+ ASSERT_FALSE(
+ TransformationAddFunction(std::vector<protobufs::Instruction>({}))
+ .IsApplicable(context.get(), fact_manager));
+
+ // No function begin
+ ASSERT_FALSE(
+ TransformationAddFunction(
+ std::vector<protobufs::Instruction>(
+ {MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {}),
+ MakeInstructionMessage(SpvOpFunctionParameter, 9, 12, {}),
+ MakeInstructionMessage(SpvOpLabel, 0, 14, {})}))
+ .IsApplicable(context.get(), fact_manager));
+
+ // No OpLabel
+ ASSERT_FALSE(
+ TransformationAddFunction(
+ std::vector<protobufs::Instruction>(
+ {MakeInstructionMessage(SpvOpFunction, 8, 13,
+ {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
+ {SpvFunctionControlMaskNone}},
+ {SPV_OPERAND_TYPE_ID, {10}}}),
+ MakeInstructionMessage(SpvOpReturnValue, 0, 0,
+ {{SPV_OPERAND_TYPE_ID, {39}}}),
+ MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})}))
+ .IsApplicable(context.get(), fact_manager));
+
+ // Abrupt end of instructions
+ ASSERT_FALSE(TransformationAddFunction(
+ std::vector<protobufs::Instruction>({MakeInstructionMessage(
+ SpvOpFunction, 8, 13,
+ {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
+ {SpvFunctionControlMaskNone}},
+ {SPV_OPERAND_TYPE_ID, {10}}})}))
+ .IsApplicable(context.get(), fact_manager));
+
+ // No function end
+ ASSERT_FALSE(
+ TransformationAddFunction(
+ std::vector<protobufs::Instruction>(
+ {MakeInstructionMessage(SpvOpFunction, 8, 13,
+ {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
+ {SpvFunctionControlMaskNone}},
+ {SPV_OPERAND_TYPE_ID, {10}}}),
+ MakeInstructionMessage(SpvOpLabel, 0, 14, {}),
+ MakeInstructionMessage(SpvOpReturnValue, 0, 0,
+ {{SPV_OPERAND_TYPE_ID, {39}}})}))
+ .IsApplicable(context.get(), fact_manager));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/test/fuzz/transformation_add_global_undef_test.cpp b/test/fuzz/transformation_add_global_undef_test.cpp
new file mode 100644
index 0000000..c14f7e9
--- /dev/null
+++ b/test/fuzz/transformation_add_global_undef_test.cpp
@@ -0,0 +1,118 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_global_undef.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddGlobalUndefTest, BasicTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeInt 32 1
+ %8 = OpTypeVector %6 2
+ %9 = OpTypeVector %6 3
+ %10 = OpTypeVector %6 4
+ %11 = OpTypeVector %7 2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ // Id already in use
+ ASSERT_FALSE(TransformationAddGlobalUndef(4, 11).IsApplicable(context.get(),
+ fact_manager));
+ // %1 is not a type
+ ASSERT_FALSE(TransformationAddGlobalUndef(100, 1).IsApplicable(context.get(),
+ fact_manager));
+
+ // %3 is a function type
+ ASSERT_FALSE(TransformationAddGlobalUndef(100, 3).IsApplicable(context.get(),
+ fact_manager));
+
+ TransformationAddGlobalUndef transformations[] = {
+ // %100 = OpUndef %6
+ TransformationAddGlobalUndef(100, 6),
+
+ // %101 = OpUndef %7
+ TransformationAddGlobalUndef(101, 7),
+
+ // %102 = OpUndef %8
+ TransformationAddGlobalUndef(102, 8),
+
+ // %103 = OpUndef %9
+ TransformationAddGlobalUndef(103, 9),
+
+ // %104 = OpUndef %10
+ TransformationAddGlobalUndef(104, 10),
+
+ // %105 = OpUndef %11
+ TransformationAddGlobalUndef(105, 11)};
+
+ for (auto& transformation : transformations) {
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ }
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeInt 32 1
+ %8 = OpTypeVector %6 2
+ %9 = OpTypeVector %6 3
+ %10 = OpTypeVector %6 4
+ %11 = OpTypeVector %7 2
+ %100 = OpUndef %6
+ %101 = OpUndef %7
+ %102 = OpUndef %8
+ %103 = OpUndef %9
+ %104 = OpUndef %10
+ %105 = OpUndef %11
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/test/fuzz/transformation_add_global_variable_test.cpp b/test/fuzz/transformation_add_global_variable_test.cpp
new file mode 100644
index 0000000..eda6828
--- /dev/null
+++ b/test/fuzz/transformation_add_global_variable_test.cpp
@@ -0,0 +1,275 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_global_variable.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddGlobalVariableTest, BasicTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeInt 32 1
+ %8 = OpTypeVector %6 2
+ %9 = OpTypePointer Function %6
+ %10 = OpTypePointer Private %6
+ %20 = OpTypePointer Uniform %6
+ %11 = OpTypePointer Function %7
+ %12 = OpTypePointer Private %7
+ %13 = OpTypePointer Private %8
+ %14 = OpVariable %10 Private
+ %15 = OpVariable %20 Uniform
+ %16 = OpConstant %7 1
+ %17 = OpTypePointer Private %10
+ %18 = OpTypeBool
+ %19 = OpTypePointer Private %18
+ %21 = OpConstantTrue %18
+ %22 = OpConstantFalse %18
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ // Id already in use
+ ASSERT_FALSE(TransformationAddGlobalVariable(4, 10, 0).IsApplicable(
+ context.get(), fact_manager));
+ // %1 is not a type
+ ASSERT_FALSE(TransformationAddGlobalVariable(100, 1, 0).IsApplicable(
+ context.get(), fact_manager));
+
+ // %7 is not a pointer type
+ ASSERT_FALSE(TransformationAddGlobalVariable(100, 7, 0).IsApplicable(
+ context.get(), fact_manager));
+
+ // %9 does not have Private storage class
+ ASSERT_FALSE(TransformationAddGlobalVariable(100, 9, 0).IsApplicable(
+ context.get(), fact_manager));
+
+ // %15 does not have Private storage class
+ ASSERT_FALSE(TransformationAddGlobalVariable(100, 15, 0)
+ .IsApplicable(context.get(), fact_manager));
+
+ // %10 is a pointer to float, while %16 is an int constant
+ ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, 16)
+ .IsApplicable(context.get(), fact_manager));
+
+ // %10 is a Private pointer to float, while %15 is a variable with type
+ // Uniform float pointer
+ ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, 15)
+ .IsApplicable(context.get(), fact_manager));
+
+ // %12 is a Private pointer to int, while %10 is a variable with type
+ // Private float pointer
+ ASSERT_FALSE(TransformationAddGlobalVariable(100, 12, 10)
+ .IsApplicable(context.get(), fact_manager));
+
+ // %10 is pointer-to-float, and %14 has type pointer-to-float; that's not OK
+ // since the initializer's type should be the *pointee* type.
+ ASSERT_FALSE(TransformationAddGlobalVariable(104, 10, 14)
+ .IsApplicable(context.get(), fact_manager));
+
+ // This would work in principle, but logical addressing does not allow
+ // a pointer to a pointer.
+ ASSERT_FALSE(TransformationAddGlobalVariable(104, 17, 14)
+ .IsApplicable(context.get(), fact_manager));
+
+ TransformationAddGlobalVariable transformations[] = {
+ // %100 = OpVariable %12 Private
+ TransformationAddGlobalVariable(100, 12, 0),
+
+ // %101 = OpVariable %10 Private
+ TransformationAddGlobalVariable(101, 10, 0),
+
+ // %102 = OpVariable %13 Private
+ TransformationAddGlobalVariable(102, 13, 0),
+
+ // %103 = OpVariable %12 Private %16
+ TransformationAddGlobalVariable(103, 12, 16),
+
+ // %104 = OpVariable %19 Private %21
+ TransformationAddGlobalVariable(104, 19, 21),
+
+ // %105 = OpVariable %19 Private %22
+ TransformationAddGlobalVariable(105, 19, 22)};
+
+ for (auto& transformation : transformations) {
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ }
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeInt 32 1
+ %8 = OpTypeVector %6 2
+ %9 = OpTypePointer Function %6
+ %10 = OpTypePointer Private %6
+ %20 = OpTypePointer Uniform %6
+ %11 = OpTypePointer Function %7
+ %12 = OpTypePointer Private %7
+ %13 = OpTypePointer Private %8
+ %14 = OpVariable %10 Private
+ %15 = OpVariable %20 Uniform
+ %16 = OpConstant %7 1
+ %17 = OpTypePointer Private %10
+ %18 = OpTypeBool
+ %19 = OpTypePointer Private %18
+ %21 = OpConstantTrue %18
+ %22 = OpConstantFalse %18
+ %100 = OpVariable %12 Private
+ %101 = OpVariable %10 Private
+ %102 = OpVariable %13 Private
+ %103 = OpVariable %12 Private %16
+ %104 = OpVariable %19 Private %21
+ %105 = OpVariable %19 Private %22
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationAddGlobalVariableTest, TestEntryPointInterfaceEnlargement) {
+ // This checks that when global variables are added to a SPIR-V 1.4+ module,
+ // they are also added to entry points of that module.
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "m1"
+ OpEntryPoint Vertex %5 "m2"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeInt 32 1
+ %8 = OpTypeVector %6 2
+ %9 = OpTypePointer Function %6
+ %10 = OpTypePointer Private %6
+ %20 = OpTypePointer Uniform %6
+ %11 = OpTypePointer Function %7
+ %12 = OpTypePointer Private %7
+ %13 = OpTypePointer Private %8
+ %14 = OpVariable %10 Private
+ %15 = OpVariable %20 Uniform
+ %16 = OpConstant %7 1
+ %17 = OpTypePointer Private %10
+ %18 = OpTypeBool
+ %19 = OpTypePointer Private %18
+ %21 = OpConstantTrue %18
+ %4 = OpFunction %2 None %3
+ %30 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %5 = OpFunction %2 None %3
+ %31 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationAddGlobalVariable transformations[] = {
+ // %100 = OpVariable %12 Private
+ TransformationAddGlobalVariable(100, 12, 0),
+
+ // %101 = OpVariable %12 Private %16
+ TransformationAddGlobalVariable(101, 12, 16),
+
+ // %102 = OpVariable %19 Private %21
+ TransformationAddGlobalVariable(102, 19, 21)};
+
+ for (auto& transformation : transformations) {
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ }
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "m1" %100 %101 %102
+ OpEntryPoint Vertex %5 "m2" %100 %101 %102
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeInt 32 1
+ %8 = OpTypeVector %6 2
+ %9 = OpTypePointer Function %6
+ %10 = OpTypePointer Private %6
+ %20 = OpTypePointer Uniform %6
+ %11 = OpTypePointer Function %7
+ %12 = OpTypePointer Private %7
+ %13 = OpTypePointer Private %8
+ %14 = OpVariable %10 Private
+ %15 = OpVariable %20 Uniform
+ %16 = OpConstant %7 1
+ %17 = OpTypePointer Private %10
+ %18 = OpTypeBool
+ %19 = OpTypePointer Private %18
+ %21 = OpConstantTrue %18
+ %100 = OpVariable %12 Private
+ %101 = OpVariable %12 Private %16
+ %102 = OpVariable %19 Private %21
+ %4 = OpFunction %2 None %3
+ %30 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %5 = OpFunction %2 None %3
+ %31 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/test/fuzz/transformation_add_type_array_test.cpp b/test/fuzz/transformation_add_type_array_test.cpp
new file mode 100644
index 0000000..2bcbe73
--- /dev/null
+++ b/test/fuzz/transformation_add_type_array_test.cpp
@@ -0,0 +1,136 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_type_array.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddTypeArrayTest, BasicTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeInt 32 1
+ %8 = OpTypeVector %6 2
+ %9 = OpTypeVector %6 3
+ %10 = OpTypeVector %6 4
+ %11 = OpTypeVector %7 2
+ %12 = OpConstant %7 3
+ %13 = OpConstant %7 0
+ %14 = OpConstant %7 -1
+ %15 = OpTypeInt 32 0
+ %16 = OpConstant %15 5
+ %17 = OpConstant %15 0
+ %18 = OpConstant %6 1
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ // Id already in use
+ ASSERT_FALSE(TransformationAddTypeArray(4, 10, 16).IsApplicable(
+ context.get(), fact_manager));
+ // %1 is not a type
+ ASSERT_FALSE(TransformationAddTypeArray(100, 1, 16)
+ .IsApplicable(context.get(), fact_manager));
+
+ // %3 is a function type
+ ASSERT_FALSE(TransformationAddTypeArray(100, 3, 16)
+ .IsApplicable(context.get(), fact_manager));
+
+ // %2 is not a constant
+ ASSERT_FALSE(TransformationAddTypeArray(100, 11, 2)
+ .IsApplicable(context.get(), fact_manager));
+
+ // %18 is not an integer
+ ASSERT_FALSE(TransformationAddTypeArray(100, 11, 18)
+ .IsApplicable(context.get(), fact_manager));
+
+ // %13 is signed 0
+ ASSERT_FALSE(TransformationAddTypeArray(100, 11, 13)
+ .IsApplicable(context.get(), fact_manager));
+
+ // %14 is negative
+ ASSERT_FALSE(TransformationAddTypeArray(100, 11, 14)
+ .IsApplicable(context.get(), fact_manager));
+
+ // %17 is unsigned 0
+ ASSERT_FALSE(TransformationAddTypeArray(100, 11, 17)
+ .IsApplicable(context.get(), fact_manager));
+
+ TransformationAddTypeArray transformations[] = {
+ // %100 = OpTypeArray %10 %16
+ TransformationAddTypeArray(100, 10, 16),
+
+ // %101 = OpTypeArray %7 %12
+ TransformationAddTypeArray(101, 7, 12)};
+
+ for (auto& transformation : transformations) {
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ }
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeInt 32 1
+ %8 = OpTypeVector %6 2
+ %9 = OpTypeVector %6 3
+ %10 = OpTypeVector %6 4
+ %11 = OpTypeVector %7 2
+ %12 = OpConstant %7 3
+ %13 = OpConstant %7 0
+ %14 = OpConstant %7 -1
+ %15 = OpTypeInt 32 0
+ %16 = OpConstant %15 5
+ %17 = OpConstant %15 0
+ %18 = OpConstant %6 1
+ %100 = OpTypeArray %10 %16
+ %101 = OpTypeArray %7 %12
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/test/fuzz/transformation_add_type_function_test.cpp b/test/fuzz/transformation_add_type_function_test.cpp
new file mode 100644
index 0000000..46bd436
--- /dev/null
+++ b/test/fuzz/transformation_add_type_function_test.cpp
@@ -0,0 +1,134 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_type_function.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddTypeFunctionTest, BasicTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %8 = OpTypeFunction %2 %7
+ %12 = OpTypeFloat 32
+ %13 = OpTypeStruct %6 %12
+ %14 = OpTypePointer Function %13
+ %200 = OpTypePointer Function %13
+ %15 = OpTypeVector %12 3
+ %16 = OpTypePointer Function %15
+ %17 = OpTypeVector %12 2
+ %18 = OpTypeFunction %17 %14 %16
+ %23 = OpConstant %12 1
+ %24 = OpConstantComposite %17 %23 %23
+ %27 = OpConstant %6 3
+ %30 = OpConstant %6 1
+ %31 = OpConstant %12 2
+ %32 = OpConstantComposite %13 %30 %31
+ %33 = OpConstantComposite %15 %23 %23 %23
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ // Id already in use
+ ASSERT_FALSE(TransformationAddTypeFunction(4, 12, {12, 16, 14})
+ .IsApplicable(context.get(), fact_manager));
+ // %1 is not a type
+ ASSERT_FALSE(TransformationAddTypeFunction(100, 1, {12, 16, 14})
+ .IsApplicable(context.get(), fact_manager));
+
+ // %18 is a function type
+ ASSERT_FALSE(TransformationAddTypeFunction(100, 12, {18})
+ .IsApplicable(context.get(), fact_manager));
+
+ // A function of this signature already exists
+ ASSERT_FALSE(TransformationAddTypeFunction(100, 17, {14, 16})
+ .IsApplicable(context.get(), fact_manager));
+
+ TransformationAddTypeFunction transformations[] = {
+ // %100 = OpTypeFunction %12 %12 %16 %14
+ TransformationAddTypeFunction(100, 12, {12, 16, 14}),
+
+ // %101 = OpTypeFunction %12
+ TransformationAddTypeFunction(101, 12, {}),
+
+ // %102 = OpTypeFunction %17 %200 %16
+ TransformationAddTypeFunction(102, 17, {200, 16})};
+
+ for (auto& transformation : transformations) {
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ }
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %8 = OpTypeFunction %2 %7
+ %12 = OpTypeFloat 32
+ %13 = OpTypeStruct %6 %12
+ %14 = OpTypePointer Function %13
+ %200 = OpTypePointer Function %13
+ %15 = OpTypeVector %12 3
+ %16 = OpTypePointer Function %15
+ %17 = OpTypeVector %12 2
+ %18 = OpTypeFunction %17 %14 %16
+ %23 = OpConstant %12 1
+ %24 = OpConstantComposite %17 %23 %23
+ %27 = OpConstant %6 3
+ %30 = OpConstant %6 1
+ %31 = OpConstant %12 2
+ %32 = OpConstantComposite %13 %30 %31
+ %33 = OpConstantComposite %15 %23 %23 %23
+ %100 = OpTypeFunction %12 %12 %16 %14
+ %101 = OpTypeFunction %12
+ %102 = OpTypeFunction %17 %200 %16
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/test/fuzz/transformation_add_type_matrix_test.cpp b/test/fuzz/transformation_add_type_matrix_test.cpp
new file mode 100644
index 0000000..84f27e9
--- /dev/null
+++ b/test/fuzz/transformation_add_type_matrix_test.cpp
@@ -0,0 +1,130 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_type_matrix.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddTypeMatrixTest, BasicTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeInt 32 1
+ %8 = OpTypeVector %6 2
+ %9 = OpTypeVector %6 3
+ %10 = OpTypeVector %6 4
+ %11 = OpTypeVector %7 2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ // Id already in use
+ ASSERT_FALSE(TransformationAddTypeMatrix(4, 9, 2).IsApplicable(context.get(),
+ fact_manager));
+ // %1 is not a type
+ ASSERT_FALSE(TransformationAddTypeMatrix(100, 1, 2).IsApplicable(
+ context.get(), fact_manager));
+
+ // %11 is not a floating-point vector
+ ASSERT_FALSE(TransformationAddTypeMatrix(100, 11, 2)
+ .IsApplicable(context.get(), fact_manager));
+
+ TransformationAddTypeMatrix transformations[] = {
+ // %100 = OpTypeMatrix %8 2
+ TransformationAddTypeMatrix(100, 8, 2),
+
+ // %101 = OpTypeMatrix %8 3
+ TransformationAddTypeMatrix(101, 8, 3),
+
+ // %102 = OpTypeMatrix %8 4
+ TransformationAddTypeMatrix(102, 8, 4),
+
+ // %103 = OpTypeMatrix %9 2
+ TransformationAddTypeMatrix(103, 9, 2),
+
+ // %104 = OpTypeMatrix %9 3
+ TransformationAddTypeMatrix(104, 9, 3),
+
+ // %105 = OpTypeMatrix %9 4
+ TransformationAddTypeMatrix(105, 9, 4),
+
+ // %106 = OpTypeMatrix %10 2
+ TransformationAddTypeMatrix(106, 10, 2),
+
+ // %107 = OpTypeMatrix %10 3
+ TransformationAddTypeMatrix(107, 10, 3),
+
+ // %108 = OpTypeMatrix %10 4
+ TransformationAddTypeMatrix(108, 10, 4)};
+
+ for (auto& transformation : transformations) {
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ }
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeInt 32 1
+ %8 = OpTypeVector %6 2
+ %9 = OpTypeVector %6 3
+ %10 = OpTypeVector %6 4
+ %11 = OpTypeVector %7 2
+ %100 = OpTypeMatrix %8 2
+ %101 = OpTypeMatrix %8 3
+ %102 = OpTypeMatrix %8 4
+ %103 = OpTypeMatrix %9 2
+ %104 = OpTypeMatrix %9 3
+ %105 = OpTypeMatrix %9 4
+ %106 = OpTypeMatrix %10 2
+ %107 = OpTypeMatrix %10 3
+ %108 = OpTypeMatrix %10 4
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/test/fuzz/transformation_add_type_struct_test.cpp b/test/fuzz/transformation_add_type_struct_test.cpp
new file mode 100644
index 0000000..ae68c9a
--- /dev/null
+++ b/test/fuzz/transformation_add_type_struct_test.cpp
@@ -0,0 +1,110 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_type_struct.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddTypeStructTest, BasicTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeInt 32 1
+ %8 = OpTypeVector %6 2
+ %9 = OpTypeVector %6 3
+ %10 = OpTypeVector %6 4
+ %11 = OpTypeVector %7 2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ // Id already in use
+ ASSERT_FALSE(TransformationAddTypeStruct(4, {}).IsApplicable(context.get(),
+ fact_manager));
+ // %1 is not a type
+ ASSERT_FALSE(TransformationAddTypeStruct(100, {1}).IsApplicable(
+ context.get(), fact_manager));
+
+ // %3 is a function type
+ ASSERT_FALSE(TransformationAddTypeStruct(100, {3}).IsApplicable(
+ context.get(), fact_manager));
+
+ TransformationAddTypeStruct transformations[] = {
+ // %100 = OpTypeStruct %6 %7 %8 %9 %10 %11
+ TransformationAddTypeStruct(100, {6, 7, 8, 9, 10, 11}),
+
+ // %101 = OpTypeStruct
+ TransformationAddTypeStruct(101, {}),
+
+ // %102 = OpTypeStruct %6
+ TransformationAddTypeStruct(102, {6}),
+
+ // %103 = OpTypeStruct %6 %6
+ TransformationAddTypeStruct(103, {6, 6})};
+
+ for (auto& transformation : transformations) {
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ }
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeInt 32 1
+ %8 = OpTypeVector %6 2
+ %9 = OpTypeVector %6 3
+ %10 = OpTypeVector %6 4
+ %11 = OpTypeVector %7 2
+ %100 = OpTypeStruct %6 %7 %8 %9 %10 %11
+ %101 = OpTypeStruct
+ %102 = OpTypeStruct %6
+ %103 = OpTypeStruct %6 %6
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/test/fuzz/transformation_add_type_vector_test.cpp b/test/fuzz/transformation_add_type_vector_test.cpp
new file mode 100644
index 0000000..6ac4498
--- /dev/null
+++ b/test/fuzz/transformation_add_type_vector_test.cpp
@@ -0,0 +1,102 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_type_vector.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddTypeVectorTest, BasicTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeInt 32 1
+ %8 = OpTypeInt 32 0
+ %9 = OpTypeBool
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ // Id already in use
+ ASSERT_FALSE(TransformationAddTypeVector(4, 6, 2).IsApplicable(context.get(),
+ fact_manager));
+ // %1 is not a type
+ ASSERT_FALSE(TransformationAddTypeVector(100, 1, 2).IsApplicable(
+ context.get(), fact_manager));
+
+ TransformationAddTypeVector transformations[] = {
+ // %100 = OpTypeVector %6 2
+ TransformationAddTypeVector(100, 6, 2),
+
+ // %101 = OpTypeVector %7 3
+ TransformationAddTypeVector(101, 7, 3),
+
+ // %102 = OpTypeVector %8 4
+ TransformationAddTypeVector(102, 8, 4),
+
+ // %103 = OpTypeVector %9 2
+ TransformationAddTypeVector(103, 9, 2)};
+
+ for (auto& transformation : transformations) {
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ }
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeInt 32 1
+ %8 = OpTypeInt 32 0
+ %9 = OpTypeBool
+ %100 = OpTypeVector %6 2
+ %101 = OpTypeVector %7 3
+ %102 = OpTypeVector %8 4
+ %103 = OpTypeVector %9 2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/test/fuzz/transformation_copy_object_test.cpp b/test/fuzz/transformation_copy_object_test.cpp
index 9d3fde0..b489f71 100644
--- a/test/fuzz/transformation_copy_object_test.cpp
+++ b/test/fuzz/transformation_copy_object_test.cpp
@@ -291,7 +291,7 @@
%31 = OpLabel
%42 = OpAccessChain %36 %18 %41
%43 = OpLoad %11 %42
- OpSelectionMerge %47 None
+ OpSelectionMerge %45 None
OpSwitch %43 %46 0 %44 1 %45
%46 = OpLabel
%69 = OpIAdd %11 %96 %27
diff --git a/test/fuzz/transformation_merge_blocks_test.cpp b/test/fuzz/transformation_merge_blocks_test.cpp
new file mode 100644
index 0000000..e2b4aa6
--- /dev/null
+++ b/test/fuzz/transformation_merge_blocks_test.cpp
@@ -0,0 +1,675 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_merge_blocks.h"
+
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationMergeBlocksTest, BlockDoesNotExist) {
+ 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
+ %5 = OpLabel
+ OpBranch %6
+ %6 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ ASSERT_FALSE(
+ TransformationMergeBlocks(3).IsApplicable(context.get(), fact_manager));
+ ASSERT_FALSE(
+ TransformationMergeBlocks(7).IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationMergeBlocksTest, DoNotMergeFirstBlockHasMultipleSuccessors) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %7 = OpTypeBool
+ %8 = OpConstantTrue %7
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpSelectionMerge %10 None
+ OpBranchConditional %8 %6 %9
+ %6 = OpLabel
+ OpBranch %10
+ %9 = OpLabel
+ OpBranch %10
+ %10 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ ASSERT_FALSE(
+ TransformationMergeBlocks(6).IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationMergeBlocksTest,
+ DoNotMergeSecondBlockHasMultiplePredecessors) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %7 = OpTypeBool
+ %8 = OpConstantTrue %7
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpSelectionMerge %10 None
+ OpBranchConditional %8 %6 %9
+ %6 = OpLabel
+ OpBranch %10
+ %9 = OpLabel
+ OpBranch %10
+ %10 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ ASSERT_FALSE(
+ TransformationMergeBlocks(10).IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockIsSelectionMerge) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %7 = OpTypeBool
+ %8 = OpConstantTrue %7
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpSelectionMerge %10 None
+ OpBranchConditional %8 %6 %9
+ %6 = OpLabel
+ OpBranch %11
+ %9 = OpLabel
+ OpBranch %11
+ %11 = OpLabel
+ OpBranch %10
+ %10 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationMergeBlocks transformation(10);
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %7 = OpTypeBool
+ %8 = OpConstantTrue %7
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpSelectionMerge %11 None
+ OpBranchConditional %8 %6 %9
+ %6 = OpLabel
+ OpBranch %11
+ %9 = OpLabel
+ OpBranch %11
+ %11 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockIsLoopMerge) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %7 = OpTypeBool
+ %8 = OpConstantTrue %7
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %12 = OpLabel
+ OpBranch %5
+ %5 = OpLabel
+ OpLoopMerge %10 %11 None
+ OpBranch %6
+ %6 = OpLabel
+ OpBranchConditional %8 %9 %11
+ %9 = OpLabel
+ OpBranch %10
+ %11 = OpLabel
+ OpBranch %5
+ %10 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationMergeBlocks transformation(10);
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %7 = OpTypeBool
+ %8 = OpConstantTrue %7
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %12 = OpLabel
+ OpBranch %5
+ %5 = OpLabel
+ OpLoopMerge %9 %11 None
+ OpBranch %6
+ %6 = OpLabel
+ OpBranchConditional %8 %9 %11
+ %9 = OpLabel
+ OpReturn
+ %11 = OpLabel
+ OpBranch %5
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockIsLoopContinue) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %7 = OpTypeBool
+ %8 = OpConstantTrue %7
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %13 = OpLabel
+ OpBranch %5
+ %5 = OpLabel
+ OpLoopMerge %10 %11 None
+ OpBranch %6
+ %6 = OpLabel
+ OpSelectionMerge %9 None
+ OpBranchConditional %8 %9 %12
+ %12 = OpLabel
+ OpBranch %11
+ %11 = OpLabel
+ OpBranch %5
+ %9 = OpLabel
+ OpBranch %10
+ %10 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationMergeBlocks transformation(11);
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %7 = OpTypeBool
+ %8 = OpConstantTrue %7
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %13 = OpLabel
+ OpBranch %5
+ %5 = OpLabel
+ OpLoopMerge %10 %12 None
+ OpBranch %6
+ %6 = OpLabel
+ OpSelectionMerge %9 None
+ OpBranchConditional %8 %9 %12
+ %12 = OpLabel
+ OpBranch %5
+ %9 = OpLabel
+ OpBranch %10
+ %10 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockStartsWithOpPhi) {
+ 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
+ %7 = OpTypeBool
+ %8 = OpUndef %7
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %6
+ %6 = OpLabel
+ %9 = OpPhi %7 %8 %5
+ %10 = OpCopyObject %7 %9
+ OpBranch %11
+ %11 = OpLabel
+ %12 = OpCopyObject %7 %9
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationMergeBlocks transformation(6);
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %7 = OpTypeBool
+ %8 = OpUndef %7
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %10 = OpCopyObject %7 %8
+ OpBranch %11
+ %11 = OpLabel
+ %12 = OpCopyObject %7 %8
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationMergeBlocksTest, BasicMerge) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 2
+ %11 = OpConstant %6 3
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ OpStore %8 %9
+ OpBranch %100
+ %100 = OpLabel
+ OpStore %10 %11
+ %12 = OpLoad %6 %10
+ %13 = OpLoad %6 %8
+ OpBranch %101
+ %101 = OpLabel
+ %14 = OpIAdd %6 %13 %12
+ OpStore %8 %14
+ %15 = OpLoad %6 %8
+ OpBranch %102
+ %102 = OpLabel
+ %16 = OpLoad %6 %10
+ %17 = OpIMul %6 %16 %15
+ OpBranch %103
+ %103 = OpLabel
+ OpStore %10 %17
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ for (auto& transformation :
+ {TransformationMergeBlocks(100), TransformationMergeBlocks(101),
+ TransformationMergeBlocks(102), TransformationMergeBlocks(103)}) {
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ }
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 2
+ %11 = OpConstant %6 3
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ %12 = OpLoad %6 %10
+ %13 = OpLoad %6 %8
+ %14 = OpIAdd %6 %13 %12
+ OpStore %8 %14
+ %15 = OpLoad %6 %8
+ %16 = OpLoad %6 %10
+ %17 = OpIMul %6 %16 %15
+ OpStore %10 %17
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockIsSelectionHeader) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 2
+ %11 = OpConstant %6 3
+ %50 = OpTypeBool
+ %51 = OpConstantTrue %50
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ OpStore %8 %9
+ OpBranch %100
+ %100 = OpLabel
+ OpStore %10 %11
+ %12 = OpLoad %6 %10
+ %13 = OpLoad %6 %8
+ OpBranch %101
+ %101 = OpLabel
+ OpSelectionMerge %103 None
+ OpBranchConditional %51 %102 %103
+ %102 = OpLabel
+ %14 = OpIAdd %6 %13 %12
+ OpStore %8 %14
+ OpBranch %103
+ %103 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ for (auto& transformation :
+ {TransformationMergeBlocks(101), TransformationMergeBlocks(100)}) {
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ }
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 2
+ %11 = OpConstant %6 3
+ %50 = OpTypeBool
+ %51 = OpConstantTrue %50
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ %12 = OpLoad %6 %10
+ %13 = OpLoad %6 %8
+ OpSelectionMerge %103 None
+ OpBranchConditional %51 %102 %103
+ %102 = OpLabel
+ %14 = OpIAdd %6 %13 %12
+ OpStore %8 %14
+ OpBranch %103
+ %103 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationMergeBlocksTest,
+ MergeWhenFirstBlockIsLoopMergeFollowedByUnconditionalBranch) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 2
+ %11 = OpConstant %6 3
+ %50 = OpTypeBool
+ %51 = OpConstantTrue %50
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ OpStore %8 %9
+ OpBranch %100
+ %100 = OpLabel
+ OpLoopMerge %102 %103 None
+ OpBranch %101
+ %101 = OpLabel
+ %200 = OpCopyObject %6 %9
+ OpBranchConditional %51 %102 %103
+ %103 = OpLabel
+ OpBranch %100
+ %102 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationMergeBlocks transformation(101);
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 2
+ %11 = OpConstant %6 3
+ %50 = OpTypeBool
+ %51 = OpConstantTrue %50
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ OpStore %8 %9
+ OpBranch %100
+ %100 = OpLabel
+ %200 = OpCopyObject %6 %9
+ OpLoopMerge %102 %103 None
+ OpBranchConditional %51 %102 %103
+ %103 = OpLabel
+ OpBranch %100
+ %102 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/test/fuzz/transformation_outline_function_test.cpp b/test/fuzz/transformation_outline_function_test.cpp
new file mode 100644
index 0000000..4f828b6
--- /dev/null
+++ b/test/fuzz/transformation_outline_function_test.cpp
@@ -0,0 +1,2045 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_outline_function.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationOutlineFunctionTest, TrivialOutline) {
+ // This tests outlining of a single, empty basic block.
+
+ 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
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(5, 5, /* not relevant */ 200,
+ 100, 101, 102, 103,
+ /* not relevant */ 201, {}, {});
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %103 = OpFunctionCall %2 %101
+ OpReturn
+ OpFunctionEnd
+ %101 = OpFunction %2 None %3
+ %102 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest,
+ DoNotOutlineIfRegionStartsWithOpVariable) {
+ // This checks that we do not outline the first block of a function if it
+ // contains OpVariable.
+
+ 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
+ %7 = OpTypeBool
+ %8 = OpTypePointer Function %7
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %6 = 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);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(5, 5, /* not relevant */ 200,
+ 100, 101, 102, 103,
+ /* not relevant */ 201, {}, {});
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest, OutlineInterestingControlFlowNoState) {
+ // This tests outlining of some non-trivial control flow, but such that the
+ // basic blocks in the control flow do not actually do anything.
+
+ 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
+ %20 = OpTypeBool
+ %21 = OpConstantTrue %20
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %6
+ %6 = OpLabel
+ OpBranch %7
+ %7 = OpLabel
+ OpSelectionMerge %9 None
+ OpBranchConditional %21 %8 %9
+ %8 = OpLabel
+ OpBranch %9
+ %9 = OpLabel
+ OpLoopMerge %12 %11 None
+ OpBranch %10
+ %10 = OpLabel
+ OpBranchConditional %21 %11 %12
+ %11 = OpLabel
+ OpBranch %9
+ %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, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(6, 13, /* not relevant */
+ 200, 100, 101, 102, 103,
+ /* not relevant */ 201, {}, {});
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %20 = OpTypeBool
+ %21 = OpConstantTrue %20
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %6
+ %6 = OpLabel
+ %103 = OpFunctionCall %2 %101
+ OpReturn
+ OpFunctionEnd
+ %101 = OpFunction %2 None %3
+ %102 = OpLabel
+ OpBranch %7
+ %7 = OpLabel
+ OpSelectionMerge %9 None
+ OpBranchConditional %21 %8 %9
+ %8 = OpLabel
+ OpBranch %9
+ %9 = OpLabel
+ OpLoopMerge %12 %11 None
+ OpBranch %10
+ %10 = OpLabel
+ OpBranchConditional %21 %11 %12
+ %11 = OpLabel
+ OpBranch %9
+ %12 = OpLabel
+ OpBranch %13
+ %13 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest, OutlineCodeThatGeneratesUnusedIds) {
+ // This tests outlining of a single basic block that does some computation,
+ // but that does not use nor generate ids required outside of the outlined
+ // region.
+
+ 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
+ %20 = OpTypeInt 32 1
+ %21 = OpConstant %20 5
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %6
+ %6 = OpLabel
+ %7 = OpCopyObject %20 %21
+ %8 = OpCopyObject %20 %21
+ %9 = OpIAdd %20 %7 %8
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(6, 6, /* not relevant */ 200,
+ 100, 101, 102, 103,
+ /* not relevant */ 201, {}, {});
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %20 = OpTypeInt 32 1
+ %21 = OpConstant %20 5
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %6
+ %6 = OpLabel
+ %103 = OpFunctionCall %2 %101
+ OpReturn
+ OpFunctionEnd
+ %101 = OpFunction %2 None %3
+ %102 = OpLabel
+ %7 = OpCopyObject %20 %21
+ %8 = OpCopyObject %20 %21
+ %9 = OpIAdd %20 %7 %8
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest, OutlineCodeThatGeneratesSingleUsedId) {
+ // This tests outlining of a block that generates an id that is used in a
+ // later block.
+
+ 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
+ %20 = OpTypeInt 32 1
+ %21 = OpConstant %20 5
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %6
+ %6 = OpLabel
+ %7 = OpCopyObject %20 %21
+ %8 = OpCopyObject %20 %21
+ %9 = OpIAdd %20 %7 %8
+ OpBranch %10
+ %10 = OpLabel
+ %11 = OpCopyObject %20 %9
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(6, 6, 99, 100, 101, 102, 103,
+ 105, {}, {{9, 104}});
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %20 = OpTypeInt 32 1
+ %21 = OpConstant %20 5
+ %3 = OpTypeFunction %2
+ %99 = OpTypeStruct %20
+ %100 = OpTypeFunction %99
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %6
+ %6 = OpLabel
+ %103 = OpFunctionCall %99 %101
+ %9 = OpCompositeExtract %20 %103 0
+ OpBranch %10
+ %10 = OpLabel
+ %11 = OpCopyObject %20 %9
+ OpReturn
+ OpFunctionEnd
+ %101 = OpFunction %99 None %100
+ %102 = OpLabel
+ %7 = OpCopyObject %20 %21
+ %8 = OpCopyObject %20 %21
+ %104 = OpIAdd %20 %7 %8
+ %105 = OpCompositeConstruct %99 %104
+ OpReturnValue %105
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest, OutlineDiamondThatGeneratesSeveralIds) {
+ // This tests outlining of several blocks that generate a number of ids that
+ // are used in later blocks.
+
+ 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
+ %20 = OpTypeInt 32 1
+ %21 = OpConstant %20 5
+ %22 = OpTypeBool
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %6
+ %6 = OpLabel
+ %7 = OpCopyObject %20 %21
+ %8 = OpCopyObject %20 %21
+ %9 = OpSLessThan %22 %7 %8
+ OpSelectionMerge %12 None
+ OpBranchConditional %9 %10 %11
+ %10 = OpLabel
+ %13 = OpIAdd %20 %7 %8
+ OpBranch %12
+ %11 = OpLabel
+ %14 = OpIAdd %20 %7 %7
+ OpBranch %12
+ %12 = OpLabel
+ %15 = OpPhi %20 %13 %10 %14 %11
+ OpBranch %80
+ %80 = OpLabel
+ OpBranch %16
+ %16 = OpLabel
+ %17 = OpCopyObject %20 %15
+ %18 = OpCopyObject %22 %9
+ %19 = OpIAdd %20 %7 %8
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(
+ 6, 80, 100, 101, 102, 103, 104, 105, {},
+ {{15, 106}, {9, 107}, {7, 108}, {8, 109}});
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %20 = OpTypeInt 32 1
+ %21 = OpConstant %20 5
+ %22 = OpTypeBool
+ %3 = OpTypeFunction %2
+ %100 = OpTypeStruct %20 %20 %22 %20
+ %101 = OpTypeFunction %100
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %6
+ %6 = OpLabel
+ %104 = OpFunctionCall %100 %102
+ %7 = OpCompositeExtract %20 %104 0
+ %8 = OpCompositeExtract %20 %104 1
+ %9 = OpCompositeExtract %22 %104 2
+ %15 = OpCompositeExtract %20 %104 3
+ OpBranch %16
+ %16 = OpLabel
+ %17 = OpCopyObject %20 %15
+ %18 = OpCopyObject %22 %9
+ %19 = OpIAdd %20 %7 %8
+ OpReturn
+ OpFunctionEnd
+ %102 = OpFunction %100 None %101
+ %103 = OpLabel
+ %108 = OpCopyObject %20 %21
+ %109 = OpCopyObject %20 %21
+ %107 = OpSLessThan %22 %108 %109
+ OpSelectionMerge %12 None
+ OpBranchConditional %107 %10 %11
+ %10 = OpLabel
+ %13 = OpIAdd %20 %108 %109
+ OpBranch %12
+ %11 = OpLabel
+ %14 = OpIAdd %20 %108 %108
+ OpBranch %12
+ %12 = OpLabel
+ %106 = OpPhi %20 %13 %10 %14 %11
+ OpBranch %80
+ %80 = OpLabel
+ %105 = OpCompositeConstruct %100 %108 %109 %107 %106
+ OpReturnValue %105
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest, OutlineCodeThatUsesASingleId) {
+ // This tests outlining of a block that uses an id defined earlier.
+
+ 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
+ %20 = OpTypeInt 32 1
+ %21 = OpConstant %20 5
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %7 = OpCopyObject %20 %21
+ OpBranch %6
+ %6 = OpLabel
+ %8 = OpCopyObject %20 %7
+ OpBranch %10
+ %10 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(6, 6, 100, 101, 102, 103, 104,
+ 105, {{7, 106}}, {});
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %20 = OpTypeInt 32 1
+ %21 = OpConstant %20 5
+ %3 = OpTypeFunction %2
+ %101 = OpTypeFunction %2 %20
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %7 = OpCopyObject %20 %21
+ OpBranch %6
+ %6 = OpLabel
+ %104 = OpFunctionCall %2 %102 %7
+ OpBranch %10
+ %10 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %102 = OpFunction %2 None %101
+ %106 = OpFunctionParameter %20
+ %103 = OpLabel
+ %8 = OpCopyObject %20 %106
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest, OutlineCodeThatUsesAVariable) {
+ // This tests outlining of a block that uses a variable.
+
+ 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
+ %20 = OpTypeInt 32 1
+ %21 = OpConstant %20 5
+ %3 = OpTypeFunction %2
+ %12 = OpTypePointer Function %20
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %13 = OpVariable %12 Function
+ OpBranch %6
+ %6 = OpLabel
+ %8 = OpLoad %20 %13
+ OpBranch %10
+ %10 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(6, 6, 100, 101, 102, 103, 104,
+ 105, {{13, 106}}, {});
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %20 = OpTypeInt 32 1
+ %21 = OpConstant %20 5
+ %3 = OpTypeFunction %2
+ %12 = OpTypePointer Function %20
+ %101 = OpTypeFunction %2 %12
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %13 = OpVariable %12 Function
+ OpBranch %6
+ %6 = OpLabel
+ %104 = OpFunctionCall %2 %102 %13
+ OpBranch %10
+ %10 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %102 = OpFunction %2 None %101
+ %106 = OpFunctionParameter %12
+ %103 = OpLabel
+ %8 = OpLoad %20 %106
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest, OutlineCodeThatUsesAParameter) {
+ // This tests outlining of a block that uses a function parameter.
+
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %10 "foo(i1;"
+ OpName %9 "x"
+ OpName %18 "param"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %8 = OpTypeFunction %6 %7
+ %13 = OpConstant %6 1
+ %17 = OpConstant %6 3
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %18 = OpVariable %7 Function
+ OpStore %18 %17
+ %19 = OpFunctionCall %6 %10 %18
+ OpReturn
+ OpFunctionEnd
+ %10 = OpFunction %6 None %8
+ %9 = OpFunctionParameter %7
+ %11 = OpLabel
+ %12 = OpLoad %6 %9
+ %14 = OpIAdd %6 %12 %13
+ OpReturnValue %14
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(11, 11, 100, 101, 102, 103, 104,
+ 105, {{9, 106}}, {{14, 107}});
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %10 "foo(i1;"
+ OpName %9 "x"
+ OpName %18 "param"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %8 = OpTypeFunction %6 %7
+ %13 = OpConstant %6 1
+ %17 = OpConstant %6 3
+ %100 = OpTypeStruct %6
+ %101 = OpTypeFunction %100 %7
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %18 = OpVariable %7 Function
+ OpStore %18 %17
+ %19 = OpFunctionCall %6 %10 %18
+ OpReturn
+ OpFunctionEnd
+ %10 = OpFunction %6 None %8
+ %9 = OpFunctionParameter %7
+ %11 = OpLabel
+ %104 = OpFunctionCall %100 %102 %9
+ %14 = OpCompositeExtract %6 %104 0
+ OpReturnValue %14
+ OpFunctionEnd
+ %102 = OpFunction %100 None %101
+ %106 = OpFunctionParameter %7
+ %103 = OpLabel
+ %12 = OpLoad %6 %106
+ %107 = OpIAdd %6 %12 %13
+ %105 = OpCompositeConstruct %100 %107
+ OpReturnValue %105
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest,
+ DoNotOutlineIfLoopMergeIsOutsideRegion) {
+ 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
+ %9 = OpTypeBool
+ %10 = OpConstantTrue %9
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %6
+ %6 = OpLabel
+ OpLoopMerge %7 %8 None
+ OpBranch %8
+ %8 = OpLabel
+ OpBranchConditional %10 %6 %7
+ %7 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(6, 8, 100, 101, 102, 103, 104,
+ 105, {}, {});
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest, DoNotOutlineIfRegionInvolvesReturn) {
+ 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
+ %20 = OpTypeBool
+ %21 = OpConstantTrue %20
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %6
+ %6 = OpLabel
+ OpBranch %7
+ %7 = OpLabel
+ OpSelectionMerge %10 None
+ OpBranchConditional %21 %8 %9
+ %8 = OpLabel
+ OpReturn
+ %9 = OpLabel
+ OpBranch %10
+ %10 = OpLabel
+ OpBranch %11
+ %11 = OpLabel
+ OpBranch %12
+ %12 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(6, 11, /* not relevant */ 200,
+ 100, 101, 102, 103,
+ /* not relevant */ 201, {}, {});
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest, DoNotOutlineIfRegionInvolvesKill) {
+ 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
+ %20 = OpTypeBool
+ %21 = OpConstantTrue %20
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %6
+ %6 = OpLabel
+ OpBranch %7
+ %7 = OpLabel
+ OpSelectionMerge %10 None
+ OpBranchConditional %21 %8 %9
+ %8 = OpLabel
+ OpKill
+ %9 = OpLabel
+ OpBranch %10
+ %10 = OpLabel
+ OpBranch %11
+ %11 = OpLabel
+ OpBranch %12
+ %12 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(6, 11, /* not relevant */ 200,
+ 100, 101, 102, 103,
+ /* not relevant */ 201, {}, {});
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest,
+ DoNotOutlineIfRegionInvolvesUnreachable) {
+ 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
+ %20 = OpTypeBool
+ %21 = OpConstantTrue %20
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %6
+ %6 = OpLabel
+ OpBranch %7
+ %7 = OpLabel
+ OpSelectionMerge %10 None
+ OpBranchConditional %21 %8 %9
+ %8 = OpLabel
+ OpBranch %10
+ %9 = OpLabel
+ OpUnreachable
+ %10 = OpLabel
+ OpBranch %11
+ %11 = OpLabel
+ OpBranch %12
+ %12 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(6, 11, /* not relevant */ 200,
+ 100, 101, 102, 103,
+ /* not relevant */ 201, {}, {});
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest,
+ DoNotOutlineIfSelectionMergeIsOutsideRegion) {
+ 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
+ %9 = OpTypeBool
+ %10 = OpConstantTrue %9
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %6
+ %6 = OpLabel
+ OpSelectionMerge %7 None
+ OpBranchConditional %10 %8 %7
+ %8 = OpLabel
+ OpBranch %7
+ %7 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(6, 8, 100, 101, 102, 103, 104,
+ 105, {}, {});
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest, DoNotOutlineIfLoopHeadIsOutsideRegion) {
+ 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
+ %9 = OpTypeBool
+ %10 = OpConstantTrue %9
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %6
+ %6 = OpLabel
+ OpLoopMerge %8 %11 None
+ OpBranch %7
+ %7 = OpLabel
+ OpBranchConditional %10 %11 %8
+ %11 = OpLabel
+ OpBranch %6
+ %8 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(7, 8, 100, 101, 102, 103, 104,
+ 105, {}, {});
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest,
+ DoNotOutlineIfLoopContinueIsOutsideRegion) {
+ 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
+ %9 = OpTypeBool
+ %10 = OpConstantTrue %9
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %6
+ %6 = OpLabel
+ OpLoopMerge %7 %8 None
+ OpBranch %7
+ %8 = OpLabel
+ OpBranch %6
+ %7 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(6, 7, 100, 101, 102, 103, 104,
+ 105, {}, {});
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest,
+ DoNotOutlineWithLoopCarriedPhiDependence) {
+ 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
+ %9 = OpTypeBool
+ %10 = OpConstantTrue %9
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %6
+ %6 = OpLabel
+ %12 = OpPhi %9 %10 %5 %13 %8
+ OpLoopMerge %7 %8 None
+ OpBranch %8
+ %8 = OpLabel
+ %13 = OpCopyObject %9 %10
+ OpBranchConditional %10 %6 %7
+ %7 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(6, 7, 100, 101, 102, 103, 104,
+ 105, {}, {});
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest,
+ DoNotOutlineSelectionHeaderNotInRegion) {
+ 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
+ %6 = OpTypeBool
+ %7 = OpConstantTrue %6
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpSelectionMerge %10 None
+ OpBranchConditional %7 %8 %8
+ %8 = OpLabel
+ OpBranch %9
+ %9 = OpLabel
+ OpBranch %10
+ %10 = OpLabel
+ OpBranch %11
+ %11 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(8, 11, 100, 101, 102, 103, 104,
+ 105, {}, {});
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest, OutlineRegionEndingWithReturnVoid) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %20 = OpTypeInt 32 0
+ %21 = OpConstant %20 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %22 = OpCopyObject %20 %21
+ OpBranch %54
+ %54 = OpLabel
+ OpBranch %57
+ %57 = OpLabel
+ %23 = OpCopyObject %20 %22
+ OpBranch %58
+ %58 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(
+ /*entry_block*/ 54,
+ /*exit_block*/ 58,
+ /*new_function_struct_return_type_id*/ 200,
+ /*new_function_type_id*/ 201,
+ /*new_function_id*/ 202,
+ /*new_function_region_entry_block*/ 203,
+ /*new_caller_result_id*/ 204,
+ /*new_callee_result_id*/ 205,
+ /*input_id_to_fresh_id*/ {{22, 206}},
+ /*output_id_to_fresh_id*/ {});
+
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %20 = OpTypeInt 32 0
+ %21 = OpConstant %20 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %201 = OpTypeFunction %2 %20
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %22 = OpCopyObject %20 %21
+ OpBranch %54
+ %54 = OpLabel
+ %204 = OpFunctionCall %2 %202 %22
+ OpReturn
+ OpFunctionEnd
+ %202 = OpFunction %2 None %201
+ %206 = OpFunctionParameter %20
+ %203 = OpLabel
+ OpBranch %57
+ %57 = OpLabel
+ %23 = OpCopyObject %20 %206
+ OpBranch %58
+ %58 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest, OutlineRegionEndingWithReturnValue) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %20 = OpTypeInt 32 0
+ %21 = OpConstant %20 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %30 = OpTypeFunction %20
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %6 = OpFunctionCall %20 %100
+ OpReturn
+ OpFunctionEnd
+ %100 = OpFunction %20 None %30
+ %8 = OpLabel
+ %31 = OpCopyObject %20 %21
+ OpBranch %9
+ %9 = OpLabel
+ %32 = OpCopyObject %20 %31
+ OpBranch %10
+ %10 = OpLabel
+ OpReturnValue %32
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(
+ /*entry_block*/ 9,
+ /*exit_block*/ 10,
+ /*new_function_struct_return_type_id*/ 200,
+ /*new_function_type_id*/ 201,
+ /*new_function_id*/ 202,
+ /*new_function_region_entry_block*/ 203,
+ /*new_caller_result_id*/ 204,
+ /*new_callee_result_id*/ 205,
+ /*input_id_to_fresh_id*/ {{31, 206}},
+ /*output_id_to_fresh_id*/ {{32, 207}});
+
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %20 = OpTypeInt 32 0
+ %21 = OpConstant %20 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %30 = OpTypeFunction %20
+ %200 = OpTypeStruct %20
+ %201 = OpTypeFunction %200 %20
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %6 = OpFunctionCall %20 %100
+ OpReturn
+ OpFunctionEnd
+ %100 = OpFunction %20 None %30
+ %8 = OpLabel
+ %31 = OpCopyObject %20 %21
+ OpBranch %9
+ %9 = OpLabel
+ %204 = OpFunctionCall %200 %202 %31
+ %32 = OpCompositeExtract %20 %204 0
+ OpReturnValue %32
+ OpFunctionEnd
+ %202 = OpFunction %200 None %201
+ %206 = OpFunctionParameter %20
+ %203 = OpLabel
+ %207 = OpCopyObject %20 %206
+ OpBranch %10
+ %10 = OpLabel
+ %205 = OpCompositeConstruct %200 %207
+ OpReturnValue %205
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest,
+ OutlineRegionEndingWithConditionalBranch) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %20 = OpTypeBool
+ %21 = OpConstantTrue %20
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %54
+ %54 = OpLabel
+ %6 = OpCopyObject %20 %21
+ OpSelectionMerge %8 None
+ OpBranchConditional %6 %7 %8
+ %7 = OpLabel
+ OpBranch %8
+ %8 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(
+ /*entry_block*/ 54,
+ /*exit_block*/ 54,
+ /*new_function_struct_return_type_id*/ 200,
+ /*new_function_type_id*/ 201,
+ /*new_function_id*/ 202,
+ /*new_function_region_entry_block*/ 203,
+ /*new_caller_result_id*/ 204,
+ /*new_callee_result_id*/ 205,
+ /*input_id_to_fresh_id*/ {{}},
+ /*output_id_to_fresh_id*/ {{6, 206}});
+
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %20 = OpTypeBool
+ %21 = OpConstantTrue %20
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %200 = OpTypeStruct %20
+ %201 = OpTypeFunction %200
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %54
+ %54 = OpLabel
+ %204 = OpFunctionCall %200 %202
+ %6 = OpCompositeExtract %20 %204 0
+ OpSelectionMerge %8 None
+ OpBranchConditional %6 %7 %8
+ %7 = OpLabel
+ OpBranch %8
+ %8 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %202 = OpFunction %200 None %201
+ %203 = OpLabel
+ %206 = OpCopyObject %20 %21
+ %205 = OpCompositeConstruct %200 %206
+ OpReturnValue %205
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest,
+ OutlineRegionEndingWithConditionalBranch2) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %20 = OpTypeBool
+ %21 = OpConstantTrue %20
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %6 = OpCopyObject %20 %21
+ OpBranch %54
+ %54 = OpLabel
+ OpSelectionMerge %8 None
+ OpBranchConditional %6 %7 %8
+ %7 = OpLabel
+ OpBranch %8
+ %8 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(
+ /*entry_block*/ 54,
+ /*exit_block*/ 54,
+ /*new_function_struct_return_type_id*/ 200,
+ /*new_function_type_id*/ 201,
+ /*new_function_id*/ 202,
+ /*new_function_region_entry_block*/ 203,
+ /*new_caller_result_id*/ 204,
+ /*new_callee_result_id*/ 205,
+ /*input_id_to_fresh_id*/ {},
+ /*output_id_to_fresh_id*/ {});
+
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %20 = OpTypeBool
+ %21 = OpConstantTrue %20
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %6 = OpCopyObject %20 %21
+ OpBranch %54
+ %54 = OpLabel
+ %204 = OpFunctionCall %2 %202
+ OpSelectionMerge %8 None
+ OpBranchConditional %6 %7 %8
+ %7 = OpLabel
+ OpBranch %8
+ %8 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %202 = OpFunction %2 None %3
+ %203 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest, DoNotOutlineRegionThatStartsWithOpPhi) {
+ 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
+ %6 = OpTypeBool
+ %7 = OpConstantTrue %6
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %21
+ %21 = OpLabel
+ %22 = OpPhi %6 %7 %5
+ %23 = OpCopyObject %6 %22
+ OpBranch %24
+ %24 = OpLabel
+ %25 = OpCopyObject %6 %23
+ %26 = OpCopyObject %6 %22
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(
+ /*entry_block*/ 21,
+ /*exit_block*/ 21,
+ /*new_function_struct_return_type_id*/ 200,
+ /*new_function_type_id*/ 201,
+ /*new_function_id*/ 202,
+ /*new_function_region_entry_block*/ 204,
+ /*new_caller_result_id*/ 205,
+ /*new_callee_result_id*/ 206,
+ /*input_id_to_fresh_id*/ {{22, 207}},
+ /*output_id_to_fresh_id*/ {{23, 208}});
+
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest,
+ DoNotOutlineRegionThatStartsWithLoopHeader) {
+ 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
+ %6 = OpTypeBool
+ %7 = OpConstantTrue %6
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %21
+ %21 = OpLabel
+ OpLoopMerge %22 %23 None
+ OpBranch %24
+ %24 = OpLabel
+ OpBranchConditional %7 %22 %23
+ %23 = OpLabel
+ OpBranch %21
+ %22 = OpLabel
+ OpBranch %25
+ %25 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(
+ /*entry_block*/ 21,
+ /*exit_block*/ 24,
+ /*new_function_struct_return_type_id*/ 200,
+ /*new_function_type_id*/ 201,
+ /*new_function_id*/ 202,
+ /*new_function_region_entry_block*/ 204,
+ /*new_caller_result_id*/ 205,
+ /*new_callee_result_id*/ 206,
+ /*input_id_to_fresh_id*/ {},
+ /*output_id_to_fresh_id*/ {});
+
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest,
+ DoNotOutlineRegionThatEndsWithLoopMerge) {
+ 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
+ %6 = OpTypeBool
+ %7 = OpConstantTrue %6
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %21
+ %21 = OpLabel
+ OpLoopMerge %22 %23 None
+ OpBranch %24
+ %24 = OpLabel
+ OpBranchConditional %7 %22 %23
+ %23 = OpLabel
+ OpBranch %21
+ %22 = OpLabel
+ OpBranch %25
+ %25 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(
+ /*entry_block*/ 5,
+ /*exit_block*/ 22,
+ /*new_function_struct_return_type_id*/ 200,
+ /*new_function_type_id*/ 201,
+ /*new_function_id*/ 202,
+ /*new_function_region_entry_block*/ 204,
+ /*new_caller_result_id*/ 205,
+ /*new_callee_result_id*/ 206,
+ /*input_id_to_fresh_id*/ {},
+ /*output_id_to_fresh_id*/ {});
+
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest, DoNotOutlineRegionThatUsesAccessChain) {
+ // An access chain result is a pointer, but it cannot be passed as a function
+ // parameter, as it is not a memory object.
+ 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
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Function %7
+ %9 = OpTypePointer Function %6
+ %18 = OpTypeInt 32 0
+ %19 = OpConstant %18 0
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %10 = OpVariable %8 Function
+ OpBranch %11
+ %11 = OpLabel
+ %12 = OpAccessChain %9 %10 %19
+ OpBranch %13
+ %13 = OpLabel
+ %14 = OpLoad %6 %12
+ OpBranch %15
+ %15 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(
+ /*entry_block*/ 13,
+ /*exit_block*/ 15,
+ /*new_function_struct_return_type_id*/ 200,
+ /*new_function_type_id*/ 201,
+ /*new_function_id*/ 202,
+ /*new_function_region_entry_block*/ 204,
+ /*new_caller_result_id*/ 205,
+ /*new_callee_result_id*/ 206,
+ /*input_id_to_fresh_id*/ {{12, 207}},
+ /*output_id_to_fresh_id*/ {});
+
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest, Miscellaneous1) {
+ // This tests outlining of some non-trivial code.
+
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %85
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %28 "buf"
+ OpMemberName %28 0 "u1"
+ OpMemberName %28 1 "u2"
+ OpName %30 ""
+ OpName %85 "color"
+ OpMemberDecorate %28 0 Offset 0
+ OpMemberDecorate %28 1 Offset 4
+ OpDecorate %28 Block
+ OpDecorate %30 DescriptorSet 0
+ OpDecorate %30 Binding 0
+ OpDecorate %85 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %10 = OpConstant %6 1
+ %11 = OpConstant %6 2
+ %12 = OpConstant %6 3
+ %13 = OpConstant %6 4
+ %14 = OpConstantComposite %7 %10 %11 %12 %13
+ %15 = OpTypeInt 32 1
+ %18 = OpConstant %15 0
+ %28 = OpTypeStruct %6 %6
+ %29 = OpTypePointer Uniform %28
+ %30 = OpVariable %29 Uniform
+ %31 = OpTypePointer Uniform %6
+ %35 = OpTypeBool
+ %39 = OpConstant %15 1
+ %84 = OpTypePointer Output %7
+ %85 = OpVariable %84 Output
+ %114 = OpConstant %15 8
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %22
+ %22 = OpLabel
+ %103 = OpPhi %15 %18 %5 %106 %43
+ %102 = OpPhi %7 %14 %5 %107 %43
+ %101 = OpPhi %15 %18 %5 %40 %43
+ %32 = OpAccessChain %31 %30 %18
+ %33 = OpLoad %6 %32
+ %34 = OpConvertFToS %15 %33
+ %36 = OpSLessThan %35 %101 %34
+ OpLoopMerge %24 %43 None
+ OpBranchConditional %36 %23 %24
+ %23 = OpLabel
+ %40 = OpIAdd %15 %101 %39
+ OpBranch %150
+ %150 = OpLabel
+ OpBranch %41
+ %41 = OpLabel
+ %107 = OpPhi %7 %102 %150 %111 %65
+ %106 = OpPhi %15 %103 %150 %110 %65
+ %104 = OpPhi %15 %40 %150 %81 %65
+ %47 = OpAccessChain %31 %30 %39
+ %48 = OpLoad %6 %47
+ %49 = OpConvertFToS %15 %48
+ %50 = OpSLessThan %35 %104 %49
+ OpLoopMerge %1000 %65 None
+ OpBranchConditional %50 %42 %1000
+ %42 = OpLabel
+ %60 = OpIAdd %15 %106 %114
+ %63 = OpSGreaterThan %35 %104 %60
+ OpBranchConditional %63 %64 %65
+ %64 = OpLabel
+ %71 = OpCompositeExtract %6 %107 0
+ %72 = OpFAdd %6 %71 %11
+ %97 = OpCompositeInsert %7 %72 %107 0
+ %76 = OpCompositeExtract %6 %107 3
+ %77 = OpConvertFToS %15 %76
+ %79 = OpIAdd %15 %60 %77
+ OpBranch %65
+ %65 = OpLabel
+ %111 = OpPhi %7 %107 %42 %97 %64
+ %110 = OpPhi %15 %60 %42 %79 %64
+ %81 = OpIAdd %15 %104 %39
+ OpBranch %41
+ %1000 = OpLabel
+ OpBranch %1001
+ %1001 = OpLabel
+ OpBranch %43
+ %43 = OpLabel
+ OpBranch %22
+ %24 = OpLabel
+ %87 = OpCompositeExtract %6 %102 0
+ %91 = OpConvertSToF %6 %103
+ %92 = OpCompositeConstruct %7 %87 %11 %91 %10
+ OpStore %85 %92
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(
+ /*entry_block*/ 150,
+ /*exit_block*/ 1001,
+ /*new_function_struct_return_type_id*/ 200,
+ /*new_function_type_id*/ 201,
+ /*new_function_id*/ 202,
+ /*new_function_region_entry_block*/ 203,
+ /*new_caller_result_id*/ 204,
+ /*new_callee_result_id*/ 205,
+ /*input_id_to_fresh_id*/ {{102, 300}, {103, 301}, {40, 302}},
+ /*output_id_to_fresh_id*/ {{106, 400}, {107, 401}});
+
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %85
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %28 "buf"
+ OpMemberName %28 0 "u1"
+ OpMemberName %28 1 "u2"
+ OpName %30 ""
+ OpName %85 "color"
+ OpMemberDecorate %28 0 Offset 0
+ OpMemberDecorate %28 1 Offset 4
+ OpDecorate %28 Block
+ OpDecorate %30 DescriptorSet 0
+ OpDecorate %30 Binding 0
+ OpDecorate %85 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %10 = OpConstant %6 1
+ %11 = OpConstant %6 2
+ %12 = OpConstant %6 3
+ %13 = OpConstant %6 4
+ %14 = OpConstantComposite %7 %10 %11 %12 %13
+ %15 = OpTypeInt 32 1
+ %18 = OpConstant %15 0
+ %28 = OpTypeStruct %6 %6
+ %29 = OpTypePointer Uniform %28
+ %30 = OpVariable %29 Uniform
+ %31 = OpTypePointer Uniform %6
+ %35 = OpTypeBool
+ %39 = OpConstant %15 1
+ %84 = OpTypePointer Output %7
+ %85 = OpVariable %84 Output
+ %114 = OpConstant %15 8
+ %200 = OpTypeStruct %7 %15
+ %201 = OpTypeFunction %200 %15 %7 %15
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %22
+ %22 = OpLabel
+ %103 = OpPhi %15 %18 %5 %106 %43
+ %102 = OpPhi %7 %14 %5 %107 %43
+ %101 = OpPhi %15 %18 %5 %40 %43
+ %32 = OpAccessChain %31 %30 %18
+ %33 = OpLoad %6 %32
+ %34 = OpConvertFToS %15 %33
+ %36 = OpSLessThan %35 %101 %34
+ OpLoopMerge %24 %43 None
+ OpBranchConditional %36 %23 %24
+ %23 = OpLabel
+ %40 = OpIAdd %15 %101 %39
+ OpBranch %150
+ %150 = OpLabel
+ %204 = OpFunctionCall %200 %202 %103 %102 %40
+ %107 = OpCompositeExtract %7 %204 0
+ %106 = OpCompositeExtract %15 %204 1
+ OpBranch %43
+ %43 = OpLabel
+ OpBranch %22
+ %24 = OpLabel
+ %87 = OpCompositeExtract %6 %102 0
+ %91 = OpConvertSToF %6 %103
+ %92 = OpCompositeConstruct %7 %87 %11 %91 %10
+ OpStore %85 %92
+ OpReturn
+ OpFunctionEnd
+ %202 = OpFunction %200 None %201
+ %301 = OpFunctionParameter %15
+ %300 = OpFunctionParameter %7
+ %302 = OpFunctionParameter %15
+ %203 = OpLabel
+ OpBranch %41
+ %41 = OpLabel
+ %401 = OpPhi %7 %300 %203 %111 %65
+ %400 = OpPhi %15 %301 %203 %110 %65
+ %104 = OpPhi %15 %302 %203 %81 %65
+ %47 = OpAccessChain %31 %30 %39
+ %48 = OpLoad %6 %47
+ %49 = OpConvertFToS %15 %48
+ %50 = OpSLessThan %35 %104 %49
+ OpLoopMerge %1000 %65 None
+ OpBranchConditional %50 %42 %1000
+ %42 = OpLabel
+ %60 = OpIAdd %15 %400 %114
+ %63 = OpSGreaterThan %35 %104 %60
+ OpBranchConditional %63 %64 %65
+ %64 = OpLabel
+ %71 = OpCompositeExtract %6 %401 0
+ %72 = OpFAdd %6 %71 %11
+ %97 = OpCompositeInsert %7 %72 %401 0
+ %76 = OpCompositeExtract %6 %401 3
+ %77 = OpConvertFToS %15 %76
+ %79 = OpIAdd %15 %60 %77
+ OpBranch %65
+ %65 = OpLabel
+ %111 = OpPhi %7 %401 %42 %97 %64
+ %110 = OpPhi %15 %60 %42 %79 %64
+ %81 = OpIAdd %15 %104 %39
+ OpBranch %41
+ %1000 = OpLabel
+ OpBranch %1001
+ %1001 = OpLabel
+ %205 = OpCompositeConstruct %200 %401 %400
+ OpReturnValue %205
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest, Miscellaneous2) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %21 = OpTypeBool
+ %167 = OpConstantTrue %21
+ %168 = OpConstantFalse %21
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %34
+ %34 = OpLabel
+ OpLoopMerge %36 %37 None
+ OpBranchConditional %168 %37 %38
+ %38 = OpLabel
+ OpBranchConditional %168 %37 %36
+ %37 = OpLabel
+ OpBranch %34
+ %36 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(
+ /*entry_block*/ 38,
+ /*exit_block*/ 36,
+ /*new_function_struct_return_type_id*/ 200,
+ /*new_function_type_id*/ 201,
+ /*new_function_id*/ 202,
+ /*new_function_region_entry_block*/ 203,
+ /*new_caller_result_id*/ 204,
+ /*new_callee_result_id*/ 205,
+ /*input_id_to_fresh_id*/ {},
+ /*output_id_to_fresh_id*/ {});
+
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest, Miscellaneous3) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %6 "main"
+ OpExecutionMode %6 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %21 = OpTypeBool
+ %167 = OpConstantTrue %21
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ OpBranch %80
+ %80 = OpLabel
+ OpBranch %14
+ %14 = OpLabel
+ OpLoopMerge %16 %17 None
+ OpBranch %18
+ %18 = OpLabel
+ OpBranchConditional %167 %15 %16
+ %15 = OpLabel
+ OpBranch %17
+ %16 = OpLabel
+ OpBranch %81
+ %81 = OpLabel
+ OpReturn
+ %17 = OpLabel
+ OpBranch %14
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ TransformationOutlineFunction transformation(
+ /*entry_block*/ 80,
+ /*exit_block*/ 81,
+ /*new_function_struct_return_type_id*/ 300,
+ /*new_function_type_id*/ 301,
+ /*new_function_id*/ 302,
+ /*new_function_region_entry_block*/ 304,
+ /*new_caller_result_id*/ 305,
+ /*new_callee_result_id*/ 306,
+ /*input_id_to_fresh_id*/ {},
+ /*output_id_to_fresh_id*/ {});
+
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %6 "main"
+ OpExecutionMode %6 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %21 = OpTypeBool
+ %167 = OpConstantTrue %21
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ OpBranch %80
+ %80 = OpLabel
+ %305 = OpFunctionCall %2 %302
+ OpReturn
+ OpFunctionEnd
+ %302 = OpFunction %2 None %3
+ %304 = OpLabel
+ OpBranch %14
+ %14 = OpLabel
+ OpLoopMerge %16 %17 None
+ OpBranch %18
+ %18 = OpLabel
+ OpBranchConditional %167 %15 %16
+ %15 = OpLabel
+ OpBranch %17
+ %16 = OpLabel
+ OpBranch %81
+ %81 = OpLabel
+ OpReturn
+ %17 = OpLabel
+ OpBranch %14
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/test/fuzz/transformation_replace_id_with_synonym_test.cpp
index 75e117a..41b6116 100644
--- a/test/fuzz/transformation_replace_id_with_synonym_test.cpp
+++ b/test/fuzz/transformation_replace_id_with_synonym_test.cpp
@@ -159,18 +159,20 @@
%65 = OpAccessChain %13 %11 %64
%66 = OpLoad %6 %65
%67 = OpSGreaterThan %29 %84 %66
- OpSelectionMerge %69 None
+ OpSelectionMerge %1000 None
OpBranchConditional %67 %68 %72
%68 = OpLabel
%71 = OpIAdd %6 %84 %26
- OpBranch %69
+ OpBranch %1000
%72 = OpLabel
%74 = OpIAdd %6 %84 %64
%205 = OpCopyObject %6 %74
- OpBranch %69
- %69 = OpLabel
+ OpBranch %1000
+ %1000 = OpLabel
%86 = OpPhi %6 %71 %68 %74 %72
%301 = OpPhi %6 %71 %68 %15 %72
+ OpBranch %69
+ %69 = OpLabel
OpBranch %20
%22 = OpLabel
%75 = OpAccessChain %46 %42 %50
@@ -421,18 +423,20 @@
%65 = OpAccessChain %13 %11 %64
%66 = OpLoad %6 %65
%67 = OpSGreaterThan %29 %84 %66
- OpSelectionMerge %69 None
+ OpSelectionMerge %1000 None
OpBranchConditional %67 %68 %72
%68 = OpLabel
%71 = OpIAdd %6 %84 %26
- OpBranch %69
+ OpBranch %1000
%72 = OpLabel
%74 = OpIAdd %6 %84 %64
%205 = OpCopyObject %6 %74
- OpBranch %69
- %69 = OpLabel
+ OpBranch %1000
+ %1000 = OpLabel
%86 = OpPhi %6 %71 %68 %205 %72
%301 = OpPhi %6 %71 %68 %15 %72
+ OpBranch %69
+ %69 = OpLabel
OpBranch %20
%22 = OpLabel
%75 = OpAccessChain %46 %42 %50
diff --git a/test/fuzzers/spvtools_as_fuzzer.cpp b/test/fuzzers/spvtools_as_fuzzer.cpp
index 1b1de00..8cecb05 100644
--- a/test/fuzzers/spvtools_as_fuzzer.cpp
+++ b/test/fuzzers/spvtools_as_fuzzer.cpp
@@ -36,7 +36,7 @@
input_str.resize(char_count);
memcpy(input_str.data(), input.data(), input.size() * sizeof(uint32_t));
- spv_binary binary;
+ spv_binary binary = nullptr;
spv_diagnostic diagnostic = nullptr;
spvTextToBinaryWithOptions(context, input_str.data(), input_str.size(),
SPV_TEXT_TO_BINARY_OPTION_NONE, &binary,
@@ -66,5 +66,7 @@
binary = nullptr;
}
+ spvContextDestroy(context);
+
return 0;
}
diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp
index 9e5197d..e7303ba 100644
--- a/test/opt/aggressive_dead_code_elim_test.cpp
+++ b/test/opt/aggressive_dead_code_elim_test.cpp
@@ -5932,7 +5932,6 @@
%42 = OpLabel
%43 = OpLoad %int %i
%44 = OpSLessThan %bool %43 %int_1
-OpSelectionMerge %45 None
OpBranchConditional %44 %46 %40
%46 = OpLabel
%47 = OpLoad %int %i
diff --git a/test/opt/block_merge_test.cpp b/test/opt/block_merge_test.cpp
index f87fd80..11fba73 100644
--- a/test/opt/block_merge_test.cpp
+++ b/test/opt/block_merge_test.cpp
@@ -447,10 +447,53 @@
OpLoopMerge %merge %continue None
OpBranch %inner_header
%inner_header = OpLabel
-OpSelectionMerge %continue None
-OpBranchConditional %true %then %continue
+OpSelectionMerge %if_merge None
+OpBranchConditional %true %then %if_merge
%then = OpLabel
OpBranch %continue
+%if_merge = OpLabel
+OpBranch %continue
+%continue = OpLabel
+OpBranchConditional %false %merge %header
+%merge = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<BlockMergePass>(text, true);
+}
+
+TEST_F(BlockMergeTest, CannotMergeContinue) {
+ const std::string text = R"(
+; CHECK: OpBranch [[loop_header:%\w+]]
+; CHECK: [[loop_header]] = OpLabel
+; CHECK-NEXT: OpLoopMerge {{%\w+}} [[continue:%\w+]]
+; CHECK-NEXT: OpBranchConditional {{%\w+}} [[if_header:%\w+]]
+; CHECK: [[if_header]] = OpLabel
+; CHECK-NEXT: OpSelectionMerge
+; CHECK: [[continue]] = OpLabel
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %func "func"
+OpExecutionMode %func OriginUpperLeft
+%void = OpTypeVoid
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%false = OpConstantFalse %bool
+%functy = OpTypeFunction %void
+%func = OpFunction %void None %functy
+%entry = OpLabel
+OpBranch %header
+%header = OpLabel
+OpLoopMerge %merge %continue None
+OpBranchConditional %true %inner_header %merge
+%inner_header = OpLabel
+OpSelectionMerge %if_merge None
+OpBranchConditional %true %then %if_merge
+%then = OpLabel
+OpBranch %continue
+%if_merge = OpLabel
+OpBranch %continue
%continue = OpLabel
OpBranchConditional %false %merge %header
%merge = OpLabel
diff --git a/test/opt/convert_relaxed_to_half_test.cpp b/test/opt/convert_relaxed_to_half_test.cpp
index 3ac8009..c138154 100644
--- a/test/opt/convert_relaxed_to_half_test.cpp
+++ b/test/opt/convert_relaxed_to_half_test.cpp
@@ -997,7 +997,7 @@
%99 = OpFConvert %v4half %15
OpBranch %65
%65 = OpLabel
-%96 = OpPhi %v4half %99 %5 %76 %71
+%96 = OpPhi %v4half %99 %5 %100 %71
%95 = OpPhi %int %int_0 %5 %78 %71
%70 = OpSLessThan %bool %95 %int_10
OpLoopMerge %66 %71 None
@@ -1222,6 +1222,115 @@
defs_after + func_after, true, true);
}
+TEST_F(ConvertToHalfTest, ConvertToHalfWithClosure) {
+ // Include as many contiguous composite instructions as possible into
+ // half-precision computations
+ //
+ // Compiled with glslang -V -Os
+ //
+ // clang-format off
+ //
+ // #version 410 core
+ //
+ // precision mediump float;
+ //
+ // layout(location = 1) in vec3 foo;
+ // layout(location = 2) in mat2 bar;
+ // layout(location = 1) out vec3 res;
+ //
+ // vec3 func(vec3 tap, mat2 M) {
+ // return vec3(M * tap.xy, 1.0);
+ // }
+ //
+ // void main() {
+ // res = func(foo, bar);
+ // }
+ //
+ // clang-format on
+
+ const std::string defs =
+ R"(OpCapability Shader
+; CHECK: OpCapability Float16
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %res %foo %bar
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 410
+OpName %main "main"
+OpName %res "res"
+OpName %foo "foo"
+OpName %bar "bar"
+OpDecorate %res RelaxedPrecision
+; CHECK-NOT: OpDecorate %res RelaxedPrecision
+OpDecorate %res Location 1
+OpDecorate %foo RelaxedPrecision
+; CHECK-NOT: OpDecorate %foo RelaxedPrecision
+OpDecorate %foo Location 1
+OpDecorate %bar RelaxedPrecision
+; CHECK-NOT: OpDecorate %bar RelaxedPrecision
+OpDecorate %bar Location 2
+OpDecorate %34 RelaxedPrecision
+OpDecorate %36 RelaxedPrecision
+OpDecorate %41 RelaxedPrecision
+OpDecorate %42 RelaxedPrecision
+; CHECK-NOT: OpDecorate %34 RelaxedPrecision
+; CHECK-NOT: OpDecorate %36 RelaxedPrecision
+; CHECK-NOT: OpDecorate %41 RelaxedPrecision
+; CHECK-NOT: OpDecorate %42 RelaxedPrecision
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v3float = OpTypeVector %float 3
+%v2float = OpTypeVector %float 2
+%mat2v2float = OpTypeMatrix %v2float 2
+%float_1 = OpConstant %float 1
+%_ptr_Output_v3float = OpTypePointer Output %v3float
+%res = OpVariable %_ptr_Output_v3float Output
+%_ptr_Input_v3float = OpTypePointer Input %v3float
+%foo = OpVariable %_ptr_Input_v3float Input
+%_ptr_Input_mat2v2float = OpTypePointer Input %mat2v2float
+%bar = OpVariable %_ptr_Input_mat2v2float Input
+)";
+
+ const std::string func =
+ R"(%main = OpFunction %void None %3
+%5 = OpLabel
+%34 = OpLoad %v3float %foo
+%36 = OpLoad %mat2v2float %bar
+; CHECK: %48 = OpFConvert %v3half %34
+; CHECK: %49 = OpFConvert %v3half %34
+%41 = OpVectorShuffle %v2float %34 %34 0 1
+; CHECK-NOT: %41 = OpVectorShuffle %v2float %34 %34 0 1
+; CHECK: %41 = OpVectorShuffle %v2half %48 %49 0 1
+%42 = OpMatrixTimesVector %v2float %36 %41
+; CHECK-NOT: %42 = OpMatrixTimesVector %v2float %36 %41
+; CHECK: %55 = OpCompositeExtract %v2float %36 0
+; CHECK: %56 = OpFConvert %v2half %55
+; CHECK: %57 = OpCompositeExtract %v2float %36 1
+; CHECK: %58 = OpFConvert %v2half %57
+; CHECK: %59 = OpCompositeConstruct %mat2v2half %56 %58
+; CHECK: %52 = OpCopyObject %mat2v2float %36
+; CHECK: %42 = OpMatrixTimesVector %v2half %59 %41
+%43 = OpCompositeExtract %float %42 0
+%44 = OpCompositeExtract %float %42 1
+; CHECK-NOT: %43 = OpCompositeExtract %float %42 0
+; CHECK-NOT: %44 = OpCompositeExtract %float %42 1
+; CHECK: %43 = OpCompositeExtract %half %42 0
+; CHECK: %44 = OpCompositeExtract %half %42 1
+%45 = OpCompositeConstruct %v3float %43 %44 %float_1
+; CHECK-NOT: %45 = OpCompositeConstruct %v3float %43 %44 %float_1
+; CHECK: %53 = OpFConvert %float %43
+; CHECK: %54 = OpFConvert %float %44
+; CHECK: %45 = OpCompositeConstruct %v3float %53 %54 %float_1
+OpStore %res %45
+OpReturn
+OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<ConvertToHalfPass>(defs + func, true);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/dead_branch_elim_test.cpp b/test/opt/dead_branch_elim_test.cpp
index 5d41bdc..e612867 100644
--- a/test/opt/dead_branch_elim_test.cpp
+++ b/test/opt/dead_branch_elim_test.cpp
@@ -2798,7 +2798,9 @@
OpFunctionEnd
)";
- SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, true);
+ // The selection merge in the loop naming the continue target as merge is
+ // invalid, but handled by this pass so validation is disabled.
+ SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, false);
}
TEST_F(DeadBranchElimTest, SelectionMergeWithNestedLoop) {
@@ -2942,7 +2944,9 @@
OpFunctionEnd
)";
- SinglePassRunAndMatch<DeadBranchElimPass>(body, true);
+ // The selection merge in the loop naming the continue target as merge is
+ // invalid, but handled by this pass so validation is disabled.
+ SinglePassRunAndMatch<DeadBranchElimPass>(body, false);
}
TEST_F(DeadBranchElimTest, UnreachableMergeAndContinueSameBlock) {
diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp
index 68a7d18..26d1220 100644
--- a/test/opt/fold_test.cpp
+++ b/test/opt/fold_test.cpp
@@ -3396,7 +3396,17 @@
"%2 = OpCompositeConstruct %v2int %103 %103\n" +
"OpReturn\n" +
"OpFunctionEnd",
- 2, VEC2_0_ID)
+ 2, VEC2_0_ID),
+ // Test case 5: Don't segfault when trying to fold an OpCompositeConstruct
+ // for an empty struct, and we reached the id limit.
+ InstructionFoldingCase<uint32_t>(
+ Header() + "%empty_struct = OpTypeStruct\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%4194303 = OpCompositeConstruct %empty_struct\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 4194303, 0)
));
INSTANTIATE_TEST_SUITE_P(PhiFoldingTest, GeneralInstructionFoldingTest,
@@ -6111,6 +6121,23 @@
"%4 = OpFSub %float %float_2 %3\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
+ 4, true),
+ // Test case 13: merge subtract of subtract with mixed types.
+ // 2 - (1 - x) = x + 1
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
+ "; CHECK: [[int_1:%\\w+]] = OpConstant [[int]] 1\n" +
+ "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" +
+ "; CHECK: %4 = OpIAdd [[int]] [[ld]] [[int_1]]\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 %uint_1 %2\n" +
+ "%4 = OpISub %int %int_2 %3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd\n",
4, true)
));
diff --git a/test/opt/graphics_robust_access_test.cpp b/test/opt/graphics_robust_access_test.cpp
index 58bd404..d38571e 100644
--- a/test/opt/graphics_robust_access_test.cpp
+++ b/test/opt/graphics_robust_access_test.cpp
@@ -271,7 +271,7 @@
%uint_4 = OpConstant %uint 4
)"
<< MainPrefix() << R"(
- %var = OpVariable %var_ty Function)" << ACCheck(ac, "%uint_4", "%uint_3")
+ %var = OpVariable %var_ty Function)" << ACCheck(ac, "%uint_4", "%int_3")
<< MainSuffix();
SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
}
@@ -329,7 +329,7 @@
; CHECK-DAG: %int_0 = OpConstant %int 0
; CHECK-DAG: %int_3 = OpConstant %int 3
; CHECK: OpLabel
- ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_3
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_3
%var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
<< MainSuffix();
SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -354,7 +354,7 @@
; CHECK-DAG: %short_3 = OpConstant %short 3
; CHECK-NOT: = OpTypeInt 32
; CHECK: OpLabel
- ; CHECK: %[[clamp:\w+]] = OpExtInst %short %[[GLSLSTD450]] UClamp %i %short_0 %short_3
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %short %[[GLSLSTD450]] SClamp %i %short_0 %short_3
%var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
<< MainSuffix();
SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -375,11 +375,11 @@
<< MainPrefix() << R"(
; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
; CHECK-NOT: = OpTypeInt 32
- ; CHECK-DAG: %ushort_0 = OpConstant %ushort 0
- ; CHECK-DAG: %ushort_3 = OpConstant %ushort 3
+ ; CHECK-DAG: %short_0 = OpConstant %short 0
+ ; CHECK-DAG: %short_3 = OpConstant %short 3
; CHECK-NOT: = OpTypeInt 32
; CHECK: OpLabel
- ; CHECK: %[[clamp:\w+]] = OpExtInst %ushort %[[GLSLSTD450]] UClamp %i %ushort_0 %ushort_3
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %ushort %[[GLSLSTD450]] SClamp %i %short_0 %short_3
%var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
<< MainSuffix();
SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -404,7 +404,7 @@
; CHECK-DAG: %long_3 = OpConstant %long 3
; CHECK-NOT: = OpTypeInt 32
; CHECK: OpLabel
- ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] UClamp %i %long_0 %long_3
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] SClamp %i %long_0 %long_3
%var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
<< MainSuffix();
SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -425,11 +425,11 @@
<< MainPrefix() << R"(
; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
; CHECK-NOT: = OpTypeInt 32
- ; CHECK-DAG: %ulong_0 = OpConstant %ulong 0
- ; CHECK-DAG: %ulong_3 = OpConstant %ulong 3
+ ; CHECK-DAG: %long_0 = OpConstant %long 0
+ ; CHECK-DAG: %long_3 = OpConstant %long 3
; CHECK-NOT: = OpTypeInt 32
; CHECK: OpLabel
- ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UClamp %i %ulong_0 %ulong_3
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %long_0 %long_3
%var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
<< MainSuffix();
SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -486,10 +486,9 @@
%uint_1 = OpConstant %uint 1
%uint_4 = OpConstant %uint 4
)" << MainPrefix() << R"(
- ; CHECK: %uint_3 = OpConstant %uint 3
+ ; CHECK: %int_3 = OpConstant %int 3
%var = OpVariable %var_ty Function)"
- << ACCheck(ac, "%uint_4 %uint_1", "%uint_3 %uint_1")
- << MainSuffix();
+ << ACCheck(ac, "%uint_4 %uint_1", "%int_3 %uint_1") << MainSuffix();
SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
}
}
@@ -529,7 +528,7 @@
; CHECK-DAG: %int_0 = OpConstant %int 0
; CHECK-DAG: %int_3 = OpConstant %int 3
; CHECK: OpLabel
- ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_3
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_3
%var = OpVariable %var_ty Function)"
<< ACCheck(ac, "%i %uint_1", "%[[clamp]] %uint_1") << MainSuffix();
SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -585,7 +584,7 @@
; CHECK-DAG: %int_0 = OpConstant %int 0
; CHECK-DAG: %int_199 = OpConstant %int 199
; CHECK: OpLabel
- ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_199
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_199
%var = OpVariable %var_ty Function)"
<< ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -606,11 +605,11 @@
%i = OpUndef %short
)" << MainPrefix() << R"(
; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
- ; CHECK-DAG: %uint_0 = OpConstant %uint 0
- ; CHECK-DAG: %uint_69999 = OpConstant %uint 69999
+ ; CHECK-DAG: %int_0 = OpConstant %int 0
+ ; CHECK-DAG: %int_69999 = OpConstant %int 69999
; CHECK: OpLabel
; CHECK: %[[i_ext:\w+]] = OpSConvert %uint %i
- ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %[[i_ext]] %uint_0 %uint_69999
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %int_0 %int_69999
%var = OpVariable %var_ty Function)"
<< ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -631,11 +630,11 @@
%i = OpUndef %ushort
)" << MainPrefix() << R"(
; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
- ; CHECK-DAG: %uint_0 = OpConstant %uint 0
- ; CHECK-DAG: %uint_69999 = OpConstant %uint 69999
+ ; CHECK-DAG: %int_0 = OpConstant %int 0
+ ; CHECK-DAG: %int_69999 = OpConstant %int 69999
; CHECK: OpLabel
; CHECK: %[[i_ext:\w+]] = OpUConvert %uint %i
- ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %[[i_ext]] %uint_0 %uint_69999
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %int_0 %int_69999
%var = OpVariable %var_ty Function)"
<< ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -656,10 +655,10 @@
%i = OpUndef %uint
)" << MainPrefix() << R"(
; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
- ; CHECK-DAG: %uint_0 = OpConstant %uint 0
- ; CHECK-DAG: %uint_199 = OpConstant %uint 199
+ ; CHECK-DAG: %int_0 = OpConstant %int 0
+ ; CHECK-DAG: %int_199 = OpConstant %int 199
; CHECK: OpLabel
- ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %i %uint_0 %uint_199
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %i %int_0 %int_199
%var = OpVariable %var_ty Function)"
<< ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -683,7 +682,7 @@
; CHECK-DAG: %int_0 = OpConstant %int 0
; CHECK-DAG: %int_199 = OpConstant %int 199
; CHECK: OpLabel
- ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_199
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_199
%var = OpVariable %var_ty Function)"
<< ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -707,7 +706,7 @@
; CHECK-DAG: %long_0 = OpConstant %long 0
; CHECK-DAG: %long_199 = OpConstant %long 199
; CHECK: OpLabel
- ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] UClamp %i %long_0 %long_199
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] SClamp %i %long_0 %long_199
%var = OpVariable %var_ty Function)"
<< ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -728,16 +727,91 @@
%i = OpUndef %ulong
)" << MainPrefix() << R"(
; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
- ; CHECK-DAG: %ulong_0 = OpConstant %ulong 0
- ; CHECK-DAG: %ulong_199 = OpConstant %ulong 199
+ ; CHECK-DAG: %long_0 = OpConstant %long 0
+ ; CHECK-DAG: %long_199 = OpConstant %long 199
; CHECK: OpLabel
- ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UClamp %i %ulong_0 %ulong_199
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %long_0 %long_199
%var = OpVariable %var_ty Function)"
<< ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
}
}
+TEST_F(GraphicsRobustAccessTest,
+ ACArrayGeneralShortIndeArrayBiggerThanShortMaxClipsToShortIntMax) {
+ for (auto* ac : AccessChains()) {
+ std::ostringstream shaders;
+ shaders << "OpCapability Int16\n"
+ << ShaderPreambleAC({"i"}) << TypesVoid() << TypesShort()
+ << TypesInt() << TypesFloat() << R"(
+ %uint_50000 = OpConstant %uint 50000
+ %arr = OpTypeArray %float %uint_50000
+ %var_ty = OpTypePointer Function %arr
+ %ptr_ty = OpTypePointer Function %float
+ %i = OpUndef %ushort
+ )" << MainPrefix() << R"(
+ ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
+ ; CHECK-DAG: %short_0 = OpConstant %short 0
+ ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %short 32767
+ ; CHECK: OpLabel
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %ushort %[[GLSLSTD450]] SClamp %i %short_0 %[[intmax]]
+ %var = OpVariable %var_ty Function)"
+ << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
+ SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
+ }
+}
+
+TEST_F(GraphicsRobustAccessTest,
+ ACArrayGeneralIntIndexArrayBiggerThanIntMaxClipsToSignedIntMax) {
+ for (auto* ac : AccessChains()) {
+ std::ostringstream shaders;
+ shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
+ << TypesFloat() << R"(
+ %uint_3000000000 = OpConstant %uint 3000000000
+ %arr = OpTypeArray %float %uint_3000000000
+ %var_ty = OpTypePointer Function %arr
+ %ptr_ty = OpTypePointer Function %float
+ %i = OpUndef %uint
+ )" << MainPrefix() << R"(
+ ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
+ ; CHECK-DAG: %int_0 = OpConstant %int 0
+ ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
+ ; CHECK: OpLabel
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %i %int_0 %[[intmax]]
+ %var = OpVariable %var_ty Function)"
+ << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
+ SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
+ }
+}
+
+TEST_F(GraphicsRobustAccessTest,
+ ACArrayGeneralLongIndexArrayBiggerThanLongMaxClipsToSignedLongMax) {
+ for (auto* ac : AccessChains()) {
+ std::ostringstream shaders;
+ shaders << "OpCapability Int64\n"
+ << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
+ << TypesLong()
+ << TypesFloat()
+ // 2^63 == 9,223,372,036,854,775,807
+ << R"(
+ %ulong_9223372036854775999 = OpConstant %ulong 9223372036854775999
+ %arr = OpTypeArray %float %ulong_9223372036854775999
+ %var_ty = OpTypePointer Function %arr
+ %ptr_ty = OpTypePointer Function %float
+ %i = OpUndef %ulong
+ )"
+ << MainPrefix() << R"(
+ ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
+ ; CHECK-DAG: %long_0 = OpConstant %long 0
+ ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %long 9223372036854775807
+ ; CHECK: OpLabel
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %long_0 %[[intmax]]
+ %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
+ << MainSuffix();
+ SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
+ }
+}
+
TEST_F(GraphicsRobustAccessTest, ACArraySpecIdSizedAlwaysClamped) {
for (auto* ac : AccessChains()) {
std::ostringstream shaders;
@@ -753,9 +827,11 @@
; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
; CHECK-DAG: %uint_0 = OpConstant %uint 0
; CHECK-DAG: %uint_1 = OpConstant %uint 1
+ ; CHECK-DAG: %[[uint_intmax:\w+]] = OpConstant %uint 2147483647
; CHECK: OpLabel
; CHECK: %[[max:\w+]] = OpISub %uint %spec200 %uint_1
- ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %uint_5 %uint_0 %[[max]]
+ ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[uint_intmax]]
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %uint_5 %uint_0 %[[smin]]
%var = OpVariable %var_ty Function)"
<< ACCheck(ac, "%uint_5", "%[[clamp]]") << MainSuffix();
SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -908,11 +984,13 @@
%int_0 = OpConstant %int 0
%int_2 = OpConstant %int 2
; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
- ; CHECK: %int_1 = OpConstant %int 1
+ ; CHECK-DAG: %int_1 = OpConstant %int 1
+ ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
; CHECK: OpLabel
; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
- ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %int_0 %int_0 %[[max]]
+ ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %int_0 %int_0 %[[smin]]
)"
<< MainPrefix() << ACCheck(ac, "%int_2 %int_0", "%int_2 %[[clamp]]")
<< MainSuffix();
@@ -937,11 +1015,13 @@
; CHECK: %uint = OpTypeInt 32 0
; CHECK-DAG: %uint_1 = OpConstant %uint 1
; CHECK-DAG: %uint_0 = OpConstant %uint 0
+ ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %uint 2147483647
; CHECK: OpLabel
; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
; CHECK-DAG: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1
; CHECK-DAG: %[[i_ext:\w+]] = OpSConvert %uint %i
- ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %[[i_ext]] %uint_0 %[[max]]
+ ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %uint_0 %[[smin]]
)"
<< MainPrefix() << ACCheck(ac, "%short_2 %i", "%short_2 %[[clamp]]")
<< MainSuffix();
@@ -966,11 +1046,13 @@
; CHECK: %uint = OpTypeInt 32 0
; CHECK-DAG: %uint_1 = OpConstant %uint 1
; CHECK-DAG: %uint_0 = OpConstant %uint 0
+ ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %uint 2147483647
; CHECK: OpLabel
; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
; CHECK-DAG: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1
; CHECK-DAG: %[[i_ext:\w+]] = OpSConvert %uint %i
- ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %[[i_ext]] %uint_0 %[[max]]
+ ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %uint_0 %[[smin]]
)"
<< MainPrefix() << ACCheck(ac, "%short_2 %i", "%short_2 %[[clamp]]")
<< MainSuffix();
@@ -993,10 +1075,12 @@
; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
; CHECK-DAG: %int_1 = OpConstant %int 1
; CHECK-DAG: %int_0 = OpConstant %int 0
+ ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
; CHECK: OpLabel
; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
- ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %[[max]]
+ ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %[[smin]]
)"
<< MainPrefix() << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]")
<< MainSuffix();
@@ -1019,10 +1103,12 @@
; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
; CHECK-DAG: %uint_1 = OpConstant %uint 1
; CHECK-DAG: %uint_0 = OpConstant %uint 0
+ ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %uint 2147483647
; CHECK: OpLabel
; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
; CHECK: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1
- ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %i %uint_0 %[[max]]
+ ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %i %uint_0 %[[smin]]
)"
<< MainPrefix() << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]")
<< MainSuffix();
@@ -1046,11 +1132,13 @@
; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
; CHECK-DAG: %long_0 = OpConstant %long 0
; CHECK-DAG: %long_1 = OpConstant %long 1
+ ; CHECK-DAG: %[[longmax:\w+]] = OpConstant %long 9223372036854775807
; CHECK: OpLabel
; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
; CHECK: %[[arrlen_ext:\w+]] = OpUConvert %ulong %[[arrlen]]
; CHECK: %[[max:\w+]] = OpISub %long %[[arrlen_ext]] %long_1
- ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] UClamp %i %long_0 %[[max]]
+ ; CHECK: %[[smin:\w+]] = OpExtInst %long %[[GLSLSTD450]] UMin %[[max]] %[[longmax]]
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] SClamp %i %long_0 %[[smin]]
)" << MainPrefix()
<< ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") << MainSuffix();
SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -1073,11 +1161,13 @@
; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
; CHECK-DAG: %ulong_0 = OpConstant %ulong 0
; CHECK-DAG: %ulong_1 = OpConstant %ulong 1
+ ; CHECK-DAG: %[[longmax:\w+]] = OpConstant %ulong 9223372036854775807
; CHECK: OpLabel
; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
; CHECK: %[[arrlen_ext:\w+]] = OpUConvert %ulong %[[arrlen]]
; CHECK: %[[max:\w+]] = OpISub %ulong %[[arrlen_ext]] %ulong_1
- ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UClamp %i %ulong_0 %[[max]]
+ ; CHECK: %[[smin:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UMin %[[max]] %[[longmax]]
+ ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %ulong_0 %[[smin]]
)" << MainPrefix()
<< ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") << MainSuffix();
SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -1109,11 +1199,13 @@
; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
; CHECK-DAG: %int_0 = OpConstant %int 0
; CHECK-DAG: %int_3 = OpConstant %int 3
+ ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
; CHECK: OpLabel
; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
- ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %[[max]]
- ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %j %int_0 %int_3
+ ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+ ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %[[smin]]
+ ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %j %int_0 %int_3
)" << MainPrefix()
<< ACCheck(ac, "%int_2 %i %int_1 %j",
"%int_2 %[[clamp_i]] %int_1 %[[clamp_j]]")
@@ -1148,6 +1240,7 @@
; CHECK-DAG: %[[ssbo_p:\w+]] = OpTypePointer Uniform %ssbo_s
; CHECK-DAG: %int_0 = OpConstant %int 0
; 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.
; Note that the %int_9 is already clamped
@@ -1155,7 +1248,8 @@
<< R"( %[[ssbo_p]] %var %int_9
; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %[[ssbo_base]] 2
; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
- ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %[[max]]
+ ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+ ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %[[smin]]
)" << MainPrefix()
<< ACCheck(ac, "%int_17 %int_2 %i %int_1 %int_2",
"%int_9 %int_2 %[[clamp_i]] %int_1 %int_2")
@@ -1195,8 +1289,9 @@
; CHECK-DAG: %int_0 = OpConstant %int 0
; CHECK-DAG: %int_9 = OpConstant %int 9
; CHECK-DAG: %int_3 = OpConstant %int 3
+ ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
; CHECK: OpLabel
- ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_9
+ ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_9
; CHECK: %ac_ssbo = )" << ac
<< R"( %ssbo_pty %var %[[clamp_i]]
; CHECK: %ac_rtarr = )"
@@ -1207,8 +1302,9 @@
; definition to find the base pointer %ac_ssbo.
; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %ac_ssbo 2
; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
- ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %j %int_0 %[[max]]
- ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %k %int_0 %int_3
+ ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+ ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %j %int_0 %[[smin]]
+ ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %k %int_0 %int_3
; CHECK: %ac = )" << ac
<< R"( %ptr_ty %ac_rtarr %[[clamp_j]] %int_1 %[[clamp_k]]
; CHECK-NOT: AccessChain
@@ -1258,8 +1354,9 @@
; CHECK-DAG: %int_0 = OpConstant %int 0
; CHECK-DAG: %int_9 = OpConstant %int 9
; CHECK-DAG: %int_3 = OpConstant %int 3
+ ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
; CHECK: OpLabel
- ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_9
+ ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_9
; CHECK: %ac_ssbo = )" << ac
<< R"( %ssbo_pty %var %[[clamp_i]]
; CHECK: %bb1 = OpLabel
@@ -1272,8 +1369,9 @@
; definition to find the base pointer %ac_ssbo.
; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %ac_ssbo 2
; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
- ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %j %int_0 %[[max]]
- ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %k %int_0 %int_3
+ ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+ ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %j %int_0 %[[smin]]
+ ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %k %int_0 %int_3
; CHECK: %ac = )" << ac
<< R"( %ptr_ty %ac_rtarr %[[clamp_j]] %int_1 %[[clamp_k]]
; CHECK-NOT: AccessChain
diff --git a/test/opt/inline_test.cpp b/test/opt/inline_test.cpp
index fac49ca..f44c04a 100644
--- a/test/opt/inline_test.cpp
+++ b/test/opt/inline_test.cpp
@@ -1819,7 +1819,7 @@
%9 = OpLabel
OpBranch %10
%10 = OpLabel
-OpLoopMerge %12 %13 None
+OpLoopMerge %12 %10 None
OpBranch %13
%13 = OpLabel
OpBranchConditional %true %10 %12
@@ -1980,7 +1980,7 @@
OpBranch %13
%13 = OpLabel
%14 = OpCopyObject %bool %false
-OpLoopMerge %16 %19 None
+OpLoopMerge %16 %13 None
OpBranch %17
%17 = OpLabel
%18 = OpCopyObject %bool %true
@@ -2145,7 +2145,7 @@
%19 = OpLabel
%20 = OpCopyObject %int %int_2
%25 = OpCopyObject %int %int_0
-OpLoopMerge %23 %26 None
+OpLoopMerge %23 %19 None
OpBranch %26
%27 = OpLabel
%28 = OpCopyObject %int %int_1
diff --git a/test/opt/inst_bindless_check_test.cpp b/test/opt/inst_bindless_check_test.cpp
index b4db2d5..d867b01 100644
--- a/test/opt/inst_bindless_check_test.cpp
+++ b/test/opt/inst_bindless_check_test.cpp
@@ -14,7 +14,6 @@
// limitations under the License.
// Bindless Check Instrumentation Tests.
-// Tests ending with V2 use version 2 record format.
#include <string>
#include <vector>
@@ -29,235 +28,6 @@
using InstBindlessTest = PassTest<::testing::Test>;
-TEST_F(InstBindlessTest, Simple) {
- // Texture2D g_tColor[128];
- //
- // layout(push_constant) cbuffer PerViewConstantBuffer_t
- // {
- // uint g_nDataIdx;
- // };
- //
- // SamplerState g_sAniso;
- //
- // struct PS_INPUT
- // {
- // float2 vTextureCoords : TEXCOORD2;
- // };
- //
- // struct PS_OUTPUT
- // {
- // float4 vColor : SV_Target0;
- // };
- //
- // PS_OUTPUT MainPs(PS_INPUT i)
- // {
- // PS_OUTPUT ps_output;
- // ps_output.vColor =
- // g_tColor[ g_nDataIdx ].Sample(g_sAniso, i.vTextureCoords.xy);
- // return ps_output;
- // }
-
- const std::string entry_before =
- R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-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"
-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
-OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %g_sAniso DescriptorSet 0
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-)";
-
- 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
-%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_128 = OpConstant %uint 128
-%_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
-%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
-)";
-
- 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_9 = OpConstant %uint 9
-%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_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_56 = OpConstant %uint 56
-%103 = OpConstantNull %v4float
-)";
-
- const std::string func_pt1 =
- 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
-)";
-
- const std::string func_pt2_before =
- R"(%37 = OpImageSampleImplicitLod %v4float %36 %30
-OpStore %_entryPointOutput_vColor %37
-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_9
-%63 = OpIAdd %uint %62 %uint_9
-%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_9
-%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_6
-%94 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %93
-OpStore %94 %50
-%96 = OpIAdd %uint %62 %uint_7
-%97 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %96
-OpStore %97 %51
-%99 = OpIAdd %uint %62 %uint_8
-%100 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %99
-OpStore %100 %52
-OpBranch %66
-%66 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- 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, 1u);
-}
-
TEST_F(InstBindlessTest, NoInstrumentConstIndexInbounds) {
// Texture2D g_tColor[128];
//
@@ -336,1872 +106,7 @@
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(before, before, true, true, 7u,
- 23u, false, false, 1u);
-}
-
-TEST_F(InstBindlessTest, InstrumentMultipleInstructions) {
- // Texture2D g_tColor[128];
- //
- // layout(push_constant) cbuffer PerViewConstantBuffer_t
- // {
- // uint g_nDataIdx;
- // uint g_nDataIdx2;
- // };
- //
- // SamplerState g_sAniso;
- //
- // struct PS_INPUT
- // {
- // float2 vTextureCoords : TEXCOORD2;
- // };
- //
- // struct PS_OUTPUT
- // {
- // float4 vColor : SV_Target0;
- // };
- //
- // PS_OUTPUT MainPs(PS_INPUT i)
- // {
- // PS_OUTPUT ps_output;
- //
- // float t = g_tColor[g_nDataIdx ].Sample(g_sAniso, i.vTextureCoords.xy);
- // float t2 = g_tColor[g_nDataIdx2].Sample(g_sAniso, i.vTextureCoords.xy);
- // ps_output.vColor = t + t2;
- // return ps_output;
- // }
-
- const std::string defs_before =
- R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-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
-%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
-)";
-
- 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_9 = OpConstant %uint 9
-%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_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_58 = OpConstant %uint 58
-%111 = OpConstantNull %v4float
-%uint_64 = OpConstant %uint 64
-)";
-
- const std::string func_before =
- 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
-%38 = OpImageSampleImplicitLod %v4float %37 %31
-%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
-%44 = OpImageSampleImplicitLod %v4float %43 %31
-%45 = OpFAdd %v4float %38 %44
-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_9
-%71 = OpIAdd %uint %70 %uint_9
-%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_9
-%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_6
-%102 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %101
-OpStore %102 %58
-%104 = OpIAdd %uint %70 %uint_7
-%105 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %104
-OpStore %105 %59
-%107 = OpIAdd %uint %70 %uint_8
-%108 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %107
-OpStore %108 %60
-OpBranch %74
-%74 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + output_func, true,
- true, 7u, 23u, false, false, 1u);
-}
-
-TEST_F(InstBindlessTest, InstrumentOpImage) {
- // This test verifies that the pass will correctly instrument shader
- // using OpImage. This test was created by editing the SPIR-V
- // from the Simple test.
-
- const std::string defs_before =
- R"(OpCapability Shader
-OpCapability StorageImageReadWithoutFormat
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-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
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%int = OpTypeInt 32 1
-%v2int = OpTypeVector %int 2
-%int_0 = OpConstant %int 0
-%20 = OpTypeImage %float 2D 0 0 0 0 Unknown
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%39 = OpTypeSampledImage %20
-%_arr_39_uint_128 = OpTypeArray %39 %uint_128
-%_ptr_UniformConstant__arr_39_uint_128 = OpTypePointer UniformConstant %_arr_39_uint_128
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_39_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_39 = OpTypePointer UniformConstant %39
-%_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
-)";
-
- 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_9 = OpConstant %uint 9
-%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_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_51 = OpConstant %uint 51
-%99 = OpConstantNull %v4float
-)";
-
- const std::string func_before =
- R"(%MainPs = OpFunction %void None %3
-%5 = OpLabel
-%53 = OpLoad %v2int %i_vTextureCoords
-%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%64 = OpLoad %uint %63
-%65 = OpAccessChain %_ptr_UniformConstant_39 %g_tColor %64
-%66 = OpLoad %39 %65
-%75 = OpImage %20 %66
-%71 = OpImageRead %v4float %75 %53
-OpStore %_entryPointOutput_vColor %71
-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 =
- 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_9
-%59 = OpIAdd %uint %58 %uint_9
-%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_9
-%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_6
-%90 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %89
-OpStore %90 %46
-%92 = OpIAdd %uint %58 %uint_7
-%93 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %92
-OpStore %93 %47
-%95 = OpIAdd %uint %58 %uint_8
-%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<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + output_func, true,
- true, 7u, 23u, false, false, 1u);
-}
-
-TEST_F(InstBindlessTest, InstrumentSampledImage) {
- // This test verifies that the pass will correctly instrument shader
- // using sampled image. This test was created by editing the SPIR-V
- // from the Simple test.
-
- const std::string defs_before =
- R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-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
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%v4float = OpTypeVector %float 4
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%20 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%39 = OpTypeSampledImage %20
-%_arr_39_uint_128 = OpTypeArray %39 %uint_128
-%_ptr_UniformConstant__arr_39_uint_128 = OpTypePointer UniformConstant %_arr_39_uint_128
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_39_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_39 = OpTypePointer UniformConstant %39
-%_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
-)";
-
- 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_9 = OpConstant %uint 9
-%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_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_49 = OpConstant %uint 49
-%97 = OpConstantNull %v4float
-)";
-
- const std::string func_before =
- R"(%MainPs = OpFunction %void None %3
-%5 = OpLabel
-%53 = OpLoad %v2float %i_vTextureCoords
-%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%64 = OpLoad %uint %63
-%65 = OpAccessChain %_ptr_UniformConstant_39 %g_tColor %64
-%66 = OpLoad %39 %65
-%71 = OpImageSampleImplicitLod %v4float %66 %53
-OpStore %_entryPointOutput_vColor %71
-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 =
- 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_9
-%57 = OpIAdd %uint %56 %uint_9
-%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_9
-%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_6
-%88 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %87
-OpStore %88 %44
-%90 = OpIAdd %uint %56 %uint_7
-%91 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %90
-OpStore %91 %45
-%93 = OpIAdd %uint %56 %uint_8
-%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<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + output_func, true,
- true, 7u, 23u, false, false, 1u);
-}
-
-TEST_F(InstBindlessTest, InstrumentImageWrite) {
- // This test verifies that the pass will correctly instrument shader
- // 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
-OpCapability StorageImageWriteWithoutFormat
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-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
-%void = OpTypeVoid
-%3 = 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
-%20 = OpTypeImage %float 2D 0 0 0 0 Unknown
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%80 = OpConstantNull %v4float
-%_arr_20_uint_128 = OpTypeArray %20 %uint_128
-%_ptr_UniformConstant__arr_20_uint_128 = OpTypePointer UniformConstant %_arr_20_uint_128
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_20_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_20 = OpTypePointer UniformConstant %20
-%_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
-)";
-
- 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_9 = OpConstant %uint 9
-%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_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_51 = OpConstant %uint 51
-)";
-
- const std::string func_before =
- R"(%MainPs = OpFunction %void None %3
-%5 = OpLabel
-%53 = OpLoad %v2int %i_vTextureCoords
-%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%64 = OpLoad %uint %63
-%65 = OpAccessChain %_ptr_UniformConstant_20 %g_tColor %64
-%66 = OpLoad %20 %65
-OpImageWrite %66 %53 %80
-OpStore %_entryPointOutput_vColor %80
-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_9
-%56 = OpIAdd %uint %55 %uint_9
-%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_9
-%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_6
-%87 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %86
-OpStore %87 %43
-%89 = OpIAdd %uint %55 %uint_7
-%90 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %89
-OpStore %90 %44
-%92 = OpIAdd %uint %55 %uint_8
-%93 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %92
-OpStore %93 %45
-OpBranch %59
-%59 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + output_func, true,
- true, 7u, 23u, false, false, 1u);
-}
-
-TEST_F(InstBindlessTest, InstrumentVertexSimple) {
- // This test verifies that the pass will correctly instrument shader
- // 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
-OpCapability Sampled1D
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Vertex %main "main" %_ %coords2D
-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
-%void = OpTypeVoid
-%3 = 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
-%21 = OpTypeImage %float 1D 0 0 0 1 Unknown
-%22 = OpTypeSampledImage %21
-%uint_128 = OpConstant %uint 128
-%_arr_22_uint_128 = OpTypeArray %22 %uint_128
-%_ptr_UniformConstant__arr_22_uint_128 = OpTypePointer UniformConstant %_arr_22_uint_128
-%texSampler1D = OpVariable %_ptr_UniformConstant__arr_22_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_22 = OpTypePointer UniformConstant %22
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%v2float = OpTypeVector %float 2
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%coords2D = OpVariable %_ptr_Input_v2float Input
-)";
-
- 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_9 = OpConstant %uint 9
-%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_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_74 = OpConstant %uint 74
-%106 = OpConstantNull %v4float
-)";
-
- const std::string func_before =
- R"(%main = OpFunction %void None %3
-%5 = OpLabel
-%lod = OpVariable %_ptr_Function_float Function
-%coords1D = OpVariable %_ptr_Function_float Function
-OpStore %lod %float_3
-OpStore %coords1D %float_1_78900003
-%31 = OpAccessChain %_ptr_Uniform_int %__0 %int_0
-%32 = OpLoad %int %31
-%34 = OpAccessChain %_ptr_UniformConstant_22 %texSampler1D %32
-%35 = OpLoad %22 %34
-%36 = OpLoad %float %coords1D
-%37 = OpLoad %float %lod
-%38 = OpImageSampleExplicitLod %v4float %35 %36 Lod %37
-%40 = OpAccessChain %_ptr_Output_v4float %_ %int_0
-OpStore %40 %38
-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_9
-%69 = OpIAdd %uint %68 %uint_9
-%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_9
-%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_6
-%97 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %96
-OpStore %97 %56
-%99 = OpIAdd %uint %68 %uint_7
-%100 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %99
-OpStore %100 %57
-%102 = OpIAdd %uint %68 %uint_8
-%103 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %102
-OpStore %103 %58
-OpBranch %72
-%72 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + output_func, true,
- true, 7u, 23u, false, false, 1u);
-}
-
-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
-%2 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-OpExecutionMode %MainPs OriginUpperLeft
-%1 = OpString "foo5.frag"
-OpSource HLSL 500 %1
-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
-%void = OpTypeVoid
-%4 = 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
-%13 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
-%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%21 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%_arr_21_uint_128 = OpTypeArray %21 %uint_128
-%_ptr_UniformConstant__arr_21_uint_128 = OpTypePointer UniformConstant %_arr_21_uint_128
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_21_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_21 = OpTypePointer UniformConstant %21
-%36 = OpTypeSampler
-%_ptr_UniformConstant_36 = OpTypePointer UniformConstant %36
-%g_sAniso = OpVariable %_ptr_UniformConstant_36 UniformConstant
-%40 = OpTypeSampledImage %21
-%_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
-)";
-
- 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_9 = OpConstant %uint 9
-%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_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_93 = OpConstant %uint 93
-%125 = OpConstantNull %v4float
-)";
-
- const std::string func1_before =
- R"(%MainPs = OpFunction %void None %4
-%6 = OpLabel
-%i_0 = OpVariable %_ptr_Function_PS_INPUT Function
-%param = OpVariable %_ptr_Function_PS_INPUT Function
-OpLine %1 21 0
-%54 = OpLoad %v2float %i_vTextureCoords
-%55 = OpAccessChain %_ptr_Function_v2float %i_0 %int_0
-OpStore %55 %54
-%59 = OpLoad %PS_INPUT %i_0
-OpStore %param %59
-%60 = OpFunctionCall %PS_OUTPUT %_MainPs_struct_PS_INPUT_vf21_ %param
-%61 = OpCompositeExtract %v4float %60 0
-OpStore %_entryPointOutput_vColor %61
-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
-%i = OpFunctionParameter %_ptr_Function_PS_INPUT
-%16 = OpLabel
-%ps_output = OpVariable %_ptr_Function_PS_OUTPUT Function
-OpLine %1 24 0
-%31 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%32 = OpLoad %uint %31
-%34 = OpAccessChain %_ptr_UniformConstant_21 %g_tColor %32
-%35 = OpLoad %21 %34
-%39 = OpLoad %36 %g_sAniso
-%41 = OpSampledImage %40 %35 %39
-%43 = OpAccessChain %_ptr_Function_v2float %i %int_0
-%44 = OpLoad %v2float %43
-%45 = OpImageSampleImplicitLod %v4float %41 %44
-%47 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0
-OpStore %47 %45
-OpLine %1 25 0
-%48 = OpLoad %PS_OUTPUT %ps_output
-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
-%62 = OpULessThan %bool %50 %uint_128
-OpSelectionMerge %63 None
-OpBranchConditional %62 %64 %65
-%64 = OpLabel
-%66 = OpLoad %27 %51
-%67 = OpSampledImage %37 %66 %53
-%68 = OpImageSampleImplicitLod %v4float %67 %56
-OpBranch %63
-%65 = OpLabel
-%124 = OpFunctionCall %void %69 %uint_93 %uint_0 %50 %uint_128
-OpBranch %63
-%63 = OpLabel
-%126 = OpPhi %v4float %68 %64 %125 %65
-%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_9
-%85 = OpIAdd %uint %84 %uint_9
-%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_9
-%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_6
-%116 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %115
-OpStore %116 %72
-%118 = OpIAdd %uint %84 %uint_7
-%119 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %118
-OpStore %119 %73
-%121 = OpIAdd %uint %84 %uint_8
-%122 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %121
-OpStore %122 %74
-OpBranch %88
-%88 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func1_before + func2_before,
- defs_after + func1_after + func2_after + output_func, true, true, 7u, 23u,
- false, false, 1u);
-}
-
-TEST_F(InstBindlessTest, RuntimeArray) {
- // This test verifies that the pass will correctly instrument shader
- // 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
-OpCapability RuntimeDescriptorArray
-OpExtension "SPV_EXT_descriptor_indexing"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-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
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%v4float = OpTypeVector %float 4
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%20 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%uint = OpTypeInt 32 0
-%uint_1 = OpConstant %uint 1
-%_rarr_20 = OpTypeRuntimeArray %20
-%_ptr_UniformConstant__arr_20 = OpTypePointer UniformConstant %_rarr_20
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_20 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_20 = OpTypePointer UniformConstant %20
-%35 = OpTypeSampler
-%_ptr_UniformConstant_35 = OpTypePointer UniformConstant %35
-%g_sAniso = OpVariable %_ptr_UniformConstant_35 UniformConstant
-%39 = OpTypeSampledImage %20
-%_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
-)";
-
- 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_9 = OpConstant %uint 9
-%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_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%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
-%5 = OpLabel
-%53 = OpLoad %v2float %i_vTextureCoords
-%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%64 = OpLoad %uint %63
-%65 = OpAccessChain %_ptr_UniformConstant_20 %g_tColor %64
-%66 = OpLoad %20 %65
-%67 = OpLoad %35 %g_sAniso
-%68 = OpSampledImage %39 %66 %67
-%71 = OpImageSampleImplicitLod %v4float %68 %53
-OpStore %_entryPointOutput_vColor %71
-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 = OpINotEqual %bool %136 %uint_0
-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_9
-%78 = OpIAdd %uint %77 %uint_9
-%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_9
-%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_6
-%107 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %106
-OpStore %107 %67
-%109 = OpIAdd %uint %77 %uint_7
-%110 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %109
-OpStore %110 %68
-%112 = OpIAdd %uint %77 %uint_8
-%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
-)";
-
- // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true);
+ 23u, false, false);
}
TEST_F(InstBindlessTest, NoInstrumentNonBindless) {
@@ -2278,1960 +183,10 @@
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(whole_file, whole_file, true,
- true, 7u, 23u, false, false, 1u);
+ true, 7u, 23u, false, false);
}
-TEST_F(InstBindlessTest, InstrumentInitCheckOnScalarDescriptor) {
- // This test verifies that the pass will correctly instrument vanilla
- // texture sample on a scalar descriptor with an initialization check if the
- // input_init_enable argument is set to true. This can happen when the
- // descriptor indexing extension is enabled in the API but the SPIR-V
- // 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
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-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
-%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
-)";
-
- 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_9 = OpConstant %uint 9
-%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_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_39 = OpConstant %uint 39
-%113 = OpConstantNull %v4float
-)";
-
- const std::string func_before =
- 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
-%24 = OpImageSampleImplicitLod %v4float %23 %20
-OpStore %_entryPointOutput_vColor %24
-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 = OpINotEqual %bool %50 %uint_0
-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_9
-%74 = OpIAdd %uint %73 %uint_9
-%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_9
-%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_6
-%104 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %103
-OpStore %104 %63
-%106 = OpIAdd %uint %73 %uint_7
-%107 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %106
-OpStore %107 %64
-%109 = OpIAdd %uint %73 %uint_8
-%110 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %109
-OpStore %110 %65
-OpBranch %77
-%77 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true);
-}
-
-TEST_F(InstBindlessTest, SPV14AddToEntryPoint) {
- const std::string text = R"(
-; CHECK: OpEntryPoint Fragment {{%\w+}} "foo" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]]
-; CHECK: OpDecorate [[v1]] DescriptorSet 7
-; CHECK: OpDecorate [[v2]] DescriptorSet 7
-; CHECK: [[v1]] = OpVariable {{%\w+}} StorageBuffer
-; CHECK: [[v2]] = OpVariable {{%\w+}} StorageBuffer
-OpCapability Shader
-OpExtension "SPV_EXT_descriptor_indexing"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %foo "foo" %gid %image_var %sampler_var
-OpExecutionMode %foo OriginUpperLeft
-OpDecorate %image_var DescriptorSet 0
-OpDecorate %image_var Binding 0
-OpDecorate %sampler_var DescriptorSet 0
-OpDecorate %sampler_var Binding 1
-OpDecorate %gid DescriptorSet 0
-OpDecorate %gid Binding 2
-OpDecorate %struct Block
-OpMemberDecorate %struct 0 Offset 0
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_0 = OpConstant %int 0
-%v3int = OpTypeVector %int 3
-%float = OpTypeFloat 32
-%v3float = OpTypeVector %float 3
-%v4float = OpTypeVector %float 4
-%struct = OpTypeStruct %v3int
-%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct
-%ptr_ssbo_v3int = OpTypePointer StorageBuffer %v3int
-%gid = OpVariable %ptr_ssbo_struct StorageBuffer
-%image = OpTypeImage %float 3D 0 0 0 1 Unknown
-%ptr_uc_image = OpTypePointer UniformConstant %image
-%sampler = OpTypeSampler
-%ptr_uc_sampler = OpTypePointer UniformConstant %sampler
-%image_var = OpVariable %ptr_uc_image UniformConstant
-%sampler_var = OpVariable %ptr_uc_sampler UniformConstant
-%sampled = OpTypeSampledImage %image
-%void_fn = OpTypeFunction %void
-%foo = OpFunction %void None %void_fn
-%entry = OpLabel
-%ld_image = OpLoad %image %image_var
-%ld_sampler = OpLoad %sampler %sampler_var
-%gep = OpAccessChain %ptr_ssbo_v3int %gid %int_0
-%ld_gid = OpLoad %v3int %gep
-%convert = OpConvertUToF %v3float %ld_gid
-%sampled_image = OpSampledImage %sampled %ld_image %ld_sampler
-%sample = OpImageSampleImplicitLod %v4float %sampled_image %convert
-OpReturn
-OpFunctionEnd
-)";
-
- SetTargetEnv(SPV_ENV_VULKAN_1_1_SPIRV_1_4);
- SinglePassRunAndMatch<InstBindlessCheckPass>(text, true);
-}
-
-TEST_F(InstBindlessTest, SPV14AddToEntryPoints) {
- const std::string text = R"(
-; CHECK: OpEntryPoint Fragment {{%\w+}} "foo" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]]
-; CHECK: OpEntryPoint Fragment {{%\w+}} "bar" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]]
-; CHECK: OpDecorate [[v1]] DescriptorSet 7
-; CHECK: OpDecorate [[v2]] DescriptorSet 7
-; CHECK: [[v1]] = OpVariable {{%\w+}} StorageBuffer
-; CHECK: [[v2]] = OpVariable {{%\w+}} StorageBuffer
-OpCapability Shader
-OpExtension "SPV_EXT_descriptor_indexing"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %foo "foo" %gid %image_var %sampler_var
-OpEntryPoint Fragment %foo "bar" %gid %image_var %sampler_var
-OpExecutionMode %foo OriginUpperLeft
-OpDecorate %image_var DescriptorSet 0
-OpDecorate %image_var Binding 0
-OpDecorate %sampler_var DescriptorSet 0
-OpDecorate %sampler_var Binding 1
-OpDecorate %gid DescriptorSet 0
-OpDecorate %gid Binding 2
-OpDecorate %struct Block
-OpMemberDecorate %struct 0 Offset 0
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_0 = OpConstant %int 0
-%v3int = OpTypeVector %int 3
-%float = OpTypeFloat 32
-%v3float = OpTypeVector %float 3
-%v4float = OpTypeVector %float 4
-%struct = OpTypeStruct %v3int
-%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct
-%ptr_ssbo_v3int = OpTypePointer StorageBuffer %v3int
-%gid = OpVariable %ptr_ssbo_struct StorageBuffer
-%image = OpTypeImage %float 3D 0 0 0 1 Unknown
-%ptr_uc_image = OpTypePointer UniformConstant %image
-%sampler = OpTypeSampler
-%ptr_uc_sampler = OpTypePointer UniformConstant %sampler
-%image_var = OpVariable %ptr_uc_image UniformConstant
-%sampler_var = OpVariable %ptr_uc_sampler UniformConstant
-%sampled = OpTypeSampledImage %image
-%void_fn = OpTypeFunction %void
-%foo = OpFunction %void None %void_fn
-%entry = OpLabel
-%ld_image = OpLoad %image %image_var
-%ld_sampler = OpLoad %sampler %sampler_var
-%gep = OpAccessChain %ptr_ssbo_v3int %gid %int_0
-%ld_gid = OpLoad %v3int %gep
-%convert = OpConvertUToF %v3float %ld_gid
-%sampled_image = OpSampledImage %sampled %ld_image %ld_sampler
-%sample = OpImageSampleImplicitLod %v4float %sampled_image %convert
-OpReturn
-OpFunctionEnd
-)";
-
- SetTargetEnv(SPV_ENV_VULKAN_1_1_SPIRV_1_4);
- SinglePassRunAndMatch<InstBindlessCheckPass>(text, true);
-}
-
-TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedUBOArray) {
- // #version 450
- // #extension GL_EXT_nonuniform_qualifier : enable
- //
- // layout(location=0) in nonuniformEXT flat int nu_ii;
- // layout(location=0) out float b;
- //
- // layout(binding=3) uniform uname { float a; } uniformBuffer[];
- //
- // void main()
- // {
- // b = uniformBuffer[nu_ii].a;
- // }
-
- const std::string defs_before =
- R"(OpCapability Shader
-OpCapability ShaderNonUniform
-OpCapability RuntimeDescriptorArray
-OpCapability UniformBufferArrayNonUniformIndexing
-OpExtension "SPV_EXT_descriptor_indexing"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %b %nu_ii
-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 %16 NonUniform
-OpDecorate %20 NonUniform
-%void = OpTypeVoid
-%3 = 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
-)";
-
- 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_9 = OpConstant %uint 9
-%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_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%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
-%5 = OpLabel
-%16 = OpLoad %int %nu_ii
-%19 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %16 %int_0
-%20 = OpLoad %float %19
-OpStore %b %20
-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 = OpINotEqual %bool %122 %uint_0
-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_9
-%62 = OpIAdd %uint %61 %uint_9
-%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_9
-%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_6
-%92 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %91
-OpStore %92 %51
-%94 = OpIAdd %uint %61 %uint_7
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %94
-OpStore %95 %52
-%97 = OpIAdd %uint %61 %uint_8
-%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
-)";
-
- // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true);
-}
-
-TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArrayDeprecated) {
- // #version 450
- // #extension GL_EXT_nonuniform_qualifier : enable
- //
- // layout(location=0) in nonuniformEXT flat int nu_ii;
- // layout(location=0) out float b;
- //
- // layout(binding=3) buffer bname { float b; } storageBuffer[];
- //
- // void main()
- // {
- // b = storageBuffer[nu_ii].b;
- // }
-
- const std::string defs_before =
- R"(OpCapability Shader
-OpCapability ShaderNonUniform
-OpCapability RuntimeDescriptorArray
-OpCapability UniformBufferArrayNonUniformIndexing
-OpExtension "SPV_EXT_descriptor_indexing"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %b %nu_ii
-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 BufferBlock
-OpDecorate %storageBuffer DescriptorSet 0
-OpDecorate %storageBuffer Binding 3
-OpDecorate %nu_ii Flat
-OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniform
-OpDecorate %16 NonUniform
-OpDecorate %20 NonUniform
-%void = OpTypeVoid
-%3 = 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_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_Uniform_float = OpTypePointer Uniform %float
-)";
-
- 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 %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 BufferBlock
-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_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_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_9 = OpConstant %uint 9
-%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_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%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
-%5 = OpLabel
-%16 = OpLoad %int %nu_ii
-%19 = OpAccessChain %_ptr_Uniform_float %storageBuffer %16 %int_0
-%20 = OpLoad %float %19
-OpStore %b %20
-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 %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 = OpINotEqual %bool %122 %uint_0
-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_9
-%62 = OpIAdd %uint %61 %uint_9
-%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_9
-%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_6
-%92 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %91
-OpStore %92 %51
-%94 = OpIAdd %uint %61 %uint_7
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %94
-OpStore %95 %52
-%97 = OpIAdd %uint %61 %uint_8
-%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
-)";
-
- // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true);
-}
-
-TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArray) {
- // Same as Deprecated but declaring as StorageBuffer Block
-
- const std::string defs_before =
- R"(OpCapability Shader
-OpCapability ShaderNonUniform
-OpCapability RuntimeDescriptorArray
-OpCapability StorageBufferArrayNonUniformIndexing
-OpExtension "SPV_EXT_descriptor_indexing"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %b %nu_ii
-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 %16 NonUniform
-OpDecorate %20 NonUniform
-%void = OpTypeVoid
-%3 = 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
-)";
-
- 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_9 = OpConstant %uint 9
-%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_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%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
-%5 = OpLabel
-%16 = OpLoad %int %nu_ii
-%19 = OpAccessChain %_ptr_StorageBuffer_float %storageBuffer %16 %int_0
-%20 = OpLoad %float %19
-OpStore %b %20
-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 = OpINotEqual %bool %122 %uint_0
-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_9
-%62 = OpIAdd %uint %61 %uint_9
-%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_9
-%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_6
-%92 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %91
-OpStore %92 %51
-%94 = OpIAdd %uint %61 %uint_7
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %94
-OpStore %95 %52
-%97 = OpIAdd %uint %61 %uint_8
-%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
-)";
-
- // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true);
-}
-
-TEST_F(InstBindlessTest, InstInitLoadUBOScalar) {
- // #version 450
- // #extension GL_EXT_nonuniform_qualifier : enable
- //
- // layout(location=0) out float b;
- // layout(binding=3) uniform uname { float a; } uniformBuffer;
- //
- // void main()
- // {
- // b = uniformBuffer.a;
- // }
-
- const std::string defs_before =
- R"(OpCapability Shader
-OpExtension "SPV_EXT_descriptor_indexing"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %b
-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
-%void = OpTypeVoid
-%3 = 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
-)";
-
- 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_9 = OpConstant %uint 9
-%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_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_32 = OpConstant %uint 32
-%104 = OpConstantNull %float
-)";
-
- const std::string func_before =
- R"(%main = OpFunction %void None %3
-%5 = OpLabel
-%15 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %int_0
-%16 = OpLoad %float %15
-OpStore %b %16
-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 = OpINotEqual %bool %43 %uint_0
-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_9
-%65 = OpIAdd %uint %64 %uint_9
-%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_9
-%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_6
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %94
-OpStore %95 %54
-%97 = OpIAdd %uint %64 %uint_7
-%98 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %97
-OpStore %98 %55
-%100 = OpIAdd %uint %64 %uint_8
-%101 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %100
-OpStore %101 %56
-OpBranch %68
-%68 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true);
-}
-
-TEST_F(InstBindlessTest, InstBoundsInitStoreUnsizedSSBOArray) {
- // #version 450
- // #extension GL_EXT_nonuniform_qualifier : enable
- //
- // layout(location=0) in nonuniformEXT flat int nu_ii;
- // layout(location=1) in float b;
- //
- // layout(binding=4) buffer bname { float b; } storageBuffer[];
- //
- // void main()
- // {
- // storageBuffer[nu_ii].b = b;
- // }
-
- const std::string defs_before =
- R"(OpCapability Shader
-OpCapability ShaderNonUniform
-OpCapability RuntimeDescriptorArray
-OpCapability StorageBufferArrayNonUniformIndexing
-OpExtension "SPV_EXT_descriptor_indexing"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %nu_ii %b
-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 %14 NonUniform
-OpDecorate %b Location 1
-%void = OpTypeVoid
-%3 = 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
-)";
-
- 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_9 = OpConstant %uint 9
-%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_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_45 = OpConstant %uint 45
-%102 = OpTypeFunction %uint %uint %uint %uint %uint
-)";
-
- const std::string func_before =
- 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
-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 = OpINotEqual %bool %119 %uint_0
-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_9
-%60 = OpIAdd %uint %59 %uint_9
-%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_9
-%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_6
-%91 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %90
-OpStore %91 %50
-%93 = OpIAdd %uint %59 %uint_7
-%94 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %93
-OpStore %94 %51
-%96 = OpIAdd %uint %59 %uint_8
-%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
-)";
-
- // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true);
-}
-
-TEST_F(InstBindlessTest, InstBoundsInitLoadSizedUBOArray) {
- // #version 450
- // #extension GL_EXT_nonuniform_qualifier : enable
- //
- // layout(location=0) in nonuniformEXT flat int nu_ii;
- // layout(location=0) out float b;
- //
- // layout(binding=3) uniform uname { float a; } uniformBuffer[128];
- //
- // void main()
- // {
- // b = uniformBuffer[nu_ii].a;
- // }
-
- const std::string defs_before =
- R"(OpCapability Shader
-OpCapability ShaderNonUniform
-OpCapability UniformBufferArrayNonUniformIndexing
-OpExtension "SPV_EXT_descriptor_indexing"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %b %nu_ii
-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 %18 NonUniform
-OpDecorate %22 NonUniform
-%void = OpTypeVoid
-%3 = 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
-)";
-
- 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_9 = OpConstant %uint 9
-%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_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%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
-%5 = OpLabel
-%18 = OpLoad %int %nu_ii
-%21 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %18 %int_0
-%22 = OpLoad %float %21
-OpStore %b %22
-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 = OpINotEqual %bool %112 %uint_0
-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_9
-%47 = OpIAdd %uint %46 %uint_9
-%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_9
-%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_6
-%79 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %78
-OpStore %79 %34
-%81 = OpIAdd %uint %46 %uint_7
-%82 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %81
-OpStore %82 %35
-%84 = OpIAdd %uint %46 %uint_8
-%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
-)";
-
- // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true);
-}
-
-TEST_F(InstBindlessTest, SimpleV2) {
+TEST_F(InstBindlessTest, Simple) {
// Texture2D g_tColor[128];
//
// layout(push_constant) cbuffer PerViewConstantBuffer_t
@@ -4452,16 +407,15 @@
)";
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass, uint32_t, uint32_t, bool, bool,
- uint32_t>(
+ SinglePassRunAndCheck<InstBindlessCheckPass, uint32_t, uint32_t, bool, bool>(
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, 2u);
+ true, true, 7u, 23u, false, false);
}
-TEST_F(InstBindlessTest, InstrumentMultipleInstructionsV2) {
+TEST_F(InstBindlessTest, InstrumentMultipleInstructions) {
// Texture2D g_tColor[128];
//
// layout(push_constant) cbuffer PerViewConstantBuffer_t
@@ -4753,10 +707,10 @@
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + output_func, true,
- true, 7u, 23u, false, false, 2u);
+ true, 7u, 23u, false, false);
}
-TEST_F(InstBindlessTest, InstrumentOpImageV2) {
+TEST_F(InstBindlessTest, InstrumentOpImage) {
// This test verifies that the pass will correctly instrument shader
// using OpImage. This test was created by editing the SPIR-V
// from the Simple test.
@@ -4981,10 +935,10 @@
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + output_func, true,
- true, 7u, 23u, false, false, 2u);
+ true, 7u, 23u, false, false);
}
-TEST_F(InstBindlessTest, InstrumentSampledImageV2) {
+TEST_F(InstBindlessTest, InstrumentSampledImage) {
// This test verifies that the pass will correctly instrument shader
// using sampled image. This test was created by editing the SPIR-V
// from the Simple test.
@@ -5204,10 +1158,10 @@
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + output_func, true,
- true, 7u, 23u, false, false, 2u);
+ true, 7u, 23u, false, false);
}
-TEST_F(InstBindlessTest, InstrumentImageWriteV2) {
+TEST_F(InstBindlessTest, InstrumentImageWrite) {
// This test verifies that the pass will correctly instrument shader
// doing bindless image write. This test was created by editing the SPIR-V
// from the Simple test.
@@ -5429,10 +1383,10 @@
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + output_func, true,
- true, 7u, 23u, false, false, 2u);
+ true, 7u, 23u, false, false);
}
-TEST_F(InstBindlessTest, InstrumentVertexSimpleV2) {
+TEST_F(InstBindlessTest, InstrumentVertexSimple) {
// This test verifies that the pass will correctly instrument shader
// doing bindless image write. This test was created by editing the SPIR-V
// from the Simple test.
@@ -5703,10 +1657,10 @@
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + output_func, true,
- true, 7u, 23u, false, false, 2u);
+ true, 7u, 23u, false, false);
}
-TEST_F(InstBindlessTest, InstrumentTeseSimpleV2) {
+TEST_F(InstBindlessTest, InstrumentTeseSimple) {
// This test verifies that the pass will correctly instrument tessellation
// evaluation shader doing bindless buffer load.
//
@@ -5979,10 +1933,10 @@
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + output_func, true,
- true, 7u, 23u, false, false, 2u);
+ true, 7u, 23u, false, false);
}
-TEST_F(InstBindlessTest, MultipleDebugFunctionsV2) {
+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.
@@ -6303,10 +2257,10 @@
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func1_before + func2_before,
defs_after + func1_after + func2_after + output_func, true, true, 7u, 23u,
- false, false, 2u);
+ false, false);
}
-TEST_F(InstBindlessTest, RuntimeArrayV2) {
+TEST_F(InstBindlessTest, RuntimeArray) {
// This test verifies that the pass will correctly instrument shader
// with runtime descriptor array. This test was created by editing the
// SPIR-V from the Simple test.
@@ -6599,10 +2553,10 @@
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, 2u);
+ true, 7u, 23u, true, true);
}
-TEST_F(InstBindlessTest, InstrumentInitCheckOnScalarDescriptorV2) {
+TEST_F(InstBindlessTest, InstrumentInitCheckOnScalarDescriptor) {
// This test verifies that the pass will correctly instrument vanilla
// texture sample on a scalar descriptor with an initialization check if the
// input_init_enable argument is set to true. This can happen when the
@@ -6835,10 +2789,10 @@
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, 2u);
+ true, 7u, 23u, true, true);
}
-TEST_F(InstBindlessTest, SPV14AddToEntryPointV2) {
+TEST_F(InstBindlessTest, SPV14AddToEntryPoint) {
const std::string text = R"(
; CHECK: OpEntryPoint Fragment {{%\w+}} "foo" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]]
; CHECK: OpDecorate [[v1]] DescriptorSet 7
@@ -6891,11 +2845,10 @@
)";
SetTargetEnv(SPV_ENV_VULKAN_1_1_SPIRV_1_4);
- SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, true, true,
- 2u);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, true, true);
}
-TEST_F(InstBindlessTest, SPV14AddToEntryPointsV2) {
+TEST_F(InstBindlessTest, SPV14AddToEntryPoints) {
const std::string text = R"(
; CHECK: OpEntryPoint Fragment {{%\w+}} "foo" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]]
; CHECK: OpEntryPoint Fragment {{%\w+}} "bar" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]]
@@ -6950,11 +2903,10 @@
)";
SetTargetEnv(SPV_ENV_VULKAN_1_1_SPIRV_1_4);
- SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, true, true,
- 2u);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, true, true);
}
-TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedUBOArrayV2) {
+TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedUBOArray) {
// #version 450
// #extension GL_EXT_nonuniform_qualifier : enable
//
@@ -7232,10 +3184,10 @@
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, 2u);
+ true, 7u, 23u, true, true);
}
-TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArrayDeprecatedV2) {
+TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArrayDeprecated) {
// #version 450
// #extension GL_EXT_nonuniform_qualifier : enable
//
@@ -7513,10 +3465,10 @@
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, 2u);
+ true, 7u, 23u, true, true);
}
-TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArrayV2) {
+TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArray) {
// Same as Deprecated but declaring as StorageBuffer Block
const std::string defs_before =
@@ -7783,10 +3735,10 @@
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, 2u);
+ true, 7u, 23u, true, true);
}
-TEST_F(InstBindlessTest, InstInitLoadUBOScalarV2) {
+TEST_F(InstBindlessTest, InstInitLoadUBOScalar) {
// #version 450
// #extension GL_EXT_nonuniform_qualifier : enable
//
@@ -8008,10 +3960,10 @@
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, 2u);
+ true, 7u, 23u, true, true);
}
-TEST_F(InstBindlessTest, InstBoundsInitStoreUnsizedSSBOArrayV2) {
+TEST_F(InstBindlessTest, InstBoundsInitStoreUnsizedSSBOArray) {
// #version 450
// #extension GL_EXT_nonuniform_qualifier : enable
//
@@ -8282,10 +4234,10 @@
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, 2u);
+ true, 7u, 23u, true, true);
}
-TEST_F(InstBindlessTest, InstBoundsInitLoadSizedUBOArrayV2) {
+TEST_F(InstBindlessTest, InstBoundsInitLoadSizedUBOArray) {
// #version 450
// #extension GL_EXT_nonuniform_qualifier : enable
//
@@ -8551,7 +4503,7 @@
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, 2u);
+ true, 7u, 23u, true, true);
}
TEST_F(InstBindlessTest,
@@ -8873,7 +4825,7 @@
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, 2u);
+ true, 7u, 23u, true, true);
}
TEST_F(InstBindlessTest,
@@ -9197,7 +5149,7 @@
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, 2u);
+ true, 7u, 23u, true, true);
}
TEST_F(InstBindlessTest,
@@ -9521,7 +5473,7 @@
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, 2u);
+ true, 7u, 23u, true, true);
}
TEST_F(InstBindlessTest,
@@ -9845,7 +5797,7 @@
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, 2u);
+ true, 7u, 23u, true, true);
}
TEST_F(InstBindlessTest,
@@ -10169,7 +6121,7 @@
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, 2u);
+ true, 7u, 23u, true, true);
}
TEST_F(InstBindlessTest,
@@ -10493,7 +6445,7 @@
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, 2u);
+ true, 7u, 23u, true, true);
}
TEST_F(InstBindlessTest,
@@ -10817,7 +6769,7 @@
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, 2u);
+ true, 7u, 23u, true, true);
}
TEST_F(InstBindlessTest, InstBoundsInitSameBlockOpReplication) {
@@ -11226,7 +7178,7 @@
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, 2u);
+ true, 7u, 23u, true, true);
}
// TODO(greg-lunarg): Add tests to verify handling of these cases:
diff --git a/test/opt/inst_buff_addr_check_test.cpp b/test/opt/inst_buff_addr_check_test.cpp
index f8448a9..41ead67 100644
--- a/test/opt/inst_buff_addr_check_test.cpp
+++ b/test/opt/inst_buff_addr_check_test.cpp
@@ -310,7 +310,7 @@
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBuffAddrCheckPass>(
defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, 2u);
+ true, 7u, 23u);
}
TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferLoadAndStore) {
@@ -612,7 +612,7 @@
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBuffAddrCheckPass>(
defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, 2u);
+ true, 7u, 23u);
}
} // namespace
diff --git a/test/opt/local_ssa_elim_test.cpp b/test/opt/local_ssa_elim_test.cpp
index f10d118..7afbb4c 100644
--- a/test/opt/local_ssa_elim_test.cpp
+++ b/test/opt/local_ssa_elim_test.cpp
@@ -193,7 +193,24 @@
)";
const std::string before =
- R"(%main = OpFunction %void None %9
+ R"(
+; CHECK: = OpFunction
+; CHECK-NEXT: [[entry:%\w+]] = OpLabel
+; CHECK: [[outer_header:%\w+]] = OpLabel
+; CHECK-NEXT: [[outer_f:%\w+]] = OpPhi %float %float_0 [[entry]] [[inner_f:%\w+]] [[outer_be:%\w+]]
+; CHECK-NEXT: [[i:%\w+]] = OpPhi %int %int_0 [[entry]] [[i_next:%\w+]] [[outer_be]]
+; CHECK-NEXT: OpSLessThan {{%\w+}} [[i]]
+; CHECK: [[inner_pre_header:%\w+]] = OpLabel
+; CHECK: [[inner_header:%\w+]] = OpLabel
+; CHECK-NEXT: [[inner_f]] = OpPhi %float [[outer_f]] [[inner_pre_header]] [[f_next:%\w+]] [[inner_be:%\w+]]
+; CHECK-NEXT: [[j:%\w+]] = OpPhi %int %int_0 [[inner_pre_header]] [[j_next:%\w+]] [[inner_be]]
+; CHECK: [[inner_be]] = OpLabel
+; CHECK: [[f_next]] = OpFAdd %float [[inner_f]]
+; CHECK: [[j_next]] = OpIAdd %int [[j]] %int_1
+; CHECK: [[outer_be]] = OpLabel
+; CHECK: [[i_next]] = OpIAdd
+; CHECK: OpStore %fo [[outer_f]]
+%main = OpFunction %void None %9
%24 = OpLabel
%f = OpVariable %_ptr_Function_float Function
%i = OpVariable %_ptr_Function_int Function
@@ -212,8 +229,8 @@
%31 = OpLabel
%32 = OpLoad %int %j
%33 = OpSLessThan %bool %32 %int_4
-OpLoopMerge %29 %34 None
-OpBranchConditional %33 %34 %29
+OpLoopMerge %50 %34 None
+OpBranchConditional %33 %34 %50
%34 = OpLabel
%35 = OpLoad %float %f
%36 = OpLoad %int %i
@@ -226,6 +243,8 @@
%42 = OpIAdd %int %41 %int_1
OpStore %j %42
OpBranch %31
+%50 = OpLabel
+OpBranch %29
%29 = OpLabel
%43 = OpLoad %int %i
%44 = OpIAdd %int %43 %int_1
@@ -238,50 +257,7 @@
OpFunctionEnd
)";
- const std::string after =
- R"(%main = OpFunction %void None %9
-%24 = OpLabel
-%f = OpVariable %_ptr_Function_float Function
-%i = OpVariable %_ptr_Function_int Function
-%j = OpVariable %_ptr_Function_int Function
-OpStore %f %float_0
-OpStore %i %int_0
-OpBranch %25
-%25 = OpLabel
-%47 = OpPhi %float %float_0 %24 %50 %29
-%46 = OpPhi %int %int_0 %24 %44 %29
-%27 = OpSLessThan %bool %46 %int_4
-OpLoopMerge %28 %29 None
-OpBranchConditional %27 %30 %28
-%30 = OpLabel
-OpStore %j %int_0
-OpBranch %31
-%31 = OpLabel
-%50 = OpPhi %float %47 %30 %40 %34
-%48 = OpPhi %int %int_0 %30 %42 %34
-%33 = OpSLessThan %bool %48 %int_4
-OpLoopMerge %29 %34 None
-OpBranchConditional %33 %34 %29
-%34 = OpLabel
-%38 = OpAccessChain %_ptr_Input_float %BC %46 %48
-%39 = OpLoad %float %38
-%40 = OpFAdd %float %50 %39
-OpStore %f %40
-%42 = OpIAdd %int %48 %int_1
-OpStore %j %42
-OpBranch %31
-%29 = OpLabel
-%44 = OpIAdd %int %46 %int_1
-OpStore %i %44
-OpBranch %25
-%28 = OpLabel
-OpStore %fo %47
-OpReturn
-OpFunctionEnd
-)";
-
- SinglePassRunAndCheck<SSARewritePass>(predefs + before, predefs + after, true,
- true);
+ SinglePassRunAndMatch<SSARewritePass>(predefs + before, true);
}
TEST_F(LocalSSAElimTest, ForLoopWithContinue) {
diff --git a/test/opt/loop_optimizations/fusion_legal.cpp b/test/opt/loop_optimizations/fusion_legal.cpp
index 41d796f..56b0b76 100644
--- a/test/opt/loop_optimizations/fusion_legal.cpp
+++ b/test/opt/loop_optimizations/fusion_legal.cpp
@@ -3177,7 +3177,7 @@
%21 = OpLabel
%29 = OpSMod %6 %96 %28
%30 = OpIEqual %17 %29 %9
- OpSelectionMerge %23 None
+ OpSelectionMerge %sel_merge None
OpBranchConditional %30 %31 %48
%31 = OpLabel
%44 = OpAccessChain %7 %41 %91 %96
@@ -3187,8 +3187,10 @@
OpStore %47 %46
OpBranch %32
%48 = OpLabel
- OpBranch %23
+ OpBranch %sel_merge
%32 = OpLabel
+ OpBranch %sel_merge
+ %sel_merge = OpLabel
OpBranch %23
%23 = OpLabel
%52 = OpIAdd %6 %96 %51
diff --git a/test/opt/optimizer_test.cpp b/test/opt/optimizer_test.cpp
index 5c0707d..945aa78 100644
--- a/test/opt/optimizer_test.cpp
+++ b/test/opt/optimizer_test.cpp
@@ -234,7 +234,6 @@
"eliminate-dead-code-aggressive",
"eliminate-dead-const",
"flatten-decorations",
- "strip-debug",
"strip-atomic-counter-memory",
"generate-webgpu-initializers",
"legalize-vector-shuffle",
@@ -330,35 +329,6 @@
"OpFunctionEnd\n",
// pass
"flatten-decorations"},
- // Strip Debug
- {// input
- "OpCapability Shader\n"
- "OpCapability VulkanMemoryModel\n"
- "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
- "OpMemoryModel Logical Vulkan\n"
- "OpEntryPoint Vertex %func \"shader\"\n"
- "OpName %main \"main\"\n"
- "OpName %void_fn \"void_fn\"\n"
- "%void = OpTypeVoid\n"
- "%void_f = OpTypeFunction %void\n"
- "%func = OpFunction %void None %void_f\n"
- "%label = OpLabel\n"
- "OpReturn\n"
- "OpFunctionEnd\n",
- // expected
- "OpCapability Shader\n"
- "OpCapability VulkanMemoryModel\n"
- "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
- "OpMemoryModel Logical Vulkan\n"
- "OpEntryPoint Vertex %1 \"shader\"\n"
- "%void = OpTypeVoid\n"
- "%3 = OpTypeFunction %void\n"
- "%1 = OpFunction %void None %3\n"
- "%4 = OpLabel\n"
- "OpReturn\n"
- "OpFunctionEnd\n",
- // pass
- "strip-debug"},
// Eliminate Dead Constants
{// input
"OpCapability Shader\n"
diff --git a/test/opt/pass_fixture.h b/test/opt/pass_fixture.h
index 53fb206..64c089d 100644
--- a/test/opt/pass_fixture.h
+++ b/test/opt/pass_fixture.h
@@ -60,8 +60,7 @@
// from pass Process() function.
std::tuple<std::vector<uint32_t>, Pass::Status> OptimizeToBinary(
Pass* pass, const std::string& original, bool skip_nop) {
- context_ =
- std::move(BuildModule(env_, consumer_, original, assemble_options_));
+ context_ = BuildModule(env_, consumer_, original, assemble_options_);
EXPECT_NE(nullptr, context()) << "Assembling failed for shader:\n"
<< original << std::endl;
if (!context()) {
@@ -197,8 +196,7 @@
// messages.
template <typename PassT, typename... Args>
void SinglePassRunAndFail(const std::string& original, Args&&... args) {
- context_ =
- std::move(BuildModule(env_, consumer_, original, assemble_options_));
+ context_ = BuildModule(env_, consumer_, original, assemble_options_);
EXPECT_NE(nullptr, context()) << "Assembling failed for shader:\n"
<< original << std::endl;
std::ostringstream errs;
@@ -235,8 +233,7 @@
void RunAndCheck(const std::string& original, const std::string& expected) {
assert(manager_->NumPasses());
- context_ =
- std::move(BuildModule(env_, nullptr, original, assemble_options_));
+ context_ = BuildModule(env_, nullptr, original, assemble_options_);
ASSERT_NE(nullptr, context());
context()->set_preserve_bindings(OptimizerOptions()->preserve_bindings_);
diff --git a/test/opt/pass_merge_return_test.cpp b/test/opt/pass_merge_return_test.cpp
index a305680..4118169 100644
--- a/test/opt/pass_merge_return_test.cpp
+++ b/test/opt/pass_merge_return_test.cpp
@@ -1390,7 +1390,6 @@
OpLoopMerge %36 %40 None
OpBranch %35
%35 = OpLabel
- OpSelectionMerge %40 None
OpBranchConditional %77 %39 %40
%39 = OpLabel
OpReturnValue %16
@@ -1402,7 +1401,6 @@
OpLoopMerge %45 %49 None
OpBranch %44
%44 = OpLabel
- OpSelectionMerge %49 None
OpBranchConditional %77 %48 %49
%48 = OpLabel
OpReturnValue %16
@@ -1415,7 +1413,6 @@
OpLoopMerge %64 %68 None
OpBranch %63
%63 = OpLabel
- OpSelectionMerge %68 None
OpBranchConditional %77 %67 %68
%67 = OpLabel
OpReturnValue %16
@@ -1813,12 +1810,14 @@
OpLoopMerge %11 %12 None
OpBranch %13
%13 = OpLabel
- OpLoopMerge %12 %14 None
- OpBranchConditional %8 %15 %12
+ OpLoopMerge %18 %14 None
+ OpBranchConditional %8 %15 %18
%15 = OpLabel
OpReturn
%14 = OpLabel
OpBranch %13
+ %18 = OpLabel
+ OpBranch %12
%12 = OpLabel
%16 = OpUndef %float
OpBranchConditional %8 %10 %11
diff --git a/test/opt/register_liveness.cpp b/test/opt/register_liveness.cpp
index cb973d2..7cb210f 100644
--- a/test/opt/register_liveness.cpp
+++ b/test/opt/register_liveness.cpp
@@ -1277,6 +1277,46 @@
}
}
+// Test that register liveness does not fail when there is an unreachable block.
+// We are not testing if the liveness is computed correctly because the specific
+// results do not matter for unreachable blocks.
+TEST_F(PassClassTest, RegisterLivenessWithUnreachableBlock) {
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main"
+ OpExecutionMode %2 OriginLowerLeft
+ OpSource GLSL 330
+ OpSourceExtension "GL_ARB_shading_language_420pack"
+ %void = OpTypeVoid
+ %4 = OpTypeFunction %void
+ %2 = OpFunction %void None %4
+ %5 = OpLabel
+ OpBranch %6
+ %6 = OpLabel
+ OpLoopMerge %7 %8 None
+ OpBranch %9
+ %9 = OpLabel
+ OpBranch %7
+ %8 = OpLabel
+ OpBranch %6
+ %7 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ std::unique_ptr<IRContext> 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 shader:\n"
+ << text << std::endl;
+ Function* f = &*module->begin();
+ LivenessAnalysis* liveness_analysis = context->GetLivenessAnalysis();
+ liveness_analysis->Get(f);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/strip_debug_info_test.cpp b/test/opt/strip_debug_info_test.cpp
index 2f2ff46..088bba9 100644
--- a/test/opt/strip_debug_info_test.cpp
+++ b/test/opt/strip_debug_info_test.cpp
@@ -152,6 +152,53 @@
/* do_validation */ true);
}
+TEST_F(StripDebugStringTest, OpStringRemovedWithNonSemantic) {
+ std::vector<const char*> input{
+ // clang-format off
+ "OpCapability Shader",
+ "OpExtension \"SPV_KHR_non_semantic_info\"",
+ "%1 = OpExtInstImport \"NonSemantic.Testing.Set\"",
+ "OpMemoryModel Logical GLSL450",
+ "OpEntryPoint Vertex %2 \"main\"",
+ // this string is not referenced, should be removed fully
+ "%3 = OpString \"minimal.vert\"",
+ "OpName %3 \"bob\"",
+ // this string is referenced and cannot be removed,
+ // but the name should be
+ "%4 = OpString \"secondary.inc\"",
+ "OpName %4 \"sue\"",
+ "%void = OpTypeVoid",
+ "%6 = OpTypeFunction %void",
+ "%2 = OpFunction %void None %6",
+ "%7 = OpLabel",
+ "%8 = OpExtInst %void %1 5 %4",
+ "OpReturn",
+ "OpFunctionEnd",
+ // clang-format on
+ };
+ std::vector<const char*> output{
+ // clang-format off
+ "OpCapability Shader",
+ "OpExtension \"SPV_KHR_non_semantic_info\"",
+ "%1 = OpExtInstImport \"NonSemantic.Testing.Set\"",
+ "OpMemoryModel Logical GLSL450",
+ "OpEntryPoint Vertex %2 \"main\"",
+ "%4 = OpString \"secondary.inc\"",
+ "%void = OpTypeVoid",
+ "%6 = OpTypeFunction %void",
+ "%2 = OpFunction %void None %6",
+ "%7 = OpLabel",
+ "%8 = OpExtInst %void %1 5 %4",
+ "OpReturn",
+ "OpFunctionEnd",
+ // clang-format on
+ };
+ SinglePassRunAndCheck<StripDebugInfoPass>(JoinAllInsts(input),
+ JoinAllInsts(output),
+ /* skip_nop = */ false,
+ /* do_validation */ true);
+}
+
using StripDebugInfoTest = PassTest<::testing::TestWithParam<const char*>>;
TEST_P(StripDebugInfoTest, Kind) {
diff --git a/test/opt/strip_reflect_info_test.cpp b/test/opt/strip_reflect_info_test.cpp
index a9cfd47..5db34b7 100644
--- a/test/opt/strip_reflect_info_test.cpp
+++ b/test/opt/strip_reflect_info_test.cpp
@@ -13,6 +13,9 @@
// limitations under the License.
#include <string>
+#include "gmock/gmock.h"
+
+#include "spirv-tools/optimizer.hpp"
#include "test/opt/pass_fixture.h"
#include "test/opt/pass_utils.h"
@@ -23,6 +26,50 @@
using StripLineReflectInfoTest = 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(StripLineReflectInfoTest, 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<uint32_t> binary_in;
+ tools.Assemble(before, &binary_in);
+
+ // Instantiate the optimizer, and run the strip-reflection-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::CreateStripReflectInfoPass());
+ std::vector<uint32_t> 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(StripLineReflectInfoTest, StripHlslSemantic) {
// This is a non-sensical example, but exercises the instructions.
std::string before = R"(OpCapability Shader
diff --git a/test/opt/value_table_test.cpp b/test/opt/value_table_test.cpp
index 0b7530c..a0942cc 100644
--- a/test/opt/value_table_test.cpp
+++ b/test/opt/value_table_test.cpp
@@ -653,6 +653,37 @@
EXPECT_NE(vtable.GetValueNumber(phi1), vtable.GetValueNumber(phi2));
}
+// Test to make sure that OpPhi instructions with no in operands are handled
+// correctly.
+TEST_F(ValueTableTest, EmptyPhiTest) {
+ 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
+ %void = OpTypeVoid
+ %4 = OpTypeFunction %void
+ %bool = OpTypeBool
+ %true = OpConstantTrue %bool
+ %2 = OpFunction %void None %4
+ %7 = OpLabel
+ OpSelectionMerge %8 None
+ OpBranchConditional %true %9 %8
+ %9 = OpLabel
+ OpKill
+ %8 = OpLabel
+ %10 = OpPhi %bool
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
+ ValueNumberTable vtable(context.get());
+ Instruction* inst = context->get_def_use_mgr()->GetDef(10);
+ vtable.GetValueNumber(inst);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/reduce/reducer_test.cpp b/test/reduce/reducer_test.cpp
index a650d3b..59f2803 100644
--- a/test/reduce/reducer_test.cpp
+++ b/test/reduce/reducer_test.cpp
@@ -125,19 +125,21 @@
%29 = OpAccessChain %28 %27 %9
%30 = OpLoad %24 %29
%32 = OpFOrdGreaterThan %22 %30 %31
- OpSelectionMerge %34 None
+ OpSelectionMerge %90 None
OpBranchConditional %32 %33 %46
%33 = OpLabel
%40 = OpFAdd %24 %71 %30
%45 = OpISub %6 %73 %21
- OpBranch %34
+ OpBranch %90
%46 = OpLabel
%50 = OpFMul %24 %71 %30
%54 = OpSDiv %6 %73 %21
- OpBranch %34
- %34 = OpLabel
+ OpBranch %90
+ %90 = OpLabel
%77 = OpPhi %6 %45 %33 %54 %46
%76 = OpPhi %24 %40 %33 %50 %46
+ OpBranch %34
+ %34 = OpLabel
%57 = OpIAdd %6 %70 %56
OpBranch %10
%12 = OpLabel
@@ -193,11 +195,13 @@
OpLoopMerge %12 %34 None
OpBranchConditional %100 %11 %12
%11 = OpLabel
- OpSelectionMerge %34 None
+ OpSelectionMerge %90 None
OpBranchConditional %100 %33 %46
%33 = OpLabel
- OpBranch %34
+ OpBranch %90
%46 = OpLabel
+ OpBranch %90
+ %90 = OpLabel
OpBranch %34
%34 = OpLabel
OpBranch %10
@@ -345,7 +349,6 @@
OpLoopMerge %33 %38 None
OpBranch %32
%32 = OpLabel
- OpSelectionMerge %38 None
OpBranchConditional %30 %37 %38
%37 = OpLabel
OpSelectionMerge %42 None
diff --git a/test/reduce/validation_during_reduction_test.cpp b/test/reduce/validation_during_reduction_test.cpp
index 4051bac..2981c2e 100644
--- a/test/reduce/validation_during_reduction_test.cpp
+++ b/test/reduce/validation_during_reduction_test.cpp
@@ -13,7 +13,6 @@
// limitations under the License.
#include "source/reduce/reducer.h"
-
#include "source/reduce/reduction_opportunity.h"
#include "source/reduce/remove_instruction_reduction_opportunity.h"
#include "test/reduce/reduce_test_util.h"
@@ -23,8 +22,8 @@
namespace {
using opt::Function;
-using opt::IRContext;
using opt::Instruction;
+using opt::IRContext;
// A dumb reduction opportunity finder that finds opportunities to remove global
// values regardless of whether they are referenced. This is very likely to make
@@ -181,19 +180,21 @@
%29 = OpAccessChain %28 %27 %9
%30 = OpLoad %24 %29
%32 = OpFOrdGreaterThan %22 %30 %31
- OpSelectionMerge %34 None
+ OpSelectionMerge %90 None
OpBranchConditional %32 %33 %46
%33 = OpLabel
%40 = OpFAdd %24 %71 %30
%45 = OpISub %6 %73 %21
- OpBranch %34
+ OpBranch %90
%46 = OpLabel
%50 = OpFMul %24 %71 %30
%54 = OpSDiv %6 %73 %21
- OpBranch %34
- %34 = OpLabel
+ OpBranch %90
+ %90 = OpLabel
%77 = OpPhi %6 %45 %33 %54 %46
%76 = OpPhi %24 %40 %33 %50 %46
+ OpBranch %34
+ %34 = OpLabel
%57 = OpIAdd %6 %70 %56
OpBranch %10
%12 = OpLabel
@@ -303,19 +304,21 @@
%29 = OpAccessChain %28 %27 %9
%30 = OpLoad %24 %29
%32 = OpFOrdGreaterThan %22 %30 %31
- OpSelectionMerge %34 None
+ OpSelectionMerge %90 None
OpBranchConditional %32 %33 %46
%33 = OpLabel
%40 = OpFAdd %24 %71 %30
%45 = OpISub %6 %73 %21
- OpBranch %34
+ OpBranch %90
%46 = OpLabel
%50 = OpFMul %24 %71 %30
%54 = OpSDiv %6 %73 %21
- OpBranch %34
- %34 = OpLabel
+ OpBranch %90
+ %90 = OpLabel
%77 = OpPhi %6 %45 %33 %54 %46
%76 = OpPhi %24 %40 %33 %50 %46
+ OpBranch %34
+ %34 = OpLabel
%57 = OpIAdd %6 %70 %56
OpBranch %10
%12 = OpLabel
@@ -392,19 +395,21 @@
%29 = OpAccessChain %28 %27 %9
%30 = OpLoad %24 %29
%32 = OpFOrdGreaterThan %22 %30 %31
- OpSelectionMerge %34 None
+ OpSelectionMerge %90 None
OpBranchConditional %32 %33 %46
%33 = OpLabel
%40 = OpFAdd %24 %71 %30
%45 = OpISub %6 %73 %21
- OpBranch %34
+ OpBranch %90
%46 = OpLabel
%50 = OpFMul %24 %71 %30
%54 = OpSDiv %6 %73 %21
- OpBranch %34
- %34 = OpLabel
+ OpBranch %90
+ %90 = OpLabel
%77 = OpPhi %6 %45 %33 %54 %46
%76 = OpPhi %24 %40 %33 %50 %46
+ OpBranch %34
+ %34 = OpLabel
%57 = OpIAdd %6 %70 %56
OpBranch %10
%12 = OpLabel
diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt
index cee95ca..99f9780 100644
--- a/test/tools/CMakeLists.txt
+++ b/test/tools/CMakeLists.txt
@@ -12,7 +12,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-spirv_add_nosetests(expect)
-spirv_add_nosetests(spirv_test_framework)
-
+add_test(NAME spirv-tools_expect_unittests
+ COMMAND ${PYTHON_EXECUTABLE} -m unittest expect_unittest.py
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+add_test(NAME spirv-tools_spirv_test_framework_unittests
+ COMMAND ${PYTHON_EXECUTABLE} -m unittest spirv_test_framework_unittest.py
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
add_subdirectory(opt)
diff --git a/test/tools/expect.py b/test/tools/expect.py
index 52999ce..0b51adc 100755
--- a/test/tools/expect.py
+++ b/test/tools/expect.py
@@ -510,7 +510,7 @@
if not status.stderr:
return False, 'Expected error message, but no output on stderr'
if self.expected_error_substr not in convert_to_unix_line_endings(
- status.stderr.decode('utf8')):
+ status.stderr):
return False, ('Incorrect stderr output:\n{act}\n'
'Expected substring not found in stderr:\n{exp}'.format(
act=status.stderr, exp=self.expected_error_substr))
@@ -530,7 +530,7 @@
' command execution')
if not status.stderr:
return False, 'Expected warning message, but no output on stderr'
- if self.expected_warning != convert_to_unix_line_endings(status.stderr.decode('utf8')):
+ if self.expected_warning != convert_to_unix_line_endings(status.stderr):
return False, ('Incorrect stderr output:\n{act}\n'
'Expected:\n{exp}'.format(
act=status.stderr, exp=self.expected_warning))
@@ -590,16 +590,16 @@
if not status.stdout:
return False, 'Expected something on stdout'
elif type(self.expected_stdout) == str:
- if self.expected_stdout != convert_to_unix_line_endings(status.stdout.decode('utf8')):
+ if self.expected_stdout != convert_to_unix_line_endings(status.stdout):
return False, ('Incorrect stdout output:\n{ac}\n'
'Expected:\n{ex}'.format(
ac=status.stdout, ex=self.expected_stdout))
else:
- converted = convert_to_unix_line_endings(status.stdout.decode('utf8'))
+ converted = convert_to_unix_line_endings(status.stdout)
if not self.expected_stdout.search(converted):
return False, ('Incorrect stdout output:\n{ac}\n'
'Expected to match regex:\n{ex}'.format(
- ac=status.stdout.decode('utf8'), ex=self.expected_stdout.pattern))
+ ac=status.stdout, ex=self.expected_stdout.pattern))
return True, ''
@@ -624,13 +624,13 @@
if not status.stderr:
return False, 'Expected something on stderr'
elif type(self.expected_stderr) == str:
- if self.expected_stderr != convert_to_unix_line_endings(status.stderr.decode('utf8')):
+ if self.expected_stderr != convert_to_unix_line_endings(status.stderr):
return False, ('Incorrect stderr output:\n{ac}\n'
'Expected:\n{ex}'.format(
ac=status.stderr, ex=self.expected_stderr))
else:
if not self.expected_stderr.search(
- convert_to_unix_line_endings(status.stderr.decode('utf8'))):
+ convert_to_unix_line_endings(status.stderr)):
return False, ('Incorrect stderr output:\n{ac}\n'
'Expected to match regex:\n{ex}'.format(
ac=status.stderr, ex=self.expected_stderr.pattern))
@@ -695,7 +695,7 @@
# Collect all the output lines containing a pass name.
pass_names = []
pass_name_re = re.compile(r'.*IR before pass (?P<pass_name>[\S]+)')
- for line in status.stderr.decode('utf8').splitlines():
+ for line in status.stderr.splitlines():
match = pass_name_re.match(line)
if match:
pass_names.append(match.group('pass_name'))
diff --git a/test/tools/expect_nosetest.py b/test/tools/expect_nosetest.py
deleted file mode 100755
index b591a2d..0000000
--- a/test/tools/expect_nosetest.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# 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.
-"""Tests for the expect module."""
-
-import expect
-from spirv_test_framework import TestStatus
-from nose.tools import assert_equal, assert_true, assert_false
-import re
-
-
-def nosetest_get_object_name():
- """Tests get_object_filename()."""
- source_and_object_names = [('a.vert', 'a.vert.spv'), ('b.frag', 'b.frag.spv'),
- ('c.tesc', 'c.tesc.spv'), ('d.tese', 'd.tese.spv'),
- ('e.geom', 'e.geom.spv'), ('f.comp', 'f.comp.spv'),
- ('file', 'file.spv'), ('file.', 'file.spv'),
- ('file.uk',
- 'file.spv'), ('file.vert.',
- 'file.vert.spv'), ('file.vert.bla',
- 'file.vert.spv')]
- actual_object_names = [
- expect.get_object_filename(f[0]) for f in source_and_object_names
- ]
- expected_object_names = [f[1] for f in source_and_object_names]
-
- assert_equal(actual_object_names, expected_object_names)
-
-
-class TestStdoutMatchADotC(expect.StdoutMatch):
- expected_stdout = re.compile('a.c')
-
-
-def nosetest_stdout_match_regex_has_match():
- test = TestStdoutMatchADotC()
- status = TestStatus(
- test_manager=None,
- returncode=0,
- stdout='0abc1',
- stderr=None,
- directory=None,
- inputs=None,
- input_filenames=None)
- assert_true(test.check_stdout_match(status)[0])
-
-
-def nosetest_stdout_match_regex_no_match():
- test = TestStdoutMatchADotC()
- status = TestStatus(
- test_manager=None,
- returncode=0,
- stdout='ab',
- stderr=None,
- directory=None,
- inputs=None,
- input_filenames=None)
- assert_false(test.check_stdout_match(status)[0])
-
-
-def nosetest_stdout_match_regex_empty_stdout():
- test = TestStdoutMatchADotC()
- status = TestStatus(
- test_manager=None,
- returncode=0,
- stdout='',
- stderr=None,
- directory=None,
- inputs=None,
- input_filenames=None)
- assert_false(test.check_stdout_match(status)[0])
diff --git a/test/tools/expect_unittest.py b/test/tools/expect_unittest.py
new file mode 100644
index 0000000..a28de1b
--- /dev/null
+++ b/test/tools/expect_unittest.py
@@ -0,0 +1,82 @@
+# Copyright (c) 2019 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Tests for the expect module."""
+
+import expect
+from spirv_test_framework import TestStatus
+import re
+import unittest
+
+
+class TestStdoutMatchADotC(expect.StdoutMatch):
+ expected_stdout = re.compile('a.c')
+
+
+class TestExpect(unittest.TestCase):
+ def test_get_object_name(self):
+ """Tests get_object_filename()."""
+ source_and_object_names = [('a.vert', 'a.vert.spv'),
+ ('b.frag', 'b.frag.spv'),
+ ('c.tesc', 'c.tesc.spv'),
+ ('d.tese', 'd.tese.spv'),
+ ('e.geom', 'e.geom.spv'),
+ ('f.comp', 'f.comp.spv'),
+ ('file', 'file.spv'), ('file.', 'file.spv'),
+ ('file.uk',
+ 'file.spv'), ('file.vert.',
+ 'file.vert.spv'),
+ ('file.vert.bla',
+ 'file.vert.spv')]
+ actual_object_names = [
+ expect.get_object_filename(f[0]) for f in source_and_object_names
+ ]
+ expected_object_names = [f[1] for f in source_and_object_names]
+
+ self.assertEqual(actual_object_names, expected_object_names)
+
+ def test_stdout_match_regex_has_match(self):
+ test = TestStdoutMatchADotC()
+ status = TestStatus(
+ test_manager=None,
+ returncode=0,
+ stdout=b'0abc1',
+ stderr=None,
+ directory=None,
+ inputs=None,
+ input_filenames=None)
+ self.assertTrue(test.check_stdout_match(status)[0])
+
+ def test_stdout_match_regex_no_match(self):
+ test = TestStdoutMatchADotC()
+ status = TestStatus(
+ test_manager=None,
+ returncode=0,
+ stdout=b'ab',
+ stderr=None,
+ directory=None,
+ inputs=None,
+ input_filenames=None)
+ self.assertFalse(test.check_stdout_match(status)[0])
+
+ def test_stdout_match_regex_empty_stdout(self):
+ test = TestStdoutMatchADotC()
+ status = TestStatus(
+ test_manager=None,
+ returncode=0,
+ stdout=b'',
+ stderr=None,
+ directory=None,
+ inputs=None,
+ input_filenames=None)
+ self.assertFalse(test.check_stdout_match(status)[0])
diff --git a/test/tools/opt/flags.py b/test/tools/opt/flags.py
index b34a168..2f6c0a7 100644
--- a/test/tools/opt/flags.py
+++ b/test/tools/opt/flags.py
@@ -187,28 +187,35 @@
'eliminate-dead-branches',
'merge-return',
'inline-entry-points-exhaustive',
- 'eliminate-dead-code-aggressive',
+ 'eliminate-dead-functions',
'private-to-local',
- 'scalar-replacement=100',
- 'convert-local-access-chains',
- 'eliminate-local-single-block',
- 'eliminate-local-single-store',
- 'eliminate-dead-code-aggressive',
- 'simplify-instructions',
- 'eliminate-dead-inserts',
+ 'scalar-replacement=0',
'ssa-rewrite',
- 'eliminate-dead-code-aggressive',
'ccp',
+ 'loop-unroll',
+ 'eliminate-dead-branches',
+ 'simplify-instructions',
+ 'scalar-replacement=0',
+ 'eliminate-local-single-store',
+ 'if-conversion',
+ 'simplify-instructions',
'eliminate-dead-code-aggressive',
'eliminate-dead-branches',
- 'if-conversion',
- 'eliminate-dead-code-aggressive',
'merge-blocks',
- 'simplify-instructions',
- 'eliminate-dead-inserts',
- 'redundancy-elimination',
- 'cfg-cleanup',
+ 'convert-local-access-chains',
+ 'eliminate-local-single-block',
'eliminate-dead-code-aggressive',
+ 'copy-propagate-arrays',
+ 'vector-dce',
+ 'eliminate-dead-inserts',
+ 'eliminate-dead-members',
+ 'eliminate-local-single-store',
+ 'merge-blocks',
+ 'ssa-rewrite',
+ 'redundancy-elimination',
+ 'simplify-instructions',
+ 'eliminate-dead-code-aggressive',
+ 'cfg-cleanup',
]
shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm')
output = placeholder.TempFileName('output.spv')
diff --git a/test/tools/spirv_test_framework.py b/test/tools/spirv_test_framework.py
index d8d64f3..42f83c6 100755
--- a/test/tools/spirv_test_framework.py
+++ b/test/tools/spirv_test_framework.py
@@ -70,7 +70,7 @@
def get_all_superclasses(cls):
- """Returns all superclasses of a given class.
+ """Returns all superclasses of a given class. Omits root 'object' superclass.
Returns:
A list of superclasses of the given class. The order guarantees that
@@ -83,11 +83,12 @@
classes = []
for superclass in cls.__bases__:
for c in get_all_superclasses(superclass):
- if c not in classes:
+ if c is not object and c not in classes:
classes.append(c)
for superclass in cls.__bases__:
- if superclass not in classes:
+ if superclass is not object and superclass not in classes:
classes.append(superclass)
+
return classes
@@ -142,8 +143,28 @@
inputs, input_filenames):
self.test_manager = test_manager
self.returncode = returncode
- self.stdout = stdout
- self.stderr = stderr
+ # Some of our MacOS bots still run Python 2, so need to be backwards
+ # compatible here.
+ if type(stdout) is not str:
+ if sys.version_info[0] is 2:
+ self.stdout = stdout.decode('utf-8')
+ elif sys.version_info[0] is 3:
+ self.stdout = str(stdout, encoding='utf-8') if stdout is not None else stdout
+ else:
+ raise Exception('Unable to determine if running Python 2 or 3 from {}'.format(sys.version_info))
+ else:
+ self.stdout = stdout
+
+ if type(stderr) is not str:
+ if sys.version_info[0] is 2:
+ self.stderr = stderr.decode('utf-8')
+ elif sys.version_info[0] is 3:
+ self.stderr = str(stderr, encoding='utf-8') if stderr is not None else stderr
+ else:
+ raise Exception('Unable to determine if running Python 2 or 3 from {}'.format(sys.version_info))
+ else:
+ self.stderr = stderr
+
# temporary directory where the test runs
self.directory = directory
# List of inputs, as PlaceHolder objects.
diff --git a/test/tools/spirv_test_framework_nosetest.py b/test/tools/spirv_test_framework_nosetest.py
deleted file mode 100755
index c0fbed5..0000000
--- a/test/tools/spirv_test_framework_nosetest.py
+++ /dev/null
@@ -1,155 +0,0 @@
-# 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.
-
-from spirv_test_framework import get_all_test_methods, get_all_superclasses
-from nose.tools import assert_equal, with_setup
-
-
-# Classes to be used in testing get_all_{superclasses|test_methods}()
-class Root:
-
- def check_root(self):
- pass
-
-
-class A(Root):
-
- def check_a(self):
- pass
-
-
-class B(Root):
-
- def check_b(self):
- pass
-
-
-class C(Root):
-
- def check_c(self):
- pass
-
-
-class D(Root):
-
- def check_d(self):
- pass
-
-
-class E(Root):
-
- def check_e(self):
- pass
-
-
-class H(B, C, D):
-
- def check_h(self):
- pass
-
-
-class I(E):
-
- def check_i(self):
- pass
-
-
-class O(H, I):
-
- def check_o(self):
- pass
-
-
-class U(A, O):
-
- def check_u(self):
- pass
-
-
-class X(U, A):
-
- def check_x(self):
- pass
-
-
-class R1:
-
- def check_r1(self):
- pass
-
-
-class R2:
-
- def check_r2(self):
- pass
-
-
-class Multi(R1, R2):
-
- def check_multi(self):
- pass
-
-
-def nosetest_get_all_superclasses():
- """Tests get_all_superclasses()."""
-
- assert_equal(get_all_superclasses(A), [Root])
- assert_equal(get_all_superclasses(B), [Root])
- assert_equal(get_all_superclasses(C), [Root])
- assert_equal(get_all_superclasses(D), [Root])
- assert_equal(get_all_superclasses(E), [Root])
-
- assert_equal(get_all_superclasses(H), [Root, B, C, D])
- assert_equal(get_all_superclasses(I), [Root, E])
-
- assert_equal(get_all_superclasses(O), [Root, B, C, D, E, H, I])
-
- assert_equal(get_all_superclasses(U), [Root, B, C, D, E, H, I, A, O])
- assert_equal(get_all_superclasses(X), [Root, B, C, D, E, H, I, A, O, U])
-
- assert_equal(get_all_superclasses(Multi), [R1, R2])
-
-
-def nosetest_get_all_methods():
- """Tests get_all_test_methods()."""
- assert_equal(get_all_test_methods(A), ['check_root', 'check_a'])
- assert_equal(get_all_test_methods(B), ['check_root', 'check_b'])
- assert_equal(get_all_test_methods(C), ['check_root', 'check_c'])
- assert_equal(get_all_test_methods(D), ['check_root', 'check_d'])
- assert_equal(get_all_test_methods(E), ['check_root', 'check_e'])
-
- assert_equal(
- get_all_test_methods(H),
- ['check_root', 'check_b', 'check_c', 'check_d', 'check_h'])
- assert_equal(get_all_test_methods(I), ['check_root', 'check_e', 'check_i'])
-
- assert_equal(
- get_all_test_methods(O), [
- 'check_root', 'check_b', 'check_c', 'check_d', 'check_e', 'check_h',
- 'check_i', 'check_o'
- ])
-
- assert_equal(
- get_all_test_methods(U), [
- 'check_root', 'check_b', 'check_c', 'check_d', 'check_e', 'check_h',
- 'check_i', 'check_a', 'check_o', 'check_u'
- ])
- assert_equal(
- get_all_test_methods(X), [
- 'check_root', 'check_b', 'check_c', 'check_d', 'check_e', 'check_h',
- 'check_i', 'check_a', 'check_o', 'check_u', 'check_x'
- ])
-
- assert_equal(
- get_all_test_methods(Multi), ['check_r1', 'check_r2', 'check_multi'])
diff --git a/test/tools/spirv_test_framework_unittest.py b/test/tools/spirv_test_framework_unittest.py
new file mode 100644
index 0000000..e64e86c
--- /dev/null
+++ b/test/tools/spirv_test_framework_unittest.py
@@ -0,0 +1,158 @@
+# Copyright (c) 2019 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Tests for the spirv test framework module."""
+
+from spirv_test_framework import get_all_test_methods, get_all_superclasses
+import unittest
+
+# Classes to be used in testing get_all_{superclasses|test_methods}()
+
+
+class Root:
+
+ def check_root(self):
+ pass
+
+
+class A(Root):
+
+ def check_a(self):
+ pass
+
+
+class B(Root):
+
+ def check_b(self):
+ pass
+
+
+class C(Root):
+
+ def check_c(self):
+ pass
+
+
+class D(Root):
+
+ def check_d(self):
+ pass
+
+
+class E(Root):
+
+ def check_e(self):
+ pass
+
+
+class H(B, C, D):
+
+ def check_h(self):
+ pass
+
+
+class I(E):
+
+ def check_i(self):
+ pass
+
+
+class O(H, I):
+
+ def check_o(self):
+ pass
+
+
+class U(A, O):
+
+ def check_u(self):
+ pass
+
+
+class X(U, A):
+
+ def check_x(self):
+ pass
+
+
+class R1:
+
+ def check_r1(self):
+ pass
+
+
+class R2:
+
+ def check_r2(self):
+ pass
+
+
+class Multi(R1, R2):
+
+ def check_multi(self):
+ pass
+
+
+class TestSpirvTestFramework(unittest.TestCase):
+ def test_get_all_superclasses(self):
+ self.assertEqual(get_all_superclasses(A), [Root])
+ self.assertEqual(get_all_superclasses(B), [Root])
+ self.assertEqual(get_all_superclasses(C), [Root])
+ self.assertEqual(get_all_superclasses(D), [Root])
+ self.assertEqual(get_all_superclasses(E), [Root])
+
+ self.assertEqual(get_all_superclasses(H), [Root, B, C, D])
+ self.assertEqual(get_all_superclasses(I), [Root, E])
+
+ self.assertEqual(get_all_superclasses(O), [Root, B, C, D, E, H, I])
+
+ self.assertEqual(get_all_superclasses(
+ U), [Root, B, C, D, E, H, I, A, O])
+ self.assertEqual(get_all_superclasses(
+ X), [Root, B, C, D, E, H, I, A, O, U])
+
+ self.assertEqual(get_all_superclasses(Multi), [R1, R2])
+
+ def test_get_all_methods(self):
+ self.assertEqual(get_all_test_methods(A), ['check_root', 'check_a'])
+ self.assertEqual(get_all_test_methods(B), ['check_root', 'check_b'])
+ self.assertEqual(get_all_test_methods(C), ['check_root', 'check_c'])
+ self.assertEqual(get_all_test_methods(D), ['check_root', 'check_d'])
+ self.assertEqual(get_all_test_methods(E), ['check_root', 'check_e'])
+
+ self.assertEqual(
+ get_all_test_methods(H),
+ ['check_root', 'check_b', 'check_c', 'check_d', 'check_h'])
+ self.assertEqual(get_all_test_methods(
+ I), ['check_root', 'check_e', 'check_i'])
+
+ self.assertEqual(
+ get_all_test_methods(O), [
+ 'check_root', 'check_b', 'check_c', 'check_d', 'check_e', 'check_h',
+ 'check_i', 'check_o'
+ ])
+
+ self.assertEqual(
+ get_all_test_methods(U), [
+ 'check_root', 'check_b', 'check_c', 'check_d', 'check_e', 'check_h',
+ 'check_i', 'check_a', 'check_o', 'check_u'
+ ])
+
+ self.assertEqual(
+ get_all_test_methods(X), [
+ 'check_root', 'check_b', 'check_c', 'check_d', 'check_e', 'check_h',
+ 'check_i', 'check_a', 'check_o', 'check_u', 'check_x'
+ ])
+
+ self.assertEqual(
+ get_all_test_methods(Multi), ['check_r1', 'check_r2', 'check_multi'])
diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt
index d4bfe1d..138e711 100644
--- a/test/val/CMakeLists.txt
+++ b/test/val/CMakeLists.txt
@@ -70,6 +70,7 @@
val_memory_test.cpp
val_misc_test.cpp
val_modes_test.cpp
+ val_non_semantic_test.cpp
val_non_uniform_test.cpp
val_opencl_test.cpp
val_primitives_test.cpp
diff --git a/test/val/val_adjacency_test.cpp b/test/val/val_adjacency_test.cpp
index e61c03d..0b09de0 100644
--- a/test/val/val_adjacency_test.cpp
+++ b/test/val/val_adjacency_test.cpp
@@ -315,6 +315,243 @@
"non-OpPhi instructions"));
}
+TEST_F(ValidateAdjacency, NonSemanticBeforeOpPhiBad) {
+ const std::string body = R"(
+OpSelectionMerge %end_label None
+OpBranchConditional %true %true_label %false_label
+%true_label = OpLabel
+OpBranch %end_label
+%false_label = OpLabel
+OpBranch %end_label
+%end_label = OpLabel
+%dummy = OpExtInst %void %extinst 123 %int_1
+%result = OpPhi %bool %true %true_label %false %false_label
+)";
+
+ const std::string extra = R"(OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%extinst = OpExtInstImport "NonSemantic.Testing.Set"
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, extra));
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("OpPhi must appear within a non-entry block before all "
+ "non-OpPhi instructions"));
+}
+
+TEST_F(ValidateAdjacency, NonSemanticBetweenOpPhiBad) {
+ const std::string body = R"(
+OpSelectionMerge %end_label None
+OpBranchConditional %true %true_label %false_label
+%true_label = OpLabel
+OpBranch %end_label
+%false_label = OpLabel
+OpBranch %end_label
+%end_label = OpLabel
+%result1 = OpPhi %bool %true %true_label %false %false_label
+%dummy = OpExtInst %void %extinst 123 %int_1
+%result2 = OpPhi %bool %true %true_label %false %false_label
+)";
+
+ const std::string extra = R"(OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%extinst = OpExtInstImport "NonSemantic.Testing.Set"
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, extra));
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("OpPhi must appear within a non-entry block before all "
+ "non-OpPhi instructions"));
+}
+
+TEST_F(ValidateAdjacency, NonSemanticAfterOpPhiGood) {
+ const std::string body = R"(
+OpSelectionMerge %end_label None
+OpBranchConditional %true %true_label %false_label
+%true_label = OpLabel
+OpBranch %end_label
+%false_label = OpLabel
+OpBranch %end_label
+%end_label = OpLabel
+OpLine %string 0 0
+%result = OpPhi %bool %true %true_label %false %false_label
+%dummy = OpExtInst %void %extinst 123 %int_1
+)";
+
+ const std::string extra = R"(OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%extinst = OpExtInstImport "NonSemantic.Testing.Set"
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, extra));
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateAdjacency, NonSemanticBeforeOpFunctionParameterBad) {
+ const std::string body = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%extinst = OpExtInstImport "NonSemantic.Testing.Set"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+
+%string = OpString ""
+%void = OpTypeVoid
+%bool = OpTypeBool
+%int = OpTypeInt 32 0
+%true = OpConstantTrue %bool
+%false = OpConstantFalse %bool
+%zero = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%func = OpTypeFunction %void
+%func_int = OpTypePointer Function %int
+%paramfunc_type = OpTypeFunction %void %int %int
+
+%paramfunc = OpFunction %void None %paramfunc_type
+%dummy = OpExtInst %void %extinst 123 %int_1
+%a = OpFunctionParameter %int
+%b = OpFunctionParameter %int
+%paramfunc_entry = OpLabel
+OpReturn
+OpFunctionEnd
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(body);
+ EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Non-semantic OpExtInst within function definition "
+ "must appear in a block"));
+}
+
+TEST_F(ValidateAdjacency, NonSemanticBetweenOpFunctionParameterBad) {
+ const std::string body = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%extinst = OpExtInstImport "NonSemantic.Testing.Set"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+
+%string = OpString ""
+%void = OpTypeVoid
+%bool = OpTypeBool
+%int = OpTypeInt 32 0
+%true = OpConstantTrue %bool
+%false = OpConstantFalse %bool
+%zero = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%func = OpTypeFunction %void
+%func_int = OpTypePointer Function %int
+%paramfunc_type = OpTypeFunction %void %int %int
+
+%paramfunc = OpFunction %void None %paramfunc_type
+%a = OpFunctionParameter %int
+%dummy = OpExtInst %void %extinst 123 %int_1
+%b = OpFunctionParameter %int
+%paramfunc_entry = OpLabel
+OpReturn
+OpFunctionEnd
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(body);
+ EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Non-semantic OpExtInst within function definition "
+ "must appear in a block"));
+}
+
+TEST_F(ValidateAdjacency, NonSemanticAfterOpFunctionParameterGood) {
+ const std::string body = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%extinst = OpExtInstImport "NonSemantic.Testing.Set"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+
+%string = OpString ""
+%void = OpTypeVoid
+%bool = OpTypeBool
+%int = OpTypeInt 32 0
+%true = OpConstantTrue %bool
+%false = OpConstantFalse %bool
+%zero = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%func = OpTypeFunction %void
+%func_int = OpTypePointer Function %int
+%paramfunc_type = OpTypeFunction %void %int %int
+
+%paramfunc = OpFunction %void None %paramfunc_type
+%a = OpFunctionParameter %int
+%b = OpFunctionParameter %int
+%paramfunc_entry = OpLabel
+%dummy = OpExtInst %void %extinst 123 %int_1
+OpReturn
+OpFunctionEnd
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(body);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateAdjacency, NonSemanticBetweenFunctionsGood) {
+ const std::string body = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%extinst = OpExtInstImport "NonSemantic.Testing.Set"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+
+%string = OpString ""
+%void = OpTypeVoid
+%bool = OpTypeBool
+%int = OpTypeInt 32 0
+%true = OpConstantTrue %bool
+%false = OpConstantFalse %bool
+%zero = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%func = OpTypeFunction %void
+%func_int = OpTypePointer Function %int
+%paramfunc_type = OpTypeFunction %void %int %int
+
+%paramfunc = OpFunction %void None %paramfunc_type
+%a = OpFunctionParameter %int
+%b = OpFunctionParameter %int
+%paramfunc_entry = OpLabel
+OpReturn
+OpFunctionEnd
+
+%dummy = OpExtInst %void %extinst 123 %int_1
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(body);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
TEST_F(ValidateAdjacency, OpVariableInFunctionGood) {
const std::string body = R"(
OpLine %string 1 1
diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp
index f06f36c..8d8de37 100644
--- a/test/val/val_cfg_test.cpp
+++ b/test/val/val_cfg_test.cpp
@@ -23,7 +23,6 @@
#include <vector>
#include "gmock/gmock.h"
-
#include "source/diagnostic.h"
#include "source/spirv_target_env.h"
#include "source/val/validate.h"
@@ -1355,7 +1354,7 @@
}
if (cap == SpvCapabilityShader) {
branch.AppendBody("OpLoopMerge %merge %target None\n");
- body.AppendBody("OpSelectionMerge %target None\n");
+ body.AppendBody("OpSelectionMerge %f None\n");
}
if (!spvIsWebGPUEnv(env))
@@ -1650,25 +1649,27 @@
Block entry("entry");
Block loop1("loop1", SpvOpBranchConditional);
Block loop2("loop2", SpvOpBranchConditional);
- Block loop2_merge("loop2_merge", SpvOpBranchConditional);
+ Block loop2_merge("loop2_merge");
+ Block loop1_cont("loop1_cont", SpvOpBranchConditional);
Block be_block("be_block");
Block exit("exit", SpvOpReturn);
entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
if (is_shader) {
- loop1.SetBody("OpLoopMerge %exit %loop2_merge None\n");
+ loop1.SetBody("OpLoopMerge %exit %loop1_cont None\n");
loop2.SetBody("OpLoopMerge %loop2_merge %loop2 None\n");
}
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("loop1", "loop2", "be_block", "loop2_merge") +
- types_consts() +
- "%func = OpFunction %voidt None %funct\n";
+ std::string str =
+ GetDefaultHeader(GetParam()) +
+ nameOps("loop1", "loop2", "be_block", "loop1_cont", "loop2_merge") +
+ types_consts() + "%func = OpFunction %voidt None %funct\n";
str += entry >> loop1;
str += loop1 >> std::vector<Block>({loop2, exit});
str += loop2 >> std::vector<Block>({loop2, loop2_merge});
- str += loop2_merge >> std::vector<Block>({be_block, exit});
+ str += loop2_merge >> loop1_cont;
+ str += loop1_cont >> std::vector<Block>({be_block, exit});
str += be_block >> loop1;
str += exit;
str += "OpFunctionEnd";
@@ -1678,7 +1679,7 @@
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
MatchesRegex("The continue construct with the continue target "
- ".\\[%loop2_merge\\] is not post dominated by the "
+ ".\\[%loop1_cont\\] is not post dominated by the "
"back-edge block .\\[%be_block\\]\n"
" %be_block = OpLabel\n"));
} else {
@@ -2037,13 +2038,10 @@
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
}
-TEST_P(ValidateCFG, ContinueTargetCanBeMergeBlockForNestedStructureGood) {
- // This example is valid. It shows that the validator can't just add
- // an edge from the loop head to the continue target. If that edge
- // is added, then the "if_merge" block is both the continue target
- // for the loop and also the merge block for the nested selection, but
- // then it wouldn't be dominated by "if_head", the header block for the
- // nested selection.
+TEST_P(ValidateCFG, ContinueTargetCanBeMergeBlockForNestedStructure) {
+ // The continue construct cannot be the merge target of a nested selection
+ // because the loop construct must contain "if_merge" because it contains
+ // "if_head".
bool is_shader = GetParam() == SpvCapabilityShader;
Block entry("entry");
Block loop("loop");
@@ -2072,7 +2070,16 @@
str += "OpFunctionEnd";
CompileSuccessfully(str);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
+ if (is_shader) {
+ 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"));
+ } else {
+ EXPECT_THAT(SPV_SUCCESS, ValidateInstructions());
+ }
}
TEST_P(ValidateCFG, SingleLatchBlockMultipleBranchesToLoopHeader) {
@@ -3681,17 +3688,21 @@
OpLoopMerge %8 %9 None
OpBranch %10
%10 = OpLabel
-OpLoopMerge %9 %11 None
+OpLoopMerge %11 %12 None
+OpBranch %13
+%13 = OpLabel
+OpSelectionMerge %14 None
+OpBranchConditional %3 %14 %15
+%15 = OpLabel
+OpBranch %8
+%14 = OpLabel
OpBranch %12
%12 = OpLabel
-OpSelectionMerge %11 None
-OpBranchConditional %3 %11 %13
-%13 = OpLabel
-OpBranch %8
+OpBranchConditional %3 %10 %11
%11 = OpLabel
-OpBranchConditional %3 %9 %10
+OpBranch %9
%9 = OpLabel
-OpBranchConditional %3 %8 %7
+OpBranchConditional %3 %7 %8
%8 = OpLabel
OpReturn
OpFunctionEnd
@@ -3700,7 +3711,7 @@
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("block <ID> 13[%13] exits the loop headed by <ID> "
+ HasSubstr("block <ID> 15[%15] exits the loop headed by <ID> "
"10[%10], but not via a structured exit"));
}
@@ -3726,9 +3737,11 @@
OpSelectionMerge %31 None
OpBranchConditional %undef %30 %31
%30 = OpLabel
-OpSelectionMerge %37 None
-OpBranchConditional %undef %36 %37
+OpSelectionMerge %38 None
+OpBranchConditional %undef %36 %38
%36 = OpLabel
+OpBranch %38
+%38 = OpLabel
OpBranch %37
%37 = OpLabel
OpBranch %10
@@ -4100,6 +4113,80 @@
EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
}
+TEST_F(ValidateCFG, ContinueCannotBeSelectionMergeTarget) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpName %loop "loop"
+OpName %continue "continue"
+OpName %body "body"
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpBranch %loop
+%loop = OpLabel
+OpLoopMerge %exit %continue None
+OpBranch %body
+%body = OpLabel
+OpSelectionMerge %continue None
+OpBranchConditional %undef %exit %continue
+%continue = OpLabel
+OpBranch %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text);
+ 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"));
+}
+
+TEST_F(ValidateCFG, ContinueCannotBeLoopMergeTarget) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpName %loop "loop"
+OpName %continue "continue"
+OpName %inner "inner"
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpBranch %loop
+%loop = OpLabel
+OpLoopMerge %exit %continue None
+OpBranchConditional %undef %exit %inner
+%inner = OpLabel
+OpLoopMerge %continue %inner None
+OpBranchConditional %undef %inner %continue
+%continue = OpLabel
+OpBranch %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text);
+ 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"));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/test/val/val_id_test.cpp b/test/val/val_id_test.cpp
index 327aef1..019d91a 100644
--- a/test/val/val_id_test.cpp
+++ b/test/val/val_id_test.cpp
@@ -749,20 +749,40 @@
// Signed or unsigned.
enum Signed { kSigned, kUnsigned };
-// Creates an assembly snippet declaring OpTypeArray with the given length.
-std::string MakeArrayLength(const std::string& len, Signed isSigned,
- int width) {
+// Creates an assembly module declaring OpTypeArray with the given length.
+std::string MakeArrayLength(const std::string& len, Signed isSigned, int width,
+ int max_int_width = 64,
+ bool use_vulkan_memory_model = false) {
std::ostringstream ss;
ss << R"(
OpCapability Shader
- OpCapability Linkage
- OpCapability Int16
- OpCapability Int64
)";
- ss << "OpMemoryModel Logical GLSL450\n";
+ if (use_vulkan_memory_model) {
+ ss << " OpCapability VulkanMemoryModel\n";
+ }
+ if (width == 16) {
+ ss << " OpCapability Int16\n";
+ }
+ if (max_int_width > 32) {
+ ss << "\n OpCapability Int64\n";
+ }
+ if (use_vulkan_memory_model) {
+ ss << " OpExtension \"SPV_KHR_vulkan_memory_model\"\n";
+ ss << "OpMemoryModel Logical Vulkan\n";
+ } else {
+ ss << "OpMemoryModel Logical GLSL450\n";
+ }
+ ss << "OpEntryPoint GLCompute %main \"main\"\n";
+ ss << "OpExecutionMode %main LocalSize 1 1 1\n";
ss << " %t = OpTypeInt " << width << (isSigned == kSigned ? " 1" : " 0");
ss << " %l = OpConstant %t " << len;
ss << " %a = OpTypeArray %t %l";
+ ss << " %void = OpTypeVoid \n"
+ " %voidfn = OpTypeFunction %void \n"
+ " %main = OpFunction %void None %voidfn \n"
+ " %entry = OpLabel\n"
+ " OpReturn\n"
+ " OpFunctionEnd\n";
return ss.str();
}
@@ -772,7 +792,8 @@
: public spvtest::TextToBinaryTestBase<::testing::TestWithParam<int>> {
protected:
OpTypeArrayLengthTest()
- : position_(spv_position_t{0, 0, 0}),
+ : env_(SPV_ENV_UNIVERSAL_1_0),
+ position_(spv_position_t{0, 0, 0}),
diagnostic_(spvDiagnosticCreate(&position_, "")) {}
~OpTypeArrayLengthTest() { spvDiagnosticDestroy(diagnostic_); }
@@ -783,7 +804,7 @@
spvDiagnosticDestroy(diagnostic_);
diagnostic_ = nullptr;
const auto status =
- spvValidate(ScopedContext().context, &cbinary, &diagnostic_);
+ spvValidate(ScopedContext(env_).context, &cbinary, &diagnostic_);
if (status != SPV_SUCCESS) {
spvDiagnosticPrint(diagnostic_);
EXPECT_THAT(std::string(diagnostic_->error),
@@ -792,12 +813,15 @@
return status;
}
+ protected:
+ spv_target_env env_;
+
private:
spv_position_t position_; // For creating diagnostic_.
spv_diagnostic diagnostic_;
};
-TEST_P(OpTypeArrayLengthTest, LengthPositive) {
+TEST_P(OpTypeArrayLengthTest, LengthPositiveSmall) {
const int width = GetParam();
EXPECT_EQ(SPV_SUCCESS,
Val(CompileSuccessfully(MakeArrayLength("1", kSigned, width))));
@@ -814,20 +838,19 @@
const std::string fpad(width / 4 - 1, 'F');
EXPECT_EQ(
SPV_SUCCESS,
- Val(CompileSuccessfully(MakeArrayLength("0x7" + fpad, kSigned, width))));
- EXPECT_EQ(SPV_SUCCESS, Val(CompileSuccessfully(
- MakeArrayLength("0xF" + fpad, kUnsigned, width))));
+ Val(CompileSuccessfully(MakeArrayLength("0x7" + fpad, kSigned, width))))
+ << MakeArrayLength("0x7" + fpad, kSigned, width);
}
TEST_P(OpTypeArrayLengthTest, LengthZero) {
const int width = GetParam();
EXPECT_EQ(SPV_ERROR_INVALID_ID,
Val(CompileSuccessfully(MakeArrayLength("0", kSigned, width)),
- "OpTypeArray Length <id> '2\\[%.*\\]' default value must be at "
+ "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
"least 1."));
EXPECT_EQ(SPV_ERROR_INVALID_ID,
Val(CompileSuccessfully(MakeArrayLength("0", kUnsigned, width)),
- "OpTypeArray Length <id> '2\\[%.*\\]' default value must be at "
+ "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
"least 1."));
}
@@ -835,23 +858,88 @@
const int width = GetParam();
EXPECT_EQ(SPV_ERROR_INVALID_ID,
Val(CompileSuccessfully(MakeArrayLength("-1", kSigned, width)),
- "OpTypeArray Length <id> '2\\[%.*\\]' default value must be at "
+ "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
"least 1."));
EXPECT_EQ(SPV_ERROR_INVALID_ID,
Val(CompileSuccessfully(MakeArrayLength("-2", kSigned, width)),
- "OpTypeArray Length <id> '2\\[%.*\\]' default value must be at "
+ "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
"least 1."));
EXPECT_EQ(SPV_ERROR_INVALID_ID,
Val(CompileSuccessfully(MakeArrayLength("-123", kSigned, width)),
- "OpTypeArray Length <id> '2\\[%.*\\]' default value must be at "
+ "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
"least 1."));
const std::string neg_max = "0x8" + std::string(width / 4 - 1, '0');
EXPECT_EQ(SPV_ERROR_INVALID_ID,
Val(CompileSuccessfully(MakeArrayLength(neg_max, kSigned, width)),
- "OpTypeArray Length <id> '2\\[%.*\\]' default value must be at "
+ "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
"least 1."));
}
+// Returns the string form of an integer of the form 0x80....0 of the
+// given bit width.
+std::string big_num_ending_0(int bit_width) {
+ return "0x8" + std::string(bit_width / 4 - 1, '0');
+}
+
+// Returns the string form of an integer of the form 0x80..001 of the
+// given bit width.
+std::string big_num_ending_1(int bit_width) {
+ return "0x8" + std::string(bit_width / 4 - 2, '0') + "1";
+}
+
+TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding0InVulkan) {
+ env_ = SPV_ENV_VULKAN_1_0;
+ const int width = GetParam();
+ for (int max_int_width : {32, 64}) {
+ if (width > max_int_width) {
+ // Not valid to even make the OpConstant in this case.
+ continue;
+ }
+ const auto module = CompileSuccessfully(MakeArrayLength(
+ big_num_ending_0(width), kUnsigned, width, max_int_width));
+ EXPECT_EQ(SPV_SUCCESS, Val(module));
+ }
+}
+
+TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding1InVulkan) {
+ env_ = SPV_ENV_VULKAN_1_0;
+ const int width = GetParam();
+ for (int max_int_width : {32, 64}) {
+ if (width > max_int_width) {
+ // Not valid to even make the OpConstant in this case.
+ continue;
+ }
+ const auto module = CompileSuccessfully(MakeArrayLength(
+ big_num_ending_1(width), kUnsigned, width, max_int_width));
+ EXPECT_EQ(SPV_SUCCESS, Val(module));
+ }
+}
+
+TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding0InWebGPU) {
+ env_ = SPV_ENV_WEBGPU_0;
+ const int width = GetParam();
+ // WebGPU only has 32 bit integers.
+ if (width != 32) return;
+ const int max_int_width = 32;
+ const auto module = CompileSuccessfully(MakeArrayLength(
+ big_num_ending_0(width), kUnsigned, width, max_int_width, true));
+ EXPECT_EQ(SPV_SUCCESS, Val(module));
+}
+
+TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding1InWebGPU) {
+ env_ = SPV_ENV_WEBGPU_0;
+ const int width = GetParam();
+ // WebGPU only has 32 bit integers.
+ if (width != 32) return;
+ const int max_int_width = 32;
+ const auto module = CompileSuccessfully(MakeArrayLength(
+ big_num_ending_1(width), kUnsigned, width, max_int_width, true));
+ EXPECT_EQ(SPV_ERROR_INVALID_ID,
+ Val(module,
+ "OpTypeArray Length <id> '3\\[%.*\\]' size exceeds max value "
+ "2147483648 permitted by WebGPU: got 2147483649"));
+}
+
// The only valid widths for integers are 8, 16, 32, and 64.
// Since the Int8 capability requires the Kernel capability, and the Kernel
// capability prohibits usage of signed integers, we can skip 8-bit integers
diff --git a/test/val/val_non_semantic_test.cpp b/test/val/val_non_semantic_test.cpp
new file mode 100644
index 0000000..b80bb1a
--- /dev/null
+++ b/test/val/val_non_semantic_test.cpp
@@ -0,0 +1,195 @@
+// Copyright (c) 2019 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Validation tests for non-semantic instructions
+
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "test/unit_spirv.h"
+#include "test/val/val_code_generator.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+struct TestResult {
+ TestResult(spv_result_t in_validation_result = SPV_SUCCESS,
+ const char* in_error_str = nullptr,
+ const char* in_error_str2 = nullptr)
+ : validation_result(in_validation_result),
+ error_str(in_error_str),
+ error_str2(in_error_str2) {}
+ spv_result_t validation_result;
+ const char* error_str;
+ const char* error_str2;
+};
+
+using ::testing::Combine;
+using ::testing::HasSubstr;
+using ::testing::Values;
+using ::testing::ValuesIn;
+
+using ValidateNonSemanticGenerated = spvtest::ValidateBase<
+ std::tuple<bool, bool, const char*, const char*, TestResult>>;
+using ValidateNonSemanticString = spvtest::ValidateBase<bool>;
+
+CodeGenerator GetNonSemanticCodeGenerator(const bool declare_ext,
+ const bool declare_extinst,
+ const char* const global_extinsts,
+ const char* const function_extinsts) {
+ CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();
+
+ if (declare_ext) {
+ generator.extensions_ += "OpExtension \"SPV_KHR_non_semantic_info\"\n";
+ }
+ if (declare_extinst) {
+ generator.extensions_ +=
+ "%extinst = OpExtInstImport \"NonSemantic.Testing.Set\"\n";
+ }
+
+ generator.after_types_ = global_extinsts;
+
+ generator.before_types_ = "%decorate_group = OpDecorationGroup";
+
+ EntryPoint entry_point;
+ entry_point.name = "main";
+ entry_point.execution_model = "Vertex";
+
+ entry_point.body = R"(
+)";
+ entry_point.body += function_extinsts;
+ generator.entry_points_.push_back(std::move(entry_point));
+
+ return generator;
+}
+
+TEST_P(ValidateNonSemanticGenerated, InTest) {
+ const bool declare_ext = std::get<0>(GetParam());
+ const bool declare_extinst = std::get<1>(GetParam());
+ const char* const global_extinsts = std::get<2>(GetParam());
+ const char* const function_extinsts = std::get<3>(GetParam());
+ const TestResult& test_result = std::get<4>(GetParam());
+
+ CodeGenerator generator = GetNonSemanticCodeGenerator(
+ declare_ext, declare_extinst, global_extinsts, function_extinsts);
+
+ CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(test_result.validation_result,
+ ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ if (test_result.error_str) {
+ EXPECT_THAT(getDiagnosticString(),
+ testing::ContainsRegex(test_result.error_str));
+ }
+ if (test_result.error_str2) {
+ EXPECT_THAT(getDiagnosticString(),
+ testing::ContainsRegex(test_result.error_str2));
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(OnlyOpExtension, ValidateNonSemanticGenerated,
+ Combine(Values(true), Values(false), Values(""),
+ Values(""), Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+ MissingOpExtension, ValidateNonSemanticGenerated,
+ Combine(Values(false), Values(true), Values(""), Values(""),
+ Values(TestResult(
+ SPV_ERROR_INVALID_DATA,
+ "NonSemantic extended instruction sets cannot be declared "
+ "without SPV_KHR_non_semantic_info."))));
+
+INSTANTIATE_TEST_SUITE_P(NoExtInst, ValidateNonSemanticGenerated,
+ Combine(Values(true), Values(true), Values(""),
+ Values(""), Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+ SimpleGlobalExtInst, ValidateNonSemanticGenerated,
+ Combine(Values(true), Values(true),
+ Values("%result = OpExtInst %void %extinst 123 %i32"), Values(""),
+ Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+ ComplexGlobalExtInst, ValidateNonSemanticGenerated,
+ Combine(Values(true), Values(true),
+ Values("%result = OpExtInst %void %extinst 123 %i32 %u32_2 "
+ "%f32vec4_1234 %u32_0"),
+ Values(""), Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+ SimpleFunctionLevelExtInst, ValidateNonSemanticGenerated,
+ Combine(Values(true), Values(true), Values(""),
+ Values("%result = OpExtInst %void %extinst 123 %i32"),
+ Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+ FunctionTypeReference, ValidateNonSemanticGenerated,
+ Combine(Values(true), Values(true),
+ Values("%result = OpExtInst %void %extinst 123 %func"), Values(""),
+ Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+ EntryPointReference, ValidateNonSemanticGenerated,
+ Combine(Values(true), Values(true), Values(""),
+ Values("%result = OpExtInst %void %extinst 123 %main"),
+ Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+ DecorationGroupReference, ValidateNonSemanticGenerated,
+ Combine(Values(true), Values(true), Values(""),
+ Values("%result = OpExtInst %void %extinst 123 %decorate_group"),
+ Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+ UnknownIDReference, ValidateNonSemanticGenerated,
+ Combine(Values(true), Values(true),
+ Values("%result = OpExtInst %void %extinst 123 %undefined_id"),
+ Values(""),
+ Values(TestResult(SPV_ERROR_INVALID_ID,
+ "ID .* has not been defined"))));
+
+INSTANTIATE_TEST_SUITE_P(
+ NonSemanticUseInSemantic, ValidateNonSemanticGenerated,
+ Combine(Values(true), Values(true),
+ Values("%result = OpExtInst %f32 %extinst 123 %i32\n"
+ "%invalid = OpConstantComposite %f32vec2 %f32_0 %result"),
+ Values(""),
+ Values(TestResult(SPV_ERROR_INVALID_ID,
+ "in semantic instruction cannot be a "
+ "non-semantic instruction"))));
+
+TEST_F(ValidateNonSemanticString, InvalidSectionOpExtInst) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%extinst = OpExtInstImport "NonSemantic.Testing.Set"
+%test = OpExtInst %void %extinst 4 %void
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main"
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+
+ // 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"));
+}
+
+} // namespace
+} // namespace val
+} // namespace spvtools
diff --git a/test/val/val_webgpu_test.cpp b/test/val/val_webgpu_test.cpp
index 8f62555..e81fc7c 100644
--- a/test/val/val_webgpu_test.cpp
+++ b/test/val/val_webgpu_test.cpp
@@ -54,117 +54,170 @@
EXPECT_THAT(getDiagnosticString(), HasSubstr("OpUndef is disallowed"));
}
-TEST_F(ValidateWebGPU, OpNameIsDisallowed) {
+TEST_F(ValidateWebGPU, OpNameIsAllowed) {
std::string spirv = R"(
- OpCapability Shader
- OpCapability VulkanMemoryModelKHR
- OpExtension "SPV_KHR_vulkan_memory_model"
- OpMemoryModel Logical VulkanKHR
- OpName %1 "foo"
-%1 = OpTypeFloat 32
+ OpCapability Shader
+ OpCapability VulkanMemoryModelKHR
+ OpExtension "SPV_KHR_vulkan_memory_model"
+ OpMemoryModel Logical VulkanKHR
+ OpEntryPoint Vertex %func "shader"
+ OpName %1 "foo"
+ %1 = OpTypeFloat 32
+ %void = OpTypeVoid
+ %void_f = OpTypeFunction %void
+ %func = OpFunction %void None %void_f
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
)";
CompileSuccessfully(spirv);
-
- EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Debugging instructions are not allowed in the WebGPU "
- "execution environment.\n OpName %foo \"foo\"\n"));
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
}
-TEST_F(ValidateWebGPU, OpMemberNameIsDisallowed) {
+TEST_F(ValidateWebGPU, OpMemberNameIsAllowed) {
std::string spirv = R"(
- OpCapability Shader
- OpCapability VulkanMemoryModelKHR
- OpExtension "SPV_KHR_vulkan_memory_model"
- OpMemoryModel Logical VulkanKHR
- OpMemberName %2 0 "foo"
-%1 = OpTypeFloat 32
-%2 = OpTypeStruct %1
+ OpCapability Shader
+ OpCapability VulkanMemoryModelKHR
+ OpExtension "SPV_KHR_vulkan_memory_model"
+ OpMemoryModel Logical VulkanKHR
+ OpEntryPoint Vertex %func "shader"
+ OpMemberName %2 0 "foo"
+ %1 = OpTypeFloat 32
+ %2 = OpTypeStruct %1
+ %void = OpTypeVoid
+ %void_f = OpTypeFunction %void
+ %func = OpFunction %void None %void_f
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+
)";
CompileSuccessfully(spirv);
-
- EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Debugging instructions are not allowed in the WebGPU "
- "execution environment.\n OpMemberName %_struct_1 0 "
- "\"foo\"\n"));
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
}
-TEST_F(ValidateWebGPU, OpSourceIsDisallowed) {
+TEST_F(ValidateWebGPU, OpSourceIsAllowed) {
std::string spirv = R"(
- OpCapability Shader
- OpCapability VulkanMemoryModelKHR
- OpExtension "SPV_KHR_vulkan_memory_model"
- OpMemoryModel Logical VulkanKHR
- OpSource GLSL 450
+ OpCapability Shader
+ OpCapability VulkanMemoryModelKHR
+ OpExtension "SPV_KHR_vulkan_memory_model"
+ OpMemoryModel Logical VulkanKHR
+ OpEntryPoint Vertex %func "shader"
+ OpSource GLSL 450
+ %void = OpTypeVoid
+ %void_f = OpTypeFunction %void
+ %func = OpFunction %void None %void_f
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
)";
CompileSuccessfully(spirv);
-
- EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Debugging instructions are not allowed in the WebGPU "
- "execution environment.\n OpSource GLSL 450\n"));
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
}
-// OpSourceContinued does not have a test case, because it requires being
-// preceded by OpSource, which will cause a validation error.
-
-TEST_F(ValidateWebGPU, OpSourceExtensionIsDisallowed) {
+TEST_F(ValidateWebGPU, OpSourceContinuedIsAllowed) {
std::string spirv = R"(
- OpCapability Shader
- OpCapability VulkanMemoryModelKHR
- OpExtension "SPV_KHR_vulkan_memory_model"
- OpMemoryModel Logical VulkanKHR
- OpSourceExtension "bar"
+ OpCapability Shader
+ OpCapability VulkanMemoryModelKHR
+ OpExtension "SPV_KHR_vulkan_memory_model"
+ OpMemoryModel Logical VulkanKHR
+ OpEntryPoint Vertex %func "shader"
+ OpSource GLSL 450
+ OpSourceContinued "I am a happy shader! Yay! ;"
+ %void = OpTypeVoid
+ %void_f = OpTypeFunction %void
+ %func = OpFunction %void None %void_f
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
)";
CompileSuccessfully(spirv);
-
- EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Debugging instructions are not allowed in the WebGPU "
- "execution environment.\n OpSourceExtension "
- "\"bar\"\n"));
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
}
-TEST_F(ValidateWebGPU, OpStringIsDisallowed) {
+TEST_F(ValidateWebGPU, OpSourceExtensionIsAllowed) {
std::string spirv = R"(
- OpCapability Shader
- OpCapability VulkanMemoryModelKHR
- OpExtension "SPV_KHR_vulkan_memory_model"
- OpMemoryModel Logical VulkanKHR
-%1 = OpString "foo"
+ OpCapability Shader
+ OpCapability VulkanMemoryModelKHR
+ OpExtension "SPV_KHR_vulkan_memory_model"
+ OpMemoryModel Logical VulkanKHR
+ OpEntryPoint Vertex %func "shader"
+ OpSourceExtension "bar"
+ %void = OpTypeVoid
+ %void_f = OpTypeFunction %void
+ %func = OpFunction %void None %void_f
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
)";
CompileSuccessfully(spirv);
-
- EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Debugging instructions are not allowed in the WebGPU "
- "execution environment.\n %1 = OpString \"foo\"\n"));
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
}
-// OpLine does not have a test case, because it requires being preceded by
-// OpString, which will cause a validation error.
-
-TEST_F(ValidateWebGPU, OpNoLineDisallowed) {
+TEST_F(ValidateWebGPU, OpStringIsAllowed) {
std::string spirv = R"(
- OpCapability Shader
- OpCapability VulkanMemoryModelKHR
- OpExtension "SPV_KHR_vulkan_memory_model"
- OpMemoryModel Logical VulkanKHR
- OpNoLine
+ OpCapability Shader
+ OpCapability VulkanMemoryModelKHR
+ OpExtension "SPV_KHR_vulkan_memory_model"
+ OpMemoryModel Logical VulkanKHR
+ OpEntryPoint Vertex %func "shader"
+ %1 = OpString "foo"
+ %void = OpTypeVoid
+ %void_f = OpTypeFunction %void
+ %func = OpFunction %void None %void_f
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
)";
CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
+}
- EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Debugging instructions are not allowed in the WebGPU "
- "execution environment.\n OpNoLine\n"));
+TEST_F(ValidateWebGPU, OpLineIsAllowed) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpCapability VulkanMemoryModelKHR
+ OpExtension "SPV_KHR_vulkan_memory_model"
+ OpMemoryModel Logical VulkanKHR
+ OpEntryPoint Vertex %func "shader"
+ %1 = OpString "minimal.vert"
+ OpLine %1 1 1
+ %void = OpTypeVoid
+ %void_f = OpTypeFunction %void
+ %func = OpFunction %void None %void_f
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
+}
+
+TEST_F(ValidateWebGPU, OpNoLineIsAllowed) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpCapability VulkanMemoryModelKHR
+ OpExtension "SPV_KHR_vulkan_memory_model"
+ OpMemoryModel Logical VulkanKHR
+ OpEntryPoint Vertex %func "shader"
+ OpNoLine
+ %void = OpTypeVoid
+ %void_f = OpTypeFunction %void
+ %func = OpFunction %void None %void_f
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
}
TEST_F(ValidateWebGPU, LogicalAddressingVulkanKHRMemoryGood) {
@@ -183,7 +236,6 @@
)";
CompileSuccessfully(spirv);
-
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
}
diff --git a/tools/fuzz/fuzz.cpp b/tools/fuzz/fuzz.cpp
index 5d582f5..cb98914 100644
--- a/tools/fuzz/fuzz.cpp
+++ b/tools/fuzz/fuzz.cpp
@@ -17,6 +17,7 @@
#include <cstring>
#include <fstream>
#include <functional>
+#include <random>
#include <sstream>
#include <string>
@@ -106,6 +107,10 @@
facts to make the guard non-obviously false. This option is a
helper for massaging crash-inducing tests into a runnable
format; it does not perform any fuzzing.
+ --fuzzer-pass-validation
+ Run the validator after applying each fuzzer pass during
+ fuzzing. Aborts fuzzing early if an invalid binary is created.
+ Useful for debugging spirv-fuzz.
--replay
File from which to read a sequence of transformations to replay
(instead of fuzzing)
@@ -179,6 +184,9 @@
} else if (0 == strncmp(cur_arg, "--force-render-red",
sizeof("--force-render-red") - 1)) {
force_render_red = true;
+ } else if (0 == strncmp(cur_arg, "--fuzzer-pass-validation",
+ sizeof("--fuzzer-pass-validation") - 1)) {
+ fuzzer_options->enable_fuzzer_pass_validation();
} else if (0 == strncmp(cur_arg, "--replay=", sizeof("--replay=") - 1)) {
const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
*replay_transformations_file = std::string(split_flag.second);
@@ -397,16 +405,21 @@
}
bool Fuzz(const spv_target_env& target_env,
- const spvtools::FuzzerOptions& fuzzer_options,
+ spv_const_fuzzer_options fuzzer_options,
const std::vector<uint32_t>& binary_in,
const spvtools::fuzz::protobufs::FactSequence& initial_facts,
std::vector<uint32_t>* binary_out,
spvtools::fuzz::protobufs::TransformationSequence*
transformations_applied) {
- spvtools::fuzz::Fuzzer fuzzer(target_env);
+ spvtools::fuzz::Fuzzer fuzzer(
+ target_env,
+ fuzzer_options->has_random_seed
+ ? fuzzer_options->random_seed
+ : static_cast<uint32_t>(std::random_device()()),
+ fuzzer_options->fuzzer_pass_validation_enabled);
fuzzer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
- auto fuzz_result_status = fuzzer.Run(binary_in, initial_facts, fuzzer_options,
- binary_out, transformations_applied);
+ auto fuzz_result_status =
+ fuzzer.Run(binary_in, initial_facts, binary_out, transformations_applied);
if (fuzz_result_status !=
spvtools::fuzz::Fuzzer::FuzzerResultStatus::kComplete) {
spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error running fuzzer");
diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp
index 4364e3e..0ff937a 100644
--- a/tools/opt/opt.cpp
+++ b/tools/opt/opt.cpp
@@ -467,7 +467,8 @@
printf(R"(
--strip-reflect
Remove all reflection information. For now, this covers
- reflection information defined by SPV_GOOGLE_hlsl_functionality1.)");
+ reflection information defined by SPV_GOOGLE_hlsl_functionality1
+ and SPV_KHR_non_semantic_info)");
printf(R"(
--target-env=<env>
Set the target environment. Without this flag the target
diff --git a/utils/check_copyright.py b/utils/check_copyright.py
index cfeef80..969371d 100755
--- a/utils/check_copyright.py
+++ b/utils/check_copyright.py
@@ -34,7 +34,7 @@
'Samsung Inc']
CURRENT_YEAR='2019'
-YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017|2018|2019)'
+YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017|2017-2019|2018|2019)'
COPYRIGHT_RE = re.compile(
'Copyright \(c\) {} ({})'.format(YEARS, '|'.join(AUTHORS)))
diff --git a/utils/generate_grammar_tables.py b/utils/generate_grammar_tables.py
index ed24bd0..06fcf94 100755
--- a/utils/generate_grammar_tables.py
+++ b/utils/generate_grammar_tables.py
@@ -30,6 +30,7 @@
SPV_AMD_gpu_shader_half_float
SPV_AMD_gpu_shader_int16
SPV_AMD_shader_trinary_minmax
+SPV_KHR_non_semantic_info
"""
@@ -359,14 +360,22 @@
return '{}\n\n{}\n\n{}'.format(caps_arrays, exts_arrays, '\n'.join(insts))
-def generate_extended_instruction_table(inst_table, set_name):
+def generate_extended_instruction_table(json_grammar, set_name, operand_kind_prefix=""):
"""Returns the info table containing all SPIR-V extended instructions,
sorted by opcode, and prefixed by capability arrays.
Arguments:
- inst_table: a list containing all SPIR-V instructions.
- set_name: the name of the extended instruction set.
+ - operand_kind_prefix: the prefix, if any, to add to the front
+ of operand kind names.
"""
+ if operand_kind_prefix:
+ prefix_operand_kind_names(operand_kind_prefix, json_grammar)
+
+ inst_table = json_grammar["instructions"]
+ set_name = set_name.replace(".", "_")
+
inst_table = sorted(inst_table, key=lambda k: k['opcode'])
caps = [inst.get('capabilities', []) for inst in inst_table]
caps_arrays = generate_capability_arrays(caps)
@@ -451,6 +460,7 @@
def generate_enum_operand_kind(enum, synthetic_exts_list):
"""Returns the C definition for the given operand kind.
+ It's a static const named array of spv_operand_desc_t.
Also appends to |synthetic_exts_list| a list of extension lists
used.
@@ -680,6 +690,26 @@
return operand_kinds
+def prefix_operand_kind_names(prefix, json_dict):
+ """Modifies json_dict, by prefixing all the operand kind names
+ with the given prefix. Also modifies their uses in the instructions
+ to match.
+ """
+
+ old_to_new = {}
+ for operand_kind in json_dict["operand_kinds"]:
+ old_name = operand_kind["kind"]
+ new_name = prefix + old_name
+ operand_kind["kind"] = new_name
+ old_to_new[old_name] = new_name
+
+ for instruction in json_dict["instructions"]:
+ for operand in instruction.get("operands", []):
+ replacement = old_to_new.get(operand["kind"])
+ if replacement is not None:
+ operand["kind"] = replacement
+
+
def main():
import argparse
parser = argparse.ArgumentParser(description='Generate SPIR-V info tables')
@@ -692,6 +722,10 @@
type=str, required=False, default=None,
help='input JSON grammar file for DebugInfo extended '
'instruction set')
+ parser.add_argument('--extinst-cldebuginfo100-grammar', metavar='<path>',
+ type=str, required=False, default=None,
+ help='input JSON grammar file for OpenCL.DebugInfo.100 '
+ 'extended instruction set')
parser.add_argument('--extinst-glsl-grammar', metavar='<path>',
type=str, required=False, default=None,
help='input JSON grammar file for GLSL extended '
@@ -726,6 +760,9 @@
parser.add_argument('--vendor-insts-output', metavar='<path>',
type=str, required=False, default=None,
help='output file for vendor extended instruction set')
+ parser.add_argument('--vendor-operand-kind-prefix', metavar='<string>',
+ type=str, required=False, default=None,
+ help='prefix for operand kinds (to disambiguate operand type enums)')
args = parser.parse_args()
if (args.core_insts_output is None) != \
@@ -733,9 +770,12 @@
print('error: --core-insts-output and --operand-kinds-output '
'should be specified together.')
exit(1)
- if args.operand_kinds_output and not (args.spirv_core_grammar and args.extinst_debuginfo_grammar):
+ if args.operand_kinds_output and not (args.spirv_core_grammar and
+ args.extinst_debuginfo_grammar and
+ args.extinst_cldebuginfo100_grammar):
print('error: --operand-kinds-output requires --spirv-core-grammar '
- 'and --exinst-debuginfo-grammar')
+ 'and --exinst-debuginfo-grammar '
+ 'and --exinst-cldebuginfo100-grammar')
exit(1)
if (args.glsl_insts_output is None) != \
(args.extinst_glsl_grammar is None):
@@ -766,14 +806,19 @@
core_grammar = json.loads(json_file.read())
with open(args.extinst_debuginfo_grammar) as debuginfo_json_file:
debuginfo_grammar = json.loads(debuginfo_json_file.read())
- instructions = []
- instructions.extend(core_grammar['instructions'])
- instructions.extend(debuginfo_grammar['instructions'])
- operand_kinds = []
- operand_kinds.extend(core_grammar['operand_kinds'])
- operand_kinds.extend(debuginfo_grammar['operand_kinds'])
- extensions = get_extension_list(instructions, operand_kinds)
- operand_kinds = precondition_operand_kinds(operand_kinds)
+ with open(args.extinst_cldebuginfo100_grammar) as cldebuginfo100_json_file:
+ cldebuginfo100_grammar = json.loads(cldebuginfo100_json_file.read())
+ prefix_operand_kind_names("CLDEBUG100_", cldebuginfo100_grammar)
+ instructions = []
+ instructions.extend(core_grammar['instructions'])
+ instructions.extend(debuginfo_grammar['instructions'])
+ instructions.extend(cldebuginfo100_grammar['instructions'])
+ operand_kinds = []
+ operand_kinds.extend(core_grammar['operand_kinds'])
+ operand_kinds.extend(debuginfo_grammar['operand_kinds'])
+ operand_kinds.extend(cldebuginfo100_grammar['operand_kinds'])
+ extensions = get_extension_list(instructions, operand_kinds)
+ operand_kinds = precondition_operand_kinds(operand_kinds)
if args.core_insts_output is not None:
make_path_to_file(args.core_insts_output)
make_path_to_file(args.operand_kinds_output)
@@ -798,7 +843,7 @@
make_path_to_file(args.glsl_insts_output)
with open(args.glsl_insts_output, 'w') as f:
f.write(generate_extended_instruction_table(
- grammar['instructions'], 'glsl'))
+ grammar, 'glsl'))
if args.extinst_opencl_grammar is not None:
with open(args.extinst_opencl_grammar) as json_file:
@@ -806,7 +851,7 @@
make_path_to_file(args.opencl_insts_output)
with open(args.opencl_insts_output, 'w') as f:
f.write(generate_extended_instruction_table(
- grammar['instructions'], 'opencl'))
+ grammar, 'opencl'))
if args.extinst_vendor_grammar is not None:
with open(args.extinst_vendor_grammar) as json_file:
@@ -817,7 +862,7 @@
name = name[start:-len('.grammar.json')].replace('-', '_')
with open(args.vendor_insts_output, 'w') as f:
f.write(generate_extended_instruction_table(
- grammar['instructions'], name))
+ grammar, name, args.vendor_operand_kind_prefix))
if __name__ == '__main__':
diff --git a/utils/vscode/README.md b/utils/vscode/README.md
index afbb246..bc02211 100644
--- a/utils/vscode/README.md
+++ b/utils/vscode/README.md
@@ -1,7 +1,17 @@
# Visual Studio Code extension for SPIR-V disassembly files
-This directory holds a Visual Studio Code extension adding syntax highlighting for SPIR-V disassembly files (.spirv)
+This directory holds a Visual Studio Code extension adding syntax highlighting for SPIR-V assembly files (`.spvasm`)
+
+## Dependencies
+
+In order to build and install the Visual Studio Code language server extension, you will need to install and have on your `PATH` the following dependencies:
+* [`npm`](https://www.npmjs.com/)
+* [`golang`](https://golang.org/)
## Installing (macOS / Linux)
-Simply run `install.sh`
+Run `install.sh`
+
+## Installing (Windows)
+
+Run `install.bat`
diff --git a/utils/vscode/install.bat b/utils/vscode/install.bat
new file mode 100644
index 0000000..21a52ec
--- /dev/null
+++ b/utils/vscode/install.bat
@@ -0,0 +1,30 @@
+@REM Copyright (c) 2019 Google Inc.
+@REM
+@REM Licensed under the Apache License, Version 2.0 (the "License");
+@REM you may not use this file except in compliance with the License.
+@REM You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing, software
+@REM distributed under the License is distributed on an "AS IS" BASIS,
+@REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@REM See the License for the specific language governing permissions and
+@REM limitations under the License.
+
+@set EXT_PATH=%userprofile%\.vscode\extensions\google.spirvls-0.0.1
+@set ROOT_PATH=%~dp0
+
+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
+
+if not exist %EXT_PATH% mkdir -p %EXT_PATH%
+copy %ROOT_PATH%\extension.js %EXT_PATH%
+copy %ROOT_PATH%\package.json %EXT_PATH%
+copy %ROOT_PATH%\spirv.json %EXT_PATH%
+
+go build -o %EXT_PATH%\langsvr %ROOT_PATH%\src\langsvr.go
+
+@pushd %EXT_PATH%
+call npm install
+@popd
diff --git a/utils/vscode/src/tools/gen-grammar.go b/utils/vscode/src/tools/gen-grammar.go
index f9980b9..42cbbe9 100644
--- a/utils/vscode/src/tools/gen-grammar.go
+++ b/utils/vscode/src/tools/gen-grammar.go
@@ -77,9 +77,12 @@
}
return rel
}
+ escape := func(str string) string {
+ return strings.ReplaceAll(str, `\`, `/`)
+ }
args := []string{
- "--template=" + relPath(*templatePath),
- "--out=" + relPath(*outputPath),
+ "--template=" + escape(relPath(*templatePath)),
+ "--out=" + escape(relPath(*outputPath)),
}
return "gen-grammar.go " + strings.Join(args, " ")
},