blob: 88c0af31617cc92444029901c364a2d40242f074 [file] [edit]
// 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);
}
}
}
}
}
}