//===--- LLVMInfo.cpp - Implementation of LLVM info objects for J3---------===//
//
//                            The VMKit project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CallingConv.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MutexGuard.h"
#include "llvm/IR/DataLayout.h"


#include "vmkit/JIT.h"

#include "JavaConstantPool.h"
#include "JavaString.h"
#include "JavaThread.h"
#include "JavaTypes.h"
#include "JavaUpcalls.h"
#include "Jnjvm.h"
#include "Reader.h"

#include "j3/JavaLLVMCompiler.h"
#include "j3/LLVMInfo.h"

#include <cstdio>

using namespace j3;
using namespace llvm;

Type* LLVMClassInfo::getVirtualType() {
  if (!virtualType) {
    std::vector<llvm::Type*> fields;
    const DataLayout* targetData = Compiler->TheDataLayout;
    const StructLayout* sl = 0;
    StructType* structType = 0;
    LLVMContext& context = Compiler->getLLVMModule()->getContext();

    if (classDef->super) {
      LLVMClassInfo* CLI = Compiler->getClassInfo(classDef->super);
      llvm::Type* Ty = CLI->getVirtualType()->getContainedType(0);
      fields.push_back(Ty);
    
      for (uint32 i = 0; i < classDef->nbVirtualFields; ++i) {
        JavaField& field = classDef->virtualFields[i];
        Typedef* type = field.getSignature();
        LLVMAssessorInfo& LAI = Compiler->getTypedefInfo(type);
        fields.push_back(LAI.llvmType);
      }
    
    
      structType = StructType::get(context, fields, false);
      virtualType = PointerType::getUnqual(structType);
      sl = targetData->getStructLayout(structType);
    
    } else {
      virtualType = Compiler->getIntrinsics()->JavaObjectType;
      assert(virtualType && "intrinsics not initalized");
      structType = dyn_cast<StructType>(virtualType->getContainedType(0));
      sl = targetData->getStructLayout(structType);
      
    }
    
    uint64 size = targetData->getTypeAllocSize(structType);
    virtualSizeConstant = ConstantInt::get(Type::getInt32Ty(context), size);
    
    // TODO: put that elsewhere.
    // The class is resolved if it was precompiled.
    if ((!classDef->isResolved() || Compiler->isStaticCompiling())
        && Compiler == classDef->classLoader->getCompiler()) { 
      for (uint32 i = 0; i < classDef->nbVirtualFields; ++i) {
        JavaField& field = classDef->virtualFields[i];
        field.ptrOffset = sl->getElementOffset(i + 1);
        field.num = i + 1;
      }
    
      classDef->virtualSize = (uint32)size;
      classDef->alignment = sl->getAlignment();
   
      Compiler->makeVT(classDef);
      Compiler->makeIMT(classDef);
    }
  }

  return virtualType;
}

Type* LLVMClassInfo::getStaticType() {
  
  if (!staticType) {
    Class* cl = (Class*)classDef;
    std::vector<llvm::Type*> fields;
    
    LLVMContext& context = Compiler->getLLVMModule()->getContext();

    for (uint32 i = 0; i < classDef->nbStaticFields; ++i) {
      JavaField& field = classDef->staticFields[i];
      Typedef* type = field.getSignature();
      LLVMAssessorInfo& LAI = Compiler->getTypedefInfo(type);
      fields.push_back(LAI.llvmType);
    }
  
    StructType* structType = StructType::get(context, fields, false);
    staticType = PointerType::getUnqual(structType);
    const DataLayout* targetData = Compiler->TheDataLayout;
    const StructLayout* sl = targetData->getStructLayout(structType);
    
    // TODO: put that elsewhere.
    if (Compiler == classDef->classLoader->getCompiler()) { 
      for (uint32 i = 0; i < classDef->nbStaticFields; ++i) {
        JavaField& field = classDef->staticFields[i];
        field.num = i;
        field.ptrOffset = sl->getElementOffset(i);
      }
    
      uint64 size = targetData->getTypeAllocSize(structType);
      cl->staticSize = size;
    }
  }
  return staticType;
}


Value* LLVMClassInfo::getVirtualSize() {
  if (!virtualSizeConstant) {
    getVirtualType();
    assert(virtualSizeConstant && "No size for a class?");
  }
  return virtualSizeConstant;
}

