1 // Copyright (c) 2016 Google Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef INCLUDE_SPIRV_TOOLS_LIBSPIRV_HPP_ 16 #define INCLUDE_SPIRV_TOOLS_LIBSPIRV_HPP_ 17 18 #include <functional> 19 #include <memory> 20 #include <string> 21 #include <vector> 22 23 #include "spirv-tools/libspirv.h" 24 25 namespace spvtools { 26 27 // Message consumer. The C strings for source and message are only alive for the 28 // specific invocation. 29 using MessageConsumer = std::function<void( 30 spv_message_level_t /* level */, const char* /* source */, 31 const spv_position_t& /* position */, const char* /* message */ 32 )>; 33 34 using HeaderParser = std::function<spv_result_t( 35 const spv_endianness_t endianess, const spv_parsed_header_t& instruction)>; 36 using InstructionParser = 37 std::function<spv_result_t(const spv_parsed_instruction_t& instruction)>; 38 39 // C++ RAII wrapper around the C context object spv_context. 40 class Context { 41 public: 42 // Constructs a context targeting the given environment |env|. 43 // 44 // See specific API calls for how the target environment is interpreted 45 // (particularly assembly and validation). 46 // 47 // The constructed instance will have an empty message consumer, which just 48 // ignores all messages from the library. Use SetMessageConsumer() to supply 49 // one if messages are of concern. 50 explicit Context(spv_target_env env); 51 52 // Enables move constructor/assignment operations. 53 Context(Context&& other); 54 Context& operator=(Context&& other); 55 56 // Disables copy constructor/assignment operations. 57 Context(const Context&) = delete; 58 Context& operator=(const Context&) = delete; 59 60 // Destructs this instance. 61 ~Context(); 62 63 // Sets the message consumer to the given |consumer|. The |consumer| will be 64 // invoked once for each message communicated from the library. 65 void SetMessageConsumer(MessageConsumer consumer); 66 67 // Returns the underlying spv_context. 68 spv_context& CContext(); 69 const spv_context& CContext() const; 70 71 private: 72 spv_context context_; 73 }; 74 75 // A RAII wrapper around a validator options object. 76 class ValidatorOptions { 77 public: ValidatorOptions()78 ValidatorOptions() : options_(spvValidatorOptionsCreate()) {} ~ValidatorOptions()79 ~ValidatorOptions() { spvValidatorOptionsDestroy(options_); } 80 // Allow implicit conversion to the underlying object. operator spv_validator_options() const81 operator spv_validator_options() const { return options_; } 82 83 // Sets a limit. SetUniversalLimit(spv_validator_limit limit_type,uint32_t limit)84 void SetUniversalLimit(spv_validator_limit limit_type, uint32_t limit) { 85 spvValidatorOptionsSetUniversalLimit(options_, limit_type, limit); 86 } 87 SetRelaxStructStore(bool val)88 void SetRelaxStructStore(bool val) { 89 spvValidatorOptionsSetRelaxStoreStruct(options_, val); 90 } 91 92 // Enables VK_KHR_relaxed_block_layout when validating standard 93 // uniform/storage buffer/push-constant layout. If true, disables 94 // scalar block layout rules. SetRelaxBlockLayout(bool val)95 void SetRelaxBlockLayout(bool val) { 96 spvValidatorOptionsSetRelaxBlockLayout(options_, val); 97 } 98 99 // Enables VK_KHR_uniform_buffer_standard_layout when validating standard 100 // uniform layout. If true, disables scalar block layout rules. SetUniformBufferStandardLayout(bool val)101 void SetUniformBufferStandardLayout(bool val) { 102 spvValidatorOptionsSetUniformBufferStandardLayout(options_, val); 103 } 104 105 // Enables VK_EXT_scalar_block_layout when validating standard 106 // uniform/storage buffer/push-constant layout. If true, disables 107 // relaxed block layout rules. SetScalarBlockLayout(bool val)108 void SetScalarBlockLayout(bool val) { 109 spvValidatorOptionsSetScalarBlockLayout(options_, val); 110 } 111 112 // Enables scalar layout when validating Workgroup blocks. See 113 // VK_KHR_workgroup_memory_explicit_layout. SetWorkgroupScalarBlockLayout(bool val)114 void SetWorkgroupScalarBlockLayout(bool val) { 115 spvValidatorOptionsSetWorkgroupScalarBlockLayout(options_, val); 116 } 117 118 // Skips validating standard uniform/storage buffer/push-constant layout. SetSkipBlockLayout(bool val)119 void SetSkipBlockLayout(bool val) { 120 spvValidatorOptionsSetSkipBlockLayout(options_, val); 121 } 122 123 // Enables LocalSizeId decorations where the environment would not otherwise 124 // allow them. SetAllowLocalSizeId(bool val)125 void SetAllowLocalSizeId(bool val) { 126 spvValidatorOptionsSetAllowLocalSizeId(options_, val); 127 } 128 129 // Records whether or not the validator should relax the rules on pointer 130 // usage in logical addressing mode. 131 // 132 // When relaxed, it will allow the following usage cases of pointers: 133 // 1) OpVariable allocating an object whose type is a pointer type 134 // 2) OpReturnValue returning a pointer value SetRelaxLogicalPointer(bool val)135 void SetRelaxLogicalPointer(bool val) { 136 spvValidatorOptionsSetRelaxLogicalPointer(options_, val); 137 } 138 139 // Records whether or not the validator should relax the rules because it is 140 // expected that the optimizations will make the code legal. 141 // 142 // When relaxed, it will allow the following: 143 // 1) It will allow relaxed logical pointers. Setting this option will also 144 // set that option. 145 // 2) Pointers that are pass as parameters to function calls do not have to 146 // match the storage class of the formal parameter. 147 // 3) Pointers that are actual parameters on function calls do not have to 148 // point to the same type pointed as the formal parameter. The types just 149 // need to logically match. 150 // 4) GLSLstd450 Interpolate* instructions can have a load of an interpolant 151 // for a first argument. SetBeforeHlslLegalization(bool val)152 void SetBeforeHlslLegalization(bool val) { 153 spvValidatorOptionsSetBeforeHlslLegalization(options_, val); 154 } 155 156 // Whether friendly names should be used in validation error messages. SetFriendlyNames(bool val)157 void SetFriendlyNames(bool val) { 158 spvValidatorOptionsSetFriendlyNames(options_, val); 159 } 160 161 private: 162 spv_validator_options options_; 163 }; 164 165 // A C++ wrapper around an optimization options object. 166 class OptimizerOptions { 167 public: OptimizerOptions()168 OptimizerOptions() : options_(spvOptimizerOptionsCreate()) {} ~OptimizerOptions()169 ~OptimizerOptions() { spvOptimizerOptionsDestroy(options_); } 170 171 // Allow implicit conversion to the underlying object. operator spv_optimizer_options() const172 operator spv_optimizer_options() const { return options_; } 173 174 // Records whether or not the optimizer should run the validator before 175 // optimizing. If |run| is true, the validator will be run. set_run_validator(bool run)176 void set_run_validator(bool run) { 177 spvOptimizerOptionsSetRunValidator(options_, run); 178 } 179 180 // Records the validator options that should be passed to the validator if it 181 // is run. set_validator_options(const ValidatorOptions & val_options)182 void set_validator_options(const ValidatorOptions& val_options) { 183 spvOptimizerOptionsSetValidatorOptions(options_, val_options); 184 } 185 186 // Records the maximum possible value for the id bound. set_max_id_bound(uint32_t new_bound)187 void set_max_id_bound(uint32_t new_bound) { 188 spvOptimizerOptionsSetMaxIdBound(options_, new_bound); 189 } 190 191 // Records whether all bindings within the module should be preserved. set_preserve_bindings(bool preserve_bindings)192 void set_preserve_bindings(bool preserve_bindings) { 193 spvOptimizerOptionsSetPreserveBindings(options_, preserve_bindings); 194 } 195 196 // Records whether all specialization constants within the module 197 // should be preserved. set_preserve_spec_constants(bool preserve_spec_constants)198 void set_preserve_spec_constants(bool preserve_spec_constants) { 199 spvOptimizerOptionsSetPreserveSpecConstants(options_, 200 preserve_spec_constants); 201 } 202 203 private: 204 spv_optimizer_options options_; 205 }; 206 207 // A C++ wrapper around a reducer options object. 208 class ReducerOptions { 209 public: ReducerOptions()210 ReducerOptions() : options_(spvReducerOptionsCreate()) {} ~ReducerOptions()211 ~ReducerOptions() { spvReducerOptionsDestroy(options_); } 212 213 // Allow implicit conversion to the underlying object. operator spv_reducer_options() const214 operator spv_reducer_options() const { // NOLINT(google-explicit-constructor) 215 return options_; 216 } 217 218 // See spvReducerOptionsSetStepLimit. set_step_limit(uint32_t step_limit)219 void set_step_limit(uint32_t step_limit) { 220 spvReducerOptionsSetStepLimit(options_, step_limit); 221 } 222 223 // See spvReducerOptionsSetFailOnValidationError. set_fail_on_validation_error(bool fail_on_validation_error)224 void set_fail_on_validation_error(bool fail_on_validation_error) { 225 spvReducerOptionsSetFailOnValidationError(options_, 226 fail_on_validation_error); 227 } 228 229 // See spvReducerOptionsSetTargetFunction. set_target_function(uint32_t target_function)230 void set_target_function(uint32_t target_function) { 231 spvReducerOptionsSetTargetFunction(options_, target_function); 232 } 233 234 private: 235 spv_reducer_options options_; 236 }; 237 238 // A C++ wrapper around a fuzzer options object. 239 class FuzzerOptions { 240 public: FuzzerOptions()241 FuzzerOptions() : options_(spvFuzzerOptionsCreate()) {} ~FuzzerOptions()242 ~FuzzerOptions() { spvFuzzerOptionsDestroy(options_); } 243 244 // Allow implicit conversion to the underlying object. operator spv_fuzzer_options() const245 operator spv_fuzzer_options() const { // NOLINT(google-explicit-constructor) 246 return options_; 247 } 248 249 // See spvFuzzerOptionsEnableReplayValidation. enable_replay_validation()250 void enable_replay_validation() { 251 spvFuzzerOptionsEnableReplayValidation(options_); 252 } 253 254 // See spvFuzzerOptionsSetRandomSeed. set_random_seed(uint32_t seed)255 void set_random_seed(uint32_t seed) { 256 spvFuzzerOptionsSetRandomSeed(options_, seed); 257 } 258 259 // See spvFuzzerOptionsSetReplayRange. set_replay_range(int32_t replay_range)260 void set_replay_range(int32_t replay_range) { 261 spvFuzzerOptionsSetReplayRange(options_, replay_range); 262 } 263 264 // See spvFuzzerOptionsSetShrinkerStepLimit. set_shrinker_step_limit(uint32_t shrinker_step_limit)265 void set_shrinker_step_limit(uint32_t shrinker_step_limit) { 266 spvFuzzerOptionsSetShrinkerStepLimit(options_, shrinker_step_limit); 267 } 268 269 // See spvFuzzerOptionsEnableFuzzerPassValidation. enable_fuzzer_pass_validation()270 void enable_fuzzer_pass_validation() { 271 spvFuzzerOptionsEnableFuzzerPassValidation(options_); 272 } 273 274 // See spvFuzzerOptionsEnableAllPasses. enable_all_passes()275 void enable_all_passes() { spvFuzzerOptionsEnableAllPasses(options_); } 276 277 private: 278 spv_fuzzer_options options_; 279 }; 280 281 // C++ interface for SPIRV-Tools functionalities. It wraps the context 282 // (including target environment and the corresponding SPIR-V grammar) and 283 // provides methods for assembling, disassembling, and validating. 284 // 285 // Instances of this class provide basic thread-safety guarantee. 286 class SpirvTools { 287 public: 288 enum { 289 // Default assembling option used by assemble(): 290 kDefaultAssembleOption = SPV_TEXT_TO_BINARY_OPTION_NONE, 291 292 // Default disassembling option used by Disassemble(): 293 // * Avoid prefix comments from decoding the SPIR-V module header, and 294 // * Use friendly names for variables. 295 kDefaultDisassembleOption = SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | 296 SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES 297 }; 298 299 // Constructs an instance targeting the given environment |env|. 300 // 301 // The constructed instance will have an empty message consumer, which just 302 // ignores all messages from the library. Use SetMessageConsumer() to supply 303 // one if messages are of concern. 304 explicit SpirvTools(spv_target_env env); 305 306 // Disables copy/move constructor/assignment operations. 307 SpirvTools(const SpirvTools&) = delete; 308 SpirvTools(SpirvTools&&) = delete; 309 SpirvTools& operator=(const SpirvTools&) = delete; 310 SpirvTools& operator=(SpirvTools&&) = delete; 311 312 // Destructs this instance. 313 ~SpirvTools(); 314 315 // Sets the message consumer to the given |consumer|. The |consumer| will be 316 // invoked once for each message communicated from the library. 317 void SetMessageConsumer(MessageConsumer consumer); 318 319 // Assembles the given assembly |text| and writes the result to |binary|. 320 // Returns true on successful assembling. |binary| will be kept untouched if 321 // assembling is unsuccessful. 322 // The SPIR-V binary version is set to the highest version of SPIR-V supported 323 // by the target environment with which this SpirvTools object was created. 324 bool Assemble(const std::string& text, std::vector<uint32_t>* binary, 325 uint32_t options = kDefaultAssembleOption) const; 326 // |text_size| specifies the number of bytes in |text|. A terminating null 327 // character is not required to present in |text| as long as |text| is valid. 328 // The SPIR-V binary version is set to the highest version of SPIR-V supported 329 // by the target environment with which this SpirvTools object was created. 330 bool Assemble(const char* text, size_t text_size, 331 std::vector<uint32_t>* binary, 332 uint32_t options = kDefaultAssembleOption) const; 333 334 // Disassembles the given SPIR-V |binary| with the given |options| and writes 335 // the assembly to |text|. Returns true on successful disassembling. |text| 336 // will be kept untouched if diassembling is unsuccessful. 337 bool Disassemble(const std::vector<uint32_t>& binary, std::string* text, 338 uint32_t options = kDefaultDisassembleOption) const; 339 // |binary_size| specifies the number of words in |binary|. 340 bool Disassemble(const uint32_t* binary, size_t binary_size, 341 std::string* text, 342 uint32_t options = kDefaultDisassembleOption) const; 343 344 // Parses a SPIR-V binary, specified as counted sequence of 32-bit words. 345 // Parsing feedback is provided via two callbacks provided as std::function. 346 // In a valid parse the parsed-header callback is called once, and 347 // then the parsed-instruction callback is called once for each instruction 348 // in the stream. 349 // Returns true on successful parsing. 350 // If diagnostic is non-null, a diagnostic is emitted on failed parsing. 351 // If diagnostic is null the context's message consumer 352 // will be used to emit any errors. If a callback returns anything other than 353 // SPV_SUCCESS, then that status code is returned, no further callbacks are 354 // issued, and no additional diagnostics are emitted. 355 // This is a wrapper around the C API spvBinaryParse. 356 bool Parse(const std::vector<uint32_t>& binary, 357 const HeaderParser& header_parser, 358 const InstructionParser& instruction_parser, 359 spv_diagnostic* diagnostic = nullptr); 360 361 // Validates the given SPIR-V |binary|. Returns true if no issues are found. 362 // Otherwise, returns false and communicates issues via the message consumer 363 // registered. 364 // Validates for SPIR-V spec rules for the SPIR-V version named in the 365 // binary's header (at word offset 1). Additionally, if the target 366 // environment is a client API (such as Vulkan 1.1), then validate for that 367 // client API version, to the extent that it is verifiable from data in the 368 // binary itself. 369 bool Validate(const std::vector<uint32_t>& binary) const; 370 // Like the previous overload, but provides the binary as a pointer and size: 371 // |binary_size| specifies the number of words in |binary|. 372 // Validates for SPIR-V spec rules for the SPIR-V version named in the 373 // binary's header (at word offset 1). Additionally, if the target 374 // environment is a client API (such as Vulkan 1.1), then validate for that 375 // client API version, to the extent that it is verifiable from data in the 376 // binary itself. 377 bool Validate(const uint32_t* binary, size_t binary_size) const; 378 // Like the previous overload, but takes an options object. 379 // Validates for SPIR-V spec rules for the SPIR-V version named in the 380 // binary's header (at word offset 1). Additionally, if the target 381 // environment is a client API (such as Vulkan 1.1), then validate for that 382 // client API version, to the extent that it is verifiable from data in the 383 // binary itself, or in the validator options. 384 bool Validate(const uint32_t* binary, size_t binary_size, 385 spv_validator_options options) const; 386 387 // Was this object successfully constructed. 388 bool IsValid() const; 389 390 private: 391 struct Impl; // Opaque struct for holding the data fields used by this class. 392 std::unique_ptr<Impl> impl_; // Unique pointer to implementation data. 393 }; 394 395 } // namespace spvtools 396 397 #endif // INCLUDE_SPIRV_TOOLS_LIBSPIRV_HPP_ 398