blob: 4e8dcac3caf00fec742b2bfbf2a2d8db81fc71a8 [file] [log] [blame]
// 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;
}