//===- Sample.cpp - Example code from "Writing an LLVM Pass" ---------------===//
//
//                     The LLVM Compiler Infrastructure
//


#include "llvm/Constants.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Instructions.h"
#include "llvm/Transforms/Instrumentation.h"
#include "llvm/Transforms/ProfilingUtils.h"  
//copied from lib/Transforms/Instrumentation to include/Transforms
#include "llvm/Assembly/Writer.h"
#include "llvm/Support/Debug.h"



#include "llvm/Pass.h"
#include "llvm/Function.h"
#include "llvm/Module.h"
#include "Samplefunctions.h"
#include <iostream>
#include <sstream>
using namespace llvm;

void InsertPrintCode(Value *v,std::string str,Function *Func,Instruction *InsertBefore,Module &M)
{
      std::ostringstream OutStr;
      if (v) WriteAsOperand(OutStr, v);
      Constant *Init = ConstantArray::get(str+OutStr.str()+"\n");
      GlobalVariable *GV = new GlobalVariable(Init->getType(), true,
                                          GlobalValue::InternalLinkage, Init,
                                          "");
      M.getGlobalList().push_back(GV);
      Constant *GEP=ConstantExpr::getGetElementPtr(GV,
                std::vector<Constant*>(2,Constant::getNullValue(Type::LongTy)));
      std::vector<Value*> PrintArgs;
      PrintArgs.push_back(GEP);
      if (v) {
    
      PrintArgs.push_back(v);
    
      }
      
     
      new CallInst(Func, PrintArgs, "", InsertBefore);
     
}
static void InsertInstrumentationCall (BasicBlock *BB,
                                       const std::string FnName) {
  DEBUG (std::cerr << "InsertInstrumentationCall (\"" << BB->getName ()
                   << "\", \"" << FnName <<  ")\n");
  Module &M = *BB->getParent ()->getParent ();
  Function *InstrFn = M.getOrInsertFunction (FnName, Type::VoidTy,NULL);
 
  // Insert the call after any alloca or PHI instructions...
  BasicBlock::iterator InsertPos = BB->begin();
  while (isa<AllocaInst>(InsertPos) || isa<PHINode>(InsertPos))
    ++InsertPos;
    
  std::vector<Value*> noargs;
  Instruction *InstrCall = new CallInst (InstrFn,"", InsertPos);
}



namespace {
  // FuncProf - The first implementation, without getAnalysisUsage.
  /*struct FuncProf : public FunctionPass {
    virtual bool runOnFunction(Function &F) {
    
    
      std::cerr << "Function Profiling : " << F.getName() << "\n";
      return false;
    }
  };
  RegisterOpt<FuncProf> X("myprofile", "Runtime Optimization Pass");*/

  // ModuleProf - The first implementation, without getAnalysisUsage.
   struct ModuleProf : public ModulePass {
    virtual bool runOnModule(Module &M) {
       std::cerr << "O.K..1\n";
      const Type *SBP=PointerType::get(Type::SByteTy);
      const FunctionType *MTy=FunctionType::get(Type::IntTy,std::vector<const Type*>(1,SBP),true);
      Function *PrintFunc=M.getOrInsertFunction("printf",MTy);
      
      unsigned i = 0;
      BasicBlock *BB;
      Instruction *InsertPos;
      ReturnInst *RetPos;
      const std::string &msgentry="Entering Function : ";
      const std::string &msgexit ="Leaving Function : ";
      const std::string &msgexit1 ="Leaving Function : %d ";
     
      GlobalVariable *Count = new GlobalVariable(Type::IntTy, false,
                                   GlobalValue::InternalLinkage, 0,
                                   "FunCtr");
      
      M.getGlobalList().push_back(Count);
     
       
      // Create the getelementptr constant expression
      std::vector<Value*> indices;
      indices.push_back(ConstantInt::get(Type::IntTy, 0));
      indices.push_back(ConstantInt::get(Type::IntTy, 0));
      
      GlobalValue *Counter=Count;
      std::cerr << "O.K..2\n";
      Constant *FnVar = ConstantExpr::getGetElementPtr(Counter, indices);
       std::cerr << "O.K..3\n";
           
      Value *Int0 = ConstantInt::get(Type::IntTy, 0);
      Value *Int1=ConstantSInt::get(Type::UIntTy,1);
     
      Instruction *rInst;
      for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F)
      {
        BB=&F->getEntryBlock();
        
        if (!F->isExternal())
         {
	   
	   InsertPos=BB->begin();
	   RetPos= cast<ReturnInst>(BB->getTerminator());   
	   //FunCtr++
	  
    	   Value *OldVal=new LoadInst(FnVar, "FunCtr", InsertPos);
           //BB->getInstList().push_back(OldVal);

           Value *NewVal=BinaryOperator::create(Instruction::Add, OldVal, Int1,"FunCtr", InsertPos);
           //BB->getInstList().push_back(NewVal);
	   
          
	   Instruction *stInst = new StoreInst(NewVal, FnVar, InsertPos);
           //BB->getInstList().push_back(stInst);
	   //
	   
	  
	   //IncrementCounterInBlock(F->begin(), 0, Counters);
	   InsertPrintCode(0,msgentry+F->getName(),PrintFunc,InsertPos,M);
	   InsertInstrumentationCall (BB, "llvm_profile_function");
	   InsertPrintCode(0,msgexit+F->getName(),PrintFunc,RetPos,M);
	   //Value *Intem = ConstantInt::get(Type::IntTy, Counters);
	   //InsertPrintCode(Intem,msgexit1+F->getName(),PrintFunc,RetPos,M);
	  
	 }  
     }
    
      return true; 
    }
  };
  RegisterOpt<ModuleProf> X("moduleprofile", "Runtime Optimization Module Pass");
}
