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 } 174} 175 176// Keep fact message types in alphabetical order: 177 178message FactBlockIsDead { 179 180 // Records the fact that a block is guaranteed to be dynamically unreachable. 181 // This is useful because it informs the fuzzer that rather arbitrary changes 182 // can be made to this block. 183 184 uint32 block_id = 1; 185} 186 187message FactConstantUniform { 188 189 // Records the fact that a uniform buffer element is guaranteed to be equal 190 // to a particular constant value. spirv-fuzz can use such guarantees to 191 // obfuscate code, e.g. to manufacture an expression that will (due to the 192 // guarantee) evaluate to a particular value at runtime but in a manner that 193 // cannot be predicted at compile-time. 194 195 // An element of a uniform buffer 196 UniformBufferElementDescriptor uniform_buffer_element_descriptor = 1; 197 198 // The words of the associated constant 199 repeated uint32 constant_word = 2; 200 201} 202 203message FactDataSynonym { 204 205 // Records the fact that the data held in two data descriptors are guaranteed 206 // to be equal. spirv-fuzz can use this to replace uses of one piece of data 207 // with a known-to-be-equal piece of data. 208 209 // Data descriptors guaranteed to hold identical data. 210 DataDescriptor data1 = 1; 211 212 DataDescriptor data2 = 2; 213 214} 215 216message FactFunctionIsLivesafe { 217 218 // Records the fact that a function is guaranteed to be "livesafe", meaning 219 // that it will not make out-of-bounds accesses, does not contain reachable 220 // OpKill or OpUnreachable instructions, does not contain loops that will 221 // execute for large numbers of iterations, and only invokes other livesafe 222 // functions. 223 224 uint32 function_id = 1; 225} 226 227message FactIdEquation { 228 229 // Records the fact that the equation: 230 // 231 // lhs_id = opcode rhs_id[0] rhs_id[1] ... rhs_id[N-1] 232 // 233 // holds; e.g. that the equation: 234 // 235 // %12 = OpIAdd %13 %14 236 // 237 // holds in the case where lhs_id is 12, rhs_id is [13, 14], and the opcode is 238 // OpIAdd. 239 240 // The left-hand-side of the equation. 241 uint32 lhs_id = 1; 242 243 // A SPIR-V opcode, from a restricted set of instructions for which equation 244 // facts make sense. 245 uint32 opcode = 2; 246 247 // The operands to the right-hand-side of the equation. 248 repeated uint32 rhs_id = 3; 249 250} 251 252message FactPointeeValueIsIrrelevant { 253 254 // Records the fact that value of the data pointed to by a pointer id does 255 // not influence the observable behaviour of the module. This means that 256 // arbitrary stores can be made through the pointer, and that nothing can be 257 // guaranteed about the values that are loaded via the pointer. 258 259 // A result id of pointer type 260 uint32 pointer_id = 1; 261} 262 263message AccessChainClampingInfo { 264 265 // When making a function livesafe it is necessary to clamp the indices that 266 // occur as operands to access chain instructions so that they are guaranteed 267 // to be in bounds. This message type allows an access chain instruction to 268 // have an associated sequence of ids that are reserved for comparing an 269 // access chain index with a bound (e.g. an array size), and selecting 270 // between the access chain index (if it is within bounds) and the bound (if 271 // it is not). 272 // 273 // This allows turning an instruction of the form: 274 // 275 // %result = OpAccessChain %type %object ... %index ... 276 // 277 // into: 278 // 279 // %t1 = OpULessThanEqual %bool %index %bound_minus_one 280 // %t2 = OpSelect %int_type %t1 %index %bound_minus_one 281 // %result = OpAccessChain %type %object ... %t2 ... 282 283 // The result id of an OpAccessChain or OpInBoundsAccessChain instruction. 284 uint32 access_chain_id = 1; 285 286 // A series of pairs of fresh ids, one per access chain index, for the results 287 // of a compare instruction and a select instruction, serving the roles of %t1 288 // and %t2 in the above example. 289 repeated UInt32Pair compare_and_select_ids = 2; 290 291} 292 293message LoopLimiterInfo { 294 295 // Structure capturing the information required to manipulate a loop limiter 296 // at a loop header. 297 298 // The header for the loop. 299 uint32 loop_header_id = 1; 300 301 // A fresh id into which the loop limiter's current value can be loaded. 302 uint32 load_id = 2; 303 304 // A fresh id that can be used to increment the loaded value by 1. 305 uint32 increment_id = 3; 306 307 // A fresh id that can be used to compare the loaded value with the loop 308 // limit. 309 uint32 compare_id = 4; 310 311 // A fresh id that can be used to compute the conjunction or disjunction of 312 // an original loop exit condition with |compare_id|, if the loop's back edge 313 // block can conditionally exit the loop. 314 uint32 logical_op_id = 5; 315 316 // A sequence of ids suitable for extending OpPhi instructions of the loop 317 // merge block if it did not previously have an incoming edge from the loop 318 // back edge block. 319 repeated uint32 phi_id = 6; 320 321} 322 323message TransformationSequence { 324 repeated Transformation transformation = 1; 325} 326 327message Transformation { 328 oneof transformation { 329 // Order the transformation options by numeric id (rather than 330 // alphabetically). 331 TransformationMoveBlockDown move_block_down = 1; 332 TransformationSplitBlock split_block = 2; 333 TransformationAddConstantBoolean add_constant_boolean = 3; 334 TransformationAddConstantScalar add_constant_scalar = 4; 335 TransformationAddTypeBoolean add_type_boolean = 5; 336 TransformationAddTypeFloat add_type_float = 6; 337 TransformationAddTypeInt add_type_int = 7; 338 TransformationAddDeadBreak add_dead_break = 8; 339 TransformationReplaceBooleanConstantWithConstantBinary 340 replace_boolean_constant_with_constant_binary = 9; 341 TransformationAddTypePointer add_type_pointer = 10; 342 TransformationReplaceConstantWithUniform replace_constant_with_uniform = 11; 343 TransformationAddDeadContinue add_dead_continue = 12; 344 TransformationCopyObject copy_object = 13; 345 TransformationReplaceIdWithSynonym replace_id_with_synonym = 14; 346 TransformationSetSelectionControl set_selection_control = 15; 347 TransformationCompositeConstruct composite_construct = 16; 348 TransformationSetLoopControl set_loop_control = 17; 349 TransformationSetFunctionControl set_function_control = 18; 350 TransformationAddNoContractionDecoration add_no_contraction_decoration = 19; 351 TransformationSetMemoryOperandsMask set_memory_operands_mask = 20; 352 TransformationCompositeExtract composite_extract = 21; 353 TransformationVectorShuffle vector_shuffle = 22; 354 TransformationOutlineFunction outline_function = 23; 355 TransformationMergeBlocks merge_blocks = 24; 356 TransformationAddTypeVector add_type_vector = 25; 357 TransformationAddTypeArray add_type_array = 26; 358 TransformationAddTypeMatrix add_type_matrix = 27; 359 TransformationAddTypeStruct add_type_struct = 28; 360 TransformationAddTypeFunction add_type_function = 29; 361 TransformationAddConstantComposite add_constant_composite = 30; 362 TransformationAddGlobalVariable add_global_variable = 31; 363 TransformationAddGlobalUndef add_global_undef = 32; 364 TransformationAddFunction add_function = 33; 365 TransformationAddDeadBlock add_dead_block = 34; 366 TransformationAddLocalVariable add_local_variable = 35; 367 TransformationLoad load = 36; 368 TransformationStore store = 37; 369 TransformationFunctionCall function_call = 38; 370 TransformationAccessChain access_chain = 39; 371 TransformationEquationInstruction equation_instruction = 40; 372 TransformationSwapCommutableOperands swap_commutable_operands = 41; 373 TransformationPermuteFunctionParameters permute_function_parameters = 42; 374 TransformationToggleAccessChainInstruction toggle_access_chain_instruction = 43; 375 TransformationAddConstantNull add_constant_null = 44; 376 // Add additional option using the next available number. 377 } 378} 379 380// Keep transformation message types in alphabetical order: 381 382message TransformationAccessChain { 383 384 // Adds an access chain instruction based on a given pointer and indices. 385 386 // Result id for the access chain 387 uint32 fresh_id = 1; 388 389 // The pointer from which the access chain starts 390 uint32 pointer_id = 2; 391 392 // Zero or more access chain indices 393 repeated uint32 index_id = 3; 394 395 // A descriptor for an instruction in a block before which the new 396 // OpAccessChain instruction should be inserted 397 InstructionDescriptor instruction_to_insert_before = 4; 398 399} 400 401message TransformationAddConstantBoolean { 402 403 // Supports adding the constants true and false to a module, which may be 404 // necessary in order to enable other transformations if they are not present. 405 406 uint32 fresh_id = 1; 407 bool is_true = 2; 408 409} 410 411message TransformationAddConstantComposite { 412 413 // Adds a constant of the given composite type to the module. 414 415 // Fresh id for the composite 416 uint32 fresh_id = 1; 417 418 // A composite type id 419 uint32 type_id = 2; 420 421 // Constituent ids for the composite 422 repeated uint32 constituent_id = 3; 423 424} 425 426message TransformationAddConstantNull { 427 428 // Adds a null constant. 429 430 // Id for the constant 431 uint32 fresh_id = 1; 432 433 // Type of the constant 434 uint32 type_id = 2; 435 436} 437 438message TransformationAddConstantScalar { 439 440 // Adds a constant of the given scalar type. 441 442 // Id for the constant 443 uint32 fresh_id = 1; 444 445 // Id for the scalar type of the constant 446 uint32 type_id = 2; 447 448 // Value of the constant 449 repeated uint32 word = 3; 450 451} 452 453message TransformationAddDeadBlock { 454 455 // Adds a new block to the module that is statically reachable from an 456 // existing block, but dynamically unreachable. 457 458 // Fresh id for the dead block 459 uint32 fresh_id = 1; 460 461 // Id of an existing block terminated with OpBranch, such that this OpBranch 462 // can be replaced with an OpBranchConditional to its exiting successor or 463 // the dead block 464 uint32 existing_block = 2; 465 466 // Determines whether the condition associated with the OpBranchConditional 467 // is true or false 468 bool condition_value = 3; 469 470} 471 472message TransformationAddDeadBreak { 473 474 // A transformation that turns a basic block that unconditionally branches to 475 // its successor into a block that potentially breaks out of a structured 476 // control flow construct, but in such a manner that the break cannot actually 477 // be taken. 478 479 // The block to break from 480 uint32 from_block = 1; 481 482 // The merge block to break to 483 uint32 to_block = 2; 484 485 // Determines whether the break condition is true or false 486 bool break_condition_value = 3; 487 488 // A sequence of ids suitable for extending OpPhi instructions as a result of 489 // the new break edge 490 repeated uint32 phi_id = 4; 491 492} 493 494message TransformationAddDeadContinue { 495 496 // A transformation that turns a basic block appearing in a loop and that 497 // unconditionally branches to its successor into a block that potentially 498 // branches to the continue target of the loop, but in such a manner that the 499 // continue branch cannot actually be taken. 500 501 // The block to continue from 502 uint32 from_block = 1; 503 504 // Determines whether the continue condition is true or false 505 bool continue_condition_value = 2; 506 507 // A sequence of ids suitable for extending OpPhi instructions as a result of 508 // the new break edge 509 repeated uint32 phi_id = 3; 510 511} 512 513message TransformationAddFunction { 514 515 // Adds a SPIR-V function to the module. 516 517 // The series of instructions that comprise the function. 518 repeated Instruction instruction = 1; 519 520 // True if and only if the given function should be made livesafe (see 521 // FactFunctionIsLivesafe for definition). 522 bool is_livesafe = 2; 523 524 // Fresh id for a new variable that will serve as a "loop limiter" for the 525 // function; only relevant if |is_livesafe| holds. 526 uint32 loop_limiter_variable_id = 3; 527 528 // Id of an existing unsigned integer constant providing the maximum value 529 // that the loop limiter can reach before the loop is broken from; only 530 // relevant if |is_livesafe| holds. 531 uint32 loop_limit_constant_id = 4; 532 533 // Fresh ids for each loop in the function that allow the loop limiter to be 534 // manipulated; only relevant if |is_livesafe| holds. 535 repeated LoopLimiterInfo loop_limiter_info = 5; 536 537 // Id of an existing global value with the same return type as the function 538 // that can be used to replace OpKill and OpReachable instructions with 539 // ReturnValue instructions. Ignored if the function has void return type. 540 uint32 kill_unreachable_return_value_id = 6; 541 542 // A mapping (represented as a sequence) from every access chain result id in 543 // the function to the ids required to clamp its indices to ensure they are in 544 // bounds. 545 repeated AccessChainClampingInfo access_chain_clamping_info = 7; 546 547} 548 549message TransformationAddGlobalUndef { 550 551 // Adds an undefined value of a given type to the module at global scope. 552 553 // Fresh id for the undefined value 554 uint32 fresh_id = 1; 555 556 // The type of the undefined value 557 uint32 type_id = 2; 558 559} 560 561message TransformationAddGlobalVariable { 562 563 // Adds a global variable of the given type to the module, with Private or 564 // Workgroup storage class, and optionally (for the Private case) with an 565 // initializer. 566 567 // Fresh id for the global variable 568 uint32 fresh_id = 1; 569 570 // The type of the global variable 571 uint32 type_id = 2; 572 573 uint32 storage_class = 3; 574 575 // Initial value of the variable 576 uint32 initializer_id = 4; 577 578 // True if and only if the behaviour of the module should not depend on the 579 // value of the variable, in which case stores to the variable can be 580 // performed in an arbitrary fashion. 581 bool value_is_irrelevant = 5; 582 583} 584 585message TransformationAddLocalVariable { 586 587 // Adds a local variable of the given type (which must be a pointer with 588 // Function storage class) to the given function, initialized to the given 589 // id. 590 591 // Fresh id for the local variable 592 uint32 fresh_id = 1; 593 594 // The type of the local variable 595 uint32 type_id = 2; 596 597 // The id of the function to which the local variable should be added 598 uint32 function_id = 3; 599 600 // Initial value of the variable 601 uint32 initializer_id = 4; 602 603 // True if and only if the behaviour of the module should not depend on the 604 // value of the variable, in which case stores to the variable can be 605 // performed in an arbitrary fashion. 606 bool value_is_irrelevant = 5; 607 608} 609 610message TransformationAddNoContractionDecoration { 611 612 // Applies OpDecorate NoContraction to the given result id 613 614 // Result id to be decorated 615 uint32 result_id = 1; 616 617} 618 619message TransformationAddTypeArray { 620 621 // Adds an array type of the given element type and size to the module 622 623 // Fresh id for the array type 624 uint32 fresh_id = 1; 625 626 // The array's element type 627 uint32 element_type_id = 2; 628 629 // The array's size 630 uint32 size_id = 3; 631 632} 633 634message TransformationAddTypeBoolean { 635 636 // Adds OpTypeBool to the module 637 638 // Id to be used for the type 639 uint32 fresh_id = 1; 640 641} 642 643message TransformationAddTypeFloat { 644 645 // Adds OpTypeFloat to the module with the given width 646 647 // Id to be used for the type 648 uint32 fresh_id = 1; 649 650 // Floating-point width 651 uint32 width = 2; 652 653} 654 655message TransformationAddTypeFunction { 656 657 // Adds a function type to the module 658 659 // Fresh id for the function type 660 uint32 fresh_id = 1; 661 662 // The function's return type 663 uint32 return_type_id = 2; 664 665 // The function's argument types 666 repeated uint32 argument_type_id = 3; 667 668} 669 670message TransformationAddTypeInt { 671 672 // Adds OpTypeInt to the module with the given width and signedness 673 674 // Id to be used for the type 675 uint32 fresh_id = 1; 676 677 // Integer width 678 uint32 width = 2; 679 680 // True if and only if this is a signed type 681 bool is_signed = 3; 682 683} 684 685message TransformationAddTypeMatrix { 686 687 // Adds a matrix type to the module 688 689 // Fresh id for the matrix type 690 uint32 fresh_id = 1; 691 692 // The matrix's column type, which must be a floating-point vector (as per 693 // the "data rules" in the SPIR-V specification). 694 uint32 column_type_id = 2; 695 696 // The matrix's column count 697 uint32 column_count = 3; 698 699} 700 701message TransformationAddTypePointer { 702 703 // Adds OpTypePointer to the module, with the given storage class and base 704 // type 705 706 // Id to be used for the type 707 uint32 fresh_id = 1; 708 709 // Pointer storage class 710 uint32 storage_class = 2; 711 712 // Id of the base type for the pointer 713 uint32 base_type_id = 3; 714 715} 716 717message TransformationAddTypeStruct { 718 719 // Adds a struct type to the module 720 721 // Fresh id for the struct type 722 uint32 fresh_id = 1; 723 724 // The struct's member types 725 repeated uint32 member_type_id = 3; 726 727} 728 729message TransformationAddTypeVector { 730 731 // Adds a vector type to the module 732 733 // Fresh id for the vector type 734 uint32 fresh_id = 1; 735 736 // The vector's component type 737 uint32 component_type_id = 2; 738 739 // The vector's component count 740 uint32 component_count = 3; 741 742} 743 744message TransformationCompositeConstruct { 745 746 // A transformation that introduces an OpCompositeConstruct instruction to 747 // make a composite object. 748 749 // Id of the type of the composite that is to be constructed 750 uint32 composite_type_id = 1; 751 752 // Ids of the objects that will form the components of the composite 753 repeated uint32 component = 2; 754 755 // A descriptor for an instruction in a block before which the new 756 // OpCompositeConstruct instruction should be inserted 757 InstructionDescriptor instruction_to_insert_before = 3; 758 759 // A fresh id for the composite object 760 uint32 fresh_id = 4; 761 762} 763 764message TransformationCompositeExtract { 765 766 // A transformation that adds an instruction to extract an element from a 767 // composite. 768 769 // A descriptor for an instruction in a block before which the new 770 // OpCompositeExtract instruction should be inserted 771 InstructionDescriptor instruction_to_insert_before = 1; 772 773 // Result id for the extract operation. 774 uint32 fresh_id = 2; 775 776 // Id of the composite from which data is to be extracted. 777 uint32 composite_id = 3; 778 779 // Indices that indicate which part of the composite should be extracted. 780 repeated uint32 index = 4; 781 782} 783 784message TransformationCopyObject { 785 786 // A transformation that introduces an OpCopyObject instruction to make a 787 // copy of an object. 788 789 // Id of the object to be copied 790 uint32 object = 1; 791 792 // A descriptor for an instruction in a block before which the new 793 // OpCopyObject instruction should be inserted 794 InstructionDescriptor instruction_to_insert_before = 2; 795 796 // A fresh id for the copied object 797 uint32 fresh_id = 3; 798 799} 800 801message TransformationEquationInstruction { 802 803 // A transformation that adds an instruction to the module that defines an 804 // equation between its result id and input operand ids, such that the 805 // equation is guaranteed to hold at any program point where all ids involved 806 // are available (i.e. at any program point dominated by the instruction). 807 808 // The result id of the new instruction 809 uint32 fresh_id = 1; 810 811 // The instruction's opcode 812 uint32 opcode = 2; 813 814 // The input operands to the instruction 815 repeated uint32 in_operand_id = 3; 816 817 // A descriptor for an instruction in a block before which the new 818 // instruction should be inserted 819 InstructionDescriptor instruction_to_insert_before = 4; 820 821} 822 823message TransformationFunctionCall { 824 825 // A transformation that introduces an OpFunctionCall instruction. The call 826 // must not make the module's call graph cyclic. Beyond that, if the call 827 // is in a dead block it can be to any function with arbitrary suitably-typed 828 // arguments; otherwise it must be to a livesafe function, with injected 829 // variables as pointer arguments and arbitrary non-pointer arguments. 830 831 // A fresh id for the result of the call 832 uint32 fresh_id = 1; 833 834 // Id of the function to be called 835 uint32 callee_id = 2; 836 837 // Ids for arguments to the function 838 repeated uint32 argument_id = 3; 839 840 // A descriptor for an instruction in a block before which the new 841 // OpFunctionCall instruction should be inserted 842 InstructionDescriptor instruction_to_insert_before = 4; 843 844} 845 846message TransformationLoad { 847 848 // Transformation that adds an OpLoad instruction from a pointer into an id. 849 850 // The result of the load instruction 851 uint32 fresh_id = 1; 852 853 // The pointer to be loaded from 854 uint32 pointer_id = 2; 855 856 // A descriptor for an instruction in a block before which the new OpLoad 857 // instruction should be inserted 858 InstructionDescriptor instruction_to_insert_before = 3; 859 860} 861 862message TransformationMergeBlocks { 863 864 // A transformation that merges a block with its predecessor. 865 866 // The id of the block that is to be merged with its predecessor; the merged 867 // block will have the *predecessor's* id. 868 uint32 block_id = 1; 869 870} 871 872message TransformationMoveBlockDown { 873 874 // A transformation that moves a basic block to be one position lower in 875 // program order. 876 877 // The id of the block to move down. 878 uint32 block_id = 1; 879} 880 881message TransformationOutlineFunction { 882 883 // A transformation that outlines a single-entry single-exit region of a 884 // control flow graph into a separate function, and replaces the region with 885 // a call to that function. 886 887 // Id of the entry block of the single-entry single-exit region to be outlined 888 uint32 entry_block = 1; 889 890 // Id of the exit block of the single-entry single-exit region to be outlined 891 uint32 exit_block = 2; 892 893 // Id of a struct that will store the return values of the new function 894 uint32 new_function_struct_return_type_id = 3; 895 896 // A fresh id for the type of the outlined function 897 uint32 new_function_type_id = 4; 898 899 // A fresh id for the outlined function itself 900 uint32 new_function_id = 5; 901 902 // A fresh id to represent the block in the outlined function that represents 903 // the first block of the outlined region. 904 uint32 new_function_region_entry_block = 6; 905 906 // A fresh id for the result of the OpFunctionCall instruction that will call 907 // the outlined function 908 uint32 new_caller_result_id = 7; 909 910 // A fresh id to capture the return value of the outlined function - the 911 // argument to OpReturn 912 uint32 new_callee_result_id = 8; 913 914 // Ids defined outside the region and used inside the region will become 915 // parameters to the outlined function. This is a mapping from used ids to 916 // fresh parameter ids. 917 repeated UInt32Pair input_id_to_fresh_id = 9; 918 919 // Ids defined inside the region and used outside the region will become 920 // fresh ids defined by the outlined function, which get copied into the 921 // function's struct return value and then copied into their destination ids 922 // by the caller. This is a mapping from original ids to corresponding fresh 923 // ids. 924 repeated UInt32Pair output_id_to_fresh_id = 10; 925 926} 927 928message TransformationPermuteFunctionParameters { 929 930 // A transformation that, given a non-entry-point function taking n 931 // parameters and a permutation of the set [0, n-1]: 932 // - Introduces a new function type that is the same as the original 933 // function's type but with the order of arguments permuted 934 // (only if it doesn't already exist) 935 // - Changes the type of the function to this type 936 // - Adjusts all calls to the function so that their arguments are permuted 937 938 // Function, whose parameters will be permuted 939 uint32 function_id = 1; 940 941 // |new_type_id| is a result id of a valid OpTypeFunction instruction. 942 // New type is valid if: 943 // - it has the same number of operands as the old one 944 // - function's result type is the same as the old one 945 // - function's arguments are permuted according to |permutation| vector 946 uint32 new_type_id = 2; 947 948 // An array of size |n|, where |n| is a number of arguments to a function 949 // with |function_id|. For each i: 0 <= permutation[i] < n. 950 // 951 // i-th element of this array contains a position for an i-th 952 // function's argument (i.e. i-th argument will be permutation[i]-th 953 // after running this transformation) 954 repeated uint32 permutation = 3; 955 956} 957 958message TransformationReplaceBooleanConstantWithConstantBinary { 959 960 // A transformation to capture replacing a use of a boolean constant with 961 // binary operation on two constant values 962 963 // A descriptor for the boolean constant id we would like to replace 964 IdUseDescriptor id_use_descriptor = 1; 965 966 // Id for the constant to be used on the LHS of the comparision 967 uint32 lhs_id = 2; 968 969 // Id for the constant to be used on the RHS of the comparision 970 uint32 rhs_id = 3; 971 972 // Opcode for binary operator 973 uint32 opcode = 4; 974 975 // Id that will store the result of the binary operation instruction 976 uint32 fresh_id_for_binary_operation = 5; 977 978} 979 980message TransformationReplaceConstantWithUniform { 981 982 // Replaces a use of a constant id with the result of a load from an 983 // element of uniform buffer known to hold the same value as the constant 984 985 // A descriptor for the id we would like to replace 986 IdUseDescriptor id_use_descriptor = 1; 987 988 // Uniform descriptor to identify which uniform value to choose 989 UniformBufferElementDescriptor uniform_descriptor = 2; 990 991 // Id that will store the result of an access chain 992 uint32 fresh_id_for_access_chain = 3; 993 994 // Id that will store the result of a load 995 uint32 fresh_id_for_load = 4; 996 997} 998 999message TransformationReplaceIdWithSynonym { 1000 1001 // Replaces a use of an id with an id that is known to be synonymous, e.g. 1002 // because it was obtained via applying OpCopyObject 1003 1004 // The id use that is to be replaced 1005 IdUseDescriptor id_use_descriptor = 1; 1006 1007 // The synonymous id 1008 uint32 synonymous_id = 2; 1009 1010} 1011 1012message TransformationSetFunctionControl { 1013 1014 // A transformation that sets the function control operand of an OpFunction 1015 // instruction. 1016 1017 // The result id of an OpFunction instruction 1018 uint32 function_id = 1; 1019 1020 // The value to which the 'function control' operand should be set. 1021 uint32 function_control = 2; 1022 1023} 1024 1025message TransformationSetLoopControl { 1026 1027 // A transformation that sets the loop control operand of an OpLoopMerge 1028 // instruction. 1029 1030 // The id of a basic block that should contain OpLoopMerge 1031 uint32 block_id = 1; 1032 1033 // The value to which the 'loop control' operand should be set. 1034 // This must be a legal loop control mask. 1035 uint32 loop_control = 2; 1036 1037 // Provides a peel count value for the loop. Used if and only if the 1038 // PeelCount bit is set. Must be zero if the PeelCount bit is not set (can 1039 // still be zero if this bit is set). 1040 uint32 peel_count = 3; 1041 1042 // Provides a partial count value for the loop. Used if and only if the 1043 // PartialCount bit is set. Must be zero if the PartialCount bit is not set 1044 // (can still be zero if this bit is set). 1045 uint32 partial_count = 4; 1046 1047} 1048 1049message TransformationSetMemoryOperandsMask { 1050 1051 // A transformation that sets the memory operands mask of a memory access 1052 // instruction. 1053 1054 // A descriptor for a memory access instruction, e.g. an OpLoad 1055 InstructionDescriptor memory_access_instruction = 1; 1056 1057 // A mask of memory operands to be applied to the instruction. It must be the 1058 // same as the original mask, except that Volatile can be added, and 1059 // Nontemporal can be added or removed. 1060 uint32 memory_operands_mask = 2; 1061 1062 // Some memory access instructions allow more than one mask to be specified; 1063 // this field indicates which mask should be set 1064 uint32 memory_operands_mask_index = 3; 1065 1066} 1067 1068message TransformationSetSelectionControl { 1069 1070 // A transformation that sets the selection control operand of an 1071 // OpSelectionMerge instruction. 1072 1073 // The id of a basic block that should contain OpSelectionMerge 1074 uint32 block_id = 1; 1075 1076 // The value to which the 'selection control' operand should be set. 1077 // Although technically 'selection control' is a literal mask that can be 1078 // some combination of 'None', 'Flatten' and 'DontFlatten', the combination 1079 // 'Flatten | DontFlatten' does not make sense and is not allowed here. 1080 uint32 selection_control = 2; 1081 1082} 1083 1084message TransformationSplitBlock { 1085 1086 // A transformation that splits a basic block into two basic blocks 1087 1088 // A descriptor for an instruction such that the block containing the 1089 // described instruction should be split right before the instruction. 1090 InstructionDescriptor instruction_to_split_before = 1; 1091 1092 // An id that must not yet be used by the module to which this transformation 1093 // is applied. Rather than having the transformation choose a suitable id on 1094 // application, we require the id to be given upfront in order to facilitate 1095 // reducing fuzzed shaders by removing transformations. The reason is that 1096 // future transformations may refer to the fresh id introduced by this 1097 // transformation, and if we end up changing what that id is, due to removing 1098 // earlier transformations, it may inhibit later transformations from 1099 // applying. 1100 uint32 fresh_id = 2; 1101 1102} 1103 1104message TransformationStore { 1105 1106 // Transformation that adds an OpStore instruction of an id to a pointer. 1107 1108 // The pointer to be stored to 1109 uint32 pointer_id = 1; 1110 1111 // The value to be stored 1112 uint32 value_id = 2; 1113 1114 // A descriptor for an instruction in a block before which the new OpStore 1115 // instruction should be inserted 1116 InstructionDescriptor instruction_to_insert_before = 3; 1117 1118} 1119 1120message TransformationSwapCommutableOperands { 1121 1122 // A transformation that swaps the operands of a commutative instruction. 1123 1124 // A descriptor for a commutative instruction 1125 InstructionDescriptor instruction_descriptor = 1; 1126 1127} 1128 1129message TransformationToggleAccessChainInstruction { 1130 1131 // A transformation that toggles an access chain instruction. 1132 1133 // A descriptor for an access chain instruction 1134 InstructionDescriptor instruction_descriptor = 1; 1135 1136} 1137 1138message TransformationVectorShuffle { 1139 1140 // A transformation that adds a vector shuffle instruction. 1141 1142 // A descriptor for an instruction in a block before which the new 1143 // OpVectorShuffle instruction should be inserted 1144 InstructionDescriptor instruction_to_insert_before = 1; 1145 1146 // Result id for the shuffle operation. 1147 uint32 fresh_id = 2; 1148 1149 // Id of the first vector operand. 1150 uint32 vector1 = 3; 1151 1152 // Id of the second vector operand. 1153 uint32 vector2 = 4; 1154 1155 // Indices that indicate which components of the input vectors should be used. 1156 repeated uint32 component = 5; 1157 1158} 1159