static char* GetMethodName(vmkit::ThreadAllocator& allocator,
                           JavaMethod* methodDef,
                           Class* customizeFor) {
  const UTF8* jniConsClName = methodDef->classDef->name;
  const UTF8* jniConsName = methodDef->name;
  const UTF8* jniConsType = methodDef->type;
  sint32 clen = jniConsClName->size;
  sint32 mnlen = jniConsName->size;
  sint32 mtlen = jniConsType->size;

  char* buf = (char*)allocator.Allocate(
      3 + JNI_NAME_PRE_LEN + 1 + ((mnlen + clen + mtlen) << 3));
  
  methodDef->jniConsFromMethOverloaded(buf + 1);
  memcpy(buf, "JnJVM", 5);

  if (customizeFor != NULL) {
    int len = strlen(buf);
    UTF8Buffer buffer(customizeFor->name);
    buffer.toCompileName("_Customized");
    buf[len] = '_';
    buf[len + 1] = '_';
    memcpy(buf + len + 2, buffer.cString(), strlen(buffer.cString()) + 1);
  }

  return buf;
}

Function* LLVMMethodInfo::getMethod(Class* customizeFor) {
  assert(!isAbstract(methodDef->access));
  bool customizing = false;
  Function* result = NULL;
  if (customizeFor != NULL && isCustomizable) {
    customizing = true;
    result = customizedVersions[customizeFor];
  } else {
    result = methodFunction;
  }

  if (result == NULL) {
    if (Compiler->emitFunctionName()) {
      vmkit::ThreadAllocator allocator;
      char* buf = GetMethodName(
          allocator, methodDef, customizing ? customizeFor : NULL);
      result = Function::Create(getFunctionType(), 
                                GlobalValue::ExternalWeakLinkage, buf,
                                Compiler->getLLVMModule());
    } else {
      result = Function::Create(getFunctionType(), 
                                GlobalValue::ExternalWeakLinkage,
                                "", Compiler->getLLVMModule());
    }
   
    result->setGC("vmkit");
    if (Compiler->useCooperativeGC()) { 
      result->addFnAttr(Attribute::NoInline);
    }
    result->addFnAttr(Attribute::NoUnwind);
    
    Compiler->functions.insert(std::make_pair(result, methodDef));
    if (!Compiler->isStaticCompiling() && !customizing && methodDef->code) {
      Compiler->setMethod(result, methodDef->code, result->getName().data());
    }
  }

  if (customizing) {
    customizedVersions[customizeFor] = result;
  } else {
    methodFunction = result;
  }
  return result;
}

FunctionType* LLVMMethodInfo::getFunctionType() {
  if (!functionType) {
    Signdef* sign = methodDef->getSignature();
    LLVMSignatureInfo* LSI = Compiler->getSignatureInfo(sign);
    assert(LSI);
    if (isStatic(methodDef->access)) {
      functionType = LSI->getStaticType();
    } else {
      functionType = LSI->getVirtualType();
    }
  }
  return functionType;
}

Constant* LLVMMethodInfo::getOffset() {
  if (!offsetConstant) {
    LLVMContext& context = Compiler->getLLVMModule()->getContext();
    
    Compiler->resolveVirtualClass(methodDef->classDef);
    offsetConstant = ConstantInt::get(Type::getInt32Ty(context),
                                      methodDef->offset);
  }
  return offsetConstant;
}

Constant* LLVMFieldInfo::getOffset() {
  if (!offsetConstant) {
    LLVMContext& context = Compiler->getLLVMModule()->getContext();
    
    if (isStatic(fieldDef->access)) {
      Compiler->resolveStaticClass(fieldDef->classDef); 
    } else {
      Compiler->resolveVirtualClass(fieldDef->classDef); 
    }
    
    offsetConstant = ConstantInt::get(Type::getInt32Ty(context), fieldDef->num);
  }
  return offsetConstant;
}

llvm::FunctionType* LLVMSignatureInfo::getVirtualType() {
 if (!virtualType) {
    // Lock here because we are called by arbitrary code
    vmkit::VmkitModule::protectIR();
    std::vector<llvm::Type*> llvmArgs;
    uint32 size = signature->nbArguments;
    Typedef* const* arguments = signature->getArgumentsType();

    llvmArgs.push_back(Compiler->getIntrinsics()->JavaObjectType);

    for (uint32 i = 0; i < size; ++i) {
      Typedef* type = arguments[i];
      LLVMAssessorInfo& LAI = Compiler->getTypedefInfo(type);
      llvmArgs.push_back(LAI.llvmType);
    }

    LLVMAssessorInfo& LAI =
      Compiler->getTypedefInfo(signature->getReturnType());
    virtualType = FunctionType::get(LAI.llvmType, llvmArgs, false);
    vmkit::VmkitModule::unprotectIR();
  }
  return virtualType;
}

