• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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    TransformationSwapFunctionVariables swap_function_variables = 86;
561    TransformationSwapTwoFunctions swap_two_functions = 87;
562    TransformationWrapVectorSynonym wrap_vector_synonym = 88;
563    // Add additional option using the next available number.
564  }
565}
566
567// Keep transformation message types in alphabetical order:
568
569message TransformationAccessChain {
570
571  // Adds an access chain instruction based on a given pointer and indices.
572
573  // When accessing a struct, the corresponding indices must be 32-bit integer constants.
574  // For any other composite, the indices can be any 32-bit integer, and the transformation
575  // adds two instructions for each such index to clamp it to the bound, as follows:
576  //
577  // %t1 = OpULessThanEqual %bool %index %bound_minus_one
578  // %t2 = OpSelect %int_type %t1 %index %bound_minus_one
579
580  // Result id for the access chain
581  uint32 fresh_id = 1;
582
583  // The pointer from which the access chain starts
584  uint32 pointer_id = 2;
585
586  // Zero or more access chain indices
587  repeated uint32 index_id = 3;
588
589  // A descriptor for an instruction in a block before which the new
590  // OpAccessChain instruction should be inserted
591  InstructionDescriptor instruction_to_insert_before = 4;
592
593  // Additional fresh ids, required to clamp index variables. A pair is needed
594  // for each access to a non-struct composite.
595  repeated UInt32Pair fresh_ids_for_clamping = 5;
596
597}
598
599message TransformationAddBitInstructionSynonym {
600
601  // A transformation that adds synonyms for bit instructions by evaluating
602  // each bit with the corresponding operation. There is a SPIR-V code example in the
603  // header file of the transformation class that can help understand the transformation.
604
605  // This transformation is only applicable if the described instruction has one of the following opcodes.
606  // Supported:
607  //   OpBitwiseOr
608  //   OpBitwiseXor
609  //   OpBitwiseAnd
610  //   OpNot
611  // To be supported in the future:
612  //   OpShiftRightLogical
613  //   OpShiftRightArithmetic
614  //   OpShiftLeftLogical
615  //   OpBitReverse
616  //   OpBitCount
617
618  // The bit instruction result id.
619  uint32 instruction_result_id = 1;
620
621  // The fresh ids required to apply the transformation.
622  repeated uint32 fresh_ids = 2;
623
624}
625
626message TransformationAddConstantBoolean {
627
628  // Supports adding the constants true and false to a module, which may be
629  // necessary in order to enable other transformations if they are not present.
630  // Also, creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is true.
631
632  uint32 fresh_id = 1;
633  bool is_true = 2;
634
635  // If the constant should be marked as irrelevant.
636  bool is_irrelevant = 3;
637
638}
639
640message TransformationAddConstantComposite {
641
642  // Adds a constant of the given composite type to the module.
643  // Also, creates an IdIsIrrelevant fact about |fresh_id| if
644  // |is_irrelevant| is true.
645
646  // Fresh id for the composite
647  uint32 fresh_id = 1;
648
649  // A composite type id
650  uint32 type_id = 2;
651
652  // Constituent ids for the composite
653  repeated uint32 constituent_id = 3;
654
655  // If the constant should be marked as irrelevant.
656  bool is_irrelevant = 4;
657
658}
659
660message TransformationAddConstantNull {
661
662  // Adds a null constant.
663
664  // Id for the constant
665  uint32 fresh_id = 1;
666
667  // Type of the constant
668  uint32 type_id = 2;
669
670}
671
672message TransformationAddConstantScalar {
673
674  // Adds a constant of the given scalar type.
675  // Also, creates an IdIsIrrelevant fact about
676  // |fresh_id| if |is_irrelevant| is true.
677
678  // Id for the constant
679  uint32 fresh_id = 1;
680
681  // Id for the scalar type of the constant
682  uint32 type_id = 2;
683
684  // Value of the constant
685  repeated uint32 word = 3;
686
687  // If the constant should be marked as irrelevant.
688  bool is_irrelevant = 4;
689
690}
691
692message TransformationAddCopyMemory {
693
694  // Adds an OpCopyMemory instruction into the module.
695  // Creates either a global or a local variable (based on
696  // |storage_class| field) to copy the target into.
697
698  // OpCopyMemory will be inserted before this instruction.
699  InstructionDescriptor instruction_descriptor = 1;
700
701  // Fresh id to copy memory into.
702  uint32 fresh_id = 2;
703
704  // Source to copy memory from.
705  uint32 source_id = 3;
706
707  // Storage class for the target variable. Can be either Function or Private.
708  uint32 storage_class = 4;
709
710  // Result id for the variable's initializer operand. Its type must be equal to
711  // variable's pointee type.
712  uint32 initializer_id = 5;
713
714}
715
716message TransformationAddDeadBlock {
717
718  // Adds a new block to the module that is statically reachable from an
719  // existing block, but dynamically unreachable.
720
721  // Fresh id for the dead block
722  uint32 fresh_id = 1;
723
724  // Id of an existing block terminated with OpBranch, such that this OpBranch
725  // can be replaced with an OpBranchConditional to its exiting successor or
726  // the dead block
727  uint32 existing_block = 2;
728
729  // Determines whether the condition associated with the OpBranchConditional
730  // is true or false
731  bool condition_value = 3;
732
733}
734
735message TransformationAddDeadBreak {
736
737  // A transformation that turns a basic block that unconditionally branches to
738  // its successor into a block that potentially breaks out of a structured
739  // control flow construct, but in such a manner that the break cannot actually
740  // be taken.
741
742  // The block to break from
743  uint32 from_block = 1;
744
745  // The merge block to break to
746  uint32 to_block = 2;
747
748  // Determines whether the break condition is true or false
749  bool break_condition_value = 3;
750
751  // A sequence of ids suitable for extending OpPhi instructions as a result of
752  // the new break edge
753  repeated uint32 phi_id = 4;
754
755}
756
757message TransformationAddDeadContinue {
758
759  // A transformation that turns a basic block appearing in a loop and that
760  // unconditionally branches to its successor into a block that potentially
761  // branches to the continue target of the loop, but in such a manner that the
762  // continue branch cannot actually be taken.
763
764  // The block to continue from
765  uint32 from_block = 1;
766
767  // Determines whether the continue condition is true or false
768  bool continue_condition_value = 2;
769
770  // A sequence of ids suitable for extending OpPhi instructions as a result of
771  // the new break edge
772  repeated uint32 phi_id = 3;
773
774}
775
776message TransformationAddEarlyTerminatorWrapper {
777
778  // Adds a function to the module containing a single block with a single non-
779  // label instruction that is either OpKill, OpUnreachable, or
780  // OpTerminateInvocation.  The purpose of this is to allow such instructions
781  // to be subsequently replaced with wrapper functions, which can then enable
782  // transformations (such as inlining) that are hard in the direct presence
783  // of these instructions.
784
785  // Fresh id for the function.
786  uint32 function_fresh_id = 1;
787
788  // Fresh id for the single basic block in the function.
789  uint32 label_fresh_id = 2;
790
791  // One of OpKill, OpUnreachable, OpTerminateInvocation.  If additional early
792  // termination instructions are added to SPIR-V they should also be handled
793  // here.
794  uint32 opcode = 3;
795
796}
797
798message TransformationAddFunction {
799
800  // Adds a SPIR-V function to the module.
801
802  // The series of instructions that comprise the function.
803  repeated Instruction instruction = 1;
804
805  // True if and only if the given function should be made livesafe (see
806  // FactFunctionIsLivesafe for definition).
807  bool is_livesafe = 2;
808
809  // Fresh id for a new variable that will serve as a "loop limiter" for the
810  // function; only relevant if |is_livesafe| holds.
811  uint32 loop_limiter_variable_id = 3;
812
813  // Id of an existing unsigned integer constant providing the maximum value
814  // that the loop limiter can reach before the loop is broken from; only
815  // relevant if |is_livesafe| holds.
816  uint32 loop_limit_constant_id = 4;
817
818  // Fresh ids for each loop in the function that allow the loop limiter to be
819  // manipulated; only relevant if |is_livesafe| holds.
820  repeated LoopLimiterInfo loop_limiter_info = 5;
821
822  // Id of an existing global value with the same return type as the function
823  // that can be used to replace OpKill and OpReachable instructions with
824  // ReturnValue instructions.  Ignored if the function has void return type.
825  // Only relevant if |is_livesafe| holds.
826  uint32 kill_unreachable_return_value_id = 6;
827
828  // A mapping (represented as a sequence) from every access chain result id in
829  // the function to the ids required to clamp its indices to ensure they are in
830  // bounds; only relevant if |is_livesafe| holds.
831  repeated AccessChainClampingInfo access_chain_clamping_info = 7;
832
833}
834
835message TransformationAddGlobalUndef {
836
837  // Adds an undefined value of a given type to the module at global scope.
838
839  // Fresh id for the undefined value
840  uint32 fresh_id = 1;
841
842  // The type of the undefined value
843  uint32 type_id = 2;
844
845}
846
847message TransformationAddGlobalVariable {
848
849  // Adds a global variable of the given type to the module, with Private or
850  // Workgroup storage class, and optionally (for the Private case) with an
851  // initializer.
852
853  // Fresh id for the global variable
854  uint32 fresh_id = 1;
855
856  // The type of the global variable
857  uint32 type_id = 2;
858
859  uint32 storage_class = 3;
860
861  // Initial value of the variable
862  uint32 initializer_id = 4;
863
864  // True if and only if the behaviour of the module should not depend on the
865  // value of the variable, in which case stores to the variable can be
866  // performed in an arbitrary fashion.
867  bool value_is_irrelevant = 5;
868
869}
870
871message TransformationAddImageSampleUnusedComponents {
872
873  // A transformation that adds unused components to an image sample coordinate.
874
875  // An vector id with the original coordinate and the unused components.
876  uint32 coordinate_with_unused_components_id = 1;
877
878  // A descriptor for an image sample instruction.
879  InstructionDescriptor instruction_descriptor = 2;
880
881}
882
883message TransformationAddLocalVariable {
884
885  // Adds a local variable of the given type (which must be a pointer with
886  // Function storage class) to the given function, initialized to the given
887  // id.
888
889  // Fresh id for the local variable
890  uint32 fresh_id = 1;
891
892  // The type of the local variable
893  uint32 type_id = 2;
894
895  // The id of the function to which the local variable should be added
896  uint32 function_id = 3;
897
898  // Initial value of the variable
899  uint32 initializer_id = 4;
900
901  // True if and only if the behaviour of the module should not depend on the
902  // value of the variable, in which case stores to the variable can be
903  // performed in an arbitrary fashion.
904  bool value_is_irrelevant = 5;
905
906}
907
908message TransformationAddLoopPreheader {
909
910  // A transformation that adds a loop preheader block before the given loop header.
911
912  // The id of the loop header block
913  uint32 loop_header_block = 1;
914
915  // A fresh id for the preheader block
916  uint32 fresh_id = 2;
917
918  // Fresh ids for splitting the OpPhi instructions in the header.
919  // A new OpPhi instruction in the preheader is needed for each OpPhi instruction in the header,
920  // if the header has more than one predecessor outside of the loop.
921  // This allows turning instructions of the form:
922  //
923  //   %loop_header_block = OpLabel
924  //                 %id1 = OpPhi %type %val1 %pred1_id %val2 %pred2_id %val3 %backedge_block_id
925  //
926  // into:
927  //            %fresh_id = OpLabel
928  //             %phi_id1 = OpPhi %type %val1 %pred1_id %val2 %pred2_id
929  //                        OpBranch %header_id
930  //   %loop_header_block = OpLabel
931  //                 %id1 = OpPhi %type %phi_id1 %fresh_id %val3 %backedge_block_id
932  repeated uint32 phi_id = 3;
933
934}
935
936message TransformationAddLoopToCreateIntConstantSynonym {
937  // A transformation that uses a loop to create a synonym for an integer
938  // constant C (scalar or vector) using an initial value I, a step value S and
939  // a number of iterations N such that C = I - N * S. For each iteration, S is
940  // subtracted from the total.
941  // The loop can be made up of one or two blocks, and it is inserted before a
942  // block with a single predecessor. In the one-block case, it is of the form:
943  //
944  //            %loop_id = OpLabel
945  //             %ctr_id = OpPhi %int %int_0 %pred %incremented_ctr_id %loop_id
946  //            %temp_id = OpPhi %type_of_I %I %pred %eventual_syn_id %loop_id
947  //    %eventual_syn_id = OpISub %type_of_I %temp_id %step_val_id
948  // %incremented_ctr_id = OpIAdd %int %ctr_id %int_1
949  //            %cond_id = OpSLessThan %bool %incremented_ctr_id %num_iterations_id
950  //                       OpLoopMerge %block_after_loop_id %loop_id None
951  //                       OpBranchConditional %cond_id %loop_id %block_after_loop_id
952  //
953  // A new OpPhi instruction is then added to %block_after_loop_id, as follows:
954  //
955  //  %block_after_loop_id = OpLabel
956  //               %syn_id = OpPhi %type_of_I %eventual_syn_id %loop_id
957  //
958  // This can be translated, assuming that N > 0, to:
959  // int syn = I;
960  // for (int ctr = 0; ctr < N; ctr++) syn = syn - S;
961  //
962  // All existing OpPhi instructions in %block_after_loop_id are also updated
963  // to reflect the fact that its predecessor is now %loop_id.
964
965  // The following are existing ids.
966
967  // The id of the integer constant C that we want a synonym of.
968  uint32 constant_id = 1;
969
970  // The id of the initial value integer constant I.
971  uint32 initial_val_id = 2;
972
973  // The id of the step value integer constant S.
974  uint32 step_val_id = 3;
975
976  // The id of the integer scalar constant, its value being the number of
977  // iterations N.
978  uint32 num_iterations_id = 4;
979
980  // The label id of the block before which the loop must be inserted.
981  uint32 block_after_loop_id = 5;
982
983
984  // The following are fresh ids.
985
986  // A fresh id for the synonym.
987  uint32 syn_id = 6;
988
989  // A fresh id for the label of the loop,
990  uint32 loop_id = 7;
991
992  // A fresh id for the counter.
993  uint32 ctr_id = 8;
994
995  // A fresh id taking the value I - S * ctr at the ctr-th iteration.
996  uint32 temp_id = 9;
997
998  // A fresh id taking the value I - S * (ctr + 1) at the ctr-th iteration, and
999  // thus I - S * N at the last iteration.
1000  uint32 eventual_syn_id = 10;
1001
1002  // A fresh id for the incremented counter.
1003  uint32 incremented_ctr_id = 11;
1004
1005  // A fresh id for the loop condition.
1006  uint32 cond_id = 12;
1007
1008  // The instructions in the loop can also be laid out in two basic blocks, as follows:
1009  //
1010  //  %loop_id = OpLabel
1011  //   %ctr_id = OpPhi %int %int_0 %pred %incremented_ctr_id %loop_id
1012  //  %temp_id = OpPhi %type_of_I %I %pred %eventual_syn_id %loop_id
1013  //             OpLoopMerge %block_after_loop_id %additional_block_id None
1014  //             OpBranch %additional_block_id
1015  //
1016  //  %additional_block_id = OpLabel
1017  //      %eventual_syn_id = OpISub %type_of_I %temp_id %step_val_id
1018  //   %incremented_ctr_id = OpIAdd %int %ctr_id %int_1
1019  //              %cond_id = OpSLessThan %bool %incremented_ctr_id %num_iterations_id
1020  //                         OpBranchConditional %cond_id %loop_id %block_after_loop_id
1021
1022  // A fresh id for the additional block. If this is 0, it means that only one
1023  // block is to be created.
1024  uint32 additional_block_id = 13;
1025}
1026
1027message TransformationAddNoContractionDecoration {
1028
1029  // Applies OpDecorate NoContraction to the given result id
1030
1031  // Result id to be decorated
1032  uint32 result_id = 1;
1033
1034}
1035
1036message TransformationAddOpPhiSynonym {
1037
1038  // Adds an OpPhi instruction at the start of a block with n predecessors (pred_1, pred_2, ..., pred_n)
1039  // and n related ids (id_1, id_2, ..., id_n) which are pairwise synonymous.
1040  // The instruction will be of the form:
1041  //       %fresh_id = OpPhi %type %id_1 %pred_1 %id_2 %pred_2 ... %id_n %pred_n
1042  // and fresh_id will be recorded as being synonymous with all the other ids.
1043
1044  // Label id of the block
1045  uint32 block_id = 1;
1046
1047  // Pairs (pred_i, id_i)
1048  repeated UInt32Pair pred_to_id = 2;
1049
1050  // Fresh id for the new instruction
1051  uint32 fresh_id = 3;
1052}
1053
1054message TransformationAddParameter {
1055
1056  // Adds a new parameter into the function.
1057
1058  // Result id of the function to add parameters to.
1059  uint32 function_id = 1;
1060
1061  // Fresh id for a new parameter.
1062  uint32 parameter_fresh_id = 2;
1063
1064  // Type id for a new parameter.
1065  uint32 parameter_type_id = 3;
1066
1067  // A map that maps from the OpFunctionCall id to the id that will be passed as the new
1068  // parameter at that call site. It must have the same type as that of the new parameter.
1069  repeated UInt32Pair call_parameter_ids = 4;
1070
1071  // A fresh id for a new function type. This might not be used
1072  // if a required function type already exists or if we can change
1073  // the old function type.
1074  uint32 function_type_fresh_id = 5;
1075
1076}
1077
1078message TransformationAddRelaxedDecoration {
1079
1080  // Applies OpDecorate RelaxedPrecision to the given result id
1081
1082  // Result id to be decorated
1083  uint32 result_id = 1;
1084
1085}
1086
1087message TransformationAddSpecConstantOp {
1088
1089  // Adds OpSpecConstantOp into the module.
1090
1091  // Result id for the new instruction.
1092  uint32 fresh_id = 1;
1093
1094  // Type id for the new instruction.
1095  uint32 type_id = 2;
1096
1097  // Opcode operand of the OpSpecConstantOp instruction.
1098  uint32 opcode = 3;
1099
1100  // Operands of the |opcode| instruction.
1101  repeated InstructionOperand operand = 4;
1102
1103}
1104
1105message TransformationAddSynonym {
1106
1107  // Adds a |synonymous_instruction| before |insert_before| instruction with
1108  // and creates a fact that |result_id| and the result id of |synonymous_instruction|
1109  // are synonymous.
1110
1111  // Result id of the first synonym.
1112  uint32 result_id = 1;
1113
1114  // Type of the synonym to apply. Some types might produce instructions
1115  // with commutative operands. Such types do not specify the order of the
1116  // operands since we have a special transformation to swap commutable operands.
1117  //
1118  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3499):
1119  //  Consider adding more types here.
1120  enum SynonymType {
1121    // New synonym is derived by adding zero to the |result_id|.
1122    ADD_ZERO = 0;
1123
1124    // New synonym is derived by subtracting zero from the |result_id|.
1125    SUB_ZERO = 1;
1126
1127    // New synonym is derived by multiplying |result_id| by one.
1128    MUL_ONE = 2;
1129
1130    // New synonym is derived by applying OpCopyObject instruction to |result_id|.
1131    COPY_OBJECT = 3;
1132
1133    // New synonym is derived by applying OpLogicalOr to |result_id| with the second
1134    // operand being 'false'.
1135    LOGICAL_OR = 4;
1136
1137    // New synonym is derived by applying OpLogicalAnd to |result_id| with the second
1138    // operand being 'true'.
1139    LOGICAL_AND = 5;
1140
1141    // New synonym is derived by applying OpBitwiseOr to |result_id| with the second
1142    // operand being 0 taken with the same bit length as |result_id|
1143    BITWISE_OR = 6;
1144
1145    // New synonym is derived by applying OpBitwiseXor to |result_id| with the second
1146    // operand being 0 taken with the same bit length as |result_id|
1147    BITWISE_XOR = 7;
1148  }
1149
1150  // Type of the synonym to create. See SynonymType for more details.
1151  SynonymType synonym_type = 2;
1152
1153  // Fresh result id for a created synonym.
1154  uint32 synonym_fresh_id = 3;
1155
1156  // An instruction to insert a new synonym before.
1157  InstructionDescriptor insert_before = 4;
1158
1159}
1160
1161message TransformationAddTypeArray {
1162
1163  // Adds an array type of the given element type and size to the module
1164
1165  // Fresh id for the array type
1166  uint32 fresh_id = 1;
1167
1168  // The array's element type
1169  uint32 element_type_id = 2;
1170
1171  // The array's size
1172  uint32 size_id = 3;
1173
1174}
1175
1176message TransformationAddTypeBoolean {
1177
1178  // Adds OpTypeBool to the module
1179
1180  // Id to be used for the type
1181  uint32 fresh_id = 1;
1182
1183}
1184
1185message TransformationAddTypeFloat {
1186
1187  // Adds OpTypeFloat to the module with the given width
1188
1189  // Id to be used for the type
1190  uint32 fresh_id = 1;
1191
1192  // Floating-point width
1193  uint32 width = 2;
1194
1195}
1196
1197message TransformationAddTypeFunction {
1198
1199  // Adds a function type to the module
1200
1201  // Fresh id for the function type
1202  uint32 fresh_id = 1;
1203
1204  // The function's return type
1205  uint32 return_type_id = 2;
1206
1207  // The function's argument types
1208  repeated uint32 argument_type_id = 3;
1209
1210}
1211
1212message TransformationAddTypeInt {
1213
1214  // Adds OpTypeInt to the module with the given width and signedness
1215
1216  // Id to be used for the type
1217  uint32 fresh_id = 1;
1218
1219  // Integer width
1220  uint32 width = 2;
1221
1222  // True if and only if this is a signed type
1223  bool is_signed = 3;
1224
1225}
1226
1227message TransformationAddTypeMatrix {
1228
1229  // Adds a matrix type to the module
1230
1231  // Fresh id for the matrix type
1232  uint32 fresh_id = 1;
1233
1234  // The matrix's column type, which must be a floating-point vector (as per
1235  // the "data rules" in the SPIR-V specification).
1236  uint32 column_type_id = 2;
1237
1238  // The matrix's column count
1239  uint32 column_count = 3;
1240
1241}
1242
1243message TransformationAddTypePointer {
1244
1245  // Adds OpTypePointer to the module, with the given storage class and base
1246  // type
1247
1248  // Id to be used for the type
1249  uint32 fresh_id = 1;
1250
1251  // Pointer storage class
1252  uint32 storage_class = 2;
1253
1254  // Id of the base type for the pointer
1255  uint32 base_type_id = 3;
1256
1257}
1258
1259message TransformationAddTypeStruct {
1260
1261  // Adds a struct type to the module
1262
1263  // Fresh id for the struct type
1264  uint32 fresh_id = 1;
1265
1266  // The struct's member types
1267  repeated uint32 member_type_id = 3;
1268
1269}
1270
1271message TransformationAddTypeVector {
1272
1273  // Adds a vector type to the module
1274
1275  // Fresh id for the vector type
1276  uint32 fresh_id = 1;
1277
1278  // The vector's component type
1279  uint32 component_type_id = 2;
1280
1281  // The vector's component count
1282  uint32 component_count = 3;
1283
1284}
1285
1286message TransformationAdjustBranchWeights {
1287
1288  // A transformation that adjusts the branch weights
1289  // of a branch conditional instruction.
1290
1291  // A descriptor for a branch conditional instruction.
1292  InstructionDescriptor instruction_descriptor = 1;
1293
1294  // Branch weights of a branch conditional instruction.
1295  UInt32Pair branch_weights = 2;
1296
1297}
1298
1299message TransformationCompositeConstruct {
1300
1301  // A transformation that introduces an OpCompositeConstruct instruction to
1302  // make a composite object.
1303
1304  // Id of the type of the composite that is to be constructed
1305  uint32 composite_type_id = 1;
1306
1307  // Ids of the objects that will form the components of the composite
1308  repeated uint32 component = 2;
1309
1310  // A descriptor for an instruction in a block before which the new
1311  // OpCompositeConstruct instruction should be inserted
1312  InstructionDescriptor instruction_to_insert_before = 3;
1313
1314  // A fresh id for the composite object
1315  uint32 fresh_id = 4;
1316
1317}
1318
1319message TransformationCompositeExtract {
1320
1321  // A transformation that adds an instruction to extract an element from a
1322  // composite.
1323
1324  // A descriptor for an instruction in a block before which the new
1325  // OpCompositeExtract instruction should be inserted
1326  InstructionDescriptor instruction_to_insert_before = 1;
1327
1328  // Result id for the extract operation.
1329  uint32 fresh_id = 2;
1330
1331  // Id of the composite from which data is to be extracted.
1332  uint32 composite_id = 3;
1333
1334  // Indices that indicate which part of the composite should be extracted.
1335  repeated uint32 index = 4;
1336
1337}
1338
1339message TransformationCompositeInsert {
1340
1341  // A transformation that adds an instruction OpCompositeInsert which creates
1342  // a new composite from an existing composite, with an element inserted.
1343
1344  // A descriptor for an instruction before which the new instruction
1345  // OpCompositeInsert should be inserted.
1346  InstructionDescriptor instruction_to_insert_before = 1;
1347
1348  // Result id of the inserted OpCompositeInsert instruction.
1349  uint32 fresh_id = 2;
1350
1351  // Id of the composite used as the basis for the insertion.
1352  uint32 composite_id = 3;
1353
1354  // Id of the object to be inserted.
1355  uint32 object_id = 4;
1356
1357  // Indices that indicate which part of the composite should be inserted into.
1358  repeated uint32 index = 5;
1359
1360}
1361
1362message TransformationComputeDataSynonymFactClosure {
1363
1364  // A transformation that impacts the fact manager only, forcing a computation
1365  // of the closure of data synonym facts, so that e.g. if the components of
1366  // vectors v and w are known to be pairwise synonymous, it is deduced that v
1367  // and w are themselves synonymous.
1368
1369  // When searching equivalence classes for implied facts, equivalence classes
1370  // larger than this size will be skipped.
1371  uint32 maximum_equivalence_class_size = 1;
1372
1373}
1374
1375message TransformationDuplicateRegionWithSelection {
1376
1377  // A transformation that inserts a conditional statement with a boolean expression
1378  // of arbitrary value and duplicates a given single-entry, single-exit region, so
1379  // that it is present in each conditional branch and will be executed regardless
1380  // of which branch will be taken.
1381
1382  // Fresh id for a label of the new entry block.
1383  uint32 new_entry_fresh_id = 1;
1384
1385  // Id for a boolean expression.
1386  uint32 condition_id = 2;
1387
1388  // Fresh id for a label of the merge block of the conditional.
1389  uint32 merge_label_fresh_id = 3;
1390
1391  // Block id of the entry block of the original region.
1392  uint32 entry_block_id = 4;
1393
1394  // Block id of the exit block of the original region.
1395  uint32 exit_block_id = 5;
1396
1397  // Map that maps from a label in the original region to the corresponding label
1398  // in the duplicated region.
1399  repeated UInt32Pair original_label_to_duplicate_label = 6;
1400
1401  // Map that maps from a result id in the original region to the corresponding
1402  // result id in the duplicated region.
1403  repeated UInt32Pair original_id_to_duplicate_id = 7;
1404
1405  // Map that maps from a result id in the original region to the result id of the
1406  // corresponding OpPhi instruction.
1407  repeated UInt32Pair original_id_to_phi_id = 8;
1408}
1409
1410message TransformationEquationInstruction {
1411
1412  // A transformation that adds an instruction to the module that defines an
1413  // equation between its result id and input operand ids, such that the
1414  // equation is guaranteed to hold at any program point where all ids involved
1415  // are available (i.e. at any program point dominated by the instruction).
1416
1417  // The result id of the new instruction
1418  uint32 fresh_id = 1;
1419
1420  // The instruction's opcode
1421  uint32 opcode = 2;
1422
1423  // The input operands to the instruction
1424  repeated uint32 in_operand_id = 3;
1425
1426  // A descriptor for an instruction in a block before which the new
1427  // instruction should be inserted
1428  InstructionDescriptor instruction_to_insert_before = 4;
1429
1430}
1431
1432message TransformationExpandVectorReduction {
1433
1434  // A transformation that adds synonyms for OpAny and OpAll instructions by
1435  // evaluating each vector component with the corresponding logical operation.
1436  // There is a SPIR-V code example in the header file of the transformation
1437  // class that can help understand the transformation.
1438
1439  // The OpAny or OpAll instruction result id.
1440  uint32 instruction_result_id = 1;
1441
1442  // The fresh ids required to apply the transformation.
1443  repeated uint32 fresh_ids = 2;
1444
1445}
1446
1447message TransformationFlattenConditionalBranch {
1448
1449  // A transformation that takes a selection construct with a header
1450  // containing an OpBranchConditional instruction and flattens it.
1451  // For example, something of the form:
1452  //
1453  // %1 = OpLabel
1454  //      [header instructions]
1455  //      OpSelectionMerge %4 None
1456  //      OpBranchConditional %cond %2 %3
1457  // %2 = OpLabel
1458  //      [true branch instructions]
1459  //      OpBranch %4
1460  // %3 = OpLabel
1461  //      [false branch instructions]
1462  //      OpBranch %4
1463  // %4 = OpLabel
1464  //      ...
1465  //
1466  // becomes:
1467  //
1468  // %1 = OpLabel
1469  //      [header instructions]
1470  //      OpBranch %2
1471  // %2 = OpLabel
1472  //      [true branch instructions]
1473  //      OpBranch %3
1474  // %3 = OpLabel
1475  //      [false branch instructions]
1476  //      OpBranch %4
1477  // %4 = OpLabel
1478  //      ...
1479  //
1480  // If all of the instructions in the true or false branches have
1481  // no side effects, this is semantics-preserving.
1482  // Side-effecting instructions will instead be enclosed by smaller
1483  // conditionals. For more details, look at the definition for the
1484  // SideEffectWrapperInfo message.
1485  //
1486  // Nested conditionals or loops are not supported. The false branch
1487  // could also be executed before the true branch, depending on the
1488  // |true_branch_first| field.
1489
1490  // The label id of the header block
1491  uint32 header_block_id = 1;
1492
1493  // A boolean field deciding the order in which the original branches
1494  // will be laid out: the true branch will be laid out first iff this
1495  // field is true.
1496  bool true_branch_first = 2;
1497
1498  // If the convergence block contains an OpPhi with bvec2 result type, it may
1499  // be necessary to introduce a bvec2 with the selection construct's condition
1500  // in both components in order to turn the OpPhi into an OpSelect.  This
1501  // this field provides a fresh id for an OpCompositeConstruct instruction for
1502  // this purpose.  It should be set to 0 if no such instruction is required.
1503  uint32 fresh_id_for_bvec2_selector = 3;
1504
1505  // The same as |fresh_id_for_bvec2_selector| but for the bvec3 case.
1506  uint32 fresh_id_for_bvec3_selector = 4;
1507
1508  // The same as |fresh_id_for_bvec2_selector| but for the bvec4 case.
1509  uint32 fresh_id_for_bvec4_selector = 5;
1510
1511  // A list of instructions with side effects, which must be enclosed
1512  // inside smaller conditionals before flattening the main one, and
1513  // the corresponding fresh ids and module ids needed.
1514  repeated SideEffectWrapperInfo side_effect_wrapper_info = 6;
1515}
1516
1517message TransformationFunctionCall {
1518
1519  // A transformation that introduces an OpFunctionCall instruction.  The call
1520  // must not make the module's call graph cyclic.  Beyond that, if the call
1521  // is in a dead block it can be to any function with arbitrary suitably-typed
1522  // arguments; otherwise it must be to a livesafe function, with injected
1523  // variables as pointer arguments and arbitrary non-pointer arguments.
1524
1525  // A fresh id for the result of the call
1526  uint32 fresh_id = 1;
1527
1528  // Id of the function to be called
1529  uint32 callee_id = 2;
1530
1531  // Ids for arguments to the function
1532  repeated uint32 argument_id = 3;
1533
1534  // A descriptor for an instruction in a block before which the new
1535  // OpFunctionCall instruction should be inserted
1536  InstructionDescriptor instruction_to_insert_before = 4;
1537
1538}
1539
1540message TransformationInlineFunction {
1541
1542  // This transformation inlines a function by mapping the function instructions to fresh ids.
1543
1544  // Result id of the function call instruction.
1545  uint32 function_call_id = 1;
1546
1547  // For each result id defined by the called function,
1548  // this map provides an associated fresh id that can
1549  // be used in the inlined version of the function call.
1550  repeated UInt32Pair result_id_map = 2;
1551
1552}
1553
1554message TransformationInvertComparisonOperator {
1555
1556  // For some instruction with result id |operator_id| that
1557  // represents a binary comparison operator (e.g. <, >, <=), this transformation
1558  // will replace that instruction's result id with |fresh_id|,
1559  // invert the opcode (< will become >=) and insert OpLogicalNot
1560  // instruction with result id |operator_id| below.
1561
1562  // Result id of the instruction to invert.
1563  uint32 operator_id = 1;
1564
1565  // Fresh id that will be used by the operator after the inversion.
1566  uint32 fresh_id = 2;
1567
1568}
1569
1570message TransformationLoad {
1571
1572  // Transformation that adds an OpLoad or OpAtomicLoad instruction from a pointer into an id.
1573
1574  // The result of the load instruction.
1575  uint32 fresh_id = 1;
1576
1577  // The pointer to be loaded from.
1578  uint32 pointer_id = 2;
1579
1580  // True if and only if the load should be atomic.
1581  bool is_atomic = 3;
1582
1583  // The memory scope for the atomic load. Ignored unless |is_atomic| is true.
1584  uint32 memory_scope_id = 4;
1585
1586  // The memory semantics for the atomic load. Ignored unless |is_atomic| is true.
1587  uint32 memory_semantics_id = 5;
1588
1589  // A descriptor for an instruction in a block before which the new OpLoad
1590  // instruction should be inserted.
1591  InstructionDescriptor instruction_to_insert_before = 6;
1592
1593}
1594
1595message TransformationMakeVectorOperationDynamic {
1596
1597  // A transformation that replaces the OpCompositeExtract and OpCompositeInsert
1598  // instructions with the OpVectorExtractDynamic and OpVectorInsertDynamic instructions.
1599
1600  // The composite instruction result id.
1601  uint32 instruction_result_id = 1;
1602
1603  // The OpCompositeExtract/Insert instructions accept integer literals as indices to the composite object.
1604  // However, the OpVectorInsert/ExtractDynamic instructions require its single index to be an integer instruction.
1605  // This is the result id of the integer instruction.
1606  uint32 constant_index_id = 2;
1607
1608}
1609
1610message TransformationMergeBlocks {
1611
1612  // A transformation that merges a block with its predecessor.
1613
1614  // The id of the block that is to be merged with its predecessor; the merged
1615  // block will have the *predecessor's* id.
1616  uint32 block_id = 1;
1617
1618}
1619
1620message TransformationMergeFunctionReturns {
1621
1622  // A transformation that modifies a function so that it does not return early,
1623  // so it only has one return statement (ignoring unreachable blocks).
1624  //
1625  // The function is enclosed inside an outer loop, that is only executed once,
1626  // and whose merge block is the new return block of the function.
1627  //
1628  // Each return instruction is replaced by:
1629  //     OpBranch %innermost_loop_merge
1630  // where %innermost_loop_merge is the innermost loop containing the return
1631  // instruction.
1632  //
1633  // Each merge block whose associated loop contains return instructions is
1634  // changed so that it branches to the merge block of the loop containing it,
1635  // as explained in the comments to the ReturnMergingInfo message.
1636  //
1637  // The new return block (the merge block of the new outer loop) will be of
1638  // the following form (if the return type is not void):
1639  //  %outer_return_id = OpLabel
1640  //   %return_val_id = OpPhi %return_type %val1 %block_1 %val2 %block_2 ...
1641  //                    OpReturnValue %return_val_id
1642  // where %block_k is either a return block that, in the original function, is
1643  // outside of any loops, or the merge block of a loop that contains return
1644  // instructions and is not, originally, nested inside another loop, and
1645  // %block_k is the corresponding return value.
1646  // If the function has void type, there will be no OpPhi instruction and the
1647  // last instruction will be OpReturn.
1648
1649  // The id of the function to which the transformation is being applied.
1650  uint32 function_id = 1;
1651
1652  // A fresh id for the header of the new outer loop.
1653  uint32 outer_header_id = 2;
1654
1655  // A fresh id for an unreachable continue construct for the new outer loop.
1656  uint32 unreachable_continue_id = 7;
1657
1658  // A fresh id for the new return block of the function,
1659  // i.e. the merge block of the new outer loop.
1660  uint32 outer_return_id = 3;
1661
1662  // A fresh id for the value that will be returned.
1663  // This is ignored if the function has void return type.
1664  uint32 return_val_id = 4;
1665
1666  // An existing id of the same type as the return value, which is
1667  // available to use at the end of the entry block.
1668  // This is ignored if the function has void return type or if no
1669  // loops in the function contain a return instruction.
1670  // If the function is not void, the transformation will add an
1671  // OpPhi instruction to each merge block whose associated loop
1672  // contains at least a return instruction. The value associated
1673  // with existing predecessors from which the function cannot be
1674  // returning will be this id, used as a placeholder.
1675  uint32 any_returnable_val_id = 5;
1676
1677  // The information needed to modify the merge blocks of
1678  // loops containing return instructions.
1679  repeated ReturnMergingInfo return_merging_info = 6;
1680}
1681
1682message TransformationMoveBlockDown {
1683
1684  // A transformation that moves a basic block to be one position lower in
1685  // program order.
1686
1687  // The id of the block to move down.
1688  uint32 block_id = 1;
1689}
1690
1691message TransformationMoveInstructionDown {
1692
1693  // Swaps |instruction| with the next instruction in the block.
1694
1695  // The instruction to move down.
1696  InstructionDescriptor instruction = 1;
1697
1698}
1699
1700message TransformationMutatePointer {
1701
1702  // Backs up value of the pointer, writes into the pointer and
1703  // restores the original value.
1704
1705  // Result id of the pointer instruction to mutate.
1706  uint32 pointer_id = 1;
1707
1708  // Fresh id for the OpLoad instruction.
1709  uint32 fresh_id = 2;
1710
1711  // Instruction to insert backup, mutation and restoration code before.
1712  InstructionDescriptor insert_before = 3;
1713
1714}
1715
1716message TransformationOutlineFunction {
1717
1718  // A transformation that outlines a single-entry single-exit region of a
1719  // control flow graph into a separate function, and replaces the region with
1720  // a call to that function.
1721
1722  // Id of the entry block of the single-entry single-exit region to be outlined
1723  uint32 entry_block = 1;
1724
1725  // Id of the exit block of the single-entry single-exit region to be outlined
1726  uint32 exit_block = 2;
1727
1728  // Id of a struct that will store the return values of the new function
1729  uint32 new_function_struct_return_type_id = 3;
1730
1731  // A fresh id for the type of the outlined function
1732  uint32 new_function_type_id = 4;
1733
1734  // A fresh id for the outlined function itself
1735  uint32 new_function_id = 5;
1736
1737  // A fresh id to represent the block in the outlined function that represents
1738  // the first block of the outlined region.
1739  uint32 new_function_region_entry_block = 6;
1740
1741  // A fresh id for the result of the OpFunctionCall instruction that will call
1742  // the outlined function
1743  uint32 new_caller_result_id = 7;
1744
1745  // A fresh id to capture the return value of the outlined function - the
1746  // argument to OpReturn
1747  uint32 new_callee_result_id = 8;
1748
1749  // Ids defined outside the region and used inside the region will become
1750  // parameters to the outlined function.  This is a mapping from used ids to
1751  // fresh parameter ids.
1752  repeated UInt32Pair input_id_to_fresh_id = 9;
1753
1754  // Ids defined inside the region and used outside the region will become
1755  // fresh ids defined by the outlined function, which get copied into the
1756  // function's struct return value and then copied into their destination ids
1757  // by the caller.  This is a mapping from original ids to corresponding fresh
1758  // ids.
1759  repeated UInt32Pair output_id_to_fresh_id = 10;
1760
1761}
1762
1763message TransformationPermuteFunctionParameters {
1764
1765  // A transformation that, given a non-entry-point function taking n
1766  // parameters and a permutation of the set [0, n-1]:
1767  //   - Introduces a new function type that is the same as the original
1768  //     function's type but with the order of arguments permuted
1769  //     (only if it doesn't already exist)
1770  //   - Changes the type of the function to this type
1771  //   - Adjusts all calls to the function so that their arguments are permuted
1772
1773  // Function, whose parameters will be permuted
1774  uint32 function_id = 1;
1775
1776  // Fresh id for a new type of the function. This might not be used
1777  // if a required function type already exists or if we can change
1778  // the old function type.
1779  uint32 function_type_fresh_id = 2;
1780
1781  // An array of size |n|, where |n| is a number of arguments to a function
1782  // with |function_id|. For each i: 0 <= permutation[i] < n.
1783  //
1784  // i-th element of this array contains a position for an i-th
1785  // function's argument (i.e. i-th argument will be permutation[i]-th
1786  // after running this transformation)
1787  repeated uint32 permutation = 3;
1788
1789}
1790
1791message TransformationPermutePhiOperands {
1792
1793  // Permutes operands of some OpPhi instruction.
1794
1795  // Result id of the instruction to apply the transformation to.
1796  uint32 result_id = 1;
1797
1798  // A sequence of numbers in the range [0, n/2 - 1] where |n| is the number
1799  // of operands of the OpPhi instruction with |result_id|.
1800  repeated uint32 permutation = 2;
1801
1802}
1803
1804message TransformationPropagateInstructionDown {
1805
1806  // Propagates an instruction from |block_id| into its successors.
1807  // Concretely, the transformation clones the propagated instruction
1808  // into some of the successors of |block_id| and removes the original
1809  // instruction. Additionally, an OpPhi instruction may be added to make sure
1810  // that the transformation can be applied in various scenarios.
1811  //
1812  // Note that the instruction might not be propagated down into every successor
1813  // of |block_id| since it might make the module invalid.
1814
1815  // Id of the block to propagate an instruction from. The decision on what
1816  // instruction to propagate is made based on whether the instruction interacts
1817  // with memory, whether that instruction is used in its block etc (see the
1818  // transformation class for more details).
1819  uint32 block_id = 1;
1820
1821  // A fresh id for an OpPhi instruction. This might not be used by the
1822  // transformation since an OpPhi instruction is created only if needed
1823  // (e.g. an instruction is propagated into divergent blocks).
1824  uint32 phi_fresh_id = 2;
1825
1826  // A map from the id of some successor of the |block_id| to the fresh id.
1827  // The map contains a fresh id for at least every successor of the |block_id|.
1828  // Every fresh id in the map corresponds to the result id of the clone,
1829  // propagated into the corresponding successor block. This transformation
1830  // might use overflow ids if they are available and this field doesn't account
1831  // for every successor of |block_id|.
1832  repeated UInt32Pair successor_id_to_fresh_id = 3;
1833
1834}
1835
1836message TransformationPropagateInstructionUp {
1837
1838  // Propagates an instruction in the block into the block's predecessors.
1839  // Concretely, this transformation clones some particular instruction from
1840  // the |block_id| into every block's predecessor and replaces the original
1841  // instruction with OpPhi. Take a look at the transformation class to learn
1842  // more about how we choose what instruction to propagate.
1843
1844  // Id of the block to propagate an instruction from.
1845  uint32 block_id = 1;
1846
1847  // A map from the id of some predecessor of the |block_id| to the fresh id.
1848  // The map contains a fresh id for at least every predecessor of the |block_id|.
1849  // The instruction is propagated by creating a number of clones - one clone for
1850  // each predecessor. Fresh ids from this field are used as result ids of cloned
1851  // instructions.
1852  repeated UInt32Pair predecessor_id_to_fresh_id = 2;
1853
1854}
1855
1856message TransformationPushIdThroughVariable {
1857
1858  // A transformation that makes |value_synonym_id| and |value_id| to be
1859  // synonymous by storing |value_id| into |variable_id| and
1860  // loading |variable_id| to |value_synonym_id|.
1861
1862  // The value to be stored.
1863  uint32 value_id = 1;
1864
1865  // A fresh id for the result of the load instruction.
1866  uint32 value_synonym_id = 2;
1867
1868  // A fresh id for the variable to be stored to.
1869  uint32 variable_id = 3;
1870
1871  // Constant to initialize the variable from.
1872  uint32 initializer_id = 4;
1873
1874  // The variable storage class (global or local).
1875  uint32 variable_storage_class = 5;
1876
1877  // A descriptor for an instruction which the new OpStore
1878  // and OpLoad instructions might be inserted before.
1879  InstructionDescriptor instruction_descriptor = 6;
1880
1881}
1882
1883message TransformationRecordSynonymousConstants {
1884
1885  // A transformation that, given the IDs to two synonymous constants,
1886  // records the fact that they are synonymous. The module is not changed.
1887  // Two constants are synonymous if:
1888  // - they have the same type (ignoring the presence of integer sign)
1889  // - they have the same opcode (one of OpConstant, OpConstantTrue,
1890  //   OpConstantFalse, OpConstantNull)
1891  // - they have the same value
1892  // If the types are the same, OpConstantNull is equivalent to
1893  // OpConstantFalse or OpConstant with value zero.
1894
1895  // The id of a constant
1896  uint32 constant1_id = 1;
1897
1898  // The id of the synonym
1899  uint32 constant2_id = 2;
1900
1901}
1902
1903message TransformationReplaceAddSubMulWithCarryingExtended {
1904
1905  // Replaces OpIAdd with OpIAddCarry, OpISub with OpISubBorrow, OpIMul
1906  // with OpUMulExtended or OpSMulExtended (depending on the signedness
1907  // of the operands) and stores the result into a |struct_fresh_id|.
1908  // In the original instruction the result type id and the type ids of
1909  // the operands must be the same. Then the transformation extracts
1910  // the first element of the result into the original |result_id|.
1911  // This value is the same as the result of the original instruction.
1912
1913  // The fresh id of the intermediate result.
1914  uint32 struct_fresh_id = 1;
1915
1916  // The result id of the original instruction.
1917  uint32 result_id = 2;
1918
1919}
1920
1921message TransformationReplaceBranchFromDeadBlockWithExit {
1922
1923  // Given a dead block that ends with OpBranch, replaces OpBranch with an
1924  // "exit" instruction; one of OpReturn/OpReturnValue, OpKill (in a fragment
1925  // shader) or OpUnreachable.
1926
1927  // The dead block whose terminator is to be replaced.
1928  uint32 block_id = 1;
1929
1930  // The opcode of the new terminator.
1931  uint32 opcode = 2;
1932
1933  // Ignored unless opcode is OpReturnValue, in which case this field provides
1934  // a suitable result id to be returned.
1935  uint32 return_value_id = 3;
1936
1937}
1938
1939message TransformationReplaceParameterWithGlobal {
1940
1941  // Removes parameter with result id |parameter_id| from its function
1942  // and creates a global variable to pass its value to the function instead.
1943
1944  // Fresh id for a new function type. This might not be used if a required
1945  // function type already exists or if we can change the old function type.
1946  uint32 function_type_fresh_id = 2;
1947
1948  // Result id of the OpFunctionParameter instruction to remove.
1949  uint32 parameter_id = 3;
1950
1951  // Fresh id of a global variable used to pass parameter's value to the function.
1952  uint32 global_variable_fresh_id = 4;
1953
1954}
1955
1956message TransformationReplaceBooleanConstantWithConstantBinary {
1957
1958  // A transformation to capture replacing a use of a boolean constant with
1959  // binary operation on two constant values
1960
1961  // A descriptor for the boolean constant id we would like to replace
1962  IdUseDescriptor id_use_descriptor = 1;
1963
1964  // Id for the constant to be used on the LHS of the comparison
1965  uint32 lhs_id = 2;
1966
1967  // Id for the constant to be used on the RHS of the comparison
1968  uint32 rhs_id = 3;
1969
1970  // Opcode for binary operator
1971  uint32 opcode = 4;
1972
1973  // Id that will store the result of the binary operation instruction
1974  uint32 fresh_id_for_binary_operation = 5;
1975
1976}
1977
1978message TransformationReplaceConstantWithUniform {
1979
1980  // Replaces a use of a constant id with the result of a load from an
1981  // element of uniform buffer known to hold the same value as the constant
1982
1983  // A descriptor for the id we would like to replace
1984  IdUseDescriptor id_use_descriptor = 1;
1985
1986  // Uniform descriptor to identify which uniform value to choose
1987  UniformBufferElementDescriptor uniform_descriptor = 2;
1988
1989  // Id that will store the result of an access chain
1990  uint32 fresh_id_for_access_chain = 3;
1991
1992  // Id that will store the result of a load
1993  uint32 fresh_id_for_load = 4;
1994
1995}
1996
1997message TransformationReplaceCopyMemoryWithLoadStore {
1998
1999  // A transformation that replaces instructions OpCopyMemory with loading
2000  // the source variable to an intermediate value and storing this value into the
2001  // target variable of the original OpCopyMemory instruction.
2002
2003  // The intermediate value.
2004  uint32 fresh_id = 1;
2005
2006  // The instruction descriptor to OpCopyMemory. It is necessary, because
2007  // OpCopyMemory doesn't have a result id.
2008  InstructionDescriptor copy_memory_instruction_descriptor = 2;
2009}
2010
2011message TransformationReplaceCopyObjectWithStoreLoad {
2012
2013  // A transformation that replaces instruction OpCopyObject with
2014  // storing into a new variable and immediately loading from this
2015  // variable to |result_id| of the original OpCopyObject instruction.
2016
2017  // The result id of initial OpCopyObject instruction
2018  uint32 copy_object_result_id = 1;
2019
2020  // A fresh id for the variable to be stored to.
2021  uint32 fresh_variable_id = 2;
2022
2023  // The variable storage class (Function or Private).
2024  uint32 variable_storage_class = 3;
2025
2026  // Constant to initialize the variable with.
2027  uint32 variable_initializer_id = 4;
2028}
2029
2030message TransformationReplaceIdWithSynonym {
2031
2032  // Replaces a use of an id with an id that is known to be synonymous, e.g.
2033  // because it was obtained via applying OpCopyObject
2034
2035  // The id use that is to be replaced
2036  IdUseDescriptor id_use_descriptor = 1;
2037
2038  // The synonymous id
2039  uint32 synonymous_id = 2;
2040
2041}
2042
2043message TransformationReplaceIrrelevantId {
2044
2045  // Replaces an irrelevant id with another id of the same type.
2046
2047  // The id use that is to be replaced
2048  IdUseDescriptor id_use_descriptor = 1;
2049
2050  // The replacement id
2051  uint32 replacement_id = 2;
2052}
2053
2054message TransformationReplaceLinearAlgebraInstruction {
2055
2056  // Replaces a linear algebra instruction with its
2057  // mathematical definition.
2058
2059  // The fresh ids needed to apply the transformation.
2060  repeated uint32 fresh_ids = 1;
2061
2062  // A descriptor for a linear algebra instruction.
2063  InstructionDescriptor instruction_descriptor = 2;
2064
2065}
2066
2067message TransformationReplaceLoadStoreWithCopyMemory {
2068  // A transformation that takes a pair of instruction descriptors
2069  // to OpLoad and OpStore that have the same intermediate value
2070  // and replaces the OpStore with an equivalent OpCopyMemory.
2071
2072  // The instruction descriptor to OpLoad
2073  InstructionDescriptor load_instruction_descriptor = 1;
2074
2075  // The instruction descriptor to OpStore
2076  InstructionDescriptor store_instruction_descriptor = 2;
2077}
2078
2079message TransformationReplaceOpPhiIdFromDeadPredecessor {
2080
2081  // Replaces one of the ids used by an OpPhi instruction, when
2082  // the corresponding predecessor is dead, with any available id
2083  // of the correct type.
2084
2085  // The result id of the OpPhi instruction.
2086  uint32 opphi_id = 1;
2087
2088  // The label id of one of the predecessors of the block containing
2089  // the OpPhi instruction, corresponding to the id that we want to
2090  // replace.
2091  uint32 pred_label_id = 2;
2092
2093  // The id that, after the transformation, will be associated with
2094  // the given predecessor.
2095  uint32 replacement_id = 3;
2096
2097}
2098
2099message TransformationReplaceOpSelectWithConditionalBranch {
2100
2101  // A transformation that takes an OpSelect instruction with a
2102  // scalar boolean condition and replaces it with a conditional
2103  // branch and an OpPhi instruction.
2104  // The OpSelect instruction must be the first instruction in its
2105  // block, which must have a unique predecessor. The block will
2106  // become the merge block of a new construct, while its predecessor
2107  // will become the header.
2108  // Given the original OpSelect instruction:
2109  //   %id = OpSelect %type %cond %then %else
2110  // The branching instruction of the header will be:
2111  //         OpBranchConditional %cond %true_block_id %false_block_id
2112  // and the OpSelect instruction will be turned into:
2113  //   %id = OpPhi %type %then %true_block_id %else %false_block_id
2114  // At most one of |true_block_id| and |false_block_id| can be zero. In
2115  // that case, there will be no such block and all references to it
2116  // will be replaced by %merge_block (where %merge_block is the
2117  // block containing the OpSelect instruction).
2118
2119  // The result id of the OpSelect instruction.
2120  uint32 select_id = 1;
2121
2122  // A fresh id for the new block that the predecessor of the block
2123  // containing |select_id| will branch to if the condition holds.
2124  uint32 true_block_id = 2;
2125
2126  // A fresh id for the new block that the predecessor of the block
2127  // containing |select_id| will branch to if the condition does not
2128  // hold.
2129  uint32 false_block_id = 3;
2130}
2131
2132message TransformationReplaceParamsWithStruct {
2133
2134  // Replaces parameters of the function with a struct containing
2135  // values of those parameters.
2136
2137  // Result ids of parameters to replace.
2138  repeated uint32 parameter_id = 1;
2139
2140  // Fresh id for a new function type. This might be unused if the required type
2141  // already exists in the module or if we can change the old type.
2142  uint32 fresh_function_type_id = 2;
2143
2144  // Fresh id for a new struct function parameter to be used as a replacement.
2145  uint32 fresh_parameter_id = 3;
2146
2147  // Fresh ids for struct objects containing values of replaced parameters.
2148  // This field contains a fresh id for at least every result id of a relevant
2149  // OpFunctionCall instruction.
2150  repeated UInt32Pair caller_id_to_fresh_composite_id = 4;
2151
2152}
2153
2154message TransformationSetFunctionControl {
2155
2156  // A transformation that sets the function control operand of an OpFunction
2157  // instruction.
2158
2159  // The result id of an OpFunction instruction
2160  uint32 function_id = 1;
2161
2162  // The value to which the 'function control' operand should be set.
2163  uint32 function_control = 2;
2164
2165}
2166
2167message TransformationSetLoopControl {
2168
2169  // A transformation that sets the loop control operand of an OpLoopMerge
2170  // instruction.
2171
2172  // The id of a basic block that should contain OpLoopMerge
2173  uint32 block_id = 1;
2174
2175  // The value to which the 'loop control' operand should be set.
2176  // This must be a legal loop control mask.
2177  uint32 loop_control = 2;
2178
2179  // Provides a peel count value for the loop.  Used if and only if the
2180  // PeelCount bit is set.  Must be zero if the PeelCount bit is not set (can
2181  // still be zero if this bit is set).
2182  uint32 peel_count = 3;
2183
2184  // Provides a partial count value for the loop.  Used if and only if the
2185  // PartialCount bit is set.  Must be zero if the PartialCount bit is not set
2186  // (can still be zero if this bit is set).
2187  uint32 partial_count = 4;
2188
2189}
2190
2191message TransformationSetMemoryOperandsMask {
2192
2193  // A transformation that sets the memory operands mask of a memory access
2194  // instruction.
2195
2196  // A descriptor for a memory access instruction, e.g. an OpLoad
2197  InstructionDescriptor memory_access_instruction = 1;
2198
2199  // A mask of memory operands to be applied to the instruction.  It must be the
2200  // same as the original mask, except that Volatile can be added, and
2201  // Nontemporal can be added or removed.
2202  uint32 memory_operands_mask = 2;
2203
2204  // Some memory access instructions allow more than one mask to be specified;
2205  // this field indicates which mask should be set
2206  uint32 memory_operands_mask_index = 3;
2207
2208}
2209
2210message TransformationSetSelectionControl {
2211
2212  // A transformation that sets the selection control operand of an
2213  // OpSelectionMerge instruction.
2214
2215  // The id of a basic block that should contain OpSelectionMerge
2216  uint32 block_id = 1;
2217
2218  // The value to which the 'selection control' operand should be set.
2219  // Although technically 'selection control' is a literal mask that can be
2220  // some combination of 'None', 'Flatten' and 'DontFlatten', the combination
2221  // 'Flatten | DontFlatten' does not make sense and is not allowed here.
2222  uint32 selection_control = 2;
2223
2224}
2225
2226message TransformationSplitBlock {
2227
2228  // A transformation that splits a basic block into two basic blocks
2229
2230  // A descriptor for an instruction such that the block containing the
2231  // described instruction should be split right before the instruction.
2232  InstructionDescriptor instruction_to_split_before = 1;
2233
2234  // An id that must not yet be used by the module to which this transformation
2235  // is applied.  Rather than having the transformation choose a suitable id on
2236  // application, we require the id to be given upfront in order to facilitate
2237  // reducing fuzzed shaders by removing transformations.  The reason is that
2238  // future transformations may refer to the fresh id introduced by this
2239  // transformation, and if we end up changing what that id is, due to removing
2240  // earlier transformations, it may inhibit later transformations from
2241  // applying.
2242  uint32 fresh_id = 2;
2243
2244}
2245
2246message TransformationStore {
2247
2248  // Transformation that adds an OpStore or OpAtomicStore instruction of an id to a pointer.
2249
2250  // The pointer to be stored to.
2251  uint32 pointer_id = 1;
2252
2253  // True if and only if the load should be atomic.
2254  bool is_atomic = 2;
2255
2256  // The memory scope for the atomic load. Ignored unless |is_atomic| is true.
2257  uint32 memory_scope_id = 3;
2258
2259  // The memory semantics for the atomic load. Ignored unless |is_atomic| is true.
2260  uint32 memory_semantics_id = 4;
2261
2262  // The value to be stored.
2263  uint32 value_id = 5;
2264
2265  // A descriptor for an instruction in a block before which the new OpStore
2266  // instruction should be inserted.
2267  InstructionDescriptor instruction_to_insert_before = 6;
2268
2269}
2270
2271message TransformationSwapCommutableOperands {
2272
2273  // A transformation that swaps the operands of a commutative instruction.
2274
2275  // A descriptor for a commutative instruction
2276  InstructionDescriptor instruction_descriptor = 1;
2277
2278}
2279
2280message TransformationSwapConditionalBranchOperands {
2281
2282  // Swaps label ids in OpBranchConditional instruction.
2283  // Additionally, inverts the guard and swaps branch weights
2284  // if present.
2285
2286  // Descriptor of the instruction to swap operands of.
2287  InstructionDescriptor instruction_descriptor = 1;
2288
2289  // Fresh result id for the OpLogicalNot instruction, used
2290  // to invert the guard.
2291  uint32 fresh_id = 2;
2292
2293}
2294
2295message TransformationSwapFunctionVariables {
2296  // A transformation that swaps function variables
2297
2298  // Result id of the first variable.
2299  uint32 result_id1 = 1;
2300  // Result id of the second variable.
2301  uint32 result_id2 = 2;
2302
2303}
2304
2305message TransformationSwapTwoFunctions {
2306  // A transformation that swaps the position of two functions within the same module.
2307
2308  // the IDs for the two functions that are swapped.
2309  uint32 function_id1 = 1;
2310  uint32 function_id2 = 2;
2311}
2312
2313message TransformationToggleAccessChainInstruction {
2314
2315  // A transformation that toggles an access chain instruction.
2316
2317  // A descriptor for an access chain instruction
2318  InstructionDescriptor instruction_descriptor = 1;
2319
2320}
2321
2322message TransformationVectorShuffle {
2323
2324  // A transformation that adds a vector shuffle instruction.
2325
2326  // A descriptor for an instruction in a block before which the new
2327  // OpVectorShuffle instruction should be inserted
2328  InstructionDescriptor instruction_to_insert_before = 1;
2329
2330  // Result id for the shuffle operation.
2331  uint32 fresh_id = 2;
2332
2333  // Id of the first vector operand.
2334  uint32 vector1 = 3;
2335
2336  // Id of the second vector operand.
2337  uint32 vector2 = 4;
2338
2339  // Indices that indicate which components of the input vectors should be used.
2340  repeated uint32 component = 5;
2341
2342}
2343
2344message TransformationWrapEarlyTerminatorInFunction {
2345
2346  // Replaces an early terminator - OpKill, OpReachable or OpTerminateInvocation
2347  // - with a call to a wrapper function for the terminator.
2348
2349  // A fresh id for a new OpFunctionCall instruction.
2350  uint32 fresh_id = 1;
2351
2352  // A descriptor for an OpKill, OpUnreachable or OpTerminateInvocation
2353  // instruction.
2354  InstructionDescriptor early_terminator_instruction = 2;
2355
2356  // An id with the same type as the enclosing function's return type that is
2357  // available at the early terminator.  This is used to change the terminator
2358  // to OpReturnValue.  Ignored if the enclosing function has void return type,
2359  // in which case OpReturn can be used as the new terminator.
2360  uint32 returned_value_id = 3;
2361
2362}
2363
2364message TransformationWrapRegionInSelection {
2365
2366  // Transforms a single-entry-single-exit region R into
2367  // if (|branch_condition|) { R } else { R }
2368  // The entry block for R becomes a selection header and
2369  // the exit block - a selection merge.
2370  //
2371  // Note that the region R is not duplicated. Thus, the effect of
2372  // this transformation can be represented as follows:
2373  //              entry
2374  //  entry        / \
2375  //    |          \ /
2376  //    R   -->     R
2377  //    |           |
2378  //   exit        exit
2379
2380  // This behaviour is different from TransformationDuplicateRegionWithSelection
2381  // that copies the blocks in R.
2382
2383  // The entry block for the region R.
2384  uint32 region_entry_block_id = 1;
2385
2386  // The exit block for the region R.
2387  uint32 region_exit_block_id = 2;
2388
2389  // Boolean value for the condition expression.
2390  bool branch_condition = 3;
2391
2392}
2393
2394message TransformationWrapVectorSynonym {
2395  // A transformation that wraps an arithmetic operation into a vector operation
2396  // and get the result of the original operation from the corresponding index.
2397  // For instance, for this transformation, an scalar operation between two scalars:
2398  // define op ∈ {+, -, *}
2399  // c = a op b
2400  //
2401  // requires the availability of two vectors:
2402  //
2403  // va = vector(..., a, ...)
2404  // vb = vector(..., b, ...)
2405  //
2406  // where a and b are in the same position i in each of their corresponding vector
2407  // and a is synonymous with va[i] and b is synonymous with vb[i].
2408  //
2409  // The transformation then add an instruction vc = va op vb where c is synonymous
2410  // with vc[i].
2411
2412  // The result if of the original scalar operation instruction.
2413  uint32 instruction_id = 1;
2414
2415  // The result id for the first vector that contains the first value of the scalar operation.
2416  uint32 vector_operand1 = 2;
2417
2418  // The result id for the second vector that contains the second value of the scalar operation.
2419  uint32 vector_operand2 = 3;
2420
2421  // A fresh id for the resulted vector from the addition of the first and second vector.
2422  uint32 fresh_id = 4;
2423
2424  // The position in the vector where the value of original instruction is located. Must be in
2425  // the corresponding vector range.
2426  uint32 scalar_position = 5;
2427
2428}
2429