123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- #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 == ~0u || 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;
- }
|