llvm::FunctionType* LLVMSignatureInfo::getStaticType() {
 if (!staticType) {
    // Lock here because we are called by arbitrary code
    vmkit::VmkitModule::protectIR();
    std::vector<llvm::Type*> llvmArgs;
    uint32 size = signature->nbArguments;
    Typedef* const* arguments = signature->getArgumentsType();

    for (uint32 i = 0; i < size; ++i) {
      Typedef* type = arguments[i];
      LLVMAssessorInfo& LAI = Compiler->getTypedefInfo(type);
      llvmArgs.push_back(LAI.llvmType);
    }

    LLVMAssessorInfo& LAI =
      Compiler->getTypedefInfo(signature->getReturnType());
    staticType = FunctionType::get(LAI.llvmType, llvmArgs, false);
    vmkit::VmkitModule::unprotectIR();
  }
  return staticType;
}

llvm::FunctionType* LLVMSignatureInfo::getNativeType() {
  if (!nativeType) {
    // Lock here because we are called by arbitrary code
    vmkit::VmkitModule::protectIR();
    std::vector<llvm::Type*> llvmArgs;
    uint32 size = signature->nbArguments;
    Typedef* const* arguments = signature->getArgumentsType();
   
    llvm::Type* Ty =
      PointerType::getUnqual(Compiler->getIntrinsics()->JavaObjectType);

    llvmArgs.push_back(Compiler->getIntrinsics()->ptrType); // JNIEnv
    llvmArgs.push_back(Ty); // Class

    for (uint32 i = 0; i < size; ++i) {
      Typedef* type = arguments[i];
      LLVMAssessorInfo& LAI = Compiler->getTypedefInfo(type);
      llvm::Type* Ty = LAI.llvmType;
      if (Ty == Compiler->getIntrinsics()->JavaObjectType) {
        llvmArgs.push_back(LAI.llvmTypePtr);
      } else {
        llvmArgs.push_back(LAI.llvmType);
      }
    }

    LLVMAssessorInfo& LAI =
      Compiler->getTypedefInfo(signature->getReturnType());
    llvm::Type* RetType =
      LAI.llvmType == Compiler->getIntrinsics()->JavaObjectType ?
        LAI.llvmTypePtr : LAI.llvmType;
    nativeType = FunctionType::get(RetType, llvmArgs, false);
    vmkit::VmkitModule::unprotectIR();
  }
  return nativeType;
}

llvm::FunctionType* LLVMSignatureInfo::getNativeStubType() {
  // Lock here because we are called by arbitrary code
  vmkit::VmkitModule::protectIR();
  std::vector<llvm::Type*> llvmArgs;
  uint32 size = signature->nbArguments;
  Typedef* const* arguments = signature->getArgumentsType();
 
  llvm::Type* Ty =
    PointerType::getUnqual(Compiler->getIntrinsics()->JavaObjectType);

  llvmArgs.push_back(PointerType::getUnqual(getNativeType())); // method
  llvmArgs.push_back(Compiler->getIntrinsics()->ptrType); // JNIEnv
  llvmArgs.push_back(Ty); // Class

  for (uint32 i = 0; i < size; ++i) {
    Typedef* type = arguments[i];
    LLVMAssessorInfo& LAI = Compiler->getTypedefInfo(type);
    llvm::Type* Ty = LAI.llvmType;
    if (Ty == Compiler->getIntrinsics()->JavaObjectType) {
      llvmArgs.push_back(LAI.llvmTypePtr);
    } else {
      llvmArgs.push_back(LAI.llvmType);
    }
  }

  LLVMAssessorInfo& LAI =
    Compiler->getTypedefInfo(signature->getReturnType());
  llvm::Type* RetType =
    LAI.llvmType == Compiler->getIntrinsics()->JavaObjectType ?
      LAI.llvmTypePtr : LAI.llvmType;
  FunctionType* FTy = FunctionType::get(RetType, llvmArgs, false);
  vmkit::VmkitModule::unprotectIR();
  return FTy;
}


