From 4c693fc2624a8d37288815125aba19fa0a3aef0b Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 22 Feb 2018 18:06:00 +0100 Subject: [PATCH] [spirv] Added experimental spirv-tools integration Added support for the validator in order to make debugging easier, as well as the optimizer, which may help Nvidia users run DXVK. --- README.md | 14 +- include/spirv-tools/libspirv.h | 583 ++++++++++++++++++++++++++++++ include/spirv-tools/libspirv.hpp | 174 +++++++++ include/spirv-tools/linker.hpp | 83 +++++ include/spirv-tools/optimizer.hpp | 525 +++++++++++++++++++++++++++ lib/libSPIRV-Tools-opt.a | Bin 0 -> 3914900 bytes lib/libSPIRV-Tools.a | Bin 0 -> 1557288 bytes lib32/libSPIRV-Tools-opt.a | Bin 0 -> 2846358 bytes lib32/libSPIRV-Tools.a | Bin 0 -> 1353000 bytes meson.build | 3 + src/d3d11/d3d11_shader.cpp | 12 + src/dxvk/dxvk_shader.cpp | 10 + src/dxvk/dxvk_shader.h | 12 + src/spirv/meson.build | 3 +- src/spirv/spirv_code_buffer.cpp | 34 ++ src/spirv/spirv_code_buffer.h | 18 + 16 files changed, 1465 insertions(+), 6 deletions(-) create mode 100644 include/spirv-tools/libspirv.h create mode 100644 include/spirv-tools/libspirv.hpp create mode 100644 include/spirv-tools/linker.hpp create mode 100644 include/spirv-tools/optimizer.hpp create mode 100644 lib/libSPIRV-Tools-opt.a create mode 100644 lib/libSPIRV-Tools.a create mode 100644 lib32/libSPIRV-Tools-opt.a create mode 100644 lib32/libSPIRV-Tools.a diff --git a/README.md b/README.md index a2bde66b..10f7949a 100644 --- a/README.md +++ b/README.md @@ -51,18 +51,22 @@ The behaviour of DXVK can be modified with environment variables. - `DXVK_DEBUG_LAYERS=1` Enables Vulkan debug layers. Highly recommended for troubleshooting and debugging purposes. - `DXVK_SHADER_DUMP_PATH=directory` Writes all DXBC and SPIR-V shaders to the given directory - `DXVK_SHADER_READ_PATH=directory` Reads SPIR-V shaders from the given directory rather than using the shader compiler. +- `DXVK_SHADER_VALIDATE=1` Enables SPIR-V shader validation. Useful for debugging purposes. +- `DXVK_SHADER_OPTIMIZE=1` Enables SPIR-V shader optimization. Experimental, use with care. - `DXVK_LOG_LEVEL=error|warn|info|debug|trace` Controls message logging. - `DXVK_HUD=1` Enables the HUD ## Samples and executables -In addition to the DLLs, the following standalone programs are included in the project: +In addition to the DLLs, the following standalone programs are included in the project. +Most of them require a native `d3dcompiler_47.dll`, which you can retrieve from your +Windows installation in case you have one. -- `d3d11-compute`: Runs a simple compute shader demo. Requires native `d3dcompiler_47.dll`. -- `d3d11-triangle`: Renders a bunch of triangles using D3D11. Requires native `d3dcompiler_47.dll`. +- `d3d11-compute`: Runs a simple compute shader demo. +- `d3d11-triangle`: Renders a bunch of triangles using D3D11. - `dxgi-factory`: Enumerates DXGI adapters and outputs for debugging purposes. - `dxbc-compiler`: Compiles a DXBC shader to SPIR-V. -- `dxbc-disasm`: Disassembles a DXBC shader. Requires native `d3dcompiler_47.dll`. -- `hlsl-compiler`: Compiles a HLSL shader to DXBC. Requires native `d3dcompiler_47.dll`. +- `dxbc-disasm`: Disassembles a DXBC shader. +- `hlsl-compiler`: Compiles a HLSL shader to DXBC. ## Troubleshooting DXVK requires threading support from your mingw-w64 build environment. If you diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h new file mode 100644 index 00000000..7f1ebba7 --- /dev/null +++ b/include/spirv-tools/libspirv.h @@ -0,0 +1,583 @@ +// 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. + +#ifndef SPIRV_TOOLS_LIBSPIRV_H_ +#define SPIRV_TOOLS_LIBSPIRV_H_ + +#ifdef __cplusplus +extern "C" { +#else +#include +#endif + +#include +#include + +#if defined(SPIRV_TOOLS_SHAREDLIB) +#if defined(_WIN32) +#if defined(SPIRV_TOOLS_IMPLEMENTATION) +#define SPIRV_TOOLS_EXPORT __declspec(dllexport) +#else +#define SPIRV_TOOLS_EXPORT __declspec(dllimport) +#endif +#else +#if defined(SPIRV_TOOLS_IMPLEMENTATION) +#define SPIRV_TOOLS_EXPORT __attribute__((visibility("default"))) +#else +#define SPIRV_TOOLS_EXPORT +#endif +#endif +#else +#define SPIRV_TOOLS_EXPORT +#endif + +// Helpers + +#define SPV_BIT(shift) (1 << (shift)) + +#define SPV_FORCE_16_BIT_ENUM(name) _##name = 0x7fff +#define SPV_FORCE_32_BIT_ENUM(name) _##name = 0x7fffffff + +// Enumerations + +typedef enum spv_result_t { + SPV_SUCCESS = 0, + SPV_UNSUPPORTED = 1, + SPV_END_OF_STREAM = 2, + SPV_WARNING = 3, + SPV_FAILED_MATCH = 4, + SPV_REQUESTED_TERMINATION = 5, // Success, but signals early termination. + SPV_ERROR_INTERNAL = -1, + SPV_ERROR_OUT_OF_MEMORY = -2, + SPV_ERROR_INVALID_POINTER = -3, + SPV_ERROR_INVALID_BINARY = -4, + SPV_ERROR_INVALID_TEXT = -5, + SPV_ERROR_INVALID_TABLE = -6, + SPV_ERROR_INVALID_VALUE = -7, + SPV_ERROR_INVALID_DIAGNOSTIC = -8, + SPV_ERROR_INVALID_LOOKUP = -9, + SPV_ERROR_INVALID_ID = -10, + SPV_ERROR_INVALID_CFG = -11, + SPV_ERROR_INVALID_LAYOUT = -12, + SPV_ERROR_INVALID_CAPABILITY = -13, + SPV_ERROR_INVALID_DATA = -14, // Indicates data rules validation failure. + SPV_ERROR_MISSING_EXTENSION = -15, + SPV_FORCE_32_BIT_ENUM(spv_result_t) +} spv_result_t; + +// Severity levels of messages communicated to the consumer. +typedef enum spv_message_level_t { + SPV_MSG_FATAL, // Unrecoverable error due to environment. + // Will exit the program immediately. E.g., + // out of memory. + SPV_MSG_INTERNAL_ERROR, // Unrecoverable error due to SPIRV-Tools + // internals. + // Will exit the program immediately. E.g., + // unimplemented feature. + SPV_MSG_ERROR, // Normal error due to user input. + SPV_MSG_WARNING, // Warning information. + SPV_MSG_INFO, // General information. + SPV_MSG_DEBUG, // Debug information. +} spv_message_level_t; + +typedef enum spv_endianness_t { + SPV_ENDIANNESS_LITTLE, + SPV_ENDIANNESS_BIG, + SPV_FORCE_32_BIT_ENUM(spv_endianness_t) +} spv_endianness_t; + +// The kinds of operands that an instruction may have. +// +// Some operand types are "concrete". The binary parser uses a concrete +// operand type to describe an operand of a parsed instruction. +// +// The assembler uses all operand types. In addition to determining what +// kind of value an operand may be, non-concrete operand types capture the +// fact that an operand might be optional (may be absent, or present exactly +// once), or might occur zero or more times. +// +// Sometimes we also need to be able to express the fact that an operand +// is a member of an optional tuple of values. In that case the first member +// would be optional, and the subsequent members would be required. +typedef enum spv_operand_type_t { + // A sentinel value. + SPV_OPERAND_TYPE_NONE = 0, + + // Set 1: Operands that are IDs. + SPV_OPERAND_TYPE_ID, + SPV_OPERAND_TYPE_TYPE_ID, + SPV_OPERAND_TYPE_RESULT_ID, + SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, // SPIR-V Sec 3.25 + SPV_OPERAND_TYPE_SCOPE_ID, // SPIR-V Sec 3.27 + + // Set 2: Operands that are literal numbers. + SPV_OPERAND_TYPE_LITERAL_INTEGER, // Always unsigned 32-bits. + // The Instruction argument to OpExtInst. It's an unsigned 32-bit literal + // number indicating which instruction to use from an extended instruction + // set. + SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + // The Opcode argument to OpSpecConstantOp. It determines the operation + // to be performed on constant operands to compute a specialization constant + // result. + SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER, + // A literal number whose format and size are determined by a previous operand + // in the same instruction. It's a signed integer, an unsigned integer, or a + // floating point number. It also has a specified bit width. The width + // may be larger than 32, which would require such a typed literal value to + // occupy multiple SPIR-V words. + SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, + + // Set 3: The literal string operand type. + SPV_OPERAND_TYPE_LITERAL_STRING, + + // Set 4: Operands that are a single word enumerated value. + SPV_OPERAND_TYPE_SOURCE_LANGUAGE, // SPIR-V Sec 3.2 + SPV_OPERAND_TYPE_EXECUTION_MODEL, // SPIR-V Sec 3.3 + SPV_OPERAND_TYPE_ADDRESSING_MODEL, // SPIR-V Sec 3.4 + SPV_OPERAND_TYPE_MEMORY_MODEL, // SPIR-V Sec 3.5 + SPV_OPERAND_TYPE_EXECUTION_MODE, // SPIR-V Sec 3.6 + SPV_OPERAND_TYPE_STORAGE_CLASS, // SPIR-V Sec 3.7 + SPV_OPERAND_TYPE_DIMENSIONALITY, // SPIR-V Sec 3.8 + SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE, // SPIR-V Sec 3.9 + SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE, // SPIR-V Sec 3.10 + SPV_OPERAND_TYPE_SAMPLER_IMAGE_FORMAT, // SPIR-V Sec 3.11 + SPV_OPERAND_TYPE_IMAGE_CHANNEL_ORDER, // SPIR-V Sec 3.12 + SPV_OPERAND_TYPE_IMAGE_CHANNEL_DATA_TYPE, // SPIR-V Sec 3.13 + SPV_OPERAND_TYPE_FP_ROUNDING_MODE, // SPIR-V Sec 3.16 + SPV_OPERAND_TYPE_LINKAGE_TYPE, // SPIR-V Sec 3.17 + SPV_OPERAND_TYPE_ACCESS_QUALIFIER, // SPIR-V Sec 3.18 + SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE, // SPIR-V Sec 3.19 + SPV_OPERAND_TYPE_DECORATION, // SPIR-V Sec 3.20 + SPV_OPERAND_TYPE_BUILT_IN, // SPIR-V Sec 3.21 + SPV_OPERAND_TYPE_GROUP_OPERATION, // SPIR-V Sec 3.28 + SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS, // SPIR-V Sec 3.29 + SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO, // SPIR-V Sec 3.30 + SPV_OPERAND_TYPE_CAPABILITY, // SPIR-V Sec 3.31 + + // Set 5: Operands that are a single word bitmask. + // Sometimes a set bit indicates the instruction requires still more operands. + SPV_OPERAND_TYPE_IMAGE, // SPIR-V Sec 3.14 + SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, // SPIR-V Sec 3.15 + SPV_OPERAND_TYPE_SELECTION_CONTROL, // SPIR-V Sec 3.22 + SPV_OPERAND_TYPE_LOOP_CONTROL, // SPIR-V Sec 3.23 + SPV_OPERAND_TYPE_FUNCTION_CONTROL, // SPIR-V Sec 3.24 + SPV_OPERAND_TYPE_MEMORY_ACCESS, // SPIR-V Sec 3.26 + +// The remaining operand types are only used internally by the assembler. +// There are two categories: +// Optional : expands to 0 or 1 operand, like ? in regular expressions. +// Variable : expands to 0, 1 or many operands or pairs of operands. +// This is similar to * in regular expressions. + +// Macros for defining bounds on optional and variable operand types. +// Any variable operand type is also optional. +#define FIRST_OPTIONAL(ENUM) ENUM, SPV_OPERAND_TYPE_FIRST_OPTIONAL_TYPE = ENUM +#define FIRST_VARIABLE(ENUM) ENUM, SPV_OPERAND_TYPE_FIRST_VARIABLE_TYPE = ENUM +#define LAST_VARIABLE(ENUM) \ + ENUM, SPV_OPERAND_TYPE_LAST_VARIABLE_TYPE = ENUM, \ + SPV_OPERAND_TYPE_LAST_OPTIONAL_TYPE = ENUM + + // An optional operand represents zero or one logical operands. + // In an instruction definition, this may only appear at the end of the + // operand types. + FIRST_OPTIONAL(SPV_OPERAND_TYPE_OPTIONAL_ID), + // An optional image operand type. + SPV_OPERAND_TYPE_OPTIONAL_IMAGE, + // An optional memory access type. + SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, + // An optional literal integer. + SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER, + // An optional literal number, which may be either integer or floating point. + SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER, + // Like SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, but optional, and integral. + SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER, + // An optional literal string. + SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING, + // An optional access qualifier + SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER, + // An optional context-independent value, or CIV. CIVs are tokens that we can + // assemble regardless of where they occur -- literals, IDs, immediate + // integers, etc. + SPV_OPERAND_TYPE_OPTIONAL_CIV, + + // A variable operand represents zero or more logical operands. + // In an instruction definition, this may only appear at the end of the + // operand types. + FIRST_VARIABLE(SPV_OPERAND_TYPE_VARIABLE_ID), + SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER, + // A sequence of zero or more pairs of (typed literal integer, Id). + // Expands to zero or more: + // (SPV_OPERAND_TYPE_TYPED_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID) + // where the literal number must always be an integer of some sort. + SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER_ID, + // A sequence of zero or more pairs of (Id, Literal integer) + LAST_VARIABLE(SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER), + + // The following are concrete enum types. + SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS, // DebugInfo Sec 3.2. A mask. + SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING, // DebugInfo Sec 3.3 + SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE, // DebugInfo Sec 3.4 + SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER, // DebugInfo Sec 3.5 + SPV_OPERAND_TYPE_DEBUG_OPERATION, // DebugInfo Sec 3.6 + + // This is a sentinel value, and does not represent an operand type. + // It should come last. + SPV_OPERAND_TYPE_NUM_OPERAND_TYPES, + + SPV_FORCE_32_BIT_ENUM(spv_operand_type_t) +} spv_operand_type_t; + +typedef enum spv_ext_inst_type_t { + SPV_EXT_INST_TYPE_NONE = 0, + SPV_EXT_INST_TYPE_GLSL_STD_450, + SPV_EXT_INST_TYPE_OPENCL_STD, + SPV_EXT_INST_TYPE_SPV_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER, + SPV_EXT_INST_TYPE_SPV_AMD_SHADER_TRINARY_MINMAX, + SPV_EXT_INST_TYPE_SPV_AMD_GCN_SHADER, + SPV_EXT_INST_TYPE_SPV_AMD_SHADER_BALLOT, + SPV_EXT_INST_TYPE_DEBUGINFO, + + SPV_FORCE_32_BIT_ENUM(spv_ext_inst_type_t) +} spv_ext_inst_type_t; + +// This determines at a high level the kind of a binary-encoded literal +// number, but not the bit width. +// In principle, these could probably be folded into new entries in +// spv_operand_type_t. But then we'd have some special case differences +// between the assembler and disassembler. +typedef enum spv_number_kind_t { + SPV_NUMBER_NONE = 0, // The default for value initialization. + SPV_NUMBER_UNSIGNED_INT, + SPV_NUMBER_SIGNED_INT, + SPV_NUMBER_FLOATING, +} spv_number_kind_t; + +typedef enum spv_text_to_binary_options_t { + SPV_TEXT_TO_BINARY_OPTION_NONE = SPV_BIT(0), + // 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. + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS = SPV_BIT(1), + SPV_FORCE_32_BIT_ENUM(spv_text_to_binary_options_t) +} spv_text_to_binary_options_t; + +typedef enum spv_binary_to_text_options_t { + SPV_BINARY_TO_TEXT_OPTION_NONE = SPV_BIT(0), + SPV_BINARY_TO_TEXT_OPTION_PRINT = SPV_BIT(1), + SPV_BINARY_TO_TEXT_OPTION_COLOR = SPV_BIT(2), + SPV_BINARY_TO_TEXT_OPTION_INDENT = SPV_BIT(3), + SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET = SPV_BIT(4), + // Do not output the module header as leading comments in the assembly. + SPV_BINARY_TO_TEXT_OPTION_NO_HEADER = SPV_BIT(5), + // Use friendly names where possible. The heuristic may expand over + // time, but will use common names for scalar types, and debug names from + // OpName instructions. + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES = SPV_BIT(6), + SPV_FORCE_32_BIT_ENUM(spv_binary_to_text_options_t) +} spv_binary_to_text_options_t; + +// Structures + +// Information about an operand parsed from a binary SPIR-V module. +// Note that the values are not included. You still need access to the binary +// to extract the values. +typedef struct spv_parsed_operand_t { + // Location of the operand, in words from the start of the instruction. + uint16_t offset; + // Number of words occupied by this operand. + uint16_t num_words; + // The "concrete" operand type. See the definition of spv_operand_type_t + // for details. + spv_operand_type_t type; + // If type is a literal number type, then number_kind says whether it's + // a signed integer, an unsigned integer, or a floating point number. + spv_number_kind_t number_kind; + // The number of bits for a literal number type. + uint32_t number_bit_width; +} spv_parsed_operand_t; + +// An instruction parsed from a binary SPIR-V module. +typedef struct spv_parsed_instruction_t { + // An array of words for this instruction, in native endianness. + const uint32_t* words; + // The number of words in this instruction. + uint16_t num_words; + uint16_t opcode; + // The extended instruction type, if opcode is OpExtInst. Otherwise + // this is the "none" value. + spv_ext_inst_type_t ext_inst_type; + // The type id, or 0 if this instruction doesn't have one. + uint32_t type_id; + // The result id, or 0 if this instruction doesn't have one. + uint32_t result_id; + // The array of parsed operands. + const spv_parsed_operand_t* operands; + uint16_t num_operands; +} spv_parsed_instruction_t; + +typedef struct spv_const_binary_t { + const uint32_t* code; + const size_t wordCount; +} spv_const_binary_t; + +typedef struct spv_binary_t { + uint32_t* code; + size_t wordCount; +} spv_binary_t; + +typedef struct spv_text_t { + const char* str; + size_t length; +} spv_text_t; + +typedef struct spv_position_t { + size_t line; + size_t column; + size_t index; +} spv_position_t; + +typedef struct spv_diagnostic_t { + spv_position_t position; + char* error; + bool isTextSource; +} spv_diagnostic_t; + +// Opaque struct containing the context used to operate on a SPIR-V module. +// Its object is used by various translation API functions. +typedef struct spv_context_t spv_context_t; + +typedef struct spv_validator_options_t spv_validator_options_t; + +// Type Definitions + +typedef spv_const_binary_t* spv_const_binary; +typedef spv_binary_t* spv_binary; +typedef spv_text_t* spv_text; +typedef spv_position_t* spv_position; +typedef spv_diagnostic_t* spv_diagnostic; +typedef const spv_context_t* spv_const_context; +typedef spv_context_t* spv_context; +typedef spv_validator_options_t* spv_validator_options; +typedef const spv_validator_options_t* spv_const_validator_options; + +// Platform API + +// Returns the SPIRV-Tools software version as a null-terminated string. +// The contents of the underlying storage is valid for the remainder of +// the process. +SPIRV_TOOLS_EXPORT const char* spvSoftwareVersionString(); +// Returns a null-terminated string containing the name of the project, +// the software version string, and commit details. +// The contents of the underlying storage is valid for the remainder of +// the process. +SPIRV_TOOLS_EXPORT const char* spvSoftwareVersionDetailsString(); + +// Certain target environments impose additional restrictions on SPIR-V, so it's +// often necessary to specify which one applies. SPV_ENV_UNIVERSAL means +// environment-agnostic SPIR-V. +typedef enum { + SPV_ENV_UNIVERSAL_1_0, // SPIR-V 1.0 latest revision, no other restrictions. + SPV_ENV_VULKAN_1_0, // Vulkan 1.0 latest revision. + SPV_ENV_UNIVERSAL_1_1, // SPIR-V 1.1 latest revision, no other restrictions. + SPV_ENV_OPENCL_2_1, // OpenCL Full Profile 2.1 latest revision. + SPV_ENV_OPENCL_2_2, // OpenCL Full Profile 2.2 latest revision. + SPV_ENV_OPENGL_4_0, // OpenGL 4.0 plus GL_ARB_gl_spirv, latest revisions. + SPV_ENV_OPENGL_4_1, // OpenGL 4.1 plus GL_ARB_gl_spirv, latest revisions. + SPV_ENV_OPENGL_4_2, // OpenGL 4.2 plus GL_ARB_gl_spirv, latest revisions. + SPV_ENV_OPENGL_4_3, // OpenGL 4.3 plus GL_ARB_gl_spirv, latest revisions. + // There is no variant for OpenGL 4.4. + SPV_ENV_OPENGL_4_5, // OpenGL 4.5 plus GL_ARB_gl_spirv, latest revisions. + SPV_ENV_UNIVERSAL_1_2, // SPIR-V 1.2, latest revision, no other restrictions. + SPV_ENV_OPENCL_1_2, // OpenCL Full Profile 1.2 plus cl_khr_il_program, + // latest revision. + SPV_ENV_OPENCL_EMBEDDED_1_2, // OpenCL Embedded Profile 1.2 plus + // cl_khr_il_program, latest revision. + SPV_ENV_OPENCL_2_0, // OpenCL Full Profile 2.0 plus cl_khr_il_program, + // latest revision. + SPV_ENV_OPENCL_EMBEDDED_2_0, // OpenCL Embedded Profile 2.0 plus + // cl_khr_il_program, latest revision. + SPV_ENV_OPENCL_EMBEDDED_2_1, // OpenCL Embedded Profile 2.1 latest revision. + SPV_ENV_OPENCL_EMBEDDED_2_2, // OpenCL Embedded Profile 2.2 latest revision. +} spv_target_env; + +// SPIR-V Validator can be parameterized with the following Universal Limits. +typedef enum { + spv_validator_limit_max_struct_members, + spv_validator_limit_max_struct_depth, + spv_validator_limit_max_local_variables, + spv_validator_limit_max_global_variables, + spv_validator_limit_max_switch_branches, + spv_validator_limit_max_function_args, + spv_validator_limit_max_control_flow_nesting_depth, + spv_validator_limit_max_access_chain_indexes, +} spv_validator_limit; + +// Returns a string describing the given SPIR-V target environment. +SPIRV_TOOLS_EXPORT const char* spvTargetEnvDescription(spv_target_env env); + +// Creates a context object. Returns null if env is invalid. +SPIRV_TOOLS_EXPORT spv_context spvContextCreate(spv_target_env env); + +// Destroys the given context object. +SPIRV_TOOLS_EXPORT void spvContextDestroy(spv_context context); + +// Creates a Validator options object with default options. Returns a valid +// options object. The object remains valid until it is passed into +// spvValidatorOptionsDestroy. +SPIRV_TOOLS_EXPORT spv_validator_options spvValidatorOptionsCreate(); + +// Destroys the given Validator options object. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsDestroy( + spv_validator_options options); + +// Records the maximum Universal Limit that is considered valid in the given +// Validator options object. argument must be a valid options object. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetUniversalLimit( + spv_validator_options options, spv_validator_limit limit_type, + uint32_t limit); + +// Record whether or not the validator should relax the rules on types for +// stores to structs. When relaxed, it will allow a type mismatch as long as +// the types are structs with the same layout. Two structs have the same layout +// if +// +// 1) the members of the structs are either the same type or are structs with +// same layout, and +// +// 2) the decorations that affect the memory layout are identical for both +// types. Other decorations are not relevant. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetRelaxStoreStruct( + spv_validator_options options, bool val); + +// Records whether or not the validator should relax the rules on pointer usage +// in logical addressing mode. +// +// When relaxed, it will allow the following usage cases of pointers: +// 1) OpVariable allocating an object whose type is a pointer type +// 2) OpReturnValue returning a pointer value +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetRelaxLogicalPointer( + spv_validator_options options, bool val); + +// Encodes the given SPIR-V assembly text to its binary representation. The +// length parameter specifies the number of bytes for text. Encoded binary will +// be stored into *binary. Any error will be written into *diagnostic if +// diagnostic is non-null. The generated binary is independent of the context +// and may outlive it. +SPIRV_TOOLS_EXPORT spv_result_t spvTextToBinary(const spv_const_context context, + const char* text, + const size_t length, + spv_binary* binary, + spv_diagnostic* diagnostic); + +// Encodes the given SPIR-V assembly text to its binary representation. Same as +// spvTextToBinary but with options. The options parameter is a bit field of +// spv_text_to_binary_options_t. +SPIRV_TOOLS_EXPORT spv_result_t spvTextToBinaryWithOptions( + const spv_const_context context, const char* text, const size_t length, + const uint32_t options, spv_binary* binary, spv_diagnostic* diagnostic); + +// Frees an allocated text stream. This is a no-op if the text parameter +// is a null pointer. +SPIRV_TOOLS_EXPORT void spvTextDestroy(spv_text text); + +// Decodes the given SPIR-V binary representation to its assembly text. The +// word_count parameter specifies the number of words for binary. The options +// parameter is a bit field of spv_binary_to_text_options_t. Decoded text will +// be stored into *text. Any error will be written into *diagnostic if +// diagnostic is non-null. +SPIRV_TOOLS_EXPORT spv_result_t spvBinaryToText(const spv_const_context context, + const uint32_t* binary, + const size_t word_count, + const uint32_t options, + spv_text* text, + spv_diagnostic* diagnostic); + +// Frees a binary stream from memory. This is a no-op if binary is a null +// pointer. +SPIRV_TOOLS_EXPORT void spvBinaryDestroy(spv_binary binary); + +// Validates a SPIR-V binary for correctness. Any errors will be written into +// *diagnostic if diagnostic is non-null. +SPIRV_TOOLS_EXPORT spv_result_t spvValidate(const spv_const_context context, + const spv_const_binary binary, + spv_diagnostic* diagnostic); + +// Validates a SPIR-V binary for correctness. Uses the provided Validator +// options. Any errors will be written into *diagnostic if diagnostic is +// non-null. +SPIRV_TOOLS_EXPORT spv_result_t spvValidateWithOptions( + const spv_const_context context, const spv_const_validator_options options, + const spv_const_binary binary, spv_diagnostic* diagnostic); + +// Validates a raw SPIR-V binary for correctness. Any errors will be written +// into *diagnostic if diagnostic is non-null. +SPIRV_TOOLS_EXPORT spv_result_t +spvValidateBinary(const spv_const_context context, const uint32_t* words, + const size_t num_words, spv_diagnostic* diagnostic); + +// Creates a diagnostic object. The position parameter specifies the location in +// the text/binary stream. The message parameter, copied into the diagnostic +// object, contains the error message to display. +SPIRV_TOOLS_EXPORT spv_diagnostic +spvDiagnosticCreate(const spv_position position, const char* message); + +// Destroys a diagnostic object. This is a no-op if diagnostic is a null +// pointer. +SPIRV_TOOLS_EXPORT void spvDiagnosticDestroy(spv_diagnostic diagnostic); + +// Prints the diagnostic to stderr. +SPIRV_TOOLS_EXPORT spv_result_t +spvDiagnosticPrint(const spv_diagnostic diagnostic); + +// The binary parser interface. + +// A pointer to a function that accepts a parsed SPIR-V header. +// The integer arguments are the 32-bit words from the header, as specified +// in SPIR-V 1.0 Section 2.3 Table 1. +// The function should return SPV_SUCCESS if parsing should continue. +typedef spv_result_t (*spv_parsed_header_fn_t)( + void* user_data, spv_endianness_t endian, uint32_t magic, uint32_t version, + uint32_t generator, uint32_t id_bound, uint32_t reserved); + +// A pointer to a function that accepts a parsed SPIR-V instruction. +// The parsed_instruction value is transient: it may be overwritten +// or released immediately after the function has returned. That also +// applies to the words array member of the parsed instruction. The +// function should return SPV_SUCCESS if and only if parsing should +// continue. +typedef spv_result_t (*spv_parsed_instruction_fn_t)( + void* user_data, const spv_parsed_instruction_t* parsed_instruction); + +// Parses a SPIR-V binary, specified as counted sequence of 32-bit words. +// Parsing feedback is provided via two callbacks provided as function +// pointers. Each callback function pointer can be a null pointer, in +// which case it is never called. Otherwise, in a valid parse the +// parsed-header callback is called once, and then the parsed-instruction +// callback once for each instruction in the stream. The user_data parameter +// is supplied as context to the callbacks. Returns SPV_SUCCESS on successful +// parse where the callbacks always return SPV_SUCCESS. For an invalid parse, +// returns a status code other than SPV_SUCCESS, and if diagnostic is non-null +// also emits a diagnostic. If a callback returns anything other than +// SPV_SUCCESS, then that status code is returned, no further callbacks are +// issued, and no additional diagnostics are emitted. +SPIRV_TOOLS_EXPORT spv_result_t spvBinaryParse( + const spv_const_context context, void* user_data, const uint32_t* words, + const size_t num_words, spv_parsed_header_fn_t parse_header, + spv_parsed_instruction_fn_t parse_instruction, spv_diagnostic* diagnostic); + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_TOOLS_LIBSPIRV_H_ diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp new file mode 100644 index 00000000..2e4aa628 --- /dev/null +++ b/include/spirv-tools/libspirv.hpp @@ -0,0 +1,174 @@ +// 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 SPIRV_TOOLS_LIBSPIRV_HPP_ +#define SPIRV_TOOLS_LIBSPIRV_HPP_ + +#include +#include +#include +#include + +#include "spirv-tools/libspirv.h" + +namespace spvtools { + +// Message consumer. The C strings for source and message are only alive for the +// specific invocation. +using MessageConsumer = std::function; + +// C++ RAII wrapper around the C context object spv_context. +class Context { + public: + // Constructs a context targeting the given environment |env|. + // + // The constructed instance will have an empty message consumer, which just + // ignores all messages from the library. Use SetMessageConsumer() to supply + // one if messages are of concern. + explicit Context(spv_target_env env); + + // Enables move constructor/assignment operations. + Context(Context&& other); + Context& operator=(Context&& other); + + // Disables copy constructor/assignment operations. + Context(const Context&) = delete; + Context& operator=(const Context&) = delete; + + // Destructs this instance. + ~Context(); + + // Sets the message consumer to the given |consumer|. The |consumer| will be + // invoked once for each message communicated from the library. + void SetMessageConsumer(MessageConsumer consumer); + + // Returns the underlying spv_context. + spv_context& CContext(); + const spv_context& CContext() const; + + private: + spv_context context_; +}; + +// A RAII wrapper around a validator options object. +class ValidatorOptions { + public: + ValidatorOptions() : options_(spvValidatorOptionsCreate()) {} + ~ValidatorOptions() { spvValidatorOptionsDestroy(options_); } + // Allow implicit conversion to the underlying object. + operator spv_validator_options() const { return options_; } + + // Sets a limit. + void SetUniversalLimit(spv_validator_limit limit_type, uint32_t limit) { + spvValidatorOptionsSetUniversalLimit(options_, limit_type, limit); + } + + void SetRelaxStructStore(bool val) { + spvValidatorOptionsSetRelaxStoreStruct(options_, val); + } + + // Records whether or not the validator should relax the rules on pointer + // usage in logical addressing mode. + // + // When relaxed, it will allow the following usage cases of pointers: + // 1) OpVariable allocating an object whose type is a pointer type + // 2) OpReturnValue returning a pointer value + void SetRelaxLogicalPointer(bool val) { + spvValidatorOptionsSetRelaxLogicalPointer(options_, val); + } + + private: + spv_validator_options options_; +}; + +// C++ interface for SPIRV-Tools functionalities. It wraps the context +// (including target environment and the corresponding SPIR-V grammar) and +// provides methods for assembling, disassembling, and validating. +// +// Instances of this class provide basic thread-safety guarantee. +class SpirvTools { + public: + enum { + // Default assembling option used by assemble(): + kDefaultAssembleOption = SPV_TEXT_TO_BINARY_OPTION_NONE, + + // Default disassembling option used by Disassemble(): + // * Avoid prefix comments from decoding the SPIR-V module header, and + // * Use friendly names for variables. + kDefaultDisassembleOption = SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES + }; + + // Constructs an instance targeting the given environment |env|. + // + // The constructed instance will have an empty message consumer, which just + // ignores all messages from the library. Use SetMessageConsumer() to supply + // one if messages are of concern. + explicit SpirvTools(spv_target_env env); + + // Disables copy/move constructor/assignment operations. + SpirvTools(const SpirvTools&) = delete; + SpirvTools(SpirvTools&&) = delete; + SpirvTools& operator=(const SpirvTools&) = delete; + SpirvTools& operator=(SpirvTools&&) = delete; + + // Destructs this instance. + ~SpirvTools(); + + // Sets the message consumer to the given |consumer|. The |consumer| will be + // invoked once for each message communicated from the library. + void SetMessageConsumer(MessageConsumer consumer); + + // Assembles the given assembly |text| and writes the result to |binary|. + // Returns true on successful assembling. |binary| will be kept untouched if + // assembling is unsuccessful. + bool Assemble(const std::string& text, std::vector* binary, + uint32_t options = kDefaultAssembleOption) const; + // |text_size| specifies the number of bytes in |text|. A terminating null + // character is not required to present in |text| as long as |text| is valid. + bool Assemble(const char* text, size_t text_size, + std::vector* binary, + uint32_t options = kDefaultAssembleOption) const; + + // Disassembles the given SPIR-V |binary| with the given |options| and writes + // the assembly to |text|. Returns ture on successful disassembling. |text| + // will be kept untouched if diassembling is unsuccessful. + bool Disassemble(const std::vector& binary, std::string* text, + uint32_t options = kDefaultDisassembleOption) const; + // |binary_size| specifies the number of words in |binary|. + bool Disassemble(const uint32_t* binary, size_t binary_size, + std::string* text, + uint32_t options = kDefaultDisassembleOption) const; + + // Validates the given SPIR-V |binary|. Returns true if no issues are found. + // Otherwise, returns false and communicates issues via the message consumer + // registered. + bool Validate(const std::vector& binary) const; + // |binary_size| specifies the number of words in |binary|. + bool Validate(const uint32_t* binary, size_t binary_size) const; + // Like the previous overload, but takes an options object. + bool Validate(const uint32_t* binary, size_t binary_size, + const ValidatorOptions& options) const; + + private: + struct Impl; // Opaque struct for holding the data fields used by this class. + std::unique_ptr impl_; // Unique pointer to implementation data. +}; + +} // namespace spvtools + +#endif // SPIRV_TOOLS_LIBSPIRV_HPP_ diff --git a/include/spirv-tools/linker.hpp b/include/spirv-tools/linker.hpp new file mode 100644 index 00000000..df56251f --- /dev/null +++ b/include/spirv-tools/linker.hpp @@ -0,0 +1,83 @@ +// 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. + +#ifndef SPIRV_TOOLS_LINKER_HPP_ +#define SPIRV_TOOLS_LINKER_HPP_ + +#include + +#include +#include + +#include "libspirv.hpp" + +namespace spvtools { + +class LinkerOptions { + public: + LinkerOptions() : create_library_(false), verify_ids_(false) {} + + // Returns whether a library or an executable should be produced by the + // linking phase. + // + // All exported symbols are kept when creating a library, whereas they will + // be removed when creating an executable. + // The returned value will be true if creating a library, and false if + // creating an executable. + bool GetCreateLibrary() const { return create_library_; } + + // Sets whether a library or an executable should be produced. + void SetCreateLibrary(bool create_library) { + create_library_ = create_library; + } + + // Returns whether to verify the uniqueness of the unique ids in the merged + // context. + bool GetVerifyIds() const { return verify_ids_; } + + // Sets whether to verify the uniqueness of the unique ids in the merged + // context. + void SetVerifyIds(bool verify_ids) { verify_ids_ = verify_ids; } + + private: + bool create_library_; + bool verify_ids_; +}; + +// Links one or more SPIR-V modules into a new SPIR-V module. That is, combine +// several SPIR-V modules into one, resolving link dependencies between them. +// +// At least one binary has to be provided in |binaries|. Those binaries do not +// have to be valid, but they should be at least parseable. +// The functions can fail due to the following: +// * The given context was not initialised using `spvContextCreate()`; +// * No input modules were given; +// * One or more of those modules were not parseable; +// * The input modules used different addressing or memory models; +// * The ID or global variable number limit were exceeded; +// * Some entry points were defined multiple times; +// * Some imported symbols did not have an exported counterpart; +// * Possibly other reasons. +spv_result_t Link(const Context& context, + const std::vector>& binaries, + std::vector* linked_binary, + const LinkerOptions& options = LinkerOptions()); +spv_result_t Link(const Context& context, const uint32_t* const* binaries, + const size_t* binary_sizes, size_t num_binaries, + std::vector* linked_binary, + const LinkerOptions& options = LinkerOptions()); + +} // namespace spvtools + +#endif // SPIRV_TOOLS_LINKER_HPP_ diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp new file mode 100644 index 00000000..9f3b3601 --- /dev/null +++ b/include/spirv-tools/optimizer.hpp @@ -0,0 +1,525 @@ +// 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 SPIRV_TOOLS_OPTIMIZER_HPP_ +#define SPIRV_TOOLS_OPTIMIZER_HPP_ + +#include +#include +#include +#include +#include + +#include "libspirv.hpp" + +namespace spvtools { + +// C++ interface for SPIR-V optimization functionalities. It wraps the context +// (including target environment and the corresponding SPIR-V grammar) and +// provides methods for registering optimization passes and optimizing. +// +// Instances of this class provides basic thread-safety guarantee. +class Optimizer { + public: + // The token for an optimization pass. It is returned via one of the + // Create*Pass() standalone functions at the end of this header file and + // consumed by the RegisterPass() method. Tokens are one-time objects that + // only support move; copying is not allowed. + struct PassToken { + struct Impl; // Opaque struct for holding inernal data. + + PassToken(std::unique_ptr); + + // Tokens can only be moved. Copying is disabled. + PassToken(const PassToken&) = delete; + PassToken(PassToken&&); + PassToken& operator=(const PassToken&) = delete; + PassToken& operator=(PassToken&&); + + ~PassToken(); + + std::unique_ptr impl_; // Unique pointer to internal data. + }; + + // Constructs an instance with the given target |env|, which is used to decode + // the binaries to be optimized later. + // + // The constructed instance will have an empty message consumer, which just + // ignores all messages from the library. Use SetMessageConsumer() to supply + // one if messages are of concern. + explicit Optimizer(spv_target_env env); + + // Disables copy/move constructor/assignment operations. + Optimizer(const Optimizer&) = delete; + Optimizer(Optimizer&&) = delete; + Optimizer& operator=(const Optimizer&) = delete; + Optimizer& operator=(Optimizer&&) = delete; + + // Destructs this instance. + ~Optimizer(); + + // Sets the message consumer to the given |consumer|. The |consumer| will be + // invoked once for each message communicated from the library. + void SetMessageConsumer(MessageConsumer consumer); + + // Registers the given |pass| to this optimizer. Passes will be run in the + // exact order of registration. The token passed in will be consumed by this + // method. + Optimizer& RegisterPass(PassToken&& pass); + + // Registers passes that attempt to improve performance of generated code. + // This sequence of passes is subject to constant review and will change + // from time to time. + Optimizer& RegisterPerformancePasses(); + + // Registers passes that attempt to improve the size of generated code. + // This sequence of passes is subject to constant review and will change + // from time to time. + Optimizer& RegisterSizePasses(); + + // Registers passes that attempt to legalize the generated code. + // + // Note: this recipe is specially for legalizing SPIR-V. It should be used + // by compilers after translating HLSL source code literally. It should + // *not* be used by general workloads for performance or size improvement. + // + // This sequence of passes is subject to constant review and will change + // from time to time. + Optimizer& RegisterLegalizationPasses(); + + // Optimizes the given SPIR-V module |original_binary| and writes the + // optimized binary into |optimized_binary|. + // Returns true on successful optimization, whether or not the module is + // modified. Returns false if errors occur when processing |original_binary| + // using any of the registered passes. In that case, no further passes are + // executed and the contents in |optimized_binary| may be invalid. + // + // It's allowed to alias |original_binary| to the start of |optimized_binary|. + bool Run(const uint32_t* original_binary, size_t original_binary_size, + std::vector* optimized_binary) const; + + // Returns a vector of strings with all the pass names added to this + // optimizer's pass manager. These strings are valid until the associated + // pass manager is destroyed. + std::vector GetPassNames() const; + + // Sets the option to print the disassembly before each pass and after the + // last pass. If |out| is null, then no output is generated. Otherwise, + // output is sent to the |out| output stream. + Optimizer& SetPrintAll(std::ostream* out); + + private: + struct Impl; // Opaque struct for holding internal data. + std::unique_ptr impl_; // Unique pointer to internal data. +}; + +// Creates a null pass. +// A null pass does nothing to the SPIR-V module to be optimized. +Optimizer::PassToken CreateNullPass(); + +// Creates a strip-debug-info pass. +// A strip-debug-info pass removes all debug instructions (as documented in +// Section 3.32.2 of the SPIR-V spec) of the SPIR-V module to be optimized. +Optimizer::PassToken CreateStripDebugInfoPass(); + +// Creates an eliminate-dead-functions pass. +// An eliminate-dead-functions pass will remove all functions that are not in +// the call trees rooted at entry points and exported functions. These +// functions are not needed because they will never be called. +Optimizer::PassToken CreateEliminateDeadFunctionsPass(); + +// Creates a set-spec-constant-default-value pass from a mapping from spec-ids +// to the default values in the form of string. +// A set-spec-constant-default-value pass sets the default values for the +// spec constants that have SpecId decorations (i.e., those defined by +// OpSpecConstant{|True|False} instructions). +Optimizer::PassToken CreateSetSpecConstantDefaultValuePass( + const std::unordered_map& id_value_map); + +// Creates a set-spec-constant-default-value pass from a mapping from spec-ids +// to the default values in the form of bit pattern. +// A set-spec-constant-default-value pass sets the default values for the +// spec constants that have SpecId decorations (i.e., those defined by +// OpSpecConstant{|True|False} instructions). +Optimizer::PassToken CreateSetSpecConstantDefaultValuePass( + const std::unordered_map>& id_value_map); + +// Creates a flatten-decoration pass. +// A flatten-decoration pass replaces grouped decorations with equivalent +// ungrouped decorations. That is, it replaces each OpDecorationGroup +// instruction and associated OpGroupDecorate and OpGroupMemberDecorate +// instructions with equivalent OpDecorate and OpMemberDecorate instructions. +// The pass does not attempt to preserve debug information for instructions +// it removes. +Optimizer::PassToken CreateFlattenDecorationPass(); + +// Creates a freeze-spec-constant-value pass. +// A freeze-spec-constant pass specializes the value of spec constants to +// their default values. This pass only processes the spec constants that have +// SpecId decorations (defined by OpSpecConstant, OpSpecConstantTrue, or +// OpSpecConstantFalse instructions) and replaces them with their normal +// counterparts (OpConstant, OpConstantTrue, or OpConstantFalse). The +// corresponding SpecId annotation instructions will also be removed. This +// pass does not fold the newly added normal constants and does not process +// other spec constants defined by OpSpecConstantComposite or +// OpSpecConstantOp. +Optimizer::PassToken CreateFreezeSpecConstantValuePass(); + +// Creates a fold-spec-constant-op-and-composite pass. +// A fold-spec-constant-op-and-composite pass folds spec constants defined by +// OpSpecConstantOp or OpSpecConstantComposite instruction, to normal Constants +// defined by OpConstantTrue, OpConstantFalse, OpConstant, OpConstantNull, or +// OpConstantComposite instructions. Note that spec constants defined with +// OpSpecConstant, OpSpecConstantTrue, or OpSpecConstantFalse instructions are +// not handled, as these instructions indicate their value are not determined +// and can be changed in future. A spec constant is foldable if all of its +// value(s) can be determined from the module. E.g., an integer spec constant +// defined with OpSpecConstantOp instruction can be folded if its value won't +// change later. This pass will replace the original OpSpecContantOp instruction +// with an OpConstant instruction. When folding composite spec constants, +// new instructions may be inserted to define the components of the composite +// constant first, then the original spec constants will be replaced by +// OpConstantComposite instructions. +// +// There are some operations not supported yet: +// OpSConvert, OpFConvert, OpQuantizeToF16 and +// all the operations under Kernel capability. +// TODO(qining): Add support for the operations listed above. +Optimizer::PassToken CreateFoldSpecConstantOpAndCompositePass(); + +// Creates a unify-constant pass. +// A unify-constant pass de-duplicates the constants. Constants with the exact +// same value and identical form will be unified and only one constant will +// be kept for each unique pair of type and value. +// There are several cases not handled by this pass: +// 1) Constants defined by OpConstantNull instructions (null constants) and +// constants defined by OpConstantFalse, OpConstant or OpConstantComposite +// with value 0 (zero-valued normal constants) are not considered equivalent. +// So null constants won't be used to replace zero-valued normal constants, +// vice versa. +// 2) Whenever there are decorations to the constant's result id id, the +// constant won't be handled, which means, it won't be used to replace any +// other constants, neither can other constants replace it. +// 3) NaN in float point format with different bit patterns are not unified. +Optimizer::PassToken CreateUnifyConstantPass(); + +// Creates a eliminate-dead-constant pass. +// A eliminate-dead-constant pass removes dead constants, including normal +// contants defined by OpConstant, OpConstantComposite, OpConstantTrue, or +// OpConstantFalse and spec constants defined by OpSpecConstant, +// OpSpecConstantComposite, OpSpecConstantTrue, OpSpecConstantFalse or +// OpSpecConstantOp. +Optimizer::PassToken CreateEliminateDeadConstantPass(); + +// Creates a strength-reduction pass. +// A strength-reduction pass will look for opportunities to replace an +// instruction with an equivalent and less expensive one. For example, +// multiplying by a power of 2 can be replaced by a bit shift. +Optimizer::PassToken CreateStrengthReductionPass(); + +// Creates a block merge pass. +// This pass searches for blocks with a single Branch to a block with no +// other predecessors and merges the blocks into a single block. Continue +// blocks and Merge blocks are not candidates for the second block. +// +// The pass is most useful after Dead Branch Elimination, which can leave +// such sequences of blocks. Merging them makes subsequent passes more +// effective, such as single block local store-load elimination. +// +// While this pass reduces the number of occurrences of this sequence, at +// this time it does not guarantee all such sequences are eliminated. +// +// Presence of phi instructions can inhibit this optimization. Handling +// these is left for future improvements. +Optimizer::PassToken CreateBlockMergePass(); + +// Creates an exhaustive inline pass. +// An exhaustive inline pass attempts to exhaustively inline all function +// calls in all functions in an entry point call tree. The intent is to enable, +// albeit through brute force, analysis and optimization across function +// calls by subsequent optimization passes. As the inlining is exhaustive, +// there is no attempt to optimize for size or runtime performance. Functions +// that are not in the call tree of an entry point are not changed. +Optimizer::PassToken CreateInlineExhaustivePass(); + +// Creates an opaque inline pass. +// An opaque inline pass inlines all function calls in all functions in all +// entry point call trees where the called function contains an opaque type +// in either its parameter types or return type. An opaque type is currently +// defined as Image, Sampler or SampledImage. The intent is to enable, albeit +// through brute force, analysis and optimization across these function calls +// by subsequent passes in order to remove the storing of opaque types which is +// not legal in Vulkan. Functions that are not in the call tree of an entry +// point are not changed. +Optimizer::PassToken CreateInlineOpaquePass(); + +// Creates a single-block local variable load/store elimination pass. +// For every entry point function, do single block memory optimization of +// function variables referenced only with non-access-chain loads and stores. +// For each targeted variable load, if previous store to that variable in the +// block, replace the load's result id with the value id of the store. +// If previous load within the block, replace the current load's result id +// with the previous load's result id. In either case, delete the current +// load. Finally, check if any remaining stores are useless, and delete store +// and variable if possible. +// +// The presence of access chain references and function calls can inhibit +// the above optimization. +// +// Only modules with relaxed logical addressing (see opt/instruction.h) are +// currently processed. +// +// This pass is most effective if preceeded by Inlining and +// LocalAccessChainConvert. This pass will reduce the work needed to be done +// by LocalSingleStoreElim and LocalMultiStoreElim. +// +// Only functions in the call tree of an entry point are processed. +Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass(); + +// Create dead branch elimination pass. +// For each entry point function, this pass will look for SelectionMerge +// BranchConditionals with constant condition and convert to a Branch to +// the indicated label. It will delete resulting dead blocks. +// +// For all phi functions in merge block, replace all uses with the id +// corresponding to the living predecessor. +// +// Note that some branches and blocks may be left to avoid creating invalid +// control flow. Improving this is left to future work. +// +// This pass is most effective when preceeded by passes which eliminate +// local loads and stores, effectively propagating constant values where +// possible. +Optimizer::PassToken CreateDeadBranchElimPass(); + +// Creates an SSA local variable load/store elimination pass. +// For every entry point function, eliminate all loads and stores of function +// scope variables only referenced with non-access-chain loads and stores. +// Eliminate the variables as well. +// +// The presence of access chain references and function calls can inhibit +// the above optimization. +// +// Only shader modules with relaxed logical addressing (see opt/instruction.h) +// are currently processed. Currently modules with any extensions enabled are +// not processed. This is left for future work. +// +// This pass is most effective if preceeded by Inlining and +// LocalAccessChainConvert. LocalSingleStoreElim and LocalSingleBlockElim +// will reduce the work that this pass has to do. +Optimizer::PassToken CreateLocalMultiStoreElimPass(); + +// Creates a local access chain conversion pass. +// A local access chain conversion pass identifies all function scope +// variables which are accessed only with loads, stores and access chains +// with constant indices. It then converts all loads and stores of such +// variables into equivalent sequences of loads, stores, extracts and inserts. +// +// This pass only processes entry point functions. It currently only converts +// non-nested, non-ptr access chains. It does not process modules with +// non-32-bit integer types present. Optional memory access options on loads +// and stores are ignored as we are only processing function scope variables. +// +// This pass unifies access to these variables to a single mode and simplifies +// subsequent analysis and elimination of these variables along with their +// loads and stores allowing values to propagate to their points of use where +// possible. +Optimizer::PassToken CreateLocalAccessChainConvertPass(); + +// Creates a local single store elimination pass. +// For each entry point function, this pass eliminates loads and stores for +// function scope variable that are stored to only once, where possible. Only +// whole variable loads and stores are eliminated; access-chain references are +// not optimized. Replace all loads of such variables with the value that is +// stored and eliminate any resulting dead code. +// +// Currently, the presence of access chains and function calls can inhibit this +// pass, however the Inlining and LocalAccessChainConvert passes can make it +// more effective. In additional, many non-load/store memory operations are +// not supported and will prohibit optimization of a function. Support of +// these operations are future work. +// +// Only shader modules with relaxed logical addressing (see opt/instruction.h) +// are currently processed. +// +// This pass will reduce the work needed to be done by LocalSingleBlockElim +// and LocalMultiStoreElim and can improve the effectiveness of other passes +// such as DeadBranchElimination which depend on values for their analysis. +Optimizer::PassToken CreateLocalSingleStoreElimPass(); + +// Creates an insert/extract elimination pass. +// This pass processes each entry point function in the module, searching for +// extracts on a sequence of inserts. It further searches the sequence for an +// insert with indices identical to the extract. If such an insert can be +// found before hitting a conflicting insert, the extract's result id is +// replaced with the id of the values from the insert. +// +// Besides removing extracts this pass enables subsequent dead code elimination +// passes to delete the inserts. This pass performs best after access chains are +// converted to inserts and extracts and local loads and stores are eliminated. +Optimizer::PassToken CreateInsertExtractElimPass(); + +// Creates a dead insert elimination pass. +// This pass processes each entry point function in the module, searching for +// unreferenced inserts into composite types. These are most often unused +// stores to vector components. They are unused because they are never +// referenced, or because there is another insert to the same component between +// the insert and the reference. After removing the inserts, dead code +// elimination is attempted on the inserted values. +// +// This pass performs best after access chains are converted to inserts and +// extracts and local loads and stores are eliminated. While executing this +// pass can be advantageous on its own, it is also advantageous to execute +// this pass after CreateInsertExtractPass() as it will remove any unused +// inserts created by that pass. +Optimizer::PassToken CreateDeadInsertElimPass(); + +// Creates a pass to consolidate uniform references. +// For each entry point function in the module, first change all constant index +// access chain loads into equivalent composite extracts. Then consolidate +// identical uniform loads into one uniform load. Finally, consolidate +// identical uniform extracts into one uniform extract. This may require +// moving a load or extract to a point which dominates all uses. +// +// This pass requires a module to have structured control flow ie shader +// capability. It also requires logical addressing ie Addresses capability +// is not enabled. It also currently does not support any extensions. +// +// This pass currently only optimizes loads with a single index. +Optimizer::PassToken CreateCommonUniformElimPass(); + +// Create aggressive dead code elimination pass +// This pass eliminates unused code from the module. In addition, +// it detects and eliminates code which may have spurious uses but which do +// not contribute to the output of the function. The most common cause of +// such code sequences is summations in loops whose result is no longer used +// due to dead code elimination. This optimization has additional compile +// time cost over standard dead code elimination. +// +// This pass only processes entry point functions. It also only processes +// shaders with relaxed logical addressing (see opt/instruction.h). It +// currently will not process functions with function calls. Unreachable +// functions are deleted. +// +// This pass will be made more effective by first running passes that remove +// dead control flow and inlines function calls. +// +// This pass can be especially useful after running Local Access Chain +// Conversion, which tends to cause cycles of dead code to be left after +// Store/Load elimination passes are completed. These cycles cannot be +// eliminated with standard dead code elimination. +Optimizer::PassToken CreateAggressiveDCEPass(); + +// Creates a compact ids pass. +// The pass remaps result ids to a compact and gapless range starting from %1. +Optimizer::PassToken CreateCompactIdsPass(); + +// Creates a remove duplicate pass. +// This pass removes various duplicates: +// * duplicate capabilities; +// * duplicate extended instruction imports; +// * duplicate types; +// * duplicate decorations. +Optimizer::PassToken CreateRemoveDuplicatesPass(); + +// Creates a CFG cleanup pass. +// This pass removes cruft from the control flow graph of functions that are +// reachable from entry points and exported functions. It currently includes the +// following functionality: +// +// - Removal of unreachable basic blocks. +Optimizer::PassToken CreateCFGCleanupPass(); + +// Create dead variable elimination pass. +// This pass will delete module scope variables, along with their decorations, +// that are not referenced. +Optimizer::PassToken CreateDeadVariableEliminationPass(); + +// Create merge return pass. +// This pass replaces all returns with unconditional branches to a new block +// containing a return. If necessary, this new block will contain a PHI node to +// select the correct return value. +// +// This pass does not consider unreachable code, nor does it perform any other +// optimizations. +// +// This pass does not currently support structured control flow. It bails out if +// the shader capability is detected. +Optimizer::PassToken CreateMergeReturnPass(); + +// Create value numbering pass. +// This pass will look for instructions in the same basic block that compute the +// same value, and remove the redundant ones. +Optimizer::PassToken CreateLocalRedundancyEliminationPass(); + +// Create LICM pass. +// This pass will look for invariant instructions inside loops and hoist them to +// the loops preheader. +Optimizer::PassToken CreateLoopInvariantCodeMotionPass(); + +// Create global value numbering pass. +// This pass will look for instructions where the same value is computed on all +// paths leading to the instruction. Those instructions are deleted. +Optimizer::PassToken CreateRedundancyEliminationPass(); + +// Create scalar replacement pass. +// This pass replaces composite function scope variables with variables for each +// element if those elements are accessed individually. +Optimizer::PassToken CreateScalarReplacementPass(); + +// Create a private to local pass. +// This pass looks for variables delcared in the private storage class that are +// used in only one function. Those variables are moved to the function storage +// class in the function that they are used. +Optimizer::PassToken CreatePrivateToLocalPass(); + +// Creates a conditional constant propagation (CCP) pass. +// This pass implements the SSA-CCP algorithm in +// +// Constant propagation with conditional branches, +// Wegman and Zadeck, ACM TOPLAS 13(2):181-210. +// +// Constant values in expressions and conditional jumps are folded and +// simplified. This may reduce code size by removing never executed jump targets +// and computations with constant operands. +Optimizer::PassToken CreateCCPPass(); + +// Creates a workaround driver bugs pass. This pass attempts to work around +// a known driver bug (issue #1209) by identifying the bad code sequences and +// rewriting them. +// +// Current workaround: Avoid OpUnreachable instructions in loops. +Optimizer::PassToken CreateWorkaround1209Pass(); + +// Creates a pass that converts if-then-else like assignments into OpSelect. +Optimizer::PassToken CreateIfConversionPass(); + +// Creates a pass that will replace instructions that are not valid for the +// current shader stage by constants. Has no effect on non-shader modules. +Optimizer::PassToken CreateReplaceInvalidOpcodePass(); + +// Creates a pass that simplifies instructions using the instruction folder. +Optimizer::PassToken CreateSimplificationPass(); + +// Create loop unroller pass. +// Creates a pass to fully unroll loops which have the "Unroll" loop control +// mask set. The loops must meet a specific criteria in order to be unrolled +// safely this criteria is checked before doing the unroll by the +// LoopUtils::CanPerformUnroll method. Any loop that does not meet the criteria +// won't be unrolled. See CanPerformUnroll LoopUtils.h for more information. +Optimizer::PassToken CreateLoopFullyUnrollPass(); + +} // namespace spvtools + +#endif // SPIRV_TOOLS_OPTIMIZER_HPP_ diff --git a/lib/libSPIRV-Tools-opt.a b/lib/libSPIRV-Tools-opt.a new file mode 100644 index 0000000000000000000000000000000000000000..72e7ee526d391071c8d971a93fb1bf78f8cbbda4 GIT binary patch literal 3914900 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