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 InstructionDescriptor { 25 26 // Describes an instruction in some block of a function with respect to a 27 // base instruction. 28 29 // The id of an instruction after which the instruction being described is 30 // believed to be located. It might be the using instruction itself. 31 uint32 base_instruction_result_id = 1; 32 33 // The opcode for the instruction being described. 34 uint32 target_instruction_opcode = 2; 35 36 // The number of matching opcodes to skip over when searching from the base 37 // instruction to the instruction being described. 38 uint32 num_opcodes_to_ignore = 3; 39 40} 41 42message IdUseDescriptor { 43 44 // Describes a use of an id as an input operand to an instruction in some 45 // block of a function. 46 47 // Example: 48 // - id_of_interest = 42 49 // - enclosing_instruction = ( 50 // base_instruction_result_id = 50, 51 // target_instruction_opcode = OpStore 52 // num_opcodes_to_ignore = 7 53 // ) 54 // - in_operand_index = 1 55 // represents a use of id 42 as input operand 1 to an OpStore instruction, 56 // such that the OpStore instruction can be found in the same basic block as 57 // the instruction with result id 50, and in particular is the 8th OpStore 58 // instruction found from instruction 50 onwards (i.e. 7 OpStore 59 // instructions are skipped). 60 61 // An id that we would like to be able to find a use of. 62 uint32 id_of_interest = 1; 63 64 // The input operand index at which the use is expected. 65 InstructionDescriptor enclosing_instruction = 2; 66 67 uint32 in_operand_index = 3; 68 69} 70 71message DataDescriptor { 72 73 // Represents a data element that can be accessed from an id, by walking the 74 // type hierarchy via a sequence of 0 or more indices. 75 // 76 // Very similar to a UniformBufferElementDescriptor, except that a 77 // DataDescriptor is rooted at the id of a scalar or composite. 78 79 // The object being accessed - a scalar or composite 80 uint32 object = 1; 81 82 // 0 or more indices, used to index into a composite object 83 repeated uint32 index = 2; 84 85} 86 87message UniformBufferElementDescriptor { 88 89 // Represents a data element inside a uniform buffer. The element is 90 // specified via (a) the result id of a uniform variable in which the element 91 // is contained, and (b) a series of indices that need to be followed to get 92 // to the element (via fields and array/vector indices). 93 // 94 // Example: suppose there is a uniform variable with descriptor set 7 and 95 // binding 9, and that the uniform variable has the following type (using 96 // GLSL-like syntax): 97 // 98 // struct S { 99 // float f; 100 // vec3 g; 101 // int4 h[10]; 102 // }; 103 // 104 // Then: 105 // - (7, 9, [0]) describes the 'f' field. 106 // - (7, 9, [1,1]) describes the y component of the 'g' field. 107 // - (7, 9, [2,7,3]) describes the w component of element 7 of the 'h' field 108 109 // The descriptor set and binding associated with a uniform variable. 110 uint32 descriptor_set = 1; 111 uint32 binding = 2; 112 113 // An ordered sequence of indices through composite structures in the 114 // uniform buffer. 115 repeated uint32 index = 3; 116 117} 118 119message FactSequence { 120 repeated Fact fact = 1; 121} 122 123message Fact { 124 oneof fact { 125 // Order the fact options by numeric id (rather than alphabetically). 126 FactConstantUniform constant_uniform_fact = 1; 127 FactDataSynonym data_synonym_fact = 2; 128 } 129} 130 131// Keep fact message types in alphabetical order: 132 133message FactConstantUniform { 134 135 // Records the fact that a uniform buffer element is guaranteed to be equal 136 // to a particular constant value. spirv-fuzz can use such guarantees to 137 // obfuscate code, e.g. to manufacture an expression that will (due to the 138 // guarantee) evaluate to a particular value at runtime but in a manner that 139 // cannot be predicted at compile-time. 140 141 // An element of a uniform buffer 142 UniformBufferElementDescriptor uniform_buffer_element_descriptor = 1; 143 144 // The words of the associated constant 145 repeated uint32 constant_word = 2; 146 147} 148 149message FactDataSynonym { 150 151 // Records the fact that the data held in two data descriptors are guaranteed 152 // to be equal. spirv-fuzz can use this to replace uses of one piece of data 153 // with a known-to-be-equal piece of data. 154 155 // Data descriptors guaranteed to hold identical data. 156 DataDescriptor data1 = 1; 157 158 DataDescriptor data2 = 2; 159 160} 161 162message TransformationSequence { 163 repeated Transformation transformation = 1; 164} 165 166message Transformation { 167 oneof transformation { 168 // Order the transformation options by numeric id (rather than 169 // alphabetically). 170 TransformationMoveBlockDown move_block_down = 1; 171 TransformationSplitBlock split_block = 2; 172 TransformationAddConstantBoolean add_constant_boolean = 3; 173 TransformationAddConstantScalar add_constant_scalar = 4; 174 TransformationAddTypeBoolean add_type_boolean = 5; 175 TransformationAddTypeFloat add_type_float = 6; 176 TransformationAddTypeInt add_type_int = 7; 177 TransformationAddDeadBreak add_dead_break = 8; 178 TransformationReplaceBooleanConstantWithConstantBinary 179 replace_boolean_constant_with_constant_binary = 9; 180 TransformationAddTypePointer add_type_pointer = 10; 181 TransformationReplaceConstantWithUniform replace_constant_with_uniform = 11; 182 TransformationAddDeadContinue add_dead_continue = 12; 183 TransformationCopyObject copy_object = 13; 184 TransformationReplaceIdWithSynonym replace_id_with_synonym = 14; 185 TransformationSetSelectionControl set_selection_control = 15; 186 TransformationCompositeConstruct composite_construct = 16; 187 TransformationSetLoopControl set_loop_control = 17; 188 TransformationSetFunctionControl set_function_control = 18; 189 TransformationAddNoContractionDecoration add_no_contraction_decoration = 19; 190 TransformationSetMemoryOperandsMask set_memory_operands_mask = 20; 191 TransformationCompositeExtract composite_extract = 21; 192 // Add additional option using the next available number. 193 } 194} 195 196// Keep transformation message types in alphabetical order: 197 198message TransformationAddConstantBoolean { 199 200 // Supports adding the constants true and false to a module, which may be 201 // necessary in order to enable other transformations if they are not present. 202 203 uint32 fresh_id = 1; 204 bool is_true = 2; 205 206} 207 208message TransformationAddConstantScalar { 209 210 // Adds a constant of the given scalar type 211 212 // Id for the constant 213 uint32 fresh_id = 1; 214 215 // Id for the scalar type of the constant 216 uint32 type_id = 2; 217 218 // Value of the constant 219 repeated uint32 word = 3; 220 221} 222 223message TransformationAddDeadBreak { 224 225 // A transformation that turns a basic block that unconditionally branches to 226 // its successor into a block that potentially breaks out of a structured 227 // control flow construct, but in such a manner that the break cannot actually 228 // be taken. 229 230 // The block to break from 231 uint32 from_block = 1; 232 233 // The merge block to break to 234 uint32 to_block = 2; 235 236 // Determines whether the break condition is true or false 237 bool break_condition_value = 3; 238 239 // A sequence of ids suitable for extending OpPhi instructions as a result of 240 // the new break edge 241 repeated uint32 phi_id = 4; 242 243} 244 245message TransformationAddDeadContinue { 246 247 // A transformation that turns a basic block appearing in a loop and that 248 // unconditionally branches to its successor into a block that potentially 249 // branches to the continue target of the loop, but in such a manner that the 250 // continue branch cannot actually be taken. 251 252 // The block to continue from 253 uint32 from_block = 1; 254 255 // Determines whether the continue condition is true or false 256 bool continue_condition_value = 2; 257 258 // A sequence of ids suitable for extending OpPhi instructions as a result of 259 // the new break edge 260 repeated uint32 phi_id = 3; 261 262} 263 264message TransformationAddNoContractionDecoration { 265 266 // Applies OpDecorate NoContraction to the given result id 267 268 // Result id to be decorated 269 uint32 result_id = 1; 270 271} 272 273message TransformationAddTypeBoolean { 274 275 // Adds OpTypeBool to the module 276 277 // Id to be used for the type 278 uint32 fresh_id = 1; 279 280} 281 282message TransformationAddTypeFloat { 283 284 // Adds OpTypeFloat to the module with the given width 285 286 // Id to be used for the type 287 uint32 fresh_id = 1; 288 289 // Floating-point width 290 uint32 width = 2; 291 292} 293 294message TransformationAddTypeInt { 295 296 // Adds OpTypeInt to the module with the given width and signedness 297 298 // Id to be used for the type 299 uint32 fresh_id = 1; 300 301 // Integer width 302 uint32 width = 2; 303 304 // True if and only if this is a signed type 305 bool is_signed = 3; 306 307} 308 309message TransformationAddTypePointer { 310 311 // Adds OpTypePointer to the module, with the given storage class and base 312 // type 313 314 // Id to be used for the type 315 uint32 fresh_id = 1; 316 317 // Pointer storage class 318 uint32 storage_class = 2; 319 320 // Id of the base type for the pointer 321 uint32 base_type_id = 3; 322 323} 324 325message TransformationCompositeConstruct { 326 327 // A transformation that introduces an OpCompositeConstruct instruction to 328 // make a composite object. 329 330 // Id of the type of the composite that is to be constructed 331 uint32 composite_type_id = 1; 332 333 // Ids of the objects that will form the components of the composite 334 repeated uint32 component = 2; 335 336 // A descriptor for an instruction in a block before which the new 337 // OpCompositeConstruct instruction should be inserted 338 InstructionDescriptor instruction_to_insert_before = 3; 339 340 // A fresh id for the composite object 341 uint32 fresh_id = 4; 342 343} 344 345message TransformationCompositeExtract { 346 347 // A transformation that adds an instruction to extract an element from a 348 // composite. 349 350 // A descriptor for an instruction in a block before which the new 351 // OpCompositeExtract instruction should be inserted 352 InstructionDescriptor instruction_to_insert_before = 1; 353 354 // Result id for the extract operation. 355 uint32 fresh_id = 2; 356 357 // Id of the composite from which data is to be extracted. 358 uint32 composite_id = 3; 359 360 // Indices that indicate which part of the composite should be extracted. 361 repeated uint32 index = 4; 362 363} 364 365message TransformationCopyObject { 366 367 // A transformation that introduces an OpCopyObject instruction to make a 368 // copy of an object. 369 370 // Id of the object to be copied 371 uint32 object = 1; 372 373 // A descriptor for an instruction in a block before which the new 374 // OpCopyObject instruction should be inserted 375 InstructionDescriptor instruction_to_insert_before = 2; 376 377 // A fresh id for the copied object 378 uint32 fresh_id = 3; 379 380} 381 382message TransformationMoveBlockDown { 383 384 // A transformation that moves a basic block to be one position lower in 385 // program order. 386 387 // The id of the block to move down. 388 uint32 block_id = 1; 389} 390 391message TransformationReplaceBooleanConstantWithConstantBinary { 392 393 // A transformation to capture replacing a use of a boolean constant with 394 // binary operation on two constant values 395 396 // A descriptor for the boolean constant id we would like to replace 397 IdUseDescriptor id_use_descriptor = 1; 398 399 // Id for the constant to be used on the LHS of the comparision 400 uint32 lhs_id = 2; 401 402 // Id for the constant to be used on the RHS of the comparision 403 uint32 rhs_id = 3; 404 405 // Opcode for binary operator 406 uint32 opcode = 4; 407 408 // Id that will store the result of the binary operation instruction 409 uint32 fresh_id_for_binary_operation = 5; 410 411} 412 413message TransformationReplaceConstantWithUniform { 414 415 // Replaces a use of a constant id with the result of a load from an 416 // element of uniform buffer known to hold the same value as the constant 417 418 // A descriptor for the id we would like to replace 419 IdUseDescriptor id_use_descriptor = 1; 420 421 // Uniform descriptor to identify which uniform value to choose 422 UniformBufferElementDescriptor uniform_descriptor = 2; 423 424 // Id that will store the result of an access chain 425 uint32 fresh_id_for_access_chain = 3; 426 427 // Id that will store the result of a load 428 uint32 fresh_id_for_load = 4; 429 430} 431 432message TransformationReplaceIdWithSynonym { 433 434 // Replaces an id use with something known to be synonymous with that id use, 435 // e.g. because it was obtained via applying OpCopyObject 436 437 // Identifies the id use that is to be replaced 438 IdUseDescriptor id_use_descriptor = 1; 439 440 // Identifies the data with which the id use is expected to be synonymous 441 DataDescriptor data_descriptor = 2; 442 443 // In the case that a temporary is required to express the synonym (e.g. to 444 // obtain an element of a vector, provides a fresh id for the temporary; 445 // should be set to 0 if no temporary is required 446 uint32 fresh_id_for_temporary = 3; 447} 448 449message TransformationSetFunctionControl { 450 451 // A transformation that sets the function control operand of an OpFunction 452 // instruction. 453 454 // The result id of an OpFunction instruction 455 uint32 function_id = 1; 456 457 // The value to which the 'function control' operand should be set. 458 uint32 function_control = 2; 459 460} 461 462message TransformationSetLoopControl { 463 464 // A transformation that sets the loop control operand of an OpLoopMerge 465 // instruction. 466 467 // The id of a basic block that should contain OpLoopMerge 468 uint32 block_id = 1; 469 470 // The value to which the 'loop control' operand should be set. 471 // This must be a legal loop control mask. 472 uint32 loop_control = 2; 473 474 // Provides a peel count value for the loop. Used if and only if the 475 // PeelCount bit is set. Must be zero if the PeelCount bit is not set (can 476 // still be zero if this bit is set). 477 uint32 peel_count = 3; 478 479 // Provides a partial count value for the loop. Used if and only if the 480 // PartialCount bit is set. Must be zero if the PartialCount bit is not set 481 // (can still be zero if this bit is set). 482 uint32 partial_count = 4; 483 484} 485 486message TransformationSetMemoryOperandsMask { 487 488 // A transformation that sets the memory operands mask of a memory access 489 // instruction. 490 491 // A descriptor for a memory access instruction, e.g. an OpLoad 492 InstructionDescriptor memory_access_instruction = 1; 493 494 // A mask of memory operands to be applied to the instruction. It must be the 495 // same as the original mask, except that Volatile can be added, and 496 // Nontemporal can be added or removed. 497 uint32 memory_operands_mask = 2; 498 499 // Some memory access instructions allow more than one mask to be specified; 500 // this field indicates which mask should be set 501 uint32 memory_operands_mask_index = 3; 502 503} 504 505message TransformationSetSelectionControl { 506 507 // A transformation that sets the selection control operand of an 508 // OpSelectionMerge instruction. 509 510 // The id of a basic block that should contain OpSelectionMerge 511 uint32 block_id = 1; 512 513 // The value to which the 'selection control' operand should be set. 514 // Although technically 'selection control' is a literal mask that can be 515 // some combination of 'None', 'Flatten' and 'DontFlatten', the combination 516 // 'Flatten | DontFlatten' does not make sense and is not allowed here. 517 uint32 selection_control = 2; 518 519} 520 521message TransformationSplitBlock { 522 523 // A transformation that splits a basic block into two basic blocks 524 525 // A descriptor for an instruction such that the block containing the 526 // described instruction should be split right before the instruction. 527 InstructionDescriptor instruction_to_split_before = 1; 528 529 // An id that must not yet be used by the module to which this transformation 530 // is applied. Rather than having the transformation choose a suitable id on 531 // application, we require the id to be given upfront in order to facilitate 532 // reducing fuzzed shaders by removing transformations. The reason is that 533 // future transformations may refer to the fresh id introduced by this 534 // transformation, and if we end up changing what that id is, due to removing 535 // earlier transformations, it may inhibit later transformations from 536 // applying. 537 uint32 fresh_id = 2; 538 539} 540