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 UInt32Pair {
25
26  // A pair of uint32s; useful for defining mappings.
27
28  uint32 first = 1;
29
30  uint32 second = 2;
31
32}
33
34message InstructionDescriptor {
35
36  // Describes an instruction in some block of a function with respect to a
37  // base instruction.
38
39  // The id of an instruction after which the instruction being described is
40  // believed to be located.  It might be the using instruction itself.
41  uint32 base_instruction_result_id = 1;
42
43  // The opcode for the instruction being described.
44  uint32 target_instruction_opcode = 2;
45
46  // The number of matching opcodes to skip over when searching from the base
47  // instruction to the instruction being described.
48  uint32 num_opcodes_to_ignore = 3;
49
50}
51
52message IdUseDescriptor {
53
54  // Describes a use of an id as an input operand to an instruction in some
55  // block of a function.
56
57  // Example:
58  //   - id_of_interest = 42
59  //   - enclosing_instruction = (
60  //         base_instruction_result_id = 50,
61  //         target_instruction_opcode = OpStore
62  //         num_opcodes_to_ignore = 7
63  //     )
64  //   - in_operand_index = 1
65  // represents a use of id 42 as input operand 1 to an OpStore instruction,
66  // such that the OpStore instruction can be found in the same basic block as
67  // the instruction with result id 50, and in particular is the 8th OpStore
68  // instruction found from instruction 50 onwards (i.e. 7 OpStore
69  // instructions are skipped).
70
71  // An id that we would like to be able to find a use of.
72  uint32 id_of_interest = 1;
73
74  // The input operand index at which the use is expected.
75  InstructionDescriptor enclosing_instruction = 2;
76
77  uint32 in_operand_index = 3;
78
79}
80
81message DataDescriptor {
82
83  // Represents a data element that can be accessed from an id, by walking the
84  // type hierarchy via a sequence of 0 or more indices.
85  //
86  // Very similar to a UniformBufferElementDescriptor, except that a
87  // DataDescriptor is rooted at the id of a scalar or composite.
88
89  // The object being accessed - a scalar or composite
90  uint32 object = 1;
91
92  // 0 or more indices, used to index into a composite object
93  repeated uint32 index = 2;
94
95}
96
97message UniformBufferElementDescriptor {
98
99  // Represents a data element inside a uniform buffer.  The element is
100  // specified via (a) the result id of a uniform variable in which the element
101  // is contained, and (b) a series of indices that need to be followed to get
102  // to the element (via fields and array/vector indices).
103  //
104  // Example: suppose there is a uniform variable with descriptor set 7 and
105  // binding 9, and that the uniform variable has the following type (using
106  // GLSL-like syntax):
107  //
108  // struct S {
109  //   float f;
110  //   vec3 g;
111  //   int4 h[10];
112  // };
113  //
114  // Then:
115  // - (7, 9, [0]) describes the 'f' field.
116  // - (7, 9, [1,1]) describes the y component of the 'g' field.
117  // - (7, 9, [2,7,3]) describes the w component of element 7 of the 'h' field
118
119  // The descriptor set and binding associated with a uniform variable.
120  uint32 descriptor_set = 1;
121  uint32 binding = 2;
122
123  // An ordered sequence of indices through composite structures in the
124  // uniform buffer.
125  repeated uint32 index = 3;
126
127}
128
129message InstructionOperand {
130
131  // Represents an operand to a SPIR-V instruction.
132
133  // The type of the operand.
134  uint32 operand_type = 1;
135
136  // The data associated with the operand.  For most operands (e.g. ids,
137  // storage classes and literals) this will be a single word.
138  repeated uint32 operand_data = 2;
139
140}
141
142message Instruction {
143
144  // Represents a SPIR-V instruction.
145
146  // The instruction's opcode (e.g. OpLabel).
147  uint32 opcode = 1;
148
149  // The id of the instruction's result type; 0 if there is no result type.
150  uint32 result_type_id = 2;
151
152  // The id of the instruction's result; 0 if there is no result.
153  uint32 result_id = 3;
154
155  // Zero or more input operands.
156  repeated InstructionOperand input_operand = 4;
157
158}
159
160message FactSequence {
161  repeated Fact fact = 1;
162}
163
164message Fact {
165  oneof fact {
166    // Order the fact options by numeric id (rather than alphabetically).
167    FactConstantUniform constant_uniform_fact = 1;
168    FactDataSynonym data_synonym_fact = 2;
169    FactBlockIsDead block_is_dead_fact = 3;
170    FactFunctionIsLivesafe function_is_livesafe_fact = 4;
171    FactPointeeValueIsIrrelevant pointee_value_is_irrelevant_fact = 5;
172    FactIdEquation id_equation_fact = 6;
173    FactIdIsIrrelevant id_is_irrelevant = 7;
174  }
175}
176
177// Keep fact message types in alphabetical order:
178
179message FactBlockIsDead {
180
181  // Records the fact that a block is guaranteed to be dynamically unreachable.
182  // This is useful because it informs the fuzzer that rather arbitrary changes
183  // can be made to this block.
184
185  uint32 block_id = 1;
186
187}
188
189message FactConstantUniform {
190
191  // Records the fact that a uniform buffer element is guaranteed to be equal
192  // to a particular constant value.  spirv-fuzz can use such guarantees to
193  // obfuscate code, e.g. to manufacture an expression that will (due to the
194  // guarantee) evaluate to a particular value at runtime but in a manner that
195  // cannot be predicted at compile-time.
196
197  // An element of a uniform buffer
198  UniformBufferElementDescriptor uniform_buffer_element_descriptor = 1;
199
200  // The words of the associated constant
201  repeated uint32 constant_word = 2;
202
203}
204
205message FactDataSynonym {
206
207  // Records the fact that the data held in two data descriptors are guaranteed
208  // to be equal.  spirv-fuzz can use this to replace uses of one piece of data
209  // with a known-to-be-equal piece of data.
210
211  // Data descriptors guaranteed to hold identical data.
212  DataDescriptor data1 = 1;
213
214  DataDescriptor data2 = 2;
215
216}
217
218message FactFunctionIsLivesafe {
219
220  // Records the fact that a function is guaranteed to be "livesafe", meaning
221  // that it will not make out-of-bounds accesses, does not contain reachable
222  // OpKill or OpUnreachable instructions, does not contain loops that will
223  // execute for large numbers of iterations, and only invokes other livesafe
224  // functions.
225
226  uint32 function_id = 1;
227
228}
229
230message FactIdEquation {
231
232  // Records the fact that the equation:
233  //
234  // lhs_id = opcode rhs_id[0] rhs_id[1] ... rhs_id[N-1]
235  //
236  // holds; e.g. that the equation:
237  //
238  // %12 = OpIAdd %13 %14
239  //
240  // holds in the case where lhs_id is 12, rhs_id is [13, 14], and the opcode is
241  // OpIAdd.
242
243  // The left-hand-side of the equation.
244  uint32 lhs_id = 1;
245
246  // A SPIR-V opcode, from a restricted set of instructions for which equation
247  // facts make sense.
248  uint32 opcode = 2;
249
250  // The operands to the right-hand-side of the equation.
251  repeated uint32 rhs_id = 3;
252
253}
254
255message FactIdIsIrrelevant {
256
257  // Records a fact that |result_id| is irrelevant (i.e. it's usage doesn't
258  // change the semantics of the module). This implies that a use of this id
259  // can later be replaced with some other id of the same type, or the
260  // definition of |result_id| can be changed so that it yields a different value.
261
262  // An irrelevant id.
263  uint32 result_id = 1;
264
265}
266
267message FactPointeeValueIsIrrelevant {
268
269  // Records the fact that value of the data pointed to by a pointer id does
270  // not influence the observable behaviour of the module.  This means that
271  // arbitrary stores can be made through the pointer, and that nothing can be
272  // guaranteed about the values that are loaded via the pointer.
273
274  // A result id of pointer type
275  uint32 pointer_id = 1;
276
277}
278
279message AccessChainClampingInfo {
280
281  // When making a function livesafe it is necessary to clamp the indices that
282  // occur as operands to access chain instructions so that they are guaranteed
283  // to be in bounds.  This message type allows an access chain instruction to
284  // have an associated sequence of ids that are reserved for comparing an
285  // access chain index with a bound (e.g. an array size), and selecting
286  // between the access chain index (if it is within bounds) and the bound (if
287  // it is not).
288  //
289  // This allows turning an instruction of the form:
290  //
291  // %result = OpAccessChain %type %object ... %index ...
292  //
293  // into:
294  //
295  // %t1 = OpULessThanEqual %bool %index %bound_minus_one
296  // %t2 = OpSelect %int_type %t1 %index %bound_minus_one
297  // %result = OpAccessChain %type %object ... %t2 ...
298
299  // The result id of an OpAccessChain or OpInBoundsAccessChain instruction.
300  uint32 access_chain_id = 1;
301
302  // A series of pairs of fresh ids, one per access chain index, for the results
303  // of a compare instruction and a select instruction, serving the roles of %t1
304  // and %t2 in the above example.
305  repeated UInt32Pair compare_and_select_ids = 2;
306
307}
308
309message SideEffectWrapperInfo {
310  // When flattening a conditional branch, it is necessary to enclose
311  // instructions that have side effects inside conditionals, so that
312  // they are only executed if the condition holds. Otherwise, there
313  // might be unintended changes in memory, or crashes that would not
314  // originally happen.
315  // For example, the instruction %id = OpLoad %type %ptr, found in
316  // the true branch of the conditional, will be enclosed in a new
317  // conditional (assuming that the block containing it can be split
318  // around it) as follows:
319  //
320  //   [previous instructions in the block]
321  //                        OpSelectionMerge %merge_block_id None
322  //                        OpBranchConditional %cond %execute_block_id %alternative_block_id
323  //    %execute_block_id = OpLabel
324  //    %actual_result_id = OpLoad %type %ptr
325  //                       OpBranch %merge_block_id
326  //  %alternative_block_id = OpLabel
327  // %placeholder_result_id = OpCopyObject %type %value_to_copy_id
328  //                       OpBranch %merge_block_id
329  //     %merge_block_id = OpLabel
330  //                 %id = OpPhi %type %actual_result_id %execute_block_id %placeholder_result_id %alternative_block_id
331  //   [following instructions from the original block]
332  //
333  // If the instruction does not have a result id, this is simplified.
334  // For example, OpStore %ptr %value, found in the true branch of a
335  // conditional, is enclosed as follows:
336  //
337  //   [previous instructions in the block]
338  //                       OpSelectionMerge %merge_block None
339  //                       OpBranchConditional %cond %execute_block_id %merge_block_id
340  //   %execute_block_id = OpLabel
341  //                       OpStore %ptr %value
342  //                       OpBranch %merge_block_id
343  //     %merge_block_id = OpLabel
344  //   [following instructions from the original block]
345  //
346  // The same happens if the instruction is found in the false branch
347  // of the conditional being flattened, except that the label ids in
348  // the OpBranchConditional are swapped.
349
350
351  // An instruction descriptor for identifying the instruction to be
352  // enclosed inside a conditional. An instruction descriptor is
353  // necessary because the instruction might not have a result id.
354  InstructionDescriptor instruction = 1;
355
356  // A fresh id for the new merge block.
357  uint32 merge_block_id = 2;
358
359  // A fresh id for the new block where the actual instruction is
360  // executed.
361  uint32 execute_block_id = 3;
362
363  // The following fields are only needed if the original instruction has a
364  // result id. They can be set to 0 if not needed.
365
366  // A fresh id for the result id of the instruction (the original
367  // one is used by the OpPhi instruction).
368  uint32 actual_result_id = 4;
369
370  // A fresh id for the new block where the placeholder instruction
371  // is placed.
372  uint32 alternative_block_id = 5;
373
374  // A fresh id for the placeholder instruction.
375  uint32 placeholder_result_id = 6;
376
377  // An id present in the module, available to use at this point in
378  // the program and with the same type as the original instruction,
379  // that can be used to create a placeholder OpCopyObject
380  // instruction.
381  uint32 value_to_copy_id = 7;
382}
383
384message ReturnMergingInfo {
385  // TransformationMergeFunctionReturns needs to modify each merge block of
386  // loops containing return instructions, by:
387  // - adding instructions to decide whether the function is returning
388  // - adding instructions to pass on the return value of the function,
389  //   if it is returning
390  // - changing the branch instruction (which must be an unconditional branch)
391  //   to a conditional branch that, if the function is returning, branches to
392  //   the merge block of the innermost loop that contains this merge block
393  //   (which can be the new merge block introduced by the transformation).
394  //
395  // One such merge block of the form:
396  //  %block = OpLabel
397  //   %phi1 = OpPhi %type1 %val1_1 %pred1 %val1_2 %pred2
398  //   %phi2 = OpPhi %type2 %val2_1 %pred1 %val2_2 %pred2
399  //           OpBranch %next
400  //
401  // is transformed into:
402  //               %block = OpLabel
403  //     %is_returning_id = OpPhi %bool %false %pred1 %false %pred2 %true %ret_bb1 %is_bb2_returning %mer_bb2
404  // %maybe_return_val_id = OpPhi %return_type %any_returnable_val %pred1 %any_returnable_val %pred2
405  //                                            %ret_val1 %ret_bb1 %ret_val2 %mer_bb2
406  //                %phi1 = OpPhi %type1 %val1_1 %pred1 %val1_2 %pred2
407  //                                     %any_suitable_id_1 %ret_bb1 %any_suitable_id_1 %mer_bb2
408  //                %phi2 = OpPhi %type2 %val2_1 %pred1 %val2_2 %pred2
409  //                                     %any_suitable_id_1 %ret_bb1 %any_suitable_id_1 %mer_bb2
410  //                        OpBranchConditional %is_returning_id %innermost_loop_merge %next
411  //
412  // where %ret_bb1 is a block that originally contains a return instruction and %mer_bb2 is the merge block of an inner
413  // loop, from where the function might be returning.
414  //
415  // Note that the block is required to only have OpLabel, OpPhi or OpBranch instructions.
416
417  // The id of the merge block that needs to be modified.
418  uint32 merge_block_id = 1;
419
420  // A fresh id for a boolean OpPhi whose value will be true iff the function
421  // is returning. This will be used to decide whether to break out of the loop
422  // or to use the original branch of the function. This value will also be
423  // used by the merge block of the enclosing loop (if there is one) if the
424  // function is returning from this block.
425  uint32 is_returning_id = 2;
426
427  // A fresh id that will get the value being returned, if the function is
428  // returning. If the function return type is void, this is ignored.
429  uint32 maybe_return_val_id = 3;
430
431  // A mapping from each existing OpPhi id to a suitable id of the same type
432  // available to use before the instruction.
433  repeated UInt32Pair opphi_to_suitable_id = 4;
434}
435
436message LoopLimiterInfo {
437
438  // Structure capturing the information required to manipulate a loop limiter
439  // at a loop header.
440
441  // The header for the loop.
442  uint32 loop_header_id = 1;
443
444  // A fresh id into which the loop limiter's current value can be loaded.
445  uint32 load_id = 2;
446
447  // A fresh id that can be used to increment the loaded value by 1.
448  uint32 increment_id = 3;
449
450  // A fresh id that can be used to compare the loaded value with the loop
451  // limit.
452  uint32 compare_id = 4;
453
454  // A fresh id that can be used to compute the conjunction or disjunction of
455  // an original loop exit condition with |compare_id|, if the loop's back edge
456  // block can conditionally exit the loop.
457  uint32 logical_op_id = 5;
458
459  // A sequence of ids suitable for extending OpPhi instructions of the loop
460  // merge block if it did not previously have an incoming edge from the loop
461  // back edge block.
462  repeated uint32 phi_id = 6;
463
464}
465
466message TransformationSequence {
467  repeated Transformation transformation = 1;
468}
469
470message Transformation {
471  oneof transformation {
472    // Order the transformation options by numeric id (rather than
473    // alphabetically).
474    TransformationMoveBlockDown move_block_down = 1;
475    TransformationSplitBlock split_block = 2;
476    TransformationAddConstantBoolean add_constant_boolean = 3;
477    TransformationAddConstantScalar add_constant_scalar = 4;
478    TransformationAddTypeBoolean add_type_boolean = 5;
479    TransformationAddTypeFloat add_type_float = 6;
480    TransformationAddTypeInt add_type_int = 7;
481    TransformationAddDeadBreak add_dead_break = 8;
482    TransformationReplaceBooleanConstantWithConstantBinary
483        replace_boolean_constant_with_constant_binary = 9;
484    TransformationAddTypePointer add_type_pointer = 10;
485    TransformationReplaceConstantWithUniform replace_constant_with_uniform = 11;
486    TransformationAddDeadContinue add_dead_continue = 12;
487    TransformationReplaceIdWithSynonym replace_id_with_synonym = 13;
488    TransformationSetSelectionControl set_selection_control = 14;
489    TransformationCompositeConstruct composite_construct = 15;
490    TransformationSetLoopControl set_loop_control = 16;
491    TransformationSetFunctionControl set_function_control = 17;
492    TransformationAddNoContractionDecoration add_no_contraction_decoration = 18;
493    TransformationSetMemoryOperandsMask set_memory_operands_mask = 19;
494    TransformationCompositeExtract composite_extract = 20;
495    TransformationVectorShuffle vector_shuffle = 21;
496    TransformationOutlineFunction outline_function = 22;
497    TransformationMergeBlocks merge_blocks = 23;
498    TransformationAddTypeVector add_type_vector = 24;
499    TransformationAddTypeArray add_type_array = 25;
500    TransformationAddTypeMatrix add_type_matrix = 26;
501    TransformationAddTypeStruct add_type_struct = 27;
502    TransformationAddTypeFunction add_type_function = 28;
503    TransformationAddConstantComposite add_constant_composite = 29;
504    TransformationAddGlobalVariable add_global_variable = 30;
505    TransformationAddGlobalUndef add_global_undef = 31;
506    TransformationAddFunction add_function = 32;
507    TransformationAddDeadBlock add_dead_block = 33;
508    TransformationAddLocalVariable add_local_variable = 34;
509    TransformationLoad load = 35;
510    TransformationStore store = 36;
511    TransformationFunctionCall function_call = 37;
512    TransformationAccessChain access_chain = 38;
513    TransformationEquationInstruction equation_instruction = 39;
514    TransformationSwapCommutableOperands swap_commutable_operands = 40;
515    TransformationPermuteFunctionParameters permute_function_parameters = 41;
516    TransformationToggleAccessChainInstruction toggle_access_chain_instruction = 42;
517    TransformationAddConstantNull add_constant_null = 43;
518    TransformationComputeDataSynonymFactClosure compute_data_synonym_fact_closure = 44;
519    TransformationAdjustBranchWeights adjust_branch_weights = 45;
520    TransformationPushIdThroughVariable push_id_through_variable = 46;
521    TransformationAddSpecConstantOp add_spec_constant_op = 47;
522    TransformationReplaceLinearAlgebraInstruction replace_linear_algebra_instruction = 48;
523    TransformationSwapConditionalBranchOperands swap_conditional_branch_operands = 49;
524    TransformationPermutePhiOperands permute_phi_operands = 50;
525    TransformationAddParameter add_parameter = 51;
526    TransformationAddCopyMemory add_copy_memory = 52;
527    TransformationInvertComparisonOperator invert_comparison_operator = 53;
528    TransformationAddImageSampleUnusedComponents add_image_sample_unused_components = 54;
529    TransformationReplaceParameterWithGlobal replace_parameter_with_global = 55;
530    TransformationRecordSynonymousConstants record_synonymous_constants = 56;
531    TransformationAddSynonym add_synonym = 57;
532    TransformationAddRelaxedDecoration add_relaxed_decoration = 58;
533    TransformationReplaceParamsWithStruct replace_params_with_struct = 59;
534    TransformationReplaceCopyObjectWithStoreLoad replace_copy_object_with_store_load = 60;
535    TransformationReplaceCopyMemoryWithLoadStore replace_copy_memory_with_load_store = 61;
536    TransformationReplaceLoadStoreWithCopyMemory replace_load_store_with_copy_memory = 62;
537    TransformationAddLoopPreheader add_loop_preheader = 63;
538    TransformationMoveInstructionDown move_instruction_down = 64;
539    TransformationMakeVectorOperationDynamic make_vector_operation_dynamic = 65;
540    TransformationReplaceAddSubMulWithCarryingExtended replace_add_sub_mul_with_carrying_extended = 66;
541    TransformationPropagateInstructionUp propagate_instruction_up = 67;
542    TransformationCompositeInsert composite_insert = 68;
543    TransformationInlineFunction inline_function = 69;
544    TransformationAddOpPhiSynonym add_opphi_synonym = 70;
545    TransformationMutatePointer mutate_pointer = 71;
546    TransformationReplaceIrrelevantId replace_irrelevant_id = 72;
547    TransformationReplaceOpPhiIdFromDeadPredecessor replace_opphi_id_from_dead_predecessor = 73;
548    TransformationReplaceOpSelectWithConditionalBranch replace_opselect_with_conditional_branch = 74;
549    TransformationDuplicateRegionWithSelection duplicate_region_with_selection = 75;
550    TransformationFlattenConditionalBranch flatten_conditional_branch = 76;
551    TransformationAddBitInstructionSynonym add_bit_instruction_synonym = 77;
552    TransformationAddLoopToCreateIntConstantSynonym add_loop_to_create_int_constant_synonym = 78;
553    TransformationWrapRegionInSelection wrap_region_in_selection = 79;
554    TransformationAddEarlyTerminatorWrapper add_early_terminator_wrapper = 80;
555    TransformationPropagateInstructionDown propagate_instruction_down = 81;
556    TransformationReplaceBranchFromDeadBlockWithExit replace_branch_from_dead_block_with_exit = 82;
557    TransformationWrapEarlyTerminatorInFunction wrap_early_terminator_in_function = 83;
558    TransformationMergeFunctionReturns merge_function_returns = 84;
559    TransformationExpandVectorReduction expand_vector_reduction = 85;
560    // Add additional option using the next available number.
561  }
562}
563
564// Keep transformation message types in alphabetical order:
565
566message TransformationAccessChain {
567
568  // Adds an access chain instruction based on a given pointer and indices.
569
570  // When accessing a struct, the corresponding indices must be 32-bit integer constants.
571  // For any other composite, the indices can be any 32-bit integer, and the transformation
572  // adds two instructions for each such index to clamp it to the bound, as follows:
573  //
574  // %t1 = OpULessThanEqual %bool %index %bound_minus_one
575  // %t2 = OpSelect %int_type %t1 %index %bound_minus_one
576
577  // Result id for the access chain
578  uint32 fresh_id = 1;
579
580  // The pointer from which the access chain starts
581  uint32 pointer_id = 2;
582
583  // Zero or more access chain indices
584  repeated uint32 index_id = 3;
585
586  // A descriptor for an instruction in a block before which the new
587  // OpAccessChain instruction should be inserted
588  InstructionDescriptor instruction_to_insert_before = 4;
589
590  // Additional fresh ids, required to clamp index variables. A pair is needed
591  // for each access to a non-struct composite.
592  repeated UInt32Pair fresh_ids_for_clamping = 5;
593
594}
595
596message TransformationAddBitInstructionSynonym {
597
598  // A transformation that adds synonyms for bit instructions by evaluating
599  // each bit with the corresponding operation. There is a SPIR-V code example in the
600  // header file of the transformation class that can help understand the transformation.
601
602  // This transformation is only applicable if the described instruction has one of the following opcodes.
603  // Supported:
604  //   OpBitwiseOr
605  //   OpBitwiseXor
606  //   OpBitwiseAnd
607  //   OpNot
608  // To be supported in the future:
609  //   OpShiftRightLogical
610  //   OpShiftRightArithmetic
611  //   OpShiftLeftLogical
612  //   OpBitReverse
613  //   OpBitCount
614
615  // The bit instruction result id.
616  uint32 instruction_result_id = 1;
617
618  // The fresh ids required to apply the transformation.
619  repeated uint32 fresh_ids = 2;
620
621}
622
623message TransformationAddConstantBoolean {
624
625  // Supports adding the constants true and false to a module, which may be
626  // necessary in order to enable other transformations if they are not present.
627  // Also, creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is true.
628
629  uint32 fresh_id = 1;
630  bool is_true = 2;
631
632  // If the constant should be marked as irrelevant.
633  bool is_irrelevant = 3;
634
635}
636
637message TransformationAddConstantComposite {
638
639  // Adds a constant of the given composite type to the module.
640  // Also, creates an IdIsIrrelevant fact about |fresh_id| if
641  // |is_irrelevant| is true.
642
643  // Fresh id for the composite
644  uint32 fresh_id = 1;
645
646  // A composite type id
647  uint32 type_id = 2;
648
649  // Constituent ids for the composite
650  repeated uint32 constituent_id = 3;
651
652  // If the constant should be marked as irrelevant.
653  bool is_irrelevant = 4;
654
655}
656
657message TransformationAddConstantNull {
658
659  // Adds a null constant.
660
661  // Id for the constant
662  uint32 fresh_id = 1;
663
664  // Type of the constant
665  uint32 type_id = 2;
666
667}
668
669message TransformationAddConstantScalar {
670
671  // Adds a constant of the given scalar type.
672  // Also, creates an IdIsIrrelevant fact about
673  // |fresh_id| if |is_irrelevant| is true.
674
675  // Id for the constant
676  uint32 fresh_id = 1;
677
678  // Id for the scalar type of the constant
679  uint32 type_id = 2;
680
681  // Value of the constant
682  repeated uint32 word = 3;
683
684  // If the constant should be marked as irrelevant.
685  bool is_irrelevant = 4;
686
687}
688
689message TransformationAddCopyMemory {
690
691  // Adds an OpCopyMemory instruction into the module.
692  // Creates either a global or a local variable (based on
693  // |storage_class| field) to copy the target into.
694
695  // OpCopyMemory will be inserted before this instruction.
696  InstructionDescriptor instruction_descriptor = 1;
697
698  // Fresh id to copy memory into.
699  uint32 fresh_id = 2;
700
701  // Source to copy memory from.
702  uint32 source_id = 3;
703
704  // Storage class for the target variable. Can be either Function or Private.
705  uint32 storage_class = 4;
706
707  // Result id for the variable's initializer operand. Its type must be equal to
708  // variable's pointee type.
709  uint32 initializer_id = 5;
710
711}
712
713message TransformationAddDeadBlock {
714
715  // Adds a new block to the module that is statically reachable from an
716  // existing block, but dynamically unreachable.
717
718  // Fresh id for the dead block
719  uint32 fresh_id = 1;
720
721  // Id of an existing block terminated with OpBranch, such that this OpBranch
722  // can be replaced with an OpBranchConditional to its exiting successor or
723  // the dead block
724  uint32 existing_block = 2;
725
726  // Determines whether the condition associated with the OpBranchConditional
727  // is true or false
728  bool condition_value = 3;
729
730}
731
732message TransformationAddDeadBreak {
733
734  // A transformation that turns a basic block that unconditionally branches to
735  // its successor into a block that potentially breaks out of a structured
736  // control flow construct, but in such a manner that the break cannot actually
737  // be taken.
738
739  // The block to break from
740  uint32 from_block = 1;
741
742  // The merge block to break to
743  uint32 to_block = 2;
744
745  // Determines whether the break condition is true or false
746  bool break_condition_value = 3;
747
748  // A sequence of ids suitable for extending OpPhi instructions as a result of
749  // the new break edge
750  repeated uint32 phi_id = 4;
751
752}
753
754message TransformationAddDeadContinue {
755
756  // A transformation that turns a basic block appearing in a loop and that
757  // unconditionally branches to its successor into a block that potentially
758  // branches to the continue target of the loop, but in such a manner that the
759  // continue branch cannot actually be taken.
760
761  // The block to continue from
762  uint32 from_block = 1;
763
764  // Determines whether the continue condition is true or false
765  bool continue_condition_value = 2;
766
767  // A sequence of ids suitable for extending OpPhi instructions as a result of
768  // the new break edge
769  repeated uint32 phi_id = 3;
770
771}
772
773message TransformationAddEarlyTerminatorWrapper {
774
775  // Adds a function to the module containing a single block with a single non-
776  // label instruction that is either OpKill, OpUnreachable, or
777  // OpTerminateInvocation.  The purpose of this is to allow such instructions
778  // to be subsequently replaced with wrapper functions, which can then enable
779  // transformations (such as inlining) that are hard in the direct presence
780  // of these instructions.
781
782  // Fresh id for the function.
783  uint32 function_fresh_id = 1;
784
785  // Fresh id for the single basic block in the function.
786  uint32 label_fresh_id = 2;
787
788  // One of OpKill, OpUnreachable, OpTerminateInvocation.  If additional early
789  // termination instructions are added to SPIR-V they should also be handled
790  // here.
791  uint32 opcode = 3;
792
793}
794
795message TransformationAddFunction {
796
797  // Adds a SPIR-V function to the module.
798
799  // The series of instructions that comprise the function.
800  repeated Instruction instruction = 1;
801
802  // True if and only if the given function should be made livesafe (see
803  // FactFunctionIsLivesafe for definition).
804  bool is_livesafe = 2;
805
806  // Fresh id for a new variable that will serve as a "loop limiter" for the
807  // function; only relevant if |is_livesafe| holds.
808  uint32 loop_limiter_variable_id = 3;
809
810  // Id of an existing unsigned integer constant providing the maximum value
811  // that the loop limiter can reach before the loop is broken from; only
812  // relevant if |is_livesafe| holds.
813  uint32 loop_limit_constant_id = 4;
814
815  // Fresh ids for each loop in the function that allow the loop limiter to be
816  // manipulated; only relevant if |is_livesafe| holds.
817  repeated LoopLimiterInfo loop_limiter_info = 5;
818
819  // Id of an existing global value with the same return type as the function
820  // that can be used to replace OpKill and OpReachable instructions with
821  // ReturnValue instructions.  Ignored if the function has void return type.
822  // Only relevant if |is_livesafe| holds.
823  uint32 kill_unreachable_return_value_id = 6;
824
825  // A mapping (represented as a sequence) from every access chain result id in
826  // the function to the ids required to clamp its indices to ensure they are in
827  // bounds; only relevant if |is_livesafe| holds.
828  repeated AccessChainClampingInfo access_chain_clamping_info = 7;
829
830}
831
832message TransformationAddGlobalUndef {
833
834  // Adds an undefined value of a given type to the module at global scope.
835
836  // Fresh id for the undefined value
837  uint32 fresh_id = 1;
838
839  // The type of the undefined value
840  uint32 type_id = 2;
841
842}
843
844message TransformationAddGlobalVariable {
845
846  // Adds a global variable of the given type to the module, with Private or
847  // Workgroup storage class, and optionally (for the Private case) with an
848  // initializer.
849
850  // Fresh id for the global variable
851  uint32 fresh_id = 1;
852
853  // The type of the global variable
854  uint32 type_id = 2;
855
856  uint32 storage_class = 3;
857
858  // Initial value of the variable
859  uint32 initializer_id = 4;
860
861  // True if and only if the behaviour of the module should not depend on the
862  // value of the variable, in which case stores to the variable can be
863  // performed in an arbitrary fashion.
864  bool value_is_irrelevant = 5;
865
866}
867
868message TransformationAddImageSampleUnusedComponents {
869
870  // A transformation that adds unused components to an image sample coordinate.
871
872  // An vector id with the original coordinate and the unused components.
873  uint32 coordinate_with_unused_components_id = 1;
874
875  // A descriptor for an image sample instruction.
876  InstructionDescriptor instruction_descriptor = 2;
877
878}
879
880message TransformationAddLocalVariable {
881
882  // Adds a local variable of the given type (which must be a pointer with
883  // Function storage class) to the given function, initialized to the given
884  // id.
885
886  // Fresh id for the local variable
887  uint32 fresh_id = 1;
888
889  // The type of the local variable
890  uint32 type_id = 2;
891
892  // The id of the function to which the local variable should be added
893  uint32 function_id = 3;
894
895  // Initial value of the variable
896  uint32 initializer_id = 4;
897
898  // True if and only if the behaviour of the module should not depend on the
899  // value of the variable, in which case stores to the variable can be
900  // performed in an arbitrary fashion.
901  bool value_is_irrelevant = 5;
902
903}
904
905message TransformationAddLoopPreheader {
906
907  // A transformation that adds a loop preheader block before the given loop header.
908
909  // The id of the loop header block
910  uint32 loop_header_block = 1;
911
912  // A fresh id for the preheader block
913  uint32 fresh_id = 2;
914
915  // Fresh ids for splitting the OpPhi instructions in the header.
916  // A new OpPhi instruction in the preheader is needed for each OpPhi instruction in the header,
917  // if the header has more than one predecessor outside of the loop.
918  // This allows turning instructions of the form:
919  //
920  //   %loop_header_block = OpLabel
921  //                 %id1 = OpPhi %type %val1 %pred1_id %val2 %pred2_id %val3 %backedge_block_id
922  //
923  // into:
924  //            %fresh_id = OpLabel
925  //             %phi_id1 = OpPhi %type %val1 %pred1_id %val2 %pred2_id
926  //                        OpBranch %header_id
927  //   %loop_header_block = OpLabel
928  //                 %id1 = OpPhi %type %phi_id1 %fresh_id %val3 %backedge_block_id
929  repeated uint32 phi_id = 3;
930
931}
932
933message TransformationAddLoopToCreateIntConstantSynonym {
934  // A transformation that uses a loop to create a synonym for an integer
935  // constant C (scalar or vector) using an initial value I, a step value S and
936  // a number of iterations N such that C = I - N * S. For each iteration, S is
937  // subtracted from the total.
938  // The loop can be made up of one or two blocks, and it is inserted before a
939  // block with a single predecessor. In the one-block case, it is of the form:
940  //
941  //            %loop_id = OpLabel
942  //             %ctr_id = OpPhi %int %int_0 %pred %incremented_ctr_id %loop_id
943  //            %temp_id = OpPhi %type_of_I %I %pred %eventual_syn_id %loop_id
944  //    %eventual_syn_id = OpISub %type_of_I %temp_id %step_val_id
945  // %incremented_ctr_id = OpIAdd %int %ctr_id %int_1
946  //            %cond_id = OpSLessThan %bool %incremented_ctr_id %num_iterations_id
947  //                       OpLoopMerge %block_after_loop_id %loop_id None
948  //                       OpBranchConditional %cond_id %loop_id %block_after_loop_id
949  //
950  // A new OpPhi instruction is then added to %block_after_loop_id, as follows:
951  //
952  //  %block_after_loop_id = OpLabel
953  //               %syn_id = OpPhi %type_of_I %eventual_syn_id %loop_id
954  //
955  // This can be translated, assuming that N > 0, to:
956  // int syn = I;
957  // for (int ctr = 0; ctr < N; ctr++) syn = syn - S;
958  //
959  // All existing OpPhi instructions in %block_after_loop_id are also updated
960  // to reflect the fact that its predecessor is now %loop_id.
961
962  // The following are existing ids.
963
964  // The id of the integer constant C that we want a synonym of.
965  uint32 constant_id = 1;
966
967  // The id of the initial value integer constant I.
968  uint32 initial_val_id = 2;
969
970  // The id of the step value integer constant S.
971  uint32 step_val_id = 3;
972
973  // The id of the integer scalar constant, its value being the number of
974  // iterations N.
975  uint32 num_iterations_id = 4;
976
977  // The label id of the block before which the loop must be inserted.
978  uint32 block_after_loop_id = 5;
979
980
981  // The following are fresh ids.
982
983  // A fresh id for the synonym.
984  uint32 syn_id = 6;
985
986  // A fresh id for the label of the loop,
987  uint32 loop_id = 7;
988
989  // A fresh id for the counter.
990  uint32 ctr_id = 8;
991
992  // A fresh id taking the value I - S * ctr at the ctr-th iteration.
993  uint32 temp_id = 9;
994
995  // A fresh id taking the value I - S * (ctr + 1) at the ctr-th iteration, and
996  // thus I - S * N at the last iteration.
997  uint32 eventual_syn_id = 10;
998
999  // A fresh id for the incremented counter.
1000  uint32 incremented_ctr_id = 11;
1001
1002  // A fresh id for the loop condition.
1003  uint32 cond_id = 12;
1004
1005  // The instructions in the loop can also be laid out in two basic blocks, as follows:
1006  //
1007  //  %loop_id = OpLabel
1008  //   %ctr_id = OpPhi %int %int_0 %pred %incremented_ctr_id %loop_id
1009  //  %temp_id = OpPhi %type_of_I %I %pred %eventual_syn_id %loop_id
1010  //             OpLoopMerge %block_after_loop_id %additional_block_id None
1011  //             OpBranch %additional_block_id
1012  //
1013  //  %additional_block_id = OpLabel
1014  //      %eventual_syn_id = OpISub %type_of_I %temp_id %step_val_id
1015  //   %incremented_ctr_id = OpIAdd %int %ctr_id %int_1
1016  //              %cond_id = OpSLessThan %bool %incremented_ctr_id %num_iterations_id
1017  //                         OpBranchConditional %cond_id %loop_id %block_after_loop_id
1018
1019  // A fresh id for the additional block. If this is 0, it means that only one
1020  // block is to be created.
1021  uint32 additional_block_id = 13;
1022}
1023
1024message TransformationAddNoContractionDecoration {
1025
1026  // Applies OpDecorate NoContraction to the given result id
1027
1028  // Result id to be decorated
1029  uint32 result_id = 1;
1030
1031}
1032
1033message TransformationAddOpPhiSynonym {
1034
1035  // Adds an OpPhi instruction at the start of a block with n predecessors (pred_1, pred_2, ..., pred_n)
1036  // and n related ids (id_1, id_2, ..., id_n) which are pairwise synonymous.
1037  // The instruction will be of the form:
1038  //       %fresh_id = OpPhi %type %id_1 %pred_1 %id_2 %pred_2 ... %id_n %pred_n
1039  // and fresh_id will be recorded as being synonymous with all the other ids.
1040
1041  // Label id of the block
1042  uint32 block_id = 1;
1043
1044  // Pairs (pred_i, id_i)
1045  repeated UInt32Pair pred_to_id = 2;
1046
1047  // Fresh id for the new instruction
1048  uint32 fresh_id = 3;
1049}
1050
1051message TransformationAddParameter {
1052
1053  // Adds a new parameter into the function.
1054
1055  // Result id of the function to add parameters to.
1056  uint32 function_id = 1;
1057
1058  // Fresh id for a new parameter.
1059  uint32 parameter_fresh_id = 2;
1060
1061  // Type id for a new parameter.
1062  uint32 parameter_type_id = 3;
1063
1064  // A map that maps from the OpFunctionCall id to the id that will be passed as the new
1065  // parameter at that call site. It must have the same type as that of the new parameter.
1066  repeated UInt32Pair call_parameter_ids = 4;
1067
1068  // A fresh id for a new function type. This might not be used
1069  // if a required function type already exists or if we can change
1070  // the old function type.
1071  uint32 function_type_fresh_id = 5;
1072
1073}
1074
1075message TransformationAddRelaxedDecoration {
1076
1077  // Applies OpDecorate RelaxedPrecision to the given result id
1078
1079  // Result id to be decorated
1080  uint32 result_id = 1;
1081
1082}
1083
1084message TransformationAddSpecConstantOp {
1085
1086  // Adds OpSpecConstantOp into the module.
1087
1088  // Result id for the new instruction.
1089  uint32 fresh_id = 1;
1090
1091  // Type id for the new instruction.
1092  uint32 type_id = 2;
1093
1094  // Opcode operand of the OpSpecConstantOp instruction.
1095  uint32 opcode = 3;
1096
1097  // Operands of the |opcode| instruction.
1098  repeated InstructionOperand operand = 4;
1099
1100}
1101
1102message TransformationAddSynonym {
1103
1104  // Adds a |synonymous_instruction| before |insert_before| instruction with
1105  // and creates a fact that |result_id| and the result id of |synonymous_instruction|
1106  // are synonymous.
1107
1108  // Result id of the first synonym.
1109  uint32 result_id = 1;
1110
1111  // Type of the synonym to apply. Some types might produce instructions
1112  // with commutative operands. Such types do not specify the order of the
1113  // operands since we have a special transformation to swap commutable operands.
1114  //
1115  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3499):
1116  //  Consider adding more types here.
1117  enum SynonymType {
1118    // New synonym is derived by adding zero to the |result_id|.
1119    ADD_ZERO = 0;
1120
1121    // New synonym is derived by subtracting zero from the |result_id|.
1122    SUB_ZERO = 1;
1123
1124    // New synonym is derived by multiplying |result_id| by one.
1125    MUL_ONE = 2;
1126
1127    // New synonym is derived by applying OpCopyObject instruction to |result_id|.
1128    COPY_OBJECT = 3;
1129
1130    // New synonym is derived by applying OpLogicalOr to |result_id| with the second
1131    // operand being 'false'.
1132    LOGICAL_OR = 4;
1133
1134    // New synonym is derived by applying OpLogicalAnd to |result_id| with the second
1135    // operand being 'true'.
1136    LOGICAL_AND = 5;
1137  }
1138
1139  // Type of the synonym to create. See SynonymType for more details.
1140  SynonymType synonym_type = 2;
1141
1142  // Fresh result id for a created synonym.
1143  uint32 synonym_fresh_id = 3;
1144
1145  // An instruction to insert a new synonym before.
1146  InstructionDescriptor insert_before = 4;
1147
1148}
1149
1150message TransformationAddTypeArray {
1151
1152  // Adds an array type of the given element type and size to the module
1153
1154  // Fresh id for the array type
1155  uint32 fresh_id = 1;
1156
1157  // The array's element type
1158  uint32 element_type_id = 2;
1159
1160  // The array's size
1161  uint32 size_id = 3;
1162
1163}
1164
1165message TransformationAddTypeBoolean {
1166
1167  // Adds OpTypeBool to the module
1168
1169  // Id to be used for the type
1170  uint32 fresh_id = 1;
1171
1172}
1173
1174message TransformationAddTypeFloat {
1175
1176  // Adds OpTypeFloat to the module with the given width
1177
1178  // Id to be used for the type
1179  uint32 fresh_id = 1;
1180
1181  // Floating-point width
1182  uint32 width = 2;
1183
1184}
1185
1186message TransformationAddTypeFunction {
1187
1188  // Adds a function type to the module
1189
1190  // Fresh id for the function type
1191  uint32 fresh_id = 1;
1192
1193  // The function's return type
1194  uint32 return_type_id = 2;
1195
1196  // The function's argument types
1197  repeated uint32 argument_type_id = 3;
1198
1199}
1200
1201message TransformationAddTypeInt {
1202
1203  // Adds OpTypeInt to the module with the given width and signedness
1204
1205  // Id to be used for the type
1206  uint32 fresh_id = 1;
1207
1208  // Integer width
1209  uint32 width = 2;
1210
1211  // True if and only if this is a signed type
1212  bool is_signed = 3;
1213
1214}
1215
1216message TransformationAddTypeMatrix {
1217
1218  // Adds a matrix type to the module
1219
1220  // Fresh id for the matrix type
1221  uint32 fresh_id = 1;
1222
1223  // The matrix's column type, which must be a floating-point vector (as per
1224  // the "data rules" in the SPIR-V specification).
1225  uint32 column_type_id = 2;
1226
1227  // The matrix's column count
1228  uint32 column_count = 3;
1229
1230}
1231
1232message TransformationAddTypePointer {
1233
1234  // Adds OpTypePointer to the module, with the given storage class and base
1235  // type
1236
1237  // Id to be used for the type
1238  uint32 fresh_id = 1;
1239
1240  // Pointer storage class
1241  uint32 storage_class = 2;
1242
1243  // Id of the base type for the pointer
1244  uint32 base_type_id = 3;
1245
1246}
1247
1248message TransformationAddTypeStruct {
1249
1250  // Adds a struct type to the module
1251
1252  // Fresh id for the struct type
1253  uint32 fresh_id = 1;
1254
1255  // The struct's member types
1256  repeated uint32 member_type_id = 3;
1257
1258}
1259
1260message TransformationAddTypeVector {
1261
1262  // Adds a vector type to the module
1263
1264  // Fresh id for the vector type
1265  uint32 fresh_id = 1;
1266
1267  // The vector's component type
1268  uint32 component_type_id = 2;
1269
1270  // The vector's component count
1271  uint32 component_count = 3;
1272
1273}
1274
1275message TransformationAdjustBranchWeights {
1276
1277  // A transformation that adjusts the branch weights
1278  // of a branch conditional instruction.
1279
1280  // A descriptor for a branch conditional instruction.
1281  InstructionDescriptor instruction_descriptor = 1;
1282
1283  // Branch weights of a branch conditional instruction.
1284  UInt32Pair branch_weights = 2;
1285
1286}
1287
1288message TransformationCompositeConstruct {
1289
1290  // A transformation that introduces an OpCompositeConstruct instruction to
1291  // make a composite object.
1292
1293  // Id of the type of the composite that is to be constructed
1294  uint32 composite_type_id = 1;
1295
1296  // Ids of the objects that will form the components of the composite
1297  repeated uint32 component = 2;
1298
1299  // A descriptor for an instruction in a block before which the new
1300  // OpCompositeConstruct instruction should be inserted
1301  InstructionDescriptor instruction_to_insert_before = 3;
1302
1303  // A fresh id for the composite object
1304  uint32 fresh_id = 4;
1305
1306}
1307
1308message TransformationCompositeExtract {
1309
1310  // A transformation that adds an instruction to extract an element from a
1311  // composite.
1312
1313  // A descriptor for an instruction in a block before which the new
1314  // OpCompositeExtract instruction should be inserted
1315  InstructionDescriptor instruction_to_insert_before = 1;
1316
1317  // Result id for the extract operation.
1318  uint32 fresh_id = 2;
1319
1320  // Id of the composite from which data is to be extracted.
1321  uint32 composite_id = 3;
1322
1323  // Indices that indicate which part of the composite should be extracted.
1324  repeated uint32 index = 4;
1325
1326}
1327
1328message TransformationCompositeInsert {
1329
1330  // A transformation that adds an instruction OpCompositeInsert which creates
1331  // a new composite from an existing composite, with an element inserted.
1332
1333  // A descriptor for an instruction before which the new instruction
1334  // OpCompositeInsert should be inserted.
1335  InstructionDescriptor instruction_to_insert_before = 1;
1336
1337  // Result id of the inserted OpCompositeInsert instruction.
1338  uint32 fresh_id = 2;
1339
1340  // Id of the composite used as the basis for the insertion.
1341  uint32 composite_id = 3;
1342
1343  // Id of the object to be inserted.
1344  uint32 object_id = 4;
1345
1346  // Indices that indicate which part of the composite should be inserted into.
1347  repeated uint32 index = 5;
1348
1349}
1350
1351message TransformationComputeDataSynonymFactClosure {
1352
1353  // A transformation that impacts the fact manager only, forcing a computation
1354  // of the closure of data synonym facts, so that e.g. if the components of
1355  // vectors v and w are known to be pairwise synonymous, it is deduced that v
1356  // and w are themselves synonymous.
1357
1358  // When searching equivalence classes for implied facts, equivalence classes
1359  // larger than this size will be skipped.
1360  uint32 maximum_equivalence_class_size = 1;
1361
1362}
1363
1364message TransformationDuplicateRegionWithSelection {
1365
1366  // A transformation that inserts a conditional statement with a boolean expression
1367  // of arbitrary value and duplicates a given single-entry, single-exit region, so
1368  // that it is present in each conditional branch and will be executed regardless
1369  // of which branch will be taken.
1370
1371  // Fresh id for a label of the new entry block.
1372  uint32 new_entry_fresh_id = 1;
1373
1374  // Id for a boolean expression.
1375  uint32 condition_id = 2;
1376
1377  // Fresh id for a label of the merge block of the conditional.
1378  uint32 merge_label_fresh_id = 3;
1379
1380  // Block id of the entry block of the original region.
1381  uint32 entry_block_id = 4;
1382
1383  // Block id of the exit block of the original region.
1384  uint32 exit_block_id = 5;
1385
1386  // Map that maps from a label in the original region to the corresponding label
1387  // in the duplicated region.
1388  repeated UInt32Pair original_label_to_duplicate_label = 6;
1389
1390  // Map that maps from a result id in the original region to the corresponding
1391  // result id in the duplicated region.
1392  repeated UInt32Pair original_id_to_duplicate_id = 7;
1393
1394  // Map that maps from a result id in the original region to the result id of the
1395  // corresponding OpPhi instruction.
1396  repeated UInt32Pair original_id_to_phi_id = 8;
1397}
1398
1399message TransformationEquationInstruction {
1400
1401  // A transformation that adds an instruction to the module that defines an
1402  // equation between its result id and input operand ids, such that the
1403  // equation is guaranteed to hold at any program point where all ids involved
1404  // are available (i.e. at any program point dominated by the instruction).
1405
1406  // The result id of the new instruction
1407  uint32 fresh_id = 1;
1408
1409  // The instruction's opcode
1410  uint32 opcode = 2;
1411
1412  // The input operands to the instruction
1413  repeated uint32 in_operand_id = 3;
1414
1415  // A descriptor for an instruction in a block before which the new
1416  // instruction should be inserted
1417  InstructionDescriptor instruction_to_insert_before = 4;
1418
1419}
1420
1421message TransformationExpandVectorReduction {
1422
1423  // A transformation that adds synonyms for OpAny and OpAll instructions by
1424  // evaluating each vector component with the corresponding logical operation.
1425  // There is a SPIR-V code example in the header file of the transformation
1426  // class that can help understand the transformation.
1427
1428  // The OpAny or OpAll instruction result id.
1429  uint32 instruction_result_id = 1;
1430
1431  // The fresh ids required to apply the transformation.
1432  repeated uint32 fresh_ids = 2;
1433
1434}
1435
1436message TransformationFlattenConditionalBranch {
1437
1438  // A transformation that takes a selection construct with a header
1439  // containing an OpBranchConditional instruction and flattens it.
1440  // For example, something of the form:
1441  //
1442  // %1 = OpLabel
1443  //      [header instructions]
1444  //      OpSelectionMerge %4 None
1445  //      OpBranchConditional %cond %2 %3
1446  // %2 = OpLabel
1447  //      [true branch instructions]
1448  //      OpBranch %4
1449  // %3 = OpLabel
1450  //      [false branch instructions]
1451  //      OpBranch %4
1452  // %4 = OpLabel
1453  //      ...
1454  //
1455  // becomes:
1456  //
1457  // %1 = OpLabel
1458  //      [header instructions]
1459  //      OpBranch %2
1460  // %2 = OpLabel
1461  //      [true branch instructions]
1462  //      OpBranch %3
1463  // %3 = OpLabel
1464  //      [false branch instructions]
1465  //      OpBranch %4
1466  // %4 = OpLabel
1467  //      ...
1468  //
1469  // If all of the instructions in the true or false branches have
1470  // no side effects, this is semantics-preserving.
1471  // Side-effecting instructions will instead be enclosed by smaller
1472  // conditionals. For more details, look at the definition for the
1473  // SideEffectWrapperInfo message.
1474  //
1475  // Nested conditionals or loops are not supported. The false branch
1476  // could also be executed before the true branch, depending on the
1477  // |true_branch_first| field.
1478
1479  // The label id of the header block
1480  uint32 header_block_id = 1;
1481
1482  // A boolean field deciding the order in which the original branches
1483  // will be laid out: the true branch will be laid out first iff this
1484  // field is true.
1485  bool true_branch_first = 2;
1486
1487  // If the convergence block contains an OpPhi with bvec2 result type, it may
1488  // be necessary to introduce a bvec2 with the selection construct's condition
1489  // in both components in order to turn the OpPhi into an OpSelect.  This
1490  // this field provides a fresh id for an OpCompositeConstruct instruction for
1491  // this purpose.  It should be set to 0 if no such instruction is required.
1492  uint32 fresh_id_for_bvec2_selector = 3;
1493
1494  // The same as |fresh_id_for_bvec2_selector| but for the bvec3 case.
1495  uint32 fresh_id_for_bvec3_selector = 4;
1496
1497  // The same as |fresh_id_for_bvec2_selector| but for the bvec4 case.
1498  uint32 fresh_id_for_bvec4_selector = 5;
1499
1500  // A list of instructions with side effects, which must be enclosed
1501  // inside smaller conditionals before flattening the main one, and
1502  // the corresponding fresh ids and module ids needed.
1503  repeated SideEffectWrapperInfo side_effect_wrapper_info = 6;
1504}
1505
1506message TransformationFunctionCall {
1507
1508  // A transformation that introduces an OpFunctionCall instruction.  The call
1509  // must not make the module's call graph cyclic.  Beyond that, if the call
1510  // is in a dead block it can be to any function with arbitrary suitably-typed
1511  // arguments; otherwise it must be to a livesafe function, with injected
1512  // variables as pointer arguments and arbitrary non-pointer arguments.
1513
1514  // A fresh id for the result of the call
1515  uint32 fresh_id = 1;
1516
1517  // Id of the function to be called
1518  uint32 callee_id = 2;
1519
1520  // Ids for arguments to the function
1521  repeated uint32 argument_id = 3;
1522
1523  // A descriptor for an instruction in a block before which the new
1524  // OpFunctionCall instruction should be inserted
1525  InstructionDescriptor instruction_to_insert_before = 4;
1526
1527}
1528
1529message TransformationInlineFunction {
1530
1531  // This transformation inlines a function by mapping the function instructions to fresh ids.
1532
1533  // Result id of the function call instruction.
1534  uint32 function_call_id = 1;
1535
1536  // For each result id defined by the called function,
1537  // this map provides an associated fresh id that can
1538  // be used in the inlined version of the function call.
1539  repeated UInt32Pair result_id_map = 2;
1540
1541}
1542
1543message TransformationInvertComparisonOperator {
1544
1545  // For some instruction with result id |operator_id| that
1546  // represents a binary comparison operator (e.g. <, >, <=), this transformation
1547  // will replace that instruction's result id with |fresh_id|,
1548  // invert the opcode (< will become >=) and insert OpLogicalNot
1549  // instruction with result id |operator_id| below.
1550
1551  // Result id of the instruction to invert.
1552  uint32 operator_id = 1;
1553
1554  // Fresh id that will be used by the operator after the inversion.
1555  uint32 fresh_id = 2;
1556
1557}
1558
1559message TransformationLoad {
1560
1561  // Transformation that adds an OpLoad instruction from a pointer into an id.
1562
1563  // The result of the load instruction
1564  uint32 fresh_id = 1;
1565
1566  // The pointer to be loaded from
1567  uint32 pointer_id = 2;
1568
1569  // A descriptor for an instruction in a block before which the new OpLoad
1570  // instruction should be inserted
1571  InstructionDescriptor instruction_to_insert_before = 3;
1572
1573}
1574
1575message TransformationMakeVectorOperationDynamic {
1576
1577  // A transformation that replaces the OpCompositeExtract and OpCompositeInsert
1578  // instructions with the OpVectorExtractDynamic and OpVectorInsertDynamic instructions.
1579
1580  // The composite instruction result id.
1581  uint32 instruction_result_id = 1;
1582
1583  // The OpCompositeExtract/Insert instructions accept integer literals as indices to the composite object.
1584  // However, the OpVectorInsert/ExtractDynamic instructions require its single index to be an integer instruction.
1585  // This is the result id of the integer instruction.
1586  uint32 constant_index_id = 2;
1587
1588}
1589
1590message TransformationMergeBlocks {
1591
1592  // A transformation that merges a block with its predecessor.
1593
1594  // The id of the block that is to be merged with its predecessor; the merged
1595  // block will have the *predecessor's* id.
1596  uint32 block_id = 1;
1597
1598}
1599
1600message TransformationMergeFunctionReturns {
1601
1602  // A transformation that modifies a function so that it does not return early,
1603  // so it only has one return statement (ignoring unreachable blocks).
1604  //
1605  // The function is enclosed inside an outer loop, that is only executed once,
1606  // and whose merge block is the new return block of the function.
1607  //
1608  // Each return instruction is replaced by:
1609  //     OpBranch %innermost_loop_merge
1610  // where %innermost_loop_merge is the innermost loop containing the return
1611  // instruction.
1612  //
1613  // Each merge block whose associated loop contains return instructions is
1614  // changed so that it branches to the merge block of the loop containing it,
1615  // as explained in the comments to the ReturnMergingInfo message.
1616  //
1617  // The new return block (the merge block of the new outer loop) will be of
1618  // the following form (if the return type is not void):
1619  //  %outer_return_id = OpLabel
1620  //   %return_val_id = OpPhi %return_type %val1 %block_1 %val2 %block_2 ...
1621  //                    OpReturnValue %return_val_id
1622  // where %block_k is either a return block that, in the original function, is
1623  // outside of any loops, or the merge block of a loop that contains return
1624  // instructions and is not, originally, nested inside another loop, and
1625  // %block_k is the corresponding return value.
1626  // If the function has void type, there will be no OpPhi instruction and the
1627  // last instruction will be OpReturn.
1628
1629  // The id of the function to which the transformation is being applied.
1630  uint32 function_id = 1;
1631
1632  // A fresh id for the header of the new outer loop.
1633  uint32 outer_header_id = 2;
1634
1635  // A fresh id for the new return block of the function,
1636  // i.e. the merge block of the new outer loop.
1637  uint32 outer_return_id = 3;
1638
1639  // A fresh id for the value that will be returned.
1640  // This is ignored if the function has void return type.
1641  uint32 return_val_id = 4;
1642
1643  // An existing id of the same type as the return value, which is
1644  // available to use at the end of the entry block.
1645  // This is ignored if the function has void return type or if no
1646  // loops in the function contain a return instruction.
1647  // If the function is not void, the transformation will add an
1648  // OpPhi instruction to each merge block whose associated loop
1649  // contains at least a return instruction. The value associated
1650  // with existing predecessors from which the function cannot be
1651  // returning will be this id, used as a placeholder.
1652  uint32 any_returnable_val_id = 5;
1653
1654  // The information needed to modify the merge blocks of
1655  // loops containing return instructions.
1656  repeated ReturnMergingInfo return_merging_info = 6;
1657}
1658
1659message TransformationMoveBlockDown {
1660
1661  // A transformation that moves a basic block to be one position lower in
1662  // program order.
1663
1664  // The id of the block to move down.
1665  uint32 block_id = 1;
1666}
1667
1668message TransformationMoveInstructionDown {
1669
1670  // Swaps |instruction| with the next instruction in the block.
1671
1672  // The instruction to move down.
1673  InstructionDescriptor instruction = 1;
1674
1675}
1676
1677message TransformationMutatePointer {
1678
1679  // Backs up value of the pointer, writes into the pointer and
1680  // restores the original value.
1681
1682  // Result id of the pointer instruction to mutate.
1683  uint32 pointer_id = 1;
1684
1685  // Fresh id for the OpLoad instruction.
1686  uint32 fresh_id = 2;
1687
1688  // Instruction to insert backup, mutation and restoration code before.
1689  InstructionDescriptor insert_before = 3;
1690
1691}
1692
1693message TransformationOutlineFunction {
1694
1695  // A transformation that outlines a single-entry single-exit region of a
1696  // control flow graph into a separate function, and replaces the region with
1697  // a call to that function.
1698
1699  // Id of the entry block of the single-entry single-exit region to be outlined
1700  uint32 entry_block = 1;
1701
1702  // Id of the exit block of the single-entry single-exit region to be outlined
1703  uint32 exit_block = 2;
1704
1705  // Id of a struct that will store the return values of the new function
1706  uint32 new_function_struct_return_type_id = 3;
1707
1708  // A fresh id for the type of the outlined function
1709  uint32 new_function_type_id = 4;
1710
1711  // A fresh id for the outlined function itself
1712  uint32 new_function_id = 5;
1713
1714  // A fresh id to represent the block in the outlined function that represents
1715  // the first block of the outlined region.
1716  uint32 new_function_region_entry_block = 6;
1717
1718  // A fresh id for the result of the OpFunctionCall instruction that will call
1719  // the outlined function
1720  uint32 new_caller_result_id = 7;
1721
1722  // A fresh id to capture the return value of the outlined function - the
1723  // argument to OpReturn
1724  uint32 new_callee_result_id = 8;
1725
1726  // Ids defined outside the region and used inside the region will become
1727  // parameters to the outlined function.  This is a mapping from used ids to
1728  // fresh parameter ids.
1729  repeated UInt32Pair input_id_to_fresh_id = 9;
1730
1731  // Ids defined inside the region and used outside the region will become
1732  // fresh ids defined by the outlined function, which get copied into the
1733  // function's struct return value and then copied into their destination ids
1734  // by the caller.  This is a mapping from original ids to corresponding fresh
1735  // ids.
1736  repeated UInt32Pair output_id_to_fresh_id = 10;
1737
1738}
1739
1740message TransformationPermuteFunctionParameters {
1741
1742  // A transformation that, given a non-entry-point function taking n
1743  // parameters and a permutation of the set [0, n-1]:
1744  //   - Introduces a new function type that is the same as the original
1745  //     function's type but with the order of arguments permuted
1746  //     (only if it doesn't already exist)
1747  //   - Changes the type of the function to this type
1748  //   - Adjusts all calls to the function so that their arguments are permuted
1749
1750  // Function, whose parameters will be permuted
1751  uint32 function_id = 1;
1752
1753  // Fresh id for a new type of the function. This might not be used
1754  // if a required function type already exists or if we can change
1755  // the old function type.
1756  uint32 function_type_fresh_id = 2;
1757
1758  // An array of size |n|, where |n| is a number of arguments to a function
1759  // with |function_id|. For each i: 0 <= permutation[i] < n.
1760  //
1761  // i-th element of this array contains a position for an i-th
1762  // function's argument (i.e. i-th argument will be permutation[i]-th
1763  // after running this transformation)
1764  repeated uint32 permutation = 3;
1765
1766}
1767
1768message TransformationPermutePhiOperands {
1769
1770  // Permutes operands of some OpPhi instruction.
1771
1772  // Result id of the instruction to apply the transformation to.
1773  uint32 result_id = 1;
1774
1775  // A sequence of numbers in the range [0, n/2 - 1] where |n| is the number
1776  // of operands of the OpPhi instruction with |result_id|.
1777  repeated uint32 permutation = 2;
1778
1779}
1780
1781message TransformationPropagateInstructionDown {
1782
1783  // Propagates an instruction from |block_id| into its successors.
1784  // Concretely, the transformation clones the propagated instruction
1785  // into some of the successors of |block_id| and removes the original
1786  // instruction. Additionally, an OpPhi instruction may be added to make sure
1787  // that the transformation can be applied in various scenarios.
1788  //
1789  // Note that the instruction might not be propagated down into every successor
1790  // of |block_id| since it might make the module invalid.
1791
1792  // Id of the block to propagate an instruction from. The decision on what
1793  // instruction to propagate is made based on whether the instruction interacts
1794  // with memory, whether that instruction is used in its block etc (see the
1795  // transformation class for more details).
1796  uint32 block_id = 1;
1797
1798  // A fresh id for an OpPhi instruction. This might not be used by the
1799  // transformation since an OpPhi instruction is created only if needed
1800  // (e.g. an instruction is propagated into divergent blocks).
1801  uint32 phi_fresh_id = 2;
1802
1803  // A map from the id of some successor of the |block_id| to the fresh id.
1804  // The map contains a fresh id for at least every successor of the |block_id|.
1805  // Every fresh id in the map corresponds to the result id of the clone,
1806  // propagated into the corresponding successor block. This transformation
1807  // might use overflow ids if they are available and this field doesn't account
1808  // for every successor of |block_id|.
1809  repeated UInt32Pair successor_id_to_fresh_id = 3;
1810
1811}
1812
1813message TransformationPropagateInstructionUp {
1814
1815  // Propagates an instruction in the block into the block's predecessors.
1816  // Concretely, this transformation clones some particular instruction from
1817  // the |block_id| into every block's predecessor and replaces the original
1818  // instruction with OpPhi. Take a look at the transformation class to learn
1819  // more about how we choose what instruction to propagate.
1820
1821  // Id of the block to propagate an instruction from.
1822  uint32 block_id = 1;
1823
1824  // A map from the id of some predecessor of the |block_id| to the fresh id.
1825  // The map contains a fresh id for at least every predecessor of the |block_id|.
1826  // The instruction is propagated by creating a number of clones - one clone for
1827  // each predecessor. Fresh ids from this field are used as result ids of cloned
1828  // instructions.
1829  repeated UInt32Pair predecessor_id_to_fresh_id = 2;
1830
1831}
1832
1833message TransformationPushIdThroughVariable {
1834
1835  // A transformation that makes |value_synonym_id| and |value_id| to be
1836  // synonymous by storing |value_id| into |variable_id| and
1837  // loading |variable_id| to |value_synonym_id|.
1838
1839  // The value to be stored.
1840  uint32 value_id = 1;
1841
1842  // A fresh id for the result of the load instruction.
1843  uint32 value_synonym_id = 2;
1844
1845  // A fresh id for the variable to be stored to.
1846  uint32 variable_id = 3;
1847
1848  // Constant to initialize the variable from.
1849  uint32 initializer_id = 4;
1850
1851  // The variable storage class (global or local).
1852  uint32 variable_storage_class = 5;
1853
1854  // A descriptor for an instruction which the new OpStore
1855  // and OpLoad instructions might be inserted before.
1856  InstructionDescriptor instruction_descriptor = 6;
1857
1858}
1859
1860message TransformationRecordSynonymousConstants {
1861
1862  // A transformation that, given the IDs to two synonymous constants,
1863  // records the fact that they are synonymous. The module is not changed.
1864  // Two constants are synonymous if:
1865  // - they have the same type (ignoring the presence of integer sign)
1866  // - they have the same opcode (one of OpConstant, OpConstantTrue,
1867  //   OpConstantFalse, OpConstantNull)
1868  // - they have the same value
1869  // If the types are the same, OpConstantNull is equivalent to
1870  // OpConstantFalse or OpConstant with value zero.
1871
1872  // The id of a constant
1873  uint32 constant1_id = 1;
1874
1875  // The id of the synonym
1876  uint32 constant2_id = 2;
1877
1878}
1879
1880message TransformationReplaceAddSubMulWithCarryingExtended {
1881
1882  // Replaces OpIAdd with OpIAddCarry, OpISub with OpISubBorrow, OpIMul
1883  // with OpUMulExtended or OpSMulExtended (depending on the signedness
1884  // of the operands) and stores the result into a |struct_fresh_id|.
1885  // In the original instruction the result type id and the type ids of
1886  // the operands must be the same. Then the transformation extracts
1887  // the first element of the result into the original |result_id|.
1888  // This value is the same as the result of the original instruction.
1889
1890  // The fresh id of the intermediate result.
1891  uint32 struct_fresh_id = 1;
1892
1893  // The result id of the original instruction.
1894  uint32 result_id = 2;
1895
1896}
1897
1898message TransformationReplaceBranchFromDeadBlockWithExit {
1899
1900  // Given a dead block that ends with OpBranch, replaces OpBranch with an
1901  // "exit" instruction; one of OpReturn/OpReturnValue, OpKill (in a fragment
1902  // shader) or OpUnreachable.
1903
1904  // The dead block whose terminator is to be replaced.
1905  uint32 block_id = 1;
1906
1907  // The opcode of the new terminator.
1908  uint32 opcode = 2;
1909
1910  // Ignored unless opcode is OpReturnValue, in which case this field provides
1911  // a suitable result id to be returned.
1912  uint32 return_value_id = 3;
1913
1914}
1915
1916message TransformationReplaceParameterWithGlobal {
1917
1918  // Removes parameter with result id |parameter_id| from its function
1919  // and creates a global variable to pass its value to the function instead.
1920
1921  // Fresh id for a new function type. This might not be used if a required
1922  // function type already exists or if we can change the old function type.
1923  uint32 function_type_fresh_id = 2;
1924
1925  // Result id of the OpFunctionParameter instruction to remove.
1926  uint32 parameter_id = 3;
1927
1928  // Fresh id of a global variable used to pass parameter's value to the function.
1929  uint32 global_variable_fresh_id = 4;
1930
1931}
1932
1933message TransformationReplaceBooleanConstantWithConstantBinary {
1934
1935  // A transformation to capture replacing a use of a boolean constant with
1936  // binary operation on two constant values
1937
1938  // A descriptor for the boolean constant id we would like to replace
1939  IdUseDescriptor id_use_descriptor = 1;
1940
1941  // Id for the constant to be used on the LHS of the comparision
1942  uint32 lhs_id = 2;
1943
1944  // Id for the constant to be used on the RHS of the comparision
1945  uint32 rhs_id = 3;
1946
1947  // Opcode for binary operator
1948  uint32 opcode = 4;
1949
1950  // Id that will store the result of the binary operation instruction
1951  uint32 fresh_id_for_binary_operation = 5;
1952
1953}
1954
1955message TransformationReplaceConstantWithUniform {
1956
1957  // Replaces a use of a constant id with the result of a load from an
1958  // element of uniform buffer known to hold the same value as the constant
1959
1960  // A descriptor for the id we would like to replace
1961  IdUseDescriptor id_use_descriptor = 1;
1962
1963  // Uniform descriptor to identify which uniform value to choose
1964  UniformBufferElementDescriptor uniform_descriptor = 2;
1965
1966  // Id that will store the result of an access chain
1967  uint32 fresh_id_for_access_chain = 3;
1968
1969  // Id that will store the result of a load
1970  uint32 fresh_id_for_load = 4;
1971
1972}
1973
1974message TransformationReplaceCopyMemoryWithLoadStore {
1975
1976  // A transformation that replaces instructions OpCopyMemory with loading
1977  // the source variable to an intermediate value and storing this value into the
1978  // target variable of the original OpCopyMemory instruction.
1979
1980  // The intermediate value.
1981  uint32 fresh_id = 1;
1982
1983  // The instruction descriptor to OpCopyMemory. It is necessary, because
1984  // OpCopyMemory doesn't have a result id.
1985  InstructionDescriptor copy_memory_instruction_descriptor = 2;
1986}
1987
1988message TransformationReplaceCopyObjectWithStoreLoad {
1989
1990  // A transformation that replaces instruction OpCopyObject with
1991  // storing into a new variable and immediately loading from this
1992  // variable to |result_id| of the original OpCopyObject instruction.
1993
1994  // The result id of initial OpCopyObject instruction
1995  uint32 copy_object_result_id = 1;
1996
1997  // A fresh id for the variable to be stored to.
1998  uint32 fresh_variable_id = 2;
1999
2000  // The variable storage class (Function or Private).
2001  uint32 variable_storage_class = 3;
2002
2003  // Constant to initialize the variable with.
2004  uint32 variable_initializer_id = 4;
2005}
2006
2007message TransformationReplaceIdWithSynonym {
2008
2009  // Replaces a use of an id with an id that is known to be synonymous, e.g.
2010  // because it was obtained via applying OpCopyObject
2011
2012  // The id use that is to be replaced
2013  IdUseDescriptor id_use_descriptor = 1;
2014
2015  // The synonymous id
2016  uint32 synonymous_id = 2;
2017
2018}
2019
2020message TransformationReplaceIrrelevantId {
2021
2022  // Replaces an irrelevant id with another id of the same type.
2023
2024  // The id use that is to be replaced
2025  IdUseDescriptor id_use_descriptor = 1;
2026
2027  // The replacement id
2028  uint32 replacement_id = 2;
2029}
2030
2031message TransformationReplaceLinearAlgebraInstruction {
2032
2033  // Replaces a linear algebra instruction with its
2034  // mathematical definition.
2035
2036  // The fresh ids needed to apply the transformation.
2037  repeated uint32 fresh_ids = 1;
2038
2039  // A descriptor for a linear algebra instruction.
2040  InstructionDescriptor instruction_descriptor = 2;
2041
2042}
2043
2044message TransformationReplaceLoadStoreWithCopyMemory {
2045  // A transformation that takes a pair of instruction descriptors
2046  // to OpLoad and OpStore that have the same intermediate value
2047  // and replaces the OpStore with an equivalent OpCopyMemory.
2048
2049  // The instruction descriptor to OpLoad
2050  InstructionDescriptor load_instruction_descriptor = 1;
2051
2052  // The instruction descriptor to OpStore
2053  InstructionDescriptor store_instruction_descriptor = 2;
2054}
2055
2056message TransformationReplaceOpPhiIdFromDeadPredecessor {
2057
2058  // Replaces one of the ids used by an OpPhi instruction, when
2059  // the corresponding predecessor is dead, with any available id
2060  // of the correct type.
2061
2062  // The result id of the OpPhi instruction.
2063  uint32 opphi_id = 1;
2064
2065  // The label id of one of the predecessors of the block containing
2066  // the OpPhi instruction, corresponding to the id that we want to
2067  // replace.
2068  uint32 pred_label_id = 2;
2069
2070  // The id that, after the transformation, will be associated with
2071  // the given predecessor.
2072  uint32 replacement_id = 3;
2073
2074}
2075
2076message TransformationReplaceOpSelectWithConditionalBranch {
2077
2078  // A transformation that takes an OpSelect instruction with a
2079  // scalar boolean condition and replaces it with a conditional
2080  // branch and an OpPhi instruction.
2081  // The OpSelect instruction must be the first instruction in its
2082  // block, which must have a unique predecessor. The block will
2083  // become the merge block of a new construct, while its predecessor
2084  // will become the header.
2085  // Given the original OpSelect instruction:
2086  //   %id = OpSelect %type %cond %then %else
2087  // The branching instruction of the header will be:
2088  //         OpBranchConditional %cond %true_block_id %false_block_id
2089  // and the OpSelect instruction will be turned into:
2090  //   %id = OpPhi %type %then %true_block_id %else %false_block_id
2091  // At most one of |true_block_id| and |false_block_id| can be zero. In
2092  // that case, there will be no such block and all references to it
2093  // will be replaced by %merge_block (where %merge_block is the
2094  // block containing the OpSelect instruction).
2095
2096  // The result id of the OpSelect instruction.
2097  uint32 select_id = 1;
2098
2099  // A fresh id for the new block that the predecessor of the block
2100  // containing |select_id| will branch to if the condition holds.
2101  uint32 true_block_id = 2;
2102
2103  // A fresh id for the new block that the predecessor of the block
2104  // containing |select_id| will branch to if the condition does not
2105  // hold.
2106  uint32 false_block_id = 3;
2107}
2108
2109message TransformationReplaceParamsWithStruct {
2110
2111  // Replaces parameters of the function with a struct containing
2112  // values of those parameters.
2113
2114  // Result ids of parameters to replace.
2115  repeated uint32 parameter_id = 1;
2116
2117  // Fresh id for a new function type. This might be unused if the required type
2118  // already exists in the module or if we can change the old type.
2119  uint32 fresh_function_type_id = 2;
2120
2121  // Fresh id for a new struct function parameter to be used as a replacement.
2122  uint32 fresh_parameter_id = 3;
2123
2124  // Fresh ids for struct objects containing values of replaced parameters.
2125  // This field contains a fresh id for at least every result id of a relevant
2126  // OpFunctionCall instruction.
2127  repeated UInt32Pair caller_id_to_fresh_composite_id = 4;
2128
2129}
2130
2131message TransformationSetFunctionControl {
2132
2133  // A transformation that sets the function control operand of an OpFunction
2134  // instruction.
2135
2136  // The result id of an OpFunction instruction
2137  uint32 function_id = 1;
2138
2139  // The value to which the 'function control' operand should be set.
2140  uint32 function_control = 2;
2141
2142}
2143
2144message TransformationSetLoopControl {
2145
2146  // A transformation that sets the loop control operand of an OpLoopMerge
2147  // instruction.
2148
2149  // The id of a basic block that should contain OpLoopMerge
2150  uint32 block_id = 1;
2151
2152  // The value to which the 'loop control' operand should be set.
2153  // This must be a legal loop control mask.
2154  uint32 loop_control = 2;
2155
2156  // Provides a peel count value for the loop.  Used if and only if the
2157  // PeelCount bit is set.  Must be zero if the PeelCount bit is not set (can
2158  // still be zero if this bit is set).
2159  uint32 peel_count = 3;
2160
2161  // Provides a partial count value for the loop.  Used if and only if the
2162  // PartialCount bit is set.  Must be zero if the PartialCount bit is not set
2163  // (can still be zero if this bit is set).
2164  uint32 partial_count = 4;
2165
2166}
2167
2168message TransformationSetMemoryOperandsMask {
2169
2170  // A transformation that sets the memory operands mask of a memory access
2171  // instruction.
2172
2173  // A descriptor for a memory access instruction, e.g. an OpLoad
2174  InstructionDescriptor memory_access_instruction = 1;
2175
2176  // A mask of memory operands to be applied to the instruction.  It must be the
2177  // same as the original mask, except that Volatile can be added, and
2178  // Nontemporal can be added or removed.
2179  uint32 memory_operands_mask = 2;
2180
2181  // Some memory access instructions allow more than one mask to be specified;
2182  // this field indicates which mask should be set
2183  uint32 memory_operands_mask_index = 3;
2184
2185}
2186
2187message TransformationSetSelectionControl {
2188
2189  // A transformation that sets the selection control operand of an
2190  // OpSelectionMerge instruction.
2191
2192  // The id of a basic block that should contain OpSelectionMerge
2193  uint32 block_id = 1;
2194
2195  // The value to which the 'selection control' operand should be set.
2196  // Although technically 'selection control' is a literal mask that can be
2197  // some combination of 'None', 'Flatten' and 'DontFlatten', the combination
2198  // 'Flatten | DontFlatten' does not make sense and is not allowed here.
2199  uint32 selection_control = 2;
2200
2201}
2202
2203message TransformationSplitBlock {
2204
2205  // A transformation that splits a basic block into two basic blocks
2206
2207  // A descriptor for an instruction such that the block containing the
2208  // described instruction should be split right before the instruction.
2209  InstructionDescriptor instruction_to_split_before = 1;
2210
2211  // An id that must not yet be used by the module to which this transformation
2212  // is applied.  Rather than having the transformation choose a suitable id on
2213  // application, we require the id to be given upfront in order to facilitate
2214  // reducing fuzzed shaders by removing transformations.  The reason is that
2215  // future transformations may refer to the fresh id introduced by this
2216  // transformation, and if we end up changing what that id is, due to removing
2217  // earlier transformations, it may inhibit later transformations from
2218  // applying.
2219  uint32 fresh_id = 2;
2220
2221}
2222
2223message TransformationStore {
2224
2225  // Transformation that adds an OpStore instruction of an id to a pointer.
2226
2227  // The pointer to be stored to
2228  uint32 pointer_id = 1;
2229
2230  // The value to be stored
2231  uint32 value_id = 2;
2232
2233  // A descriptor for an instruction in a block before which the new OpStore
2234  // instruction should be inserted
2235  InstructionDescriptor instruction_to_insert_before = 3;
2236
2237}
2238
2239message TransformationSwapCommutableOperands {
2240
2241  // A transformation that swaps the operands of a commutative instruction.
2242
2243  // A descriptor for a commutative instruction
2244  InstructionDescriptor instruction_descriptor = 1;
2245
2246}
2247
2248message TransformationSwapConditionalBranchOperands {
2249
2250  // Swaps label ids in OpBranchConditional instruction.
2251  // Additionally, inverts the guard and swaps branch weights
2252  // if present.
2253
2254  // Descriptor of the instruction to swap operands of.
2255  InstructionDescriptor instruction_descriptor = 1;
2256
2257  // Fresh result id for the OpLogicalNot instruction, used
2258  // to invert the guard.
2259  uint32 fresh_id = 2;
2260
2261}
2262
2263message TransformationToggleAccessChainInstruction {
2264
2265  // A transformation that toggles an access chain instruction.
2266
2267  // A descriptor for an access chain instruction
2268  InstructionDescriptor instruction_descriptor = 1;
2269
2270}
2271
2272message TransformationVectorShuffle {
2273
2274  // A transformation that adds a vector shuffle instruction.
2275
2276  // A descriptor for an instruction in a block before which the new
2277  // OpVectorShuffle instruction should be inserted
2278  InstructionDescriptor instruction_to_insert_before = 1;
2279
2280  // Result id for the shuffle operation.
2281  uint32 fresh_id = 2;
2282
2283  // Id of the first vector operand.
2284  uint32 vector1 = 3;
2285
2286  // Id of the second vector operand.
2287  uint32 vector2 = 4;
2288
2289  // Indices that indicate which components of the input vectors should be used.
2290  repeated uint32 component = 5;
2291
2292}
2293
2294message TransformationWrapEarlyTerminatorInFunction {
2295
2296  // Replaces an early terminator - OpKill, OpReachable or OpTerminateInvocation
2297  // - with a call to a wrapper function for the terminator.
2298
2299  // A fresh id for a new OpFunctionCall instruction.
2300  uint32 fresh_id = 1;
2301
2302  // A descriptor for an OpKill, OpUnreachable or OpTerminateInvocation
2303  // instruction.
2304  InstructionDescriptor early_terminator_instruction = 2;
2305
2306  // An id with the same type as the enclosing function's return type that is
2307  // available at the early terminator.  This is used to change the terminator
2308  // to OpReturnValue.  Ignored if the enclosing function has void return type,
2309  // in which case OpReturn can be used as the new terminator.
2310  uint32 returned_value_id = 3;
2311
2312}
2313
2314message TransformationWrapRegionInSelection {
2315
2316  // Transforms a single-entry-single-exit region R into
2317  // if (|branch_condition|) { R } else { R }
2318  // The entry block for R becomes a selection header and
2319  // the exit block - a selection merge.
2320  //
2321  // Note that the region R is not duplicated. Thus, the effect of
2322  // this transformation can be represented as follows:
2323  //              entry
2324  //  entry        / \
2325  //    |          \ /
2326  //    R   -->     R
2327  //    |           |
2328  //   exit        exit
2329
2330  // This behaviour is different from TransformationDuplicateRegionWithSelection
2331  // that copies the blocks in R.
2332
2333  // The entry block for the region R.
2334  uint32 region_entry_block_id = 1;
2335
2336  // The exit block for the region R.
2337  uint32 region_exit_block_id = 2;
2338
2339  // Boolean value for the condition expression.
2340  bool branch_condition = 3;
2341
2342}
2343