summaryrefslogtreecommitdiff
path: root/chromium/third_party/SPIRV-Tools
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/SPIRV-Tools')
-rw-r--r--chromium/third_party/SPIRV-Tools/src/BUILD.gn8
-rw-r--r--chromium/third_party/SPIRV-Tools/src/CHANGES40
-rw-r--r--chromium/third_party/SPIRV-Tools/src/CMakeLists.txt2
-rw-r--r--chromium/third_party/SPIRV-Tools/src/DEPS8
-rw-r--r--chromium/third_party/SPIRV-Tools/src/README.md35
-rw-r--r--chromium/third_party/SPIRV-Tools/src/external/CMakeLists.txt16
-rw-r--r--chromium/third_party/SPIRV-Tools/src/include/spirv-tools/instrument.hpp35
-rw-r--r--chromium/third_party/SPIRV-Tools/src/include/spirv-tools/optimizer.hpp7
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opcode.cpp16
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opcode.h3
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/operand.cpp2
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/aggressive_dead_code_elim_pass.cpp10
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/aggressive_dead_code_elim_pass.h4
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/dead_branch_elim_pass.cpp1
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/debug_info_manager.cpp379
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/debug_info_manager.h97
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/desc_sroa.cpp237
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/desc_sroa.h23
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/function.cpp12
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/function.h17
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/inline_pass.cpp734
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/inline_pass.h77
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/inst_bindless_check_pass.h7
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/inst_buff_addr_check_pass.h4
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/inst_debug_printf_pass.h5
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/instruction.cpp27
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/instruction.h20
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/instruction_list.h10
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/instrument_pass.cpp8
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/instrument_pass.h16
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/ir_context.cpp17
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/ir_context.h29
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/local_access_chain_convert_pass.cpp10
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/local_access_chain_convert_pass.h4
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/local_single_block_elim_pass.cpp10
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/local_single_block_elim_pass.h4
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/local_single_store_elim_pass.cpp10
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/local_single_store_elim_pass.h6
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/loop_descriptor.cpp25
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/mem_pass.cpp11
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/merge_return_pass.cpp1
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/optimizer.cpp28
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/pass.h4
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/ssa_rewrite_pass.cpp18
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/opt/ssa_rewrite_pass.h7
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/reduce/CMakeLists.txt10
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/reduce/pch_source_reduce.h2
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/reduce/reducer.cpp13
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/reduce/remove_instruction_reduction_opportunity.cpp12
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/reduce/remove_struct_member_reduction_opportunity.cpp208
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/reduce/remove_struct_member_reduction_opportunity.h84
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/reduce/remove_unused_instruction_reduction_opportunity_finder.cpp (renamed from chromium/third_party/SPIRV-Tools/src/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp)101
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/reduce/remove_unused_instruction_reduction_opportunity_finder.h (renamed from chromium/third_party/SPIRV-Tools/src/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h)25
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp193
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h61
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/val/validate_extensions.cpp9
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/val/validate_function.cpp1
-rw-r--r--chromium/third_party/SPIRV-Tools/src/source/val/validate_image.cpp18
-rwxr-xr-xchromium/third_party/SPIRV-Tools/src/utils/check_symbol_exports.py6
-rwxr-xr-xchromium/third_party/SPIRV-Tools/src/utils/roll_deps.sh31
60 files changed, 2193 insertions, 625 deletions
diff --git a/chromium/third_party/SPIRV-Tools/src/BUILD.gn b/chromium/third_party/SPIRV-Tools/src/BUILD.gn
index 5860aff03f6..fae795727a2 100644
--- a/chromium/third_party/SPIRV-Tools/src/BUILD.gn
+++ b/chromium/third_party/SPIRV-Tools/src/BUILD.gn
@@ -792,8 +792,12 @@ static_library("spvtools_reduce") {
"source/reduce/remove_selection_reduction_opportunity.h",
"source/reduce/remove_selection_reduction_opportunity_finder.cpp",
"source/reduce/remove_selection_reduction_opportunity_finder.h",
- "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp",
- "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h",
+ "source/reduce/remove_struct_member_reduction_opportunity.cpp",
+ "source/reduce/remove_struct_member_reduction_opportunity.h",
+ "source/reduce/remove_unused_instruction_reduction_opportunity_finder.cpp",
+ "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h",
+ "source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp",
+ "source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h",
"source/reduce/simple_conditional_branch_to_branch_opportunity_finder.cpp",
"source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h",
"source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp",
diff --git a/chromium/third_party/SPIRV-Tools/src/CHANGES b/chromium/third_party/SPIRV-Tools/src/CHANGES
index fe6641ecf04..a46755ecc8a 100644
--- a/chromium/third_party/SPIRV-Tools/src/CHANGES
+++ b/chromium/third_party/SPIRV-Tools/src/CHANGES
@@ -1,7 +1,43 @@
Revision history for SPIRV-Tools
-v2020.3-dev 2020-03-26
- - Start v2020.3-dev
+v2020.4-dev 2020-05-27
+ - Start v2020.4-dev
+
+v2020.3 2020-05-27
+ - General
+ - Prevent Effcee install his things when build spirv-tools with testing enabled (#3256)
+ - Update acorn version (#3294)
+ - If SPIRV-Headers is in our tree, include it as subproject (#3299)
+ - allow cross compiling for Windows Store, UWP, etc. (#3330)
+ - Optimizer
+ - Remove deprecated interfaces from instrument passes (#3361)
+ - Preserve debug info in inline pass (#3349)
+ - Handle more cases in dead member elim (#3289)
+ - Preserve debug info in eliminate-dead-functions (#3251)
+ - Fix Struct CFG analysis for single block loop (#3293)
+ - Add tests for recently added command line option (#3297)
+ - Consider sampled images as read-only storage (#3295)
+ - Allow various validation options to be passed to spirv-opt (#3314)
+ - Add debug information analysis (#3305)
+ - Preserve debug info for wrap-opkill (#3331)
+ - refactor inlining pass (#3328)
+ - Add unrolling to performance passes (#3082)
+ - Validator
+ - Add validation support for ImageGatherBiasLodAMD (#3363)
+ - Validate ShaderCallKHR memory scope (#3332)
+ - Validate Buffer and BufferBlock apply only to struct types (#3259)
+ - Reduce
+ - increase default step limit (#3327)
+ - Remove unused uniforms and similar (#3321)
+ - Fuzz
+ - Add support for StorageBuffer (#3348)
+ - Add validator options (#3254)
+ - Limit adding of new variables to 'basic' types (#3257)
+ - Transformation to add OpConstantNull (#3273)
+ - Handling of more fuzzing opportunities (#3277, #3280, #3281, #3290, #3292)
+ - Respect rules for OpSampledImage (#3287)
+ - Do not outline regions that produce pointer outputs (#3291)
+ - Linker
v2020.2 2020-03-26
- General:
diff --git a/chromium/third_party/SPIRV-Tools/src/CMakeLists.txt b/chromium/third_party/SPIRV-Tools/src/CMakeLists.txt
index 9d0eb8b544c..73248f23439 100644
--- a/chromium/third_party/SPIRV-Tools/src/CMakeLists.txt
+++ b/chromium/third_party/SPIRV-Tools/src/CMakeLists.txt
@@ -40,7 +40,7 @@ if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
set(SPIRV_TIMER_ENABLED ${SPIRV_ALLOW_TIMERS})
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Emscripten")
add_definitions(-DSPIRV_EMSCRIPTEN)
-elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
+elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Windows")
add_definitions(-DSPIRV_WINDOWS)
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "CYGWIN")
add_definitions(-DSPIRV_WINDOWS)
diff --git a/chromium/third_party/SPIRV-Tools/src/DEPS b/chromium/third_party/SPIRV-Tools/src/DEPS
index c3e78a26478..a27b921bfa8 100644
--- a/chromium/third_party/SPIRV-Tools/src/DEPS
+++ b/chromium/third_party/SPIRV-Tools/src/DEPS
@@ -3,10 +3,10 @@ use_relative_paths = True
vars = {
'github': 'https://github.com',
- 'effcee_revision': 'cd25ec17e9382f99a895b9ef53ff3c277464d07d',
- 'googletest_revision': 'f2fb48c3b3d79a75a88a99fba6576b25d42ec528',
- 're2_revision': '5bd613749fd530b576b890283bfb6bc6ea6246cb',
- 'spirv_headers_revision': 'f8bf11a0253a32375c32cad92c841237b96696c0',
+ 'effcee_revision': '5af957bbfc7da4e9f7aa8cac11379fa36dd79b84',
+ 'googletest_revision': '011959aafddcd30611003de96cfd8d7a7685c700',
+ 're2_revision': 'aecba11114cf1fac5497aeb844b6966106de3eb6',
+ 'spirv_headers_revision': 'ac638f1815425403e946d0ab78bac71d2bdbf3be',
}
deps = {
diff --git a/chromium/third_party/SPIRV-Tools/src/README.md b/chromium/third_party/SPIRV-Tools/src/README.md
index 41c26a0a905..c82ca192f1f 100644
--- a/chromium/third_party/SPIRV-Tools/src/README.md
+++ b/chromium/third_party/SPIRV-Tools/src/README.md
@@ -324,8 +324,7 @@ The script `utils/git-sync-deps` can be used to checkout and/or update the
contents of the repos under `external/` instead of manually maintaining them.
### Build using CMake
-You can build The project using [CMake][cmake] to generate platform-specific
-build configurations.
+You can build the project using [CMake][cmake]:
```sh
cd <spirv-dir>
@@ -333,8 +332,36 @@ mkdir build && cd build
cmake [-G <platform-generator>] <spirv-dir>
```
-Once the build files have been generated, build using your preferred
-development environment.
+Once the build files have been generated, build using the appropriate build
+command (e.g. `ninja`, `make`, `msbuild`, etc.; this depends on the platform
+generator used above), or use your IDE, or use CMake to run the appropriate build
+command for you:
+
+```sh
+cmake --build . [--config Debug] # runs `make` or `ninja` or `msbuild` etc.
+```
+
+#### Note about the fuzzer
+
+The SPIR-V fuzzer, `spirv-fuzz`, can only be built via CMake, and is disabled by
+default. To build it, clone protobuf and use the `SPIRV_BUILD_FUZZER` CMake
+option, like so:
+
+```sh
+# In <spirv-dir> (the SPIRV-Tools repo root):
+git clone https://github.com/protocolbuffers/protobuf external/protobuf
+pushd external/protobuf
+git checkout v3.7.1
+popd
+
+# In your build directory:
+cmake [-G <platform-generator>] <spirv-dir> -DSPIRV_BUILD_FUZZER=ON
+cmake --build . --config Debug
+```
+
+You can also add `-DSPIRV_ENABLE_LONG_FUZZER_TESTS=ON` to build additional
+fuzzer tests.
+
### Build using Bazel
You can also use [Bazel](https://bazel.build/) to build the project.
diff --git a/chromium/third_party/SPIRV-Tools/src/external/CMakeLists.txt b/chromium/third_party/SPIRV-Tools/src/external/CMakeLists.txt
index 3c7b40331d7..5b341598cbe 100644
--- a/chromium/third_party/SPIRV-Tools/src/external/CMakeLists.txt
+++ b/chromium/third_party/SPIRV-Tools/src/external/CMakeLists.txt
@@ -47,7 +47,7 @@ if (NOT ${SPIRV_SKIP_TESTS})
if (TARGET gmock)
message(STATUS "Google Mock already configured")
else()
- set(GMOCK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/googletest/googlemock)
+ set(GMOCK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/googletest)
if(EXISTS ${GMOCK_DIR})
if(MSVC)
# Our tests use ::testing::Combine. Work around a compiler
@@ -88,7 +88,7 @@ if (NOT ${SPIRV_SKIP_TESTS})
set(RE2_BUILD_TESTING OFF CACHE STRING "Run RE2 Tests")
if (NOT RE2_SOURCE_DIR)
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/re2)
- set(RE2_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/re2" CACHE STRING "RE2 source dir" )
+ set(RE2_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/re2" CACHE STRING "RE2 source dir" )
endif()
endif()
endif()
@@ -98,13 +98,17 @@ if (NOT ${SPIRV_SKIP_TESTS})
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/effcee)
# If we're configuring RE2 (via Effcee), then turn off RE2 testing.
if (NOT TARGET re2)
- set(RE2_BUILD_TESTING OFF)
+ set(RE2_BUILD_TESTING OFF)
endif()
if (MSVC)
- # SPIRV-Tools uses the shared CRT with MSVC. Tell Effcee to do the same.
- set(EFFCEE_ENABLE_SHARED_CRT ON)
+ # SPIRV-Tools uses the shared CRT with MSVC. Tell Effcee to do the same.
+ set(EFFCEE_ENABLE_SHARED_CRT ON)
endif()
- add_subdirectory(effcee)
+ set(EFFCEE_BUILD_SAMPLES OFF CACHE BOOL "Do not build Effcee examples")
+ if (NOT TARGET effcee)
+ set(EFFCEE_BUILD_TESTING OFF CACHE BOOL "Do not build Effcee test suite")
+ endif()
+ add_subdirectory(effcee EXCLUDE_FROM_ALL)
set_property(TARGET effcee PROPERTY FOLDER Effcee)
# Turn off warnings for effcee and re2
set_property(TARGET effcee APPEND PROPERTY COMPILE_OPTIONS -w)
diff --git a/chromium/third_party/SPIRV-Tools/src/include/spirv-tools/instrument.hpp b/chromium/third_party/SPIRV-Tools/src/include/spirv-tools/instrument.hpp
index d3180e4442c..ef5136a8caf 100644
--- a/chromium/third_party/SPIRV-Tools/src/include/spirv-tools/instrument.hpp
+++ b/chromium/third_party/SPIRV-Tools/src/include/spirv-tools/instrument.hpp
@@ -35,10 +35,6 @@ namespace spvtools {
// generated by InstrumentPass::GenDebugStreamWrite. This method is utilized
// by InstBindlessCheckPass.
//
-// 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
// this value so as not to overwrite each others records. This value must be
@@ -94,10 +90,6 @@ static const int kInstCompOutGlobalInvocationIdX = kInstCommonOutCnt;
static const int kInstCompOutGlobalInvocationIdY = kInstCommonOutCnt + 1;
static const int kInstCompOutGlobalInvocationIdZ = kInstCommonOutCnt + 2;
-// Compute Shader Output Record Offsets - Version 1 (DEPRECATED)
-static const int kInstCompOutGlobalInvocationId = kInstCommonOutCnt;
-static const int kInstCompOutUnused = kInstCommonOutCnt + 1;
-
// Tessellation Control Shader Output Record Offsets
static const int kInstTessCtlOutInvocationId = kInstCommonOutCnt;
static const int kInstTessCtlOutPrimitiveId = kInstCommonOutCnt + 1;
@@ -108,10 +100,6 @@ static const int kInstTessEvalOutPrimitiveId = kInstCommonOutCnt;
static const int kInstTessEvalOutTessCoordU = kInstCommonOutCnt + 1;
static const int kInstTessEvalOutTessCoordV = kInstCommonOutCnt + 2;
-// Tessellation Shader Output Record Offsets - Version 1 (DEPRECATED)
-static const int kInstTessOutInvocationId = kInstCommonOutCnt;
-static const int kInstTessOutUnused = kInstCommonOutCnt + 1;
-
// Geometry Shader Output Record Offsets
static const int kInstGeomOutPrimitiveId = kInstCommonOutCnt;
static const int kInstGeomOutInvocationId = kInstCommonOutCnt + 1;
@@ -124,14 +112,12 @@ static const int kInstRayTracingOutLaunchIdZ = kInstCommonOutCnt + 2;
// Size of Common and Stage-specific Members
static const int kInstStageOutCnt = kInstCommonOutCnt + 3;
-static const int kInst2StageOutCnt = kInstCommonOutCnt + 3;
// Validation Error Code Offset
//
// This identifies the validation error. It also helps to identify
// how many words follow in the record and their meaning.
static const int kInstValidationOutError = kInstStageOutCnt;
-static const int kInst2ValidationOutError = kInst2StageOutCnt;
// Validation-specific Output Record Offsets
//
@@ -144,37 +130,19 @@ static const int kInstBindlessBoundsOutDescIndex = kInstStageOutCnt + 1;
static const int kInstBindlessBoundsOutDescBound = kInstStageOutCnt + 2;
static const int kInstBindlessBoundsOutCnt = kInstStageOutCnt + 3;
-static const int kInst2BindlessBoundsOutDescIndex = kInst2StageOutCnt + 1;
-static const int kInst2BindlessBoundsOutDescBound = kInst2StageOutCnt + 2;
-static const int kInst2BindlessBoundsOutCnt = kInst2StageOutCnt + 3;
-
// A bindless uninitialized error will output the index.
static const int kInstBindlessUninitOutDescIndex = kInstStageOutCnt + 1;
static const int kInstBindlessUninitOutUnused = kInstStageOutCnt + 2;
static const int kInstBindlessUninitOutCnt = kInstStageOutCnt + 3;
-static const int kInst2BindlessUninitOutDescIndex = kInst2StageOutCnt + 1;
-static const int kInst2BindlessUninitOutUnused = kInst2StageOutCnt + 2;
-static const int kInst2BindlessUninitOutCnt = kInst2StageOutCnt + 3;
-
// 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;
-
-// DEPRECATED
-static const int kInstBindlessOutDescIndex = kInstStageOutCnt + 1;
-static const int kInstBindlessOutDescBound = kInstStageOutCnt + 2;
-static const int kInstBindlessOutCnt = kInstStageOutCnt + 3;
-
// Maximum Output Record Member Count
static const int kInstMaxOutCnt = kInstStageOutCnt + 3;
-static const int kInst2MaxOutCnt = kInst2StageOutCnt + 3;
// Validation Error Codes
//
@@ -223,9 +191,6 @@ static const int kDebugOutputPrintfStream = 3;
// Data[ i + Data[ b + Data[ s + Data[ kDebugInputBindlessInitOffset ] ] ] ]
static const int kDebugInputBindlessInitOffset = 0;
-// DEPRECATED
-static const int kDebugInputBindlessOffsetReserved = 0;
-
// At offset kDebugInputBindlessOffsetLengths is some number of uints which
// provide the bindless length data. More specifically, the number of
// descriptors at (set=s, binding=b) is:
diff --git a/chromium/third_party/SPIRV-Tools/src/include/spirv-tools/optimizer.hpp b/chromium/third_party/SPIRV-Tools/src/include/spirv-tools/optimizer.hpp
index b9049232300..d393495bae3 100644
--- a/chromium/third_party/SPIRV-Tools/src/include/spirv-tools/optimizer.hpp
+++ b/chromium/third_party/SPIRV-Tools/src/include/spirv-tools/optimizer.hpp
@@ -762,10 +762,9 @@ Optimizer::PassToken CreateCombineAccessChainsPass();
// |input_length_enable| controls instrumentation of runtime descriptor array
// references, and |input_init_enable| controls instrumentation of descriptor
// initialization checking, both of which require input buffer support.
-// |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 = 2);
+ bool input_init_enable = false);
// Create a pass to instrument physical buffer address checking
// This pass instruments all physical buffer address references to check that
@@ -786,10 +785,8 @@ Optimizer::PassToken CreateInstBindlessCheckPass(
// The instrumentation will read and write buffers in debug
// descriptor set |desc_set|. It will write |shader_id| in each output record
// to identify the shader module which generated the record.
-// |version| specifies the output buffer record format.
Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
- uint32_t shader_id,
- uint32_t version = 2);
+ uint32_t shader_id);
// Create a pass to instrument OpDebugPrintf instructions.
// This pass replaces all OpDebugPrintf instructions with instructions to write
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opcode.cpp b/chromium/third_party/SPIRV-Tools/src/source/opcode.cpp
index 80fe3b3a9f9..079def626c6 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opcode.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/opcode.cpp
@@ -650,6 +650,22 @@ bool spvOpcodeIsCommutativeBinaryOperator(SpvOp opcode) {
}
}
+bool spvOpcodeIsLinearAlgebra(SpvOp opcode) {
+ switch (opcode) {
+ case SpvOpTranspose:
+ case SpvOpVectorTimesScalar:
+ case SpvOpMatrixTimesScalar:
+ case SpvOpVectorTimesMatrix:
+ case SpvOpMatrixTimesVector:
+ case SpvOpMatrixTimesMatrix:
+ case SpvOpOuterProduct:
+ case SpvOpDot:
+ return true;
+ default:
+ return false;
+ }
+}
+
std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode) {
switch (opcode) {
case SpvOpMemoryBarrier:
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opcode.h b/chromium/third_party/SPIRV-Tools/src/source/opcode.h
index b4f02718f87..0d8ec925716 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opcode.h
+++ b/chromium/third_party/SPIRV-Tools/src/source/opcode.h
@@ -134,6 +134,9 @@ bool spvOpcodeIsDebug(SpvOp opcode);
// where the order of the operands is irrelevant.
bool spvOpcodeIsCommutativeBinaryOperator(SpvOp opcode);
+// Returns true for opcodes that represents linear algebra instructions.
+bool spvOpcodeIsLinearAlgebra(SpvOp opcode);
+
// Returns a vector containing the indices of the memory semantics <id>
// operands for |opcode|.
std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode);
diff --git a/chromium/third_party/SPIRV-Tools/src/source/operand.cpp b/chromium/third_party/SPIRV-Tools/src/source/operand.cpp
index 755ad6ac782..09105958ed2 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/operand.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/operand.cpp
@@ -455,7 +455,7 @@ bool spvIsInIdType(spv_operand_type_t type) {
return false;
}
switch (type) {
- // Blacklist non-input IDs.
+ // Deny non-input IDs.
case SPV_OPERAND_TYPE_TYPE_ID:
case SPV_OPERAND_TYPE_RESULT_ID:
return false;
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/aggressive_dead_code_elim_pass.cpp b/chromium/third_party/SPIRV-Tools/src/source/opt/aggressive_dead_code_elim_pass.cpp
index db2b67b923d..760d8e8435e 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/aggressive_dead_code_elim_pass.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/aggressive_dead_code_elim_pass.cpp
@@ -131,11 +131,11 @@ void AggressiveDCEPass::AddStores(uint32_t ptrId) {
}
bool AggressiveDCEPass::AllExtensionsSupported() const {
- // If any extension not in whitelist, return false
+ // If any extension not in allowlist, return false
for (auto& ei : get_module()->extensions()) {
const char* extName =
reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
- if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
+ if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
return false;
}
return true;
@@ -882,14 +882,14 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
AggressiveDCEPass::AggressiveDCEPass() = default;
Pass::Status AggressiveDCEPass::Process() {
- // Initialize extensions whitelist
+ // Initialize extensions allowlist
InitExtensions();
return ProcessImpl();
}
void AggressiveDCEPass::InitExtensions() {
- extensions_whitelist_.clear();
- extensions_whitelist_.insert({
+ extensions_allowlist_.clear();
+ extensions_allowlist_.insert({
"SPV_AMD_shader_explicit_vertex_parameter",
"SPV_AMD_shader_trinary_minmax",
"SPV_AMD_gcn_shader",
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/aggressive_dead_code_elim_pass.h b/chromium/third_party/SPIRV-Tools/src/source/opt/aggressive_dead_code_elim_pass.h
index c043a96f401..131e33a26b8 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/aggressive_dead_code_elim_pass.h
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/aggressive_dead_code_elim_pass.h
@@ -87,7 +87,7 @@ class AggressiveDCEPass : public MemPass {
// to the live instruction worklist.
void AddStores(uint32_t ptrId);
- // Initialize extensions whitelist
+ // Initialize extensions allowlist
void InitExtensions();
// Return true if all extensions in this module are supported by this pass.
@@ -191,7 +191,7 @@ class AggressiveDCEPass : public MemPass {
std::vector<Instruction*> to_kill_;
// Extensions supported by this pass.
- std::unordered_set<std::string> extensions_whitelist_;
+ std::unordered_set<std::string> extensions_allowlist_;
};
} // namespace opt
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/dead_branch_elim_pass.cpp b/chromium/third_party/SPIRV-Tools/src/source/opt/dead_branch_elim_pass.cpp
index 16d9fd56361..0054f57647e 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/dead_branch_elim_pass.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/dead_branch_elim_pass.cpp
@@ -41,6 +41,7 @@ bool DeadBranchElimPass::GetConstCondition(uint32_t condId, bool* condVal) {
bool condIsConst;
Instruction* cInst = get_def_use_mgr()->GetDef(condId);
switch (cInst->opcode()) {
+ case SpvOpConstantNull:
case SpvOpConstantFalse: {
*condVal = false;
condIsConst = true;
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/debug_info_manager.cpp b/chromium/third_party/SPIRV-Tools/src/source/opt/debug_info_manager.cpp
index 3df3f2b49cc..3e17d4b81fd 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/debug_info_manager.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/debug_info_manager.cpp
@@ -24,10 +24,54 @@ static const uint32_t kOpLineOperandLineIndex = 1;
static const uint32_t kLineOperandIndexDebugFunction = 7;
static const uint32_t kLineOperandIndexDebugLexicalBlock = 5;
static const uint32_t kDebugFunctionOperandFunctionIndex = 13;
+static const uint32_t kDebugFunctionOperandParentIndex = 9;
+static const uint32_t kDebugTypeCompositeOperandParentIndex = 9;
+static const uint32_t kDebugLexicalBlockOperandParentIndex = 7;
+static const uint32_t kDebugInlinedAtOperandInlinedIndex = 6;
+static const uint32_t kDebugExpressOperandOperationIndex = 4;
+static const uint32_t kDebugDeclareOperandLocalVariableIndex = 4;
+static const uint32_t kDebugDeclareOperandVariableIndex = 5;
+static const uint32_t kDebugValueOperandLocalVariableIndex = 4;
+static const uint32_t kDebugValueOperandExpressionIndex = 6;
+static const uint32_t kDebugOperationOperandOperationIndex = 4;
+static const uint32_t kOpVariableOperandStorageClassIndex = 2;
+static const uint32_t kDebugLocalVariableOperandParentIndex = 9;
namespace spvtools {
namespace opt {
namespace analysis {
+namespace {
+
+void SetInlinedOperand(Instruction* dbg_inlined_at, uint32_t inlined_operand) {
+ assert(dbg_inlined_at);
+ assert(dbg_inlined_at->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugInlinedAt);
+ if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex) {
+ dbg_inlined_at->AddOperand(
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inlined_operand}});
+ } else {
+ dbg_inlined_at->SetOperand(kDebugInlinedAtOperandInlinedIndex,
+ {inlined_operand});
+ }
+}
+
+uint32_t GetInlinedOperand(Instruction* dbg_inlined_at) {
+ assert(dbg_inlined_at);
+ assert(dbg_inlined_at->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugInlinedAt);
+ if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex)
+ return kNoInlinedAt;
+ return dbg_inlined_at->GetSingleWordOperand(
+ kDebugInlinedAtOperandInlinedIndex);
+}
+
+bool IsEmptyDebugExpression(Instruction* instr) {
+ return instr->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugExpression &&
+ instr->NumOperands() == kDebugExpressOperandOperationIndex;
+}
+
+} // namespace
DebugInfoManager::DebugInfoManager(IRContext* c) : context_(c) {
AnalyzeDebugInsts(*c->module());
@@ -57,6 +101,20 @@ void DebugInfoManager::RegisterDbgFunction(Instruction* inst) {
fn_id_to_dbg_fn_[fn_id] = inst;
}
+void DebugInfoManager::RegisterDbgDeclare(uint32_t var_id,
+ Instruction* dbg_declare) {
+ assert(dbg_declare->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugDeclare ||
+ dbg_declare->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugValue);
+ auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_id);
+ if (dbg_decl_itr == var_id_to_dbg_decl_.end()) {
+ var_id_to_dbg_decl_[var_id] = {dbg_declare};
+ } else {
+ dbg_decl_itr->second.push_back(dbg_declare);
+ }
+}
+
uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
const DebugScope& scope) {
if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() ==
@@ -113,14 +171,79 @@ uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
// |scope| already has DebugInlinedAt. We put the existing DebugInlinedAt
// into the Inlined operand of this new DebugInlinedAt.
if (scope.GetInlinedAt() != kNoInlinedAt) {
- inlined_at->AddOperand({spv_operand_type_t::SPV_OPERAND_TYPE_RESULT_ID,
- {scope.GetInlinedAt()}});
+ inlined_at->AddOperand(
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {scope.GetInlinedAt()}});
}
RegisterDbgInst(inlined_at.get());
+ if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+ context()->get_def_use_mgr()->AnalyzeInstDefUse(inlined_at.get());
context()->module()->AddExtInstDebugInfo(std::move(inlined_at));
return result_id;
}
+DebugScope DebugInfoManager::BuildDebugScope(
+ const DebugScope& callee_instr_scope,
+ DebugInlinedAtContext* inlined_at_ctx) {
+ return DebugScope(callee_instr_scope.GetLexicalScope(),
+ BuildDebugInlinedAtChain(callee_instr_scope.GetInlinedAt(),
+ inlined_at_ctx));
+}
+
+uint32_t DebugInfoManager::BuildDebugInlinedAtChain(
+ uint32_t callee_inlined_at, DebugInlinedAtContext* inlined_at_ctx) {
+ if (inlined_at_ctx->GetScopeOfCallInstruction().GetLexicalScope() ==
+ kNoDebugScope)
+ return kNoInlinedAt;
+
+ // Reuse the already generated DebugInlinedAt chain if exists.
+ uint32_t already_generated_chain_head_id =
+ inlined_at_ctx->GetDebugInlinedAtChain(callee_inlined_at);
+ if (already_generated_chain_head_id != kNoInlinedAt) {
+ return already_generated_chain_head_id;
+ }
+
+ const uint32_t new_dbg_inlined_at_id =
+ CreateDebugInlinedAt(inlined_at_ctx->GetLineOfCallInstruction(),
+ inlined_at_ctx->GetScopeOfCallInstruction());
+ if (new_dbg_inlined_at_id == kNoInlinedAt) return kNoInlinedAt;
+
+ if (callee_inlined_at == kNoInlinedAt) {
+ inlined_at_ctx->SetDebugInlinedAtChain(kNoInlinedAt, new_dbg_inlined_at_id);
+ return new_dbg_inlined_at_id;
+ }
+
+ uint32_t chain_head_id = kNoInlinedAt;
+ uint32_t chain_iter_id = callee_inlined_at;
+ Instruction* last_inlined_at_in_chain = nullptr;
+ do {
+ Instruction* new_inlined_at_in_chain = CloneDebugInlinedAt(
+ chain_iter_id, /* insert_before */ last_inlined_at_in_chain);
+ assert(new_inlined_at_in_chain != nullptr);
+
+ // Set DebugInlinedAt of the new scope as the head of the chain.
+ if (chain_head_id == kNoInlinedAt)
+ chain_head_id = new_inlined_at_in_chain->result_id();
+
+ // Previous DebugInlinedAt of the chain must point to the new
+ // DebugInlinedAt as its Inlined operand to build a recursive
+ // chain.
+ if (last_inlined_at_in_chain != nullptr) {
+ SetInlinedOperand(last_inlined_at_in_chain,
+ new_inlined_at_in_chain->result_id());
+ }
+ last_inlined_at_in_chain = new_inlined_at_in_chain;
+
+ chain_iter_id = GetInlinedOperand(new_inlined_at_in_chain);
+ } while (chain_iter_id != kNoInlinedAt);
+
+ // Put |new_dbg_inlined_at_id| into the end of the chain.
+ SetInlinedOperand(last_inlined_at_in_chain, new_dbg_inlined_at_id);
+
+ // Keep the new chain information that will be reused it.
+ inlined_at_ctx->SetDebugInlinedAtChain(callee_inlined_at, chain_head_id);
+ return chain_head_id;
+}
+
Instruction* DebugInfoManager::GetDebugInfoNone() {
if (debug_info_none_inst_ != nullptr) return debug_info_none_inst_;
@@ -129,11 +252,11 @@ Instruction* DebugInfoManager::GetDebugInfoNone() {
context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
result_id,
{
- {SPV_OPERAND_TYPE_RESULT_ID,
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
{context()
->get_feature_mgr()
->GetExtInstImportId_OpenCL100DebugInfo()}},
- {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+ {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
{static_cast<uint32_t>(OpenCLDebugInfo100DebugInfoNone)}},
}));
@@ -143,9 +266,38 @@ Instruction* DebugInfoManager::GetDebugInfoNone() {
std::move(dbg_info_none_inst));
RegisterDbgInst(debug_info_none_inst_);
+ if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+ context()->get_def_use_mgr()->AnalyzeInstDefUse(debug_info_none_inst_);
return debug_info_none_inst_;
}
+Instruction* DebugInfoManager::GetEmptyDebugExpression() {
+ if (empty_debug_expr_inst_ != nullptr) return empty_debug_expr_inst_;
+
+ uint32_t result_id = context()->TakeNextId();
+ std::unique_ptr<Instruction> empty_debug_expr(new Instruction(
+ context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
+ result_id,
+ {
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+ {context()
+ ->get_feature_mgr()
+ ->GetExtInstImportId_OpenCL100DebugInfo()}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+ {static_cast<uint32_t>(OpenCLDebugInfo100DebugExpression)}},
+ }));
+
+ // Add to the front of |ext_inst_debuginfo_|.
+ empty_debug_expr_inst_ =
+ context()->module()->ext_inst_debuginfo_begin()->InsertBefore(
+ std::move(empty_debug_expr));
+
+ RegisterDbgInst(empty_debug_expr_inst_);
+ if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+ context()->get_def_use_mgr()->AnalyzeInstDefUse(empty_debug_expr_inst_);
+ return empty_debug_expr_inst_;
+}
+
Instruction* DebugInfoManager::GetDebugInlinedAt(uint32_t dbg_inlined_at_id) {
auto* inlined_at = GetDbgInst(dbg_inlined_at_id);
if (inlined_at == nullptr) return nullptr;
@@ -163,12 +315,162 @@ Instruction* DebugInfoManager::CloneDebugInlinedAt(uint32_t clone_inlined_at_id,
std::unique_ptr<Instruction> new_inlined_at(inlined_at->Clone(context()));
new_inlined_at->SetResultId(context()->TakeNextId());
RegisterDbgInst(new_inlined_at.get());
+ if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+ context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inlined_at.get());
if (insert_before != nullptr)
return insert_before->InsertBefore(std::move(new_inlined_at));
return context()->module()->ext_inst_debuginfo_end()->InsertBefore(
std::move(new_inlined_at));
}
+uint32_t DebugInfoManager::GetParentScope(uint32_t child_scope) {
+ auto dbg_scope_itr = id_to_dbg_inst_.find(child_scope);
+ assert(dbg_scope_itr != id_to_dbg_inst_.end());
+ OpenCLDebugInfo100Instructions debug_opcode =
+ dbg_scope_itr->second->GetOpenCL100DebugOpcode();
+ uint32_t parent_scope = kNoDebugScope;
+ switch (debug_opcode) {
+ case OpenCLDebugInfo100DebugFunction:
+ parent_scope = dbg_scope_itr->second->GetSingleWordOperand(
+ kDebugFunctionOperandParentIndex);
+ break;
+ case OpenCLDebugInfo100DebugLexicalBlock:
+ parent_scope = dbg_scope_itr->second->GetSingleWordOperand(
+ kDebugLexicalBlockOperandParentIndex);
+ break;
+ case OpenCLDebugInfo100DebugTypeComposite:
+ parent_scope = dbg_scope_itr->second->GetSingleWordOperand(
+ kDebugTypeCompositeOperandParentIndex);
+ break;
+ case OpenCLDebugInfo100DebugCompilationUnit:
+ // DebugCompilationUnit does not have a parent scope.
+ break;
+ default:
+ assert(false &&
+ "Unreachable. A debug scope instruction must be "
+ "DebugFunction, DebugTypeComposite, DebugLexicalBlock, "
+ "or DebugCompilationUnit.");
+ break;
+ }
+ return parent_scope;
+}
+
+bool DebugInfoManager::IsAncestorOfScope(uint32_t scope, uint32_t ancestor) {
+ uint32_t ancestor_scope_itr = scope;
+ while (ancestor_scope_itr != kNoDebugScope) {
+ if (ancestor == ancestor_scope_itr) return true;
+ ancestor_scope_itr = GetParentScope(ancestor_scope_itr);
+ }
+ return false;
+}
+
+bool DebugInfoManager::IsDeclareVisibleToInstr(Instruction* dbg_declare,
+ uint32_t instr_scope_id) {
+ if (instr_scope_id == kNoDebugScope) return false;
+
+ uint32_t dbg_local_var_id =
+ dbg_declare->GetSingleWordOperand(kDebugDeclareOperandLocalVariableIndex);
+ auto dbg_local_var_itr = id_to_dbg_inst_.find(dbg_local_var_id);
+ assert(dbg_local_var_itr != id_to_dbg_inst_.end());
+ uint32_t decl_scope_id = dbg_local_var_itr->second->GetSingleWordOperand(
+ kDebugLocalVariableOperandParentIndex);
+
+ // If the scope of DebugDeclare is an ancestor scope of the instruction's
+ // scope, the local variable is visible to the instruction.
+ return IsAncestorOfScope(instr_scope_id, decl_scope_id);
+}
+
+void DebugInfoManager::AddDebugValue(Instruction* scope_and_line,
+ uint32_t variable_id, uint32_t value_id,
+ Instruction* insert_pos) {
+ auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
+ if (dbg_decl_itr == var_id_to_dbg_decl_.end()) return;
+
+ uint32_t instr_scope_id = scope_and_line->GetDebugScope().GetLexicalScope();
+ for (auto* dbg_decl_or_val : dbg_decl_itr->second) {
+ if (!IsDeclareVisibleToInstr(dbg_decl_or_val, instr_scope_id)) continue;
+
+ uint32_t result_id = context()->TakeNextId();
+ std::unique_ptr<Instruction> new_dbg_value(new Instruction(
+ context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
+ result_id,
+ {
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+ {context()
+ ->get_feature_mgr()
+ ->GetExtInstImportId_OpenCL100DebugInfo()}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+ {static_cast<uint32_t>(OpenCLDebugInfo100DebugValue)}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+ {dbg_decl_or_val->GetSingleWordOperand(
+ kDebugValueOperandLocalVariableIndex)}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {value_id}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+ {GetEmptyDebugExpression()->result_id()}},
+ }));
+
+ if (dbg_decl_or_val->NumOperands() >
+ kDebugValueOperandExpressionIndex + 1) {
+ assert(dbg_decl_or_val->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugValue);
+ for (uint32_t i = kDebugValueOperandExpressionIndex + 1;
+ i < dbg_decl_or_val->NumOperands(); ++i) {
+ new_dbg_value->AddOperand({spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+ {dbg_decl_or_val->GetSingleWordOperand(i)}});
+ }
+ }
+
+ // Avoid inserting the new DebugValue between OpPhi or OpVariable
+ // instructions.
+ Instruction* insert_before = insert_pos->NextNode();
+ while (insert_before->opcode() == SpvOpPhi ||
+ insert_before->opcode() == SpvOpVariable) {
+ insert_before = insert_before->NextNode();
+ }
+
+ Instruction* added_dbg_value =
+ insert_before->InsertBefore(std::move(new_dbg_value));
+ added_dbg_value->UpdateDebugInfo(scope_and_line);
+ AnalyzeDebugInst(added_dbg_value);
+ if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+ context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_value);
+ }
+}
+
+uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare(
+ Instruction* inst) {
+ if (inst->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugValue) return 0;
+
+ auto* expr =
+ GetDbgInst(inst->GetSingleWordOperand(kDebugValueOperandExpressionIndex));
+ if (expr == nullptr) return 0;
+ if (expr->NumOperands() != kDebugExpressOperandOperationIndex + 1) return 0;
+
+ auto* operation = GetDbgInst(
+ expr->GetSingleWordOperand(kDebugExpressOperandOperationIndex));
+ if (operation == nullptr) return 0;
+ if (operation->GetSingleWordOperand(kDebugOperationOperandOperationIndex) !=
+ OpenCLDebugInfo100Deref) {
+ return 0;
+ }
+
+ uint32_t var_id =
+ inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
+ if (!context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) {
+ assert(false &&
+ "Checking a DebugValue can be used for declare needs DefUseManager");
+ return 0;
+ }
+
+ auto* var = context()->get_def_use_mgr()->GetDef(var_id);
+ if (var->opcode() == SpvOpVariable &&
+ SpvStorageClass(var->GetSingleWordOperand(
+ kOpVariableOperandStorageClassIndex)) == SpvStorageClassFunction) {
+ return var_id;
+ }
+ return 0;
+}
+
void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
if (dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100InstructionsMax)
return;
@@ -186,12 +488,37 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) {
debug_info_none_inst_ = dbg_inst;
}
+
+ if (empty_debug_expr_inst_ == nullptr && IsEmptyDebugExpression(dbg_inst)) {
+ empty_debug_expr_inst_ = dbg_inst;
+ }
+
+ if (dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) {
+ uint32_t var_id =
+ dbg_inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
+ RegisterDbgDeclare(var_id, dbg_inst);
+ }
+
+ if (uint32_t var_id = GetVariableIdOfDebugValueUsedForDeclare(dbg_inst)) {
+ RegisterDbgDeclare(var_id, dbg_inst);
+ }
}
void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
debug_info_none_inst_ = nullptr;
+ empty_debug_expr_inst_ = nullptr;
module.ForEachInst([this](Instruction* cpi) { AnalyzeDebugInst(cpi); });
+ // Move |empty_debug_expr_inst_| to the beginning of the debug instruction
+ // list.
+ if (empty_debug_expr_inst_ != nullptr &&
+ empty_debug_expr_inst_->PreviousNode() != nullptr &&
+ empty_debug_expr_inst_->PreviousNode()->GetOpenCL100DebugOpcode() !=
+ OpenCLDebugInfo100InstructionsMax) {
+ empty_debug_expr_inst_->InsertBefore(
+ &*context()->module()->ext_inst_debuginfo_begin());
+ }
+
// Move |debug_info_none_inst_| to the beginning of the debug instruction
// list.
if (debug_info_none_inst_ != nullptr &&
@@ -203,6 +530,50 @@ void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
}
}
+void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
+ if (instr == nullptr ||
+ instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100InstructionsMax) {
+ return;
+ }
+
+ id_to_dbg_inst_.erase(instr->result_id());
+
+ if (instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) {
+ auto fn_id =
+ instr->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex);
+ fn_id_to_dbg_fn_.erase(fn_id);
+ }
+
+ if (instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) {
+ auto var_id =
+ instr->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
+ var_id_to_dbg_decl_.erase(var_id);
+ }
+
+ if (debug_info_none_inst_ == instr) {
+ debug_info_none_inst_ = nullptr;
+ for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin();
+ dbg_instr_itr != context()->module()->ext_inst_debuginfo_end();
+ ++dbg_instr_itr) {
+ if (dbg_instr_itr->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugInfoNone) {
+ debug_info_none_inst_ = &*dbg_instr_itr;
+ }
+ }
+ }
+
+ if (empty_debug_expr_inst_ == instr) {
+ empty_debug_expr_inst_ = nullptr;
+ for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin();
+ dbg_instr_itr != context()->module()->ext_inst_debuginfo_end();
+ ++dbg_instr_itr) {
+ if (IsEmptyDebugExpression(&*dbg_instr_itr)) {
+ empty_debug_expr_inst_ = &*dbg_instr_itr;
+ }
+ }
+ }
+}
+
} // namespace analysis
} // namespace opt
} // namespace spvtools
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/debug_info_manager.h b/chromium/third_party/SPIRV-Tools/src/source/opt/debug_info_manager.h
index a2746decae6..c0ac3ce57c2 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/debug_info_manager.h
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/debug_info_manager.h
@@ -16,6 +16,7 @@
#define SOURCE_OPT_DEBUG_INFO_MANAGER_H_
#include <unordered_map>
+#include <vector>
#include "source/opt/instruction.h"
#include "source/opt/module.h"
@@ -24,6 +25,48 @@ namespace spvtools {
namespace opt {
namespace analysis {
+// When an instruction of a callee function is inlined to its caller function,
+// we need the line and the scope information of the function call instruction
+// to generate DebugInlinedAt. This class keeps the data. For multiple inlining
+// of a single instruction, we have to create multiple DebugInlinedAt
+// instructions as a chain. This class keeps the information of the generated
+// DebugInlinedAt chains to reduce the number of chains.
+class DebugInlinedAtContext {
+ public:
+ explicit DebugInlinedAtContext(Instruction* call_inst)
+ : call_inst_line_(call_inst->dbg_line_inst()),
+ call_inst_scope_(call_inst->GetDebugScope()) {}
+
+ const Instruction* GetLineOfCallInstruction() { return call_inst_line_; }
+ const DebugScope& GetScopeOfCallInstruction() { return call_inst_scope_; }
+ // Puts the DebugInlinedAt chain that is generated for the callee instruction
+ // whose DebugInlinedAt of DebugScope is |callee_instr_inlined_at| into
+ // |callee_inlined_at2chain_|.
+ void SetDebugInlinedAtChain(uint32_t callee_instr_inlined_at,
+ uint32_t chain_head_id) {
+ callee_inlined_at2chain_[callee_instr_inlined_at] = chain_head_id;
+ }
+ // Gets the DebugInlinedAt chain from |callee_inlined_at2chain_|.
+ uint32_t GetDebugInlinedAtChain(uint32_t callee_instr_inlined_at) {
+ auto chain_itr = callee_inlined_at2chain_.find(callee_instr_inlined_at);
+ if (chain_itr != callee_inlined_at2chain_.end()) return chain_itr->second;
+ return kNoInlinedAt;
+ }
+
+ private:
+ // The line information of the function call instruction that will be
+ // replaced by the callee function.
+ const Instruction* call_inst_line_;
+
+ // The scope information of the function call instruction that will be
+ // replaced by the callee function.
+ const DebugScope call_inst_scope_;
+
+ // Map from DebugInlinedAt ids of callee to head ids of new generated
+ // DebugInlinedAt chain.
+ std::unordered_map<uint32_t, uint32_t> callee_inlined_at2chain_;
+};
+
// A class for analyzing, managing, and creating OpenCL.DebugInfo.100 extension
// instructions.
class DebugInfoManager {
@@ -74,6 +117,27 @@ class DebugInfoManager {
Instruction* CloneDebugInlinedAt(uint32_t clone_inlined_at_id,
Instruction* insert_before = nullptr);
+ // Returns the debug scope corresponding to an inlining instruction in the
+ // scope |callee_instr_scope| into |inlined_at_ctx|. Generates all new
+ // debug instructions needed to represent the scope.
+ DebugScope BuildDebugScope(const DebugScope& callee_instr_scope,
+ DebugInlinedAtContext* inlined_at_ctx);
+
+ // Returns DebugInlinedAt corresponding to inlining an instruction, which
+ // was inlined at |callee_inlined_at|, into |inlined_at_ctx|. Generates all
+ // new debug instructions needed to represent the DebugInlinedAt.
+ uint32_t BuildDebugInlinedAtChain(uint32_t callee_inlined_at,
+ DebugInlinedAtContext* inlined_at_ctx);
+
+ // Generates a DebugValue instruction with value |value_id| for every local
+ // variable that is in the scope of |scope_and_line| and whose memory is
+ // |variable_id| and inserts it after the instruction |insert_pos|.
+ void AddDebugValue(Instruction* scope_and_line, uint32_t variable_id,
+ uint32_t value_id, Instruction* insert_pos);
+
+ // Erases |instr| from data structures of this class.
+ void ClearDebugInfo(Instruction* instr);
+
private:
IRContext* context() { return context_; }
@@ -93,6 +157,30 @@ class DebugInfoManager {
// in |inst| must not already be registered.
void RegisterDbgFunction(Instruction* inst);
+ // Register the DebugDeclare instruction |dbg_declare| into
+ // |var_id_to_dbg_decl_| using OpVariable id |var_id| as a key.
+ void RegisterDbgDeclare(uint32_t var_id, Instruction* dbg_declare);
+
+ // Returns a DebugExpression instruction without Operation operands.
+ Instruction* GetEmptyDebugExpression();
+
+ // Returns the id of Value operand if |inst| is DebugValue who has Deref
+ // operation and its Value operand is a result id of OpVariable with
+ // Function storage class. Otherwise, returns 0.
+ uint32_t GetVariableIdOfDebugValueUsedForDeclare(Instruction* inst);
+
+ // Returns true if a scope |ancestor| is |scope| or an ancestor scope
+ // of |scope|.
+ bool IsAncestorOfScope(uint32_t scope, uint32_t ancestor);
+
+ // Returns true if the declaration of a local variable |dbg_declare|
+ // is visible in the scope of an instruction |instr_scope_id|.
+ bool IsDeclareVisibleToInstr(Instruction* dbg_declare,
+ uint32_t instr_scope_id);
+
+ // Returns the parent scope of the scope |child_scope|.
+ uint32_t GetParentScope(uint32_t child_scope);
+
IRContext* context_;
// Mapping from ids of OpenCL.DebugInfo.100 extension instructions
@@ -103,9 +191,18 @@ class DebugInfoManager {
// operand is the function.
std::unordered_map<uint32_t, Instruction*> fn_id_to_dbg_fn_;
+ // Mapping from local variable ids to DebugDeclare instructions whose
+ // operand is the local variable.
+ std::unordered_map<uint32_t, std::vector<Instruction*>> var_id_to_dbg_decl_;
+
// DebugInfoNone instruction. We need only a single DebugInfoNone.
// To reuse the existing one, we keep it using this member variable.
Instruction* debug_info_none_inst_;
+
+ // DebugExpression instruction without Operation operands. We need only
+ // a single DebugExpression without Operation operands. To reuse the
+ // existing one, we keep it using this member variable.
+ Instruction* empty_debug_expr_inst_;
};
} // namespace analysis
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/desc_sroa.cpp b/chromium/third_party/SPIRV-Tools/src/source/opt/desc_sroa.cpp
index 1f25b33b82f..b68549a0c53 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/desc_sroa.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/desc_sroa.cpp
@@ -56,7 +56,23 @@ bool DescriptorScalarReplacement::IsCandidate(Instruction* var) {
uint32_t var_type_id = ptr_type_inst->GetSingleWordInOperand(1);
Instruction* var_type_inst =
context()->get_def_use_mgr()->GetDef(var_type_id);
- if (var_type_inst->opcode() != SpvOpTypeArray) {
+ if (var_type_inst->opcode() != SpvOpTypeArray &&
+ var_type_inst->opcode() != SpvOpTypeStruct) {
+ return false;
+ }
+
+ // All structures with descriptor assignments must be replaced by variables,
+ // one for each of their members - with the exceptions of buffers.
+ // Buffers are represented as structures, but we shouldn't replace a buffer
+ // with its elements. All buffers have offset decorations for members of their
+ // structure types.
+ bool has_offset_decoration = false;
+ context()->get_decoration_mgr()->ForEachDecoration(
+ var_type_inst->result_id(), SpvDecorationOffset,
+ [&has_offset_decoration](const Instruction&) {
+ has_offset_decoration = true;
+ });
+ if (has_offset_decoration) {
return false;
}
@@ -84,9 +100,11 @@ bool DescriptorScalarReplacement::IsCandidate(Instruction* var) {
}
bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) {
- std::vector<Instruction*> work_list;
+ std::vector<Instruction*> access_chain_work_list;
+ std::vector<Instruction*> load_work_list;
bool failed = !get_def_use_mgr()->WhileEachUser(
- var->result_id(), [this, &work_list](Instruction* use) {
+ var->result_id(),
+ [this, &access_chain_work_list, &load_work_list](Instruction* use) {
if (use->opcode() == SpvOpName) {
return true;
}
@@ -98,7 +116,10 @@ bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) {
switch (use->opcode()) {
case SpvOpAccessChain:
case SpvOpInBoundsAccessChain:
- work_list.push_back(use);
+ access_chain_work_list.push_back(use);
+ return true;
+ case SpvOpLoad:
+ load_work_list.push_back(use);
return true;
default:
context()->EmitErrorMessage(
@@ -112,11 +133,16 @@ bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) {
return false;
}
- for (Instruction* use : work_list) {
+ for (Instruction* use : access_chain_work_list) {
if (!ReplaceAccessChain(var, use)) {
return false;
}
}
+ for (Instruction* use : load_work_list) {
+ if (!ReplaceLoadedValue(var, use)) {
+ return false;
+ }
+ }
return true;
}
@@ -177,21 +203,36 @@ uint32_t DescriptorScalarReplacement::GetReplacementVariable(Instruction* var,
uint32_t ptr_type_id = var->type_id();
Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id);
assert(ptr_type_inst->opcode() == SpvOpTypePointer &&
- "Variable should be a pointer to an array.");
- uint32_t arr_type_id = ptr_type_inst->GetSingleWordInOperand(1);
- Instruction* arr_type_inst = get_def_use_mgr()->GetDef(arr_type_id);
- assert(arr_type_inst->opcode() == SpvOpTypeArray &&
- "Variable should be a pointer to an array.");
-
- uint32_t array_len_id = arr_type_inst->GetSingleWordInOperand(1);
- const analysis::Constant* array_len_const =
- context()->get_constant_mgr()->FindDeclaredConstant(array_len_id);
- assert(array_len_const != nullptr && "Array length must be a constant.");
- uint32_t array_len = array_len_const->GetU32();
-
- replacement_vars = replacement_variables_
- .insert({var, std::vector<uint32_t>(array_len, 0)})
- .first;
+ "Variable should be a pointer to an array or structure.");
+ uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1);
+ Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(pointee_type_id);
+ const bool is_array = pointee_type_inst->opcode() == SpvOpTypeArray;
+ const bool is_struct = pointee_type_inst->opcode() == SpvOpTypeStruct;
+ assert((is_array || is_struct) &&
+ "Variable should be a pointer to an array or structure.");
+
+ // For arrays, each array element should be replaced with a new replacement
+ // variable
+ if (is_array) {
+ uint32_t array_len_id = pointee_type_inst->GetSingleWordInOperand(1);
+ const analysis::Constant* array_len_const =
+ context()->get_constant_mgr()->FindDeclaredConstant(array_len_id);
+ assert(array_len_const != nullptr && "Array length must be a constant.");
+ uint32_t array_len = array_len_const->GetU32();
+
+ replacement_vars = replacement_variables_
+ .insert({var, std::vector<uint32_t>(array_len, 0)})
+ .first;
+ }
+ // For structures, each member should be replaced with a new replacement
+ // variable
+ if (is_struct) {
+ const uint32_t num_members = pointee_type_inst->NumInOperands();
+ replacement_vars =
+ replacement_variables_
+ .insert({var, std::vector<uint32_t>(num_members, 0)})
+ .first;
+ }
}
if (replacement_vars->second[idx] == 0) {
@@ -212,12 +253,17 @@ uint32_t DescriptorScalarReplacement::CreateReplacementVariable(
uint32_t ptr_type_id = var->type_id();
Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id);
assert(ptr_type_inst->opcode() == SpvOpTypePointer &&
- "Variable should be a pointer to an array.");
- uint32_t arr_type_id = ptr_type_inst->GetSingleWordInOperand(1);
- Instruction* arr_type_inst = get_def_use_mgr()->GetDef(arr_type_id);
- assert(arr_type_inst->opcode() == SpvOpTypeArray &&
- "Variable should be a pointer to an array.");
- uint32_t element_type_id = arr_type_inst->GetSingleWordInOperand(0);
+ "Variable should be a pointer to an array or structure.");
+ uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1);
+ Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(pointee_type_id);
+ const bool is_array = pointee_type_inst->opcode() == SpvOpTypeArray;
+ const bool is_struct = pointee_type_inst->opcode() == SpvOpTypeStruct;
+ assert((is_array || is_struct) &&
+ "Variable should be a pointer to an array or structure.");
+
+ uint32_t element_type_id =
+ is_array ? pointee_type_inst->GetSingleWordInOperand(0)
+ : pointee_type_inst->GetSingleWordInOperand(idx);
uint32_t ptr_element_type_id = context()->get_type_mgr()->FindPointerToType(
element_type_id, storage_class);
@@ -242,19 +288,42 @@ uint32_t DescriptorScalarReplacement::CreateReplacementVariable(
uint32_t decoration = new_decoration->GetSingleWordInOperand(1u);
if (decoration == SpvDecorationBinding) {
- uint32_t new_binding = new_decoration->GetSingleWordInOperand(2) + idx;
+ uint32_t new_binding = new_decoration->GetSingleWordInOperand(2);
+ if (is_array) {
+ new_binding += idx * GetNumBindingsUsedByType(ptr_element_type_id);
+ }
+ if (is_struct) {
+ // The binding offset that should be added is the sum of binding numbers
+ // used by previous members of the current struct.
+ for (uint32_t i = 0; i < idx; ++i) {
+ new_binding += GetNumBindingsUsedByType(
+ pointee_type_inst->GetSingleWordInOperand(i));
+ }
+ }
new_decoration->SetInOperand(2, {new_binding});
}
context()->AddAnnotationInst(std::move(new_decoration));
}
// Create a new OpName for the replacement variable.
+ std::vector<std::unique_ptr<Instruction>> names_to_add;
for (auto p : context()->GetNames(var->result_id())) {
Instruction* name_inst = p.second;
std::string name_str = utils::MakeString(name_inst->GetOperand(1).words);
- name_str += "[";
- name_str += utils::ToString(idx);
- name_str += "]";
+ if (is_array) {
+ name_str += "[" + utils::ToString(idx) + "]";
+ }
+ if (is_struct) {
+ Instruction* member_name_inst =
+ context()->GetMemberName(pointee_type_inst->result_id(), idx);
+ name_str += ".";
+ if (member_name_inst)
+ name_str += utils::MakeString(member_name_inst->GetOperand(2).words);
+ else
+ // In case the member does not have a name assigned to it, use the
+ // member index.
+ name_str += utils::ToString(idx);
+ }
std::unique_ptr<Instruction> new_name(new Instruction(
context(), SpvOpName, 0, 0,
@@ -262,12 +331,118 @@ uint32_t DescriptorScalarReplacement::CreateReplacementVariable(
{SPV_OPERAND_TYPE_ID, {id}},
{SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}}));
Instruction* new_name_inst = new_name.get();
- context()->AddDebug2Inst(std::move(new_name));
get_def_use_mgr()->AnalyzeInstDefUse(new_name_inst);
+ names_to_add.push_back(std::move(new_name));
}
+ // We shouldn't add the new names when we are iterating over name ranges
+ // above. We can add all the new names now.
+ for (auto& new_name : names_to_add)
+ context()->AddDebug2Inst(std::move(new_name));
+
return id;
}
+uint32_t DescriptorScalarReplacement::GetNumBindingsUsedByType(
+ uint32_t type_id) {
+ Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
+
+ // If it's a pointer, look at the underlying type.
+ if (type_inst->opcode() == SpvOpTypePointer) {
+ type_id = type_inst->GetSingleWordInOperand(1);
+ type_inst = get_def_use_mgr()->GetDef(type_id);
+ }
+
+ // Arrays consume N*M binding numbers where N is the array length, and M is
+ // the number of bindings used by each array element.
+ if (type_inst->opcode() == SpvOpTypeArray) {
+ uint32_t element_type_id = type_inst->GetSingleWordInOperand(0);
+ uint32_t length_id = type_inst->GetSingleWordInOperand(1);
+ const analysis::Constant* length_const =
+ context()->get_constant_mgr()->FindDeclaredConstant(length_id);
+ // OpTypeArray's length must always be a constant
+ assert(length_const != nullptr);
+ uint32_t num_elems = length_const->GetU32();
+ return num_elems * GetNumBindingsUsedByType(element_type_id);
+ }
+
+ // The number of bindings consumed by a structure is the sum of the bindings
+ // used by its members.
+ if (type_inst->opcode() == SpvOpTypeStruct) {
+ uint32_t sum = 0;
+ for (uint32_t i = 0; i < type_inst->NumInOperands(); i++)
+ sum += GetNumBindingsUsedByType(type_inst->GetSingleWordInOperand(i));
+ return sum;
+ }
+
+ // All other types are considered to take up 1 binding number.
+ return 1;
+}
+
+bool DescriptorScalarReplacement::ReplaceLoadedValue(Instruction* var,
+ Instruction* value) {
+ // |var| is the global variable that has to be eliminated (OpVariable).
+ // |value| is the OpLoad instruction that has loaded |var|.
+ // The function expects all users of |value| to be OpCompositeExtract
+ // instructions. Otherwise the function returns false with an error message.
+ assert(value->opcode() == SpvOpLoad);
+ assert(value->GetSingleWordInOperand(0) == var->result_id());
+ std::vector<Instruction*> work_list;
+ bool failed = !get_def_use_mgr()->WhileEachUser(
+ value->result_id(), [this, &work_list](Instruction* use) {
+ if (use->opcode() != SpvOpCompositeExtract) {
+ context()->EmitErrorMessage(
+ "Variable cannot be replaced: invalid instruction", use);
+ return false;
+ }
+ work_list.push_back(use);
+ return true;
+ });
+
+ if (failed) {
+ return false;
+ }
+
+ for (Instruction* use : work_list) {
+ if (!ReplaceCompositeExtract(var, use)) {
+ return false;
+ }
+ }
+
+ // All usages of the loaded value have been killed. We can kill the OpLoad.
+ context()->KillInst(value);
+ return true;
+}
+
+bool DescriptorScalarReplacement::ReplaceCompositeExtract(
+ Instruction* var, Instruction* extract) {
+ assert(extract->opcode() == SpvOpCompositeExtract);
+ // We're currently only supporting extractions of one index at a time. If we
+ // need to, we can handle cases with multiple indexes in the future.
+ if (extract->NumInOperands() != 2) {
+ context()->EmitErrorMessage(
+ "Variable cannot be replaced: invalid instruction", extract);
+ return false;
+ }
+
+ uint32_t replacement_var =
+ GetReplacementVariable(var, extract->GetSingleWordInOperand(1));
+
+ // The result type of the OpLoad is the same as the result type of the
+ // OpCompositeExtract.
+ uint32_t load_id = TakeNextId();
+ std::unique_ptr<Instruction> load(
+ new Instruction(context(), SpvOpLoad, extract->type_id(), load_id,
+ std::initializer_list<Operand>{
+ {SPV_OPERAND_TYPE_ID, {replacement_var}}}));
+ Instruction* load_instr = load.get();
+ get_def_use_mgr()->AnalyzeInstDefUse(load_instr);
+ context()->set_instr_block(load_instr, context()->get_instr_block(extract));
+ extract->InsertBefore(std::move(load));
+ context()->ReplaceAllUsesWith(extract->result_id(), load_id);
+ context()->KillInst(extract);
+ return true;
+}
+
} // namespace opt
} // namespace spvtools
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/desc_sroa.h b/chromium/third_party/SPIRV-Tools/src/source/opt/desc_sroa.h
index a95c6b582c9..c3aa0ea2b1f 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/desc_sroa.h
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/desc_sroa.h
@@ -61,6 +61,20 @@ class DescriptorScalarReplacement : public Pass {
// |true| if successful.
bool ReplaceAccessChain(Instruction* var, Instruction* use);
+ // Replaces the given compososite variable |var| loaded by OpLoad |value| with
+ // replacement variables, one for each component that's accessed in the
+ // shader. Assumes that |value| is only used by OpCompositeExtract
+ // instructions, one index at a time. Returns true on success, and false
+ // otherwise.
+ bool ReplaceLoadedValue(Instruction* var, Instruction* value);
+
+ // Replaces the given OpCompositeExtract |extract| and all of its references
+ // with an OpLoad of a replacement variable. |var| is the variable with
+ // composite type whose value is being used by |extract|. Assumes that
+ // |extract| is extracting one index only. Returns true on success, and false
+ // otherwise.
+ bool ReplaceCompositeExtract(Instruction* var, Instruction* extract);
+
// Returns the id of the variable that will be used to replace the |idx|th
// element of |var|. The variable is created if it has not already been
// created.
@@ -70,6 +84,15 @@ class DescriptorScalarReplacement : public Pass {
// element of |var|.
uint32_t CreateReplacementVariable(Instruction* var, uint32_t idx);
+ // Returns the number of bindings used by the given |type_id|.
+ // All types are considered to use 1 binding slot, except:
+ // 1- A pointer type consumes as many binding numbers as its pointee.
+ // 2- An array of size N consumes N*M binding numbers, where M is the number
+ // of bindings used by each array element.
+ // 3- The number of bindings consumed by a structure is the sum of the
+ // bindings used by its members.
+ uint32_t GetNumBindingsUsedByType(uint32_t type_id);
+
// A map from an OpVariable instruction to the set of variables that will be
// used to replace it. The entry |replacement_variables_[var][i]| is the id of
// a variable that will be used in the place of the the ith element of the
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/function.cpp b/chromium/third_party/SPIRV-Tools/src/source/opt/function.cpp
index 2b7b4fb809e..320f8cabf67 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/function.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/function.cpp
@@ -154,6 +154,18 @@ void Function::ForEachParam(const std::function<void(const Instruction*)>& f,
->ForEachInst(f, run_on_debug_line_insts);
}
+void Function::ForEachDebugInstructionsInHeader(
+ const std::function<void(Instruction*)>& f) {
+ if (debug_insts_in_header_.empty()) return;
+
+ Instruction* di = &debug_insts_in_header_.front();
+ while (di != nullptr) {
+ Instruction* next_instruction = di->NextNode();
+ di->ForEachInst(f);
+ di = next_instruction;
+ }
+}
+
BasicBlock* Function::InsertBasicBlockAfter(
std::unique_ptr<BasicBlock>&& new_block, BasicBlock* position) {
for (auto bb_iter = begin(); bb_iter != end(); ++bb_iter) {
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/function.h b/chromium/third_party/SPIRV-Tools/src/source/opt/function.h
index e68a1d0a961..f5035f08b38 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/function.h
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/function.h
@@ -72,6 +72,10 @@ class Function {
// Delete all basic blocks that contain no instructions.
inline void RemoveEmptyBlocks();
+ // Removes a parameter from the function with result id equal to |id|.
+ // Does nothing if the function doesn't have such a parameter.
+ inline void RemoveParameter(uint32_t id);
+
// Saves the given function end instruction.
inline void SetFunctionEnd(std::unique_ptr<Instruction> end_inst);
@@ -133,6 +137,11 @@ class Function {
void ForEachParam(const std::function<void(Instruction*)>& f,
bool run_on_debug_line_insts = false);
+ // Runs the given function |f| on each debug instruction in this function's
+ // header in order.
+ void ForEachDebugInstructionsInHeader(
+ const std::function<void(Instruction*)>& f);
+
BasicBlock* InsertBasicBlockAfter(std::unique_ptr<BasicBlock>&& new_block,
BasicBlock* position);
@@ -214,6 +223,14 @@ inline void Function::RemoveEmptyBlocks() {
blocks_.erase(first_empty, std::end(blocks_));
}
+inline void Function::RemoveParameter(uint32_t id) {
+ params_.erase(std::remove_if(params_.begin(), params_.end(),
+ [id](const std::unique_ptr<Instruction>& param) {
+ return param->result_id() == id;
+ }),
+ params_.end());
+}
+
inline void Function::SetFunctionEnd(std::unique_ptr<Instruction> end_inst) {
end_inst_ = std::move(end_inst);
}
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/inline_pass.cpp b/chromium/third_party/SPIRV-Tools/src/source/opt/inline_pass.cpp
index 3c874a7ef00..cb5a1265ec6 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/inline_pass.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/inline_pass.cpp
@@ -20,6 +20,7 @@
#include <utility>
#include "source/cfa.h"
+#include "source/opt/reflect.h"
#include "source/util/make_unique.h"
// Indices of operands in SPIR-V instructions
@@ -83,19 +84,31 @@ void InlinePass::AddLoopMerge(uint32_t merge_id, uint32_t continue_id,
}
void InlinePass::AddStore(uint32_t ptr_id, uint32_t val_id,
- std::unique_ptr<BasicBlock>* block_ptr) {
+ std::unique_ptr<BasicBlock>* block_ptr,
+ const Instruction* line_inst,
+ const DebugScope& dbg_scope) {
std::unique_ptr<Instruction> newStore(
new Instruction(context(), SpvOpStore, 0, 0,
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {val_id}}}));
+ if (line_inst != nullptr) {
+ newStore->dbg_line_insts().push_back(*line_inst);
+ }
+ newStore->SetDebugScope(dbg_scope);
(*block_ptr)->AddInstruction(std::move(newStore));
}
void InlinePass::AddLoad(uint32_t type_id, uint32_t resultId, uint32_t ptr_id,
- std::unique_ptr<BasicBlock>* block_ptr) {
+ std::unique_ptr<BasicBlock>* block_ptr,
+ const Instruction* line_inst,
+ const DebugScope& dbg_scope) {
std::unique_ptr<Instruction> newLoad(
new Instruction(context(), SpvOpLoad, type_id, resultId,
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}}}));
+ if (line_inst != nullptr) {
+ newLoad->dbg_line_insts().push_back(*line_inst);
+ }
+ newLoad->SetDebugScope(dbg_scope);
(*block_ptr)->AddInstruction(std::move(newLoad));
}
@@ -140,10 +153,18 @@ void InlinePass::MapParams(
bool InlinePass::CloneAndMapLocals(
Function* calleeFn, std::vector<std::unique_ptr<Instruction>>* new_vars,
- std::unordered_map<uint32_t, uint32_t>* callee2caller) {
+ std::unordered_map<uint32_t, uint32_t>* callee2caller,
+ analysis::DebugInlinedAtContext* inlined_at_ctx) {
auto callee_block_itr = calleeFn->begin();
auto callee_var_itr = callee_block_itr->begin();
- while (callee_var_itr->opcode() == SpvOp::SpvOpVariable) {
+ while (callee_var_itr->opcode() == SpvOp::SpvOpVariable ||
+ callee_var_itr->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugDeclare) {
+ if (callee_var_itr->opcode() != SpvOp::SpvOpVariable) {
+ ++callee_var_itr;
+ continue;
+ }
+
std::unique_ptr<Instruction> var_inst(callee_var_itr->Clone(context()));
uint32_t newId = context()->TakeNextId();
if (newId == 0) {
@@ -151,6 +172,9 @@ bool InlinePass::CloneAndMapLocals(
}
get_decoration_mgr()->CloneDecorations(callee_var_itr->result_id(), newId);
var_inst->SetResultId(newId);
+ var_inst->UpdateDebugInlinedAt(
+ context()->get_debug_info_mgr()->BuildDebugInlinedAtChain(
+ callee_var_itr->GetDebugInlinedAt(), inlined_at_ctx));
(*callee2caller)[callee_var_itr->result_id()] = newId;
new_vars->push_back(std::move(var_inst));
++callee_var_itr;
@@ -232,6 +256,248 @@ bool InlinePass::CloneSameBlockOps(
});
}
+void InlinePass::MoveInstsBeforeEntryBlock(
+ std::unordered_map<uint32_t, Instruction*>* preCallSB,
+ BasicBlock* new_blk_ptr, BasicBlock::iterator call_inst_itr,
+ UptrVectorIterator<BasicBlock> call_block_itr) {
+ for (auto cii = call_block_itr->begin(); cii != call_inst_itr;
+ cii = call_block_itr->begin()) {
+ Instruction* inst = &*cii;
+ inst->RemoveFromList();
+ std::unique_ptr<Instruction> cp_inst(inst);
+ // Remember same-block ops for possible regeneration.
+ if (IsSameBlockOp(&*cp_inst)) {
+ auto* sb_inst_ptr = cp_inst.get();
+ (*preCallSB)[cp_inst->result_id()] = sb_inst_ptr;
+ }
+ new_blk_ptr->AddInstruction(std::move(cp_inst));
+ }
+}
+
+std::unique_ptr<BasicBlock> InlinePass::AddGuardBlock(
+ std::vector<std::unique_ptr<BasicBlock>>* new_blocks,
+ std::unordered_map<uint32_t, uint32_t>* callee2caller,
+ std::unique_ptr<BasicBlock> new_blk_ptr, uint32_t entry_blk_label_id) {
+ const auto guard_block_id = context()->TakeNextId();
+ if (guard_block_id == 0) {
+ return nullptr;
+ }
+ AddBranch(guard_block_id, &new_blk_ptr);
+ new_blocks->push_back(std::move(new_blk_ptr));
+ // Start the next block.
+ new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(guard_block_id));
+ // Reset the mapping of the callee's entry block to point to
+ // the guard block. Do this so we can fix up phis later on to
+ // satisfy dominance.
+ (*callee2caller)[entry_blk_label_id] = guard_block_id;
+ return new_blk_ptr;
+}
+
+InstructionList::iterator InlinePass::AddStoresForVariableInitializers(
+ const std::unordered_map<uint32_t, uint32_t>& callee2caller,
+ analysis::DebugInlinedAtContext* inlined_at_ctx,
+ std::unique_ptr<BasicBlock>* new_blk_ptr,
+ UptrVectorIterator<BasicBlock> callee_first_block_itr) {
+ auto callee_itr = callee_first_block_itr->begin();
+ while (callee_itr->opcode() == SpvOp::SpvOpVariable ||
+ callee_itr->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugDeclare) {
+ if (callee_itr->opcode() == SpvOp::SpvOpVariable &&
+ callee_itr->NumInOperands() == 2) {
+ assert(callee2caller.count(callee_itr->result_id()) &&
+ "Expected the variable to have already been mapped.");
+ uint32_t new_var_id = callee2caller.at(callee_itr->result_id());
+
+ // The initializer must be a constant or global value. No mapped
+ // should be used.
+ uint32_t val_id = callee_itr->GetSingleWordInOperand(1);
+ AddStore(new_var_id, val_id, new_blk_ptr, callee_itr->dbg_line_inst(),
+ context()->get_debug_info_mgr()->BuildDebugScope(
+ callee_itr->GetDebugScope(), inlined_at_ctx));
+ }
+ if (callee_itr->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugDeclare) {
+ InlineSingleInstruction(
+ callee2caller, new_blk_ptr->get(), &*callee_itr,
+ context()->get_debug_info_mgr()->BuildDebugInlinedAtChain(
+ callee_itr->GetDebugScope().GetInlinedAt(), inlined_at_ctx));
+ }
+ ++callee_itr;
+ }
+ return callee_itr;
+}
+
+bool InlinePass::InlineSingleInstruction(
+ const std::unordered_map<uint32_t, uint32_t>& callee2caller,
+ BasicBlock* new_blk_ptr, const Instruction* inst, uint32_t dbg_inlined_at) {
+ // If we have return, it must be at the end of the callee. We will handle
+ // it at the end.
+ if (inst->opcode() == SpvOpReturnValue || inst->opcode() == SpvOpReturn)
+ return true;
+
+ // Copy callee instruction and remap all input Ids.
+ std::unique_ptr<Instruction> cp_inst(inst->Clone(context()));
+ cp_inst->ForEachInId([&callee2caller](uint32_t* iid) {
+ const auto mapItr = callee2caller.find(*iid);
+ if (mapItr != callee2caller.end()) {
+ *iid = mapItr->second;
+ }
+ });
+
+ // If result id is non-zero, remap it.
+ const uint32_t rid = cp_inst->result_id();
+ if (rid != 0) {
+ const auto mapItr = callee2caller.find(rid);
+ if (mapItr == callee2caller.end()) {
+ return false;
+ }
+ uint32_t nid = mapItr->second;
+ cp_inst->SetResultId(nid);
+ get_decoration_mgr()->CloneDecorations(rid, nid);
+ }
+
+ cp_inst->UpdateDebugInlinedAt(dbg_inlined_at);
+ new_blk_ptr->AddInstruction(std::move(cp_inst));
+ return true;
+}
+
+std::unique_ptr<BasicBlock> InlinePass::InlineReturn(
+ const std::unordered_map<uint32_t, uint32_t>& callee2caller,
+ std::vector<std::unique_ptr<BasicBlock>>* new_blocks,
+ std::unique_ptr<BasicBlock> new_blk_ptr,
+ analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn,
+ const Instruction* inst, uint32_t returnVarId) {
+ // Store return value to return variable.
+ if (inst->opcode() == SpvOpReturnValue) {
+ assert(returnVarId != 0);
+ uint32_t valId = inst->GetInOperand(kSpvReturnValueId).words[0];
+ const auto mapItr = callee2caller.find(valId);
+ if (mapItr != callee2caller.end()) {
+ valId = mapItr->second;
+ }
+ AddStore(returnVarId, valId, &new_blk_ptr, inst->dbg_line_inst(),
+ context()->get_debug_info_mgr()->BuildDebugScope(
+ inst->GetDebugScope(), inlined_at_ctx));
+ }
+
+ uint32_t returnLabelId = 0;
+ for (auto callee_block_itr = calleeFn->begin();
+ callee_block_itr != calleeFn->end(); ++callee_block_itr) {
+ if (callee_block_itr->tail()->opcode() == SpvOpUnreachable ||
+ callee_block_itr->tail()->opcode() == SpvOpKill) {
+ returnLabelId = context()->TakeNextId();
+ break;
+ }
+ }
+ if (returnLabelId == 0) return new_blk_ptr;
+
+ if (inst->opcode() == SpvOpReturn || inst->opcode() == SpvOpReturnValue)
+ AddBranch(returnLabelId, &new_blk_ptr);
+ new_blocks->push_back(std::move(new_blk_ptr));
+ return MakeUnique<BasicBlock>(NewLabel(returnLabelId));
+}
+
+bool InlinePass::InlineEntryBlock(
+ const std::unordered_map<uint32_t, uint32_t>& callee2caller,
+ std::unique_ptr<BasicBlock>* new_blk_ptr,
+ UptrVectorIterator<BasicBlock> callee_first_block,
+ analysis::DebugInlinedAtContext* inlined_at_ctx) {
+ auto callee_inst_itr = AddStoresForVariableInitializers(
+ callee2caller, inlined_at_ctx, new_blk_ptr, callee_first_block);
+
+ while (callee_inst_itr != callee_first_block->end()) {
+ if (!InlineSingleInstruction(
+ callee2caller, new_blk_ptr->get(), &*callee_inst_itr,
+ context()->get_debug_info_mgr()->BuildDebugInlinedAtChain(
+ callee_inst_itr->GetDebugScope().GetInlinedAt(),
+ inlined_at_ctx))) {
+ return false;
+ }
+ ++callee_inst_itr;
+ }
+ return true;
+}
+
+std::unique_ptr<BasicBlock> InlinePass::InlineBasicBlocks(
+ std::vector<std::unique_ptr<BasicBlock>>* new_blocks,
+ const std::unordered_map<uint32_t, uint32_t>& callee2caller,
+ std::unique_ptr<BasicBlock> new_blk_ptr,
+ analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn) {
+ auto callee_block_itr = calleeFn->begin();
+ ++callee_block_itr;
+
+ while (callee_block_itr != calleeFn->end()) {
+ new_blocks->push_back(std::move(new_blk_ptr));
+ const auto mapItr =
+ callee2caller.find(callee_block_itr->GetLabelInst()->result_id());
+ if (mapItr == callee2caller.end()) return nullptr;
+ new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(mapItr->second));
+
+ auto tail_inst_itr = callee_block_itr->end();
+ for (auto inst_itr = callee_block_itr->begin(); inst_itr != tail_inst_itr;
+ ++inst_itr) {
+ if (!InlineSingleInstruction(
+ callee2caller, new_blk_ptr.get(), &*inst_itr,
+ context()->get_debug_info_mgr()->BuildDebugInlinedAtChain(
+ inst_itr->GetDebugScope().GetInlinedAt(), inlined_at_ctx))) {
+ return nullptr;
+ }
+ }
+
+ ++callee_block_itr;
+ }
+ return new_blk_ptr;
+}
+
+bool InlinePass::MoveCallerInstsAfterFunctionCall(
+ std::unordered_map<uint32_t, Instruction*>* preCallSB,
+ std::unordered_map<uint32_t, uint32_t>* postCallSB,
+ std::unique_ptr<BasicBlock>* new_blk_ptr,
+ BasicBlock::iterator call_inst_itr, bool multiBlocks) {
+ // Copy remaining instructions from caller block.
+ for (Instruction* inst = call_inst_itr->NextNode(); inst;
+ inst = call_inst_itr->NextNode()) {
+ inst->RemoveFromList();
+ std::unique_ptr<Instruction> cp_inst(inst);
+ // If multiple blocks generated, regenerate any same-block
+ // instruction that has not been seen in this last block.
+ if (multiBlocks) {
+ if (!CloneSameBlockOps(&cp_inst, postCallSB, preCallSB, new_blk_ptr)) {
+ return false;
+ }
+
+ // Remember same-block ops in this block.
+ if (IsSameBlockOp(&*cp_inst)) {
+ const uint32_t rid = cp_inst->result_id();
+ (*postCallSB)[rid] = rid;
+ }
+ }
+ new_blk_ptr->get()->AddInstruction(std::move(cp_inst));
+ }
+
+ return true;
+}
+
+void InlinePass::MoveLoopMergeInstToFirstBlock(
+ std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
+ // Move the OpLoopMerge from the last block back to the first, where
+ // it belongs.
+ auto& first = new_blocks->front();
+ auto& last = new_blocks->back();
+ assert(first != last);
+
+ // Insert a modified copy of the loop merge into the first block.
+ auto loop_merge_itr = last->tail();
+ --loop_merge_itr;
+ assert(loop_merge_itr->opcode() == SpvOpLoopMerge);
+ std::unique_ptr<Instruction> cp_inst(loop_merge_itr->Clone(context()));
+ first->tail().InsertBefore(std::move(cp_inst));
+
+ // Remove the loop merge from the last block.
+ loop_merge_itr->RemoveFromList();
+ delete &*loop_merge_itr;
+}
+
bool InlinePass::GenInlineCode(
std::vector<std::unique_ptr<BasicBlock>>* new_blocks,
std::vector<std::unique_ptr<Instruction>>* new_vars,
@@ -245,27 +511,60 @@ bool InlinePass::GenInlineCode(
// Post-call same-block op ids
std::unordered_map<uint32_t, uint32_t> postCallSB;
+ analysis::DebugInlinedAtContext inlined_at_ctx(&*call_inst_itr);
+
// Invalidate the def-use chains. They are not kept up to date while
// inlining. However, certain calls try to keep them up-to-date if they are
// valid. These operations can fail.
context()->InvalidateAnalyses(IRContext::kAnalysisDefUse);
+ // 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 = call_block_itr->GetLoopMergeInst() != nullptr;
+
+ // Single-trip loop continue block
+ std::unique_ptr<BasicBlock> single_trip_loop_cont_blk;
+
Function* calleeFn = id2function_[call_inst_itr->GetSingleWordOperand(
kSpvFunctionCallFunctionId)];
- // Check for multiple returns in the callee.
- auto fi = early_return_funcs_.find(calleeFn->result_id());
- const bool earlyReturn = fi != early_return_funcs_.end();
-
// Map parameters to actual arguments.
MapParams(calleeFn, call_inst_itr, &callee2caller);
// Define caller local variables for all callee variables and create map to
// them.
- if (!CloneAndMapLocals(calleeFn, new_vars, &callee2caller)) {
+ if (!CloneAndMapLocals(calleeFn, new_vars, &callee2caller, &inlined_at_ctx)) {
return false;
}
+ // First block needs to use label of original block
+ // but map callee label in case of phi reference.
+ uint32_t entry_blk_label_id = calleeFn->begin()->GetLabelInst()->result_id();
+ callee2caller[entry_blk_label_id] = call_block_itr->id();
+ std::unique_ptr<BasicBlock> new_blk_ptr =
+ MakeUnique<BasicBlock>(NewLabel(call_block_itr->id()));
+
+ // Move instructions of original caller block up to call instruction.
+ MoveInstsBeforeEntryBlock(&preCallSB, new_blk_ptr.get(), call_inst_itr,
+ call_block_itr);
+
+ if (caller_is_loop_header &&
+ (*(calleeFn->begin())).GetMergeInst() != nullptr) {
+ // We can't place both the caller's merge instruction and
+ // another merge instruction in the same block. So split the
+ // calling block. Insert an unconditional branch to a new guard
+ // block. Later, once we know the ID of the last block, we
+ // will move the caller's OpLoopMerge from the last generated
+ // block into the first block. We also wait to avoid
+ // invalidating various iterators.
+ new_blk_ptr = AddGuardBlock(new_blocks, &callee2caller,
+ std::move(new_blk_ptr), entry_blk_label_id);
+ if (new_blk_ptr == nullptr) return false;
+ }
+
// Create return var if needed.
const uint32_t calleeTypeId = calleeFn->type_id();
uint32_t returnVarId = 0;
@@ -277,341 +576,62 @@ bool InlinePass::GenInlineCode(
}
}
- // Create set of callee result ids. Used to detect forward references
- std::unordered_set<uint32_t> callee_result_ids;
- calleeFn->ForEachInst([&callee_result_ids](const Instruction* cpi) {
+ calleeFn->WhileEachInst([&callee2caller, this](const Instruction* cpi) {
+ // Create set of callee result ids. Used to detect forward references
const uint32_t rid = cpi->result_id();
- if (rid != 0) callee_result_ids.insert(rid);
+ if (rid != 0 && callee2caller.find(rid) == callee2caller.end()) {
+ const uint32_t nid = context()->TakeNextId();
+ if (nid == 0) return false;
+ callee2caller[rid] = nid;
+ }
+ return true;
});
- // 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 (call_block_itr->GetLoopMergeInst()) {
- caller_is_loop_header = true;
- }
-
- bool callee_begins_with_structured_header =
- (*(calleeFn->begin())).GetMergeInst() != nullptr;
-
- // Clone and map callee code. Copy caller block code to beginning of
- // first block and end of last block.
- bool prevInstWasReturn = false;
- uint32_t singleTripLoopHeaderId = 0;
- uint32_t singleTripLoopContinueId = 0;
- uint32_t returnLabelId = 0;
- bool multiBlocks = false;
- // new_blk_ptr is a new basic block in the caller. New instructions are
- // written to it. It is created when we encounter the OpLabel
- // of the first callee block. It is appended to new_blocks only when
- // it is complete.
- std::unique_ptr<BasicBlock> new_blk_ptr;
- bool successful = calleeFn->WhileEachInst(
- [&new_blocks, &callee2caller, &call_block_itr, &call_inst_itr,
- &new_blk_ptr, &prevInstWasReturn, &returnLabelId, &returnVarId,
- caller_is_loop_header, callee_begins_with_structured_header,
- &calleeTypeId, &multiBlocks, &postCallSB, &preCallSB, earlyReturn,
- &singleTripLoopHeaderId, &singleTripLoopContinueId, &callee_result_ids,
- this](const Instruction* cpi) {
- switch (cpi->opcode()) {
- case SpvOpFunction:
- case SpvOpFunctionParameter:
- // Already processed
- break;
- case SpvOpVariable:
- if (cpi->NumInOperands() == 2) {
- assert(callee2caller.count(cpi->result_id()) &&
- "Expected the variable to have already been mapped.");
- uint32_t new_var_id = callee2caller.at(cpi->result_id());
-
- // The initializer must be a constant or global value. No mapped
- // should be used.
- uint32_t val_id = cpi->GetSingleWordInOperand(1);
- AddStore(new_var_id, val_id, &new_blk_ptr);
- }
- break;
- case SpvOpUnreachable:
- case SpvOpKill: {
- // Generate a return label so that we split the block with the
- // function call. Copy the terminator into the new block.
- if (returnLabelId == 0) {
- returnLabelId = context()->TakeNextId();
- if (returnLabelId == 0) {
- return false;
- }
- }
- std::unique_ptr<Instruction> terminator(
- new Instruction(context(), cpi->opcode(), 0, 0, {}));
- new_blk_ptr->AddInstruction(std::move(terminator));
- break;
- }
- case SpvOpLabel: {
- // If previous instruction was early return, insert branch
- // instruction to return block.
- if (prevInstWasReturn) {
- if (returnLabelId == 0) {
- returnLabelId = context()->TakeNextId();
- if (returnLabelId == 0) {
- return false;
- }
- }
- AddBranch(returnLabelId, &new_blk_ptr);
- prevInstWasReturn = false;
- }
- // Finish current block (if it exists) and get label for next block.
- uint32_t labelId;
- bool firstBlock = false;
- if (new_blk_ptr != nullptr) {
- new_blocks->push_back(std::move(new_blk_ptr));
- // If result id is already mapped, use it, otherwise get a new
- // one.
- const uint32_t rid = cpi->result_id();
- const auto mapItr = callee2caller.find(rid);
- labelId = (mapItr != callee2caller.end())
- ? mapItr->second
- : context()->TakeNextId();
- if (labelId == 0) {
- return false;
- }
- } else {
- // First block needs to use label of original block
- // but map callee label in case of phi reference.
- labelId = call_block_itr->id();
- callee2caller[cpi->result_id()] = labelId;
- firstBlock = true;
- }
- // Create first/next block.
- new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(labelId));
- if (firstBlock) {
- // Copy contents of original caller block up to call instruction.
- for (auto cii = call_block_itr->begin(); cii != call_inst_itr;
- cii = call_block_itr->begin()) {
- Instruction* inst = &*cii;
- inst->RemoveFromList();
- std::unique_ptr<Instruction> cp_inst(inst);
- // Remember same-block ops for possible regeneration.
- if (IsSameBlockOp(&*cp_inst)) {
- auto* sb_inst_ptr = cp_inst.get();
- preCallSB[cp_inst->result_id()] = sb_inst_ptr;
- }
- new_blk_ptr->AddInstruction(std::move(cp_inst));
- }
- if (caller_is_loop_header &&
- callee_begins_with_structured_header) {
- // We can't place both the caller's merge instruction and
- // another merge instruction in the same block. So split the
- // calling block. Insert an unconditional branch to a new guard
- // block. Later, once we know the ID of the last block, we
- // will move the caller's OpLoopMerge from the last generated
- // block into the first block. We also wait to avoid
- // invalidating various iterators.
- const auto guard_block_id = context()->TakeNextId();
- if (guard_block_id == 0) {
- return false;
- }
- AddBranch(guard_block_id, &new_blk_ptr);
- new_blocks->push_back(std::move(new_blk_ptr));
- // Start the next block.
- new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(guard_block_id));
- // Reset the mapping of the callee's entry block to point to
- // the guard block. Do this so we can fix up phis later on to
- // satisfy dominance.
- callee2caller[cpi->result_id()] = guard_block_id;
- }
- // If callee has early return, insert a header block for
- // single-trip loop that will encompass callee code. Start
- // postheader block.
- //
- // Note: Consider the following combination:
- // - the caller is a single block loop
- // - the callee does not begin with a structure header
- // - the callee has multiple returns.
- // We still need to split the caller block and insert a guard
- // block. But we only need to do it once. We haven't done it yet,
- // but the single-trip loop header will serve the same purpose.
- if (earlyReturn) {
- singleTripLoopHeaderId = context()->TakeNextId();
- if (singleTripLoopHeaderId == 0) {
- return false;
- }
- AddBranch(singleTripLoopHeaderId, &new_blk_ptr);
- new_blocks->push_back(std::move(new_blk_ptr));
- new_blk_ptr =
- MakeUnique<BasicBlock>(NewLabel(singleTripLoopHeaderId));
- returnLabelId = context()->TakeNextId();
- singleTripLoopContinueId = context()->TakeNextId();
- if (returnLabelId == 0 || singleTripLoopContinueId == 0) {
- return false;
- }
- AddLoopMerge(returnLabelId, singleTripLoopContinueId,
- &new_blk_ptr);
- uint32_t postHeaderId = context()->TakeNextId();
- if (postHeaderId == 0) {
- return false;
- }
- AddBranch(postHeaderId, &new_blk_ptr);
- new_blocks->push_back(std::move(new_blk_ptr));
- new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(postHeaderId));
- multiBlocks = true;
- // Reset the mapping of the callee's entry block to point to
- // the post-header block. Do this so we can fix up phis later
- // on to satisfy dominance.
- callee2caller[cpi->result_id()] = postHeaderId;
- }
- } else {
- multiBlocks = true;
- }
- } break;
- case SpvOpReturnValue: {
- // Store return value to return variable.
- assert(returnVarId != 0);
- uint32_t valId = cpi->GetInOperand(kSpvReturnValueId).words[0];
- const auto mapItr = callee2caller.find(valId);
- if (mapItr != callee2caller.end()) {
- valId = mapItr->second;
- }
- AddStore(returnVarId, valId, &new_blk_ptr);
-
- // Remember we saw a return; if followed by a label, will need to
- // insert branch.
- prevInstWasReturn = true;
- } break;
- case SpvOpReturn: {
- // Remember we saw a return; if followed by a label, will need to
- // insert branch.
- prevInstWasReturn = true;
- } break;
- case SpvOpFunctionEnd: {
- // If there was an early return, we generated a return label id
- // for it. Now we have to generate the return block with that Id.
- if (returnLabelId != 0) {
- // If previous instruction was return, insert branch instruction
- // to return block.
- if (prevInstWasReturn) AddBranch(returnLabelId, &new_blk_ptr);
- if (earlyReturn) {
- // If we generated a loop header for the single-trip loop
- // to accommodate early returns, insert the continue
- // target block now, with a false branch back to the loop
- // header.
- new_blocks->push_back(std::move(new_blk_ptr));
- new_blk_ptr =
- MakeUnique<BasicBlock>(NewLabel(singleTripLoopContinueId));
- uint32_t false_id = GetFalseId();
- if (false_id == 0) {
- return false;
- }
- AddBranchCond(false_id, singleTripLoopHeaderId, returnLabelId,
- &new_blk_ptr);
- }
- // Generate the return block.
- new_blocks->push_back(std::move(new_blk_ptr));
- new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(returnLabelId));
- multiBlocks = true;
- }
- // Load return value into result id of call, if it exists.
- if (returnVarId != 0) {
- const uint32_t resId = call_inst_itr->result_id();
- assert(resId != 0);
- AddLoad(calleeTypeId, resId, returnVarId, &new_blk_ptr);
- }
- // Copy remaining instructions from caller block.
- for (Instruction* inst = call_inst_itr->NextNode(); inst;
- inst = call_inst_itr->NextNode()) {
- inst->RemoveFromList();
- std::unique_ptr<Instruction> cp_inst(inst);
- // If multiple blocks generated, regenerate any same-block
- // instruction that has not been seen in this last block.
- if (multiBlocks) {
- if (!CloneSameBlockOps(&cp_inst, &postCallSB, &preCallSB,
- &new_blk_ptr)) {
- return false;
- }
-
- // Remember same-block ops in this block.
- if (IsSameBlockOp(&*cp_inst)) {
- const uint32_t rid = cp_inst->result_id();
- postCallSB[rid] = rid;
- }
- }
- new_blk_ptr->AddInstruction(std::move(cp_inst));
- }
- // Finalize inline code.
- new_blocks->push_back(std::move(new_blk_ptr));
- } break;
- default: {
- // Copy callee instruction and remap all input Ids.
- std::unique_ptr<Instruction> cp_inst(cpi->Clone(context()));
- bool succeeded = cp_inst->WhileEachInId(
- [&callee2caller, &callee_result_ids, this](uint32_t* iid) {
- const auto mapItr = callee2caller.find(*iid);
- if (mapItr != callee2caller.end()) {
- *iid = mapItr->second;
- } else if (callee_result_ids.find(*iid) !=
- callee_result_ids.end()) {
- // Forward reference. Allocate a new id, map it,
- // use it and check for it when remapping result ids
- const uint32_t nid = context()->TakeNextId();
- if (nid == 0) {
- return false;
- }
- callee2caller[*iid] = nid;
- *iid = nid;
- }
- return true;
- });
- if (!succeeded) {
- return false;
- }
- // If result id is non-zero, remap it. If already mapped, use mapped
- // value, else use next id.
- const uint32_t rid = cp_inst->result_id();
- if (rid != 0) {
- const auto mapItr = callee2caller.find(rid);
- uint32_t nid;
- if (mapItr != callee2caller.end()) {
- nid = mapItr->second;
- } else {
- nid = context()->TakeNextId();
- if (nid == 0) {
- return false;
- }
- callee2caller[rid] = nid;
- }
- cp_inst->SetResultId(nid);
- get_decoration_mgr()->CloneDecorations(rid, nid);
- }
- new_blk_ptr->AddInstruction(std::move(cp_inst));
- } break;
- }
- return true;
+ // Inline DebugClare instructions in the callee's header.
+ calleeFn->ForEachDebugInstructionsInHeader(
+ [&new_blk_ptr, &callee2caller, &inlined_at_ctx, this](Instruction* inst) {
+ InlineSingleInstruction(
+ callee2caller, new_blk_ptr.get(), inst,
+ context()->get_debug_info_mgr()->BuildDebugInlinedAtChain(
+ inst->GetDebugScope().GetInlinedAt(), &inlined_at_ctx));
});
- if (!successful) {
+ // Inline the entry block of the callee function.
+ if (!InlineEntryBlock(callee2caller, &new_blk_ptr, calleeFn->begin(),
+ &inlined_at_ctx)) {
return false;
}
- if (caller_is_loop_header && (new_blocks->size() > 1)) {
- // Move the OpLoopMerge from the last block back to the first, where
- // it belongs.
- auto& first = new_blocks->front();
- auto& last = new_blocks->back();
- assert(first != last);
-
- // Insert a modified copy of the loop merge into the first block.
- auto loop_merge_itr = last->tail();
- --loop_merge_itr;
- assert(loop_merge_itr->opcode() == SpvOpLoopMerge);
- std::unique_ptr<Instruction> cp_inst(loop_merge_itr->Clone(context()));
- first->tail().InsertBefore(std::move(cp_inst));
-
- // Remove the loop merge from the last block.
- loop_merge_itr->RemoveFromList();
- delete &*loop_merge_itr;
+ // Inline blocks of the callee function other than the entry block.
+ new_blk_ptr =
+ InlineBasicBlocks(new_blocks, callee2caller, std::move(new_blk_ptr),
+ &inlined_at_ctx, calleeFn);
+ if (new_blk_ptr == nullptr) return false;
+
+ new_blk_ptr = InlineReturn(callee2caller, new_blocks, std::move(new_blk_ptr),
+ &inlined_at_ctx, calleeFn,
+ &*(calleeFn->tail()->tail()), returnVarId);
+
+ // Load return value into result id of call, if it exists.
+ if (returnVarId != 0) {
+ const uint32_t resId = call_inst_itr->result_id();
+ assert(resId != 0);
+ AddLoad(calleeTypeId, resId, returnVarId, &new_blk_ptr,
+ call_inst_itr->dbg_line_inst(), call_inst_itr->GetDebugScope());
}
+ // Move instructions of original caller block after call instruction.
+ if (!MoveCallerInstsAfterFunctionCall(&preCallSB, &postCallSB, &new_blk_ptr,
+ call_inst_itr,
+ calleeFn->begin() != calleeFn->end()))
+ return false;
+
+ // Finalize inline code.
+ new_blocks->push_back(std::move(new_blk_ptr));
+
+ if (caller_is_loop_header && (new_blocks->size() > 1))
+ MoveLoopMergeInstToFirstBlock(new_blocks);
+
// Update block map given replacement blocks.
for (auto& blk : *new_blocks) {
id2block_[blk->id()] = &*blk;
@@ -624,7 +644,21 @@ bool InlinePass::IsInlinableFunctionCall(const Instruction* inst) {
const uint32_t calleeFnId =
inst->GetSingleWordOperand(kSpvFunctionCallFunctionId);
const auto ci = inlinable_.find(calleeFnId);
- return ci != inlinable_.cend();
+ if (ci == inlinable_.cend()) return false;
+
+ if (early_return_funcs_.find(calleeFnId) != early_return_funcs_.end()) {
+ // We rely on the merge-return pass to handle the early return case
+ // in advance.
+ std::string message =
+ "The function '" + id2function_[calleeFnId]->DefInst().PrettyPrint() +
+ "' could not be inlined because the return instruction "
+ "is not at the end of the function. This could be fixed by "
+ "running merge-return before inlining.";
+ consumer()(SPV_MSG_WARNING, "", {0, 0, 0}, message.c_str());
+ return false;
+ }
+
+ return true;
}
void InlinePass::UpdateSucceedingPhis(
@@ -645,26 +679,6 @@ void InlinePass::UpdateSucceedingPhis(
});
}
-bool InlinePass::HasNoReturnInStructuredConstruct(Function* func) {
- // If control not structured, do not do loop/return analysis
- // TODO: Analyze returns in non-structured control flow
- if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
- return false;
- const auto structured_analysis = context()->GetStructuredCFGAnalysis();
- // Search for returns in structured construct.
- bool return_in_construct = false;
- for (auto& blk : *func) {
- auto terminal_ii = blk.cend();
- --terminal_ii;
- if (spvOpcodeIsReturn(terminal_ii->opcode()) &&
- structured_analysis->ContainingConstruct(blk.id()) != 0) {
- return_in_construct = true;
- break;
- }
- }
- return !return_in_construct;
-}
-
bool InlinePass::HasNoReturnInLoop(Function* func) {
// If control not structured, do not do loop/return analysis
// TODO: Analyze returns in non-structured control flow
@@ -686,10 +700,18 @@ bool InlinePass::HasNoReturnInLoop(Function* func) {
}
void InlinePass::AnalyzeReturns(Function* func) {
+ // Analyze functions without a return in loop.
if (HasNoReturnInLoop(func)) {
no_return_in_loop_.insert(func->result_id());
- if (!HasNoReturnInStructuredConstruct(func))
+ }
+ // Analyze functions with a return before its tail basic block.
+ for (auto& blk : *func) {
+ auto terminal_ii = blk.cend();
+ --terminal_ii;
+ if (spvOpcodeIsReturn(terminal_ii->opcode()) && &blk != func->tail()) {
early_return_funcs_.insert(func->result_id());
+ break;
+ }
}
}
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/inline_pass.h b/chromium/third_party/SPIRV-Tools/src/source/opt/inline_pass.h
index bc5f78127dd..202bc97fdcc 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/inline_pass.h
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/inline_pass.h
@@ -24,6 +24,7 @@
#include <unordered_map>
#include <vector>
+#include "source/opt/debug_info_manager.h"
#include "source/opt/decoration_manager.h"
#include "source/opt/module.h"
#include "source/opt/pass.h"
@@ -58,11 +59,13 @@ class InlinePass : public Pass {
// Add store of valId to ptrId to end of block block_ptr.
void AddStore(uint32_t ptrId, uint32_t valId,
- std::unique_ptr<BasicBlock>* block_ptr);
+ std::unique_ptr<BasicBlock>* block_ptr,
+ const Instruction* line_inst, const DebugScope& dbg_scope);
// Add load of ptrId into resultId to end of block block_ptr.
void AddLoad(uint32_t typeId, uint32_t resultId, uint32_t ptrId,
- std::unique_ptr<BasicBlock>* block_ptr);
+ std::unique_ptr<BasicBlock>* block_ptr,
+ const Instruction* line_inst, const DebugScope& dbg_scope);
// Return new label.
std::unique_ptr<Instruction> NewLabel(uint32_t label_id);
@@ -79,7 +82,8 @@ class InlinePass : public Pass {
// Clone and map callee locals. Return true if successful.
bool CloneAndMapLocals(Function* calleeFn,
std::vector<std::unique_ptr<Instruction>>* new_vars,
- std::unordered_map<uint32_t, uint32_t>* callee2caller);
+ std::unordered_map<uint32_t, uint32_t>* callee2caller,
+ analysis::DebugInlinedAtContext* inlined_at_ctx);
// Create return variable for callee clone code. The return type of
// |calleeFn| must not be void. Returns the id of the return variable if
@@ -124,10 +128,6 @@ class InlinePass : public Pass {
// Return true if |inst| is a function call that can be inlined.
bool IsInlinableFunctionCall(const Instruction* inst);
- // Return true if |func| does not have a return that is
- // nested in a structured if, switch or loop.
- bool HasNoReturnInStructuredConstruct(Function* func);
-
// Return true if |func| has no return in a loop. The current analysis
// requires structured control flow, so return false if control flow not
// structured ie. module is not a shader.
@@ -171,6 +171,69 @@ class InlinePass : public Pass {
// Set of functions that are originally called directly or indirectly from a
// continue construct.
std::unordered_set<uint32_t> funcs_called_from_continue_;
+
+ private:
+ // Moves instructions of the caller function up to the call instruction
+ // to |new_blk_ptr|.
+ void MoveInstsBeforeEntryBlock(
+ std::unordered_map<uint32_t, Instruction*>* preCallSB,
+ BasicBlock* new_blk_ptr, BasicBlock::iterator call_inst_itr,
+ UptrVectorIterator<BasicBlock> call_block_itr);
+
+ // Returns a new guard block after adding a branch to the end of
+ // |new_blocks|.
+ std::unique_ptr<BasicBlock> AddGuardBlock(
+ std::vector<std::unique_ptr<BasicBlock>>* new_blocks,
+ std::unordered_map<uint32_t, uint32_t>* callee2caller,
+ std::unique_ptr<BasicBlock> new_blk_ptr, uint32_t entry_blk_label_id);
+
+ // Add store instructions for initializers of variables.
+ InstructionList::iterator AddStoresForVariableInitializers(
+ const std::unordered_map<uint32_t, uint32_t>& callee2caller,
+ analysis::DebugInlinedAtContext* inlined_at_ctx,
+ std::unique_ptr<BasicBlock>* new_blk_ptr,
+ UptrVectorIterator<BasicBlock> callee_block_itr);
+
+ // Inlines a single instruction of the callee function.
+ bool InlineSingleInstruction(
+ const std::unordered_map<uint32_t, uint32_t>& callee2caller,
+ BasicBlock* new_blk_ptr, const Instruction* inst,
+ uint32_t dbg_inlined_at);
+
+ // Inlines the return instruction of the callee function.
+ std::unique_ptr<BasicBlock> InlineReturn(
+ const std::unordered_map<uint32_t, uint32_t>& callee2caller,
+ std::vector<std::unique_ptr<BasicBlock>>* new_blocks,
+ std::unique_ptr<BasicBlock> new_blk_ptr,
+ analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn,
+ const Instruction* inst, uint32_t returnVarId);
+
+ // Inlines the entry block of the callee function.
+ bool InlineEntryBlock(
+ const std::unordered_map<uint32_t, uint32_t>& callee2caller,
+ std::unique_ptr<BasicBlock>* new_blk_ptr,
+ UptrVectorIterator<BasicBlock> callee_first_block,
+ analysis::DebugInlinedAtContext* inlined_at_ctx);
+
+ // Inlines basic blocks of the callee function other than the entry basic
+ // block.
+ std::unique_ptr<BasicBlock> InlineBasicBlocks(
+ std::vector<std::unique_ptr<BasicBlock>>* new_blocks,
+ const std::unordered_map<uint32_t, uint32_t>& callee2caller,
+ std::unique_ptr<BasicBlock> new_blk_ptr,
+ analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn);
+
+ // Moves instructions of the caller function after the call instruction
+ // to |new_blk_ptr|.
+ bool MoveCallerInstsAfterFunctionCall(
+ std::unordered_map<uint32_t, Instruction*>* preCallSB,
+ std::unordered_map<uint32_t, uint32_t>* postCallSB,
+ std::unique_ptr<BasicBlock>* new_blk_ptr,
+ BasicBlock::iterator call_inst_itr, bool multiBlocks);
+
+ // Move the OpLoopMerge from the last block back to the first.
+ void MoveLoopMergeInstToFirstBlock(
+ std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
};
} // namespace opt
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/inst_bindless_check_pass.h b/chromium/third_party/SPIRV-Tools/src/source/opt/inst_bindless_check_pass.h
index 447871bfc96..9335fa5baf0 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/inst_bindless_check_pass.h
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/inst_bindless_check_pass.h
@@ -28,13 +28,6 @@ namespace opt {
// external design may change as the layer evolves.
class InstBindlessCheckPass : public InstrumentPass {
public:
- // 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)
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/inst_buff_addr_check_pass.h b/chromium/third_party/SPIRV-Tools/src/source/opt/inst_buff_addr_check_pass.h
index 67ffcc39241..ec7bb6846f9 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/inst_buff_addr_check_pass.h
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/inst_buff_addr_check_pass.h
@@ -28,10 +28,6 @@ namespace opt {
// external design of this class may change as the layer evolves.
class InstBuffAddrCheckPass : public InstrumentPass {
public:
- // 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) {}
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/inst_debug_printf_pass.h b/chromium/third_party/SPIRV-Tools/src/source/opt/inst_debug_printf_pass.h
index 2968a203af0..70b0a72bd77 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/inst_debug_printf_pass.h
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/inst_debug_printf_pass.h
@@ -28,11 +28,10 @@ namespace opt {
class InstDebugPrintfPass : public InstrumentPass {
public:
// For test harness only
- InstDebugPrintfPass()
- : InstrumentPass(7, 23, kInstValidationIdDebugPrintf, 2) {}
+ InstDebugPrintfPass() : InstrumentPass(7, 23, kInstValidationIdDebugPrintf) {}
// For all other interfaces
InstDebugPrintfPass(uint32_t desc_set, uint32_t shader_id)
- : InstrumentPass(desc_set, shader_id, kInstValidationIdDebugPrintf, 2) {}
+ : InstrumentPass(desc_set, shader_id, kInstValidationIdDebugPrintf) {}
~InstDebugPrintfPass() override = default;
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/instruction.cpp b/chromium/third_party/SPIRV-Tools/src/source/opt/instruction.cpp
index 7052d3e7249..126848e719f 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/instruction.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/instruction.cpp
@@ -38,6 +38,10 @@ const uint32_t kExtInstInstructionInIdx = 1;
const uint32_t kDebugScopeNumWords = 7;
const uint32_t kDebugScopeNumWordsWithoutInlinedAt = 6;
const uint32_t kDebugNoScopeNumWords = 5;
+
+// Number of operands of an OpBranchConditional instruction
+// with weights.
+const uint32_t kOpBranchConditionalWithWeightsNumOperands = 5;
} // namespace
Instruction::Instruction(IRContext* c)
@@ -166,6 +170,15 @@ uint32_t Instruction::NumInOperandWords() const {
return size;
}
+bool Instruction::HasBranchWeights() const {
+ if (opcode_ == SpvOpBranchConditional &&
+ NumOperands() == kOpBranchConditionalWithWeightsNumOperands) {
+ return true;
+ }
+
+ return false;
+}
+
void Instruction::ToBinaryWithoutAttachedDebugInsts(
std::vector<uint32_t>* binary) const {
const uint32_t num_words = 1 + NumOperandWords();
@@ -610,7 +623,19 @@ bool Instruction::IsFoldableByFoldScalar() const {
return false;
}
Instruction* type = context()->get_def_use_mgr()->GetDef(type_id());
- return folder.IsFoldableType(type);
+ if (!folder.IsFoldableType(type)) {
+ return false;
+ }
+
+ // Even if the type of the instruction is foldable, its operands may not be
+ // foldable (e.g., comparisons of 64bit types). Check that all operand types
+ // are foldable before accepting the instruction.
+ return WhileEachInOperand([&folder, this](const uint32_t* op_id) {
+ Instruction* def_inst = context()->get_def_use_mgr()->GetDef(*op_id);
+ Instruction* def_inst_type =
+ context()->get_def_use_mgr()->GetDef(def_inst->type_id());
+ return folder.IsFoldableType(def_inst_type);
+ });
}
bool Instruction::IsFloatingPointFoldingAllowed() const {
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/instruction.h b/chromium/third_party/SPIRV-Tools/src/source/opt/instruction.h
index aa29c5e7b47..7d8fed84788 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/instruction.h
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/instruction.h
@@ -239,6 +239,10 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
return dbg_line_insts_;
}
+ const Instruction* dbg_line_inst() const {
+ return dbg_line_insts_.empty() ? nullptr : &dbg_line_insts_[0];
+ }
+
// Clear line-related debug instructions attached to this instruction.
void clear_dbg_line_insts() { dbg_line_insts_.clear(); }
@@ -291,6 +295,11 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
// Sets DebugScope.
inline void SetDebugScope(const DebugScope& scope);
inline const DebugScope& GetDebugScope() const { return dbg_scope_; }
+ // Updates DebugInlinedAt of DebugScope and OpLine.
+ inline void UpdateDebugInlinedAt(uint32_t new_inlined_at);
+ inline uint32_t GetDebugInlinedAt() const {
+ return dbg_scope_.GetInlinedAt();
+ }
// Updates OpLine and DebugScope based on the information of |from|.
inline void UpdateDebugInfo(const Instruction* from);
// Remove the |index|-th operand
@@ -366,6 +375,10 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
inline bool WhileEachInOperand(
const std::function<bool(const uint32_t*)>& f) const;
+ // Returns true if it's an OpBranchConditional instruction
+ // with branch weights.
+ bool HasBranchWeights() const;
+
// Returns true if any operands can be labels
inline bool HasLabels() const;
@@ -638,6 +651,13 @@ inline void Instruction::SetDebugScope(const DebugScope& scope) {
}
}
+inline void Instruction::UpdateDebugInlinedAt(uint32_t new_inlined_at) {
+ dbg_scope_.SetInlinedAt(new_inlined_at);
+ for (auto& i : dbg_line_insts_) {
+ i.dbg_scope_.SetInlinedAt(new_inlined_at);
+ }
+}
+
inline void Instruction::UpdateDebugInfo(const Instruction* from) {
if (from == nullptr) return;
clear_dbg_line_insts();
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/instruction_list.h b/chromium/third_party/SPIRV-Tools/src/source/opt/instruction_list.h
index ea1cc7c46ca..417cbd768b9 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/instruction_list.h
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/instruction_list.h
@@ -61,6 +61,16 @@ class InstructionList : public utils::IntrusiveList<Instruction> {
: utils::IntrusiveList<Instruction>::iterator(i) {}
iterator(Instruction* i) : utils::IntrusiveList<Instruction>::iterator(i) {}
+ iterator& operator++() {
+ utils::IntrusiveList<Instruction>::iterator::operator++();
+ return *this;
+ }
+
+ iterator& operator--() {
+ utils::IntrusiveList<Instruction>::iterator::operator--();
+ return *this;
+ }
+
// DEPRECATED: Please use MoveBefore with an InstructionList instead.
//
// Moves the nodes in |list| to the list that |this| points to. The
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/instrument_pass.cpp b/chromium/third_party/SPIRV-Tools/src/source/opt/instrument_pass.cpp
index c8c6c211301..4210ad5d7eb 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/instrument_pass.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/instrument_pass.cpp
@@ -885,14 +885,6 @@ bool InstrumentPass::InstProcessCallTreeFromRoots(InstProcessFunction& pfn,
}
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
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/instrument_pass.h b/chromium/third_party/SPIRV-Tools/src/source/opt/instrument_pass.h
index 11afdce82bb..f6884d2da39 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/instrument_pass.h
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/instrument_pass.h
@@ -87,18 +87,7 @@ class InstrumentPass : public Pass {
: 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(),
- desc_set_(desc_set),
- shader_id_(shader_id),
- validation_id_(validation_id),
- version_(version) {}
+ validation_id_(validation_id) {}
// Initialize state for instrumentation of module.
void InitializeInstrument();
@@ -425,9 +414,6 @@ class InstrumentPass : public Pass {
// id for void type
uint32_t void_id_;
- // Record format version
- uint32_t version_;
-
// boolean to remember storage buffer extension
bool storage_buffer_ext_defined_;
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/ir_context.cpp b/chromium/third_party/SPIRV-Tools/src/source/opt/ir_context.cpp
index df04066f332..baff0000310 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/ir_context.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/ir_context.cpp
@@ -97,8 +97,9 @@ void IRContext::InvalidateAnalysesExceptFor(
}
void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) {
- // The ConstantManager contains Type pointers. If the TypeManager goes
- // away, the ConstantManager has to go away.
+ // The ConstantManager and DebugInfoManager contain Type pointers. If the
+ // TypeManager goes away, the ConstantManager and DebugInfoManager have to
+ // go away.
if (analyses_to_invalidate & kAnalysisTypes) {
analyses_to_invalidate |= kAnalysisConstants;
analyses_to_invalidate |= kAnalysisDebugInfo;
@@ -179,6 +180,9 @@ Instruction* IRContext::KillInst(Instruction* inst) {
decoration_mgr_->RemoveDecoration(inst);
}
}
+ if (AreAnalysesValid(kAnalysisDebugInfo)) {
+ get_debug_info_mgr()->ClearDebugInfo(inst);
+ }
if (type_mgr_ && IsTypeInst(inst->opcode())) {
type_mgr_->RemoveId(inst->result_id());
}
@@ -218,6 +222,13 @@ bool IRContext::KillDef(uint32_t id) {
return false;
}
+void IRContext::KillDebugDeclareInsts(Function* fn) {
+ fn->ForEachInst([this](Instruction* inst) {
+ if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare)
+ KillInst(inst);
+ });
+}
+
bool IRContext::ReplaceAllUsesWith(uint32_t before, uint32_t after) {
return ReplaceAllUsesWithPredicate(
before, after, [](Instruction*, uint32_t) { return true; });
@@ -394,6 +405,7 @@ void IRContext::KillOperandFromDebugInstructions(Instruction* inst) {
if (operand.words[0] == id) {
operand.words[0] =
get_debug_info_mgr()->GetDebugInfoNone()->result_id();
+ get_def_use_mgr()->AnalyzeInstUse(&*it);
}
}
}
@@ -408,6 +420,7 @@ void IRContext::KillOperandFromDebugInstructions(Instruction* inst) {
if (operand.words[0] == id) {
operand.words[0] =
get_debug_info_mgr()->GetDebugInfoNone()->result_id();
+ get_def_use_mgr()->AnalyzeInstUse(&*it);
}
}
}
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/ir_context.h b/chromium/third_party/SPIRV-Tools/src/source/opt/ir_context.h
index a1b63ff9e07..37be8365318 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/ir_context.h
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/ir_context.h
@@ -357,6 +357,13 @@ class IRContext {
inline IteratorRange<std::multimap<uint32_t, Instruction*>::iterator>
GetNames(uint32_t id);
+ // Returns an OpMemberName instruction that targets |struct_type_id| at
+ // index |index|. Returns nullptr if no such instruction exists.
+ // While the SPIR-V spec does not prohibit having multiple OpMemberName
+ // instructions for the same structure member, it is hard to imagine a member
+ // having more than one name. This method returns the first one it finds.
+ inline Instruction* GetMemberName(uint32_t struct_type_id, uint32_t index);
+
// Sets the message consumer to the given |consumer|. |consumer| which will be
// invoked every time there is a message to be communicated to the outside.
void SetMessageConsumer(MessageConsumer c) { consumer_ = std::move(c); }
@@ -396,6 +403,9 @@ class IRContext {
// instruction exists.
Instruction* KillInst(Instruction* inst);
+ // Deletes DebugDeclare instructions in the given function |fn|.
+ void KillDebugDeclareInsts(Function* fn);
+
// Returns true if all of the given analyses are valid.
bool AreAnalysesValid(Analysis set) { return (set & valid_analyses_) == set; }
@@ -1061,7 +1071,9 @@ void IRContext::AddDebug1Inst(std::unique_ptr<Instruction>&& d) {
void IRContext::AddDebug2Inst(std::unique_ptr<Instruction>&& d) {
if (AreAnalysesValid(kAnalysisNameMap)) {
if (d->opcode() == SpvOpName || d->opcode() == SpvOpMemberName) {
- id_to_name_->insert({d->result_id(), d.get()});
+ // OpName and OpMemberName do not have result-ids. The target of the
+ // instruction is at InOperand index 0.
+ id_to_name_->insert({d->GetSingleWordInOperand(0), d.get()});
}
}
module()->AddDebug2Inst(std::move(d));
@@ -1135,6 +1147,21 @@ IRContext::GetNames(uint32_t id) {
return make_range(std::move(result.first), std::move(result.second));
}
+Instruction* IRContext::GetMemberName(uint32_t struct_type_id, uint32_t index) {
+ if (!AreAnalysesValid(kAnalysisNameMap)) {
+ BuildIdToNameMap();
+ }
+ auto result = id_to_name_->equal_range(struct_type_id);
+ for (auto i = result.first; i != result.second; ++i) {
+ auto* name_instr = i->second;
+ if (name_instr->opcode() == SpvOpMemberName &&
+ name_instr->GetSingleWordInOperand(1) == index) {
+ return name_instr;
+ }
+ }
+ return nullptr;
+}
+
} // namespace opt
} // namespace spvtools
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/local_access_chain_convert_pass.cpp b/chromium/third_party/SPIRV-Tools/src/source/opt/local_access_chain_convert_pass.cpp
index 0afe798582f..05704c1489a 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/local_access_chain_convert_pass.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/local_access_chain_convert_pass.cpp
@@ -281,7 +281,7 @@ void LocalAccessChainConvertPass::Initialize() {
// Initialize collections
supported_ref_ptrs_.clear();
- // Initialize extension whitelist
+ // Initialize extension allowlist
InitExtensions();
}
@@ -292,11 +292,11 @@ bool LocalAccessChainConvertPass::AllExtensionsSupported() const {
if (context()->get_feature_mgr()->HasCapability(
SpvCapabilityVariablePointers))
return false;
- // If any extension not in whitelist, return false
+ // If any extension not in allowlist, return false
for (auto& ei : get_module()->extensions()) {
const char* extName =
reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
- if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
+ if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
return false;
}
return true;
@@ -336,8 +336,8 @@ Pass::Status LocalAccessChainConvertPass::Process() {
}
void LocalAccessChainConvertPass::InitExtensions() {
- extensions_whitelist_.clear();
- extensions_whitelist_.insert({
+ extensions_allowlist_.clear();
+ extensions_allowlist_.insert({
"SPV_AMD_shader_explicit_vertex_parameter",
"SPV_AMD_shader_trinary_minmax",
"SPV_AMD_gcn_shader",
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/local_access_chain_convert_pass.h b/chromium/third_party/SPIRV-Tools/src/source/opt/local_access_chain_convert_pass.h
index e3592bf0ce0..552062e52f4 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/local_access_chain_convert_pass.h
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/local_access_chain_convert_pass.h
@@ -110,7 +110,7 @@ class LocalAccessChainConvertPass : public MemPass {
// Returns a status to indicate success or failure, and change or no change.
Status ConvertLocalAccessChains(Function* func);
- // Initialize extensions whitelist
+ // Initialize extensions allowlist
void InitExtensions();
// Return true if all extensions in this module are allowed by this pass.
@@ -124,7 +124,7 @@ class LocalAccessChainConvertPass : public MemPass {
std::unordered_set<uint32_t> supported_ref_ptrs_;
// Extensions supported by this pass.
- std::unordered_set<std::string> extensions_whitelist_;
+ std::unordered_set<std::string> extensions_allowlist_;
};
} // namespace opt
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/local_single_block_elim_pass.cpp b/chromium/third_party/SPIRV-Tools/src/source/opt/local_single_block_elim_pass.cpp
index b5435bb7a54..401dad8e3b3 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/local_single_block_elim_pass.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/local_single_block_elim_pass.cpp
@@ -168,16 +168,16 @@ void LocalSingleBlockLoadStoreElimPass::Initialize() {
// Clear collections
supported_ref_ptrs_.clear();
- // Initialize extensions whitelist
+ // Initialize extensions allowlist
InitExtensions();
}
bool LocalSingleBlockLoadStoreElimPass::AllExtensionsSupported() const {
- // If any extension not in whitelist, return false
+ // If any extension not in allowlist, return false
for (auto& ei : get_module()->extensions()) {
const char* extName =
reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
- if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
+ if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
return false;
}
return true;
@@ -214,8 +214,8 @@ Pass::Status LocalSingleBlockLoadStoreElimPass::Process() {
}
void LocalSingleBlockLoadStoreElimPass::InitExtensions() {
- extensions_whitelist_.clear();
- extensions_whitelist_.insert({
+ extensions_allowlist_.clear();
+ extensions_allowlist_.insert({
"SPV_AMD_shader_explicit_vertex_parameter",
"SPV_AMD_shader_trinary_minmax",
"SPV_AMD_gcn_shader",
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/local_single_block_elim_pass.h b/chromium/third_party/SPIRV-Tools/src/source/opt/local_single_block_elim_pass.h
index 0fe7732a822..ea72816a8e6 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/local_single_block_elim_pass.h
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/local_single_block_elim_pass.h
@@ -63,7 +63,7 @@ class LocalSingleBlockLoadStoreElimPass : public MemPass {
// where possible. Assumes logical addressing.
bool LocalSingleBlockLoadStoreElim(Function* func);
- // Initialize extensions whitelist
+ // Initialize extensions allowlist
void InitExtensions();
// Return true if all extensions in this module are supported by this pass.
@@ -94,7 +94,7 @@ class LocalSingleBlockLoadStoreElimPass : public MemPass {
std::unordered_set<uint32_t> pinned_vars_;
// Extensions supported by this pass.
- std::unordered_set<std::string> extensions_whitelist_;
+ std::unordered_set<std::string> extensions_allowlist_;
// Variables that are only referenced by supported operations for this
// pass ie. loads and stores.
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/local_single_store_elim_pass.cpp b/chromium/third_party/SPIRV-Tools/src/source/opt/local_single_store_elim_pass.cpp
index 4c71ce1ca85..3f618534776 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/local_single_store_elim_pass.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/local_single_store_elim_pass.cpp
@@ -46,11 +46,11 @@ bool LocalSingleStoreElimPass::LocalSingleStoreElim(Function* func) {
}
bool LocalSingleStoreElimPass::AllExtensionsSupported() const {
- // If any extension not in whitelist, return false
+ // If any extension not in allowlist, return false
for (auto& ei : get_module()->extensions()) {
const char* extName =
reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
- if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
+ if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
return false;
}
return true;
@@ -74,12 +74,12 @@ Pass::Status LocalSingleStoreElimPass::ProcessImpl() {
LocalSingleStoreElimPass::LocalSingleStoreElimPass() = default;
Pass::Status LocalSingleStoreElimPass::Process() {
- InitExtensionWhiteList();
+ InitExtensionAllowList();
return ProcessImpl();
}
-void LocalSingleStoreElimPass::InitExtensionWhiteList() {
- extensions_whitelist_.insert({
+void LocalSingleStoreElimPass::InitExtensionAllowList() {
+ extensions_allowlist_.insert({
"SPV_AMD_shader_explicit_vertex_parameter",
"SPV_AMD_shader_trinary_minmax",
"SPV_AMD_gcn_shader",
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/local_single_store_elim_pass.h b/chromium/third_party/SPIRV-Tools/src/source/opt/local_single_store_elim_pass.h
index 4cf8bbb86ae..a7cdd1920a8 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/local_single_store_elim_pass.h
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/local_single_store_elim_pass.h
@@ -57,8 +57,8 @@ class LocalSingleStoreElimPass : public Pass {
// any resulting dead code.
bool LocalSingleStoreElim(Function* func);
- // Initialize extensions whitelist
- void InitExtensionWhiteList();
+ // Initialize extensions allowlist
+ void InitExtensionAllowList();
// Return true if all extensions in this module are allowed by this pass.
bool AllExtensionsSupported() const;
@@ -94,7 +94,7 @@ class LocalSingleStoreElimPass : public Pass {
const std::vector<Instruction*>& uses);
// Extensions supported by this pass.
- std::unordered_set<std::string> extensions_whitelist_;
+ std::unordered_set<std::string> extensions_allowlist_;
};
} // namespace opt
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/loop_descriptor.cpp b/chromium/third_party/SPIRV-Tools/src/source/opt/loop_descriptor.cpp
index 11f7e9cfac4..ed0dd28f42e 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/loop_descriptor.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/loop_descriptor.cpp
@@ -485,10 +485,27 @@ void Loop::ComputeLoopStructuredOrder(
if (include_pre_header && GetPreHeaderBlock())
ordered_loop_blocks->push_back(loop_preheader_);
- cfg.ForEachBlockInReversePostOrder(
- loop_header_, [ordered_loop_blocks, this](BasicBlock* bb) {
- if (IsInsideLoop(bb)) ordered_loop_blocks->push_back(bb);
- });
+
+ bool is_shader =
+ context_->get_feature_mgr()->HasCapability(SpvCapabilityShader);
+ if (!is_shader) {
+ cfg.ForEachBlockInReversePostOrder(
+ loop_header_, [ordered_loop_blocks, this](BasicBlock* bb) {
+ if (IsInsideLoop(bb)) ordered_loop_blocks->push_back(bb);
+ });
+ } else {
+ // If this is a shader, it is possible that there are unreachable merge and
+ // continue blocks that must be copied to retain the structured order.
+ // The structured order will include these.
+ std::list<BasicBlock*> order;
+ cfg.ComputeStructuredOrder(loop_header_->GetParent(), loop_header_, &order);
+ for (BasicBlock* bb : order) {
+ if (bb == GetMergeBlock()) {
+ break;
+ }
+ ordered_loop_blocks->push_back(bb);
+ }
+ }
if (include_merge && GetMergeBlock())
ordered_loop_blocks->push_back(loop_merge_);
}
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/mem_pass.cpp b/chromium/third_party/SPIRV-Tools/src/source/opt/mem_pass.cpp
index 04e2e8aea93..573879842cb 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/mem_pass.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/mem_pass.cpp
@@ -20,6 +20,7 @@
#include <set>
#include <vector>
+#include "OpenCLDebugInfo100.h"
#include "source/cfa.h"
#include "source/opt/basic_block.h"
#include "source/opt/dominator_analysis.h"
@@ -97,6 +98,11 @@ Instruction* MemPass::GetPtr(uint32_t ptrId, uint32_t* varId) {
Instruction* ptrInst = get_def_use_mgr()->GetDef(*varId);
Instruction* varInst;
+ if (ptrInst->opcode() == SpvOpConstantNull) {
+ *varId = 0;
+ return ptrInst;
+ }
+
if (ptrInst->opcode() != SpvOpVariable &&
ptrInst->opcode() != SpvOpFunctionParameter) {
varInst = ptrInst->GetBaseAddress();
@@ -220,6 +226,11 @@ MemPass::MemPass() {}
bool MemPass::HasOnlySupportedRefs(uint32_t varId) {
return get_def_use_mgr()->WhileEachUser(varId, [this](Instruction* user) {
+ auto dbg_op = user->GetOpenCL100DebugOpcode();
+ if (dbg_op == OpenCLDebugInfo100DebugDeclare ||
+ dbg_op == OpenCLDebugInfo100DebugValue) {
+ return true;
+ }
SpvOp op = user->opcode();
if (op != SpvOpStore && op != SpvOpLoad && op != SpvOpName &&
!IsNonTypeDecorate(op)) {
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/merge_return_pass.cpp b/chromium/third_party/SPIRV-Tools/src/source/opt/merge_return_pass.cpp
index 7d238daef95..8cb429945aa 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/merge_return_pass.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/merge_return_pass.cpp
@@ -424,7 +424,6 @@ bool MergeReturnPass::BreakFromConstruct(
auto old_body_id = TakeNextId();
BasicBlock* old_body = block->SplitBasicBlock(context(), old_body_id, iter);
predicated->insert(old_body);
- cfg()->AddEdges(old_body);
// If a return block is being split, mark the new body block also as a return
// block.
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/optimizer.cpp b/chromium/third_party/SPIRV-Tools/src/source/opt/optimizer.cpp
index f11dc346f6f..25adee976c9 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/optimizer.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/optimizer.cpp
@@ -175,9 +175,18 @@ Optimizer& Optimizer::RegisterPerformancePasses() {
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateCCPPass())
.RegisterPass(CreateAggressiveDCEPass())
+ .RegisterPass(CreateLoopUnrollPass(true))
+ .RegisterPass(CreateDeadBranchElimPass())
.RegisterPass(CreateRedundancyEliminationPass())
.RegisterPass(CreateCombineAccessChainsPass())
.RegisterPass(CreateSimplificationPass())
+ .RegisterPass(CreateScalarReplacementPass())
+ .RegisterPass(CreateLocalAccessChainConvertPass())
+ .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
+ .RegisterPass(CreateLocalSingleStoreElimPass())
+ .RegisterPass(CreateAggressiveDCEPass())
+ .RegisterPass(CreateSSARewritePass())
+ .RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateVectorDCEPass())
.RegisterPass(CreateDeadInsertElimPass())
.RegisterPass(CreateDeadBranchElimPass())
@@ -407,19 +416,19 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
} else if (pass_name == "replace-invalid-opcode") {
RegisterPass(CreateReplaceInvalidOpcodePass());
} else if (pass_name == "inst-bindless-check") {
- RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false, 2));
+ RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false));
RegisterPass(CreateSimplificationPass());
RegisterPass(CreateDeadBranchElimPass());
RegisterPass(CreateBlockMergePass());
RegisterPass(CreateAggressiveDCEPass());
} else if (pass_name == "inst-desc-idx-check") {
- RegisterPass(CreateInstBindlessCheckPass(7, 23, true, true, 2));
+ RegisterPass(CreateInstBindlessCheckPass(7, 23, true, true));
RegisterPass(CreateSimplificationPass());
RegisterPass(CreateDeadBranchElimPass());
RegisterPass(CreateBlockMergePass());
RegisterPass(CreateAggressiveDCEPass());
} else if (pass_name == "inst-buff-addr-check") {
- RegisterPass(CreateInstBuffAddrCheckPass(7, 23, 2));
+ RegisterPass(CreateInstBuffAddrCheckPass(7, 23));
RegisterPass(CreateAggressiveDCEPass());
} else if (pass_name == "convert-relaxed-to-half") {
RegisterPass(CreateConvertRelaxedToHalfPass());
@@ -885,12 +894,10 @@ Optimizer::PassToken CreateUpgradeMemoryModelPass() {
Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set,
uint32_t shader_id,
bool input_length_enable,
- bool input_init_enable,
- uint32_t version) {
+ bool input_init_enable) {
return MakeUnique<Optimizer::PassToken::Impl>(
- MakeUnique<opt::InstBindlessCheckPass>(desc_set, shader_id,
- input_length_enable,
- input_init_enable, version));
+ MakeUnique<opt::InstBindlessCheckPass>(
+ desc_set, shader_id, input_length_enable, input_init_enable));
}
Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set,
@@ -900,10 +907,9 @@ Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set,
}
Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
- uint32_t shader_id,
- uint32_t version) {
+ uint32_t shader_id) {
return MakeUnique<Optimizer::PassToken::Impl>(
- MakeUnique<opt::InstBuffAddrCheckPass>(desc_set, shader_id, version));
+ MakeUnique<opt::InstBuffAddrCheckPass>(desc_set, shader_id));
}
Optimizer::PassToken CreateConvertRelaxedToHalfPass() {
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/pass.h b/chromium/third_party/SPIRV-Tools/src/source/opt/pass.h
index a8c9c4b43c1..a1192079225 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/pass.h
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/pass.h
@@ -71,6 +71,10 @@ class Pass {
return context()->get_def_use_mgr();
}
+ analysis::DebugInfoManager* get_debug_info_mgr() const {
+ return context()->get_debug_info_mgr();
+ }
+
analysis::DecorationManager* get_decoration_mgr() const {
return context()->get_decoration_mgr();
}
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/ssa_rewrite_pass.cpp b/chromium/third_party/SPIRV-Tools/src/source/opt/ssa_rewrite_pass.cpp
index 69c3a1f3edc..6eed1fdd465 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/ssa_rewrite_pass.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/ssa_rewrite_pass.cpp
@@ -307,6 +307,7 @@ void SSARewriter::ProcessStore(Instruction* inst, BasicBlock* bb) {
}
if (pass_->IsTargetVar(var_id)) {
WriteVariable(var_id, bb, val_id);
+ pass_->get_debug_info_mgr()->AddDebugValue(inst, var_id, val_id, inst);
#if SSA_REWRITE_DEBUGGING_LEVEL > 1
std::cerr << "\tFound store '%" << var_id << " = %" << val_id << "': "
@@ -437,6 +438,8 @@ bool SSARewriter::ApplyReplacements() {
// Add Phi instructions from completed Phi candidates.
std::vector<Instruction*> generated_phis;
+ // Add DebugValue instructions for Phi instructions.
+ std::vector<Instruction*> dbg_values_for_phis;
for (const PhiCandidate* phi_candidate : phis_to_generate_) {
#if SSA_REWRITE_DEBUGGING_LEVEL > 2
std::cerr << "Phi candidate: " << phi_candidate->PrettyPrint(pass_->cfg())
@@ -447,9 +450,10 @@ bool SSARewriter::ApplyReplacements() {
"Tried to instantiate a Phi instruction from an incomplete Phi "
"candidate");
+ auto* local_var = pass_->get_def_use_mgr()->GetDef(phi_candidate->var_id());
+
// Build the vector of operands for the new OpPhi instruction.
- uint32_t type_id = pass_->GetPointeeTypeId(
- pass_->get_def_use_mgr()->GetDef(phi_candidate->var_id()));
+ uint32_t type_id = pass_->GetPointeeTypeId(local_var);
std::vector<Operand> phi_operands;
uint32_t arg_ix = 0;
std::unordered_map<uint32_t, uint32_t> already_seen;
@@ -479,11 +483,17 @@ bool SSARewriter::ApplyReplacements() {
pass_->get_def_use_mgr()->AnalyzeInstDef(&*phi_inst);
pass_->context()->set_instr_block(&*phi_inst, phi_candidate->bb());
auto insert_it = phi_candidate->bb()->begin();
- insert_it.InsertBefore(std::move(phi_inst));
+ insert_it = insert_it.InsertBefore(std::move(phi_inst));
pass_->context()->get_decoration_mgr()->CloneDecorations(
phi_candidate->var_id(), phi_candidate->result_id(),
{SpvDecorationRelaxedPrecision});
+ // Add DebugValue for the new OpPhi instruction.
+ insert_it->SetDebugScope(local_var->GetDebugScope());
+ pass_->get_debug_info_mgr()->AddDebugValue(
+ &*insert_it, phi_candidate->var_id(), phi_candidate->result_id(),
+ &*insert_it);
+
modified = true;
}
@@ -604,6 +614,8 @@ Pass::Status SSARewriter::RewriteFunctionIntoSSA(Function* fp) {
<< fp->PrettyPrint(0) << "\n";
#endif
+ if (modified) pass_->context()->KillDebugDeclareInsts(fp);
+
return modified ? Pass::Status::SuccessWithChange
: Pass::Status::SuccessWithoutChange;
}
diff --git a/chromium/third_party/SPIRV-Tools/src/source/opt/ssa_rewrite_pass.h b/chromium/third_party/SPIRV-Tools/src/source/opt/ssa_rewrite_pass.h
index bbe89c8afa0..bbbfebbf145 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/opt/ssa_rewrite_pass.h
+++ b/chromium/third_party/SPIRV-Tools/src/source/opt/ssa_rewrite_pass.h
@@ -39,8 +39,7 @@ namespace opt {
// (https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6)
class SSARewriter {
public:
- SSARewriter(MemPass* pass)
- : pass_(pass), first_phi_id_(pass_->get_module()->IdBound()) {}
+ SSARewriter(MemPass* pass) : pass_(pass) {}
// Rewrites SSA-target variables in function |fp| into SSA. This is the
// entry point for the SSA rewrite algorithm. SSA-target variables are
@@ -287,10 +286,6 @@ class SSARewriter {
// Memory pass requesting the SSA rewriter.
MemPass* pass_;
-
- // ID of the first Phi created by the SSA rewriter. During rewriting, any
- // ID bigger than this corresponds to a Phi candidate.
- uint32_t first_phi_id_;
};
class SSARewritePass : public MemPass {
diff --git a/chromium/third_party/SPIRV-Tools/src/source/reduce/CMakeLists.txt b/chromium/third_party/SPIRV-Tools/src/source/reduce/CMakeLists.txt
index 51e9b1d0ffd..d945bd20a60 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/reduce/CMakeLists.txt
+++ b/chromium/third_party/SPIRV-Tools/src/source/reduce/CMakeLists.txt
@@ -26,12 +26,14 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
reduction_util.h
remove_block_reduction_opportunity.h
remove_block_reduction_opportunity_finder.h
- remove_instruction_reduction_opportunity.h
remove_function_reduction_opportunity.h
remove_function_reduction_opportunity_finder.h
+ remove_instruction_reduction_opportunity.h
remove_selection_reduction_opportunity.h
remove_selection_reduction_opportunity_finder.h
- remove_unreferenced_instruction_reduction_opportunity_finder.h
+ remove_struct_member_reduction_opportunity.h
+ remove_unused_instruction_reduction_opportunity_finder.h
+ remove_unused_struct_member_reduction_opportunity_finder.h
structured_loop_to_selection_reduction_opportunity.h
structured_loop_to_selection_reduction_opportunity_finder.h
conditional_branch_to_simple_conditional_branch_opportunity_finder.h
@@ -57,7 +59,9 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
remove_instruction_reduction_opportunity.cpp
remove_selection_reduction_opportunity.cpp
remove_selection_reduction_opportunity_finder.cpp
- remove_unreferenced_instruction_reduction_opportunity_finder.cpp
+ remove_struct_member_reduction_opportunity.cpp
+ remove_unused_instruction_reduction_opportunity_finder.cpp
+ remove_unused_struct_member_reduction_opportunity_finder.cpp
structured_loop_to_selection_reduction_opportunity.cpp
structured_loop_to_selection_reduction_opportunity_finder.cpp
conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp
diff --git a/chromium/third_party/SPIRV-Tools/src/source/reduce/pch_source_reduce.h b/chromium/third_party/SPIRV-Tools/src/source/reduce/pch_source_reduce.h
index 6c0da0c7b52..81bed2086b5 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/reduce/pch_source_reduce.h
+++ b/chromium/third_party/SPIRV-Tools/src/source/reduce/pch_source_reduce.h
@@ -20,4 +20,4 @@
#include "source/reduce/reduction_opportunity.h"
#include "source/reduce/reduction_pass.h"
#include "source/reduce/remove_instruction_reduction_opportunity.h"
-#include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
+#include "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h"
diff --git a/chromium/third_party/SPIRV-Tools/src/source/reduce/reducer.cpp b/chromium/third_party/SPIRV-Tools/src/source/reduce/reducer.cpp
index bda41ce9459..092d4090746 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/reduce/reducer.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/reduce/reducer.cpp
@@ -25,7 +25,8 @@
#include "source/reduce/remove_block_reduction_opportunity_finder.h"
#include "source/reduce/remove_function_reduction_opportunity_finder.h"
#include "source/reduce/remove_selection_reduction_opportunity_finder.h"
-#include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
+#include "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h"
+#include "source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h"
#include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h"
#include "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h"
#include "source/spirv_reducer_options.h"
@@ -103,8 +104,8 @@ Reducer::ReductionResultStatus Reducer::Run(
void Reducer::AddDefaultReductionPasses() {
AddReductionPass(
- spvtools::MakeUnique<
- RemoveUnreferencedInstructionReductionOpportunityFinder>(false));
+ spvtools::MakeUnique<RemoveUnusedInstructionReductionOpportunityFinder>(
+ false));
AddReductionPass(
spvtools::MakeUnique<OperandToUndefReductionOpportunityFinder>());
AddReductionPass(
@@ -126,12 +127,14 @@ void Reducer::AddDefaultReductionPasses() {
ConditionalBranchToSimpleConditionalBranchOpportunityFinder>());
AddReductionPass(
spvtools::MakeUnique<SimpleConditionalBranchToBranchOpportunityFinder>());
+ AddReductionPass(spvtools::MakeUnique<
+ RemoveUnusedStructMemberReductionOpportunityFinder>());
// Cleanup passes.
AddCleanupReductionPass(
- spvtools::MakeUnique<
- RemoveUnreferencedInstructionReductionOpportunityFinder>(true));
+ spvtools::MakeUnique<RemoveUnusedInstructionReductionOpportunityFinder>(
+ true));
}
void Reducer::AddReductionPass(
diff --git a/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_instruction_reduction_opportunity.cpp b/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_instruction_reduction_opportunity.cpp
index 9ca093bf3ef..8026204f516 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_instruction_reduction_opportunity.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_instruction_reduction_opportunity.cpp
@@ -22,6 +22,18 @@ namespace reduce {
bool RemoveInstructionReductionOpportunity::PreconditionHolds() { return true; }
void RemoveInstructionReductionOpportunity::Apply() {
+ const uint32_t kNumEntryPointInOperandsBeforeInterfaceIds = 3;
+ for (auto& entry_point : inst_->context()->module()->entry_points()) {
+ opt::Instruction::OperandList new_entry_point_in_operands;
+ for (uint32_t index = 0; index < entry_point.NumInOperands(); index++) {
+ if (index >= kNumEntryPointInOperandsBeforeInterfaceIds &&
+ entry_point.GetSingleWordInOperand(index) == inst_->result_id()) {
+ continue;
+ }
+ new_entry_point_in_operands.push_back(entry_point.GetInOperand(index));
+ }
+ entry_point.SetInOperands(std::move(new_entry_point_in_operands));
+ }
inst_->context()->KillInst(inst_);
}
diff --git a/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_struct_member_reduction_opportunity.cpp b/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_struct_member_reduction_opportunity.cpp
new file mode 100644
index 00000000000..787c629b7aa
--- /dev/null
+++ b/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_struct_member_reduction_opportunity.cpp
@@ -0,0 +1,208 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/reduce/remove_struct_member_reduction_opportunity.h"
+
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace reduce {
+
+bool RemoveStructMemberReductionOpportunity::PreconditionHolds() {
+ return struct_type_->NumInOperands() == original_number_of_members_;
+}
+
+void RemoveStructMemberReductionOpportunity::Apply() {
+ std::set<opt::Instruction*> decorations_to_kill;
+
+ // We need to remove decorations that target the removed struct member, and
+ // adapt decorations that target later struct members by decrementing the
+ // member identifier. We also need to adapt composite construction
+ // instructions so that no id is provided for the member being removed.
+ //
+ // To do this, we consider every use of the struct type.
+ struct_type_->context()->get_def_use_mgr()->ForEachUse(
+ struct_type_, [this, &decorations_to_kill](opt::Instruction* user,
+ uint32_t /*operand_index*/) {
+ switch (user->opcode()) {
+ case SpvOpCompositeConstruct:
+ case SpvOpConstantComposite:
+ // This use is constructing a composite of the struct type, so we
+ // must remove the id that was provided for the member we are
+ // removing.
+ user->RemoveInOperand(member_index_);
+ break;
+ case SpvOpMemberDecorate:
+ // This use is decorating a member of the struct.
+ if (user->GetSingleWordInOperand(1) == member_index_) {
+ // The member we are removing is being decorated, so we record
+ // that we need to get rid of the decoration.
+ decorations_to_kill.insert(user);
+ } else if (user->GetSingleWordInOperand(1) > member_index_) {
+ // A member beyond the one we are removing is being decorated, so
+ // we adjust the index that identifies the member.
+ user->SetInOperand(1, {user->GetSingleWordInOperand(1) - 1});
+ }
+ break;
+ default:
+ break;
+ }
+ });
+
+ // Get rid of all the decorations that were found to target the member being
+ // removed.
+ for (auto decoration_to_kill : decorations_to_kill) {
+ decoration_to_kill->context()->KillInst(decoration_to_kill);
+ }
+
+ // We now look through all instructions that access composites via sequences
+ // of indices. Every time we find an index into the struct whose member is
+ // being removed, and if the member being accessed comes after the member
+ // being removed, we need to adjust the index accordingly.
+ //
+ // We go through every relevant instruction in every block of every function,
+ // and invoke a helper to adjust it.
+ auto context = struct_type_->context();
+ for (auto& function : *context->module()) {
+ for (auto& block : function) {
+ for (auto& inst : block) {
+ switch (inst.opcode()) {
+ case SpvOpAccessChain:
+ case SpvOpInBoundsAccessChain: {
+ // These access chain instructions take sequences of ids for
+ // indexing, starting from input operand 1.
+ auto composite_type_id =
+ context->get_def_use_mgr()
+ ->GetDef(context->get_def_use_mgr()
+ ->GetDef(inst.GetSingleWordInOperand(0))
+ ->type_id())
+ ->GetSingleWordInOperand(1);
+ AdjustAccessedIndices(composite_type_id, 1, false, context, &inst);
+ } break;
+ case SpvOpPtrAccessChain:
+ case SpvOpInBoundsPtrAccessChain: {
+ // These access chain instructions take sequences of ids for
+ // indexing, starting from input operand 2.
+ auto composite_type_id =
+ context->get_def_use_mgr()
+ ->GetDef(context->get_def_use_mgr()
+ ->GetDef(inst.GetSingleWordInOperand(1))
+ ->type_id())
+ ->GetSingleWordInOperand(1);
+ AdjustAccessedIndices(composite_type_id, 2, false, context, &inst);
+ } break;
+ case SpvOpCompositeExtract: {
+ // OpCompositeExtract uses literals for indexing, starting at input
+ // operand 1.
+ auto composite_type_id =
+ context->get_def_use_mgr()
+ ->GetDef(inst.GetSingleWordInOperand(0))
+ ->type_id();
+ AdjustAccessedIndices(composite_type_id, 1, true, context, &inst);
+ } break;
+ case SpvOpCompositeInsert: {
+ // OpCompositeInsert uses literals for indexing, starting at input
+ // operand 2.
+ auto composite_type_id =
+ context->get_def_use_mgr()
+ ->GetDef(inst.GetSingleWordInOperand(1))
+ ->type_id();
+ AdjustAccessedIndices(composite_type_id, 2, true, context, &inst);
+ } break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ // Remove the member from the struct type.
+ struct_type_->RemoveInOperand(member_index_);
+}
+
+void RemoveStructMemberReductionOpportunity::AdjustAccessedIndices(
+ uint32_t composite_type_id, uint32_t first_index_input_operand,
+ bool literal_indices, opt::IRContext* context,
+ opt::Instruction* composite_access_instruction) const {
+ // Walk the series of types that are encountered by following the
+ // instruction's sequence of indices. For all types except structs, this is
+ // routine: the type of the composite dictates what the next type will be
+ // regardless of the specific index value.
+ uint32_t next_type = composite_type_id;
+ for (uint32_t i = first_index_input_operand;
+ i < composite_access_instruction->NumInOperands(); i++) {
+ auto type_inst = context->get_def_use_mgr()->GetDef(next_type);
+ switch (type_inst->opcode()) {
+ case SpvOpTypeArray:
+ case SpvOpTypeMatrix:
+ case SpvOpTypeRuntimeArray:
+ case SpvOpTypeVector:
+ next_type = type_inst->GetSingleWordInOperand(0);
+ break;
+ case SpvOpTypeStruct: {
+ // Struct types are special becuase (a) we may need to adjust the index
+ // being used, if the struct type is the one from which we are removing
+ // a member, and (b) the type encountered by following the current index
+ // is dependent on the value of the index.
+
+ // Work out the member being accessed. If literal indexing is used this
+ // is simple; otherwise we need to look up the id of the constant
+ // instruction being used as an index and get the value of the constant.
+ uint32_t index_operand =
+ composite_access_instruction->GetSingleWordInOperand(i);
+ uint32_t member = literal_indices ? index_operand
+ : context->get_def_use_mgr()
+ ->GetDef(index_operand)
+ ->GetSingleWordInOperand(0);
+
+ // The next type we will consider is obtained by looking up the struct
+ // type at |member|.
+ next_type = type_inst->GetSingleWordInOperand(member);
+
+ if (type_inst == struct_type_ && member > member_index_) {
+ // The struct type is the struct from which we are removing a member,
+ // and the member being accessed is beyond the member we are removing.
+ // We thus need to decrement the index by 1.
+ uint32_t new_in_operand;
+ if (literal_indices) {
+ // With literal indexing this is straightforward.
+ new_in_operand = member - 1;
+ } else {
+ // With id-based indexing this is more tricky: we need to find or
+ // create a constant instruction whose value is one less than
+ // |member|, and use the id of this constant as the replacement
+ // input operand.
+ auto constant_inst =
+ context->get_def_use_mgr()->GetDef(index_operand);
+ auto int_type = context->get_type_mgr()
+ ->GetType(constant_inst->type_id())
+ ->AsInteger();
+ auto new_index_constant =
+ opt::analysis::IntConstant(int_type, {member - 1});
+ new_in_operand = context->get_constant_mgr()
+ ->GetDefiningInstruction(&new_index_constant)
+ ->result_id();
+ }
+ composite_access_instruction->SetInOperand(i, {new_in_operand});
+ }
+ } break;
+ default:
+ assert(0 && "Unknown composite type.");
+ break;
+ }
+ }
+}
+
+} // namespace reduce
+} // namespace spvtools
diff --git a/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_struct_member_reduction_opportunity.h b/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_struct_member_reduction_opportunity.h
new file mode 100644
index 00000000000..899e5ea7760
--- /dev/null
+++ b/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_struct_member_reduction_opportunity.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_REDUCE_REMOVE_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_H_
+#define SOURCE_REDUCE_REMOVE_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_H_
+
+#include "source/reduce/reduction_opportunity.h"
+
+#include "source/opt/instruction.h"
+
+namespace spvtools {
+namespace reduce {
+
+// An opportunity for removing a member from a struct type, adjusting all uses
+// of the struct accordingly.
+class RemoveStructMemberReductionOpportunity : public ReductionOpportunity {
+ public:
+ // Constructs a reduction opportunity from the struct type |struct_type|, for
+ // removal of member |member_index|.
+ RemoveStructMemberReductionOpportunity(opt::Instruction* struct_type,
+ uint32_t member_index)
+ : struct_type_(struct_type),
+ member_index_(member_index),
+ original_number_of_members_(struct_type->NumInOperands()) {}
+
+ // Opportunities to remove fields from a common struct type mutually
+ // invalidate each other. We guard against this by requiring that the struct
+ // still has the number of members it had when the opportunity was created.
+ bool PreconditionHolds() override;
+
+ protected:
+ void Apply() override;
+
+ private:
+ // |composite_access_instruction| is an instruction that accesses a composite
+ // id using either a series of literal indices (e.g. in the case of
+ // OpCompositeInsert) or a series of index ids (e.g. in the case of
+ // OpAccessChain).
+ //
+ // This function adjusts the indices that are used by
+ // |composite_access_instruction| to that whenever an index is accessing a
+ // member of |struct_type_|, it is decremented if the member is beyond
+ // |member_index_|, to account for the removal of the |member_index_|-th
+ // member.
+ //
+ // |composite_type_id| is the id of the composite type that the series of
+ // indices is to be applied to.
+ //
+ // |first_index_input_operand| specifies the first input operand that is an
+ // index.
+ //
+ // |literal_indices| specifies whether indices are given as literals (true),
+ // or as ids (false).
+ //
+ // If id-based indexing is used, this function will add a constant for
+ // |member_index_| - 1 to the module if needed.
+ void AdjustAccessedIndices(
+ uint32_t composite_type_id, uint32_t first_index_input_operand,
+ bool literal_indices, opt::IRContext* context,
+ opt::Instruction* composite_access_instruction) const;
+
+ // The struct type from which a member is to be removed.
+ opt::Instruction* struct_type_;
+
+ uint32_t member_index_;
+
+ uint32_t original_number_of_members_;
+};
+
+} // namespace reduce
+} // namespace spvtools
+
+#endif // SOURCE_REDUCE_REMOVE_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_H_
diff --git a/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp b/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_unused_instruction_reduction_opportunity_finder.cpp
index ce66691155f..91ec542c6f1 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_unused_instruction_reduction_opportunity_finder.cpp
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
+#include "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h"
#include "source/opcode.h"
#include "source/opt/instruction.h"
@@ -21,14 +21,14 @@
namespace spvtools {
namespace reduce {
-RemoveUnreferencedInstructionReductionOpportunityFinder::
- RemoveUnreferencedInstructionReductionOpportunityFinder(
+RemoveUnusedInstructionReductionOpportunityFinder::
+ RemoveUnusedInstructionReductionOpportunityFinder(
bool remove_constants_and_undefs)
: remove_constants_and_undefs_(remove_constants_and_undefs) {}
std::vector<std::unique_ptr<ReductionOpportunity>>
-RemoveUnreferencedInstructionReductionOpportunityFinder::
- GetAvailableOpportunities(opt::IRContext* context) const {
+RemoveUnusedInstructionReductionOpportunityFinder::GetAvailableOpportunities(
+ opt::IRContext* context) const {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
for (auto& inst : context->module()->debugs1()) {
@@ -60,13 +60,14 @@ RemoveUnreferencedInstructionReductionOpportunityFinder::
}
for (auto& inst : context->module()->types_values()) {
- if (context->get_def_use_mgr()->NumUsers(&inst) > 0) {
- continue;
- }
if (!remove_constants_and_undefs_ &&
spvOpcodeIsConstantOrUndef(inst.opcode())) {
continue;
}
+ if (!OnlyReferencedByIntimateDecorationOrEntryPointInterface(context,
+ inst)) {
+ continue;
+ }
result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
}
@@ -74,38 +75,9 @@ RemoveUnreferencedInstructionReductionOpportunityFinder::
if (context->get_def_use_mgr()->NumUsers(&inst) > 0) {
continue;
}
-
- uint32_t decoration = SpvDecorationMax;
- switch (inst.opcode()) {
- case SpvOpDecorate:
- case SpvOpDecorateId:
- case SpvOpDecorateString:
- decoration = inst.GetSingleWordInOperand(1u);
- break;
- case SpvOpMemberDecorate:
- case SpvOpMemberDecorateString:
- decoration = inst.GetSingleWordInOperand(2u);
- break;
- default:
- break;
- }
-
- // We conservatively only remove specific decorations that we believe will
- // not change the shader interface, will not make the shader invalid, will
- // actually be found in practice, etc.
-
- switch (decoration) {
- case SpvDecorationRelaxedPrecision:
- case SpvDecorationNoSignedWrap:
- case SpvDecorationNoContraction:
- case SpvDecorationNoUnsignedWrap:
- case SpvDecorationUserSemantic:
- break;
- default:
- // Give up.
- continue;
+ if (!IsIndependentlyRemovableDecoration(inst)) {
+ continue;
}
-
result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
}
@@ -139,9 +111,54 @@ RemoveUnreferencedInstructionReductionOpportunityFinder::
return result;
}
-std::string RemoveUnreferencedInstructionReductionOpportunityFinder::GetName()
- const {
- return "RemoveUnreferencedInstructionReductionOpportunityFinder";
+std::string RemoveUnusedInstructionReductionOpportunityFinder::GetName() const {
+ return "RemoveUnusedInstructionReductionOpportunityFinder";
+}
+
+bool RemoveUnusedInstructionReductionOpportunityFinder::
+ OnlyReferencedByIntimateDecorationOrEntryPointInterface(
+ opt::IRContext* context, const opt::Instruction& inst) const {
+ return context->get_def_use_mgr()->WhileEachUse(
+ &inst, [this](opt::Instruction* user, uint32_t use_index) -> bool {
+ return (user->IsDecoration() &&
+ !IsIndependentlyRemovableDecoration(*user)) ||
+ (user->opcode() == SpvOpEntryPoint && use_index > 2);
+ });
+}
+
+bool RemoveUnusedInstructionReductionOpportunityFinder::
+ IsIndependentlyRemovableDecoration(const opt::Instruction& inst) const {
+ uint32_t decoration;
+ switch (inst.opcode()) {
+ case SpvOpDecorate:
+ case SpvOpDecorateId:
+ case SpvOpDecorateString:
+ decoration = inst.GetSingleWordInOperand(1u);
+ break;
+ case SpvOpMemberDecorate:
+ case SpvOpMemberDecorateString:
+ decoration = inst.GetSingleWordInOperand(2u);
+ break;
+ default:
+ // The instruction is not a decoration. It is legitimate for this to be
+ // reached: it allows the method to be invoked on arbitrary instructions.
+ return false;
+ }
+
+ // We conservatively only remove specific decorations that we believe will
+ // not change the shader interface, will not make the shader invalid, will
+ // actually be found in practice, etc.
+
+ switch (decoration) {
+ case SpvDecorationRelaxedPrecision:
+ case SpvDecorationNoSignedWrap:
+ case SpvDecorationNoContraction:
+ case SpvDecorationNoUnsignedWrap:
+ case SpvDecorationUserSemantic:
+ return true;
+ default:
+ return false;
+ }
}
} // namespace reduce
diff --git a/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h b/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_unused_instruction_reduction_opportunity_finder.h
index bc4f137f130..cbf6a5bd169 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h
+++ b/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_unused_instruction_reduction_opportunity_finder.h
@@ -21,17 +21,19 @@ namespace spvtools {
namespace reduce {
// A finder for opportunities to remove non-control-flow instructions in blocks
-// in cases where the instruction's id is not referenced. As well as making the
-// module smaller, removing an instruction that references particular ids may
-// create opportunities for subsequently removing the instructions that
+// in cases where the instruction's id is either not referenced at all, or
+// referenced only in a trivial manner (for example, we regard a struct type as
+// unused if it is referenced only by struct layout decorations). As well as
+// making the module smaller, removing an instruction that references particular
+// ids may create opportunities for subsequently removing the instructions that
// generated those ids.
-class RemoveUnreferencedInstructionReductionOpportunityFinder
+class RemoveUnusedInstructionReductionOpportunityFinder
: public ReductionOpportunityFinder {
public:
- explicit RemoveUnreferencedInstructionReductionOpportunityFinder(
+ explicit RemoveUnusedInstructionReductionOpportunityFinder(
bool remove_constants_and_undefs);
- ~RemoveUnreferencedInstructionReductionOpportunityFinder() override = default;
+ ~RemoveUnusedInstructionReductionOpportunityFinder() override = default;
std::string GetName() const final;
@@ -39,6 +41,17 @@ class RemoveUnreferencedInstructionReductionOpportunityFinder
opt::IRContext* context) const final;
private:
+ // Returns true if and only if the only uses of |inst| are by decorations that
+ // relate intimately to the instruction (as opposed to decorations that could
+ // be removed independently), or by interface ids in OpEntryPoint.
+ bool OnlyReferencedByIntimateDecorationOrEntryPointInterface(
+ opt::IRContext* context, const opt::Instruction& inst) const;
+
+ // Returns true if and only if |inst| is a decoration instruction that can
+ // legitimately be removed on its own (rather than one that has to be removed
+ // simultaneously with other instructions).
+ bool IsIndependentlyRemovableDecoration(const opt::Instruction& inst) const;
+
bool remove_constants_and_undefs_;
};
diff --git a/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp b/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp
new file mode 100644
index 00000000000..39ce47f311a
--- /dev/null
+++ b/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp
@@ -0,0 +1,193 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h"
+
+#include <map>
+#include <set>
+
+#include "source/reduce/remove_struct_member_reduction_opportunity.h"
+
+namespace spvtools {
+namespace reduce {
+
+std::vector<std::unique_ptr<ReductionOpportunity>>
+RemoveUnusedStructMemberReductionOpportunityFinder::GetAvailableOpportunities(
+ opt::IRContext* context) const {
+ std::vector<std::unique_ptr<ReductionOpportunity>> result;
+
+ // We track those struct members that are never accessed. We do this by
+ // associating a member index to all the structs that have this member index
+ // but do not use it. This representation is designed to allow reduction
+ // opportunities to be provided in a useful manner, so that opportunities
+ // associated with the same struct are unlikely to be adjacent.
+ std::map<uint32_t, std::set<opt::Instruction*>> unused_member_to_structs;
+
+ // Consider every struct type in the module.
+ for (auto& type_or_value : context->types_values()) {
+ if (type_or_value.opcode() != SpvOpTypeStruct) {
+ continue;
+ }
+
+ // Initially, we assume that *every* member of the struct is unused. We
+ // then refine this based on observed uses.
+ std::set<uint32_t> unused_members;
+ for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) {
+ unused_members.insert(i);
+ }
+
+ // A separate reduction pass deals with removal of names. If a struct
+ // member is still named, we treat it as being used.
+ context->get_def_use_mgr()->ForEachUse(
+ &type_or_value,
+ [&unused_members](opt::Instruction* user, uint32_t /*operand_index*/) {
+ switch (user->opcode()) {
+ case SpvOpMemberName:
+ unused_members.erase(user->GetSingleWordInOperand(1));
+ break;
+ default:
+ break;
+ }
+ });
+
+ for (uint32_t member : unused_members) {
+ if (!unused_member_to_structs.count(member)) {
+ unused_member_to_structs.insert(
+ {member, std::set<opt::Instruction*>()});
+ }
+ unused_member_to_structs.at(member).insert(&type_or_value);
+ }
+ }
+
+ // We now go through every instruction that might index into a struct, and
+ // refine our tracking of which struct members are used based on the struct
+ // indexing we observe. We cannot just go through all uses of a struct type
+ // because the type is not necessarily even referenced, e.g. when walking
+ // arrays of structs.
+ for (auto& function : *context->module()) {
+ for (auto& block : function) {
+ for (auto& inst : block) {
+ switch (inst.opcode()) {
+ // For each indexing operation we observe, we invoke a helper to
+ // remove from our map those struct indices that are found to be used.
+ // The way the helper is invoked depends on whether the instruction
+ // uses literal or id indices, and the offset into the instruction's
+ // input operands from which index operands are provided.
+ case SpvOpAccessChain:
+ case SpvOpInBoundsAccessChain: {
+ auto composite_type_id =
+ context->get_def_use_mgr()
+ ->GetDef(context->get_def_use_mgr()
+ ->GetDef(inst.GetSingleWordInOperand(0))
+ ->type_id())
+ ->GetSingleWordInOperand(1);
+ MarkAccessedMembersAsUsed(context, composite_type_id, 1, false,
+ inst, &unused_member_to_structs);
+ } break;
+ case SpvOpPtrAccessChain:
+ case SpvOpInBoundsPtrAccessChain: {
+ auto composite_type_id =
+ context->get_def_use_mgr()
+ ->GetDef(context->get_def_use_mgr()
+ ->GetDef(inst.GetSingleWordInOperand(1))
+ ->type_id())
+ ->GetSingleWordInOperand(1);
+ MarkAccessedMembersAsUsed(context, composite_type_id, 2, false,
+ inst, &unused_member_to_structs);
+ } break;
+ case SpvOpCompositeExtract: {
+ auto composite_type_id =
+ context->get_def_use_mgr()
+ ->GetDef(inst.GetSingleWordInOperand(0))
+ ->type_id();
+ MarkAccessedMembersAsUsed(context, composite_type_id, 1, true, inst,
+ &unused_member_to_structs);
+ } break;
+ case SpvOpCompositeInsert: {
+ auto composite_type_id =
+ context->get_def_use_mgr()
+ ->GetDef(inst.GetSingleWordInOperand(1))
+ ->type_id();
+ MarkAccessedMembersAsUsed(context, composite_type_id, 2, true, inst,
+ &unused_member_to_structs);
+ } break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ // We now know those struct indices that are unsed, and we make a reduction
+ // opportunity for each of them. By mapping each relevant member index to the
+ // structs in which it is unsed, we will group all opportunities to remove
+ // member k of a struct (for some k) together. This reduces the likelihood
+ // that opportunities to remove members from the same struct will be adjacent,
+ // which is good because such opportunities mutually disable one another.
+ for (auto& entry : unused_member_to_structs) {
+ for (auto struct_type : entry.second) {
+ result.push_back(MakeUnique<RemoveStructMemberReductionOpportunity>(
+ struct_type, entry.first));
+ }
+ }
+ return result;
+}
+
+void RemoveUnusedStructMemberReductionOpportunityFinder::
+ MarkAccessedMembersAsUsed(
+ opt::IRContext* context, uint32_t composite_type_id,
+ uint32_t first_index_in_operand, bool literal_indices,
+ const opt::Instruction& composite_access_instruction,
+ std::map<uint32_t, std::set<opt::Instruction*>>*
+ unused_member_to_structs) const {
+ uint32_t next_type = composite_type_id;
+ for (uint32_t i = first_index_in_operand;
+ i < composite_access_instruction.NumInOperands(); i++) {
+ auto type_inst = context->get_def_use_mgr()->GetDef(next_type);
+ switch (type_inst->opcode()) {
+ case SpvOpTypeArray:
+ case SpvOpTypeMatrix:
+ case SpvOpTypeRuntimeArray:
+ case SpvOpTypeVector:
+ next_type = type_inst->GetSingleWordInOperand(0);
+ break;
+ case SpvOpTypeStruct: {
+ uint32_t index_operand =
+ composite_access_instruction.GetSingleWordInOperand(i);
+ uint32_t member = literal_indices ? index_operand
+ : context->get_def_use_mgr()
+ ->GetDef(index_operand)
+ ->GetSingleWordInOperand(0);
+ // Remove the struct type from the struct types associated with this
+ // member index, but only if a set of struct types is known to be
+ // associated with this member index.
+ if (unused_member_to_structs->count(member)) {
+ unused_member_to_structs->at(member).erase(type_inst);
+ }
+ next_type = type_inst->GetSingleWordInOperand(member);
+ } break;
+ default:
+ assert(0 && "Unknown composite type.");
+ break;
+ }
+ }
+}
+
+std::string RemoveUnusedStructMemberReductionOpportunityFinder::GetName()
+ const {
+ return "RemoveUnusedStructMemberReductionOpportunityFinder";
+}
+
+} // namespace reduce
+} // namespace spvtools
diff --git a/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h b/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h
new file mode 100644
index 00000000000..13f40172bfd
--- /dev/null
+++ b/chromium/third_party/SPIRV-Tools/src/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_REDUCE_REMOVE_UNUSED_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_FINDER_H_
+#define SOURCE_REDUCE_REMOVE_UNUSED_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_FINDER_H_
+
+#include "source/reduce/reduction_opportunity_finder.h"
+
+namespace spvtools {
+namespace reduce {
+
+// A finder for opportunities to remove struct members that are not explicitly
+// used by extract, insert or access chain instructions.
+class RemoveUnusedStructMemberReductionOpportunityFinder
+ : public ReductionOpportunityFinder {
+ public:
+ RemoveUnusedStructMemberReductionOpportunityFinder() = default;
+
+ ~RemoveUnusedStructMemberReductionOpportunityFinder() override = default;
+
+ std::string GetName() const final;
+
+ std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
+ opt::IRContext* context) const final;
+
+ private:
+ // A helper method to update |unused_members_to_structs| by removing from it
+ // all struct member accesses that take place in
+ // |composite_access_instruction|.
+ //
+ // |composite_type_id| is the type of the root object indexed into by the
+ // instruction.
+ //
+ // |first_index_in_operand| provides indicates where in the input operands the
+ // sequence of indices begins.
+ //
+ // |literal_indices| indicates whether indices are literals (true) or ids
+ // (false).
+ void MarkAccessedMembersAsUsed(
+ opt::IRContext* context, uint32_t composite_type_id,
+ uint32_t first_index_in_operand, bool literal_indices,
+ const opt::Instruction& composite_access_instruction,
+ std::map<uint32_t, std::set<opt::Instruction*>>* unused_member_to_structs)
+ const;
+};
+
+} // namespace reduce
+} // namespace spvtools
+
+#endif // SOURCE_REDUCE_REMOVE_UNUSED_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_FINDER_H_
diff --git a/chromium/third_party/SPIRV-Tools/src/source/val/validate_extensions.cpp b/chromium/third_party/SPIRV-Tools/src/source/val/validate_extensions.cpp
index 1e311c19d0f..7ce681c4f85 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/val/validate_extensions.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/val/validate_extensions.cpp
@@ -2300,7 +2300,14 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
if (validate_parent != SPV_SUCCESS) return validate_parent;
CHECK_OPERAND("Linkage Name", SpvOpString, 11);
- CHECK_OPERAND("Size", SpvOpConstant, 12);
+ if (!DoesDebugInfoOperandMatchExpectation(
+ _,
+ [](OpenCLDebugInfo100Instructions dbg_inst) {
+ return dbg_inst == OpenCLDebugInfo100DebugInfoNone;
+ },
+ inst, 12)) {
+ CHECK_OPERAND("Size", SpvOpConstant, 12);
+ }
for (uint32_t word_index = 14; word_index < num_words; ++word_index) {
if (!DoesDebugInfoOperandMatchExpectation(
_,
diff --git a/chromium/third_party/SPIRV-Tools/src/source/val/validate_function.cpp b/chromium/third_party/SPIRV-Tools/src/source/val/validate_function.cpp
index f130eacdad2..596186bbe6a 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/val/validate_function.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/val/validate_function.cpp
@@ -71,6 +71,7 @@ spv_result_t ValidateFunction(ValidationState_t& _, const Instruction* inst) {
}
const std::vector<SpvOp> acceptable = {
+ SpvOpGroupDecorate,
SpvOpDecorate,
SpvOpEnqueueKernel,
SpvOpEntryPoint,
diff --git a/chromium/third_party/SPIRV-Tools/src/source/val/validate_image.cpp b/chromium/third_party/SPIRV-Tools/src/source/val/validate_image.cpp
index 5b77058c89e..9ce74a3d0a5 100644
--- a/chromium/third_party/SPIRV-Tools/src/source/val/validate_image.cpp
+++ b/chromium/third_party/SPIRV-Tools/src/source/val/validate_image.cpp
@@ -160,6 +160,17 @@ bool IsValidLodOperand(const ValidationState_t& _, SpvOp opcode) {
}
}
+bool IsValidGatherLodBiasAMD(const ValidationState_t& _, SpvOp opcode) {
+ switch (opcode) {
+ case SpvOpImageGather:
+ case SpvOpImageSparseGather:
+ return _.HasCapability(SpvCapabilityImageGatherBiasLodAMD);
+ default:
+ break;
+ }
+ return false;
+}
+
// Returns true if the opcode is a Image instruction which applies
// homogenous projection to the coordinates.
bool IsProj(SpvOp opcode) {
@@ -260,11 +271,12 @@ spv_result_t ValidateImageOperands(ValidationState_t& _,
const bool is_implicit_lod = IsImplicitLod(opcode);
const bool is_explicit_lod = IsExplicitLod(opcode);
const bool is_valid_lod_operand = IsValidLodOperand(_, opcode);
+ const bool is_valid_gather_lod_bias_amd = IsValidGatherLodBiasAMD(_, opcode);
// The checks should be done in the order of definition of OperandImage.
if (mask & SpvImageOperandsBiasMask) {
- if (!is_implicit_lod) {
+ if (!is_implicit_lod && !is_valid_gather_lod_bias_amd) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Image Operand Bias can only be used with ImplicitLod opcodes";
}
@@ -290,7 +302,7 @@ spv_result_t ValidateImageOperands(ValidationState_t& _,
if (mask & SpvImageOperandsLodMask) {
if (!is_valid_lod_operand && opcode != SpvOpImageFetch &&
- opcode != SpvOpImageSparseFetch) {
+ opcode != SpvOpImageSparseFetch && !is_valid_gather_lod_bias_amd) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Image Operand Lod can only be used with ExplicitLod opcodes "
<< "and OpImageFetch";
@@ -303,7 +315,7 @@ spv_result_t ValidateImageOperands(ValidationState_t& _,
}
const uint32_t type_id = _.GetTypeId(inst->word(word_index++));
- if (is_explicit_lod) {
+ if (is_explicit_lod || is_valid_gather_lod_bias_amd) {
if (!_.IsFloatScalarType(type_id)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Expected Image Operand Lod to be float scalar when used "
diff --git a/chromium/third_party/SPIRV-Tools/src/utils/check_symbol_exports.py b/chromium/third_party/SPIRV-Tools/src/utils/check_symbol_exports.py
index e14c2eb8964..bcd77da6876 100755
--- a/chromium/third_party/SPIRV-Tools/src/utils/check_symbol_exports.py
+++ b/chromium/third_party/SPIRV-Tools/src/utils/check_symbol_exports.py
@@ -55,11 +55,11 @@ def check_library(library):
# _Z[0-9]+spv[A-Z_] : C++ symbol starting with spv[A-Z_]
symbol_ok_pattern = re.compile(r'^(spv[A-Z]|_ZN|_Z[0-9]+spv[A-Z_])')
- # In addition, the following pattern whitelists global functions that are added
+ # In addition, the following pattern allowlists global functions that are added
# by the protobuf compiler:
# - AddDescriptors_spvtoolsfuzz_2eproto()
# - InitDefaults_spvtoolsfuzz_2eproto()
- symbol_whitelist_pattern = re.compile(r'_Z[0-9]+(InitDefaults|AddDescriptors)_spvtoolsfuzz_2eprotov')
+ symbol_allowlist_pattern = re.compile(r'_Z[0-9]+(InitDefaults|AddDescriptors)_spvtoolsfuzz_2eprotov')
seen = set()
result = 0
@@ -70,7 +70,7 @@ def check_library(library):
if symbol not in seen:
seen.add(symbol)
#print("look at '{}'".format(symbol))
- if not (symbol_whitelist_pattern.match(symbol) or symbol_ok_pattern.match(symbol)):
+ if not (symbol_allowlist_pattern.match(symbol) or symbol_ok_pattern.match(symbol)):
print('{}: error: Unescaped exported symbol: {}'.format(PROG, symbol))
result = 1
return result
diff --git a/chromium/third_party/SPIRV-Tools/src/utils/roll_deps.sh b/chromium/third_party/SPIRV-Tools/src/utils/roll_deps.sh
index 622afc9ed89..d5c547fe073 100755
--- a/chromium/third_party/SPIRV-Tools/src/utils/roll_deps.sh
+++ b/chromium/third_party/SPIRV-Tools/src/utils/roll_deps.sh
@@ -13,19 +13,34 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Attempts to roll all entries in DEPS to origin/master and creates a
-# commit.
+# Attempts to roll all entries in DEPS to tip-of-tree and create a commit.
#
# Depends on roll-dep from depot_path being in PATH.
+effcee_dir="third_party/effcee/"
+effcee_trunk="origin/main"
+googletest_dir="third_party/googletest/"
+googletest_trunk="origin/master"
+re2_dir="third_party/re2/"
+re2_trunk="origin/master"
+spirv_headers_dir="third_party/spirv-headers/"
+spirv_headers_trunk="origin/master"
+
# This script assumes it's parent directory is the repo root.
repo_path=$(dirname "$0")/..
-effcee_dir="external/effcee/"
-googletest_dir="external/googletest/"
-re2_dir="external/re2/"
-spirv_headers_dir="external/spirv-headers/"
-
cd "$repo_path"
-roll-dep "$@" "${effcee_dir}" "${googletest_dir}" "${re2_dir}" "${spirv_headers_dir}"
+if [[ $(git diff --stat) != '' ]]; then
+ echo "Working tree is dirty, commit changes before attempting to roll DEPS"
+ exit 1
+fi
+
+old_head=$(git rev-parse HEAD)
+
+roll-dep --ignore-dirty-tree --roll-to="${effcee_trunk}" "${effcee_dir}"
+roll-dep --ignore-dirty-tree --roll-to="${googletest_trunk}" "${googletest_dir}"
+roll-dep --ignore-dirty-tree --roll-to="${re2_trunk}" "${re2_dir}"
+roll-dep --ignore-dirty-tree --roll-to="${spirv_headers_trunk}" "${spirv_headers_dir}"
+
+git rebase --interactive "${old_head}"