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