#include "source/fuzz/transformation_add_function.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/instruction_message.h"
namespace spvtools {
namespace fuzz {
TransformationAddFunction::TransformationAddFunction(
protobufs::TransformationAddFunction message)
: message_(std::move(message)) {}
TransformationAddFunction::TransformationAddFunction(
const std::vector<protobufs::Instruction>& instructions) {
for (auto& instruction : instructions) {
*message_.add_instruction() = instruction;
}
message_.set_is_livesafe(false);
}
TransformationAddFunction::TransformationAddFunction(
const std::vector<protobufs::Instruction>& instructions,
uint32_t loop_limiter_variable_id, uint32_t loop_limit_constant_id,
const std::vector<protobufs::LoopLimiterInfo>& loop_limiters,
uint32_t kill_unreachable_return_value_id,
const std::vector<protobufs::AccessChainClampingInfo>&
access_chain_clampers) {
for (auto& instruction : instructions) {
*message_.add_instruction() = instruction;
}
message_.set_is_livesafe(true);
message_.set_loop_limiter_variable_id(loop_limiter_variable_id);
message_.set_loop_limit_constant_id(loop_limit_constant_id);
for (auto& loop_limiter : loop_limiters) {
*message_.add_loop_limiter_info() = loop_limiter;
}
message_.set_kill_unreachable_return_value_id(
kill_unreachable_return_value_id);
for (auto& access_clamper : access_chain_clampers) {
*message_.add_access_chain_clamping_info() = access_clamper;
}
}
bool TransformationAddFunction::IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const {
std::set<uint32_t> ids_used_by_this_transformation;
for (auto& instruction : message_.instruction()) {
if (instruction.result_id()) {
if (!CheckIdIsFreshAndNotUsedByThisTransformation(
instruction.result_id(), ir_context,
&ids_used_by_this_transformation)) {
return false;
}
}
}
if (message_.is_livesafe()) {
if (!CheckIdIsFreshAndNotUsedByThisTransformation(
message_.loop_limiter_variable_id(), ir_context,
&ids_used_by_this_transformation)) {
return false;
}
for (auto& loop_limiter_info : message_.loop_limiter_info()) {
if (!CheckIdIsFreshAndNotUsedByThisTransformation(
loop_limiter_info.load_id(), ir_context,
&ids_used_by_this_transformation)) {
return false;
}
if (!CheckIdIsFreshAndNotUsedByThisTransformation(
loop_limiter_info.increment_id(), ir_context,
&ids_used_by_this_transformation)) {
return false;
}
if (!CheckIdIsFreshAndNotUsedByThisTransformation(
loop_limiter_info.compare_id(), ir_context,
&ids_used_by_this_transformation)) {
return false;
}
if (!CheckIdIsFreshAndNotUsedByThisTransformation(
loop_limiter_info.logical_op_id(), ir_context,
&ids_used_by_this_transformation)) {
return false;
}
}
for (auto& access_chain_clamping_info :
message_.access_chain_clamping_info()) {
for (auto& pair : access_chain_clamping_info.compare_and_select_ids()) {
if (!CheckIdIsFreshAndNotUsedByThisTransformation(
pair.first(), ir_context, &ids_used_by_this_transformation)) {
return false;
}
if (!CheckIdIsFreshAndNotUsedByThisTransformation(
pair.second(), ir_context, &ids_used_by_this_transformation)) {
return false;
}
}
}
}
auto cloned_module = fuzzerutil::CloneIRContext(ir_context);
if (!TryToAddFunction(cloned_module.get())) {
return false;
}
if (!fuzzerutil::IsValid(cloned_module.get(),
transformation_context.GetValidatorOptions(),
fuzzerutil::kSilentMessageConsumer)) {
return false;
}
if (message_.is_livesafe()) {
if (!TryToMakeFunctionLivesafe(cloned_module.get(),
transformation_context)) {
return false;
}
if (!fuzzerutil::IsValid(cloned_module.get(),
transformation_context.GetValidatorOptions(),
fuzzerutil::kSilentMessageConsumer)) {
return false;
}
}
return true;
}
void TransformationAddFunction::Apply(
opt::IRContext* ir_context,
TransformationContext* transformation_context) const {
bool success = TryToAddFunction(ir_context);
assert(success && "The function should be successfully added.");
(void)(success);
if (message_.is_livesafe()) {
success = TryToMakeFunctionLivesafe(ir_context, *transformation_context);
assert(success && "It should be possible to make the function livesafe.");
(void)(success);
}
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
assert(spv::Op(message_.instruction(0).opcode()) == spv::Op::OpFunction &&
"The first instruction of an 'add function' transformation must be "
"OpFunction.");
if (message_.is_livesafe()) {
transformation_context->GetFactManager()->AddFactFunctionIsLivesafe(
message_.instruction(0).result_id());
} else {
for (auto& inst : message_.instruction()) {
if (spv::Op(inst.opcode()) == spv::Op::OpLabel) {
transformation_context->GetFactManager()->AddFactBlockIsDead(
inst.result_id());
}
}
}
for (auto& instruction : message_.instruction()) {
switch (spv::Op(instruction.opcode())) {
case spv::Op::OpFunctionParameter:
if (ir_context->get_def_use_mgr()
->GetDef(instruction.result_type_id())
->opcode() == spv::Op::OpTypePointer) {
transformation_context->GetFactManager()
->AddFactValueOfPointeeIsIrrelevant(instruction.result_id());
}
break;
case spv::Op::OpVariable:
transformation_context->GetFactManager()
->AddFactValueOfPointeeIsIrrelevant(instruction.result_id());
break;
default:
break;
}
}
}
protobufs::Transformation TransformationAddFunction::ToMessage() const {
protobufs::Transformation result;
*result.mutable_add_function() = message_;
return result;
}
bool TransformationAddFunction::TryToAddFunction(
opt::IRContext* ir_context) const {
if (message_.instruction().empty()) {
return false;
}
auto function_begin = message_.instruction(0);
if (spv::Op(function_begin.opcode()) != spv::Op::OpFunction) {
return false;
}
std::unique_ptr<opt::Function> new_function = MakeUnique<opt::Function>(
InstructionFromMessage(ir_context, function_begin));
uint32_t instruction_index = 1;
const auto num_instructions =
static_cast<uint32_t>(message_.instruction().size());
while (instruction_index < num_instructions &&
spv::Op(message_.instruction(instruction_index).opcode()) ==
spv::Op::OpFunctionParameter) {
new_function->AddParameter(InstructionFromMessage(
ir_context, message_.instruction(instruction_index)));
instruction_index++;
}
if (instruction_index == num_instructions ||
spv::Op(message_.instruction(instruction_index).opcode()) !=
spv::Op::OpLabel) {
return false;
}
while (instruction_index < num_instructions &&
spv::Op(message_.instruction(instruction_index).opcode()) !=
spv::Op::OpFunctionEnd) {
assert(spv::Op(message_.instruction(instruction_index).opcode()) ==
spv::Op::OpLabel);
std::unique_ptr<opt::BasicBlock> block =
MakeUnique<opt::BasicBlock>(InstructionFromMessage(
ir_context, message_.instruction(instruction_index)));
instruction_index++;
while (instruction_index < num_instructions &&
spv::Op(message_.instruction(instruction_index).opcode()) !=
spv::Op::OpFunctionEnd &&
spv::Op(message_.instruction(instruction_index).opcode()) !=
spv::Op::OpLabel) {
block->AddInstruction(InstructionFromMessage(
ir_context, message_.instruction(instruction_index)));
instruction_index++;
}
new_function->AddBasicBlock(std::move(block));
}
if (instruction_index != num_instructions - 1 ||
spv::Op(message_.instruction(instruction_index).opcode()) !=
spv::Op::OpFunctionEnd) {
return false;
}
new_function->SetFunctionEnd(InstructionFromMessage(
ir_context, message_.instruction(instruction_index)));
ir_context->AddFunction(std::move(new_function));
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
return true;
}
bool TransformationAddFunction::TryToMakeFunctionLivesafe(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const {
assert(message_.is_livesafe() && "Precondition: is_livesafe must hold.");
opt::Function* added_function = nullptr;
for (auto& function : *ir_context->module()) {
if (function.result_id() == message_.instruction(0).result_id()) {
added_function = &function;
break;
}
}
assert(added_function && "The added function should have been found.");
if (!TryToAddLoopLimiters(ir_context, added_function)) {
return false;
}
for (auto& block : *added_function) {
for (auto& inst : block) {
switch (inst.opcode()) {
case spv::Op::OpKill:
case spv::Op::OpUnreachable:
if (!TryToTurnKillOrUnreachableIntoReturn(ir_context, added_function,
&inst)) {
return false;
}
break;
case spv::Op::OpAccessChain:
case spv::Op::OpInBoundsAccessChain:
if (!TryToClampAccessChainIndices(ir_context, &inst)) {
return false;
}
break;
case spv::Op::OpFunctionCall:
if (!transformation_context.GetFactManager()->FunctionIsLivesafe(
inst.GetSingleWordInOperand(0))) {
return false;
}
default:
break;
}
}
}
return true;
}
uint32_t TransformationAddFunction::GetBackEdgeBlockId(
opt::IRContext* ir_context, uint32_t loop_header_block_id) {
const auto* loop_header_block =
ir_context->cfg()->block(loop_header_block_id);
assert(loop_header_block && "|loop_header_block_id| is invalid");
for (auto pred : ir_context->cfg()->preds(loop_header_block_id)) {
if (ir_context->GetDominatorAnalysis(loop_header_block->GetParent())
->Dominates(loop_header_block_id, pred)) {
return pred;
}
}
return 0;
}
bool TransformationAddFunction::TryToAddLoopLimiters(
opt::IRContext* ir_context, opt::Function* added_function) const {
std::vector<opt::BasicBlock*> loop_headers;
for (auto& block : *added_function) {
if (block.IsLoopHeader()) {
loop_headers.push_back(&block);
}
}
if (loop_headers.empty()) {
return true;
}
auto loop_limit_constant_id_instr =
ir_context->get_def_use_mgr()->GetDef(message_.loop_limit_constant_id());
if (!loop_limit_constant_id_instr ||
loop_limit_constant_id_instr->opcode() != spv::Op::OpConstant) {
return false;
}
auto loop_limit_type = ir_context->get_def_use_mgr()->GetDef(
loop_limit_constant_id_instr->type_id());
if (loop_limit_type->opcode() != spv::Op::OpTypeInt ||
loop_limit_type->GetSingleWordInOperand(0) != 32) {
return false;
}
opt::analysis::Integer unsigned_int_type(32, false);
uint32_t unsigned_int_type_id =
ir_context->get_type_mgr()->GetId(&unsigned_int_type);
if (!unsigned_int_type_id) {
return false;
}
auto registered_unsigned_int_type =
ir_context->get_type_mgr()->GetRegisteredType(&unsigned_int_type);
opt::analysis::IntConstant zero(registered_unsigned_int_type->AsInteger(),
{0});
auto registered_zero = ir_context->get_constant_mgr()->FindConstant(&zero);
if (!registered_zero) {
return false;
}
uint32_t zero_id = ir_context->get_constant_mgr()
->GetDefiningInstruction(registered_zero)
->result_id();
opt::analysis::IntConstant one(registered_unsigned_int_type->AsInteger(),
{1});
auto registered_one = ir_context->get_constant_mgr()->FindConstant(&one);
if (!registered_one) {
return false;
}
uint32_t one_id = ir_context->get_constant_mgr()
->GetDefiningInstruction(registered_one)
->result_id();
opt::analysis::Pointer pointer_to_unsigned_int_type(
registered_unsigned_int_type, spv::StorageClass::Function);
uint32_t pointer_to_unsigned_int_type_id =
ir_context->get_type_mgr()->GetId(&pointer_to_unsigned_int_type);
if (!pointer_to_unsigned_int_type_id) {
return false;
}
opt::analysis::Bool bool_type;
uint32_t bool_type_id = ir_context->get_type_mgr()->GetId(&bool_type);
if (!bool_type_id) {
return false;
}
added_function->begin()->begin()->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpVariable, pointer_to_unsigned_int_type_id,
message_.loop_limiter_variable_id(),
opt::Instruction::OperandList({{SPV_OPERAND_TYPE_STORAGE_CLASS,
{uint32_t(spv::StorageClass::Function)}},
{SPV_OPERAND_TYPE_ID, {zero_id}}})));
fuzzerutil::UpdateModuleIdBound(ir_context,
message_.loop_limiter_variable_id());
for (auto loop_header : loop_headers) {
const auto back_edge_block_id =
GetBackEdgeBlockId(ir_context, loop_header->id());
if (!back_edge_block_id) {
continue;
}
if (!ir_context->GetDominatorAnalysis(added_function)
->Dominates(loop_header->id(), loop_header->MergeBlockId())) {
return false;
}
bool found = false;
protobufs::LoopLimiterInfo loop_limiter_info;
for (auto& info : message_.loop_limiter_info()) {
if (info.loop_header_id() == loop_header->id()) {
loop_limiter_info = info;
found = true;
break;
}
}
if (!found) {
return false;
}
auto back_edge_block = ir_context->cfg()->block(back_edge_block_id);
auto back_edge_block_terminator = back_edge_block->terminator();
bool compare_using_greater_than_equal;
if (back_edge_block_terminator->opcode() == spv::Op::OpBranch) {
compare_using_greater_than_equal = true;
} else {
assert(back_edge_block_terminator->opcode() ==
spv::Op::OpBranchConditional);
assert(((back_edge_block_terminator->GetSingleWordInOperand(1) ==
loop_header->id() &&
back_edge_block_terminator->GetSingleWordInOperand(2) ==
loop_header->MergeBlockId()) ||
(back_edge_block_terminator->GetSingleWordInOperand(2) ==
loop_header->id() &&
back_edge_block_terminator->GetSingleWordInOperand(1) ==
loop_header->MergeBlockId())) &&
"A back edge edge block must branch to"
" either the loop header or merge");
compare_using_greater_than_equal =
back_edge_block_terminator->GetSingleWordInOperand(1) ==
loop_header->MergeBlockId();
}
std::vector<std::unique_ptr<opt::Instruction>> new_instructions;
new_instructions.push_back(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpLoad, unsigned_int_type_id,
loop_limiter_info.load_id(),
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {message_.loop_limiter_variable_id()}}})));
new_instructions.push_back(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpIAdd, unsigned_int_type_id,
loop_limiter_info.increment_id(),
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {loop_limiter_info.load_id()}},
{SPV_OPERAND_TYPE_ID, {one_id}}})));
new_instructions.push_back(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpStore, 0, 0,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {message_.loop_limiter_variable_id()}},
{SPV_OPERAND_TYPE_ID, {loop_limiter_info.increment_id()}}})));
new_instructions.push_back(MakeUnique<opt::Instruction>(
ir_context,
compare_using_greater_than_equal ? spv::Op::OpUGreaterThanEqual
: spv::Op::OpULessThan,
bool_type_id, loop_limiter_info.compare_id(),
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {loop_limiter_info.load_id()}},
{SPV_OPERAND_TYPE_ID, {message_.loop_limit_constant_id()}}})));
if (back_edge_block_terminator->opcode() == spv::Op::OpBranchConditional) {
new_instructions.push_back(MakeUnique<opt::Instruction>(
ir_context,
compare_using_greater_than_equal ? spv::Op::OpLogicalOr
: spv::Op::OpLogicalAnd,
bool_type_id, loop_limiter_info.logical_op_id(),
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID,
{back_edge_block_terminator->GetSingleWordInOperand(0)}},
{SPV_OPERAND_TYPE_ID, {loop_limiter_info.compare_id()}}})));
}
if (back_edge_block->GetLoopMergeInst()) {
back_edge_block->GetLoopMergeInst()->InsertBefore(
std::move(new_instructions));
} else {
back_edge_block_terminator->InsertBefore(std::move(new_instructions));
}
if (back_edge_block_terminator->opcode() == spv::Op::OpBranchConditional) {
back_edge_block_terminator->SetInOperand(
0, {loop_limiter_info.logical_op_id()});
} else {
assert(back_edge_block_terminator->opcode() == spv::Op::OpBranch &&
"Back-edge terminator must be OpBranch or OpBranchConditional");
auto merge_block = ir_context->cfg()->block(loop_header->MergeBlockId());
if (!fuzzerutil::PhiIdsOkForNewEdge(ir_context, back_edge_block,
merge_block,
loop_limiter_info.phi_id())) {
return false;
}
uint32_t phi_index = 0;
for (auto& inst : *merge_block) {
if (inst.opcode() != spv::Op::OpPhi) {
break;
}
assert(phi_index <
static_cast<uint32_t>(loop_limiter_info.phi_id().size()) &&
"There should be at least one phi id per OpPhi instruction.");
inst.AddOperand(
{SPV_OPERAND_TYPE_ID, {loop_limiter_info.phi_id(phi_index)}});
inst.AddOperand({SPV_OPERAND_TYPE_ID, {back_edge_block_id}});
phi_index++;
}
back_edge_block_terminator->SetOpcode(spv::Op::OpBranchConditional);
back_edge_block_terminator->SetInOperands(opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {loop_limiter_info.compare_id()}},
{SPV_OPERAND_TYPE_ID, {loop_header->MergeBlockId()}},
{SPV_OPERAND_TYPE_ID, {loop_header->id()}}}));
}
fuzzerutil::UpdateModuleIdBound(ir_context, loop_limiter_info.load_id());
fuzzerutil::UpdateModuleIdBound(ir_context,
loop_limiter_info.increment_id());
fuzzerutil::UpdateModuleIdBound(ir_context, loop_limiter_info.compare_id());
fuzzerutil::UpdateModuleIdBound(ir_context,
loop_limiter_info.logical_op_id());
}
return true;
}
bool TransformationAddFunction::TryToTurnKillOrUnreachableIntoReturn(
opt::IRContext* ir_context, opt::Function* added_function,
opt::Instruction* kill_or_unreachable_inst) const {
assert((kill_or_unreachable_inst->opcode() == spv::Op::OpKill ||
kill_or_unreachable_inst->opcode() == spv::Op::OpUnreachable) &&
"Precondition: instruction must be OpKill or OpUnreachable.");
auto function_return_type_inst =
ir_context->get_def_use_mgr()->GetDef(added_function->type_id());
if (function_return_type_inst->opcode() == spv::Op::OpTypeVoid) {
kill_or_unreachable_inst->SetOpcode(spv::Op::OpReturn);
} else {
if (ir_context->get_def_use_mgr()
->GetDef(message_.kill_unreachable_return_value_id())
->type_id() != function_return_type_inst->result_id()) {
return false;
}
kill_or_unreachable_inst->SetOpcode(spv::Op::OpReturnValue);
kill_or_unreachable_inst->SetInOperands(
{{SPV_OPERAND_TYPE_ID, {message_.kill_unreachable_return_value_id()}}});
}
return true;
}
bool TransformationAddFunction::TryToClampAccessChainIndices(
opt::IRContext* ir_context, opt::Instruction* access_chain_inst) const {
assert((access_chain_inst->opcode() == spv::Op::OpAccessChain ||
access_chain_inst->opcode() == spv::Op::OpInBoundsAccessChain) &&
"Precondition: instruction must be OpAccessChain or "
"OpInBoundsAccessChain.");
const protobufs::AccessChainClampingInfo* access_chain_clamping_info =
nullptr;
for (auto& clamping_info : message_.access_chain_clamping_info()) {
if (clamping_info.access_chain_id() == access_chain_inst->result_id()) {
access_chain_clamping_info = &clamping_info;
break;
}
}
if (!access_chain_clamping_info) {
return false;
}
if (static_cast<uint32_t>(
access_chain_clamping_info->compare_and_select_ids().size()) !=
access_chain_inst->NumInOperands() - 1) {
return false;
}
auto base_object = ir_context->get_def_use_mgr()->GetDef(
access_chain_inst->GetSingleWordInOperand(0));
assert(base_object && "The base object must exist.");
auto pointer_type =
ir_context->get_def_use_mgr()->GetDef(base_object->type_id());
assert(pointer_type && pointer_type->opcode() == spv::Op::OpTypePointer &&
"The base object must have pointer type.");
auto should_be_composite_type = ir_context->get_def_use_mgr()->GetDef(
pointer_type->GetSingleWordInOperand(1));
for (uint32_t index = 1; index < access_chain_inst->NumInOperands();
index++) {
uint32_t bound = fuzzerutil::GetBoundForCompositeIndex(
*should_be_composite_type, ir_context);
const uint32_t index_id = access_chain_inst->GetSingleWordInOperand(index);
auto index_inst = ir_context->get_def_use_mgr()->GetDef(index_id);
auto index_type_inst =
ir_context->get_def_use_mgr()->GetDef(index_inst->type_id());
assert(index_type_inst->opcode() == spv::Op::OpTypeInt);
assert(index_type_inst->GetSingleWordInOperand(0) == 32);
opt::analysis::Integer* index_int_type =
ir_context->get_type_mgr()
->GetType(index_type_inst->result_id())
->AsInteger();
if (index_inst->opcode() != spv::Op::OpConstant ||
index_inst->GetSingleWordInOperand(0) >= bound) {
assert(should_be_composite_type->opcode() != spv::Op::OpTypeStruct &&
"Access chain indices into structures are required to be "
"constants.");
opt::analysis::IntConstant bound_minus_one(index_int_type, {bound - 1});
if (!ir_context->get_constant_mgr()->FindConstant(&bound_minus_one)) {
return false;
}
opt::analysis::Bool bool_type;
uint32_t bool_type_id = ir_context->get_type_mgr()->GetId(&bool_type);
if (!bool_type_id) {
return false;
}
uint32_t bound_minus_one_id =
ir_context->get_constant_mgr()
->GetDefiningInstruction(&bound_minus_one)
->result_id();
uint32_t compare_id =
access_chain_clamping_info->compare_and_select_ids(index - 1).first();
uint32_t select_id =
access_chain_clamping_info->compare_and_select_ids(index - 1)
.second();
std::vector<std::unique_ptr<opt::Instruction>> new_instructions;
new_instructions.push_back(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpULessThanEqual, bool_type_id, compare_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {index_inst->result_id()}},
{SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
new_instructions.push_back(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpSelect, index_type_inst->result_id(),
select_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {compare_id}},
{SPV_OPERAND_TYPE_ID, {index_inst->result_id()}},
{SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
access_chain_inst->InsertBefore(std::move(new_instructions));
access_chain_inst->SetInOperand(index, {select_id});
fuzzerutil::UpdateModuleIdBound(ir_context, compare_id);
fuzzerutil::UpdateModuleIdBound(ir_context, select_id);
}
should_be_composite_type =
FollowCompositeIndex(ir_context, *should_be_composite_type, index_id);
}
return true;
}
opt::Instruction* TransformationAddFunction::FollowCompositeIndex(
opt::IRContext* ir_context, const opt::Instruction& composite_type_inst,
uint32_t index_id) {
uint32_t sub_object_type_id;
switch (composite_type_inst.opcode()) {
case spv::Op::OpTypeArray:
case spv::Op::OpTypeRuntimeArray:
sub_object_type_id = composite_type_inst.GetSingleWordInOperand(0);
break;
case spv::Op::OpTypeMatrix:
case spv::Op::OpTypeVector:
sub_object_type_id = composite_type_inst.GetSingleWordInOperand(0);
break;
case spv::Op::OpTypeStruct: {
auto index_inst = ir_context->get_def_use_mgr()->GetDef(index_id);
assert(index_inst->opcode() == spv::Op::OpConstant);
assert(ir_context->get_def_use_mgr()
->GetDef(index_inst->type_id())
->opcode() == spv::Op::OpTypeInt);
assert(ir_context->get_def_use_mgr()
->GetDef(index_inst->type_id())
->GetSingleWordInOperand(0) == 32);
uint32_t index_value = index_inst->GetSingleWordInOperand(0);
sub_object_type_id =
composite_type_inst.GetSingleWordInOperand(index_value);
break;
}
default:
assert(false && "Unknown composite type.");
sub_object_type_id = 0;
break;
}
assert(sub_object_type_id && "No sub-object found.");
return ir_context->get_def_use_mgr()->GetDef(sub_object_type_id);
}
std::unordered_set<uint32_t> TransformationAddFunction::GetFreshIds() const {
std::unordered_set<uint32_t> result;
for (auto& instruction : message_.instruction()) {
result.insert(instruction.result_id());
}
if (message_.is_livesafe()) {
result.insert(message_.loop_limiter_variable_id());
for (auto& loop_limiter_info : message_.loop_limiter_info()) {
result.insert(loop_limiter_info.load_id());
result.insert(loop_limiter_info.increment_id());
result.insert(loop_limiter_info.compare_id());
result.insert(loop_limiter_info.logical_op_id());
}
for (auto& access_chain_clamping_info :
message_.access_chain_clamping_info()) {
for (auto& pair : access_chain_clamping_info.compare_and_select_ids()) {
result.insert(pair.first());
result.insert(pair.second());
}
}
}
return result;
}
}
}