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 interpreted 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 // Enables LocalSizeId decorations where the environment would not otherwise 119 // allow them. SetAllowLocalSizeId(bool val)120 void SetAllowLocalSizeId(bool val) { 121 spvValidatorOptionsSetAllowLocalSizeId(options_, val); 122 } 123 124 // Records whether or not the validator should relax the rules on pointer 125 // usage in logical addressing mode. 126 // 127 // When relaxed, it will allow the following usage cases of pointers: 128 // 1) OpVariable allocating an object whose type is a pointer type 129 // 2) OpReturnValue returning a pointer value SetRelaxLogicalPointer(bool val)130 void SetRelaxLogicalPointer(bool val) { 131 spvValidatorOptionsSetRelaxLogicalPointer(options_, val); 132 } 133 134 // Records whether or not the validator should relax the rules because it is 135 // expected that the optimizations will make the code legal. 136 // 137 // When relaxed, it will allow the following: 138 // 1) It will allow relaxed logical pointers. Setting this option will also 139 // set that option. 140 // 2) Pointers that are pass as parameters to function calls do not have to 141 // match the storage class of the formal parameter. 142 // 3) Pointers that are actual parameters on function calls do not have to 143 // point to the same type pointed as the formal parameter. The types just 144 // need to logically match. 145 // 4) GLSLstd450 Interpolate* instructions can have a load of an interpolant 146 // for a first argument. SetBeforeHlslLegalization(bool val)147 void SetBeforeHlslLegalization(bool val) { 148 spvValidatorOptionsSetBeforeHlslLegalization(options_, val); 149 } 150 151 private: 152 spv_validator_options options_; 153 }; 154 155 // A C++ wrapper around an optimization options object. 156 class OptimizerOptions { 157 public: OptimizerOptions()158 OptimizerOptions() : options_(spvOptimizerOptionsCreate()) {} ~OptimizerOptions()159 ~OptimizerOptions() { spvOptimizerOptionsDestroy(options_); } 160 161 // Allow implicit conversion to the underlying object. operator spv_optimizer_options() const162 operator spv_optimizer_options() const { return options_; } 163 164 // Records whether or not the optimizer should run the validator before 165 // optimizing. If |run| is true, the validator will be run. set_run_validator(bool run)166 void set_run_validator(bool run) { 167 spvOptimizerOptionsSetRunValidator(options_, run); 168 } 169 170 // Records the validator options that should be passed to the validator if it 171 // is run. set_validator_options(const ValidatorOptions & val_options)172 void set_validator_options(const ValidatorOptions& val_options) { 173 spvOptimizerOptionsSetValidatorOptions(options_, val_options); 174 } 175 176 // Records the maximum possible value for the id bound. set_max_id_bound(uint32_t new_bound)177 void set_max_id_bound(uint32_t new_bound) { 178 spvOptimizerOptionsSetMaxIdBound(options_, new_bound); 179 } 180 181 // Records whether all bindings within the module should be preserved. set_preserve_bindings(bool preserve_bindings)182 void set_preserve_bindings(bool preserve_bindings) { 183 spvOptimizerOptionsSetPreserveBindings(options_, preserve_bindings); 184 } 185 186 // Records whether all specialization constants within the module 187 // should be preserved. set_preserve_spec_constants(bool preserve_spec_constants)188 void set_preserve_spec_constants(bool preserve_spec_constants) { 189 spvOptimizerOptionsSetPreserveSpecConstants(options_, 190 preserve_spec_constants); 191 } 192 193 private: 194 spv_optimizer_options options_; 195 }; 196 197 // A C++ wrapper around a reducer options object. 198 class ReducerOptions { 199 public: ReducerOptions()200 ReducerOptions() : options_(spvReducerOptionsCreate()) {} ~ReducerOptions()201 ~ReducerOptions() { spvReducerOptionsDestroy(options_); } 202 203 // Allow implicit conversion to the underlying object. operator spv_reducer_options() const204 operator spv_reducer_options() const { // NOLINT(google-explicit-constructor) 205 return options_; 206 } 207 208 // See spvReducerOptionsSetStepLimit. set_step_limit(uint32_t step_limit)209 void set_step_limit(uint32_t step_limit) { 210 spvReducerOptionsSetStepLimit(options_, step_limit); 211 } 212 213 // See spvReducerOptionsSetFailOnValidationError. set_fail_on_validation_error(bool fail_on_validation_error)214 void set_fail_on_validation_error(bool fail_on_validation_error) { 215 spvReducerOptionsSetFailOnValidationError(options_, 216 fail_on_validation_error); 217 } 218 219 // See spvReducerOptionsSetTargetFunction. set_target_function(uint32_t target_function)220 void set_target_function(uint32_t target_function) { 221 spvReducerOptionsSetTargetFunction(options_, target_function); 222 } 223 224 private: 225 spv_reducer_options options_; 226 }; 227 228 // A C++ wrapper around a fuzzer options object. 229 class FuzzerOptions { 230 public: FuzzerOptions()231 FuzzerOptions() : options_(spvFuzzerOptionsCreate()) {} ~FuzzerOptions()232 ~FuzzerOptions() { spvFuzzerOptionsDestroy(options_); } 233 234 // Allow implicit conversion to the underlying object. operator spv_fuzzer_options() const235 operator spv_fuzzer_options() const { // NOLINT(google-explicit-constructor) 236 return options_; 237 } 238 239 // See spvFuzzerOptionsEnableReplayValidation. enable_replay_validation()240 void enable_replay_validation() { 241 spvFuzzerOptionsEnableReplayValidation(options_); 242 } 243 244 // See spvFuzzerOptionsSetRandomSeed. set_random_seed(uint32_t seed)245 void set_random_seed(uint32_t seed) { 246 spvFuzzerOptionsSetRandomSeed(options_, seed); 247 } 248 249 // See spvFuzzerOptionsSetReplayRange. set_replay_range(int32_t replay_range)250 void set_replay_range(int32_t replay_range) { 251 spvFuzzerOptionsSetReplayRange(options_, replay_range); 252 } 253 254 // See spvFuzzerOptionsSetShrinkerStepLimit. set_shrinker_step_limit(uint32_t shrinker_step_limit)255 void set_shrinker_step_limit(uint32_t shrinker_step_limit) { 256 spvFuzzerOptionsSetShrinkerStepLimit(options_, shrinker_step_limit); 257 } 258 259 // See spvFuzzerOptionsEnableFuzzerPassValidation. enable_fuzzer_pass_validation()260 void enable_fuzzer_pass_validation() { 261 spvFuzzerOptionsEnableFuzzerPassValidation(options_); 262 } 263 264 // See spvFuzzerOptionsEnableAllPasses. enable_all_passes()265 void enable_all_passes() { spvFuzzerOptionsEnableAllPasses(options_); } 266 267 private: 268 spv_fuzzer_options options_; 269 }; 270 271 // C++ interface for SPIRV-Tools functionalities. It wraps the context 272 // (including target environment and the corresponding SPIR-V grammar) and 273 // provides methods for assembling, disassembling, and validating. 274 // 275 // Instances of this class provide basic thread-safety guarantee. 276 class SpirvTools { 277 public: 278 enum { 279 // Default assembling option used by assemble(): 280 kDefaultAssembleOption = SPV_TEXT_TO_BINARY_OPTION_NONE, 281 282 // Default disassembling option used by Disassemble(): 283 // * Avoid prefix comments from decoding the SPIR-V module header, and 284 // * Use friendly names for variables. 285 kDefaultDisassembleOption = SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | 286 SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES 287 }; 288 289 // Constructs an instance targeting the given environment |env|. 290 // 291 // The constructed instance will have an empty message consumer, which just 292 // ignores all messages from the library. Use SetMessageConsumer() to supply 293 // one if messages are of concern. 294 explicit SpirvTools(spv_target_env env); 295 296 // Disables copy/move constructor/assignment operations. 297 SpirvTools(const SpirvTools&) = delete; 298 SpirvTools(SpirvTools&&) = delete; 299 SpirvTools& operator=(const SpirvTools&) = delete; 300 SpirvTools& operator=(SpirvTools&&) = delete; 301 302 // Destructs this instance. 303 ~SpirvTools(); 304 305 // Sets the message consumer to the given |consumer|. The |consumer| will be 306 // invoked once for each message communicated from the library. 307 void SetMessageConsumer(MessageConsumer consumer); 308 309 // Assembles the given assembly |text| and writes the result to |binary|. 310 // Returns true on successful assembling. |binary| will be kept untouched if 311 // assembling is unsuccessful. 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 std::string& text, std::vector<uint32_t>* binary, 315 uint32_t options = kDefaultAssembleOption) const; 316 // |text_size| specifies the number of bytes in |text|. A terminating null 317 // character is not required to present in |text| as long as |text| is valid. 318 // The SPIR-V binary version is set to the highest version of SPIR-V supported 319 // by the target environment with which this SpirvTools object was created. 320 bool Assemble(const char* text, size_t text_size, 321 std::vector<uint32_t>* binary, 322 uint32_t options = kDefaultAssembleOption) const; 323 324 // Disassembles the given SPIR-V |binary| with the given |options| and writes 325 // the assembly to |text|. Returns true on successful disassembling. |text| 326 // will be kept untouched if diassembling is unsuccessful. 327 bool Disassemble(const std::vector<uint32_t>& binary, std::string* text, 328 uint32_t options = kDefaultDisassembleOption) const; 329 // |binary_size| specifies the number of words in |binary|. 330 bool Disassemble(const uint32_t* binary, size_t binary_size, 331 std::string* text, 332 uint32_t options = kDefaultDisassembleOption) const; 333 334 // Validates the given SPIR-V |binary|. Returns true if no issues are found. 335 // Otherwise, returns false and communicates issues via the message consumer 336 // registered. 337 // Validates for SPIR-V spec rules for the SPIR-V version named in the 338 // binary's header (at word offset 1). Additionally, if the target 339 // environment is a client API (such as Vulkan 1.1), then validate for that 340 // client API version, to the extent that it is verifiable from data in the 341 // binary itself. 342 bool Validate(const std::vector<uint32_t>& binary) const; 343 // Like the previous overload, but provides the binary as a pointer and size: 344 // |binary_size| specifies the number of words in |binary|. 345 // Validates for SPIR-V spec rules for the SPIR-V version named in the 346 // binary's header (at word offset 1). Additionally, if the target 347 // environment is a client API (such as Vulkan 1.1), then validate for that 348 // client API version, to the extent that it is verifiable from data in the 349 // binary itself. 350 bool Validate(const uint32_t* binary, size_t binary_size) const; 351 // Like the previous overload, but takes an options object. 352 // Validates for SPIR-V spec rules for the SPIR-V version named in the 353 // binary's header (at word offset 1). Additionally, if the target 354 // environment is a client API (such as Vulkan 1.1), then validate for that 355 // client API version, to the extent that it is verifiable from data in the 356 // binary itself, or in the validator options. 357 bool Validate(const uint32_t* binary, size_t binary_size, 358 spv_validator_options options) const; 359 360 // Was this object successfully constructed. 361 bool IsValid() const; 362 363 private: 364 struct Impl; // Opaque struct for holding the data fields used by this class. 365 std::unique_ptr<Impl> impl_; // Unique pointer to implementation data. 366 }; 367 368 } // namespace spvtools 369 370 #endif // INCLUDE_SPIRV_TOOLS_LIBSPIRV_HPP_ 371