mirror of
https://github.com/bkaradzic/bgfx.git
synced 2026-02-19 21:42:59 +01:00
Updated spirv-tools.
This commit is contained in:
4
3rdparty/spirv-tools/DEPS
vendored
4
3rdparty/spirv-tools/DEPS
vendored
@@ -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
3rdparty/spirv-tools/README.md
vendored
Normal file
620
3rdparty/spirv-tools/README.md
vendored
Normal file
@@ -0,0 +1,620 @@
|
||||
# SPIR-V Tools
|
||||
|
||||
## Overview
|
||||
|
||||
The SPIR-V Tools project provides an API and commands for processing SPIR-V
|
||||
modules.
|
||||
|
||||
The project includes an assembler, binary module parser, disassembler,
|
||||
validator, and optimizer for SPIR-V. Except for the optimizer, all are based
|
||||
on a common static library. The library contains all of the implementation
|
||||
details, and is used in the standalone tools whilst also enabling integration
|
||||
into other code bases directly. The optimizer implementation resides in its
|
||||
own library, which depends on the core library.
|
||||
|
||||
The interfaces have stabilized:
|
||||
We don't anticipate making a breaking change for existing features.
|
||||
|
||||
SPIR-V is defined by the Khronos Group Inc.
|
||||
See the [SPIR-V Registry][spirv-registry] for the SPIR-V specification,
|
||||
headers, and XML registry.
|
||||
|
||||
## Downloads
|
||||
|
||||
[](https://ci.appveyor.com/project/Khronoswebmaster/spirv-tools/branch/master)
|
||||
<img alt="Linux" src="kokoro/img/linux.png" width="20px" height="20px" hspace="2px"/>[](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"/>[](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"/>[](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_release.html)
|
||||
|
||||
[More downloads](docs/downloads.md)
|
||||
|
||||
## Versioning SPIRV-Tools
|
||||
|
||||
See [`CHANGES`](CHANGES) for a high level summary of recent changes, by version.
|
||||
|
||||
SPIRV-Tools project version numbers are of the form `v`*year*`.`*index* and with
|
||||
an optional `-dev` suffix to indicate work in progress. For exampe, the
|
||||
following versions are ordered from oldest to newest:
|
||||
|
||||
* `v2016.0`
|
||||
* `v2016.1-dev`
|
||||
* `v2016.1`
|
||||
* `v2016.2-dev`
|
||||
* `v2016.2`
|
||||
|
||||
Use the `--version` option on each command line tool to see the software
|
||||
version. An API call reports the software version as a C-style string.
|
||||
|
||||
## Supported features
|
||||
|
||||
### Assembler, binary parser, and disassembler
|
||||
|
||||
* Support for SPIR-V 1.0, 1.1, 1.2, and 1.3
|
||||
* Based on SPIR-V syntax described by JSON grammar files in the
|
||||
[SPIRV-Headers](https://github.com/KhronosGroup/SPIRV-Headers) repository.
|
||||
* Support for extended instruction sets:
|
||||
* GLSL std450 version 1.0 Rev 3
|
||||
* OpenCL version 1.0 Rev 2
|
||||
* Assembler only does basic syntax checking. No cross validation of
|
||||
IDs or types is performed, except to check literal arguments to
|
||||
`OpConstant`, `OpSpecConstant`, and `OpSwitch`.
|
||||
|
||||
See [`docs/syntax.md`](docs/syntax.md) for the assembly language syntax.
|
||||
|
||||
### Validator
|
||||
|
||||
The validator checks validation rules described by the SPIR-V specification.
|
||||
|
||||
Khronos recommends that tools that create or transform SPIR-V modules use the
|
||||
validator to ensure their outputs are valid, and that tools that consume SPIR-V
|
||||
modules optionally use the validator to protect themselves from bad inputs.
|
||||
This is especially encouraged for debug and development scenarios.
|
||||
|
||||
The validator has one-sided error: it will only return an error when it has
|
||||
implemented a rule check and the module violates that rule.
|
||||
|
||||
The validator is incomplete.
|
||||
See the [CHANGES](CHANGES) file for reports on completed work, and
|
||||
the [Validator
|
||||
sub-project](https://github.com/KhronosGroup/SPIRV-Tools/projects/1) for planned
|
||||
and in-progress work.
|
||||
|
||||
*Note*: The validator checks some Universal Limits, from section 2.17 of the SPIR-V spec.
|
||||
The validator will fail on a module that exceeds those minimum upper bound limits.
|
||||
It is [future work](https://github.com/KhronosGroup/SPIRV-Tools/projects/1#card-1052403)
|
||||
to parameterize the validator to allow larger
|
||||
limits accepted by a more than minimally capable SPIR-V consumer.
|
||||
|
||||
|
||||
### Optimizer
|
||||
|
||||
*Note:* The optimizer is still under development.
|
||||
|
||||
Currently supported optimizations:
|
||||
* General
|
||||
* Strip debug info
|
||||
* Specialization Constants
|
||||
* Set spec constant default value
|
||||
* Freeze spec constant
|
||||
* Fold `OpSpecConstantOp` and `OpSpecConstantComposite`
|
||||
* Unify constants
|
||||
* Eliminate dead constant
|
||||
* Code Reduction
|
||||
* Inline all function calls exhaustively
|
||||
* Convert local access chains to inserts/extracts
|
||||
* Eliminate local load/store in single block
|
||||
* Eliminate local load/store with single store
|
||||
* Eliminate local load/store with multiple stores
|
||||
* Eliminate local extract from insert
|
||||
* Eliminate dead instructions (aggressive)
|
||||
* Eliminate dead branches
|
||||
* Merge single successor / single predecessor block pairs
|
||||
* Eliminate common uniform loads
|
||||
* Remove duplicates: Capabilities, extended instruction imports, types, and
|
||||
decorations.
|
||||
|
||||
For the latest list with detailed documentation, please refer to
|
||||
[`include/spirv-tools/optimizer.hpp`](include/spirv-tools/optimizer.hpp).
|
||||
|
||||
For suggestions on using the code reduction options, please refer to this [white paper](https://www.lunarg.com/shader-compiler-technologies/white-paper-spirv-opt/).
|
||||
|
||||
|
||||
### Linker
|
||||
|
||||
*Note:* The linker is still under development.
|
||||
|
||||
Current features:
|
||||
* Combine multiple SPIR-V binary modules together.
|
||||
* Combine into a library (exports are retained) or an executable (no symbols
|
||||
are exported).
|
||||
|
||||
See the [CHANGES](CHANGES) file for reports on completed work, and the [General
|
||||
sub-project](https://github.com/KhronosGroup/SPIRV-Tools/projects/2) for
|
||||
planned and in-progress work.
|
||||
|
||||
|
||||
### Reducer
|
||||
|
||||
*Note:* The reducer is still under development.
|
||||
|
||||
The reducer simplifies and shrinks a SPIR-V module with respect to a
|
||||
user-supplied *interestingness function*. For example, given a large
|
||||
SPIR-V module that cause some SPIR-V compiler to fail with a given
|
||||
fatal error message, the reducer could be used to look for a smaller
|
||||
version of the module that causes the compiler to fail with the same
|
||||
fatal error message.
|
||||
|
||||
To suggest an additional capability for the reducer, [file an
|
||||
issue](https://github.com/KhronosGroup/SPIRV-Tools/issues]) with
|
||||
"Reducer:" as the start of its title.
|
||||
|
||||
|
||||
### Extras
|
||||
|
||||
* [Utility filters](#utility-filters)
|
||||
* Build target `spirv-tools-vimsyntax` generates file `spvasm.vim`.
|
||||
Copy that file into your `$HOME/.vim/syntax` directory to get SPIR-V assembly syntax
|
||||
highlighting in Vim. This build target is not built by default.
|
||||
|
||||
## Contributing
|
||||
|
||||
The SPIR-V Tools project is maintained by members of the The Khronos Group Inc.,
|
||||
and is hosted at https://github.com/KhronosGroup/SPIRV-Tools.
|
||||
|
||||
Consider joining the `public_spirv_tools_dev@khronos.org` mailing list, via
|
||||
[https://www.khronos.org/spir/spirv-tools-mailing-list/](https://www.khronos.org/spir/spirv-tools-mailing-list/).
|
||||
The mailing list is used to discuss development plans for the SPIRV-Tools as an open source project.
|
||||
Once discussion is resolved,
|
||||
specific work is tracked via issues and sometimes in one of the
|
||||
[projects][spirv-tools-projects].
|
||||
|
||||
(To provide feedback on the SPIR-V _specification_, file an issue on the
|
||||
[SPIRV-Headers][spirv-headers] GitHub repository.)
|
||||
|
||||
See [`docs/projects.md`](docs/projects.md) to see how we use the
|
||||
[GitHub Project
|
||||
feature](https://help.github.com/articles/tracking-the-progress-of-your-work-with-projects/)
|
||||
to organize planned and in-progress work.
|
||||
|
||||
Contributions via merge request are welcome. Changes should:
|
||||
* Be provided under the [Apache 2.0](#license).
|
||||
* You'll be prompted with a one-time "click-through"
|
||||
[Khronos Open Source Contributor License Agreement][spirv-tools-cla]
|
||||
(CLA) dialog as part of submitting your pull request or
|
||||
other contribution to GitHub.
|
||||
* Include tests to cover updated functionality.
|
||||
* C++ code should follow the [Google C++ Style Guide][cpp-style-guide].
|
||||
* Code should be formatted with `clang-format`.
|
||||
[kokoro/check-format/build.sh](kokoro/check-format/build.sh)
|
||||
shows how to download it. Note that we currently use
|
||||
`clang-format version 5.0.0` for SPIRV-Tools. Settings are defined by
|
||||
the included [.clang-format](.clang-format) file.
|
||||
|
||||
We intend to maintain a linear history on the GitHub `master` branch.
|
||||
|
||||
### Source code organization
|
||||
|
||||
* `example`: demo code of using SPIRV-Tools APIs
|
||||
* `external/googletest`: Intended location for the
|
||||
[googletest][googletest] sources, not provided
|
||||
* `external/effcee`: Location of [Effcee][effcee] sources, if the `effcee` library
|
||||
is not already configured by an enclosing project.
|
||||
* `external/re2`: Location of [RE2][re2] sources, if the `re2` library is not already
|
||||
configured by an enclosing project.
|
||||
(The Effcee project already requires RE2.)
|
||||
* `include/`: API clients should add this directory to the include search path
|
||||
* `external/spirv-headers`: Intended location for
|
||||
[SPIR-V headers][spirv-headers], not provided
|
||||
* `include/spirv-tools/libspirv.h`: C API public interface
|
||||
* `source/`: API implementation
|
||||
* `test/`: Tests, using the [googletest][googletest] framework
|
||||
* `tools/`: Command line executables
|
||||
|
||||
Example of getting sources, assuming SPIRV-Tools is configured as a standalone project:
|
||||
|
||||
git clone https://github.com/KhronosGroup/SPIRV-Tools.git spirv-tools
|
||||
git clone https://github.com/KhronosGroup/SPIRV-Headers.git spirv-tools/external/spirv-headers
|
||||
git clone https://github.com/google/googletest.git spirv-tools/external/googletest
|
||||
git clone https://github.com/google/effcee.git spirv-tools/external/effcee
|
||||
git clone https://github.com/google/re2.git spirv-tools/external/re2
|
||||
|
||||
### Tests
|
||||
|
||||
The project contains a number of tests, used to drive development
|
||||
and ensure correctness. The tests are written using the
|
||||
[googletest][googletest] framework. The `googletest`
|
||||
source is not provided with this project. There are two ways to enable
|
||||
tests:
|
||||
* If SPIR-V Tools is configured as part of an enclosing project, then the
|
||||
enclosing project should configure `googletest` before configuring SPIR-V Tools.
|
||||
* If SPIR-V Tools is configured as a standalone project, then download the
|
||||
`googletest` source into the `<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 @@
|
||||
"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-89-g84b19760"
|
||||
"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-102-gc18c9ff6"
|
||||
|
||||
@@ -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
3rdparty/spirv-tools/source/fuzz/force_render_red.cpp
vendored
Normal file
368
3rdparty/spirv-tools/source/fuzz/force_render_red.cpp
vendored
Normal file
@@ -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
3rdparty/spirv-tools/source/fuzz/force_render_red.h
vendored
Normal file
48
3rdparty/spirv-tools/source/fuzz/force_render_red.h
vendored
Normal file
@@ -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_
|
||||
5
3rdparty/spirv-tools/source/opt/cfg.h
vendored
5
3rdparty/spirv-tools/source/opt/cfg.h
vendored
@@ -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);
|
||||
|
||||
|
||||
18
3rdparty/spirv-tools/source/opt/inline_pass.cpp
vendored
18
3rdparty/spirv-tools/source/opt/inline_pass.cpp
vendored
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
7
3rdparty/spirv-tools/source/opt/ir_context.h
vendored
7
3rdparty/spirv-tools/source/opt/ir_context.h
vendored
@@ -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(
|
||||
|
||||
@@ -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
3rdparty/spirv-tools/source/opt/module.cpp
vendored
3
3rdparty/spirv-tools/source/opt/module.cpp
vendored
@@ -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
|
||||
}
|
||||
|
||||
|
||||
33
3rdparty/spirv-tools/source/opt/module.h
vendored
33
3rdparty/spirv-tools/source/opt/module.h
vendored
@@ -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,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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&);
|
||||
|
||||
|
||||
56
3rdparty/spirv-tools/test/opt/inline_test.cpp
vendored
56
3rdparty/spirv-tools/test/opt/inline_test.cpp
vendored
@@ -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
|
||||
|
||||
@@ -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
3rdparty/spirv-tools/test/opt/module_test.cpp
vendored
67
3rdparty/spirv-tools/test/opt/module_test.cpp
vendored
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
195
3rdparty/spirv-tools/test/opt/wrap_opkill_test.cpp
vendored
195
3rdparty/spirv-tools/test/opt/wrap_opkill_test.cpp
vendored
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
3rdparty/spirv-tools/test/val/val_misc_test.cpp
vendored
81
3rdparty/spirv-tools/test/val/val_misc_test.cpp
vendored
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user