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