| // Copyright (c) 2019 Google LLC | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| // This file is specifically named spvtools_fuzz.proto so that the string | |
| // 'spvtools_fuzz' appears in the names of global-scope symbols that protoc | |
| // generates when targeting C++. This is to reduce the potential for name | |
| // clashes with other globally-scoped symbols. | |
| syntax = "proto3"; | |
| package spvtools.fuzz.protobufs; | |
| message IdUseDescriptor { | |
| // Describes a use of an id as an input operand to an instruction in some block | |
| // of a function. | |
| // Example: | |
| // - id_of_interest = 42 | |
| // - target_instruction_opcode = OpStore | |
| // - in_operand_index = 1 | |
| // - base_instruction_result_id = 50 | |
| // - num_opcodes_to_ignore = 7 | |
| // represents a use of id 42 as input operand 1 to an OpStore instruction, | |
| // such that the OpStore instruction can be found in the same basic block as | |
| // the instruction with result id 50, and in particular is the 8th OpStore | |
| // instruction found from instruction 50 onwards (i.e. 7 OpStore | |
| // instructions are skipped). | |
| // An id that we would like to be able to find a use of. | |
| uint32 id_of_interest = 1; | |
| // The opcode for the instruction that uses the id. | |
| uint32 target_instruction_opcode = 2; | |
| // The input operand index at which the use is expected. | |
| uint32 in_operand_index = 3; | |
| // The id of an instruction after which the instruction that contains the use | |
| // is believed to occur; it might be the using instruction itself. | |
| uint32 base_instruction_result_id = 4; | |
| // The number of matching opcodes to skip over when searching for the using | |
| // instruction from the base instruction. | |
| uint32 num_opcodes_to_ignore = 5; | |
| } | |
| message DataDescriptor { | |
| // Represents a data element that can be accessed from an id, by walking the | |
| // type hierarchy via a sequence of 0 or more indices. | |
| // | |
| // Very similar to a UniformBufferElementDescriptor, except that a | |
| // DataDescriptor is rooted at the id of a scalar or composite. | |
| // The object being accessed - a scalar or composite | |
| uint32 object = 1; | |
| // 0 or more indices, used to index into a composite object | |
| repeated uint32 index = 2; | |
| } | |
| message UniformBufferElementDescriptor { | |
| // Represents a data element inside a uniform buffer. The element is | |
| // specified via (a) the result id of a uniform variable in which the element | |
| // is contained, and (b) a series of indices that need to be followed to get | |
| // to the element (via fields and array/vector indices). | |
| // | |
| // Example: suppose there is a uniform variable with descriptor set 7 and | |
| // binding 9, and that the uniform variable has the following type (using | |
| // GLSL-like syntax): | |
| // | |
| // struct S { | |
| // float f; | |
| // vec3 g; | |
| // int4 h[10]; | |
| // }; | |
| // | |
| // Then: | |
| // - (7, 9, [0]) describes the 'f' field. | |
| // - (7, 9, [1,1]) describes the y component of the 'g' field. | |
| // - (7, 9, [2,7,3]) describes the w component of element 7 of the 'h' field | |
| // The descriptor set and binding associated with a uniform variable. | |
| uint32 descriptor_set = 1; | |
| uint32 binding = 2; | |
| // An ordered sequence of indices through composite structures in the | |
| // uniform buffer. | |
| repeated uint32 index = 3; | |
| } | |
| message FactSequence { | |
| repeated Fact fact = 1; | |
| } | |
| message Fact { | |
| oneof fact { | |
| // Order the fact options by numeric id (rather than alphabetically). | |
| FactConstantUniform constant_uniform_fact = 1; | |
| FactIdSynonym id_synonym_fact = 2; | |
| } | |
| } | |
| // Keep fact message types in alphabetical order: | |
| message FactConstantUniform { | |
| // Records the fact that a uniform buffer element is guaranteed to be equal | |
| // to a particular constant value. spirv-fuzz can use such guarantees to | |
| // obfuscate code, e.g. to manufacture an expression that will (due to the | |
| // guarantee) evaluate to a particular value at runtime but in a manner that | |
| // cannot be predicted at compile-time. | |
| // An element of a uniform buffer | |
| UniformBufferElementDescriptor uniform_buffer_element_descriptor = 1; | |
| // The words of the associated constant | |
| repeated uint32 constant_word = 2; | |
| } | |
| message FactIdSynonym { | |
| // Records the fact that the data held in an id is guaranteed to be equal to | |
| // the data held in a data descriptor. spirv-fuzz can use this to replace | |
| // uses of the id with references to the data described by the data | |
| // descriptor. | |
| // An id | |
| uint32 id = 1; | |
| // A data descriptor guaranteed to hold a value identical to that held by the | |
| // id | |
| DataDescriptor data_descriptor = 2; | |
| } | |
| message TransformationSequence { | |
| repeated Transformation transformation = 1; | |
| } | |
| message Transformation { | |
| oneof transformation { | |
| // Order the transformation options by numeric id (rather than | |
| // alphabetically). | |
| TransformationMoveBlockDown move_block_down = 1; | |
| TransformationSplitBlock split_block = 2; | |
| TransformationAddConstantBoolean add_constant_boolean = 3; | |
| TransformationAddConstantScalar add_constant_scalar = 4; | |
| TransformationAddTypeBoolean add_type_boolean = 5; | |
| TransformationAddTypeFloat add_type_float = 6; | |
| TransformationAddTypeInt add_type_int = 7; | |
| TransformationAddDeadBreak add_dead_break = 8; | |
| TransformationReplaceBooleanConstantWithConstantBinary replace_boolean_constant_with_constant_binary = 9; | |
| TransformationAddTypePointer add_type_pointer = 10; | |
| TransformationReplaceConstantWithUniform replace_constant_with_uniform = 11; | |
| TransformationAddDeadContinue add_dead_continue = 12; | |
| TransformationCopyObject copy_object = 13; | |
| // Add additional option using the next available number. | |
| } | |
| } | |
| // Keep transformation message types in alphabetical order: | |
| message TransformationAddConstantBoolean { | |
| // Supports adding the constants true and false to a module, which may be | |
| // necessary in order to enable other transformations if they are not present. | |
| uint32 fresh_id = 1; | |
| bool is_true = 2; | |
| } | |
| message TransformationAddConstantScalar { | |
| // Adds a constant of the given scalar type | |
| // Id for the constant | |
| uint32 fresh_id = 1; | |
| // Id for the scalar type of the constant | |
| uint32 type_id = 2; | |
| // Value of the constant | |
| repeated uint32 word = 3; | |
| } | |
| message TransformationAddDeadBreak { | |
| // A transformation that turns a basic block that unconditionally branches to | |
| // its successor into a block that potentially breaks out of a structured | |
| // control flow construct, but in such a manner that the break cannot actually | |
| // be taken. | |
| // The block to break from | |
| uint32 from_block = 1; | |
| // The merge block to break to | |
| uint32 to_block = 2; | |
| // Determines whether the break condition is true or false | |
| bool break_condition_value = 3; | |
| // A sequence of ids suitable for extending OpPhi instructions as a result of | |
| // the new break edge | |
| repeated uint32 phi_id = 4; | |
| } | |
| message TransformationAddDeadContinue { | |
| // A transformation that turns a basic block appearing in a loop and that | |
| // unconditionally branches to its successor into a block that potentially | |
| // branches to the continue target of the loop, but in such a manner that the | |
| // continue branch cannot actually be taken. | |
| // The block to continue from | |
| uint32 from_block = 1; | |
| // Determines whether the continue condition is true or false | |
| bool continue_condition_value = 2; | |
| // A sequence of ids suitable for extending OpPhi instructions as a result of | |
| // the new break edge | |
| repeated uint32 phi_id = 3; | |
| } | |
| message TransformationAddTypeBoolean { | |
| // Adds OpTypeBool to the module | |
| // Id to be used for the type | |
| uint32 fresh_id = 1; | |
| } | |
| message TransformationAddTypeFloat { | |
| // Adds OpTypeFloat to the module with the given width | |
| // Id to be used for the type | |
| uint32 fresh_id = 1; | |
| // Floating-point width | |
| uint32 width = 2; | |
| } | |
| message TransformationAddTypeInt { | |
| // Adds OpTypeInt to the module with the given width and signedness | |
| // Id to be used for the type | |
| uint32 fresh_id = 1; | |
| // Integer width | |
| uint32 width = 2; | |
| // True if and only if this is a signed type | |
| bool is_signed = 3; | |
| } | |
| message TransformationAddTypePointer { | |
| // Adds OpTypePointer to the module, with the given storage class and base | |
| // type | |
| // Id to be used for the type | |
| uint32 fresh_id = 1; | |
| // Pointer storage class | |
| uint32 storage_class = 2; | |
| // Id of the base type for the pointer | |
| uint32 base_type_id = 3; | |
| } | |
| message TransformationCopyObject { | |
| // A transformation that introduces an OpCopyObject instruction to make a | |
| // copy of an object. | |
| // Id of the object to be copied | |
| uint32 object = 1; | |
| // The id of an instruction in a block | |
| uint32 base_instruction_id = 2; | |
| // An offset, such that OpCopyObject instruction should be inserted right | |
| // before the instruction |offset| instructions after |base_instruction_id| | |
| uint32 offset = 3; | |
| // A fresh id for the copied object | |
| uint32 fresh_id = 4; | |
| } | |
| message TransformationMoveBlockDown { | |
| // A transformation that moves a basic block to be one position lower in | |
| // program order. | |
| // The id of the block to move down. | |
| uint32 block_id = 1; | |
| } | |
| message TransformationReplaceConstantWithUniform { | |
| // Replaces a use of a constant id with the the result of a load from an | |
| // element of uniform buffer known to hold the same value as the constant | |
| // A descriptor for the id we would like to replace | |
| IdUseDescriptor id_use_descriptor = 1; | |
| // Uniform descriptor to identify which uniform value to choose | |
| UniformBufferElementDescriptor uniform_descriptor = 2; | |
| // Id that will store the result of an access chain | |
| uint32 fresh_id_for_access_chain = 3; | |
| // Id that will store the result of a load | |
| uint32 fresh_id_for_load = 4; | |
| } | |
| message TransformationReplaceBooleanConstantWithConstantBinary { | |
| // A transformation to capture replacing a use of a boolean constant with | |
| // binary operation on two constant values | |
| // A descriptor for the boolean constant id we would like to replace | |
| IdUseDescriptor id_use_descriptor = 1; | |
| // Id for the constant to be used on the LHS of the comparision | |
| uint32 lhs_id = 2; | |
| // Id for the constant to be used on the RHS of the comparision | |
| uint32 rhs_id = 3; | |
| // Opcode for binary operator | |
| uint32 opcode = 4; | |
| // Id that will store the result of the binary operation instruction | |
| uint32 fresh_id_for_binary_operation = 5; | |
| } | |
| message TransformationSplitBlock { | |
| // A transformation that splits a basic block into two basic blocks | |
| // The result id of an instruction | |
| uint32 base_instruction_id = 1; | |
| // An offset, such that the block containing |base_instruction_id| should be | |
| // split right before the instruction |offset| instructions after | |
| // |base_instruction_id| | |
| uint32 offset = 2; | |
| // An id that must not yet be used by the module to which this transformation | |
| // is applied. Rather than having the transformation choose a suitable id on | |
| // application, we require the id to be given upfront in order to facilitate | |
| // reducing fuzzed shaders by removing transformations. The reason is that | |
| // future transformations may refer to the fresh id introduced by this | |
| // transformation, and if we end up changing what that id is, due to removing | |
| // earlier transformations, it may inhibit later transformations from | |
| // applying. | |
| uint32 fresh_id = 3; | |
| } |