#include "source/fuzz/transformation_add_dead_block.h"
#include "source/fuzz/fuzzer_util.h"
namespace spvtools {
namespace fuzz {
TransformationAddDeadBlock::TransformationAddDeadBlock(
protobufs::TransformationAddDeadBlock message)
: message_(std::move(message)) {}
TransformationAddDeadBlock::TransformationAddDeadBlock(uint32_t fresh_id,
uint32_t existing_block,
bool condition_value) {
message_.set_fresh_id(fresh_id);
message_.set_existing_block(existing_block);
message_.set_condition_value(condition_value);
}
bool TransformationAddDeadBlock::IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const {
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
return false;
}
if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
message_.condition_value(), false)) {
return false;
}
auto existing_block =
fuzzerutil::MaybeFindBlock(ir_context, message_.existing_block());
if (!existing_block) {
return false;
}
if (existing_block->IsLoopHeader()) {
return false;
}
if (existing_block->terminator()->opcode() != spv::Op::OpBranch) {
return false;
}
auto successor_block_id =
existing_block->terminator()->GetSingleWordInOperand(0);
if (fuzzerutil::IsMergeOrContinue(ir_context, successor_block_id)) {
return false;
}
if (ir_context->cfg()->block(successor_block_id)->IsLoopHeader()) {
return false;
}
if (!ir_context->IsReachable(*existing_block)) {
return false;
}
assert(existing_block->id() != successor_block_id &&
"|existing_block| must be different from |successor_block_id|");
opt::DominatorAnalysis* dominator_analysis =
ir_context->GetDominatorAnalysis(existing_block->GetParent());
if (!dominator_analysis->Dominates(existing_block->id(),
successor_block_id)) {
return false;
}
return true;
}
void TransformationAddDeadBlock::Apply(
opt::IRContext* ir_context,
TransformationContext* transformation_context) const {
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
auto existing_block = ir_context->cfg()->block(message_.existing_block());
auto successor_block_id =
existing_block->terminator()->GetSingleWordInOperand(0);
auto bool_id = fuzzerutil::MaybeGetBoolConstant(
ir_context, *transformation_context, message_.condition_value(), false);
auto enclosing_function = existing_block->GetParent();
std::unique_ptr<opt::BasicBlock> new_block =
MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpLabel, 0, message_.fresh_id(),
opt::Instruction::OperandList()));
new_block->AddInstruction(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpBranch, 0, 0,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {successor_block_id}}})));
existing_block->terminator()->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpSelectionMerge, 0, 0,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {successor_block_id}},
{SPV_OPERAND_TYPE_SELECTION_CONTROL,
{uint32_t(spv::SelectionControlMask::MaskNone)}}})));
existing_block->terminator()->SetOpcode(spv::Op::OpBranchConditional);
existing_block->terminator()->SetInOperands(
{{SPV_OPERAND_TYPE_ID, {bool_id}},
{SPV_OPERAND_TYPE_ID,
{message_.condition_value() ? successor_block_id
: message_.fresh_id()}},
{SPV_OPERAND_TYPE_ID,
{message_.condition_value() ? message_.fresh_id()
: successor_block_id}}});
enclosing_function->InsertBasicBlockAfter(std::move(new_block),
existing_block);
ir_context->cfg()
->block(successor_block_id)
->ForEachPhiInst([this](opt::Instruction* phi_inst) {
opt::Operand copy_of_existing_operand = phi_inst->GetInOperand(0);
phi_inst->AddOperand(std::move(copy_of_existing_operand));
phi_inst->AddOperand({SPV_OPERAND_TYPE_ID, {message_.fresh_id()}});
});
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
transformation_context->GetFactManager()->AddFactBlockIsDead(
message_.fresh_id());
}
protobufs::Transformation TransformationAddDeadBlock::ToMessage() const {
protobufs::Transformation result;
*result.mutable_add_dead_block() = message_;
return result;
}
std::unordered_set<uint32_t> TransformationAddDeadBlock::GetFreshIds() const {
return {message_.fresh_id()};
}
}
}