diff --git a/3rdparty/spirv-tools/DEPS b/3rdparty/spirv-tools/DEPS
index fd626f600..8a61f227d 100644
--- a/3rdparty/spirv-tools/DEPS
+++ b/3rdparty/spirv-tools/DEPS
@@ -3,10 +3,10 @@ use_relative_paths = True
vars = {
'github': 'https://github.com',
- 'effcee_revision': '6fa2a03cebb4fb18fbad086d53d1054928bef54e',
+ 'effcee_revision': 'cd25ec17e9382f99a895b9ef53ff3c277464d07d',
'googletest_revision': 'f2fb48c3b3d79a75a88a99fba6576b25d42ec528',
're2_revision': '5bd613749fd530b576b890283bfb6bc6ea6246cb',
- 'spirv_headers_revision': '601d738723ac381741311c6c98c36d6170be14a2',
+ 'spirv_headers_revision': '842ec90674627ed2ffef609e3cd79d1562eded01',
}
deps = {
diff --git a/3rdparty/spirv-tools/README.md b/3rdparty/spirv-tools/README.md
new file mode 100644
index 000000000..3bebbf2d4
--- /dev/null
+++ b/3rdparty/spirv-tools/README.md
@@ -0,0 +1,620 @@
+# SPIR-V Tools
+
+## Overview
+
+The SPIR-V Tools project provides an API and commands for processing SPIR-V
+modules.
+
+The project includes an assembler, binary module parser, disassembler,
+validator, and optimizer for SPIR-V. Except for the optimizer, all are based
+on a common static library. The library contains all of the implementation
+details, and is used in the standalone tools whilst also enabling integration
+into other code bases directly. The optimizer implementation resides in its
+own library, which depends on the core library.
+
+The interfaces have stabilized:
+We don't anticipate making a breaking change for existing features.
+
+SPIR-V is defined by the Khronos Group Inc.
+See the [SPIR-V Registry][spirv-registry] for the SPIR-V specification,
+headers, and XML registry.
+
+## Downloads
+
+[](https://ci.appveyor.com/project/Khronoswebmaster/spirv-tools/branch/master)
+
[](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_release.html)
+
[](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_release.html)
+
[](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_release.html)
+
+[More downloads](docs/downloads.md)
+
+## Versioning SPIRV-Tools
+
+See [`CHANGES`](CHANGES) for a high level summary of recent changes, by version.
+
+SPIRV-Tools project version numbers are of the form `v`*year*`.`*index* and with
+an optional `-dev` suffix to indicate work in progress. For exampe, the
+following versions are ordered from oldest to newest:
+
+* `v2016.0`
+* `v2016.1-dev`
+* `v2016.1`
+* `v2016.2-dev`
+* `v2016.2`
+
+Use the `--version` option on each command line tool to see the software
+version. An API call reports the software version as a C-style string.
+
+## Supported features
+
+### Assembler, binary parser, and disassembler
+
+* Support for SPIR-V 1.0, 1.1, 1.2, and 1.3
+ * Based on SPIR-V syntax described by JSON grammar files in the
+ [SPIRV-Headers](https://github.com/KhronosGroup/SPIRV-Headers) repository.
+* Support for extended instruction sets:
+ * GLSL std450 version 1.0 Rev 3
+ * OpenCL version 1.0 Rev 2
+* Assembler only does basic syntax checking. No cross validation of
+ IDs or types is performed, except to check literal arguments to
+ `OpConstant`, `OpSpecConstant`, and `OpSwitch`.
+
+See [`docs/syntax.md`](docs/syntax.md) for the assembly language syntax.
+
+### Validator
+
+The validator checks validation rules described by the SPIR-V specification.
+
+Khronos recommends that tools that create or transform SPIR-V modules use the
+validator to ensure their outputs are valid, and that tools that consume SPIR-V
+modules optionally use the validator to protect themselves from bad inputs.
+This is especially encouraged for debug and development scenarios.
+
+The validator has one-sided error: it will only return an error when it has
+implemented a rule check and the module violates that rule.
+
+The validator is incomplete.
+See the [CHANGES](CHANGES) file for reports on completed work, and
+the [Validator
+sub-project](https://github.com/KhronosGroup/SPIRV-Tools/projects/1) for planned
+and in-progress work.
+
+*Note*: The validator checks some Universal Limits, from section 2.17 of the SPIR-V spec.
+The validator will fail on a module that exceeds those minimum upper bound limits.
+It is [future work](https://github.com/KhronosGroup/SPIRV-Tools/projects/1#card-1052403)
+to parameterize the validator to allow larger
+limits accepted by a more than minimally capable SPIR-V consumer.
+
+
+### Optimizer
+
+*Note:* The optimizer is still under development.
+
+Currently supported optimizations:
+* General
+ * Strip debug info
+* Specialization Constants
+ * Set spec constant default value
+ * Freeze spec constant
+ * Fold `OpSpecConstantOp` and `OpSpecConstantComposite`
+ * Unify constants
+ * Eliminate dead constant
+* Code Reduction
+ * Inline all function calls exhaustively
+ * Convert local access chains to inserts/extracts
+ * Eliminate local load/store in single block
+ * Eliminate local load/store with single store
+ * Eliminate local load/store with multiple stores
+ * Eliminate local extract from insert
+ * Eliminate dead instructions (aggressive)
+ * Eliminate dead branches
+ * Merge single successor / single predecessor block pairs
+ * Eliminate common uniform loads
+ * Remove duplicates: Capabilities, extended instruction imports, types, and
+ decorations.
+
+For the latest list with detailed documentation, please refer to
+[`include/spirv-tools/optimizer.hpp`](include/spirv-tools/optimizer.hpp).
+
+For suggestions on using the code reduction options, please refer to this [white paper](https://www.lunarg.com/shader-compiler-technologies/white-paper-spirv-opt/).
+
+
+### Linker
+
+*Note:* The linker is still under development.
+
+Current features:
+* Combine multiple SPIR-V binary modules together.
+* Combine into a library (exports are retained) or an executable (no symbols
+ are exported).
+
+See the [CHANGES](CHANGES) file for reports on completed work, and the [General
+sub-project](https://github.com/KhronosGroup/SPIRV-Tools/projects/2) for
+planned and in-progress work.
+
+
+### Reducer
+
+*Note:* The reducer is still under development.
+
+The reducer simplifies and shrinks a SPIR-V module with respect to a
+user-supplied *interestingness function*. For example, given a large
+SPIR-V module that cause some SPIR-V compiler to fail with a given
+fatal error message, the reducer could be used to look for a smaller
+version of the module that causes the compiler to fail with the same
+fatal error message.
+
+To suggest an additional capability for the reducer, [file an
+issue](https://github.com/KhronosGroup/SPIRV-Tools/issues]) with
+"Reducer:" as the start of its title.
+
+
+### Extras
+
+* [Utility filters](#utility-filters)
+* Build target `spirv-tools-vimsyntax` generates file `spvasm.vim`.
+ Copy that file into your `$HOME/.vim/syntax` directory to get SPIR-V assembly syntax
+ highlighting in Vim. This build target is not built by default.
+
+## Contributing
+
+The SPIR-V Tools project is maintained by members of the The Khronos Group Inc.,
+and is hosted at https://github.com/KhronosGroup/SPIRV-Tools.
+
+Consider joining the `public_spirv_tools_dev@khronos.org` mailing list, via
+[https://www.khronos.org/spir/spirv-tools-mailing-list/](https://www.khronos.org/spir/spirv-tools-mailing-list/).
+The mailing list is used to discuss development plans for the SPIRV-Tools as an open source project.
+Once discussion is resolved,
+specific work is tracked via issues and sometimes in one of the
+[projects][spirv-tools-projects].
+
+(To provide feedback on the SPIR-V _specification_, file an issue on the
+[SPIRV-Headers][spirv-headers] GitHub repository.)
+
+See [`docs/projects.md`](docs/projects.md) to see how we use the
+[GitHub Project
+feature](https://help.github.com/articles/tracking-the-progress-of-your-work-with-projects/)
+to organize planned and in-progress work.
+
+Contributions via merge request are welcome. Changes should:
+* Be provided under the [Apache 2.0](#license).
+* You'll be prompted with a one-time "click-through"
+ [Khronos Open Source Contributor License Agreement][spirv-tools-cla]
+ (CLA) dialog as part of submitting your pull request or
+ other contribution to GitHub.
+* Include tests to cover updated functionality.
+* C++ code should follow the [Google C++ Style Guide][cpp-style-guide].
+* Code should be formatted with `clang-format`.
+ [kokoro/check-format/build.sh](kokoro/check-format/build.sh)
+ shows how to download it. Note that we currently use
+ `clang-format version 5.0.0` for SPIRV-Tools. Settings are defined by
+ the included [.clang-format](.clang-format) file.
+
+We intend to maintain a linear history on the GitHub `master` branch.
+
+### Source code organization
+
+* `example`: demo code of using SPIRV-Tools APIs
+* `external/googletest`: Intended location for the
+ [googletest][googletest] sources, not provided
+* `external/effcee`: Location of [Effcee][effcee] sources, if the `effcee` library
+ is not already configured by an enclosing project.
+* `external/re2`: Location of [RE2][re2] sources, if the `re2` library is not already
+ configured by an enclosing project.
+ (The Effcee project already requires RE2.)
+* `include/`: API clients should add this directory to the include search path
+* `external/spirv-headers`: Intended location for
+ [SPIR-V headers][spirv-headers], not provided
+* `include/spirv-tools/libspirv.h`: C API public interface
+* `source/`: API implementation
+* `test/`: Tests, using the [googletest][googletest] framework
+* `tools/`: Command line executables
+
+Example of getting sources, assuming SPIRV-Tools is configured as a standalone project:
+
+ git clone https://github.com/KhronosGroup/SPIRV-Tools.git spirv-tools
+ git clone https://github.com/KhronosGroup/SPIRV-Headers.git spirv-tools/external/spirv-headers
+ git clone https://github.com/google/googletest.git spirv-tools/external/googletest
+ git clone https://github.com/google/effcee.git spirv-tools/external/effcee
+ git clone https://github.com/google/re2.git spirv-tools/external/re2
+
+### Tests
+
+The project contains a number of tests, used to drive development
+and ensure correctness. The tests are written using the
+[googletest][googletest] framework. The `googletest`
+source is not provided with this project. There are two ways to enable
+tests:
+* If SPIR-V Tools is configured as part of an enclosing project, then the
+ enclosing project should configure `googletest` before configuring SPIR-V Tools.
+* If SPIR-V Tools is configured as a standalone project, then download the
+ `googletest` source into the `/external/googletest` directory before
+ configuring and building the project.
+
+*Note*: You must use a version of googletest that includes
+[a fix][googletest-pull-612] for [googletest issue 610][googletest-issue-610].
+The fix is included on the googletest master branch any time after 2015-11-10.
+In particular, googletest must be newer than version 1.7.0.
+
+### Dependency on Effcee
+
+Some tests depend on the [Effcee][effcee] library for stateful matching.
+Effcee itself depends on [RE2][re2].
+
+* If SPIRV-Tools is configured as part of a larger project that already uses
+ Effcee, then that project should include Effcee before SPIRV-Tools.
+* Otherwise, SPIRV-Tools expects Effcee sources to appear in `external/effcee`
+ and RE2 sources to appear in `external/re2`.
+
+
+## Build
+
+Instead of building manually, you can also download the binaries for your
+platform directly from the [master-tot release][master-tot-release] on GitHub.
+Those binaries are automatically uploaded by the buildbots after successful
+testing and they always reflect the current top of the tree of the master
+branch.
+
+In order to build the code, you first need to sync the external repositories
+that it depends on. Assume that `` is the root directory of the
+checked out code:
+
+```sh
+cd
+git clone https://github.com/KhronosGroup/SPIRV-Headers.git external/spirv-headers
+git clone https://github.com/google/effcee.git external/effcee
+git clone https://github.com/google/re2.git external/re2
+git clone https://github.com/google/googletest.git external/googletest # optional
+
+```
+
+*Note*:
+The script `utils/git-sync-deps` can be used to checkout and/or update the
+contents of the repos under `external/` instead of manually maintaining them.
+
+### Build using CMake
+You can build The project using [CMake][cmake] to generate platform-specific
+build configurations.
+
+```sh
+cd
+mkdir build && cd build
+cmake [-G ]
+```
+
+Once the build files have been generated, build using your preferred
+development environment.
+
+### Build using Bazel
+You can also use [Bazel](https://bazel.build/) to build the project.
+```sh
+cd
+bazel build :all
+```
+
+### Tools you'll need
+
+For building and testing SPIRV-Tools, the following tools should be
+installed regardless of your OS:
+
+- [CMake](http://www.cmake.org/): if using CMake for generating compilation
+targets, you need to install CMake Version 2.8.12 or later.
+- [Python 3](http://www.python.org/): for utility scripts and running the test
+suite.
+- [Bazel](https://baze.build/) (optional): if building the source with Bazel,
+you need to install Bazel Version 0.29.1 on your machine. Other versions may
+also work, but are not verified.
+
+SPIRV-Tools is regularly tested with the the following compilers:
+
+On Linux
+- GCC version 4.8.5
+- Clang version 3.8
+
+On MacOS
+- AppleClang 10.0
+
+On Windows
+- Visual Studio 2015
+- Visual Studio 2017
+
+Other compilers or later versions may work, but they are not tested.
+
+### CMake options
+
+The following CMake options are supported:
+
+* `SPIRV_COLOR_TERMINAL={ON|OFF}`, default `ON` - Enables color console output.
+* `SPIRV_SKIP_TESTS={ON|OFF}`, default `OFF`- Build only the library and
+ the command line tools. This will prevent the tests from being built.
+* `SPIRV_SKIP_EXECUTABLES={ON|OFF}`, default `OFF`- Build only the library, not
+ the command line tools and tests.
+* `SPIRV_USE_SANITIZER=`, default is no sanitizing - On UNIX
+ platforms with an appropriate version of `clang` this option enables the use
+ of the sanitizers documented [here][clang-sanitizers].
+ This should only be used with a debug build.
+* `SPIRV_WARN_EVERYTHING={ON|OFF}`, default `OFF` - On UNIX platforms enable
+ more strict warnings. The code might not compile with this option enabled.
+ For Clang, enables `-Weverything`. For GCC, enables `-Wpedantic`.
+ See [`CMakeLists.txt`](CMakeLists.txt) for details.
+* `SPIRV_WERROR={ON|OFF}`, default `ON` - Forces a compilation error on any
+ warnings encountered by enabling the compiler-specific compiler front-end
+ option. No compiler front-end options are enabled when this option is OFF.
+
+Additionally, you can pass additional C preprocessor definitions to SPIRV-Tools
+via setting `SPIRV_TOOLS_EXTRA_DEFINITIONS`. For example, by setting it to
+`/D_ITERATOR_DEBUG_LEVEL=0` on Windows, you can disable checked iterators and
+iterator debugging.
+
+### Android
+
+SPIR-V Tools supports building static libraries `libSPIRV-Tools.a` and
+`libSPIRV-Tools-opt.a` for Android:
+
+```
+cd
+
+export ANDROID_NDK=/path/to/your/ndk
+
+mkdir build && cd build
+mkdir libs
+mkdir app
+
+$ANDROID_NDK/ndk-build -C ../android_test \
+ NDK_PROJECT_PATH=. \
+ NDK_LIBS_OUT=`pwd`/libs \
+ NDK_APP_OUT=`pwd`/app
+```
+
+### Updating DEPS
+Occasionally the entries in DEPS will need to be updated. This is done on demand
+when there is a request to do this, often due to downstream breakages. There is
+a script `utils/roll_deps.sh` provided, which will generate a patch with the
+updated DEPS values. This will still need to be tested in your checkout to
+confirm that there are no integration issues that need to be resolved.
+
+## Library
+
+### Usage
+
+The internals of the library use C++11 features, and are exposed via both a C
+and C++ API.
+
+In order to use the library from an application, the include path should point
+to `/include`, which will enable the application to include the
+header `/include/spirv-tools/libspirv.h{|pp}` then linking against
+the static library in `/source/libSPIRV-Tools.a` or
+`/source/SPIRV-Tools.lib`.
+For optimization, the header file is
+`/include/spirv-tools/optimizer.hpp`, and the static library is
+`/source/libSPIRV-Tools-opt.a` or
+`/source/SPIRV-Tools-opt.lib`.
+
+* `SPIRV-Tools` CMake target: Creates the static library:
+ * `/source/libSPIRV-Tools.a` on Linux and OS X.
+ * `/source/libSPIRV-Tools.lib` on Windows.
+* `SPIRV-Tools-opt` CMake target: Creates the static library:
+ * `/source/libSPIRV-Tools-opt.a` on Linux and OS X.
+ * `/source/libSPIRV-Tools-opt.lib` on Windows.
+
+#### Entry points
+
+The interfaces are still under development, and are expected to change.
+
+There are five main entry points into the library in the C interface:
+
+* `spvTextToBinary`: An assembler, translating text to a binary SPIR-V module.
+* `spvBinaryToText`: A disassembler, translating a binary SPIR-V module to
+ text.
+* `spvBinaryParse`: The entry point to a binary parser API. It issues callbacks
+ for the header and each parsed instruction. The disassembler is implemented
+ as a client of `spvBinaryParse`.
+* `spvValidate` implements the validator functionality. *Incomplete*
+* `spvValidateBinary` implements the validator functionality. *Incomplete*
+
+The C++ interface is comprised of three classes, `SpirvTools`, `Optimizer` and
+`Linker`, all in the `spvtools` namespace.
+* `SpirvTools` provides `Assemble`, `Disassemble`, and `Validate` methods.
+* `Optimizer` provides methods for registering and running optimization passes.
+* `Linker` provides methods for combining together multiple binaries.
+
+## Command line tools
+
+Command line tools, which wrap the above library functions, are provided to
+assemble or disassemble shader files. It's a convention to name SPIR-V
+assembly and binary files with suffix `.spvasm` and `.spv`, respectively.
+
+### Assembler tool
+
+The assembler reads the assembly language text, and emits the binary form.
+
+The standalone assembler is the exectuable called `spirv-as`, and is located in
+`/tools/spirv-as`. The functionality of the assembler is implemented
+by the `spvTextToBinary` library function.
+
+* `spirv-as` - the standalone assembler
+ * `/tools/as`
+
+Use option `-h` to print help.
+
+### Disassembler tool
+
+The disassembler reads the binary form, and emits assembly language text.
+
+The standalone disassembler is the executable called `spirv-dis`, and is located in
+`/tools/spirv-dis`. The functionality of the disassembler is implemented
+by the `spvBinaryToText` library function.
+
+* `spirv-dis` - the standalone disassembler
+ * `/tools/dis`
+
+Use option `-h` to print help.
+
+The output includes syntax colouring when printing to the standard output stream,
+on Linux, Windows, and OS X.
+
+### Linker tool
+
+The linker combines multiple SPIR-V binary modules together, resulting in a single
+binary module as output.
+
+This is a work in progress.
+The linker does not support OpenCL program linking options related to math
+flags. (See section 5.6.5.2 in OpenCL 1.2)
+
+* `spirv-link` - the standalone linker
+ * `/tools/link`
+
+### Optimizer tool
+
+The optimizer processes a SPIR-V binary module, applying transformations
+in the specified order.
+
+This is a work in progress, with initially only few available transformations.
+
+* `spirv-opt` - the standalone optimizer
+ * `/tools/opt`
+
+### Validator tool
+
+*Warning:* This functionality is under development, and is incomplete.
+
+The standalone validator is the executable called `spirv-val`, and is located in
+`/tools/spirv-val`. The functionality of the validator is implemented
+by the `spvValidate` library function.
+
+The validator operates on the binary form.
+
+* `spirv-val` - the standalone validator
+ * `/tools/val`
+
+### Reducer tool
+
+The reducer shrinks a SPIR-V binary module, guided by a user-supplied
+*interestingness test*.
+
+This is a work in progress, with initially only shrinks a module in a few ways.
+
+* `spirv-reduce` - the standalone reducer
+ * `/tools/reduce`
+
+Run `spirv-reduce --help` to see how to specify interestingness.
+
+### Control flow dumper tool
+
+The control flow dumper prints the control flow graph for a SPIR-V module as a
+[GraphViz](http://www.graphviz.org/) graph.
+
+This is experimental.
+
+* `spirv-cfg` - the control flow graph dumper
+ * `/tools/cfg`
+
+### Utility filters
+
+* `spirv-lesspipe.sh` - Automatically disassembles `.spv` binary files for the
+ `less` program, on compatible systems. For example, set the `LESSOPEN`
+ environment variable as follows, assuming both `spirv-lesspipe.sh` and
+ `spirv-dis` are on your executable search path:
+ ```
+ export LESSOPEN='| spirv-lesspipe.sh "%s"'
+ ```
+ Then you page through a disassembled module as follows:
+ ```
+ less foo.spv
+ ```
+ * The `spirv-lesspipe.sh` script will pass through any extra arguments to
+ `spirv-dis`. So, for example, you can turn off colours and friendly ID
+ naming as follows:
+ ```
+ export LESSOPEN='| spirv-lesspipe.sh "%s" --no-color --raw-id'
+ ```
+
+* [vim-spirv](https://github.com/kbenzie/vim-spirv) - A vim plugin which
+ supports automatic disassembly of `.spv` files using the `:edit` command and
+ assembly using the `:write` command. The plugin also provides additional
+ features which include; syntax highlighting; highlighting of all ID's matching
+ the ID under the cursor; and highlighting errors where the `Instruction`
+ operand of `OpExtInst` is used without an appropriate `OpExtInstImport`.
+
+* `50spirv-tools.el` - Automatically disassembles '.spv' binary files when
+ loaded into the emacs text editor, and re-assembles them when saved,
+ provided any modifications to the file are valid. This functionality
+ must be explicitly requested by defining the symbol
+ SPIRV_TOOLS_INSTALL_EMACS_HELPERS as follows:
+ ```
+ cmake -DSPIRV_TOOLS_INSTALL_EMACS_HELPERS=true ...
+ ```
+
+ In addition, this helper is only installed if the directory /etc/emacs/site-start.d
+ exists, which is typically true if emacs is installed on the system.
+
+ Note that symbol IDs are not currently preserved through a load/edit/save operation.
+ This may change if the ability is added to spirv-as.
+
+
+### Tests
+
+Tests are only built when googletest is found. Use `ctest` to run all the
+tests.
+
+## Future Work
+
+
+_See the [projects pages](https://github.com/KhronosGroup/SPIRV-Tools/projects)
+for more information._
+
+### Assembler and disassembler
+
+* The disassembler could emit helpful annotations in comments. For example:
+ * Use variable name information from debug instructions to annotate
+ key operations on variables.
+ * Show control flow information by annotating `OpLabel` instructions with
+ that basic block's predecessors.
+* Error messages could be improved.
+
+### Validator
+
+This is a work in progress.
+
+### Linker
+
+* The linker could accept math transformations such as allowing MADs, or other
+ math flags passed at linking-time in OpenCL.
+* Linkage attributes can not be applied through a group.
+* Check decorations of linked functions attributes.
+* Remove dead instructions, such as OpName targeting imported symbols.
+
+## Licence
+
+Full license terms are in [LICENSE](LICENSE)
+```
+Copyright (c) 2015-2016 The Khronos Group Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+```
+
+[spirv-tools-cla]: https://cla-assistant.io/KhronosGroup/SPIRV-Tools
+[spirv-tools-projects]: https://github.com/KhronosGroup/SPIRV-Tools/projects
+[spirv-tools-mailing-list]: https://www.khronos.org/spir/spirv-tools-mailing-list
+[spirv-registry]: https://www.khronos.org/registry/spir-v/
+[spirv-headers]: https://github.com/KhronosGroup/SPIRV-Headers
+[googletest]: https://github.com/google/googletest
+[googletest-pull-612]: https://github.com/google/googletest/pull/612
+[googletest-issue-610]: https://github.com/google/googletest/issues/610
+[effcee]: https://github.com/google/effcee
+[re2]: https://github.com/google/re2
+[CMake]: https://cmake.org/
+[cpp-style-guide]: https://google.github.io/styleguide/cppguide.html
+[clang-sanitizers]: http://clang.llvm.org/docs/UsersManual.html#controlling-code-generation
+[master-tot-release]: https://github.com/KhronosGroup/SPIRV-Tools/releases/tag/master-tot
diff --git a/3rdparty/spirv-tools/include/generated/build-version.inc b/3rdparty/spirv-tools/include/generated/build-version.inc
index 06a28c38b..115b30e9d 100644
--- a/3rdparty/spirv-tools/include/generated/build-version.inc
+++ b/3rdparty/spirv-tools/include/generated/build-version.inc
@@ -1 +1 @@
-"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-89-g84b19760"
+"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-102-gc18c9ff6"
diff --git a/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt b/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
index ac8c002b3..b21d210d0 100644
--- a/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
+++ b/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
@@ -31,6 +31,7 @@ if(SPIRV_BUILD_FUZZER)
set(SPIRV_TOOLS_FUZZ_SOURCES
data_descriptor.h
fact_manager.h
+ force_render_red.h
fuzzer.h
fuzzer_context.h
fuzzer_pass.h
@@ -62,13 +63,14 @@ if(SPIRV_BUILD_FUZZER)
transformation_move_block_down.h
transformation_replace_boolean_constant_with_constant_binary.h
transformation_replace_constant_with_uniform.h
- transformation_replace_id_with_synonym.h
+ transformation_replace_id_with_synonym.h
transformation_split_block.h
uniform_buffer_element_descriptor.h
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
data_descriptor.cpp
fact_manager.cpp
+ force_render_red.cpp
fuzzer.cpp
fuzzer_context.cpp
fuzzer_pass.cpp
diff --git a/3rdparty/spirv-tools/source/fuzz/force_render_red.cpp b/3rdparty/spirv-tools/source/fuzz/force_render_red.cpp
new file mode 100644
index 000000000..3bc117249
--- /dev/null
+++ b/3rdparty/spirv-tools/source/fuzz/force_render_red.cpp
@@ -0,0 +1,368 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/force_render_red.h"
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation_replace_constant_with_uniform.h"
+#include "source/fuzz/uniform_buffer_element_descriptor.h"
+#include "source/opt/build_module.h"
+#include "source/opt/ir_context.h"
+#include "source/opt/types.h"
+#include "source/util/make_unique.h"
+#include "tools/util/cli_consumer.h"
+
+#include
+#include
+
+namespace spvtools {
+namespace fuzz {
+
+namespace {
+
+// Helper method to find the fragment shader entry point, complaining if there
+// is no shader or if there is no fragment entry point.
+opt::Function* FindFragmentShaderEntryPoint(opt::IRContext* ir_context,
+ MessageConsumer message_consumer) {
+ // Check that this is a fragment shader
+ bool found_capability_shader = false;
+ for (auto& capability : ir_context->capabilities()) {
+ assert(capability.opcode() == SpvOpCapability);
+ if (capability.GetSingleWordInOperand(0) == SpvCapabilityShader) {
+ found_capability_shader = true;
+ break;
+ }
+ }
+ if (!found_capability_shader) {
+ message_consumer(
+ SPV_MSG_ERROR, nullptr, {},
+ "Forcing of red rendering requires the Shader capability.");
+ return nullptr;
+ }
+
+ opt::Instruction* fragment_entry_point = nullptr;
+ for (auto& entry_point : ir_context->module()->entry_points()) {
+ if (entry_point.GetSingleWordInOperand(0) == SpvExecutionModelFragment) {
+ fragment_entry_point = &entry_point;
+ break;
+ }
+ }
+ if (fragment_entry_point == nullptr) {
+ message_consumer(SPV_MSG_ERROR, nullptr, {},
+ "Forcing of red rendering requires an entry point with "
+ "the Fragment execution model.");
+ return nullptr;
+ }
+
+ for (auto& function : *ir_context->module()) {
+ if (function.result_id() ==
+ fragment_entry_point->GetSingleWordInOperand(1)) {
+ return &function;
+ }
+ }
+ assert(
+ false &&
+ "A valid module must have a function associate with each entry point.");
+ return nullptr;
+}
+
+// Helper method to check that there is a single vec4 output variable and get a
+// pointer to it.
+opt::Instruction* FindVec4OutputVariable(opt::IRContext* ir_context,
+ MessageConsumer message_consumer) {
+ opt::Instruction* output_variable = nullptr;
+ for (auto& inst : ir_context->types_values()) {
+ if (inst.opcode() == SpvOpVariable &&
+ inst.GetSingleWordInOperand(0) == SpvStorageClassOutput) {
+ if (output_variable != nullptr) {
+ message_consumer(SPV_MSG_ERROR, nullptr, {},
+ "Only one output variable can be handled at present; "
+ "found multiple.");
+ return nullptr;
+ }
+ output_variable = &inst;
+ // Do not break, as we want to check for multiple output variables.
+ }
+ }
+ if (output_variable == nullptr) {
+ message_consumer(SPV_MSG_ERROR, nullptr, {},
+ "No output variable to which to write red was found.");
+ return nullptr;
+ }
+
+ auto output_variable_base_type = ir_context->get_type_mgr()
+ ->GetType(output_variable->type_id())
+ ->AsPointer()
+ ->pointee_type()
+ ->AsVector();
+ if (!output_variable_base_type ||
+ output_variable_base_type->element_count() != 4 ||
+ !output_variable_base_type->element_type()->AsFloat()) {
+ message_consumer(SPV_MSG_ERROR, nullptr, {},
+ "The output variable must have type vec4.");
+ return nullptr;
+ }
+
+ return output_variable;
+}
+
+// Helper to get the ids of float constants 0.0 and 1.0, creating them if
+// necessary.
+std::pair FindOrCreateFloatZeroAndOne(
+ opt::IRContext* ir_context, opt::analysis::Float* float_type) {
+ float one = 1.0;
+ uint32_t one_as_uint;
+ memcpy(&one_as_uint, &one, sizeof(float));
+ std::vector zero_bytes = {0};
+ std::vector one_bytes = {one_as_uint};
+ auto constant_zero = ir_context->get_constant_mgr()->RegisterConstant(
+ MakeUnique(float_type, zero_bytes));
+ auto constant_one = ir_context->get_constant_mgr()->RegisterConstant(
+ MakeUnique(float_type, one_bytes));
+ auto constant_zero_id = ir_context->get_constant_mgr()
+ ->GetDefiningInstruction(constant_zero)
+ ->result_id();
+ auto constant_one_id = ir_context->get_constant_mgr()
+ ->GetDefiningInstruction(constant_one)
+ ->result_id();
+ return std::pair(constant_zero_id, constant_one_id);
+}
+
+std::unique_ptr
+MakeConstantUniformReplacement(opt::IRContext* ir_context,
+ const FactManager& fact_manager,
+ uint32_t constant_id,
+ uint32_t greater_than_instruction,
+ uint32_t in_operand_index) {
+ return MakeUnique(
+ transformation::MakeIdUseDescriptor(constant_id, SpvOpFOrdGreaterThan,
+ in_operand_index,
+ greater_than_instruction, 0),
+ fact_manager.GetUniformDescriptorsForConstant(ir_context, constant_id)[0],
+ ir_context->TakeNextId(), ir_context->TakeNextId());
+}
+
+} // namespace
+
+bool ForceRenderRed(
+ const spv_target_env& target_env, const std::vector& binary_in,
+ const spvtools::fuzz::protobufs::FactSequence& initial_facts,
+ std::vector* binary_out) {
+ auto message_consumer = spvtools::utils::CLIMessageConsumer;
+ spvtools::SpirvTools tools(target_env);
+ if (!tools.IsValid()) {
+ message_consumer(SPV_MSG_ERROR, nullptr, {},
+ "Failed to create SPIRV-Tools interface; stopping.");
+ return false;
+ }
+
+ // Initial binary should be valid.
+ if (!tools.Validate(&binary_in[0], binary_in.size())) {
+ message_consumer(SPV_MSG_ERROR, nullptr, {},
+ "Initial binary is invalid; stopping.");
+ return false;
+ }
+
+ // Build the module from the input binary.
+ std::unique_ptr ir_context = BuildModule(
+ target_env, message_consumer, binary_in.data(), binary_in.size());
+ assert(ir_context);
+
+ // Set up a fact manager with any given initial facts.
+ FactManager fact_manager;
+ for (auto& fact : initial_facts.fact()) {
+ fact_manager.AddFact(fact, ir_context.get());
+ }
+
+ auto entry_point_function =
+ FindFragmentShaderEntryPoint(ir_context.get(), message_consumer);
+ auto output_variable =
+ FindVec4OutputVariable(ir_context.get(), message_consumer);
+ if (entry_point_function == nullptr || output_variable == nullptr) {
+ return false;
+ }
+
+ opt::analysis::Float temp_float_type(32);
+ opt::analysis::Float* float_type = ir_context->get_type_mgr()
+ ->GetRegisteredType(&temp_float_type)
+ ->AsFloat();
+ std::pair zero_one_float_ids =
+ FindOrCreateFloatZeroAndOne(ir_context.get(), float_type);
+
+ // Make the new exit block
+ auto new_exit_block_id = ir_context->TakeNextId();
+ {
+ auto label = MakeUnique(ir_context.get(), SpvOpLabel, 0,
+ new_exit_block_id,
+ opt::Instruction::OperandList());
+ auto new_exit_block = MakeUnique(std::move(label));
+ new_exit_block->AddInstruction(MakeUnique(
+ ir_context.get(), SpvOpReturn, 0, 0, opt::Instruction::OperandList()));
+ entry_point_function->AddBasicBlock(std::move(new_exit_block));
+ }
+
+ // Make the new entry block
+ {
+ auto label = MakeUnique(ir_context.get(), SpvOpLabel, 0,
+ ir_context->TakeNextId(),
+ opt::Instruction::OperandList());
+ auto new_entry_block = MakeUnique(std::move(label));
+
+ // Make an instruction to construct vec4(1.0, 0.0, 0.0, 1.0), representing
+ // the colour red.
+ opt::Operand zero_float = {SPV_OPERAND_TYPE_ID, {zero_one_float_ids.first}};
+ opt::Operand one_float = {SPV_OPERAND_TYPE_ID, {zero_one_float_ids.second}};
+ opt::Instruction::OperandList op_composite_construct_operands = {
+ one_float, zero_float, zero_float, one_float};
+ auto temp_vec4 = opt::analysis::Vector(float_type, 4);
+ auto vec4_id = ir_context->get_type_mgr()->GetId(&temp_vec4);
+ auto red = MakeUnique(
+ ir_context.get(), SpvOpCompositeConstruct, vec4_id,
+ ir_context->TakeNextId(), op_composite_construct_operands);
+ auto red_id = red->result_id();
+ new_entry_block->AddInstruction(std::move(red));
+
+ // Make an instruction to store red into the output color.
+ opt::Operand variable_to_store_into = {SPV_OPERAND_TYPE_ID,
+ {output_variable->result_id()}};
+ opt::Operand value_to_be_stored = {SPV_OPERAND_TYPE_ID, {red_id}};
+ opt::Instruction::OperandList op_store_operands = {variable_to_store_into,
+ value_to_be_stored};
+ new_entry_block->AddInstruction(MakeUnique(
+ ir_context.get(), SpvOpStore, 0, 0, op_store_operands));
+
+ // We are going to attempt to construct 'false' as an expression of the form
+ // 'literal1 > literal2'. If we succeed, we will later replace each literal
+ // with a uniform of the same value - we can only do that replacement once
+ // we have added the entry block to the module.
+ std::unique_ptr
+ first_greater_then_operand_replacement = nullptr;
+ std::unique_ptr
+ second_greater_then_operand_replacement = nullptr;
+ uint32_t id_guaranteed_to_be_false = 0;
+
+ opt::analysis::Bool temp_bool_type;
+ opt::analysis::Bool* registered_bool_type =
+ ir_context->get_type_mgr()
+ ->GetRegisteredType(&temp_bool_type)
+ ->AsBool();
+
+ auto float_type_id = ir_context->get_type_mgr()->GetId(float_type);
+ auto types_for_which_uniforms_are_known =
+ fact_manager.GetTypesForWhichUniformValuesAreKnown();
+
+ // Check whether we have any float uniforms.
+ if (std::find(types_for_which_uniforms_are_known.begin(),
+ types_for_which_uniforms_are_known.end(),
+ float_type_id) != types_for_which_uniforms_are_known.end()) {
+ // We have at least one float uniform; let's see whether we have at least
+ // two.
+ auto available_constants =
+ fact_manager.GetConstantsAvailableFromUniformsForType(
+ ir_context.get(), float_type_id);
+ if (available_constants.size() > 1) {
+ // Grab the float constants associated with the first two known float
+ // uniforms.
+ auto first_constant =
+ ir_context->get_constant_mgr()
+ ->GetConstantFromInst(ir_context->get_def_use_mgr()->GetDef(
+ available_constants[0]))
+ ->AsFloatConstant();
+ auto second_constant =
+ ir_context->get_constant_mgr()
+ ->GetConstantFromInst(ir_context->get_def_use_mgr()->GetDef(
+ available_constants[1]))
+ ->AsFloatConstant();
+
+ // Now work out which of the two constants is larger than the other.
+ uint32_t larger_constant_index = 0;
+ uint32_t smaller_constant_index = 0;
+ if (first_constant->GetFloat() > second_constant->GetFloat()) {
+ larger_constant_index = 0;
+ smaller_constant_index = 1;
+ } else if (first_constant->GetFloat() < second_constant->GetFloat()) {
+ larger_constant_index = 1;
+ smaller_constant_index = 0;
+ }
+
+ // Only proceed with these constants if they have turned out to be
+ // distinct.
+ if (larger_constant_index != smaller_constant_index) {
+ // We are in a position to create 'false' as 'literal1 > literal2', so
+ // reserve an id for this computation; this id will end up being
+ // guaranteed to be 'false'.
+ id_guaranteed_to_be_false = ir_context->TakeNextId();
+
+ auto smaller_constant = available_constants[smaller_constant_index];
+ auto larger_constant = available_constants[larger_constant_index];
+
+ opt::Instruction::OperandList greater_than_operands = {
+ {SPV_OPERAND_TYPE_ID, {smaller_constant}},
+ {SPV_OPERAND_TYPE_ID, {larger_constant}}};
+ new_entry_block->AddInstruction(MakeUnique(
+ ir_context.get(), SpvOpFOrdGreaterThan,
+ ir_context->get_type_mgr()->GetId(registered_bool_type),
+ id_guaranteed_to_be_false, greater_than_operands));
+
+ first_greater_then_operand_replacement =
+ MakeConstantUniformReplacement(ir_context.get(), fact_manager,
+ smaller_constant,
+ id_guaranteed_to_be_false, 0);
+ second_greater_then_operand_replacement =
+ MakeConstantUniformReplacement(ir_context.get(), fact_manager,
+ larger_constant,
+ id_guaranteed_to_be_false, 1);
+ }
+ }
+ }
+
+ if (id_guaranteed_to_be_false == 0) {
+ auto constant_false = ir_context->get_constant_mgr()->RegisterConstant(
+ MakeUnique(registered_bool_type, false));
+ id_guaranteed_to_be_false = ir_context->get_constant_mgr()
+ ->GetDefiningInstruction(constant_false)
+ ->result_id();
+ }
+
+ opt::Operand false_condition = {SPV_OPERAND_TYPE_ID,
+ {id_guaranteed_to_be_false}};
+ opt::Operand then_block = {SPV_OPERAND_TYPE_ID,
+ {entry_point_function->entry()->id()}};
+ opt::Operand else_block = {SPV_OPERAND_TYPE_ID, {new_exit_block_id}};
+ opt::Instruction::OperandList op_branch_conditional_operands = {
+ false_condition, then_block, else_block};
+ new_entry_block->AddInstruction(
+ MakeUnique(ir_context.get(), SpvOpBranchConditional,
+ 0, 0, op_branch_conditional_operands));
+
+ entry_point_function->InsertBasicBlockBefore(
+ std::move(new_entry_block), entry_point_function->entry().get());
+
+ for (auto& replacement : {first_greater_then_operand_replacement.get(),
+ second_greater_then_operand_replacement.get()}) {
+ if (replacement) {
+ assert(replacement->IsApplicable(ir_context.get(), fact_manager));
+ replacement->Apply(ir_context.get(), &fact_manager);
+ }
+ }
+ }
+
+ // Write out the module as a binary.
+ ir_context->module()->ToBinary(binary_out, false);
+ return true;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/3rdparty/spirv-tools/source/fuzz/force_render_red.h b/3rdparty/spirv-tools/source/fuzz/force_render_red.h
new file mode 100644
index 000000000..2484d2788
--- /dev/null
+++ b/3rdparty/spirv-tools/source/fuzz/force_render_red.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FORCE_RENDER_RED_H_
+#define SOURCE_FORCE_RENDER_RED_H_
+
+#include
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "spirv-tools/libspirv.hpp"
+
+namespace spvtools {
+namespace fuzz {
+
+// Requires |binary_in| to be a valid SPIR-V module with Shader capability,
+// containing an entry point with the Fragment execution model, and a single
+// output variable of type vec4.
+//
+// Turns the body of this entry point into effectively:
+//
+// output_variable = vec4(1.0, 0.0, 0.0, 1.0);
+// if (false) {
+// original_body
+// }
+//
+// If suitable facts about values of uniforms are available, the 'false' will
+// instead become: 'u > v', where 'u' and 'v' are pieces of uniform data for
+// which it is known that 'u < v' holds.
+bool ForceRenderRed(
+ const spv_target_env& target_env, const std::vector& binary_in,
+ const spvtools::fuzz::protobufs::FactSequence& initial_facts,
+ std::vector* binary_out);
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FORCE_RENDER_RED_H_
diff --git a/3rdparty/spirv-tools/source/opt/cfg.h b/3rdparty/spirv-tools/source/opt/cfg.h
index 4c7e936ca..f28068229 100644
--- a/3rdparty/spirv-tools/source/opt/cfg.h
+++ b/3rdparty/spirv-tools/source/opt/cfg.h
@@ -60,8 +60,9 @@ class CFG {
// Compute structured block order into |order| for |func| starting at |root|.
// This order has the property that dominators come before all blocks they
- // dominate and merge blocks come after all blocks that are in the control
- // constructs of their header.
+ // dominate, merge blocks come after all blocks that are in the control
+ // constructs of their header, and continue blocks come after all of the
+ // blocks in the body of their loop.
void ComputeStructuredOrder(Function* func, BasicBlock* root,
std::list* order);
diff --git a/3rdparty/spirv-tools/source/opt/inline_pass.cpp b/3rdparty/spirv-tools/source/opt/inline_pass.cpp
index 98daaf43c..36991ec56 100644
--- a/3rdparty/spirv-tools/source/opt/inline_pass.cpp
+++ b/3rdparty/spirv-tools/source/opt/inline_pass.cpp
@@ -720,18 +720,24 @@ bool InlinePass::IsInlinableFunction(Function* func) {
return false;
}
- // Do not inline functions with an OpKill because they may be inlined into a
- // continue construct.
- bool has_opkill = !func->WhileEachInst(
- [](Instruction* inst) { return inst->opcode() != SpvOpKill; });
+ // Do not inline functions with an OpKill if they are called from a continue
+ // construct. If it is inlined into a continue construct it will generate
+ // invalid code.
+ bool func_is_called_from_continue =
+ funcs_called_from_continue_.count(func->result_id()) != 0;
- if (has_opkill) {
+ if (func_is_called_from_continue && ContainsKill(func)) {
return false;
}
return true;
}
+bool InlinePass::ContainsKill(Function* func) const {
+ return !func->WhileEachInst(
+ [](Instruction* inst) { return inst->opcode() != SpvOpKill; });
+}
+
void InlinePass::InitializeInline() {
false_id_ = 0;
@@ -741,6 +747,8 @@ void InlinePass::InitializeInline() {
inlinable_.clear();
no_return_in_loop_.clear();
early_return_funcs_.clear();
+ funcs_called_from_continue_ =
+ context()->GetStructuredCFGAnalysis()->FindFuncsCalledFromContinue();
for (auto& fn : *get_module()) {
// Initialize function and block maps.
diff --git a/3rdparty/spirv-tools/source/opt/inline_pass.h b/3rdparty/spirv-tools/source/opt/inline_pass.h
index ecfe964f1..bc5f78127 100644
--- a/3rdparty/spirv-tools/source/opt/inline_pass.h
+++ b/3rdparty/spirv-tools/source/opt/inline_pass.h
@@ -139,6 +139,9 @@ class InlinePass : public Pass {
// Return true if |func| is a function that can be inlined.
bool IsInlinableFunction(Function* func);
+ // Returns true if |func| contains an OpKill instruction.
+ bool ContainsKill(Function* func) const;
+
// Update phis in succeeding blocks to point to new last block
void UpdateSucceedingPhis(
std::vector>& new_blocks);
@@ -164,6 +167,10 @@ class InlinePass : public Pass {
// result id for OpConstantFalse
uint32_t false_id_;
+
+ // Set of functions that are originally called directly or indirectly from a
+ // continue construct.
+ std::unordered_set funcs_called_from_continue_;
};
} // namespace opt
diff --git a/3rdparty/spirv-tools/source/opt/inst_buff_addr_check_pass.cpp b/3rdparty/spirv-tools/source/opt/inst_buff_addr_check_pass.cpp
index ef29ce552..fa6c2c6a0 100644
--- a/3rdparty/spirv-tools/source/opt/inst_buff_addr_check_pass.cpp
+++ b/3rdparty/spirv-tools/source/opt/inst_buff_addr_check_pass.cpp
@@ -96,8 +96,22 @@ void InstBuffAddrCheckPass::GenCheckCode(
uid2offset_[ref_inst->unique_id()], stage_idx,
{error_id, lo_uptr_inst->result_id(), hi_uptr_inst->result_id()},
&builder);
- // Gen zero for invalid reference
- uint32_t ref_type_id = ref_inst->type_id();
+ // Gen zero for invalid load. If pointer type, need to convert uint64
+ // zero to pointer; cannot create ConstantNull of pointer type.
+ uint32_t null_id = 0;
+ if (new_ref_id != 0) {
+ uint32_t ref_type_id = ref_inst->type_id();
+ analysis::TypeManager* type_mgr = context()->get_type_mgr();
+ analysis::Type* ref_type = type_mgr->GetType(ref_type_id);
+ if (ref_type->AsPointer() != nullptr) {
+ uint32_t null_u64_id = GetNullId(GetUint64Id());
+ Instruction* null_ptr_inst =
+ builder.AddUnaryOp(ref_type_id, SpvOpConvertUToPtr, null_u64_id);
+ null_id = null_ptr_inst->result_id();
+ } else {
+ null_id = GetNullId(ref_type_id);
+ }
+ }
(void)builder.AddBranch(merge_blk_id);
new_blocks->push_back(std::move(new_blk_ptr));
// Gen merge block
@@ -107,9 +121,9 @@ void InstBuffAddrCheckPass::GenCheckCode(
// result id of the original reference with that of the Phi. Kill original
// reference.
if (new_ref_id != 0) {
- Instruction* phi_inst = builder.AddPhi(
- ref_type_id,
- {new_ref_id, valid_blk_id, GetNullId(ref_type_id), invalid_blk_id});
+ Instruction* phi_inst =
+ builder.AddPhi(ref_inst->type_id(),
+ {new_ref_id, valid_blk_id, null_id, invalid_blk_id});
context()->ReplaceAllUsesWith(ref_inst->result_id(), phi_inst->result_id());
}
new_blocks->push_back(std::move(new_blk_ptr));
diff --git a/3rdparty/spirv-tools/source/opt/ir_context.h b/3rdparty/spirv-tools/source/opt/ir_context.h
index 3bbf18091..45bf1290e 100644
--- a/3rdparty/spirv-tools/source/opt/ir_context.h
+++ b/3rdparty/spirv-tools/source/opt/ir_context.h
@@ -187,9 +187,6 @@ class IRContext {
inline IteratorRange debugs3();
inline IteratorRange debugs3() const;
- // Clears all debug instructions (excluding OpLine & OpNoLine).
- inline void debug_clear();
-
// Add |capability| to the module, if it is not already enabled.
inline void AddCapability(SpvCapability capability);
@@ -543,7 +540,7 @@ class IRContext {
return GetFunction(inst->result_id());
}
- // Add to |todo| all ids of functions called in |func|.
+ // Add to |todo| all ids of functions called directly from |func|.
void AddCalls(const Function* func, std::queue* todo);
// Applies |pfn| to every function in the call trees that are rooted at the
@@ -928,8 +925,6 @@ IteratorRange IRContext::debugs3() const {
return ((const Module*)module_.get())->debugs3();
}
-void IRContext::debug_clear() { module_->debug_clear(); }
-
void IRContext::AddCapability(SpvCapability capability) {
if (!get_feature_mgr()->HasCapability(capability)) {
std::unique_ptr capability_inst(new Instruction(
diff --git a/3rdparty/spirv-tools/source/opt/ir_loader.cpp b/3rdparty/spirv-tools/source/opt/ir_loader.cpp
index edd4832d9..c68b3e26b 100644
--- a/3rdparty/spirv-tools/source/opt/ir_loader.cpp
+++ b/3rdparty/spirv-tools/source/opt/ir_loader.cpp
@@ -159,6 +159,9 @@ void IrLoader::EndModule() {
for (auto& function : *module_) {
for (auto& bb : function) bb.SetParent(&function);
}
+
+ // Copy any trailing Op*Line instruction into the module
+ module_->SetTrailingDbgLineInfo(std::move(dbg_line_info_));
}
} // namespace opt
diff --git a/3rdparty/spirv-tools/source/opt/module.cpp b/3rdparty/spirv-tools/source/opt/module.cpp
index 04e4e9733..c7fc2473d 100644
--- a/3rdparty/spirv-tools/source/opt/module.cpp
+++ b/3rdparty/spirv-tools/source/opt/module.cpp
@@ -121,6 +121,9 @@ void Module::ForEachInst(const std::function& f,
static_cast(i.get())->ForEachInst(f,
run_on_debug_line_insts);
}
+ if (run_on_debug_line_insts) {
+ for (auto& i : trailing_dbg_line_info_) DELEGATE(i);
+ }
#undef DELEGATE
}
diff --git a/3rdparty/spirv-tools/source/opt/module.h b/3rdparty/spirv-tools/source/opt/module.h
index cf7c274de..aefa2a548 100644
--- a/3rdparty/spirv-tools/source/opt/module.h
+++ b/3rdparty/spirv-tools/source/opt/module.h
@@ -192,22 +192,6 @@ class Module {
inline IteratorRange execution_modes();
inline IteratorRange execution_modes() const;
- // Clears all debug instructions (excluding OpLine & OpNoLine).
- void debug_clear() {
- debug1_clear();
- debug2_clear();
- debug3_clear();
- }
-
- // Clears all debug 1 instructions (excluding OpLine & OpNoLine).
- void debug1_clear() { debugs1_.clear(); }
-
- // Clears all debug 2 instructions (excluding OpLine & OpNoLine).
- void debug2_clear() { debugs2_.clear(); }
-
- // Clears all debug 3 instructions (excluding OpLine & OpNoLine).
- void debug3_clear() { debugs3_.clear(); }
-
// Iterators for annotation instructions contained in this module.
inline inst_iterator annotation_begin();
inline inst_iterator annotation_end();
@@ -261,6 +245,19 @@ class Module {
// Gets the associated context for this module
IRContext* context() const { return context_; }
+ // Sets the trailing debug line info to |dbg_line_info|.
+ void SetTrailingDbgLineInfo(std::vector&& dbg_line_info) {
+ trailing_dbg_line_info_ = std::move(dbg_line_info);
+ }
+
+ std::vector& trailing_dbg_line_info() {
+ return trailing_dbg_line_info_;
+ }
+
+ const std::vector& trailing_dbg_line_info() const {
+ return trailing_dbg_line_info_;
+ }
+
private:
ModuleHeader header_; // Module header
@@ -281,6 +278,10 @@ class Module {
// Type declarations, constants, and global variable declarations.
InstructionList types_values_;
std::vector> functions_;
+
+ // If the module ends with Op*Line instruction, they will not be attached to
+ // any instruction. We record them here, so they will not be lost.
+ std::vector trailing_dbg_line_info_;
};
// Pretty-prints |module| to |str|. Returns |str|.
diff --git a/3rdparty/spirv-tools/source/opt/strip_debug_info_pass.cpp b/3rdparty/spirv-tools/source/opt/strip_debug_info_pass.cpp
index 5d9c5fec8..9e7fad0b2 100644
--- a/3rdparty/spirv-tools/source/opt/strip_debug_info_pass.cpp
+++ b/3rdparty/spirv-tools/source/opt/strip_debug_info_pass.cpp
@@ -22,13 +22,34 @@ Pass::Status StripDebugInfoPass::Process() {
bool modified = !context()->debugs1().empty() ||
!context()->debugs2().empty() ||
!context()->debugs3().empty();
- context()->debug_clear();
+
+ std::vector to_kill;
+ for (auto& dbg : context()->debugs1()) to_kill.push_back(&dbg);
+ for (auto& dbg : context()->debugs2()) to_kill.push_back(&dbg);
+ for (auto& dbg : context()->debugs3()) to_kill.push_back(&dbg);
+
+ // OpName must come first, since they may refer to other debug instructions.
+ // If they are after the instructions that refer to, then they will be killed
+ // when that instruction is killed, which will lead to a double kill.
+ std::sort(to_kill.begin(), to_kill.end(),
+ [](Instruction* lhs, Instruction* rhs) -> bool {
+ if (lhs->opcode() == SpvOpName && rhs->opcode() != SpvOpName)
+ return true;
+ return false;
+ });
+
+ for (auto* inst : to_kill) context()->KillInst(inst);
context()->module()->ForEachInst([&modified](Instruction* inst) {
modified |= !inst->dbg_line_insts().empty();
inst->dbg_line_insts().clear();
});
+ if (!get_module()->trailing_dbg_line_info().empty()) {
+ modified = true;
+ get_module()->trailing_dbg_line_info().clear();
+ }
+
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
diff --git a/3rdparty/spirv-tools/source/opt/struct_cfg_analysis.cpp b/3rdparty/spirv-tools/source/opt/struct_cfg_analysis.cpp
index 152ded508..b16322c1f 100644
--- a/3rdparty/spirv-tools/source/opt/struct_cfg_analysis.cpp
+++ b/3rdparty/spirv-tools/source/opt/struct_cfg_analysis.cpp
@@ -45,6 +45,7 @@ void StructuredCFGAnalysis::AddBlocksInFunction(Function* func) {
struct TraversalInfo {
ConstructInfo cinfo;
uint32_t merge_node;
+ uint32_t continue_node;
};
// Set up a stack to keep track of currently active constructs.
@@ -53,7 +54,9 @@ void StructuredCFGAnalysis::AddBlocksInFunction(Function* func) {
state[0].cinfo.containing_construct = 0;
state[0].cinfo.containing_loop = 0;
state[0].cinfo.containing_switch = 0;
+ state[0].cinfo.in_continue = false;
state[0].merge_node = 0;
+ state[0].continue_node = 0;
for (BasicBlock* block : order) {
if (context_->cfg()->IsPseudoEntryBlock(block) ||
@@ -65,6 +68,12 @@ void StructuredCFGAnalysis::AddBlocksInFunction(Function* func) {
state.pop_back();
}
+ // This works because the structured order is designed to keep the blocks in
+ // the continue construct between the continue header and the merge node.
+ if (block->id() == state.back().continue_node) {
+ state.back().cinfo.in_continue = true;
+ }
+
bb_to_construct_.emplace(std::make_pair(block->id(), state.back().cinfo));
if (Instruction* merge_inst = block->GetMergeInst()) {
@@ -76,8 +85,14 @@ void StructuredCFGAnalysis::AddBlocksInFunction(Function* func) {
if (merge_inst->opcode() == SpvOpLoopMerge) {
new_state.cinfo.containing_loop = block->id();
new_state.cinfo.containing_switch = 0;
+ new_state.cinfo.in_continue = false;
+ new_state.continue_node =
+ merge_inst->GetSingleWordInOperand(kContinueNodeIndex);
} else {
new_state.cinfo.containing_loop = state.back().cinfo.containing_loop;
+ new_state.cinfo.in_continue = state.back().cinfo.in_continue;
+ new_state.continue_node = state.back().continue_node;
+
if (merge_inst->NextNode()->opcode() == SpvOpSwitch) {
new_state.cinfo.containing_switch = block->id();
} else {
@@ -146,9 +161,59 @@ bool StructuredCFGAnalysis::IsContinueBlock(uint32_t bb_id) {
return LoopContinueBlock(bb_id) == bb_id;
}
+bool StructuredCFGAnalysis::IsInContainingLoopsContinueConstruct(
+ uint32_t bb_id) {
+ auto it = bb_to_construct_.find(bb_id);
+ if (it == bb_to_construct_.end()) {
+ return false;
+ }
+ return it->second.in_continue;
+}
+
+bool StructuredCFGAnalysis::IsInContinueConstruct(uint32_t bb_id) {
+ while (bb_id != 0) {
+ if (IsInContainingLoopsContinueConstruct(bb_id)) {
+ return true;
+ }
+ bb_id = ContainingLoop(bb_id);
+ }
+ return false;
+}
+
bool StructuredCFGAnalysis::IsMergeBlock(uint32_t bb_id) {
return merge_blocks_.Get(bb_id);
}
+std::unordered_set
+StructuredCFGAnalysis::FindFuncsCalledFromContinue() {
+ std::unordered_set called_from_continue;
+ std::queue funcs_to_process;
+
+ // First collect the functions that are called directly from a continue
+ // construct.
+ for (Function& func : *context_->module()) {
+ for (auto& bb : func) {
+ if (IsInContainingLoopsContinueConstruct(bb.id())) {
+ for (const Instruction& inst : bb) {
+ if (inst.opcode() == SpvOpFunctionCall) {
+ funcs_to_process.push(inst.GetSingleWordInOperand(0));
+ }
+ }
+ }
+ }
+ }
+
+ // Now collect all of the functions that are indirectly called as well.
+ while (!funcs_to_process.empty()) {
+ uint32_t func_id = funcs_to_process.front();
+ funcs_to_process.pop();
+ Function* func = context_->GetFunction(func_id);
+ if (called_from_continue.insert(func_id).second) {
+ context_->AddCalls(func, &funcs_to_process);
+ }
+ }
+ return called_from_continue;
+}
+
} // namespace opt
} // namespace spvtools
diff --git a/3rdparty/spirv-tools/source/opt/struct_cfg_analysis.h b/3rdparty/spirv-tools/source/opt/struct_cfg_analysis.h
index f25266a8f..dfae6d4f2 100644
--- a/3rdparty/spirv-tools/source/opt/struct_cfg_analysis.h
+++ b/3rdparty/spirv-tools/source/opt/struct_cfg_analysis.h
@@ -16,6 +16,7 @@
#define SOURCE_OPT_STRUCT_CFG_ANALYSIS_H_
#include
+#include
#include "source/opt/function.h"
#include "source/util/bit_vector.h"
@@ -88,21 +89,46 @@ class StructuredCFGAnalysis {
// if no such block exists.
uint32_t SwitchMergeBlock(uint32_t bb_id);
+ // Returns true if |bb_id| is the continue block for a loop.
bool IsContinueBlock(uint32_t bb_id);
+
+ // Returns true if |bb_id| is in the continue construct for its inner most
+ // containing loop.
+ bool IsInContainingLoopsContinueConstruct(uint32_t bb_id);
+
+ // Returns true if |bb_id| is in the continue construct for any loop in its
+ // function.
+ bool IsInContinueConstruct(uint32_t bb_id);
+
+ // Return true if |bb_id| is the merge block for a construct.
bool IsMergeBlock(uint32_t bb_id);
+ // Returns the set of function ids that are called directly or indirectly from
+ // a continue construct.
+ std::unordered_set FindFuncsCalledFromContinue();
+
private:
// Struct used to hold the information for a basic block.
// |containing_construct| is the header for the innermost containing
// construct, or 0 if no such construct exists. It could be a selection
- // construct or a loop construct. |containing_loop| is the innermost
- // containing loop construct, or 0 if the basic bloc is not in a loop. If the
- // basic block is in a selection construct that is contained in a loop
- // construct, then these two values will not be the same.
+ // construct or a loop construct.
+ //
+ // |containing_loop| is the innermost containing loop construct, or 0 if the
+ // basic bloc is not in a loop. If the basic block is in a selection
+ // construct that is contained in a loop construct, then these two values will
+ // not be the same.
+ //
+ // |containing_switch| is the innermost contain selection construct with an
+ // |OpSwitch| for the branch, as long as there is not intervening loop. This
+ // is used to identify the selection construct from which it can break.
+ //
+ // |in_continue| is true of the block is in the continue construct for its
+ // innermost containing loop.
struct ConstructInfo {
uint32_t containing_construct;
uint32_t containing_loop;
uint32_t containing_switch;
+ bool in_continue;
};
// Populates |bb_to_construct_| with the innermost containing merge and loop
diff --git a/3rdparty/spirv-tools/source/opt/wrap_opkill.cpp b/3rdparty/spirv-tools/source/opt/wrap_opkill.cpp
index 76714d630..ffd7a1050 100644
--- a/3rdparty/spirv-tools/source/opt/wrap_opkill.cpp
+++ b/3rdparty/spirv-tools/source/opt/wrap_opkill.cpp
@@ -22,8 +22,11 @@ namespace opt {
Pass::Status WrapOpKill::Process() {
bool modified = false;
- for (auto& func : *get_module()) {
- bool successful = func.WhileEachInst([this, &modified](Instruction* inst) {
+ auto func_to_process =
+ context()->GetStructuredCFGAnalysis()->FindFuncsCalledFromContinue();
+ for (uint32_t func_id : func_to_process) {
+ Function* func = context()->GetFunction(func_id);
+ bool successful = func->WhileEachInst([this, &modified](Instruction* inst) {
if (inst->opcode() == SpvOpKill) {
modified = true;
if (!ReplaceWithFunctionCall(inst)) {
diff --git a/3rdparty/spirv-tools/source/val/validate_constants.cpp b/3rdparty/spirv-tools/source/val/validate_constants.cpp
index 95ec1680d..dea95c8a2 100644
--- a/3rdparty/spirv-tools/source/val/validate_constants.cpp
+++ b/3rdparty/spirv-tools/source/val/validate_constants.cpp
@@ -304,7 +304,6 @@ bool IsTypeNullable(const std::vector& instruction,
case SpvOpTypeBool:
case SpvOpTypeInt:
case SpvOpTypeFloat:
- case SpvOpTypePointer:
case SpvOpTypeEvent:
case SpvOpTypeDeviceEvent:
case SpvOpTypeReserveId:
@@ -325,6 +324,11 @@ bool IsTypeNullable(const std::vector& instruction,
}
return true;
}
+ case SpvOpTypePointer:
+ if (instruction[2] == SpvStorageClassPhysicalStorageBuffer) {
+ return false;
+ }
+ return true;
default:
return false;
}
diff --git a/3rdparty/spirv-tools/source/val/validate_instruction.cpp b/3rdparty/spirv-tools/source/val/validate_instruction.cpp
index fecc35199..6478b3cb6 100644
--- a/3rdparty/spirv-tools/source/val/validate_instruction.cpp
+++ b/3rdparty/spirv-tools/source/val/validate_instruction.cpp
@@ -55,18 +55,6 @@ std::string ToString(const CapabilitySet& capabilities,
return ss.str();
}
-bool IsValidWebGPUStorageClass(SpvStorageClass storage_class) {
- return storage_class == SpvStorageClassUniformConstant ||
- storage_class == SpvStorageClassUniform ||
- storage_class == SpvStorageClassStorageBuffer ||
- storage_class == SpvStorageClassInput ||
- storage_class == SpvStorageClassOutput ||
- storage_class == SpvStorageClassImage ||
- storage_class == SpvStorageClassWorkgroup ||
- storage_class == SpvStorageClassPrivate ||
- storage_class == SpvStorageClassFunction;
-}
-
// Returns capabilities that enable an opcode. An empty result is interpreted
// as no prohibition of use of the opcode. If the result is non-empty, then
// the opcode may only be used if at least one of the capabilities is specified
@@ -249,23 +237,6 @@ spv_result_t ReservedCheck(ValidationState_t& _, const Instruction* inst) {
return SPV_SUCCESS;
}
-// Returns SPV_ERROR_INVALID_BINARY and emits a diagnostic if the instruction
-// is invalid because of an execution environment constraint.
-spv_result_t EnvironmentCheck(ValidationState_t& _, const Instruction* inst) {
- const SpvOp opcode = inst->opcode();
- switch (opcode) {
- case SpvOpUndef:
- if (_.features().bans_op_undef) {
- return _.diag(SPV_ERROR_INVALID_BINARY, inst)
- << "OpUndef is disallowed";
- }
- break;
- default:
- break;
- }
- return SPV_SUCCESS;
-}
-
// Returns SPV_ERROR_INVALID_CAPABILITY and emits a diagnostic if the
// instruction is invalid because the required capability isn't declared
// in the module.
@@ -499,38 +470,6 @@ spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst) {
}
_.set_addressing_model(inst->GetOperandAs(0));
_.set_memory_model(inst->GetOperandAs(1));
-
- if (_.memory_model() != SpvMemoryModelVulkanKHR &&
- _.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "VulkanMemoryModelKHR capability must only be specified if "
- "the "
- "VulkanKHR memory model is used.";
- }
-
- if (spvIsWebGPUEnv(_.context()->target_env)) {
- if (_.addressing_model() != SpvAddressingModelLogical) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Addressing model must be Logical for WebGPU environment.";
- }
- if (_.memory_model() != SpvMemoryModelVulkanKHR) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Memory model must be VulkanKHR for WebGPU environment.";
- }
- }
-
- if (spvIsOpenCLEnv(_.context()->target_env)) {
- if ((_.addressing_model() != SpvAddressingModelPhysical32) &&
- (_.addressing_model() != SpvAddressingModelPhysical64)) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Addressing model must be Physical32 or Physical64 "
- << "in the OpenCL environment.";
- }
- if (_.memory_model() != SpvMemoryModelOpenCL) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Memory model must be OpenCL in the OpenCL environment.";
- }
- }
} else if (opcode == SpvOpExecutionMode) {
const uint32_t entry_point = inst->word(1);
_.RegisterExecutionModeForEntryPoint(entry_point,
@@ -540,61 +479,9 @@ spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst) {
if (auto error = LimitCheckNumVars(_, inst->id(), storage_class)) {
return error;
}
-
- if (spvIsWebGPUEnv(_.context()->target_env) &&
- !IsValidWebGPUStorageClass(storage_class)) {
- return _.diag(SPV_ERROR_INVALID_BINARY, inst)
- << "For WebGPU, OpVariable storage class must be one of "
- "UniformConstant, Uniform, StorageBuffer, Input, Output, "
- "Image, Workgroup, Private, Function for WebGPU";
- }
-
- if (storage_class == SpvStorageClassGeneric)
- return _.diag(SPV_ERROR_INVALID_BINARY, inst)
- << "OpVariable storage class cannot be Generic";
- if (_.current_layout_section() == kLayoutFunctionDefinitions) {
- if (storage_class != SpvStorageClassFunction) {
- return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
- << "Variables must have a function[7] storage class inside"
- " of a function";
- }
- if (_.current_function().IsFirstBlock(
- _.current_function().current_block()->id()) == false) {
- return _.diag(SPV_ERROR_INVALID_CFG, inst)
- << "Variables can only be defined "
- "in the first block of a "
- "function";
- }
- } else {
- if (storage_class == SpvStorageClassFunction) {
- return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
- << "Variables can not have a function[7] storage class "
- "outside of a function";
- }
- }
- } else if (opcode == SpvOpTypePointer) {
- const auto storage_class = inst->GetOperandAs(1);
- if (spvIsWebGPUEnv(_.context()->target_env) &&
- !IsValidWebGPUStorageClass(storage_class)) {
- return _.diag(SPV_ERROR_INVALID_BINARY, inst)
- << "For WebGPU, OpTypePointer storage class must be one of "
- "UniformConstant, Uniform, StorageBuffer, Input, Output, "
- "Image, Workgroup, Private, Function";
- }
- }
-
- // SPIR-V Spec 2.16.3: Validation Rules for Kernel Capabilities: The
- // Signedness in OpTypeInt must always be 0.
- if (SpvOpTypeInt == inst->opcode() && _.HasCapability(SpvCapabilityKernel) &&
- inst->GetOperandAs(2) != 0u) {
- return _.diag(SPV_ERROR_INVALID_BINARY, inst)
- << "The Signedness in OpTypeInt "
- "must always be 0 when Kernel "
- "capability is used.";
}
if (auto error = ReservedCheck(_, inst)) return error;
- if (auto error = EnvironmentCheck(_, inst)) return error;
if (auto error = CapabilityCheck(_, inst)) return error;
if (auto error = LimitCheckIdBound(_, inst)) return error;
if (auto error = LimitCheckStruct(_, inst)) return error;
diff --git a/3rdparty/spirv-tools/source/val/validate_memory.cpp b/3rdparty/spirv-tools/source/val/validate_memory.cpp
index 8e220971c..bff8b20ee 100644
--- a/3rdparty/spirv-tools/source/val/validate_memory.cpp
+++ b/3rdparty/spirv-tools/source/val/validate_memory.cpp
@@ -461,6 +461,28 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
}
}
+ if (!_.IsValidStorageClass(storage_class)) {
+ return _.diag(SPV_ERROR_INVALID_BINARY, inst)
+ << "Invalid storage class for target environment";
+ }
+
+ if (storage_class == SpvStorageClassGeneric) {
+ return _.diag(SPV_ERROR_INVALID_BINARY, inst)
+ << "OpVariable storage class cannot be Generic";
+ }
+
+ if (inst->function() && storage_class != SpvStorageClassFunction) {
+ return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+ << "Variables must have a function[7] storage class inside"
+ " of a function";
+ }
+
+ if (!inst->function() && storage_class == SpvStorageClassFunction) {
+ return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+ << "Variables can not have a function[7] storage class "
+ "outside of a function";
+ }
+
// SPIR-V 3.32.8: Check that pointer type and variable type have the same
// storage class.
const auto result_storage_class_index = 1;
@@ -1568,8 +1590,8 @@ spv_result_t ValidatePtrComparison(ValidationState_t& _,
<< "Operand type must be a pointer";
}
+ SpvStorageClass sc = op1_type->GetOperandAs(1u);
if (_.addressing_model() == SpvAddressingModelLogical) {
- SpvStorageClass sc = op1_type->GetOperandAs(1u);
if (sc != SpvStorageClassWorkgroup && sc != SpvStorageClassStorageBuffer) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Invalid pointer storage class";
@@ -1580,6 +1602,9 @@ spv_result_t ValidatePtrComparison(ValidationState_t& _,
<< "Workgroup storage class pointer requires VariablePointers "
"capability to be specified";
}
+ } else if (sc == SpvStorageClassPhysicalStorageBuffer) {
+ return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << "Cannot use a pointer in the PhysicalStorageBuffer storage class";
}
return SPV_SUCCESS;
diff --git a/3rdparty/spirv-tools/source/val/validate_misc.cpp b/3rdparty/spirv-tools/source/val/validate_misc.cpp
index 28e3fc684..7542f32c5 100644
--- a/3rdparty/spirv-tools/source/val/validate_misc.cpp
+++ b/3rdparty/spirv-tools/source/val/validate_misc.cpp
@@ -18,6 +18,7 @@
#include "source/opcode.h"
#include "source/spirv_target_env.h"
#include "source/val/instruction.h"
+#include "source/val/validate_scopes.h"
#include "source/val/validation_state.h"
namespace spvtools {
@@ -32,6 +33,34 @@ spv_result_t ValidateUndef(ValidationState_t& _, const Instruction* inst) {
<< "Cannot create undefined values with 8- or 16-bit types";
}
+ if (spvIsWebGPUEnv(_.context()->target_env)) {
+ return _.diag(SPV_ERROR_INVALID_BINARY, inst) << "OpUndef is disallowed";
+ }
+
+ return SPV_SUCCESS;
+}
+
+spv_result_t ValidateShaderClock(ValidationState_t& _,
+ const Instruction* inst) {
+ const uint32_t execution_scope = inst->word(3);
+ if (auto error = ValidateExecutionScope(_, inst, execution_scope)) {
+ return error;
+ }
+
+ // Result Type must be a 64 - bit unsigned integer type or
+ // a vector of two - components of 32 -
+ // bit unsigned integer type
+ const uint32_t result_type = inst->type_id();
+ if (!(_.IsUnsignedIntScalarType(result_type) &&
+ _.GetBitWidth(result_type) == 64) &&
+ !(_.IsUnsignedIntVectorType(result_type) &&
+ _.GetDimension(result_type) == 2 && _.GetBitWidth(result_type) == 32)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Value to be a "
+ "vector of two components"
+ " of unsigned integer"
+ " or 64bit unsigned integer";
+ }
+
return SPV_SUCCESS;
}
@@ -110,6 +139,11 @@ spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst) {
<< spvOpcodeString(inst->opcode());
break;
}
+ case SpvOpReadClockKHR:
+ if (auto error = ValidateShaderClock(_, inst)) {
+ return error;
+ }
+ break;
default:
break;
}
diff --git a/3rdparty/spirv-tools/source/val/validate_mode_setting.cpp b/3rdparty/spirv-tools/source/val/validate_mode_setting.cpp
index cbcf11a3f..e020f5ae6 100644
--- a/3rdparty/spirv-tools/source/val/validate_mode_setting.cpp
+++ b/3rdparty/spirv-tools/source/val/validate_mode_setting.cpp
@@ -485,6 +485,44 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _,
return SPV_SUCCESS;
}
+spv_result_t ValidateMemoryModel(ValidationState_t& _,
+ const Instruction* inst) {
+ // Already produced an error if multiple memory model instructions are
+ // present.
+ if (_.memory_model() != SpvMemoryModelVulkanKHR &&
+ _.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "VulkanMemoryModelKHR capability must only be specified if "
+ "the VulkanKHR memory model is used.";
+ }
+
+ if (spvIsWebGPUEnv(_.context()->target_env)) {
+ if (_.addressing_model() != SpvAddressingModelLogical) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Addressing model must be Logical for WebGPU environment.";
+ }
+ if (_.memory_model() != SpvMemoryModelVulkanKHR) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Memory model must be VulkanKHR for WebGPU environment.";
+ }
+ }
+
+ if (spvIsOpenCLEnv(_.context()->target_env)) {
+ if ((_.addressing_model() != SpvAddressingModelPhysical32) &&
+ (_.addressing_model() != SpvAddressingModelPhysical64)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Addressing model must be Physical32 or Physical64 "
+ << "in the OpenCL environment.";
+ }
+ if (_.memory_model() != SpvMemoryModelOpenCL) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Memory model must be OpenCL in the OpenCL environment.";
+ }
+ }
+
+ return SPV_SUCCESS;
+}
+
} // namespace
spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) {
@@ -496,6 +534,9 @@ spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) {
case SpvOpExecutionModeId:
if (auto error = ValidateExecutionMode(_, inst)) return error;
break;
+ case SpvOpMemoryModel:
+ if (auto error = ValidateMemoryModel(_, inst)) return error;
+ break;
default:
break;
}
diff --git a/3rdparty/spirv-tools/source/val/validate_type.cpp b/3rdparty/spirv-tools/source/val/validate_type.cpp
index d3872da78..4d673b4a7 100644
--- a/3rdparty/spirv-tools/source/val/validate_type.cpp
+++ b/3rdparty/spirv-tools/source/val/validate_type.cpp
@@ -106,6 +106,17 @@ spv_result_t ValidateTypeInt(ValidationState_t& _, const Instruction* inst) {
return _.diag(SPV_ERROR_INVALID_VALUE, inst)
<< "OpTypeInt has invalid signedness:";
}
+
+ // SPIR-V Spec 2.16.3: Validation Rules for Kernel Capabilities: The
+ // Signedness in OpTypeInt must always be 0.
+ if (SpvOpTypeInt == inst->opcode() && _.HasCapability(SpvCapabilityKernel) &&
+ inst->GetOperandAs(2) != 0u) {
+ return _.diag(SPV_ERROR_INVALID_BINARY, inst)
+ << "The Signedness in OpTypeInt "
+ "must always be 0 when Kernel "
+ "capability is used.";
+ }
+
return SPV_SUCCESS;
}
@@ -445,6 +456,12 @@ spv_result_t ValidateTypePointer(ValidationState_t& _,
if (sampled == 2) _.RegisterPointerToStorageImage(inst->id());
}
}
+
+ if (!_.IsValidStorageClass(storage_class)) {
+ return _.diag(SPV_ERROR_INVALID_BINARY, inst)
+ << "Invalid storage class for target environment";
+ }
+
return SPV_SUCCESS;
}
diff --git a/3rdparty/spirv-tools/source/val/validation_state.cpp b/3rdparty/spirv-tools/source/val/validation_state.cpp
index 794d0f7b8..20eaf88e4 100644
--- a/3rdparty/spirv-tools/source/val/validation_state.cpp
+++ b/3rdparty/spirv-tools/source/val/validation_state.cpp
@@ -212,14 +212,6 @@ ValidationState_t::ValidationState_t(const spv_const_context ctx,
}
}
- switch (env) {
- case SPV_ENV_WEBGPU_0:
- features_.bans_op_undef = true;
- break;
- default:
- break;
- }
-
// Only attempt to count if we have words, otherwise let the other validation
// fail and generate an error.
if (num_words > 0) {
@@ -1277,5 +1269,52 @@ bool ValidationState_t::ContainsLimitedUseIntOrFloatType(uint32_t id) const {
return false;
}
+bool ValidationState_t::IsValidStorageClass(
+ SpvStorageClass storage_class) const {
+ if (spvIsWebGPUEnv(context()->target_env)) {
+ switch (storage_class) {
+ case SpvStorageClassUniformConstant:
+ case SpvStorageClassUniform:
+ case SpvStorageClassStorageBuffer:
+ case SpvStorageClassInput:
+ case SpvStorageClassOutput:
+ case SpvStorageClassImage:
+ case SpvStorageClassWorkgroup:
+ case SpvStorageClassPrivate:
+ case SpvStorageClassFunction:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ if (spvIsVulkanEnv(context()->target_env)) {
+ switch (storage_class) {
+ case SpvStorageClassUniformConstant:
+ case SpvStorageClassUniform:
+ case SpvStorageClassStorageBuffer:
+ case SpvStorageClassInput:
+ case SpvStorageClassOutput:
+ case SpvStorageClassImage:
+ case SpvStorageClassWorkgroup:
+ case SpvStorageClassPrivate:
+ case SpvStorageClassFunction:
+ case SpvStorageClassPushConstant:
+ case SpvStorageClassPhysicalStorageBuffer:
+ case SpvStorageClassRayPayloadNV:
+ case SpvStorageClassIncomingRayPayloadNV:
+ case SpvStorageClassHitAttributeNV:
+ case SpvStorageClassCallableDataNV:
+ case SpvStorageClassIncomingCallableDataNV:
+ case SpvStorageClassShaderRecordBufferNV:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
+
} // namespace val
} // namespace spvtools
diff --git a/3rdparty/spirv-tools/source/val/validation_state.h b/3rdparty/spirv-tools/source/val/validation_state.h
index e650d2ea0..e5d31acf6 100644
--- a/3rdparty/spirv-tools/source/val/validation_state.h
+++ b/3rdparty/spirv-tools/source/val/validation_state.h
@@ -79,9 +79,6 @@ class ValidationState_t {
// Permit group oerations Reduce, InclusiveScan, ExclusiveScan
bool group_ops_reduce_and_scans = false;
- // Disallows the use of OpUndef
- bool bans_op_undef = false;
-
// Allow OpTypeInt with 8 bit width?
bool declare_int8_type = false;
@@ -707,6 +704,9 @@ class ValidationState_t {
// * OpCopyObject
const Instruction* TracePointer(const Instruction* inst) const;
+ // Validates the storage class for the target environment.
+ bool IsValidStorageClass(SpvStorageClass storage_class) const;
+
private:
ValidationState_t(const ValidationState_t&);
diff --git a/3rdparty/spirv-tools/test/opt/inline_test.cpp b/3rdparty/spirv-tools/test/opt/inline_test.cpp
index 1fb0e7210..fac49ca6a 100644
--- a/3rdparty/spirv-tools/test/opt/inline_test.cpp
+++ b/3rdparty/spirv-tools/test/opt/inline_test.cpp
@@ -2936,7 +2936,7 @@ OpFunctionEnd
SinglePassRunAndCheck(test, test, false, true);
}
-TEST_F(InlineTest, DontInlineFuncWithOpKill) {
+TEST_F(InlineTest, DontInlineFuncWithOpKillInContinue) {
const std::string test =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@@ -2976,6 +2976,60 @@ OpFunctionEnd
SinglePassRunAndCheck(test, test, false, true);
}
+TEST_F(InlineTest, InlineFuncWithOpKillNotInContinue) {
+ const std::string before =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 330
+OpName %main "main"
+OpName %kill_ "kill("
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%main = OpFunction %void None %3
+%5 = OpLabel
+%16 = OpFunctionCall %void %kill_
+OpReturn
+OpFunctionEnd
+%kill_ = OpFunction %void None %3
+%7 = OpLabel
+OpKill
+OpFunctionEnd
+)";
+
+ const std::string after =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 330
+OpName %main "main"
+OpName %kill_ "kill("
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%main = OpFunction %void None %3
+%5 = OpLabel
+OpKill
+%17 = OpLabel
+OpReturn
+OpFunctionEnd
+%kill_ = OpFunction %void None %3
+%7 = OpLabel
+OpKill
+OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndCheck(before, after, false, true);
+}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// Empty modules
diff --git a/3rdparty/spirv-tools/test/opt/inst_buff_addr_check_test.cpp b/3rdparty/spirv-tools/test/opt/inst_buff_addr_check_test.cpp
index 837c8166b..f8448a98f 100644
--- a/3rdparty/spirv-tools/test/opt/inst_buff_addr_check_test.cpp
+++ b/3rdparty/spirv-tools/test/opt/inst_buff_addr_check_test.cpp
@@ -464,7 +464,7 @@ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_blockType PhysicalStorageBuffer
%uint_7 = OpConstant %uint 7
%uint_9 = OpConstant %uint 9
%uint_44 = OpConstant %uint 44
-%132 = OpConstantNull %_ptr_PhysicalStorageBuffer_blockType
+%132 = OpConstantNull %ulong
%uint_46 = OpConstant %uint 46
)";
@@ -499,24 +499,25 @@ OpBranch %68
%74 = OpShiftRightLogical %ulong %30 %uint_32
%75 = OpUConvert %uint %74
%131 = OpFunctionCall %void %76 %uint_44 %uint_2 %72 %75
+%133 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_blockType %132
OpBranch %68
%68 = OpLabel
-%133 = OpPhi %_ptr_PhysicalStorageBuffer_blockType %71 %69 %132 %70
-%26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %133 %int_0
-%134 = OpConvertPtrToU %ulong %26
-%135 = OpFunctionCall %bool %32 %134 %uint_4
-OpSelectionMerge %136 None
-OpBranchConditional %135 %137 %138
-%137 = OpLabel
-OpStore %26 %int_531 Aligned 16
-OpBranch %136
+%134 = OpPhi %_ptr_PhysicalStorageBuffer_blockType %71 %69 %133 %70
+%26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %134 %int_0
+%135 = OpConvertPtrToU %ulong %26
+%136 = OpFunctionCall %bool %32 %135 %uint_4
+OpSelectionMerge %137 None
+OpBranchConditional %136 %138 %139
%138 = OpLabel
-%139 = OpUConvert %uint %134
-%140 = OpShiftRightLogical %ulong %134 %uint_32
-%141 = OpUConvert %uint %140
-%143 = OpFunctionCall %void %76 %uint_46 %uint_2 %139 %141
-OpBranch %136
-%136 = OpLabel
+OpStore %26 %int_531 Aligned 16
+OpBranch %137
+%139 = OpLabel
+%140 = OpUConvert %uint %135
+%141 = OpShiftRightLogical %ulong %135 %uint_32
+%142 = OpUConvert %uint %141
+%144 = OpFunctionCall %void %76 %uint_46 %uint_2 %140 %142
+OpBranch %137
+%137 = OpLabel
OpReturn
OpFunctionEnd
)";
diff --git a/3rdparty/spirv-tools/test/opt/module_test.cpp b/3rdparty/spirv-tools/test/opt/module_test.cpp
index 569cf9bcd..406da0939 100644
--- a/3rdparty/spirv-tools/test/opt/module_test.cpp
+++ b/3rdparty/spirv-tools/test/opt/module_test.cpp
@@ -21,6 +21,7 @@
#include "gtest/gtest.h"
#include "source/opt/build_module.h"
#include "source/opt/module.h"
+#include "source/opt/pass.h"
#include "spirv-tools/libspirv.hpp"
#include "test/opt/module_utils.h"
@@ -228,6 +229,72 @@ OpFunctionEnd)";
EXPECT_EQ(next_id_bound, 0);
EXPECT_EQ(current_bound, context->module()->id_bound());
}
+
+// Tests that "text" does not change when it is assembled, converted into a
+// module, converted back to a binary, and then disassembled.
+void AssembleAndDisassemble(const std::string& text) {
+ std::unique_ptr context = BuildModule(text);
+ std::vector binary;
+
+ context->module()->ToBinary(&binary, false);
+
+ SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
+ std::string s;
+ tools.Disassemble(binary, &s);
+ EXPECT_EQ(s, text);
+}
+
+TEST(ModuleTest, TrailingOpLine) {
+ const std::string text = R"(OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%5 = OpString "file.ext"
+%void = OpTypeVoid
+%2 = OpTypeFunction %void
+%3 = OpFunction %void None %2
+%4 = OpLabel
+OpReturn
+OpFunctionEnd
+OpLine %5 1 0
+)";
+
+ AssembleAndDisassemble(text);
+}
+
+TEST(ModuleTest, TrailingOpNoLine) {
+ const std::string text = R"(OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%2 = OpTypeFunction %void
+%3 = OpFunction %void None %2
+%4 = OpLabel
+OpReturn
+OpFunctionEnd
+OpNoLine
+)";
+
+ AssembleAndDisassemble(text);
+}
+
+TEST(ModuleTest, MulitpleTrailingOpLine) {
+ const std::string text = R"(OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%5 = OpString "file.ext"
+%void = OpTypeVoid
+%2 = OpTypeFunction %void
+%3 = OpFunction %void None %2
+%4 = OpLabel
+OpReturn
+OpFunctionEnd
+OpLine %5 1 0
+OpNoLine
+OpLine %5 1 1
+)";
+
+ AssembleAndDisassemble(text);
+}
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/3rdparty/spirv-tools/test/opt/strip_debug_info_test.cpp b/3rdparty/spirv-tools/test/opt/strip_debug_info_test.cpp
index 25cf7d8ac..2f2ff4670 100644
--- a/3rdparty/spirv-tools/test/opt/strip_debug_info_test.cpp
+++ b/3rdparty/spirv-tools/test/opt/strip_debug_info_test.cpp
@@ -51,6 +51,8 @@ TEST_F(StripLineDebugInfoTest, LineNoLine) {
"OpLine %3 4 4",
"OpNoLine",
"OpFunctionEnd",
+ "OpNoLine",
+ "OpLine %3 4 5"
// clang-format on
};
SinglePassRunAndCheck(JoinAllInsts(text),
@@ -74,6 +76,82 @@ TEST_F(StripLineDebugInfoTest, LineNoLine) {
/* skip_nop = */ false);
}
+using StripDebugStringTest = PassTest<::testing::Test>;
+
+TEST_F(StripDebugStringTest, OpDecorateRemoved) {
+ std::vector input{
+ // clang-format off
+ "OpCapability Shader",
+ "%1 = OpExtInstImport \"GLSL.std.450\"",
+ "OpMemoryModel Logical GLSL450",
+ "OpEntryPoint Vertex %2 \"main\"",
+ "%3 = OpString \"minimal.vert\"",
+ "OpDecorate %3 Location 1337",
+ "%void = OpTypeVoid",
+ "%5 = OpTypeFunction %void",
+ "%2 = OpFunction %void None %5",
+ "%6 = OpLabel",
+ "OpReturn",
+ "OpFunctionEnd",
+ // clang-format on
+ };
+ std::vector output{
+ // clang-format off
+ "OpCapability Shader",
+ "%1 = OpExtInstImport \"GLSL.std.450\"",
+ "OpMemoryModel Logical GLSL450",
+ "OpEntryPoint Vertex %2 \"main\"",
+ "%void = OpTypeVoid",
+ "%5 = OpTypeFunction %void",
+ "%2 = OpFunction %void None %5",
+ "%6 = OpLabel",
+ "OpReturn",
+ "OpFunctionEnd",
+ // clang-format on
+ };
+ SinglePassRunAndCheck(JoinAllInsts(input),
+ JoinAllInsts(output),
+ /* skip_nop = */ false,
+ /* do_validation */ true);
+}
+
+TEST_F(StripDebugStringTest, OpNameRemoved) {
+ std::vector input{
+ // clang-format off
+ "OpCapability Shader",
+ "%1 = OpExtInstImport \"GLSL.std.450\"",
+ "OpMemoryModel Logical GLSL450",
+ "OpEntryPoint Vertex %2 \"main\"",
+ "%3 = OpString \"minimal.vert\"",
+ "OpName %3 \"bob\"",
+ "%void = OpTypeVoid",
+ "%5 = OpTypeFunction %void",
+ "%2 = OpFunction %void None %5",
+ "%6 = OpLabel",
+ "OpReturn",
+ "OpFunctionEnd",
+ // clang-format on
+ };
+ std::vector output{
+ // clang-format off
+ "OpCapability Shader",
+ "%1 = OpExtInstImport \"GLSL.std.450\"",
+ "OpMemoryModel Logical GLSL450",
+ "OpEntryPoint Vertex %2 \"main\"",
+ "%void = OpTypeVoid",
+ "%5 = OpTypeFunction %void",
+ "%2 = OpFunction %void None %5",
+ "%6 = OpLabel",
+ "OpReturn",
+ "OpFunctionEnd",
+ // clang-format on
+ };
+ SinglePassRunAndCheck(JoinAllInsts(input),
+ JoinAllInsts(output),
+ /* skip_nop = */ false,
+ /* do_validation */ true);
+}
+
using StripDebugInfoTest = PassTest<::testing::TestWithParam>;
TEST_P(StripDebugInfoTest, Kind) {
diff --git a/3rdparty/spirv-tools/test/opt/struct_cfg_analysis_test.cpp b/3rdparty/spirv-tools/test/opt/struct_cfg_analysis_test.cpp
index 7fb378432..0451a8b46 100644
--- a/3rdparty/spirv-tools/test/opt/struct_cfg_analysis_test.cpp
+++ b/3rdparty/spirv-tools/test/opt/struct_cfg_analysis_test.cpp
@@ -26,6 +26,7 @@ namespace opt {
namespace {
using StructCFGAnalysisTest = PassTest<::testing::Test>;
+using ::testing::UnorderedElementsAre;
TEST_F(StructCFGAnalysisTest, BBInSelection) {
const std::string text = R"(
@@ -62,6 +63,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(1));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsMergeBlock(1));
// BB2 is in the construct.
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@@ -70,6 +75,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(2));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsMergeBlock(2));
// The merge node is not in the construct.
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@@ -78,6 +87,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(3));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+ EXPECT_TRUE(analysis.IsMergeBlock(3));
}
TEST_F(StructCFGAnalysisTest, BBInLoop) {
@@ -119,6 +132,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(1));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsMergeBlock(1));
// BB2 is in the construct.
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@@ -127,6 +144,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(2));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsMergeBlock(2));
// The merge node is not in the construct.
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@@ -135,6 +156,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(3));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+ EXPECT_TRUE(analysis.IsMergeBlock(3));
// The continue block is in the construct.
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@@ -143,6 +168,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
EXPECT_EQ(analysis.ContainingSwitch(4), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
+ EXPECT_TRUE(analysis.IsContinueBlock(4));
+ EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(4));
+ EXPECT_TRUE(analysis.IsInContinueConstruct(4));
+ EXPECT_FALSE(analysis.IsMergeBlock(4));
}
TEST_F(StructCFGAnalysisTest, SelectionInLoop) {
@@ -189,6 +218,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(1));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsMergeBlock(1));
// Selection header is in the loop only.
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@@ -197,6 +230,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(2));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsMergeBlock(2));
// The loop merge node is not in either construct.
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@@ -205,6 +242,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(3));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+ EXPECT_TRUE(analysis.IsMergeBlock(3));
// The continue block is in the loop only.
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@@ -213,14 +254,22 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
EXPECT_EQ(analysis.ContainingSwitch(4), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
+ EXPECT_TRUE(analysis.IsContinueBlock(4));
+ EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(4));
+ EXPECT_TRUE(analysis.IsInContinueConstruct(4));
+ EXPECT_FALSE(analysis.IsMergeBlock(4));
- // BB5 is in the selection fist and the loop.
+ // BB5 is in the selection and the loop.
EXPECT_EQ(analysis.ContainingConstruct(5), 2);
EXPECT_EQ(analysis.ContainingLoop(5), 1);
EXPECT_EQ(analysis.MergeBlock(5), 6);
EXPECT_EQ(analysis.LoopMergeBlock(5), 3);
EXPECT_EQ(analysis.ContainingSwitch(5), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(5));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(5));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(5));
+ EXPECT_FALSE(analysis.IsMergeBlock(5));
// The selection merge is in the loop only.
EXPECT_EQ(analysis.ContainingConstruct(6), 1);
@@ -229,6 +278,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(6), 3);
EXPECT_EQ(analysis.ContainingSwitch(6), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(6));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(6));
+ EXPECT_TRUE(analysis.IsMergeBlock(6));
}
TEST_F(StructCFGAnalysisTest, LoopInSelection) {
@@ -275,6 +328,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(1));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsMergeBlock(1));
// Loop header is in the selection only.
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@@ -283,6 +340,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(2));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsMergeBlock(2));
// The selection merge node is not in either construct.
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@@ -291,6 +352,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(3));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+ EXPECT_TRUE(analysis.IsMergeBlock(3));
// The loop merge is in the selection only.
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@@ -299,6 +364,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
EXPECT_EQ(analysis.ContainingSwitch(4), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(4));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(4));
+ EXPECT_TRUE(analysis.IsMergeBlock(4));
// The loop continue target is in the loop.
EXPECT_EQ(analysis.ContainingConstruct(5), 2);
@@ -307,6 +376,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(5), 4);
EXPECT_EQ(analysis.ContainingSwitch(5), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
+ EXPECT_TRUE(analysis.IsContinueBlock(5));
+ EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5));
+ EXPECT_TRUE(analysis.IsInContinueConstruct(5));
+ EXPECT_FALSE(analysis.IsMergeBlock(5));
// BB6 is in the loop.
EXPECT_EQ(analysis.ContainingConstruct(6), 2);
@@ -315,6 +388,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(6), 4);
EXPECT_EQ(analysis.ContainingSwitch(6), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(6));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(6));
+ EXPECT_FALSE(analysis.IsMergeBlock(6));
}
TEST_F(StructCFGAnalysisTest, SelectionInSelection) {
@@ -359,6 +436,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(1));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsMergeBlock(1));
// The inner header is in the outer selection.
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@@ -367,6 +448,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(2));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsMergeBlock(2));
// The outer merge node is not in either construct.
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@@ -375,6 +460,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(3));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+ EXPECT_TRUE(analysis.IsMergeBlock(3));
// The inner merge is in the outer selection.
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@@ -383,6 +472,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
EXPECT_EQ(analysis.ContainingSwitch(4), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(4));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(4));
+ EXPECT_TRUE(analysis.IsMergeBlock(4));
// BB5 is in the inner selection.
EXPECT_EQ(analysis.ContainingConstruct(5), 2);
@@ -391,6 +484,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(5), 0);
EXPECT_EQ(analysis.ContainingSwitch(5), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(5));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(5));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(5));
+ EXPECT_FALSE(analysis.IsMergeBlock(5));
}
TEST_F(StructCFGAnalysisTest, LoopInLoop) {
@@ -439,6 +536,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(1));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsMergeBlock(1));
// The inner loop header is in the outer loop.
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@@ -447,6 +548,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(2));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsMergeBlock(2));
// The outer merge node is not in either construct.
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@@ -455,6 +560,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(3));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+ EXPECT_TRUE(analysis.IsMergeBlock(3));
// The inner merge is in the outer loop.
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@@ -463,6 +572,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
EXPECT_EQ(analysis.ContainingSwitch(4), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(4));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(4));
+ EXPECT_TRUE(analysis.IsMergeBlock(4));
// The inner continue target is in the inner loop.
EXPECT_EQ(analysis.ContainingConstruct(5), 2);
@@ -471,6 +584,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(5), 4);
EXPECT_EQ(analysis.ContainingSwitch(5), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
+ EXPECT_TRUE(analysis.IsContinueBlock(5));
+ EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5));
+ EXPECT_TRUE(analysis.IsInContinueConstruct(5));
+ EXPECT_FALSE(analysis.IsMergeBlock(5));
// BB6 is in the loop.
EXPECT_EQ(analysis.ContainingConstruct(6), 2);
@@ -479,6 +596,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(6), 4);
EXPECT_EQ(analysis.ContainingSwitch(6), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(6));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(6));
+ EXPECT_FALSE(analysis.IsMergeBlock(6));
// The outer continue target is in the outer loop.
EXPECT_EQ(analysis.ContainingConstruct(7), 1);
@@ -487,6 +608,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(7), 3);
EXPECT_EQ(analysis.ContainingSwitch(7), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(7), 0);
+ EXPECT_TRUE(analysis.IsContinueBlock(7));
+ EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(7));
+ EXPECT_TRUE(analysis.IsInContinueConstruct(7));
+ EXPECT_FALSE(analysis.IsMergeBlock(7));
}
TEST_F(StructCFGAnalysisTest, KernelTest) {
@@ -523,6 +648,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(i), 0);
EXPECT_EQ(analysis.ContainingSwitch(i), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(i), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(i));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(i));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(i));
+ EXPECT_FALSE(analysis.IsMergeBlock(i));
}
}
@@ -581,6 +710,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(1));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsMergeBlock(1));
// BB2 is in the construct.
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@@ -589,6 +722,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
EXPECT_EQ(analysis.ContainingSwitch(2), 1);
EXPECT_EQ(analysis.SwitchMergeBlock(2), 3);
+ EXPECT_FALSE(analysis.IsContinueBlock(2));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsMergeBlock(2));
// The merge node is not in the construct.
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@@ -597,6 +734,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(3));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+ EXPECT_TRUE(analysis.IsMergeBlock(3));
}
TEST_F(StructCFGAnalysisTest, LoopInSwitch) {
@@ -643,6 +784,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(1));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsMergeBlock(1));
// Loop header is in the selection only.
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@@ -651,6 +796,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
EXPECT_EQ(analysis.ContainingSwitch(2), 1);
EXPECT_EQ(analysis.SwitchMergeBlock(2), 3);
+ EXPECT_FALSE(analysis.IsContinueBlock(2));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsMergeBlock(2));
// The selection merge node is not in either construct.
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@@ -659,6 +808,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(3));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+ EXPECT_TRUE(analysis.IsMergeBlock(3));
// The loop merge is in the selection only.
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@@ -667,6 +820,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
EXPECT_EQ(analysis.ContainingSwitch(4), 1);
EXPECT_EQ(analysis.SwitchMergeBlock(4), 3);
+ EXPECT_FALSE(analysis.IsContinueBlock(4));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(4));
+ EXPECT_TRUE(analysis.IsMergeBlock(4));
// The loop continue target is in the loop.
EXPECT_EQ(analysis.ContainingConstruct(5), 2);
@@ -675,6 +832,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(5), 4);
EXPECT_EQ(analysis.ContainingSwitch(5), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
+ EXPECT_TRUE(analysis.IsContinueBlock(5));
+ EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5));
+ EXPECT_TRUE(analysis.IsInContinueConstruct(5));
+ EXPECT_FALSE(analysis.IsMergeBlock(5));
// BB6 is in the loop.
EXPECT_EQ(analysis.ContainingConstruct(6), 2);
@@ -683,6 +844,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(6), 4);
EXPECT_EQ(analysis.ContainingSwitch(6), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(6));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(6));
+ EXPECT_FALSE(analysis.IsMergeBlock(6));
}
TEST_F(StructCFGAnalysisTest, SelectionInSwitch) {
@@ -727,6 +892,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(1));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsMergeBlock(1));
// The inner header is in the outer selection.
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@@ -735,6 +904,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
EXPECT_EQ(analysis.ContainingSwitch(2), 1);
EXPECT_EQ(analysis.SwitchMergeBlock(2), 3);
+ EXPECT_FALSE(analysis.IsContinueBlock(2));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsMergeBlock(2));
// The outer merge node is not in either construct.
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@@ -743,6 +916,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(3));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+ EXPECT_TRUE(analysis.IsMergeBlock(3));
// The inner merge is in the outer selection.
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@@ -751,6 +928,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
EXPECT_EQ(analysis.ContainingSwitch(4), 1);
EXPECT_EQ(analysis.SwitchMergeBlock(4), 3);
+ EXPECT_FALSE(analysis.IsContinueBlock(4));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(4));
+ EXPECT_TRUE(analysis.IsMergeBlock(4));
// BB5 is in the inner selection.
EXPECT_EQ(analysis.ContainingConstruct(5), 2);
@@ -759,6 +940,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(5), 0);
EXPECT_EQ(analysis.ContainingSwitch(5), 1);
EXPECT_EQ(analysis.SwitchMergeBlock(5), 3);
+ EXPECT_FALSE(analysis.IsContinueBlock(5));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(5));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(5));
+ EXPECT_FALSE(analysis.IsMergeBlock(5));
}
TEST_F(StructCFGAnalysisTest, SwitchInSelection) {
@@ -803,6 +988,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(1));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsMergeBlock(1));
// The inner header is in the outer selection.
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@@ -811,6 +1000,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(2));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsMergeBlock(2));
// The outer merge node is not in either construct.
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@@ -819,6 +1012,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(3));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+ EXPECT_TRUE(analysis.IsMergeBlock(3));
// The inner merge is in the outer selection.
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@@ -827,6 +1024,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
EXPECT_EQ(analysis.ContainingSwitch(4), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(4));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(4));
+ EXPECT_TRUE(analysis.IsMergeBlock(4));
// BB5 is in the inner selection.
EXPECT_EQ(analysis.ContainingConstruct(5), 2);
@@ -835,8 +1036,339 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(5), 0);
EXPECT_EQ(analysis.ContainingSwitch(5), 2);
EXPECT_EQ(analysis.SwitchMergeBlock(5), 4);
+ EXPECT_FALSE(analysis.IsContinueBlock(5));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(5));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(5));
+ EXPECT_FALSE(analysis.IsMergeBlock(5));
}
+TEST_F(StructCFGAnalysisTest, SelectionInContinue) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_undef = OpUndef %bool
+%uint = OpTypeInt 32 0
+%uint_undef = OpUndef %uint
+%void_func = OpTypeFunction %void
+%main = OpFunction %void None %void_func
+%entry_lab = OpLabel
+OpBranch %1
+%1 = OpLabel
+OpLoopMerge %3 %4 None
+OpBranchConditional %undef_bool %2 %3
+%2 = OpLabel
+OpBranch %3
+%4 = OpLabel
+OpSelectionMerge %6 None
+OpBranchConditional %undef_bool %5 %6
+%5 = OpLabel
+OpBranch %6
+%6 = OpLabel
+OpBranch %1
+%3 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ std::unique_ptr context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+ StructuredCFGAnalysis analysis(context.get());
+
+ // The loop header is not in either construct.
+ EXPECT_EQ(analysis.ContainingConstruct(1), 0);
+ EXPECT_EQ(analysis.ContainingLoop(1), 0);
+ EXPECT_EQ(analysis.MergeBlock(1), 0);
+ EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
+ EXPECT_EQ(analysis.ContainingSwitch(1), 0);
+ EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(1));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsMergeBlock(1));
+
+ // Selection header is in the loop only.
+ EXPECT_EQ(analysis.ContainingConstruct(2), 1);
+ EXPECT_EQ(analysis.ContainingLoop(2), 1);
+ EXPECT_EQ(analysis.MergeBlock(2), 3);
+ EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
+ EXPECT_EQ(analysis.ContainingSwitch(2), 0);
+ EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(2));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsMergeBlock(2));
+
+ // The loop merge node is not in either construct.
+ EXPECT_EQ(analysis.ContainingConstruct(3), 0);
+ EXPECT_EQ(analysis.ContainingLoop(3), 0);
+ EXPECT_EQ(analysis.MergeBlock(3), 0);
+ EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
+ EXPECT_EQ(analysis.ContainingSwitch(3), 0);
+ EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(3));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+ EXPECT_TRUE(analysis.IsMergeBlock(3));
+
+ // The continue block is in the loop only.
+ EXPECT_EQ(analysis.ContainingConstruct(4), 1);
+ EXPECT_EQ(analysis.ContainingLoop(4), 1);
+ EXPECT_EQ(analysis.MergeBlock(4), 3);
+ EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
+ EXPECT_EQ(analysis.ContainingSwitch(4), 0);
+ EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
+ EXPECT_TRUE(analysis.IsContinueBlock(4));
+ EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(4));
+ EXPECT_TRUE(analysis.IsInContinueConstruct(4));
+ EXPECT_FALSE(analysis.IsMergeBlock(4));
+
+ // BB5 is in the selection and the continue for the loop.
+ EXPECT_EQ(analysis.ContainingConstruct(5), 4);
+ EXPECT_EQ(analysis.ContainingLoop(5), 1);
+ EXPECT_EQ(analysis.MergeBlock(5), 6);
+ EXPECT_EQ(analysis.LoopMergeBlock(5), 3);
+ EXPECT_EQ(analysis.ContainingSwitch(5), 0);
+ EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(5));
+ EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5));
+ EXPECT_TRUE(analysis.IsInContinueConstruct(5));
+ EXPECT_FALSE(analysis.IsMergeBlock(5));
+
+ // BB5 is in the continue for the loop.
+ EXPECT_EQ(analysis.ContainingConstruct(6), 1);
+ EXPECT_EQ(analysis.ContainingLoop(6), 1);
+ EXPECT_EQ(analysis.MergeBlock(6), 3);
+ EXPECT_EQ(analysis.LoopMergeBlock(6), 3);
+ EXPECT_EQ(analysis.ContainingSwitch(6), 0);
+ EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(6));
+ EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(6));
+ EXPECT_TRUE(analysis.IsInContinueConstruct(6));
+ EXPECT_TRUE(analysis.IsMergeBlock(6));
+}
+
+TEST_F(StructCFGAnalysisTest, LoopInContinue) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_undef = OpUndef %bool
+%uint = OpTypeInt 32 0
+%uint_undef = OpUndef %uint
+%void_func = OpTypeFunction %void
+%main = OpFunction %void None %void_func
+%entry_lab = OpLabel
+OpBranch %1
+%1 = OpLabel
+OpLoopMerge %3 %7 None
+OpBranchConditional %undef_bool %2 %3
+%2 = OpLabel
+OpBranchConditional %undef_bool %3 %7
+%7 = OpLabel
+OpLoopMerge %4 %5 None
+OpBranchConditional %undef_bool %4 %6
+%5 = OpLabel
+OpBranch %7
+%6 = OpLabel
+OpBranch %4
+%4 = OpLabel
+OpBranch %1
+%3 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ std::unique_ptr context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+ StructuredCFGAnalysis analysis(context.get());
+
+ // The outer loop header is not in either construct.
+ EXPECT_EQ(analysis.ContainingConstruct(1), 0);
+ EXPECT_EQ(analysis.ContainingLoop(1), 0);
+ EXPECT_EQ(analysis.MergeBlock(1), 0);
+ EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
+ EXPECT_EQ(analysis.ContainingSwitch(1), 0);
+ EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(1));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+ EXPECT_FALSE(analysis.IsMergeBlock(1));
+
+ // BB2 is a regular block in the inner loop.
+ EXPECT_EQ(analysis.ContainingConstruct(2), 1);
+ EXPECT_EQ(analysis.ContainingLoop(2), 1);
+ EXPECT_EQ(analysis.MergeBlock(2), 3);
+ EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
+ EXPECT_EQ(analysis.ContainingSwitch(2), 0);
+ EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(2));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+ EXPECT_FALSE(analysis.IsMergeBlock(2));
+
+ // The outer merge node is not in either construct.
+ EXPECT_EQ(analysis.ContainingConstruct(3), 0);
+ EXPECT_EQ(analysis.ContainingLoop(3), 0);
+ EXPECT_EQ(analysis.MergeBlock(3), 0);
+ EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
+ EXPECT_EQ(analysis.ContainingSwitch(3), 0);
+ EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(3));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+ EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+ EXPECT_TRUE(analysis.IsMergeBlock(3));
+
+ // The inner merge is in the continue of the outer loop.
+ EXPECT_EQ(analysis.ContainingConstruct(4), 1);
+ EXPECT_EQ(analysis.ContainingLoop(4), 1);
+ EXPECT_EQ(analysis.MergeBlock(4), 3);
+ EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
+ EXPECT_EQ(analysis.ContainingSwitch(4), 0);
+ EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(4));
+ EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(4));
+ EXPECT_TRUE(analysis.IsInContinueConstruct(4));
+ EXPECT_TRUE(analysis.IsMergeBlock(4));
+
+ // The inner continue target is in the inner loop.
+ EXPECT_EQ(analysis.ContainingConstruct(5), 7);
+ EXPECT_EQ(analysis.ContainingLoop(5), 7);
+ EXPECT_EQ(analysis.MergeBlock(5), 4);
+ EXPECT_EQ(analysis.LoopMergeBlock(5), 4);
+ EXPECT_EQ(analysis.ContainingSwitch(5), 0);
+ EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
+ EXPECT_TRUE(analysis.IsContinueBlock(5));
+ EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5));
+ EXPECT_TRUE(analysis.IsInContinueConstruct(5));
+ EXPECT_FALSE(analysis.IsMergeBlock(5));
+
+ // BB6 is a regular block in the inner loop.
+ EXPECT_EQ(analysis.ContainingConstruct(6), 7);
+ EXPECT_EQ(analysis.ContainingLoop(6), 7);
+ EXPECT_EQ(analysis.MergeBlock(6), 4);
+ EXPECT_EQ(analysis.LoopMergeBlock(6), 4);
+ EXPECT_EQ(analysis.ContainingSwitch(6), 0);
+ EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
+ EXPECT_FALSE(analysis.IsContinueBlock(6));
+ EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6));
+ EXPECT_TRUE(analysis.IsInContinueConstruct(6));
+ EXPECT_FALSE(analysis.IsMergeBlock(6));
+
+ // The outer continue target is in the outer loop.
+ EXPECT_EQ(analysis.ContainingConstruct(7), 1);
+ EXPECT_EQ(analysis.ContainingLoop(7), 1);
+ EXPECT_EQ(analysis.MergeBlock(7), 3);
+ EXPECT_EQ(analysis.LoopMergeBlock(7), 3);
+ EXPECT_EQ(analysis.ContainingSwitch(7), 0);
+ EXPECT_EQ(analysis.SwitchMergeBlock(7), 0);
+ EXPECT_TRUE(analysis.IsContinueBlock(7));
+ EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(7));
+ EXPECT_TRUE(analysis.IsInContinueConstruct(7));
+ EXPECT_FALSE(analysis.IsMergeBlock(7));
+}
+
+TEST_F(StructCFGAnalysisTest, FuncCallInContinueDirect) {
+ const std::string text = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 "main"
+ %void = OpTypeVoid
+ %bool = OpTypeBool
+ %4 = OpUndef %bool
+ %uint = OpTypeInt 32 0
+ %6 = OpUndef %uint
+ %7 = OpTypeFunction %void
+ %1 = OpFunction %void None %7
+ %8 = OpLabel
+ OpBranch %9
+ %9 = OpLabel
+ OpLoopMerge %10 %11 None
+ OpBranchConditional %12 %10 %11
+ %11 = OpLabel
+ %13 = OpFunctionCall %void %14
+ OpBranch %9
+ %10 = OpLabel
+ %15 = OpFunctionCall %void %16
+ OpReturn
+ OpFunctionEnd
+ %14 = OpFunction %void None %7
+ %17 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %16 = OpFunction %void None %7
+ %18 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ std::unique_ptr context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+ StructuredCFGAnalysis analysis(context.get());
+
+ auto c = analysis.FindFuncsCalledFromContinue();
+ EXPECT_THAT(c, UnorderedElementsAre(14u));
+}
+
+TEST_F(StructCFGAnalysisTest, FuncCallInContinueIndirect) {
+ const std::string text = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 "main"
+ %void = OpTypeVoid
+ %bool = OpTypeBool
+ %4 = OpUndef %bool
+ %uint = OpTypeInt 32 0
+ %6 = OpUndef %uint
+ %7 = OpTypeFunction %void
+ %1 = OpFunction %void None %7
+ %8 = OpLabel
+ OpBranch %9
+ %9 = OpLabel
+ OpLoopMerge %10 %11 None
+ OpBranchConditional %12 %10 %11
+ %11 = OpLabel
+ %13 = OpFunctionCall %void %14
+ OpBranch %9
+ %10 = OpLabel
+ %15 = OpFunctionCall %void %16
+ OpReturn
+ OpFunctionEnd
+ %14 = OpFunction %void None %7
+ %17 = OpLabel
+ %19 = OpFunctionCall %void %16
+ OpReturn
+ OpFunctionEnd
+ %16 = OpFunction %void None %7
+ %18 = OpLabel
+ %20 = OpFunctionCall %void %21
+ OpReturn
+ OpFunctionEnd
+ %21 = OpFunction %void None %7
+ %22 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ std::unique_ptr context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+ StructuredCFGAnalysis analysis(context.get());
+
+ auto c = analysis.FindFuncsCalledFromContinue();
+ EXPECT_THAT(c, UnorderedElementsAre(14u, 16u, 21u));
+}
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/3rdparty/spirv-tools/test/opt/wrap_opkill_test.cpp b/3rdparty/spirv-tools/test/opt/wrap_opkill_test.cpp
index 89022ae8c..d50af28a1 100644
--- a/3rdparty/spirv-tools/test/opt/wrap_opkill_test.cpp
+++ b/3rdparty/spirv-tools/test/opt/wrap_opkill_test.cpp
@@ -249,15 +249,33 @@ TEST_F(WrapOpKillTest, IdBoundOverflow1) {
const std::string text = R"(
OpCapability GeometryStreams
OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %4 "main"
-OpExecutionMode %4 OriginUpperLeft
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
%2 = OpTypeVoid
%3 = OpTypeFunction %2
-%4 = OpFunction %2 Pure|Const %3
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%main = OpFunction %2 None %3
+%8 = OpLabel
+OpBranch %9
+%9 = OpLabel
+OpLoopMerge %10 %11 None
+OpBranch %12
+%12 = OpLabel
+OpBranchConditional %true %13 %10
+%13 = OpLabel
+OpBranch %11
+%11 = OpLabel
+%14 = OpFunctionCall %void %kill_
+OpBranch %9
+%10 = OpLabel
+OpReturn
+OpFunctionEnd
+%kill_ = OpFunction %2 Pure|Const %3
%4194302 = OpLabel
OpKill
OpFunctionEnd
- )";
+)";
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
@@ -272,15 +290,33 @@ TEST_F(WrapOpKillTest, IdBoundOverflow2) {
const std::string text = R"(
OpCapability GeometryStreams
OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %4 "main"
-OpExecutionMode %4 OriginUpperLeft
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
%2 = OpTypeVoid
%3 = OpTypeFunction %2
-%4 = OpFunction %2 Pure|Const %3
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%main = OpFunction %2 None %3
+%8 = OpLabel
+OpBranch %9
+%9 = OpLabel
+OpLoopMerge %10 %11 None
+OpBranch %12
+%12 = OpLabel
+OpBranchConditional %true %13 %10
+%13 = OpLabel
+OpBranch %11
+%11 = OpLabel
+%14 = OpFunctionCall %void %kill_
+OpBranch %9
+%10 = OpLabel
+OpReturn
+OpFunctionEnd
+%kill_ = OpFunction %2 Pure|Const %3
%4194301 = OpLabel
OpKill
OpFunctionEnd
- )";
+)";
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
@@ -295,15 +331,33 @@ TEST_F(WrapOpKillTest, IdBoundOverflow3) {
const std::string text = R"(
OpCapability GeometryStreams
OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %4 "main"
-OpExecutionMode %4 OriginUpperLeft
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
%2 = OpTypeVoid
%3 = OpTypeFunction %2
-%4 = OpFunction %2 Pure|Const %3
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%main = OpFunction %2 None %3
+%8 = OpLabel
+OpBranch %9
+%9 = OpLabel
+OpLoopMerge %10 %11 None
+OpBranch %12
+%12 = OpLabel
+OpBranchConditional %true %13 %10
+%13 = OpLabel
+OpBranch %11
+%11 = OpLabel
+%14 = OpFunctionCall %void %kill_
+OpBranch %9
+%10 = OpLabel
+OpReturn
+OpFunctionEnd
+%kill_ = OpFunction %2 Pure|Const %3
%4194300 = OpLabel
OpKill
OpFunctionEnd
- )";
+)";
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
@@ -318,16 +372,34 @@ TEST_F(WrapOpKillTest, IdBoundOverflow4) {
const std::string text = R"(
OpCapability DerivativeControl
OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %4 "main"
-OpExecutionMode %4 OriginUpperLeft
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
OpDecorate %2 Location 539091968
%2 = OpTypeVoid
%3 = OpTypeFunction %2
-%4 = OpFunction %2 Inline|Pure|Const %3
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%main = OpFunction %2 None %3
+%8 = OpLabel
+OpBranch %9
+%9 = OpLabel
+OpLoopMerge %10 %11 None
+OpBranch %12
+%12 = OpLabel
+OpBranchConditional %true %13 %10
+%13 = OpLabel
+OpBranch %11
+%11 = OpLabel
+%14 = OpFunctionCall %void %kill_
+OpBranch %9
+%10 = OpLabel
+OpReturn
+OpFunctionEnd
+%kill_ = OpFunction %2 Inline|Pure|Const %3
%4194302 = OpLabel
OpKill
OpFunctionEnd
- )";
+)";
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
@@ -342,32 +414,52 @@ TEST_F(WrapOpKillTest, IdBoundOverflow5) {
const std::string text = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %4 "main"
- OpExecutionMode %4 OriginUpperLeft
+ OpEntryPoint Fragment %1 "main"
+ OpExecutionMode %1 OriginUpperLeft
OpDecorate %void Location 539091968
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
- %_struct_7 = OpTypeStruct %float %float
- %_struct_8 = OpTypeStruct %_struct_7
-%_ptr_Function__struct_8 = OpTypePointer Function %_struct_8
+ %_struct_5 = OpTypeStruct %float %float
+ %_struct_6 = OpTypeStruct %_struct_5
+%_ptr_Function__struct_6 = OpTypePointer Function %_struct_6
%_ptr_Output_float = OpTypePointer Output %float
- %18 = OpTypeFunction %_struct_7 %_ptr_Function__struct_8
- %4 = OpFunction %void Inline|Pure|Const %3
- %850212 = OpLabel
- %10 = OpVariable %_ptr_Function__struct_8 Function
- %1441807 = OpFunctionCall %_struct_7 %32257 %10
+ %9 = OpTypeFunction %_struct_5 %_ptr_Function__struct_6
+ %bool = OpTypeBool
+ %true = OpConstantTrue %bool
+ %1 = OpFunction %void None %3
+ %12 = OpLabel
+ %13 = OpVariable %_ptr_Function__struct_6 Function
+ OpBranch %14
+ %14 = OpLabel
+ OpLoopMerge %15 %16 None
+ OpBranch %17
+ %17 = OpLabel
+ OpBranchConditional %true %18 %15
+ %18 = OpLabel
+ OpBranch %16
+ %16 = OpLabel
+ %19 = OpFunctionCall %void %20
+ %21 = OpFunctionCall %_struct_5 %22 %13
+ OpBranch %14
+ %15 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %20 = OpFunction %void Inline|Pure|Const %3
+ %23 = OpLabel
+ %24 = OpVariable %_ptr_Function__struct_6 Function
+ %25 = OpFunctionCall %_struct_5 %26 %24
OpKill
OpFunctionEnd
- %32257 = OpFunction %_struct_7 None %18
- %28 = OpLabel
+ %26 = OpFunction %_struct_5 None %9
+ %27 = OpLabel
OpUnreachable
OpFunctionEnd
- %64821 = OpFunction %_struct_7 Inline %18
+ %22 = OpFunction %_struct_5 Inline %9
%4194295 = OpLabel
OpKill
OpFunctionEnd
- )";
+)";
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
@@ -378,6 +470,49 @@ TEST_F(WrapOpKillTest, IdBoundOverflow5) {
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
}
+TEST_F(WrapOpKillTest, SkipEntryPoint) {
+ const std::string text = R"(
+OpCapability GeometryStreams
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %4 "main"
+OpExecutionMode %4 OriginUpperLeft
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%4 = OpFunction %2 Pure|Const %3
+%5 = OpLabel
+OpKill
+OpFunctionEnd
+)";
+
+ auto result = SinglePassRunToBinary(text, true);
+ EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
+}
+
+TEST_F(WrapOpKillTest, SkipFunctionNotInContinue) {
+ const std::string text = R"(
+OpCapability GeometryStreams
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%main = OpFunction %2 None %3
+%6 = OpLabel
+%7 = OpFunctionCall %void %4
+OpReturn
+OpFunctionEnd
+%4 = OpFunction %2 Pure|Const %3
+%5 = OpLabel
+OpKill
+OpFunctionEnd
+)";
+
+ auto result = SinglePassRunToBinary(text, true);
+ EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/3rdparty/spirv-tools/test/val/val_cfg_test.cpp b/3rdparty/spirv-tools/test/val/val_cfg_test.cpp
index b22db0655..547eb578f 100644
--- a/3rdparty/spirv-tools/test/val/val_cfg_test.cpp
+++ b/3rdparty/spirv-tools/test/val/val_cfg_test.cpp
@@ -342,11 +342,10 @@ TEST_P(ValidateCFG, VariableNotInFirstBlockBad) {
str += "OpFunctionEnd\n";
CompileSuccessfully(str);
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Variables can only be defined in the first block of a function"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("All OpVariable instructions in a function must be the "
+ "first instructions in the first block"));
}
TEST_P(ValidateCFG, BlockSelfLoopIsOk) {
diff --git a/3rdparty/spirv-tools/test/val/val_constants_test.cpp b/3rdparty/spirv-tools/test/val/val_constants_test.cpp
index 2499f5ca4..301539d98 100644
--- a/3rdparty/spirv-tools/test/val/val_constants_test.cpp
+++ b/3rdparty/spirv-tools/test/val/val_constants_test.cpp
@@ -458,6 +458,26 @@ OpMemoryModel Logical GLSL450
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
+TEST_F(ValidateConstant, NullPhysicalStorageBuffer) {
+ std::string spirv = R"(
+OpCapability Shader
+OpCapability PhysicalStorageBufferAddresses
+OpCapability Linkage
+OpExtension "SPV_KHR_physical_storage_buffer"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpName %ptr "ptr"
+%int = OpTypeInt 32 0
+%ptr = OpTypePointer PhysicalStorageBuffer %int
+%null = OpConstantNull %ptr
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("OpConstantNull Result Type '1[%ptr]' cannot have "
+ "a null value"));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/3rdparty/spirv-tools/test/val/val_memory_test.cpp b/3rdparty/spirv-tools/test/val/val_memory_test.cpp
index 858ef8ddc..17f26eec3 100644
--- a/3rdparty/spirv-tools/test/val/val_memory_test.cpp
+++ b/3rdparty/spirv-tools/test/val/val_memory_test.cpp
@@ -4386,6 +4386,96 @@ OpFunctionEnd
"typed as OpTypeStruct, or an array of this type"));
}
+TEST_F(ValidateMemory, PhysicalStorageBufferPtrEqual) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpCapability Int64
+OpCapability PhysicalStorageBufferAddresses
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%long = OpTypeInt 64 0
+%long_0 = OpConstant %long 0
+%ptr_pssbo_long = OpTypePointer PhysicalStorageBuffer %long
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%conv = OpConvertUToPtr %ptr_pssbo_long %long_0
+%eq = OpPtrEqual %bool %conv %conv
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "Cannot use a pointer in the PhysicalStorageBuffer storage class"));
+}
+
+TEST_F(ValidateMemory, PhysicalStorageBufferPtrNotEqual) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpCapability Int64
+OpCapability PhysicalStorageBufferAddresses
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%long = OpTypeInt 64 0
+%long_0 = OpConstant %long 0
+%ptr_pssbo_long = OpTypePointer PhysicalStorageBuffer %long
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%conv = OpConvertUToPtr %ptr_pssbo_long %long_0
+%neq = OpPtrNotEqual %bool %conv %conv
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "Cannot use a pointer in the PhysicalStorageBuffer storage class"));
+}
+
+TEST_F(ValidateMemory, PhysicalStorageBufferPtrDiff) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpCapability Int64
+OpCapability PhysicalStorageBufferAddresses
+OpCapability VariablePointers
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%long = OpTypeInt 64 0
+%long_0 = OpConstant %long 0
+%ptr_pssbo_long = OpTypePointer PhysicalStorageBuffer %long
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%conv = OpConvertUToPtr %ptr_pssbo_long %long_0
+%diff = OpPtrDiff %long %conv %conv
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "Cannot use a pointer in the PhysicalStorageBuffer storage class"));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/3rdparty/spirv-tools/test/val/val_misc_test.cpp b/3rdparty/spirv-tools/test/val/val_misc_test.cpp
index 350f5616b..d266653e3 100644
--- a/3rdparty/spirv-tools/test/val/val_misc_test.cpp
+++ b/3rdparty/spirv-tools/test/val/val_misc_test.cpp
@@ -83,6 +83,87 @@ OpMemoryModel Logical GLSL450
getDiagnosticString(),
HasSubstr("Cannot create undefined values with 8- or 16-bit types"));
}
+
+const std::string ShaderClockSpriv = R"(
+OpCapability Shader
+OpCapability Int64
+OpCapability ShaderClockKHR
+OpExtension "SPV_KHR_shader_clock"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+OpSourceExtension "GL_ARB_gpu_shader_int64"
+OpSourceExtension "GL_ARB_shader_clock"
+OpSourceExtension "GL_EXT_shader_realtime_clock"
+OpName %main "main"
+OpName %time1 "time1"
+%void = OpTypeVoid
+)";
+
+TEST_F(ValidateMisc, ShaderClockInt64) {
+ const std::string spirv = ShaderClockSpriv + R"(
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%_ptr_Function_uint = OpTypePointer Function %uint
+%uint_3 = OpConstant %uint 3
+%uint_1 = OpConstant %uint 1
+%main = OpFunction %void None %3
+%5 = OpLabel
+%time1 = OpVariable %_ptr_Function_uint Function
+%11 = OpReadClockKHR %uint %uint_3
+OpStore %time1 %11
+OpReturn
+OpFunctionEnd)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("or 64bit unsigned integer"));
+}
+
+TEST_F(ValidateMisc, ShaderClockVec2) {
+ const std::string spirv = ShaderClockSpriv + R"(
+%3 = OpTypeFunction %void
+%ulong = OpTypeInt 64 0
+%_ptr_Function_ulong = OpTypePointer Function %ulong
+%uint = OpTypeInt 32 0
+%uint_3 = OpConstant %uint 3
+%v2uint = OpTypeVector %ulong 2
+%_ptr_Function_v2uint = OpTypePointer Function %v2uint
+%main = OpFunction %void None %3
+%5 = OpLabel
+%time1 = OpVariable %_ptr_Function_v2uint Function
+%15 = OpReadClockKHR %v2uint %uint_3
+OpStore %time1 %15
+OpReturn
+OpFunctionEnd)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("vector of two components"));
+}
+
+TEST_F(ValidateMisc, ShaderClockExecutionScope) {
+ const std::string spirv = ShaderClockSpriv + R"(
+%3 = OpTypeFunction %void
+%ulong = OpTypeInt 64 0
+%uint = OpTypeInt 32 0
+%_ptr_Function_ulong = OpTypePointer Function %ulong
+%uint_3 = OpConstant %uint 10
+%uint_1 = OpConstant %uint 1
+%main = OpFunction %void None %3
+%5 = OpLabel
+%time1 = OpVariable %_ptr_Function_ulong Function
+%11 = OpReadClockKHR %ulong %uint_3
+OpStore %time1 %11
+OpReturn
+OpFunctionEnd)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("Invalid scope value"));
+}
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/3rdparty/spirv-tools/test/val/val_opencl_test.cpp b/3rdparty/spirv-tools/test/val/val_opencl_test.cpp
index 18b2f715e..10641587f 100644
--- a/3rdparty/spirv-tools/test/val/val_opencl_test.cpp
+++ b/3rdparty/spirv-tools/test/val/val_opencl_test.cpp
@@ -45,15 +45,18 @@ TEST_F(ValidateOpenCL, NonPhysicalAddressingModelBad) {
TEST_F(ValidateOpenCL, NonOpenCLMemoryModelBad) {
std::string spirv = R"(
OpCapability Kernel
- OpMemoryModel Physical32 GLSL450
+ OpCapability Addresses
+ OpCapability VulkanMemoryModelKHR
+ OpExtension "SPV_KHR_vulkan_memory_model"
+ OpMemoryModel Physical32 VulkanKHR
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Memory model must be OpenCL in the OpenCL environment."
- "\n OpMemoryModel Physical32 GLSL450\n"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Memory model must be OpenCL in the OpenCL environment."));
}
TEST_F(ValidateOpenCL, NonVoidSampledTypeImageBad) {
diff --git a/3rdparty/spirv-tools/test/val/val_storage_test.cpp b/3rdparty/spirv-tools/test/val/val_storage_test.cpp
index f54b425ea..fe37a93c6 100644
--- a/3rdparty/spirv-tools/test/val/val_storage_test.cpp
+++ b/3rdparty/spirv-tools/test/val/val_storage_test.cpp
@@ -157,6 +157,7 @@ TEST_F(ValidateStorage, GenericVariableOutsideFunction) {
const auto str = R"(
OpCapability Kernel
OpCapability Linkage
+ OpCapability GenericPointer
OpMemoryModel Logical OpenCL
%intt = OpTypeInt 32 0
%ptrt = OpTypePointer Function %intt
@@ -172,6 +173,7 @@ TEST_F(ValidateStorage, GenericVariableInsideFunction) {
const auto str = R"(
OpCapability Shader
OpCapability Linkage
+ OpCapability GenericPointer
OpMemoryModel Logical GLSL450
%intt = OpTypeInt 32 1
%voidt = OpTypeVoid
@@ -184,7 +186,7 @@ TEST_F(ValidateStorage, GenericVariableInsideFunction) {
OpFunctionEnd
)";
CompileSuccessfully(str);
- ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions());
+ EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpVariable storage class cannot be Generic"));
}
@@ -307,12 +309,10 @@ INSTANTIATE_TEST_SUITE_P(
std::make_tuple("Workgroup", false, true, ""),
std::make_tuple("Private", false, true, ""),
std::make_tuple("Function", true, true, ""),
- std::make_tuple(
- "CrossWorkgroup", false, false,
- "For WebGPU, OpTypePointer storage class must be one of"),
- std::make_tuple(
- "PushConstant", false, false,
- "For WebGPU, OpTypePointer storage class must be one of")));
+ std::make_tuple("CrossWorkgroup", false, false,
+ "Invalid storage class for target environment"),
+ std::make_tuple("PushConstant", false, false,
+ "Invalid storage class for target environment")));
} // namespace
} // namespace val
diff --git a/3rdparty/spirv-tools/test/val/val_webgpu_test.cpp b/3rdparty/spirv-tools/test/val/val_webgpu_test.cpp
index 1eae0d39c..8f625551b 100644
--- a/3rdparty/spirv-tools/test/val/val_webgpu_test.cpp
+++ b/3rdparty/spirv-tools/test/val/val_webgpu_test.cpp
@@ -187,23 +187,6 @@ TEST_F(ValidateWebGPU, LogicalAddressingVulkanKHRMemoryGood) {
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
}
-TEST_F(ValidateWebGPU, NonLogicalAddressingModelBad) {
- std::string spirv = R"(
- OpCapability Shader
- OpCapability VulkanMemoryModelKHR
- OpExtension "SPV_KHR_vulkan_memory_model"
- OpMemoryModel Physical32 VulkanKHR
-)";
-
- CompileSuccessfully(spirv);
-
- EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Addressing model must be Logical for WebGPU "
- "environment.\n OpMemoryModel Physical32 "
- "Vulkan\n"));
-}
-
TEST_F(ValidateWebGPU, NonVulkanKHRMemoryModelBad) {
std::string spirv = R"(
OpCapability Shader