1// Copyright (c) 2019 Google LLC
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// This file is specifically named spvtools_fuzz.proto so that the string
16// 'spvtools_fuzz' appears in the names of global-scope symbols that protoc
17// generates when targeting C++.  This is to reduce the potential for name
18// clashes with other globally-scoped symbols.
19
20syntax = "proto3";
21
22package spvtools.fuzz.protobufs;
23
24message InstructionDescriptor {
25
26  // Describes an instruction in some block of a function with respect to a
27  // base instruction.
28
29  // The id of an instruction after which the instruction being described is
30  // believed to be located.  It might be the using instruction itself.
31  uint32 base_instruction_result_id = 1;
32
33  // The opcode for the instruction being described.
34  uint32 target_instruction_opcode = 2;
35
36  // The number of matching opcodes to skip over when searching from the base
37  // instruction to the instruction being described.
38  uint32 num_opcodes_to_ignore = 3;
39
40}
41
42message IdUseDescriptor {
43
44  // Describes a use of an id as an input operand to an instruction in some
45  // block of a function.
46
47  // Example:
48  //   - id_of_interest = 42
49  //   - enclosing_instruction = (
50  //         base_instruction_result_id = 50,
51  //         target_instruction_opcode = OpStore
52  //         num_opcodes_to_ignore = 7
53  //     )
54  //   - in_operand_index = 1
55  // represents a use of id 42 as input operand 1 to an OpStore instruction,
56  // such that the OpStore instruction can be found in the same basic block as
57  // the instruction with result id 50, and in particular is the 8th OpStore
58  // instruction found from instruction 50 onwards (i.e. 7 OpStore
59  // instructions are skipped).
60
61  // An id that we would like to be able to find a use of.
62  uint32 id_of_interest = 1;
63
64  // The input operand index at which the use is expected.
65  InstructionDescriptor enclosing_instruction = 2;
66
67  uint32 in_operand_index = 3;
68
69}
70
71message DataDescriptor {
72
73  // Represents a data element that can be accessed from an id, by walking the
74  // type hierarchy via a sequence of 0 or more indices.
75  //
76  // Very similar to a UniformBufferElementDescriptor, except that a
77  // DataDescriptor is rooted at the id of a scalar or composite.
78
79  // The object being accessed - a scalar or composite
80  uint32 object = 1;
81
82  // 0 or more indices, used to index into a composite object
83  repeated uint32 index = 2;
84
85}
86
87message UniformBufferElementDescriptor {
88
89  // Represents a data element inside a uniform buffer.  The element is
90  // specified via (a) the result id of a uniform variable in which the element
91  // is contained, and (b) a series of indices that need to be followed to get
92  // to the element (via fields and array/vector indices).
93  //
94  // Example: suppose there is a uniform variable with descriptor set 7 and
95  // binding 9, and that the uniform variable has the following type (using
96  // GLSL-like syntax):
97  //
98  // struct S {
99  //   float f;
100  //   vec3 g;
101  //   int4 h[10];
102  // };
103  //
104  // Then:
105  // - (7, 9, [0]) describes the 'f' field.
106  // - (7, 9, [1,1]) describes the y component of the 'g' field.
107  // - (7, 9, [2,7,3]) describes the w component of element 7 of the 'h' field
108
109  // The descriptor set and binding associated with a uniform variable.
110  uint32 descriptor_set = 1;
111  uint32 binding = 2;
112
113  // An ordered sequence of indices through composite structures in the
114  // uniform buffer.
115  repeated uint32 index = 3;
116
117}
118
119message FactSequence {
120  repeated Fact fact = 1;
121}
122
123message Fact {
124  oneof fact {
125    // Order the fact options by numeric id (rather than alphabetically).
126    FactConstantUniform constant_uniform_fact = 1;
127    FactDataSynonym data_synonym_fact = 2;
128  }
129}
130
131// Keep fact message types in alphabetical order:
132
133message FactConstantUniform {
134
135  // Records the fact that a uniform buffer element is guaranteed to be equal
136  // to a particular constant value.  spirv-fuzz can use such guarantees to
137  // obfuscate code, e.g. to manufacture an expression that will (due to the
138  // guarantee) evaluate to a particular value at runtime but in a manner that
139  // cannot be predicted at compile-time.
140
141  // An element of a uniform buffer
142  UniformBufferElementDescriptor uniform_buffer_element_descriptor = 1;
143
144  // The words of the associated constant
145  repeated uint32 constant_word = 2;
146
147}
148
149message FactDataSynonym {
150
151  // Records the fact that the data held in two data descriptors are guaranteed
152  // to be equal.  spirv-fuzz can use this to replace uses of one piece of data
153  // with a known-to-be-equal piece of data.
154
155  // Data descriptors guaranteed to hold identical data.
156  DataDescriptor data1 = 1;
157
158  DataDescriptor data2 = 2;
159
160}
161
162message TransformationSequence {
163  repeated Transformation transformation = 1;
164}
165
166message Transformation {
167  oneof transformation {
168    // Order the transformation options by numeric id (rather than
169    // alphabetically).
170    TransformationMoveBlockDown move_block_down = 1;
171    TransformationSplitBlock split_block = 2;
172    TransformationAddConstantBoolean add_constant_boolean = 3;
173    TransformationAddConstantScalar add_constant_scalar = 4;
174    TransformationAddTypeBoolean add_type_boolean = 5;
175    TransformationAddTypeFloat add_type_float = 6;
176    TransformationAddTypeInt add_type_int = 7;
177    TransformationAddDeadBreak add_dead_break = 8;
178    TransformationReplaceBooleanConstantWithConstantBinary
179      replace_boolean_constant_with_constant_binary = 9;
180    TransformationAddTypePointer add_type_pointer = 10;
181    TransformationReplaceConstantWithUniform replace_constant_with_uniform = 11;
182    TransformationAddDeadContinue add_dead_continue = 12;
183    TransformationCopyObject copy_object = 13;
184    TransformationReplaceIdWithSynonym replace_id_with_synonym = 14;
185    TransformationSetSelectionControl set_selection_control = 15;
186    TransformationCompositeConstruct composite_construct = 16;
187    TransformationSetLoopControl set_loop_control = 17;
188    TransformationSetFunctionControl set_function_control = 18;
189    TransformationAddNoContractionDecoration add_no_contraction_decoration = 19;
190    TransformationSetMemoryOperandsMask set_memory_operands_mask = 20;
191    TransformationCompositeExtract composite_extract = 21;
192    // Add additional option using the next available number.
193  }
194}
195
196// Keep transformation message types in alphabetical order:
197
198message TransformationAddConstantBoolean {
199
200  // Supports adding the constants true and false to a module, which may be
201  // necessary in order to enable other transformations if they are not present.
202
203  uint32 fresh_id = 1;
204  bool is_true = 2;
205
206}
207
208message TransformationAddConstantScalar {
209
210  // Adds a constant of the given scalar type
211
212  // Id for the constant
213  uint32 fresh_id = 1;
214
215  // Id for the scalar type of the constant
216  uint32 type_id = 2;
217
218  // Value of the constant
219  repeated uint32 word = 3;
220
221}
222
223message TransformationAddDeadBreak {
224
225  // A transformation that turns a basic block that unconditionally branches to
226  // its successor into a block that potentially breaks out of a structured
227  // control flow construct, but in such a manner that the break cannot actually
228  // be taken.
229
230  // The block to break from
231  uint32 from_block = 1;
232
233  // The merge block to break to
234  uint32 to_block = 2;
235
236  // Determines whether the break condition is true or false
237  bool break_condition_value = 3;
238
239  // A sequence of ids suitable for extending OpPhi instructions as a result of
240  // the new break edge
241  repeated uint32 phi_id = 4;
242
243}
244
245message TransformationAddDeadContinue {
246
247  // A transformation that turns a basic block appearing in a loop and that
248  // unconditionally branches to its successor into a block that potentially
249  // branches to the continue target of the loop, but in such a manner that the
250  // continue branch cannot actually be taken.
251
252  // The block to continue from
253  uint32 from_block = 1;
254
255  // Determines whether the continue condition is true or false
256  bool continue_condition_value = 2;
257
258  // A sequence of ids suitable for extending OpPhi instructions as a result of
259  // the new break edge
260  repeated uint32 phi_id = 3;
261
262}
263
264message TransformationAddNoContractionDecoration {
265
266  // Applies OpDecorate NoContraction to the given result id
267
268  // Result id to be decorated
269  uint32 result_id = 1;
270
271}
272
273message TransformationAddTypeBoolean {
274
275  // Adds OpTypeBool to the module
276
277  // Id to be used for the type
278  uint32 fresh_id = 1;
279
280}
281
282message TransformationAddTypeFloat {
283
284  // Adds OpTypeFloat to the module with the given width
285
286  // Id to be used for the type
287  uint32 fresh_id = 1;
288
289  // Floating-point width
290  uint32 width = 2;
291
292}
293
294message TransformationAddTypeInt {
295
296  // Adds OpTypeInt to the module with the given width and signedness
297
298  // Id to be used for the type
299  uint32 fresh_id = 1;
300
301  // Integer width
302  uint32 width = 2;
303
304  // True if and only if this is a signed type
305  bool is_signed = 3;
306
307}
308
309message TransformationAddTypePointer {
310
311  // Adds OpTypePointer to the module, with the given storage class and base
312  // type
313
314  // Id to be used for the type
315  uint32 fresh_id = 1;
316
317  // Pointer storage class
318  uint32 storage_class = 2;
319
320  // Id of the base type for the pointer
321  uint32 base_type_id = 3;
322
323}
324
325message TransformationCompositeConstruct {
326
327  // A transformation that introduces an OpCompositeConstruct instruction to
328  // make a composite object.
329
330  // Id of the type of the composite that is to be constructed
331  uint32 composite_type_id = 1;
332
333  // Ids of the objects that will form the components of the composite
334  repeated uint32 component = 2;
335
336  // A descriptor for an instruction in a block before which the new
337  // OpCompositeConstruct instruction should be inserted
338  InstructionDescriptor instruction_to_insert_before = 3;
339
340  // A fresh id for the composite object
341  uint32 fresh_id = 4;
342
343}
344
345message TransformationCompositeExtract {
346
347  // A transformation that adds an instruction to extract an element from a
348  // composite.
349
350  // A descriptor for an instruction in a block before which the new
351  // OpCompositeExtract instruction should be inserted
352  InstructionDescriptor instruction_to_insert_before = 1;
353
354  // Result id for the extract operation.
355  uint32 fresh_id = 2;
356
357  // Id of the composite from which data is to be extracted.
358  uint32 composite_id = 3;
359
360  // Indices that indicate which part of the composite should be extracted.
361  repeated uint32 index = 4;
362
363}
364
365message TransformationCopyObject {
366
367  // A transformation that introduces an OpCopyObject instruction to make a
368  // copy of an object.
369
370  // Id of the object to be copied
371  uint32 object = 1;
372
373  // A descriptor for an instruction in a block before which the new
374  // OpCopyObject instruction should be inserted
375  InstructionDescriptor instruction_to_insert_before = 2;
376
377  // A fresh id for the copied object
378  uint32 fresh_id = 3;
379
380}
381
382message TransformationMoveBlockDown {
383
384  // A transformation that moves a basic block to be one position lower in
385  // program order.
386
387  // The id of the block to move down.
388  uint32 block_id = 1;
389}
390
391message TransformationReplaceBooleanConstantWithConstantBinary {
392
393  // A transformation to capture replacing a use of a boolean constant with
394  // binary operation on two constant values
395
396  // A descriptor for the boolean constant id we would like to replace
397  IdUseDescriptor id_use_descriptor = 1;
398
399  // Id for the constant to be used on the LHS of the comparision
400  uint32 lhs_id = 2;
401
402  // Id for the constant to be used on the RHS of the comparision
403  uint32 rhs_id = 3;
404
405  // Opcode for binary operator
406  uint32 opcode = 4;
407
408  // Id that will store the result of the binary operation instruction
409  uint32 fresh_id_for_binary_operation = 5;
410
411}
412
413message TransformationReplaceConstantWithUniform {
414
415  // Replaces a use of a constant id with the result of a load from an
416  // element of uniform buffer known to hold the same value as the constant
417
418  // A descriptor for the id we would like to replace
419  IdUseDescriptor id_use_descriptor = 1;
420
421  // Uniform descriptor to identify which uniform value to choose
422  UniformBufferElementDescriptor uniform_descriptor = 2;
423
424  // Id that will store the result of an access chain
425  uint32 fresh_id_for_access_chain = 3;
426
427  // Id that will store the result of a load
428  uint32 fresh_id_for_load = 4;
429
430}
431
432message TransformationReplaceIdWithSynonym {
433
434  // Replaces an id use with something known to be synonymous with that id use,
435  // e.g. because it was obtained via applying OpCopyObject
436
437  // Identifies the id use that is to be replaced
438  IdUseDescriptor id_use_descriptor = 1;
439
440  // Identifies the data with which the id use is expected to be synonymous
441  DataDescriptor data_descriptor = 2;
442
443  // In the case that a temporary is required to express the synonym (e.g. to
444  // obtain an element of a vector, provides a fresh id for the temporary;
445  // should be set to 0 if no temporary is required
446  uint32 fresh_id_for_temporary = 3;
447}
448
449message TransformationSetFunctionControl {
450
451  // A transformation that sets the function control operand of an OpFunction
452  // instruction.
453
454  // The result id of an OpFunction instruction
455  uint32 function_id = 1;
456
457  // The value to which the 'function control' operand should be set.
458  uint32 function_control = 2;
459
460}
461
462message TransformationSetLoopControl {
463
464  // A transformation that sets the loop control operand of an OpLoopMerge
465  // instruction.
466
467  // The id of a basic block that should contain OpLoopMerge
468  uint32 block_id = 1;
469
470  // The value to which the 'loop control' operand should be set.
471  // This must be a legal loop control mask.
472  uint32 loop_control = 2;
473
474  // Provides a peel count value for the loop.  Used if and only if the
475  // PeelCount bit is set.  Must be zero if the PeelCount bit is not set (can
476  // still be zero if this bit is set).
477  uint32 peel_count = 3;
478
479  // Provides a partial count value for the loop.  Used if and only if the
480  // PartialCount bit is set.  Must be zero if the PartialCount bit is not set
481  // (can still be zero if this bit is set).
482  uint32 partial_count = 4;
483
484}
485
486message TransformationSetMemoryOperandsMask {
487
488  // A transformation that sets the memory operands mask of a memory access
489  // instruction.
490
491  // A descriptor for a memory access instruction, e.g. an OpLoad
492  InstructionDescriptor memory_access_instruction = 1;
493
494  // A mask of memory operands to be applied to the instruction.  It must be the
495  // same as the original mask, except that Volatile can be added, and
496  // Nontemporal can be added or removed.
497  uint32 memory_operands_mask = 2;
498
499  // Some memory access instructions allow more than one mask to be specified;
500  // this field indicates which mask should be set
501  uint32 memory_operands_mask_index = 3;
502
503}
504
505message TransformationSetSelectionControl {
506
507  // A transformation that sets the selection control operand of an
508  // OpSelectionMerge instruction.
509
510  // The id of a basic block that should contain OpSelectionMerge
511  uint32 block_id = 1;
512
513  // The value to which the 'selection control' operand should be set.
514  // Although technically 'selection control' is a literal mask that can be
515  // some combination of 'None', 'Flatten' and 'DontFlatten', the combination
516  // 'Flatten | DontFlatten' does not make sense and is not allowed here.
517  uint32 selection_control = 2;
518
519}
520
521message TransformationSplitBlock {
522
523  // A transformation that splits a basic block into two basic blocks
524
525  // A descriptor for an instruction such that the block containing the
526  // described instruction should be split right before the instruction.
527  InstructionDescriptor instruction_to_split_before = 1;
528
529  // An id that must not yet be used by the module to which this transformation
530  // is applied.  Rather than having the transformation choose a suitable id on
531  // application, we require the id to be given upfront in order to facilitate
532  // reducing fuzzed shaders by removing transformations.  The reason is that
533  // future transformations may refer to the fresh id introduced by this
534  // transformation, and if we end up changing what that id is, due to removing
535  // earlier transformations, it may inhibit later transformations from
536  // applying.
537  uint32 fresh_id = 2;
538
539}
540