mirror of
https://github.com/bkaradzic/bgfx.git
synced 2026-02-17 20:52:36 +01:00
Updated spirv-tools.
This commit is contained in:
5
3rdparty/spirv-tools/.gitignore
vendored
5
3rdparty/spirv-tools/.gitignore
vendored
@@ -16,6 +16,11 @@ compile_commands.json
|
||||
/testing
|
||||
/tools/clang/
|
||||
/utils/clang-format-diff.py
|
||||
bazel-bin
|
||||
bazel-genfiles
|
||||
bazel-out
|
||||
bazel-spirv-tools
|
||||
bazel-testlogs
|
||||
|
||||
# Vim
|
||||
[._]*.s[a-w][a-z]
|
||||
|
||||
1
3rdparty/spirv-tools/Android.mk
vendored
1
3rdparty/spirv-tools/Android.mk
vendored
@@ -49,7 +49,6 @@ SPVTOOLS_SRC_FILES := \
|
||||
source/val/validate_composites.cpp \
|
||||
source/val/validate_constants.cpp \
|
||||
source/val/validate_conversion.cpp \
|
||||
source/val/validate_datarules.cpp \
|
||||
source/val/validate_debug.cpp \
|
||||
source/val/validate_decorations.cpp \
|
||||
source/val/validate_derivatives.cpp \
|
||||
|
||||
2
3rdparty/spirv-tools/BUILD.gn
vendored
2
3rdparty/spirv-tools/BUILD.gn
vendored
@@ -410,7 +410,6 @@ static_library("spvtools_val") {
|
||||
"source/val/validate_composites.cpp",
|
||||
"source/val/validate_constants.cpp",
|
||||
"source/val/validate_conversion.cpp",
|
||||
"source/val/validate_datarules.cpp",
|
||||
"source/val/validate_debug.cpp",
|
||||
"source/val/validate_decorations.cpp",
|
||||
"source/val/validate_derivatives.cpp",
|
||||
@@ -877,6 +876,7 @@ source_set("spvtools_util_cli_consumer") {
|
||||
"tools/util/cli_consumer.cpp",
|
||||
"tools/util/cli_consumer.h",
|
||||
]
|
||||
deps = [ ":spvtools_headers" ]
|
||||
configs += [ ":spvtools_internal_config" ]
|
||||
}
|
||||
|
||||
|
||||
313
3rdparty/spirv-tools/CMakeLists.txt
vendored
313
3rdparty/spirv-tools/CMakeLists.txt
vendored
@@ -1,313 +0,0 @@
|
||||
# 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.
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
if (POLICY CMP0048)
|
||||
cmake_policy(SET CMP0048 NEW)
|
||||
endif()
|
||||
if (POLICY CMP0054)
|
||||
# Avoid dereferencing variables or interpret keywords that have been
|
||||
# quoted or bracketed.
|
||||
# https://cmake.org/cmake/help/v3.1/policy/CMP0054.html
|
||||
cmake_policy(SET CMP0054 NEW)
|
||||
endif()
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
||||
project(spirv-tools)
|
||||
enable_testing()
|
||||
set(SPIRV_TOOLS "SPIRV-Tools")
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include(cmake/setup_build.cmake)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
option(SPIRV_ALLOW_TIMERS "Allow timers via clock_gettime on supported platforms" ON)
|
||||
|
||||
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
|
||||
add_definitions(-DSPIRV_LINUX)
|
||||
set(SPIRV_TIMER_ENABLED ${SPIRV_ALLOW_TIMERS})
|
||||
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
|
||||
add_definitions(-DSPIRV_WINDOWS)
|
||||
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "CYGWIN")
|
||||
add_definitions(-DSPIRV_WINDOWS)
|
||||
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
|
||||
add_definitions(-DSPIRV_MAC)
|
||||
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Android")
|
||||
add_definitions(-DSPIRV_ANDROID)
|
||||
set(SPIRV_TIMER_ENABLED ${SPIRV_ALLOW_TIMERS})
|
||||
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD")
|
||||
add_definitions(-DSPIRV_FREEBSD)
|
||||
else()
|
||||
message(FATAL_ERROR "Your platform '${CMAKE_SYSTEM_NAME}' is not supported!")
|
||||
endif()
|
||||
|
||||
if (${SPIRV_TIMER_ENABLED})
|
||||
add_definitions(-DSPIRV_TIMER_ENABLED)
|
||||
endif()
|
||||
|
||||
if ("${CMAKE_BUILD_TYPE}" STREQUAL "")
|
||||
message(STATUS "No build type selected, default to Debug")
|
||||
set(CMAKE_BUILD_TYPE "Debug")
|
||||
endif()
|
||||
|
||||
option(SKIP_SPIRV_TOOLS_INSTALL "Skip installation" ${SKIP_SPIRV_TOOLS_INSTALL})
|
||||
if(NOT ${SKIP_SPIRV_TOOLS_INSTALL})
|
||||
set(ENABLE_SPIRV_TOOLS_INSTALL ON)
|
||||
endif()
|
||||
|
||||
option(SPIRV_BUILD_COMPRESSION "Build SPIR-V compressing codec" OFF)
|
||||
if(SPIRV_BUILD_COMPRESSION)
|
||||
message(FATAL_ERROR "SPIR-V compression codec has been removed from SPIR-V tools. "
|
||||
"Please remove SPIRV_BUILD_COMPRESSION from your build options.")
|
||||
endif(SPIRV_BUILD_COMPRESSION)
|
||||
|
||||
option(SPIRV_WERROR "Enable error on warning" ON)
|
||||
if(("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR (("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") AND (NOT CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")))
|
||||
set(COMPILER_IS_LIKE_GNU TRUE)
|
||||
endif()
|
||||
if(${COMPILER_IS_LIKE_GNU})
|
||||
set(SPIRV_WARNINGS -Wall -Wextra -Wnon-virtual-dtor -Wno-missing-field-initializers)
|
||||
|
||||
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||
set(SPIRV_WARNINGS ${SPIRV_WARNINGS} -Wno-self-assign)
|
||||
endif()
|
||||
|
||||
option(SPIRV_WARN_EVERYTHING "Enable -Weverything" ${SPIRV_WARN_EVERYTHING})
|
||||
if(${SPIRV_WARN_EVERYTHING})
|
||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
set(SPIRV_WARNINGS ${SPIRV_WARNINGS}
|
||||
-Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded)
|
||||
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
set(SPIRV_WARNINGS ${SPIRV_WARNINGS} -Wpedantic -pedantic-errors)
|
||||
else()
|
||||
message(STATUS "Unknown compiler ${CMAKE_CXX_COMPILER_ID}, "
|
||||
"so SPIRV_WARN_EVERYTHING has no effect")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(${SPIRV_WERROR})
|
||||
set(SPIRV_WARNINGS ${SPIRV_WARNINGS} -Werror)
|
||||
endif()
|
||||
elseif(MSVC)
|
||||
set(SPIRV_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS /wd4800)
|
||||
|
||||
if(${SPIRV_WERROR})
|
||||
set(SPIRV_WARNINGS ${SPIRV_WARNINGS} /WX)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/)
|
||||
|
||||
option(SPIRV_COLOR_TERMINAL "Enable color terminal output" ON)
|
||||
if(${SPIRV_COLOR_TERMINAL})
|
||||
add_definitions(-DSPIRV_COLOR_TERMINAL)
|
||||
endif()
|
||||
|
||||
option(SPIRV_LOG_DEBUG "Enable excessive debug output" OFF)
|
||||
if(${SPIRV_LOG_DEBUG})
|
||||
add_definitions(-DSPIRV_LOG_DEBUG)
|
||||
endif()
|
||||
|
||||
if (DEFINED SPIRV_TOOLS_EXTRA_DEFINITIONS)
|
||||
add_definitions(${SPIRV_TOOLS_EXTRA_DEFINITIONS})
|
||||
endif()
|
||||
|
||||
function(spvtools_default_compile_options TARGET)
|
||||
target_compile_options(${TARGET} PRIVATE ${SPIRV_WARNINGS})
|
||||
|
||||
if (${COMPILER_IS_LIKE_GNU})
|
||||
target_compile_options(${TARGET} PRIVATE
|
||||
-std=c++11 -fno-exceptions -fno-rtti)
|
||||
target_compile_options(${TARGET} PRIVATE
|
||||
-Wall -Wextra -Wno-long-long -Wshadow -Wundef -Wconversion
|
||||
-Wno-sign-conversion)
|
||||
# For good call stacks in profiles, keep the frame pointers.
|
||||
if(NOT "${SPIRV_PERF}" STREQUAL "")
|
||||
target_compile_options(${TARGET} PRIVATE -fno-omit-frame-pointer)
|
||||
endif()
|
||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
set(SPIRV_USE_SANITIZER "" CACHE STRING
|
||||
"Use the clang sanitizer [address|memory|thread|...]")
|
||||
if(NOT "${SPIRV_USE_SANITIZER}" STREQUAL "")
|
||||
target_compile_options(${TARGET} PRIVATE
|
||||
-fsanitize=${SPIRV_USE_SANITIZER})
|
||||
set_target_properties(${TARGET} PROPERTIES
|
||||
LINK_FLAGS -fsanitize=${SPIRV_USE_SANITIZER})
|
||||
endif()
|
||||
target_compile_options(${TARGET} PRIVATE
|
||||
-ftemplate-depth=1024)
|
||||
else()
|
||||
target_compile_options(${TARGET} PRIVATE
|
||||
-Wno-missing-field-initializers)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
# Specify /EHs for exception handling. This makes using SPIRV-Tools as
|
||||
# dependencies in other projects easier.
|
||||
target_compile_options(${TARGET} PRIVATE /EHs)
|
||||
endif()
|
||||
|
||||
# For MinGW cross compile, statically link to the C++ runtime.
|
||||
# But it still depends on MSVCRT.dll.
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
if (${CMAKE_CXX_COMPILER_ID} MATCHES "GNU")
|
||||
set_target_properties(${TARGET} PROPERTIES
|
||||
LINK_FLAGS -static -static-libgcc -static-libstdc++)
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
if(NOT COMMAND find_host_package)
|
||||
macro(find_host_package)
|
||||
find_package(${ARGN})
|
||||
endmacro()
|
||||
endif()
|
||||
if(NOT COMMAND find_host_program)
|
||||
macro(find_host_program)
|
||||
find_program(${ARGN})
|
||||
endmacro()
|
||||
endif()
|
||||
|
||||
# Tests require Python3
|
||||
find_host_package(PythonInterp 3 REQUIRED)
|
||||
|
||||
# Check for symbol exports on Linux.
|
||||
# At the moment, this check will fail on the OSX build machines for the Android NDK.
|
||||
# It appears they don't have objdump.
|
||||
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
|
||||
macro(spvtools_check_symbol_exports TARGET)
|
||||
if (NOT "${SPIRV_SKIP_TESTS}")
|
||||
add_test(NAME spirv-tools-symbol-exports-${TARGET}
|
||||
COMMAND ${PYTHON_EXECUTABLE}
|
||||
${spirv-tools_SOURCE_DIR}/utils/check_symbol_exports.py "$<TARGET_FILE:${TARGET}>")
|
||||
endif()
|
||||
endmacro()
|
||||
else()
|
||||
macro(spvtools_check_symbol_exports TARGET)
|
||||
if (NOT "${SPIRV_SKIP_TESTS}")
|
||||
message("Skipping symbol exports test for ${TARGET}")
|
||||
endif()
|
||||
endmacro()
|
||||
endif()
|
||||
|
||||
# Defaults to OFF if the user didn't set it.
|
||||
option(SPIRV_SKIP_EXECUTABLES
|
||||
"Skip building the executable and tests along with the library"
|
||||
${SPIRV_SKIP_EXECUTABLES})
|
||||
option(SPIRV_SKIP_TESTS
|
||||
"Skip building tests along with the library" ${SPIRV_SKIP_TESTS})
|
||||
if ("${SPIRV_SKIP_EXECUTABLES}")
|
||||
set(SPIRV_SKIP_TESTS ON)
|
||||
endif()
|
||||
|
||||
# Defaults to ON. The checks can be time consuming.
|
||||
# Turn off if they take too long.
|
||||
option(SPIRV_CHECK_CONTEXT "In a debug build, check if the IR context is in a valid state." ON)
|
||||
if (${SPIRV_CHECK_CONTEXT})
|
||||
add_definitions(-DSPIRV_CHECK_CONTEXT)
|
||||
endif()
|
||||
|
||||
# Precompiled header macro. Parameters are source file list and filename for pch cpp file.
|
||||
macro(spvtools_pch SRCS PCHPREFIX)
|
||||
if(MSVC AND CMAKE_GENERATOR MATCHES "^Visual Studio")
|
||||
set(PCH_NAME "$(IntDir)\\${PCHPREFIX}.pch")
|
||||
# make source files use/depend on PCH_NAME
|
||||
set_source_files_properties(${${SRCS}} PROPERTIES COMPILE_FLAGS "/Yu${PCHPREFIX}.h /FI${PCHPREFIX}.h /Fp${PCH_NAME} /Zm300" OBJECT_DEPENDS "${PCH_NAME}")
|
||||
# make PCHPREFIX.cpp file compile and generate PCH_NAME
|
||||
set_source_files_properties("${PCHPREFIX}.cpp" PROPERTIES COMPILE_FLAGS "/Yc${PCHPREFIX}.h /Fp${PCH_NAME} /Zm300" OBJECT_OUTPUTS "${PCH_NAME}")
|
||||
list(APPEND ${SRCS} "${PCHPREFIX}.cpp")
|
||||
endif()
|
||||
endmacro(spvtools_pch)
|
||||
|
||||
add_subdirectory(external)
|
||||
|
||||
# Warning about extra semi-colons.
|
||||
#
|
||||
# This is not supported on all compilers/versions. so enabling only
|
||||
# for clang, since that works for all versions that our bots run.
|
||||
#
|
||||
# This is intentionally done after adding the external subdirectory,
|
||||
# so we don't enforce this flag on our dependencies, some of which do
|
||||
# not pass it.
|
||||
#
|
||||
# If the minimum version of CMake supported is updated to 3.0 or
|
||||
# later, then check_cxx_compiler_flag could be used instead.
|
||||
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||
add_compile_options("-Wextra-semi")
|
||||
endif()
|
||||
|
||||
add_subdirectory(source)
|
||||
add_subdirectory(tools)
|
||||
|
||||
add_subdirectory(test)
|
||||
add_subdirectory(examples)
|
||||
|
||||
if(ENABLE_SPIRV_TOOLS_INSTALL)
|
||||
install(
|
||||
FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/spirv-tools/libspirv.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/spirv-tools/libspirv.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/spirv-tools/optimizer.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/spirv-tools/linker.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/spirv-tools/instrument.hpp
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_INCLUDEDIR}/spirv-tools/)
|
||||
endif(ENABLE_SPIRV_TOOLS_INSTALL)
|
||||
|
||||
if (NOT "${SPIRV_SKIP_TESTS}")
|
||||
add_test(NAME spirv-tools-copyrights
|
||||
COMMAND ${PYTHON_EXECUTABLE} utils/check_copyright.py
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
set(SPIRV_LIBRARIES "-lSPIRV-Tools -lSPIRV-Tools-link -lSPIRV-Tools-opt")
|
||||
set(SPIRV_SHARED_LIBRARIES "-lSPIRV-Tools-shared")
|
||||
|
||||
# Build pkg-config file
|
||||
# Use a first-class target so it's regenerated when relevant files are updated.
|
||||
add_custom_target(spirv-tools-pkg-config ALL
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DCHANGES_FILE=${CMAKE_CURRENT_SOURCE_DIR}/CHANGES
|
||||
-DTEMPLATE_FILE=${CMAKE_CURRENT_SOURCE_DIR}/cmake/SPIRV-Tools.pc.in
|
||||
-DOUT_FILE=${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools.pc
|
||||
-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}
|
||||
-DCMAKE_INSTALL_LIBDIR=${CMAKE_INSTALL_LIBDIR}
|
||||
-DCMAKE_INSTALL_INCLUDEDIR=${CMAKE_INSTALL_INCLUDEDIR}
|
||||
-DSPIRV_LIBRARIES=${SPIRV_LIBRARIES}
|
||||
-P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake
|
||||
DEPENDS "CHANGES" "cmake/SPIRV-Tools.pc.in" "cmake/write_pkg_config.cmake")
|
||||
add_custom_target(spirv-tools-shared-pkg-config ALL
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DCHANGES_FILE=${CMAKE_CURRENT_SOURCE_DIR}/CHANGES
|
||||
-DTEMPLATE_FILE=${CMAKE_CURRENT_SOURCE_DIR}/cmake/SPIRV-Tools-shared.pc.in
|
||||
-DOUT_FILE=${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools-shared.pc
|
||||
-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}
|
||||
-DCMAKE_INSTALL_LIBDIR=${CMAKE_INSTALL_LIBDIR}
|
||||
-DCMAKE_INSTALL_INCLUDEDIR=${CMAKE_INSTALL_INCLUDEDIR}
|
||||
-DSPIRV_SHARED_LIBRARIES=${SPIRV_SHARED_LIBRARIES}
|
||||
-P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake
|
||||
DEPENDS "CHANGES" "cmake/SPIRV-Tools-shared.pc.in" "cmake/write_pkg_config.cmake")
|
||||
|
||||
# Install pkg-config file
|
||||
if (ENABLE_SPIRV_TOOLS_INSTALL)
|
||||
install(
|
||||
FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools.pc
|
||||
${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools-shared.pc
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
endif()
|
||||
1
3rdparty/spirv-tools/CODE_OF_CONDUCT.md
vendored
1
3rdparty/spirv-tools/CODE_OF_CONDUCT.md
vendored
@@ -1 +0,0 @@
|
||||
A reminder that this issue tracker is managed by the Khronos Group. Interactions here should follow the Khronos Code of Conduct (https://www.khronos.org/developers/code-of-conduct), which prohibits aggressive or derogatory language. Please keep the discussion friendly and civil.
|
||||
6
3rdparty/spirv-tools/DEPS
vendored
6
3rdparty/spirv-tools/DEPS
vendored
@@ -3,9 +3,9 @@ use_relative_paths = True
|
||||
vars = {
|
||||
'github': 'https://github.com',
|
||||
|
||||
'effcee_revision': 'b83b58d177b797edd1f94c5f10837f2cc2863f0a',
|
||||
'googletest_revision': '2f42d769ad1b08742f7ccb5ad4dd357fc5ff248c',
|
||||
're2_revision': 'e356bd3f80e0c15c1050323bb5a2d0f8ea4845f4',
|
||||
'effcee_revision': '6fa2a03cebb4fb18fbad086d53d1054928bef54e',
|
||||
'googletest_revision': 'f2fb48c3b3d79a75a88a99fba6576b25d42ec528',
|
||||
're2_revision': '5bd613749fd530b576b890283bfb6bc6ea6246cb',
|
||||
'spirv_headers_revision': '601d738723ac381741311c6c98c36d6170be14a2',
|
||||
}
|
||||
|
||||
|
||||
602
3rdparty/spirv-tools/README.md
vendored
602
3rdparty/spirv-tools/README.md
vendored
@@ -1,602 +0,0 @@
|
||||
# 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.
|
||||
|
||||
The project uses [CMake][cmake] to generate platform-specific build
|
||||
configurations. 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
|
||||
|
||||
mkdir build && cd build
|
||||
cmake [-G <platform-generator>] <spirv-dir>
|
||||
```
|
||||
|
||||
*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.
|
||||
|
||||
Once the build files have been generated, build using your preferred
|
||||
development environment.
|
||||
|
||||
### 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/): for generating compilation targets. Version
|
||||
2.8.12 or later.
|
||||
- [Python 3](http://www.python.org/): for utility scripts and running the test
|
||||
suite.
|
||||
|
||||
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,12 +0,0 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
|
||||
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
||||
|
||||
Name: SPIRV-Tools
|
||||
Description: Tools for SPIR-V
|
||||
Version: @CURRENT_VERSION@
|
||||
URL: https://github.com/KhronosGroup/SPIRV-Tools
|
||||
|
||||
Libs: -L${libdir} @SPIRV_SHARED_LIBRARIES@
|
||||
Cflags: -I${includedir}
|
||||
12
3rdparty/spirv-tools/cmake/SPIRV-Tools.pc.in
vendored
12
3rdparty/spirv-tools/cmake/SPIRV-Tools.pc.in
vendored
@@ -1,12 +0,0 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
|
||||
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
||||
|
||||
Name: SPIRV-Tools
|
||||
Description: Tools for SPIR-V
|
||||
Version: @CURRENT_VERSION@
|
||||
URL: https://github.com/KhronosGroup/SPIRV-Tools
|
||||
|
||||
Libs: -L${libdir} @SPIRV_LIBRARIES@
|
||||
Cflags: -I${includedir}
|
||||
20
3rdparty/spirv-tools/cmake/setup_build.cmake
vendored
20
3rdparty/spirv-tools/cmake/setup_build.cmake
vendored
@@ -1,20 +0,0 @@
|
||||
# Find nosetests; see spirv_add_nosetests() for opting in to nosetests in a
|
||||
# specific directory.
|
||||
find_program(NOSETESTS_EXE NAMES nosetests PATHS $ENV{PYTHON_PACKAGE_PATH})
|
||||
if (NOT NOSETESTS_EXE)
|
||||
message(STATUS "SPIRV-Tools: nosetests was not found - python support code will not be tested")
|
||||
else()
|
||||
message(STATUS "SPIRV-Tools: nosetests found - python support code will be tested")
|
||||
endif()
|
||||
|
||||
# Run nosetests on file ${PREFIX}_nosetest.py. Nosetests will look for classes
|
||||
# and functions whose names start with "nosetest". The test name will be
|
||||
# ${PREFIX}_nosetests.
|
||||
function(spirv_add_nosetests PREFIX)
|
||||
if(NOT "${SPIRV_SKIP_TESTS}" AND NOSETESTS_EXE)
|
||||
add_test(
|
||||
NAME ${PREFIX}_nosetests
|
||||
COMMAND ${NOSETESTS_EXE} -m "^[Nn]ose[Tt]est" -v
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/${PREFIX}_nosetest.py)
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -1,31 +0,0 @@
|
||||
# Copyright (c) 2017 Pierre Moreau
|
||||
#
|
||||
# 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.
|
||||
|
||||
# First, retrieve the current version from CHANGES
|
||||
file(STRINGS ${CHANGES_FILE} CHANGES_CONTENT)
|
||||
string(
|
||||
REGEX
|
||||
MATCH "v[0-9]+(.[0-9]+)?(-dev)? [0-9]+-[0-9]+-[0-9]+"
|
||||
FIRST_VERSION_LINE
|
||||
${CHANGES_CONTENT})
|
||||
string(
|
||||
REGEX
|
||||
REPLACE "^v([^ ]+) .+$" "\\1"
|
||||
CURRENT_VERSION
|
||||
"${FIRST_VERSION_LINE}")
|
||||
# If this is a development version, replace "-dev" by ".0" as pkg-config nor
|
||||
# CMake support "-dev" in the version.
|
||||
# If it's not a "-dev" version then ensure it ends with ".1"
|
||||
string(REGEX REPLACE "-dev.1" ".0" CURRENT_VERSION "${CURRENT_VERSION}.1")
|
||||
configure_file(${TEMPLATE_FILE} ${OUT_FILE} @ONLY)
|
||||
@@ -1 +1 @@
|
||||
"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-71-g6b072126"
|
||||
"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-89-g84b19760"
|
||||
|
||||
1
3rdparty/spirv-tools/source/CMakeLists.txt
vendored
1
3rdparty/spirv-tools/source/CMakeLists.txt
vendored
@@ -283,7 +283,6 @@ set(SPIRV_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_composites.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_constants.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_conversion.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_datarules.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_debug.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_decorations.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_derivatives.cpp
|
||||
|
||||
73
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
vendored
73
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
vendored
@@ -15,6 +15,7 @@
|
||||
#include "source/fuzz/fuzzer.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include "source/fuzz/fact_manager.h"
|
||||
@@ -38,8 +39,25 @@ namespace fuzz {
|
||||
|
||||
namespace {
|
||||
const uint32_t kIdBoundGap = 100;
|
||||
|
||||
const uint32_t kTransformationLimit = 500;
|
||||
|
||||
const uint32_t kChanceOfApplyingAnotherPass = 85;
|
||||
|
||||
template <typename T>
|
||||
void MaybeAddPass(
|
||||
std::vector<std::unique_ptr<FuzzerPass>>* passes,
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformation_sequence_out) {
|
||||
if (fuzzer_context->ChooseEven()) {
|
||||
passes->push_back(MakeUnique<T>(ir_context, fact_manager, fuzzer_context,
|
||||
transformation_sequence_out));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
struct Fuzzer::Impl {
|
||||
explicit Impl(spv_target_env env) : target_env(env) {}
|
||||
|
||||
@@ -109,29 +127,40 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
.Apply();
|
||||
|
||||
// Apply some semantics-preserving passes.
|
||||
FuzzerPassCopyObjects(ir_context.get(), &fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out)
|
||||
.Apply();
|
||||
FuzzerPassApplyIdSynonyms(ir_context.get(), &fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out)
|
||||
.Apply();
|
||||
FuzzerPassSplitBlocks(ir_context.get(), &fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out)
|
||||
.Apply();
|
||||
FuzzerPassAddDeadBreaks(ir_context.get(), &fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out)
|
||||
.Apply();
|
||||
FuzzerPassAddDeadContinues(ir_context.get(), &fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out)
|
||||
.Apply();
|
||||
FuzzerPassObfuscateConstants(ir_context.get(), &fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out)
|
||||
.Apply();
|
||||
std::vector<std::unique_ptr<FuzzerPass>> passes;
|
||||
while (passes.empty()) {
|
||||
MaybeAddPass<FuzzerPassAddDeadBreaks>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddDeadContinues>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassApplyIdSynonyms>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassCopyObjects>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassObfuscateConstants>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassPermuteBlocks>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassSplitBlocks>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
}
|
||||
|
||||
// Finally, give the blocks in the module a good shake-up.
|
||||
FuzzerPassPermuteBlocks(ir_context.get(), &fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out)
|
||||
.Apply();
|
||||
bool is_first = true;
|
||||
while (static_cast<uint32_t>(
|
||||
transformation_sequence_out->transformation_size()) <
|
||||
kTransformationLimit &&
|
||||
(is_first ||
|
||||
fuzzer_context.ChoosePercentage(kChanceOfApplyingAnotherPass))) {
|
||||
is_first = false;
|
||||
passes[fuzzer_context.RandomIndex(passes)]->Apply();
|
||||
}
|
||||
|
||||
// Encode the module as a binary.
|
||||
ir_context->module()->ToBinary(binary_out, false);
|
||||
|
||||
@@ -20,17 +20,16 @@ namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
namespace {
|
||||
// Default probabilities for applying various transformations.
|
||||
// All values are percentages.
|
||||
// Keep them in alphabetical order.
|
||||
// Default <minimum, maximum> pairs of probabilities for applying various
|
||||
// transformations. All values are percentages. Keep them in alphabetical order.
|
||||
|
||||
const uint32_t kDefaultChanceOfAddingDeadBreak = 20;
|
||||
const uint32_t kDefaultChanceOfAddingDeadContinue = 20;
|
||||
const uint32_t kDefaultChanceOfCopyingObject = 20;
|
||||
const uint32_t kDefaultChanceOfMovingBlockDown = 25;
|
||||
const uint32_t kDefaultChanceOfObfuscatingConstant = 20;
|
||||
const uint32_t kDefaultChanceOfReplacingIdWithSynonym = 20;
|
||||
const uint32_t kDefaultChanceOfSplittingBlock = 20;
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBreak = {5, 80};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadContinue = {5, 80};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
|
||||
|
||||
// Default functions for controlling how deep to go during recursive
|
||||
// generation/transformation. Keep them in alphabetical order.
|
||||
@@ -48,16 +47,21 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
||||
uint32_t min_fresh_id)
|
||||
: random_generator_(random_generator),
|
||||
next_fresh_id_(min_fresh_id),
|
||||
chance_of_adding_dead_break_(kDefaultChanceOfAddingDeadBreak),
|
||||
chance_of_adding_dead_continue_(kDefaultChanceOfAddingDeadContinue),
|
||||
chance_of_copying_object_(kDefaultChanceOfCopyingObject),
|
||||
chance_of_moving_block_down_(kDefaultChanceOfMovingBlockDown),
|
||||
chance_of_obfuscating_constant_(kDefaultChanceOfObfuscatingConstant),
|
||||
chance_of_replacing_id_with_synonym_(
|
||||
kDefaultChanceOfReplacingIdWithSynonym),
|
||||
chance_of_splitting_block_(kDefaultChanceOfSplittingBlock),
|
||||
go_deeper_in_constant_obfuscation_(
|
||||
kDefaultGoDeeperInConstantObfuscation) {}
|
||||
kDefaultGoDeeperInConstantObfuscation) {
|
||||
chance_of_adding_dead_break_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingDeadBreak);
|
||||
chance_of_adding_dead_continue_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingDeadContinue);
|
||||
chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject);
|
||||
chance_of_moving_block_down_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfMovingBlockDown);
|
||||
chance_of_obfuscating_constant_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfObfuscatingConstant);
|
||||
chance_of_replacing_id_with_synonym_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
|
||||
chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
|
||||
}
|
||||
|
||||
FuzzerContext::~FuzzerContext() = default;
|
||||
|
||||
@@ -70,5 +74,12 @@ bool FuzzerContext::ChoosePercentage(uint32_t percentage_chance) {
|
||||
return random_generator_->RandomPercentage() < percentage_chance;
|
||||
}
|
||||
|
||||
uint32_t FuzzerContext::ChooseBetweenMinAndMax(
|
||||
const std::pair<uint32_t, uint32_t>& min_max) {
|
||||
assert(min_max.first <= min_max.second);
|
||||
return min_max.first +
|
||||
random_generator_->RandomUint32(min_max.second - min_max.first + 1);
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#define SOURCE_FUZZ_FUZZER_CONTEXT_H_
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
#include "source/fuzz/random_generator.h"
|
||||
#include "source/opt/function.h"
|
||||
@@ -45,7 +46,7 @@ class FuzzerContext {
|
||||
// method, and which must be non-empty. Typically 'HasSizeMethod' will be an
|
||||
// std::vector.
|
||||
template <typename HasSizeMethod>
|
||||
uint32_t RandomIndex(HasSizeMethod sequence) {
|
||||
uint32_t RandomIndex(const HasSizeMethod& sequence) {
|
||||
assert(sequence.size() > 0);
|
||||
return random_generator_->RandomUint32(
|
||||
static_cast<uint32_t>(sequence.size()));
|
||||
@@ -97,6 +98,10 @@ class FuzzerContext {
|
||||
// or mutating constructs recursively.
|
||||
const std::function<bool(uint32_t, RandomGenerator*)>&
|
||||
go_deeper_in_constant_obfuscation_;
|
||||
|
||||
// Requires |min_max.first| <= |min_max.second|, and returns a value in the
|
||||
// range [ |min_max.first|, |min_max.second| ]
|
||||
uint32_t ChooseBetweenMinAndMax(const std::pair<uint32_t, uint32_t>& min_max);
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
|
||||
@@ -47,6 +47,12 @@ void FuzzerPassApplyIdSynonyms::Apply() {
|
||||
return;
|
||||
}
|
||||
|
||||
// |use_index| is the absolute index of the operand. We require
|
||||
// the index of the operand restricted to input operands only, so
|
||||
// we subtract the number of non-input operands from |use_index|.
|
||||
uint32_t use_in_operand_index =
|
||||
use_index - use_inst->NumOperands() + use_inst->NumInOperands();
|
||||
|
||||
std::vector<const protobufs::DataDescriptor*> synonyms_to_try;
|
||||
for (auto& data_descriptor :
|
||||
GetFactManager()->GetSynonymsForId(id_with_known_synonyms)) {
|
||||
@@ -63,19 +69,14 @@ void FuzzerPassApplyIdSynonyms::Apply() {
|
||||
|
||||
if (!TransformationReplaceIdWithSynonym::
|
||||
ReplacingUseWithSynonymIsOk(GetIRContext(), use_inst,
|
||||
use_index, *synonym_to_try)) {
|
||||
use_in_operand_index,
|
||||
*synonym_to_try)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// |use_index| is the absolute index of the operand. We require
|
||||
// the index of the operand restricted to input operands only, so
|
||||
// we subtract the number of non-input operands from |use_index|.
|
||||
uint32_t number_of_non_input_operands =
|
||||
use_inst->NumOperands() - use_inst->NumInOperands();
|
||||
TransformationReplaceIdWithSynonym replace_id_transformation(
|
||||
transformation::MakeIdUseDescriptorFromUse(
|
||||
GetIRContext(), use_inst,
|
||||
use_index - number_of_non_input_operands),
|
||||
GetIRContext(), use_inst, use_in_operand_index),
|
||||
*synonym_to_try, 0);
|
||||
// The transformation should be applicable by construction.
|
||||
assert(replace_id_transformation.IsApplicable(GetIRContext(),
|
||||
|
||||
223
3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
vendored
223
3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
vendored
@@ -205,156 +205,71 @@ opt::BasicBlock::iterator GetIteratorForBaseInstructionAndOffset(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Returns the ids of all successors of |block|
|
||||
std::vector<uint32_t> GetSuccessors(opt::BasicBlock* block) {
|
||||
std::vector<uint32_t> result;
|
||||
switch (block->terminator()->opcode()) {
|
||||
case SpvOpBranch:
|
||||
result.push_back(block->terminator()->GetSingleWordInOperand(0));
|
||||
break;
|
||||
case SpvOpBranchConditional:
|
||||
result.push_back(block->terminator()->GetSingleWordInOperand(1));
|
||||
result.push_back(block->terminator()->GetSingleWordInOperand(2));
|
||||
break;
|
||||
case SpvOpSwitch:
|
||||
for (uint32_t i = 1; i < block->terminator()->NumInOperands(); i += 2) {
|
||||
result.push_back(block->terminator()->GetSingleWordInOperand(i));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
bool NewEdgeRespectsUseDefDominance(opt::IRContext* context,
|
||||
opt::BasicBlock* bb_from,
|
||||
opt::BasicBlock* bb_to) {
|
||||
assert(bb_from->terminator()->opcode() == SpvOpBranch);
|
||||
|
||||
// If there is *already* an edge from |bb_from| to |bb_to|, then adding
|
||||
// another edge is fine from a dominance point of view.
|
||||
if (bb_from->terminator()->GetSingleWordInOperand(0) == bb_to->id()) {
|
||||
return true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// The FindBypassedBlocks method and its helpers perform a depth-first search;
|
||||
// this struct represents an element of the stack used during depth-first
|
||||
// search.
|
||||
struct FindBypassedBlocksDfsStackNode {
|
||||
opt::BasicBlock* block; // The block that is being explored
|
||||
bool handled_merge; // We visit merge blocks before successors; this field
|
||||
// tracks whether we have yet processed the merge block
|
||||
// (if any) associated with the block
|
||||
uint32_t next_successor; // The next as-yet unexplored successor of this
|
||||
// block; exploration of a block is complete when
|
||||
// this field's value reaches the successor count
|
||||
};
|
||||
|
||||
// Helper method for the depth-first-search routine that collects blocks that a
|
||||
// new break or continue control flow graph edge will bypass.
|
||||
void HandleSuccessorDuringSearchForBypassedBlocks(
|
||||
opt::BasicBlock* successor, bool new_blocks_will_be_bypassed,
|
||||
std::set<uint32_t>* already_visited,
|
||||
std::set<opt::BasicBlock*>* bypassed_blocks,
|
||||
std::vector<FindBypassedBlocksDfsStackNode>* dfs_stack) {
|
||||
if (already_visited->count(successor->id()) == 0) {
|
||||
// This is a new block; mark it as visited so that we don't regard it as new
|
||||
// in the future, and push it on to the stack for exploration.
|
||||
already_visited->insert(successor->id());
|
||||
dfs_stack->push_back({successor, false, 0});
|
||||
if (new_blocks_will_be_bypassed) {
|
||||
// We are in the region of the control-flow graph consisting of blocks
|
||||
// that the new edge will bypass, so grab this block.
|
||||
bypassed_blocks->insert(successor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determines those block that will be bypassed by a break or continue edge from
|
||||
// |bb_from| to |bb_to|.
|
||||
void FindBypassedBlocks(opt::IRContext* context, opt::BasicBlock* bb_from,
|
||||
opt::BasicBlock* bb_to,
|
||||
std::set<opt::BasicBlock*>* bypassed_blocks) {
|
||||
// This algorithm finds all blocks different from |bb_from| that:
|
||||
// - are in the innermost structured control flow construct containing
|
||||
// |bb_from|
|
||||
// - can be reached from |bb_from| without traversing a back-edge or going
|
||||
// through |bb_to|
|
||||
// Let us assume that the module being manipulated is valid according to the
|
||||
// rules of the SPIR-V language.
|
||||
//
|
||||
// This is achieved by doing a depth-first search of the function's CFG,
|
||||
// exploring merge blocks before successors, and grabbing all blocks that are
|
||||
// visited in the sub-search rooted at |bb_from|. (As an optimization, the
|
||||
// search terminates as soon as exploration of |bb_from| has completed.)
|
||||
// Suppose that some block Y is dominated by |bb_to| (which includes the case
|
||||
// where Y = |bb_to|).
|
||||
//
|
||||
// Suppose that Y uses an id i that is defined in some other block X.
|
||||
//
|
||||
// Because the module is valid, X must dominate Y. We are concerned about
|
||||
// whether an edge from |bb_from| to |bb_to| could *stop* X from dominating
|
||||
// Y.
|
||||
//
|
||||
// Because |bb_to| dominates Y, a new edge from |bb_from| to |bb_to| can
|
||||
// only affect whether X dominates Y if X dominates |bb_to|.
|
||||
//
|
||||
// So let us assume that X does dominate |bb_to|, so that we have:
|
||||
//
|
||||
// (X defines i) dominates |bb_to| dominates (Y uses i)
|
||||
//
|
||||
// The new edge from |bb_from| to |bb_to| will stop the definition of i in X
|
||||
// from dominating the use of i in Y exactly when the new edge will stop X
|
||||
// from dominating |bb_to|.
|
||||
//
|
||||
// Now, the block X that we are worried about cannot dominate |bb_from|,
|
||||
// because in that case X would still dominate |bb_to| after we add an edge
|
||||
// from |bb_from| to |bb_to|.
|
||||
//
|
||||
// Also, it cannot be that X = |bb_to|, because nothing can stop a block
|
||||
// from dominating itself.
|
||||
//
|
||||
// So we are looking for a block X such that:
|
||||
//
|
||||
// - X strictly dominates |bb_to|
|
||||
// - X does not dominate |bb_from|
|
||||
// - X defines an id i
|
||||
// - i is used in some block Y
|
||||
// - |bb_to| dominates Y
|
||||
|
||||
auto enclosing_function = bb_from->GetParent();
|
||||
|
||||
// The set of block ids already visited during search. We put |bb_to| in
|
||||
// there initially so that search automatically backtracks when this block is
|
||||
// reached.
|
||||
std::set<uint32_t> already_visited;
|
||||
already_visited.insert(bb_to->id());
|
||||
|
||||
// Tracks when we are in the region of blocks that the new edge would bypass;
|
||||
// we flip this to 'true' once we reach |bb_from| and have finished searching
|
||||
// its merge block (in the case that it happens to be a header.
|
||||
bool new_blocks_will_be_bypassed = false;
|
||||
|
||||
std::vector<FindBypassedBlocksDfsStackNode> dfs_stack;
|
||||
opt::BasicBlock* entry_block = enclosing_function->entry().get();
|
||||
dfs_stack.push_back({entry_block, false, 0});
|
||||
while (!dfs_stack.empty()) {
|
||||
auto node_index = dfs_stack.size() - 1;
|
||||
|
||||
// First make sure we search the merge block associated ith this block, if
|
||||
// there is one.
|
||||
if (!dfs_stack[node_index].handled_merge) {
|
||||
dfs_stack[node_index].handled_merge = true;
|
||||
if (dfs_stack[node_index].block->MergeBlockIdIfAny()) {
|
||||
opt::BasicBlock* merge_block = context->cfg()->block(
|
||||
dfs_stack[node_index].block->MergeBlockIdIfAny());
|
||||
// A block can only be the merge block for one header, so this block
|
||||
// should only be in |visited| if it is |bb_to|, which we put into
|
||||
// |visited| in advance.
|
||||
assert(already_visited.count(merge_block->id()) == 0 ||
|
||||
merge_block == bb_to);
|
||||
HandleSuccessorDuringSearchForBypassedBlocks(
|
||||
merge_block, new_blocks_will_be_bypassed, &already_visited,
|
||||
bypassed_blocks, &dfs_stack);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we find |bb_from|, we are interested in grabbing previously unseen
|
||||
// successor blocks (by this point we will have already searched the merge
|
||||
// block associated with |bb_from|, if there is one.
|
||||
if (dfs_stack[node_index].block == bb_from) {
|
||||
new_blocks_will_be_bypassed = true;
|
||||
}
|
||||
|
||||
// Consider the next unexplored successor.
|
||||
auto successors = GetSuccessors(dfs_stack[node_index].block);
|
||||
if (dfs_stack[node_index].next_successor < successors.size()) {
|
||||
HandleSuccessorDuringSearchForBypassedBlocks(
|
||||
context->cfg()->block(
|
||||
successors[dfs_stack[node_index].next_successor]),
|
||||
new_blocks_will_be_bypassed, &already_visited, bypassed_blocks,
|
||||
&dfs_stack);
|
||||
dfs_stack[node_index].next_successor++;
|
||||
} else {
|
||||
// We have finished exploring |node|. If it is |bb_from|, we can
|
||||
// terminate search -- we have grabbed all the relevant blocks.
|
||||
if (dfs_stack[node_index].block == bb_from) {
|
||||
break;
|
||||
}
|
||||
dfs_stack.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool NewEdgeLeavingConstructBodyRespectsUseDefDominance(
|
||||
opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to) {
|
||||
// Find those blocks that the edge from |bb_from| to |bb_to| might bypass.
|
||||
std::set<opt::BasicBlock*> bypassed_blocks;
|
||||
FindBypassedBlocks(context, bb_from, bb_to, &bypassed_blocks);
|
||||
|
||||
// For each bypassed block, check whether it contains a definition that is
|
||||
// used by some non-bypassed block - that would be problematic.
|
||||
for (auto defining_block : bypassed_blocks) {
|
||||
for (auto& inst : *defining_block) {
|
||||
// Walk the dominator tree backwards, starting from the immediate dominator
|
||||
// of |bb_to|. We can stop when we find a block that also dominates
|
||||
// |bb_from|.
|
||||
auto dominator_analysis = context->GetDominatorAnalysis(bb_from->GetParent());
|
||||
for (auto dominator = dominator_analysis->ImmediateDominator(bb_to);
|
||||
dominator != nullptr &&
|
||||
!dominator_analysis->Dominates(dominator, bb_from);
|
||||
dominator = dominator_analysis->ImmediateDominator(dominator)) {
|
||||
// |dominator| is a candidate for block X in the above description.
|
||||
// We now look through the instructions for a candidate instruction i.
|
||||
for (auto& inst : *dominator) {
|
||||
// Consider all the uses of this instruction.
|
||||
if (!context->get_def_use_mgr()->WhileEachUse(
|
||||
&inst,
|
||||
[context, &bypassed_blocks](opt::Instruction* user,
|
||||
uint32_t operand_index) -> bool {
|
||||
[bb_to, context, dominator_analysis](
|
||||
opt::Instruction* user, uint32_t operand_index) -> bool {
|
||||
// If this use is in an OpPhi, we need to check that dominance
|
||||
// of the relevant *parent* block is not spoiled. Otherwise we
|
||||
// need to check that dominance of the block containing the use
|
||||
@@ -371,13 +286,12 @@ bool NewEdgeLeavingConstructBodyRespectsUseDefDominance(
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the use-block is not in |bypassed_blocks| then we have
|
||||
// found a block in the construct that is reachable from
|
||||
// |from_block|, and which defines an id that is used outside of
|
||||
// the construct. Adding an edge from |from_block| to
|
||||
// |to_block| would prevent this use being dominated.
|
||||
return bypassed_blocks.find(use_block_or_phi_parent) !=
|
||||
bypassed_blocks.end();
|
||||
// With reference to the above discussion,
|
||||
// |use_block_or_phi_parent| is a candidate for the block Y.
|
||||
// If |bb_to| dominates this block, the new edge would be
|
||||
// problematic.
|
||||
return !dominator_analysis->Dominates(bb_to,
|
||||
use_block_or_phi_parent);
|
||||
})) {
|
||||
return false;
|
||||
}
|
||||
@@ -386,6 +300,13 @@ bool NewEdgeLeavingConstructBodyRespectsUseDefDominance(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BlockIsReachableInItsFunction(opt::IRContext* context,
|
||||
opt::BasicBlock* bb) {
|
||||
auto enclosing_function = bb->GetParent();
|
||||
return context->GetDominatorAnalysis(enclosing_function)
|
||||
->Dominates(enclosing_function->entry().get(), bb);
|
||||
}
|
||||
|
||||
} // namespace fuzzerutil
|
||||
|
||||
} // namespace fuzz
|
||||
|
||||
18
3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
vendored
18
3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
vendored
@@ -74,18 +74,18 @@ bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
|
||||
opt::BasicBlock::iterator GetIteratorForBaseInstructionAndOffset(
|
||||
opt::BasicBlock* block, const opt::Instruction* base_inst, uint32_t offset);
|
||||
|
||||
// Block |bb_from| is assumed to be in a structured control flow construct, and
|
||||
// block |bb_to| is assumed to be either the merge bock for that construct (in
|
||||
// the case of a loop, conditional or switch) or the continue target for that
|
||||
// construct (in the case of a loop only).
|
||||
//
|
||||
// The function determines whether adding an edge from |bb_from| to |bb_to| -
|
||||
// i.e. a break or continue for the construct
|
||||
// - is legitimate with respect to the SPIR-V rule that a definition must
|
||||
// is legitimate with respect to the SPIR-V rule that a definition must
|
||||
// dominate all of its uses. This is because adding such an edge can change
|
||||
// dominance in the control flow graph, potentially making the module invalid.
|
||||
bool NewEdgeLeavingConstructBodyRespectsUseDefDominance(
|
||||
opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to);
|
||||
bool NewEdgeRespectsUseDefDominance(opt::IRContext* context,
|
||||
opt::BasicBlock* bb_from,
|
||||
opt::BasicBlock* bb_to);
|
||||
|
||||
// Returns true if and only if there is a path to |bb| from the entry block of
|
||||
// the function that contains |bb|.
|
||||
bool BlockIsReachableInItsFunction(opt::IRContext* context,
|
||||
opt::BasicBlock* bb);
|
||||
|
||||
} // namespace fuzzerutil
|
||||
|
||||
|
||||
@@ -138,6 +138,13 @@ bool TransformationAddDeadBreak::IsApplicable(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!fuzzerutil::BlockIsReachableInItsFunction(context, bb_to)) {
|
||||
// If the target of the break is unreachable, we conservatively do not
|
||||
// allow adding a dead break, to avoid the compilations that arise due to
|
||||
// the lack of sensible dominance information for unreachable blocks.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that |message_.from_block| ends with an unconditional branch.
|
||||
if (bb_from->terminator()->opcode() != SpvOpBranch) {
|
||||
// The block associated with the id does not end with an unconditional
|
||||
@@ -170,8 +177,7 @@ bool TransformationAddDeadBreak::IsApplicable(
|
||||
|
||||
// Check that adding the break would not violate the property that a
|
||||
// definition must dominate all of its uses.
|
||||
return fuzzerutil::NewEdgeLeavingConstructBodyRespectsUseDefDominance(
|
||||
context, bb_from, bb_to);
|
||||
return fuzzerutil::NewEdgeRespectsUseDefDominance(context, bb_from, bb_to);
|
||||
}
|
||||
|
||||
void TransformationAddDeadBreak::Apply(opt::IRContext* context,
|
||||
|
||||
@@ -83,14 +83,22 @@ bool TransformationAddDeadContinue::IsApplicable(
|
||||
return false;
|
||||
}
|
||||
|
||||
auto continue_block = context->cfg()->block(loop_header)->ContinueBlockId();
|
||||
|
||||
if (!fuzzerutil::BlockIsReachableInItsFunction(
|
||||
context, context->cfg()->block(continue_block))) {
|
||||
// If the loop's continue block is unreachable, we conservatively do not
|
||||
// allow adding a dead continue, to avoid the compilations that arise due to
|
||||
// the lack of sensible dominance information for unreachable blocks.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fuzzerutil::BlockIsInLoopContinueConstruct(context, message_.from_block(),
|
||||
loop_header)) {
|
||||
// We cannot jump to the continue target from the continue construct.
|
||||
return false;
|
||||
}
|
||||
|
||||
auto continue_block = context->cfg()->block(loop_header)->ContinueBlockId();
|
||||
|
||||
if (context->GetStructuredCFGAnalysis()->IsMergeBlock(continue_block)) {
|
||||
// A branch straight to the continue target that is also a merge block might
|
||||
// break the property that a construct header must dominate its merge block
|
||||
@@ -100,7 +108,7 @@ bool TransformationAddDeadContinue::IsApplicable(
|
||||
|
||||
// Check that adding the continue would not violate the property that a
|
||||
// definition must dominate all of its uses.
|
||||
if (!fuzzerutil::NewEdgeLeavingConstructBodyRespectsUseDefDominance(
|
||||
if (!fuzzerutil::NewEdgeRespectsUseDefDominance(
|
||||
context, bb_from, context->cfg()->block(continue_block))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -36,6 +36,8 @@ class TransformationAddDeadContinue : public Transformation {
|
||||
|
||||
// - |message_.from_block| must be the id of a block a in the given module.
|
||||
// - a must be contained in a loop with continue target b
|
||||
// - The continue target b must be dominated by the head of the loop in which
|
||||
// it is contained
|
||||
// - b must not be the merge block of a selection construct
|
||||
// - if |message_.continue_condition_value| holds (does not hold) then
|
||||
// OpConstantTrue (OpConstantFalse) must be present in the module
|
||||
|
||||
@@ -84,12 +84,12 @@ void TransformationMoveBlockDown::Apply(opt::IRContext* context,
|
||||
"To be able to move a block down, it needs to have a "
|
||||
"program-order successor.");
|
||||
function.MoveBasicBlockToAfter(message_.block_id(), &*block_it);
|
||||
// It is prudent to invalidate analyses after changing block ordering in
|
||||
// case any of them depend on it, but the ones that definitely do not
|
||||
// depend on ordering can be preserved. These include the following,
|
||||
// which can likely be extended.
|
||||
// For performance, it is vital to keep the dominator analysis valid
|
||||
// (which due to https://github.com/KhronosGroup/SPIRV-Tools/issues/2889
|
||||
// requires keeping the CFG analysis valid).
|
||||
context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisDefUse |
|
||||
opt::IRContext::Analysis::kAnalysisCFG |
|
||||
opt::IRContext::Analysis::kAnalysisDominatorAnalysis);
|
||||
|
||||
return;
|
||||
|
||||
@@ -178,7 +178,7 @@ bool TransformationReplaceBooleanConstantWithConstantBinary::IsApplicable(
|
||||
context->get_constant_mgr()->FindDeclaredConstant(message_.rhs_id());
|
||||
bool expected_result = (boolean_constant->opcode() == SpvOpConstantTrue);
|
||||
|
||||
const SpvOp binary_opcode = static_cast<SpvOp>(message_.opcode());
|
||||
const auto binary_opcode = static_cast<SpvOp>(message_.opcode());
|
||||
|
||||
// We consider the floating point, signed and unsigned integer cases
|
||||
// separately. In each case the logic is very similar.
|
||||
@@ -237,8 +237,17 @@ bool TransformationReplaceBooleanConstantWithConstantBinary::IsApplicable(
|
||||
}
|
||||
|
||||
// The id use descriptor must identify some instruction
|
||||
return transformation::FindInstruction(message_.id_use_descriptor(),
|
||||
context) != nullptr;
|
||||
auto instruction =
|
||||
transformation::FindInstruction(message_.id_use_descriptor(), context);
|
||||
if (instruction == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The instruction must not be an OpPhi, as we cannot insert a binary
|
||||
// operator instruction before an OpPhi.
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902): there is
|
||||
// scope for being less conservative.
|
||||
return instruction->opcode() != SpvOpPhi;
|
||||
}
|
||||
|
||||
void TransformationReplaceBooleanConstantWithConstantBinary::Apply(
|
||||
|
||||
@@ -43,6 +43,12 @@ class TransformationReplaceBooleanConstantWithConstantBinary
|
||||
// - |message_.opcode| must be suitable for applying to |message.lhs_id| and
|
||||
// |message_.rhs_id|, and the result must evaluate to the boolean constant
|
||||
// c.
|
||||
// - The boolean constant usage must not be an argument to OpPhi, because in
|
||||
// this case it is not legal to insert a binary operator instruction right
|
||||
// before the OpPhi.
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902): consider
|
||||
// replacing a boolean in an OpPhi by adding a binary operator instruction
|
||||
// to the parent block for the OpPhi.
|
||||
bool IsApplicable(opt::IRContext* context,
|
||||
const FactManager& fact_manager) const override;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "source/fuzz/data_descriptor.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/id_use_descriptor.h"
|
||||
#include "source/opt/types.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
@@ -116,15 +117,74 @@ bool TransformationReplaceIdWithSynonym::ReplacingUseWithSynonymIsOk(
|
||||
|
||||
if (use_instruction->opcode() == SpvOpAccessChain &&
|
||||
use_in_operand_index > 0) {
|
||||
// This is an access chain index. If the object being accessed has
|
||||
// pointer-to-struct type then we cannot replace the use with a synonym, as
|
||||
// the use needs to be an OpConstant.
|
||||
// This is an access chain index. If the (sub-)object being accessed by the
|
||||
// given index has struct type then we cannot replace the use with a
|
||||
// synonym, as the use needs to be an OpConstant.
|
||||
|
||||
// Get the top-level composite type that is being accessed.
|
||||
auto object_being_accessed = context->get_def_use_mgr()->GetDef(
|
||||
use_instruction->GetSingleWordInOperand(0));
|
||||
auto pointer_type =
|
||||
context->get_type_mgr()->GetType(object_being_accessed->type_id());
|
||||
assert(pointer_type->AsPointer());
|
||||
if (pointer_type->AsPointer()->pointee_type()->AsStruct()) {
|
||||
auto composite_type_being_accessed =
|
||||
pointer_type->AsPointer()->pointee_type();
|
||||
|
||||
// Now walk the access chain, tracking the type of each sub-object of the
|
||||
// composite that is traversed, until the index of interest is reached.
|
||||
for (uint32_t index_in_operand = 1; index_in_operand < use_in_operand_index;
|
||||
index_in_operand++) {
|
||||
// For vectors, matrices and arrays, getting the type of the sub-object is
|
||||
// trivial. For the struct case, the sub-object type is field-sensitive,
|
||||
// and depends on the constant index that is used.
|
||||
if (composite_type_being_accessed->AsVector()) {
|
||||
composite_type_being_accessed =
|
||||
composite_type_being_accessed->AsVector()->element_type();
|
||||
} else if (composite_type_being_accessed->AsMatrix()) {
|
||||
composite_type_being_accessed =
|
||||
composite_type_being_accessed->AsMatrix()->element_type();
|
||||
} else if (composite_type_being_accessed->AsArray()) {
|
||||
composite_type_being_accessed =
|
||||
composite_type_being_accessed->AsArray()->element_type();
|
||||
} else {
|
||||
assert(composite_type_being_accessed->AsStruct());
|
||||
auto constant_index_instruction = context->get_def_use_mgr()->GetDef(
|
||||
use_instruction->GetSingleWordInOperand(index_in_operand));
|
||||
assert(constant_index_instruction->opcode() == SpvOpConstant);
|
||||
uint32_t member_index =
|
||||
constant_index_instruction->GetSingleWordInOperand(0);
|
||||
composite_type_being_accessed =
|
||||
composite_type_being_accessed->AsStruct()
|
||||
->element_types()[member_index];
|
||||
}
|
||||
}
|
||||
|
||||
// We have found the composite type being accessed by the index we are
|
||||
// considering replacing. If it is a struct, then we cannot do the
|
||||
// replacement as struct indices must be constants.
|
||||
if (composite_type_being_accessed->AsStruct()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_instruction->opcode() == SpvOpFunctionCall &&
|
||||
use_in_operand_index > 0) {
|
||||
// This is a function call argument. It is not allowed to have pointer
|
||||
// type.
|
||||
|
||||
// Get the definition of the function being called.
|
||||
auto function = context->get_def_use_mgr()->GetDef(
|
||||
use_instruction->GetSingleWordInOperand(0));
|
||||
// From the function definition, get the function type.
|
||||
auto function_type =
|
||||
context->get_def_use_mgr()->GetDef(function->GetSingleWordInOperand(1));
|
||||
// OpTypeFunction's 0-th input operand is the function return type, and the
|
||||
// function argument types follow. Because the arguments to OpFunctionCall
|
||||
// start from input operand 1, we can use |use_in_operand_index| to get the
|
||||
// type associated with this function argument.
|
||||
auto parameter_type = context->get_type_mgr()->GetType(
|
||||
function_type->GetSingleWordInOperand(use_in_operand_index));
|
||||
if (parameter_type->AsPointer()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,8 @@ class TransformationReplaceIdWithSynonym : public Transformation {
|
||||
// dominated by their definitions.
|
||||
// - The id must not be an index into an access chain whose base object has
|
||||
// struct type, as such indices must be constants.
|
||||
// - The id must not be a pointer argument to a function call (because the
|
||||
// synonym might not be a memory object declaration).
|
||||
// - |fresh_id_for_temporary| must be 0.
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2855): the
|
||||
// motivation for the temporary is to support the case where an id is
|
||||
|
||||
@@ -1274,6 +1274,12 @@ FoldingRule CompositeConstructFeedingExtract() {
|
||||
"Wrong opcode. Should be OpCompositeExtract.");
|
||||
analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
|
||||
analysis::TypeManager* type_mgr = context->get_type_mgr();
|
||||
|
||||
// If there are no index operands, then this rule cannot do anything.
|
||||
if (inst->NumInOperands() <= 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx);
|
||||
Instruction* cinst = def_use_mgr->GetDef(cid);
|
||||
|
||||
@@ -1401,6 +1407,20 @@ FoldingRule CompositeExtractFeedingConstruct() {
|
||||
};
|
||||
}
|
||||
|
||||
// Folds an OpCompositeExtract instruction with no indexes into an OpCopyObject.
|
||||
bool ExtractWithNoIndexes(IRContext*, Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>&) {
|
||||
assert(inst->opcode() == SpvOpCompositeExtract &&
|
||||
"Wrong opcode. Should be OpCompositeExtract.");
|
||||
|
||||
if (inst->NumInOperands() > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
inst->SetOpcode(SpvOpCopyObject);
|
||||
return true;
|
||||
}
|
||||
|
||||
FoldingRule InsertFeedingExtract() {
|
||||
return [](IRContext* context, Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>&) {
|
||||
@@ -2207,6 +2227,7 @@ void FoldingRules::AddFoldingRules() {
|
||||
// Take that into consideration.
|
||||
rules_[SpvOpCompositeConstruct].push_back(CompositeExtractFeedingConstruct());
|
||||
|
||||
rules_[SpvOpCompositeExtract].push_back(ExtractWithNoIndexes);
|
||||
rules_[SpvOpCompositeExtract].push_back(InsertFeedingExtract());
|
||||
rules_[SpvOpCompositeExtract].push_back(CompositeConstructFeedingExtract());
|
||||
rules_[SpvOpCompositeExtract].push_back(VectorShuffleFeedingExtract());
|
||||
|
||||
11
3rdparty/spirv-tools/source/opt/function.h
vendored
11
3rdparty/spirv-tools/source/opt/function.h
vendored
@@ -104,19 +104,24 @@ class Function {
|
||||
});
|
||||
}
|
||||
|
||||
// Runs the given function |f| on each instruction in this function, and
|
||||
// optionally on debug line instructions that might precede them.
|
||||
// Runs the given function |f| on instructions in this function, in order,
|
||||
// and optionally on debug line instructions that might precede them.
|
||||
void ForEachInst(const std::function<void(Instruction*)>& f,
|
||||
bool run_on_debug_line_insts = false);
|
||||
void ForEachInst(const std::function<void(const Instruction*)>& f,
|
||||
bool run_on_debug_line_insts = false) const;
|
||||
// Runs the given function |f| on instructions in this function, in order,
|
||||
// and optionally on debug line instructions that might precede them.
|
||||
// If |f| returns false, iteration is terminated and this function returns
|
||||
// false.
|
||||
bool WhileEachInst(const std::function<bool(Instruction*)>& f,
|
||||
bool run_on_debug_line_insts = false);
|
||||
bool WhileEachInst(const std::function<bool(const Instruction*)>& f,
|
||||
bool run_on_debug_line_insts = false) const;
|
||||
|
||||
// Runs the given function |f| on each parameter instruction in this function,
|
||||
// and optionally on debug line instructions that might precede them.
|
||||
// in order, and optionally on debug line instructions that might precede
|
||||
// them.
|
||||
void ForEachParam(const std::function<void(const Instruction*)>& f,
|
||||
bool run_on_debug_line_insts = false) const;
|
||||
void ForEachParam(const std::function<void(Instruction*)>& f,
|
||||
|
||||
@@ -46,6 +46,10 @@ uint32_t LocalAccessChainConvertPass::BuildAndAppendVarLoad(
|
||||
const Instruction* ptrInst, uint32_t* varId, uint32_t* varPteTypeId,
|
||||
std::vector<std::unique_ptr<Instruction>>* newInsts) {
|
||||
const uint32_t ldResultId = TakeNextId();
|
||||
if (ldResultId == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*varId = ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx);
|
||||
const Instruction* varInst = get_def_use_mgr()->GetDef(*varId);
|
||||
assert(varInst->opcode() == SpvOpVariable);
|
||||
@@ -70,7 +74,7 @@ void LocalAccessChainConvertPass::AppendConstantOperands(
|
||||
});
|
||||
}
|
||||
|
||||
void LocalAccessChainConvertPass::ReplaceAccessChainLoad(
|
||||
bool LocalAccessChainConvertPass::ReplaceAccessChainLoad(
|
||||
const Instruction* address_inst, Instruction* original_load) {
|
||||
// Build and append load of variable in ptrInst
|
||||
std::vector<std::unique_ptr<Instruction>> new_inst;
|
||||
@@ -78,6 +82,10 @@ void LocalAccessChainConvertPass::ReplaceAccessChainLoad(
|
||||
uint32_t varPteTypeId;
|
||||
const uint32_t ldResultId =
|
||||
BuildAndAppendVarLoad(address_inst, &varId, &varPteTypeId, &new_inst);
|
||||
if (ldResultId == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
context()->get_decoration_mgr()->CloneDecorations(
|
||||
original_load->result_id(), ldResultId, {SpvDecorationRelaxedPrecision});
|
||||
original_load->InsertBefore(std::move(new_inst));
|
||||
@@ -95,9 +103,10 @@ void LocalAccessChainConvertPass::ReplaceAccessChainLoad(
|
||||
original_load->SetOpcode(SpvOpCompositeExtract);
|
||||
original_load->ReplaceOperands(new_operands);
|
||||
context()->UpdateDefUse(original_load);
|
||||
return true;
|
||||
}
|
||||
|
||||
void LocalAccessChainConvertPass::GenAccessChainStoreReplacement(
|
||||
bool LocalAccessChainConvertPass::GenAccessChainStoreReplacement(
|
||||
const Instruction* ptrInst, uint32_t valId,
|
||||
std::vector<std::unique_ptr<Instruction>>* newInsts) {
|
||||
// Build and append load of variable in ptrInst
|
||||
@@ -105,11 +114,18 @@ void LocalAccessChainConvertPass::GenAccessChainStoreReplacement(
|
||||
uint32_t varPteTypeId;
|
||||
const uint32_t ldResultId =
|
||||
BuildAndAppendVarLoad(ptrInst, &varId, &varPteTypeId, newInsts);
|
||||
if (ldResultId == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
context()->get_decoration_mgr()->CloneDecorations(
|
||||
varId, ldResultId, {SpvDecorationRelaxedPrecision});
|
||||
|
||||
// Build and append Insert
|
||||
const uint32_t insResultId = TakeNextId();
|
||||
if (insResultId == 0) {
|
||||
return false;
|
||||
}
|
||||
std::vector<Operand> ins_in_opnds = {
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {valId}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ldResultId}}};
|
||||
@@ -125,6 +141,7 @@ void LocalAccessChainConvertPass::GenAccessChainStoreReplacement(
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {varId}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {insResultId}}},
|
||||
newInsts);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LocalAccessChainConvertPass::IsConstantIndexAccessChain(
|
||||
@@ -198,7 +215,8 @@ void LocalAccessChainConvertPass::FindTargetVars(Function* func) {
|
||||
}
|
||||
}
|
||||
|
||||
bool LocalAccessChainConvertPass::ConvertLocalAccessChains(Function* func) {
|
||||
Pass::Status LocalAccessChainConvertPass::ConvertLocalAccessChains(
|
||||
Function* func) {
|
||||
FindTargetVars(func);
|
||||
// Replace access chains of all targeted variables with equivalent
|
||||
// extract and insert sequences
|
||||
@@ -213,7 +231,9 @@ bool LocalAccessChainConvertPass::ConvertLocalAccessChains(Function* func) {
|
||||
if (!IsNonPtrAccessChain(ptrInst->opcode())) break;
|
||||
if (!IsTargetVar(varId)) break;
|
||||
std::vector<std::unique_ptr<Instruction>> newInsts;
|
||||
ReplaceAccessChainLoad(ptrInst, &*ii);
|
||||
if (!ReplaceAccessChainLoad(ptrInst, &*ii)) {
|
||||
return Status::Failure;
|
||||
}
|
||||
modified = true;
|
||||
} break;
|
||||
case SpvOpStore: {
|
||||
@@ -223,7 +243,9 @@ bool LocalAccessChainConvertPass::ConvertLocalAccessChains(Function* func) {
|
||||
if (!IsTargetVar(varId)) break;
|
||||
std::vector<std::unique_ptr<Instruction>> newInsts;
|
||||
uint32_t valId = ii->GetSingleWordInOperand(kStoreValIdInIdx);
|
||||
GenAccessChainStoreReplacement(ptrInst, valId, &newInsts);
|
||||
if (!GenAccessChainStoreReplacement(ptrInst, valId, &newInsts)) {
|
||||
return Status::Failure;
|
||||
}
|
||||
dead_instructions.push_back(&*ii);
|
||||
++ii;
|
||||
ii = ii.InsertBefore(std::move(newInsts));
|
||||
@@ -248,7 +270,7 @@ bool LocalAccessChainConvertPass::ConvertLocalAccessChains(Function* func) {
|
||||
});
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
|
||||
}
|
||||
|
||||
void LocalAccessChainConvertPass::Initialize() {
|
||||
@@ -294,12 +316,16 @@ Pass::Status LocalAccessChainConvertPass::ProcessImpl() {
|
||||
if (ai.opcode() == SpvOpGroupDecorate) return Status::SuccessWithoutChange;
|
||||
// Do not process if any disallowed extensions are enabled
|
||||
if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
|
||||
// Process all entry point functions.
|
||||
ProcessFunction pfn = [this](Function* fp) {
|
||||
return ConvertLocalAccessChains(fp);
|
||||
};
|
||||
bool modified = context()->ProcessEntryPointCallTree(pfn);
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
|
||||
// Process all functions in the module.
|
||||
Status status = Status::SuccessWithoutChange;
|
||||
for (Function& func : *get_module()) {
|
||||
status = CombineStatus(status, ConvertLocalAccessChains(&func));
|
||||
if (status == Status::Failure) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
LocalAccessChainConvertPass::LocalAccessChainConvertPass() {}
|
||||
|
||||
@@ -82,16 +82,16 @@ class LocalAccessChainConvertPass : public MemPass {
|
||||
|
||||
// Create a load/insert/store equivalent to a store of
|
||||
// |valId| through (constant index) access chaing |ptrInst|.
|
||||
// Append to |newInsts|.
|
||||
void GenAccessChainStoreReplacement(
|
||||
// Append to |newInsts|. Returns true if successful.
|
||||
bool GenAccessChainStoreReplacement(
|
||||
const Instruction* ptrInst, uint32_t valId,
|
||||
std::vector<std::unique_ptr<Instruction>>* newInsts);
|
||||
|
||||
// For the (constant index) access chain |address_inst|, create an
|
||||
// equivalent load and extract that replaces |original_load|. The result id
|
||||
// of the extract will be the same as the original result id of
|
||||
// |original_load|.
|
||||
void ReplaceAccessChainLoad(const Instruction* address_inst,
|
||||
// |original_load|. Returns true if successful.
|
||||
bool ReplaceAccessChainLoad(const Instruction* address_inst,
|
||||
Instruction* original_load);
|
||||
|
||||
// Return true if all indices of access chain |acp| are OpConstant integers
|
||||
@@ -106,7 +106,9 @@ class LocalAccessChainConvertPass : public MemPass {
|
||||
//
|
||||
// Nested access chains and pointer access chains are not currently
|
||||
// converted.
|
||||
bool ConvertLocalAccessChains(Function* func);
|
||||
//
|
||||
// Returns a status to indicate success or failure, and change or no change.
|
||||
Status ConvertLocalAccessChains(Function* func);
|
||||
|
||||
// Initialize extensions whitelist
|
||||
void InitExtensions();
|
||||
|
||||
@@ -820,7 +820,8 @@ ScalarReplacementPass::GetUsedComponents(Instruction* inst) {
|
||||
// Look for extract from the load.
|
||||
std::vector<uint32_t> t;
|
||||
if (def_use_mgr->WhileEachUser(use, [&t](Instruction* use2) {
|
||||
if (use2->opcode() != SpvOpCompositeExtract) {
|
||||
if (use2->opcode() != SpvOpCompositeExtract ||
|
||||
use2->NumInOperands() <= 1) {
|
||||
return false;
|
||||
}
|
||||
t.push_back(use2->GetSingleWordInOperand(1));
|
||||
|
||||
14
3rdparty/spirv-tools/source/opt/wrap_opkill.cpp
vendored
14
3rdparty/spirv-tools/source/opt/wrap_opkill.cpp
vendored
@@ -60,13 +60,23 @@ bool WrapOpKill::ReplaceWithFunctionCall(Instruction* inst) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Instruction* return_inst = nullptr;
|
||||
uint32_t return_type_id = GetOwningFunctionsReturnType(inst);
|
||||
if (return_type_id != GetVoidTypeId()) {
|
||||
Instruction* undef = ir_builder.AddNullaryOp(return_type_id, SpvOpUndef);
|
||||
ir_builder.AddUnaryOp(0, SpvOpReturnValue, undef->result_id());
|
||||
if (undef == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return_inst =
|
||||
ir_builder.AddUnaryOp(0, SpvOpReturnValue, undef->result_id());
|
||||
} else {
|
||||
ir_builder.AddNullaryOp(0, SpvOpReturn);
|
||||
return_inst = ir_builder.AddNullaryOp(0, SpvOpReturn);
|
||||
}
|
||||
|
||||
if (return_inst == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
context()->KillInst(inst);
|
||||
return true;
|
||||
}
|
||||
|
||||
4
3rdparty/spirv-tools/source/val/validate.cpp
vendored
4
3rdparty/spirv-tools/source/val/validate.cpp
vendored
@@ -343,7 +343,6 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
|
||||
}
|
||||
|
||||
if (auto error = CapabilityPass(*vstate, &instruction)) return error;
|
||||
if (auto error = DataRulesPass(*vstate, &instruction)) return error;
|
||||
if (auto error = ModuleLayoutPass(*vstate, &instruction)) return error;
|
||||
if (auto error = CfgPass(*vstate, &instruction)) return error;
|
||||
if (auto error = InstructionPass(*vstate, &instruction)) return error;
|
||||
@@ -352,6 +351,9 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
|
||||
{
|
||||
Instruction* inst = const_cast<Instruction*>(&instruction);
|
||||
vstate->RegisterInstruction(inst);
|
||||
if (inst->opcode() == SpvOpTypeForwardPointer) {
|
||||
vstate->RegisterForwardPointer(inst->GetOperandAs<uint32_t>(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
5
3rdparty/spirv-tools/source/val/validate.h
vendored
5
3rdparty/spirv-tools/source/val/validate.h
vendored
@@ -123,11 +123,6 @@ spv_result_t ControlFlowPass(ValidationState_t& _, const Instruction* inst);
|
||||
/// Performs Id and SSA validation of a module
|
||||
spv_result_t IdPass(ValidationState_t& _, Instruction* inst);
|
||||
|
||||
/// Performs validation of the Data Rules subsection of 2.16.1 Universal
|
||||
/// Validation Rules.
|
||||
/// TODO(ehsann): add more comments here as more validation code is added.
|
||||
spv_result_t DataRulesPass(ValidationState_t& _, const Instruction* inst);
|
||||
|
||||
/// Performs instruction validation.
|
||||
spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst);
|
||||
|
||||
|
||||
@@ -2504,20 +2504,20 @@ spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference(
|
||||
switch (execution_model) {
|
||||
case SpvExecutionModelGeometry:
|
||||
case SpvExecutionModelFragment:
|
||||
case SpvExecutionModelMeshNV: {
|
||||
case SpvExecutionModelMeshNV:
|
||||
// Ok.
|
||||
break;
|
||||
case SpvExecutionModelVertex:
|
||||
case SpvExecutionModelTessellationEvaluation:
|
||||
if (!_.HasCapability(SpvCapabilityShaderViewportIndexLayerEXT)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
|
||||
<< "Using BuiltIn "
|
||||
<< _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
|
||||
decoration.params()[0])
|
||||
<< " in Vertex or Tessellation execution model requires "
|
||||
"the ShaderViewportIndexLayerEXT capability.";
|
||||
}
|
||||
break;
|
||||
case SpvExecutionModelVertex:
|
||||
case SpvExecutionModelTessellationEvaluation: {
|
||||
if (!_.HasCapability(SpvCapabilityShaderViewportIndexLayerEXT)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
|
||||
<< "Using BuiltIn "
|
||||
<< _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
|
||||
decoration.params()[0])
|
||||
<< " in Vertex or Tessellation execution model requires "
|
||||
"the ShaderViewportIndexLayerEXT capability.";
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
|
||||
|
||||
@@ -342,6 +342,21 @@ spv_result_t ValidateConstantNull(ValidationState_t& _,
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Validates that OpSpecConstant specializes to either int or float type.
|
||||
spv_result_t ValidateSpecConstant(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
// Operand 0 is the <id> of the type that we're specializing to.
|
||||
auto type_id = inst->GetOperandAs<const uint32_t>(0);
|
||||
auto type_instruction = _.FindDef(type_id);
|
||||
auto type_opcode = type_instruction->opcode();
|
||||
if (type_opcode != SpvOpTypeInt && type_opcode != SpvOpTypeFloat) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Specialization constant "
|
||||
"must be an integer or "
|
||||
"floating-point number.";
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateSpecConstantOp(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
const auto op = inst->GetOperandAs<SpvOp>(2);
|
||||
@@ -422,6 +437,9 @@ spv_result_t ConstantPass(ValidationState_t& _, const Instruction* inst) {
|
||||
case SpvOpConstantNull:
|
||||
if (auto error = ValidateConstantNull(_, inst)) return error;
|
||||
break;
|
||||
case SpvOpSpecConstant:
|
||||
if (auto error = ValidateSpecConstant(_, inst)) return error;
|
||||
break;
|
||||
case SpvOpSpecConstantOp:
|
||||
if (auto error = ValidateSpecConstantOp(_, inst)) return error;
|
||||
break;
|
||||
|
||||
@@ -1,286 +0,0 @@
|
||||
// Copyright (c) 2016 Google 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.
|
||||
|
||||
// Ensures Data Rules are followed according to the specifications.
|
||||
|
||||
#include "source/val/validate.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "source/diagnostic.h"
|
||||
#include "source/opcode.h"
|
||||
#include "source/operand.h"
|
||||
#include "source/val/instruction.h"
|
||||
#include "source/val/validation_state.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace val {
|
||||
namespace {
|
||||
|
||||
// Validates that the number of components in the vector is valid.
|
||||
// Vector types can only be parameterized as having 2, 3, or 4 components.
|
||||
// If the Vector16 capability is added, 8 and 16 components are also allowed.
|
||||
spv_result_t ValidateVecNumComponents(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
// Operand 2 specifies the number of components in the vector.
|
||||
auto num_components = inst->GetOperandAs<const uint32_t>(2);
|
||||
if (num_components == 2 || num_components == 3 || num_components == 4) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
if (num_components == 8 || num_components == 16) {
|
||||
if (_.HasCapability(SpvCapabilityVector16)) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Having " << num_components << " components for "
|
||||
<< spvOpcodeString(inst->opcode())
|
||||
<< " requires the Vector16 capability";
|
||||
}
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Illegal number of components (" << num_components << ") for "
|
||||
<< spvOpcodeString(inst->opcode());
|
||||
}
|
||||
|
||||
// Validates that the number of bits specifed for a float type is valid.
|
||||
// Scalar floating-point types can be parameterized only with 32-bits.
|
||||
// Float16 capability allows using a 16-bit OpTypeFloat.
|
||||
// Float16Buffer capability allows creation of a 16-bit OpTypeFloat.
|
||||
// Float64 capability allows using a 64-bit OpTypeFloat.
|
||||
spv_result_t ValidateFloatSize(ValidationState_t& _, const Instruction* inst) {
|
||||
// Operand 1 is the number of bits for this float
|
||||
auto num_bits = inst->GetOperandAs<const uint32_t>(1);
|
||||
if (num_bits == 32) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
if (num_bits == 16) {
|
||||
if (_.features().declare_float16_type) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Using a 16-bit floating point "
|
||||
<< "type requires the Float16 or Float16Buffer capability,"
|
||||
" or an extension that explicitly enables 16-bit floating point.";
|
||||
}
|
||||
if (num_bits == 64) {
|
||||
if (_.HasCapability(SpvCapabilityFloat64)) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Using a 64-bit floating point "
|
||||
<< "type requires the Float64 capability.";
|
||||
}
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Invalid number of bits (" << num_bits << ") used for OpTypeFloat.";
|
||||
}
|
||||
|
||||
// Validates that the number of bits specified for an Int type is valid.
|
||||
// Scalar integer types can be parameterized only with 32-bits.
|
||||
// Int8, Int16, and Int64 capabilities allow using 8-bit, 16-bit, and 64-bit
|
||||
// integers, respectively.
|
||||
spv_result_t ValidateIntSize(ValidationState_t& _, const Instruction* inst) {
|
||||
// Operand 1 is the number of bits for this integer.
|
||||
auto num_bits = inst->GetOperandAs<const uint32_t>(1);
|
||||
if (num_bits == 32) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
if (num_bits == 8) {
|
||||
if (_.features().declare_int8_type) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Using an 8-bit integer type requires the Int8 capability,"
|
||||
" or an extension that explicitly enables 8-bit integers.";
|
||||
}
|
||||
if (num_bits == 16) {
|
||||
if (_.features().declare_int16_type) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Using a 16-bit integer type requires the Int16 capability,"
|
||||
" or an extension that explicitly enables 16-bit integers.";
|
||||
}
|
||||
if (num_bits == 64) {
|
||||
if (_.HasCapability(SpvCapabilityInt64)) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Using a 64-bit integer type requires the Int64 capability.";
|
||||
}
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Invalid number of bits (" << num_bits << ") used for OpTypeInt.";
|
||||
}
|
||||
|
||||
// Validates that the matrix is parameterized with floating-point types.
|
||||
spv_result_t ValidateMatrixColumnType(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
// Find the component type of matrix columns (must be vector).
|
||||
// Operand 1 is the <id> of the type specified for matrix columns.
|
||||
auto type_id = inst->GetOperandAs<const uint32_t>(1);
|
||||
auto col_type_instr = _.FindDef(type_id);
|
||||
if (col_type_instr->opcode() != SpvOpTypeVector) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Columns in a matrix must be of type vector.";
|
||||
}
|
||||
|
||||
// Trace back once more to find out the type of components in the vector.
|
||||
// Operand 1 is the <id> of the type of data in the vector.
|
||||
auto comp_type_id =
|
||||
col_type_instr->words()[col_type_instr->operands()[1].offset];
|
||||
auto comp_type_instruction = _.FindDef(comp_type_id);
|
||||
if (comp_type_instruction->opcode() != SpvOpTypeFloat) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Matrix types can only be "
|
||||
"parameterized with "
|
||||
"floating-point types.";
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Validates that the matrix has 2,3, or 4 columns.
|
||||
spv_result_t ValidateMatrixNumCols(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
// Operand 2 is the number of columns in the matrix.
|
||||
auto num_cols = inst->GetOperandAs<const uint32_t>(2);
|
||||
if (num_cols != 2 && num_cols != 3 && num_cols != 4) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Matrix types can only be "
|
||||
"parameterized as having "
|
||||
"only 2, 3, or 4 columns.";
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Validates that OpSpecConstant specializes to either int or float type.
|
||||
spv_result_t ValidateSpecConstNumerical(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
// Operand 0 is the <id> of the type that we're specializing to.
|
||||
auto type_id = inst->GetOperandAs<const uint32_t>(0);
|
||||
auto type_instruction = _.FindDef(type_id);
|
||||
auto type_opcode = type_instruction->opcode();
|
||||
if (type_opcode != SpvOpTypeInt && type_opcode != SpvOpTypeFloat) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Specialization constant "
|
||||
"must be an integer or "
|
||||
"floating-point number.";
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Validates that OpSpecConstantTrue and OpSpecConstantFalse specialize to bool.
|
||||
spv_result_t ValidateSpecConstBoolean(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
// Find out the type that we're specializing to.
|
||||
auto type_instruction = _.FindDef(inst->type_id());
|
||||
if (type_instruction->opcode() != SpvOpTypeBool) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Specialization constant must be a boolean type.";
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Records the <id> of the forward pointer to be used for validation.
|
||||
spv_result_t ValidateForwardPointer(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
// Record the <id> (which is operand 0) to ensure it's used properly.
|
||||
// OpTypeStruct can only include undefined pointers that are
|
||||
// previously declared as a ForwardPointer
|
||||
return (_.RegisterForwardPointer(inst->GetOperandAs<uint32_t>(0)));
|
||||
}
|
||||
|
||||
// Validates that any undefined component of the struct is a forward pointer.
|
||||
// It is valid to declare a forward pointer, and use its <id> as one of the
|
||||
// components of a struct.
|
||||
spv_result_t ValidateStruct(ValidationState_t& _, const Instruction* inst) {
|
||||
// Struct components are operands 1, 2, etc.
|
||||
for (unsigned i = 1; i < inst->operands().size(); i++) {
|
||||
auto type_id = inst->GetOperandAs<const uint32_t>(i);
|
||||
auto type_instruction = _.FindDef(type_id);
|
||||
if (type_instruction == nullptr && !_.IsForwardPointer(type_id)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Forward reference operands in an OpTypeStruct must first be "
|
||||
"declared using OpTypeForwardPointer.";
|
||||
}
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Validates that any undefined type of the array is a forward pointer.
|
||||
// It is valid to declare a forward pointer, and use its <id> as the element
|
||||
// type of the array.
|
||||
spv_result_t ValidateArray(ValidationState_t& _, const Instruction* inst) {
|
||||
auto element_type_id = inst->GetOperandAs<const uint32_t>(1);
|
||||
auto element_type_instruction = _.FindDef(element_type_id);
|
||||
if (element_type_instruction == nullptr &&
|
||||
!_.IsForwardPointer(element_type_id)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Forward reference operands in an OpTypeArray must first be "
|
||||
"declared using OpTypeForwardPointer.";
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Validates that Data Rules are followed according to the specifications.
|
||||
// (Data Rules subsection of 2.16.1 Universal Validation Rules)
|
||||
spv_result_t DataRulesPass(ValidationState_t& _, const Instruction* inst) {
|
||||
switch (inst->opcode()) {
|
||||
case SpvOpTypeVector: {
|
||||
if (auto error = ValidateVecNumComponents(_, inst)) return error;
|
||||
break;
|
||||
}
|
||||
case SpvOpTypeFloat: {
|
||||
if (auto error = ValidateFloatSize(_, inst)) return error;
|
||||
break;
|
||||
}
|
||||
case SpvOpTypeInt: {
|
||||
if (auto error = ValidateIntSize(_, inst)) return error;
|
||||
break;
|
||||
}
|
||||
case SpvOpTypeMatrix: {
|
||||
if (auto error = ValidateMatrixColumnType(_, inst)) return error;
|
||||
if (auto error = ValidateMatrixNumCols(_, inst)) return error;
|
||||
break;
|
||||
}
|
||||
// TODO(ehsan): Add OpSpecConstantComposite validation code.
|
||||
// TODO(ehsan): Add OpSpecConstantOp validation code (if any).
|
||||
case SpvOpSpecConstant: {
|
||||
if (auto error = ValidateSpecConstNumerical(_, inst)) return error;
|
||||
break;
|
||||
}
|
||||
case SpvOpSpecConstantFalse:
|
||||
case SpvOpSpecConstantTrue: {
|
||||
if (auto error = ValidateSpecConstBoolean(_, inst)) return error;
|
||||
break;
|
||||
}
|
||||
case SpvOpTypeForwardPointer: {
|
||||
if (auto error = ValidateForwardPointer(_, inst)) return error;
|
||||
break;
|
||||
}
|
||||
case SpvOpTypeStruct: {
|
||||
if (auto error = ValidateStruct(_, inst)) return error;
|
||||
break;
|
||||
}
|
||||
case SpvOpTypeArray: {
|
||||
if (auto error = ValidateArray(_, inst)) return error;
|
||||
break;
|
||||
}
|
||||
// TODO(ehsan): add more data rules validation here.
|
||||
default: { break; }
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
@@ -191,7 +191,14 @@ spv_result_t IdPass(ValidationState_t& _, Instruction* inst) {
|
||||
ret = SPV_SUCCESS;
|
||||
}
|
||||
} else if (can_have_forward_declared_ids(i)) {
|
||||
ret = _.ForwardDeclareId(operand_word);
|
||||
if (inst->opcode() == SpvOpTypeStruct &&
|
||||
!_.IsForwardPointer(operand_word)) {
|
||||
ret = _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Operand " << _.getIdName(operand_word)
|
||||
<< " requires a previous definition";
|
||||
} else {
|
||||
ret = _.ForwardDeclareId(operand_word);
|
||||
}
|
||||
} else {
|
||||
ret = _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "ID " << _.getIdName(operand_word)
|
||||
|
||||
139
3rdparty/spirv-tools/source/val/validate_type.cpp
vendored
139
3rdparty/spirv-tools/source/val/validate_type.cpp
vendored
@@ -67,6 +67,39 @@ spv_result_t ValidateUniqueness(ValidationState_t& _, const Instruction* inst) {
|
||||
}
|
||||
|
||||
spv_result_t ValidateTypeInt(ValidationState_t& _, const Instruction* inst) {
|
||||
// Validates that the number of bits specified for an Int type is valid.
|
||||
// Scalar integer types can be parameterized only with 32-bits.
|
||||
// Int8, Int16, and Int64 capabilities allow using 8-bit, 16-bit, and 64-bit
|
||||
// integers, respectively.
|
||||
auto num_bits = inst->GetOperandAs<const uint32_t>(1);
|
||||
if (num_bits != 32) {
|
||||
if (num_bits == 8) {
|
||||
if (_.features().declare_int8_type) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Using an 8-bit integer type requires the Int8 capability,"
|
||||
" or an extension that explicitly enables 8-bit integers.";
|
||||
} else if (num_bits == 16) {
|
||||
if (_.features().declare_int16_type) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Using a 16-bit integer type requires the Int16 capability,"
|
||||
" or an extension that explicitly enables 16-bit integers.";
|
||||
} else if (num_bits == 64) {
|
||||
if (_.HasCapability(SpvCapabilityInt64)) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Using a 64-bit integer type requires the Int64 capability.";
|
||||
} else {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Invalid number of bits (" << num_bits
|
||||
<< ") used for OpTypeInt.";
|
||||
}
|
||||
}
|
||||
|
||||
const auto signedness_index = 2;
|
||||
const auto signedness = inst->GetOperandAs<uint32_t>(signedness_index);
|
||||
if (signedness != 0 && signedness != 1) {
|
||||
@@ -76,6 +109,36 @@ spv_result_t ValidateTypeInt(ValidationState_t& _, const Instruction* inst) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateTypeFloat(ValidationState_t& _, const Instruction* inst) {
|
||||
// Validates that the number of bits specified for an Int type is valid.
|
||||
// Scalar integer types can be parameterized only with 32-bits.
|
||||
// Int8, Int16, and Int64 capabilities allow using 8-bit, 16-bit, and 64-bit
|
||||
// integers, respectively.
|
||||
auto num_bits = inst->GetOperandAs<const uint32_t>(1);
|
||||
if (num_bits == 32) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
if (num_bits == 16) {
|
||||
if (_.features().declare_float16_type) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Using a 16-bit floating point "
|
||||
<< "type requires the Float16 or Float16Buffer capability,"
|
||||
" or an extension that explicitly enables 16-bit floating point.";
|
||||
}
|
||||
if (num_bits == 64) {
|
||||
if (_.HasCapability(SpvCapabilityFloat64)) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Using a 64-bit floating point "
|
||||
<< "type requires the Float64 capability.";
|
||||
}
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Invalid number of bits (" << num_bits << ") used for OpTypeFloat.";
|
||||
}
|
||||
|
||||
spv_result_t ValidateTypeVector(ValidationState_t& _, const Instruction* inst) {
|
||||
const auto component_index = 1;
|
||||
const auto component_id = inst->GetOperandAs<uint32_t>(component_index);
|
||||
@@ -85,6 +148,27 @@ spv_result_t ValidateTypeVector(ValidationState_t& _, const Instruction* inst) {
|
||||
<< "OpTypeVector Component Type <id> '" << _.getIdName(component_id)
|
||||
<< "' is not a scalar type.";
|
||||
}
|
||||
|
||||
// Validates that the number of components in the vector is valid.
|
||||
// Vector types can only be parameterized as having 2, 3, or 4 components.
|
||||
// If the Vector16 capability is added, 8 and 16 components are also allowed.
|
||||
auto num_components = inst->GetOperandAs<const uint32_t>(2);
|
||||
if (num_components == 2 || num_components == 3 || num_components == 4) {
|
||||
return SPV_SUCCESS;
|
||||
} else if (num_components == 8 || num_components == 16) {
|
||||
if (_.HasCapability(SpvCapabilityVector16)) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Having " << num_components << " components for "
|
||||
<< spvOpcodeString(inst->opcode())
|
||||
<< " requires the Vector16 capability";
|
||||
} else {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Illegal number of components (" << num_components << ") for "
|
||||
<< spvOpcodeString(inst->opcode());
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -94,9 +178,27 @@ spv_result_t ValidateTypeMatrix(ValidationState_t& _, const Instruction* inst) {
|
||||
const auto column_type = _.FindDef(column_type_id);
|
||||
if (!column_type || SpvOpTypeVector != column_type->opcode()) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpTypeMatrix Column Type <id> '" << _.getIdName(column_type_id)
|
||||
<< "' is not a vector.";
|
||||
<< "Columns in a matrix must be of type vector.";
|
||||
}
|
||||
|
||||
// Trace back once more to find out the type of components in the vector.
|
||||
// Operand 1 is the <id> of the type of data in the vector.
|
||||
const auto comp_type_id = column_type->GetOperandAs<uint32_t>(1);
|
||||
auto comp_type_instruction = _.FindDef(comp_type_id);
|
||||
if (comp_type_instruction->opcode() != SpvOpTypeFloat) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Matrix types can only be "
|
||||
"parameterized with "
|
||||
"floating-point types.";
|
||||
}
|
||||
|
||||
// Validates that the matrix has 2,3, or 4 columns.
|
||||
auto num_cols = inst->GetOperandAs<const uint32_t>(2);
|
||||
if (num_cols != 2 && num_cols != 3 && num_cols != 4) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Matrix types can only be "
|
||||
"parameterized as having "
|
||||
"only 2, 3, or 4 columns.";
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -224,6 +326,11 @@ spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) {
|
||||
for (size_t member_type_index = 1;
|
||||
member_type_index < inst->operands().size(); ++member_type_index) {
|
||||
auto member_type_id = inst->GetOperandAs<uint32_t>(member_type_index);
|
||||
if (member_type_id == inst->id()) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Structure members may not be self references";
|
||||
}
|
||||
|
||||
auto member_type = _.FindDef(member_type_id);
|
||||
if (!member_type || !spvOpcodeGeneratesType(member_type->opcode())) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
@@ -245,22 +352,6 @@ spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) {
|
||||
<< " contains structure <id> " << _.getIdName(member_type_id)
|
||||
<< ".";
|
||||
}
|
||||
if (_.IsForwardPointer(member_type_id)) {
|
||||
// If we're dealing with a forward pointer:
|
||||
// Find out the type that the pointer is pointing to (must be struct)
|
||||
// word 3 is the <id> of the type being pointed to.
|
||||
auto type_pointing_to = _.FindDef(member_type->words()[3]);
|
||||
if (type_pointing_to && type_pointing_to->opcode() != SpvOpTypeStruct) {
|
||||
// Forward declared operands of a struct may only point to a struct.
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "A forward reference operand in an OpTypeStruct must be an "
|
||||
"OpTypePointer that points to an OpTypeStruct. "
|
||||
"Found OpTypePointer that points to Op"
|
||||
<< spvOpcodeString(
|
||||
static_cast<SpvOp>(type_pointing_to->opcode()))
|
||||
<< ".";
|
||||
}
|
||||
}
|
||||
|
||||
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env) &&
|
||||
member_type->opcode() == SpvOpTypeRuntimeArray) {
|
||||
@@ -356,7 +447,6 @@ spv_result_t ValidateTypePointer(ValidationState_t& _,
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
spv_result_t ValidateTypeFunction(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
@@ -425,6 +515,13 @@ spv_result_t ValidateTypeForwardPointer(ValidationState_t& _,
|
||||
<< "pointer definition.";
|
||||
}
|
||||
|
||||
const auto pointee_type_id = pointer_type_inst->GetOperandAs<uint32_t>(2);
|
||||
const auto pointee_type = _.FindDef(pointee_type_id);
|
||||
if (!pointee_type || pointee_type->opcode() != SpvOpTypeStruct) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Forward pointers must point to a structure";
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -474,6 +571,7 @@ spv_result_t ValidateTypeCooperativeMatrixNV(ValidationState_t& _,
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
spv_result_t TypePass(ValidationState_t& _, const Instruction* inst) {
|
||||
if (!spvOpcodeGeneratesType(inst->opcode()) &&
|
||||
@@ -487,6 +585,9 @@ spv_result_t TypePass(ValidationState_t& _, const Instruction* inst) {
|
||||
case SpvOpTypeInt:
|
||||
if (auto error = ValidateTypeInt(_, inst)) return error;
|
||||
break;
|
||||
case SpvOpTypeFloat:
|
||||
if (auto error = ValidateTypeFloat(_, inst)) return error;
|
||||
break;
|
||||
case SpvOpTypeVector:
|
||||
if (auto error = ValidateTypeVector(_, inst)) return error;
|
||||
break;
|
||||
|
||||
@@ -54,7 +54,7 @@ void RunFuzzerAndReplayer(const std::string& shader,
|
||||
std::vector<uint32_t> replayer_binary_out;
|
||||
protobufs::TransformationSequence replayer_transformation_sequence_out;
|
||||
|
||||
Replayer replayer(env, true);
|
||||
Replayer replayer(env, false);
|
||||
replayer.SetMessageConsumer(kSilentConsumer);
|
||||
auto replayer_result_status = replayer.Run(
|
||||
binary_in, initial_facts, fuzzer_transformation_sequence_out,
|
||||
|
||||
@@ -125,7 +125,7 @@ void RunAndCheckShrinker(
|
||||
const std::vector<uint32_t>& expected_binary_out,
|
||||
uint32_t expected_transformations_out_size, uint32_t step_limit) {
|
||||
// Run the shrinker.
|
||||
Shrinker shrinker(target_env, step_limit, true);
|
||||
Shrinker shrinker(target_env, step_limit, false);
|
||||
shrinker.SetMessageConsumer(kSilentConsumer);
|
||||
|
||||
std::vector<uint32_t> binary_out;
|
||||
|
||||
@@ -2559,6 +2559,55 @@ TEST(TransformationAddDeadBreakTest, RespectDominanceRules8) {
|
||||
ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
|
||||
}
|
||||
|
||||
TEST(TransformationAddDeadBreakTest,
|
||||
BreakWouldDisobeyDominanceBlockOrderingRules) {
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeBool
|
||||
%9 = OpConstantTrue %6
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %16 %15 None
|
||||
OpBranch %11
|
||||
%11 = OpLabel
|
||||
OpSelectionMerge %14 None
|
||||
OpBranchConditional %9 %12 %13
|
||||
%14 = OpLabel
|
||||
OpBranch %15
|
||||
%12 = OpLabel
|
||||
OpBranch %16
|
||||
%13 = OpLabel
|
||||
OpBranch %16
|
||||
%15 = OpLabel
|
||||
OpBranch %10
|
||||
%16 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
FactManager fact_manager;
|
||||
|
||||
// Bad because 14 comes before 12 in the module, and 14 has no predecessors.
|
||||
// This means that an edge from 12 to 14 will lead to 12 dominating 14, which
|
||||
// is illegal if 12 appears after 14.
|
||||
auto bad_transformation = TransformationAddDeadBreak(12, 14, true, {});
|
||||
ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -1388,6 +1388,244 @@ TEST(TransformationAddDeadContinueTest, Miscellaneous1) {
|
||||
ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
|
||||
}
|
||||
|
||||
TEST(TransformationAddDeadContinueTest, Miscellaneous2) {
|
||||
// A miscellaneous test that exposed a bug in spirv-fuzz.
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%51 = OpTypeBool
|
||||
%395 = OpConstantTrue %51
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpBranch %389
|
||||
%389 = OpLabel
|
||||
OpLoopMerge %388 %391 None
|
||||
OpBranch %339
|
||||
%339 = OpLabel
|
||||
OpSelectionMerge %396 None
|
||||
OpBranchConditional %395 %388 %396
|
||||
%396 = OpLabel
|
||||
OpBranch %1552
|
||||
%1552 = OpLabel
|
||||
OpLoopMerge %1553 %1554 None
|
||||
OpBranch %1556
|
||||
%1556 = OpLabel
|
||||
OpLoopMerge %1557 %1570 None
|
||||
OpBranchConditional %395 %1562 %1557
|
||||
%1562 = OpLabel
|
||||
OpSelectionMerge %1570 None
|
||||
OpBranchConditional %395 %1571 %1570
|
||||
%1571 = OpLabel
|
||||
OpBranch %1557
|
||||
%1570 = OpLabel
|
||||
OpBranch %1556
|
||||
%1557 = OpLabel
|
||||
OpSelectionMerge %1586 None
|
||||
OpBranchConditional %395 %1553 %1586
|
||||
%1586 = OpLabel
|
||||
OpBranch %1553
|
||||
%1554 = OpLabel
|
||||
OpBranch %1552
|
||||
%1553 = OpLabel
|
||||
OpBranch %388
|
||||
%391 = OpLabel
|
||||
OpBranch %389
|
||||
%388 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
FactManager fact_manager;
|
||||
|
||||
// This transformation would introduce a branch from a continue target to
|
||||
// itself.
|
||||
auto bad_transformation = TransformationAddDeadContinue(1554, true, {});
|
||||
ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
|
||||
}
|
||||
|
||||
TEST(TransformationAddDeadContinueTest, Miscellaneous3) {
|
||||
// A miscellaneous test that exposed a bug in spirv-fuzz.
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%85 = OpTypeBool
|
||||
%434 = OpConstantFalse %85
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpBranch %234
|
||||
%234 = OpLabel
|
||||
OpLoopMerge %235 %236 None
|
||||
OpBranch %259
|
||||
%259 = OpLabel
|
||||
OpLoopMerge %260 %274 None
|
||||
OpBranchConditional %434 %265 %260
|
||||
%265 = OpLabel
|
||||
OpBranch %275
|
||||
%275 = OpLabel
|
||||
OpBranch %260
|
||||
%274 = OpLabel
|
||||
OpBranch %259
|
||||
%260 = OpLabel
|
||||
OpSelectionMerge %298 None
|
||||
OpBranchConditional %434 %299 %300
|
||||
%300 = OpLabel
|
||||
OpBranch %235
|
||||
%298 = OpLabel
|
||||
OpUnreachable
|
||||
%236 = OpLabel
|
||||
OpBranch %234
|
||||
%299 = OpLabel
|
||||
OpBranch %235
|
||||
%235 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
FactManager fact_manager;
|
||||
|
||||
auto bad_transformation = TransformationAddDeadContinue(299, false, {});
|
||||
|
||||
// The continue edge would connect %299 to the previously-unreachable %236,
|
||||
// making %299 dominate %236, and breaking the rule that block ordering must
|
||||
// respect dominance.
|
||||
ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
|
||||
}
|
||||
|
||||
TEST(TransformationAddDeadContinueTest, Miscellaneous4) {
|
||||
// A miscellaneous test that exposed a bug in spirv-fuzz.
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %4 "main"
|
||||
OpName %8 "i"
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeInt 32 1
|
||||
%7 = OpTypePointer Function %6
|
||||
%9 = OpConstant %6 0
|
||||
%16 = OpConstant %6 100
|
||||
%17 = OpTypeBool
|
||||
%100 = OpConstantFalse %17
|
||||
%21 = OpConstant %6 1
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%8 = OpVariable %7 Function
|
||||
OpStore %8 %9
|
||||
OpBranch %10
|
||||
%13 = OpLabel
|
||||
%20 = OpLoad %6 %8
|
||||
%22 = OpIAdd %6 %20 %21
|
||||
OpStore %8 %22
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %12 %13 None
|
||||
OpBranch %14
|
||||
%14 = OpLabel
|
||||
%15 = OpLoad %6 %8
|
||||
%18 = OpSLessThan %17 %15 %16
|
||||
OpBranchConditional %18 %11 %12
|
||||
%11 = OpLabel
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
FactManager fact_manager;
|
||||
|
||||
auto bad_transformation = TransformationAddDeadContinue(10, false, {});
|
||||
|
||||
// The continue edge would connect %10 to the previously-unreachable %13,
|
||||
// making %10 dominate %13, and breaking the rule that block ordering must
|
||||
// respect dominance.
|
||||
ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
|
||||
}
|
||||
|
||||
TEST(TransformationAddDeadContinueTest, Miscellaneous5) {
|
||||
// A miscellaneous test that exposed a bug in spirv-fuzz.
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeBool
|
||||
%7 = OpTypePointer Function %6
|
||||
%9 = OpConstantTrue %6
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpBranch %98
|
||||
%98 = OpLabel
|
||||
OpLoopMerge %100 %101 None
|
||||
OpBranch %99
|
||||
%99 = OpLabel
|
||||
OpSelectionMerge %111 None
|
||||
OpBranchConditional %9 %110 %111
|
||||
%110 = OpLabel
|
||||
OpBranch %100
|
||||
%111 = OpLabel
|
||||
%200 = OpCopyObject %6 %9
|
||||
OpBranch %101
|
||||
%101 = OpLabel
|
||||
%201 = OpCopyObject %6 %200
|
||||
OpBranchConditional %9 %98 %100
|
||||
%100 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
FactManager fact_manager;
|
||||
|
||||
auto bad_transformation = TransformationAddDeadContinue(110, true, {});
|
||||
|
||||
// The continue edge would lead to the use of %200 in block %101 no longer
|
||||
// being dominated by its definition in block %111.
|
||||
ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -597,6 +597,56 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,
|
||||
ASSERT_TRUE(IsEqual(env, after, context.get()));
|
||||
}
|
||||
|
||||
TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, OpPhi) {
|
||||
// Hand-written SPIR-V to check applicability of the transformation on an
|
||||
// OpPhi argument.
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %4 "main"
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeBool
|
||||
%7 = OpTypePointer Function %6
|
||||
%9 = OpConstantTrue %6
|
||||
%16 = OpConstantFalse %6
|
||||
%10 = OpTypeInt 32 1
|
||||
%11 = OpTypePointer Function %10
|
||||
%13 = OpConstant %10 0
|
||||
%15 = OpConstant %10 1
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpSelectionMerge %20 None
|
||||
OpBranchConditional %9 %21 %22
|
||||
%21 = OpLabel
|
||||
OpBranch %20
|
||||
%22 = OpLabel
|
||||
OpBranch %20
|
||||
%20 = OpLabel
|
||||
%23 = OpPhi %6 %9 %21 %16 %22
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
FactManager fact_manager;
|
||||
|
||||
auto replacement = TransformationReplaceBooleanConstantWithConstantBinary(
|
||||
transformation::MakeIdUseDescriptor(9, SpvOpPhi, 0, 23, 0), 13, 15,
|
||||
SpvOpSLessThan, 100);
|
||||
|
||||
ASSERT_FALSE(replacement.IsApplicable(context.get(), fact_manager));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -564,6 +564,613 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfVariables) {
|
||||
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
|
||||
}
|
||||
|
||||
TEST(TransformationReplaceIdWithSynonymTest,
|
||||
SynonymOfVariableNoGoodInFunctionCall) {
|
||||
// The following SPIR-V comes from this GLSL, with an object copy added:
|
||||
//
|
||||
// #version 310 es
|
||||
//
|
||||
// precision highp int;
|
||||
//
|
||||
// void foo(int x) { }
|
||||
//
|
||||
// void main() {
|
||||
// int a;
|
||||
// a = 2;
|
||||
// foo(a);
|
||||
// }
|
||||
const std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %4 "main"
|
||||
OpName %10 "foo(i1;"
|
||||
OpName %9 "x"
|
||||
OpName %12 "a"
|
||||
OpName %14 "param"
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeInt 32 1
|
||||
%7 = OpTypePointer Function %6
|
||||
%8 = OpTypeFunction %2 %7
|
||||
%13 = OpConstant %6 2
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%12 = OpVariable %7 Function
|
||||
%14 = OpVariable %7 Function
|
||||
OpStore %12 %13
|
||||
%15 = OpLoad %6 %12
|
||||
OpStore %14 %15
|
||||
%100 = OpCopyObject %7 %14
|
||||
%16 = OpFunctionCall %2 %10 %14
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%10 = OpFunction %2 None %8
|
||||
%9 = OpFunctionParameter %7
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
FactManager fact_manager;
|
||||
|
||||
fact_manager.AddFact(MakeFact(14, 100), context.get());
|
||||
|
||||
// Replace %14 with %100 in:
|
||||
// %16 = OpFunctionCall %2 %10 %14
|
||||
auto replacement = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(14, SpvOpFunctionCall, 1, 16, 0),
|
||||
MakeDataDescriptor(100, {}), 0);
|
||||
ASSERT_FALSE(replacement.IsApplicable(context.get(), fact_manager));
|
||||
}
|
||||
|
||||
TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
|
||||
// The following SPIR-V comes from this GLSL, with object copies added:
|
||||
//
|
||||
// #version 310 es
|
||||
//
|
||||
// precision highp float;
|
||||
// precision highp int;
|
||||
//
|
||||
// struct S {
|
||||
// int[3] a;
|
||||
// vec4 b;
|
||||
// bool c;
|
||||
// } d;
|
||||
//
|
||||
// float[20] e;
|
||||
//
|
||||
// struct T {
|
||||
// float f;
|
||||
// S g;
|
||||
// } h;
|
||||
//
|
||||
// T[4] i;
|
||||
//
|
||||
// void main() {
|
||||
// d.a[2] = 10;
|
||||
// d.b[3] = 11.0;
|
||||
// d.c = false;
|
||||
// e[17] = 12.0;
|
||||
// h.f = 13.0;
|
||||
// h.g.a[1] = 14;
|
||||
// h.g.b[0] = 15.0;
|
||||
// h.g.c = true;
|
||||
// i[0].f = 16.0;
|
||||
// i[1].g.a[0] = 17;
|
||||
// i[2].g.b[1] = 18.0;
|
||||
// i[3].g.c = true;
|
||||
// }
|
||||
const std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %4 "main"
|
||||
OpName %13 "S"
|
||||
OpMemberName %13 0 "a"
|
||||
OpMemberName %13 1 "b"
|
||||
OpMemberName %13 2 "c"
|
||||
OpName %15 "d"
|
||||
OpName %31 "e"
|
||||
OpName %35 "T"
|
||||
OpMemberName %35 0 "f"
|
||||
OpMemberName %35 1 "g"
|
||||
OpName %37 "h"
|
||||
OpName %50 "i"
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeInt 32 1
|
||||
%7 = OpTypeInt 32 0
|
||||
%8 = OpConstant %7 3
|
||||
%9 = OpTypeArray %6 %8
|
||||
%10 = OpTypeFloat 32
|
||||
%11 = OpTypeVector %10 4
|
||||
%12 = OpTypeBool
|
||||
%13 = OpTypeStruct %9 %11 %12
|
||||
%14 = OpTypePointer Private %13
|
||||
%15 = OpVariable %14 Private
|
||||
%16 = OpConstant %6 0
|
||||
%17 = OpConstant %6 2
|
||||
%18 = OpConstant %6 10
|
||||
%19 = OpTypePointer Private %6
|
||||
%21 = OpConstant %6 1
|
||||
%22 = OpConstant %10 11
|
||||
%23 = OpTypePointer Private %10
|
||||
%25 = OpConstantFalse %12
|
||||
%26 = OpTypePointer Private %12
|
||||
%28 = OpConstant %7 20
|
||||
%29 = OpTypeArray %10 %28
|
||||
%30 = OpTypePointer Private %29
|
||||
%31 = OpVariable %30 Private
|
||||
%32 = OpConstant %6 17
|
||||
%33 = OpConstant %10 12
|
||||
%35 = OpTypeStruct %10 %13
|
||||
%36 = OpTypePointer Private %35
|
||||
%37 = OpVariable %36 Private
|
||||
%38 = OpConstant %10 13
|
||||
%40 = OpConstant %6 14
|
||||
%42 = OpConstant %10 15
|
||||
%43 = OpConstant %7 0
|
||||
%45 = OpConstantTrue %12
|
||||
%47 = OpConstant %7 4
|
||||
%48 = OpTypeArray %35 %47
|
||||
%49 = OpTypePointer Private %48
|
||||
%50 = OpVariable %49 Private
|
||||
%51 = OpConstant %10 16
|
||||
%54 = OpConstant %10 18
|
||||
%55 = OpConstant %7 1
|
||||
%57 = OpConstant %6 3
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
|
||||
%100 = OpCopyObject %6 %16 ; 0
|
||||
%101 = OpCopyObject %6 %21 ; 1
|
||||
%102 = OpCopyObject %6 %17 ; 2
|
||||
%103 = OpCopyObject %6 %57 ; 3
|
||||
%104 = OpCopyObject %6 %18 ; 10
|
||||
%105 = OpCopyObject %6 %40 ; 14
|
||||
%106 = OpCopyObject %6 %32 ; 17
|
||||
%107 = OpCopyObject %7 %43 ; 0
|
||||
%108 = OpCopyObject %7 %55 ; 1
|
||||
%109 = OpCopyObject %7 %8 ; 3
|
||||
%110 = OpCopyObject %7 %47 ; 4
|
||||
%111 = OpCopyObject %7 %28 ; 20
|
||||
%112 = OpCopyObject %12 %45 ; true
|
||||
|
||||
%20 = OpAccessChain %19 %15 %16 %17
|
||||
OpStore %20 %18
|
||||
%24 = OpAccessChain %23 %15 %21 %8
|
||||
OpStore %24 %22
|
||||
%27 = OpAccessChain %26 %15 %17
|
||||
OpStore %27 %25
|
||||
%34 = OpAccessChain %23 %31 %32
|
||||
OpStore %34 %33
|
||||
%39 = OpAccessChain %23 %37 %16
|
||||
OpStore %39 %38
|
||||
%41 = OpAccessChain %19 %37 %21 %16 %21
|
||||
OpStore %41 %40
|
||||
%44 = OpAccessChain %23 %37 %21 %21 %43
|
||||
OpStore %44 %42
|
||||
%46 = OpAccessChain %26 %37 %21 %17
|
||||
OpStore %46 %45
|
||||
%52 = OpAccessChain %23 %50 %16 %16
|
||||
OpStore %52 %51
|
||||
%53 = OpAccessChain %19 %50 %21 %21 %16 %16
|
||||
OpStore %53 %32
|
||||
%56 = OpAccessChain %23 %50 %17 %21 %21 %55
|
||||
OpStore %56 %54
|
||||
%58 = OpAccessChain %26 %50 %57 %21 %17
|
||||
OpStore %58 %45
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
FactManager fact_manager;
|
||||
|
||||
// Add synonym facts corresponding to the OpCopyObject operations that have
|
||||
// been applied to all constants in the module.
|
||||
fact_manager.AddFact(MakeFact(16, 100), context.get());
|
||||
fact_manager.AddFact(MakeFact(21, 101), context.get());
|
||||
fact_manager.AddFact(MakeFact(17, 102), context.get());
|
||||
fact_manager.AddFact(MakeFact(57, 103), context.get());
|
||||
fact_manager.AddFact(MakeFact(18, 104), context.get());
|
||||
fact_manager.AddFact(MakeFact(40, 105), context.get());
|
||||
fact_manager.AddFact(MakeFact(32, 106), context.get());
|
||||
fact_manager.AddFact(MakeFact(43, 107), context.get());
|
||||
fact_manager.AddFact(MakeFact(55, 108), context.get());
|
||||
fact_manager.AddFact(MakeFact(8, 109), context.get());
|
||||
fact_manager.AddFact(MakeFact(47, 110), context.get());
|
||||
fact_manager.AddFact(MakeFact(28, 111), context.get());
|
||||
fact_manager.AddFact(MakeFact(45, 112), context.get());
|
||||
|
||||
// Replacements of the form %16 -> %100
|
||||
|
||||
// %20 = OpAccessChain %19 %15 *%16* %17
|
||||
// Corresponds to d.*a*[2]
|
||||
// The index %16 used for a cannot be replaced
|
||||
auto replacement1 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 1, 20, 0),
|
||||
MakeDataDescriptor(100, {}), 0);
|
||||
ASSERT_FALSE(replacement1.IsApplicable(context.get(), fact_manager));
|
||||
|
||||
// %39 = OpAccessChain %23 %37 *%16*
|
||||
// Corresponds to h.*f*
|
||||
// The index %16 used for f cannot be replaced
|
||||
auto replacement2 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 1, 39, 0),
|
||||
MakeDataDescriptor(100, {}), 0);
|
||||
ASSERT_FALSE(replacement2.IsApplicable(context.get(), fact_manager));
|
||||
|
||||
// %41 = OpAccessChain %19 %37 %21 *%16* %21
|
||||
// Corresponds to h.g.*a*[1]
|
||||
// The index %16 used for a cannot be replaced
|
||||
auto replacement3 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 2, 41, 0),
|
||||
MakeDataDescriptor(100, {}), 0);
|
||||
ASSERT_FALSE(replacement3.IsApplicable(context.get(), fact_manager));
|
||||
|
||||
// %52 = OpAccessChain %23 %50 *%16* %16
|
||||
// Corresponds to i[*0*].f
|
||||
// The index %16 used for 0 *can* be replaced
|
||||
auto replacement4 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 1, 52, 0),
|
||||
MakeDataDescriptor(100, {}), 0);
|
||||
ASSERT_TRUE(replacement4.IsApplicable(context.get(), fact_manager));
|
||||
replacement4.Apply(context.get(), &fact_manager);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
// %52 = OpAccessChain %23 %50 %16 *%16*
|
||||
// Corresponds to i[0].*f*
|
||||
// The index %16 used for f cannot be replaced
|
||||
auto replacement5 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 2, 52, 0),
|
||||
MakeDataDescriptor(100, {}), 0);
|
||||
ASSERT_FALSE(replacement5.IsApplicable(context.get(), fact_manager));
|
||||
|
||||
// %53 = OpAccessChain %19 %50 %21 %21 *%16* %16
|
||||
// Corresponds to i[1].g.*a*[0]
|
||||
// The index %16 used for a cannot be replaced
|
||||
auto replacement6 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 3, 53, 0),
|
||||
MakeDataDescriptor(100, {}), 0);
|
||||
ASSERT_FALSE(replacement6.IsApplicable(context.get(), fact_manager));
|
||||
|
||||
// %53 = OpAccessChain %19 %50 %21 %21 %16 *%16*
|
||||
// Corresponds to i[1].g.a[*0*]
|
||||
// The index %16 used for 0 *can* be replaced
|
||||
auto replacement7 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 4, 53, 0),
|
||||
MakeDataDescriptor(100, {}), 0);
|
||||
ASSERT_TRUE(replacement7.IsApplicable(context.get(), fact_manager));
|
||||
replacement7.Apply(context.get(), &fact_manager);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
// Replacements of the form %21 -> %101
|
||||
|
||||
// %24 = OpAccessChain %23 %15 *%21* %8
|
||||
// Corresponds to d.*b*[3]
|
||||
// The index %24 used for b cannot be replaced
|
||||
auto replacement8 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 24, 0),
|
||||
MakeDataDescriptor(101, {}), 0);
|
||||
ASSERT_FALSE(replacement8.IsApplicable(context.get(), fact_manager));
|
||||
|
||||
// %41 = OpAccessChain %19 %37 *%21* %16 %21
|
||||
// Corresponds to h.*g*.a[1]
|
||||
// The index %24 used for g cannot be replaced
|
||||
auto replacement9 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 41, 0),
|
||||
MakeDataDescriptor(101, {}), 0);
|
||||
ASSERT_FALSE(replacement9.IsApplicable(context.get(), fact_manager));
|
||||
|
||||
// %41 = OpAccessChain %19 %37 %21 %16 *%21*
|
||||
// Corresponds to h.g.a[*1*]
|
||||
// The index %24 used for 1 *can* be replaced
|
||||
auto replacement10 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 3, 41, 0),
|
||||
MakeDataDescriptor(101, {}), 0);
|
||||
ASSERT_TRUE(replacement10.IsApplicable(context.get(), fact_manager));
|
||||
replacement10.Apply(context.get(), &fact_manager);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
// %44 = OpAccessChain %23 %37 *%21* %21 %43
|
||||
// Corresponds to h.*g*.b[0]
|
||||
// The index %24 used for g cannot be replaced
|
||||
auto replacement11 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 44, 0),
|
||||
MakeDataDescriptor(101, {}), 0);
|
||||
ASSERT_FALSE(replacement11.IsApplicable(context.get(), fact_manager));
|
||||
|
||||
// %44 = OpAccessChain %23 %37 %21 *%21* %43
|
||||
// Corresponds to h.g.*b*[0]
|
||||
// The index %24 used for b cannot be replaced
|
||||
auto replacement12 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 2, 44, 0),
|
||||
MakeDataDescriptor(101, {}), 0);
|
||||
ASSERT_FALSE(replacement12.IsApplicable(context.get(), fact_manager));
|
||||
|
||||
// %46 = OpAccessChain %26 %37 *%21* %17
|
||||
// Corresponds to h.*g*.c
|
||||
// The index %24 used for g cannot be replaced
|
||||
auto replacement13 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 46, 0),
|
||||
MakeDataDescriptor(101, {}), 0);
|
||||
ASSERT_FALSE(replacement13.IsApplicable(context.get(), fact_manager));
|
||||
|
||||
// %53 = OpAccessChain %19 %50 *%21* %21 %16 %16
|
||||
// Corresponds to i[*1*].g.a[0]
|
||||
// The index %24 used for 1 *can* be replaced
|
||||
auto replacement14 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 53, 0),
|
||||
MakeDataDescriptor(101, {}), 0);
|
||||
ASSERT_TRUE(replacement14.IsApplicable(context.get(), fact_manager));
|
||||
replacement14.Apply(context.get(), &fact_manager);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
// %53 = OpAccessChain %19 %50 %21 *%21* %16 %16
|
||||
// Corresponds to i[1].*g*.a[0]
|
||||
// The index %24 used for g cannot be replaced
|
||||
auto replacement15 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 2, 53, 0),
|
||||
MakeDataDescriptor(101, {}), 0);
|
||||
ASSERT_FALSE(replacement15.IsApplicable(context.get(), fact_manager));
|
||||
|
||||
// %56 = OpAccessChain %23 %50 %17 *%21* %21 %55
|
||||
// Corresponds to i[2].*g*.b[1]
|
||||
// The index %24 used for g cannot be replaced
|
||||
auto replacement16 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 2, 56, 0),
|
||||
MakeDataDescriptor(101, {}), 0);
|
||||
ASSERT_FALSE(replacement16.IsApplicable(context.get(), fact_manager));
|
||||
|
||||
// %56 = OpAccessChain %23 %50 %17 %21 *%21* %55
|
||||
// Corresponds to i[2].g.*b*[1]
|
||||
// The index %24 used for b cannot be replaced
|
||||
auto replacement17 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 3, 56, 0),
|
||||
MakeDataDescriptor(101, {}), 0);
|
||||
ASSERT_FALSE(replacement17.IsApplicable(context.get(), fact_manager));
|
||||
|
||||
// %58 = OpAccessChain %26 %50 %57 *%21* %17
|
||||
// Corresponds to i[3].*g*.c
|
||||
// The index %24 used for g cannot be replaced
|
||||
auto replacement18 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 2, 58, 0),
|
||||
MakeDataDescriptor(101, {}), 0);
|
||||
ASSERT_FALSE(replacement18.IsApplicable(context.get(), fact_manager));
|
||||
|
||||
// Replacements of the form %17 -> %102
|
||||
|
||||
// %20 = OpAccessChain %19 %15 %16 %17
|
||||
// Corresponds to d.a[*2*]
|
||||
// The index %17 used for 2 *can* be replaced
|
||||
auto replacement19 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 2, 20, 0),
|
||||
MakeDataDescriptor(102, {}), 0);
|
||||
ASSERT_TRUE(replacement19.IsApplicable(context.get(), fact_manager));
|
||||
replacement19.Apply(context.get(), &fact_manager);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
// %27 = OpAccessChain %26 %15 %17
|
||||
// Corresponds to d.c
|
||||
// The index %17 used for c cannot be replaced
|
||||
auto replacement20 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 1, 27, 0),
|
||||
MakeDataDescriptor(102, {}), 0);
|
||||
ASSERT_FALSE(replacement20.IsApplicable(context.get(), fact_manager));
|
||||
|
||||
// %46 = OpAccessChain %26 %37 %21 %17
|
||||
// Corresponds to h.g.*c*
|
||||
// The index %17 used for c cannot be replaced
|
||||
auto replacement21 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 2, 46, 0),
|
||||
MakeDataDescriptor(102, {}), 0);
|
||||
ASSERT_FALSE(replacement21.IsApplicable(context.get(), fact_manager));
|
||||
|
||||
// %56 = OpAccessChain %23 %50 %17 %21 %21 %55
|
||||
// Corresponds to i[*2*].g.b[1]
|
||||
// The index %17 used for 2 *can* be replaced
|
||||
auto replacement22 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 1, 56, 0),
|
||||
MakeDataDescriptor(102, {}), 0);
|
||||
ASSERT_TRUE(replacement22.IsApplicable(context.get(), fact_manager));
|
||||
replacement22.Apply(context.get(), &fact_manager);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
// %58 = OpAccessChain %26 %50 %57 %21 %17
|
||||
// Corresponds to i[3].g.*c*
|
||||
// The index %17 used for c cannot be replaced
|
||||
auto replacement23 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 3, 58, 0),
|
||||
MakeDataDescriptor(102, {}), 0);
|
||||
ASSERT_FALSE(replacement23.IsApplicable(context.get(), fact_manager));
|
||||
|
||||
// Replacements of the form %57 -> %103
|
||||
|
||||
// %58 = OpAccessChain %26 %50 *%57* %21 %17
|
||||
// Corresponds to i[*3*].g.c
|
||||
// The index %57 used for 3 *can* be replaced
|
||||
auto replacement24 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(57, SpvOpAccessChain, 1, 58, 0),
|
||||
MakeDataDescriptor(103, {}), 0);
|
||||
ASSERT_TRUE(replacement24.IsApplicable(context.get(), fact_manager));
|
||||
replacement24.Apply(context.get(), &fact_manager);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
// Replacements of the form %32 -> %106
|
||||
|
||||
// %34 = OpAccessChain %23 %31 *%32*
|
||||
// Corresponds to e[*17*]
|
||||
// The index %32 used for 17 *can* be replaced
|
||||
auto replacement25 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(32, SpvOpAccessChain, 1, 34, 0),
|
||||
MakeDataDescriptor(106, {}), 0);
|
||||
ASSERT_TRUE(replacement25.IsApplicable(context.get(), fact_manager));
|
||||
replacement25.Apply(context.get(), &fact_manager);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
// Replacements of the form %43 -> %107
|
||||
|
||||
// %44 = OpAccessChain %23 %37 %21 %21 *%43*
|
||||
// Corresponds to h.g.b[*0*]
|
||||
// The index %43 used for 0 *can* be replaced
|
||||
auto replacement26 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(43, SpvOpAccessChain, 3, 44, 0),
|
||||
MakeDataDescriptor(107, {}), 0);
|
||||
ASSERT_TRUE(replacement26.IsApplicable(context.get(), fact_manager));
|
||||
replacement26.Apply(context.get(), &fact_manager);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
// Replacements of the form %55 -> %108
|
||||
|
||||
// %56 = OpAccessChain %23 %50 %17 %21 %21 *%55*
|
||||
// Corresponds to i[2].g.b[*1*]
|
||||
// The index %55 used for 1 *can* be replaced
|
||||
auto replacement27 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(55, SpvOpAccessChain, 4, 56, 0),
|
||||
MakeDataDescriptor(108, {}), 0);
|
||||
ASSERT_TRUE(replacement27.IsApplicable(context.get(), fact_manager));
|
||||
replacement27.Apply(context.get(), &fact_manager);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
// Replacements of the form %8 -> %109
|
||||
|
||||
// %24 = OpAccessChain %23 %15 %21 *%8*
|
||||
// Corresponds to d.b[*3*]
|
||||
// The index %8 used for 3 *can* be replaced
|
||||
auto replacement28 = TransformationReplaceIdWithSynonym(
|
||||
transformation::MakeIdUseDescriptor(8, SpvOpAccessChain, 2, 24, 0),
|
||||
MakeDataDescriptor(109, {}), 0);
|
||||
ASSERT_TRUE(replacement28.IsApplicable(context.get(), fact_manager));
|
||||
replacement28.Apply(context.get(), &fact_manager);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
const std::string after_transformation = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %4 "main"
|
||||
OpName %13 "S"
|
||||
OpMemberName %13 0 "a"
|
||||
OpMemberName %13 1 "b"
|
||||
OpMemberName %13 2 "c"
|
||||
OpName %15 "d"
|
||||
OpName %31 "e"
|
||||
OpName %35 "T"
|
||||
OpMemberName %35 0 "f"
|
||||
OpMemberName %35 1 "g"
|
||||
OpName %37 "h"
|
||||
OpName %50 "i"
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeInt 32 1
|
||||
%7 = OpTypeInt 32 0
|
||||
%8 = OpConstant %7 3
|
||||
%9 = OpTypeArray %6 %8
|
||||
%10 = OpTypeFloat 32
|
||||
%11 = OpTypeVector %10 4
|
||||
%12 = OpTypeBool
|
||||
%13 = OpTypeStruct %9 %11 %12
|
||||
%14 = OpTypePointer Private %13
|
||||
%15 = OpVariable %14 Private
|
||||
%16 = OpConstant %6 0
|
||||
%17 = OpConstant %6 2
|
||||
%18 = OpConstant %6 10
|
||||
%19 = OpTypePointer Private %6
|
||||
%21 = OpConstant %6 1
|
||||
%22 = OpConstant %10 11
|
||||
%23 = OpTypePointer Private %10
|
||||
%25 = OpConstantFalse %12
|
||||
%26 = OpTypePointer Private %12
|
||||
%28 = OpConstant %7 20
|
||||
%29 = OpTypeArray %10 %28
|
||||
%30 = OpTypePointer Private %29
|
||||
%31 = OpVariable %30 Private
|
||||
%32 = OpConstant %6 17
|
||||
%33 = OpConstant %10 12
|
||||
%35 = OpTypeStruct %10 %13
|
||||
%36 = OpTypePointer Private %35
|
||||
%37 = OpVariable %36 Private
|
||||
%38 = OpConstant %10 13
|
||||
%40 = OpConstant %6 14
|
||||
%42 = OpConstant %10 15
|
||||
%43 = OpConstant %7 0
|
||||
%45 = OpConstantTrue %12
|
||||
%47 = OpConstant %7 4
|
||||
%48 = OpTypeArray %35 %47
|
||||
%49 = OpTypePointer Private %48
|
||||
%50 = OpVariable %49 Private
|
||||
%51 = OpConstant %10 16
|
||||
%54 = OpConstant %10 18
|
||||
%55 = OpConstant %7 1
|
||||
%57 = OpConstant %6 3
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
|
||||
%100 = OpCopyObject %6 %16 ; 0
|
||||
%101 = OpCopyObject %6 %21 ; 1
|
||||
%102 = OpCopyObject %6 %17 ; 2
|
||||
%103 = OpCopyObject %6 %57 ; 3
|
||||
%104 = OpCopyObject %6 %18 ; 10
|
||||
%105 = OpCopyObject %6 %40 ; 14
|
||||
%106 = OpCopyObject %6 %32 ; 17
|
||||
%107 = OpCopyObject %7 %43 ; 0
|
||||
%108 = OpCopyObject %7 %55 ; 1
|
||||
%109 = OpCopyObject %7 %8 ; 3
|
||||
%110 = OpCopyObject %7 %47 ; 4
|
||||
%111 = OpCopyObject %7 %28 ; 20
|
||||
%112 = OpCopyObject %12 %45 ; true
|
||||
|
||||
%20 = OpAccessChain %19 %15 %16 %102
|
||||
OpStore %20 %18
|
||||
%24 = OpAccessChain %23 %15 %21 %109
|
||||
OpStore %24 %22
|
||||
%27 = OpAccessChain %26 %15 %17
|
||||
OpStore %27 %25
|
||||
%34 = OpAccessChain %23 %31 %106
|
||||
OpStore %34 %33
|
||||
%39 = OpAccessChain %23 %37 %16
|
||||
OpStore %39 %38
|
||||
%41 = OpAccessChain %19 %37 %21 %16 %101
|
||||
OpStore %41 %40
|
||||
%44 = OpAccessChain %23 %37 %21 %21 %107
|
||||
OpStore %44 %42
|
||||
%46 = OpAccessChain %26 %37 %21 %17
|
||||
OpStore %46 %45
|
||||
%52 = OpAccessChain %23 %50 %100 %16
|
||||
OpStore %52 %51
|
||||
%53 = OpAccessChain %19 %50 %101 %21 %16 %100
|
||||
OpStore %53 %32
|
||||
%56 = OpAccessChain %23 %50 %102 %21 %21 %108
|
||||
OpStore %56 %54
|
||||
%58 = OpAccessChain %26 %50 %103 %21 %17
|
||||
OpStore %58 %45
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -687,12 +687,12 @@ TEST(TransformationSplitBlockTest, SplitOpPhiWithSinglePredecessor) {
|
||||
%10 = OpVariable %7 Function
|
||||
OpStore %8 %9
|
||||
%11 = OpLoad %6 %8
|
||||
OpBranch %20
|
||||
%20 = OpLabel
|
||||
%21 = OpPhi %6 %11 %5
|
||||
OpStore %10 %21
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
OpBranch %20
|
||||
%20 = OpLabel
|
||||
%21 = OpPhi %6 %11 %5
|
||||
OpStore %10 %21
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
@@ -732,14 +732,14 @@ TEST(TransformationSplitBlockTest, SplitOpPhiWithSinglePredecessor) {
|
||||
%10 = OpVariable %7 Function
|
||||
OpStore %8 %9
|
||||
%11 = OpLoad %6 %8
|
||||
OpBranch %20
|
||||
%20 = OpLabel
|
||||
OpBranch %100
|
||||
%100 = OpLabel
|
||||
%21 = OpPhi %6 %11 %20
|
||||
OpStore %10 %21
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
OpBranch %20
|
||||
%20 = OpLabel
|
||||
OpBranch %100
|
||||
%100 = OpLabel
|
||||
%21 = OpPhi %6 %11 %20
|
||||
OpStore %10 %21
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
ASSERT_TRUE(IsEqual(env, after_split, context.get()));
|
||||
}
|
||||
|
||||
11
3rdparty/spirv-tools/test/opt/fold_test.cpp
vendored
11
3rdparty/spirv-tools/test/opt/fold_test.cpp
vendored
@@ -3333,7 +3333,16 @@ INSTANTIATE_TEST_SUITE_P(CompositeExtractFoldingTest, GeneralInstructionFoldingT
|
||||
"%3 = OpCompositeExtract %float %2 4\n" +
|
||||
"OpReturn\n" +
|
||||
"OpFunctionEnd",
|
||||
3, 0)
|
||||
3, 0),
|
||||
// Test case 14: Fold OpCompositeExtract with no indexes.
|
||||
InstructionFoldingCase<uint32_t>(
|
||||
Header() + "%main = OpFunction %void None %void_func\n" +
|
||||
"%main_lab = OpLabel\n" +
|
||||
"%2 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1\n" +
|
||||
"%3 = OpCompositeExtract %v4float %2\n" +
|
||||
"OpReturn\n" +
|
||||
"OpFunctionEnd",
|
||||
3, 2)
|
||||
));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(CompositeConstructFoldingTest, GeneralInstructionFoldingTest,
|
||||
|
||||
@@ -820,6 +820,113 @@ OpFunctionEnd
|
||||
SinglePassRunAndCheck<LocalAccessChainConvertPass>(test, test, false, true);
|
||||
}
|
||||
|
||||
TEST_F(LocalAccessChainConvertTest, IdOverflowReplacingLoad) {
|
||||
const std::string text =
|
||||
R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "PSMain"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpDecorate %10 Location 47360
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%float = OpTypeFloat 32
|
||||
%v4float = OpTypeVector %float 4
|
||||
%_struct_8 = OpTypeStruct %v4float
|
||||
%_ptr_Function__struct_8 = OpTypePointer Function %_struct_8
|
||||
%int = OpTypeInt 32 1
|
||||
%int_0 = OpConstant %int 0
|
||||
%_ptr_Function_v4float = OpTypePointer Function %v4float
|
||||
%4 = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
%10 = OpVariable %_ptr_Function__struct_8 Function
|
||||
%4194301 = OpAccessChain %_ptr_Function_v4float %10 %int_0
|
||||
%4194302 = OpLoad %v4float %4194301
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
|
||||
std::vector<Message> messages = {
|
||||
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
|
||||
SetMessageConsumer(GetTestMessageConsumer(messages));
|
||||
auto result = SinglePassRunToBinary<LocalAccessChainConvertPass>(text, true);
|
||||
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
|
||||
}
|
||||
|
||||
TEST_F(LocalAccessChainConvertTest, IdOverflowReplacingStore1) {
|
||||
const std::string text =
|
||||
R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "PSMain"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpDecorate %10 Location 47360
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%float = OpTypeFloat 32
|
||||
%v4float = OpTypeVector %float 4
|
||||
%_struct_7 = OpTypeStruct %v4float
|
||||
%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7
|
||||
%int = OpTypeInt 32 1
|
||||
%int_0 = OpConstant %int 0
|
||||
%_ptr_Function_v4float = OpTypePointer Function %v4float
|
||||
%13 = OpConstantNull %v4float
|
||||
%4 = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
%10 = OpVariable %_ptr_Function__struct_7 Function
|
||||
%4194302 = OpAccessChain %_ptr_Function_v4float %10 %int_0
|
||||
OpStore %4194302 %13
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
|
||||
std::vector<Message> messages = {
|
||||
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
|
||||
SetMessageConsumer(GetTestMessageConsumer(messages));
|
||||
auto result = SinglePassRunToBinary<LocalAccessChainConvertPass>(text, true);
|
||||
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
|
||||
}
|
||||
|
||||
TEST_F(LocalAccessChainConvertTest, IdOverflowReplacingStore2) {
|
||||
const std::string text =
|
||||
R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "PSMain"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpDecorate %10 Location 47360
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%float = OpTypeFloat 32
|
||||
%v4float = OpTypeVector %float 4
|
||||
%_struct_7 = OpTypeStruct %v4float
|
||||
%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7
|
||||
%int = OpTypeInt 32 1
|
||||
%int_0 = OpConstant %int 0
|
||||
%_ptr_Function_v4float = OpTypePointer Function %v4float
|
||||
%13 = OpConstantNull %v4float
|
||||
%4 = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
%10 = OpVariable %_ptr_Function__struct_7 Function
|
||||
%4194301 = OpAccessChain %_ptr_Function_v4float %10 %int_0
|
||||
OpStore %4194301 %13
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
|
||||
std::vector<Message> messages = {
|
||||
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
|
||||
SetMessageConsumer(GetTestMessageConsumer(messages));
|
||||
auto result = SinglePassRunToBinary<LocalAccessChainConvertPass>(text, true);
|
||||
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
|
||||
}
|
||||
|
||||
// TODO(greg-lunarg): Add tests to verify handling of these cases:
|
||||
//
|
||||
// Assorted vector and matrix types
|
||||
|
||||
@@ -1899,6 +1899,37 @@ TEST_F(ScalarReplacementTest, RelaxedPrecisionMemberDecoration) {
|
||||
SinglePassRunAndMatch<ScalarReplacementPass>(text, true);
|
||||
}
|
||||
|
||||
TEST_F(ScalarReplacementTest, ExtractWithNoIndex) {
|
||||
const std::string text = R"(
|
||||
; CHECK: [[var1:%\w+]] = OpVariable %_ptr_Function_float Function
|
||||
; CHECK: [[var2:%\w+]] = OpVariable %_ptr_Function_float Function
|
||||
; CHECK: [[ld1:%\w+]] = OpLoad %float [[var1]]
|
||||
; CHECK: [[ld2:%\w+]] = OpLoad %float [[var2]]
|
||||
; CHECK: [[constr:%\w+]] = OpCompositeConstruct {{%\w+}} [[ld2]] [[ld1]]
|
||||
; CHECK: OpCompositeExtract {{%\w+}} [[constr]]
|
||||
OpCapability Shader
|
||||
OpExtension ""
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %1 "main"
|
||||
OpExecutionMode %1 OriginUpperLeft
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%float = OpTypeFloat 32
|
||||
%_struct_5 = OpTypeStruct %float %float
|
||||
%_ptr_Private__struct_5 = OpTypePointer Private %_struct_5
|
||||
%_ptr_Function__struct_5 = OpTypePointer Function %_struct_5
|
||||
%1 = OpFunction %void Inline %3
|
||||
%8 = OpLabel
|
||||
%9 = OpVariable %_ptr_Function__struct_5 Function
|
||||
%10 = OpLoad %_struct_5 %9
|
||||
%11 = OpCompositeExtract %_struct_5 %10
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndMatch<ScalarReplacementPass>(text, true);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -338,6 +338,46 @@ OpFunctionEnd
|
||||
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
|
||||
}
|
||||
|
||||
TEST_F(WrapOpKillTest, IdBoundOverflow5) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 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
|
||||
%_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
|
||||
OpKill
|
||||
OpFunctionEnd
|
||||
%32257 = OpFunction %_struct_7 None %18
|
||||
%28 = OpLabel
|
||||
OpUnreachable
|
||||
OpFunctionEnd
|
||||
%64821 = OpFunction %_struct_7 Inline %18
|
||||
%4194295 = OpLabel
|
||||
OpKill
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
|
||||
std::vector<Message> messages = {
|
||||
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
|
||||
SetMessageConsumer(GetTestMessageConsumer(messages));
|
||||
auto result = SinglePassRunToBinary<WrapOpKill>(text, true);
|
||||
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
45
3rdparty/spirv-tools/test/val/val_data_test.cpp
vendored
45
3rdparty/spirv-tools/test/val/val_data_test.cpp
vendored
@@ -629,15 +629,26 @@ TEST_F(ValidateData, specialize_boolean) {
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateData, specialize_boolean_to_int) {
|
||||
TEST_F(ValidateData, specialize_boolean_true_to_int) {
|
||||
std::string str = header + R"(
|
||||
%2 = OpTypeInt 32 1
|
||||
%3 = OpSpecConstantTrue %2)";
|
||||
CompileSuccessfully(str.c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("OpSpecConstantTrue Result Type <id> '1[%int]' is not "
|
||||
"a boolean type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateData, specialize_boolean_false_to_int) {
|
||||
std::string str = header + R"(
|
||||
%2 = OpTypeInt 32 1
|
||||
%3 = OpSpecConstantTrue %2
|
||||
%4 = OpSpecConstantFalse %2)";
|
||||
CompileSuccessfully(str.c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Specialization constant must be a boolean"));
|
||||
HasSubstr("OpSpecConstantFalse Result Type <id> '1[%int]' is not "
|
||||
"a boolean type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateData, missing_forward_pointer_decl) {
|
||||
@@ -648,7 +659,7 @@ TEST_F(ValidateData, missing_forward_pointer_decl) {
|
||||
CompileSuccessfully(str.c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("must first be declared using OpTypeForwardPointer"));
|
||||
HasSubstr("Operand 3[%3] requires a previous definition"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateData, missing_forward_pointer_decl_self_reference) {
|
||||
@@ -658,8 +669,9 @@ TEST_F(ValidateData, missing_forward_pointer_decl_self_reference) {
|
||||
)";
|
||||
CompileSuccessfully(str.c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("must first be declared using OpTypeForwardPointer"));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Operand 2[%_struct_2] requires a previous definition"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateData, forward_pointer_missing_definition) {
|
||||
@@ -698,9 +710,7 @@ OpTypeForwardPointer %_ptr_Generic_struct_A Generic
|
||||
CompileSuccessfully(str.c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("A forward reference operand in an OpTypeStruct must "
|
||||
"be an OpTypePointer that points to an OpTypeStruct. "
|
||||
"Found OpTypePointer that points to OpTypeInt."));
|
||||
HasSubstr("Forward pointers must point to a structure"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateData, struct_forward_pointer_good) {
|
||||
@@ -934,23 +944,6 @@ TEST_F(ValidateData, webgpu_RTA_not_at_end_of_struct) {
|
||||
"OpTypeStruct %_runtimearr_uint %uint\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateData, invalid_forward_reference_in_array) {
|
||||
std::string str = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_1 = OpConstant %uint 1
|
||||
%_arr_3_uint_1 = OpTypeArray %_arr_3_uint_1 %uint_1
|
||||
)";
|
||||
|
||||
CompileSuccessfully(str.c_str(), SPV_ENV_UNIVERSAL_1_3);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Forward reference operands in an OpTypeArray must "
|
||||
"first be declared using OpTypeForwardPointer."));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
||||
32
3rdparty/spirv-tools/test/val/val_id_test.cpp
vendored
32
3rdparty/spirv-tools/test/val/val_id_test.cpp
vendored
@@ -1476,7 +1476,8 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantTrueBad) {
|
||||
CompileSuccessfully(spirv.c_str());
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Specialization constant must be a boolean type."));
|
||||
HasSubstr("OpSpecConstantTrue Result Type <id> '1[%void]' is not "
|
||||
"a boolean type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateIdWithMessage, OpSpecConstantFalseGood) {
|
||||
@@ -1492,8 +1493,10 @@ TEST_F(ValidateIdWithMessage, OpSpecConstantFalseBad) {
|
||||
%2 = OpSpecConstantFalse %1)";
|
||||
CompileSuccessfully(spirv.c_str());
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Specialization constant must be a boolean type."));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("OpSpecConstantFalse Result Type <id> '1[%void]' is not "
|
||||
"a boolean type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateIdWithMessage, OpSpecConstantGood) {
|
||||
@@ -5176,8 +5179,7 @@ TEST_F(ValidateIdWithMessage, UndefinedTypeId) {
|
||||
CompileSuccessfully(spirv.c_str());
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Forward reference operands in an OpTypeStruct must "
|
||||
"first be declared using OpTypeForwardPointer."));
|
||||
HasSubstr("Operand 2[%2] requires a previous definition"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateIdWithMessage, UndefinedIdScope) {
|
||||
@@ -6462,6 +6464,26 @@ TEST_F(ValidateIdWithMessage, OpTypeForwardPointerWrongStorageClass) {
|
||||
"pointer definition.\n OpTypeForwardPointer "
|
||||
"%_ptr_Function_int CrossWorkgroup"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateIdWithMessage, MissingForwardPointer) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Linkage
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical Simple
|
||||
%float = OpTypeFloat 32
|
||||
%_struct_9 = OpTypeStruct %float %_ptr_Uniform__struct_9
|
||||
%_ptr_Uniform__struct_9 = OpTypePointer Uniform %_struct_9
|
||||
%1278 = OpVariable %_ptr_Uniform__struct_9 Uniform
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Operand 3[%_ptr_Uniform__struct_2] requires a previous definition"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -1415,7 +1415,8 @@ TEST_F(ValidateSSA, TypeForwardPointerForwardReference) {
|
||||
OpName %intptrt "intptrt"
|
||||
OpTypeForwardPointer %intptrt UniformConstant
|
||||
%uint = OpTypeInt 32 0
|
||||
%intptrt = OpTypePointer UniformConstant %uint
|
||||
%struct = OpTypeStruct %uint
|
||||
%intptrt = OpTypePointer UniformConstant %struct
|
||||
)";
|
||||
|
||||
CompileSuccessfully(str);
|
||||
|
||||
@@ -210,9 +210,11 @@ OpMemoryModel Physical32 OpenCL
|
||||
OpTypeForwardPointer %ptr Generic
|
||||
OpTypeForwardPointer %ptr2 Generic
|
||||
%intt = OpTypeInt 32 0
|
||||
%int_struct = OpTypeStruct %intt
|
||||
%floatt = OpTypeFloat 32
|
||||
%ptr = OpTypePointer Generic %intt
|
||||
%ptr2 = OpTypePointer Generic %floatt
|
||||
%ptr = OpTypePointer Generic %int_struct
|
||||
%float_struct = OpTypeStruct %floatt
|
||||
%ptr2 = OpTypePointer Generic %float_struct
|
||||
)";
|
||||
CompileSuccessfully(str.c_str());
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
|
||||
75
3rdparty/spirv-tools/tools/CMakeLists.txt
vendored
75
3rdparty/spirv-tools/tools/CMakeLists.txt
vendored
@@ -1,75 +0,0 @@
|
||||
# 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.
|
||||
|
||||
if (NOT ${SPIRV_SKIP_EXECUTABLES})
|
||||
add_subdirectory(lesspipe)
|
||||
endif()
|
||||
add_subdirectory(emacs)
|
||||
|
||||
# Add a SPIR-V Tools command line tool. Signature:
|
||||
# add_spvtools_tool(
|
||||
# TARGET target_name
|
||||
# SRCS src_file1.cpp src_file2.cpp
|
||||
# LIBS lib_target1 lib_target2
|
||||
# )
|
||||
function(add_spvtools_tool)
|
||||
set(one_value_args TARGET)
|
||||
set(multi_value_args SRCS LIBS)
|
||||
cmake_parse_arguments(
|
||||
ARG "" "${one_value_args}" "${multi_value_args}" ${ARGN})
|
||||
|
||||
add_executable(${ARG_TARGET} ${ARG_SRCS})
|
||||
spvtools_default_compile_options(${ARG_TARGET})
|
||||
target_link_libraries(${ARG_TARGET} PRIVATE ${ARG_LIBS})
|
||||
target_include_directories(${ARG_TARGET} PRIVATE
|
||||
${spirv-tools_SOURCE_DIR}
|
||||
${spirv-tools_BINARY_DIR}
|
||||
)
|
||||
set_property(TARGET ${ARG_TARGET} PROPERTY FOLDER "SPIRV-Tools executables")
|
||||
endfunction()
|
||||
|
||||
if (NOT ${SPIRV_SKIP_EXECUTABLES})
|
||||
add_spvtools_tool(TARGET spirv-as SRCS as/as.cpp LIBS ${SPIRV_TOOLS})
|
||||
add_spvtools_tool(TARGET spirv-dis SRCS dis/dis.cpp LIBS ${SPIRV_TOOLS})
|
||||
add_spvtools_tool(TARGET spirv-val SRCS val/val.cpp util/cli_consumer.cpp LIBS ${SPIRV_TOOLS})
|
||||
add_spvtools_tool(TARGET spirv-opt SRCS opt/opt.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS})
|
||||
if (NOT DEFINED IOS_PLATFORM) # iOS does not allow std::system calls which spirv-reduce requires
|
||||
add_spvtools_tool(TARGET spirv-reduce SRCS reduce/reduce.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-reduce ${SPIRV_TOOLS})
|
||||
endif()
|
||||
add_spvtools_tool(TARGET spirv-link SRCS link/linker.cpp LIBS SPIRV-Tools-link ${SPIRV_TOOLS})
|
||||
add_spvtools_tool(TARGET spirv-cfg
|
||||
SRCS cfg/cfg.cpp
|
||||
cfg/bin_to_dot.h
|
||||
cfg/bin_to_dot.cpp
|
||||
LIBS ${SPIRV_TOOLS})
|
||||
target_include_directories(spirv-cfg PRIVATE ${spirv-tools_SOURCE_DIR}
|
||||
${SPIRV_HEADER_INCLUDE_DIR})
|
||||
set(SPIRV_INSTALL_TARGETS spirv-as spirv-dis spirv-val spirv-opt
|
||||
spirv-cfg spirv-link)
|
||||
if(NOT DEFINED IOS_PLATFORM)
|
||||
set(SPIRV_INSTALL_TARGETS ${SPIRV_INSTALL_TARGETS} spirv-reduce)
|
||||
endif()
|
||||
|
||||
if(SPIRV_BUILD_FUZZER)
|
||||
add_spvtools_tool(TARGET spirv-fuzz SRCS fuzz/fuzz.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-fuzz ${SPIRV_TOOLS})
|
||||
set(SPIRV_INSTALL_TARGETS ${SPIRV_INSTALL_TARGETS} spirv-fuzz)
|
||||
endif(SPIRV_BUILD_FUZZER)
|
||||
|
||||
if(ENABLE_SPIRV_TOOLS_INSTALL)
|
||||
install(TARGETS ${SPIRV_INSTALL_TARGETS}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
endif(ENABLE_SPIRV_TOOLS_INSTALL)
|
||||
endif()
|
||||
154
3rdparty/spirv-tools/tools/as/as.cpp
vendored
154
3rdparty/spirv-tools/tools/as/as.cpp
vendored
@@ -1,154 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "source/spirv_target_env.h"
|
||||
#include "spirv-tools/libspirv.h"
|
||||
#include "tools/io.h"
|
||||
|
||||
void print_usage(char* argv0) {
|
||||
std::string target_env_list = spvTargetEnvList(19, 80);
|
||||
printf(
|
||||
R"(%s - Create a SPIR-V binary module from SPIR-V assembly text
|
||||
|
||||
Usage: %s [options] [<filename>]
|
||||
|
||||
The SPIR-V assembly text is read from <filename>. If no file is specified,
|
||||
or if the filename is "-", then the assembly text is read from standard input.
|
||||
The SPIR-V binary module is written to file "out.spv", unless the -o option
|
||||
is used.
|
||||
|
||||
Options:
|
||||
|
||||
-h, --help Print this help.
|
||||
|
||||
-o <filename> Set the output filename. Use '-' to mean stdout.
|
||||
--version Display assembler version information.
|
||||
--preserve-numeric-ids
|
||||
Numeric IDs in the binary will have the same values as in the
|
||||
source. Non-numeric IDs are allocated by filling in the gaps,
|
||||
starting with 1 and going up.
|
||||
--target-env {%s}
|
||||
Use specified environment.
|
||||
)",
|
||||
argv0, argv0, target_env_list.c_str());
|
||||
}
|
||||
|
||||
static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
const char* inFile = nullptr;
|
||||
const char* outFile = nullptr;
|
||||
uint32_t options = 0;
|
||||
spv_target_env target_env = kDefaultEnvironment;
|
||||
for (int argi = 1; argi < argc; ++argi) {
|
||||
if ('-' == argv[argi][0]) {
|
||||
switch (argv[argi][1]) {
|
||||
case 'h': {
|
||||
print_usage(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
case 'o': {
|
||||
if (!outFile && argi + 1 < argc) {
|
||||
outFile = argv[++argi];
|
||||
} else {
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
} break;
|
||||
case 0: {
|
||||
// Setting a filename of "-" to indicate stdin.
|
||||
if (!inFile) {
|
||||
inFile = argv[argi];
|
||||
} else {
|
||||
fprintf(stderr, "error: More than one input file specified\n");
|
||||
return 1;
|
||||
}
|
||||
} break;
|
||||
case '-': {
|
||||
// Long options
|
||||
if (0 == strcmp(argv[argi], "--version")) {
|
||||
printf("%s\n", spvSoftwareVersionDetailsString());
|
||||
printf("Target: %s\n",
|
||||
spvTargetEnvDescription(kDefaultEnvironment));
|
||||
return 0;
|
||||
} else if (0 == strcmp(argv[argi], "--help")) {
|
||||
print_usage(argv[0]);
|
||||
return 0;
|
||||
} else if (0 == strcmp(argv[argi], "--preserve-numeric-ids")) {
|
||||
options |= SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS;
|
||||
} else if (0 == strcmp(argv[argi], "--target-env")) {
|
||||
if (argi + 1 < argc) {
|
||||
const auto env_str = argv[++argi];
|
||||
if (!spvParseTargetEnv(env_str, &target_env)) {
|
||||
fprintf(stderr, "error: Unrecognized target env: %s\n",
|
||||
env_str);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "error: Missing argument to --target-env\n");
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "error: Unrecognized option: %s\n\n", argv[argi]);
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
fprintf(stderr, "error: Unrecognized option: %s\n\n", argv[argi]);
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (!inFile) {
|
||||
inFile = argv[argi];
|
||||
} else {
|
||||
fprintf(stderr, "error: More than one input file specified\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!outFile) {
|
||||
outFile = "out.spv";
|
||||
}
|
||||
|
||||
std::vector<char> contents;
|
||||
if (!ReadFile<char>(inFile, "r", &contents)) return 1;
|
||||
|
||||
spv_binary binary;
|
||||
spv_diagnostic diagnostic = nullptr;
|
||||
spv_context context = spvContextCreate(target_env);
|
||||
spv_result_t error = spvTextToBinaryWithOptions(
|
||||
context, contents.data(), contents.size(), options, &binary, &diagnostic);
|
||||
spvContextDestroy(context);
|
||||
if (error) {
|
||||
spvDiagnosticPrint(diagnostic);
|
||||
spvDiagnosticDestroy(diagnostic);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!WriteFile<uint32_t>(outFile, "wb", binary->code, binary->wordCount)) {
|
||||
spvBinaryDestroy(binary);
|
||||
return 1;
|
||||
}
|
||||
|
||||
spvBinaryDestroy(binary);
|
||||
|
||||
return 0;
|
||||
}
|
||||
187
3rdparty/spirv-tools/tools/cfg/bin_to_dot.cpp
vendored
187
3rdparty/spirv-tools/tools/cfg/bin_to_dot.cpp
vendored
@@ -1,187 +0,0 @@
|
||||
// Copyright (c) 2016 Google 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.
|
||||
|
||||
#include "tools/cfg/bin_to_dot.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "source/assembly_grammar.h"
|
||||
#include "source/name_mapper.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const char* kMergeStyle = "style=dashed";
|
||||
const char* kContinueStyle = "style=dotted";
|
||||
|
||||
// A DotConverter can be used to dump the GraphViz "dot" graph for
|
||||
// a SPIR-V module.
|
||||
class DotConverter {
|
||||
public:
|
||||
DotConverter(spvtools::NameMapper name_mapper, std::iostream* out)
|
||||
: name_mapper_(std::move(name_mapper)), out_(*out) {}
|
||||
|
||||
// Emits the graph preamble.
|
||||
void Begin() const {
|
||||
out_ << "digraph {\n";
|
||||
// Emit a simple legend
|
||||
out_ << "legend_merge_src [shape=plaintext, label=\"\"];\n"
|
||||
<< "legend_merge_dest [shape=plaintext, label=\"\"];\n"
|
||||
<< "legend_merge_src -> legend_merge_dest [label=\" merge\","
|
||||
<< kMergeStyle << "];\n"
|
||||
<< "legend_continue_src [shape=plaintext, label=\"\"];\n"
|
||||
<< "legend_continue_dest [shape=plaintext, label=\"\"];\n"
|
||||
<< "legend_continue_src -> legend_continue_dest [label=\" continue\","
|
||||
<< kContinueStyle << "];\n";
|
||||
}
|
||||
// Emits the graph postamble.
|
||||
void End() const { out_ << "}\n"; }
|
||||
|
||||
// Emits the Dot commands for the given instruction.
|
||||
spv_result_t HandleInstruction(const spv_parsed_instruction_t& inst);
|
||||
|
||||
private:
|
||||
// Ends processing for the current block, emitting its dot code.
|
||||
void FlushBlock(const std::vector<uint32_t>& successors);
|
||||
|
||||
// The ID of the current functio, or 0 if outside of a function.
|
||||
uint32_t current_function_id_ = 0;
|
||||
|
||||
// The ID of the current basic block, or 0 if outside of a block.
|
||||
uint32_t current_block_id_ = 0;
|
||||
|
||||
// Have we completed processing for the entry block to this fuction?
|
||||
bool seen_function_entry_block_ = false;
|
||||
|
||||
// The Id of the merge block for this block if it exists, or 0 otherwise.
|
||||
uint32_t merge_ = 0;
|
||||
// The Id of the continue target block for this block if it exists, or 0
|
||||
// otherwise.
|
||||
uint32_t continue_target_ = 0;
|
||||
|
||||
// An object for mapping Ids to names.
|
||||
spvtools::NameMapper name_mapper_;
|
||||
|
||||
// The output stream.
|
||||
std::ostream& out_;
|
||||
};
|
||||
|
||||
spv_result_t DotConverter::HandleInstruction(
|
||||
const spv_parsed_instruction_t& inst) {
|
||||
switch (inst.opcode) {
|
||||
case SpvOpFunction:
|
||||
current_function_id_ = inst.result_id;
|
||||
seen_function_entry_block_ = false;
|
||||
break;
|
||||
case SpvOpFunctionEnd:
|
||||
current_function_id_ = 0;
|
||||
break;
|
||||
|
||||
case SpvOpLabel:
|
||||
current_block_id_ = inst.result_id;
|
||||
break;
|
||||
|
||||
case SpvOpBranch:
|
||||
FlushBlock({inst.words[1]});
|
||||
break;
|
||||
case SpvOpBranchConditional:
|
||||
FlushBlock({inst.words[2], inst.words[3]});
|
||||
break;
|
||||
case SpvOpSwitch: {
|
||||
std::vector<uint32_t> successors{inst.words[2]};
|
||||
for (size_t i = 3; i < inst.num_operands; i += 2) {
|
||||
successors.push_back(inst.words[inst.operands[i].offset]);
|
||||
}
|
||||
FlushBlock(successors);
|
||||
} break;
|
||||
|
||||
case SpvOpKill:
|
||||
case SpvOpReturn:
|
||||
case SpvOpUnreachable:
|
||||
case SpvOpReturnValue:
|
||||
FlushBlock({});
|
||||
break;
|
||||
|
||||
case SpvOpLoopMerge:
|
||||
merge_ = inst.words[1];
|
||||
continue_target_ = inst.words[2];
|
||||
break;
|
||||
case SpvOpSelectionMerge:
|
||||
merge_ = inst.words[1];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
void DotConverter::FlushBlock(const std::vector<uint32_t>& successors) {
|
||||
out_ << current_block_id_;
|
||||
if (!seen_function_entry_block_) {
|
||||
out_ << " [label=\"" << name_mapper_(current_block_id_) << "\nFn "
|
||||
<< name_mapper_(current_function_id_) << " entry\", shape=box];\n";
|
||||
} else {
|
||||
out_ << " [label=\"" << name_mapper_(current_block_id_) << "\"];\n";
|
||||
}
|
||||
|
||||
for (auto successor : successors) {
|
||||
out_ << current_block_id_ << " -> " << successor << ";\n";
|
||||
}
|
||||
|
||||
if (merge_) {
|
||||
out_ << current_block_id_ << " -> " << merge_ << " [" << kMergeStyle
|
||||
<< "];\n";
|
||||
}
|
||||
if (continue_target_) {
|
||||
out_ << current_block_id_ << " -> " << continue_target_ << " ["
|
||||
<< kContinueStyle << "];\n";
|
||||
}
|
||||
|
||||
// Reset the book-keeping for a block.
|
||||
seen_function_entry_block_ = true;
|
||||
merge_ = 0;
|
||||
continue_target_ = 0;
|
||||
}
|
||||
|
||||
spv_result_t HandleInstruction(
|
||||
void* user_data, const spv_parsed_instruction_t* parsed_instruction) {
|
||||
assert(user_data);
|
||||
auto converter = static_cast<DotConverter*>(user_data);
|
||||
return converter->HandleInstruction(*parsed_instruction);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
spv_result_t BinaryToDot(const spv_const_context context, const uint32_t* words,
|
||||
size_t num_words, std::iostream* out,
|
||||
spv_diagnostic* diagnostic) {
|
||||
// Invalid arguments return error codes, but don't necessarily generate
|
||||
// diagnostics. These are programmer errors, not user errors.
|
||||
if (!diagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC;
|
||||
const spvtools::AssemblyGrammar grammar(context);
|
||||
if (!grammar.isValid()) return SPV_ERROR_INVALID_TABLE;
|
||||
|
||||
spvtools::FriendlyNameMapper friendly_mapper(context, words, num_words);
|
||||
DotConverter converter(friendly_mapper.GetNameMapper(), out);
|
||||
converter.Begin();
|
||||
if (auto error = spvBinaryParse(context, &converter, words, num_words,
|
||||
nullptr, HandleInstruction, diagnostic)) {
|
||||
return error;
|
||||
}
|
||||
converter.End();
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
28
3rdparty/spirv-tools/tools/cfg/bin_to_dot.h
vendored
28
3rdparty/spirv-tools/tools/cfg/bin_to_dot.h
vendored
@@ -1,28 +0,0 @@
|
||||
// Copyright (c) 2016 Google 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.
|
||||
|
||||
#ifndef TOOLS_CFG_BIN_TO_DOT_H_
|
||||
#define TOOLS_CFG_BIN_TO_DOT_H_
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "spirv-tools/libspirv.h"
|
||||
|
||||
// Dumps the control flow graph for the given module to the output stream.
|
||||
// Returns SPV_SUCCESS on succes.
|
||||
spv_result_t BinaryToDot(const spv_const_context context, const uint32_t* words,
|
||||
size_t num_words, std::iostream* out,
|
||||
spv_diagnostic* diagnostic);
|
||||
|
||||
#endif // TOOLS_CFG_BIN_TO_DOT_H_
|
||||
127
3rdparty/spirv-tools/tools/cfg/cfg.cpp
vendored
127
3rdparty/spirv-tools/tools/cfg/cfg.cpp
vendored
@@ -1,127 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "spirv-tools/libspirv.h"
|
||||
#include "tools/cfg/bin_to_dot.h"
|
||||
#include "tools/io.h"
|
||||
|
||||
// Prints a program usage message to stdout.
|
||||
static void print_usage(const char* argv0) {
|
||||
printf(
|
||||
R"(%s - Show the control flow graph in GraphiViz "dot" form. EXPERIMENTAL
|
||||
|
||||
Usage: %s [options] [<filename>]
|
||||
|
||||
The SPIR-V binary is read from <filename>. If no file is specified,
|
||||
or if the filename is "-", then the binary is read from standard input.
|
||||
|
||||
Options:
|
||||
|
||||
-h, --help Print this help.
|
||||
--version Display version information.
|
||||
|
||||
-o <filename> Set the output filename.
|
||||
Output goes to standard output if this option is
|
||||
not specified, or if the filename is "-".
|
||||
)",
|
||||
argv0, argv0);
|
||||
}
|
||||
|
||||
static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
const char* inFile = nullptr;
|
||||
const char* outFile = nullptr; // Stays nullptr if printing to stdout.
|
||||
|
||||
for (int argi = 1; argi < argc; ++argi) {
|
||||
if ('-' == argv[argi][0]) {
|
||||
switch (argv[argi][1]) {
|
||||
case 'h':
|
||||
print_usage(argv[0]);
|
||||
return 0;
|
||||
case 'o': {
|
||||
if (!outFile && argi + 1 < argc) {
|
||||
outFile = argv[++argi];
|
||||
} else {
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
} break;
|
||||
case '-': {
|
||||
// Long options
|
||||
if (0 == strcmp(argv[argi], "--help")) {
|
||||
print_usage(argv[0]);
|
||||
return 0;
|
||||
} else if (0 == strcmp(argv[argi], "--version")) {
|
||||
printf("%s EXPERIMENTAL\n", spvSoftwareVersionDetailsString());
|
||||
printf("Target: %s\n",
|
||||
spvTargetEnvDescription(kDefaultEnvironment));
|
||||
return 0;
|
||||
} else {
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
} break;
|
||||
case 0: {
|
||||
// Setting a filename of "-" to indicate stdin.
|
||||
if (!inFile) {
|
||||
inFile = argv[argi];
|
||||
} else {
|
||||
fprintf(stderr, "error: More than one input file specified\n");
|
||||
return 1;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (!inFile) {
|
||||
inFile = argv[argi];
|
||||
} else {
|
||||
fprintf(stderr, "error: More than one input file specified\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read the input binary.
|
||||
std::vector<uint32_t> contents;
|
||||
if (!ReadFile<uint32_t>(inFile, "rb", &contents)) return 1;
|
||||
spv_context context = spvContextCreate(kDefaultEnvironment);
|
||||
spv_diagnostic diagnostic = nullptr;
|
||||
|
||||
std::stringstream ss;
|
||||
auto error =
|
||||
BinaryToDot(context, contents.data(), contents.size(), &ss, &diagnostic);
|
||||
if (error) {
|
||||
spvDiagnosticPrint(diagnostic);
|
||||
spvDiagnosticDestroy(diagnostic);
|
||||
spvContextDestroy(context);
|
||||
return error;
|
||||
}
|
||||
std::string str = ss.str();
|
||||
WriteFile(outFile, "w", str.data(), str.size());
|
||||
|
||||
spvDiagnosticDestroy(diagnostic);
|
||||
spvContextDestroy(context);
|
||||
|
||||
return 0;
|
||||
}
|
||||
209
3rdparty/spirv-tools/tools/dis/dis.cpp
vendored
209
3rdparty/spirv-tools/tools/dis/dis.cpp
vendored
@@ -1,209 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
|
||||
#include <stdio.h> // Need fileno
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "spirv-tools/libspirv.h"
|
||||
#include "tools/io.h"
|
||||
|
||||
static void print_usage(char* argv0) {
|
||||
printf(
|
||||
R"(%s - Disassemble a SPIR-V binary module
|
||||
|
||||
Usage: %s [options] [<filename>]
|
||||
|
||||
The SPIR-V binary is read from <filename>. If no file is specified,
|
||||
or if the filename is "-", then the binary is read from standard input.
|
||||
|
||||
Options:
|
||||
|
||||
-h, --help Print this help.
|
||||
--version Display disassembler version information.
|
||||
|
||||
-o <filename> Set the output filename.
|
||||
Output goes to standard output if this option is
|
||||
not specified, or if the filename is "-".
|
||||
|
||||
--color Force color output. The default when printing to a terminal.
|
||||
Overrides a previous --no-color option.
|
||||
--no-color Don't print in color. Overrides a previous --color option.
|
||||
The default when output goes to something other than a
|
||||
terminal (e.g. a file, a pipe, or a shell redirection).
|
||||
|
||||
--no-indent Don't indent instructions.
|
||||
|
||||
--no-header Don't output the header as leading comments.
|
||||
|
||||
--raw-id Show raw Id values instead of friendly names.
|
||||
|
||||
--offsets Show byte offsets for each instruction.
|
||||
)",
|
||||
argv0, argv0);
|
||||
}
|
||||
|
||||
static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
const char* inFile = nullptr;
|
||||
const char* outFile = nullptr;
|
||||
|
||||
bool color_is_possible =
|
||||
#if SPIRV_COLOR_TERMINAL
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
bool force_color = false;
|
||||
bool force_no_color = false;
|
||||
|
||||
bool allow_indent = true;
|
||||
bool show_byte_offsets = false;
|
||||
bool no_header = false;
|
||||
bool friendly_names = true;
|
||||
|
||||
for (int argi = 1; argi < argc; ++argi) {
|
||||
if ('-' == argv[argi][0]) {
|
||||
switch (argv[argi][1]) {
|
||||
case 'h':
|
||||
print_usage(argv[0]);
|
||||
return 0;
|
||||
case 'o': {
|
||||
if (!outFile && argi + 1 < argc) {
|
||||
outFile = argv[++argi];
|
||||
} else {
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
} break;
|
||||
case '-': {
|
||||
// Long options
|
||||
if (0 == strcmp(argv[argi], "--no-color")) {
|
||||
force_no_color = true;
|
||||
force_color = false;
|
||||
} else if (0 == strcmp(argv[argi], "--color")) {
|
||||
force_no_color = false;
|
||||
force_color = true;
|
||||
} else if (0 == strcmp(argv[argi], "--no-indent")) {
|
||||
allow_indent = false;
|
||||
} else if (0 == strcmp(argv[argi], "--offsets")) {
|
||||
show_byte_offsets = true;
|
||||
} else if (0 == strcmp(argv[argi], "--no-header")) {
|
||||
no_header = true;
|
||||
} else if (0 == strcmp(argv[argi], "--raw-id")) {
|
||||
friendly_names = false;
|
||||
} else if (0 == strcmp(argv[argi], "--help")) {
|
||||
print_usage(argv[0]);
|
||||
return 0;
|
||||
} else if (0 == strcmp(argv[argi], "--version")) {
|
||||
printf("%s\n", spvSoftwareVersionDetailsString());
|
||||
printf("Target: %s\n",
|
||||
spvTargetEnvDescription(kDefaultEnvironment));
|
||||
return 0;
|
||||
} else {
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
} break;
|
||||
case 0: {
|
||||
// Setting a filename of "-" to indicate stdin.
|
||||
if (!inFile) {
|
||||
inFile = argv[argi];
|
||||
} else {
|
||||
fprintf(stderr, "error: More than one input file specified\n");
|
||||
return 1;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (!inFile) {
|
||||
inFile = argv[argi];
|
||||
} else {
|
||||
fprintf(stderr, "error: More than one input file specified\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t options = SPV_BINARY_TO_TEXT_OPTION_NONE;
|
||||
|
||||
if (allow_indent) options |= SPV_BINARY_TO_TEXT_OPTION_INDENT;
|
||||
|
||||
if (show_byte_offsets) options |= SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET;
|
||||
|
||||
if (no_header) options |= SPV_BINARY_TO_TEXT_OPTION_NO_HEADER;
|
||||
|
||||
if (friendly_names) options |= SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES;
|
||||
|
||||
if (!outFile || (0 == strcmp("-", outFile))) {
|
||||
// Print to standard output.
|
||||
options |= SPV_BINARY_TO_TEXT_OPTION_PRINT;
|
||||
|
||||
if (color_is_possible && !force_no_color) {
|
||||
bool output_is_tty = true;
|
||||
#if defined(_POSIX_VERSION)
|
||||
output_is_tty = isatty(fileno(stdout));
|
||||
#endif
|
||||
if (output_is_tty || force_color) {
|
||||
options |= SPV_BINARY_TO_TEXT_OPTION_COLOR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read the input binary.
|
||||
std::vector<uint32_t> contents;
|
||||
if (!ReadFile<uint32_t>(inFile, "rb", &contents)) return 1;
|
||||
|
||||
// If printing to standard output, then spvBinaryToText should
|
||||
// do the printing. In particular, colour printing on Windows is
|
||||
// controlled by modifying console objects synchronously while
|
||||
// outputting to the stream rather than by injecting escape codes
|
||||
// into the output stream.
|
||||
// If the printing option is off, then save the text in memory, so
|
||||
// it can be emitted later in this function.
|
||||
const bool print_to_stdout = SPV_BINARY_TO_TEXT_OPTION_PRINT & options;
|
||||
spv_text text = nullptr;
|
||||
spv_text* textOrNull = print_to_stdout ? nullptr : &text;
|
||||
spv_diagnostic diagnostic = nullptr;
|
||||
spv_context context = spvContextCreate(kDefaultEnvironment);
|
||||
spv_result_t error =
|
||||
spvBinaryToText(context, contents.data(), contents.size(), options,
|
||||
textOrNull, &diagnostic);
|
||||
spvContextDestroy(context);
|
||||
if (error) {
|
||||
spvDiagnosticPrint(diagnostic);
|
||||
spvDiagnosticDestroy(diagnostic);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!print_to_stdout) {
|
||||
if (!WriteFile<char>(outFile, "w", text->str, text->length)) {
|
||||
spvTextDestroy(text);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
spvTextDestroy(text);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
;; Copyright (c) 2016 LunarG 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.
|
||||
|
||||
;; Upon loading a file with the .spv extension into emacs, the file
|
||||
;; will be disassembled using spirv-dis, and the result colorized with
|
||||
;; asm-mode in emacs. The file may be edited within the constraints
|
||||
;; of validity, and when re-saved will be re-assembled using spirv-as.
|
||||
|
||||
;; Note that symbol IDs are not preserved through a load/edit/save operation.
|
||||
;; This may change if the ability is added to spirv-as.
|
||||
|
||||
;; It is required that those tools be in your PATH. If that is not the case
|
||||
;; when starting emacs, the path can be modified as in this example:
|
||||
;; (setenv "PATH" (concat (getenv "PATH") ":/path/to/spirv/tools"))
|
||||
;;
|
||||
;; See https://github.com/KhronosGroup/SPIRV-Tools/issues/359
|
||||
|
||||
(require 'jka-compr)
|
||||
(require 'asm-mode)
|
||||
|
||||
(add-to-list 'jka-compr-compression-info-list
|
||||
'["\\.spv\\'"
|
||||
"Assembling SPIRV" "spirv-as" ("-o" "-")
|
||||
"Disassembling SPIRV" "spirv-dis" ("--no-color" "--raw-id")
|
||||
t nil "\003\002\043\007"])
|
||||
|
||||
(add-to-list 'auto-mode-alist '("\\.spv\\'" . asm-mode))
|
||||
|
||||
(jka-compr-update)
|
||||
48
3rdparty/spirv-tools/tools/emacs/CMakeLists.txt
vendored
48
3rdparty/spirv-tools/tools/emacs/CMakeLists.txt
vendored
@@ -1,48 +0,0 @@
|
||||
# Copyright (c) 2016 LunarG 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.
|
||||
|
||||
# Install a script for use with the auto-compression feature of emacs(1).
|
||||
# Upon loading a file with the .spv extension, the file will be disassembled
|
||||
# using spirv-dis, and the result colorized with asm-mode in emacs. The file
|
||||
# may be edited within the constraints of validity, and when re-saved will be
|
||||
# re-assembled using spirv-as.
|
||||
|
||||
# It is required that those tools be in your PATH. If that is not the case
|
||||
# when starting emacs, the path can be modified as in this example:
|
||||
# (setenv "PATH" (concat (getenv "PATH") ":/path/to/spirv/tools"))
|
||||
#
|
||||
# See https://github.com/KhronosGroup/SPIRV-Tools/issues/359
|
||||
|
||||
# This is an absolute directory, and ignores CMAKE_INSTALL_PREFIX, or
|
||||
# it will not be found by emacs upon startup. It is only installed if
|
||||
# both of the following are true:
|
||||
# 1. SPIRV_TOOLS_INSTALL_EMACS_HELPERS is defined
|
||||
# 2. The directory /etc/emacs/site-start.d already exists at the time of
|
||||
# cmake invocation (not at the time of make install). This is
|
||||
# typically true if emacs is installed on the system.
|
||||
|
||||
# Note that symbol IDs are not preserved through a load/edit/save operation.
|
||||
# This may change if the ability is added to spirv-as.
|
||||
|
||||
option(SPIRV_TOOLS_INSTALL_EMACS_HELPERS
|
||||
"Install Emacs helper to disassemble/assemble SPIR-V binaries on file load/save."
|
||||
${SPIRV_TOOLS_INSTALL_EMACS_HELPERS})
|
||||
if (${SPIRV_TOOLS_INSTALL_EMACS_HELPERS})
|
||||
if(EXISTS /etc/emacs/site-start.d)
|
||||
if(ENABLE_SPIRV_TOOLS_INSTALL)
|
||||
install(FILES 50spirv-tools.el DESTINATION /etc/emacs/site-start.d)
|
||||
endif(ENABLE_SPIRV_TOOLS_INSTALL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
519
3rdparty/spirv-tools/tools/fuzz/fuzz.cpp
vendored
519
3rdparty/spirv-tools/tools/fuzz/fuzz.cpp
vendored
@@ -1,519 +0,0 @@
|
||||
// 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 <cassert>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "source/fuzz/fuzzer.h"
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/replayer.h"
|
||||
#include "source/fuzz/shrinker.h"
|
||||
#include "source/opt/build_module.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
#include "source/opt/log.h"
|
||||
#include "source/spirv_fuzzer_options.h"
|
||||
#include "source/util/string_utils.h"
|
||||
#include "tools/io.h"
|
||||
#include "tools/util/cli_consumer.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Check that the std::system function can actually be used.
|
||||
bool CheckExecuteCommand() {
|
||||
int res = std::system(nullptr);
|
||||
return res != 0;
|
||||
}
|
||||
|
||||
// Execute a command using the shell.
|
||||
// Returns true if and only if the command's exit status was 0.
|
||||
bool ExecuteCommand(const std::string& command) {
|
||||
errno = 0;
|
||||
int status = std::system(command.c_str());
|
||||
assert(errno == 0 && "failed to execute command");
|
||||
// The result returned by 'system' is implementation-defined, but is
|
||||
// usually the case that the returned value is 0 when the command's exit
|
||||
// code was 0. We are assuming that here, and that's all we depend on.
|
||||
return status == 0;
|
||||
}
|
||||
|
||||
// Status and actions to perform after parsing command-line arguments.
|
||||
enum class FuzzActions {
|
||||
FUZZ, // Run the fuzzer to apply transformations in a randomized fashion.
|
||||
REPLAY, // Replay an existing sequence of transformations.
|
||||
SHRINK, // Shrink an existing sequence of transformations with respect to an
|
||||
// interestingness function.
|
||||
STOP // Do nothing.
|
||||
};
|
||||
|
||||
struct FuzzStatus {
|
||||
FuzzActions action;
|
||||
int code;
|
||||
};
|
||||
|
||||
void PrintUsage(const char* program) {
|
||||
// NOTE: Please maintain flags in lexicographical order.
|
||||
printf(
|
||||
R"(%s - Fuzzes an equivalent SPIR-V binary based on a given binary.
|
||||
|
||||
USAGE: %s [options] <input.spv> -o <output.spv>
|
||||
|
||||
The SPIR-V binary is read from <input.spv>, which must have extension .spv. If
|
||||
<input.facts> is also present, facts about the SPIR-V binary are read from this
|
||||
file.
|
||||
|
||||
The transformed SPIR-V binary is written to <output.spv>. Human-readable and
|
||||
binary representations of the transformations that were applied are written to
|
||||
<output.transformations_json> and <output.transformations>, respectively.
|
||||
|
||||
NOTE: The fuzzer is a work in progress.
|
||||
|
||||
Options (in lexicographical order):
|
||||
|
||||
-h, --help
|
||||
Print this help.
|
||||
--replay
|
||||
File from which to read a sequence of transformations to replay
|
||||
(instead of fuzzing)
|
||||
--seed
|
||||
Unsigned 32-bit integer seed to control random number
|
||||
generation.
|
||||
--shrink
|
||||
File from which to read a sequence of transformations to shrink
|
||||
(instead of fuzzing)
|
||||
--shrinker-step-limit
|
||||
Unsigned 32-bit integer specifying maximum number of steps the
|
||||
shrinker will take before giving up. Ignored unless --shrink
|
||||
is used.
|
||||
--interestingness
|
||||
Path to an interestingness function to guide shrinking: a script
|
||||
that returns 0 if and only if a given binary is interesting.
|
||||
Required if --shrink is provided; disallowed otherwise.
|
||||
--replay-validation
|
||||
Run the validator after applying each transformation during
|
||||
replay (including the replay that occurs during shrinking).
|
||||
Aborts if an invalid binary is created. Useful for debugging
|
||||
spirv-fuzz.
|
||||
--version
|
||||
Display fuzzer version information.
|
||||
|
||||
)",
|
||||
program, program);
|
||||
}
|
||||
|
||||
// Message consumer for this tool. Used to emit diagnostics during
|
||||
// initialization and setup. Note that |source| and |position| are irrelevant
|
||||
// here because we are still not processing a SPIR-V input file.
|
||||
void FuzzDiagnostic(spv_message_level_t level, const char* /*source*/,
|
||||
const spv_position_t& /*position*/, const char* message) {
|
||||
if (level == SPV_MSG_ERROR) {
|
||||
fprintf(stderr, "error: ");
|
||||
}
|
||||
fprintf(stderr, "%s\n", message);
|
||||
}
|
||||
|
||||
bool EndsWithSpv(const std::string& filename) {
|
||||
std::string dot_spv = ".spv";
|
||||
return filename.length() >= dot_spv.length() &&
|
||||
0 == filename.compare(filename.length() - dot_spv.length(),
|
||||
filename.length(), dot_spv);
|
||||
}
|
||||
|
||||
FuzzStatus ParseFlags(int argc, const char** argv, std::string* in_binary_file,
|
||||
std::string* out_binary_file,
|
||||
std::string* replay_transformations_file,
|
||||
std::string* interestingness_function_file,
|
||||
std::string* shrink_transformations_file,
|
||||
spvtools::FuzzerOptions* fuzzer_options) {
|
||||
uint32_t positional_arg_index = 0;
|
||||
|
||||
for (int argi = 1; argi < argc; ++argi) {
|
||||
const char* cur_arg = argv[argi];
|
||||
if ('-' == cur_arg[0]) {
|
||||
if (0 == strcmp(cur_arg, "--version")) {
|
||||
spvtools::Logf(FuzzDiagnostic, SPV_MSG_INFO, nullptr, {}, "%s\n",
|
||||
spvSoftwareVersionDetailsString());
|
||||
return {FuzzActions::STOP, 0};
|
||||
} else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) {
|
||||
PrintUsage(argv[0]);
|
||||
return {FuzzActions::STOP, 0};
|
||||
} else if (0 == strcmp(cur_arg, "-o")) {
|
||||
if (out_binary_file->empty() && argi + 1 < argc) {
|
||||
*out_binary_file = std::string(argv[++argi]);
|
||||
} else {
|
||||
PrintUsage(argv[0]);
|
||||
return {FuzzActions::STOP, 1};
|
||||
}
|
||||
} else if (0 == strncmp(cur_arg, "--replay=", sizeof("--replay=") - 1)) {
|
||||
const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
|
||||
*replay_transformations_file = std::string(split_flag.second);
|
||||
} else if (0 == strncmp(cur_arg, "--interestingness=",
|
||||
sizeof("--interestingness=") - 1)) {
|
||||
const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
|
||||
*interestingness_function_file = std::string(split_flag.second);
|
||||
} else if (0 == strncmp(cur_arg, "--replay-validation",
|
||||
sizeof("--replay-validation") - 1)) {
|
||||
fuzzer_options->enable_replay_validation();
|
||||
} else if (0 == strncmp(cur_arg, "--shrink=", sizeof("--shrink=") - 1)) {
|
||||
const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
|
||||
*shrink_transformations_file = std::string(split_flag.second);
|
||||
} else if (0 == strncmp(cur_arg, "--seed=", sizeof("--seed=") - 1)) {
|
||||
const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
|
||||
char* end = nullptr;
|
||||
errno = 0;
|
||||
const auto seed =
|
||||
static_cast<uint32_t>(strtol(split_flag.second.c_str(), &end, 10));
|
||||
assert(end != split_flag.second.c_str() && errno == 0);
|
||||
fuzzer_options->set_random_seed(seed);
|
||||
} else if (0 == strncmp(cur_arg, "--shrinker-step-limit=",
|
||||
sizeof("--shrinker-step-limit=") - 1)) {
|
||||
const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
|
||||
char* end = nullptr;
|
||||
errno = 0;
|
||||
const auto step_limit =
|
||||
static_cast<uint32_t>(strtol(split_flag.second.c_str(), &end, 10));
|
||||
assert(end != split_flag.second.c_str() && errno == 0);
|
||||
fuzzer_options->set_shrinker_step_limit(step_limit);
|
||||
} else if ('\0' == cur_arg[1]) {
|
||||
// We do not support fuzzing from standard input. We could support
|
||||
// this if there was a compelling use case.
|
||||
PrintUsage(argv[0]);
|
||||
return {FuzzActions::STOP, 0};
|
||||
}
|
||||
} else if (positional_arg_index == 0) {
|
||||
// Binary input file name
|
||||
assert(in_binary_file->empty());
|
||||
*in_binary_file = std::string(cur_arg);
|
||||
positional_arg_index++;
|
||||
} else {
|
||||
spvtools::Error(FuzzDiagnostic, nullptr, {},
|
||||
"Too many positional arguments specified");
|
||||
return {FuzzActions::STOP, 1};
|
||||
}
|
||||
}
|
||||
|
||||
if (in_binary_file->empty()) {
|
||||
spvtools::Error(FuzzDiagnostic, nullptr, {}, "No input file specified");
|
||||
return {FuzzActions::STOP, 1};
|
||||
}
|
||||
|
||||
if (!EndsWithSpv(*in_binary_file)) {
|
||||
spvtools::Error(FuzzDiagnostic, nullptr, {},
|
||||
"Input filename must have extension .spv");
|
||||
return {FuzzActions::STOP, 1};
|
||||
}
|
||||
|
||||
if (out_binary_file->empty()) {
|
||||
spvtools::Error(FuzzDiagnostic, nullptr, {}, "-o required");
|
||||
return {FuzzActions::STOP, 1};
|
||||
}
|
||||
|
||||
if (!EndsWithSpv(*out_binary_file)) {
|
||||
spvtools::Error(FuzzDiagnostic, nullptr, {},
|
||||
"Output filename must have extension .spv");
|
||||
return {FuzzActions::STOP, 1};
|
||||
}
|
||||
|
||||
if (replay_transformations_file->empty() &&
|
||||
shrink_transformations_file->empty() &&
|
||||
static_cast<spv_const_fuzzer_options>(*fuzzer_options)
|
||||
->replay_validation_enabled) {
|
||||
spvtools::Error(FuzzDiagnostic, nullptr, {},
|
||||
"The --replay-validation argument can only be used with "
|
||||
"one of the --replay or --shrink arguments.");
|
||||
return {FuzzActions::STOP, 1};
|
||||
}
|
||||
|
||||
if (!replay_transformations_file->empty()) {
|
||||
// A replay transformations file was given, thus the tool is being invoked
|
||||
// in replay mode.
|
||||
if (!shrink_transformations_file->empty()) {
|
||||
spvtools::Error(
|
||||
FuzzDiagnostic, nullptr, {},
|
||||
"The --replay and --shrink arguments are mutually exclusive.");
|
||||
return {FuzzActions::STOP, 1};
|
||||
}
|
||||
if (!interestingness_function_file->empty()) {
|
||||
spvtools::Error(FuzzDiagnostic, nullptr, {},
|
||||
"The --replay and --interestingness arguments are "
|
||||
"mutually exclusive.");
|
||||
return {FuzzActions::STOP, 1};
|
||||
}
|
||||
return {FuzzActions::REPLAY, 0};
|
||||
}
|
||||
|
||||
if (!shrink_transformations_file->empty() ^
|
||||
!interestingness_function_file->empty()) {
|
||||
spvtools::Error(FuzzDiagnostic, nullptr, {},
|
||||
"Both or neither of the --shrink and --interestingness "
|
||||
"arguments must be provided.");
|
||||
return {FuzzActions::STOP, 1};
|
||||
}
|
||||
|
||||
if (!shrink_transformations_file->empty()) {
|
||||
// The tool is being invoked in shrink mode.
|
||||
assert(!interestingness_function_file->empty() &&
|
||||
"An error should have been raised if --shrink but not --interesting "
|
||||
"was provided.");
|
||||
return {FuzzActions::SHRINK, 0};
|
||||
}
|
||||
|
||||
return {FuzzActions::FUZZ, 0};
|
||||
}
|
||||
|
||||
bool ParseTransformations(
|
||||
const std::string& transformations_file,
|
||||
spvtools::fuzz::protobufs::TransformationSequence* transformations) {
|
||||
std::ifstream transformations_stream;
|
||||
transformations_stream.open(transformations_file,
|
||||
std::ios::in | std::ios::binary);
|
||||
auto parse_success =
|
||||
transformations->ParseFromIstream(&transformations_stream);
|
||||
transformations_stream.close();
|
||||
if (!parse_success) {
|
||||
spvtools::Error(FuzzDiagnostic, nullptr, {},
|
||||
("Error reading transformations from file '" +
|
||||
transformations_file + "'")
|
||||
.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Replay(const spv_target_env& target_env,
|
||||
spv_const_fuzzer_options fuzzer_options,
|
||||
const std::vector<uint32_t>& binary_in,
|
||||
const spvtools::fuzz::protobufs::FactSequence& initial_facts,
|
||||
const std::string& replay_transformations_file,
|
||||
std::vector<uint32_t>* binary_out,
|
||||
spvtools::fuzz::protobufs::TransformationSequence*
|
||||
transformations_applied) {
|
||||
spvtools::fuzz::protobufs::TransformationSequence transformation_sequence;
|
||||
if (!ParseTransformations(replay_transformations_file,
|
||||
&transformation_sequence)) {
|
||||
return false;
|
||||
}
|
||||
spvtools::fuzz::Replayer replayer(target_env,
|
||||
fuzzer_options->replay_validation_enabled);
|
||||
replayer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
|
||||
auto replay_result_status =
|
||||
replayer.Run(binary_in, initial_facts, transformation_sequence,
|
||||
binary_out, transformations_applied);
|
||||
return !(replay_result_status !=
|
||||
spvtools::fuzz::Replayer::ReplayerResultStatus::kComplete);
|
||||
}
|
||||
|
||||
bool Shrink(const spv_target_env& target_env,
|
||||
spv_const_fuzzer_options fuzzer_options,
|
||||
const std::vector<uint32_t>& binary_in,
|
||||
const spvtools::fuzz::protobufs::FactSequence& initial_facts,
|
||||
const std::string& shrink_transformations_file,
|
||||
const std::string& interestingness_function_file,
|
||||
std::vector<uint32_t>* binary_out,
|
||||
spvtools::fuzz::protobufs::TransformationSequence*
|
||||
transformations_applied) {
|
||||
spvtools::fuzz::protobufs::TransformationSequence transformation_sequence;
|
||||
if (!ParseTransformations(shrink_transformations_file,
|
||||
&transformation_sequence)) {
|
||||
return false;
|
||||
}
|
||||
spvtools::fuzz::Shrinker shrinker(target_env,
|
||||
fuzzer_options->shrinker_step_limit,
|
||||
fuzzer_options->replay_validation_enabled);
|
||||
shrinker.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
|
||||
|
||||
spvtools::fuzz::Shrinker::InterestingnessFunction interestingness_function =
|
||||
[interestingness_function_file](std::vector<uint32_t> binary,
|
||||
uint32_t reductions_applied) -> bool {
|
||||
std::stringstream ss;
|
||||
ss << "temp_" << std::setw(4) << std::setfill('0') << reductions_applied
|
||||
<< ".spv";
|
||||
const auto spv_file = ss.str();
|
||||
const std::string command =
|
||||
std::string(interestingness_function_file) + " " + spv_file;
|
||||
auto write_file_succeeded =
|
||||
WriteFile(spv_file.c_str(), "wb", &binary[0], binary.size());
|
||||
(void)(write_file_succeeded);
|
||||
assert(write_file_succeeded);
|
||||
return ExecuteCommand(command);
|
||||
};
|
||||
|
||||
auto shrink_result_status = shrinker.Run(
|
||||
binary_in, initial_facts, transformation_sequence,
|
||||
interestingness_function, binary_out, transformations_applied);
|
||||
return spvtools::fuzz::Shrinker::ShrinkerResultStatus::kComplete ==
|
||||
shrink_result_status ||
|
||||
spvtools::fuzz::Shrinker::ShrinkerResultStatus::kStepLimitReached ==
|
||||
shrink_result_status;
|
||||
}
|
||||
|
||||
bool Fuzz(const spv_target_env& target_env,
|
||||
const spvtools::FuzzerOptions& fuzzer_options,
|
||||
const std::vector<uint32_t>& binary_in,
|
||||
const spvtools::fuzz::protobufs::FactSequence& initial_facts,
|
||||
std::vector<uint32_t>* binary_out,
|
||||
spvtools::fuzz::protobufs::TransformationSequence*
|
||||
transformations_applied) {
|
||||
spvtools::fuzz::Fuzzer fuzzer(target_env);
|
||||
fuzzer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
|
||||
auto fuzz_result_status = fuzzer.Run(binary_in, initial_facts, fuzzer_options,
|
||||
binary_out, transformations_applied);
|
||||
if (fuzz_result_status !=
|
||||
spvtools::fuzz::Fuzzer::FuzzerResultStatus::kComplete) {
|
||||
spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error running fuzzer");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Dumps |binary| to file |filename|. Useful for interactive debugging.
|
||||
void DumpShader(const std::vector<uint32_t>& binary, const char* filename) {
|
||||
auto write_file_succeeded =
|
||||
WriteFile(filename, "wb", &binary[0], binary.size());
|
||||
if (!write_file_succeeded) {
|
||||
std::cerr << "Failed to dump shader" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Dumps the SPIRV-V module in |context| to file |filename|. Useful for
|
||||
// interactive debugging.
|
||||
void DumpShader(spvtools::opt::IRContext* context, const char* filename) {
|
||||
std::vector<uint32_t> binary;
|
||||
context->module()->ToBinary(&binary, false);
|
||||
DumpShader(binary, filename);
|
||||
}
|
||||
|
||||
const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_3;
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
std::string in_binary_file;
|
||||
std::string out_binary_file;
|
||||
std::string replay_transformations_file;
|
||||
std::string interestingness_function_file;
|
||||
std::string shrink_transformations_file;
|
||||
|
||||
spvtools::FuzzerOptions fuzzer_options;
|
||||
|
||||
FuzzStatus status =
|
||||
ParseFlags(argc, argv, &in_binary_file, &out_binary_file,
|
||||
&replay_transformations_file, &interestingness_function_file,
|
||||
&shrink_transformations_file, &fuzzer_options);
|
||||
|
||||
if (status.action == FuzzActions::STOP) {
|
||||
return status.code;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> binary_in;
|
||||
if (!ReadFile<uint32_t>(in_binary_file.c_str(), "rb", &binary_in)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
spvtools::fuzz::protobufs::FactSequence initial_facts;
|
||||
const std::string dot_spv(".spv");
|
||||
std::string in_facts_file =
|
||||
in_binary_file.substr(0, in_binary_file.length() - dot_spv.length()) +
|
||||
".facts";
|
||||
std::ifstream facts_input(in_facts_file);
|
||||
if (facts_input) {
|
||||
std::string facts_json_string((std::istreambuf_iterator<char>(facts_input)),
|
||||
std::istreambuf_iterator<char>());
|
||||
facts_input.close();
|
||||
if (google::protobuf::util::Status::OK !=
|
||||
google::protobuf::util::JsonStringToMessage(facts_json_string,
|
||||
&initial_facts)) {
|
||||
spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error reading facts data");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint32_t> binary_out;
|
||||
spvtools::fuzz::protobufs::TransformationSequence transformations_applied;
|
||||
|
||||
spv_target_env target_env = kDefaultEnvironment;
|
||||
|
||||
switch (status.action) {
|
||||
case FuzzActions::FUZZ:
|
||||
if (!Fuzz(target_env, fuzzer_options, binary_in, initial_facts,
|
||||
&binary_out, &transformations_applied)) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case FuzzActions::REPLAY:
|
||||
if (!Replay(target_env, fuzzer_options, binary_in, initial_facts,
|
||||
replay_transformations_file, &binary_out,
|
||||
&transformations_applied)) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case FuzzActions::SHRINK: {
|
||||
if (!CheckExecuteCommand()) {
|
||||
std::cerr << "could not find shell interpreter for executing a command"
|
||||
<< std::endl;
|
||||
return 1;
|
||||
}
|
||||
if (!Shrink(target_env, fuzzer_options, binary_in, initial_facts,
|
||||
shrink_transformations_file, interestingness_function_file,
|
||||
&binary_out, &transformations_applied)) {
|
||||
return 1;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
assert(false && "Unknown fuzzer action.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!WriteFile<uint32_t>(out_binary_file.c_str(), "wb", binary_out.data(),
|
||||
binary_out.size())) {
|
||||
spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error writing out binary");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string output_file_prefix =
|
||||
out_binary_file.substr(0, out_binary_file.length() - dot_spv.length());
|
||||
std::ofstream transformations_file;
|
||||
transformations_file.open(output_file_prefix + ".transformations",
|
||||
std::ios::out | std::ios::binary);
|
||||
bool success =
|
||||
transformations_applied.SerializeToOstream(&transformations_file);
|
||||
transformations_file.close();
|
||||
if (!success) {
|
||||
spvtools::Error(FuzzDiagnostic, nullptr, {},
|
||||
"Error writing out transformations binary");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string json_string;
|
||||
auto json_options = google::protobuf::util::JsonOptions();
|
||||
json_options.add_whitespace = true;
|
||||
auto json_generation_status = google::protobuf::util::MessageToJsonString(
|
||||
transformations_applied, &json_string, json_options);
|
||||
if (json_generation_status != google::protobuf::util::Status::OK) {
|
||||
spvtools::Error(FuzzDiagnostic, nullptr, {},
|
||||
"Error writing out transformations in JSON format");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::ofstream transformations_json_file(output_file_prefix +
|
||||
".transformations_json");
|
||||
transformations_json_file << json_string;
|
||||
transformations_json_file.close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
85
3rdparty/spirv-tools/tools/io.h
vendored
85
3rdparty/spirv-tools/tools/io.h
vendored
@@ -1,85 +0,0 @@
|
||||
// Copyright (c) 2016 Google 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.
|
||||
|
||||
#ifndef TOOLS_IO_H_
|
||||
#define TOOLS_IO_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
// Appends the content from the file named as |filename| to |data|, assuming
|
||||
// each element in the file is of type |T|. The file is opened with the given
|
||||
// |mode|. If |filename| is nullptr or "-", reads from the standard input, but
|
||||
// reopened with the given mode. If any error occurs, writes error messages to
|
||||
// standard error and returns false.
|
||||
template <typename T>
|
||||
bool ReadFile(const char* filename, const char* mode, std::vector<T>* data) {
|
||||
const int buf_size = 1024;
|
||||
const bool use_file = filename && strcmp("-", filename);
|
||||
if (FILE* fp =
|
||||
(use_file ? fopen(filename, mode) : freopen(nullptr, mode, stdin))) {
|
||||
T buf[buf_size];
|
||||
while (size_t len = fread(buf, sizeof(T), buf_size, fp)) {
|
||||
data->insert(data->end(), buf, buf + len);
|
||||
}
|
||||
if (ftell(fp) == -1L) {
|
||||
if (ferror(fp)) {
|
||||
fprintf(stderr, "error: error reading file '%s'\n", filename);
|
||||
if (use_file) fclose(fp);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (sizeof(T) != 1 && (ftell(fp) % sizeof(T))) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"error: file size should be a multiple of %zd; file '%s' corrupt\n",
|
||||
sizeof(T), filename);
|
||||
if (use_file) fclose(fp);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (use_file) fclose(fp);
|
||||
} else {
|
||||
fprintf(stderr, "error: file does not exist '%s'\n", filename);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Writes the given |data| into the file named as |filename| using the given
|
||||
// |mode|, assuming |data| is an array of |count| elements of type |T|. If
|
||||
// |filename| is nullptr or "-", writes to standard output. If any error occurs,
|
||||
// returns false and outputs error message to standard error.
|
||||
template <typename T>
|
||||
bool WriteFile(const char* filename, const char* mode, const T* data,
|
||||
size_t count) {
|
||||
const bool use_stdout =
|
||||
!filename || (filename[0] == '-' && filename[1] == '\0');
|
||||
if (FILE* fp = (use_stdout ? stdout : fopen(filename, mode))) {
|
||||
size_t written = fwrite(data, sizeof(T), count, fp);
|
||||
if (count != written) {
|
||||
fprintf(stderr, "error: could not write to file '%s'\n", filename);
|
||||
if (!use_stdout) fclose(fp);
|
||||
return false;
|
||||
}
|
||||
if (!use_stdout) fclose(fp);
|
||||
} else {
|
||||
fprintf(stderr, "error: could not open file '%s'\n", filename);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // TOOLS_IO_H_
|
||||
@@ -1,28 +0,0 @@
|
||||
# Copyright (c) 2016 Google 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.
|
||||
|
||||
# Install a script for use with the LESSOPEN of less(1).
|
||||
# For example, after installation into /usr/local do:
|
||||
# export LESSOPEN='|/usr/local/bin "%s"'
|
||||
# less -R foo.spv
|
||||
#
|
||||
# See https://github.com/KhronosGroup/SPIRV-Tools/issues/359
|
||||
|
||||
# The script will be installed with everyone having read and execute
|
||||
# permissions.
|
||||
# We have a .sh extension because Windows users often configure
|
||||
# executable settings via filename extension.
|
||||
if(ENABLE_SPIRV_TOOLS_INSTALL)
|
||||
install(PROGRAMS spirv-lesspipe.sh DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
endif(ENABLE_SPIRV_TOOLS_INSTALL)
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
# Copyright (c) 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.
|
||||
|
||||
# A script for automatically disassembling a .spv file
|
||||
# for less(1). This assumes spirv-dis is on our PATH.
|
||||
#
|
||||
# See https://github.com/KhronosGroup/SPIRV-Tools/issues/359
|
||||
|
||||
case "$1" in
|
||||
*.spv) spirv-dis "$@" 2>/dev/null;;
|
||||
*) exit 1;;
|
||||
esac
|
||||
|
||||
exit $?
|
||||
|
||||
160
3rdparty/spirv-tools/tools/link/linker.cpp
vendored
160
3rdparty/spirv-tools/tools/link/linker.cpp
vendored
@@ -1,160 +0,0 @@
|
||||
// Copyright (c) 2017 Pierre Moreau
|
||||
//
|
||||
// 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 <cstring>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "source/spirv_target_env.h"
|
||||
#include "source/table.h"
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
#include "spirv-tools/linker.hpp"
|
||||
#include "tools/io.h"
|
||||
|
||||
void print_usage(char* argv0) {
|
||||
std::string target_env_list = spvTargetEnvList(27, 95);
|
||||
printf(
|
||||
R"(%s - Link SPIR-V binary files together.
|
||||
|
||||
USAGE: %s [options] <filename> [<filename> ...]
|
||||
|
||||
The SPIR-V binaries are read from the different <filename>.
|
||||
|
||||
NOTE: The linker is a work in progress.
|
||||
|
||||
Options:
|
||||
-h, --help Print this help.
|
||||
-o Name of the resulting linked SPIR-V binary.
|
||||
--create-library Link the binaries into a library, keeping all exported symbols.
|
||||
--allow-partial-linkage Allow partial linkage by accepting imported symbols to be unresolved.
|
||||
--verify-ids Verify that IDs in the resulting modules are truly unique.
|
||||
--version Display linker version information
|
||||
--target-env {%s}
|
||||
Use validation rules from the specified environment.
|
||||
)",
|
||||
argv0, argv0, target_env_list.c_str());
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
std::vector<const char*> inFiles;
|
||||
const char* outFile = nullptr;
|
||||
spv_target_env target_env = SPV_ENV_UNIVERSAL_1_0;
|
||||
spvtools::LinkerOptions options;
|
||||
bool continue_processing = true;
|
||||
int return_code = 0;
|
||||
|
||||
for (int argi = 1; continue_processing && argi < argc; ++argi) {
|
||||
const char* cur_arg = argv[argi];
|
||||
if ('-' == cur_arg[0]) {
|
||||
if (0 == strcmp(cur_arg, "-o")) {
|
||||
if (argi + 1 < argc) {
|
||||
if (!outFile) {
|
||||
outFile = argv[++argi];
|
||||
} else {
|
||||
fprintf(stderr, "error: More than one output file specified\n");
|
||||
continue_processing = false;
|
||||
return_code = 1;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "error: Missing argument to %s\n", cur_arg);
|
||||
continue_processing = false;
|
||||
return_code = 1;
|
||||
}
|
||||
} else if (0 == strcmp(cur_arg, "--create-library")) {
|
||||
options.SetCreateLibrary(true);
|
||||
} else if (0 == strcmp(cur_arg, "--verify-ids")) {
|
||||
options.SetVerifyIds(true);
|
||||
} else if (0 == strcmp(cur_arg, "--allow-partial-linkage")) {
|
||||
options.SetAllowPartialLinkage(true);
|
||||
} else if (0 == strcmp(cur_arg, "--version")) {
|
||||
printf("%s\n", spvSoftwareVersionDetailsString());
|
||||
// TODO(dneto): Add OpenCL 2.2 at least.
|
||||
printf("Targets:\n %s\n %s\n %s\n",
|
||||
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1),
|
||||
spvTargetEnvDescription(SPV_ENV_VULKAN_1_0),
|
||||
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_2));
|
||||
continue_processing = false;
|
||||
return_code = 0;
|
||||
} else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) {
|
||||
print_usage(argv[0]);
|
||||
continue_processing = false;
|
||||
return_code = 0;
|
||||
} else if (0 == strcmp(cur_arg, "--target-env")) {
|
||||
if (argi + 1 < argc) {
|
||||
const auto env_str = argv[++argi];
|
||||
if (!spvParseTargetEnv(env_str, &target_env)) {
|
||||
fprintf(stderr, "error: Unrecognized target env: %s\n", env_str);
|
||||
continue_processing = false;
|
||||
return_code = 1;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "error: Missing argument to --target-env\n");
|
||||
continue_processing = false;
|
||||
return_code = 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
inFiles.push_back(cur_arg);
|
||||
}
|
||||
}
|
||||
|
||||
// Exit if command line parsing was not successful.
|
||||
if (!continue_processing) {
|
||||
return return_code;
|
||||
}
|
||||
|
||||
if (inFiles.empty()) {
|
||||
fprintf(stderr, "error: No input file specified\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<std::vector<uint32_t>> contents(inFiles.size());
|
||||
for (size_t i = 0u; i < inFiles.size(); ++i) {
|
||||
if (!ReadFile<uint32_t>(inFiles[i], "rb", &contents[i])) return 1;
|
||||
}
|
||||
|
||||
const spvtools::MessageConsumer consumer = [](spv_message_level_t level,
|
||||
const char*,
|
||||
const spv_position_t& position,
|
||||
const char* message) {
|
||||
switch (level) {
|
||||
case SPV_MSG_FATAL:
|
||||
case SPV_MSG_INTERNAL_ERROR:
|
||||
case SPV_MSG_ERROR:
|
||||
std::cerr << "error: " << position.index << ": " << message
|
||||
<< std::endl;
|
||||
break;
|
||||
case SPV_MSG_WARNING:
|
||||
std::cout << "warning: " << position.index << ": " << message
|
||||
<< std::endl;
|
||||
break;
|
||||
case SPV_MSG_INFO:
|
||||
std::cout << "info: " << position.index << ": " << message << std::endl;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
spvtools::Context context(target_env);
|
||||
context.SetMessageConsumer(consumer);
|
||||
|
||||
std::vector<uint32_t> linkingResult;
|
||||
spv_result_t status = Link(context, contents, &linkingResult, options);
|
||||
|
||||
if (!WriteFile<uint32_t>(outFile, "wb", linkingResult.data(),
|
||||
linkingResult.size()))
|
||||
return 1;
|
||||
|
||||
return status == SPV_SUCCESS ? 0 : 1;
|
||||
}
|
||||
887
3rdparty/spirv-tools/tools/opt/opt.cpp
vendored
887
3rdparty/spirv-tools/tools/opt/opt.cpp
vendored
@@ -1,887 +0,0 @@
|
||||
// Copyright (c) 2016 Google 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.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "source/opt/log.h"
|
||||
#include "source/spirv_target_env.h"
|
||||
#include "source/util/string_utils.h"
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
#include "spirv-tools/optimizer.hpp"
|
||||
#include "tools/io.h"
|
||||
#include "tools/util/cli_consumer.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Status and actions to perform after parsing command-line arguments.
|
||||
enum OptActions { OPT_CONTINUE, OPT_STOP };
|
||||
|
||||
struct OptStatus {
|
||||
OptActions action;
|
||||
int code;
|
||||
};
|
||||
|
||||
// Message consumer for this tool. Used to emit diagnostics during
|
||||
// initialization and setup. Note that |source| and |position| are irrelevant
|
||||
// here because we are still not processing a SPIR-V input file.
|
||||
void opt_diagnostic(spv_message_level_t level, const char* /*source*/,
|
||||
const spv_position_t& /*positon*/, const char* message) {
|
||||
if (level == SPV_MSG_ERROR) {
|
||||
fprintf(stderr, "error: ");
|
||||
}
|
||||
fprintf(stderr, "%s\n", message);
|
||||
}
|
||||
|
||||
std::string GetListOfPassesAsString(const spvtools::Optimizer& optimizer) {
|
||||
std::stringstream ss;
|
||||
for (const auto& name : optimizer.GetPassNames()) {
|
||||
ss << "\n\t\t" << name;
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5;
|
||||
|
||||
std::string GetLegalizationPasses() {
|
||||
spvtools::Optimizer optimizer(kDefaultEnvironment);
|
||||
optimizer.RegisterLegalizationPasses();
|
||||
return GetListOfPassesAsString(optimizer);
|
||||
}
|
||||
|
||||
std::string GetOptimizationPasses() {
|
||||
spvtools::Optimizer optimizer(kDefaultEnvironment);
|
||||
optimizer.RegisterPerformancePasses();
|
||||
return GetListOfPassesAsString(optimizer);
|
||||
}
|
||||
|
||||
std::string GetSizePasses() {
|
||||
spvtools::Optimizer optimizer(kDefaultEnvironment);
|
||||
optimizer.RegisterSizePasses();
|
||||
return GetListOfPassesAsString(optimizer);
|
||||
}
|
||||
|
||||
std::string GetVulkanToWebGPUPasses() {
|
||||
spvtools::Optimizer optimizer(SPV_ENV_VULKAN_1_1);
|
||||
optimizer.RegisterVulkanToWebGPUPasses();
|
||||
return GetListOfPassesAsString(optimizer);
|
||||
}
|
||||
|
||||
std::string GetWebGPUToVulkanPasses() {
|
||||
spvtools::Optimizer optimizer(SPV_ENV_WEBGPU_0);
|
||||
optimizer.RegisterWebGPUToVulkanPasses();
|
||||
return GetListOfPassesAsString(optimizer);
|
||||
}
|
||||
|
||||
void PrintUsage(const char* program) {
|
||||
std::string target_env_list = spvTargetEnvList(16, 80);
|
||||
// NOTE: Please maintain flags in lexicographical order.
|
||||
printf(
|
||||
R"(%s - Optimize a SPIR-V binary file.
|
||||
|
||||
USAGE: %s [options] [<input>] -o <output>
|
||||
|
||||
The SPIR-V binary is read from <input>. If no file is specified,
|
||||
or if <input> is "-", then the binary is read from standard input.
|
||||
if <output> is "-", then the optimized output is written to
|
||||
standard output.
|
||||
|
||||
NOTE: The optimizer is a work in progress.
|
||||
|
||||
Options (in lexicographical order):)",
|
||||
program, program);
|
||||
printf(R"(
|
||||
--amd-ext-to-khr
|
||||
Replaces the extensions VK_AMD_shader_ballot, VK_AMD_gcn_shader,
|
||||
and VK_AMD_shader_trinary_minmax with equivalant code using core
|
||||
instructions and capabilities.)");
|
||||
printf(R"(
|
||||
--ccp
|
||||
Apply the conditional constant propagation transform. This will
|
||||
propagate constant values throughout the program, and simplify
|
||||
expressions and conditional jumps with known predicate
|
||||
values. Performed on entry point call tree functions and
|
||||
exported functions.)");
|
||||
printf(R"(
|
||||
--cfg-cleanup
|
||||
Cleanup the control flow graph. This will remove any unnecessary
|
||||
code from the CFG like unreachable code. Performed on entry
|
||||
point call tree functions and exported functions.)");
|
||||
printf(R"(
|
||||
--combine-access-chains
|
||||
Combines chained access chains to produce a single instruction
|
||||
where possible.)");
|
||||
printf(R"(
|
||||
--compact-ids
|
||||
Remap result ids to a compact range starting from %%1 and without
|
||||
any gaps.)");
|
||||
printf(R"(
|
||||
--convert-local-access-chains
|
||||
Convert constant index access chain loads/stores into
|
||||
equivalent load/stores with inserts and extracts. Performed
|
||||
on function scope variables referenced only with load, store,
|
||||
and constant index access chains in entry point call tree
|
||||
functions.)");
|
||||
printf(R"(
|
||||
--convert-relaxed-to-half
|
||||
Convert all RelaxedPrecision arithmetic operations to half
|
||||
precision, inserting conversion operations where needed.
|
||||
Run after function scope variable load and store elimination
|
||||
for better results. Simplify-instructions, redundancy-elimination
|
||||
and DCE should be run after this pass to eliminate excess
|
||||
conversions. This conversion is useful when the target platform
|
||||
does not support RelaxedPrecision or ignores it. This pass also
|
||||
removes all RelaxedPrecision decorations.)");
|
||||
printf(R"(
|
||||
--copy-propagate-arrays
|
||||
Does propagation of memory references when an array is a copy of
|
||||
another. It will only propagate an array if the source is never
|
||||
written to, and the only store to the target is the copy.)");
|
||||
printf(R"(
|
||||
--decompose-initialized-variables
|
||||
Decomposes initialized variable declarations into a declaration
|
||||
followed by a store of the initial value. This is done to work
|
||||
around known issues with some Vulkan drivers for initialize
|
||||
variables.)");
|
||||
printf(R"(
|
||||
--descriptor-scalar-replacement
|
||||
Replaces every array variable |desc| that has a DescriptorSet
|
||||
and Binding decorations with a new variable for each element of
|
||||
the array. Suppose |desc| was bound at binding |b|. Then the
|
||||
variable corresponding to |desc[i]| will have binding |b+i|.
|
||||
The descriptor set will be the same. All accesses to |desc|
|
||||
must be in OpAccessChain instructions with a literal index for
|
||||
the first index.)");
|
||||
printf(R"(
|
||||
--eliminate-dead-branches
|
||||
Convert conditional branches with constant condition to the
|
||||
indicated unconditional brranch. Delete all resulting dead
|
||||
code. Performed only on entry point call tree functions.)");
|
||||
printf(R"(
|
||||
--eliminate-dead-code-aggressive
|
||||
Delete instructions which do not contribute to a function's
|
||||
output. Performed only on entry point call tree functions.)");
|
||||
printf(R"(
|
||||
--eliminate-dead-const
|
||||
Eliminate dead constants.)");
|
||||
printf(R"(
|
||||
--eliminate-dead-functions
|
||||
Deletes functions that cannot be reached from entry points or
|
||||
exported functions.)");
|
||||
printf(R"(
|
||||
--eliminate-dead-inserts
|
||||
Deletes unreferenced inserts into composites, most notably
|
||||
unused stores to vector components, that are not removed by
|
||||
aggressive dead code elimination.)");
|
||||
printf(R"(
|
||||
--eliminate-dead-variables
|
||||
Deletes module scope variables that are not referenced.)");
|
||||
printf(R"(
|
||||
--eliminate-insert-extract
|
||||
DEPRECATED. This pass has been replaced by the simplification
|
||||
pass, and that pass will be run instead.
|
||||
See --simplify-instructions.)");
|
||||
printf(R"(
|
||||
--eliminate-local-multi-store
|
||||
Replace stores and loads of function scope variables that are
|
||||
stored multiple times. Performed on variables referenceed only
|
||||
with loads and stores. Performed only on entry point call tree
|
||||
functions.)");
|
||||
printf(R"(
|
||||
--eliminate-local-single-block
|
||||
Perform single-block store/load and load/load elimination.
|
||||
Performed only on function scope variables in entry point
|
||||
call tree functions.)");
|
||||
printf(R"(
|
||||
--eliminate-local-single-store
|
||||
Replace stores and loads of function scope variables that are
|
||||
only stored once. Performed on variables referenceed only with
|
||||
loads and stores. Performed only on entry point call tree
|
||||
functions.)");
|
||||
printf(R"(
|
||||
--flatten-decorations
|
||||
Replace decoration groups with repeated OpDecorate and
|
||||
OpMemberDecorate instructions.)");
|
||||
printf(R"(
|
||||
--fold-spec-const-op-composite
|
||||
Fold the spec constants defined by OpSpecConstantOp or
|
||||
OpSpecConstantComposite instructions to front-end constants
|
||||
when possible.)");
|
||||
printf(R"(
|
||||
--freeze-spec-const
|
||||
Freeze the values of specialization constants to their default
|
||||
values.)");
|
||||
printf(R"(
|
||||
--graphics-robust-access
|
||||
Clamp indices used to access buffers and internal composite
|
||||
values, providing guarantees that satisfy Vulkan's
|
||||
robustBufferAccess rules.)");
|
||||
printf(R"(
|
||||
--generate-webgpu-initializers
|
||||
Adds initial values to OpVariable instructions that are missing
|
||||
them, due to their storage type requiring them for WebGPU.)");
|
||||
printf(R"(
|
||||
--if-conversion
|
||||
Convert if-then-else like assignments into OpSelect.)");
|
||||
printf(R"(
|
||||
--inline-entry-points-exhaustive
|
||||
Exhaustively inline all function calls in entry point call tree
|
||||
functions. Currently does not inline calls to functions with
|
||||
early return in a loop.)");
|
||||
printf(R"(
|
||||
--legalize-hlsl
|
||||
Runs a series of optimizations that attempts to take SPIR-V
|
||||
generated by an HLSL front-end and generates legal Vulkan SPIR-V.
|
||||
The optimizations are:
|
||||
%s
|
||||
|
||||
Note this does not guarantee legal code. This option passes the
|
||||
option --relax-logical-pointer to the validator.)",
|
||||
GetLegalizationPasses().c_str());
|
||||
printf(R"(
|
||||
--legalize-vector-shuffle
|
||||
Converts any usages of 0xFFFFFFFF for the literals in
|
||||
OpVectorShuffle to a literal 0. This is done since 0xFFFFFFFF is
|
||||
forbidden in WebGPU.)");
|
||||
printf(R"(
|
||||
--local-redundancy-elimination
|
||||
Looks for instructions in the same basic block that compute the
|
||||
same value, and deletes the redundant ones.)");
|
||||
printf(R"(
|
||||
--loop-fission
|
||||
Splits any top level loops in which the register pressure has
|
||||
exceeded a given threshold. The threshold must follow the use of
|
||||
this flag and must be a positive integer value.)");
|
||||
printf(R"(
|
||||
--loop-fusion
|
||||
Identifies adjacent loops with the same lower and upper bound.
|
||||
If this is legal, then merge the loops into a single loop.
|
||||
Includes heuristics to ensure it does not increase number of
|
||||
registers too much, while reducing the number of loads from
|
||||
memory. Takes an additional positive integer argument to set
|
||||
the maximum number of registers.)");
|
||||
printf(R"(
|
||||
--loop-invariant-code-motion
|
||||
Identifies code in loops that has the same value for every
|
||||
iteration of the loop, and move it to the loop pre-header.)");
|
||||
printf(R"(
|
||||
--loop-unroll
|
||||
Fully unrolls loops marked with the Unroll flag)");
|
||||
printf(R"(
|
||||
--loop-unroll-partial
|
||||
Partially unrolls loops marked with the Unroll flag. Takes an
|
||||
additional non-0 integer argument to set the unroll factor, or
|
||||
how many times a loop body should be duplicated)");
|
||||
printf(R"(
|
||||
--loop-peeling
|
||||
Execute few first (respectively last) iterations before
|
||||
(respectively after) the loop if it can elide some branches.)");
|
||||
printf(R"(
|
||||
--loop-peeling-threshold
|
||||
Takes a non-0 integer argument to set the loop peeling code size
|
||||
growth threshold. The threshold prevents the loop peeling
|
||||
from happening if the code size increase created by
|
||||
the optimization is above the threshold.)");
|
||||
printf(R"(
|
||||
--max-id-bound=<n>
|
||||
Sets the maximum value for the id bound for the moudle. The
|
||||
default is the minimum value for this limit, 0x3FFFFF. See
|
||||
section 2.17 of the Spir-V specification.)");
|
||||
printf(R"(
|
||||
--merge-blocks
|
||||
Join two blocks into a single block if the second has the
|
||||
first as its only predecessor. Performed only on entry point
|
||||
call tree functions.)");
|
||||
printf(R"(
|
||||
--merge-return
|
||||
Changes functions that have multiple return statements so they
|
||||
have a single return statement.
|
||||
|
||||
For structured control flow it is assumed that the only
|
||||
unreachable blocks in the function are trivial merge and continue
|
||||
blocks.
|
||||
|
||||
A trivial merge block contains the label and an OpUnreachable
|
||||
instructions, nothing else. A trivial continue block contain a
|
||||
label and an OpBranch to the header, nothing else.
|
||||
|
||||
These conditions are guaranteed to be met after running
|
||||
dead-branch elimination.)");
|
||||
printf(R"(
|
||||
--loop-unswitch
|
||||
Hoists loop-invariant conditionals out of loops by duplicating
|
||||
the loop on each branch of the conditional and adjusting each
|
||||
copy of the loop.)");
|
||||
printf(R"(
|
||||
-O
|
||||
Optimize for performance. Apply a sequence of transformations
|
||||
in an attempt to improve the performance of the generated
|
||||
code. For this version of the optimizer, this flag is equivalent
|
||||
to specifying the following optimization code names:
|
||||
%s)",
|
||||
GetOptimizationPasses().c_str());
|
||||
printf(R"(
|
||||
-Os
|
||||
Optimize for size. Apply a sequence of transformations in an
|
||||
attempt to minimize the size of the generated code. For this
|
||||
version of the optimizer, this flag is equivalent to specifying
|
||||
the following optimization code names:
|
||||
%s
|
||||
|
||||
NOTE: The specific transformations done by -O and -Os change
|
||||
from release to release.)",
|
||||
GetSizePasses().c_str());
|
||||
printf(R"(
|
||||
-Oconfig=<file>
|
||||
Apply the sequence of transformations indicated in <file>.
|
||||
This file contains a sequence of strings separated by whitespace
|
||||
(tabs, newlines or blanks). Each string is one of the flags
|
||||
accepted by spirv-opt. Optimizations will be applied in the
|
||||
sequence they appear in the file. This is equivalent to
|
||||
specifying all the flags on the command line. For example,
|
||||
given the file opts.cfg with the content:
|
||||
|
||||
--inline-entry-points-exhaustive
|
||||
--eliminate-dead-code-aggressive
|
||||
|
||||
The following two invocations to spirv-opt are equivalent:
|
||||
|
||||
$ spirv-opt -Oconfig=opts.cfg program.spv
|
||||
|
||||
$ spirv-opt --inline-entry-points-exhaustive \
|
||||
--eliminate-dead-code-aggressive program.spv
|
||||
|
||||
Lines starting with the character '#' in the configuration
|
||||
file indicate a comment and will be ignored.
|
||||
|
||||
The -O, -Os, and -Oconfig flags act as macros. Using one of them
|
||||
is equivalent to explicitly inserting the underlying flags at
|
||||
that position in the command line. For example, the invocation
|
||||
'spirv-opt --merge-blocks -O ...' applies the transformation
|
||||
--merge-blocks followed by all the transformations implied by
|
||||
-O.)");
|
||||
printf(R"(
|
||||
--preserve-bindings
|
||||
Ensure that the optimizer preserves all bindings declared within
|
||||
the module, even when those bindings are unused.)");
|
||||
printf(R"(
|
||||
--preserve-spec-constants
|
||||
Ensure that the optimizer preserves all specialization constants declared
|
||||
within the module, even when those constants are unused.)");
|
||||
printf(R"(
|
||||
--print-all
|
||||
Print SPIR-V assembly to standard error output before each pass
|
||||
and after the last pass.)");
|
||||
printf(R"(
|
||||
--private-to-local
|
||||
Change the scope of private variables that are used in a single
|
||||
function to that function.)");
|
||||
printf(R"(
|
||||
--reduce-load-size
|
||||
Replaces loads of composite objects where not every component is
|
||||
used by loads of just the elements that are used.)");
|
||||
printf(R"(
|
||||
--redundancy-elimination
|
||||
Looks for instructions in the same function that compute the
|
||||
same value, and deletes the redundant ones.)");
|
||||
printf(R"(
|
||||
--relax-float-ops
|
||||
Decorate all float operations with RelaxedPrecision if not already
|
||||
so decorated. This does not decorate types or variables.)");
|
||||
printf(R"(
|
||||
--relax-struct-store
|
||||
Allow store from one struct type to a different type with
|
||||
compatible layout and members. This option is forwarded to the
|
||||
validator.)");
|
||||
printf(R"(
|
||||
--remove-duplicates
|
||||
Removes duplicate types, decorations, capabilities and extension
|
||||
instructions.)");
|
||||
printf(R"(
|
||||
--replace-invalid-opcode
|
||||
Replaces instructions whose opcode is valid for shader modules,
|
||||
but not for the current shader stage. To have an effect, all
|
||||
entry points must have the same execution model.)");
|
||||
printf(R"(
|
||||
--ssa-rewrite
|
||||
Replace loads and stores to function local variables with
|
||||
operations on SSA IDs.)");
|
||||
printf(R"(
|
||||
--scalar-replacement[=<n>]
|
||||
Replace aggregate function scope variables that are only accessed
|
||||
via their elements with new function variables representing each
|
||||
element. <n> is a limit on the size of the aggragates that will
|
||||
be replaced. 0 means there is no limit. The default value is
|
||||
100.)");
|
||||
printf(R"(
|
||||
--set-spec-const-default-value "<spec id>:<default value> ..."
|
||||
Set the default values of the specialization constants with
|
||||
<spec id>:<default value> pairs specified in a double-quoted
|
||||
string. <spec id>:<default value> pairs must be separated by
|
||||
blank spaces, and in each pair, spec id and default value must
|
||||
be separated with colon ':' without any blank spaces in between.
|
||||
e.g.: --set-spec-const-default-value "1:100 2:400")");
|
||||
printf(R"(
|
||||
--simplify-instructions
|
||||
Will simplify all instructions in the function as much as
|
||||
possible.)");
|
||||
printf(R"(
|
||||
--split-invalid-unreachable
|
||||
Attempts to legalize for WebGPU cases where an unreachable
|
||||
merge-block is also a continue-target by splitting it into two
|
||||
seperate blocks. There exist legal, for Vulkan, instances of this
|
||||
pattern that cannot be converted into legal WebGPU, so this
|
||||
conversion may not succeed.)");
|
||||
printf(R"(
|
||||
--skip-validation
|
||||
Will not validate the SPIR-V before optimizing. If the SPIR-V
|
||||
is invalid, the optimizer may fail or generate incorrect code.
|
||||
This options should be used rarely, and with caution.)");
|
||||
printf(R"(
|
||||
--strength-reduction
|
||||
Replaces instructions with equivalent and less expensive ones.)");
|
||||
printf(R"(
|
||||
--strip-atomic-counter-memory
|
||||
Removes AtomicCountMemory bit from memory semantics values.)");
|
||||
printf(R"(
|
||||
--strip-debug
|
||||
Remove all debug instructions.)");
|
||||
printf(R"(
|
||||
--strip-reflect
|
||||
Remove all reflection information. For now, this covers
|
||||
reflection information defined by SPV_GOOGLE_hlsl_functionality1.)");
|
||||
printf(R"(
|
||||
--target-env=<env>
|
||||
Set the target environment. Without this flag the target
|
||||
enviroment defaults to spv1.3. <env> must be one of
|
||||
{%s})",
|
||||
target_env_list.c_str());
|
||||
printf(R"(
|
||||
--time-report
|
||||
Print the resource utilization of each pass (e.g., CPU time,
|
||||
RSS) to standard error output. Currently it supports only Unix
|
||||
systems. This option is the same as -ftime-report in GCC. It
|
||||
prints CPU/WALL/USR/SYS time (and RSS if possible), but note that
|
||||
USR/SYS time are returned by getrusage() and can have a small
|
||||
error.)");
|
||||
printf(R"(
|
||||
--upgrade-memory-model
|
||||
Upgrades the Logical GLSL450 memory model to Logical VulkanKHR.
|
||||
Transforms memory, image, atomic and barrier operations to conform
|
||||
to that model's requirements.)");
|
||||
printf(R"(
|
||||
--vector-dce
|
||||
This pass looks for components of vectors that are unused, and
|
||||
removes them from the vector. Note this would still leave around
|
||||
lots of dead code that a pass of ADCE will be able to remove.)");
|
||||
printf(R"(
|
||||
--vulkan-to-webgpu
|
||||
Turns on the prescribed passes for converting from Vulkan to
|
||||
WebGPU and sets the target environment to webgpu0. Other passes
|
||||
may be turned on via additional flags, but such combinations are
|
||||
not tested.
|
||||
Using --target-env with this flag is not allowed.
|
||||
|
||||
This flag is the equivalent of passing in --target-env=webgpu0
|
||||
and specifying the following optimization code names:
|
||||
%s
|
||||
|
||||
NOTE: This flag is a WIP and its behaviour is subject to change.)",
|
||||
GetVulkanToWebGPUPasses().c_str());
|
||||
printf(R"(
|
||||
--webgpu-to-vulkan
|
||||
Turns on the prescribed passes for converting from WebGPU to
|
||||
Vulkan and sets the target environment to vulkan1.1. Other passes
|
||||
may be turned on via additional flags, but such combinations are
|
||||
not tested.
|
||||
Using --target-env with this flag is not allowed.
|
||||
|
||||
This flag is the equivalent of passing in --target-env=vulkan1.1
|
||||
and specifying the following optimization code names:
|
||||
%s
|
||||
|
||||
NOTE: This flag is a WIP and its behaviour is subject to change.)",
|
||||
GetWebGPUToVulkanPasses().c_str());
|
||||
printf(R"(
|
||||
--workaround-1209
|
||||
Rewrites instructions for which there are known driver bugs to
|
||||
avoid triggering those bugs.
|
||||
Current workarounds: Avoid OpUnreachable in loops.)");
|
||||
printf(R"(
|
||||
--unify-const
|
||||
Remove the duplicated constants.)");
|
||||
printf(R"(
|
||||
--validate-after-all
|
||||
Validate the module after each pass is performed.)");
|
||||
printf(R"(
|
||||
-h, --help
|
||||
Print this help.)");
|
||||
printf(R"(
|
||||
--version
|
||||
Display optimizer version information.
|
||||
)");
|
||||
}
|
||||
|
||||
// Reads command-line flags the file specified in |oconfig_flag|. This string
|
||||
// is assumed to have the form "-Oconfig=FILENAME". This function parses the
|
||||
// string and extracts the file name after the '=' sign.
|
||||
//
|
||||
// Flags found in |FILENAME| are pushed at the end of the vector |file_flags|.
|
||||
//
|
||||
// This function returns true on success, false on failure.
|
||||
bool ReadFlagsFromFile(const char* oconfig_flag,
|
||||
std::vector<std::string>* file_flags) {
|
||||
const char* fname = strchr(oconfig_flag, '=');
|
||||
if (fname == nullptr || fname[0] != '=') {
|
||||
spvtools::Errorf(opt_diagnostic, nullptr, {}, "Invalid -Oconfig flag %s",
|
||||
oconfig_flag);
|
||||
return false;
|
||||
}
|
||||
fname++;
|
||||
|
||||
std::ifstream input_file;
|
||||
input_file.open(fname);
|
||||
if (input_file.fail()) {
|
||||
spvtools::Errorf(opt_diagnostic, nullptr, {}, "Could not open file '%s'",
|
||||
fname);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
while (std::getline(input_file, line)) {
|
||||
// Ignore empty lines and lines starting with the comment marker '#'.
|
||||
if (line.length() == 0 || line[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Tokenize the line. Add all found tokens to the list of found flags. This
|
||||
// mimics the way the shell will parse whitespace on the command line. NOTE:
|
||||
// This does not support quoting and it is not intended to.
|
||||
std::istringstream iss(line);
|
||||
while (!iss.eof()) {
|
||||
std::string flag;
|
||||
iss >> flag;
|
||||
file_flags->push_back(flag);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
OptStatus ParseFlags(int argc, const char** argv,
|
||||
spvtools::Optimizer* optimizer, const char** in_file,
|
||||
const char** out_file,
|
||||
spvtools::ValidatorOptions* validator_options,
|
||||
spvtools::OptimizerOptions* optimizer_options);
|
||||
|
||||
// Parses and handles the -Oconfig flag. |prog_name| contains the name of
|
||||
// the spirv-opt binary (used to build a new argv vector for the recursive
|
||||
// invocation to ParseFlags). |opt_flag| contains the -Oconfig=FILENAME flag.
|
||||
// |optimizer|, |in_file|, |out_file|, |validator_options|, and
|
||||
// |optimizer_options| are as in ParseFlags.
|
||||
//
|
||||
// This returns the same OptStatus instance returned by ParseFlags.
|
||||
OptStatus ParseOconfigFlag(const char* prog_name, const char* opt_flag,
|
||||
spvtools::Optimizer* optimizer, const char** in_file,
|
||||
const char** out_file,
|
||||
spvtools::ValidatorOptions* validator_options,
|
||||
spvtools::OptimizerOptions* optimizer_options) {
|
||||
std::vector<std::string> flags;
|
||||
flags.push_back(prog_name);
|
||||
|
||||
std::vector<std::string> file_flags;
|
||||
if (!ReadFlagsFromFile(opt_flag, &file_flags)) {
|
||||
spvtools::Error(opt_diagnostic, nullptr, {},
|
||||
"Could not read optimizer flags from configuration file");
|
||||
return {OPT_STOP, 1};
|
||||
}
|
||||
flags.insert(flags.end(), file_flags.begin(), file_flags.end());
|
||||
|
||||
const char** new_argv = new const char*[flags.size()];
|
||||
for (size_t i = 0; i < flags.size(); i++) {
|
||||
if (flags[i].find("-Oconfig=") != std::string::npos) {
|
||||
spvtools::Error(
|
||||
opt_diagnostic, nullptr, {},
|
||||
"Flag -Oconfig= may not be used inside the configuration file");
|
||||
return {OPT_STOP, 1};
|
||||
}
|
||||
new_argv[i] = flags[i].c_str();
|
||||
}
|
||||
|
||||
auto ret_val =
|
||||
ParseFlags(static_cast<int>(flags.size()), new_argv, optimizer, in_file,
|
||||
out_file, validator_options, optimizer_options);
|
||||
delete[] new_argv;
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
// Canonicalize the flag in |argv[argi]| of the form '--pass arg' into
|
||||
// '--pass=arg'. The optimizer only accepts arguments to pass names that use the
|
||||
// form '--pass_name=arg'. Since spirv-opt also accepts the other form, this
|
||||
// function makes the necessary conversion.
|
||||
//
|
||||
// Pass flags that require additional arguments should be handled here. Note
|
||||
// that additional arguments should be given as a single string. If the flag
|
||||
// requires more than one argument, the pass creator in
|
||||
// Optimizer::GetPassFromFlag() should parse it accordingly (e.g., see the
|
||||
// handler for --set-spec-const-default-value).
|
||||
//
|
||||
// If the argument requests one of the passes that need an additional argument,
|
||||
// |argi| is modified to point past the current argument, and the string
|
||||
// "argv[argi]=argv[argi + 1]" is returned. Otherwise, |argi| is unmodified and
|
||||
// the string "|argv[argi]|" is returned.
|
||||
std::string CanonicalizeFlag(const char** argv, int argc, int* argi) {
|
||||
const char* cur_arg = argv[*argi];
|
||||
const char* next_arg = (*argi + 1 < argc) ? argv[*argi + 1] : nullptr;
|
||||
std::ostringstream canonical_arg;
|
||||
canonical_arg << cur_arg;
|
||||
|
||||
// NOTE: DO NOT ADD NEW FLAGS HERE.
|
||||
//
|
||||
// These flags are supported for backwards compatibility. When adding new
|
||||
// passes that need extra arguments in its command-line flag, please make them
|
||||
// use the syntax "--pass_name[=pass_arg].
|
||||
if (0 == strcmp(cur_arg, "--set-spec-const-default-value") ||
|
||||
0 == strcmp(cur_arg, "--loop-fission") ||
|
||||
0 == strcmp(cur_arg, "--loop-fusion") ||
|
||||
0 == strcmp(cur_arg, "--loop-unroll-partial") ||
|
||||
0 == strcmp(cur_arg, "--loop-peeling-threshold")) {
|
||||
if (next_arg) {
|
||||
canonical_arg << "=" << next_arg;
|
||||
++(*argi);
|
||||
}
|
||||
}
|
||||
|
||||
return canonical_arg.str();
|
||||
}
|
||||
|
||||
// Parses command-line flags. |argc| contains the number of command-line flags.
|
||||
// |argv| points to an array of strings holding the flags. |optimizer| is the
|
||||
// Optimizer instance used to optimize the program.
|
||||
//
|
||||
// On return, this function stores the name of the input program in |in_file|.
|
||||
// The name of the output file in |out_file|. The return value indicates whether
|
||||
// optimization should continue and a status code indicating an error or
|
||||
// success.
|
||||
OptStatus ParseFlags(int argc, const char** argv,
|
||||
spvtools::Optimizer* optimizer, const char** in_file,
|
||||
const char** out_file,
|
||||
spvtools::ValidatorOptions* validator_options,
|
||||
spvtools::OptimizerOptions* optimizer_options) {
|
||||
std::vector<std::string> pass_flags;
|
||||
bool target_env_set = false;
|
||||
bool vulkan_to_webgpu_set = false;
|
||||
bool webgpu_to_vulkan_set = false;
|
||||
for (int argi = 1; argi < argc; ++argi) {
|
||||
const char* cur_arg = argv[argi];
|
||||
if ('-' == cur_arg[0]) {
|
||||
if (0 == strcmp(cur_arg, "--version")) {
|
||||
spvtools::Logf(opt_diagnostic, SPV_MSG_INFO, nullptr, {}, "%s\n",
|
||||
spvSoftwareVersionDetailsString());
|
||||
return {OPT_STOP, 0};
|
||||
} else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) {
|
||||
PrintUsage(argv[0]);
|
||||
return {OPT_STOP, 0};
|
||||
} else if (0 == strcmp(cur_arg, "-o")) {
|
||||
if (!*out_file && argi + 1 < argc) {
|
||||
*out_file = argv[++argi];
|
||||
} else {
|
||||
PrintUsage(argv[0]);
|
||||
return {OPT_STOP, 1};
|
||||
}
|
||||
} else if ('\0' == cur_arg[1]) {
|
||||
// Setting a filename of "-" to indicate stdin.
|
||||
if (!*in_file) {
|
||||
*in_file = cur_arg;
|
||||
} else {
|
||||
spvtools::Error(opt_diagnostic, nullptr, {},
|
||||
"More than one input file specified");
|
||||
return {OPT_STOP, 1};
|
||||
}
|
||||
} else if (0 == strncmp(cur_arg, "-Oconfig=", sizeof("-Oconfig=") - 1)) {
|
||||
OptStatus status =
|
||||
ParseOconfigFlag(argv[0], cur_arg, optimizer, in_file, out_file,
|
||||
validator_options, optimizer_options);
|
||||
if (status.action != OPT_CONTINUE) {
|
||||
return status;
|
||||
}
|
||||
} else if (0 == strcmp(cur_arg, "--skip-validation")) {
|
||||
optimizer_options->set_run_validator(false);
|
||||
} else if (0 == strcmp(cur_arg, "--print-all")) {
|
||||
optimizer->SetPrintAll(&std::cerr);
|
||||
} else if (0 == strcmp(cur_arg, "--preserve-bindings")) {
|
||||
optimizer_options->set_preserve_bindings(true);
|
||||
} else if (0 == strcmp(cur_arg, "--preserve-spec-constants")) {
|
||||
optimizer_options->set_preserve_spec_constants(true);
|
||||
} else if (0 == strcmp(cur_arg, "--time-report")) {
|
||||
optimizer->SetTimeReport(&std::cerr);
|
||||
} else if (0 == strcmp(cur_arg, "--relax-struct-store")) {
|
||||
validator_options->SetRelaxStructStore(true);
|
||||
} else if (0 == strncmp(cur_arg, "--max-id-bound=",
|
||||
sizeof("--max-id-bound=") - 1)) {
|
||||
auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
|
||||
// Will not allow values in the range [2^31,2^32).
|
||||
uint32_t max_id_bound =
|
||||
static_cast<uint32_t>(atoi(split_flag.second.c_str()));
|
||||
|
||||
// That SPIR-V mandates the minimum value for max id bound but
|
||||
// implementations may allow higher minimum bounds.
|
||||
if (max_id_bound < kDefaultMaxIdBound) {
|
||||
spvtools::Error(opt_diagnostic, nullptr, {},
|
||||
"The max id bound must be at least 0x3FFFFF");
|
||||
return {OPT_STOP, 1};
|
||||
}
|
||||
optimizer_options->set_max_id_bound(max_id_bound);
|
||||
validator_options->SetUniversalLimit(spv_validator_limit_max_id_bound,
|
||||
max_id_bound);
|
||||
} else if (0 == strncmp(cur_arg,
|
||||
"--target-env=", sizeof("--target-env=") - 1)) {
|
||||
target_env_set = true;
|
||||
if (vulkan_to_webgpu_set) {
|
||||
spvtools::Error(opt_diagnostic, nullptr, {},
|
||||
"--vulkan-to-webgpu defines the target environment, "
|
||||
"so --target-env cannot be set at the same time");
|
||||
return {OPT_STOP, 1};
|
||||
}
|
||||
if (webgpu_to_vulkan_set) {
|
||||
spvtools::Error(opt_diagnostic, nullptr, {},
|
||||
"--webgpu-to-vulkan defines the target environment, "
|
||||
"so --target-env cannot be set at the same time");
|
||||
return {OPT_STOP, 1};
|
||||
}
|
||||
const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
|
||||
const auto target_env_str = split_flag.second.c_str();
|
||||
spv_target_env target_env;
|
||||
if (!spvParseTargetEnv(target_env_str, &target_env)) {
|
||||
spvtools::Error(opt_diagnostic, nullptr, {},
|
||||
"Invalid value passed to --target-env");
|
||||
return {OPT_STOP, 1};
|
||||
}
|
||||
optimizer->SetTargetEnv(target_env);
|
||||
} else if (0 == strcmp(cur_arg, "--vulkan-to-webgpu")) {
|
||||
vulkan_to_webgpu_set = true;
|
||||
if (target_env_set) {
|
||||
spvtools::Error(opt_diagnostic, nullptr, {},
|
||||
"--vulkan-to-webgpu defines the target environment, "
|
||||
"so --target-env cannot be set at the same time");
|
||||
return {OPT_STOP, 1};
|
||||
}
|
||||
if (webgpu_to_vulkan_set) {
|
||||
spvtools::Error(opt_diagnostic, nullptr, {},
|
||||
"Cannot use both --webgpu-to-vulkan and "
|
||||
"--vulkan-to-webgpu at the same time, invoke twice "
|
||||
"if you are wanting to go to and from");
|
||||
return {OPT_STOP, 1};
|
||||
}
|
||||
|
||||
optimizer->SetTargetEnv(SPV_ENV_VULKAN_1_1);
|
||||
optimizer->RegisterVulkanToWebGPUPasses();
|
||||
} else if (0 == strcmp(cur_arg, "--webgpu-to-vulkan")) {
|
||||
webgpu_to_vulkan_set = true;
|
||||
if (target_env_set) {
|
||||
spvtools::Error(opt_diagnostic, nullptr, {},
|
||||
"--webgpu-to-vulkan defines the target environment, "
|
||||
"so --target-env cannot be set at the same time");
|
||||
return {OPT_STOP, 1};
|
||||
}
|
||||
if (vulkan_to_webgpu_set) {
|
||||
spvtools::Error(opt_diagnostic, nullptr, {},
|
||||
"Cannot use both --webgpu-to-vulkan and "
|
||||
"--vulkan-to-webgpu at the same time, invoke twice "
|
||||
"if you are wanting to go to and from");
|
||||
return {OPT_STOP, 1};
|
||||
}
|
||||
|
||||
optimizer->SetTargetEnv(SPV_ENV_WEBGPU_0);
|
||||
optimizer->RegisterWebGPUToVulkanPasses();
|
||||
} else if (0 == strcmp(cur_arg, "--validate-after-all")) {
|
||||
optimizer->SetValidateAfterAll(true);
|
||||
} else {
|
||||
// Some passes used to accept the form '--pass arg', canonicalize them
|
||||
// to '--pass=arg'.
|
||||
pass_flags.push_back(CanonicalizeFlag(argv, argc, &argi));
|
||||
|
||||
// If we were requested to legalize SPIR-V generated from the HLSL
|
||||
// front-end, skip validation.
|
||||
if (0 == strcmp(cur_arg, "--legalize-hlsl")) {
|
||||
validator_options->SetBeforeHlslLegalization(true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!*in_file) {
|
||||
*in_file = cur_arg;
|
||||
} else {
|
||||
spvtools::Error(opt_diagnostic, nullptr, {},
|
||||
"More than one input file specified");
|
||||
return {OPT_STOP, 1};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!optimizer->RegisterPassesFromFlags(pass_flags)) {
|
||||
return {OPT_STOP, 1};
|
||||
}
|
||||
|
||||
return {OPT_CONTINUE, 0};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
const char* in_file = nullptr;
|
||||
const char* out_file = nullptr;
|
||||
|
||||
spv_target_env target_env = kDefaultEnvironment;
|
||||
|
||||
spvtools::Optimizer optimizer(target_env);
|
||||
optimizer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
|
||||
|
||||
spvtools::ValidatorOptions validator_options;
|
||||
spvtools::OptimizerOptions optimizer_options;
|
||||
OptStatus status = ParseFlags(argc, argv, &optimizer, &in_file, &out_file,
|
||||
&validator_options, &optimizer_options);
|
||||
optimizer_options.set_validator_options(validator_options);
|
||||
|
||||
if (status.action == OPT_STOP) {
|
||||
return status.code;
|
||||
}
|
||||
|
||||
if (out_file == nullptr) {
|
||||
spvtools::Error(opt_diagnostic, nullptr, {}, "-o required");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> binary;
|
||||
if (!ReadFile<uint32_t>(in_file, "rb", &binary)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// By using the same vector as input and output, we save time in the case
|
||||
// that there was no change.
|
||||
bool ok =
|
||||
optimizer.Run(binary.data(), binary.size(), &binary, optimizer_options);
|
||||
|
||||
if (!WriteFile<uint32_t>(out_file, "wb", binary.data(), binary.size())) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return ok ? 0 : 1;
|
||||
}
|
||||
282
3rdparty/spirv-tools/tools/reduce/reduce.cpp
vendored
282
3rdparty/spirv-tools/tools/reduce/reduce.cpp
vendored
@@ -1,282 +0,0 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
|
||||
#include "source/opt/build_module.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
#include "source/opt/log.h"
|
||||
#include "source/reduce/reducer.h"
|
||||
#include "source/spirv_reducer_options.h"
|
||||
#include "source/util/string_utils.h"
|
||||
#include "tools/io.h"
|
||||
#include "tools/util/cli_consumer.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Check that the std::system function can actually be used.
|
||||
bool CheckExecuteCommand() {
|
||||
int res = std::system(nullptr);
|
||||
return res != 0;
|
||||
}
|
||||
|
||||
// Execute a command using the shell.
|
||||
// Returns true if and only if the command's exit status was 0.
|
||||
bool ExecuteCommand(const std::string& command) {
|
||||
errno = 0;
|
||||
int status = std::system(command.c_str());
|
||||
assert(errno == 0 && "failed to execute command");
|
||||
// The result returned by 'system' is implementation-defined, but is
|
||||
// usually the case that the returned value is 0 when the command's exit
|
||||
// code was 0. We are assuming that here, and that's all we depend on.
|
||||
return status == 0;
|
||||
}
|
||||
|
||||
// Status and actions to perform after parsing command-line arguments.
|
||||
enum ReduceActions { REDUCE_CONTINUE, REDUCE_STOP };
|
||||
|
||||
struct ReduceStatus {
|
||||
ReduceActions action;
|
||||
int code;
|
||||
};
|
||||
|
||||
void PrintUsage(const char* program) {
|
||||
// NOTE: Please maintain flags in lexicographical order.
|
||||
printf(
|
||||
R"(%s - Reduce a SPIR-V binary file with respect to a user-provided
|
||||
interestingness test.
|
||||
|
||||
USAGE: %s [options] <input> <interestingness-test>
|
||||
|
||||
The SPIR-V binary is read from <input>.
|
||||
|
||||
Whether a binary is interesting is determined by <interestingness-test>, which
|
||||
should be the path to a script.
|
||||
|
||||
* The script must be executable.
|
||||
|
||||
* The script should take the path to a SPIR-V binary file (.spv) as its single
|
||||
argument, and exit with code 0 if and only if the binary file is
|
||||
interesting.
|
||||
|
||||
* Example: an interestingness test for reducing a SPIR-V binary file that
|
||||
causes tool "foo" to exit with error code 1 and print "Fatal error: bar" to
|
||||
standard error should:
|
||||
- invoke "foo" on the binary passed as the script argument;
|
||||
- capture the return code and standard error from "bar";
|
||||
- exit with code 0 if and only if the return code of "foo" was 1 and the
|
||||
standard error from "bar" contained "Fatal error: bar".
|
||||
|
||||
* The reducer does not place a time limit on how long the interestingness test
|
||||
takes to run, so it is advisable to use per-command timeouts inside the
|
||||
script when invoking SPIR-V-processing tools (such as "foo" in the above
|
||||
example).
|
||||
|
||||
NOTE: The reducer is a work in progress.
|
||||
|
||||
Options (in lexicographical order):
|
||||
|
||||
--fail-on-validation-error
|
||||
Stop reduction with an error if any reduction step produces a
|
||||
SPIR-V module that fails to validate.
|
||||
-h, --help
|
||||
Print this help.
|
||||
--step-limit
|
||||
32-bit unsigned integer specifying maximum number of steps the
|
||||
reducer will take before giving up.
|
||||
--version
|
||||
Display reducer version information.
|
||||
|
||||
Supported validator options are as follows. See `spirv-val --help` for details.
|
||||
--before-hlsl-legalization
|
||||
--relax-block-layout
|
||||
--relax-logical-pointer
|
||||
--relax-struct-store
|
||||
--scalar-block-layout
|
||||
--skip-block-layout
|
||||
)",
|
||||
program, program);
|
||||
}
|
||||
|
||||
// Message consumer for this tool. Used to emit diagnostics during
|
||||
// initialization and setup. Note that |source| and |position| are irrelevant
|
||||
// here because we are still not processing a SPIR-V input file.
|
||||
void ReduceDiagnostic(spv_message_level_t level, const char* /*source*/,
|
||||
const spv_position_t& /*position*/, const char* message) {
|
||||
if (level == SPV_MSG_ERROR) {
|
||||
fprintf(stderr, "error: ");
|
||||
}
|
||||
fprintf(stderr, "%s\n", message);
|
||||
}
|
||||
|
||||
ReduceStatus ParseFlags(int argc, const char** argv, const char** in_file,
|
||||
const char** interestingness_test,
|
||||
spvtools::ReducerOptions* reducer_options,
|
||||
spvtools::ValidatorOptions* validator_options) {
|
||||
uint32_t positional_arg_index = 0;
|
||||
|
||||
for (int argi = 1; argi < argc; ++argi) {
|
||||
const char* cur_arg = argv[argi];
|
||||
if ('-' == cur_arg[0]) {
|
||||
if (0 == strcmp(cur_arg, "--version")) {
|
||||
spvtools::Logf(ReduceDiagnostic, SPV_MSG_INFO, nullptr, {}, "%s\n",
|
||||
spvSoftwareVersionDetailsString());
|
||||
return {REDUCE_STOP, 0};
|
||||
} else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) {
|
||||
PrintUsage(argv[0]);
|
||||
return {REDUCE_STOP, 0};
|
||||
} else if ('\0' == cur_arg[1]) {
|
||||
// We do not support reduction from standard input. We could support
|
||||
// this if there was a compelling use case.
|
||||
PrintUsage(argv[0]);
|
||||
return {REDUCE_STOP, 0};
|
||||
} else if (0 == strncmp(cur_arg,
|
||||
"--step-limit=", sizeof("--step-limit=") - 1)) {
|
||||
const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
|
||||
char* end = nullptr;
|
||||
errno = 0;
|
||||
const auto step_limit =
|
||||
static_cast<uint32_t>(strtol(split_flag.second.c_str(), &end, 10));
|
||||
assert(end != split_flag.second.c_str() && errno == 0);
|
||||
reducer_options->set_step_limit(step_limit);
|
||||
}
|
||||
} else if (positional_arg_index == 0) {
|
||||
// Input file name
|
||||
assert(!*in_file);
|
||||
*in_file = cur_arg;
|
||||
positional_arg_index++;
|
||||
} else if (positional_arg_index == 1) {
|
||||
assert(!*interestingness_test);
|
||||
*interestingness_test = cur_arg;
|
||||
positional_arg_index++;
|
||||
} else if (0 == strcmp(cur_arg, "--fail-on-validation-error")) {
|
||||
reducer_options->set_fail_on_validation_error(true);
|
||||
} else if (0 == strcmp(cur_arg, "--before-hlsl-legalization")) {
|
||||
validator_options->SetBeforeHlslLegalization(true);
|
||||
} else if (0 == strcmp(cur_arg, "--relax-logical-pointer")) {
|
||||
validator_options->SetRelaxLogicalPointer(true);
|
||||
} else if (0 == strcmp(cur_arg, "--relax-block-layout")) {
|
||||
validator_options->SetRelaxBlockLayout(true);
|
||||
} else if (0 == strcmp(cur_arg, "--scalar-block-layout")) {
|
||||
validator_options->SetScalarBlockLayout(true);
|
||||
} else if (0 == strcmp(cur_arg, "--skip-block-layout")) {
|
||||
validator_options->SetSkipBlockLayout(true);
|
||||
} else if (0 == strcmp(cur_arg, "--relax-struct-store")) {
|
||||
validator_options->SetRelaxStructStore(true);
|
||||
} else {
|
||||
spvtools::Error(ReduceDiagnostic, nullptr, {},
|
||||
"Too many positional arguments specified");
|
||||
return {REDUCE_STOP, 1};
|
||||
}
|
||||
}
|
||||
|
||||
if (!*in_file) {
|
||||
spvtools::Error(ReduceDiagnostic, nullptr, {}, "No input file specified");
|
||||
return {REDUCE_STOP, 1};
|
||||
}
|
||||
|
||||
if (!*interestingness_test) {
|
||||
spvtools::Error(ReduceDiagnostic, nullptr, {},
|
||||
"No interestingness test specified");
|
||||
return {REDUCE_STOP, 1};
|
||||
}
|
||||
|
||||
return {REDUCE_CONTINUE, 0};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Dumps |binary| to file |filename|. Useful for interactive debugging.
|
||||
void DumpShader(const std::vector<uint32_t>& binary, const char* filename) {
|
||||
auto write_file_succeeded =
|
||||
WriteFile(filename, "wb", &binary[0], binary.size());
|
||||
if (!write_file_succeeded) {
|
||||
std::cerr << "Failed to dump shader" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Dumps the SPIRV-V module in |context| to file |filename|. Useful for
|
||||
// interactive debugging.
|
||||
void DumpShader(spvtools::opt::IRContext* context, const char* filename) {
|
||||
std::vector<uint32_t> binary;
|
||||
context->module()->ToBinary(&binary, false);
|
||||
DumpShader(binary, filename);
|
||||
}
|
||||
|
||||
const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5;
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
const char* in_file = nullptr;
|
||||
const char* interestingness_test = nullptr;
|
||||
|
||||
spv_target_env target_env = kDefaultEnvironment;
|
||||
spvtools::ReducerOptions reducer_options;
|
||||
spvtools::ValidatorOptions validator_options;
|
||||
|
||||
ReduceStatus status = ParseFlags(argc, argv, &in_file, &interestingness_test,
|
||||
&reducer_options, &validator_options);
|
||||
|
||||
if (status.action == REDUCE_STOP) {
|
||||
return status.code;
|
||||
}
|
||||
|
||||
if (!CheckExecuteCommand()) {
|
||||
std::cerr << "could not find shell interpreter for executing a command"
|
||||
<< std::endl;
|
||||
return 2;
|
||||
}
|
||||
|
||||
spvtools::reduce::Reducer reducer(target_env);
|
||||
|
||||
reducer.SetInterestingnessFunction(
|
||||
[interestingness_test](std::vector<uint32_t> binary,
|
||||
uint32_t reductions_applied) -> bool {
|
||||
std::stringstream ss;
|
||||
ss << "temp_" << std::setw(4) << std::setfill('0') << reductions_applied
|
||||
<< ".spv";
|
||||
const auto spv_file = ss.str();
|
||||
const std::string command =
|
||||
std::string(interestingness_test) + " " + spv_file;
|
||||
auto write_file_succeeded =
|
||||
WriteFile(spv_file.c_str(), "wb", &binary[0], binary.size());
|
||||
(void)(write_file_succeeded);
|
||||
assert(write_file_succeeded);
|
||||
return ExecuteCommand(command);
|
||||
});
|
||||
|
||||
reducer.AddDefaultReductionPasses();
|
||||
|
||||
reducer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
|
||||
|
||||
std::vector<uint32_t> binary_in;
|
||||
if (!ReadFile<uint32_t>(in_file, "rb", &binary_in)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> binary_out;
|
||||
const auto reduction_status = reducer.Run(std::move(binary_in), &binary_out,
|
||||
reducer_options, validator_options);
|
||||
|
||||
if (reduction_status == spvtools::reduce::Reducer::ReductionResultStatus::
|
||||
kInitialStateNotInteresting ||
|
||||
!WriteFile<uint32_t>("_reduced_final.spv", "wb", binary_out.data(),
|
||||
binary_out.size())) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
25
3rdparty/spirv-tools/tools/sva/.eslintrc.json
vendored
25
3rdparty/spirv-tools/tools/sva/.eslintrc.json
vendored
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"node": true,
|
||||
"mocha": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"block-scoped-var": "error",
|
||||
"consistent-return": "error",
|
||||
"eqeqeq": ["error", "always"],
|
||||
"indent": [ "error", 2 ],
|
||||
"linebreak-style": [ "error", "unix" ],
|
||||
"no-eval": "error",
|
||||
"no-shadow": "error",
|
||||
"no-shadow-restricted-names": "error",
|
||||
"quotes": [ "error", "double" ],
|
||||
"semi": [ "error", "always" ]
|
||||
}
|
||||
}
|
||||
6
3rdparty/spirv-tools/tools/sva/.gitignore
vendored
6
3rdparty/spirv-tools/tools/sva/.gitignore
vendored
@@ -1,6 +0,0 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
third_party/spirv-headers
|
||||
o.sva
|
||||
build
|
||||
yarn-error.log
|
||||
41
3rdparty/spirv-tools/tools/sva/README.md
vendored
41
3rdparty/spirv-tools/tools/sva/README.md
vendored
@@ -1,41 +0,0 @@
|
||||
# SVA
|
||||
|
||||
SPIR-V Assember for WebGPU. The SPIR-V Assembler is a JavaScript library to
|
||||
convert SPIR-V assembly (as produced by spirv-dis in SPIR-V Tools) into a
|
||||
SPIR-V binary. The assembler assumes it is generating WebGPU SPIR-V and thus has
|
||||
the following limitations.
|
||||
|
||||
* Only 32 bit integers and floats supported
|
||||
* Only GLSL accepted as an extended instruction set
|
||||
* Doesn't support ! syntax for integers
|
||||
* Doesn't support hex encoding for float
|
||||
|
||||
```shell
|
||||
yarn install
|
||||
yarn test
|
||||
```
|
||||
|
||||
You can also use `yarn watch` to watch all of the files and re-run tests as
|
||||
needed.
|
||||
|
||||
## Webserver
|
||||
Using `yarn serve` will start a webserver on localhost:5000. If you load the
|
||||
`tests/index.html` file this will load the SVA files into browser.
|
||||
|
||||
## Command Line
|
||||
There is a simple assembler binary with can be executed from the command line.
|
||||
|
||||
```shell
|
||||
yarn sva tests/simple.spv_asm
|
||||
```
|
||||
|
||||
The above will generate a `o.sva` file in the current directory.
|
||||
|
||||
## Update spirv.data.json
|
||||
|
||||
If there is a new spirv-headers release update the externals folder checkout
|
||||
and then:
|
||||
|
||||
```shell
|
||||
./tools/process_grammar.rb > src/spirv.data.json
|
||||
```
|
||||
32
3rdparty/spirv-tools/tools/sva/bin/sva.js
vendored
32
3rdparty/spirv-tools/tools/sva/bin/sva.js
vendored
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
//
|
||||
// Copyright 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.
|
||||
|
||||
"use strict";
|
||||
|
||||
const fs = require("fs");
|
||||
|
||||
import SVA from "../src/sva.js";
|
||||
|
||||
let input = fs.readFileSync(process.argv[2], "utf-8");
|
||||
let u = SVA.assemble(input);
|
||||
|
||||
if (typeof u === "string") {
|
||||
console.log(u);
|
||||
} else {
|
||||
fs.writeFileSync("o.sva", new Buffer(u.buffer), (err) => {
|
||||
console.log(["ERROR", err]);
|
||||
});
|
||||
}
|
||||
1
3rdparty/spirv-tools/tools/sva/mocha.opts
vendored
1
3rdparty/spirv-tools/tools/sva/mocha.opts
vendored
@@ -1 +0,0 @@
|
||||
--recursive
|
||||
25
3rdparty/spirv-tools/tools/sva/package.json
vendored
25
3rdparty/spirv-tools/tools/sva/package.json
vendored
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"name": "sva",
|
||||
"version": "0.1.0",
|
||||
"description": "SPIR-V Assembler",
|
||||
"main": "index.js",
|
||||
"author": "dan sinclair <dsinclair@google.com>",
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"sva": "node -r esm bin/sva.js",
|
||||
"lint": "eslint --fix --ext .js .",
|
||||
"test": "mocha --require esm src/**/*_test.js",
|
||||
"watch": "mocha --require esm --watch --watch-extension js \"src/**/*_test.js\"",
|
||||
"serve": "serve",
|
||||
"bundle": "rollup -c"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.2.0",
|
||||
"eslint": "^6.3.0",
|
||||
"esm": "^3.2.25",
|
||||
"mocha": "^6.2.0",
|
||||
"rollup": "^1.21.4",
|
||||
"serve": "^11.1.0"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export default {
|
||||
input: 'src/sva.js',
|
||||
output: {
|
||||
file: 'build/sva.js',
|
||||
format: 'esm',
|
||||
}
|
||||
}
|
||||
98
3rdparty/spirv-tools/tools/sva/src/assembler.js
vendored
98
3rdparty/spirv-tools/tools/sva/src/assembler.js
vendored
@@ -1,98 +0,0 @@
|
||||
// Copyright 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.
|
||||
|
||||
export default class Assembler {
|
||||
static get GENERATOR_ID() { return 0; }
|
||||
|
||||
/**
|
||||
* @param {AST} the AST to build the SPIR-V from
|
||||
*/
|
||||
constructor(ast) {
|
||||
this.ast_ = ast;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles the AST into binary SPIR-V.
|
||||
* @return {Uint32Array} The SPIR-V binary data.
|
||||
*/
|
||||
assemble() {
|
||||
let total_size = 5;
|
||||
for (const inst of this.ast_.instructions()) {
|
||||
total_size += 1;
|
||||
for (const op of inst.operands()) {
|
||||
total_size += op.length();
|
||||
}
|
||||
}
|
||||
|
||||
let u = new Uint32Array(total_size);
|
||||
u[0] = 0x07230203; // Magic
|
||||
u[1] = 0x00010500; // Version 1.5
|
||||
u[2] = Assembler.GENERATOR_ID; // Generator magic number
|
||||
u[3] = this.ast_.getIdBounds(); // ID bounds
|
||||
u[4] = 0; // Reserved
|
||||
|
||||
let idx = 5;
|
||||
for (const inst of this.ast_.instructions()) {
|
||||
let op_size = 1;
|
||||
for (const op of inst.operands()) {
|
||||
op_size += op.length();
|
||||
}
|
||||
|
||||
u[idx++] = op_size << 16 | inst.opcode();
|
||||
for (const op of inst.operands()) {
|
||||
idx = this.processOp(u, idx, op);
|
||||
}
|
||||
}
|
||||
|
||||
return u;
|
||||
}
|
||||
|
||||
processOp(u, idx, op) {
|
||||
if (op.type() === "string") {
|
||||
let len = 0;
|
||||
let v = 0;
|
||||
for (const ch of op.value()) {
|
||||
v = v | (ch.charCodeAt(0) << (len * 8));
|
||||
len += 1;
|
||||
|
||||
if (len === 4) {
|
||||
u[idx++] = v;
|
||||
len = 0;
|
||||
v = 0;
|
||||
}
|
||||
}
|
||||
// Make sure either the terminating 0 byte is written or the last
|
||||
// partial word is written.
|
||||
u[idx++] = v;
|
||||
|
||||
} else if (op.type() === "float") {
|
||||
// TODO(dsinclair): Handle 64 bit floats ...
|
||||
let b = new ArrayBuffer(4);
|
||||
let f = new Float32Array(b);
|
||||
f[0] = op.value();
|
||||
|
||||
let u2 = new Uint32Array(b);
|
||||
|
||||
u[idx++] = u2[0];
|
||||
} else {
|
||||
u[idx++] = op.value();
|
||||
}
|
||||
|
||||
for (const param of op.params()) {
|
||||
idx = this.processOp(u, idx, param);
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
165
3rdparty/spirv-tools/tools/sva/src/assembler_test.js
vendored
165
3rdparty/spirv-tools/tools/sva/src/assembler_test.js
vendored
@@ -1,165 +0,0 @@
|
||||
// Copyright 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.
|
||||
|
||||
import { assert } from "chai";
|
||||
import Lexer from "./lexer";
|
||||
import Parser from "./parser";
|
||||
import grammar from "./spirv.data.js";
|
||||
import Assembler from "./assembler";
|
||||
|
||||
describe("assembler", () => {
|
||||
it("generates SPIR-V magic number", () => {
|
||||
let input = `; SPIR-V
|
||||
; Version: 1.0
|
||||
; Generator: Khronos Glslang Reference Front End; 7
|
||||
; Bound: 6
|
||||
; Schema: 0
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource GLSL 440
|
||||
OpName %main "main"
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd`;
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast, p.error);
|
||||
|
||||
let a = new Assembler(ast);
|
||||
let res = a.assemble();
|
||||
assert.equal(res[0], 0x07230203);
|
||||
});
|
||||
|
||||
it("assembles enumerant params", () => {
|
||||
let input = "OpExecutionMode %main LocalSize 2 3 4";
|
||||
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast, p.error);
|
||||
|
||||
let a = new Assembler(ast);
|
||||
let res = a.assemble();
|
||||
|
||||
assert.lengthOf(res, 11);
|
||||
assert.equal(res[5], (6 /* word count */ << 16) | 16 /* opcode */);
|
||||
assert.equal(res[6], 1 /* %main */);
|
||||
assert.equal(res[7], 17 /* LocalSize */);
|
||||
assert.equal(res[8], 2);
|
||||
assert.equal(res[9], 3);
|
||||
assert.equal(res[10], 4);
|
||||
});
|
||||
|
||||
it("assembles float 32 values", () => {
|
||||
let input = `%float = OpTypeFloat 32
|
||||
%float1 = OpConstant %float 0.400000006`;
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast, p.error);
|
||||
|
||||
let a = new Assembler(ast);
|
||||
let res = a.assemble();
|
||||
|
||||
assert.lengthOf(res, 12);
|
||||
assert.equal(res[8], (4 /* word count */ << 16) | 43 /* opcode */);
|
||||
assert.equal(res[9], 1 /* %float */);
|
||||
assert.equal(res[10], 2 /* %float */);
|
||||
assert.equal(res[11], 0x3ecccccd /* 0.400000006 */);
|
||||
});
|
||||
|
||||
describe("strings", () => {
|
||||
it("assembles 'abcd'", () => {
|
||||
let input = `OpName %mains "abcd"`;
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast, p.error);
|
||||
|
||||
let a = new Assembler(ast);
|
||||
let res = a.assemble();
|
||||
|
||||
assert.lengthOf(res, 9);
|
||||
assert.equal(res[5], (4 /* word count */ << 16) | 5 /* opcode */);
|
||||
assert.equal(res[6], 1 /* %mains */);
|
||||
assert.equal(res[7], 0x64636261 /* food */);
|
||||
assert.equal(res[8], 0x00000000 /* null byte */);
|
||||
});
|
||||
|
||||
it("assembles 'abcde'", () => {
|
||||
let input = `OpName %mains "abcde"`;
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast, p.error);
|
||||
|
||||
let a = new Assembler(ast);
|
||||
let res = a.assemble();
|
||||
|
||||
assert.lengthOf(res, 9);
|
||||
assert.equal(res[5], (4 /* word count */ << 16) | 5 /* opcode */);
|
||||
assert.equal(res[6], 1 /* %mains */);
|
||||
assert.equal(res[7], 0x64636261 /* abcd */);
|
||||
assert.equal(res[8], 0x00000065 /* e */);
|
||||
});
|
||||
|
||||
it("assembles 'abcdef'", () => {
|
||||
let input = `OpName %mains "abcdef"`;
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast, p.error);
|
||||
|
||||
let a = new Assembler(ast);
|
||||
let res = a.assemble();
|
||||
|
||||
assert.lengthOf(res, 9);
|
||||
assert.equal(res[5], (4 /* word count */ << 16) | 5 /* opcode */);
|
||||
assert.equal(res[6], 1 /* %mains */);
|
||||
assert.equal(res[7], 0x64636261 /* abcd */);
|
||||
assert.equal(res[8], 0x00006665 /* ef */);
|
||||
});
|
||||
|
||||
it("assembles 'abcdefg'", () => {
|
||||
let input = `OpName %mains "abcdefg"`;
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast, p.error);
|
||||
|
||||
let a = new Assembler(ast);
|
||||
let res = a.assemble();
|
||||
|
||||
assert.lengthOf(res, 9);
|
||||
assert.equal(res[5], (4 /* word count */ << 16) | 5 /* opcode */);
|
||||
assert.equal(res[6], 1 /* %mains */);
|
||||
assert.equal(res[7], 0x64636261 /* abcd */);
|
||||
assert.equal(res[8], 0x00676665 /* efg */);
|
||||
});
|
||||
});
|
||||
});
|
||||
141
3rdparty/spirv-tools/tools/sva/src/ast.js
vendored
141
3rdparty/spirv-tools/tools/sva/src/ast.js
vendored
@@ -1,141 +0,0 @@
|
||||
// Copyright 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.
|
||||
|
||||
class Module {
|
||||
constructor() {
|
||||
this.instructions_ = [];
|
||||
this.next_id_ = 1;
|
||||
|
||||
/**
|
||||
* Maps {string, hash} where the string is the type name and the hash is:
|
||||
* type- 'float' or 'int'
|
||||
* width- number of bits needed to store number
|
||||
* signed- the sign of the number
|
||||
*/
|
||||
this.types_ = {};
|
||||
|
||||
/**
|
||||
* Maps {string, number} where the string is the type name and the number is
|
||||
* the id value.
|
||||
*/
|
||||
this.assigned_ids_ = {};
|
||||
}
|
||||
|
||||
instructions() { return this.instructions_; }
|
||||
|
||||
instruction(val) { return this.instructions_[val]; }
|
||||
|
||||
addInstruction(inst) {
|
||||
this.instructions_.push(inst);
|
||||
|
||||
// Record type information
|
||||
if (inst.name() === "OpTypeInt" || inst.name() === "OpTypeFloat") {
|
||||
let is_int = inst.name() === "OpTypeInt";
|
||||
|
||||
this.types_[inst.operand(0).name()] = {
|
||||
type: is_int ? "int" : "float",
|
||||
width: inst.operand(1).value(),
|
||||
signed: is_int ? inst.operand(2).value() : 1
|
||||
};
|
||||
}
|
||||
|
||||
// Record operand result id's
|
||||
inst.operands().forEach((op) => {
|
||||
if (op.rawValue() !== undefined && op.type() === "result_id") {
|
||||
this.next_id_ = Math.max(this.next_id_, op.rawValue() + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getType(name) { return this.types_[name]; }
|
||||
|
||||
getId(name) {
|
||||
if (this.assigned_ids_[name] !== undefined) {
|
||||
return this.assigned_ids_[name];
|
||||
}
|
||||
|
||||
let next = this.next_id_;
|
||||
this.assigned_ids_[name] = next;
|
||||
|
||||
this.next_id_ += 1;
|
||||
return next;
|
||||
}
|
||||
|
||||
getIdBounds() { return this.next_id_; }
|
||||
}
|
||||
|
||||
class Instruction {
|
||||
constructor(name, opcode, operands) {
|
||||
this.name_ = name;
|
||||
this.opcode_ = opcode;
|
||||
this.operands_ = operands;
|
||||
}
|
||||
|
||||
name() { return this.name_; }
|
||||
|
||||
opcode() { return this.opcode_; }
|
||||
|
||||
operands() { return this.operands_; }
|
||||
|
||||
operand(val) { return this.operands_[val]; }
|
||||
}
|
||||
|
||||
class Operand {
|
||||
constructor(mod, name, type, value, params) {
|
||||
this.module_ = mod;
|
||||
this.name_ = name;
|
||||
this.type_ = type;
|
||||
this.value_ = value;
|
||||
this.params_ = params;
|
||||
}
|
||||
|
||||
name() { return this.name_; }
|
||||
|
||||
length() {
|
||||
// Get the value just to force it to be filled.
|
||||
this.value();
|
||||
|
||||
if (this.type_ === "string") {
|
||||
return Math.ceil((this.value_.length + 1) / 4);
|
||||
}
|
||||
|
||||
let size = 1;
|
||||
for (const param of this.params_) {
|
||||
size += param.length();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
type() { return this.type_; }
|
||||
|
||||
rawValue() { return this.value_; }
|
||||
|
||||
// This method should only be called on ResultId's after the full parse is
|
||||
// complete. This is because the AST will only have the maximum seen numeric
|
||||
// ResultId when the parse is done.
|
||||
value() {
|
||||
if (this.value_ === undefined) {
|
||||
this.value_ = this.module_.getId(this.name_);
|
||||
}
|
||||
return this.value_;
|
||||
}
|
||||
|
||||
params() { return this.params_; }
|
||||
}
|
||||
|
||||
export {
|
||||
Module,
|
||||
Instruction,
|
||||
Operand
|
||||
};
|
||||
363
3rdparty/spirv-tools/tools/sva/src/lexer.js
vendored
363
3rdparty/spirv-tools/tools/sva/src/lexer.js
vendored
@@ -1,363 +0,0 @@
|
||||
// Copyright 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.
|
||||
|
||||
import { Token, TokenType } from "./token.js";
|
||||
|
||||
export default class Lexer {
|
||||
/**
|
||||
* @param {String} input The input string to tokenize.
|
||||
*/
|
||||
constructor(input) {
|
||||
this.input_ = input;
|
||||
this.len_ = input.length;
|
||||
this.cur_pos_ = 0;
|
||||
this.cur_line_ = 1;
|
||||
|
||||
this.num_regex_ = /^[0-9]+$/;
|
||||
this.alpha_regex_ = /^[a-zA-Z_]+$/;
|
||||
this.op_regex_ = /^Op[A-Z][^\s]*$/;
|
||||
this.hex_regex_ = /^[0-9a-fA-F]$/;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the next token from the input stream.
|
||||
* @return {Token} the next token.
|
||||
*/
|
||||
next() {
|
||||
this.skipWhitespace();
|
||||
this.skipComments();
|
||||
|
||||
if (this.cur_pos_ >= this.len_)
|
||||
return new Token(TokenType.kEOF, this.cur_line_);
|
||||
|
||||
let n = this.tryHexInteger();
|
||||
if (n !== undefined)
|
||||
return n;
|
||||
|
||||
n = this.tryFloat();
|
||||
if (n !== undefined)
|
||||
return n;
|
||||
|
||||
n = this.tryInteger();
|
||||
if (n !== undefined)
|
||||
return n;
|
||||
|
||||
n = this.tryString();
|
||||
if (n !== undefined)
|
||||
return n;
|
||||
|
||||
n = this.tryOp();
|
||||
if (n !== undefined)
|
||||
return n;
|
||||
|
||||
n = this.tryPunctuation();
|
||||
if (n !== undefined)
|
||||
return n;
|
||||
|
||||
n = this.tryResultId();
|
||||
if (n !== undefined)
|
||||
return n;
|
||||
|
||||
n = this.tryIdent();
|
||||
if (n !== undefined)
|
||||
return n;
|
||||
|
||||
return new Token(TokenType.kError, this.cur_line_, "Failed to match token");
|
||||
}
|
||||
|
||||
is(str) {
|
||||
if (this.len_ <= this.cur_pos_ + (str.length - 1))
|
||||
return false;
|
||||
|
||||
for (let i = 0; i < str.length; ++i) {
|
||||
if (this.input_[this.cur_pos_ + i] !== str[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
isNum(ch) {
|
||||
return ch.match(this.num_regex_);
|
||||
}
|
||||
|
||||
isAlpha(ch) {
|
||||
return ch.match(this.alpha_regex_);
|
||||
}
|
||||
|
||||
isAlphaNum(ch) {
|
||||
return this.isNum(ch) || this.isAlpha(ch);
|
||||
}
|
||||
|
||||
isHex(char) {
|
||||
return char.match(this.hex_regex_);
|
||||
}
|
||||
|
||||
isCurWhitespace() {
|
||||
return this.is(" ") || this.is("\t") || this.is("\r") || this.is("\n");
|
||||
}
|
||||
|
||||
skipWhitespace() {
|
||||
for(;;) {
|
||||
let cur_pos = this.cur_pos_;
|
||||
while (this.cur_pos_ < this.len_ &&
|
||||
this.isCurWhitespace()) {
|
||||
if (this.is("\n"))
|
||||
this.cur_line_ += 1;
|
||||
|
||||
this.cur_pos_ += 1;
|
||||
}
|
||||
|
||||
this.skipComments();
|
||||
|
||||
// Cursor didn't move so no whitespace matched.
|
||||
if (cur_pos === this.cur_pos_)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
skipComments() {
|
||||
if (!this.is(";"))
|
||||
return;
|
||||
|
||||
while (this.cur_pos_ < this.len_ && !this.is("\n"))
|
||||
this.cur_pos_ += 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to parse the next part of the input as a float.
|
||||
* @return {Token|undefined} returns a Token if a float is matched,
|
||||
* undefined otherwise.
|
||||
*/
|
||||
tryFloat() {
|
||||
let start = this.cur_pos_;
|
||||
let end = start;
|
||||
|
||||
if (this.cur_pos_ >= this.len_)
|
||||
return undefined;
|
||||
if (this.input_[end] === "-")
|
||||
end += 1;
|
||||
|
||||
while (end < this.len_ && this.isNum(this.input_[end]))
|
||||
end += 1;
|
||||
|
||||
// Must have a "." in a float
|
||||
if (end >= this.len_ || this.input_[end] !== ".")
|
||||
return undefined;
|
||||
|
||||
end += 1;
|
||||
while (end < this.len_ && this.isNum(this.input_[end]))
|
||||
end += 1;
|
||||
|
||||
let substr = this.input_.substr(start, end - start);
|
||||
if (substr === "." || substr === "-.")
|
||||
return undefined;
|
||||
|
||||
this.cur_pos_ = end;
|
||||
|
||||
return new Token(TokenType.kFloatLiteral, this.cur_line_, parseFloat(substr));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to parse a hex encoded integer.
|
||||
* @return {Token|undefined} returns a Token if a Hex number is matched,
|
||||
* undefined otherwise.
|
||||
*/
|
||||
tryHexInteger() {
|
||||
let start = this.cur_pos_;
|
||||
let end = start;
|
||||
|
||||
if (this.cur_pos_ >= this.len_)
|
||||
return undefined;
|
||||
if (end + 2 >= this.len_ || this.input_[end] !== "0" ||
|
||||
this.input_[end + 1] !== "x") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
end += 2;
|
||||
|
||||
while (end < this.len_ && this.isHex(this.input_[end]))
|
||||
end += 1;
|
||||
|
||||
this.cur_pos_ = end;
|
||||
|
||||
let val = parseInt(this.input_.substr(start, end - start), 16);
|
||||
return new Token(TokenType.kIntegerLiteral, this.cur_line_, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to parse an encoded integer.
|
||||
* @return {Token|undefined} returns a Token if a number is matched,
|
||||
* undefined otherwise.
|
||||
*/
|
||||
tryInteger() {
|
||||
let start = this.cur_pos_;
|
||||
let end = start;
|
||||
|
||||
if (this.cur_pos_ >= this.len_)
|
||||
return undefined;
|
||||
if (this.input_[end] === "-")
|
||||
end += 1;
|
||||
|
||||
if (end >= this.len_ || !this.isNum(this.input_[end]))
|
||||
return undefined;
|
||||
|
||||
while (end < this.len_ && this.isNum(this.input_[end]))
|
||||
end += 1;
|
||||
|
||||
this.cur_pos_ = end;
|
||||
|
||||
let val = parseInt(this.input_.substr(start, end - start), 10);
|
||||
return new Token(TokenType.kIntegerLiteral, this.cur_line_, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to parse a result id.
|
||||
* @return {Token|undefined} returns a Token if a result id is matched,
|
||||
* undefined otherwise.
|
||||
*/
|
||||
tryResultId() {
|
||||
let start = this.cur_pos_;
|
||||
if (start >= this.len_)
|
||||
return undefined;
|
||||
if (!this.is("%"))
|
||||
return undefined;
|
||||
|
||||
start += 1;
|
||||
this.cur_pos_ += 1;
|
||||
while (this.cur_pos_ < this.len_ &&
|
||||
(this.isAlphaNum(this.input_[this.cur_pos_]) || this.is("_"))) {
|
||||
this.cur_pos_ += 1;
|
||||
}
|
||||
|
||||
let ident = this.input_.substr(start, this.cur_pos_ - start);
|
||||
let value = undefined;
|
||||
if (ident.match(this.num_regex_))
|
||||
value = parseInt(ident, 10);
|
||||
|
||||
return new Token(TokenType.kResultId, this.cur_line_, {
|
||||
name: ident,
|
||||
val: value
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to parse an identifier.
|
||||
* @return {Token|undefined} returns a Token if an identifier is matched,
|
||||
* undefined otherwise.
|
||||
*/
|
||||
tryIdent() {
|
||||
let start = this.cur_pos_;
|
||||
if (start >= this.len_)
|
||||
return undefined;
|
||||
|
||||
while (this.cur_pos_ < this.len_ &&
|
||||
(this.isAlphaNum(this.input_[this.cur_pos_]) || this.is("_"))) {
|
||||
this.cur_pos_ += 1;
|
||||
}
|
||||
|
||||
let ident = this.input_.substr(start, this.cur_pos_ - start);
|
||||
return new Token(TokenType.kIdentifier, this.cur_line_, ident);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to parse an Op command.
|
||||
* @return {Token|undefined} returns a Token if an Op command is matched,
|
||||
* undefined otherwise.
|
||||
*/
|
||||
tryOp() {
|
||||
let start = this.cur_pos_;
|
||||
if (this.cur_pos_ >= this.len_ || (this.cur_pos_ + 1 >= this.len_))
|
||||
return undefined;
|
||||
|
||||
if (this.input_[this.cur_pos_] !== "O" ||
|
||||
this.input_[this.cur_pos_ + 1] !== "p") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
while (this.cur_pos_ < this.len_ &&
|
||||
!this.isCurWhitespace()) {
|
||||
this.cur_pos_ += 1;
|
||||
}
|
||||
|
||||
return new Token(TokenType.kOp, this.cur_line_, {
|
||||
name: this.input_.substr(start, this.cur_pos_ - start)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to match punctuation strings against the input
|
||||
* @return {Token|undefined} Returns the Token for the punctuation or
|
||||
* undefined if no matches found.
|
||||
*/
|
||||
tryPunctuation() {
|
||||
let type = undefined;
|
||||
if (this.is("="))
|
||||
type = TokenType.kEqual;
|
||||
else if (this.is("|"))
|
||||
type = TokenType.kPipe;
|
||||
|
||||
if (type === undefined)
|
||||
return undefined;
|
||||
|
||||
this.cur_pos_ += type.length;
|
||||
return new Token(type, this.cur_line_, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to match strings against the input
|
||||
* @return {Token|undefined} Returns the Token for the string or undefined
|
||||
* if no match found.
|
||||
*/
|
||||
tryString() {
|
||||
let start = this.cur_pos_;
|
||||
|
||||
// Must have at least 2 chars for a string.
|
||||
if (this.cur_pos_ >= this.len_ || (this.cur_pos_ + 1 >= this.len_))
|
||||
return undefined;
|
||||
if (!this.is("\""))
|
||||
return undefined;
|
||||
|
||||
this.cur_pos_ += 1;
|
||||
let str = "";
|
||||
while (this.cur_pos_ <= this.len_) {
|
||||
if (this.is("\""))
|
||||
break;
|
||||
|
||||
if (this.is("\\")) {
|
||||
this.cur_pos_ += 1;
|
||||
if (this.cur_pos_ >= this.len_)
|
||||
return undefined;
|
||||
|
||||
if (this.is("\\")) {
|
||||
str += "\\";
|
||||
} else if (this.is("\"")) {
|
||||
str += '"';
|
||||
} else {
|
||||
str += this.input_[this.cur_pos_];
|
||||
}
|
||||
} else {
|
||||
str += this.input_[this.cur_pos_];
|
||||
}
|
||||
this.cur_pos_ += 1;
|
||||
}
|
||||
|
||||
if (this.cur_pos_ >= this.len_)
|
||||
return undefined;
|
||||
|
||||
this.cur_pos_ += 1;
|
||||
|
||||
return new Token(TokenType.kStringLiteral, this.cur_line_, str);
|
||||
}
|
||||
}
|
||||
191
3rdparty/spirv-tools/tools/sva/src/lexer_test.js
vendored
191
3rdparty/spirv-tools/tools/sva/src/lexer_test.js
vendored
@@ -1,191 +0,0 @@
|
||||
// Copyright 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.
|
||||
|
||||
import { assert } from "chai";
|
||||
import Lexer from "./lexer";
|
||||
import { TokenType } from "./token";
|
||||
|
||||
describe("lexer", () => {
|
||||
describe("skipped content", () => {
|
||||
it("skips whitespace", () => {
|
||||
let input = " \t\r\n\t \tOpKill\t\n\t \r ";
|
||||
let l = new Lexer(input);
|
||||
|
||||
let t = l.next();
|
||||
assert.equal(t.type, TokenType.kOp);
|
||||
assert.equal(t.line, 2);
|
||||
assert.equal(t.data.name, "OpKill");
|
||||
|
||||
t = l.next();
|
||||
assert.equal(t.type, TokenType.kEOF);
|
||||
assert.equal(t.line, 3);
|
||||
});
|
||||
|
||||
it("skips ; comments", () => {
|
||||
let input = `; start with comment
|
||||
OpKill ; end of line comment
|
||||
; another comment
|
||||
%1`;
|
||||
|
||||
let l = new Lexer(input);
|
||||
let t = l.next();
|
||||
assert.equal(t.type, TokenType.kOp);
|
||||
assert.equal(t.data.name, "OpKill");
|
||||
assert.equal(t.line, 2);
|
||||
|
||||
t = l.next();
|
||||
assert.equal(t.type, TokenType.kResultId);
|
||||
assert.equal(t.data.name, "1");
|
||||
assert.equal(t.data.val, 1);
|
||||
assert.equal(t.line, 4);
|
||||
});
|
||||
});
|
||||
|
||||
describe("numerics", () => {
|
||||
it("parses floats", () => {
|
||||
let input = ["0.0", "0.", ".0", "5.7", "5.", ".7", "-0.0", "-.0",
|
||||
"-0.", "-5.7", "-5.", "-.7"];
|
||||
|
||||
let results = [0.0, 0.0, 0.0, 5.7, 5.0, 0.7, 0.0, 0.0, 0.0, -5.7, -5.0,
|
||||
-0.7];
|
||||
input.forEach((val, idx) => {
|
||||
let l = new Lexer(val);
|
||||
let t = l.next();
|
||||
|
||||
assert.equal(t.type, TokenType.kFloatLiteral,
|
||||
`expected ${val} to be a float got ${t.type}`);
|
||||
assert.equal(t.data, results[idx],
|
||||
`expected ${results[idx]} === ${t.data}`);
|
||||
|
||||
t = l.next();
|
||||
assert.equal(t.type, TokenType.kEOF);
|
||||
assert.equal(t.data, undefined);
|
||||
});
|
||||
});
|
||||
|
||||
it("handles invalid floats", () => {
|
||||
let input = [".", "-."];
|
||||
input.forEach((val) => {
|
||||
let l = new Lexer(val);
|
||||
let t = l.next();
|
||||
|
||||
assert.notEqual(t.type, TokenType.kFloatLiteral,
|
||||
`expect ${val} to not match type float`);
|
||||
});
|
||||
});
|
||||
|
||||
it("parses integers", () => {
|
||||
let input = ["0", "-0", "123", "-123", "2147483647", "-2147483648",
|
||||
"4294967295", "0x00", "0x24"];
|
||||
let results = [0, 0, 123, -123,2147483647, -2147483648, 4294967295,
|
||||
0x0, 0x24];
|
||||
|
||||
input.forEach((val, idx) => {
|
||||
let l = new Lexer(val);
|
||||
let t = l.next();
|
||||
|
||||
assert.equal(t.type, TokenType.kIntegerLiteral,
|
||||
`expected ${val} to be an integer got ${t.type}`);
|
||||
assert.equal(t.data, results[idx],
|
||||
`expected ${results[idx]} === ${t.data}`);
|
||||
|
||||
t = l.next();
|
||||
assert.equal(t.type, TokenType.kEOF);
|
||||
assert.equal(t.data, undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("matches result_ids", () => {
|
||||
let input = `%123
|
||||
%001
|
||||
%main
|
||||
%_a_b_c`;
|
||||
|
||||
let result = [
|
||||
{name: "123", val: 123},
|
||||
{name: "001", val: 1},
|
||||
{name: "main", val: undefined},
|
||||
{name: "_a_b_c", val: undefined}
|
||||
];
|
||||
|
||||
let l = new Lexer(input);
|
||||
for (let i = 0; i < result.length; ++i) {
|
||||
let t = l.next();
|
||||
assert.equal(t.type, TokenType.kResultId);
|
||||
assert.equal(t.data.name, result[i].name);
|
||||
assert.equal(t.data.val, result[i].val);
|
||||
}
|
||||
});
|
||||
|
||||
it("matches punctuation", () => {
|
||||
let input = "=";
|
||||
let results = [TokenType.kEqual];
|
||||
|
||||
let l = new Lexer(input);
|
||||
for (let i = 0; i < results.length; ++i) {
|
||||
let t = l.next();
|
||||
assert.equal(t.type, results[i]);
|
||||
assert.equal(t.line, i + 1);
|
||||
}
|
||||
|
||||
let t = l.next();
|
||||
assert.equal(t.type, TokenType.kEOF);
|
||||
});
|
||||
|
||||
describe("strings", () => {
|
||||
it("matches strings", () => {
|
||||
let input = "\"GLSL.std.450\"";
|
||||
|
||||
let l = new Lexer(input);
|
||||
let t = l.next();
|
||||
assert.equal(t.type, TokenType.kStringLiteral);
|
||||
assert.equal(t.data, "GLSL.std.450");
|
||||
});
|
||||
|
||||
it("handles unfinished strings", () => {
|
||||
let input = "\"GLSL.std.450";
|
||||
|
||||
let l = new Lexer(input);
|
||||
let t = l.next();
|
||||
assert.equal(t.type, TokenType.kError);
|
||||
});
|
||||
|
||||
it("handles escapes", () => {
|
||||
let input = `"embedded\\"quote"
|
||||
"embedded\\\\slash"
|
||||
"embedded\\nchar"`;
|
||||
let results = [`embedded\"quote`, `embedded\\slash`, `embeddednchar`];
|
||||
|
||||
let l = new Lexer(input);
|
||||
for (let i = 0; i < results.length; ++i) {
|
||||
let t = l.next();
|
||||
assert.equal(t.type, TokenType.kStringLiteral, results[i]);
|
||||
assert.equal(t.data, results[i]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("matches keywords", () => {
|
||||
let input = "GLSL Function";
|
||||
let results = ["GLSL", "Function"];
|
||||
|
||||
let l = new Lexer(input);
|
||||
for (let i = 0; i < results.length; ++i) {
|
||||
let t = l.next();
|
||||
assert.equal(t.type, TokenType.kIdentifier, results[i]);
|
||||
assert.equal(t.data, results[i]);
|
||||
}
|
||||
});
|
||||
});
|
||||
277
3rdparty/spirv-tools/tools/sva/src/parser.js
vendored
277
3rdparty/spirv-tools/tools/sva/src/parser.js
vendored
@@ -1,277 +0,0 @@
|
||||
// Copyright 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.
|
||||
|
||||
import { TokenType } from "./token.js";
|
||||
import * as AST from "./ast.js";
|
||||
|
||||
export default class Parser {
|
||||
/**
|
||||
* @param {Hash} The SPIR-V grammar
|
||||
* @param {Lexer} The lexer
|
||||
* @return {AST} Attempts to build an AST from the tokens returned by the
|
||||
* given lexer
|
||||
*/
|
||||
constructor(grammar, lexer) {
|
||||
this.grammar_ = grammar;
|
||||
this.lexer_ = lexer;
|
||||
|
||||
this.peek_ = [];
|
||||
this.error_ = "";
|
||||
}
|
||||
|
||||
get error() { return this.error_; }
|
||||
|
||||
next() {
|
||||
return this.peek_.shift() || this.lexer_.next();
|
||||
}
|
||||
|
||||
peek(idx) {
|
||||
while (this.peek_.length <= idx) {
|
||||
this.peek_.push(this.lexer_.next());
|
||||
}
|
||||
return this.peek_[idx];
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the parser.
|
||||
*
|
||||
* @return {AST|undefined} returns a parsed AST on success or undefined
|
||||
* on error. The error message can be retrieved by
|
||||
* calling error().
|
||||
*/
|
||||
parse() {
|
||||
let ast = new AST.Module();
|
||||
for(;;) {
|
||||
let token = this.next();
|
||||
if (token === TokenType.kError) {
|
||||
this.error_ = token.line() + ": " + token.data();
|
||||
return undefined;
|
||||
}
|
||||
if (token.type === TokenType.kEOF)
|
||||
break;
|
||||
|
||||
let result_id = undefined;
|
||||
if (token.type === TokenType.kResultId) {
|
||||
result_id = token;
|
||||
|
||||
token = this.next();
|
||||
if (token.type !== TokenType.kEqual) {
|
||||
this.error_ = token.line + ": expected = after result id";
|
||||
return undefined;
|
||||
}
|
||||
|
||||
token = this.next();
|
||||
}
|
||||
|
||||
if (token.type !== TokenType.kOp) {
|
||||
this.error_ = token.line + ": expected Op got " + token.type;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let name = token.data.name;
|
||||
let data = this.getInstructionData(name);
|
||||
let operands = [];
|
||||
let result_type = undefined;
|
||||
|
||||
for (let operand of data.operands) {
|
||||
if (operand.kind === "IdResult") {
|
||||
if (result_id === undefined) {
|
||||
this.error_ = token.line + ": expected result id";
|
||||
return undefined;
|
||||
}
|
||||
let o = new AST.Operand(ast, result_id.data.name, "result_id",
|
||||
result_id.data.val, []);
|
||||
if (o === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
operands.push(o);
|
||||
} else {
|
||||
if (operand.quantifier === "?") {
|
||||
if (this.nextIsNewInstr()) {
|
||||
break;
|
||||
}
|
||||
} else if (operand.quantifier === "*") {
|
||||
while (!this.nextIsNewInstr()) {
|
||||
let o = this.extractOperand(ast, result_type, operand);
|
||||
if (o === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
operands.push(o);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
let o = this.extractOperand(ast, result_type, operand);
|
||||
if (o === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Store the result type away so we can use it for context dependent
|
||||
// numbers if needed.
|
||||
if (operand.kind === "IdResultType") {
|
||||
result_type = ast.getType(o.name());
|
||||
}
|
||||
|
||||
operands.push(o);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify only GLSL extended instructions are used
|
||||
if (name === "OpExtInstImport" && operands[1].value() !== "GLSL.std.450") {
|
||||
this.error_ = token.line + ": Only GLSL.std.450 external instructions supported";
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let inst = new AST.Instruction(name, data.opcode, operands);
|
||||
|
||||
ast.addInstruction(inst);
|
||||
}
|
||||
return ast;
|
||||
}
|
||||
|
||||
getInstructionData(name) {
|
||||
return this.grammar_["instructions"][name];
|
||||
}
|
||||
|
||||
nextIsNewInstr() {
|
||||
let n0 = this.peek(0);
|
||||
if (n0.type === TokenType.kOp || n0.type === TokenType.kEOF) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let n1 = this.peek(1);
|
||||
if (n1.type === TokenType.kEOF) {
|
||||
return false;
|
||||
}
|
||||
if (n0.type === TokenType.kResultId && n1.type === TokenType.kEqual)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
extractOperand(ast, result_type, data) {
|
||||
let t = this.next();
|
||||
|
||||
let name = undefined;
|
||||
let kind = undefined;
|
||||
let value = undefined;
|
||||
let params = [];
|
||||
|
||||
// TODO(dsinclair): There are a bunch of missing types here. See
|
||||
// https://github.com/KhronosGroup/SPIRV-Tools/blob/master/source/text.cpp#L210
|
||||
if (data.kind === "IdResult" || data.kind === "IdRef"
|
||||
|| data.kind === "IdResultType") {
|
||||
if (t.type !== TokenType.kResultId) {
|
||||
this.error_ = t.line + ": expected result id";
|
||||
return undefined;
|
||||
}
|
||||
|
||||
name = t.data.name;
|
||||
kind = "result_id";
|
||||
value = t.data.val;
|
||||
} else if (data.kind === "LiteralString") {
|
||||
if (t.type !== TokenType.kStringLiteral) {
|
||||
this.error_ = t.line + ": expected string not found";
|
||||
return undefined;
|
||||
}
|
||||
|
||||
name = t.data;
|
||||
kind = "string";
|
||||
value = t.data;
|
||||
} else if (data.kind === "LiteralInteger") {
|
||||
if (t.type !== TokenType.kIntegerLiteral) {
|
||||
this.error_ = t.line + ": expected integer not found";
|
||||
return undefined;
|
||||
}
|
||||
|
||||
name = "" + t.data;
|
||||
kind = t.type;
|
||||
value = t.data;
|
||||
} else if (data.kind === "LiteralContextDependentNumber") {
|
||||
if (result_type === undefined) {
|
||||
this.error_ = t.line +
|
||||
": missing result type for context dependent number";
|
||||
return undefined;
|
||||
}
|
||||
if (t.type !== TokenType.kIntegerLiteral
|
||||
&& t.type !== TokenType.kFloatLiteral) {
|
||||
this.error_ = t.line + ": expected number not found";
|
||||
return undefined;
|
||||
}
|
||||
|
||||
name = "" + t.data;
|
||||
kind = result_type.type;
|
||||
value = t.data;
|
||||
|
||||
} else if (data.kind === "LiteralExtInstInteger") {
|
||||
if (t.type !== TokenType.kIdentifier) {
|
||||
this.error_ = t.line + ": expected instruction identifier";
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (this.grammar_.ext[t.data] === undefined) {
|
||||
this.error_ = t.line + `: unable to find extended instruction (${t.data})`;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
name = t.data;
|
||||
kind = "integer";
|
||||
value = this.grammar_.ext[t.data];
|
||||
|
||||
} else {
|
||||
let d = this.grammar_.operand_kinds[data.kind];
|
||||
if (d === undefined) {
|
||||
this.error_ = t.line + ": expected " + data.kind + " not found";
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let val = d.values[t.data]["value"];
|
||||
let names = [t.data];
|
||||
if (d.type === "BitEnum") {
|
||||
for(;;) {
|
||||
let tmp = this.peek(0);
|
||||
if (tmp.type !== TokenType.kPipe) {
|
||||
break;
|
||||
}
|
||||
|
||||
this.next(); // skip pipe
|
||||
tmp = this.next();
|
||||
|
||||
if (tmp.type !== TokenType.kIdentifier) {
|
||||
this.error_ = tmp.line() + ": expected identifier";
|
||||
return undefined;
|
||||
}
|
||||
|
||||
val |= d.values[tmp.data]["value"];
|
||||
names.push(tmp.data);
|
||||
}
|
||||
}
|
||||
|
||||
name = names.join("|");
|
||||
kind = d.type;
|
||||
value = val;
|
||||
|
||||
for (const op_name of names) {
|
||||
if (d.values[op_name]['params'] === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const param of d.values[op_name]["params"]) {
|
||||
params.push(this.extractOperand(ast, result_type, { kind: param }));
|
||||
}
|
||||
}
|
||||
}
|
||||
return new AST.Operand(ast, name, kind, value, params);
|
||||
}
|
||||
}
|
||||
464
3rdparty/spirv-tools/tools/sva/src/parser_test.js
vendored
464
3rdparty/spirv-tools/tools/sva/src/parser_test.js
vendored
@@ -1,464 +0,0 @@
|
||||
// Copyright 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.
|
||||
|
||||
import { assert } from "chai";
|
||||
import Lexer from "./lexer";
|
||||
import Parser from "./parser";
|
||||
import grammar from "./spirv.data.js";
|
||||
|
||||
describe("parser", () => {
|
||||
it("parses an opcode", () => {
|
||||
let input = "OpKill";
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast);
|
||||
assert.lengthOf(ast.instructions(), 1);
|
||||
|
||||
let inst = ast.instruction(0);
|
||||
assert.equal(inst.name(), "OpKill");
|
||||
assert.equal(inst.opcode(), 252);
|
||||
assert.lengthOf(inst.operands, 0);
|
||||
});
|
||||
|
||||
it("parses an opcode with an identifier", () => {
|
||||
let input = "OpCapability Shader";
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast, p.error);
|
||||
assert.lengthOf(ast.instructions(), 1);
|
||||
|
||||
let inst = ast.instruction(0);
|
||||
assert.equal(inst.name(), "OpCapability");
|
||||
assert.equal(inst.opcode(), 17);
|
||||
assert.lengthOf(inst.operands(), 1);
|
||||
|
||||
let op = inst.operand(0);
|
||||
assert.equal(op.name(), "Shader");
|
||||
assert.equal(op.type(), "ValueEnum");
|
||||
assert.equal(op.value(), 1);
|
||||
});
|
||||
|
||||
it("parses an opcode with a result", () => {
|
||||
let input = "%void = OpTypeVoid";
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast);
|
||||
assert.lengthOf(ast.instructions(), 1);
|
||||
|
||||
let inst = ast.instruction(0);
|
||||
assert.equal(inst.name(), "OpTypeVoid");
|
||||
assert.equal(inst.opcode(), 19);
|
||||
assert.lengthOf(inst.operands(), 1);
|
||||
|
||||
let op = inst.operand(0);
|
||||
assert.equal(op.name(), "void");
|
||||
assert.equal(op.value(), 1);
|
||||
});
|
||||
|
||||
it("sets module bounds based on numeric result", () => {
|
||||
let input = "%3 = OpTypeVoid";
|
||||
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast);
|
||||
assert.equal(ast.getId("next"), 4);
|
||||
});
|
||||
|
||||
it("returns the same value for a named result_id", () => {
|
||||
let input = "%3 = OpTypeFunction %int %int";
|
||||
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast);
|
||||
assert.lengthOf(ast.instructions(), 1);
|
||||
|
||||
let inst = ast.instruction(0);
|
||||
let op1 = inst.operand(1);
|
||||
assert.equal(op1.name(), "int");
|
||||
assert.equal(op1.value(), 4);
|
||||
|
||||
let op2 = inst.operand(2);
|
||||
assert.equal(op2.name(), "int");
|
||||
assert.equal(op2.value(), 4);
|
||||
});
|
||||
|
||||
it("parses an opcode with a string", () => {
|
||||
let input = "OpEntryPoint Fragment %main \"main\"";
|
||||
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast);
|
||||
assert.lengthOf(ast.instructions(), 1);
|
||||
|
||||
let inst = ast.instruction(0);
|
||||
let op = inst.operand(2);
|
||||
assert.equal(op.name(), "main");
|
||||
assert.equal(op.value(), "main");
|
||||
});
|
||||
|
||||
describe("numerics", () => {
|
||||
describe("integers", () => {
|
||||
it("parses an opcode with an integer", () => {
|
||||
let input = "OpSource GLSL 440";
|
||||
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast);
|
||||
assert.lengthOf(ast.instructions(), 1);
|
||||
|
||||
let inst = ast.instruction(0);
|
||||
let op0 = inst.operand(0);
|
||||
assert.equal(op0.name(), "GLSL");
|
||||
assert.equal(op0.type(), "ValueEnum");
|
||||
assert.equal(op0.value(), 2);
|
||||
|
||||
let op1 = inst.operand(1);
|
||||
assert.equal(op1.name(), "440");
|
||||
assert.equal(op1.value(), 440);
|
||||
});
|
||||
|
||||
it("parses an opcode with a hex integer", () => {
|
||||
let input = "OpSource GLSL 0x440";
|
||||
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast);
|
||||
assert.lengthOf(ast.instructions(), 1);
|
||||
|
||||
let inst = ast.instruction(0);
|
||||
let op0 = inst.operand(0);
|
||||
assert.equal(op0.name(), "GLSL");
|
||||
assert.equal(op0.type(), "ValueEnum");
|
||||
assert.equal(op0.value(), 2);
|
||||
|
||||
let op1 = inst.operand(1);
|
||||
assert.equal(op1.name(), "1088");
|
||||
assert.equal(op1.value(), 0x440);
|
||||
});
|
||||
|
||||
it.skip("parses immediate integers", () => {
|
||||
// TODO(dsinclair): Support or skip?
|
||||
});
|
||||
});
|
||||
|
||||
describe("floats", () => {
|
||||
it("parses floats", () => {
|
||||
let input = `%float = OpTypeFloat 32
|
||||
%float1 = OpConstant %float 0.400000006`;
|
||||
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast, p.error);
|
||||
assert.lengthOf(ast.instructions(), 2);
|
||||
|
||||
let inst = ast.instruction(1);
|
||||
let op2 = inst.operand(2);
|
||||
assert.equal(op2.value(), 0.400000006);
|
||||
});
|
||||
|
||||
// TODO(dsinclair): Make hex encoded floats parse ...
|
||||
it.skip("parses hex floats", () => {
|
||||
let input = `%float = OpTypeFloat 32
|
||||
%nfloat = OpConstant %float -0.4p+2
|
||||
%pfloat = OpConstant %float 0.4p-2
|
||||
%inf = OpConstant %float32 0x1p+128
|
||||
%neginf = OpConstant %float32 -0x1p+128
|
||||
%aNaN = OpConstant %float32 0x1.8p+128
|
||||
%moreNaN = OpConstant %float32 -0x1.0002p+128`;
|
||||
|
||||
let results = [-40.0, .004, 0x00000, 0x00000, 0x7fc00000, 0xff800100];
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast, p.error);
|
||||
assert.lengthOf(ast.instructions(), 7);
|
||||
|
||||
for (const idx in results) {
|
||||
let inst = ast.instruction(idx);
|
||||
let op2 = inst.operand(2);
|
||||
assert.equal(op2.value(), results[idx]);
|
||||
}
|
||||
});
|
||||
|
||||
it("parses a float that looks like an int", () => {
|
||||
let input = `%float = OpTypeFloat 32
|
||||
%float1 = OpConstant %float 1`;
|
||||
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast, p.error);
|
||||
assert.lengthOf(ast.instructions(), 2);
|
||||
|
||||
let inst = ast.instruction(1);
|
||||
let op2 = inst.operand(2);
|
||||
assert.equal(op2.value(), 1);
|
||||
assert.equal(op2.type(), "float");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("enums", () => {
|
||||
it("parses enum values", () => {
|
||||
let input = `%1 = OpTypeFloat 32
|
||||
%30 = OpImageSampleExplicitLod %1 %20 %18 Grad|ConstOffset %22 %24 %29`;
|
||||
|
||||
let vals = [{val: 1, name: "1"},
|
||||
{val: 30, name: "30"},
|
||||
{val: 20, name: "20"},
|
||||
{val: 18, name: "18"},
|
||||
{val: 12, name: "Grad|ConstOffset"}];
|
||||
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast, p.error);
|
||||
assert.lengthOf(ast.instructions(), 2);
|
||||
|
||||
let inst = ast.instruction(1);
|
||||
for (let idx in vals) {
|
||||
let op = inst.operand(idx);
|
||||
assert.equal(op.name(), vals[idx].name);
|
||||
assert.equal(op.value(), vals[idx].val);
|
||||
}
|
||||
|
||||
// BitEnum
|
||||
let params = inst.operand(4).params();
|
||||
assert.lengthOf(params, 3);
|
||||
assert.equal(params[0].name(), "22");
|
||||
assert.equal(params[0].value(), 22);
|
||||
assert.equal(params[1].name(), "24");
|
||||
assert.equal(params[1].value(), 24);
|
||||
assert.equal(params[2].name(), "29");
|
||||
assert.equal(params[2].value(), 29);
|
||||
});
|
||||
|
||||
it("parses enumerants with parameters", () => {
|
||||
let input ="OpExecutionMode %main LocalSize 2 3 4";
|
||||
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast, p.error);
|
||||
assert.lengthOf(ast.instructions(), 1);
|
||||
|
||||
let inst = ast.instruction(0);
|
||||
assert.equal(inst.name(), "OpExecutionMode");
|
||||
assert.lengthOf(inst.operands(), 2);
|
||||
assert.equal(inst.operand(0).name(), "main");
|
||||
assert.equal(inst.operand(1).name(), "LocalSize");
|
||||
|
||||
let params = inst.operand(1).params();
|
||||
assert.lengthOf(params, 3);
|
||||
assert.equal(params[0].name(), "2");
|
||||
assert.equal(params[1].name(), "3");
|
||||
assert.equal(params[2].name(), "4");
|
||||
});
|
||||
});
|
||||
|
||||
it("parses result into second operand if needed", () => {
|
||||
let input = `%int = OpTypeInt 32 1
|
||||
%int_3 = OpConstant %int 3`;
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast);
|
||||
assert.lengthOf(ast.instructions(), 2);
|
||||
|
||||
let inst = ast.instruction(1);
|
||||
assert.equal(inst.name(), "OpConstant");
|
||||
assert.equal(inst.opcode(), 43);
|
||||
assert.lengthOf(inst.operands(), 3);
|
||||
|
||||
let op0 = inst.operand(0);
|
||||
assert.equal(op0.name(), "int");
|
||||
assert.equal(op0.value(), 1);
|
||||
|
||||
let op1 = inst.operand(1);
|
||||
assert.equal(op1.name(), "int_3");
|
||||
assert.equal(op1.value(), 2);
|
||||
|
||||
let op2 = inst.operand(2);
|
||||
assert.equal(op2.name(), "3");
|
||||
assert.equal(op2.value(), 3);
|
||||
});
|
||||
|
||||
describe("quantifiers", () => {
|
||||
describe("?", () => {
|
||||
it("skips if missing", () => {
|
||||
let input = `OpImageWrite %1 %2 %3
|
||||
OpKill`;
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast);
|
||||
assert.lengthOf(ast.instructions(), 2);
|
||||
|
||||
let inst = ast.instruction(0);
|
||||
assert.equal(inst.name(), "OpImageWrite");
|
||||
assert.lengthOf(inst.operands(), 3);
|
||||
});
|
||||
|
||||
it("skips if missing at EOF", () => {
|
||||
let input = "OpImageWrite %1 %2 %3";
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast);
|
||||
assert.lengthOf(ast.instructions(), 1);
|
||||
|
||||
let inst = ast.instruction(0);
|
||||
assert.equal(inst.name(), "OpImageWrite");
|
||||
assert.lengthOf(inst.operands(), 3);
|
||||
});
|
||||
|
||||
it("extracts if available", () => {
|
||||
let input = `OpImageWrite %1 %2 %3 ConstOffset %2
|
||||
OpKill`;
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast);
|
||||
assert.lengthOf(ast.instructions(), 2);
|
||||
|
||||
let inst = ast.instruction(0);
|
||||
assert.equal(inst.name(), "OpImageWrite");
|
||||
assert.lengthOf(inst.operands(), 4);
|
||||
assert.equal(inst.operand(3).name(), "ConstOffset");
|
||||
});
|
||||
});
|
||||
|
||||
describe("*", () => {
|
||||
it("skips if missing", () => {
|
||||
let input = `OpEntryPoint Fragment %main "main"
|
||||
OpKill`;
|
||||
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast);
|
||||
assert.lengthOf(ast.instructions(), 2);
|
||||
|
||||
let inst = ast.instruction(0);
|
||||
assert.equal(inst.name(), "OpEntryPoint");
|
||||
assert.lengthOf(inst.operands(), 3);
|
||||
assert.equal(inst.operand(2).name(), "main");
|
||||
});
|
||||
|
||||
it("extracts one if available", () => {
|
||||
let input = `OpEntryPoint Fragment %main "main" %2
|
||||
OpKill`;
|
||||
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast);
|
||||
assert.lengthOf(ast.instructions(), 2);
|
||||
|
||||
let inst = ast.instruction(0);
|
||||
assert.equal(inst.name(), "OpEntryPoint");
|
||||
assert.lengthOf(inst.operands(), 4);
|
||||
assert.equal(inst.operand(3).name(), "2");
|
||||
});
|
||||
|
||||
it("extracts multiple if available", () => {
|
||||
let input = `OpEntryPoint Fragment %main "main" %2 %3 %4 %5
|
||||
OpKill`;
|
||||
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast);
|
||||
assert.lengthOf(ast.instructions(), 2);
|
||||
|
||||
let inst = ast.instruction(0);
|
||||
assert.equal(inst.name(), "OpEntryPoint");
|
||||
assert.lengthOf(inst.operands(), 7);
|
||||
assert.equal(inst.operand(3).name(), "2");
|
||||
assert.equal(inst.operand(4).name(), "3");
|
||||
assert.equal(inst.operand(5).name(), "4");
|
||||
assert.equal(inst.operand(6).name(), "5");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("extended instructions", () => {
|
||||
it("errors on non-glsl extensions", () => {
|
||||
let input = "%1 = OpExtInstImport \"OpenCL.std.100\"";
|
||||
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
assert.isUndefined(p.parse());
|
||||
});
|
||||
|
||||
it("handles extended instructions", () => {
|
||||
let input = `%1 = OpExtInstImport "GLSL.std.450"
|
||||
%44 = OpExtInst %7 %1 Sqrt %43`;
|
||||
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
assert.exists(ast, p.error);
|
||||
assert.lengthOf(ast.instructions(), 2);
|
||||
|
||||
let inst = ast.instruction(1);
|
||||
assert.lengthOf(inst.operands(), 5);
|
||||
assert.equal(inst.operand(3).value(), 31);
|
||||
assert.equal(inst.operand(3).name(), "Sqrt");
|
||||
assert.equal(inst.operand(4).value(), 43);
|
||||
assert.equal(inst.operand(4).name(), "43");
|
||||
});
|
||||
});
|
||||
|
||||
it.skip("handles spec constant ops", () => {
|
||||
// let input = "%sum = OpSpecConstantOp %i32 IAdd %a %b";
|
||||
});
|
||||
|
||||
it.skip("handles OpCopyMemory", () => {
|
||||
// let input = "OpCopyMemory %1 %2 " +
|
||||
// "Volatile|Nontemporal|MakePointerVisible %3 " +
|
||||
// "Aligned|MakePointerAvailable|NonPrivatePointer 16 %4";
|
||||
});
|
||||
});
|
||||
4567
3rdparty/spirv-tools/tools/sva/src/spirv.data.js
vendored
4567
3rdparty/spirv-tools/tools/sva/src/spirv.data.js
vendored
File diff suppressed because it is too large
Load Diff
40
3rdparty/spirv-tools/tools/sva/src/sva.js
vendored
40
3rdparty/spirv-tools/tools/sva/src/sva.js
vendored
@@ -1,40 +0,0 @@
|
||||
// Copyright 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.
|
||||
|
||||
import Parser from "./parser.js";
|
||||
import Lexer from "./lexer.js";
|
||||
import Assembler from "./assembler.js";
|
||||
|
||||
import grammar from "./spirv.data.js";
|
||||
|
||||
export default class SVA {
|
||||
/**
|
||||
* Attempts to convert |input| SPIR-V assembly into SPIR-V binary.
|
||||
*
|
||||
* @param {String} the input string containing the assembly
|
||||
* @return {Uint32Array|string} returns a Uint32Array containing the binary
|
||||
* SPIR-V or a string on error.
|
||||
*/
|
||||
static assemble(input) {
|
||||
let l = new Lexer(input);
|
||||
let p = new Parser(grammar, l);
|
||||
|
||||
let ast = p.parse();
|
||||
if (ast === undefined)
|
||||
return p.error;
|
||||
|
||||
let a = new Assembler(ast);
|
||||
return a.assemble();
|
||||
}
|
||||
}
|
||||
55
3rdparty/spirv-tools/tools/sva/src/token.js
vendored
55
3rdparty/spirv-tools/tools/sva/src/token.js
vendored
@@ -1,55 +0,0 @@
|
||||
// Copyright 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.
|
||||
|
||||
const TokenType = {
|
||||
kEOF: "end of file",
|
||||
kError: "error",
|
||||
|
||||
kIdentifier: "identifier",
|
||||
|
||||
kIntegerLiteral: "integer_literal",
|
||||
kFloatLiteral: "float_literal",
|
||||
kStringLiteral: "string_literal",
|
||||
kResultId: "result_id",
|
||||
|
||||
kOp: "Op",
|
||||
kEqual: "=",
|
||||
kPipe: "|",
|
||||
};
|
||||
|
||||
class Token {
|
||||
/**
|
||||
* @param {TokenType} type The type of token
|
||||
* @param {Integer} line The line number this token was on
|
||||
* @param {Any} data Data attached to the token
|
||||
* @param {Integer} bits If the type is a float or integer the bit width
|
||||
*/
|
||||
constructor(type, line, data) {
|
||||
this.type_ = type;
|
||||
this.line_ = line;
|
||||
this.data_ = data;
|
||||
this.bits_ = 0;
|
||||
}
|
||||
|
||||
get type() { return this.type_; }
|
||||
get line() { return this.line_; }
|
||||
|
||||
get data() { return this.data_; }
|
||||
set data(val) { this.data_ = val; }
|
||||
|
||||
get bits() { return this.bits_; }
|
||||
set bits(val) { this.bits_ = val; }
|
||||
}
|
||||
|
||||
export {Token, TokenType};
|
||||
@@ -1,18 +0,0 @@
|
||||
; SPIR-V
|
||||
; Version: 1.0
|
||||
; Generator: Khronos Glslang Reference Front End; 7
|
||||
; Bound: 6
|
||||
; Schema: 0
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource GLSL 440
|
||||
OpName %main "main"
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
23
3rdparty/spirv-tools/tools/sva/tests/index.html
vendored
23
3rdparty/spirv-tools/tools/sva/tests/index.html
vendored
@@ -1,23 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
</head>
|
||||
<body>
|
||||
<pre id='code'><code></code></pre>
|
||||
|
||||
<script type="module">
|
||||
let c = document.getElementById('code');
|
||||
|
||||
import SVA from "/build/sva.js";
|
||||
|
||||
let assembly = SVA.assemble("OpCapability Shader");
|
||||
if (typeof assembly === "string") {
|
||||
c.innerText = assembly;
|
||||
} else {
|
||||
c.innerText = Array.from(assembly)
|
||||
.map(b => b.toString(16).padStart(8, "0")).join(" ");
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,30 +0,0 @@
|
||||
; SPIR-V
|
||||
; Version: 1.0
|
||||
; Generator: Khronos Glslang Reference Front End; 7
|
||||
; Bound: 14
|
||||
; Schema: 0
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main" %gl_FragColor
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource GLSL 330
|
||||
OpName %main "main"
|
||||
OpName %gl_FragColor "gl_FragColor"
|
||||
OpDecorate %gl_FragColor Location 0
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%float = OpTypeFloat 32
|
||||
%v4float = OpTypeVector %float 4
|
||||
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
||||
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
|
||||
%float_0_400000006 = OpConstant %float 0.400000006
|
||||
%float_0_800000012 = OpConstant %float 0.800000012
|
||||
%float_1 = OpConstant %float 1
|
||||
%13 = OpConstantComposite %v4float %float_0_400000006 %float_0_400000006 %float_0_800000012 %float_1
|
||||
%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
OpStore %gl_FragColor %13
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
# Copyright 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.
|
||||
|
||||
require 'json'
|
||||
|
||||
GRAMMAR = "../../external/spirv-headers/include/spirv/unified1/spirv.core.grammar.json"
|
||||
GLSL = "../../external/spirv-headers/include/spirv/unified1/extinst.glsl.std.450.grammar.json"
|
||||
|
||||
CAPABILITIES = %w(
|
||||
Matrix
|
||||
Shader
|
||||
Sampled1D
|
||||
Image1D
|
||||
DerivativeControl
|
||||
ImageQuery
|
||||
VulkanMemoryModel
|
||||
)
|
||||
|
||||
g = JSON.parse(File.open(GRAMMAR).read)
|
||||
magic = g['magic_number']
|
||||
vers = [g['major_version'], g['minor_version']]
|
||||
instructions = {}
|
||||
|
||||
g['instructions'].each do |inst|
|
||||
if (inst.has_key?('capabilities'))
|
||||
skip = true
|
||||
inst['capabilities'].each do |cap|
|
||||
if CAPABILITIES.include?(cap)
|
||||
skip = false
|
||||
break
|
||||
end
|
||||
end
|
||||
next if skip
|
||||
end
|
||||
|
||||
op = {
|
||||
opcode: inst['opcode'],
|
||||
operands: []
|
||||
}
|
||||
|
||||
if !inst['operands'].nil?
|
||||
inst['operands'].each do |operand|
|
||||
operand.delete('name')
|
||||
op[:operands] << operand
|
||||
end
|
||||
end
|
||||
|
||||
instructions[inst['opname']] = op
|
||||
end
|
||||
|
||||
operand_kinds = {}
|
||||
g['operand_kinds'].each do |op_kind|
|
||||
next if op_kind['category'] !~ /Enum/
|
||||
|
||||
kind = {
|
||||
type: op_kind['category'],
|
||||
values: {}
|
||||
}
|
||||
|
||||
op_kind['enumerants'].each do |enum|
|
||||
if (enum.has_key?('capabilities'))
|
||||
skip = true
|
||||
enum['capabilities'].each do |cap|
|
||||
if CAPABILITIES.include?(cap)
|
||||
skip = false
|
||||
break
|
||||
end
|
||||
end
|
||||
next if skip
|
||||
end
|
||||
|
||||
v = if op_kind['category'] == 'BitEnum'
|
||||
enum['value'].to_i(16)
|
||||
else
|
||||
enum['value'].to_i
|
||||
end
|
||||
params = []
|
||||
if enum.has_key?('parameters')
|
||||
enum['parameters'].each do |param|
|
||||
params << param['kind']
|
||||
end
|
||||
end
|
||||
kind[:values][enum['enumerant']] = {value: v}
|
||||
kind[:values][enum['enumerant']][:params] = params unless params.empty?
|
||||
end
|
||||
|
||||
next if kind[:values].empty?
|
||||
operand_kinds[op_kind['kind']] = kind
|
||||
end
|
||||
|
||||
# We only support GLSL extensions at the moment.
|
||||
ext = {}
|
||||
glsl = JSON.parse(File.open(GLSL).read)
|
||||
glsl['instructions'].each do |inst|
|
||||
ext[inst['opname']] = inst['opcode']
|
||||
end
|
||||
|
||||
puts "/*#{g['copyright'].join("\n")}*/"
|
||||
puts "\n// THIS FILE IS GENERATED WITH tools/process_grammar.rb\n\n"
|
||||
puts "export default " + JSON.pretty_generate({
|
||||
magic: magic,
|
||||
version: vers,
|
||||
instructions: instructions,
|
||||
operand_kinds: operand_kinds,
|
||||
ext: ext
|
||||
})
|
||||
1778
3rdparty/spirv-tools/tools/sva/yarn.lock
vendored
1778
3rdparty/spirv-tools/tools/sva/yarn.lock
vendored
File diff suppressed because it is too large
Load Diff
45
3rdparty/spirv-tools/tools/util/cli_consumer.cpp
vendored
45
3rdparty/spirv-tools/tools/util/cli_consumer.cpp
vendored
@@ -1,45 +0,0 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "tools/util/cli_consumer.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace spvtools {
|
||||
namespace utils {
|
||||
|
||||
void CLIMessageConsumer(spv_message_level_t level, const char*,
|
||||
const spv_position_t& position, const char* message) {
|
||||
switch (level) {
|
||||
case SPV_MSG_FATAL:
|
||||
case SPV_MSG_INTERNAL_ERROR:
|
||||
case SPV_MSG_ERROR:
|
||||
std::cerr << "error: line " << position.index << ": " << message
|
||||
<< std::endl;
|
||||
break;
|
||||
case SPV_MSG_WARNING:
|
||||
std::cout << "warning: line " << position.index << ": " << message
|
||||
<< std::endl;
|
||||
break;
|
||||
case SPV_MSG_INFO:
|
||||
std::cout << "info: line " << position.index << ": " << message
|
||||
<< std::endl;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace spvtools
|
||||
31
3rdparty/spirv-tools/tools/util/cli_consumer.h
vendored
31
3rdparty/spirv-tools/tools/util/cli_consumer.h
vendored
@@ -1,31 +0,0 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_UTIL_CLI_CONSUMMER_H_
|
||||
#define SOURCE_UTIL_CLI_CONSUMMER_H_
|
||||
|
||||
#include "include/spirv-tools/libspirv.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace utils {
|
||||
|
||||
// A message consumer that can be used by command line tools like spirv-opt and
|
||||
// spirv-val to display messages.
|
||||
void CLIMessageConsumer(spv_message_level_t level, const char*,
|
||||
const spv_position_t& position, const char* message);
|
||||
|
||||
} // namespace utils
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_UTIL_CLI_CONSUMMER_H_
|
||||
195
3rdparty/spirv-tools/tools/val/val.cpp
vendored
195
3rdparty/spirv-tools/tools/val/val.cpp
vendored
@@ -1,195 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "source/spirv_target_env.h"
|
||||
#include "source/spirv_validator_options.h"
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
#include "tools/io.h"
|
||||
#include "tools/util/cli_consumer.h"
|
||||
|
||||
void print_usage(char* argv0) {
|
||||
std::string target_env_list = spvTargetEnvList(36, 105);
|
||||
printf(
|
||||
R"(%s - Validate a SPIR-V binary file.
|
||||
|
||||
USAGE: %s [options] [<filename>]
|
||||
|
||||
The SPIR-V binary is read from <filename>. If no file is specified,
|
||||
or if the filename is "-", then the binary is read from standard input.
|
||||
|
||||
NOTE: The validator is a work in progress.
|
||||
|
||||
Options:
|
||||
-h, --help Print this help.
|
||||
--max-struct-members <maximum number of structure members allowed>
|
||||
--max-struct-depth <maximum allowed nesting depth of structures>
|
||||
--max-local-variables <maximum number of local variables allowed>
|
||||
--max-global-variables <maximum number of global variables allowed>
|
||||
--max-switch-branches <maximum number of branches allowed in switch statements>
|
||||
--max-function-args <maximum number arguments allowed per function>
|
||||
--max-control-flow-nesting-depth <maximum Control Flow nesting depth allowed>
|
||||
--max-access-chain-indexes <maximum number of indexes allowed to use for Access Chain instructions>
|
||||
--max-id-bound <maximum value for the id bound>
|
||||
--relax-logical-pointer Allow allocating an object of a pointer type and returning
|
||||
a pointer value from a function in logical addressing mode
|
||||
--relax-block-layout Enable VK_KHR_relaxed_block_layout when checking standard
|
||||
uniform, storage buffer, and push constant layouts.
|
||||
This is the default when targeting Vulkan 1.1 or later.
|
||||
--uniform-buffer-standard-layout Enable VK_KHR_uniform_buffer_standard_layout when checking standard
|
||||
uniform buffer layouts.
|
||||
--scalar-block-layout Enable VK_EXT_scalar_block_layout when checking standard
|
||||
uniform, storage buffer, and push constant layouts. Scalar layout
|
||||
rules are more permissive than relaxed block layout so in effect
|
||||
this will override the --relax-block-layout option.
|
||||
--skip-block-layout Skip checking standard uniform/storage buffer layout.
|
||||
Overrides any --relax-block-layout or --scalar-block-layout option.
|
||||
--relax-struct-store Allow store from one struct type to a
|
||||
different type with compatible layout and
|
||||
members.
|
||||
--before-hlsl-legalization Allows code patterns that are intended to be
|
||||
fixed by spirv-opt's legalization passes.
|
||||
--version Display validator version information.
|
||||
--target-env {%s}
|
||||
Use validation rules from the specified environment.
|
||||
)",
|
||||
argv0, argv0, target_env_list.c_str());
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
const char* inFile = nullptr;
|
||||
spv_target_env target_env = SPV_ENV_UNIVERSAL_1_5;
|
||||
spvtools::ValidatorOptions options;
|
||||
bool continue_processing = true;
|
||||
int return_code = 0;
|
||||
|
||||
for (int argi = 1; continue_processing && argi < argc; ++argi) {
|
||||
const char* cur_arg = argv[argi];
|
||||
if ('-' == cur_arg[0]) {
|
||||
if (0 == strncmp(cur_arg, "--max-", 6)) {
|
||||
if (argi + 1 < argc) {
|
||||
spv_validator_limit limit_type;
|
||||
if (spvParseUniversalLimitsOptions(cur_arg, &limit_type)) {
|
||||
uint32_t limit = 0;
|
||||
if (sscanf(argv[++argi], "%u", &limit)) {
|
||||
options.SetUniversalLimit(limit_type, limit);
|
||||
} else {
|
||||
fprintf(stderr, "error: missing argument to %s\n", cur_arg);
|
||||
continue_processing = false;
|
||||
return_code = 1;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "error: unrecognized option: %s\n", cur_arg);
|
||||
continue_processing = false;
|
||||
return_code = 1;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "error: Missing argument to %s\n", cur_arg);
|
||||
continue_processing = false;
|
||||
return_code = 1;
|
||||
}
|
||||
} else if (0 == strcmp(cur_arg, "--version")) {
|
||||
printf("%s\n", spvSoftwareVersionDetailsString());
|
||||
printf(
|
||||
"Targets:\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n "
|
||||
"%s\n %s\n",
|
||||
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_0),
|
||||
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1),
|
||||
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_2),
|
||||
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_3),
|
||||
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_4),
|
||||
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_5),
|
||||
spvTargetEnvDescription(SPV_ENV_OPENCL_2_2),
|
||||
spvTargetEnvDescription(SPV_ENV_VULKAN_1_0),
|
||||
spvTargetEnvDescription(SPV_ENV_VULKAN_1_1),
|
||||
spvTargetEnvDescription(SPV_ENV_VULKAN_1_1_SPIRV_1_4),
|
||||
spvTargetEnvDescription(SPV_ENV_WEBGPU_0));
|
||||
continue_processing = false;
|
||||
return_code = 0;
|
||||
} else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) {
|
||||
print_usage(argv[0]);
|
||||
continue_processing = false;
|
||||
return_code = 0;
|
||||
} else if (0 == strcmp(cur_arg, "--target-env")) {
|
||||
if (argi + 1 < argc) {
|
||||
const auto env_str = argv[++argi];
|
||||
if (!spvParseTargetEnv(env_str, &target_env)) {
|
||||
fprintf(stderr, "error: Unrecognized target env: %s\n", env_str);
|
||||
continue_processing = false;
|
||||
return_code = 1;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "error: Missing argument to --target-env\n");
|
||||
continue_processing = false;
|
||||
return_code = 1;
|
||||
}
|
||||
} else if (0 == strcmp(cur_arg, "--before-hlsl-legalization")) {
|
||||
options.SetBeforeHlslLegalization(true);
|
||||
} else if (0 == strcmp(cur_arg, "--relax-logical-pointer")) {
|
||||
options.SetRelaxLogicalPointer(true);
|
||||
} else if (0 == strcmp(cur_arg, "--relax-block-layout")) {
|
||||
options.SetRelaxBlockLayout(true);
|
||||
} else if (0 == strcmp(cur_arg, "--uniform-buffer-standard-layout")) {
|
||||
options.SetUniformBufferStandardLayout(true);
|
||||
} else if (0 == strcmp(cur_arg, "--scalar-block-layout")) {
|
||||
options.SetScalarBlockLayout(true);
|
||||
} else if (0 == strcmp(cur_arg, "--skip-block-layout")) {
|
||||
options.SetSkipBlockLayout(true);
|
||||
} else if (0 == strcmp(cur_arg, "--relax-struct-store")) {
|
||||
options.SetRelaxStructStore(true);
|
||||
} else if (0 == cur_arg[1]) {
|
||||
// Setting a filename of "-" to indicate stdin.
|
||||
if (!inFile) {
|
||||
inFile = cur_arg;
|
||||
} else {
|
||||
fprintf(stderr, "error: More than one input file specified\n");
|
||||
continue_processing = false;
|
||||
return_code = 1;
|
||||
}
|
||||
} else {
|
||||
print_usage(argv[0]);
|
||||
continue_processing = false;
|
||||
return_code = 1;
|
||||
}
|
||||
} else {
|
||||
if (!inFile) {
|
||||
inFile = cur_arg;
|
||||
} else {
|
||||
fprintf(stderr, "error: More than one input file specified\n");
|
||||
continue_processing = false;
|
||||
return_code = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exit if command line parsing was not successful.
|
||||
if (!continue_processing) {
|
||||
return return_code;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> contents;
|
||||
if (!ReadFile<uint32_t>(inFile, "rb", &contents)) return 1;
|
||||
|
||||
spvtools::SpirvTools tools(target_env);
|
||||
tools.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
|
||||
|
||||
bool succeed = tools.Validate(contents.data(), contents.size(), options);
|
||||
|
||||
return !succeed;
|
||||
}
|
||||
@@ -108,7 +108,6 @@ project "spirv-opt"
|
||||
path.join(SPIRV_TOOLS, "source/val/validate_composites.cpp"),
|
||||
path.join(SPIRV_TOOLS, "source/val/validate_constants.cpp"),
|
||||
path.join(SPIRV_TOOLS, "source/val/validate_conversion.cpp"),
|
||||
path.join(SPIRV_TOOLS, "source/val/validate_datarules.cpp"),
|
||||
path.join(SPIRV_TOOLS, "source/val/validate_debug.cpp"),
|
||||
path.join(SPIRV_TOOLS, "source/val/validate_decorations.cpp"),
|
||||
path.join(SPIRV_TOOLS, "source/val/validate_derivatives.cpp"),
|
||||
|
||||
Reference in New Issue
Block a user