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 // C++ RAII wrapper around the C context object spv_context. 35 class Context { 36 public: 37 // Constructs a context targeting the given environment |env|. 38 // 39 // See specific API calls for how the target environment is interpeted 40 // (particularly assembly and validation). 41 // 42 // The constructed instance will have an empty message consumer, which just 43 // ignores all messages from the library. Use SetMessageConsumer() to supply 44 // one if messages are of concern. 45 explicit Context(spv_target_env env); 46 47 // Enables move constructor/assignment operations. 48 Context(Context&& other); 49 Context& operator=(Context&& other); 50 51 // Disables copy constructor/assignment operations. 52 Context(const Context&) = delete; 53 Context& operator=(const Context&) = delete; 54 55 // Destructs this instance. 56 ~Context(); 57 58 // Sets the message consumer to the given |consumer|. The |consumer| will be 59 // invoked once for each message communicated from the library. 60 void SetMessageConsumer(MessageConsumer consumer); 61 62 // Returns the underlying spv_context. 63 spv_context& CContext(); 64 const spv_context& CContext() const; 65 66 private: 67 spv_context context_; 68 }; 69 70 // A RAII wrapper around a validator options object. 71 class ValidatorOptions { 72 public: ValidatorOptions()73 ValidatorOptions() : options_(spvValidatorOptionsCreate()) {} ~ValidatorOptions()74 ~ValidatorOptions() { spvValidatorOptionsDestroy(options_); } 75 // Allow implicit conversion to the underlying object. operator spv_validator_options() const76 operator spv_validator_options() const { return options_; } 77 78 // Sets a limit. SetUniversalLimit(spv_validator_limit limit_type,uint32_t limit)79 void SetUniversalLimit(spv_validator_limit limit_type, uint32_t limit) { 80 spvValidatorOptionsSetUniversalLimit(options_, limit_type, limit); 81 } 82 SetRelaxStructStore(bool val)83 void SetRelaxStructStore(bool val) { 84 spvValidatorOptionsSetRelaxStoreStruct(options_, val); 85 } 86 87 // Enables VK_KHR_relaxed_block_layout when validating standard 88 // uniform/storage buffer/push-constant layout. If true, disables 89 // scalar block layout rules. SetRelaxBlockLayout(bool val)90 void SetRelaxBlockLayout(bool val) { 91 spvValidatorOptionsSetRelaxBlockLayout(options_, val); 92 } 93 94 // Enables VK_KHR_uniform_buffer_standard_layout when validating standard 95 // uniform layout. If true, disables scalar block layout rules. SetUniformBufferStandardLayout(bool val)96 void SetUniformBufferStandardLayout(bool val) { 97 spvValidatorOptionsSetUniformBufferStandardLayout(options_, val); 98 } 99 100 // Enables VK_EXT_scalar_block_layout when validating standard 101 // uniform/storage buffer/push-constant layout. If true, disables 102 // relaxed block layout rules. SetScalarBlockLayout(bool val)103 void SetScalarBlockLayout(bool val) { 104 spvValidatorOptionsSetScalarBlockLayout(options_, val); 105 } 106 107 // Enables scalar layout when validating Workgroup blocks. See 108 // VK_KHR_workgroup_memory_explicit_layout. SetWorkgroupScalarBlockLayout(bool val)109 void SetWorkgroupScalarBlockLayout(bool val) { 110 spvValidatorOptionsSetWorkgroupScalarBlockLayout(options_, val); 111 } 112 113 // Skips validating standard uniform/storage buffer/push-constant layout. SetSkipBlockLayout(bool val)114 void SetSkipBlockLayout(bool val) { 115 spvValidatorOptionsSetSkipBlockLayout(options_, val); 116 } 117 118 // Records whether or not the validator should relax the rules on pointer 119 // usage in logical addressing mode. 120 // 121 // When relaxed, it will allow the following usage cases of pointers: 122 // 1) OpVariable allocating an object whose type is a pointer type 123 // 2) OpReturnValue returning a pointer value SetRelaxLogicalPointer(bool val)124 void SetRelaxLogicalPointer(bool val) { 125 spvValidatorOptionsSetRelaxLogicalPointer(options_, val); 126 } 127 128 // Records whether or not the validator should relax the rules because it is 129 // expected that the optimizations will make the code legal. 130 // 131 // When relaxed, it will allow the following: 132 // 1) It will allow relaxed logical pointers. Setting this option will also 133 // set that option. 134 // 2) Pointers that are pass as parameters to function calls do not have to 135 // match the storage class of the formal parameter. 136 // 3) Pointers that are actaul parameters on function calls do not have to 137 // point to the same type pointed as the formal parameter. The types just 138 // need to logically match. 139 // 4) GLSLstd450 Interpolate* instructions can have a load of an interpolant 140 // for a first argument. SetBeforeHlslLegalization(bool val)141 void SetBeforeHlslLegalization(bool val) { 142 spvValidatorOptionsSetBeforeHlslLegalization(options_, val); 143 } 144 145 private: 146 spv_validator_options options_; 147 }; 148 149 // A C++ wrapper around an optimization options object. 150 class OptimizerOptions { 151 public: OptimizerOptions()152 OptimizerOptions() : options_(spvOptimizerOptionsCreate()) {} ~OptimizerOptions()153 ~OptimizerOptions() { spvOptimizerOptionsDestroy(options_); } 154 155 // Allow implicit conversion to the underlying object. operator spv_optimizer_options() const156 operator spv_optimizer_options() const { return options_; } 157 158 // Records whether or not the optimizer should run the validator before 159 // optimizing. If |run| is true, the validator will be run. set_run_validator(bool run)160 void set_run_validator(bool run) { 161 spvOptimizerOptionsSetRunValidator(options_, run); 162 } 163 164 // Records the validator options that should be passed to the validator if it 165 // is run. set_validator_options(const ValidatorOptions & val_options)166 void set_validator_options(const ValidatorOptions& val_options) { 167 spvOptimizerOptionsSetValidatorOptions(options_, val_options); 168 } 169 170 // Records the maximum possible value for the id bound. set_max_id_bound(uint32_t new_bound)171 void set_max_id_bound(uint32_t new_bound) { 172 spvOptimizerOptionsSetMaxIdBound(options_, new_bound); 173 } 174 175 // Records whether all bindings within the module should be preserved. set_preserve_bindings(bool preserve_bindings)176 void set_preserve_bindings(bool preserve_bindings) { 177 spvOptimizerOptionsSetPreserveBindings(options_, preserve_bindings); 178 } 179 180 // Records whether all specialization constants within the module 181 // should be preserved. set_preserve_spec_constants(bool preserve_spec_constants)182 void set_preserve_spec_constants(bool preserve_spec_constants) { 183 spvOptimizerOptionsSetPreserveSpecConstants(options_, 184 preserve_spec_constants); 185 } 186 187 private: 188 spv_optimizer_options options_; 189 }; 190 191 // A C++ wrapper around a reducer options object. 192 class ReducerOptions { 193 public: ReducerOptions()194 ReducerOptions() : options_(spvReducerOptionsCreate()) {} ~ReducerOptions()195 ~ReducerOptions() { spvReducerOptionsDestroy(options_); } 196 197 // Allow implicit conversion to the underlying object. operator spv_reducer_options() const198 operator spv_reducer_options() const { // NOLINT(google-explicit-constructor) 199 return options_; 200 } 201 202 // See spvReducerOptionsSetStepLimit. set_step_limit(uint32_t step_limit)203 void set_step_limit(uint32_t step_limit) { 204 spvReducerOptionsSetStepLimit(options_, step_limit); 205 } 206 207 // See spvReducerOptionsSetFailOnValidationError. set_fail_on_validation_error(bool fail_on_validation_error)208 void set_fail_on_validation_error(bool fail_on_validation_error) { 209 spvReducerOptionsSetFailOnValidationError(options_, 210 fail_on_validation_error); 211 } 212 213 // See spvReducerOptionsSetTargetFunction. set_target_function(uint32_t target_function)214 void set_target_function(uint32_t target_function) { 215 spvReducerOptionsSetTargetFunction(options_, target_function); 216 } 217 218 private: 219 spv_reducer_options options_; 220 }; 221 222 // A C++ wrapper around a fuzzer options object. 223 class FuzzerOptions { 224 public: FuzzerOptions()225 FuzzerOptions() : options_(spvFuzzerOptionsCreate()) {} ~FuzzerOptions()226 ~FuzzerOptions() { spvFuzzerOptionsDestroy(options_); } 227 228 // Allow implicit conversion to the underlying object. operator spv_fuzzer_options() const229 operator spv_fuzzer_options() const { // NOLINT(google-explicit-constructor) 230 return options_; 231 } 232 233 // See spvFuzzerOptionsEnableReplayValidation. enable_replay_validation()234 void enable_replay_validation() { 235 spvFuzzerOptionsEnableReplayValidation(options_); 236 } 237 238 // See spvFuzzerOptionsSetRandomSeed. set_random_seed(uint32_t seed)239 void set_random_seed(uint32_t seed) { 240 spvFuzzerOptionsSetRandomSeed(options_, seed); 241 } 242 243 // See spvFuzzerOptionsSetReplayRange. set_replay_range(int32_t replay_range)244 void set_replay_range(int32_t replay_range) { 245 spvFuzzerOptionsSetReplayRange(options_, replay_range); 246 } 247 248 // See spvFuzzerOptionsSetShrinkerStepLimit. set_shrinker_step_limit(uint32_t shrinker_step_limit)249 void set_shrinker_step_limit(uint32_t shrinker_step_limit) { 250 spvFuzzerOptionsSetShrinkerStepLimit(options_, shrinker_step_limit); 251 } 252 253 // See spvFuzzerOptionsEnableFuzzerPassValidation. enable_fuzzer_pass_validation()254 void enable_fuzzer_pass_validation() { 255 spvFuzzerOptionsEnableFuzzerPassValidation(options_); 256 } 257 258 // See spvFuzzerOptionsEnableAllPasses. enable_all_passes()259 void enable_all_passes() { spvFuzzerOptionsEnableAllPasses(options_); } 260 261 private: 262 spv_fuzzer_options options_; 263 }; 264 265 // C++ interface for SPIRV-Tools functionalities. It wraps the context 266 // (including target environment and the corresponding SPIR-V grammar) and 267 // provides methods for assembling, disassembling, and validating. 268 // 269 // Instances of this class provide basic thread-safety guarantee. 270 class SpirvTools { 271 public: 272 enum { 273 // Default assembling option used by assemble(): 274 kDefaultAssembleOption = SPV_TEXT_TO_BINARY_OPTION_NONE, 275 276 // Default disassembling option used by Disassemble(): 277 // * Avoid prefix comments from decoding the SPIR-V module header, and 278 // * Use friendly names for variables. 279 kDefaultDisassembleOption = SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | 280 SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES 281 }; 282 283 // Constructs an instance targeting the given environment |env|. 284 // 285 // The constructed instance will have an empty message consumer, which just 286 // ignores all messages from the library. Use SetMessageConsumer() to supply 287 // one if messages are of concern. 288 explicit SpirvTools(spv_target_env env); 289 290 // Disables copy/move constructor/assignment operations. 291 SpirvTools(const SpirvTools&) = delete; 292 SpirvTools(SpirvTools&&) = delete; 293 SpirvTools& operator=(const SpirvTools&) = delete; 294 SpirvTools& operator=(SpirvTools&&) = delete; 295 296 // Destructs this instance. 297 ~SpirvTools(); 298 299 // Sets the message consumer to the given |consumer|. The |consumer| will be 300 // invoked once for each message communicated from the library. 301 void SetMessageConsumer(MessageConsumer consumer); 302 303 // Assembles the given assembly |text| and writes the result to |binary|. 304 // Returns true on successful assembling. |binary| will be kept untouched if 305 // assembling is unsuccessful. 306 // The SPIR-V binary version is set to the highest version of SPIR-V supported 307 // by the target environment with which this SpirvTools object was created. 308 bool Assemble(const std::string& text, std::vector<uint32_t>* binary, 309 uint32_t options = kDefaultAssembleOption) const; 310 // |text_size| specifies the number of bytes in |text|. A terminating null 311 // character is not required to present in |text| as long as |text| is valid. 312 // The SPIR-V binary version is set to the highest version of SPIR-V supported 313 // by the target environment with which this SpirvTools object was created. 314 bool Assemble(const char* text, size_t text_size, 315 std::vector<uint32_t>* binary, 316 uint32_t options = kDefaultAssembleOption) const; 317 318 // Disassembles the given SPIR-V |binary| with the given |options| and writes 319 // the assembly to |text|. Returns true on successful disassembling. |text| 320 // will be kept untouched if diassembling is unsuccessful. 321 bool Disassemble(const std::vector<uint32_t>& binary, std::string* text, 322 uint32_t options = kDefaultDisassembleOption) const; 323 // |binary_size| specifies the number of words in |binary|. 324 bool Disassemble(const uint32_t* binary, size_t binary_size, 325 std::string* text, 326 uint32_t options = kDefaultDisassembleOption) const; 327 328 // Validates the given SPIR-V |binary|. Returns true if no issues are found. 329 // Otherwise, returns false and communicates issues via the message consumer 330 // registered. 331 // Validates for SPIR-V spec rules for the SPIR-V version named in the 332 // binary's header (at word offset 1). Additionally, if the target 333 // environment is a client API (such as Vulkan 1.1), then validate for that 334 // client API version, to the extent that it is verifiable from data in the 335 // binary itself. 336 bool Validate(const std::vector<uint32_t>& binary) const; 337 // Like the previous overload, but provides the binary as a pointer and size: 338 // |binary_size| specifies the number of words in |binary|. 339 // Validates for SPIR-V spec rules for the SPIR-V version named in the 340 // binary's header (at word offset 1). Additionally, if the target 341 // environment is a client API (such as Vulkan 1.1), then validate for that 342 // client API version, to the extent that it is verifiable from data in the 343 // binary itself. 344 bool Validate(const uint32_t* binary, size_t binary_size) const; 345 // Like the previous overload, but takes an options object. 346 // Validates for SPIR-V spec rules for the SPIR-V version named in the 347 // binary's header (at word offset 1). Additionally, if the target 348 // environment is a client API (such as Vulkan 1.1), then validate for that 349 // client API version, to the extent that it is verifiable from data in the 350 // binary itself, or in the validator options. 351 bool Validate(const uint32_t* binary, size_t binary_size, 352 spv_validator_options options) const; 353 354 // Was this object successfully constructed. 355 bool IsValid() const; 356 357 private: 358 struct Impl; // Opaque struct for holding the data fields used by this class. 359 std::unique_ptr<Impl> impl_; // Unique pointer to implementation data. 360 }; 361 362 } // namespace spvtools 363 364 #endif // INCLUDE_SPIRV_TOOLS_LIBSPIRV_HPP_ 365