Browse Source

Updated spirv-tools.

Бранимир Караџић 6 years ago
parent
commit
fc16266e51
39 changed files with 2527 additions and 252 deletions
  1. 2 2
      3rdparty/spirv-tools/DEPS
  2. 620 0
      3rdparty/spirv-tools/README.md
  3. 1 1
      3rdparty/spirv-tools/include/generated/build-version.inc
  4. 3 1
      3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
  5. 368 0
      3rdparty/spirv-tools/source/fuzz/force_render_red.cpp
  6. 48 0
      3rdparty/spirv-tools/source/fuzz/force_render_red.h
  7. 3 2
      3rdparty/spirv-tools/source/opt/cfg.h
  8. 13 5
      3rdparty/spirv-tools/source/opt/inline_pass.cpp
  9. 7 0
      3rdparty/spirv-tools/source/opt/inline_pass.h
  10. 19 5
      3rdparty/spirv-tools/source/opt/inst_buff_addr_check_pass.cpp
  11. 1 6
      3rdparty/spirv-tools/source/opt/ir_context.h
  12. 3 0
      3rdparty/spirv-tools/source/opt/ir_loader.cpp
  13. 3 0
      3rdparty/spirv-tools/source/opt/module.cpp
  14. 17 16
      3rdparty/spirv-tools/source/opt/module.h
  15. 22 1
      3rdparty/spirv-tools/source/opt/strip_debug_info_pass.cpp
  16. 65 0
      3rdparty/spirv-tools/source/opt/struct_cfg_analysis.cpp
  17. 30 4
      3rdparty/spirv-tools/source/opt/struct_cfg_analysis.h
  18. 5 2
      3rdparty/spirv-tools/source/opt/wrap_opkill.cpp
  19. 5 1
      3rdparty/spirv-tools/source/val/validate_constants.cpp
  20. 0 113
      3rdparty/spirv-tools/source/val/validate_instruction.cpp
  21. 26 1
      3rdparty/spirv-tools/source/val/validate_memory.cpp
  22. 34 0
      3rdparty/spirv-tools/source/val/validate_misc.cpp
  23. 41 0
      3rdparty/spirv-tools/source/val/validate_mode_setting.cpp
  24. 17 0
      3rdparty/spirv-tools/source/val/validate_type.cpp
  25. 47 8
      3rdparty/spirv-tools/source/val/validation_state.cpp
  26. 3 3
      3rdparty/spirv-tools/source/val/validation_state.h
  27. 55 1
      3rdparty/spirv-tools/test/opt/inline_test.cpp
  28. 17 16
      3rdparty/spirv-tools/test/opt/inst_buff_addr_check_test.cpp
  29. 67 0
      3rdparty/spirv-tools/test/opt/module_test.cpp
  30. 78 0
      3rdparty/spirv-tools/test/opt/strip_debug_info_test.cpp
  31. 533 1
      3rdparty/spirv-tools/test/opt/struct_cfg_analysis_test.cpp
  32. 165 30
      3rdparty/spirv-tools/test/opt/wrap_opkill_test.cpp
  33. 4 5
      3rdparty/spirv-tools/test/val/val_cfg_test.cpp
  34. 20 0
      3rdparty/spirv-tools/test/val/val_constants_test.cpp
  35. 90 0
      3rdparty/spirv-tools/test/val/val_memory_test.cpp
  36. 81 0
      3rdparty/spirv-tools/test/val/val_misc_test.cpp
  37. 7 4
      3rdparty/spirv-tools/test/val/val_opencl_test.cpp
  38. 7 7
      3rdparty/spirv-tools/test/val/val_storage_test.cpp
  39. 0 17
      3rdparty/spirv-tools/test/val/val_webgpu_test.cpp

