//===- Sample.cpp - Example code from "Writing an LLVM Pass" ---------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by Sandra 
//
//===----------------------------------------------------------------------===//


#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) {
         
      //Function *Main = M.getMainFunction();    
      //std::cerr << "Module Profiling : " << Main->getName() << "\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 *Counters = new GlobalVariable(ArrayType::get(Type::UIntTy, 1), true,
      		 GlobalValue::ExternalLinkage,0, "FunCtr");
      
      M.getGlobalList().push_back(Counters);
      //Constant *ctr=ConstantExpr::getGetElementPtr(Counters,
        //        std::vector<Constant*>(2,Constant::getNullValue(Type::LongTy)));
		
     
      
      for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F)
      {
        BB=&F->getEntryBlock();
        if ((F->getName() == "main")) // && (!F->isExternal()))
	{
	 //variable FunCtr declaration , initialization	
	 
      Instruction *FnVar=new AllocaInst(Type::IntTy,ConstantUInt::get(Type::UIntTy,1),"FunCtr");
      
      BasicBlock::iterator here=BB->begin();
      BB->getInstList().insert(here,FnVar);
      Value *Int0 = ConstantInt::get(Type::IntTy, 0);
      new StoreInst(Int0, FnVar, here);
	}
        if (!F->isExternal())
         {
	  
	   InsertPos=BB->begin();
	   RetPos= cast<ReturnInst>(BB->getTerminator());
	   //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);
	    
	   //InsertPrintCode(Counters,msgexit1+F->getName(),PrintFunc,RetPos,M);
	  
	 }  
     }
    
      return true; 
    }
  };
  RegisterOpt<ModuleProf> X("moduleprofile", "Runtime Optimization Module Pass");
}



