blob: 3ad411facefa8e46805cca0afbadf0c9b1cdbe8a [file] [log] [blame]
//===--------- JavaJITCompiler.cpp - Support for JIT compiling -------------===//
//
// The VMKit project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/DebugInfo.h"
#include "llvm/CodeGen/GCStrategy.h"
#include <llvm/CodeGen/JITCodeEmitter.h>
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/DataLayout.h"
#include <lib/ExecutionEngine/JIT/JIT.h>
#include "VmkitGC.h"
#include "vmkit/VirtualMachine.h"
#include "JavaClass.h"
#include "JavaConstantPool.h"
#include "JavaThread.h"
#include "JavaTypes.h"
#include "Jnjvm.h"
#include "j3/JavaJITCompiler.h"
#include "j3/J3Intrinsics.h"
using namespace j3;
using namespace llvm;
void JavaJITListener::NotifyFunctionEmitted(const Function &F,
void *Code, size_t Size,
const EmittedFunctionDetails &Details) {
assert(F.hasGC());
if (TheCompiler->GCInfo == NULL) {
TheCompiler->GCInfo = Details.MF->getGMI();
}
assert(TheCompiler->GCInfo == Details.MF->getGMI());
}
Constant* JavaJITCompiler::getNativeClass(CommonClass* classDef) {
Type* Ty = classDef->isClass() ? JavaIntrinsics.JavaClassType :
JavaIntrinsics.JavaCommonClassType;
ConstantInt* CI = ConstantInt::get(Type::getInt64Ty(getLLVMContext()),
uint64_t(classDef));
return ConstantExpr::getIntToPtr(CI, Ty);
}
Constant* JavaJITCompiler::getResolvedConstantPool(JavaConstantPool* ctp) {
void* ptr = ctp->ctpRes;
assert(ptr && "No constant pool found");
ConstantInt* CI = ConstantInt::get(Type::getInt64Ty(getLLVMContext()),
uint64_t(ptr));
return ConstantExpr::getIntToPtr(CI, JavaIntrinsics.ResolvedConstantPoolType);
}
Constant* JavaJITCompiler::getMethodInClass(JavaMethod* meth) {
ConstantInt* CI = ConstantInt::get(Type::getInt64Ty(getLLVMContext()),
(int64_t)meth);
return ConstantExpr::getIntToPtr(CI, JavaIntrinsics.JavaMethodType);
}
Constant* JavaJITCompiler::getString(JavaString* str) {
llvm_gcroot(str, 0);
fprintf(stderr, "Should not be here\n");
abort();
}
Constant* JavaJITCompiler::getStringPtr(JavaString** str) {
assert(str && "No string given");
Type* Ty = PointerType::getUnqual(JavaIntrinsics.JavaObjectType);
ConstantInt* CI = ConstantInt::get(Type::getInt64Ty(getLLVMContext()),
uint64(str));
return ConstantExpr::getIntToPtr(CI, Ty);
}
Constant* JavaJITCompiler::getJavaClass(CommonClass* cl) {
fprintf(stderr, "Should not be here\n");
abort();
}
Constant* JavaJITCompiler::getJavaClassPtr(CommonClass* cl) {
Jnjvm* vm = JavaThread::get()->getJVM();
JavaObject* const* obj = cl->getClassDelegateePtr(vm);
assert(obj && "Delegatee not created");
Constant* CI = ConstantInt::get(Type::getInt64Ty(getLLVMContext()),
uint64(obj));
Type* Ty = PointerType::getUnqual(JavaIntrinsics.JavaObjectType);
return ConstantExpr::getIntToPtr(CI, Ty);
}
JavaObject* JavaJITCompiler::getFinalObject(llvm::Value* obj) {
// obj can not encode direclty an object.
return NULL;
}
Constant* JavaJITCompiler::getFinalObject(JavaObject* obj, CommonClass* cl) {
llvm_gcroot(obj, 0);
return NULL;
}
Constant* JavaJITCompiler::getStaticInstance(Class* classDef) {
void* obj = classDef->getStaticInstance();
if (!obj) {
classDef->acquire();
obj = classDef->getStaticInstance();
if (!obj) {
// Allocate now so that compiled code can reference it.
obj = classDef->allocateStaticInstance(JavaThread::get()->getJVM());
}
classDef->release();
}
Constant* CI = ConstantInt::get(Type::getInt64Ty(getLLVMContext()),
(uint64_t(obj)));
return ConstantExpr::getIntToPtr(CI, JavaIntrinsics.ptrType);
}
Constant* JavaJITCompiler::getVirtualTable(JavaVirtualTable* VT) {
if (VT->cl->isClass()) {
LLVMClassInfo* LCI = getClassInfo(VT->cl->asClass());
LCI->getVirtualType();
}
ConstantInt* CI = ConstantInt::get(Type::getInt64Ty(getLLVMContext()),
uint64_t(VT));
return ConstantExpr::getIntToPtr(CI, JavaIntrinsics.VTType);
}
Constant* JavaJITCompiler::getNativeFunction(JavaMethod* meth, void* ptr) {
LLVMSignatureInfo* LSI = getSignatureInfo(meth->getSignature());
Type* valPtrType = LSI->getNativePtrType();
assert(ptr && "No native function given");
Constant* CI = ConstantInt::get(Type::getInt64Ty(getLLVMContext()),
uint64_t(ptr));
return ConstantExpr::getIntToPtr(CI, valPtrType);
}
JavaJITCompiler::JavaJITCompiler(
const std::string &ModuleID, bool compiling_garbage_collector) :
JavaLLVMCompiler(ModuleID, compiling_garbage_collector), listener(this) {
EmitFunctionName = false;
GCInfo = NULL;
EngineBuilder engine(TheModule);
TargetOptions options;
options.NoFramePointerElim = true;
engine.setTargetOptions(options);
engine.setEngineKind(EngineKind::JIT);
executionEngine = engine.create();
executionEngine->RegisterJITEventListener(&listener);
TheDataLayout = executionEngine->getDataLayout();
TheModule->setDataLayout(TheDataLayout->getStringRepresentation());
TheModule->setTargetTriple(vmkit::VmkitModule::getHostTriple());
JavaIntrinsics.init(TheModule);
initialiseAssessorInfo();
addJavaPasses();
// Set the pointer to methods that will be inlined, so that these methods
// do not get compiled by the JIT.
executionEngine->updateGlobalMapping(
JavaIntrinsics.AllocateFunction, (void*)(word_t)vmkitgcmalloc);
executionEngine->updateGlobalMapping(
JavaIntrinsics.VTAllocateFunction, (void*)(word_t)VTgcmalloc);
executionEngine->updateGlobalMapping(
JavaIntrinsics.ArrayWriteBarrierFunction, (void*)(word_t)arrayWriteBarrier);
executionEngine->updateGlobalMapping(
JavaIntrinsics.FieldWriteBarrierFunction, (void*)(word_t)fieldWriteBarrier);
executionEngine->updateGlobalMapping(
JavaIntrinsics.NonHeapWriteBarrierFunction, (void*)(word_t)nonHeapWriteBarrier);
executionEngine->updateGlobalMapping(JavaIntrinsics.IsSecondaryClassFunctionInner, (void*)(word_t) IsSubtypeIntrinsic);
executionEngine->updateGlobalMapping(JavaIntrinsics.IsSubclassOfFunctionInner, (void*)(word_t) IsSubtypeIntrinsic);
executionEngine->updateGlobalMapping(JavaIntrinsics.CheckIfAssignable, (void*)(word_t) CheckIfObjectIsAssignableToArrayPosition);
}
JavaJITCompiler::~JavaJITCompiler() {
executionEngine->removeModule(TheModule);
delete executionEngine;
// ~JavaLLVMCompiler will delete the module.
}
void JavaJITCompiler::makeVT(Class* cl) {
JavaVirtualTable* VT = cl->virtualVT;
assert(VT && "No VT was allocated!");
if (VT->init) {
// The VT has already been filled by the AOT compiler so there
// is nothing left to do!
return;
}
Class* current = cl;
word_t* functions = VT->getFunctions();
while (current != NULL) {
// Fill the virtual table with function pointers.
for (uint32 i = 0; i < current->nbVirtualMethods; ++i) {
JavaMethod& meth = current->virtualMethods[i];
if (meth.offset != 0 || current->super != NULL) {
functions[meth.offset] = getPointerOrStub(meth, JavaMethod::Virtual);
}
}
current = current->super;
}
}
extern "C" void ThrowUnfoundInterface() {
JavaThread *th = JavaThread::get();
BEGIN_NATIVE_EXCEPTION(1);
// Lookup the caller of this class.
vmkit::StackWalker Walker(th);
vmkit::FrameInfo* FI = Walker.get();
assert(FI->Metadata != NULL && "Wrong stack trace");
JavaMethod* meth = (JavaMethod*)FI->Metadata;
// Lookup the method info in the constant pool of the caller.
uint16 ctpIndex = meth->lookupCtpIndex(FI);
assert(ctpIndex && "No constant pool index");
JavaConstantPool* ctpInfo = meth->classDef->getConstantPool();
CommonClass* ctpCl = 0;
const UTF8* utf8 = 0;
Signdef* sign = 0;
ctpInfo->resolveMethod(ctpIndex, ctpCl, utf8, sign);
JavaThread::get()->getJVM()->abstractMethodError(ctpCl, utf8);
END_NATIVE_EXCEPTION
}
void JavaJITCompiler::makeIMT(Class* cl) {
InterfaceMethodTable* IMT = cl->virtualVT->IMT;
if (!IMT) return;
std::set<JavaMethod*> contents[InterfaceMethodTable::NumIndexes];
cl->fillIMT(contents);
for (uint32_t i = 0; i < InterfaceMethodTable::NumIndexes; ++i) {
std::set<JavaMethod*>& atIndex = contents[i];
uint32_t size = atIndex.size();
if (size == 1) {
JavaMethod* Imeth = *(atIndex.begin());
JavaMethod* meth = cl->lookupMethodDontThrow(Imeth->name,
Imeth->type,
false, true, 0);
if (meth) {
IMT->contents[i] = getPointerOrStub(*meth, JavaMethod::Interface);
} else {
IMT->contents[i] = (word_t)ThrowUnfoundInterface;
}
} else if (size > 1) {
std::vector<JavaMethod*> methods;
bool SameMethod = true;
JavaMethod* OldMethod = 0;
for (std::set<JavaMethod*>::iterator it = atIndex.begin(),
et = atIndex.end(); it != et; ++it) {
JavaMethod* Imeth = *it;
JavaMethod* Cmeth = cl->lookupMethodDontThrow(Imeth->name, Imeth->type,
false, true, 0);
if (OldMethod && OldMethod != Cmeth) SameMethod = false;
else OldMethod = Cmeth;
methods.push_back(Cmeth);
}
if (SameMethod) {
if (methods[0]) {
IMT->contents[i] = getPointerOrStub(*(methods[0]),
JavaMethod::Interface);
} else {
IMT->contents[i] = (word_t)ThrowUnfoundInterface;
}
} else {
// Add one to have a NULL-terminated table.
uint32_t length = (2 * size + 1) * sizeof(word_t);
word_t* table = (word_t*)
cl->classLoader->allocator.Allocate(length, "IMT");
IMT->contents[i] = (word_t)table | 1;
uint32_t j = 0;
std::set<JavaMethod*>::iterator Interf = atIndex.begin();
for (std::vector<JavaMethod*>::iterator it = methods.begin(),
et = methods.end(); it != et; ++it, j += 2, ++Interf) {
JavaMethod* Imeth = *Interf;
JavaMethod* Cmeth = *it;
assert(Imeth != NULL);
assert(j < 2 * size - 1);
table[j] = (word_t)Imeth;
if (Cmeth) {
table[j + 1] = getPointerOrStub(*Cmeth, JavaMethod::Interface);
} else {
table[j + 1] = (word_t)ThrowUnfoundInterface;
}
}
assert(Interf == atIndex.end());
}
}
}
}
void JavaJITCompiler::setMethod(Function* func, void* ptr, const char* name) {
func->setLinkage(GlobalValue::ExternalLinkage);
func->setName(name);
assert(ptr && "No value given");
executionEngine->updateGlobalMapping(func, ptr);
}
void* JavaJITCompiler::materializeFunction(JavaMethod* meth, Class* customizeFor) {
vmkit::VmkitModule::protectIR();
Function* func = parseFunction(meth, customizeFor);
void* res = executionEngine->getPointerToGlobal(func);
if (!func->isDeclaration()) {
llvm::GCFunctionInfo& GFI = GCInfo->getFunctionInfo(*func);
Jnjvm* vm = JavaThread::get()->getJVM();
vmkit::VmkitModule::addToVM(vm, &GFI, (JIT*)executionEngine, allocator, meth);
// Now that it's compiled, we don't need the IR anymore
func->deleteBody();
}
vmkit::VmkitModule::unprotectIR();
if (customizeFor == NULL || !getMethodInfo(meth)->isCustomizable) {
meth->code = res;
}
return res;
}
void* JavaJITCompiler::GenerateStub(llvm::Function* F) {
vmkit::VmkitModule::protectIR();
void* res = executionEngine->getPointerToGlobal(F);
// If the stub was already generated through an equivalent signature,
// The body has been deleted, so the function just becomes a declaration.
if (!F->isDeclaration()) {
llvm::GCFunctionInfo& GFI = GCInfo->getFunctionInfo(*F);
Jnjvm* vm = JavaThread::get()->getJVM();
vmkit::VmkitModule::addToVM(vm, &GFI, (JIT*)executionEngine, allocator, NULL);
// Now that it's compiled, we don't need the IR anymore
F->deleteBody();
}
vmkit::VmkitModule::unprotectIR();
return res;
}
// Helper function to run an executable with a JIT
extern "C" int StartJnjvmWithJIT(int argc, char** argv, char* mainClass) {
llvm::llvm_shutdown_obj X;
vmkit::VmkitModule::initialise(argc, argv);
vmkit::Collector::initialise(argc, argv);
vmkit::ThreadAllocator allocator;
char** newArgv = (char**)allocator.Allocate((argc + 1) * sizeof(char*));
memcpy(newArgv + 1, argv, argc * sizeof(void*));
newArgv[0] = newArgv[1];
newArgv[1] = mainClass;
vmkit::BumpPtrAllocator Allocator;
JavaJITCompiler* Comp = JavaJITCompiler::CreateCompiler("JITModule");
JnjvmBootstrapLoader* loader = new(Allocator, "Bootstrap loader")
JnjvmBootstrapLoader(Allocator, Comp, true);
Jnjvm* vm = new(Allocator, "VM") Jnjvm(Allocator, NULL, loader);
vm->runApplication(argc + 1, newArgv);
vm->waitForExit();
return 0;
}
word_t JavaJ3LazyJITCompiler::getPointerOrStub(JavaMethod& meth,
int side) {
return meth.getSignature()->getVirtualCallStub();
}
Value* JavaJ3LazyJITCompiler::addCallback(Class* cl, uint16 index,
Signdef* sign, bool stat,
llvm::BasicBlock* insert) {
LLVMSignatureInfo* LSI = getSignatureInfo(sign);
// Set the stub in the constant pool.
JavaConstantPool* ctpInfo = cl->ctpInfo;
word_t stub = stat ? sign->getStaticCallStub() : sign->getSpecialCallStub();
if (!ctpInfo->ctpRes[index]) {
// Do a compare and swap, so that we do not overwrite what a stub might
// have just updated.
word_t val = (word_t)
__sync_val_compare_and_swap(&(ctpInfo->ctpRes[index]), NULL, (void*)stub);
// If there is something in the the constant pool that is not NULL nor
// the stub, then it's the method.
if (val != 0 && val != stub) {
return ConstantExpr::getIntToPtr(
ConstantInt::get(Type::getInt64Ty(insert->getContext()), val),
stat ? LSI->getStaticPtrType() : LSI->getVirtualPtrType());
}
}
// Load the constant pool.
Value* CTP = getResolvedConstantPool(ctpInfo);
Value* Index = ConstantInt::get(Type::getInt32Ty(insert->getContext()),
index);
Value* func = GetElementPtrInst::Create(CTP, Index, "", insert);
func = new LoadInst(func, "", false, insert);
// Bitcast it to the LLVM function.
func = new BitCastInst(func, stat ? LSI->getStaticPtrType() :
LSI->getVirtualPtrType(),
"", insert);
return func;
}
bool JavaJ3LazyJITCompiler::needsCallback(JavaMethod* meth,
Class* customizeFor,
bool* needsInit) {
*needsInit = true;
return (meth == NULL ||
getMethod(meth, customizeFor)->hasExternalWeakLinkage());
}
JavaJ3LazyJITCompiler::JavaJ3LazyJITCompiler(
const std::string& ModuleID, bool compiling_garbage_collector) :
JavaJITCompiler(ModuleID, compiling_garbage_collector) {}
JavaJITCompiler* JavaJITCompiler::CreateCompiler(
const std::string& ModuleID, bool compiling_garbage_collector)
{
return new JavaJ3LazyJITCompiler(ModuleID, compiling_garbage_collector);
}