357 строки
9.1 KiB
C++
357 строки
9.1 KiB
C++
#include "Reducibility.h"
|
|
|
|
#include "llvm/ADT/SetVector.h"
|
|
#include "llvm/IR/BasicBlock.h"
|
|
#include "llvm/IR/CFG.h"
|
|
#include "llvm/IR/Function.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/IR/LegacyPassManager.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "llvm/Transforms/Utils/Cloning.h"
|
|
#include "llvm/Transforms/Scalar.h"
|
|
|
|
#include "LLVMUtils.h"
|
|
|
|
#include <fstream>
|
|
#include <vector>
|
|
#include <map>
|
|
|
|
#define DBGS errs
|
|
//#define DBGS dbgs
|
|
|
|
using namespace llvm;
|
|
|
|
struct Node
|
|
{
|
|
SetVector<Node*> in;
|
|
SetVector<Node*> out;
|
|
SetVector<BasicBlock*> blocks; // block 0 dominates all others in this node
|
|
size_t numInstructions = 0;
|
|
|
|
Node() {}
|
|
Node(BasicBlock* B) { insert(B); }
|
|
|
|
void insert(BasicBlock* B)
|
|
{
|
|
numInstructions += B->size();
|
|
blocks.insert(B);
|
|
}
|
|
};
|
|
|
|
|
|
static void printDotGraph(const std::vector<Node*> nodes, const std::string& filename)
|
|
{
|
|
DBGS() << "Writing " << filename << " ...";
|
|
std::ofstream out(filename);
|
|
if (!out)
|
|
{
|
|
DBGS() << "FAILED\n";
|
|
return;
|
|
}
|
|
|
|
// Give nodes a numerical index to make the output cleaner
|
|
std::map<Node*, int> idxMap;
|
|
for (size_t i = 0; i < nodes.size(); ++i)
|
|
idxMap[nodes[i]] = i;
|
|
|
|
|
|
// Error check - make sure that all the out/in nodes are in the map
|
|
for (Node* N : nodes)
|
|
{
|
|
for (Node* P : N->in)
|
|
{
|
|
if (idxMap.find(P) == idxMap.end())
|
|
DBGS() << "MISSING INPUT NODE\n";
|
|
if (P->out.count(N) == 0)
|
|
DBGS() << "MISSING OUTGOING EDGE FROM PREDECESSOR.\n";
|
|
}
|
|
for (Node* S : N->out)
|
|
{
|
|
if (idxMap.find(S) == idxMap.end())
|
|
DBGS() << "MISSING OUTPUT NODE\n";
|
|
if (S->in.count(N) == 0)
|
|
DBGS() << "MISSING INCOMING EDGE FROM SUCCESSOR.\n";
|
|
}
|
|
}
|
|
|
|
|
|
// Print header
|
|
out << "digraph g {\n";
|
|
out << "node [\n";
|
|
out << " fontsize = \"12\"\n";
|
|
out << " labeljust = \"l\"\n";
|
|
out << "]\n";
|
|
|
|
for (unsigned i = 0; i < nodes.size(); ++i)
|
|
{
|
|
Node* N = nodes[i];
|
|
|
|
// node
|
|
out << " N" << i << " [shape=record,label=\"";
|
|
for (BasicBlock* B : N->blocks)
|
|
out << B->getName().str() << "\\n";
|
|
out << "\"];\n";
|
|
|
|
// out edges
|
|
for (Node* S : N->out)
|
|
out << " N" << i << " -> N" << idxMap[S] << ";\n";
|
|
|
|
// in edges
|
|
//for( Node* P : N->in )
|
|
// out << " N" << idxMap[P] << " -> N" << i << " [style=dotted];\n";
|
|
}
|
|
|
|
out << "}\n";
|
|
|
|
DBGS() << "\n";
|
|
}
|
|
|
|
static void printDotGraph(const std::vector<Node*> nodes, Function* F, int step)
|
|
{
|
|
printDotGraph(nodes, ("red." + F->getName() + "_" + std::to_string(step) + ".dot").str());
|
|
}
|
|
|
|
|
|
static Node* split(Node* N, std::map<BasicBlock*, Node*>& bbToNode, bool firstSplit)
|
|
{
|
|
// Remove one predecessor P from N
|
|
assert(N->in.size() > 1);
|
|
Node* P = N->in.pop_back_val();
|
|
P->out.remove(N);
|
|
|
|
// Point P to the clone of N, Np
|
|
Node* Np = new Node();
|
|
P->out.insert(Np);
|
|
Np->in.insert(P);
|
|
|
|
// Copy successors of N to Np
|
|
for (Node* S : N->out)
|
|
{
|
|
Np->out.insert(S);
|
|
S->in.insert(Np);
|
|
}
|
|
|
|
#if 1
|
|
// Run reg2mem on the whole function so we don't have to deal with phis
|
|
if (firstSplit)
|
|
{
|
|
runPasses(N->blocks[0]->getParent(), {
|
|
createDemoteRegisterToMemoryPass()
|
|
});
|
|
}
|
|
|
|
|
|
// Clone N into Np
|
|
ValueToValueMapTy VMap;
|
|
for (BasicBlock* B : N->blocks)
|
|
{
|
|
BasicBlock* Bp = CloneBasicBlock(B, VMap, ".c", B->getParent());
|
|
Np->insert(Bp);
|
|
VMap[B] = Bp;
|
|
}
|
|
for (BasicBlock* B : Np->blocks)
|
|
for (Instruction& I : *B)
|
|
RemapInstruction(&I, VMap, RF_NoModuleLevelChanges | RF_IgnoreMissingEntries);
|
|
|
|
// Remap terminators of P from N to Np
|
|
for (BasicBlock* B : P->blocks)
|
|
RemapInstruction(B->getTerminator(), VMap, RF_NoModuleLevelChanges | RF_IgnoreMissingEntries);
|
|
|
|
#else
|
|
// Clone N into Np
|
|
ValueToValueMapTy VMap;
|
|
for (BasicBlock* B : N->blocks)
|
|
{
|
|
BasicBlock* Bp = CloneBasicBlock(B, VMap, ".c", B->getParent());
|
|
Np->insert(Bp);
|
|
VMap[B] = Bp;
|
|
}
|
|
for (BasicBlock* B : Np->blocks)
|
|
for (Instruction& I : *B)
|
|
RemapInstruction(&I, VMap, RF_NoModuleLevelChanges | RF_IgnoreMissingEntries);
|
|
|
|
|
|
// Remove incoming values from phis in Np that don't come from actual predecessors
|
|
BasicBlock* NpEntry = Np->blocks[0];
|
|
std::set<BasicBlock*> predSet(pred_begin(NpEntry), pred_end(NpEntry));
|
|
auto I = NpEntry->begin();
|
|
while (PHINode* phi = dyn_cast<PHINode>(I++))
|
|
{
|
|
if (phi->getNumIncomingValues() == predSet.size())
|
|
continue;
|
|
for (unsigned i = 0; i < phi->getNumIncomingValues(); )
|
|
{
|
|
BasicBlock* B = phi->getIncomingBlock(i);
|
|
if (!predSet.count(B))
|
|
{
|
|
phi->removeIncomingValue(B);
|
|
continue;
|
|
}
|
|
++i;
|
|
}
|
|
}
|
|
|
|
|
|
// Remove phi references to P in N. (Do this before remapping terminators.)
|
|
BasicBlock* Nentry = N->blocks[0];
|
|
for (BasicBlock* PB : predecessors(Nentry))
|
|
{
|
|
if (P->blocks.count(PB))
|
|
Nentry->removePredecessor(PB);
|
|
}
|
|
|
|
// Remap terminators of P from N to Np
|
|
for (BasicBlock* B : P->blocks)
|
|
RemapInstruction(B->getTerminator(), VMap, RF_NoModuleLevelChanges | RF_IgnoreMissingEntries);
|
|
|
|
|
|
// Update phis in successors of Np.
|
|
// There are several cases for a value Vs reaching S. Vs may be defined in N and
|
|
// a clone Vsp in Np or only passing through one or the other. Furthermore, Vs may
|
|
// either appear in a phi in the entry block of S or not.
|
|
// 1) Vs defined in N (and clone Vsp in Np) and in phi:
|
|
// Add incoming value [Vsp, Bp] for cloned value Vsp from predecessor basic
|
|
// block Bp in Np wherever [Vs, B] appears
|
|
// 2) Vs defined in N (and clone Vsp in Np) and not in phi:
|
|
// Add phi [Vs,B],[Vsp,Bp] if Vs reaches a use in or through S
|
|
// 3) Vs passing through N or Np and in phi
|
|
// Change [Vs,B] to [Vs,Bp] in phis in S if Vs reached S through P
|
|
// 4) Vs passing through N or Np and not in a phi
|
|
// Do nothing
|
|
//
|
|
// TODO: Only 1) is implemented below and it isn't checking for definition in N
|
|
for (Node* S : Np->out)
|
|
{
|
|
BasicBlock* Sentry = S->blocks[0];
|
|
auto I = Sentry->begin();
|
|
while (PHINode* phi = dyn_cast<PHINode>(I++))
|
|
{
|
|
for (unsigned i = 0; i < phi->getNumIncomingValues(); ++i)
|
|
{
|
|
BasicBlock* B = phi->getIncomingBlock(i);
|
|
if (N->blocks.count(B))
|
|
{
|
|
Value* V = phi->getIncomingValue(i);
|
|
Value* Vp = VMap[V];
|
|
if (!Vp)
|
|
Vp = V; // Def not in N
|
|
BasicBlock* Bp = dyn_cast<BasicBlock>(VMap[B]);
|
|
phi->addIncoming(Vp, Bp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return Np;
|
|
}
|
|
|
|
// Returns the number of splits
|
|
int makeReducible(Function* F)
|
|
{
|
|
// Break critical edges now in case we need to do mem2reg in split(). mem2reg
|
|
// will break critical edges and the CFG needs to remain unchanged.
|
|
runPasses(F, {
|
|
createBreakCriticalEdgesPass()
|
|
});
|
|
|
|
// initialize nodes
|
|
std::vector<Node*> nodes;
|
|
std::map<BasicBlock*, Node*> bbToNode;
|
|
for (BasicBlock& B : *F)
|
|
{
|
|
nodes.push_back(new Node(&B));
|
|
bbToNode[&B] = nodes.back();
|
|
}
|
|
|
|
// initialize edges
|
|
for (Node* N : nodes)
|
|
{
|
|
for (BasicBlock* B : successors(N->blocks[0]))
|
|
{
|
|
Node* BN = bbToNode[B];
|
|
N->out.insert(BN);
|
|
BN->in.insert(N);
|
|
}
|
|
}
|
|
|
|
int step = 0;
|
|
bool print = false;
|
|
if (print) printDotGraph(nodes, F, step++);
|
|
|
|
int numSplits = 0;
|
|
while (!nodes.empty())
|
|
{
|
|
bool changed;
|
|
do
|
|
{
|
|
// It might more efficient to use a worklist based implementation instead
|
|
// of iterating over the vector.
|
|
changed = false;
|
|
for (size_t i = 0; i < nodes.size(); )
|
|
{
|
|
Node* N = nodes[i];
|
|
|
|
// Remove self references
|
|
if (N->in.count(N))
|
|
{
|
|
N->in.remove(N);
|
|
N->out.remove(N);
|
|
changed = true;
|
|
}
|
|
|
|
// Remove singletons
|
|
if (N->in.size() == 0 && N->out.size() == 0)
|
|
{
|
|
nodes.erase(nodes.begin() + i);
|
|
changed = true;
|
|
if (print) printDotGraph(nodes, F, step++);
|
|
continue;
|
|
}
|
|
|
|
// Remove nodes with only one incoming edge
|
|
if (N->in.size() == 1)
|
|
{
|
|
// fold into predecessor
|
|
Node* P = N->in.back();
|
|
P->blocks.insert(N->blocks.begin(), N->blocks.end());
|
|
P->out.remove(N);
|
|
for (Node* S : N->out)
|
|
{
|
|
S->in.remove(N);
|
|
P->out.insert(S);
|
|
S->in.insert(P);
|
|
}
|
|
P->numInstructions += N->numInstructions;
|
|
nodes.erase(nodes.begin() + i);
|
|
changed = true;
|
|
if (print) printDotGraph(nodes, F, step++);
|
|
continue;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
} while (changed);
|
|
|
|
if (!nodes.empty())
|
|
{
|
|
// Duplicate the smallest node with more than one incoming edge. Better
|
|
// methods exist for picking the node to split, e.g. "Making Graphs Reducible
|
|
// with Controlled Node Splitting" by Janssen and Corporaal.
|
|
size_t idxMin = ~0;
|
|
for (size_t i = 0; i < nodes.size(); ++i)
|
|
{
|
|
if (nodes[i]->in.size() <= 1)
|
|
continue;
|
|
|
|
if (idxMin == ~0 || nodes[i]->numInstructions < nodes[idxMin]->numInstructions)
|
|
idxMin = i;
|
|
}
|
|
nodes.push_back(split(nodes[idxMin], bbToNode, numSplits == 0));
|
|
numSplits++;
|
|
if (print) printDotGraph(nodes, F, step++);
|
|
}
|
|
}
|
|
return numSplits;
|
|
}
|