| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721 |
- // Copyright (c) 2020 Google LLC
- //
- // 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_duplicate_region_with_selection.h"
- #include "source/fuzz/fuzzer_util.h"
- namespace spvtools {
- namespace fuzz {
- TransformationDuplicateRegionWithSelection::
- TransformationDuplicateRegionWithSelection(
- protobufs::TransformationDuplicateRegionWithSelection message)
- : message_(std::move(message)) {}
- TransformationDuplicateRegionWithSelection::
- TransformationDuplicateRegionWithSelection(
- uint32_t new_entry_fresh_id, uint32_t condition_id,
- uint32_t merge_label_fresh_id, uint32_t entry_block_id,
- uint32_t exit_block_id,
- const std::map<uint32_t, uint32_t>& original_label_to_duplicate_label,
- const std::map<uint32_t, uint32_t>& original_id_to_duplicate_id,
- const std::map<uint32_t, uint32_t>& original_id_to_phi_id) {
- message_.set_new_entry_fresh_id(new_entry_fresh_id);
- message_.set_condition_id(condition_id);
- message_.set_merge_label_fresh_id(merge_label_fresh_id);
- message_.set_entry_block_id(entry_block_id);
- message_.set_exit_block_id(exit_block_id);
- *message_.mutable_original_label_to_duplicate_label() =
- fuzzerutil::MapToRepeatedUInt32Pair(original_label_to_duplicate_label);
- *message_.mutable_original_id_to_duplicate_id() =
- fuzzerutil::MapToRepeatedUInt32Pair(original_id_to_duplicate_id);
- *message_.mutable_original_id_to_phi_id() =
- fuzzerutil::MapToRepeatedUInt32Pair(original_id_to_phi_id);
- }
- bool TransformationDuplicateRegionWithSelection::IsApplicable(
- opt::IRContext* ir_context,
- const TransformationContext& transformation_context) const {
- // Instruction with the id |condition_id| must exist and must be of a bool
- // type.
- auto bool_instr =
- ir_context->get_def_use_mgr()->GetDef(message_.condition_id());
- if (bool_instr == nullptr || !bool_instr->type_id()) {
- return false;
- }
- if (!ir_context->get_type_mgr()->GetType(bool_instr->type_id())->AsBool()) {
- return false;
- }
- // The |new_entry_fresh_id| must be fresh and distinct.
- std::set<uint32_t> ids_used_by_this_transformation;
- if (!CheckIdIsFreshAndNotUsedByThisTransformation(
- message_.new_entry_fresh_id(), ir_context,
- &ids_used_by_this_transformation)) {
- return false;
- }
- // The |merge_label_fresh_id| must be fresh and distinct.
- if (!CheckIdIsFreshAndNotUsedByThisTransformation(
- message_.merge_label_fresh_id(), ir_context,
- &ids_used_by_this_transformation)) {
- return false;
- }
- // The entry and exit block ids must refer to blocks.
- for (auto block_id : {message_.entry_block_id(), message_.exit_block_id()}) {
- auto block_label = ir_context->get_def_use_mgr()->GetDef(block_id);
- if (!block_label || block_label->opcode() != spv::Op::OpLabel) {
- return false;
- }
- }
- auto entry_block = ir_context->cfg()->block(message_.entry_block_id());
- auto exit_block = ir_context->cfg()->block(message_.exit_block_id());
- // The |entry_block| and the |exit_block| must be in the same function.
- if (entry_block->GetParent() != exit_block->GetParent()) {
- return false;
- }
- // The |entry_block| must dominate the |exit_block|.
- auto dominator_analysis =
- ir_context->GetDominatorAnalysis(entry_block->GetParent());
- if (!dominator_analysis->Dominates(entry_block, exit_block)) {
- return false;
- }
- // The |exit_block| must post-dominate the |entry_block|.
- auto postdominator_analysis =
- ir_context->GetPostDominatorAnalysis(entry_block->GetParent());
- if (!postdominator_analysis->Dominates(exit_block, entry_block)) {
- return false;
- }
- auto enclosing_function = entry_block->GetParent();
- // |entry_block| cannot be the first block of the |enclosing_function|.
- if (&*enclosing_function->begin() == entry_block) {
- return false;
- }
- // To make the process of resolving OpPhi instructions easier, we require that
- // the entry block has only one predecessor.
- auto entry_block_preds = ir_context->cfg()->preds(entry_block->id());
- std::sort(entry_block_preds.begin(), entry_block_preds.end());
- entry_block_preds.erase(
- std::unique(entry_block_preds.begin(), entry_block_preds.end()),
- entry_block_preds.end());
- if (entry_block_preds.size() > 1) {
- return false;
- }
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3785):
- // The following code has been copied from TransformationOutlineFunction.
- // Consider refactoring to avoid duplication.
- auto region_set = GetRegionBlocks(ir_context, entry_block, exit_block);
- // Check whether |region_set| really is a single-entry single-exit region, and
- // also check whether structured control flow constructs and their merge
- // and continue constructs are either wholly in or wholly out of the region -
- // e.g. avoid the situation where the region contains the head of a loop but
- // not the loop's continue construct.
- //
- // This is achieved by going through every block in the |enclosing_function|
- for (auto& block : *enclosing_function) {
- if (&block == exit_block) {
- // It is not OK for the exit block to head a loop construct or a
- // conditional construct.
- if (block.GetMergeInst()) {
- return false;
- }
- continue;
- }
- if (region_set.count(&block) != 0) {
- // The block is in the region and is not the region's exit block. Let's
- // see whether all of the block's successors are in the region. If they
- // are not, the region is not single-entry single-exit.
- bool all_successors_in_region = true;
- block.WhileEachSuccessorLabel([&all_successors_in_region, ir_context,
- ®ion_set](uint32_t successor) -> bool {
- if (region_set.count(ir_context->cfg()->block(successor)) == 0) {
- all_successors_in_region = false;
- return false;
- }
- return true;
- });
- if (!all_successors_in_region) {
- return false;
- }
- }
- if (auto merge = block.GetMergeInst()) {
- // The block is a loop or selection header. The header and its
- // associated merge block must be both in the region or both be
- // outside the region.
- auto merge_block =
- ir_context->cfg()->block(merge->GetSingleWordOperand(0));
- if (region_set.count(&block) != region_set.count(merge_block)) {
- return false;
- }
- }
- if (auto loop_merge = block.GetLoopMergeInst()) {
- // The continue target of a loop must be within the region if and only if
- // the header of the loop is.
- auto continue_target =
- ir_context->cfg()->block(loop_merge->GetSingleWordOperand(1));
- // The continue target is a single-entry, single-exit region. Therefore,
- // if the continue target is the exit block, the region might not contain
- // the loop header. However, we would like to exclude this situation,
- // since it would be impossible for the modified exit block to branch to
- // the new selection merge block. In this scenario the exit block is
- // required to branch to the loop header.
- if (region_set.count(&block) != region_set.count(continue_target)) {
- return false;
- }
- }
- }
- // Get the maps from the protobuf.
- std::map<uint32_t, uint32_t> original_label_to_duplicate_label =
- fuzzerutil::RepeatedUInt32PairToMap(
- message_.original_label_to_duplicate_label());
- std::map<uint32_t, uint32_t> original_id_to_duplicate_id =
- fuzzerutil::RepeatedUInt32PairToMap(
- message_.original_id_to_duplicate_id());
- std::map<uint32_t, uint32_t> original_id_to_phi_id =
- fuzzerutil::RepeatedUInt32PairToMap(message_.original_id_to_phi_id());
- for (auto block : region_set) {
- // The label of every block in the region must be present in the map
- // |original_label_to_duplicate_label|, unless overflow ids are present.
- if (original_label_to_duplicate_label.count(block->id()) == 0) {
- if (!transformation_context.GetOverflowIdSource()->HasOverflowIds()) {
- return false;
- }
- } else {
- auto duplicate_label = original_label_to_duplicate_label.at(block->id());
- // Each id assigned to labels in the region must be distinct and fresh.
- if (!duplicate_label ||
- !CheckIdIsFreshAndNotUsedByThisTransformation(
- duplicate_label, ir_context, &ids_used_by_this_transformation)) {
- return false;
- }
- }
- for (auto& instr : *block) {
- if (!instr.HasResultId()) {
- continue;
- }
- // Every instruction with a result id in the region must be present in the
- // map |original_id_to_duplicate_id|, unless overflow ids are present.
- if (original_id_to_duplicate_id.count(instr.result_id()) == 0) {
- if (!transformation_context.GetOverflowIdSource()->HasOverflowIds()) {
- return false;
- }
- } else {
- auto duplicate_id = original_id_to_duplicate_id.at(instr.result_id());
- // Id assigned to this result id in the region must be distinct and
- // fresh.
- if (!duplicate_id ||
- !CheckIdIsFreshAndNotUsedByThisTransformation(
- duplicate_id, ir_context, &ids_used_by_this_transformation)) {
- return false;
- }
- }
- // If the instruction is available at the end of the region then we would
- // like to be able to add an OpPhi instruction at the merge point of the
- // duplicated region to capture the values computed by both duplicates of
- // the instruction, so that this is also available after the region. We
- // do this not just for instructions that are already used after the
- // region, but for all instructions so that the phi is available to future
- // transformations.
- if (AvailableAfterRegion(instr, exit_block, ir_context)) {
- if (!ValidOpPhiArgument(instr, ir_context)) {
- // The instruction cannot be used as an OpPhi argument. This is a
- // blocker if there are uses of the instruction after the region.
- // Otherwise we can simply avoid generating an OpPhi for this
- // instruction and its duplicate.
- if (!ir_context->get_def_use_mgr()->WhileEachUser(
- &instr,
- [ir_context,
- ®ion_set](opt::Instruction* use_instr) -> bool {
- opt::BasicBlock* use_block =
- ir_context->get_instr_block(use_instr);
- return use_block == nullptr ||
- region_set.count(use_block) > 0;
- })) {
- return false;
- }
- } else {
- // Every instruction with a result id available at the end of the
- // region must be present in the map |original_id_to_phi_id|, unless
- // overflow ids are present.
- if (original_id_to_phi_id.count(instr.result_id()) == 0) {
- if (!transformation_context.GetOverflowIdSource()
- ->HasOverflowIds()) {
- return false;
- }
- } else {
- auto phi_id = original_id_to_phi_id.at(instr.result_id());
- // Id assigned to this result id in the region must be distinct and
- // fresh.
- if (!phi_id ||
- !CheckIdIsFreshAndNotUsedByThisTransformation(
- phi_id, ir_context, &ids_used_by_this_transformation)) {
- return false;
- }
- }
- }
- }
- }
- }
- return true;
- }
- void TransformationDuplicateRegionWithSelection::Apply(
- opt::IRContext* ir_context,
- TransformationContext* transformation_context) const {
- fuzzerutil::UpdateModuleIdBound(ir_context, message_.new_entry_fresh_id());
- fuzzerutil::UpdateModuleIdBound(ir_context, message_.merge_label_fresh_id());
- // Create the new entry block containing the main conditional instruction. Set
- // its parent to the parent of the original entry block, since it is located
- // in the same function.
- std::unique_ptr<opt::BasicBlock> new_entry_block =
- MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
- ir_context, spv::Op::OpLabel, 0, message_.new_entry_fresh_id(),
- opt::Instruction::OperandList()));
- auto entry_block = ir_context->cfg()->block(message_.entry_block_id());
- auto enclosing_function = entry_block->GetParent();
- auto exit_block = ir_context->cfg()->block(message_.exit_block_id());
- // Get the blocks contained in the region.
- std::set<opt::BasicBlock*> region_blocks =
- GetRegionBlocks(ir_context, entry_block, exit_block);
- // Construct the merge block.
- std::unique_ptr<opt::BasicBlock> merge_block =
- MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
- ir_context, spv::Op::OpLabel, 0, message_.merge_label_fresh_id(),
- opt::Instruction::OperandList()));
- // Get the maps from the protobuf.
- std::map<uint32_t, uint32_t> original_label_to_duplicate_label =
- fuzzerutil::RepeatedUInt32PairToMap(
- message_.original_label_to_duplicate_label());
- std::map<uint32_t, uint32_t> original_id_to_duplicate_id =
- fuzzerutil::RepeatedUInt32PairToMap(
- message_.original_id_to_duplicate_id());
- std::map<uint32_t, uint32_t> original_id_to_phi_id =
- fuzzerutil::RepeatedUInt32PairToMap(message_.original_id_to_phi_id());
- // Use overflow ids to fill in any required ids that are missing from these
- // maps.
- for (auto block : region_blocks) {
- if (original_label_to_duplicate_label.count(block->id()) == 0) {
- original_label_to_duplicate_label.insert(
- {block->id(),
- transformation_context->GetOverflowIdSource()->GetNextOverflowId()});
- }
- for (auto& instr : *block) {
- if (!instr.HasResultId()) {
- continue;
- }
- if (original_id_to_duplicate_id.count(instr.result_id()) == 0) {
- original_id_to_duplicate_id.insert(
- {instr.result_id(), transformation_context->GetOverflowIdSource()
- ->GetNextOverflowId()});
- }
- if (AvailableAfterRegion(instr, exit_block, ir_context) &&
- ValidOpPhiArgument(instr, ir_context)) {
- if (original_id_to_phi_id.count(instr.result_id()) == 0) {
- original_id_to_phi_id.insert(
- {instr.result_id(), transformation_context->GetOverflowIdSource()
- ->GetNextOverflowId()});
- }
- }
- }
- }
- // Before adding duplicate blocks, we need to update the OpPhi instructions in
- // the successors of the |exit_block|. We know that the execution of the
- // transformed region will end in |merge_block|. Hence, we need to change all
- // occurrences of the label id of the |exit_block| to the label id of the
- // |merge_block|.
- exit_block->ForEachSuccessorLabel([this, ir_context](uint32_t label_id) {
- auto block = ir_context->cfg()->block(label_id);
- for (auto& instr : *block) {
- if (instr.opcode() == spv::Op::OpPhi) {
- instr.ForEachId([this](uint32_t* id) {
- if (*id == message_.exit_block_id()) {
- *id = message_.merge_label_fresh_id();
- }
- });
- }
- }
- });
- // Get vector of predecessors id of |entry_block|. Remove any duplicate
- // values.
- auto entry_block_preds = ir_context->cfg()->preds(entry_block->id());
- std::sort(entry_block_preds.begin(), entry_block_preds.end());
- entry_block_preds.erase(
- unique(entry_block_preds.begin(), entry_block_preds.end()),
- entry_block_preds.end());
- // We know that |entry_block| has only one predecessor, since the region is
- // single-entry, single-exit and its constructs and their merge blocks must be
- // either wholly within or wholly outside of the region.
- assert(entry_block_preds.size() == 1 &&
- "The entry of the region to be duplicated can have only one "
- "predecessor.");
- uint32_t entry_block_pred_id =
- ir_context->get_instr_block(entry_block_preds[0])->id();
- // Update all the OpPhi instructions in the |entry_block|. Change every
- // occurrence of |entry_block_pred_id| to the id of |new_entry|, because we
- // will insert |new_entry| before |entry_block|.
- for (auto& instr : *entry_block) {
- if (instr.opcode() == spv::Op::OpPhi) {
- instr.ForEachId([this, entry_block_pred_id](uint32_t* id) {
- if (*id == entry_block_pred_id) {
- *id = message_.new_entry_fresh_id();
- }
- });
- }
- }
- // Duplication of blocks will invalidate iterators. Store all the blocks from
- // the enclosing function.
- std::vector<opt::BasicBlock*> blocks;
- for (auto& block : *enclosing_function) {
- blocks.push_back(&block);
- }
- opt::BasicBlock* previous_block = nullptr;
- opt::BasicBlock* duplicated_exit_block = nullptr;
- // Iterate over all blocks of the function to duplicate blocks of the original
- // region and their instructions.
- for (auto& block : blocks) {
- // The block must be contained in the region.
- if (region_blocks.count(block) == 0) {
- continue;
- }
- fuzzerutil::UpdateModuleIdBound(
- ir_context, original_label_to_duplicate_label.at(block->id()));
- std::unique_ptr<opt::BasicBlock> duplicated_block =
- MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
- ir_context, spv::Op::OpLabel, 0,
- original_label_to_duplicate_label.at(block->id()),
- opt::Instruction::OperandList()));
- for (auto& instr : *block) {
- // Case where an instruction is the terminator of the exit block is
- // handled separately.
- if (block == exit_block && instr.IsBlockTerminator()) {
- switch (instr.opcode()) {
- case spv::Op::OpBranch:
- case spv::Op::OpBranchConditional:
- case spv::Op::OpReturn:
- case spv::Op::OpReturnValue:
- case spv::Op::OpUnreachable:
- case spv::Op::OpKill:
- continue;
- default:
- assert(false &&
- "Unexpected terminator for |exit_block| of the region.");
- }
- }
- // Duplicate the instruction.
- auto cloned_instr = instr.Clone(ir_context);
- duplicated_block->AddInstruction(
- std::unique_ptr<opt::Instruction>(cloned_instr));
- if (instr.HasResultId()) {
- fuzzerutil::UpdateModuleIdBound(
- ir_context, original_id_to_duplicate_id.at(instr.result_id()));
- }
- // If an id from the original region was used in this instruction,
- // replace it with the value from |original_id_to_duplicate_id|.
- // If a label from the original region was used in this instruction,
- // replace it with the value from |original_label_to_duplicate_label|.
- cloned_instr->ForEachId(
- [original_id_to_duplicate_id,
- original_label_to_duplicate_label](uint32_t* op) {
- if (original_id_to_duplicate_id.count(*op) != 0) {
- *op = original_id_to_duplicate_id.at(*op);
- } else if (original_label_to_duplicate_label.count(*op) != 0) {
- *op = original_label_to_duplicate_label.at(*op);
- }
- });
- }
- // If the block is the first duplicated block, insert it after the exit
- // block of the original region. Otherwise, insert it after the preceding
- // one.
- auto duplicated_block_ptr = duplicated_block.get();
- if (previous_block) {
- enclosing_function->InsertBasicBlockAfter(std::move(duplicated_block),
- previous_block);
- } else {
- enclosing_function->InsertBasicBlockAfter(std::move(duplicated_block),
- exit_block);
- }
- previous_block = duplicated_block_ptr;
- if (block == exit_block) {
- // After execution of the loop, this variable stores a pointer to the last
- // duplicated block.
- duplicated_exit_block = duplicated_block_ptr;
- }
- }
- for (auto& block : region_blocks) {
- for (auto& instr : *block) {
- if (instr.result_id() == 0) {
- continue;
- }
- if (AvailableAfterRegion(instr, exit_block, ir_context) &&
- ValidOpPhiArgument(instr, ir_context)) {
- // Add an OpPhi instruction for every result id that is available at
- // the end of the region, as long as the result id is valid for use
- // with OpPhi.
- merge_block->AddInstruction(MakeUnique<opt::Instruction>(
- ir_context, spv::Op::OpPhi, instr.type_id(),
- original_id_to_phi_id.at(instr.result_id()),
- opt::Instruction::OperandList({
- {SPV_OPERAND_TYPE_ID, {instr.result_id()}},
- {SPV_OPERAND_TYPE_ID, {exit_block->id()}},
- {SPV_OPERAND_TYPE_ID,
- {original_id_to_duplicate_id.at(instr.result_id())}},
- {SPV_OPERAND_TYPE_ID, {duplicated_exit_block->id()}},
- })));
- fuzzerutil::UpdateModuleIdBound(
- ir_context, original_id_to_phi_id.at(instr.result_id()));
- // If the instruction has been remapped by an OpPhi, look
- // for all its uses outside of the region and outside of the
- // merge block (to not overwrite just added instructions in
- // the merge block) and replace the original instruction id
- // with the id of the corresponding OpPhi instruction.
- ir_context->get_def_use_mgr()->ForEachUse(
- &instr,
- [ir_context, &instr, region_blocks, original_id_to_phi_id,
- &merge_block](opt::Instruction* user, uint32_t operand_index) {
- auto user_block = ir_context->get_instr_block(user);
- if ((region_blocks.find(user_block) != region_blocks.end()) ||
- user_block == merge_block.get()) {
- return;
- }
- user->SetOperand(operand_index,
- {original_id_to_phi_id.at(instr.result_id())});
- });
- }
- }
- }
- // Construct a conditional instruction in the |new_entry_block|.
- // If the condition is true, the execution proceeds in the
- // |entry_block| of the original region. If the condition is
- // false, the execution proceeds in the first block of the
- // duplicated region.
- new_entry_block->AddInstruction(MakeUnique<opt::Instruction>(
- ir_context, spv::Op::OpSelectionMerge, 0, 0,
- opt::Instruction::OperandList(
- {{SPV_OPERAND_TYPE_ID, {message_.merge_label_fresh_id()}},
- {SPV_OPERAND_TYPE_SELECTION_CONTROL,
- {uint32_t(spv::SelectionControlMask::MaskNone)}}})));
- new_entry_block->AddInstruction(MakeUnique<opt::Instruction>(
- ir_context, spv::Op::OpBranchConditional, 0, 0,
- opt::Instruction::OperandList(
- {{SPV_OPERAND_TYPE_ID, {message_.condition_id()}},
- {SPV_OPERAND_TYPE_ID, {message_.entry_block_id()}},
- {SPV_OPERAND_TYPE_ID,
- {original_label_to_duplicate_label.at(
- message_.entry_block_id())}}})));
- // Move the terminator of |exit_block| to the end of
- // |merge_block|.
- auto exit_block_terminator = exit_block->terminator();
- auto cloned_instr = exit_block_terminator->Clone(ir_context);
- merge_block->AddInstruction(std::unique_ptr<opt::Instruction>(cloned_instr));
- ir_context->KillInst(exit_block_terminator);
- // Add OpBranch instruction to the merge block at the end of
- // |exit_block| and at the end of |duplicated_exit_block|, so that
- // the execution proceeds in the |merge_block|.
- opt::Instruction merge_branch_instr = opt::Instruction(
- ir_context, spv::Op::OpBranch, 0, 0,
- opt::Instruction::OperandList(
- {{SPV_OPERAND_TYPE_ID, {message_.merge_label_fresh_id()}}}));
- exit_block->AddInstruction(MakeUnique<opt::Instruction>(merge_branch_instr));
- duplicated_exit_block->AddInstruction(
- std::unique_ptr<opt::Instruction>(merge_branch_instr.Clone(ir_context)));
- // Execution needs to start in the |new_entry_block|. Change all
- // the uses of |entry_block_label_instr| outside of the original
- // region to |message_.new_entry_fresh_id|.
- auto entry_block_label_instr =
- ir_context->get_def_use_mgr()->GetDef(message_.entry_block_id());
- ir_context->get_def_use_mgr()->ForEachUse(
- entry_block_label_instr,
- [this, ir_context, region_blocks](opt::Instruction* user,
- uint32_t operand_index) {
- auto user_block = ir_context->get_instr_block(user);
- if ((region_blocks.count(user_block) != 0)) {
- return;
- }
- switch (user->opcode()) {
- case spv::Op::OpSwitch:
- case spv::Op::OpBranch:
- case spv::Op::OpBranchConditional:
- case spv::Op::OpLoopMerge:
- case spv::Op::OpSelectionMerge: {
- user->SetOperand(operand_index, {message_.new_entry_fresh_id()});
- } break;
- case spv::Op::OpName:
- break;
- default:
- assert(false &&
- "The label id cannot be used by instructions "
- "other than "
- "OpSwitch, OpBranch, OpBranchConditional, "
- "OpLoopMerge, "
- "OpSelectionMerge");
- }
- });
- opt::Instruction* merge_block_terminator = merge_block->terminator();
- switch (merge_block_terminator->opcode()) {
- case spv::Op::OpReturnValue:
- case spv::Op::OpBranchConditional: {
- uint32_t operand = merge_block_terminator->GetSingleWordInOperand(0);
- if (original_id_to_phi_id.count(operand)) {
- merge_block_terminator->SetInOperand(
- 0, {original_id_to_phi_id.at(operand)});
- }
- break;
- }
- default:
- break;
- }
- // Insert the merge block after the |duplicated_exit_block| (the
- // last duplicated block).
- enclosing_function->InsertBasicBlockAfter(std::move(merge_block),
- duplicated_exit_block);
- // Insert the |new_entry_block| before the entry block of the
- // original region.
- enclosing_function->InsertBasicBlockBefore(std::move(new_entry_block),
- entry_block);
- // Since we have changed the module, most of the analysis are now
- // invalid. We can invalidate analyses now after all of the blocks
- // have been registered.
- ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
- }
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3785):
- // The following method has been copied from
- // TransformationOutlineFunction. Consider refactoring to avoid
- // duplication.
- std::set<opt::BasicBlock*>
- TransformationDuplicateRegionWithSelection::GetRegionBlocks(
- opt::IRContext* ir_context, opt::BasicBlock* entry_block,
- opt::BasicBlock* exit_block) {
- auto enclosing_function = entry_block->GetParent();
- auto dominator_analysis =
- ir_context->GetDominatorAnalysis(enclosing_function);
- auto postdominator_analysis =
- ir_context->GetPostDominatorAnalysis(enclosing_function);
- // A block belongs to a region between the entry block and the exit
- // block if and only if it is dominated by the entry block and
- // post-dominated by the exit block.
- std::set<opt::BasicBlock*> result;
- for (auto& block : *enclosing_function) {
- if (dominator_analysis->Dominates(entry_block, &block) &&
- postdominator_analysis->Dominates(exit_block, &block)) {
- result.insert(&block);
- }
- }
- return result;
- }
- protobufs::Transformation
- TransformationDuplicateRegionWithSelection::ToMessage() const {
- protobufs::Transformation result;
- *result.mutable_duplicate_region_with_selection() = message_;
- return result;
- }
- std::unordered_set<uint32_t>
- TransformationDuplicateRegionWithSelection::GetFreshIds() const {
- std::unordered_set<uint32_t> result = {message_.new_entry_fresh_id(),
- message_.merge_label_fresh_id()};
- for (auto& pair : message_.original_label_to_duplicate_label()) {
- result.insert(pair.second());
- }
- for (auto& pair : message_.original_id_to_duplicate_id()) {
- result.insert(pair.second());
- }
- for (auto& pair : message_.original_id_to_phi_id()) {
- result.insert(pair.second());
- }
- return result;
- }
- bool TransformationDuplicateRegionWithSelection::AvailableAfterRegion(
- const opt::Instruction& instr, opt::BasicBlock* exit_block,
- opt::IRContext* ir_context) {
- opt::Instruction* final_instruction_in_region = &*exit_block->tail();
- return &instr == final_instruction_in_region ||
- fuzzerutil::IdIsAvailableBeforeInstruction(
- ir_context, final_instruction_in_region, instr.result_id());
- }
- bool TransformationDuplicateRegionWithSelection::ValidOpPhiArgument(
- const opt::Instruction& instr, opt::IRContext* ir_context) {
- opt::Instruction* instr_type =
- ir_context->get_def_use_mgr()->GetDef(instr.type_id());
- // It is invalid to apply OpPhi to void-typed values.
- if (instr_type->opcode() == spv::Op::OpTypeVoid) {
- return false;
- }
- // Using pointers with OpPhi requires capability VariablePointers.
- if (instr_type->opcode() == spv::Op::OpTypePointer &&
- !ir_context->get_feature_mgr()->HasCapability(
- spv::Capability::VariablePointers)) {
- return false;
- }
- // OpTypeSampledImage cannot be the result type of an OpPhi instruction.
- if (instr_type->opcode() == spv::Op::OpTypeSampledImage) {
- return false;
- }
- return true;
- }
- } // namespace fuzz
- } // namespace spvtools
|