Function* LLVMSignatureInfo::createFunctionCallBuf(bool virt) {
  
  std::vector<Value*> Args;

  LLVMContext& context = Compiler->getLLVMModule()->getContext();
  J3Intrinsics& Intrinsics = *Compiler->getIntrinsics();
  Function* res = 0;
  FunctionType* FTy = virt ? getVirtualBufType() : getStaticBufType();
  if (virt) {
    res = Compiler->virtualBufs[FTy];
  } else {
    res = Compiler->staticBufs[FTy];
  }
  if (res != NULL) {
    return res;
  }
  if (Compiler->isStaticCompiling()) {
    vmkit::ThreadAllocator allocator;
    const char* type = virt ? "virtual_buf" : "static_buf";
    char* buf = (char*)allocator.Allocate(
        (signature->keyName->size << 1) + 1 + 11);
    signature->nativeName(buf, type);
    res = Function::Create(
        FTy, GlobalValue::ExternalLinkage, buf, Compiler->getLLVMModule());
  } else {
    res = Function::Create(
        FTy, GlobalValue::ExternalLinkage, "", Compiler->getLLVMModule());
  }

  BasicBlock* currentBlock = BasicBlock::Create(context, "enter", res);
  Function::arg_iterator i = res->arg_begin();
  Value *obj, *ptr, *func;
  ++i;
  func = i;
  func->setName("func");
  ++i;
  if (virt) {
    obj = i;
    ++i;
    Args.push_back(obj);
    obj->setName("object");
  }
  ptr = i;
  ptr->setName("nextArgPtr");

  Typedef* const* arguments = signature->getArgumentsType();
  for (uint32 i = 0; i < signature->nbArguments; ++i) {
  
    LLVMAssessorInfo& LAI = Compiler->getTypedefInfo(arguments[i]);
    Value* arg = new LoadInst(ptr, "loadedArg", currentBlock);
    
    if (arguments[i]->isReference()) {
      arg = new IntToPtrInst(arg, Intrinsics.JavaObjectType, "", currentBlock);
      Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ,
                                Intrinsics.JavaObjectNullConstant,
                                arg, "isNullRefArg");
      BasicBlock* endBlock = BasicBlock::Create(context, "refArgDone", res);
      BasicBlock* loadBlock = BasicBlock::Create(context, "loadRefArg", res);
      PHINode* node = PHINode::Create(Intrinsics.JavaObjectType, 2, "refArg",
                                      endBlock);
      node->addIncoming(Intrinsics.JavaObjectNullConstant, currentBlock);
      BranchInst::Create(endBlock, loadBlock, cmp, currentBlock);
      currentBlock = loadBlock;
      arg = new BitCastInst(arg,
                            PointerType::getUnqual(Intrinsics.JavaObjectType),
                            "", currentBlock);
      arg = new LoadInst(arg, "loadedRefArg", false, currentBlock);
      node->addIncoming(arg, currentBlock);
      BranchInst::Create(endBlock, currentBlock);
      currentBlock = endBlock;
      arg = node;
    } else if (arguments[i]->isFloat()) {
      arg = new TruncInst(arg, Compiler->AssessorInfo[I_INT].llvmType,
                          "", currentBlock);
      arg = new BitCastInst(arg, LAI.llvmType, "arg", currentBlock);
    } else if (arguments[i]->isDouble()) {
      arg = new BitCastInst(arg, LAI.llvmType, "arg", currentBlock);
    } else if (!arguments[i]->isLong()){
      arg = new TruncInst(arg, LAI.llvmType, "arg", currentBlock);
    }
    Args.push_back(arg);
    ptr = GetElementPtrInst::Create(ptr, Intrinsics.constantOne,"nextArgPtr",
                                    currentBlock);
  }

  Value* val = CallInst::Create(func, Args, signature->getReturnType()->isVoid() ? "" : "retVal", currentBlock);
  if (!signature->getReturnType()->isVoid()) {
    ReturnInst::Create(context, val, currentBlock);
  } else {
    ReturnInst::Create(context, currentBlock);
  }
  
  res->setGC("vmkit");
  res->addFnAttr(Attribute::NoInline);
  res->addFnAttr(Attribute::NoUnwind);

  if (virt) {
    Compiler->virtualBufs[FTy] = res;
  } else {
    Compiler->staticBufs[FTy] = res;
  }

  return res;
}