+ 2 - 2
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 = {

+ 620 - 0
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
+
+[![Build status](https://ci.appveyor.com/api/projects/status/gpue87cesrx3pi0d/branch/master?svg=true)](https://ci.appveyor.com/project/Khronoswebmaster/spirv-tools/branch/master)
+<img alt="Linux" src="kokoro/img/linux.png" width="20px" height="20px" hspace="2px"/>[![Linux Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_linux_clang_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_release.html)
+<img alt="MacOS" src="kokoro/img/macos.png" width="20px" height="20px" hspace="2px"/>[![MacOS Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_macos_clang_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_release.html)
+<img alt="Windows" src="kokoro/img/windows.png" width="20px" height="20px" hspace="2px"/>[![Windows Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_windows_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_release.html)
+
+[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 `[email protected]` 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 `<spirv-dir>/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 `<spirv-dir>` is the root directory of the
+checked out code:
+
+```sh
+cd <spirv-dir>
+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 <spirv-dir>
+mkdir build && cd build
+cmake [-G <platform-generator>] <spirv-dir>
+```
+
+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 <spirv-dir>
+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=<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 <spirv-dir>
+
+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 `<spirv-dir>/include`, which will enable the application to include the
+header `<spirv-dir>/include/spirv-tools/libspirv.h{|pp}` then linking against
+the static library in `<spirv-build-dir>/source/libSPIRV-Tools.a` or
+`<spirv-build-dir>/source/SPIRV-Tools.lib`.
+For optimization, the header file is
+`<spirv-dir>/include/spirv-tools/optimizer.hpp`, and the static library is
+`<spirv-build-dir>/source/libSPIRV-Tools-opt.a` or
+`<spirv-build-dir>/source/SPIRV-Tools-opt.lib`.
+
+* `SPIRV-Tools` CMake target: Creates the static library:
+  * `<spirv-build-dir>/source/libSPIRV-Tools.a` on Linux and OS X.
+  * `<spirv-build-dir>/source/libSPIRV-Tools.lib` on Windows.
+* `SPIRV-Tools-opt` CMake target: Creates the static library:
+  * `<spirv-build-dir>/source/libSPIRV-Tools-opt.a` on Linux and OS X.
+  * `<spirv-build-dir>/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
+`<spirv-build-dir>/tools/spirv-as`.  The functionality of the assembler is implemented
+by the `spvTextToBinary` library function.
+
+* `spirv-as` - the standalone assembler
+  * `<spirv-dir>/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
+`<spirv-build-dir>/tools/spirv-dis`. The functionality of the disassembler is implemented
+by the `spvBinaryToText` library function.
+
+* `spirv-dis` - the standalone disassembler
+  * `<spirv-dir>/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
+  * `<spirv-dir>/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
+  * `<spirv-dir>/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
+`<spirv-build-dir>/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
+  * `<spirv-dir>/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
+  * `<spirv-dir>/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
+  * `<spirv-dir>/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
+<a name="future"></a>
+
+_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
+<a name="license"></a>
+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

+ 1 - 1
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"

+ 3 - 1
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

+ 368 - 0
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 <algorithm>
+#include <utility>
+
+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<uint32_t, uint32_t> 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<uint32_t> zero_bytes = {0};
+  std::vector<uint32_t> one_bytes = {one_as_uint};
+  auto constant_zero = ir_context->get_constant_mgr()->RegisterConstant(
+      MakeUnique<opt::analysis::FloatConstant>(float_type, zero_bytes));
+  auto constant_one = ir_context->get_constant_mgr()->RegisterConstant(
+      MakeUnique<opt::analysis::FloatConstant>(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<uint32_t, uint32_t>(constant_zero_id, constant_one_id);
+}
+
+std::unique_ptr<TransformationReplaceConstantWithUniform>
+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<TransformationReplaceConstantWithUniform>(
+      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<uint32_t>& binary_in,
+    const spvtools::fuzz::protobufs::FactSequence& initial_facts,
+    std::vector<uint32_t>* 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<opt::IRContext> 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<uint32_t, uint32_t> 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<opt::Instruction>(ir_context.get(), SpvOpLabel, 0,
+                                              new_exit_block_id,
+                                              opt::Instruction::OperandList());
+    auto new_exit_block = MakeUnique<opt::BasicBlock>(std::move(label));
+    new_exit_block->AddInstruction(MakeUnique<opt::Instruction>(
+        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<opt::Instruction>(ir_context.get(), SpvOpLabel, 0,
+                                              ir_context->TakeNextId(),
+                                              opt::Instruction::OperandList());
+    auto new_entry_block = MakeUnique<opt::BasicBlock>(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<opt::Instruction>(
+        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<opt::Instruction>(
+        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<TransformationReplaceConstantWithUniform>
+        first_greater_then_operand_replacement = nullptr;
+    std::unique_ptr<TransformationReplaceConstantWithUniform>
+        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<opt::Instruction>(
+              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<opt::analysis::BoolConstant>(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<opt::Instruction>(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

+ 48 - 0
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 <vector>
+
+#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<uint32_t>& binary_in,
+    const spvtools::fuzz::protobufs::FactSequence& initial_facts,
+    std::vector<uint32_t>* binary_out);
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FORCE_RENDER_RED_H_

+ 3 - 2
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<BasicBlock*>* order);
 

+ 13 - 5
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.

+ 7 - 0
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<std::unique_ptr<BasicBlock>>& 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<uint32_t> funcs_called_from_continue_;
 };
 
 }  // namespace opt

+ 19 - 5
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));

+ 1 - 6
3rdparty/spirv-tools/source/opt/ir_context.h

@@ -187,9 +187,6 @@ class IRContext {
   inline IteratorRange<Module::inst_iterator> debugs3();
   inline IteratorRange<Module::const_inst_iterator> 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<uint32_t>* todo);
 
   // Applies |pfn| to every function in the call trees that are rooted at the
@@ -928,8 +925,6 @@ IteratorRange<Module::const_inst_iterator> 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<Instruction> capability_inst(new Instruction(

+ 3 - 0
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

+ 3 - 0
3rdparty/spirv-tools/source/opt/module.cpp

@@ -121,6 +121,9 @@ void Module::ForEachInst(const std::function<void(const Instruction*)>& f,
     static_cast<const Function*>(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
 }
 

+ 17 - 16
3rdparty/spirv-tools/source/opt/module.h

@@ -192,22 +192,6 @@ class Module {
   inline IteratorRange<inst_iterator> execution_modes();
   inline IteratorRange<const_inst_iterator> 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<Instruction>&& dbg_line_info) {
+    trailing_dbg_line_info_ = std::move(dbg_line_info);
+  }
+
+  std::vector<Instruction>& trailing_dbg_line_info() {
+    return trailing_dbg_line_info_;
+  }
+
+  const std::vector<Instruction>& 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<std::unique_ptr<Function>> 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<Instruction> trailing_dbg_line_info_;
 };
 
 // Pretty-prints |module| to |str|. Returns |str|.

+ 22 - 1
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<Instruction*> 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;
 }
 

+ 65 - 0
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<uint32_t>
+StructuredCFGAnalysis::FindFuncsCalledFromContinue() {
+  std::unordered_set<uint32_t> called_from_continue;
+  std::queue<uint32_t> 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

+ 30 - 4
3rdparty/spirv-tools/source/opt/struct_cfg_analysis.h

@@ -16,6 +16,7 @@
 #define SOURCE_OPT_STRUCT_CFG_ANALYSIS_H_
 
 #include <unordered_map>
+#include <unordered_set>
 
 #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<uint32_t> 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

+ 5 - 2
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)) {

+ 5 - 1
3rdparty/spirv-tools/source/val/validate_constants.cpp

@@ -304,7 +304,6 @@ bool IsTypeNullable(const std::vector<uint32_t>& instruction,
     case SpvOpTypeBool:
     case SpvOpTypeInt:
     case SpvOpTypeFloat:
-    case SpvOpTypePointer:
     case SpvOpTypeEvent:
     case SpvOpTypeDeviceEvent:
     case SpvOpTypeReserveId:
@@ -325,6 +324,11 @@ bool IsTypeNullable(const std::vector<uint32_t>& instruction,
       }
       return true;
     }
+    case SpvOpTypePointer:
+      if (instruction[2] == SpvStorageClassPhysicalStorageBuffer) {
+        return false;
+      }
+      return true;
     default:
       return false;
   }

+ 0 - 113
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<SpvAddressingModel>(0));
     _.set_memory_model(inst->GetOperandAs<SpvMemoryModel>(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<SpvStorageClass>(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<uint32_t>(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;

+ 26 - 1
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<SpvStorageClass>(1u);
   if (_.addressing_model() == SpvAddressingModelLogical) {
-    SpvStorageClass sc = op1_type->GetOperandAs<SpvStorageClass>(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;

+ 34 - 0
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;
   }

+ 41 - 0
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;
   }

+ 17 - 0
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<uint32_t>(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;
 }
 

+ 47 - 8
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

+ 3 - 3
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&);
 

+ 55 - 1
3rdparty/spirv-tools/test/opt/inline_test.cpp

@@ -2936,7 +2936,7 @@ OpFunctionEnd
   SinglePassRunAndCheck<InlineExhaustivePass>(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<InlineExhaustivePass>(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<InlineExhaustivePass>(before, after, false, true);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    Empty modules

+ 17 - 16
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
 )";

+ 67 - 0
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<IRContext> context = BuildModule(text);
+  std::vector<uint32_t> 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

+ 78 - 0
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<StripDebugInfoPass>(JoinAllInsts(text),
@@ -74,6 +76,82 @@ TEST_F(StripLineDebugInfoTest, LineNoLine) {
                                             /* skip_nop = */ false);
 }
 
+using StripDebugStringTest = PassTest<::testing::Test>;
+
+TEST_F(StripDebugStringTest, OpDecorateRemoved) {
+  std::vector<const char*> 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<const char*> 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<StripDebugInfoPass>(JoinAllInsts(input),
+                                            JoinAllInsts(output),
+                                            /* skip_nop = */ false,
+                                            /* do_validation */ true);
+}
+
+TEST_F(StripDebugStringTest, OpNameRemoved) {
+  std::vector<const char*> 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<const char*> 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<StripDebugInfoPass>(JoinAllInsts(input),
+                                            JoinAllInsts(output),
+                                            /* skip_nop = */ false,
+                                            /* do_validation */ true);
+}
+
 using StripDebugInfoTest = PassTest<::testing::TestWithParam<const char*>>;
 
 TEST_P(StripDebugInfoTest, Kind) {

+ 533 - 1
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<IRContext> 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<IRContext> 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<IRContext> 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<IRContext> 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

+ 165 - 30
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<WrapOpKill>(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<WrapOpKill>(text, true);
+  EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools

+ 4 - 5
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) {

+ 20 - 0
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 <id> '1[%ptr]' cannot have "
+                        "a null value"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools

+ 90 - 0
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

+ 81 - 0
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

+ 7 - 4
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) {

+ 7 - 7
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

+ 0 - 17
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