//===----- LowerConstantCalls.cpp - Changes arrayLength calls  --------------===//
//
//                            The VMKit project
//
// This file is distributed under the University of Illinois Open Source 
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/Constants.h"
#include "llvm/GlobalVariable.h"
#include "llvm/Pass.h"
#include "llvm/Function.h"
#include "llvm/Instructions.h"
#include "llvm/Support/CallSite.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"

#include "JavaClass.h"
#include "j3/JavaLLVMCompiler.h"
#include "j3/J3Intrinsics.h"

using namespace llvm;

namespace j3 {

  class LowerConstantCalls : public FunctionPass {
  public:
    static char ID;
    JavaLLVMCompiler* TheCompiler;
    LowerConstantCalls(JavaLLVMCompiler* Compiler) : FunctionPass(ID),
      TheCompiler(Compiler) { }

    virtual bool runOnFunction(Function &F);
  private:
  };
  char LowerConstantCalls::ID = 0;

#if 0
  static RegisterPass<LowerConstantCalls> X("LowerConstantCalls",
                                            "Lower Constant calls");
#endif


#ifdef ISOLATE
static Value* getTCM(J3Intrinsics* intrinsics, Value* Arg, Instruction* CI) {
  Value* GEP[2] = { intrinsics->constantZero,
                    intrinsics->OffsetTaskClassMirrorInClassConstant };
  Value* TCMArray = GetElementPtrInst::Create(Arg, GEP, GEP + 2, "", CI);

  Value* threadId = CallInst::Create(intrinsics->llvm_frameaddress,
                                     intrinsics->constantZero, "", CI);
  threadId = new PtrToIntInst(threadId, intrinsics->pointerSizeType, "", CI);
  threadId = BinaryOperator::CreateAnd(threadId, intrinsics->constantThreadIDMask,
                                       "", CI);
  
  threadId = new IntToPtrInst(threadId, intrinsics->ptr32Type, "", CI);
  
  Value* IsolateID = GetElementPtrInst::Create(threadId,
      intrinsics->OffsetIsolateInThreadConstant, "", CI);

  IsolateID = new LoadInst(IsolateID, "", CI);

  Value* GEP2[2] = { intrinsics->constantZero, IsolateID };

  Value* TCM = GetElementPtrInst::Create(TCMArray, GEP2, GEP2 + 2, "",
                                         CI);
  return TCM;
}

static Value* getDelegatee(J3Intrinsics* intrinsics, Value* Arg, Instruction* CI) {
  Value* GEP[2] = { intrinsics->constantZero,
                    intrinsics->constantTwo };
  Value* TCMArray = GetElementPtrInst::Create(Arg, GEP, GEP + 2, "", CI);

  Value* threadId = CallInst::Create(intrinsics->llvm_frameaddress,
                                     intrinsics->constantZero, "", CI);
  threadId = new PtrToIntInst(threadId, intrinsics->pointerSizeType, "", CI);
  threadId = BinaryOperator::CreateAnd(threadId, intrinsics->constantThreadIDMask,
                                       "", CI);
  
  threadId = new IntToPtrInst(threadId, intrinsics->ptr32Type, "", CI);
  
  Value* IsolateID = GetElementPtrInst::Create(threadId, 
      intrinsics->OffsetIsolateInThreadConstant, "", CI);

  IsolateID = new LoadInst(IsolateID, "", CI);

  Value* GEP2[2] = { intrinsics->constantZero, IsolateID };

  Value* TCM = GetElementPtrInst::Create(TCMArray, GEP2, GEP2 + 2, "",
                                         CI);
  return new LoadInst(TCM, "", CI);
}

#else

static Value* getTCM(J3Intrinsics* intrinsics, Value* Arg, Instruction* CI) {
  Value* GEP[2] = { intrinsics->constantZero,
                    intrinsics->OffsetTaskClassMirrorInClassConstant };
  Value* TCMArray = GetElementPtrInst::Create(Arg, GEP, GEP + 2, "", CI);
  
  Value* GEP2[2] = { intrinsics->constantZero, intrinsics->constantZero };

  Value* TCM = GetElementPtrInst::Create(TCMArray, GEP2, GEP2 + 2, "",
                                         CI);
  return TCM;

}

static Value* getDelegatee(J3Intrinsics* intrinsics, Value* Arg, Instruction* CI) {
  Value* GEP[2] = { intrinsics->constantZero,
                    intrinsics->constantZero };
  Value* TCMArray = GetElementPtrInst::Create(Arg, GEP, GEP + 2, "", CI);
  
  Value* GEP2[2] = { intrinsics->constantZero, intrinsics->constantZero };

  Value* TCM = GetElementPtrInst::Create(TCMArray, GEP2, GEP2 + 2, "",
                                         CI);
  return new LoadInst(TCM, "", CI);

}
#endif

bool LowerConstantCalls::runOnFunction(Function& F) {
  LLVMContext* Context = &F.getContext();
  bool Changed = false;
  J3Intrinsics* intrinsics = TheCompiler->getIntrinsics();
  JavaMethod* meth = TheCompiler->getJavaMethod(F);
  assert(meth && "Method not registered");
  for (Function::iterator BI = F.begin(), BE = F.end(); BI != BE; BI++) { 
    BasicBlock *Cur = BI; 
    for (BasicBlock::iterator II = Cur->begin(), IE = Cur->end(); II != IE;) {
      Instruction *I = II;
      II++;

      if (ICmpInst* Cmp = dyn_cast<ICmpInst>(I)) {
        if (Cmp->getOperand(1) == intrinsics->JavaObjectNullConstant) {
          Value* Arg = Cmp->getOperand(0);
          if (isVirtual(meth->access) && Arg == F.arg_begin()) {
            Changed = true;
            Cmp->replaceAllUsesWith(ConstantInt::getFalse(*Context));
            Cmp->eraseFromParent();
            break;
          }
         
          Instruction* InsArg = dyn_cast<Instruction>(Arg); 
          if (InsArg != NULL &&
              (InsArg->getOpcode() == Instruction::Call ||
               InsArg->getOpcode() == Instruction::Invoke)) { 
            CallSite Ca(Arg);
            if (Ca.getCalledValue() == intrinsics->AllocateFunction) {
              Changed = true;
              Cmp->replaceAllUsesWith(ConstantInt::getFalse(*Context));
              Cmp->eraseFromParent();
              break;
            }
          }
        }
      }
     
      // Remove useless Alloca's, usually used for stacks or temporary values.
      // The optimizers may have rendered them useless.
      if (AllocaInst* AI = dyn_cast<AllocaInst>(I)) {
        bool ToDelete = true;
        for (Value::use_iterator UI = AI->use_begin(), UE = AI->use_end();
             UI != UE; ++UI) {
          if (dyn_cast<StoreInst>(*UI)) continue;
          if (BitCastInst* BI = dyn_cast<BitCastInst>(*UI)) {
            if (BI->hasOneUse()) {
              Instruction* use = dyn_cast<Instruction>(*(BI->use_begin()));
              if (use != NULL &&
                  (use->getOpcode() == Instruction::Call ||
                   use->getOpcode() == Instruction::Invoke)) { 
                CallSite Call(use);
                if (Call.getCalledFunction() == intrinsics->llvm_gc_gcroot) {
                  continue;
                }
              }
            }
          }
          
          ToDelete = false;
          break;
        }
        
        if (ToDelete) {
          Changed = true;
          for (Value::use_iterator UI = AI->use_begin(), UE = AI->use_end();
               UI != UE;) {
            Value* Temp = *UI;
            ++UI;
            if (StoreInst* SI = dyn_cast<StoreInst>(Temp)) {
              if (dyn_cast<Instruction>(II) == SI) ++II;
              SI->eraseFromParent();
            } else if (BitCastInst* BI = dyn_cast<BitCastInst>(Temp)) {
              CallSite Call(*(BI->use_begin()));
              Instruction* CI = Call.getInstruction();
              if (dyn_cast<Instruction>(II) == CI) ++II;
              CI->eraseFromParent();
              if (dyn_cast<Instruction>(II) == BI) ++II;
              BI->eraseFromParent();
            }
          }
          AI->eraseFromParent();
        }
        continue;
      }
      // Make sure all Loads on objects are volatile to cooperate with the GC.
      if (LoadInst* LI = dyn_cast<LoadInst>(I)) {
        if (LI->getType() == intrinsics->JavaObjectType &&
            dyn_cast<AllocaInst>(LI->getPointerOperand()) != NULL) {
          if (TheCompiler->useCooperativeGC()) {
            assert(LI->isVolatile());
          } else {
            assert(!LI->isVolatile());
          }
        }
      }

      if ((I->getOpcode() == Instruction::Call ||
           I->getOpcode() == Instruction::Invoke)) { 
        Instruction* CI = I;
        CallSite Call(I);
        Value* V = Call.getCalledValue();
        if (V == intrinsics->ArrayLengthFunction) {
          Changed = true;
          Value* val = Call.getArgument(0); // get the array
          Value* array = new BitCastInst(val, intrinsics->JavaArrayType,
                                         "", CI);
          Value* args[2] = { intrinsics->constantZero, 
                             intrinsics->JavaArraySizeOffsetConstant };
          Value* ptr = GetElementPtrInst::Create(array, args, args + 2,
                                                 "", CI);
          Value* load = new LoadInst(ptr, "", CI);
          load = new PtrToIntInst(load, Type::getInt32Ty(*Context), "", CI);
          CI->replaceAllUsesWith(load);
          CI->eraseFromParent();
        } else if (V == intrinsics->GetVTFunction) {
          Changed = true;
          Value* val = Call.getArgument(0); // get the object
          Value* indexes[2] = { intrinsics->constantZero, intrinsics->constantZero };
          Value* VTPtr = GetElementPtrInst::Create(val, indexes, indexes + 2,
                                                   "", CI);
          Value* VT = new LoadInst(VTPtr, "", CI);
          CI->replaceAllUsesWith(VT);
          CI->eraseFromParent();
        } else if (V == intrinsics->GetIMTFunction) {
          Changed = true;
          Value* val = Call.getArgument(0); // get the VT
          Value* indexes[2] = { intrinsics->constantZero,
                                intrinsics->OffsetIMTInVTConstant };
          Value* IMTPtr = GetElementPtrInst::Create(val, indexes, indexes + 2,
                                                    "", CI);
          Value* IMT = new LoadInst(IMTPtr, "", CI);
          IMT = new BitCastInst(IMT, CI->getType(), "", CI);
          CI->replaceAllUsesWith(IMT);
          CI->eraseFromParent();
        } else if (V == intrinsics->GetClassFunction) {
          Changed = true;
          Value* val = Call.getArgument(0); // get the object
          Value* args2[2] = { intrinsics->constantZero,
                              intrinsics->JavaObjectVTOffsetConstant };
          Value* VTPtr = GetElementPtrInst::Create(val, args2, args2 + 2,
                                                   "", CI);
          Value* VT = new LoadInst(VTPtr, "", CI);
          Value* args3[2] = { intrinsics->constantZero,
                              intrinsics->OffsetClassInVTConstant };

          Value* clPtr = GetElementPtrInst::Create(VT, args3, args3 + 2,
                                                   "", CI);
          Value* cl = new LoadInst(clPtr, "", CI);
          cl = new BitCastInst(cl, intrinsics->JavaCommonClassType, "", CI);

          CI->replaceAllUsesWith(cl);
          CI->eraseFromParent();
        } else if (V == intrinsics->GetVTFromClassFunction) {
          Changed = true;
          
          Value* val = Call.getArgument(0);
          Value* indexes[3] = { intrinsics->constantZero, 
                                intrinsics->constantZero, 
                                intrinsics->OffsetVTInClassConstant };
          Value* VTPtr = GetElementPtrInst::Create(val, indexes, indexes + 3,
                                                   "", CI);
          Value* VT = new LoadInst(VTPtr, "", CI);
          CI->replaceAllUsesWith(VT);
          CI->eraseFromParent();
        } else if (V == intrinsics->GetVTFromCommonClassFunction) {
          Changed = true;
          
          Value* val = Call.getArgument(0);
          Value* indexes[2] = { intrinsics->constantZero, 
                                intrinsics->OffsetVTInClassConstant };
          Value* VTPtr = GetElementPtrInst::Create(val, indexes, indexes + 2,
                                                   "", CI);
          Value* VT = new LoadInst(VTPtr, "", CI);
          CI->replaceAllUsesWith(VT);
          CI->eraseFromParent();
        } else if (V == intrinsics->GetVTFromClassArrayFunction) {
          Changed = true;
          
          Value* val = Call.getArgument(0);
          Value* indexes[3] = { intrinsics->constantZero,
                                intrinsics->constantZero,
                                intrinsics->OffsetVTInClassConstant };
          Value* VTPtr = GetElementPtrInst::Create(val, indexes, indexes + 3,
                                                   "", CI);
          Value* VT = new LoadInst(VTPtr, "", CI);
          CI->replaceAllUsesWith(VT);
          CI->eraseFromParent();
        } else if (V == intrinsics->GetBaseClassVTFromVTFunction) {
          Changed = true;
          
          Value* val = Call.getArgument(0);
          Value* indexes[2] = { intrinsics->constantZero,
                                intrinsics->OffsetBaseClassVTInVTConstant };
          Value* VTPtr = GetElementPtrInst::Create(val, indexes, indexes + 2,
                                                   "", CI);
          Value* VT = new LoadInst(VTPtr, "", CI);
          VT = new BitCastInst(VT, intrinsics->VTType, "", CI);
          CI->replaceAllUsesWith(VT);
          CI->eraseFromParent();
        } else if (V == intrinsics->GetObjectSizeFromClassFunction) {
          Changed = true;
          
          Value* val = Call.getArgument(0); 
          Value* indexes[2] = { intrinsics->constantZero, 
                                intrinsics->OffsetObjectSizeInClassConstant };
          Value* SizePtr = GetElementPtrInst::Create(val, indexes, indexes + 2,
                                                     "", CI);
          Value* Size = new LoadInst(SizePtr, "", CI);
          CI->replaceAllUsesWith(Size);
          CI->eraseFromParent();
        } else if (V == intrinsics->GetDepthFunction) {
          Changed = true;
          Value* val = Call.getArgument(0); 
          Value* indexes[2] = { intrinsics->constantZero,
                                intrinsics->OffsetDepthInVTConstant };
          Value* DepthPtr = GetElementPtrInst::Create(val, indexes,
                                                      indexes + 2, "", CI);
          Value* Depth = new LoadInst(DepthPtr, "", CI);
          Depth = new PtrToIntInst(Depth, Type::getInt32Ty(*Context), "", CI);
          CI->replaceAllUsesWith(Depth);
          CI->eraseFromParent();
        } else if (V == intrinsics->GetDisplayFunction) {
          Changed = true;
          Value* val = Call.getArgument(0);
          Value* indexes[2] = { intrinsics->constantZero,
                                intrinsics->OffsetDisplayInVTConstant };
          Value* DisplayPtr = GetElementPtrInst::Create(val, indexes,
                                                        indexes + 2, "", CI);
          const llvm::Type* Ty = PointerType::getUnqual(intrinsics->VTType);
          DisplayPtr = new BitCastInst(DisplayPtr, Ty, "", CI);
          CI->replaceAllUsesWith(DisplayPtr);
          CI->eraseFromParent();
        } else if (V == intrinsics->GetVTInDisplayFunction) {
          Changed = true;
          Value* val = Call.getArgument(0);
          Value* depth = Call.getArgument(1);
          Value* ClassPtr = GetElementPtrInst::Create(val, depth, "", CI);
          Value* Class = new LoadInst(ClassPtr, "", CI);
          CI->replaceAllUsesWith(Class);
          CI->eraseFromParent();
#if defined(ISOLATE)
        } else if (V == intrinsics->GetStaticInstanceFunction) {
          Changed = true;

          Value* TCM = getTCM(intrinsics, Call.getArgument(0), CI);
          Constant* C = intrinsics->OffsetStaticInstanceInTaskClassMirrorConstant;
          Value* GEP[2] = { intrinsics->constantZero, C };
          Value* Replace = GetElementPtrInst::Create(TCM, GEP, GEP + 2, "", CI);
          Replace = new LoadInst(Replace, "", CI);
          CI->replaceAllUsesWith(Replace);
          CI->eraseFromParent();
#endif
        } else if (V == intrinsics->GetClassDelegateeFunction) {
          Changed = true;
          BasicBlock* NBB = II->getParent()->splitBasicBlock(II);
          I->getParent()->getTerminator()->eraseFromParent();
          Value* Del = getDelegatee(intrinsics, Call.getArgument(0), CI);
          Value* cmp = new ICmpInst(CI, ICmpInst::ICMP_EQ, Del, 
                                    intrinsics->JavaObjectNullConstant, "");
          
          BasicBlock* NoDelegatee = BasicBlock::Create(*Context, "No delegatee", &F);
          BasicBlock* DelegateeOK = BasicBlock::Create(*Context, "Delegatee OK", &F);
          BranchInst::Create(NoDelegatee, DelegateeOK, cmp, CI);
          PHINode* phi = PHINode::Create(intrinsics->JavaObjectType, "", DelegateeOK);
          phi->addIncoming(Del, CI->getParent());
          
          Instruction* Res = CallInst::Create(intrinsics->RuntimeDelegateeFunction,
                                              Call.getArgument(0), "", NoDelegatee);
          Res->setDebugLoc(CI->getDebugLoc());
          BranchInst::Create(DelegateeOK, NoDelegatee);
          phi->addIncoming(Res, NoDelegatee);

          CI->replaceAllUsesWith(phi);
          CI->eraseFromParent();
          BranchInst::Create(NBB, DelegateeOK);
          break;
         
        } else if (V == intrinsics->InitialisationCheckFunction) {
          Changed = true;
          
          BasicBlock* NBB = 0;
          if (CI->getParent()->getTerminator() != CI) {
            NBB = II->getParent()->splitBasicBlock(II);
            CI->getParent()->getTerminator()->eraseFromParent();
          } else {
            InvokeInst* Invoke = dyn_cast<InvokeInst>(CI);
            assert(Invoke && "Last instruction is not an invoke");
            NBB = Invoke->getNormalDest();
          }
         
          Value* Cl = Call.getArgument(0); 
          Value* TCM = getTCM(intrinsics, Call.getArgument(0), CI);
          Value* GEP[2] = 
            { intrinsics->constantZero,
              intrinsics->OffsetInitializedInTaskClassMirrorConstant };
          Value* StatusPtr = GetElementPtrInst::Create(TCM, GEP, GEP + 2, "",
                                                       CI);
          
          Value* test = new LoadInst(StatusPtr, "", CI);
          
          BasicBlock* trueCl = BasicBlock::Create(*Context, "Initialized", &F);
          BasicBlock* falseCl = BasicBlock::Create(*Context, "Uninitialized", &F);
          PHINode* node = llvm::PHINode::Create(intrinsics->JavaClassType, "", trueCl);
          node->addIncoming(Cl, CI->getParent());
          BranchInst::Create(trueCl, falseCl, test, CI);
  
          
          Instruction* res = 0;
          if (InvokeInst* Invoke = dyn_cast<InvokeInst>(CI)) {
            Value* Args[1] = { Cl };
            BasicBlock* UI = Invoke->getUnwindDest();

            res = InvokeInst::Create(intrinsics->InitialiseClassFunction,
                                     trueCl, UI, Args, Args + 1,
                                     "", falseCl);

            // For some reason, an LLVM pass may add PHI nodes to the
            // exception destination.
            BasicBlock::iterator Temp = UI->getInstList().begin();
            while (PHINode* PHI = dyn_cast<PHINode>(Temp)) {
              Value* Val = PHI->getIncomingValueForBlock(CI->getParent());
              PHI->removeIncomingValue(CI->getParent(), false);
              PHI->addIncoming(Val, falseCl);
              Temp++;
            }
            
            // And here we set the phi nodes of the normal dest of the Invoke
            // instruction. The phi nodes have now the trueCl as basic block.
            Temp = NBB->getInstList().begin();
            while (PHINode* PHI = dyn_cast<PHINode>(Temp)) {
              Value* Val = PHI->getIncomingValueForBlock(CI->getParent());
              PHI->removeIncomingValue(CI->getParent(), false);
              PHI->addIncoming(Val, trueCl);
              Temp++;
            }

          } else {
            res = CallInst::Create(intrinsics->InitialiseClassFunction,
                                   Cl, "", falseCl);
            BranchInst::Create(trueCl, falseCl);
          }
          res->setDebugLoc(CI->getDebugLoc());
          
          node->addIncoming(res, falseCl);


          CI->replaceAllUsesWith(node);
          CI->eraseFromParent();
          BranchInst::Create(NBB, trueCl);
          break;
        } else if (V == intrinsics->GetConstantPoolAtFunction) {
          Function* resolver = dyn_cast<Function>(Call.getArgument(0));
          assert(resolver && "Wrong use of GetConstantPoolAt");
          const Type* returnType = resolver->getReturnType();
          Value* CTP = Call.getArgument(1);
          Value* Index = Call.getArgument(3);
          Changed = true;
          BasicBlock* NBB = 0;
          if (CI->getParent()->getTerminator() != CI) {
            NBB = II->getParent()->splitBasicBlock(II);
            CI->getParent()->getTerminator()->eraseFromParent();
          } else {
            InvokeInst* Invoke = dyn_cast<InvokeInst>(CI);
            assert(Invoke && "Last instruction is not an invoke");
            NBB = Invoke->getNormalDest();
          }
          
#ifdef ISOLATE_SHARING
          ConstantInt* Cons = dyn_cast<ConstantInt>(Index);
          assert(CI && "Wrong use of GetConstantPoolAt");
          uint64 val = Cons->getZExtValue();
          Value* indexes = ConstantInt::get(Type::getInt32Ty(*Context), val + 1);
#else
          Value* indexes = Index;
#endif
          Value* arg1 = GetElementPtrInst::Create(CTP, indexes, "", CI);
          arg1 = new LoadInst(arg1, "", false, CI);
          Value* test = new ICmpInst(CI, ICmpInst::ICMP_EQ, arg1,
                                     intrinsics->constantPtrNull, "");
 
          BasicBlock* trueCl = BasicBlock::Create(*Context, "Ctp OK", &F);
          BasicBlock* falseCl = BasicBlock::Create(*Context, "Ctp Not OK", &F);
          PHINode* node = llvm::PHINode::Create(returnType, "", trueCl);
          node->addIncoming(arg1, CI->getParent());
          BranchInst::Create(falseCl, trueCl, test, CI);
  
          std::vector<Value*> Args;
          unsigned ArgSize = Call.arg_size(), i = 1;
          while (++i < ArgSize) {
            Args.push_back(Call.getArgument(i));
          }
          
          Instruction* res = 0;
          if (InvokeInst* Invoke = dyn_cast<InvokeInst>(CI)) {
            BasicBlock* UI = Invoke->getUnwindDest();
            res = InvokeInst::Create(resolver, trueCl, UI, Args.begin(),
                                     Args.end(), "", falseCl);

            // For some reason, an LLVM pass may add PHI nodes to the
            // exception destination.
            BasicBlock::iterator Temp = UI->getInstList().begin();
            while (PHINode* PHI = dyn_cast<PHINode>(Temp)) {
              Value* Val = PHI->getIncomingValueForBlock(CI->getParent());
              PHI->removeIncomingValue(CI->getParent(), false);
              PHI->addIncoming(Val, falseCl);
              Temp++;
            }

            // And here we set the phi nodes of the normal dest of the Invoke
            // instruction. The phi nodes have now the trueCl as basic block.
            Temp = NBB->getInstList().begin();
            while (PHINode* PHI = dyn_cast<PHINode>(Temp)) {
              Value* Val = PHI->getIncomingValueForBlock(CI->getParent());
              PHI->removeIncomingValue(CI->getParent(), false);
              PHI->addIncoming(Val, trueCl);
              Temp++;
            }

          } else {
            res = CallInst::Create(resolver, Args.begin(), Args.end(), "",
                                   falseCl);
            BranchInst::Create(trueCl, falseCl);
          }
          
          res->setDebugLoc(CI->getDebugLoc());
          node->addIncoming(res, falseCl);

          CI->replaceAllUsesWith(node);
          CI->eraseFromParent();
          BranchInst::Create(NBB, trueCl);
          break;
        } else if (V == intrinsics->GetArrayClassFunction) {
          const llvm::Type* Ty = PointerType::getUnqual(intrinsics->VTType);
          Constant* nullValue = Constant::getNullValue(Ty);
          // Check if we have already proceed this call.
          if (Call.getArgument(2) == nullValue) { 
            BasicBlock* NBB = II->getParent()->splitBasicBlock(II);
            I->getParent()->getTerminator()->eraseFromParent();

            Constant* init = Constant::getNullValue(intrinsics->VTType);
            GlobalVariable* GV = 
              new GlobalVariable(*(F.getParent()), intrinsics->VTType,
                                 false, GlobalValue::ExternalLinkage,
                                 init, "");

            Value* LoadedGV = new LoadInst(GV, "", CI);
            Value* cmp = new ICmpInst(CI, ICmpInst::ICMP_EQ, LoadedGV, init,
                                      "");

            BasicBlock* OKBlock = BasicBlock::Create(*Context, "", &F);
            BasicBlock* NotOKBlock = BasicBlock::Create(*Context, "", &F);
            PHINode* node = PHINode::Create(intrinsics->VTType, "",
                                            OKBlock);
            node->addIncoming(LoadedGV, CI->getParent());

            BranchInst::Create(NotOKBlock, OKBlock, cmp, CI);

            Value* args[3] = { Call.getArgument(0), Call.getArgument(1), GV };
            Instruction* res = CallInst::Create(intrinsics->GetArrayClassFunction, args,
                                                args + 3, "", NotOKBlock);
            res->setDebugLoc(CI->getDebugLoc());
            BranchInst::Create(OKBlock, NotOKBlock);
            node->addIncoming(res, NotOKBlock);
            
            CI->replaceAllUsesWith(node);
            CI->eraseFromParent();
            BranchInst::Create(NBB, OKBlock);
            Changed = true;
            break;
          }
        } else if (V == intrinsics->ForceInitialisationCheckFunction ||
                   V == intrinsics->ForceLoadedCheckFunction ) {
          Changed = true;
          CI->eraseFromParent();
        } else if (V == intrinsics->GetFinalInt8FieldFunction ||
                   V == intrinsics->GetFinalInt16FieldFunction ||
                   V == intrinsics->GetFinalInt32FieldFunction ||
                   V == intrinsics->GetFinalLongFieldFunction ||
                   V == intrinsics->GetFinalFloatFieldFunction ||
                   V == intrinsics->GetFinalDoubleFieldFunction) {
          Changed = true;
          Value* val = Call.getArgument(0);
          Value* res = new LoadInst(val, "", CI);
          CI->replaceAllUsesWith(res);
          CI->eraseFromParent();
        } else if (V == intrinsics->IsAssignableFromFunction) {
          Changed = true;
          Value* VT1 = Call.getArgument(0);
          Value* VT2 = Call.getArgument(1);
          
          BasicBlock* EndBlock = II->getParent()->splitBasicBlock(II);
          I->getParent()->getTerminator()->eraseFromParent();
          
          BasicBlock* CurEndBlock = BasicBlock::Create(*Context, "", &F);
          BasicBlock* FailedBlock = BasicBlock::Create(*Context, "", &F);
          PHINode* node = PHINode::Create(Type::getInt1Ty(*Context), "", CurEndBlock);

          ConstantInt* CC = ConstantInt::get(Type::getInt32Ty(*Context),
              JavaVirtualTable::getOffsetIndex());
          Value* indices[2] = { intrinsics->constantZero, CC };
          Value* Offset = GetElementPtrInst::Create(VT2, indices, indices + 2,
                                                    "", CI);
          Offset = new LoadInst(Offset, "", false, CI);
          Offset = new PtrToIntInst(Offset, Type::getInt32Ty(*Context), "", CI);
          indices[1] = Offset;
          Value* CurVT = GetElementPtrInst::Create(VT1, indices, indices + 2,
                                                   "", CI);
          CurVT = new LoadInst(CurVT, "", false, CI);
          CurVT = new BitCastInst(CurVT, intrinsics->VTType, "", CI);
             
          Instruction* res =
            new ICmpInst(CI, ICmpInst::ICMP_EQ, CurVT, VT2, "");

          node->addIncoming(ConstantInt::getTrue(*Context), CI->getParent());
          BranchInst::Create(CurEndBlock, FailedBlock, res, CI);

          Value* Args[2] = { VT1, VT2 };
          res = CallInst::Create(intrinsics->IsSecondaryClassFunction, Args,
                                 Args + 2, "", FailedBlock);
          res->setDebugLoc(CI->getDebugLoc());
         
          node->addIncoming(res, FailedBlock);
          BranchInst::Create(CurEndBlock, FailedBlock);

          // Branch to the next block.
          BranchInst::Create(EndBlock, CurEndBlock);
          
          // We can now replace the previous instruction.
          CI->replaceAllUsesWith(node);
          CI->eraseFromParent();
          
          // Reanalyse the current block.
          break;

        } else if (V == intrinsics->IsSecondaryClassFunction) {
          Changed = true;
          Value* VT1 = Call.getArgument(0);
          Value* VT2 = Call.getArgument(1);
            
          BasicBlock* EndBlock = II->getParent()->splitBasicBlock(II);
          I->getParent()->getTerminator()->eraseFromParent();


          BasicBlock* Preheader = BasicBlock::Create(*Context, "preheader", &F);
          BasicBlock* BB4 = BasicBlock::Create(*Context, "BB4", &F);
          BasicBlock* BB5 = BasicBlock::Create(*Context, "BB5", &F);
          BasicBlock* BB6 = BasicBlock::Create(*Context, "BB6", &F);
          BasicBlock* BB7 = BasicBlock::Create(*Context, "BB7", &F);
          BasicBlock* BB9 = BasicBlock::Create(*Context, "BB9", &F);
          const Type* Ty = PointerType::getUnqual(intrinsics->VTType);
          
          PHINode* resFwd = PHINode::Create(Type::getInt32Ty(*Context), "", BB7);
   
          // This corresponds to:
          //    if (VT1.cache == VT2 || VT1 == VT2) goto end with true;
          //    else goto headerLoop;
          ConstantInt* cacheIndex = 
            ConstantInt::get(Type::getInt32Ty(*Context), JavaVirtualTable::getCacheIndex());
          Value* indices[2] = { intrinsics->constantZero, cacheIndex };
          Instruction* CachePtr = 
            GetElementPtrInst::Create(VT1, indices, indices + 2, "", CI);
          CachePtr = new BitCastInst(CachePtr, Ty, "", CI);
          Value* Cache = new LoadInst(CachePtr, "", false, CI);
          ICmpInst* cmp1 = new ICmpInst(CI, ICmpInst::ICMP_EQ, Cache, VT2, "");
          ICmpInst* cmp2 = new ICmpInst(CI, ICmpInst::ICMP_EQ, VT1, VT2, "");
          BinaryOperator* Or = BinaryOperator::Create(Instruction::Or, cmp1,
                                                      cmp2, "", CI);
          BranchInst::Create(BB9, Preheader, Or, CI);
    
          // First test failed. Go into the loop. The Preheader looks like this:
          // headerLoop:
          //    types = VT1->secondaryTypes;
          //    size = VT1->nbSecondaryTypes;
          //    i = 0;
          //    goto test;
          ConstantInt* sizeIndex = ConstantInt::get(Type::getInt32Ty(*Context), 
              JavaVirtualTable::getNumSecondaryTypesIndex());
          indices[1] = sizeIndex;
          Instruction* Size = GetElementPtrInst::Create(VT1, indices,
                                                        indices + 2, "",
                                                        Preheader);
          Size = new LoadInst(Size, "", false, Preheader);
          Size = new PtrToIntInst(Size, Type::getInt32Ty(*Context), "", Preheader);
    
          ConstantInt* secondaryTypesIndex = ConstantInt::get(Type::getInt32Ty(*Context), 
              JavaVirtualTable::getSecondaryTypesIndex());
          indices[1] = secondaryTypesIndex;
          Instruction* secondaryTypes = 
            GetElementPtrInst::Create(VT1, indices, indices + 2, "", Preheader);
          secondaryTypes = new LoadInst(secondaryTypes, "", false, Preheader);
          secondaryTypes = new BitCastInst(secondaryTypes, Ty, "", Preheader);
          BranchInst::Create(BB7, Preheader);
    
          // Here is the test if the current secondary type is VT2.
          // test:
          //   CurVT = types[i];
          //   if (CurVT == VT2) goto update cache;
          //   est goto inc;
          Instruction* CurVT = GetElementPtrInst::Create(secondaryTypes, resFwd,
                                                         "", BB4);
          CurVT = new LoadInst(CurVT, "", false, BB4);
          cmp1 = new ICmpInst(*BB4, ICmpInst::ICMP_EQ, CurVT, VT2, "");
          BranchInst::Create(BB5, BB6, cmp1, BB4);
    
          // Increment i if the previous test failed
          // inc:
          //    ++i;
          //    goto endLoopTest;
          BinaryOperator* IndVar = 
            BinaryOperator::CreateAdd(resFwd, intrinsics->constantOne, "", BB6);
          BranchInst::Create(BB7, BB6);
    
          // Verify that we haven't reached the end of the loop:
          // endLoopTest:
          //    if (i < size) goto test
          //    else goto end with false
          resFwd->reserveOperandSpace(2);
          resFwd->addIncoming(intrinsics->constantZero, Preheader);
          resFwd->addIncoming(IndVar, BB6);
    
          cmp1 = new ICmpInst(*BB7, ICmpInst::ICMP_SGT, Size, resFwd, "");
          BranchInst::Create(BB4, BB9, cmp1, BB7);
   
          // Update the cache if the result is found.
          // updateCache:
          //    VT1->cache = result
          //    goto end with true
          new StoreInst(VT2, CachePtr, false, BB5);
          BranchInst::Create(BB9, BB5);

          // Final block, that gets the result.
          PHINode* node = PHINode::Create(Type::getInt1Ty(*Context), "", BB9);
          node->reserveOperandSpace(3);
          node->addIncoming(ConstantInt::getTrue(*Context), CI->getParent());
          node->addIncoming(ConstantInt::getFalse(*Context), BB7);
          node->addIncoming(ConstantInt::getTrue(*Context), BB5);
    
          // Don't forget to jump to the next block.
          BranchInst::Create(EndBlock, BB9);
   
          // We can now replace the previous instruction
          CI->replaceAllUsesWith(node);
          CI->eraseFromParent();

          // And reanalyse the current block.
          break;
        }
#ifdef ISOLATE_SHARING
        else if (V == intrinsics->GetCtpClassFunction) {
          Changed = true;
          Value* val = Call.getArgument(0); 
          Value* indexes[2] = { intrinsics->constantZero, 
                                intrinsics->OffsetCtpInClassConstant };
          Value* VTPtr = GetElementPtrInst::Create(val, indexes,
                                                   indexes + 2, "", CI);
          Value* VT = new LoadInst(VTPtr, "", CI);
          CI->replaceAllUsesWith(VT);
          CI->eraseFromParent();
        } else if (V == intrinsics->GetJnjvmArrayClassFunction) {
          Changed = true;
          Value* val = Call.getArgument(0); 
          Value* index = Call.getArgument(1); 
          Value* indexes[3] = { intrinsics->constantZero, intrinsics->constantTwo,
                                index };
          Value* VTPtr = GetElementPtrInst::Create(val, indexes, indexes + 3,
                                                   "", CI);
          Value* VT = new LoadInst(VTPtr, "", CI);
          CI->replaceAllUsesWith(VT);
          CI->eraseFromParent();
        } else if (V == intrinsics->GetJnjvmExceptionClassFunction) {
          Changed = true;
          Value* val = Call.getArgument(0);
          Value* indexes[2] = { intrinsics->constantZero, intrinsics->constantOne };
          Value* VTPtr = GetElementPtrInst::Create(val, indexes, indexes + 2,
                                                   "", CI);
          Value* VT = new LoadInst(VTPtr, "", CI);
          CI->replaceAllUsesWith(VT);
          CI->eraseFromParent();
        }
#endif

      }
    }
  }
  return Changed;
}


FunctionPass* createLowerConstantCallsPass(JavaLLVMCompiler* Compiler) {
  return new LowerConstantCalls(Compiler);
}

}