Function* LLVMSignatureInfo::createFunctionCallAP(bool virt) {
  
  std::vector<Value*> Args;
  
  J3Intrinsics& Intrinsics = *Compiler->getIntrinsics();
  Function* res = NULL;
  FunctionType* FTy = virt ? getVirtualBufType() : getStaticBufType();
  if (virt) {
    res = Compiler->virtualAPs[FTy];
  } else {
    res = Compiler->staticAPs[FTy];
  }
  if (res != NULL) {
    return res;
  }
  if (Compiler->isStaticCompiling()) {
    vmkit::ThreadAllocator allocator;
    const char* type = virt ? "virtual_ap" : "static_ap";
    char* buf = (char*)allocator.Allocate(
        (signature->keyName->size << 1) + 1 + 11);
    signature->nativeName(buf, type);
    res = Function::Create(
        FTy, GlobalValue::ExternalLinkage, buf, Compiler->getLLVMModule());
  } else {
    res = Function::Create(
        FTy, GlobalValue::ExternalLinkage, "", Compiler->getLLVMModule());
  }
  LLVMContext& context = Compiler->getLLVMModule()->getContext();
  
  BasicBlock* currentBlock = BasicBlock::Create(context, "enter", res);
  Function::arg_iterator i = res->arg_begin();
  Value *obj, *ap, *func;
  ++i;
  func = i;
  ++i;
  if (virt) {
    obj = i;
    Args.push_back(obj);
    ++i;
  }
  ap = i;

  Typedef* const* arguments = signature->getArgumentsType();
  for (uint32 i = 0; i < signature->nbArguments; ++i) {
    LLVMAssessorInfo& LAI = Compiler->getTypedefInfo(arguments[i]);
    Value* arg = new VAArgInst(ap, LAI.llvmType, "", currentBlock);
    if (arguments[i]->isReference()) {
      arg = new IntToPtrInst(arg, Intrinsics.JavaObjectType, "", currentBlock);
      Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ,
                                Intrinsics.JavaObjectNullConstant,
                                arg, "");
      BasicBlock* endBlock = BasicBlock::Create(context, "end", res);
      BasicBlock* loadBlock = BasicBlock::Create(context, "load", res);
      PHINode* node = PHINode::Create(Intrinsics.JavaObjectType, 2, "",
                                      endBlock);
      node->addIncoming(Intrinsics.JavaObjectNullConstant, currentBlock);
      BranchInst::Create(endBlock, loadBlock, cmp, currentBlock);
      currentBlock = loadBlock;
      arg = new BitCastInst(arg,
                            PointerType::getUnqual(Intrinsics.JavaObjectType),
                            "", currentBlock);
      arg = new LoadInst(arg, "", false, currentBlock);
      node->addIncoming(arg, currentBlock);
      BranchInst::Create(endBlock, currentBlock);
      currentBlock = endBlock;
      arg = node;
    }
    Args.push_back(arg);
  }

  Value* val = CallInst::Create(func, Args, "", currentBlock);
  if (!signature->getReturnType()->isVoid()) {
    ReturnInst::Create(context, val, currentBlock);
  } else {
    ReturnInst::Create(context, currentBlock);
  }
  
  res->setGC("vmkit");
  res->addFnAttr(Attribute::NoInline);
  res->addFnAttr(Attribute::NoUnwind);
  
  if (virt) {
    Compiler->virtualAPs[FTy] = res;
  } else {
    Compiler->staticAPs[FTy] = res;
  }
  return res;
}

