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