| 89 | } |
| 90 | |
| 91 | void TransformationSplitBlock::Apply( |
| 92 | opt::IRContext* ir_context, |
| 93 | TransformationContext* transformation_context) const { |
| 94 | opt::Instruction* instruction_to_split_before = |
| 95 | FindInstruction(message_.instruction_to_split_before(), ir_context); |
| 96 | opt::BasicBlock* block_to_split = |
| 97 | ir_context->get_instr_block(instruction_to_split_before); |
| 98 | auto split_before = fuzzerutil::GetIteratorForInstruction( |
| 99 | block_to_split, instruction_to_split_before); |
| 100 | assert(split_before != block_to_split->end() && |
| 101 | "If the transformation is applicable, we should have an " |
| 102 | "instruction to split on."); |
| 103 | |
| 104 | // We need to make sure the module's id bound is large enough to add the |
| 105 | // fresh id. |
| 106 | fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); |
| 107 | // Split the block. |
| 108 | auto new_bb = block_to_split->SplitBasicBlock(ir_context, message_.fresh_id(), |
| 109 | split_before); |
| 110 | // The split does not automatically add a branch between the two parts of |
| 111 | // the original block, so we add one. |
| 112 | auto branch_instruction = MakeUnique<opt::Instruction>( |
| 113 | ir_context, spv::Op::OpBranch, 0, 0, |
| 114 | std::initializer_list<opt::Operand>{opt::Operand( |
| 115 | spv_operand_type_t::SPV_OPERAND_TYPE_ID, {message_.fresh_id()})}); |
| 116 | auto branch_instruction_ptr = branch_instruction.get(); |
| 117 | block_to_split->AddInstruction(std::move(branch_instruction)); |
| 118 | |
| 119 | // Inform the def-use manager about the branch instruction, and record its |
| 120 | // block. |
| 121 | ir_context->get_def_use_mgr()->AnalyzeInstDefUse(branch_instruction_ptr); |
| 122 | ir_context->set_instr_block(branch_instruction_ptr, block_to_split); |
| 123 | |
| 124 | // If we split before OpPhi instructions, we need to update their |
| 125 | // predecessor operand so that the block they used to be inside is now the |
| 126 | // predecessor. |
| 127 | new_bb->ForEachPhiInst([block_to_split, |
| 128 | ir_context](opt::Instruction* phi_inst) { |
| 129 | assert( |
| 130 | phi_inst->NumInOperands() == 2 && |
| 131 | "Precondition: a block can only be split before an OpPhi if the block" |
| 132 | "has exactly one predecessor."); |
| 133 | phi_inst->SetInOperand(1, {block_to_split->id()}); |
| 134 | ir_context->UpdateDefUse(phi_inst); |
| 135 | }); |
| 136 | |
| 137 | // We have updated the def-use manager and the instruction to block mapping, |
| 138 | // but other analyses (especially control flow-related ones) need to be |
| 139 | // recomputed. |
| 140 | ir_context->InvalidateAnalysesExceptFor( |
| 141 | opt::IRContext::Analysis::kAnalysisDefUse | |
| 142 | opt::IRContext::Analysis::kAnalysisInstrToBlockMapping); |
| 143 | |
| 144 | // If the block being split was dead, the new block arising from the split is |
| 145 | // also dead. |
| 146 | if (transformation_context->GetFactManager()->BlockIsDead( |
| 147 | block_to_split->id())) { |
| 148 | transformation_context->GetFactManager()->AddFactBlockIsDead( |
nothing calls this directly
no test coverage detected