Function* LLVMSignatureInfo::createFunctionStub(bool special, bool virt) {
  
  std::vector<Value*> Args;
  std::vector<Value*> FunctionArgs;
  std::vector<Value*> TempArgs;
  
  J3Intrinsics& Intrinsics = *Compiler->getIntrinsics();
  Function* stub = NULL;
  FunctionType* FTy = (virt || special)? getVirtualType() : getStaticType();
  if (virt) {
    stub = Compiler->virtualStubs[FTy];
  } else if (special) {
    stub = Compiler->specialStubs[FTy];
  } else {
    stub = Compiler->staticStubs[FTy];
  }
  if (stub != NULL) {
    return stub;
  }
  if (Compiler->isStaticCompiling()) {
    vmkit::ThreadAllocator allocator;
    const char* type = virt ? "virtual_stub" : special ? "special_stub" : "static_stub";
    char* buf = (char*)allocator.Allocate(
        (signature->keyName->size << 1) + 1 + 11);
    signature->nativeName(buf, type);
    stub = Function::Create(
        FTy, GlobalValue::ExternalLinkage, buf, Compiler->getLLVMModule());
  

  } else {
    stub = Function::Create(
        FTy, GlobalValue::ExternalLinkage, "", Compiler->getLLVMModule());
  }
  LLVMContext& context = Compiler->getLLVMModule()->getContext();
  
  BasicBlock* currentBlock = BasicBlock::Create(context, "enter", stub);
  BasicBlock* endBlock = BasicBlock::Create(context, "end", stub);
  BasicBlock* callBlock = BasicBlock::Create(context, "call", stub);
  PHINode* node = NULL;
  if (!signature->getReturnType()->isVoid()) {
    node = PHINode::Create(stub->getReturnType(), 2, "retVal", endBlock);
  }
    

  for (Function::arg_iterator arg = stub->arg_begin();
       arg != stub->arg_end(); ++arg) {
    Value* temp = arg;
    if (Compiler->useCooperativeGC() &&
        arg->getType() == Intrinsics.JavaObjectType) {
      temp = new AllocaInst(Intrinsics.JavaObjectType, "arg", currentBlock);
      new StoreInst(arg, temp, "", currentBlock);
      Value* GCArgs[2] = {
        new BitCastInst(temp, Intrinsics.ptrPtrType, "", currentBlock),
        Intrinsics.constantPtrNull
      };
        
      CallInst::Create(Intrinsics.llvm_gc_gcroot, GCArgs, "", currentBlock);
    } else
    	arg->setName("arg");
    
    TempArgs.push_back(temp);
  }

  if (virt) {
    if (Compiler->useCooperativeGC()) {
      Args.push_back(new LoadInst(TempArgs[0], "object", false, currentBlock));
    } else {
      TempArgs[0]->setName("object");
      Args.push_back(TempArgs[0]);
    }
  }

  Value* val = CallInst::Create(virt ? Intrinsics.ResolveVirtualStubFunction :
                                special ? Intrinsics.ResolveSpecialStubFunction:
                                          Intrinsics.ResolveStaticStubFunction,
                                Args, "resolvedFuncPtr", currentBlock);
  
  Constant* nullValue = Constant::getNullValue(val->getType());
  Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ,
                            nullValue, val, "isNotResolved");
  BranchInst::Create(endBlock, callBlock, cmp, currentBlock);
  if (node) node->addIncoming(Constant::getNullValue(node->getType()),
                              currentBlock);

  currentBlock = callBlock;
  Value* Func = new BitCastInst(val, stub->getType(), "resolvedFunc", currentBlock);
  
  int i = 0;
  for (Function::arg_iterator arg = stub->arg_begin();
       arg != stub->arg_end(); ++arg, ++i) {
    Value* temp = arg;
    if (Compiler->useCooperativeGC() &&
        arg->getType() == Intrinsics.JavaObjectType) {
      temp = new LoadInst(TempArgs[i], "arg", false, currentBlock);
    } else
    	temp->setName("arg");
    FunctionArgs.push_back(temp);
  }
  Value* res = CallInst::Create(Func, FunctionArgs, node ? "funcRetVal" : "", currentBlock);
  if (node) node->addIncoming(res, currentBlock);
  BranchInst::Create(endBlock, currentBlock);

  currentBlock = endBlock;
  if (node) {
    ReturnInst::Create(context, node, currentBlock);
  } else {
    ReturnInst::Create(context, currentBlock);
  }
  
  stub->setGC("vmkit");
  stub->addFnAttr(Attribute::NoInline);
  stub->addFnAttr(Attribute::NoUnwind);
  
  if (virt) {
    Compiler->virtualStubs[FTy] = stub;
  } else if (special) {
    Compiler->specialStubs[FTy] = stub;
  } else {
    Compiler->staticStubs[FTy] = stub;
  }
  return stub;
}

PointerType* LLVMSignatureInfo::getStaticPtrType() {
  if (!staticPtrType) {
    staticPtrType = PointerType::getUnqual(getStaticType());
  }
  return staticPtrType;
}

PointerType* LLVMSignatureInfo::getVirtualPtrType() {
  if (!virtualPtrType) {
    virtualPtrType = PointerType::getUnqual(getVirtualType());
  }
  return virtualPtrType;
}

PointerType* LLVMSignatureInfo::getNativePtrType() {
  if (!nativePtrType) {
    nativePtrType = PointerType::getUnqual(getNativeType());
  }
  return nativePtrType;
}


