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 TransformationComputeDataSynonymFactClosure compute_data_synonym_fact_closure = 45; 377 // Add additional option using the next available number. 378 } 379} 380 381// Keep transformation message types in alphabetical order: 382 383message TransformationAccessChain { 384 385 // Adds an access chain instruction based on a given pointer and indices. 386 387 // Result id for the access chain 388 uint32 fresh_id = 1; 389 390 // The pointer from which the access chain starts 391 uint32 pointer_id = 2; 392 393 // Zero or more access chain indices 394 repeated uint32 index_id = 3; 395 396 // A descriptor for an instruction in a block before which the new 397 // OpAccessChain instruction should be inserted 398 InstructionDescriptor instruction_to_insert_before = 4; 399 400} 401 402message TransformationAddConstantBoolean { 403 404 // Supports adding the constants true and false to a module, which may be 405 // necessary in order to enable other transformations if they are not present. 406 407 uint32 fresh_id = 1; 408 bool is_true = 2; 409 410} 411 412message TransformationAddConstantComposite { 413 414 // Adds a constant of the given composite type to the module. 415 416 // Fresh id for the composite 417 uint32 fresh_id = 1; 418 419 // A composite type id 420 uint32 type_id = 2; 421 422 // Constituent ids for the composite 423 repeated uint32 constituent_id = 3; 424 425} 426 427message TransformationAddConstantNull { 428 429 // Adds a null constant. 430 431 // Id for the constant 432 uint32 fresh_id = 1; 433 434 // Type of the constant 435 uint32 type_id = 2; 436 437} 438 439message TransformationAddConstantScalar { 440 441 // Adds a constant of the given scalar type. 442 443 // Id for the constant 444 uint32 fresh_id = 1; 445 446 // Id for the scalar type of the constant 447 uint32 type_id = 2; 448 449 // Value of the constant 450 repeated uint32 word = 3; 451 452} 453 454message TransformationAddDeadBlock { 455 456 // Adds a new block to the module that is statically reachable from an 457 // existing block, but dynamically unreachable. 458 459 // Fresh id for the dead block 460 uint32 fresh_id = 1; 461 462 // Id of an existing block terminated with OpBranch, such that this OpBranch 463 // can be replaced with an OpBranchConditional to its exiting successor or 464 // the dead block 465 uint32 existing_block = 2; 466 467 // Determines whether the condition associated with the OpBranchConditional 468 // is true or false 469 bool condition_value = 3; 470 471} 472 473message TransformationAddDeadBreak { 474 475 // A transformation that turns a basic block that unconditionally branches to 476 // its successor into a block that potentially breaks out of a structured 477 // control flow construct, but in such a manner that the break cannot actually 478 // be taken. 479 480 // The block to break from 481 uint32 from_block = 1; 482 483 // The merge block to break to 484 uint32 to_block = 2; 485 486 // Determines whether the break condition is true or false 487 bool break_condition_value = 3; 488 489 // A sequence of ids suitable for extending OpPhi instructions as a result of 490 // the new break edge 491 repeated uint32 phi_id = 4; 492 493} 494 495message TransformationAddDeadContinue { 496 497 // A transformation that turns a basic block appearing in a loop and that 498 // unconditionally branches to its successor into a block that potentially 499 // branches to the continue target of the loop, but in such a manner that the 500 // continue branch cannot actually be taken. 501 502 // The block to continue from 503 uint32 from_block = 1; 504 505 // Determines whether the continue condition is true or false 506 bool continue_condition_value = 2; 507 508 // A sequence of ids suitable for extending OpPhi instructions as a result of 509 // the new break edge 510 repeated uint32 phi_id = 3; 511 512} 513 514message TransformationAddFunction { 515 516 // Adds a SPIR-V function to the module. 517 518 // The series of instructions that comprise the function. 519 repeated Instruction instruction = 1; 520 521 // True if and only if the given function should be made livesafe (see 522 // FactFunctionIsLivesafe for definition). 523 bool is_livesafe = 2; 524 525 // Fresh id for a new variable that will serve as a "loop limiter" for the 526 // function; only relevant if |is_livesafe| holds. 527 uint32 loop_limiter_variable_id = 3; 528 529 // Id of an existing unsigned integer constant providing the maximum value 530 // that the loop limiter can reach before the loop is broken from; only 531 // relevant if |is_livesafe| holds. 532 uint32 loop_limit_constant_id = 4; 533 534 // Fresh ids for each loop in the function that allow the loop limiter to be 535 // manipulated; only relevant if |is_livesafe| holds. 536 repeated LoopLimiterInfo loop_limiter_info = 5; 537 538 // Id of an existing global value with the same return type as the function 539 // that can be used to replace OpKill and OpReachable instructions with 540 // ReturnValue instructions. Ignored if the function has void return type. 541 uint32 kill_unreachable_return_value_id = 6; 542 543 // A mapping (represented as a sequence) from every access chain result id in 544 // the function to the ids required to clamp its indices to ensure they are in 545 // bounds. 546 repeated AccessChainClampingInfo access_chain_clamping_info = 7; 547 548} 549 550message TransformationAddGlobalUndef { 551 552 // Adds an undefined value of a given type to the module at global scope. 553 554 // Fresh id for the undefined value 555 uint32 fresh_id = 1; 556 557 // The type of the undefined value 558 uint32 type_id = 2; 559 560} 561 562message TransformationAddGlobalVariable { 563 564 // Adds a global variable of the given type to the module, with Private or 565 // Workgroup storage class, and optionally (for the Private case) with an 566 // initializer. 567 568 // Fresh id for the global variable 569 uint32 fresh_id = 1; 570 571 // The type of the global variable 572 uint32 type_id = 2; 573 574 uint32 storage_class = 3; 575 576 // Initial value of the variable 577 uint32 initializer_id = 4; 578 579 // True if and only if the behaviour of the module should not depend on the 580 // value of the variable, in which case stores to the variable can be 581 // performed in an arbitrary fashion. 582 bool value_is_irrelevant = 5; 583 584} 585 586message TransformationAddLocalVariable { 587 588 // Adds a local variable of the given type (which must be a pointer with 589 // Function storage class) to the given function, initialized to the given 590 // id. 591 592 // Fresh id for the local variable 593 uint32 fresh_id = 1; 594 595 // The type of the local variable 596 uint32 type_id = 2; 597 598 // The id of the function to which the local variable should be added 599 uint32 function_id = 3; 600 601 // Initial value of the variable 602 uint32 initializer_id = 4; 603 604 // True if and only if the behaviour of the module should not depend on the 605 // value of the variable, in which case stores to the variable can be 606 // performed in an arbitrary fashion. 607 bool value_is_irrelevant = 5; 608 609} 610 611message TransformationAddNoContractionDecoration { 612 613 // Applies OpDecorate NoContraction to the given result id 614 615 // Result id to be decorated 616 uint32 result_id = 1; 617 618} 619 620message TransformationAddTypeArray { 621 622 // Adds an array type of the given element type and size to the module 623 624 // Fresh id for the array type 625 uint32 fresh_id = 1; 626 627 // The array's element type 628 uint32 element_type_id = 2; 629 630 // The array's size 631 uint32 size_id = 3; 632 633} 634 635message TransformationAddTypeBoolean { 636 637 // Adds OpTypeBool to the module 638 639 // Id to be used for the type 640 uint32 fresh_id = 1; 641 642} 643 644message TransformationAddTypeFloat { 645 646 // Adds OpTypeFloat to the module with the given width 647 648 // Id to be used for the type 649 uint32 fresh_id = 1; 650 651 // Floating-point width 652 uint32 width = 2; 653 654} 655 656message TransformationAddTypeFunction { 657 658 // Adds a function type to the module 659 660 // Fresh id for the function type 661 uint32 fresh_id = 1; 662 663 // The function's return type 664 uint32 return_type_id = 2; 665 666 // The function's argument types 667 repeated uint32 argument_type_id = 3; 668 669} 670 671message TransformationAddTypeInt { 672 673 // Adds OpTypeInt to the module with the given width and signedness 674 675 // Id to be used for the type 676 uint32 fresh_id = 1; 677 678 // Integer width 679 uint32 width = 2; 680 681 // True if and only if this is a signed type 682 bool is_signed = 3; 683 684} 685 686message TransformationAddTypeMatrix { 687 688 // Adds a matrix type to the module 689 690 // Fresh id for the matrix type 691 uint32 fresh_id = 1; 692 693 // The matrix's column type, which must be a floating-point vector (as per 694 // the "data rules" in the SPIR-V specification). 695 uint32 column_type_id = 2; 696 697 // The matrix's column count 698 uint32 column_count = 3; 699 700} 701 702message TransformationAddTypePointer { 703 704 // Adds OpTypePointer to the module, with the given storage class and base 705 // type 706 707 // Id to be used for the type 708 uint32 fresh_id = 1; 709 710 // Pointer storage class 711 uint32 storage_class = 2; 712 713 // Id of the base type for the pointer 714 uint32 base_type_id = 3; 715 716} 717 718message TransformationAddTypeStruct { 719 720 // Adds a struct type to the module 721 722 // Fresh id for the struct type 723 uint32 fresh_id = 1; 724 725 // The struct's member types 726 repeated uint32 member_type_id = 3; 727 728} 729 730message TransformationAddTypeVector { 731 732 // Adds a vector type to the module 733 734 // Fresh id for the vector type 735 uint32 fresh_id = 1; 736 737 // The vector's component type 738 uint32 component_type_id = 2; 739 740 // The vector's component count 741 uint32 component_count = 3; 742 743} 744 745message TransformationCompositeConstruct { 746 747 // A transformation that introduces an OpCompositeConstruct instruction to 748 // make a composite object. 749 750 // Id of the type of the composite that is to be constructed 751 uint32 composite_type_id = 1; 752 753 // Ids of the objects that will form the components of the composite 754 repeated uint32 component = 2; 755 756 // A descriptor for an instruction in a block before which the new 757 // OpCompositeConstruct instruction should be inserted 758 InstructionDescriptor instruction_to_insert_before = 3; 759 760 // A fresh id for the composite object 761 uint32 fresh_id = 4; 762 763} 764 765message TransformationCompositeExtract { 766 767 // A transformation that adds an instruction to extract an element from a 768 // composite. 769 770 // A descriptor for an instruction in a block before which the new 771 // OpCompositeExtract instruction should be inserted 772 InstructionDescriptor instruction_to_insert_before = 1; 773 774 // Result id for the extract operation. 775 uint32 fresh_id = 2; 776 777 // Id of the composite from which data is to be extracted. 778 uint32 composite_id = 3; 779 780 // Indices that indicate which part of the composite should be extracted. 781 repeated uint32 index = 4; 782 783} 784 785message TransformationComputeDataSynonymFactClosure { 786 787 // A transformation that impacts the fact manager only, forcing a computation 788 // of the closure of data synonym facts, so that e.g. if the components of 789 // vectors v and w are known to be pairwise synonymous, it is deduced that v 790 // and w are themselves synonymous. 791 792 // When searching equivalence classes for implied facts, equivalence classes 793 // larger than this size will be skipped. 794 uint32 maximum_equivalence_class_size = 1; 795 796} 797 798message TransformationCopyObject { 799 800 // A transformation that introduces an OpCopyObject instruction to make a 801 // copy of an object. 802 803 // Id of the object to be copied 804 uint32 object = 1; 805 806 // A descriptor for an instruction in a block before which the new 807 // OpCopyObject instruction should be inserted 808 InstructionDescriptor instruction_to_insert_before = 2; 809 810 // A fresh id for the copied object 811 uint32 fresh_id = 3; 812 813} 814 815message TransformationEquationInstruction { 816 817 // A transformation that adds an instruction to the module that defines an 818 // equation between its result id and input operand ids, such that the 819 // equation is guaranteed to hold at any program point where all ids involved 820 // are available (i.e. at any program point dominated by the instruction). 821 822 // The result id of the new instruction 823 uint32 fresh_id = 1; 824 825 // The instruction's opcode 826 uint32 opcode = 2; 827 828 // The input operands to the instruction 829 repeated uint32 in_operand_id = 3; 830 831 // A descriptor for an instruction in a block before which the new 832 // instruction should be inserted 833 InstructionDescriptor instruction_to_insert_before = 4; 834 835} 836 837message TransformationFunctionCall { 838 839 // A transformation that introduces an OpFunctionCall instruction. The call 840 // must not make the module's call graph cyclic. Beyond that, if the call 841 // is in a dead block it can be to any function with arbitrary suitably-typed 842 // arguments; otherwise it must be to a livesafe function, with injected 843 // variables as pointer arguments and arbitrary non-pointer arguments. 844 845 // A fresh id for the result of the call 846 uint32 fresh_id = 1; 847 848 // Id of the function to be called 849 uint32 callee_id = 2; 850 851 // Ids for arguments to the function 852 repeated uint32 argument_id = 3; 853 854 // A descriptor for an instruction in a block before which the new 855 // OpFunctionCall instruction should be inserted 856 InstructionDescriptor instruction_to_insert_before = 4; 857 858} 859 860message TransformationLoad { 861 862 // Transformation that adds an OpLoad instruction from a pointer into an id. 863 864 // The result of the load instruction 865 uint32 fresh_id = 1; 866 867 // The pointer to be loaded from 868 uint32 pointer_id = 2; 869 870 // A descriptor for an instruction in a block before which the new OpLoad 871 // instruction should be inserted 872 InstructionDescriptor instruction_to_insert_before = 3; 873 874} 875 876message TransformationMergeBlocks { 877 878 // A transformation that merges a block with its predecessor. 879 880 // The id of the block that is to be merged with its predecessor; the merged 881 // block will have the *predecessor's* id. 882 uint32 block_id = 1; 883 884} 885 886message TransformationMoveBlockDown { 887 888 // A transformation that moves a basic block to be one position lower in 889 // program order. 890 891 // The id of the block to move down. 892 uint32 block_id = 1; 893} 894 895message TransformationOutlineFunction { 896 897 // A transformation that outlines a single-entry single-exit region of a 898 // control flow graph into a separate function, and replaces the region with 899 // a call to that function. 900 901 // Id of the entry block of the single-entry single-exit region to be outlined 902 uint32 entry_block = 1; 903 904 // Id of the exit block of the single-entry single-exit region to be outlined 905 uint32 exit_block = 2; 906 907 // Id of a struct that will store the return values of the new function 908 uint32 new_function_struct_return_type_id = 3; 909 910 // A fresh id for the type of the outlined function 911 uint32 new_function_type_id = 4; 912 913 // A fresh id for the outlined function itself 914 uint32 new_function_id = 5; 915 916 // A fresh id to represent the block in the outlined function that represents 917 // the first block of the outlined region. 918 uint32 new_function_region_entry_block = 6; 919 920 // A fresh id for the result of the OpFunctionCall instruction that will call 921 // the outlined function 922 uint32 new_caller_result_id = 7; 923 924 // A fresh id to capture the return value of the outlined function - the 925 // argument to OpReturn 926 uint32 new_callee_result_id = 8; 927 928 // Ids defined outside the region and used inside the region will become 929 // parameters to the outlined function. This is a mapping from used ids to 930 // fresh parameter ids. 931 repeated UInt32Pair input_id_to_fresh_id = 9; 932 933 // Ids defined inside the region and used outside the region will become 934 // fresh ids defined by the outlined function, which get copied into the 935 // function's struct return value and then copied into their destination ids 936 // by the caller. This is a mapping from original ids to corresponding fresh 937 // ids. 938 repeated UInt32Pair output_id_to_fresh_id = 10; 939 940} 941 942message TransformationPermuteFunctionParameters { 943 944 // A transformation that, given a non-entry-point function taking n 945 // parameters and a permutation of the set [0, n-1]: 946 // - Introduces a new function type that is the same as the original 947 // function's type but with the order of arguments permuted 948 // (only if it doesn't already exist) 949 // - Changes the type of the function to this type 950 // - Adjusts all calls to the function so that their arguments are permuted 951 952 // Function, whose parameters will be permuted 953 uint32 function_id = 1; 954 955 // |new_type_id| is a result id of a valid OpTypeFunction instruction. 956 // New type is valid if: 957 // - it has the same number of operands as the old one 958 // - function's result type is the same as the old one 959 // - function's arguments are permuted according to |permutation| vector 960 uint32 new_type_id = 2; 961 962 // An array of size |n|, where |n| is a number of arguments to a function 963 // with |function_id|. For each i: 0 <= permutation[i] < n. 964 // 965 // i-th element of this array contains a position for an i-th 966 // function's argument (i.e. i-th argument will be permutation[i]-th 967 // after running this transformation) 968 repeated uint32 permutation = 3; 969 970} 971 972message TransformationReplaceBooleanConstantWithConstantBinary { 973 974 // A transformation to capture replacing a use of a boolean constant with 975 // binary operation on two constant values 976 977 // A descriptor for the boolean constant id we would like to replace 978 IdUseDescriptor id_use_descriptor = 1; 979 980 // Id for the constant to be used on the LHS of the comparision 981 uint32 lhs_id = 2; 982 983 // Id for the constant to be used on the RHS of the comparision 984 uint32 rhs_id = 3; 985 986 // Opcode for binary operator 987 uint32 opcode = 4; 988 989 // Id that will store the result of the binary operation instruction 990 uint32 fresh_id_for_binary_operation = 5; 991 992} 993 994message TransformationReplaceConstantWithUniform { 995 996 // Replaces a use of a constant id with the result of a load from an 997 // element of uniform buffer known to hold the same value as the constant 998 999 // A descriptor for the id we would like to replace 1000 IdUseDescriptor id_use_descriptor = 1; 1001 1002 // Uniform descriptor to identify which uniform value to choose 1003 UniformBufferElementDescriptor uniform_descriptor = 2; 1004 1005 // Id that will store the result of an access chain 1006 uint32 fresh_id_for_access_chain = 3; 1007 1008 // Id that will store the result of a load 1009 uint32 fresh_id_for_load = 4; 1010 1011} 1012 1013message TransformationReplaceIdWithSynonym { 1014 1015 // Replaces a use of an id with an id that is known to be synonymous, e.g. 1016 // because it was obtained via applying OpCopyObject 1017 1018 // The id use that is to be replaced 1019 IdUseDescriptor id_use_descriptor = 1; 1020 1021 // The synonymous id 1022 uint32 synonymous_id = 2; 1023 1024} 1025 1026message TransformationSetFunctionControl { 1027 1028 // A transformation that sets the function control operand of an OpFunction 1029 // instruction. 1030 1031 // The result id of an OpFunction instruction 1032 uint32 function_id = 1; 1033 1034 // The value to which the 'function control' operand should be set. 1035 uint32 function_control = 2; 1036 1037} 1038 1039message TransformationSetLoopControl { 1040 1041 // A transformation that sets the loop control operand of an OpLoopMerge 1042 // instruction. 1043 1044 // The id of a basic block that should contain OpLoopMerge 1045 uint32 block_id = 1; 1046 1047 // The value to which the 'loop control' operand should be set. 1048 // This must be a legal loop control mask. 1049 uint32 loop_control = 2; 1050 1051 // Provides a peel count value for the loop. Used if and only if the 1052 // PeelCount bit is set. Must be zero if the PeelCount bit is not set (can 1053 // still be zero if this bit is set). 1054 uint32 peel_count = 3; 1055 1056 // Provides a partial count value for the loop. Used if and only if the 1057 // PartialCount bit is set. Must be zero if the PartialCount bit is not set 1058 // (can still be zero if this bit is set). 1059 uint32 partial_count = 4; 1060 1061} 1062 1063message TransformationSetMemoryOperandsMask { 1064 1065 // A transformation that sets the memory operands mask of a memory access 1066 // instruction. 1067 1068 // A descriptor for a memory access instruction, e.g. an OpLoad 1069 InstructionDescriptor memory_access_instruction = 1; 1070 1071 // A mask of memory operands to be applied to the instruction. It must be the 1072 // same as the original mask, except that Volatile can be added, and 1073 // Nontemporal can be added or removed. 1074 uint32 memory_operands_mask = 2; 1075 1076 // Some memory access instructions allow more than one mask to be specified; 1077 // this field indicates which mask should be set 1078 uint32 memory_operands_mask_index = 3; 1079 1080} 1081 1082message TransformationSetSelectionControl { 1083 1084 // A transformation that sets the selection control operand of an 1085 // OpSelectionMerge instruction. 1086 1087 // The id of a basic block that should contain OpSelectionMerge 1088 uint32 block_id = 1; 1089 1090 // The value to which the 'selection control' operand should be set. 1091 // Although technically 'selection control' is a literal mask that can be 1092 // some combination of 'None', 'Flatten' and 'DontFlatten', the combination 1093 // 'Flatten | DontFlatten' does not make sense and is not allowed here. 1094 uint32 selection_control = 2; 1095 1096} 1097 1098message TransformationSplitBlock { 1099 1100 // A transformation that splits a basic block into two basic blocks 1101 1102 // A descriptor for an instruction such that the block containing the 1103 // described instruction should be split right before the instruction. 1104 InstructionDescriptor instruction_to_split_before = 1; 1105 1106 // An id that must not yet be used by the module to which this transformation 1107 // is applied. Rather than having the transformation choose a suitable id on 1108 // application, we require the id to be given upfront in order to facilitate 1109 // reducing fuzzed shaders by removing transformations. The reason is that 1110 // future transformations may refer to the fresh id introduced by this 1111 // transformation, and if we end up changing what that id is, due to removing 1112 // earlier transformations, it may inhibit later transformations from 1113 // applying. 1114 uint32 fresh_id = 2; 1115 1116} 1117 1118message TransformationStore { 1119 1120 // Transformation that adds an OpStore instruction of an id to a pointer. 1121 1122 // The pointer to be stored to 1123 uint32 pointer_id = 1; 1124 1125 // The value to be stored 1126 uint32 value_id = 2; 1127 1128 // A descriptor for an instruction in a block before which the new OpStore 1129 // instruction should be inserted 1130 InstructionDescriptor instruction_to_insert_before = 3; 1131 1132} 1133 1134message TransformationSwapCommutableOperands { 1135 1136 // A transformation that swaps the operands of a commutative instruction. 1137 1138 // A descriptor for a commutative instruction 1139 InstructionDescriptor instruction_descriptor = 1; 1140 1141} 1142 1143message TransformationToggleAccessChainInstruction { 1144 1145 // A transformation that toggles an access chain instruction. 1146 1147 // A descriptor for an access chain instruction 1148 InstructionDescriptor instruction_descriptor = 1; 1149 1150} 1151 1152message TransformationVectorShuffle { 1153 1154 // A transformation that adds a vector shuffle instruction. 1155 1156 // A descriptor for an instruction in a block before which the new 1157 // OpVectorShuffle instruction should be inserted 1158 InstructionDescriptor instruction_to_insert_before = 1; 1159 1160 // Result id for the shuffle operation. 1161 uint32 fresh_id = 2; 1162 1163 // Id of the first vector operand. 1164 uint32 vector1 = 3; 1165 1166 // Id of the second vector operand. 1167 uint32 vector2 = 4; 1168 1169 // Indices that indicate which components of the input vectors should be used. 1170 repeated uint32 component = 5; 1171 1172} 1173