//===-- FuncSpec.cpp - Clone Functions With Constant Function Ptr Args ----===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "funcspec"

#include "llvm/Instructions.h"
#include "llvm/Module.h"
#include "llvm/Pass.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/ADT/Statistic.h"
#include <set>
#include <map>
#include <vector>

using namespace llvm;

STATISTIC(numCloned, "Number of Functions Cloned");
STATISTIC(numReplaced, "Number of Calls Replaced");

namespace {
  class FuncSpec : public ModulePass {
  public:
    static char ID;
    FuncSpec() : ModulePass(&ID) {}
    bool runOnModule(Module& M) {
      std::map<CallInst*, std::vector<std::pair<unsigned, Constant*> > > cloneSites;
      std::map<std::pair<Function*, std::vector<std::pair<unsigned, Constant*> > >, Function* > toClone;

      for (Module::iterator I = M.begin(); I != M.end(); ++I)
        if (!I->isDeclaration() && !I->mayBeOverridden()) {
          std::vector<unsigned> FPArgs;
          for (Function::arg_iterator ii = I->arg_begin(), ee = I->arg_end();
               ii != ee; ++ii)
            if (const PointerType* Ty = dyn_cast<PointerType>(ii->getType())) {
              if (isa<FunctionType>(Ty->getElementType())) {
                FPArgs.push_back(ii->getArgNo());
                cerr << "Eligable: " << I->getNameStr() << "\n";
              }
            } else if (isa<FunctionType>(ii->getType())) {
              FPArgs.push_back(ii->getArgNo());
              cerr << "Eligable: " << I->getNameStr() << "\n";
            }
          for(Value::use_iterator ui = I->use_begin(), ue = I->use_end();
              ui != ue; ++ui)
            if (CallInst* CI = dyn_cast<CallInst>(ui)) {
              std::vector<std::pair<unsigned, Constant*> > Consts;
              for (unsigned x = 0; x < FPArgs.size(); ++x)
                if (Constant* C = dyn_cast<Constant>(ui->getOperand(x + 1))) {
                  Consts.push_back(std::make_pair(x, C));
                  CI->dump();
                }
              if (!Consts.empty()) {
                cloneSites[CI] = Consts;
                toClone[std::make_pair(I, Consts)] = 0;
              }
            }
        }

      numCloned += toClone.size();

      for (std::map<std::pair<Function*, std::vector<std::pair<unsigned, Constant*> > >, Function* >::iterator I = toClone.begin(), E = toClone.end(); I != E; ++I) {
        Function* DirectF = CloneFunction(I->first.first);
        DirectF->setName(I->first.first->getNameStr() + "_SPEC");
        DirectF->setLinkage(GlobalValue::InternalLinkage);
        I->first.first->getParent()->getFunctionList().push_back(DirectF);
        I->second = DirectF;
      }
      
      for (std::map<CallInst*, std::vector<std::pair<unsigned, Constant*> > >::iterator ii = cloneSites.begin(), ee = cloneSites.end(); ii != ee; ++ii) {
        ii->first->setOperand(0, toClone[std::make_pair(cast<Function>(ii->first->getOperand(0)), ii->second)]);
        ++numReplaced;
      }

      return true;
    }
  };
}

char FuncSpec::ID = 0;
static RegisterPass<FuncSpec>
X("funcspec", "Specialize for Funnction Pointers");