FunctionType* LLVMSignatureInfo::getVirtualBufType() {
  if (!virtualBufType) {
    // Lock here because we are called by arbitrary code
    vmkit::VmkitModule::protectIR();
    std::vector<llvm::Type*> Args;
    Args.push_back(Compiler->getIntrinsics()->ResolvedConstantPoolType); // ctp
    Args.push_back(getVirtualPtrType());
    Args.push_back(Compiler->getIntrinsics()->JavaObjectType);
    Args.push_back(Compiler->AssessorInfo[I_LONG].llvmTypePtr);
    LLVMAssessorInfo& LAI =
      Compiler->getTypedefInfo(signature->getReturnType());
    virtualBufType = FunctionType::get(LAI.llvmType, Args, false);
    vmkit::VmkitModule::unprotectIR();
  }
  return virtualBufType;
}

FunctionType* LLVMSignatureInfo::getStaticBufType() {
  if (!staticBufType) {
    // Lock here because we are called by arbitrary code
    vmkit::VmkitModule::protectIR();
    std::vector<llvm::Type*> Args;
    Args.push_back(Compiler->getIntrinsics()->ResolvedConstantPoolType); // ctp
    Args.push_back(getStaticPtrType());
    Args.push_back(Compiler->AssessorInfo[I_LONG].llvmTypePtr);
    LLVMAssessorInfo& LAI =
      Compiler->getTypedefInfo(signature->getReturnType());
    staticBufType = FunctionType::get(LAI.llvmType, Args, false);
    vmkit::VmkitModule::unprotectIR();
  }
  return staticBufType;
}

Function* LLVMSignatureInfo::getVirtualBuf() {
  // Lock here because we are called by arbitrary code. Also put that here
  // because we are waiting on virtualBufFunction to have an address.
  vmkit::VmkitModule::protectIR();
  if (!virtualBufFunction) {
    virtualBufFunction = createFunctionCallBuf(true);
    signature->setVirtualCallBuf(Compiler->GenerateStub(virtualBufFunction));
  }
  vmkit::VmkitModule::unprotectIR();
  return virtualBufFunction;
}

Function* LLVMSignatureInfo::getVirtualAP() {
  // Lock here because we are called by arbitrary code. Also put that here
  // because we are waiting on virtualAPFunction to have an address.
  vmkit::VmkitModule::protectIR();
  if (!virtualAPFunction) {
    virtualAPFunction = createFunctionCallAP(true);
    signature->setVirtualCallAP(Compiler->GenerateStub(virtualAPFunction));
  }
  vmkit::VmkitModule::unprotectIR();
  return virtualAPFunction;
}

Function* LLVMSignatureInfo::getStaticBuf() {
  // Lock here because we are called by arbitrary code. Also put that here
  // because we are waiting on staticBufFunction to have an address.
  vmkit::VmkitModule::protectIR();
  if (!staticBufFunction) {
    staticBufFunction = createFunctionCallBuf(false);
    signature->setStaticCallBuf(Compiler->GenerateStub(staticBufFunction));
  }
  vmkit::VmkitModule::unprotectIR();
  return staticBufFunction;
}

Function* LLVMSignatureInfo::getStaticAP() {
  // Lock here because we are called by arbitrary code. Also put that here
  // because we are waiting on staticAPFunction to have an address.
  vmkit::VmkitModule::protectIR();
  if (!staticAPFunction) {
    staticAPFunction = createFunctionCallAP(false);
    signature->setStaticCallAP(Compiler->GenerateStub(staticAPFunction));
  }
  vmkit::VmkitModule::unprotectIR();
  return staticAPFunction;
}

Function* LLVMSignatureInfo::getStaticStub() {
  // Lock here because we are called by arbitrary code. Also put that here
  // because we are waiting on staticStubFunction to have an address.
  vmkit::VmkitModule::protectIR();
  if (!staticStubFunction) {
    staticStubFunction = createFunctionStub(false, false);
    signature->setStaticCallStub(Compiler->GenerateStub(staticStubFunction));
  }
  vmkit::VmkitModule::unprotectIR();
  return staticStubFunction;
}

Function* LLVMSignatureInfo::getSpecialStub() {
  // Lock here because we are called by arbitrary code. Also put that here
  // because we are waiting on specialStubFunction to have an address.
  vmkit::VmkitModule::protectIR();
  if (!specialStubFunction) {
    specialStubFunction = createFunctionStub(true, false);
    signature->setSpecialCallStub(Compiler->GenerateStub(specialStubFunction));
  }
  vmkit::VmkitModule::unprotectIR();
  return specialStubFunction;
}

