[llvm-commits] CVS: reopt/lib/LightWtProfiling/TraceToFunction.cpp
Brian Gaeke
gaeke at cs.uiuc.edu
Thu Aug 28 12:40:03 PDT 2003
Changes in directory reopt/lib/LightWtProfiling:
TraceToFunction.cpp added (r1.1)
---
Log message:
Initial checkin of TraceToFunction converter.
---
Diffs of the changes:
Index: reopt/lib/LightWtProfiling/TraceToFunction.cpp
diff -c /dev/null reopt/lib/LightWtProfiling/TraceToFunction.cpp:1.1
*** /dev/null Thu Aug 28 12:39:14 2003
--- reopt/lib/LightWtProfiling/TraceToFunction.cpp Thu Aug 28 12:39:03 2003
***************
*** 0 ****
--- 1,484 ----
+ //===- TraceToFunction.cpp - Convert traces to functions ---------*- C++ -*--=//
+ //
+ // Repackage traces as functions.
+ //
+ //===----------------------------------------------------------------------===//
+
+ #include "Trace.h"
+ #include "llvm/Transforms/Utils/Cloning.h"
+ #include "llvm/Pass.h"
+ #include "llvm/Module.h"
+ #include "llvm/DerivedTypes.h"
+ #include "llvm/iTerminators.h"
+ #include "llvm/iPHINode.h"
+ #include "llvm/iMemory.h"
+ #include "llvm/iOther.h"
+ #include "llvm/Constants.h"
+ #include "Support/StringExtras.h" // for utostr()
+ #include "Support/Debug.h" // for DEBUG()
+ #include <set>
+
+ typedef std::vector<const Type *> TypeVector;
+ typedef std::set<Value *> LiveVariableSet;
+ typedef std::map<Value *, unsigned int> ValueToIntMap;
+ typedef std::map<const Value *, Value *> ValueMap;
+ typedef std::map<BranchInst *, unsigned int> BranchNumberMap;
+
+ class TraceToFunction {
+ ValueToIntMap LiveInToParameterMap;
+ BranchNumberMap BranchNumber;
+ virtual TypeVector createFunctionArgTypeVector (PointerType *ST,
+ LiveVariableSet S);
+ virtual void fillInFunctionBody (Trace &T, Function *F, LiveVariableSet &So);
+ virtual void fixupFunctionBodyBB (Trace &T, Function *F, BasicBlock *srcB,
+ BasicBlock *dstB, ValueMap &O2CMap,
+ LiveVariableSet &So);
+ public:
+ TraceToFunction () { }
+ virtual ~TraceToFunction () { }
+ virtual Function *traceToFunction (Trace &T);
+ };
+
+ /// getTraceLiveInSet - Return the set of variables which are used in
+ /// the trace but which are not defined in the trace.
+ ///
+ static LiveVariableSet getTraceLiveInSet (Trace &T) {
+ LiveVariableSet S;
+ for (Trace::iterator TI = T.begin (), TE = T.end (); TI != TE; ++TI) {
+ BasicBlock *B = *TI;
+ // B is a basic block in the trace.
+ for (BasicBlock::iterator BI = B->begin (), BE = B->end (); BI != BE; ++BI){
+ Instruction &I = *BI;
+ // I is an instruction in B, which is in the trace.
+ for (unsigned i = 0; i < I.getNumOperands (); ++i) {
+ Value *V = I.getOperand (i);
+ // V is used in the trace by instruction I.
+ if (Instruction *VI = dyn_cast<Instruction> (V))
+ // V is defined by an instruction; not a constant or global.
+ if (!T.contains (VI->getParent ()))
+ // V is defined by an instruction outside the trace.
+ S.insert (V);
+ }
+ }
+ }
+ return S;
+ }
+
+ /// getTraceLiveOutSet - Return the set of variables which are defined
+ /// by an instruction inside the trace, and used in an instruction
+ /// outside the trace (but within that trace's function.)
+ ///
+ static LiveVariableSet getTraceLiveOutSet (Trace &T) {
+ LiveVariableSet S;
+ Function &F = *T.getFunction();
+ for (Function::iterator FI = F.begin(), FE = F.end(); FI != FE; ++FI) {
+ BasicBlock &B = *FI;
+ if (!T.contains (&B)) {
+ for (BasicBlock::iterator BI = B.begin(), BE = B.end(); BI != BE; ++BI) {
+ Instruction &I = *BI;
+ // I is an instruction in the trace's function, but outside the trace.
+ // Check its operands for uses of values defined in the trace.
+ for (unsigned i = 0; i < I.getNumOperands (); ++i) {
+ Value *V = I.getOperand (i);
+ // V is used outside the trace by instruction I.
+ if (Instruction *VI = dyn_cast<Instruction> (V))
+ // V is defined by an instruction; not a constant or global.
+ if (T.contains (VI->getParent ()))
+ // V is defined by an instruction inside the trace.
+ S.insert (V);
+ }
+ }
+ }
+ }
+ return S;
+ }
+
+ /// containsTraceExitingBranch - If B contains a branch instruction BI
+ /// that exits the trace T, returns BI. Returns NULL otherwise.
+ ///
+ static BranchInst *containsTraceExitingBranch (BasicBlock *B, Trace T) {
+ TerminatorInst *TI = B->getTerminator ();
+ assert (TI && "Only works on well-formed LLVM basic blocks w/ terminators");
+ if (BranchInst *BI = dyn_cast<BranchInst> (TI))
+ for (unsigned i = 0; i < BI->getNumSuccessors (); ++i) {
+ BasicBlock *BIT = BI->getSuccessor (i);
+ if (!T.contains (BIT))
+ return BI;
+ }
+ return NULL;
+ }
+
+ /// createLiveOutType - Given the live-out set S of a trace, create a
+ /// pointer-to-structure type that will encapsulate all the live-outs from S.
+ ///
+ static PointerType *createLiveOutType (LiveVariableSet S) {
+ TypeVector T;
+ // For each variable in S, make a new entry in T having its type.
+ for (LiveVariableSet::iterator I = S.begin (), E = S.end (); I != E; ++I) {
+ T.push_back ((*I)->getType ());
+ }
+ return PointerType::get (StructType::get (T));
+ }
+
+ /// createFunctionArgTypeVector - Given the live-in set S of a trace,
+ /// create a parameter list containing a parameter for each of the
+ /// variables in S as well as a pointer-to-structure type PST to fill
+ /// in which contains the live-outs. As a side effect, fill in the
+ /// mapping between live-ins and parameters in the global map named
+ /// LiveInToParameterMap.
+ ///
+ TypeVector TraceToFunction::createFunctionArgTypeVector (PointerType *PST,
+ LiveVariableSet S) {
+ TypeVector P;
+ P.push_back (PST);
+
+ LiveInToParameterMap.clear ();
+ for (LiveVariableSet::iterator I = S.begin (), E = S.end (); I != E; ++I) {
+ Value *V = *I;
+ P.push_back (V->getType ());
+ LiveInToParameterMap[V] = P.size () - 1;
+ }
+ // Print out mapping of instructions to arg numbers.
+ DEBUG(for (ValueToIntMap::iterator I = LiveInToParameterMap.begin (),
+ E = LiveInToParameterMap.end (); I != E; ++I)
+ std::cerr << I->first << " is parameter " << I->second << "\n");
+ return P;
+ }
+
+ /// giveNamesToFunctionArgs - Name the first argument of F "liveOut",
+ /// then use the ordering imposed by S's iterator to name the
+ /// remaining arguments of F.
+ ///
+ static void giveNamesToFunctionArgs (LiveVariableSet S, Function *F) {
+ Function::aiterator argIterator = F->abegin ();
+ unsigned argPosition = 0;
+
+ argIterator->setName ("liveOut"); // Arg 0 is always the live-out struct.
+ ++argPosition;
+ ++argIterator;
+
+ for (LiveVariableSet::iterator I = S.begin (), E = S.end (); I != E; ++I) {
+ std::string name ((*I)->getName ());
+ if (name == "")
+ name = "arg" + utostr (argPosition);
+ argIterator->setName ("liveIn." + name);
+ ++argPosition;
+ ++argIterator;
+ }
+ }
+
+ /// numberExitBranchesInTrace - Fill in M with pairs that map each
+ /// trace-exiting branch in T to a unique nonnegative integer.
+ ///
+ static void numberExitBranchesInTrace (Trace &T, BranchNumberMap &M) {
+ int Count = M.size ();
+ for (Trace::iterator I = T.begin (), E = T.end (); I != E; ++I) {
+ BasicBlock *B = *I;
+ BranchInst *BI = containsTraceExitingBranch (B, T);
+ if (BI)
+ M[BI] = Count++;
+ }
+ }
+
+ /// getFunctionArg - Return a pointer to F's ARGNOth argument.
+ ///
+ static Argument *getFunctionArg (Function *F, unsigned argno) {
+ Function::aiterator ai = F->abegin ();
+ while (argno) { ++ai; --argno; }
+ return &*ai;
+ }
+
+ /// cloneTraceBBsIntoFunction - Copy the BasicBlocks of the trace T into
+ /// the new Function F, which should be initially empty. Correspondences
+ /// between Original instructions (in T) and their Clones (in F) are added
+ /// to O2CMap.
+ ///
+ static void cloneTraceBBsIntoFunction (Trace &T, Function *F, ValueMap &O2CMap){
+ unsigned BBCount = 0;
+ // Clone each basic block into the new function.
+ for (Trace::iterator TI = T.begin (), TE = T.end (); TI != TE; ++TI) {
+ BasicBlock *srcB = *TI;
+ // Create a new BasicBlock dstB that corresponds to srcB in T.
+ BasicBlock *dstB = CloneBasicBlock (srcB, O2CMap, ".ttf");
+ DEBUG(dstB->setName ("trace" + utostr(BBCount++)));
+ // Add dstB to F.
+ F->getBasicBlockList ().push_back (dstB);
+ // Remember the correspondence between srcB and dstB.
+ // FIXME: Should this be something that CloneBasicBlock does?
+ O2CMap[srcB] = dstB;
+ }
+ }
+
+ /// fillInFunctionBody - Clone the BBs of T into F, then do all
+ /// necessary fixups to ensure that F's new contents are internally
+ /// consistent and that the live-outs So get stored in F's first
+ /// argument.
+ ///
+ void TraceToFunction::fillInFunctionBody (Trace &T, Function *F,
+ LiveVariableSet &So) {
+ ValueMap O2CMap;
+ // First copy the basic blocks from the trace.
+ cloneTraceBBsIntoFunction (T, F, O2CMap);
+
+ numberExitBranchesInTrace (T, BranchNumber);
+
+ // Fix up the cloned basic blocks of the function so that they are
+ // internally consistent.
+ for (Trace::iterator TI = T.begin (), TE = T.end (); TI != TE; ++TI) {
+ BasicBlock *srcB = *TI;
+ assert (O2CMap[srcB] && "Can't find clone of basic block I just cloned");
+ BasicBlock *dstB = dyn_cast<BasicBlock> (O2CMap[srcB]);
+ assert (dstB && "Clone of basic block I just cloned is not a basic block");
+ fixupFunctionBodyBB (T, F, srcB, dstB, O2CMap, So);
+ }
+ }
+
+ /// fixupFunctionBodyBB - Given srcB in T and its clone dstB in F, and
+ /// the map O2CMap detailing the correspondences between values in T
+ /// and values in F, fix up dstB so that its contents are internally
+ /// consistent, and so that it stores its live-out values So in F's first
+ /// argument. (This is where all the magic gets done...)
+ ///
+ void TraceToFunction::fixupFunctionBodyBB (Trace &T, Function *F,
+ BasicBlock *srcB, BasicBlock *dstB,
+ ValueMap &O2CMap,
+ LiveVariableSet &So) {
+ // Additional special handling for trace's entry basic block:
+ // The old entry BB's clone will start with a phi, one of whose args
+ // comes from off-trace (that's the trace entry point.) We can't
+ // leave that there because the off-trace source BB is now out of
+ // the function. So create new entry basic block to serve as source
+ // for old entry's initial phi node.
+ if (srcB == T.getEntryBasicBlock ()) {
+ BasicBlock *FEntry = new BasicBlock ("entryfixup", dstB);
+ FEntry->getInstList ().push_back (new BranchInst (dstB));
+ // Replace the references to the trace's predecessor with a
+ // reference to FEntry now. This is kind of a dodge because we
+ // don't have a pointer to the trace's predecessor, so we have
+ // to guess which one it is. Assume it's any phi value source in
+ // the entry BB which is not in the trace.
+ for (BasicBlock::iterator BI = srcB->begin ();
+ PHINode *PN = dyn_cast<PHINode> (BI); ++BI)
+ for (unsigned i = 0; i < PN->getNumIncomingValues (); ++i)
+ if (!T.contains (PN->getIncomingBlock (i))) {
+ // FIXME: Assert that O2CMap[PN]'s value i is live in.
+ Value *V = O2CMap[PN];
+ assert (V && "Can't find clone of Phi node from trace entry BB");
+ PHINode *PNinF = dyn_cast<PHINode> (V);
+ assert (PNinF
+ && "Clone of Phi node from entry BB is not a Phi node");
+ PNinF->setIncomingBlock (i, FEntry);
+ }
+ }
+
+ // If srcB contains a trace-exiting branch B, fix up B's clone in
+ // dstB to point to a new basic block in F that contains a return
+ // statement. Each return statement in F corresponds to a unique
+ // exit branch in T, and each return statement is numbered.
+ if (BranchInst *BI = containsTraceExitingBranch (srcB, T)) {
+ // Fix up this exit branch.
+ Value *V = O2CMap[BI];
+ assert(V && isa<BranchInst> (V)
+ && "Trace-exiting branch's clone is null, or not a branch?");
+ BranchInst *BIinF = cast<BranchInst> (V);
+ for (unsigned i = 0; i < BI->getNumSuccessors (); ++i) {
+ if (!T.contains (BI->getSuccessor (i))) {
+ // This is a trace-exiting destination of branch BI. Create a new
+ // basic block FB for its destination in the function.
+ std::string name ("exitfixup" + utostr (BranchNumber[BI]) + "_"
+ + utostr (i));
+ BasicBlock *FB = new BasicBlock (name, F);
+ // Change BI's clone's destination to FB.
+ BIinF->setSuccessor (i, FB);
+ // Add the getelementptr/store instruction pairs here that
+ // correspond to each live-out variable.
+ unsigned Slot = 0;
+ for (LiveVariableSet::iterator SI = So.begin (), SE = So.end ();
+ SI != SE; ++SI) {
+ std::vector<Value *> Index;
+ Index.push_back (Constant::getNullValue (Type::LongTy)); //long 0
+ Index.push_back (ConstantUInt::get (Type::UByteTy, Slot));//ubyte Slot
+ DEBUG(std::cerr << "About to make a GEP: " << getFunctionArg (F, 0));
+ for (std::vector<Value *>::iterator VI = Index.begin(),
+ VE = Index.end (); VI != VE; ++VI) {
+ DEBUG(std::cerr << ", " << **VI);
+ }
+ DEBUG(std::cerr << "\n");
+ GetElementPtrInst *GEP =
+ new GetElementPtrInst (getFunctionArg (F, 0), Index,
+ "liveOutP" + utostr (BranchNumber[BI])
+ + "_" + utostr (i) + "_" + utostr (Slot));
+ FB->getInstList ().push_back (GEP);
+ FB->getInstList ().push_back (new StoreInst (*SI, GEP));
+ ++Slot;
+ }
+ // Make FB contain a return instruction that returns the
+ // number of the taken exit-branch. Add it to the end of FB:
+ // (FIXME: Numbering the branches in this way is probably
+ // unnecessary except in DEBUG mode.)
+ FB->getInstList ().push_back
+ (new ReturnInst (ConstantUInt::get (Type::UIntTy,
+ BranchNumber[BI])));
+ } else {
+ // This is a non-trace-exiting destination of branch BI. Fix up
+ // its clone's destination to point to its successor's clone.
+ Value *V2 = O2CMap[BI->getSuccessor (i)];
+ DEBUG(if (!V2) {
+ std::cerr
+ << "I'm confused: saw non-trace-exiting dest. of branch "
+ << *BI << " whose clone is " << *BIinF
+ << " successor number is " << i << " and successor is "
+ << *BI->getSuccessor (i) << ", but where's its clone?\n";
+ assert(V2 && "Clone of basic block on trace is null?");
+ });
+ assert(isa<BasicBlock> (V2)
+ && "Clone of basic block on trace is not a basic block?");
+ BasicBlock *BIsuccInF = cast<BasicBlock> (V2);
+ BIinF->setSuccessor (i, BIsuccInF);
+ }
+ }
+ }
+
+ // Fix up operands of each instruction:
+ for (BasicBlock::iterator BI = dstB->begin (), BE = dstB->end ();
+ BI != BE; ++BI) {
+ Instruction &I = *BI;
+ for (unsigned i = 0; i < I.getNumOperands (); ++i) {
+ Value *V = I.getOperand (i);
+ // If the instruction I has an operand which is in the live-in
+ // set of the trace, then we must replace that operand with
+ // the corresponding argument of F. We can find out which
+ // operands to replace by looking them up in
+ // LiveInToParameterMap.
+ if (LiveInToParameterMap.find (V) != LiveInToParameterMap.end ()) {
+ DEBUG(std::cerr << *V << " in instruction " << I
+ << " is argument " << LiveInToParameterMap[V]
+ << " in new function\n");
+ assert (V->getType () ==
+ getFunctionArg (F, LiveInToParameterMap[V])->getType ()
+ && "Live-in Value's type doesn't match corresponding arg type");
+ I.setOperand(i, getFunctionArg (F, LiveInToParameterMap[V]));
+ }
+ // If the instruction I has an operand which is in the
+ // trace, that operand will have been cloned into the
+ // function, and I will still reference the version from
+ // outside the function. Replace any reference to an operand
+ // which has had a clone made with a reference to its clone.
+ else if (O2CMap.find (V) != O2CMap.end ()) {
+ DEBUG(std::cerr << *V << " in instruction " << I
+ << " is value " << O2CMap[V] << " in new function\n");
+ assert (V->getType () == O2CMap[V]->getType ()
+ && "Value's type doesn't match clone's type");
+ I.setOperand(i, O2CMap[V]);
+ }
+ }
+ // Make sure that our operand fixups did the Right Thing for
+ // branches.
+ DEBUG(if (BranchInst *BrI = dyn_cast<BranchInst> (&I)) {
+ for (unsigned i = 0; i < BrI->getNumSuccessors (); ++i) {
+ assert (BrI->getSuccessor (i)->getParent () == F
+ && "Branch out of function missed by copyTraceToFunction");
+ assert (!O2CMap[BrI->getSuccessor (i)]
+ && "Branch's clone found as key in original-->clone map; "
+ "no one told me today was opposite day!");
+ }
+ });
+ }
+
+ // Fix up Phi nodes:
+ for (BasicBlock::iterator BI = dstB->begin (), BE = dstB->end ();
+ BI != BE; ++BI) {
+ Instruction &I = *BI;
+ // In all cases, if a Phi node source in T was an on-trace basic
+ // block, then it will already have been fixed up to point to
+ // that block's clone, so we find off-trace sources by looking
+ // for source BBs which are not in F.
+ if (PHINode *PN = dyn_cast<PHINode> (&I)) {
+ unsigned onTraceSources = 0;
+ int lastSrcFound = -1;
+ for (unsigned i = 0; i < PN->getNumIncomingValues (); ++i)
+ if (PN->getIncomingBlock (i)->getParent () == F) {
+ lastSrcFound = i;
+ ++onTraceSources;
+ }
+ // Case 0. If it has 0 sources on the trace, that should really
+ // never happen.
+ assert (onTraceSources != 0
+ && "Phi node on trace has ALL its sources from off-trace!");
+ // Case 1. If it has 1 source S on the trace, replace its uses
+ // with S.
+ if (onTraceSources == 1) {
+ DEBUG(std::cerr << "Replacing Phi node" << *PN
+ << " with its lone on-trace input "
+ << *PN->getIncomingValue (lastSrcFound) << "\n");
+ PN->replaceAllUsesWith (PN->getIncomingValue (lastSrcFound));
+ dstB->getInstList ().erase (BI); // Delete the non-used Phi node
+ } else {
+ // Case N. If it has >1 source on the trace, just delete
+ // sources from the Phi node that are not on the trace.
+ int lastOffTraceSrcFound = -1;
+ do {
+ for (unsigned i = 0; i < PN->getNumIncomingValues (); ++i)
+ if (PN->getIncomingBlock (i)->getParent () != F) {
+ lastOffTraceSrcFound = i; // Found an off-trace source.
+ break;
+ }
+ if (lastOffTraceSrcFound != -1) { // Found one?
+ DEBUG(std::cerr << "Removing off-trace input "
+ << *PN->getIncomingValue (lastOffTraceSrcFound)
+ << " from Phi node " << *PN << "\n");
+ PN->removeIncomingValue (lastOffTraceSrcFound); // Delete it.
+ }
+ } while (lastOffTraceSrcFound != -1); // Continue until none found.
+ }
+ }
+ // Make sure that our Phi fixups did the Right Thing.
+ DEBUG(if (PHINode *PN = dyn_cast<PHINode> (&I))
+ for (unsigned i = 0; i < PN->getNumIncomingValues (); ++i)
+ assert (PN->getIncomingBlock (i)->getParent () == F &&
+ "Sorry, copyTraceIntoFunction mishandled a Phi node"));
+ }
+
+ // Remove calls to first-level instrumentation if we find them.
+ for (BasicBlock::iterator BI = dstB->begin (), BE = dstB->end ();
+ BI != BE; ++BI) {
+ Instruction &I = *BI;
+ if (CallInst *CI = dyn_cast<CallInst> (&I)) {
+ Function *CF = CI->getCalledFunction ();
+ if (CF->getName () == "llvm_first_trigger" && CF->isExternal ()) {
+ DEBUG(std::cerr << " (Found a call instruction " << *CI
+ << " ... Smells like llvm_first_trigger.)\n");
+ dstB->getInstList ().erase (BI);
+ }
+ }
+ }
+ }
+
+ Function *TraceToFunction::traceToFunction (Trace &T) {
+ std::string CurrentFnName = T.getFunction ()->getName ();
+ DEBUG(std::cerr << "In traceToFunction() for " << CurrentFnName << "\n");
+
+ // Get some information about the trace's relationship to its parent
+ // function.
+ LiveVariableSet Si = getTraceLiveInSet (T);
+ LiveVariableSet So = getTraceLiveOutSet (T);
+ TypeVector P = createFunctionArgTypeVector (createLiveOutType (So), Si);
+
+ // Make a new internal Function with return type int and parameter
+ // list P, in the same Module as the trace's parent function.
+ std::string name (CurrentFnName + ".trace");
+ Function *F = new Function (FunctionType::get (Type::UIntTy, P, false),
+ GlobalValue::InternalLinkage, name,
+ T.getModule ());
+ DEBUG(giveNamesToFunctionArgs (Si, F));
+ fillInFunctionBody (T, F, So);
+ return F;
+ }
+
+ /// runTraceToFunction - Entry point for TraceToFunction transformation.
+
+ Function *runTraceToFunction (Trace &T) {
+ TraceToFunction TTF;
+ return TTF.traceToFunction (T);
+ }
+
More information about the llvm-commits
mailing list