#include "transformation_add_loop_preheader.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/opt/instruction.h"
namespace spvtools {
namespace fuzz {
TransformationAddLoopPreheader::TransformationAddLoopPreheader(
protobufs::TransformationAddLoopPreheader message)
: message_(std::move(message)) {}
TransformationAddLoopPreheader::TransformationAddLoopPreheader(
uint32_t loop_header_block, uint32_t fresh_id,
std::vector<uint32_t> phi_id) {
message_.set_loop_header_block(loop_header_block);
message_.set_fresh_id(fresh_id);
for (auto id : phi_id) {
message_.add_phi_id(id);
}
}
bool TransformationAddLoopPreheader::IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& ) const {
opt::BasicBlock* loop_header_block =
fuzzerutil::MaybeFindBlock(ir_context, message_.loop_header_block());
if (!loop_header_block || !loop_header_block->IsLoopHeader()) {
return false;
}
std::set<uint32_t> used_ids;
if (!CheckIdIsFreshAndNotUsedByThisTransformation(message_.fresh_id(),
ir_context, &used_ids)) {
return false;
}
size_t num_predecessors =
ir_context->cfg()->preds(message_.loop_header_block()).size();
if (num_predecessors < 2) {
return false;
}
if (num_predecessors == 2) {
return true;
}
int32_t num_phi_insts = 0;
loop_header_block->ForEachPhiInst(
[&num_phi_insts](opt::Instruction* ) { num_phi_insts++; });
if (num_phi_insts > message_.phi_id_size()) {
return false;
}
for (int32_t i = 0; i < num_phi_insts; i++) {
if (!CheckIdIsFreshAndNotUsedByThisTransformation(message_.phi_id(i),
ir_context, &used_ids)) {
return false;
}
}
return true;
}
void TransformationAddLoopPreheader::Apply(
opt::IRContext* ir_context,
TransformationContext* ) const {
opt::BasicBlock* loop_header =
fuzzerutil::MaybeFindBlock(ir_context, message_.loop_header_block());
auto dominator_analysis =
ir_context->GetDominatorAnalysis(loop_header->GetParent());
uint32_t back_edge_block_id = 0;
ir_context->get_def_use_mgr()->ForEachUse(
loop_header->id(),
[this, &ir_context, &dominator_analysis, &loop_header,
&back_edge_block_id](opt::Instruction* use_inst, uint32_t use_index) {
if (dominator_analysis->Dominates(loop_header->GetLabelInst(),
use_inst)) {
if (use_inst->IsBranch()) {
assert(back_edge_block_id == 0 &&
"There should only be one back-edge block");
back_edge_block_id = ir_context->get_instr_block(use_inst)->id();
}
return;
}
if (!use_inst->IsBranch() &&
use_inst->opcode() != spv::Op::OpSelectionMerge &&
use_inst->opcode() != spv::Op::OpLoopMerge) {
return;
}
use_inst->SetOperand(use_index, {message_.fresh_id()});
});
assert(back_edge_block_id && "The back-edge block should have been found");
std::unique_ptr<opt::BasicBlock> preheader = MakeUnique<opt::BasicBlock>(
std::unique_ptr<opt::Instruction>(new opt::Instruction(
ir_context, spv::Op::OpLabel, 0, message_.fresh_id(), {})));
uint32_t phi_ids_used = 0;
loop_header->ForEachPhiInst([this, &ir_context, &preheader,
&back_edge_block_id,
&phi_ids_used](opt::Instruction* phi_inst) {
assert(phi_inst->NumInOperands() >= 4);
if (phi_inst->NumInOperands() == 4) {
uint32_t index_of_out_of_loop_pred_id =
phi_inst->GetInOperand(1).words[0] == back_edge_block_id ? 3 : 1;
phi_inst->SetInOperand(index_of_out_of_loop_pred_id, {preheader->id()});
} else {
std::vector<opt::Operand> preheader_in_operands;
uint32_t back_edge_val = 0;
for (uint32_t i = 0; i < phi_inst->NumInOperands(); i += 2) {
if (phi_inst->GetInOperand(i + 1).words[0] == back_edge_block_id) {
back_edge_val = phi_inst->GetInOperand(i).words[0];
} else {
preheader_in_operands.push_back(std::move(phi_inst->GetInOperand(i)));
preheader_in_operands.push_back(
std::move(phi_inst->GetInOperand(i + 1)));
}
}
uint32_t fresh_phi_id = message_.phi_id(phi_ids_used++);
fuzzerutil::UpdateModuleIdBound(ir_context, fresh_phi_id);
preheader->AddInstruction(std::unique_ptr<opt::Instruction>(
new opt::Instruction(ir_context, spv::Op::OpPhi, phi_inst->type_id(),
fresh_phi_id, preheader_in_operands)));
phi_inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {fresh_phi_id}},
{SPV_OPERAND_TYPE_ID, {preheader->id()}},
{SPV_OPERAND_TYPE_ID, {back_edge_val}},
{SPV_OPERAND_TYPE_ID, {back_edge_block_id}}});
}
});
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
preheader->AddInstruction(
std::unique_ptr<opt::Instruction>(new opt::Instruction(
ir_context, spv::Op::OpBranch, 0, 0,
std::initializer_list<opt::Operand>{opt::Operand(
spv_operand_type_t::SPV_OPERAND_TYPE_ID, {loop_header->id()})})));
loop_header->GetParent()->InsertBasicBlockBefore(std::move(preheader),
loop_header);
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
}
protobufs::Transformation TransformationAddLoopPreheader::ToMessage() const {
protobufs::Transformation result;
*result.mutable_add_loop_preheader() = message_;
return result;
}
std::unordered_set<uint32_t> TransformationAddLoopPreheader::GetFreshIds()
const {
std::unordered_set<uint32_t> result = {message_.fresh_id()};
for (auto id : message_.phi_id()) {
result.insert(id);
}
return result;
}
}
}