SPIRV-Tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp
alan-baker d35a78db57
Switch SPIRV-Tools to use spirv.hpp11 internally (#4981)
Fixes #4960

* Switches to using enum classes with an underlying type to avoid
  undefined behaviour
2022-11-04 17:27:10 -04:00

1042 lines
48 KiB
C++

// Copyright (c) 2020 André Perez Maselco
//
// 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.
#include "source/fuzz/transformation_replace_linear_algebra_instruction.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/instruction_descriptor.h"
namespace spvtools {
namespace fuzz {
TransformationReplaceLinearAlgebraInstruction::
TransformationReplaceLinearAlgebraInstruction(
protobufs::TransformationReplaceLinearAlgebraInstruction message)
: message_(std::move(message)) {}
TransformationReplaceLinearAlgebraInstruction::
TransformationReplaceLinearAlgebraInstruction(
const std::vector<uint32_t>& fresh_ids,
const protobufs::InstructionDescriptor& instruction_descriptor) {
for (auto fresh_id : fresh_ids) {
message_.add_fresh_ids(fresh_id);
}
*message_.mutable_instruction_descriptor() = instruction_descriptor;
}
bool TransformationReplaceLinearAlgebraInstruction::IsApplicable(
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
auto instruction =
FindInstruction(message_.instruction_descriptor(), ir_context);
// It must be a linear algebra instruction.
if (!spvOpcodeIsLinearAlgebra(instruction->opcode())) {
return false;
}
// |message_.fresh_ids.size| must be the exact number of fresh ids needed to
// apply the transformation.
if (static_cast<uint32_t>(message_.fresh_ids().size()) !=
GetRequiredFreshIdCount(ir_context, instruction)) {
return false;
}
// All ids in |message_.fresh_ids| must be fresh.
for (uint32_t fresh_id : message_.fresh_ids()) {
if (!fuzzerutil::IsFreshId(ir_context, fresh_id)) {
return false;
}
}
return true;
}
void TransformationReplaceLinearAlgebraInstruction::Apply(
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
auto linear_algebra_instruction =
FindInstruction(message_.instruction_descriptor(), ir_context);
switch (linear_algebra_instruction->opcode()) {
case spv::Op::OpTranspose:
ReplaceOpTranspose(ir_context, linear_algebra_instruction);
break;
case spv::Op::OpVectorTimesScalar:
ReplaceOpVectorTimesScalar(ir_context, linear_algebra_instruction);
break;
case spv::Op::OpMatrixTimesScalar:
ReplaceOpMatrixTimesScalar(ir_context, linear_algebra_instruction);
break;
case spv::Op::OpVectorTimesMatrix:
ReplaceOpVectorTimesMatrix(ir_context, linear_algebra_instruction);
break;
case spv::Op::OpMatrixTimesVector:
ReplaceOpMatrixTimesVector(ir_context, linear_algebra_instruction);
break;
case spv::Op::OpMatrixTimesMatrix:
ReplaceOpMatrixTimesMatrix(ir_context, linear_algebra_instruction);
break;
case spv::Op::OpOuterProduct:
ReplaceOpOuterProduct(ir_context, linear_algebra_instruction);
break;
case spv::Op::OpDot:
ReplaceOpDot(ir_context, linear_algebra_instruction);
break;
default:
assert(false && "Should be unreachable.");
break;
}
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
}
protobufs::Transformation
TransformationReplaceLinearAlgebraInstruction::ToMessage() const {
protobufs::Transformation result;
*result.mutable_replace_linear_algebra_instruction() = message_;
return result;
}
uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount(
opt::IRContext* ir_context, opt::Instruction* instruction) {
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
// Right now we only support certain operations.
switch (instruction->opcode()) {
case spv::Op::OpTranspose: {
// For each matrix row, |2 * matrix_column_count| OpCompositeExtract and 1
// OpCompositeConstruct will be inserted.
auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
instruction->GetSingleWordInOperand(0));
uint32_t matrix_column_count =
ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_count();
uint32_t matrix_row_count = ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_type()
->AsVector()
->element_count();
return matrix_row_count * (2 * matrix_column_count + 1);
}
case spv::Op::OpVectorTimesScalar:
// For each vector component, 1 OpCompositeExtract and 1 OpFMul will be
// inserted.
return 2 *
ir_context->get_type_mgr()
->GetType(ir_context->get_def_use_mgr()
->GetDef(instruction->GetSingleWordInOperand(0))
->type_id())
->AsVector()
->element_count();
case spv::Op::OpMatrixTimesScalar: {
// For each matrix column, |1 + column.size| OpCompositeExtract,
// |column.size| OpFMul and 1 OpCompositeConstruct instructions will be
// inserted.
auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
instruction->GetSingleWordInOperand(0));
auto matrix_type =
ir_context->get_type_mgr()->GetType(matrix_instruction->type_id());
return 2 * matrix_type->AsMatrix()->element_count() *
(1 + matrix_type->AsMatrix()
->element_type()
->AsVector()
->element_count());
}
case spv::Op::OpVectorTimesMatrix: {
// For each vector component, 1 OpCompositeExtract instruction will be
// inserted. For each matrix column, |1 + vector_component_count|
// OpCompositeExtract, |vector_component_count| OpFMul and
// |vector_component_count - 1| OpFAdd instructions will be inserted.
auto vector_instruction = ir_context->get_def_use_mgr()->GetDef(
instruction->GetSingleWordInOperand(0));
auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
instruction->GetSingleWordInOperand(1));
uint32_t vector_component_count =
ir_context->get_type_mgr()
->GetType(vector_instruction->type_id())
->AsVector()
->element_count();
uint32_t matrix_column_count =
ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_count();
return vector_component_count * (3 * matrix_column_count + 1);
}
case spv::Op::OpMatrixTimesVector: {
// For each matrix column, |1 + matrix_row_count| OpCompositeExtract
// will be inserted. For each matrix row, |matrix_column_count| OpFMul and
// |matrix_column_count - 1| OpFAdd instructions will be inserted. For
// each vector component, 1 OpCompositeExtract instruction will be
// inserted.
auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
instruction->GetSingleWordInOperand(0));
uint32_t matrix_column_count =
ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_count();
uint32_t matrix_row_count = ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_type()
->AsVector()
->element_count();
return 3 * matrix_column_count * matrix_row_count +
2 * matrix_column_count - matrix_row_count;
}
case spv::Op::OpMatrixTimesMatrix: {
// For each matrix 2 column, 1 OpCompositeExtract, 1 OpCompositeConstruct,
// |3 * matrix_1_row_count * matrix_1_column_count| OpCompositeExtract,
// |matrix_1_row_count * matrix_1_column_count| OpFMul,
// |matrix_1_row_count * (matrix_1_column_count - 1)| OpFAdd instructions
// will be inserted.
auto matrix_1_instruction = ir_context->get_def_use_mgr()->GetDef(
instruction->GetSingleWordInOperand(0));
uint32_t matrix_1_column_count =
ir_context->get_type_mgr()
->GetType(matrix_1_instruction->type_id())
->AsMatrix()
->element_count();
uint32_t matrix_1_row_count =
ir_context->get_type_mgr()
->GetType(matrix_1_instruction->type_id())
->AsMatrix()
->element_type()
->AsVector()
->element_count();
auto matrix_2_instruction = ir_context->get_def_use_mgr()->GetDef(
instruction->GetSingleWordInOperand(1));
uint32_t matrix_2_column_count =
ir_context->get_type_mgr()
->GetType(matrix_2_instruction->type_id())
->AsMatrix()
->element_count();
return matrix_2_column_count *
(2 + matrix_1_row_count * (5 * matrix_1_column_count - 1));
}
case spv::Op::OpOuterProduct: {
// For each |vector_2| component, |vector_1_component_count + 1|
// OpCompositeExtract, |vector_1_component_count| OpFMul and 1
// OpCompositeConstruct instructions will be inserted.
auto vector_1_instruction = ir_context->get_def_use_mgr()->GetDef(
instruction->GetSingleWordInOperand(0));
auto vector_2_instruction = ir_context->get_def_use_mgr()->GetDef(
instruction->GetSingleWordInOperand(1));
uint32_t vector_1_component_count =
ir_context->get_type_mgr()
->GetType(vector_1_instruction->type_id())
->AsVector()
->element_count();
uint32_t vector_2_component_count =
ir_context->get_type_mgr()
->GetType(vector_2_instruction->type_id())
->AsVector()
->element_count();
return 2 * vector_2_component_count * (vector_1_component_count + 1);
}
case spv::Op::OpDot:
// For each pair of vector components, 2 OpCompositeExtract and 1 OpFMul
// will be inserted. The first two OpFMul instructions will result the
// first OpFAdd instruction to be inserted. For each remaining OpFMul, 1
// OpFAdd will be inserted. The last OpFAdd instruction is got by changing
// the OpDot instruction.
return 4 * ir_context->get_type_mgr()
->GetType(
ir_context->get_def_use_mgr()
->GetDef(instruction->GetSingleWordInOperand(0))
->type_id())
->AsVector()
->element_count() -
2;
default:
assert(false && "Unsupported linear algebra instruction.");
return 0;
}
}
void TransformationReplaceLinearAlgebraInstruction::ReplaceOpTranspose(
opt::IRContext* ir_context,
opt::Instruction* linear_algebra_instruction) const {
// Gets OpTranspose instruction information.
auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
linear_algebra_instruction->GetSingleWordInOperand(0));
uint32_t matrix_column_count = ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_count();
auto matrix_column_type = ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_type();
auto matrix_column_component_type =
matrix_column_type->AsVector()->element_type();
uint32_t matrix_row_count = matrix_column_type->AsVector()->element_count();
auto resulting_matrix_column_type =
ir_context->get_type_mgr()
->GetType(linear_algebra_instruction->type_id())
->AsMatrix()
->element_type();
uint32_t fresh_id_index = 0;
std::vector<uint32_t> result_column_ids(matrix_row_count);
for (uint32_t i = 0; i < matrix_row_count; i++) {
std::vector<uint32_t> column_component_ids(matrix_column_count);
for (uint32_t j = 0; j < matrix_column_count; j++) {
// Extracts the matrix column.
uint32_t matrix_column_id = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeExtract,
ir_context->get_type_mgr()->GetId(matrix_column_type),
matrix_column_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
// Extracts the matrix column component.
column_component_ids[j] = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeExtract,
ir_context->get_type_mgr()->GetId(matrix_column_component_type),
column_component_ids[j],
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {matrix_column_id}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
}
// Inserts the resulting matrix column.
opt::Instruction::OperandList in_operands;
for (auto& column_component_id : column_component_ids) {
in_operands.push_back({SPV_OPERAND_TYPE_ID, {column_component_id}});
}
result_column_ids[i] = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeConstruct,
ir_context->get_type_mgr()->GetId(resulting_matrix_column_type),
result_column_ids[i], opt::Instruction::OperandList(in_operands)));
}
// The OpTranspose instruction is changed to an OpCompositeConstruct
// instruction.
linear_algebra_instruction->SetOpcode(spv::Op::OpCompositeConstruct);
linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]});
for (uint32_t i = 1; i < result_column_ids.size(); i++) {
linear_algebra_instruction->AddOperand(
{SPV_OPERAND_TYPE_ID, {result_column_ids[i]}});
}
fuzzerutil::UpdateModuleIdBound(
ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
}
void TransformationReplaceLinearAlgebraInstruction::ReplaceOpVectorTimesScalar(
opt::IRContext* ir_context,
opt::Instruction* linear_algebra_instruction) const {
// Gets OpVectorTimesScalar in operands.
auto vector = ir_context->get_def_use_mgr()->GetDef(
linear_algebra_instruction->GetSingleWordInOperand(0));
auto scalar = ir_context->get_def_use_mgr()->GetDef(
linear_algebra_instruction->GetSingleWordInOperand(1));
uint32_t vector_component_count = ir_context->get_type_mgr()
->GetType(vector->type_id())
->AsVector()
->element_count();
std::vector<uint32_t> float_multiplication_ids(vector_component_count);
uint32_t fresh_id_index = 0;
for (uint32_t i = 0; i < vector_component_count; i++) {
// Extracts |vector| component.
uint32_t vector_extract_id = message_.fresh_ids(fresh_id_index++);
fuzzerutil::UpdateModuleIdBound(ir_context, vector_extract_id);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeExtract, scalar->type_id(),
vector_extract_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {vector->result_id()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
// Multiplies the |vector| component with the |scalar|.
uint32_t float_multiplication_id = message_.fresh_ids(fresh_id_index++);
float_multiplication_ids[i] = float_multiplication_id;
fuzzerutil::UpdateModuleIdBound(ir_context, float_multiplication_id);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpFMul, scalar->type_id(), float_multiplication_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {vector_extract_id}},
{SPV_OPERAND_TYPE_ID, {scalar->result_id()}}})));
}
// The OpVectorTimesScalar instruction is changed to an OpCompositeConstruct
// instruction.
linear_algebra_instruction->SetOpcode(spv::Op::OpCompositeConstruct);
linear_algebra_instruction->SetInOperand(0, {float_multiplication_ids[0]});
linear_algebra_instruction->SetInOperand(1, {float_multiplication_ids[1]});
for (uint32_t i = 2; i < float_multiplication_ids.size(); i++) {
linear_algebra_instruction->AddOperand(
{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[i]}});
}
}
void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesScalar(
opt::IRContext* ir_context,
opt::Instruction* linear_algebra_instruction) const {
// Gets OpMatrixTimesScalar in operands.
auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
linear_algebra_instruction->GetSingleWordInOperand(0));
auto scalar_instruction = ir_context->get_def_use_mgr()->GetDef(
linear_algebra_instruction->GetSingleWordInOperand(1));
// Gets matrix information.
uint32_t matrix_column_count = ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_count();
auto matrix_column_type = ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_type();
uint32_t matrix_column_size = matrix_column_type->AsVector()->element_count();
std::vector<uint32_t> composite_construct_ids(matrix_column_count);
uint32_t fresh_id_index = 0;
for (uint32_t i = 0; i < matrix_column_count; i++) {
// Extracts |matrix| column.
uint32_t matrix_extract_id = message_.fresh_ids(fresh_id_index++);
fuzzerutil::UpdateModuleIdBound(ir_context, matrix_extract_id);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeExtract,
ir_context->get_type_mgr()->GetId(matrix_column_type),
matrix_extract_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
std::vector<uint32_t> float_multiplication_ids(matrix_column_size);
for (uint32_t j = 0; j < matrix_column_size; j++) {
// Extracts |column| component.
uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++);
fuzzerutil::UpdateModuleIdBound(ir_context, column_extract_id);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeExtract,
scalar_instruction->type_id(), column_extract_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {matrix_extract_id}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
// Multiplies the |column| component with the |scalar|.
float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++);
fuzzerutil::UpdateModuleIdBound(ir_context, float_multiplication_ids[j]);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpFMul, scalar_instruction->type_id(),
float_multiplication_ids[j],
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {column_extract_id}},
{SPV_OPERAND_TYPE_ID, {scalar_instruction->result_id()}}})));
}
// Constructs a new column multiplied by |scalar|.
opt::Instruction::OperandList composite_construct_in_operands;
for (uint32_t& float_multiplication_id : float_multiplication_ids) {
composite_construct_in_operands.push_back(
{SPV_OPERAND_TYPE_ID, {float_multiplication_id}});
}
composite_construct_ids[i] = message_.fresh_ids(fresh_id_index++);
fuzzerutil::UpdateModuleIdBound(ir_context, composite_construct_ids[i]);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeConstruct,
ir_context->get_type_mgr()->GetId(matrix_column_type),
composite_construct_ids[i], composite_construct_in_operands));
}
// The OpMatrixTimesScalar instruction is changed to an OpCompositeConstruct
// instruction.
linear_algebra_instruction->SetOpcode(spv::Op::OpCompositeConstruct);
linear_algebra_instruction->SetInOperand(0, {composite_construct_ids[0]});
linear_algebra_instruction->SetInOperand(1, {composite_construct_ids[1]});
for (uint32_t i = 2; i < composite_construct_ids.size(); i++) {
linear_algebra_instruction->AddOperand(
{SPV_OPERAND_TYPE_ID, {composite_construct_ids[i]}});
}
}
void TransformationReplaceLinearAlgebraInstruction::ReplaceOpVectorTimesMatrix(
opt::IRContext* ir_context,
opt::Instruction* linear_algebra_instruction) const {
// Gets vector information.
auto vector_instruction = ir_context->get_def_use_mgr()->GetDef(
linear_algebra_instruction->GetSingleWordInOperand(0));
uint32_t vector_component_count = ir_context->get_type_mgr()
->GetType(vector_instruction->type_id())
->AsVector()
->element_count();
auto vector_component_type = ir_context->get_type_mgr()
->GetType(vector_instruction->type_id())
->AsVector()
->element_type();
// Extracts vector components.
uint32_t fresh_id_index = 0;
std::vector<uint32_t> vector_component_ids(vector_component_count);
for (uint32_t i = 0; i < vector_component_count; i++) {
vector_component_ids[i] = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeExtract,
ir_context->get_type_mgr()->GetId(vector_component_type),
vector_component_ids[i],
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {vector_instruction->result_id()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
}
// Gets matrix information.
auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
linear_algebra_instruction->GetSingleWordInOperand(1));
uint32_t matrix_column_count = ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_count();
auto matrix_column_type = ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_type();
std::vector<uint32_t> result_component_ids(matrix_column_count);
for (uint32_t i = 0; i < matrix_column_count; i++) {
// Extracts matrix column.
uint32_t matrix_extract_id = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeExtract,
ir_context->get_type_mgr()->GetId(matrix_column_type),
matrix_extract_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
std::vector<uint32_t> float_multiplication_ids(vector_component_count);
for (uint32_t j = 0; j < vector_component_count; j++) {
// Extracts column component.
uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeExtract,
ir_context->get_type_mgr()->GetId(vector_component_type),
column_extract_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {matrix_extract_id}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
// Multiplies corresponding vector and column components.
float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpFMul,
ir_context->get_type_mgr()->GetId(vector_component_type),
float_multiplication_ids[j],
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {vector_component_ids[j]}},
{SPV_OPERAND_TYPE_ID, {column_extract_id}}})));
}
// Adds the multiplication results.
std::vector<uint32_t> float_add_ids;
uint32_t float_add_id = message_.fresh_ids(fresh_id_index++);
float_add_ids.push_back(float_add_id);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpFAdd,
ir_context->get_type_mgr()->GetId(vector_component_type), float_add_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}},
{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}})));
for (uint32_t j = 2; j < float_multiplication_ids.size(); j++) {
float_add_id = message_.fresh_ids(fresh_id_index++);
float_add_ids.push_back(float_add_id);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpFAdd,
ir_context->get_type_mgr()->GetId(vector_component_type),
float_add_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[j]}},
{SPV_OPERAND_TYPE_ID, {float_add_ids[j - 2]}}})));
}
result_component_ids[i] = float_add_ids.back();
}
// The OpVectorTimesMatrix instruction is changed to an OpCompositeConstruct
// instruction.
linear_algebra_instruction->SetOpcode(spv::Op::OpCompositeConstruct);
linear_algebra_instruction->SetInOperand(0, {result_component_ids[0]});
linear_algebra_instruction->SetInOperand(1, {result_component_ids[1]});
for (uint32_t i = 2; i < result_component_ids.size(); i++) {
linear_algebra_instruction->AddOperand(
{SPV_OPERAND_TYPE_ID, {result_component_ids[i]}});
}
fuzzerutil::UpdateModuleIdBound(
ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
}
void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesVector(
opt::IRContext* ir_context,
opt::Instruction* linear_algebra_instruction) const {
// Gets matrix information.
auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
linear_algebra_instruction->GetSingleWordInOperand(0));
uint32_t matrix_column_count = ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_count();
auto matrix_column_type = ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_type();
uint32_t matrix_row_count = matrix_column_type->AsVector()->element_count();
// Extracts matrix columns.
uint32_t fresh_id_index = 0;
std::vector<uint32_t> matrix_column_ids(matrix_column_count);
for (uint32_t i = 0; i < matrix_column_count; i++) {
matrix_column_ids[i] = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeExtract,
ir_context->get_type_mgr()->GetId(matrix_column_type),
matrix_column_ids[i],
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
}
// Gets vector information.
auto vector_instruction = ir_context->get_def_use_mgr()->GetDef(
linear_algebra_instruction->GetSingleWordInOperand(1));
auto vector_component_type = ir_context->get_type_mgr()
->GetType(vector_instruction->type_id())
->AsVector()
->element_type();
// Extracts vector components.
std::vector<uint32_t> vector_component_ids(matrix_column_count);
for (uint32_t i = 0; i < matrix_column_count; i++) {
vector_component_ids[i] = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeExtract,
ir_context->get_type_mgr()->GetId(vector_component_type),
vector_component_ids[i],
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {vector_instruction->result_id()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
}
std::vector<uint32_t> result_component_ids(matrix_row_count);
for (uint32_t i = 0; i < matrix_row_count; i++) {
std::vector<uint32_t> float_multiplication_ids(matrix_column_count);
for (uint32_t j = 0; j < matrix_column_count; j++) {
// Extracts column component.
uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeExtract,
ir_context->get_type_mgr()->GetId(vector_component_type),
column_extract_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {matrix_column_ids[j]}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
// Multiplies corresponding vector and column components.
float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpFMul,
ir_context->get_type_mgr()->GetId(vector_component_type),
float_multiplication_ids[j],
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {column_extract_id}},
{SPV_OPERAND_TYPE_ID, {vector_component_ids[j]}}})));
}
// Adds the multiplication results.
std::vector<uint32_t> float_add_ids;
uint32_t float_add_id = message_.fresh_ids(fresh_id_index++);
float_add_ids.push_back(float_add_id);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpFAdd,
ir_context->get_type_mgr()->GetId(vector_component_type), float_add_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}},
{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}})));
for (uint32_t j = 2; j < float_multiplication_ids.size(); j++) {
float_add_id = message_.fresh_ids(fresh_id_index++);
float_add_ids.push_back(float_add_id);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpFAdd,
ir_context->get_type_mgr()->GetId(vector_component_type),
float_add_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[j]}},
{SPV_OPERAND_TYPE_ID, {float_add_ids[j - 2]}}})));
}
result_component_ids[i] = float_add_ids.back();
}
// The OpMatrixTimesVector instruction is changed to an OpCompositeConstruct
// instruction.
linear_algebra_instruction->SetOpcode(spv::Op::OpCompositeConstruct);
linear_algebra_instruction->SetInOperand(0, {result_component_ids[0]});
linear_algebra_instruction->SetInOperand(1, {result_component_ids[1]});
for (uint32_t i = 2; i < result_component_ids.size(); i++) {
linear_algebra_instruction->AddOperand(
{SPV_OPERAND_TYPE_ID, {result_component_ids[i]}});
}
fuzzerutil::UpdateModuleIdBound(
ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
}
void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesMatrix(
opt::IRContext* ir_context,
opt::Instruction* linear_algebra_instruction) const {
// Gets matrix 1 information.
auto matrix_1_instruction = ir_context->get_def_use_mgr()->GetDef(
linear_algebra_instruction->GetSingleWordInOperand(0));
uint32_t matrix_1_column_count =
ir_context->get_type_mgr()
->GetType(matrix_1_instruction->type_id())
->AsMatrix()
->element_count();
auto matrix_1_column_type = ir_context->get_type_mgr()
->GetType(matrix_1_instruction->type_id())
->AsMatrix()
->element_type();
auto matrix_1_column_component_type =
matrix_1_column_type->AsVector()->element_type();
uint32_t matrix_1_row_count =
matrix_1_column_type->AsVector()->element_count();
// Gets matrix 2 information.
auto matrix_2_instruction = ir_context->get_def_use_mgr()->GetDef(
linear_algebra_instruction->GetSingleWordInOperand(1));
uint32_t matrix_2_column_count =
ir_context->get_type_mgr()
->GetType(matrix_2_instruction->type_id())
->AsMatrix()
->element_count();
auto matrix_2_column_type = ir_context->get_type_mgr()
->GetType(matrix_2_instruction->type_id())
->AsMatrix()
->element_type();
uint32_t fresh_id_index = 0;
std::vector<uint32_t> result_column_ids(matrix_2_column_count);
for (uint32_t i = 0; i < matrix_2_column_count; i++) {
// Extracts matrix 2 column.
uint32_t matrix_2_column_id = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeExtract,
ir_context->get_type_mgr()->GetId(matrix_2_column_type),
matrix_2_column_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {matrix_2_instruction->result_id()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
std::vector<uint32_t> column_component_ids(matrix_1_row_count);
for (uint32_t j = 0; j < matrix_1_row_count; j++) {
std::vector<uint32_t> float_multiplication_ids(matrix_1_column_count);
for (uint32_t k = 0; k < matrix_1_column_count; k++) {
// Extracts matrix 1 column.
uint32_t matrix_1_column_id = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeExtract,
ir_context->get_type_mgr()->GetId(matrix_1_column_type),
matrix_1_column_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {matrix_1_instruction->result_id()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {k}}})));
// Extracts matrix 1 column component.
uint32_t matrix_1_column_component_id =
message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeExtract,
ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
matrix_1_column_component_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {matrix_1_column_id}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
// Extracts matrix 2 column component.
uint32_t matrix_2_column_component_id =
message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeExtract,
ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
matrix_2_column_component_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {matrix_2_column_id}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {k}}})));
// Multiplies corresponding matrix 1 and matrix 2 column components.
float_multiplication_ids[k] = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpFMul,
ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
float_multiplication_ids[k],
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {matrix_1_column_component_id}},
{SPV_OPERAND_TYPE_ID, {matrix_2_column_component_id}}})));
}
// Adds the multiplication results.
std::vector<uint32_t> float_add_ids;
uint32_t float_add_id = message_.fresh_ids(fresh_id_index++);
float_add_ids.push_back(float_add_id);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpFAdd,
ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
float_add_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}},
{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}})));
for (uint32_t k = 2; k < float_multiplication_ids.size(); k++) {
float_add_id = message_.fresh_ids(fresh_id_index++);
float_add_ids.push_back(float_add_id);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpFAdd,
ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
float_add_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[k]}},
{SPV_OPERAND_TYPE_ID, {float_add_ids[k - 2]}}})));
}
column_component_ids[j] = float_add_ids.back();
}
// Inserts the resulting matrix column.
opt::Instruction::OperandList in_operands;
for (auto& column_component_id : column_component_ids) {
in_operands.push_back({SPV_OPERAND_TYPE_ID, {column_component_id}});
}
result_column_ids[i] = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeConstruct,
ir_context->get_type_mgr()->GetId(matrix_1_column_type),
result_column_ids[i], opt::Instruction::OperandList(in_operands)));
}
// The OpMatrixTimesMatrix instruction is changed to an OpCompositeConstruct
// instruction.
linear_algebra_instruction->SetOpcode(spv::Op::OpCompositeConstruct);
linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]});
linear_algebra_instruction->SetInOperand(1, {result_column_ids[1]});
for (uint32_t i = 2; i < result_column_ids.size(); i++) {
linear_algebra_instruction->AddOperand(
{SPV_OPERAND_TYPE_ID, {result_column_ids[i]}});
}
fuzzerutil::UpdateModuleIdBound(
ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
}
void TransformationReplaceLinearAlgebraInstruction::ReplaceOpOuterProduct(
opt::IRContext* ir_context,
opt::Instruction* linear_algebra_instruction) const {
// Gets vector 1 information.
auto vector_1_instruction = ir_context->get_def_use_mgr()->GetDef(
linear_algebra_instruction->GetSingleWordInOperand(0));
uint32_t vector_1_component_count =
ir_context->get_type_mgr()
->GetType(vector_1_instruction->type_id())
->AsVector()
->element_count();
auto vector_1_component_type = ir_context->get_type_mgr()
->GetType(vector_1_instruction->type_id())
->AsVector()
->element_type();
// Gets vector 2 information.
auto vector_2_instruction = ir_context->get_def_use_mgr()->GetDef(
linear_algebra_instruction->GetSingleWordInOperand(1));
uint32_t vector_2_component_count =
ir_context->get_type_mgr()
->GetType(vector_2_instruction->type_id())
->AsVector()
->element_count();
uint32_t fresh_id_index = 0;
std::vector<uint32_t> result_column_ids(vector_2_component_count);
for (uint32_t i = 0; i < vector_2_component_count; i++) {
// Extracts |vector_2| component.
uint32_t vector_2_component_id = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeExtract,
ir_context->get_type_mgr()->GetId(vector_1_component_type),
vector_2_component_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {vector_2_instruction->result_id()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
std::vector<uint32_t> column_component_ids(vector_1_component_count);
for (uint32_t j = 0; j < vector_1_component_count; j++) {
// Extracts |vector_1| component.
uint32_t vector_1_component_id = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeExtract,
ir_context->get_type_mgr()->GetId(vector_1_component_type),
vector_1_component_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {vector_1_instruction->result_id()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
// Multiplies |vector_1| and |vector_2| components.
column_component_ids[j] = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpFMul,
ir_context->get_type_mgr()->GetId(vector_1_component_type),
column_component_ids[j],
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {vector_2_component_id}},
{SPV_OPERAND_TYPE_ID, {vector_1_component_id}}})));
}
// Inserts the resulting matrix column.
opt::Instruction::OperandList in_operands;
for (auto& column_component_id : column_component_ids) {
in_operands.push_back({SPV_OPERAND_TYPE_ID, {column_component_id}});
}
result_column_ids[i] = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeConstruct,
vector_1_instruction->type_id(), result_column_ids[i], in_operands));
}
// The OpOuterProduct instruction is changed to an OpCompositeConstruct
// instruction.
linear_algebra_instruction->SetOpcode(spv::Op::OpCompositeConstruct);
linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]});
linear_algebra_instruction->SetInOperand(1, {result_column_ids[1]});
for (uint32_t i = 2; i < result_column_ids.size(); i++) {
linear_algebra_instruction->AddOperand(
{SPV_OPERAND_TYPE_ID, {result_column_ids[i]}});
}
fuzzerutil::UpdateModuleIdBound(
ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
}
void TransformationReplaceLinearAlgebraInstruction::ReplaceOpDot(
opt::IRContext* ir_context,
opt::Instruction* linear_algebra_instruction) const {
// Gets OpDot in operands.
auto vector_1 = ir_context->get_def_use_mgr()->GetDef(
linear_algebra_instruction->GetSingleWordInOperand(0));
auto vector_2 = ir_context->get_def_use_mgr()->GetDef(
linear_algebra_instruction->GetSingleWordInOperand(1));
uint32_t vectors_component_count = ir_context->get_type_mgr()
->GetType(vector_1->type_id())
->AsVector()
->element_count();
std::vector<uint32_t> float_multiplication_ids(vectors_component_count);
uint32_t fresh_id_index = 0;
for (uint32_t i = 0; i < vectors_component_count; i++) {
// Extracts |vector_1| component.
uint32_t vector_1_extract_id = message_.fresh_ids(fresh_id_index++);
fuzzerutil::UpdateModuleIdBound(ir_context, vector_1_extract_id);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeExtract,
linear_algebra_instruction->type_id(), vector_1_extract_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {vector_1->result_id()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
// Extracts |vector_2| component.
uint32_t vector_2_extract_id = message_.fresh_ids(fresh_id_index++);
fuzzerutil::UpdateModuleIdBound(ir_context, vector_2_extract_id);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeExtract,
linear_algebra_instruction->type_id(), vector_2_extract_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {vector_2->result_id()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
// Multiplies the pair of components.
float_multiplication_ids[i] = message_.fresh_ids(fresh_id_index++);
fuzzerutil::UpdateModuleIdBound(ir_context, float_multiplication_ids[i]);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpFMul, linear_algebra_instruction->type_id(),
float_multiplication_ids[i],
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {vector_1_extract_id}},
{SPV_OPERAND_TYPE_ID, {vector_2_extract_id}}})));
}
// If the vector has 2 components, then there will be 2 float multiplication
// instructions.
if (vectors_component_count == 2) {
linear_algebra_instruction->SetOpcode(spv::Op::OpFAdd);
linear_algebra_instruction->SetInOperand(0, {float_multiplication_ids[0]});
linear_algebra_instruction->SetInOperand(1, {float_multiplication_ids[1]});
} else {
// The first OpFAdd instruction has as operands the first two OpFMul
// instructions.
std::vector<uint32_t> float_add_ids;
uint32_t float_add_id = message_.fresh_ids(fresh_id_index++);
float_add_ids.push_back(float_add_id);
fuzzerutil::UpdateModuleIdBound(ir_context, float_add_id);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpFAdd, linear_algebra_instruction->type_id(),
float_add_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}},
{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}})));
// The remaining OpFAdd instructions has as operands an OpFMul and an OpFAdd
// instruction.
for (uint32_t i = 2; i < float_multiplication_ids.size() - 1; i++) {
float_add_id = message_.fresh_ids(fresh_id_index++);
fuzzerutil::UpdateModuleIdBound(ir_context, float_add_id);
float_add_ids.push_back(float_add_id);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpFAdd, linear_algebra_instruction->type_id(),
float_add_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[i]}},
{SPV_OPERAND_TYPE_ID, {float_add_ids[i - 2]}}})));
}
// The last OpFAdd instruction is got by changing some of the OpDot
// instruction attributes.
linear_algebra_instruction->SetOpcode(spv::Op::OpFAdd);
linear_algebra_instruction->SetInOperand(
0, {float_multiplication_ids[float_multiplication_ids.size() - 1]});
linear_algebra_instruction->SetInOperand(
1, {float_add_ids[float_add_ids.size() - 1]});
}
}
std::unordered_set<uint32_t>
TransformationReplaceLinearAlgebraInstruction::GetFreshIds() const {
std::unordered_set<uint32_t> result;
for (auto id : message_.fresh_ids()) {
result.insert(id);
}
return result;
}
} // namespace fuzz
} // namespace spvtools