Function* LLVMSignatureInfo::getVirtualStub() {
  // Lock here because we are called by arbitrary code. Also put that here
  // because we are waiting on virtualStubFunction to have an address.
  vmkit::VmkitModule::protectIR();
  if (!virtualStubFunction) {
    virtualStubFunction = createFunctionStub(false, true);
    signature->setVirtualCallStub(Compiler->GenerateStub(virtualStubFunction));
  }
  vmkit::VmkitModule::unprotectIR();
  return virtualStubFunction;
}

void JavaLLVMCompiler::initialiseAssessorInfo() {
  AssessorInfo[I_VOID].llvmType = Type::getVoidTy(getLLVMContext());
  AssessorInfo[I_VOID].llvmTypePtr = 0;
  AssessorInfo[I_VOID].logSizeInBytesConstant = 0;
  
  AssessorInfo[I_BOOL].llvmType = Type::getInt8Ty(getLLVMContext());
  AssessorInfo[I_BOOL].llvmTypePtr =
    PointerType::getUnqual(Type::getInt8Ty(getLLVMContext()));
  AssessorInfo[I_BOOL].logSizeInBytesConstant = 0;
  
  AssessorInfo[I_BYTE].llvmType = Type::getInt8Ty(getLLVMContext());
  AssessorInfo[I_BYTE].llvmTypePtr =
    PointerType::getUnqual(Type::getInt8Ty(getLLVMContext()));
  AssessorInfo[I_BYTE].logSizeInBytesConstant = 0;
  
  AssessorInfo[I_SHORT].llvmType = Type::getInt16Ty(getLLVMContext());
  AssessorInfo[I_SHORT].llvmTypePtr =
    PointerType::getUnqual(Type::getInt16Ty(getLLVMContext()));
  AssessorInfo[I_SHORT].logSizeInBytesConstant = 1;
  
  AssessorInfo[I_CHAR].llvmType = Type::getInt16Ty(getLLVMContext());
  AssessorInfo[I_CHAR].llvmTypePtr =
    PointerType::getUnqual(Type::getInt16Ty(getLLVMContext()));
  AssessorInfo[I_CHAR].logSizeInBytesConstant = 1;
  
  AssessorInfo[I_INT].llvmType = Type::getInt32Ty(getLLVMContext());
  AssessorInfo[I_INT].llvmTypePtr =
    PointerType::getUnqual(Type::getInt32Ty(getLLVMContext()));
  AssessorInfo[I_INT].logSizeInBytesConstant = 2;
  
  AssessorInfo[I_FLOAT].llvmType = Type::getFloatTy(getLLVMContext());
  AssessorInfo[I_FLOAT].llvmTypePtr =
    PointerType::getUnqual(Type::getFloatTy(getLLVMContext()));
  AssessorInfo[I_FLOAT].logSizeInBytesConstant = 2;
  
  AssessorInfo[I_LONG].llvmType = Type::getInt64Ty(getLLVMContext());
  AssessorInfo[I_LONG].llvmTypePtr =
    PointerType::getUnqual(Type::getInt64Ty(getLLVMContext()));
  AssessorInfo[I_LONG].logSizeInBytesConstant = 3;
  
  AssessorInfo[I_DOUBLE].llvmType = Type::getDoubleTy(getLLVMContext());
  AssessorInfo[I_DOUBLE].llvmTypePtr =
    PointerType::getUnqual(Type::getDoubleTy(getLLVMContext()));
  AssessorInfo[I_DOUBLE].logSizeInBytesConstant = 3;
  
  AssessorInfo[I_TAB].llvmType = JavaIntrinsics.JavaObjectType;
  AssessorInfo[I_TAB].llvmTypePtr =
    PointerType::getUnqual(AssessorInfo[I_TAB].llvmType);
  AssessorInfo[I_TAB].logSizeInBytesConstant = sizeof(JavaObject*) == 8 ? 3 : 2;
  
  AssessorInfo[I_REF].llvmType = AssessorInfo[I_TAB].llvmType;
  AssessorInfo[I_REF].llvmTypePtr = AssessorInfo[I_TAB].llvmTypePtr;
  AssessorInfo[I_REF].logSizeInBytesConstant = sizeof(JavaObject*) == 8 ? 3 : 2;
}

LLVMAssessorInfo& JavaLLVMCompiler::getTypedefInfo(const Typedef* type) {
  return AssessorInfo[type->getKey()->elements[0]];
}
