| // Copyright 2020 The Clspv Authors. All rights reserved. |
| // |
| // 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 "llvm/ADT/DenseSet.h" |
| #include "llvm/Analysis/LoopInfo.h" |
| #include "llvm/IR/Constants.h" |
| #include "llvm/Pass.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| #include "FixupStructuredCFGPass.h" |
| |
| using namespace llvm; |
| |
| PreservedAnalyses |
| clspv::FixupStructuredCFGPass::run(Function &F, FunctionAnalysisManager &FAM) { |
| // Assumes CFG has been structurized. |
| isolateContinue(F, FAM); |
| isolateConvergentLatch(F, FAM); |
| breakConditionalHeader(F, FAM); |
| |
| removeUndefPHI(F); |
| |
| PreservedAnalyses PA; |
| return PA; |
| } |
| |
| void clspv::FixupStructuredCFGPass::removeUndefPHI(Function &F) { |
| SmallVector<PHINode *> ToBeDeleted; |
| DenseMap<PHINode *, SmallVector<PHINode *>> dict; |
| for (auto &BB : F) { |
| for (auto &I : BB) { |
| if (auto phi = dyn_cast<PHINode>(&I)) { |
| if (!phi->getType()->isPointerTy()) { |
| continue; |
| } |
| bool phiIsUndef = true; |
| for (unsigned i = 0; i < phi->getNumIncomingValues(); i++) { |
| auto Val = phi->getIncomingValue(i); |
| if (auto phi2 = dyn_cast<PHINode>(Val)) { |
| for (unsigned j = 0; j < phi2->getNumIncomingValues(); j++) { |
| auto Val2 = phi2->getIncomingValue(i); |
| if (auto phi3 = dyn_cast<PHINode>(Val2)) { |
| if (phi3 != phi) { |
| phiIsUndef = false; |
| } |
| } else if (!isa<UndefValue>(Val2)) { |
| phiIsUndef = false; |
| } |
| } |
| } else if (!isa<UndefValue>(Val)) { |
| phiIsUndef = false; |
| } |
| } |
| if (!phiIsUndef) { |
| continue; |
| } |
| phi->replaceAllUsesWith(UndefValue::get(phi->getType())); |
| ToBeDeleted.push_back(phi); |
| } |
| } |
| } |
| for (auto phi : ToBeDeleted) { |
| phi->eraseFromParent(); |
| } |
| } |
| |
| void clspv::FixupStructuredCFGPass::isolateConvergentLatch( |
| Function &F, FunctionAnalysisManager &FAM) { |
| auto &LI = FAM.getResult<LoopAnalysis>(F); |
| |
| std::vector<BasicBlock *> blocks; |
| blocks.reserve(F.size()); |
| for (auto &BB : F) { |
| blocks.push_back(&BB); |
| } |
| |
| for (auto *BB : blocks) { |
| if (!LI.isLoopHeader(BB)) |
| continue; |
| |
| auto *loop = LI.getLoopFor(BB); |
| auto *latch = loop->getLoopLatch(); |
| // Skip single block loops. |
| if (!latch || latch == BB) { |
| continue; |
| } |
| |
| // Latch needs two predecessors. |
| if (!latch->hasNPredecessors(2)) { |
| continue; |
| } |
| |
| // Header is a conditional branch. |
| auto header_terminator = dyn_cast_or_null<CondBrInst>(BB->getTerminator()); |
| if (!header_terminator) { |
| continue; |
| } |
| |
| // One edge jumps to the continue target. |
| if (header_terminator->getSuccessor(0) != latch && |
| header_terminator->getSuccessor(1) != latch) { |
| continue; |
| } |
| |
| // The continue contains a convergent call. |
| bool has_convergent_call = false; |
| for (auto &inst : *latch) { |
| if (auto *call = dyn_cast<CallInst>(&inst)) { |
| if (call->hasFnAttr(Attribute::Convergent)) { |
| has_convergent_call = true; |
| break; |
| } |
| } |
| } |
| if (!has_convergent_call) { |
| continue; |
| } |
| |
| auto *latch_terminator = latch->getTerminator(); |
| |
| // Break the latch such that it is a single-entry single-exit block. |
| // This will force later transforms in this fixup to break the loop header |
| // which puts the whole loop body as a selection. |
| if (auto *cond_latch_terminator = |
| dyn_cast_or_null<CondBrInst>(latch_terminator)) { |
| // Safety valve: if this is not an exiting block then the loop is not |
| // structured as expected. |
| if (!loop->isLoopExiting(latch)) { |
| continue; |
| } |
| |
| // Conditional branch case: one edge back to header and one out of the |
| // loop. Transformed into one edge out of the loop and one edge to the new |
| // continue and thence to the header. |
| auto new_latch = |
| BasicBlock::Create(F.getContext(), "", &F, latch->getNextNode()); |
| UncondBrInst::Create(BB, new_latch); |
| loop->addBlockEntry(new_latch); |
| |
| const auto idx = cond_latch_terminator->getSuccessor(0) == BB ? 0 : 1; |
| cond_latch_terminator->setSuccessor(idx, new_latch); |
| |
| // Update phis to use the new basic block. |
| for (auto iter = BB->phis().begin(); iter != BB->phis().end(); iter++) { |
| PHINode *phi = cast<PHINode>(&*iter); |
| phi->replaceIncomingBlockWith(latch, new_latch); |
| } |
| } else if (auto *uncond_latch_terminator = |
| dyn_cast_or_null<UncondBrInst>(latch_terminator)) { |
| // Simple case: just split the block. |
| auto new_block = latch->splitBasicBlockBefore(uncond_latch_terminator); |
| loop->addBlockEntry(new_block); |
| } |
| } |
| } |
| |
| void clspv::FixupStructuredCFGPass::breakConditionalHeader( |
| Function &F, FunctionAnalysisManager &FAM) { |
| auto &LI = FAM.getResult<LoopAnalysis>(F); |
| |
| std::vector<BasicBlock *> blocks; |
| blocks.reserve(F.size()); |
| for (auto &BB : F) { |
| blocks.push_back(&BB); |
| } |
| |
| // Loop for loop headers that are terminated by a conditional branch with both |
| // edges entering the body of the loop. In such a case, split the header so |
| // that the conditional branch occurs in the body of the loop. |
| for (auto *BB : blocks) { |
| if (!LI.isLoopHeader(BB)) |
| continue; |
| |
| auto *terminator = dyn_cast_or_null<CondBrInst>(BB->getTerminator()); |
| if (!terminator) |
| continue; |
| |
| auto *loop = LI.getLoopFor(BB); |
| auto *latch = loop->getLoopLatch(); |
| auto *exit = loop->getUniqueExitBlock(); |
| |
| auto *succ1 = terminator->getSuccessor(0); |
| auto *succ2 = terminator->getSuccessor(1); |
| bool succ1_in_body = succ1 != latch && succ1 != exit; |
| bool succ2_in_body = succ2 != latch && succ2 != exit; |
| |
| if (succ1_in_body && succ2_in_body) { |
| auto new_block = BB->splitBasicBlockBefore(terminator); |
| loop->addBlockEntry(new_block); |
| } |
| } |
| } |
| |
| void clspv::FixupStructuredCFGPass::isolateContinue( |
| Function &F, FunctionAnalysisManager &FAM) { |
| auto &LI = FAM.getResult<LoopAnalysis>(F); |
| |
| SmallVector<Loop *, 16> loops; |
| for (auto loop : LI) { |
| loops.push_back(loop); |
| } |
| |
| DenseSet<Loop *> visited; |
| while (!loops.empty()) { |
| auto loop = loops.back(); |
| // Process subloops first. |
| if (!loop->getSubLoops().empty() && !visited.count(loop)) { |
| visited.insert(loop); |
| for (auto subloop : loop->getSubLoops()) { |
| loops.push_back(subloop); |
| } |
| continue; |
| } |
| |
| loops.pop_back(); |
| // Look for cases where the merge block (unique exit) of the inner loop is |
| // the same block as the outer loop's continue target (loop latch). |
| if (auto parent = loop->getParentLoop()) { |
| if (auto exit_block = loop->getUniqueExitBlock()) { |
| if (exit_block == parent->getLoopLatch()) { |
| // Create a new basic block to act as the merge of the loop. |
| auto new_exit = |
| BasicBlock::Create(F.getContext(), "", &F, exit_block); |
| UncondBrInst::Create(exit_block, new_exit); |
| parent->addBlockEntry(new_exit); |
| |
| // Collect the exit's predecessors from within the loop. |
| SmallVector<BasicBlock *, 4> loop_preds; |
| for (auto iter = pred_begin(exit_block); iter != pred_end(exit_block); |
| ++iter) { |
| if (loop->contains(*iter)) { |
| loop_preds.push_back(*iter); |
| } |
| } |
| // Split the phi nodes so that all predecessors from within the loop |
| // are part of a new phi in the new exit block. |
| for (auto iter = exit_block->begin(); |
| iter != exit_block->getFirstNonPHIIt(); ++iter) { |
| PHINode *phi = cast<PHINode>(&*iter); |
| SmallVector<Value *, 4> phi_values; |
| for (auto pred : loop_preds) { |
| auto inc = phi->getIncomingValueForBlock(&*pred); |
| if (inc) { |
| phi_values.push_back(inc); |
| } |
| } |
| assert(phi_values.size() == loop_preds.size()); |
| if (phi_values.size() == 1) { |
| // Special case: don't bother creating a single input phi. |
| // Instead, add the single value as an incoming value for the new |
| // exit. |
| phi->addIncoming(phi_values[0], new_exit); |
| } else if (!phi_values.empty()) { |
| auto new_phi = |
| PHINode::Create(phi->getType(), phi_values.size(), "", |
| new_exit->getTerminator()->getIterator()); |
| for (size_t i = 0; i < phi_values.size(); ++i) { |
| new_phi->addIncoming(phi_values[i], loop_preds[i]); |
| } |
| phi->addIncoming(new_phi, new_exit); |
| } |
| } |
| // Remove the loop predecessors from the old exit block. |
| for (auto pred : loop_preds) { |
| exit_block->removePredecessor(&*pred); |
| pred->getTerminator()->replaceUsesOfWith(exit_block, new_exit); |
| } |
| } |
| } |
| } |
| } |
| } |