| // 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 SideEffectWrapperInfo { | |
| // When flattening a conditional branch, it is necessary to enclose | |
| // instructions that have side effects inside conditionals, so that | |
| // they are only executed if the condition holds. Otherwise, there | |
| // might be unintended changes in memory, or crashes that would not | |
| // originally happen. | |
| // For example, the instruction %id = OpLoad %type %ptr, found in | |
| // the true branch of the conditional, will be enclosed in a new | |
| // conditional (assuming that the block containing it can be split | |
| // around it) as follows: | |
| // | |
| // [previous instructions in the block] | |
| // OpSelectionMerge %merge_block_id None | |
| // OpBranchConditional %cond %execute_block_id %alternative_block_id | |
| // %execute_block_id = OpLabel | |
| // %actual_result_id = OpLoad %type %ptr | |
| // OpBranch %merge_block_id | |
| // %alternative_block_id = OpLabel | |
| // %placeholder_result_id = OpCopyObject %type %value_to_copy_id | |
| // OpBranch %merge_block_id | |
| // %merge_block_id = OpLabel | |
| // %id = OpPhi %type %actual_result_id %execute_block_id %placeholder_result_id %alternative_block_id | |
| // [following instructions from the original block] | |
| // | |
| // If the instruction does not have a result id, this is simplified. | |
| // For example, OpStore %ptr %value, found in the true branch of a | |
| // conditional, is enclosed as follows: | |
| // | |
| // [previous instructions in the block] | |
| // OpSelectionMerge %merge_block None | |
| // OpBranchConditional %cond %execute_block_id %merge_block_id | |
| // %execute_block_id = OpLabel | |
| // OpStore %ptr %value | |
| // OpBranch %merge_block_id | |
| // %merge_block_id = OpLabel | |
| // [following instructions from the original block] | |
| // | |
| // The same happens if the instruction is found in the false branch | |
| // of the conditional being flattened, except that the label ids in | |
| // the OpBranchConditional are swapped. | |
| // An instruction descriptor for identifying the instruction to be | |
| // enclosed inside a conditional. An instruction descriptor is | |
| // necessary because the instruction might not have a result id. | |
| InstructionDescriptor instruction = 1; | |
| // A fresh id for the new merge block. | |
| uint32 merge_block_id = 2; | |
| // A fresh id for the new block where the actual instruction is | |
| // executed. | |
| uint32 execute_block_id = 3; | |
| // The following fields are only needed if the original instruction has a | |
| // result id. They can be set to 0 if not needed. | |
| // A fresh id for the result id of the instruction (the original | |
| // one is used by the OpPhi instruction). | |
| uint32 actual_result_id = 4; | |
| // A fresh id for the new block where the placeholder instruction | |
| // is placed. | |
| uint32 alternative_block_id = 5; | |
| // A fresh id for the placeholder instruction. | |
| uint32 placeholder_result_id = 6; | |
| // An id present in the module, available to use at this point in | |
| // the program and with the same type as the original instruction, | |
| // that can be used to create a placeholder OpCopyObject | |
| // instruction. | |
| uint32 value_to_copy_id = 7; | |
| } | |
| message ReturnMergingInfo { | |
| // TransformationMergeFunctionReturns needs to modify each merge block of | |
| // loops containing return instructions, by: | |
| // - adding instructions to decide whether the function is returning | |
| // - adding instructions to pass on the return value of the function, | |
| // if it is returning | |
| // - changing the branch instruction (which must be an unconditional branch) | |
| // to a conditional branch that, if the function is returning, branches to | |
| // the merge block of the innermost loop that contains this merge block | |
| // (which can be the new merge block introduced by the transformation). | |
| // | |
| // One such merge block of the form: | |
| // %block = OpLabel | |
| // %phi1 = OpPhi %type1 %val1_1 %pred1 %val1_2 %pred2 | |
| // %phi2 = OpPhi %type2 %val2_1 %pred1 %val2_2 %pred2 | |
| // OpBranch %next | |
| // | |
| // is transformed into: | |
| // %block = OpLabel | |
| // %is_returning_id = OpPhi %bool %false %pred1 %false %pred2 %true %ret_bb1 %is_bb2_returning %mer_bb2 | |
| // %maybe_return_val_id = OpPhi %return_type %any_returnable_val %pred1 %any_returnable_val %pred2 | |
| // %ret_val1 %ret_bb1 %ret_val2 %mer_bb2 | |
| // %phi1 = OpPhi %type1 %val1_1 %pred1 %val1_2 %pred2 | |
| // %any_suitable_id_1 %ret_bb1 %any_suitable_id_1 %mer_bb2 | |
| // %phi2 = OpPhi %type2 %val2_1 %pred1 %val2_2 %pred2 | |
| // %any_suitable_id_1 %ret_bb1 %any_suitable_id_1 %mer_bb2 | |
| // OpBranchConditional %is_returning_id %innermost_loop_merge %next | |
| // | |
| // where %ret_bb1 is a block that originally contains a return instruction and %mer_bb2 is the merge block of an inner | |
| // loop, from where the function might be returning. | |
| // | |
| // Note that the block is required to only have OpLabel, OpPhi or OpBranch instructions. | |
| // The id of the merge block that needs to be modified. | |
| uint32 merge_block_id = 1; | |
| // A fresh id for a boolean OpPhi whose value will be true iff the function | |
| // is returning. This will be used to decide whether to break out of the loop | |
| // or to use the original branch of the function. This value will also be | |
| // used by the merge block of the enclosing loop (if there is one) if the | |
| // function is returning from this block. | |
| uint32 is_returning_id = 2; | |
| // A fresh id that will get the value being returned, if the function is | |
| // returning. If the function return type is void, this is ignored. | |
| uint32 maybe_return_val_id = 3; | |
| // A mapping from each existing OpPhi id to a suitable id of the same type | |
| // available to use before the instruction. | |
| repeated UInt32Pair opphi_to_suitable_id = 4; | |
| } | |
| 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; | |
| TransformationCompositeInsert composite_insert = 68; | |
| TransformationInlineFunction inline_function = 69; | |
| TransformationAddOpPhiSynonym add_opphi_synonym = 70; | |
| TransformationMutatePointer mutate_pointer = 71; | |
| TransformationReplaceIrrelevantId replace_irrelevant_id = 72; | |
| TransformationReplaceOpPhiIdFromDeadPredecessor replace_opphi_id_from_dead_predecessor = 73; | |
| TransformationReplaceOpSelectWithConditionalBranch replace_opselect_with_conditional_branch = 74; | |
| TransformationDuplicateRegionWithSelection duplicate_region_with_selection = 75; | |
| TransformationFlattenConditionalBranch flatten_conditional_branch = 76; | |
| TransformationAddBitInstructionSynonym add_bit_instruction_synonym = 77; | |
| TransformationAddLoopToCreateIntConstantSynonym add_loop_to_create_int_constant_synonym = 78; | |
| TransformationWrapRegionInSelection wrap_region_in_selection = 79; | |
| TransformationAddEarlyTerminatorWrapper add_early_terminator_wrapper = 80; | |
| TransformationPropagateInstructionDown propagate_instruction_down = 81; | |
| TransformationReplaceBranchFromDeadBlockWithExit replace_branch_from_dead_block_with_exit = 82; | |
| TransformationWrapEarlyTerminatorInFunction wrap_early_terminator_in_function = 83; | |
| TransformationMergeFunctionReturns merge_function_returns = 84; | |
| TransformationExpandVectorReduction expand_vector_reduction = 85; | |
| TransformationSwapFunctionVariables swap_function_variables = 86; | |
| TransformationSwapTwoFunctions swap_two_functions = 87; | |
| TransformationWrapVectorSynonym wrap_vector_synonym = 88; | |
| // 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 TransformationAddBitInstructionSynonym { | |
| // A transformation that adds synonyms for bit instructions by evaluating | |
| // each bit with the corresponding operation. There is a SPIR-V code example in the | |
| // header file of the transformation class that can help understand the transformation. | |
| // This transformation is only applicable if the described instruction has one of the following opcodes. | |
| // Supported: | |
| // OpBitwiseOr | |
| // OpBitwiseXor | |
| // OpBitwiseAnd | |
| // OpNot | |
| // To be supported in the future: | |
| // OpShiftRightLogical | |
| // OpShiftRightArithmetic | |
| // OpShiftLeftLogical | |
| // OpBitReverse | |
| // OpBitCount | |
| // The bit instruction result id. | |
| uint32 instruction_result_id = 1; | |
| // The fresh ids required to apply the transformation. | |
| repeated uint32 fresh_ids = 2; | |
| } | |
| 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 TransformationAddEarlyTerminatorWrapper { | |
| // Adds a function to the module containing a single block with a single non- | |
| // label instruction that is either OpKill, OpUnreachable, or | |
| // OpTerminateInvocation. The purpose of this is to allow such instructions | |
| // to be subsequently replaced with wrapper functions, which can then enable | |
| // transformations (such as inlining) that are hard in the direct presence | |
| // of these instructions. | |
| // Fresh id for the function. | |
| uint32 function_fresh_id = 1; | |
| // Fresh id for the single basic block in the function. | |
| uint32 label_fresh_id = 2; | |
| // One of OpKill, OpUnreachable, OpTerminateInvocation. If additional early | |
| // termination instructions are added to SPIR-V they should also be handled | |
| // here. | |
| uint32 opcode = 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. | |
| // Only relevant if |is_livesafe| holds. | |
| 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; only relevant if |is_livesafe| holds. | |
| 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 TransformationAddLoopToCreateIntConstantSynonym { | |
| // A transformation that uses a loop to create a synonym for an integer | |
| // constant C (scalar or vector) using an initial value I, a step value S and | |
| // a number of iterations N such that C = I - N * S. For each iteration, S is | |
| // subtracted from the total. | |
| // The loop can be made up of one or two blocks, and it is inserted before a | |
| // block with a single predecessor. In the one-block case, it is of the form: | |
| // | |
| // %loop_id = OpLabel | |
| // %ctr_id = OpPhi %int %int_0 %pred %incremented_ctr_id %loop_id | |
| // %temp_id = OpPhi %type_of_I %I %pred %eventual_syn_id %loop_id | |
| // %eventual_syn_id = OpISub %type_of_I %temp_id %step_val_id | |
| // %incremented_ctr_id = OpIAdd %int %ctr_id %int_1 | |
| // %cond_id = OpSLessThan %bool %incremented_ctr_id %num_iterations_id | |
| // OpLoopMerge %block_after_loop_id %loop_id None | |
| // OpBranchConditional %cond_id %loop_id %block_after_loop_id | |
| // | |
| // A new OpPhi instruction is then added to %block_after_loop_id, as follows: | |
| // | |
| // %block_after_loop_id = OpLabel | |
| // %syn_id = OpPhi %type_of_I %eventual_syn_id %loop_id | |
| // | |
| // This can be translated, assuming that N > 0, to: | |
| // int syn = I; | |
| // for (int ctr = 0; ctr < N; ctr++) syn = syn - S; | |
| // | |
| // All existing OpPhi instructions in %block_after_loop_id are also updated | |
| // to reflect the fact that its predecessor is now %loop_id. | |
| // The following are existing ids. | |
| // The id of the integer constant C that we want a synonym of. | |
| uint32 constant_id = 1; | |
| // The id of the initial value integer constant I. | |
| uint32 initial_val_id = 2; | |
| // The id of the step value integer constant S. | |
| uint32 step_val_id = 3; | |
| // The id of the integer scalar constant, its value being the number of | |
| // iterations N. | |
| uint32 num_iterations_id = 4; | |
| // The label id of the block before which the loop must be inserted. | |
| uint32 block_after_loop_id = 5; | |
| // The following are fresh ids. | |
| // A fresh id for the synonym. | |
| uint32 syn_id = 6; | |
| // A fresh id for the label of the loop, | |
| uint32 loop_id = 7; | |
| // A fresh id for the counter. | |
| uint32 ctr_id = 8; | |
| // A fresh id taking the value I - S * ctr at the ctr-th iteration. | |
| uint32 temp_id = 9; | |
| // A fresh id taking the value I - S * (ctr + 1) at the ctr-th iteration, and | |
| // thus I - S * N at the last iteration. | |
| uint32 eventual_syn_id = 10; | |
| // A fresh id for the incremented counter. | |
| uint32 incremented_ctr_id = 11; | |
| // A fresh id for the loop condition. | |
| uint32 cond_id = 12; | |
| // The instructions in the loop can also be laid out in two basic blocks, as follows: | |
| // | |
| // %loop_id = OpLabel | |
| // %ctr_id = OpPhi %int %int_0 %pred %incremented_ctr_id %loop_id | |
| // %temp_id = OpPhi %type_of_I %I %pred %eventual_syn_id %loop_id | |
| // OpLoopMerge %block_after_loop_id %additional_block_id None | |
| // OpBranch %additional_block_id | |
| // | |
| // %additional_block_id = OpLabel | |
| // %eventual_syn_id = OpISub %type_of_I %temp_id %step_val_id | |
| // %incremented_ctr_id = OpIAdd %int %ctr_id %int_1 | |
| // %cond_id = OpSLessThan %bool %incremented_ctr_id %num_iterations_id | |
| // OpBranchConditional %cond_id %loop_id %block_after_loop_id | |
| // A fresh id for the additional block. If this is 0, it means that only one | |
| // block is to be created. | |
| uint32 additional_block_id = 13; | |
| } | |
| message TransformationAddNoContractionDecoration { | |
| // Applies OpDecorate NoContraction to the given result id | |
| // Result id to be decorated | |
| uint32 result_id = 1; | |
| } | |
| message TransformationAddOpPhiSynonym { | |
| // Adds an OpPhi instruction at the start of a block with n predecessors (pred_1, pred_2, ..., pred_n) | |
| // and n related ids (id_1, id_2, ..., id_n) which are pairwise synonymous. | |
| // The instruction will be of the form: | |
| // %fresh_id = OpPhi %type %id_1 %pred_1 %id_2 %pred_2 ... %id_n %pred_n | |
| // and fresh_id will be recorded as being synonymous with all the other ids. | |
| // Label id of the block | |
| uint32 block_id = 1; | |
| // Pairs (pred_i, id_i) | |
| repeated UInt32Pair pred_to_id = 2; | |
| // Fresh id for the new instruction | |
| uint32 fresh_id = 3; | |
| } | |
| 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; | |
| // Type id for a new parameter. | |
| uint32 parameter_type_id = 3; | |
| // A map that maps from the OpFunctionCall id to the id that will be passed as the new | |
| // parameter at that call site. It must have the same type as that of the new parameter. | |
| repeated UInt32Pair call_parameter_ids = 4; | |
| // 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 = 5; | |
| } | |
| 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; | |
| // New synonym is derived by applying OpBitwiseOr to |result_id| with the second | |
| // operand being 0 taken with the same bit length as |result_id| | |
| BITWISE_OR = 6; | |
| // New synonym is derived by applying OpBitwiseXor to |result_id| with the second | |
| // operand being 0 taken with the same bit length as |result_id| | |
| BITWISE_XOR = 7; | |
| } | |
| // 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 TransformationCompositeInsert { | |
| // A transformation that adds an instruction OpCompositeInsert which creates | |
| // a new composite from an existing composite, with an element inserted. | |
| // A descriptor for an instruction before which the new instruction | |
| // OpCompositeInsert should be inserted. | |
| InstructionDescriptor instruction_to_insert_before = 1; | |
| // Result id of the inserted OpCompositeInsert instruction. | |
| uint32 fresh_id = 2; | |
| // Id of the composite used as the basis for the insertion. | |
| uint32 composite_id = 3; | |
| // Id of the object to be inserted. | |
| uint32 object_id = 4; | |
| // Indices that indicate which part of the composite should be inserted into. | |
| repeated uint32 index = 5; | |
| } | |
| 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 TransformationDuplicateRegionWithSelection { | |
| // A transformation that inserts a conditional statement with a boolean expression | |
| // of arbitrary value and duplicates a given single-entry, single-exit region, so | |
| // that it is present in each conditional branch and will be executed regardless | |
| // of which branch will be taken. | |
| // Fresh id for a label of the new entry block. | |
| uint32 new_entry_fresh_id = 1; | |
| // Id for a boolean expression. | |
| uint32 condition_id = 2; | |
| // Fresh id for a label of the merge block of the conditional. | |
| uint32 merge_label_fresh_id = 3; | |
| // Block id of the entry block of the original region. | |
| uint32 entry_block_id = 4; | |
| // Block id of the exit block of the original region. | |
| uint32 exit_block_id = 5; | |
| // Map that maps from a label in the original region to the corresponding label | |
| // in the duplicated region. | |
| repeated UInt32Pair original_label_to_duplicate_label = 6; | |
| // Map that maps from a result id in the original region to the corresponding | |
| // result id in the duplicated region. | |
| repeated UInt32Pair original_id_to_duplicate_id = 7; | |
| // Map that maps from a result id in the original region to the result id of the | |
| // corresponding OpPhi instruction. | |
| repeated UInt32Pair original_id_to_phi_id = 8; | |
| } | |
| 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 TransformationExpandVectorReduction { | |
| // A transformation that adds synonyms for OpAny and OpAll instructions by | |
| // evaluating each vector component with the corresponding logical operation. | |
| // There is a SPIR-V code example in the header file of the transformation | |
| // class that can help understand the transformation. | |
| // The OpAny or OpAll instruction result id. | |
| uint32 instruction_result_id = 1; | |
| // The fresh ids required to apply the transformation. | |
| repeated uint32 fresh_ids = 2; | |
| } | |
| message TransformationFlattenConditionalBranch { | |
| // A transformation that takes a selection construct with a header | |
| // containing an OpBranchConditional instruction and flattens it. | |
| // For example, something of the form: | |
| // | |
| // %1 = OpLabel | |
| // [header instructions] | |
| // OpSelectionMerge %4 None | |
| // OpBranchConditional %cond %2 %3 | |
| // %2 = OpLabel | |
| // [true branch instructions] | |
| // OpBranch %4 | |
| // %3 = OpLabel | |
| // [false branch instructions] | |
| // OpBranch %4 | |
| // %4 = OpLabel | |
| // ... | |
| // | |
| // becomes: | |
| // | |
| // %1 = OpLabel | |
| // [header instructions] | |
| // OpBranch %2 | |
| // %2 = OpLabel | |
| // [true branch instructions] | |
| // OpBranch %3 | |
| // %3 = OpLabel | |
| // [false branch instructions] | |
| // OpBranch %4 | |
| // %4 = OpLabel | |
| // ... | |
| // | |
| // If all of the instructions in the true or false branches have | |
| // no side effects, this is semantics-preserving. | |
| // Side-effecting instructions will instead be enclosed by smaller | |
| // conditionals. For more details, look at the definition for the | |
| // SideEffectWrapperInfo message. | |
| // | |
| // Nested conditionals or loops are not supported. The false branch | |
| // could also be executed before the true branch, depending on the | |
| // |true_branch_first| field. | |
| // The label id of the header block | |
| uint32 header_block_id = 1; | |
| // A boolean field deciding the order in which the original branches | |
| // will be laid out: the true branch will be laid out first iff this | |
| // field is true. | |
| bool true_branch_first = 2; | |
| // If the convergence block contains an OpPhi with bvec2 result type, it may | |
| // be necessary to introduce a bvec2 with the selection construct's condition | |
| // in both components in order to turn the OpPhi into an OpSelect. This | |
| // this field provides a fresh id for an OpCompositeConstruct instruction for | |
| // this purpose. It should be set to 0 if no such instruction is required. | |
| uint32 fresh_id_for_bvec2_selector = 3; | |
| // The same as |fresh_id_for_bvec2_selector| but for the bvec3 case. | |
| uint32 fresh_id_for_bvec3_selector = 4; | |
| // The same as |fresh_id_for_bvec2_selector| but for the bvec4 case. | |
| uint32 fresh_id_for_bvec4_selector = 5; | |
| // A list of instructions with side effects, which must be enclosed | |
| // inside smaller conditionals before flattening the main one, and | |
| // the corresponding fresh ids and module ids needed. | |
| repeated SideEffectWrapperInfo side_effect_wrapper_info = 6; | |
| } | |
| 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 TransformationInlineFunction { | |
| // This transformation inlines a function by mapping the function instructions to fresh ids. | |
| // Result id of the function call instruction. | |
| uint32 function_call_id = 1; | |
| // For each result id defined by the called function, | |
| // this map provides an associated fresh id that can | |
| // be used in the inlined version of the function call. | |
| repeated UInt32Pair result_id_map = 2; | |
| } | |
| 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 or OpAtomicLoad 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; | |
| // True if and only if the load should be atomic. | |
| bool is_atomic = 3; | |
| // The memory scope for the atomic load. Ignored unless |is_atomic| is true. | |
| uint32 memory_scope_id = 4; | |
| // The memory semantics for the atomic load. Ignored unless |is_atomic| is true. | |
| uint32 memory_semantics_id = 5; | |
| // A descriptor for an instruction in a block before which the new OpLoad | |
| // instruction should be inserted. | |
| InstructionDescriptor instruction_to_insert_before = 6; | |
| } | |
| 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 TransformationMergeFunctionReturns { | |
| // A transformation that modifies a function so that it does not return early, | |
| // so it only has one return statement (ignoring unreachable blocks). | |
| // | |
| // The function is enclosed inside an outer loop, that is only executed once, | |
| // and whose merge block is the new return block of the function. | |
| // | |
| // Each return instruction is replaced by: | |
| // OpBranch %innermost_loop_merge | |
| // where %innermost_loop_merge is the innermost loop containing the return | |
| // instruction. | |
| // | |
| // Each merge block whose associated loop contains return instructions is | |
| // changed so that it branches to the merge block of the loop containing it, | |
| // as explained in the comments to the ReturnMergingInfo message. | |
| // | |
| // The new return block (the merge block of the new outer loop) will be of | |
| // the following form (if the return type is not void): | |
| // %outer_return_id = OpLabel | |
| // %return_val_id = OpPhi %return_type %val1 %block_1 %val2 %block_2 ... | |
| // OpReturnValue %return_val_id | |
| // where %block_k is either a return block that, in the original function, is | |
| // outside of any loops, or the merge block of a loop that contains return | |
| // instructions and is not, originally, nested inside another loop, and | |
| // %block_k is the corresponding return value. | |
| // If the function has void type, there will be no OpPhi instruction and the | |
| // last instruction will be OpReturn. | |
| // The id of the function to which the transformation is being applied. | |
| uint32 function_id = 1; | |
| // A fresh id for the header of the new outer loop. | |
| uint32 outer_header_id = 2; | |
| // A fresh id for an unreachable continue construct for the new outer loop. | |
| uint32 unreachable_continue_id = 7; | |
| // A fresh id for the new return block of the function, | |
| // i.e. the merge block of the new outer loop. | |
| uint32 outer_return_id = 3; | |
| // A fresh id for the value that will be returned. | |
| // This is ignored if the function has void return type. | |
| uint32 return_val_id = 4; | |
| // An existing id of the same type as the return value, which is | |
| // available to use at the end of the entry block. | |
| // This is ignored if the function has void return type or if no | |
| // loops in the function contain a return instruction. | |
| // If the function is not void, the transformation will add an | |
| // OpPhi instruction to each merge block whose associated loop | |
| // contains at least a return instruction. The value associated | |
| // with existing predecessors from which the function cannot be | |
| // returning will be this id, used as a placeholder. | |
| uint32 any_returnable_val_id = 5; | |
| // The information needed to modify the merge blocks of | |
| // loops containing return instructions. | |
| repeated ReturnMergingInfo return_merging_info = 6; | |
| } | |
| 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 TransformationMutatePointer { | |
| // Backs up value of the pointer, writes into the pointer and | |
| // restores the original value. | |
| // Result id of the pointer instruction to mutate. | |
| uint32 pointer_id = 1; | |
| // Fresh id for the OpLoad instruction. | |
| uint32 fresh_id = 2; | |
| // Instruction to insert backup, mutation and restoration code before. | |
| InstructionDescriptor insert_before = 3; | |
| } | |
| 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 TransformationPropagateInstructionDown { | |
| // Propagates an instruction from |block_id| into its successors. | |
| // Concretely, the transformation clones the propagated instruction | |
| // into some of the successors of |block_id| and removes the original | |
| // instruction. Additionally, an OpPhi instruction may be added to make sure | |
| // that the transformation can be applied in various scenarios. | |
| // | |
| // Note that the instruction might not be propagated down into every successor | |
| // of |block_id| since it might make the module invalid. | |
| // Id of the block to propagate an instruction from. The decision on what | |
| // instruction to propagate is made based on whether the instruction interacts | |
| // with memory, whether that instruction is used in its block etc (see the | |
| // transformation class for more details). | |
| uint32 block_id = 1; | |
| // A fresh id for an OpPhi instruction. This might not be used by the | |
| // transformation since an OpPhi instruction is created only if needed | |
| // (e.g. an instruction is propagated into divergent blocks). | |
| uint32 phi_fresh_id = 2; | |
| // A map from the id of some successor of the |block_id| to the fresh id. | |
| // The map contains a fresh id for at least every successor of the |block_id|. | |
| // Every fresh id in the map corresponds to the result id of the clone, | |
| // propagated into the corresponding successor block. This transformation | |
| // might use overflow ids if they are available and this field doesn't account | |
| // for every successor of |block_id|. | |
| repeated UInt32Pair successor_id_to_fresh_id = 3; | |
| } | |
| 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 TransformationReplaceBranchFromDeadBlockWithExit { | |
| // Given a dead block that ends with OpBranch, replaces OpBranch with an | |
| // "exit" instruction; one of OpReturn/OpReturnValue, OpKill (in a fragment | |
| // shader) or OpUnreachable. | |
| // The dead block whose terminator is to be replaced. | |
| uint32 block_id = 1; | |
| // The opcode of the new terminator. | |
| uint32 opcode = 2; | |
| // Ignored unless opcode is OpReturnValue, in which case this field provides | |
| // a suitable result id to be returned. | |
| uint32 return_value_id = 3; | |
| } | |
| 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 comparison | |
| uint32 lhs_id = 2; | |
| // Id for the constant to be used on the RHS of the comparison | |
| 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 TransformationReplaceIrrelevantId { | |
| // Replaces an irrelevant id with another id of the same type. | |
| // The id use that is to be replaced | |
| IdUseDescriptor id_use_descriptor = 1; | |
| // The replacement id | |
| uint32 replacement_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 TransformationReplaceOpPhiIdFromDeadPredecessor { | |
| // Replaces one of the ids used by an OpPhi instruction, when | |
| // the corresponding predecessor is dead, with any available id | |
| // of the correct type. | |
| // The result id of the OpPhi instruction. | |
| uint32 opphi_id = 1; | |
| // The label id of one of the predecessors of the block containing | |
| // the OpPhi instruction, corresponding to the id that we want to | |
| // replace. | |
| uint32 pred_label_id = 2; | |
| // The id that, after the transformation, will be associated with | |
| // the given predecessor. | |
| uint32 replacement_id = 3; | |
| } | |
| message TransformationReplaceOpSelectWithConditionalBranch { | |
| // A transformation that takes an OpSelect instruction with a | |
| // scalar boolean condition and replaces it with a conditional | |
| // branch and an OpPhi instruction. | |
| // The OpSelect instruction must be the first instruction in its | |
| // block, which must have a unique predecessor. The block will | |
| // become the merge block of a new construct, while its predecessor | |
| // will become the header. | |
| // Given the original OpSelect instruction: | |
| // %id = OpSelect %type %cond %then %else | |
| // The branching instruction of the header will be: | |
| // OpBranchConditional %cond %true_block_id %false_block_id | |
| // and the OpSelect instruction will be turned into: | |
| // %id = OpPhi %type %then %true_block_id %else %false_block_id | |
| // At most one of |true_block_id| and |false_block_id| can be zero. In | |
| // that case, there will be no such block and all references to it | |
| // will be replaced by %merge_block (where %merge_block is the | |
| // block containing the OpSelect instruction). | |
| // The result id of the OpSelect instruction. | |
| uint32 select_id = 1; | |
| // A fresh id for the new block that the predecessor of the block | |
| // containing |select_id| will branch to if the condition holds. | |
| uint32 true_block_id = 2; | |
| // A fresh id for the new block that the predecessor of the block | |
| // containing |select_id| will branch to if the condition does not | |
| // hold. | |
| uint32 false_block_id = 3; | |
| } | |
| 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 or OpAtomicStore instruction of an id to a pointer. | |
| // The pointer to be stored to. | |
| uint32 pointer_id = 1; | |
| // True if and only if the load should be atomic. | |
| bool is_atomic = 2; | |
| // The memory scope for the atomic load. Ignored unless |is_atomic| is true. | |
| uint32 memory_scope_id = 3; | |
| // The memory semantics for the atomic load. Ignored unless |is_atomic| is true. | |
| uint32 memory_semantics_id = 4; | |
| // The value to be stored. | |
| uint32 value_id = 5; | |
| // A descriptor for an instruction in a block before which the new OpStore | |
| // instruction should be inserted. | |
| InstructionDescriptor instruction_to_insert_before = 6; | |
| } | |
| 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 TransformationSwapFunctionVariables { | |
| // A transformation that swaps function variables | |
| // Result id of the first variable. | |
| uint32 result_id1 = 1; | |
| // Result id of the second variable. | |
| uint32 result_id2 = 2; | |
| } | |
| message TransformationSwapTwoFunctions { | |
| // A transformation that swaps the position of two functions within the same module. | |
| // the IDs for the two functions that are swapped. | |
| uint32 function_id1 = 1; | |
| uint32 function_id2 = 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; | |
| } | |
| message TransformationWrapEarlyTerminatorInFunction { | |
| // Replaces an early terminator - OpKill, OpReachable or OpTerminateInvocation | |
| // - with a call to a wrapper function for the terminator. | |
| // A fresh id for a new OpFunctionCall instruction. | |
| uint32 fresh_id = 1; | |
| // A descriptor for an OpKill, OpUnreachable or OpTerminateInvocation | |
| // instruction. | |
| InstructionDescriptor early_terminator_instruction = 2; | |
| // An id with the same type as the enclosing function's return type that is | |
| // available at the early terminator. This is used to change the terminator | |
| // to OpReturnValue. Ignored if the enclosing function has void return type, | |
| // in which case OpReturn can be used as the new terminator. | |
| uint32 returned_value_id = 3; | |
| } | |
| message TransformationWrapRegionInSelection { | |
| // Transforms a single-entry-single-exit region R into | |
| // if (|branch_condition|) { R } else { R } | |
| // The entry block for R becomes a selection header and | |
| // the exit block - a selection merge. | |
| // | |
| // Note that the region R is not duplicated. Thus, the effect of | |
| // this transformation can be represented as follows: | |
| // entry | |
| // entry / \ | |
| // | \ / | |
| // R --> R | |
| // | | | |
| // exit exit | |
| // This behaviour is different from TransformationDuplicateRegionWithSelection | |
| // that copies the blocks in R. | |
| // The entry block for the region R. | |
| uint32 region_entry_block_id = 1; | |
| // The exit block for the region R. | |
| uint32 region_exit_block_id = 2; | |
| // Boolean value for the condition expression. | |
| bool branch_condition = 3; | |
| } | |
| message TransformationWrapVectorSynonym { | |
| // A transformation that wraps an arithmetic operation into a vector operation | |
| // and get the result of the original operation from the corresponding index. | |
| // For instance, for this transformation, an scalar operation between two scalars: | |
| // define op ∈ {+, -, *} | |
| // c = a op b | |
| // | |
| // requires the availability of two vectors: | |
| // | |
| // va = vector(..., a, ...) | |
| // vb = vector(..., b, ...) | |
| // | |
| // where a and b are in the same position i in each of their corresponding vector | |
| // and a is synonymous with va[i] and b is synonymous with vb[i]. | |
| // | |
| // The transformation then add an instruction vc = va op vb where c is synonymous | |
| // with vc[i]. | |
| // The result if of the original scalar operation instruction. | |
| uint32 instruction_id = 1; | |
| // The result id for the first vector that contains the first value of the scalar operation. | |
| uint32 vector_operand1 = 2; | |
| // The result id for the second vector that contains the second value of the scalar operation. | |
| uint32 vector_operand2 = 3; | |
| // A fresh id for the resulted vector from the addition of the first and second vector. | |
| uint32 fresh_id = 4; | |
| // The position in the vector where the value of original instruction is located. Must be in | |
| // the corresponding vector range. | |
| uint32 scalar_position = 5; | |
| } |