blob: 15a9b020a19f167def68ae99616bbb819ac16e58 [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 UInt32Pair {
// A pair of uint32s; useful for defining mappings.
uint32 first = 1;
uint32 second = 2;
}
message InstructionDescriptor {
// Describes an instruction in some block of a function with respect to a
// base instruction.
// The id of an instruction after which the instruction being described is
// believed to be located. It might be the using instruction itself.
uint32 base_instruction_result_id = 1;
// The opcode for the instruction being described.
uint32 target_instruction_opcode = 2;
// The number of matching opcodes to skip over when searching from the base
// instruction to the instruction being described.
uint32 num_opcodes_to_ignore = 3;
}
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
// - enclosing_instruction = (
// base_instruction_result_id = 50,
// target_instruction_opcode = OpStore
// num_opcodes_to_ignore = 7
// )
// - in_operand_index = 1
// 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 input operand index at which the use is expected.
InstructionDescriptor enclosing_instruction = 2;
uint32 in_operand_index = 3;
}
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 InstructionOperand {
// Represents an operand to a SPIR-V instruction.
// The type of the operand.
uint32 operand_type = 1;
// The data associated with the operand. For most operands (e.g. ids,
// storage classes and literals) this will be a single word.
repeated uint32 operand_data = 2;
}
message Instruction {
// Represents a SPIR-V instruction.
// The instruction's opcode (e.g. OpLabel).
uint32 opcode = 1;
// The id of the instruction's result type; 0 if there is no result type.
uint32 result_type_id = 2;
// The id of the instruction's result; 0 if there is no result.
uint32 result_id = 3;
// Zero or more input operands.
repeated InstructionOperand input_operand = 4;
}
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;
FactDataSynonym data_synonym_fact = 2;
FactBlockIsDead block_is_dead_fact = 3;
FactFunctionIsLivesafe function_is_livesafe_fact = 4;
FactPointeeValueIsIrrelevant pointee_value_is_irrelevant_fact = 5;
FactIdEquation id_equation_fact = 6;
FactIdIsIrrelevant id_is_irrelevant = 7;
}
}
// Keep fact message types in alphabetical order:
message FactBlockIsDead {
// Records the fact that a block is guaranteed to be dynamically unreachable.
// This is useful because it informs the fuzzer that rather arbitrary changes
// can be made to this block.
uint32 block_id = 1;
}
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 FactDataSynonym {
// Records the fact that the data held in two data descriptors are guaranteed
// to be equal. spirv-fuzz can use this to replace uses of one piece of data
// with a known-to-be-equal piece of data.
// Data descriptors guaranteed to hold identical data.
DataDescriptor data1 = 1;
DataDescriptor data2 = 2;
}
message FactFunctionIsLivesafe {
// Records the fact that a function is guaranteed to be "livesafe", meaning
// that it will not make out-of-bounds accesses, does not contain reachable
// OpKill or OpUnreachable instructions, does not contain loops that will
// execute for large numbers of iterations, and only invokes other livesafe
// functions.
uint32 function_id = 1;
}
message FactIdEquation {
// Records the fact that the equation:
//
// lhs_id = opcode rhs_id[0] rhs_id[1] ... rhs_id[N-1]
//
// holds; e.g. that the equation:
//
// %12 = OpIAdd %13 %14
//
// holds in the case where lhs_id is 12, rhs_id is [13, 14], and the opcode is
// OpIAdd.
// The left-hand-side of the equation.
uint32 lhs_id = 1;
// A SPIR-V opcode, from a restricted set of instructions for which equation
// facts make sense.
uint32 opcode = 2;
// The operands to the right-hand-side of the equation.
repeated uint32 rhs_id = 3;
}
message FactIdIsIrrelevant {
// Records a fact that |result_id| is irrelevant (i.e. it's usage doesn't
// change the semantics of the module). This implies that a use of this id
// can later be replaced with some other id of the same type, or the
// definition of |result_id| can be changed so that it yields a different value.
// An irrelevant id.
uint32 result_id = 1;
}
message FactPointeeValueIsIrrelevant {
// Records the fact that value of the data pointed to by a pointer id does
// not influence the observable behaviour of the module. This means that
// arbitrary stores can be made through the pointer, and that nothing can be
// guaranteed about the values that are loaded via the pointer.
// A result id of pointer type
uint32 pointer_id = 1;
}
message AccessChainClampingInfo {
// When making a function livesafe it is necessary to clamp the indices that
// occur as operands to access chain instructions so that they are guaranteed
// to be in bounds. This message type allows an access chain instruction to
// have an associated sequence of ids that are reserved for comparing an
// access chain index with a bound (e.g. an array size), and selecting
// between the access chain index (if it is within bounds) and the bound (if
// it is not).
//
// This allows turning an instruction of the form:
//
// %result = OpAccessChain %type %object ... %index ...
//
// into:
//
// %t1 = OpULessThanEqual %bool %index %bound_minus_one
// %t2 = OpSelect %int_type %t1 %index %bound_minus_one
// %result = OpAccessChain %type %object ... %t2 ...
// The result id of an OpAccessChain or OpInBoundsAccessChain instruction.
uint32 access_chain_id = 1;
// A series of pairs of fresh ids, one per access chain index, for the results
// of a compare instruction and a select instruction, serving the roles of %t1
// and %t2 in the above example.
repeated UInt32Pair compare_and_select_ids = 2;
}
message LoopLimiterInfo {
// Structure capturing the information required to manipulate a loop limiter
// at a loop header.
// The header for the loop.
uint32 loop_header_id = 1;
// A fresh id into which the loop limiter's current value can be loaded.
uint32 load_id = 2;
// A fresh id that can be used to increment the loaded value by 1.
uint32 increment_id = 3;
// A fresh id that can be used to compare the loaded value with the loop
// limit.
uint32 compare_id = 4;
// A fresh id that can be used to compute the conjunction or disjunction of
// an original loop exit condition with |compare_id|, if the loop's back edge
// block can conditionally exit the loop.
uint32 logical_op_id = 5;
// A sequence of ids suitable for extending OpPhi instructions of the loop
// merge block if it did not previously have an incoming edge from the loop
// back edge block.
repeated uint32 phi_id = 6;
}
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;
TransformationReplaceIdWithSynonym replace_id_with_synonym = 13;
TransformationSetSelectionControl set_selection_control = 14;
TransformationCompositeConstruct composite_construct = 15;
TransformationSetLoopControl set_loop_control = 16;
TransformationSetFunctionControl set_function_control = 17;
TransformationAddNoContractionDecoration add_no_contraction_decoration = 18;
TransformationSetMemoryOperandsMask set_memory_operands_mask = 19;
TransformationCompositeExtract composite_extract = 20;
TransformationVectorShuffle vector_shuffle = 21;
TransformationOutlineFunction outline_function = 22;
TransformationMergeBlocks merge_blocks = 23;
TransformationAddTypeVector add_type_vector = 24;
TransformationAddTypeArray add_type_array = 25;
TransformationAddTypeMatrix add_type_matrix = 26;
TransformationAddTypeStruct add_type_struct = 27;
TransformationAddTypeFunction add_type_function = 28;
TransformationAddConstantComposite add_constant_composite = 29;
TransformationAddGlobalVariable add_global_variable = 30;
TransformationAddGlobalUndef add_global_undef = 31;
TransformationAddFunction add_function = 32;
TransformationAddDeadBlock add_dead_block = 33;
TransformationAddLocalVariable add_local_variable = 34;
TransformationLoad load = 35;
TransformationStore store = 36;
TransformationFunctionCall function_call = 37;
TransformationAccessChain access_chain = 38;
TransformationEquationInstruction equation_instruction = 39;
TransformationSwapCommutableOperands swap_commutable_operands = 40;
TransformationPermuteFunctionParameters permute_function_parameters = 41;
TransformationToggleAccessChainInstruction toggle_access_chain_instruction = 42;
TransformationAddConstantNull add_constant_null = 43;
TransformationComputeDataSynonymFactClosure compute_data_synonym_fact_closure = 44;
TransformationAdjustBranchWeights adjust_branch_weights = 45;
TransformationPushIdThroughVariable push_id_through_variable = 46;
TransformationAddSpecConstantOp add_spec_constant_op = 47;
TransformationReplaceLinearAlgebraInstruction replace_linear_algebra_instruction = 48;
TransformationSwapConditionalBranchOperands swap_conditional_branch_operands = 49;
TransformationPermutePhiOperands permute_phi_operands = 50;
TransformationAddParameter add_parameter = 51;
TransformationAddCopyMemory add_copy_memory = 52;
TransformationInvertComparisonOperator invert_comparison_operator = 53;
TransformationAddImageSampleUnusedComponents add_image_sample_unused_components = 54;
TransformationReplaceParameterWithGlobal replace_parameter_with_global = 55;
TransformationRecordSynonymousConstants record_synonymous_constants = 56;
TransformationAddSynonym add_synonym = 57;
TransformationAddRelaxedDecoration add_relaxed_decoration = 58;
TransformationReplaceParamsWithStruct replace_params_with_struct = 59;
TransformationReplaceCopyObjectWithStoreLoad replace_copy_object_with_store_load = 60;
TransformationReplaceCopyMemoryWithLoadStore replace_copy_memory_with_load_store = 61;
TransformationReplaceLoadStoreWithCopyMemory replace_load_store_with_copy_memory = 62;
TransformationAddLoopPreheader add_loop_preheader = 63;
TransformationMoveInstructionDown move_instruction_down = 64;
TransformationMakeVectorOperationDynamic make_vector_operation_dynamic = 65;
TransformationReplaceAddSubMulWithCarryingExtended replace_add_sub_mul_with_carrying_extended = 66;
TransformationPropagateInstructionUp propagate_instruction_up = 67;
// Add additional option using the next available number.
}
}
// Keep transformation message types in alphabetical order:
message TransformationAccessChain {
// Adds an access chain instruction based on a given pointer and indices.
// When accessing a struct, the corresponding indices must be 32-bit integer constants.
// For any other composite, the indices can be any 32-bit integer, and the transformation
// adds two instructions for each such index to clamp it to the bound, as follows:
//
// %t1 = OpULessThanEqual %bool %index %bound_minus_one
// %t2 = OpSelect %int_type %t1 %index %bound_minus_one
// Result id for the access chain
uint32 fresh_id = 1;
// The pointer from which the access chain starts
uint32 pointer_id = 2;
// Zero or more access chain indices
repeated uint32 index_id = 3;
// A descriptor for an instruction in a block before which the new
// OpAccessChain instruction should be inserted
InstructionDescriptor instruction_to_insert_before = 4;
// Additional fresh ids, required to clamp index variables. A pair is needed
// for each access to a non-struct composite.
repeated UInt32Pair fresh_ids_for_clamping = 5;
}
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.
// Also, creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is true.
uint32 fresh_id = 1;
bool is_true = 2;
// If the constant should be marked as irrelevant.
bool is_irrelevant = 3;
}
message TransformationAddConstantComposite {
// Adds a constant of the given composite type to the module.
// Also, creates an IdIsIrrelevant fact about |fresh_id| if
// |is_irrelevant| is true.
// Fresh id for the composite
uint32 fresh_id = 1;
// A composite type id
uint32 type_id = 2;
// Constituent ids for the composite
repeated uint32 constituent_id = 3;
// If the constant should be marked as irrelevant.
bool is_irrelevant = 4;
}
message TransformationAddConstantNull {
// Adds a null constant.
// Id for the constant
uint32 fresh_id = 1;
// Type of the constant
uint32 type_id = 2;
}
message TransformationAddConstantScalar {
// Adds a constant of the given scalar type.
// Also, creates an IdIsIrrelevant fact about
// |fresh_id| if |is_irrelevant| is true.
// 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;
// If the constant should be marked as irrelevant.
bool is_irrelevant = 4;
}
message TransformationAddCopyMemory {
// Adds an OpCopyMemory instruction into the module.
// Creates either a global or a local variable (based on
// |storage_class| field) to copy the target into.
// OpCopyMemory will be inserted before this instruction.
InstructionDescriptor instruction_descriptor = 1;
// Fresh id to copy memory into.
uint32 fresh_id = 2;
// Source to copy memory from.
uint32 source_id = 3;
// Storage class for the target variable. Can be either Function or Private.
uint32 storage_class = 4;
// Result id for the variable's initializer operand. Its type must be equal to
// variable's pointee type.
uint32 initializer_id = 5;
}
message TransformationAddDeadBlock {
// Adds a new block to the module that is statically reachable from an
// existing block, but dynamically unreachable.
// Fresh id for the dead block
uint32 fresh_id = 1;
// Id of an existing block terminated with OpBranch, such that this OpBranch
// can be replaced with an OpBranchConditional to its exiting successor or
// the dead block
uint32 existing_block = 2;
// Determines whether the condition associated with the OpBranchConditional
// is true or false
bool condition_value = 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 TransformationAddFunction {
// Adds a SPIR-V function to the module.
// The series of instructions that comprise the function.
repeated Instruction instruction = 1;
// True if and only if the given function should be made livesafe (see
// FactFunctionIsLivesafe for definition).
bool is_livesafe = 2;
// Fresh id for a new variable that will serve as a "loop limiter" for the
// function; only relevant if |is_livesafe| holds.
uint32 loop_limiter_variable_id = 3;
// Id of an existing unsigned integer constant providing the maximum value
// that the loop limiter can reach before the loop is broken from; only
// relevant if |is_livesafe| holds.
uint32 loop_limit_constant_id = 4;
// Fresh ids for each loop in the function that allow the loop limiter to be
// manipulated; only relevant if |is_livesafe| holds.
repeated LoopLimiterInfo loop_limiter_info = 5;
// Id of an existing global value with the same return type as the function
// that can be used to replace OpKill and OpReachable instructions with
// ReturnValue instructions. Ignored if the function has void return type.
uint32 kill_unreachable_return_value_id = 6;
// A mapping (represented as a sequence) from every access chain result id in
// the function to the ids required to clamp its indices to ensure they are in
// bounds.
repeated AccessChainClampingInfo access_chain_clamping_info = 7;
}
message TransformationAddGlobalUndef {
// Adds an undefined value of a given type to the module at global scope.
// Fresh id for the undefined value
uint32 fresh_id = 1;
// The type of the undefined value
uint32 type_id = 2;
}
message TransformationAddGlobalVariable {
// Adds a global variable of the given type to the module, with Private or
// Workgroup storage class, and optionally (for the Private case) with an
// initializer.
// Fresh id for the global variable
uint32 fresh_id = 1;
// The type of the global variable
uint32 type_id = 2;
uint32 storage_class = 3;
// Initial value of the variable
uint32 initializer_id = 4;
// True if and only if the behaviour of the module should not depend on the
// value of the variable, in which case stores to the variable can be
// performed in an arbitrary fashion.
bool value_is_irrelevant = 5;
}
message TransformationAddImageSampleUnusedComponents {
// A transformation that adds unused components to an image sample coordinate.
// An vector id with the original coordinate and the unused components.
uint32 coordinate_with_unused_components_id = 1;
// A descriptor for an image sample instruction.
InstructionDescriptor instruction_descriptor = 2;
}
message TransformationAddLocalVariable {
// Adds a local variable of the given type (which must be a pointer with
// Function storage class) to the given function, initialized to the given
// id.
// Fresh id for the local variable
uint32 fresh_id = 1;
// The type of the local variable
uint32 type_id = 2;
// The id of the function to which the local variable should be added
uint32 function_id = 3;
// Initial value of the variable
uint32 initializer_id = 4;
// True if and only if the behaviour of the module should not depend on the
// value of the variable, in which case stores to the variable can be
// performed in an arbitrary fashion.
bool value_is_irrelevant = 5;
}
message TransformationAddLoopPreheader {
// A transformation that adds a loop preheader block before the given loop header.
// The id of the loop header block
uint32 loop_header_block = 1;
// A fresh id for the preheader block
uint32 fresh_id = 2;
// Fresh ids for splitting the OpPhi instructions in the header.
// A new OpPhi instruction in the preheader is needed for each OpPhi instruction in the header,
// if the header has more than one predecessor outside of the loop.
// This allows turning instructions of the form:
//
// %loop_header_block = OpLabel
// %id1 = OpPhi %type %val1 %pred1_id %val2 %pred2_id %val3 %backedge_block_id
//
// into:
// %fresh_id = OpLabel
// %phi_id1 = OpPhi %type %val1 %pred1_id %val2 %pred2_id
// OpBranch %header_id
// %loop_header_block = OpLabel
// %id1 = OpPhi %type %phi_id1 %fresh_id %val3 %backedge_block_id
repeated uint32 phi_id = 3;
}
message TransformationAddNoContractionDecoration {
// Applies OpDecorate NoContraction to the given result id
// Result id to be decorated
uint32 result_id = 1;
}
message TransformationAddParameter {
// Adds a new parameter into the function.
// Result id of the function to add parameters to.
uint32 function_id = 1;
// Fresh id for a new parameter.
uint32 parameter_fresh_id = 2;
// Result id of the instruction, used to initializer new parameter
// in function calls. Type id of that instruction is the type id of the parameter.
// It may not be OpTypeVoid.
uint32 initializer_id = 3;
// A fresh id for a new function type. This might not be used
// if a required function type already exists or if we can change
// the old function type.
uint32 function_type_fresh_id = 4;
}
message TransformationAddRelaxedDecoration {
// Applies OpDecorate RelaxedPrecision to the given result id
// Result id to be decorated
uint32 result_id = 1;
}
message TransformationAddSpecConstantOp {
// Adds OpSpecConstantOp into the module.
// Result id for the new instruction.
uint32 fresh_id = 1;
// Type id for the new instruction.
uint32 type_id = 2;
// Opcode operand of the OpSpecConstantOp instruction.
uint32 opcode = 3;
// Operands of the |opcode| instruction.
repeated InstructionOperand operand = 4;
}
message TransformationAddSynonym {
// Adds a |synonymous_instruction| before |insert_before| instruction with
// and creates a fact that |result_id| and the result id of |synonymous_instruction|
// are synonymous.
// Result id of the first synonym.
uint32 result_id = 1;
// Type of the synonym to apply. Some types might produce instructions
// with commutative operands. Such types do not specify the order of the
// operands since we have a special transformation to swap commutable operands.
//
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3499):
// Consider adding more types here.
enum SynonymType {
// New synonym is derived by adding zero to the |result_id|.
ADD_ZERO = 0;
// New synonym is derived by subtracting zero from the |result_id|.
SUB_ZERO = 1;
// New synonym is derived by multiplying |result_id| by one.
MUL_ONE = 2;
// New synonym is derived by applying OpCopyObject instruction to |result_id|.
COPY_OBJECT = 3;
// New synonym is derived by applying OpLogicalOr to |result_id| with the second
// operand being 'false'.
LOGICAL_OR = 4;
// New synonym is derived by applying OpLogicalAnd to |result_id| with the second
// operand being 'true'.
LOGICAL_AND = 5;
}
// Type of the synonym to create. See SynonymType for more details.
SynonymType synonym_type = 2;
// Fresh result id for a created synonym.
uint32 synonym_fresh_id = 3;
// An instruction to insert a new synonym before.
InstructionDescriptor insert_before = 4;
}
message TransformationAddTypeArray {
// Adds an array type of the given element type and size to the module
// Fresh id for the array type
uint32 fresh_id = 1;
// The array's element type
uint32 element_type_id = 2;
// The array's size
uint32 size_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 TransformationAddTypeFunction {
// Adds a function type to the module
// Fresh id for the function type
uint32 fresh_id = 1;
// The function's return type
uint32 return_type_id = 2;
// The function's argument types
repeated uint32 argument_type_id = 3;
}
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 TransformationAddTypeMatrix {
// Adds a matrix type to the module
// Fresh id for the matrix type
uint32 fresh_id = 1;
// The matrix's column type, which must be a floating-point vector (as per
// the "data rules" in the SPIR-V specification).
uint32 column_type_id = 2;
// The matrix's column count
uint32 column_count = 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 TransformationAddTypeStruct {
// Adds a struct type to the module
// Fresh id for the struct type
uint32 fresh_id = 1;
// The struct's member types
repeated uint32 member_type_id = 3;
}
message TransformationAddTypeVector {
// Adds a vector type to the module
// Fresh id for the vector type
uint32 fresh_id = 1;
// The vector's component type
uint32 component_type_id = 2;
// The vector's component count
uint32 component_count = 3;
}
message TransformationAdjustBranchWeights {
// A transformation that adjusts the branch weights
// of a branch conditional instruction.
// A descriptor for a branch conditional instruction.
InstructionDescriptor instruction_descriptor = 1;
// Branch weights of a branch conditional instruction.
UInt32Pair branch_weights = 2;
}
message TransformationCompositeConstruct {
// A transformation that introduces an OpCompositeConstruct instruction to
// make a composite object.
// Id of the type of the composite that is to be constructed
uint32 composite_type_id = 1;
// Ids of the objects that will form the components of the composite
repeated uint32 component = 2;
// A descriptor for an instruction in a block before which the new
// OpCompositeConstruct instruction should be inserted
InstructionDescriptor instruction_to_insert_before = 3;
// A fresh id for the composite object
uint32 fresh_id = 4;
}
message TransformationCompositeExtract {
// A transformation that adds an instruction to extract an element from a
// composite.
// A descriptor for an instruction in a block before which the new
// OpCompositeExtract instruction should be inserted
InstructionDescriptor instruction_to_insert_before = 1;
// Result id for the extract operation.
uint32 fresh_id = 2;
// Id of the composite from which data is to be extracted.
uint32 composite_id = 3;
// Indices that indicate which part of the composite should be extracted.
repeated uint32 index = 4;
}
message TransformationComputeDataSynonymFactClosure {
// A transformation that impacts the fact manager only, forcing a computation
// of the closure of data synonym facts, so that e.g. if the components of
// vectors v and w are known to be pairwise synonymous, it is deduced that v
// and w are themselves synonymous.
// When searching equivalence classes for implied facts, equivalence classes
// larger than this size will be skipped.
uint32 maximum_equivalence_class_size = 1;
}
message TransformationEquationInstruction {
// A transformation that adds an instruction to the module that defines an
// equation between its result id and input operand ids, such that the
// equation is guaranteed to hold at any program point where all ids involved
// are available (i.e. at any program point dominated by the instruction).
// The result id of the new instruction
uint32 fresh_id = 1;
// The instruction's opcode
uint32 opcode = 2;
// The input operands to the instruction
repeated uint32 in_operand_id = 3;
// A descriptor for an instruction in a block before which the new
// instruction should be inserted
InstructionDescriptor instruction_to_insert_before = 4;
}
message TransformationFunctionCall {
// A transformation that introduces an OpFunctionCall instruction. The call
// must not make the module's call graph cyclic. Beyond that, if the call
// is in a dead block it can be to any function with arbitrary suitably-typed
// arguments; otherwise it must be to a livesafe function, with injected
// variables as pointer arguments and arbitrary non-pointer arguments.
// A fresh id for the result of the call
uint32 fresh_id = 1;
// Id of the function to be called
uint32 callee_id = 2;
// Ids for arguments to the function
repeated uint32 argument_id = 3;
// A descriptor for an instruction in a block before which the new
// OpFunctionCall instruction should be inserted
InstructionDescriptor instruction_to_insert_before = 4;
}
message TransformationInvertComparisonOperator {
// For some instruction with result id |operator_id| that
// represents a binary comparison operator (e.g. <, >, <=), this transformation
// will replace that instruction's result id with |fresh_id|,
// invert the opcode (< will become >=) and insert OpLogicalNot
// instruction with result id |operator_id| below.
// Result id of the instruction to invert.
uint32 operator_id = 1;
// Fresh id that will be used by the operator after the inversion.
uint32 fresh_id = 2;
}
message TransformationLoad {
// Transformation that adds an OpLoad instruction from a pointer into an id.
// The result of the load instruction
uint32 fresh_id = 1;
// The pointer to be loaded from
uint32 pointer_id = 2;
// A descriptor for an instruction in a block before which the new OpLoad
// instruction should be inserted
InstructionDescriptor instruction_to_insert_before = 3;
}
message TransformationMakeVectorOperationDynamic {
// A transformation that replaces the OpCompositeExtract and OpCompositeInsert
// instructions with the OpVectorExtractDynamic and OpVectorInsertDynamic instructions.
// The composite instruction result id.
uint32 instruction_result_id = 1;
// The OpCompositeExtract/Insert instructions accept integer literals as indices to the composite object.
// However, the OpVectorInsert/ExtractDynamic instructions require its single index to be an integer instruction.
// This is the result id of the integer instruction.
uint32 constant_index_id = 2;
}
message TransformationMergeBlocks {
// A transformation that merges a block with its predecessor.
// The id of the block that is to be merged with its predecessor; the merged
// block will have the *predecessor's* id.
uint32 block_id = 1;
}
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 TransformationMoveInstructionDown {
// Swaps |instruction| with the next instruction in the block.
// The instruction to move down.
InstructionDescriptor instruction = 1;
}
message TransformationOutlineFunction {
// A transformation that outlines a single-entry single-exit region of a
// control flow graph into a separate function, and replaces the region with
// a call to that function.
// Id of the entry block of the single-entry single-exit region to be outlined
uint32 entry_block = 1;
// Id of the exit block of the single-entry single-exit region to be outlined
uint32 exit_block = 2;
// Id of a struct that will store the return values of the new function
uint32 new_function_struct_return_type_id = 3;
// A fresh id for the type of the outlined function
uint32 new_function_type_id = 4;
// A fresh id for the outlined function itself
uint32 new_function_id = 5;
// A fresh id to represent the block in the outlined function that represents
// the first block of the outlined region.
uint32 new_function_region_entry_block = 6;
// A fresh id for the result of the OpFunctionCall instruction that will call
// the outlined function
uint32 new_caller_result_id = 7;
// A fresh id to capture the return value of the outlined function - the
// argument to OpReturn
uint32 new_callee_result_id = 8;
// Ids defined outside the region and used inside the region will become
// parameters to the outlined function. This is a mapping from used ids to
// fresh parameter ids.
repeated UInt32Pair input_id_to_fresh_id = 9;
// Ids defined inside the region and used outside the region will become
// fresh ids defined by the outlined function, which get copied into the
// function's struct return value and then copied into their destination ids
// by the caller. This is a mapping from original ids to corresponding fresh
// ids.
repeated UInt32Pair output_id_to_fresh_id = 10;
}
message TransformationPermuteFunctionParameters {
// A transformation that, given a non-entry-point function taking n
// parameters and a permutation of the set [0, n-1]:
// - Introduces a new function type that is the same as the original
// function's type but with the order of arguments permuted
// (only if it doesn't already exist)
// - Changes the type of the function to this type
// - Adjusts all calls to the function so that their arguments are permuted
// Function, whose parameters will be permuted
uint32 function_id = 1;
// Fresh id for a new type of the function. This might not be used
// if a required function type already exists or if we can change
// the old function type.
uint32 function_type_fresh_id = 2;
// An array of size |n|, where |n| is a number of arguments to a function
// with |function_id|. For each i: 0 <= permutation[i] < n.
//
// i-th element of this array contains a position for an i-th
// function's argument (i.e. i-th argument will be permutation[i]-th
// after running this transformation)
repeated uint32 permutation = 3;
}
message TransformationPermutePhiOperands {
// Permutes operands of some OpPhi instruction.
// Result id of the instruction to apply the transformation to.
uint32 result_id = 1;
// A sequence of numbers in the range [0, n/2 - 1] where |n| is the number
// of operands of the OpPhi instruction with |result_id|.
repeated uint32 permutation = 2;
}
message TransformationPropagateInstructionUp {
// Propagates an instruction in the block into the block's predecessors.
// Concretely, this transformation clones some particular instruction from
// the |block_id| into every block's predecessor and replaces the original
// instruction with OpPhi. Take a look at the transformation class to learn
// more about how we choose what instruction to propagate.
// Id of the block to propagate an instruction from.
uint32 block_id = 1;
// A map from the id of some predecessor of the |block_id| to the fresh id.
// The map contains a fresh id for at least every predecessor of the |block_id|.
// The instruction is propagated by creating a number of clones - one clone for
// each predecessor. Fresh ids from this field are used as result ids of cloned
// instructions.
repeated UInt32Pair predecessor_id_to_fresh_id = 2;
}
message TransformationPushIdThroughVariable {
// A transformation that makes |value_synonym_id| and |value_id| to be
// synonymous by storing |value_id| into |variable_id| and
// loading |variable_id| to |value_synonym_id|.
// The value to be stored.
uint32 value_id = 1;
// A fresh id for the result of the load instruction.
uint32 value_synonym_id = 2;
// A fresh id for the variable to be stored to.
uint32 variable_id = 3;
// Constant to initialize the variable from.
uint32 initializer_id = 4;
// The variable storage class (global or local).
uint32 variable_storage_class = 5;
// A descriptor for an instruction which the new OpStore
// and OpLoad instructions might be inserted before.
InstructionDescriptor instruction_descriptor = 6;
}
message TransformationRecordSynonymousConstants {
// A transformation that, given the IDs to two synonymous constants,
// records the fact that they are synonymous. The module is not changed.
// Two constants are synonymous if:
// - they have the same type (ignoring the presence of integer sign)
// - they have the same opcode (one of OpConstant, OpConstantTrue,
// OpConstantFalse, OpConstantNull)
// - they have the same value
// If the types are the same, OpConstantNull is equivalent to
// OpConstantFalse or OpConstant with value zero.
// The id of a constant
uint32 constant1_id = 1;
// The id of the synonym
uint32 constant2_id = 2;
}
message TransformationReplaceAddSubMulWithCarryingExtended {
// Replaces OpIAdd with OpIAddCarry, OpISub with OpISubBorrow, OpIMul
// with OpUMulExtended or OpSMulExtended (depending on the signedness
// of the operands) and stores the result into a |struct_fresh_id|.
// In the original instruction the result type id and the type ids of
// the operands must be the same. Then the transformation extracts
// the first element of the result into the original |result_id|.
// This value is the same as the result of the original instruction.
// The fresh id of the intermediate result.
uint32 struct_fresh_id = 1;
// The result id of the original instruction.
uint32 result_id = 2;
}
message TransformationReplaceParameterWithGlobal {
// Removes parameter with result id |parameter_id| from its function
// and creates a global variable to pass its value to the function instead.
// Fresh id for a new function type. This might not be used if a required
// function type already exists or if we can change the old function type.
uint32 function_type_fresh_id = 2;
// Result id of the OpFunctionParameter instruction to remove.
uint32 parameter_id = 3;
// Fresh id of a global variable used to pass parameter's value to the function.
uint32 global_variable_fresh_id = 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 TransformationReplaceConstantWithUniform {
// Replaces a use of a constant id with 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 TransformationReplaceCopyMemoryWithLoadStore {
// A transformation that replaces instructions OpCopyMemory with loading
// the source variable to an intermediate value and storing this value into the
// target variable of the original OpCopyMemory instruction.
// The intermediate value.
uint32 fresh_id = 1;
// The instruction descriptor to OpCopyMemory. It is necessary, because
// OpCopyMemory doesn't have a result id.
InstructionDescriptor copy_memory_instruction_descriptor = 2;
}
message TransformationReplaceCopyObjectWithStoreLoad {
// A transformation that replaces instruction OpCopyObject with
// storing into a new variable and immediately loading from this
// variable to |result_id| of the original OpCopyObject instruction.
// The result id of initial OpCopyObject instruction
uint32 copy_object_result_id = 1;
// A fresh id for the variable to be stored to.
uint32 fresh_variable_id = 2;
// The variable storage class (Function or Private).
uint32 variable_storage_class = 3;
// Constant to initialize the variable with.
uint32 variable_initializer_id = 4;
}
message TransformationReplaceIdWithSynonym {
// Replaces a use of an id with an id that is known to be synonymous, e.g.
// because it was obtained via applying OpCopyObject
// The id use that is to be replaced
IdUseDescriptor id_use_descriptor = 1;
// The synonymous id
uint32 synonymous_id = 2;
}
message TransformationReplaceLinearAlgebraInstruction {
// Replaces a linear algebra instruction with its
// mathematical definition.
// The fresh ids needed to apply the transformation.
repeated uint32 fresh_ids = 1;
// A descriptor for a linear algebra instruction.
InstructionDescriptor instruction_descriptor = 2;
}
message TransformationReplaceLoadStoreWithCopyMemory {
// A transformation that takes a pair of instruction descriptors
// to OpLoad and OpStore that have the same intermediate value
// and replaces the OpStore with an equivalent OpCopyMemory.
// The instruction descriptor to OpLoad
InstructionDescriptor load_instruction_descriptor = 1;
// The instruction descriptor to OpStore
InstructionDescriptor store_instruction_descriptor = 2;
}
message TransformationReplaceParamsWithStruct {
// Replaces parameters of the function with a struct containing
// values of those parameters.
// Result ids of parameters to replace.
repeated uint32 parameter_id = 1;
// Fresh id for a new function type. This might be unused if the required type
// already exists in the module or if we can change the old type.
uint32 fresh_function_type_id = 2;
// Fresh id for a new struct function parameter to be used as a replacement.
uint32 fresh_parameter_id = 3;
// Fresh ids for struct objects containing values of replaced parameters.
// This field contains a fresh id for at least every result id of a relevant
// OpFunctionCall instruction.
repeated UInt32Pair caller_id_to_fresh_composite_id = 4;
}
message TransformationSetFunctionControl {
// A transformation that sets the function control operand of an OpFunction
// instruction.
// The result id of an OpFunction instruction
uint32 function_id = 1;
// The value to which the 'function control' operand should be set.
uint32 function_control = 2;
}
message TransformationSetLoopControl {
// A transformation that sets the loop control operand of an OpLoopMerge
// instruction.
// The id of a basic block that should contain OpLoopMerge
uint32 block_id = 1;
// The value to which the 'loop control' operand should be set.
// This must be a legal loop control mask.
uint32 loop_control = 2;
// Provides a peel count value for the loop. Used if and only if the
// PeelCount bit is set. Must be zero if the PeelCount bit is not set (can
// still be zero if this bit is set).
uint32 peel_count = 3;
// Provides a partial count value for the loop. Used if and only if the
// PartialCount bit is set. Must be zero if the PartialCount bit is not set
// (can still be zero if this bit is set).
uint32 partial_count = 4;
}
message TransformationSetMemoryOperandsMask {
// A transformation that sets the memory operands mask of a memory access
// instruction.
// A descriptor for a memory access instruction, e.g. an OpLoad
InstructionDescriptor memory_access_instruction = 1;
// A mask of memory operands to be applied to the instruction. It must be the
// same as the original mask, except that Volatile can be added, and
// Nontemporal can be added or removed.
uint32 memory_operands_mask = 2;
// Some memory access instructions allow more than one mask to be specified;
// this field indicates which mask should be set
uint32 memory_operands_mask_index = 3;
}
message TransformationSetSelectionControl {
// A transformation that sets the selection control operand of an
// OpSelectionMerge instruction.
// The id of a basic block that should contain OpSelectionMerge
uint32 block_id = 1;
// The value to which the 'selection control' operand should be set.
// Although technically 'selection control' is a literal mask that can be
// some combination of 'None', 'Flatten' and 'DontFlatten', the combination
// 'Flatten | DontFlatten' does not make sense and is not allowed here.
uint32 selection_control = 2;
}
message TransformationSplitBlock {
// A transformation that splits a basic block into two basic blocks
// A descriptor for an instruction such that the block containing the
// described instruction should be split right before the instruction.
InstructionDescriptor instruction_to_split_before = 1;
// 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 = 2;
}
message TransformationStore {
// Transformation that adds an OpStore instruction of an id to a pointer.
// The pointer to be stored to
uint32 pointer_id = 1;
// The value to be stored
uint32 value_id = 2;
// A descriptor for an instruction in a block before which the new OpStore
// instruction should be inserted
InstructionDescriptor instruction_to_insert_before = 3;
}
message TransformationSwapCommutableOperands {
// A transformation that swaps the operands of a commutative instruction.
// A descriptor for a commutative instruction
InstructionDescriptor instruction_descriptor = 1;
}
message TransformationSwapConditionalBranchOperands {
// Swaps label ids in OpBranchConditional instruction.
// Additionally, inverts the guard and swaps branch weights
// if present.
// Descriptor of the instruction to swap operands of.
InstructionDescriptor instruction_descriptor = 1;
// Fresh result id for the OpLogicalNot instruction, used
// to invert the guard.
uint32 fresh_id = 2;
}
message TransformationToggleAccessChainInstruction {
// A transformation that toggles an access chain instruction.
// A descriptor for an access chain instruction
InstructionDescriptor instruction_descriptor = 1;
}
message TransformationVectorShuffle {
// A transformation that adds a vector shuffle instruction.
// A descriptor for an instruction in a block before which the new
// OpVectorShuffle instruction should be inserted
InstructionDescriptor instruction_to_insert_before = 1;
// Result id for the shuffle operation.
uint32 fresh_id = 2;
// Id of the first vector operand.
uint32 vector1 = 3;
// Id of the second vector operand.
uint32 vector2 = 4;
// Indices that indicate which components of the input vectors should be used.
repeated uint32 component = 5;
}