| //===----------- JavaJIT.cpp - Java just in time compiler -----------------===// |
| // |
| // The VMKit project |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| |
| //#define DEBUG 0 |
| #define JNJVM_COMPILE 2 |
| #define JNJVM_EXECUTE 0 |
| |
| #include <string> |
| #include <sstream> |
| #include <cstring> |
| |
| #include <llvm/IR/Constants.h> |
| #include <llvm/IR/DerivedTypes.h> |
| #include <llvm/IR/Function.h> |
| #include <llvm/IR/Instructions.h> |
| #include <llvm/IR/Module.h> |
| #include <llvm/IR/Type.h> |
| #include <llvm/Support/CFG.h> |
| |
| #include "vmkit/JIT.h" |
| #include "vmkit/GC.h" |
| |
| #include "debug.h" |
| #include "JavaArray.h" |
| #include "JavaClass.h" |
| #include "JavaConstantPool.h" |
| #include "JavaObject.h" |
| #include "JavaJIT.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/J3Intrinsics.h" |
| |
| using namespace j3; |
| using namespace llvm; |
| using namespace std; |
| |
| void JavaJIT::updateStackInfo(Opinfo& info) { |
| if (stackSize()) { |
| if (!info.stack.size()) { |
| info.stack = stack; |
| } else { |
| int size = stack.size(); |
| info.stack.clear(); |
| for (int i = 0 ; i < size; i++) { |
| info.stack.push_back(MetaInfo(stack[i].type, NOP)); |
| } |
| } |
| } |
| } |
| |
| bool JavaJIT::needsInitialisationCheck(Class* cl) { |
| if (cl->isReadyForCompilation() || |
| (!cl->isInterface() && compilingClass->isSubclassOf(cl))) { |
| return false; |
| } |
| |
| if (!cl->needsInitialisationCheck()) { |
| if (!cl->isReady()) { |
| cl->setInitializationState(ready); |
| } |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void JavaJIT::checkYieldPoint() { |
| if (!TheCompiler->useCooperativeGC()) return; |
| Value* YieldPtr = getDoYieldPtr(getMutatorThreadPtr()); |
| |
| Value* Yield = new LoadInst(YieldPtr, "yield", currentBlock); |
| |
| BasicBlock* continueBlock = createBasicBlock("afterSafePoint"); |
| BasicBlock* yieldBlock = createBasicBlock("inSafePoint"); |
| BranchInst::Create(yieldBlock, continueBlock, Yield, currentBlock); |
| |
| currentBlock = yieldBlock; |
| CallInst::Create(intrinsics->conditionalSafePoint, "", currentBlock); |
| BranchInst::Create(continueBlock, currentBlock); |
| |
| currentBlock = continueBlock; |
| } |
| |
| bool JavaJIT::canBeInlined(JavaMethod* meth, bool customizing) { |
| if (inlineMethods[meth]) return false; |
| if (isSynchro(meth->access)) return false; |
| if (isNative(meth->access)) return false; |
| |
| JavaAttribute* codeAtt = meth->lookupAttribute(JavaAttribute::codeAttribute); |
| if (codeAtt == NULL) return false; |
| |
| Reader reader(codeAtt, meth->classDef->bytes); |
| /* uint16 maxStack = */ reader.readU2(); |
| /* uint16 maxLocals = */ reader.readU2(); |
| uint32 codeLen = reader.readU4(); |
| uint32 start = reader.cursor; |
| reader.seek(codeLen, Reader::SeekCur); |
| uint16 handlers = reader.readU2(); |
| if (handlers != 0) return false; |
| reader.cursor = start; |
| |
| JavaJIT jit(TheCompiler, meth, llvmFunction, customizing ? customizeFor : NULL); |
| jit.inlineMethods = inlineMethods; |
| jit.inlineMethods[meth] = true; |
| if (!jit.analyzeForInlining(reader, codeLen)) return false; |
| jit.inlineMethods[meth] = false; |
| return true; |
| } |
| |
| bool JavaJIT::isThisReference(int stackIndex) { |
| return !overridesThis |
| && (stack[stackIndex].bytecode == ALOAD_0) |
| && !isStatic(compilingMethod->access); |
| } |
| |
| void JavaJIT::invokeVirtual(uint16 index) { |
| JavaObject* source = 0; |
| llvm_gcroot(source, 0); |
| |
| JavaConstantPool* ctpInfo = compilingClass->ctpInfo; |
| CommonClass* cl = 0; |
| JavaMethod* meth = 0; |
| ctpInfo->infoOfMethod(index, ACC_VIRTUAL, cl, meth); |
| bool canBeDirect = false; |
| Value* val = NULL; // The return from the method. |
| const UTF8* name = 0; |
| Signdef* signature = ctpInfo->infoOfInterfaceOrVirtualMethod(index, name); |
| |
| bool customized = false; |
| bool thisReference = |
| isThisReference(stackSize() - signature->getNumberOfSlots() - 1); |
| if (thisReference && meth) { |
| isCustomizable = true; |
| if ((customizeFor != NULL) |
| && customizeFor->isSubclassOf(cl)) { |
| meth = customizeFor->lookupMethodDontThrow( |
| meth->name, meth->type, false, true, NULL); |
| assert(meth); |
| canBeDirect = true; |
| customized = true; |
| assert(!meth->classDef->isInterface()); |
| assert(!isAbstract(meth->access)); |
| } |
| } |
| |
| if (meth && ((cl && isFinal(cl->access)) || |
| isFinal(meth->access) || isPrivate(meth->access))) { |
| canBeDirect = true; |
| } |
| |
| if (meth && isInterface(meth->classDef->access)) { |
| // This can happen because we compute miranda methods before resolving |
| // interfaces. |
| return invokeInterface(index); |
| } |
| |
| |
| if (TheCompiler->isStaticCompiling()) { |
| Value* obj = objectStack[stack.size() - signature->nbArguments - 1]; |
| source = TheCompiler->getFinalObject(obj); |
| if (source) { |
| canBeDirect = true; |
| CommonClass* sourceClass = JavaObject::getClass(source); |
| Class* lookup = sourceClass->isArray() ? sourceClass->super : |
| sourceClass->asClass(); |
| meth = lookup->lookupMethodDontThrow(name, signature->keyName, false, |
| true, 0); |
| } |
| CommonClass* unique = TheCompiler->getUniqueBaseClass(cl); |
| if (unique) { |
| canBeDirect = true; |
| Class* lookup = unique->isArray() ? unique->super : unique->asClass(); |
| meth = lookup->lookupMethodDontThrow(name, signature->keyName, false, |
| true, 0); |
| } |
| } |
| assert((meth || !canBeDirect) && "Can't directly call a method we don't have"); |
| |
| Typedef* retTypedef = signature->getReturnType(); |
| std::vector<Value*> args; // size = [signature->nbIn + 3]; |
| LLVMSignatureInfo* LSI = TheCompiler->getSignatureInfo(signature); |
| llvm::FunctionType* virtualType = LSI->getVirtualType(); |
| FunctionType::param_iterator it = virtualType->param_end(); |
| llvm::Type* retType = virtualType->getReturnType(); |
| |
| bool needsInit = false; |
| if (canBeDirect && canBeInlined(meth, customized)) { |
| makeArgs(it, index, args, signature->nbArguments + 1); |
| if (!thisReference) JITVerifyNull(args[0]); |
| val = invokeInline(meth, args, customized); |
| } else if (canBeDirect && |
| !TheCompiler->needsCallback(meth, customized ? customizeFor : NULL, &needsInit)) { |
| makeArgs(it, index, args, signature->nbArguments + 1); |
| if (!thisReference) JITVerifyNull(args[0]); |
| val = invoke(TheCompiler->getMethod(meth, customized ? customizeFor : NULL), |
| args, "", currentBlock); |
| } else { |
| |
| BasicBlock* endBlock = 0; |
| PHINode* node = 0; |
| Value* indexes2[2]; |
| indexes2[0] = intrinsics->constantZero; |
| bool nullChecked = false; |
| |
| if (meth) { |
| LLVMMethodInfo* LMI = TheCompiler->getMethodInfo(meth); |
| Constant* Offset = LMI->getOffset(); |
| indexes2[1] = Offset; |
| } else { |
| nullChecked = true; |
| GlobalVariable* GV = new GlobalVariable(*llvmFunction->getParent(), |
| Type::getInt32Ty(*llvmContext), |
| false, |
| GlobalValue::ExternalLinkage, |
| intrinsics->constantZero, ""); |
| |
| BasicBlock* resolveVirtual = createBasicBlock("resolveVirtual"); |
| BasicBlock* endResolveVirtual = createBasicBlock("endResolveVirtual"); |
| PHINode* node = PHINode::Create(Type::getInt32Ty(*llvmContext), 2, "", |
| endResolveVirtual); |
| |
| Value* load = new LoadInst(GV, "", false, currentBlock); |
| Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, load, |
| intrinsics->constantZero, ""); |
| BranchInst::Create(resolveVirtual, endResolveVirtual, test, currentBlock); |
| node->addIncoming(load, currentBlock); |
| currentBlock = resolveVirtual; |
| std::vector<Value*> Args; |
| Args.push_back(TheCompiler->getNativeClass(compilingClass)); |
| Args.push_back(ConstantInt::get(Type::getInt32Ty(*llvmContext), index)); |
| Args.push_back(GV); |
| Value* targetObject = getTarget(signature); |
| targetObject = new LoadInst(targetObject, "", false, currentBlock); |
| if (!thisReference) JITVerifyNull(targetObject); |
| Args.push_back(targetObject); |
| load = invoke(intrinsics->VirtualLookupFunction, Args, "", currentBlock); |
| node->addIncoming(load, currentBlock); |
| BranchInst::Create(endResolveVirtual, currentBlock); |
| currentBlock = endResolveVirtual; |
| |
| indexes2[1] = node; |
| } |
| |
| makeArgs(it, index, args, signature->nbArguments + 1); |
| if (!nullChecked && !thisReference) JITVerifyNull(args[0]); |
| Value* VT = CallInst::Create(intrinsics->GetVTFunction, args[0], "", |
| currentBlock); |
| |
| Value* FuncPtr = GetElementPtrInst::Create(VT, indexes2, "", currentBlock); |
| |
| Value* Func = new LoadInst(FuncPtr, "", currentBlock); |
| |
| Func = new BitCastInst(Func, LSI->getVirtualPtrType(), "", currentBlock); |
| val = invoke(Func, args, "", currentBlock); |
| |
| if (endBlock) { |
| if (node) { |
| node->addIncoming(val, currentBlock); |
| val = node; |
| } |
| BranchInst::Create(endBlock, currentBlock); |
| currentBlock = endBlock; |
| } |
| } |
| |
| if (retType != Type::getVoidTy(*llvmContext)) { |
| if (retType == intrinsics->JavaObjectType) { |
| JnjvmClassLoader* JCL = compilingClass->classLoader; |
| push(val, false, signature->getReturnType()->findAssocClass(JCL)); |
| } else { |
| push(val, retTypedef->isUnsigned()); |
| if (retType == Type::getDoubleTy(*llvmContext) || retType == Type::getInt64Ty(*llvmContext)) { |
| push(intrinsics->constantZero, false); |
| } |
| } |
| } |
| } |
| |
| llvm::Value* JavaJIT::getMutatorThreadPtr() { |
| Value* FrameAddr = CallInst::Create(intrinsics->llvm_frameaddress, |
| intrinsics->constantZero, "", currentBlock); |
| Value* threadId = new PtrToIntInst(FrameAddr, intrinsics->pointerSizeType, "", |
| currentBlock); |
| threadId = BinaryOperator::CreateAnd(threadId, intrinsics->constantThreadIDMask, |
| "", currentBlock); |
| threadId = new IntToPtrInst(threadId, intrinsics->MutatorThreadType, "MutatorThreadPtr", currentBlock); |
| |
| return threadId; |
| } |
| |
| llvm::Value* JavaJIT::getJavaThreadPtr(llvm::Value* mutatorThreadPtr) { |
| return new BitCastInst(mutatorThreadPtr, intrinsics->JavaThreadType, "JavaThreadPtr", currentBlock); |
| } |
| |
| llvm::Value* JavaJIT::getIsolateIDPtr(llvm::Value* mutatorThreadPtr) { |
| Value* GEP[3] = { intrinsics->constantZero, |
| intrinsics->OffsetThreadInMutatorThreadConstant, |
| intrinsics->OffsetIsolateIDInThreadConstant }; |
| |
| return GetElementPtrInst::Create(mutatorThreadPtr, GEP, "isolateIDPtr", currentBlock); |
| } |
| |
| llvm::Value* JavaJIT::getVMPtr(llvm::Value* mutatorThreadPtr) { |
| Value* GEP[3] = { intrinsics->constantZero, |
| intrinsics->OffsetThreadInMutatorThreadConstant, |
| intrinsics->OffsetVMInThreadConstant }; |
| |
| return GetElementPtrInst::Create(mutatorThreadPtr, GEP, "VMPtr", currentBlock); |
| } |
| |
| llvm::Value* JavaJIT::getDoYieldPtr(llvm::Value* mutatorThreadPtr) { |
| Value* GEP[3] = { intrinsics->constantZero, |
| intrinsics->OffsetThreadInMutatorThreadConstant, |
| intrinsics->OffsetDoYieldInThreadConstant }; |
| |
| return GetElementPtrInst::Create(mutatorThreadPtr, GEP, "doYieldPtr", currentBlock); |
| } |
| |
| llvm::Value* JavaJIT::getJNIEnvPtr(llvm::Value* javaThreadPtr) { |
| Value* GEP[2] = { intrinsics->constantZero, |
| intrinsics->OffsetJNIInJavaThreadConstant }; |
| |
| return GetElementPtrInst::Create(javaThreadPtr, GEP, "JNIPtr", currentBlock); |
| } |
| |
| llvm::Value* JavaJIT::getJavaExceptionPtr(llvm::Value* javaThreadPtr) { |
| Value* GEP[2] = { intrinsics->constantZero, |
| intrinsics->OffsetJavaExceptionInJavaThreadConstant }; |
| |
| return GetElementPtrInst::Create(javaThreadPtr, GEP, "pendingExceptionPtr", currentBlock); |
| } |
| |
| static llvm::Function* GetNativeCallee(JavaLLVMCompiler* TheCompiler, |
| JavaMethod* compilingMethod) { |
| LLVMSignatureInfo* LSI = |
| TheCompiler->getSignatureInfo(compilingMethod->getSignature()); |
| FunctionType* FTy = LSI->getNativeStubType(); |
| string methName; |
| Function* callee = Function::Create(FTy, |
| GlobalValue::ExternalLinkage, |
| compilingMethod->getName(methName, true), |
| TheCompiler->getLLVMModule()); |
| std::vector<Value*> args; |
| Function::arg_iterator i = callee->arg_begin(); |
| Value* nativeFunc = i; |
| i++; |
| for (Function::arg_iterator e = callee->arg_end(); i != e; i++) { |
| args.push_back(i); |
| } |
| |
| LLVMContext& llvmContext = TheCompiler->getLLVMContext(); |
| BasicBlock* BB = BasicBlock::Create(llvmContext, "", callee); |
| Value* res = CallInst::Create(nativeFunc, args, "", BB); |
| if (callee->getFunctionType()->getReturnType() != Type::getVoidTy(llvmContext)) { |
| ReturnInst::Create(llvmContext, res, BB); |
| } else { |
| ReturnInst::Create(llvmContext, BB); |
| } |
| callee->setGC("vmkit"); |
| |
| TheCompiler->GenerateStub(callee); |
| return callee; |
| } |
| |
| llvm::Function* JavaJIT::nativeCompile(word_t natPtr) { |
| |
| PRINT_DEBUG(JNJVM_COMPILE, 1, DARK_GREEN, "nativeCompile %s.%s\n", |
| UTF8Buffer(compilingClass->name).cString(), |
| UTF8Buffer(compilingMethod->name).cString()); |
| |
| bool stat = isStatic(compilingMethod->access); |
| |
| FunctionType *funcType = llvmFunction->getFunctionType(); |
| Type* returnType = funcType->getReturnType(); |
| |
| bool j3 = false; |
| |
| const UTF8* jniConsClName = compilingClass->name; |
| const UTF8* jniConsName = compilingMethod->name; |
| const UTF8* jniConsType = compilingMethod->type; |
| sint32 clen = jniConsClName->size; |
| sint32 mnlen = jniConsName->size; |
| sint32 mtlen = jniConsType->size; |
| |
| vmkit::ThreadAllocator allocator; |
| char* functionName = (char*)allocator.Allocate( |
| 3 + JNI_NAME_PRE_LEN + ((mnlen + clen + mtlen) << 3)); |
| |
| if (!natPtr) { |
| natPtr = compilingClass->classLoader->nativeLookup(compilingMethod, j3, |
| functionName); |
| } |
| |
| if (!natPtr && !TheCompiler->isStaticCompiling()) { |
| currentBlock = createBasicBlock("start"); |
| CallInst::Create(intrinsics->ThrowExceptionFromJITFunction, "", currentBlock); |
| if (returnType != Type::getVoidTy(*llvmContext)) { |
| ReturnInst::Create(*llvmContext, Constant::getNullValue(returnType), currentBlock); |
| } else { |
| ReturnInst::Create(*llvmContext, currentBlock); |
| } |
| |
| PRINT_DEBUG(JNJVM_COMPILE, 1, COLOR_NORMAL, "end native compile %s.%s\n", |
| UTF8Buffer(compilingClass->name).cString(), |
| UTF8Buffer(compilingMethod->name).cString()); |
| |
| return llvmFunction; |
| } |
| |
| |
| Function* func = llvmFunction; |
| if (j3) { |
| Function* callee = Function::Create(llvmFunction->getFunctionType(), |
| GlobalValue::ExternalLinkage, |
| functionName, |
| llvmFunction->getParent()); |
| TheCompiler->setMethod(callee, (void*)natPtr, functionName); |
| currentBlock = createBasicBlock("start"); |
| std::vector<Value*> args; |
| for (Function::arg_iterator i = func->arg_begin(), e = func->arg_end(); |
| i != e; |
| i++) { |
| args.push_back(i); |
| } |
| Value* res = CallInst::Create(callee, args, "", currentBlock); |
| if (returnType != Type::getVoidTy(*llvmContext)) { |
| ReturnInst::Create(*llvmContext, res, currentBlock); |
| } else { |
| ReturnInst::Create(*llvmContext, currentBlock); |
| } |
| return llvmFunction; |
| } |
| |
| currentExceptionBlock = endExceptionBlock = 0; |
| currentBlock = createBasicBlock("start"); |
| endBlock = createBasicBlock("end block"); |
| |
| if (returnType != Type::getVoidTy(*llvmContext)) { |
| endNode = PHINode::Create(returnType, 0, "", endBlock); |
| } |
| |
| // Allocate currentLocalIndexNumber pointer |
| Value* temp = new AllocaInst(Type::getInt32Ty(*llvmContext), "", |
| currentBlock); |
| new StoreInst(intrinsics->constantZero, temp, false, currentBlock); |
| |
| // Allocate oldCurrentLocalIndexNumber pointer |
| Value* oldCLIN = new AllocaInst(PointerType::getUnqual(Type::getInt32Ty(*llvmContext)), "", |
| currentBlock); |
| |
| Constant* sizeF = ConstantInt::get(Type::getInt32Ty(*llvmContext), sizeof(vmkit::KnownFrame)); |
| Value* Frame = new AllocaInst(Type::getInt8Ty(*llvmContext), sizeF, "", currentBlock); |
| |
| uint32 nargs = func->arg_size() + 1 + (stat ? 1 : 0); |
| std::vector<Value*> nativeArgs; |
| nativeArgs.push_back(NULL); // Will contain the callee |
| |
| |
| Value* jniEnv = getJNIEnvPtr(getJavaThreadPtr(getMutatorThreadPtr())); |
| |
| jniEnv = new BitCastInst(jniEnv, intrinsics->ptrType, "", currentBlock); |
| |
| nativeArgs.push_back(jniEnv); |
| |
| uint32 index = 0; |
| if (stat) { |
| Value* cl = TheCompiler->getJavaClassPtr(compilingClass); |
| nativeArgs.push_back(cl); |
| index = 2; |
| } else { |
| index = 1; |
| } |
| for (Function::arg_iterator i = func->arg_begin(); |
| index < nargs; ++i, ++index) { |
| |
| if (i->getType() == intrinsics->JavaObjectType) { |
| BasicBlock* BB = createBasicBlock(""); |
| BasicBlock* NotZero = createBasicBlock(""); |
| Type* Ty = PointerType::getUnqual(intrinsics->JavaObjectType); |
| PHINode* node = PHINode::Create(Ty, 2, "", BB); |
| |
| Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, i, |
| intrinsics->JavaObjectNullConstant, ""); |
| |
| node->addIncoming(Constant::getNullValue(Ty), currentBlock); |
| BranchInst::Create(BB, NotZero, test, currentBlock); |
| |
| currentBlock = NotZero; |
| |
| Instruction* temp = new AllocaInst(intrinsics->JavaObjectType, "", |
| func->begin()->getTerminator()); |
| if (i == func->arg_begin() && !stat) { |
| this->thisObject = temp; |
| } |
| |
| if (TheCompiler->useCooperativeGC()) { |
| Value* GCArgs[2] = { |
| new BitCastInst(temp, intrinsics->ptrPtrType, "", |
| func->begin()->getTerminator()), |
| intrinsics->constantPtrNull |
| }; |
| |
| CallInst::Create(intrinsics->llvm_gc_gcroot, GCArgs, "", |
| func->begin()->getTerminator()); |
| } |
| |
| new StoreInst(i, temp, false, currentBlock); |
| node->addIncoming(temp, currentBlock); |
| BranchInst::Create(BB, currentBlock); |
| |
| currentBlock = BB; |
| |
| nativeArgs.push_back(node); |
| } else { |
| nativeArgs.push_back(i); |
| } |
| } |
| |
| |
| Instruction* ResultObject = 0; |
| if (returnType == intrinsics->JavaObjectType) { |
| ResultObject = new AllocaInst(intrinsics->JavaObjectType, "", |
| func->begin()->begin()); |
| |
| if (TheCompiler->useCooperativeGC()) { |
| |
| Value* GCArgs[2] = { |
| new BitCastInst(ResultObject, intrinsics->ptrPtrType, "", currentBlock), |
| intrinsics->constantPtrNull |
| }; |
| |
| CallInst::Create(intrinsics->llvm_gc_gcroot, GCArgs, "", |
| currentBlock); |
| } else { |
| new StoreInst(intrinsics->JavaObjectNullConstant, ResultObject, "", |
| currentBlock); |
| } |
| } |
| |
| Value* nativeFunc = TheCompiler->getNativeFunction(compilingMethod, (void*)natPtr); |
| if (TheCompiler->isStaticCompiling()) { |
| Value* Arg = TheCompiler->getMethodInClass(compilingMethod); |
| |
| // If the global variable is null, then load it. |
| BasicBlock* unloadedBlock = createBasicBlock(""); |
| BasicBlock* endBlock = createBasicBlock(""); |
| Value* test = new LoadInst(nativeFunc, "", currentBlock); |
| Type* Ty = test->getType(); |
| PHINode* node = PHINode::Create(Ty, 2, "", endBlock); |
| node->addIncoming(test, currentBlock); |
| Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, test, |
| Constant::getNullValue(Ty), ""); |
| BranchInst::Create(unloadedBlock, endBlock, cmp, currentBlock); |
| currentBlock = unloadedBlock; |
| |
| Value* res = CallInst::Create(TheCompiler->NativeLoader, Arg, "", |
| currentBlock); |
| |
| res = new BitCastInst(res, Ty, "", currentBlock); |
| new StoreInst(res, nativeFunc, currentBlock); |
| node->addIncoming(res, currentBlock); |
| BranchInst::Create(endBlock, currentBlock); |
| currentBlock = endBlock; |
| nativeFunc = node; |
| } |
| nativeArgs[0] = nativeFunc; |
| |
| // Synchronize before saying we're entering native |
| if (isSynchro(compilingMethod->access)) { |
| nbHandlers = 1; |
| beginSynchronize(); |
| } |
| |
| Value* Args4[3] = { temp, oldCLIN, Frame }; |
| |
| CallInst::Create(intrinsics->StartJNIFunction, Args4, "", currentBlock); |
| |
| Function* callee = GetNativeCallee(TheCompiler, compilingMethod); |
| Value* result = llvm::CallInst::Create(callee, nativeArgs, "", currentBlock); |
| |
| if (returnType == intrinsics->JavaObjectType) { |
| Type* Ty = PointerType::getUnqual(intrinsics->JavaObjectType); |
| Constant* C = Constant::getNullValue(Ty); |
| Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, result, C, ""); |
| BasicBlock* loadBlock = createBasicBlock(""); |
| |
| endNode->addIncoming(intrinsics->JavaObjectNullConstant, currentBlock); |
| BranchInst::Create(endBlock, loadBlock, cmp, currentBlock); |
| |
| currentBlock = loadBlock; |
| result = new LoadInst( |
| result, "", false, currentBlock); |
| new StoreInst(result, ResultObject, "", currentBlock); |
| endNode->addIncoming(result, currentBlock); |
| |
| } else if (returnType != Type::getVoidTy(*llvmContext)) { |
| endNode->addIncoming(result, currentBlock); |
| } |
| |
| BranchInst::Create(endBlock, currentBlock); |
| |
| |
| currentBlock = endBlock; |
| |
| Value* Args2[1] = { oldCLIN }; |
| |
| CallInst::Create(intrinsics->EndJNIFunction, Args2, "", currentBlock); |
| |
| // Synchronize after leaving native. |
| if (isSynchro(compilingMethod->access)) |
| endSynchronize(); |
| |
| BasicBlock* ifNormal = createBasicBlock(""); |
| BasicBlock* ifException = createBasicBlock(""); |
| Value* javaExceptionPtr = getJavaExceptionPtr(getJavaThreadPtr(getMutatorThreadPtr())); |
| Value* obj = new LoadInst(javaExceptionPtr, "", currentBlock); |
| Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_NE, obj, intrinsics->JavaObjectNullConstant, ""); |
| BranchInst::Create(ifException, ifNormal, test, currentBlock); |
| |
| currentBlock = ifException; |
| // Clear exception. |
| new StoreInst(intrinsics->JavaObjectNullConstant, javaExceptionPtr, |
| currentBlock); |
| CallInst::Create(intrinsics->ThrowExceptionFunction, obj, "", currentBlock); |
| new UnreachableInst(*llvmContext, currentBlock); |
| currentBlock = ifNormal; |
| |
| if (returnType != Type::getVoidTy(*llvmContext)) |
| ReturnInst::Create(*llvmContext, endNode, currentBlock); |
| else |
| ReturnInst::Create(*llvmContext, currentBlock); |
| |
| PRINT_DEBUG(JNJVM_COMPILE, 1, COLOR_NORMAL, "end native compile %s.%s\n", |
| UTF8Buffer(compilingClass->name).cString(), |
| UTF8Buffer(compilingMethod->name).cString()); |
| |
| return llvmFunction; |
| } |
| |
| llvm::Value* JavaJIT::objectToHeader(Value* obj) { |
| obj = new PtrToIntInst(obj, intrinsics->pointerSizeType, "", currentBlock); |
| Value* d = ConstantInt::get(intrinsics->pointerSizeType, gcHeader::hiddenHeaderSize()); |
| obj = BinaryOperator::CreateSub(obj, d, "", currentBlock); |
| return new IntToPtrInst(obj, intrinsics->ObjectHeaderType, "objectHeader", currentBlock); |
| } |
| |
| void JavaJIT::monitorEnter(Value* obj) { |
| Value* lockPtr = objectToHeader(obj); |
| |
| Value* lock = new LoadInst(lockPtr, "", currentBlock); |
| |
| Value* NonLockBitsMask = ConstantInt::get(intrinsics->pointerSizeType, |
| vmkit::ThinLock::NonLockBitsMask); |
| |
| lock = BinaryOperator::CreateAnd(lock, NonLockBitsMask, "", currentBlock); |
| |
| Value* threadId = getMutatorThreadPtr(); |
| threadId = new PtrToIntInst(threadId, intrinsics->pointerSizeType, "", |
| currentBlock); |
| Value* newValMask = BinaryOperator::CreateOr(threadId, lock, "", |
| currentBlock); |
| |
| // Do the atomic compare and swap. |
| Value* atomic = new AtomicCmpXchgInst( |
| lockPtr, lock, newValMask, SequentiallyConsistent, CrossThread, |
| currentBlock); |
| |
| Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, atomic, |
| lock, ""); |
| |
| BasicBlock* OK = createBasicBlock("synchronize passed"); |
| BasicBlock* NotOK = createBasicBlock("synchronize did not pass"); |
| |
| BranchInst::Create(OK, NotOK, cmp, currentBlock); |
| |
| // The atomic CAS did not work. |
| currentBlock = NotOK; |
| CallInst::Create(intrinsics->AquireObjectFunction, obj, "", currentBlock); |
| BranchInst::Create(OK, currentBlock); |
| |
| currentBlock = OK; |
| } |
| |
| void JavaJIT::monitorExit(Value* obj) { |
| // obj should not be null if we are here. |
| BasicBlock* nonNullObjBlock = createBasicBlock("monitorExit_nonNullObj"); |
| BasicBlock* EndBlock = createBasicBlock("monitorExit_End"); |
| |
| Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, obj, intrinsics->JavaObjectNullConstant, "isObjectNull"); |
| BranchInst::Create(EndBlock, nonNullObjBlock, test, currentBlock); |
| |
| currentBlock = nonNullObjBlock; |
| |
| Value* lockPtr = objectToHeader(obj); |
| |
| Value* lock = new LoadInst(lockPtr, "", currentBlock); |
| |
| Value* NonLockBitsMask = ConstantInt::get( |
| intrinsics->pointerSizeType, vmkit::ThinLock::NonLockBitsMask); |
| |
| Value* lockedMask = BinaryOperator::CreateAnd( |
| lock, NonLockBitsMask, "", currentBlock); |
| |
| Value* threadId = getMutatorThreadPtr(); |
| threadId = new PtrToIntInst(threadId, intrinsics->pointerSizeType, "", |
| currentBlock); |
| |
| Value* oldValMask = BinaryOperator::CreateOr(threadId, lockedMask, "", |
| currentBlock); |
| |
| std::vector<Value*> atomicArgs; |
| atomicArgs.push_back(lockPtr); |
| atomicArgs.push_back(oldValMask); |
| atomicArgs.push_back(lockedMask); |
| |
| // Do the atomic compare and swap. |
| Value* atomic = new AtomicCmpXchgInst( |
| lockPtr, oldValMask, lockedMask, SequentiallyConsistent, CrossThread, |
| currentBlock); |
| |
| Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, atomic, |
| oldValMask, ""); |
| |
| BasicBlock* LockFreeCASFailed = createBasicBlock("Lock-Free CAS Failed"); |
| |
| BranchInst::Create(EndBlock, LockFreeCASFailed, cmp, currentBlock); |
| |
| // The atomic cas did not work. |
| currentBlock = LockFreeCASFailed; |
| CallInst::Create(intrinsics->ReleaseObjectFunction, obj, "", currentBlock); |
| BranchInst::Create(EndBlock, currentBlock); |
| |
| currentBlock = EndBlock; |
| } |
| |
| void JavaJIT::beginSynchronize() { |
| Value* obj = 0; |
| if (isVirtual(compilingMethod->access)) { |
| assert(thisObject != NULL && "beginSynchronize without this"); |
| obj = new LoadInst( |
| thisObject, "", false, currentBlock); |
| } else { |
| obj = TheCompiler->getJavaClassPtr(compilingClass); |
| obj = new LoadInst(obj, "", false, currentBlock); |
| } |
| monitorEnter(obj); |
| } |
| |
| void JavaJIT::endSynchronize() { |
| Value* obj = 0; |
| if (isVirtual(compilingMethod->access)) { |
| assert(thisObject != NULL && "endSynchronize without this"); |
| obj = new LoadInst( |
| thisObject, "", false, currentBlock); |
| } else { |
| obj = TheCompiler->getJavaClassPtr(compilingClass); |
| obj = new LoadInst(obj, "", false, currentBlock); |
| } |
| monitorExit(obj); |
| } |
| |
| |
| static void removeUnusedLocals(std::vector<AllocaInst*>& locals) { |
| for (std::vector<AllocaInst*>::iterator i = locals.begin(), |
| e = locals.end(); i != e; ++i) { |
| AllocaInst* temp = *i; |
| unsigned uses = temp->getNumUses(); |
| if (!uses) { |
| temp->eraseFromParent(); |
| } else if (uses == 1 && dyn_cast<StoreInst>(*(temp->use_begin()))) { |
| dyn_cast<StoreInst>(*(temp->use_begin()))->eraseFromParent(); |
| temp->eraseFromParent(); |
| } |
| } |
| } |
| |
| static void removeUnusedObjects(std::vector<AllocaInst*>& objects, |
| J3Intrinsics* intrinsics, bool coop) { |
| for (std::vector<AllocaInst*>::iterator i = objects.begin(), |
| e = objects.end(); i != e; ++i) { |
| AllocaInst* temp = *i; |
| unsigned uses = temp->getNumUses(); |
| if (!uses) { |
| temp->eraseFromParent(); |
| } else if (uses == 1 && dyn_cast<StoreInst>(*(temp->use_begin()))) { |
| dyn_cast<StoreInst>(*(temp->use_begin()))->eraseFromParent(); |
| temp->eraseFromParent(); |
| } else { |
| if (coop) { |
| Instruction* I = new BitCastInst(temp, intrinsics->ptrPtrType, ""); |
| I->insertAfter(temp); |
| Value* GCArgs[2] = { I, intrinsics->constantPtrNull }; |
| Instruction* C = CallInst::Create(intrinsics->llvm_gc_gcroot, GCArgs, ""); |
| C->insertAfter(I); |
| } |
| } |
| } |
| } |
| |
| Instruction* JavaJIT::inlineCompile(BasicBlock*& curBB, |
| BasicBlock* endExBlock, |
| std::vector<Value*>& args) { |
| JavaAttribute* codeAtt = compilingMethod->lookupAttribute(JavaAttribute::codeAttribute); |
| Reader reader(codeAtt, compilingClass->bytes); |
| uint16 maxStack = reader.readU2(); |
| uint16 maxLocals = reader.readU2(); |
| uint32 codeLen = reader.readU4(); |
| uint32 start = reader.cursor; |
| reader.seek(codeLen, Reader::SeekCur); |
| |
| LLVMAssessorInfo& LAI = TheCompiler->getTypedefInfo( |
| compilingMethod->getSignature()->getReturnType()); |
| Type* returnType = LAI.llvmType; |
| |
| endBlock = createBasicBlock("end"); |
| |
| currentBlock = curBB; |
| endExceptionBlock = endExBlock; |
| |
| opcodeInfos = new Opinfo[codeLen]; |
| memset(opcodeInfos, 0, codeLen * sizeof(Opinfo)); |
| for (uint32 i = 0; i < codeLen; ++i) { |
| opcodeInfos[i].exceptionBlock = endExBlock; |
| } |
| |
| BasicBlock* firstBB = llvmFunction->begin(); |
| |
| if (firstBB->begin() != firstBB->end()) { |
| Instruction* firstInstruction = firstBB->begin(); |
| |
| for (int i = 0; i < maxLocals; i++) { |
| intLocals.push_back(new AllocaInst(Type::getInt32Ty(*llvmContext), "", firstInstruction)); |
| new StoreInst(Constant::getNullValue(Type::getInt32Ty(*llvmContext)), intLocals.back(), false, firstInstruction); |
| doubleLocals.push_back(new AllocaInst(Type::getDoubleTy(*llvmContext), "", |
| firstInstruction)); |
| new StoreInst(Constant::getNullValue(Type::getDoubleTy(*llvmContext)), doubleLocals.back(), false, firstInstruction); |
| longLocals.push_back(new AllocaInst(Type::getInt64Ty(*llvmContext), "", firstInstruction)); |
| new StoreInst(Constant::getNullValue(Type::getInt64Ty(*llvmContext)), longLocals.back(), false, firstInstruction); |
| floatLocals.push_back(new AllocaInst(Type::getFloatTy(*llvmContext), "", firstInstruction)); |
| new StoreInst(Constant::getNullValue(Type::getFloatTy(*llvmContext)), floatLocals.back(), false, firstInstruction); |
| objectLocals.push_back(new AllocaInst(intrinsics->JavaObjectType, "", |
| firstInstruction)); |
| |
| // The GCStrategy will already initialize the value. |
| if (!TheCompiler->useCooperativeGC()) |
| new StoreInst(Constant::getNullValue(intrinsics->JavaObjectType), objectLocals.back(), false, firstInstruction); |
| } |
| for (int i = 0; i < maxStack; i++) { |
| objectStack.push_back(new AllocaInst(intrinsics->JavaObjectType, "", |
| firstInstruction)); |
| addHighLevelType(objectStack.back(), upcalls->OfObject); |
| intStack.push_back(new AllocaInst(Type::getInt32Ty(*llvmContext), "", firstInstruction)); |
| doubleStack.push_back(new AllocaInst(Type::getDoubleTy(*llvmContext), "", |
| firstInstruction)); |
| longStack.push_back(new AllocaInst(Type::getInt64Ty(*llvmContext), "", firstInstruction)); |
| floatStack.push_back(new AllocaInst(Type::getFloatTy(*llvmContext), "", firstInstruction)); |
| } |
| |
| } else { |
| for (int i = 0; i < maxLocals; i++) { |
| intLocals.push_back(new AllocaInst(Type::getInt32Ty(*llvmContext), "", firstBB)); |
| new StoreInst(Constant::getNullValue(Type::getInt32Ty(*llvmContext)), intLocals.back(), false, firstBB); |
| doubleLocals.push_back(new AllocaInst(Type::getDoubleTy(*llvmContext), "", firstBB)); |
| new StoreInst(Constant::getNullValue(Type::getDoubleTy(*llvmContext)), doubleLocals.back(), false, firstBB); |
| longLocals.push_back(new AllocaInst(Type::getInt64Ty(*llvmContext), "", firstBB)); |
| new StoreInst(Constant::getNullValue(Type::getInt64Ty(*llvmContext)), longLocals.back(), false, firstBB); |
| floatLocals.push_back(new AllocaInst(Type::getFloatTy(*llvmContext), "", firstBB)); |
| new StoreInst(Constant::getNullValue(Type::getFloatTy(*llvmContext)), floatLocals.back(), false, firstBB); |
| objectLocals.push_back(new AllocaInst(intrinsics->JavaObjectType, "", |
| firstBB)); |
| // The GCStrategy will already initialize the value. |
| if (!TheCompiler->useCooperativeGC()) |
| new StoreInst(Constant::getNullValue(intrinsics->JavaObjectType), objectLocals.back(), false, firstBB); |
| } |
| |
| for (int i = 0; i < maxStack; i++) { |
| objectStack.push_back(new AllocaInst(intrinsics->JavaObjectType, "", |
| firstBB)); |
| addHighLevelType(objectStack.back(), upcalls->OfObject); |
| intStack.push_back(new AllocaInst(Type::getInt32Ty(*llvmContext), "", firstBB)); |
| doubleStack.push_back(new AllocaInst(Type::getDoubleTy(*llvmContext), "", firstBB)); |
| longStack.push_back(new AllocaInst(Type::getInt64Ty(*llvmContext), "", firstBB)); |
| floatStack.push_back(new AllocaInst(Type::getFloatTy(*llvmContext), "", firstBB)); |
| } |
| } |
| |
| uint32 index = 0; |
| uint32 count = 0; |
| uint32 max = args.size(); |
| |
| Signdef* sign = compilingMethod->getSignature(); |
| Typedef* const* arguments = sign->getArgumentsType(); |
| uint32 type = 0; |
| std::vector<Value*>::iterator i = args.begin(); |
| |
| if (isVirtual(compilingMethod->access)) { |
| Instruction* V = new StoreInst(*i, objectLocals[0], false, currentBlock); |
| addHighLevelType(V, compilingClass); |
| ++i; |
| ++index; |
| ++count; |
| thisObject = objectLocals[0]; |
| } |
| |
| for (;count < max; ++i, ++index, ++count, ++type) { |
| |
| const Typedef* cur = arguments[type]; |
| Type* curType = (*i)->getType(); |
| |
| if (curType == Type::getInt64Ty(*llvmContext)){ |
| new StoreInst(*i, longLocals[index], false, currentBlock); |
| ++index; |
| } else if (cur->isUnsigned()) { |
| new StoreInst(new ZExtInst(*i, Type::getInt32Ty(*llvmContext), "", currentBlock), |
| intLocals[index], false, currentBlock); |
| } else if (curType == Type::getInt8Ty(*llvmContext) || curType == Type::getInt16Ty(*llvmContext)) { |
| new StoreInst(new SExtInst(*i, Type::getInt32Ty(*llvmContext), "", currentBlock), |
| intLocals[index], false, currentBlock); |
| } else if (curType == Type::getInt32Ty(*llvmContext)) { |
| new StoreInst(*i, intLocals[index], false, currentBlock); |
| } else if (curType == Type::getDoubleTy(*llvmContext)) { |
| new StoreInst(*i, doubleLocals[index], false, currentBlock); |
| ++index; |
| } else if (curType == Type::getFloatTy(*llvmContext)) { |
| new StoreInst(*i, floatLocals[index], false, currentBlock); |
| } else { |
| Instruction* V = new StoreInst(*i, objectLocals[index], false, currentBlock); |
| addHighLevelType(V, cur->findAssocClass(compilingClass->classLoader)); |
| } |
| } |
| |
| nbHandlers = readExceptionTable(reader, codeLen); |
| |
| reader.cursor = start; |
| exploreOpcodes(reader, codeLen); |
| |
| if (returnType != Type::getVoidTy(*llvmContext)) { |
| endNode = PHINode::Create(returnType, 0, "", endBlock); |
| } |
| |
| reader.cursor = start; |
| compileOpcodes(reader, codeLen); |
| |
| PRINT_DEBUG(JNJVM_COMPILE, 1, DARK_MAGENTA, |
| "--> end inlineCompile for %s.%s\n", |
| UTF8Buffer(compilingClass->name).cString(), |
| UTF8Buffer(compilingMethod->name).cString()); |
| |
| curBB = endBlock; |
| |
| |
| removeUnusedLocals(intLocals); |
| removeUnusedLocals(doubleLocals); |
| removeUnusedLocals(floatLocals); |
| removeUnusedLocals(longLocals); |
| removeUnusedLocals(intStack); |
| removeUnusedLocals(doubleStack); |
| removeUnusedLocals(floatStack); |
| removeUnusedLocals(longStack); |
| |
| removeUnusedObjects(objectLocals, intrinsics, TheCompiler->useCooperativeGC()); |
| removeUnusedObjects(objectStack, intrinsics, TheCompiler->useCooperativeGC()); |
| |
| |
| delete[] opcodeInfos; |
| return endNode; |
| } |
| |
| static char* setInstructionName(char *s, size_t maxlen, const char * format, ...) |
| { |
| va_list args; |
| va_start(args, format); |
| vsnprintf(s, maxlen, format, args); |
| va_end(args); |
| return s; |
| } |
| |
| llvm::Function* JavaJIT::javaCompile() { |
| |
| PRINT_DEBUG(JNJVM_COMPILE, 1, DARK_CYAN, "javacompile for (%s.)%s.%s\n", |
| (compilingClass->super)?UTF8Buffer(compilingClass->super->name).cString():"", |
| UTF8Buffer(compilingClass->name).cString(), |
| UTF8Buffer(compilingMethod->name).cString()); |
| |
| string methName, methNameLink; |
| DbgSubprogram = TheCompiler->getDebugFactory()->createFunction( |
| DIDescriptor(), compilingMethod->getName(methName, false), |
| compilingMethod->getName(methNameLink, true), DIFile(), 0, |
| TheCompiler->getDebugFactory()->createSubroutineType(DIFile(), DIArray()), |
| false, false, 0); |
| |
| JavaAttribute* codeAtt = compilingMethod->lookupAttribute(JavaAttribute::codeAttribute); |
| |
| if (!codeAtt) { |
| fprintf(stderr, "I haven't verified your class file and it's malformed:" |
| " no code attribute found for %s.%s!\n", |
| UTF8Buffer(compilingClass->name).cString(), |
| UTF8Buffer(compilingMethod->name).cString()); |
| abort(); |
| } |
| |
| Reader reader(codeAtt, compilingClass->bytes); |
| uint16 maxStack = reader.readU2(); |
| uint16 maxLocals = reader.readU2(); |
| uint32 codeLen = reader.readU4(); |
| uint32 start = reader.cursor; |
| |
| reader.seek(codeLen, Reader::SeekCur); |
| |
| FunctionType *funcType = llvmFunction->getFunctionType(); |
| Type* returnType = funcType->getReturnType(); |
| |
| Function* func = llvmFunction; |
| |
| const size_t instNameLen = 4096; |
| char instName[instNameLen]; |
| |
| currentBlock = createBasicBlock("start"); |
| endExceptionBlock = createBasicBlock("endExceptionBlock"); |
| unifiedUnreachable = createBasicBlock("unifiedUnreachable"); |
| |
| opcodeInfos = new Opinfo[codeLen]; |
| memset(opcodeInfos, 0, codeLen * sizeof(Opinfo)); |
| for (uint32 i = 0; i < codeLen; ++i) { |
| opcodeInfos[i].exceptionBlock = endExceptionBlock; |
| } |
| |
| Instruction* returnValue = NULL; |
| if (returnType == intrinsics->JavaObjectType && |
| TheCompiler->useCooperativeGC()) { |
| returnValue = new AllocaInst(intrinsics->JavaObjectType, "returnValue", |
| currentBlock); |
| Instruction* cast = |
| new BitCastInst(returnValue, intrinsics->ptrPtrType, "", currentBlock); |
| Value* GCArgs[2] = { cast, intrinsics->constantPtrNull }; |
| |
| CallInst::Create(intrinsics->llvm_gc_gcroot, GCArgs, "", currentBlock); |
| } |
| |
| for (int i = 0; i < maxLocals; i++) { |
| intLocals.push_back(new AllocaInst(Type::getInt32Ty(*llvmContext), setInstructionName(instName, instNameLen, "int_%d", i), currentBlock)); |
| new StoreInst(Constant::getNullValue(Type::getInt32Ty(*llvmContext)), intLocals.back(), false, currentBlock); |
| doubleLocals.push_back(new AllocaInst(Type::getDoubleTy(*llvmContext), setInstructionName(instName, instNameLen, "double_%d", i), currentBlock)); |
| new StoreInst(Constant::getNullValue(Type::getDoubleTy(*llvmContext)), doubleLocals.back(), false, currentBlock); |
| longLocals.push_back(new AllocaInst(Type::getInt64Ty(*llvmContext), setInstructionName(instName, instNameLen, "long_%d", i), currentBlock)); |
| new StoreInst(Constant::getNullValue(Type::getInt64Ty(*llvmContext)), longLocals.back(), false, currentBlock); |
| floatLocals.push_back(new AllocaInst(Type::getFloatTy(*llvmContext), setInstructionName(instName, instNameLen, "float_%d", i), currentBlock)); |
| new StoreInst(Constant::getNullValue(Type::getFloatTy(*llvmContext)), floatLocals.back(), false, currentBlock); |
| objectLocals.push_back(new AllocaInst(intrinsics->JavaObjectType, setInstructionName(instName, instNameLen, "object_%d", i), currentBlock)); |
| // The GCStrategy will already initialize the value. |
| if (!TheCompiler->useCooperativeGC()) |
| new StoreInst(Constant::getNullValue(intrinsics->JavaObjectType), objectLocals.back(), false, currentBlock); |
| } |
| |
| for (int i = 0; i < maxStack; i++) { |
| objectStack.push_back(new AllocaInst(intrinsics->JavaObjectType, setInstructionName(instName, instNameLen, "stack_object_%d", i), currentBlock)); |
| addHighLevelType(objectStack.back(), upcalls->OfObject); |
| intStack.push_back(new AllocaInst(Type::getInt32Ty(*llvmContext), setInstructionName(instName, instNameLen, "stack_int_%d", i), currentBlock)); |
| doubleStack.push_back(new AllocaInst(Type::getDoubleTy(*llvmContext), setInstructionName(instName, instNameLen, "stack_double_%d", i), currentBlock)); |
| longStack.push_back(new AllocaInst(Type::getInt64Ty(*llvmContext), setInstructionName(instName, instNameLen, "stack_long_%d", i), currentBlock)); |
| floatStack.push_back(new AllocaInst(Type::getFloatTy(*llvmContext), setInstructionName(instName, instNameLen, "stack_float_%d", i), currentBlock)); |
| } |
| |
| uint32 index = 0; |
| uint32 count = 0; |
| uint32 max = func->arg_size(); |
| |
| Function::arg_iterator i = func->arg_begin(); |
| Signdef* sign = compilingMethod->getSignature(); |
| Typedef* const* arguments = sign->getArgumentsType(); |
| uint32 type = 0; |
| |
| if (isVirtual(compilingMethod->access)) { |
| Instruction* V = new StoreInst(i, objectLocals[0], false, currentBlock); |
| addHighLevelType(V, compilingClass); |
| ++i; |
| ++index; |
| ++count; |
| thisObject = objectLocals[0]; |
| } |
| |
| for (;count < max; ++i, ++index, ++count, ++type) { |
| |
| const Typedef* cur = arguments[type]; |
| Type* curType = i->getType(); |
| |
| if (curType == Type::getInt64Ty(*llvmContext)){ |
| new StoreInst(i, longLocals[index], false, currentBlock); |
| ++index; |
| } else if (cur->isUnsigned()) { |
| new StoreInst(new ZExtInst(i, Type::getInt32Ty(*llvmContext), "", currentBlock), |
| intLocals[index], false, currentBlock); |
| } else if (curType == Type::getInt8Ty(*llvmContext) || curType == Type::getInt16Ty(*llvmContext)) { |
| new StoreInst(new SExtInst(i, Type::getInt32Ty(*llvmContext), "", currentBlock), |
| intLocals[index], false, currentBlock); |
| } else if (curType == Type::getInt32Ty(*llvmContext)) { |
| new StoreInst(i, intLocals[index], false, currentBlock); |
| } else if (curType == Type::getDoubleTy(*llvmContext)) { |
| new StoreInst(i, doubleLocals[index], false, currentBlock); |
| ++index; |
| } else if (curType == Type::getFloatTy(*llvmContext)) { |
| new StoreInst(i, floatLocals[index], false, currentBlock); |
| } else { |
| Instruction* V = new StoreInst(i, objectLocals[index], false, currentBlock); |
| addHighLevelType(V, cur->findAssocClass(compilingClass->classLoader)); |
| } |
| } |
| |
| // Now that arguments have been setup, we can proceed with runtime calls. |
| #if JNJVM_EXECUTE > 0 |
| { |
| Value* arg = TheCompiler->getMethodInClass(compilingMethod); |
| |
| llvm::CallInst::Create(intrinsics->PrintMethodStartFunction, arg, "", |
| currentBlock); |
| } |
| #endif |
| |
| nbHandlers = readExceptionTable(reader, codeLen); |
| if (nbHandlers != 0) { |
| jmpBuffer = new AllocaInst(ArrayType::get(Type::getInt8Ty(*llvmContext), sizeof(vmkit::ExceptionBuffer)), "", currentBlock); |
| jmpBuffer = new BitCastInst(jmpBuffer, intrinsics->ptrType, "exceptionSavePoint", currentBlock); |
| } |
| |
| reader.cursor = start; |
| exploreOpcodes(reader, codeLen); |
| |
| endBlock = createBasicBlock("end"); |
| |
| if (returnType != Type::getVoidTy(*llvmContext)) { |
| endNode = llvm::PHINode::Create(returnType, 0, "", endBlock); |
| } |
| |
| checkYieldPoint(); |
| |
| if (isSynchro(compilingMethod->access)) { |
| beginSynchronize(); |
| } |
| |
| if (TheCompiler->hasExceptionsEnabled() && |
| !vmkit::System::SupportsHardwareStackOverflow()) { |
| // Variables have been allocated and the lock has been taken. Do the stack |
| // check now: if there is an exception, we will go to the lock release code. |
| currentExceptionBlock = opcodeInfos[0].exceptionBlock; |
| Value* FrameAddr = CallInst::Create(intrinsics->llvm_frameaddress, |
| intrinsics->constantZero, "", currentBlock); |
| FrameAddr = new PtrToIntInst(FrameAddr, intrinsics->pointerSizeType, "", |
| currentBlock); |
| Value* stackCheck = |
| BinaryOperator::CreateAnd(FrameAddr, intrinsics->constantStackOverflowMask, |
| "", currentBlock); |
| |
| stackCheck = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, stackCheck, |
| intrinsics->constantPtrZero, ""); |
| BasicBlock* stackOverflow = createBasicBlock("stack overflow"); |
| BasicBlock* noStackOverflow = createBasicBlock("no stack overflow"); |
| BranchInst::Create(stackOverflow, noStackOverflow, stackCheck, |
| currentBlock); |
| currentBlock = stackOverflow; |
| throwRuntimeException(intrinsics->StackOverflowErrorFunction, 0, 0); |
| currentBlock = noStackOverflow; |
| } |
| |
| reader.cursor = start; |
| compileOpcodes(reader, codeLen); |
| |
| // This isn't a real requirement, although javac-produced bytcode does |
| // seem to adhere to it. However jython and similar (clojure, etc) don't |
| // always create bytecode that matches this, and AFAICT rejecting the |
| // code is incorrect. |
| //assert(stack.size() == 0 && "Stack not empty after compiling bytecode"); |
| |
| // Fix a javac(?) bug where a method only throws an exception and does |
| // not return. |
| pred_iterator PI = pred_begin(endBlock); |
| pred_iterator PE = pred_end(endBlock); |
| if (PI == PE && returnType != Type::getVoidTy(*llvmContext)) { |
| Instruction* I = currentBlock->getTerminator(); |
| |
| if (isa<UnreachableInst>(I)) { |
| I->eraseFromParent(); |
| BranchInst::Create(endBlock, currentBlock); |
| endNode->addIncoming(Constant::getNullValue(returnType), |
| currentBlock); |
| } else if (InvokeInst* II = dyn_cast<InvokeInst>(I)) { |
| II->setNormalDest(endBlock); |
| endNode->addIncoming(Constant::getNullValue(returnType), |
| currentBlock); |
| } |
| |
| } |
| currentBlock = endBlock; |
| |
| if (returnValue != NULL) { |
| new StoreInst(endNode, returnValue, currentBlock); |
| } |
| |
| if (isSynchro(compilingMethod->access)) { |
| endSynchronize(); |
| } |
| |
| #if JNJVM_EXECUTE > 0 |
| { |
| Value* arg = TheCompiler->getMethodInClass(compilingMethod); |
| CallInst::Create(intrinsics->PrintMethodEndFunction, arg, "", currentBlock); |
| } |
| #endif |
| |
| finishExceptions(); |
| |
| PI = pred_begin(currentBlock); |
| PE = pred_end(currentBlock); |
| if (PI == PE) { |
| currentBlock->eraseFromParent(); |
| } else { |
| if (nbHandlers != 0) { |
| BasicBlock* ifNormal = createBasicBlock("No exception was thrown"); |
| BasicBlock* ifException = createBasicBlock("Rethrow Exception"); |
| Value* javaExceptionPtr = getJavaExceptionPtr(getJavaThreadPtr(getMutatorThreadPtr())); |
| Value* obj = new LoadInst(javaExceptionPtr, "pendingException", currentBlock); |
| Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_NE, obj, intrinsics->JavaObjectNullConstant, ""); |
| BranchInst::Create(ifException, ifNormal, test, currentBlock); |
| |
| currentBlock = ifException; |
| // Clear exception. |
| new StoreInst(intrinsics->JavaObjectNullConstant, javaExceptionPtr, |
| currentBlock); |
| CallInst::Create(intrinsics->ThrowExceptionFunction, obj, "", currentBlock); |
| new UnreachableInst(*llvmContext, currentBlock); |
| currentBlock = ifNormal; |
| } |
| |
| if (returnType != Type::getVoidTy(*llvmContext)) { |
| if (returnValue != NULL) { |
| Value* obj = new LoadInst( |
| returnValue, "", false, currentBlock); |
| ReturnInst::Create(*llvmContext, obj, currentBlock); |
| } else { |
| ReturnInst::Create(*llvmContext, endNode, currentBlock); |
| } |
| } else { |
| ReturnInst::Create(*llvmContext, currentBlock); |
| } |
| } |
| |
| |
| removeUnusedLocals(intLocals); |
| removeUnusedLocals(doubleLocals); |
| removeUnusedLocals(floatLocals); |
| removeUnusedLocals(longLocals); |
| removeUnusedLocals(intStack); |
| removeUnusedLocals(doubleStack); |
| removeUnusedLocals(floatStack); |
| removeUnusedLocals(longStack); |
| |
| removeUnusedObjects(objectLocals, intrinsics, TheCompiler->useCooperativeGC()); |
| removeUnusedObjects(objectStack, intrinsics, TheCompiler->useCooperativeGC()); |
| |
| delete[] opcodeInfos; |
| |
| PRINT_DEBUG(JNJVM_COMPILE, 1, DARK_CYAN, "--> end of javacompile for %s.%s\n", |
| UTF8Buffer(compilingClass->name).cString(), |
| UTF8Buffer(compilingMethod->name).cString()); |
| |
| JavaAttribute* annotationsAtt = |
| compilingMethod->lookupAttribute(JavaAttribute::annotationsAttribute); |
| |
| if (annotationsAtt) { |
| Reader reader(annotationsAtt, compilingClass->bytes); |
| AnnotationReader AR(reader, compilingClass); |
| uint16 numAnnotations = reader.readU2(); |
| for (uint16 i = 0; i < numAnnotations; ++i) { |
| AR.readAnnotation(); |
| const UTF8* name = |
| compilingClass->ctpInfo->UTF8At(AR.AnnotationNameIndex); |
| if (name->equals(TheCompiler->InlinePragma)) { |
| llvmFunction->removeAttributes(llvm::AttributeSet::FunctionIndex, |
| llvm::AttributeSet::get(*llvmContext, |
| llvm::AttributeSet::FunctionIndex, llvm::Attribute::NoInline)); |
| llvmFunction->addFnAttr(llvm::Attribute::AlwaysInline); |
| } else if (name->equals(TheCompiler->NoInlinePragma)) { |
| llvmFunction->addFnAttr(llvm::Attribute::NoInline); |
| } |
| } |
| } |
| |
| return llvmFunction; |
| } |
| |
| void JavaJIT::compareFP(Value* val1, Value* val2, Type* ty, bool l) { |
| Value* one = intrinsics->constantOne; |
| Value* zero = intrinsics->constantZero; |
| Value* minus = intrinsics->constantMinusOne; |
| |
| Value* c = new FCmpInst(*currentBlock, FCmpInst::FCMP_UGT, val1, val2, ""); |
| Value* r = llvm::SelectInst::Create(c, one, zero, "", currentBlock); |
| c = new FCmpInst(*currentBlock, FCmpInst::FCMP_ULT, val1, val2, ""); |
| r = llvm::SelectInst::Create(c, minus, r, "", currentBlock); |
| c = new FCmpInst(*currentBlock, FCmpInst::FCMP_UNO, val1, val2, ""); |
| r = llvm::SelectInst::Create(c, l ? one : minus, r, "", currentBlock); |
| |
| push(r, false); |
| |
| } |
| |
| void JavaJIT::loadConstant(uint16 index) { |
| JavaString* str = NULL; |
| llvm_gcroot(str, 0); |
| JavaConstantPool* ctpInfo = compilingClass->ctpInfo; |
| uint8 type = ctpInfo->typeAt(index); |
| |
| if (type == JavaConstantPool::ConstantString) { |
| if (TheCompiler->isStaticCompiling() && !TheCompiler->useCooperativeGC()) { |
| const UTF8* utf8 = ctpInfo->UTF8At(ctpInfo->ctpDef[index]); |
| str = *(compilingClass->classLoader->UTF8ToStr(utf8)); |
| Value* val = TheCompiler->getString(str); |
| push(val, false, upcalls->newString); |
| } else { |
| JavaString** str = (JavaString**)ctpInfo->ctpRes[index]; |
| if ((str != NULL) && !TheCompiler->isStaticCompiling()) { |
| Value* val = TheCompiler->getStringPtr(str); |
| val = new LoadInst(val, "", currentBlock); |
| push(val, false, upcalls->newString); |
| } else { |
| // Lookup the constant pool cache |
| Type* Ty = PointerType::getUnqual(intrinsics->JavaObjectType); |
| Value* val = getConstantPoolAt(index, intrinsics->StringLookupFunction, |
| Ty, 0, false); |
| val = new LoadInst(val, "", currentBlock); |
| push(val, false, upcalls->newString); |
| } |
| } |
| } else if (type == JavaConstantPool::ConstantLong) { |
| push(ConstantInt::get(Type::getInt64Ty(*llvmContext), ctpInfo->LongAt(index)), |
| false); |
| } else if (type == JavaConstantPool::ConstantDouble) { |
| push(ConstantFP::get(Type::getDoubleTy(*llvmContext), ctpInfo->DoubleAt(index)), |
| false); |
| } else if (type == JavaConstantPool::ConstantInteger) { |
| push(ConstantInt::get(Type::getInt32Ty(*llvmContext), ctpInfo->IntegerAt(index)), |
| false); |
| } else if (type == JavaConstantPool::ConstantFloat) { |
| push(ConstantFP::get(Type::getFloatTy(*llvmContext), ctpInfo->FloatAt(index)), |
| false); |
| } else if (type == JavaConstantPool::ConstantClass) { |
| UserCommonClass* cl = 0; |
| Value* res = getResolvedCommonClass(index, false, &cl); |
| |
| res = CallInst::Create(intrinsics->GetClassDelegateeFunction, res, "", |
| currentBlock); |
| push(res, false, upcalls->newClass); |
| } else { |
| fprintf(stderr, "I haven't verified your class file and it's malformed:" |
| " unknown ldc %d in %s.%s!\n", type, |
| UTF8Buffer(compilingClass->name).cString(), |
| UTF8Buffer(compilingMethod->name).cString()); |
| abort(); |
| } |
| } |
| |
| void JavaJIT::JITVerifyNull(Value* obj) { |
| if (TheCompiler->hasExceptionsEnabled()) { |
| if (nbHandlers == 0 && vmkit::System::SupportsHardwareNullCheck()) { |
| Value* indexes[2] = { intrinsics->constantZero, intrinsics->JavaObjectVTOffsetConstant }; |
| Value* VTPtr = GetElementPtrInst::Create(obj, indexes, "", currentBlock); |
| Instruction* VT = new LoadInst(VTPtr, "", true, currentBlock); |
| VT->setDebugLoc(DebugLoc::get(currentBytecodeIndex, 1, DbgSubprogram)); |
| } else { |
| Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, obj, intrinsics->JavaObjectNullConstant, ""); |
| |
| BasicBlock* nullObjBlock = createBasicBlock("object is null"); |
| BasicBlock* notNullObjBlock = createBasicBlock("object is not null"); |
| |
| BranchInst::Create(nullObjBlock, notNullObjBlock, test, currentBlock); |
| currentBlock = nullObjBlock; |
| throwRuntimeException(intrinsics->NullPointerExceptionFunction, 0, 0); |
| currentBlock = notNullObjBlock; |
| } |
| } |
| } |
| |
| Value* JavaJIT::verifyAndComputePtr(Value* obj, Value* index, |
| Type* arrayType, bool doNullCheck) { |
| if (doNullCheck) { |
| JITVerifyNull(obj); |
| } |
| |
| if (index->getType() != Type::getInt32Ty(*llvmContext)) { |
| index = new SExtInst(index, Type::getInt32Ty(*llvmContext), "", currentBlock); |
| } |
| |
| if (TheCompiler->hasExceptionsEnabled()) { |
| Value* size = arraySize(obj); |
| |
| Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_ULT, index, size, |
| ""); |
| |
| BasicBlock* ifTrue = createBasicBlock("true verifyAndComputePtr"); |
| BasicBlock* ifFalse = createBasicBlock("false verifyAndComputePtr"); |
| |
| BranchInst::Create(ifTrue, ifFalse, cmp, currentBlock); |
| |
| currentBlock = ifFalse; |
| Value* args[2] = { obj, index }; |
| throwRuntimeException(intrinsics->IndexOutOfBoundsExceptionFunction, args, 2); |
| currentBlock = ifTrue; |
| } |
| |
| Constant* zero = intrinsics->constantZero; |
| Value* val = new BitCastInst(obj, arrayType, "", currentBlock); |
| |
| Value* indexes[3] = { zero, intrinsics->JavaArrayElementsOffsetConstant, index }; |
| Value* ptr = GetElementPtrInst::Create(val, indexes, "", currentBlock); |
| |
| return ptr; |
| |
| } |
| |
| void JavaJIT::makeArgs(FunctionType::param_iterator it, |
| uint32 index, std::vector<Value*>& Args, uint32 nb) { |
| Args.reserve(nb + 2); |
| vmkit::ThreadAllocator threadAllocator; |
| Value** args = (Value**)threadAllocator.Allocate(nb*sizeof(Value*)); |
| sint32 start = nb - 1; |
| |
| for (sint32 i = start; i >= 0; --i) { |
| it--; |
| if (*it == Type::getInt64Ty(*llvmContext) |
| || *it == Type::getDoubleTy(*llvmContext)) { |
| pop(); |
| } |
| Value* tmp = pop(); |
| |
| Type* type = *it; |
| if (tmp->getType() != type) { // int8 or int16 |
| convertValue(tmp, type, currentBlock, false); |
| } |
| args[i] = tmp; |
| |
| } |
| |
| for (uint32 i = 0; i < nb; ++i) { |
| Args.push_back(args[i]); |
| } |
| } |
| |
| Value* JavaJIT::getTarget(Signdef* signature) { |
| int offset = 0; |
| Typedef* const* arguments = signature->getArgumentsType(); |
| for (uint32 i = 0; i < signature->nbArguments; i++) { |
| if (arguments[i]->isDouble() || arguments[i]->isLong()) { |
| offset++; |
| } |
| offset++; |
| } |
| return objectStack[currentStackIndex - 1 - offset]; |
| } |
| |
| Instruction* JavaJIT::lowerMathOps(const UTF8* name, |
| std::vector<Value*>& args) { |
| JnjvmBootstrapLoader* loader = compilingClass->classLoader->bootstrapLoader; |
| if (name->equals(loader->abs)) { |
| const Type* Ty = args[0]->getType(); |
| if (Ty == Type::getInt32Ty(*llvmContext)) { |
| Constant* const_int32_9 = intrinsics->constantZero; |
| Constant* const_int32_10 = intrinsics->constantMinusOne; |
| BinaryOperator* int32_tmpneg = |
| BinaryOperator::Create(Instruction::Sub, const_int32_9, args[0], |
| "tmpneg", currentBlock); |
| ICmpInst* int1_abscond = |
| new ICmpInst(*currentBlock, ICmpInst::ICMP_SGT, args[0], const_int32_10, |
| "abscond"); |
| return llvm::SelectInst::Create(int1_abscond, args[0], int32_tmpneg, |
| "abs", currentBlock); |
| } else if (Ty == Type::getInt64Ty(*llvmContext)) { |
| Constant* const_int64_9 = intrinsics->constantLongZero; |
| Constant* const_int64_10 = intrinsics->constantLongMinusOne; |
| |
| BinaryOperator* int64_tmpneg = |
| BinaryOperator::Create(Instruction::Sub, const_int64_9, args[0], |
| "tmpneg", currentBlock); |
| |
| ICmpInst* int1_abscond = new ICmpInst(*currentBlock, ICmpInst::ICMP_SGT, |
| args[0], const_int64_10, "abscond"); |
| |
| return llvm::SelectInst::Create(int1_abscond, args[0], int64_tmpneg, |
| "abs", currentBlock); |
| } else if (Ty == Type::getFloatTy(*llvmContext)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_fabs_f32, args[0], |
| "tmp1", currentBlock); |
| } else if (Ty == Type::getDoubleTy(*llvmContext)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_fabs_f64, args[0], |
| "tmp1", currentBlock); |
| } |
| } else if (name->equals(loader->sqrt)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_sqrt_f64, args[0], |
| "tmp1", currentBlock); |
| } else if (name->equals(loader->sin)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_sin_f64, args[0], |
| "tmp1", currentBlock); |
| } else if (name->equals(loader->cos)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_cos_f64, args[0], |
| "tmp1", currentBlock); |
| } else if (name->equals(loader->tan)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_tan_f64, args[0], |
| "tmp1", currentBlock); |
| } else if (name->equals(loader->asin)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_asin_f64, args[0], |
| "tmp1", currentBlock); |
| } else if (name->equals(loader->acos)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_acos_f64, args[0], |
| "tmp1", currentBlock); |
| } else if (name->equals(loader->atan)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_atan_f64, args[0], |
| "tmp1", currentBlock); |
| } else if (name->equals(loader->atan2)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_atan2_f64, |
| args, "tmp1", currentBlock); |
| } else if (name->equals(loader->exp)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_exp_f64, args[0], |
| "tmp1", currentBlock); |
| } else if (name->equals(loader->log)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_log_f64, args[0], |
| "tmp1", currentBlock); |
| } else if (name->equals(loader->pow)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_pow_f64, args, |
| "tmp1", currentBlock); |
| } else if (name->equals(loader->ceil)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_ceil_f64, args[0], "tmp1", |
| currentBlock); |
| } else if (name->equals(loader->floor)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_floor_f64, args[0], |
| "tmp1", currentBlock); |
| } else if (name->equals(loader->rint)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_rint_f64, args[0], |
| "tmp1", currentBlock); |
| } else if (name->equals(loader->cbrt)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_cbrt_f64, args[0], "tmp1", |
| currentBlock); |
| } else if (name->equals(loader->cosh)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_cosh_f64, args[0], "tmp1", |
| currentBlock); |
| } else if (name->equals(loader->expm1)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_expm1_f64, args[0], |
| "tmp1", currentBlock); |
| } else if (name->equals(loader->hypot)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_hypot_f64, args[0], |
| "tmp1", currentBlock); |
| } else if (name->equals(loader->log10)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_log10_f64, args[0], |
| "tmp1", currentBlock); |
| } else if (name->equals(loader->log1p)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_log1p_f64, args[0], |
| "tmp1", currentBlock); |
| } else if (name->equals(loader->sinh)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_sinh_f64, args[0], |
| "tmp1", currentBlock); |
| } else if (name->equals(loader->tanh)) { |
| return llvm::CallInst::Create(intrinsics->func_llvm_tanh_f64, args[0], |
| "tmp1", currentBlock); |
| } |
| |
| return 0; |
| } |
| |
| |
| Instruction* JavaJIT::lowerFloatOps(const UTF8* name, |
| std::vector<Value*>& args) { |
| JnjvmBootstrapLoader* loader = compilingClass->classLoader->bootstrapLoader; |
| if (name->equals(loader->floatToRawIntBits)) { |
| return new BitCastInst(args[0], Type::getInt32Ty(*llvmContext), "", currentBlock); |
| } else if (name->equals(loader->intBitsToFloat)) { |
| return new BitCastInst(args[0], Type::getFloatTy(*llvmContext), "", currentBlock); |
| } |
| return NULL; |
| } |
| |
| Instruction* JavaJIT::lowerDoubleOps(const UTF8* name, |
| std::vector<Value*>& args) { |
| JnjvmBootstrapLoader* loader = compilingClass->classLoader->bootstrapLoader; |
| if (name->equals(loader->doubleToRawLongBits)) { |
| return new BitCastInst(args[0], Type::getInt64Ty(*llvmContext), "", currentBlock); |
| } else if (name->equals(loader->longBitsToDouble)) { |
| return new BitCastInst(args[0], Type::getDoubleTy(*llvmContext), "", currentBlock); |
| } |
| return NULL; |
| } |
| |
| |
| Instruction* JavaJIT::invokeInline(JavaMethod* meth, |
| std::vector<Value*>& args, |
| bool customized) { |
| JavaJIT jit(TheCompiler, meth, llvmFunction, customized ? customizeFor : NULL); |
| jit.unifiedUnreachable = unifiedUnreachable; |
| jit.inlineMethods = inlineMethods; |
| jit.inlineMethods[meth] = true; |
| jit.inlining = true; |
| jit.DbgSubprogram = DbgSubprogram; |
| #if DEBUG |
| static int inlineNb = 0; |
| fprintf(stderr, "inline compile %d %s.%s%s from %s.%s (%d)\n", inlineNb++, |
| UTF8Buffer(meth->classDef->name).cString(), |
| UTF8Buffer(meth->name).cString(), |
| UTF8Buffer(meth->getSignature()->keyName).cString(), |
| UTF8Buffer(compilingClass->name).cString(), |
| UTF8Buffer(compilingMethod->name).cString(), |
| customized); |
| #endif |
| |
| Instruction* ret = jit.inlineCompile(currentBlock, |
| currentExceptionBlock, args); |
| inlineMethods[meth] = false; |
| return ret; |
| } |
| |
| void JavaJIT::invokeSpecial(uint16 index) { |
| JavaConstantPool* ctpInfo = compilingClass->ctpInfo; |
| JavaMethod* meth = 0; |
| Signdef* signature = 0; |
| const UTF8* name = 0; |
| const UTF8* cl = 0; |
| |
| ctpInfo->nameOfStaticOrSpecialMethod(index, cl, name, signature); |
| LLVMSignatureInfo* LSI = TheCompiler->getSignatureInfo(signature); |
| FunctionType* virtualType = LSI->getVirtualType(); |
| meth = ctpInfo->infoOfStaticOrSpecialMethod(index, ACC_VIRTUAL, signature); |
| bool thisReference = |
| isThisReference(stackSize() - signature->getNumberOfSlots() - 1); |
| |
| Value* func = 0; |
| bool needsInit = false; |
| if (TheCompiler->needsCallback(meth, NULL, &needsInit)) { |
| if (needsInit) { |
| // Make sure the class is loaded before materializing the method. |
| uint32 clIndex = ctpInfo->getClassIndexFromMethod(index); |
| UserCommonClass* cl = 0; |
| Value* Cl = getResolvedCommonClass(clIndex, false, &cl); |
| if (cl == NULL) { |
| CallInst::Create(intrinsics->ForceLoadedCheckFunction, Cl, "", |
| currentBlock); |
| } |
| } |
| func = TheCompiler->addCallback(compilingClass, index, signature, false, |
| currentBlock); |
| } else { |
| func = TheCompiler->getMethod(meth, NULL); |
| } |
| |
| std::vector<Value*> args; |
| FunctionType::param_iterator it = virtualType->param_end(); |
| makeArgs(it, index, args, signature->nbArguments + 1); |
| if (!thisReference) JITVerifyNull(args[0]); |
| |
| if (meth == compilingClass->classLoader->bootstrapLoader->upcalls->InitObject) { |
| return; |
| } |
| |
| llvm::Instruction* val = 0; |
| if (meth && canBeInlined(meth, false)) { |
| val = invokeInline(meth, args, false); |
| } else { |
| val = invoke(func, args, "", currentBlock); |
| } |
| |
| Type* retType = virtualType->getReturnType(); |
| if (retType != Type::getVoidTy(*llvmContext)) { |
| if (retType == intrinsics->JavaObjectType) { |
| JnjvmClassLoader* JCL = compilingClass->classLoader; |
| push(val, false, signature->getReturnType()->findAssocClass(JCL)); |
| } else { |
| push(val, signature->getReturnType()->isUnsigned()); |
| if (retType == Type::getDoubleTy(*llvmContext) || |
| retType == Type::getInt64Ty(*llvmContext)) { |
| push(intrinsics->constantZero, false); |
| } |
| } |
| } |
| } |
| |
| void JavaJIT::invokeStatic(uint16 index) { |
| JavaConstantPool* ctpInfo = compilingClass->ctpInfo; |
| Signdef* signature = 0; |
| const UTF8* name = 0; |
| const UTF8* className = 0; |
| ctpInfo->nameOfStaticOrSpecialMethod(index, className, name, signature); |
| LLVMSignatureInfo* LSI = TheCompiler->getSignatureInfo(signature); |
| FunctionType* staticType = LSI->getStaticType(); |
| ctpInfo->markAsStaticCall(index); |
| JnjvmBootstrapLoader* loader = compilingClass->classLoader->bootstrapLoader; |
| llvm::Instruction* val = 0; |
| |
| if (className->equals(loader->stackWalkerName)) { |
| callsStackWalker = true; |
| } |
| |
| JavaMethod* meth = ctpInfo->infoOfStaticOrSpecialMethod(index, ACC_STATIC, |
| signature); |
| |
| |
| uint32 clIndex = ctpInfo->getClassIndexFromMethod(index); |
| UserClass* cl = 0; |
| Value* Cl = getResolvedClass(clIndex, true, true, &cl); |
| if (!meth || (cl && needsInitialisationCheck(cl))) { |
| CallInst::Create(intrinsics->ForceInitialisationCheckFunction, Cl, "", |
| currentBlock); |
| } |
| |
| Value* func = 0; |
| bool needsInit = false; |
| if (TheCompiler->needsCallback(meth, NULL, &needsInit)) { |
| func = TheCompiler->addCallback(compilingClass, index, signature, |
| true, currentBlock); |
| } else { |
| func = TheCompiler->getMethod(meth, NULL); |
| } |
| |
| std::vector<Value*> args; // size = [signature->nbIn + 2]; |
| FunctionType::param_iterator it = staticType->param_end(); |
| makeArgs(it, index, args, signature->nbArguments); |
| |
| if (className->equals(loader->mathName)) { |
| val = lowerMathOps(name, args); |
| } else if (className->equals(loader->VMFloatName)) { |
| val = lowerFloatOps(name, args); |
| } else if (className->equals(loader->VMDoubleName)) { |
| val = lowerDoubleOps(name, args); |
| } |
| |
| if (val == NULL) { |
| if (meth != NULL && canBeInlined(meth, false)) { |
| val = invokeInline(meth, args, false); |
| } else { |
| val = invoke(func, args, "", currentBlock); |
| } |
| } |
| |
| Type* retType = staticType->getReturnType(); |
| if (retType != Type::getVoidTy(*llvmContext)) { |
| if (retType == intrinsics->JavaObjectType) { |
| JnjvmClassLoader* JCL = compilingClass->classLoader; |
| push(val, false, signature->getReturnType()->findAssocClass(JCL)); |
| } else { |
| push(val, signature->getReturnType()->isUnsigned()); |
| if (retType == Type::getDoubleTy(*llvmContext) || |
| retType == Type::getInt64Ty(*llvmContext)) { |
| push(intrinsics->constantZero, false); |
| } |
| } |
| } |
| } |
| |
| Value* JavaJIT::getConstantPoolAt(uint32 index, Function* resolver, |
| Type* returnType, |
| Value* additionalArg, bool doThrow) { |
| |
| // This makes unswitch loop very unhappy time-wise, but makes GVN happy |
| // number-wise. IMO, it's better to have this than Unswitch. |
| JavaConstantPool* ctp = compilingClass->ctpInfo; |
| Value* CTP = TheCompiler->getResolvedConstantPool(ctp); |
| Value* Cl = TheCompiler->getNativeClass(compilingClass); |
| |
| std::vector<Value*> Args; |
| Args.push_back(resolver); |
| Args.push_back(CTP); |
| Args.push_back(Cl); |
| Args.push_back(ConstantInt::get(Type::getInt32Ty(*llvmContext), index)); |
| if (additionalArg) Args.push_back(additionalArg); |
| |
| Value* res = 0; |
| if (doThrow) { |
| res = invoke(intrinsics->GetConstantPoolAtFunction, Args, "", |
| currentBlock); |
| } else { |
| res = CallInst::Create(intrinsics->GetConstantPoolAtFunction, Args, |
| "", currentBlock); |
| } |
| |
| Type* realType = |
| intrinsics->GetConstantPoolAtFunction->getReturnType(); |
| if (returnType == Type::getInt32Ty(*llvmContext)) { |
| return new PtrToIntInst(res, Type::getInt32Ty(*llvmContext), "", currentBlock); |
| } else if (returnType != realType) { |
| return new BitCastInst(res, returnType, "", currentBlock); |
| } |
| |
| return res; |
| } |
| |
| Value* JavaJIT::getResolvedCommonClass(uint16 index, bool doThrow, |
| UserCommonClass** alreadyResolved) { |
| |
| JavaConstantPool* ctpInfo = compilingClass->ctpInfo; |
| CommonClass* cl = ctpInfo->getMethodClassIfLoaded(index); |
| Value* node = 0; |
| if (cl && (!cl->isClass() || cl->asClass()->isResolved())) { |
| if (alreadyResolved) *alreadyResolved = cl; |
| node = TheCompiler->getNativeClass(cl); |
| // Since we only allocate for array classes that we own and |
| // only primitive arrays are already allocated, verify that the class |
| // array is not external. |
| if (TheCompiler->isStaticCompiling() && cl->isArray() && |
| node->getType() != intrinsics->JavaClassArrayType) { |
| node = new LoadInst(node, "", currentBlock); |
| } |
| if (node->getType() != intrinsics->JavaCommonClassType) { |
| node = new BitCastInst(node, intrinsics->JavaCommonClassType, "", |
| currentBlock); |
| } |
| } else { |
| node = getConstantPoolAt(index, intrinsics->ClassLookupFunction, |
| intrinsics->JavaCommonClassType, 0, doThrow); |
| } |
| |
| return node; |
| } |
| |
| Value* JavaJIT::getResolvedClass(uint16 index, bool clinit, bool doThrow, |
| Class** alreadyResolved) { |
| |
| JavaConstantPool* ctpInfo = compilingClass->ctpInfo; |
| Class* cl = (Class*)(ctpInfo->getMethodClassIfLoaded(index)); |
| Value* node = 0; |
| bool needsInit = true; |
| if (cl && cl->isResolved()) { |
| if (alreadyResolved) (*alreadyResolved) = cl; |
| node = TheCompiler->getNativeClass(cl); |
| needsInit = needsInitialisationCheck(cl); |
| } else { |
| node = getConstantPoolAt(index, intrinsics->ClassLookupFunction, |
| intrinsics->JavaClassType, 0, doThrow); |
| } |
| |
| |
| if (clinit && needsInit) { |
| if (node->getType() != intrinsics->JavaClassType) { |
| node = new BitCastInst(node, intrinsics->JavaClassType, "", currentBlock); |
| } |
| return invoke(intrinsics->InitialisationCheckFunction, node, "", |
| currentBlock); |
| } else { |
| return node; |
| } |
| } |
| |
| void JavaJIT::invokeNew(uint16 index) { |
| |
| Class* cl = 0; |
| Value* Cl = getResolvedClass(index, true, true, &cl); |
| |
| Value* VT = 0; |
| Value* Size = 0; |
| |
| if (cl) { |
| VT = TheCompiler->getVirtualTable(cl->virtualVT); |
| LLVMClassInfo* LCI = TheCompiler->getClassInfo(cl); |
| Size = LCI->getVirtualSize(); |
| |
| bool needsCheck = needsInitialisationCheck(cl); |
| if (needsCheck) { |
| Cl = invoke(intrinsics->ForceInitialisationCheckFunction, Cl, "", |
| currentBlock); |
| } |
| |
| } else { |
| VT = CallInst::Create(intrinsics->GetVTFromClassFunction, Cl, "", |
| currentBlock); |
| Size = CallInst::Create(intrinsics->GetObjectSizeFromClassFunction, Cl, |
| "", currentBlock); |
| } |
| |
| VT = new BitCastInst(VT, intrinsics->ptrType, "", currentBlock); |
| Instruction* val = invoke(cl ? intrinsics->VTAllocateFunction : |
| intrinsics->VTAllocateUnresolvedFunction, |
| Size, VT, "", currentBlock); |
| |
| addHighLevelType(val, cl ? cl : upcalls->OfObject); |
| Instruction* res = new BitCastInst(val, intrinsics->JavaObjectType, "", currentBlock); |
| push(res, false, cl ? cl : upcalls->OfObject); |
| |
| // Make sure to add the object to the finalization list after it has been |
| // pushed. |
| if (cl && cl->virtualVT->hasDestructor()) { |
| CallInst::Create(intrinsics->AddFinalizationCandidate, val, "", currentBlock); |
| } |
| } |
| |
| Value* JavaJIT::ldResolved(uint16 index, bool stat, Value* object, |
| Type* fieldTypePtr, bool thisReference) { |
| JavaConstantPool* info = compilingClass->ctpInfo; |
| |
| JavaField* field = info->lookupField(index, stat); |
| if (field && field->classDef->isResolved()) { |
| LLVMClassInfo* LCI = TheCompiler->getClassInfo(field->classDef); |
| LLVMFieldInfo* LFI = TheCompiler->getFieldInfo(field); |
| Type* type = NULL; |
| if (stat) { |
| type = LCI->getStaticType(); |
| Value* Cl = TheCompiler->getNativeClass(field->classDef); |
| bool needsCheck = needsInitialisationCheck(field->classDef); |
| if (needsCheck) { |
| Cl = invoke(intrinsics->InitialisationCheckFunction, Cl, "", |
| currentBlock); |
| CallInst::Create(intrinsics->ForceInitialisationCheckFunction, Cl, "", |
| currentBlock); |
| } |
| |
| object = TheCompiler->getStaticInstance(field->classDef); |
| } else { |
| object = new LoadInst( |
| object, "", false, currentBlock); |
| if (!thisReference) JITVerifyNull(object); |
| type = LCI->getVirtualType(); |
| } |
| |
| Value* objectConvert = new BitCastInst(object, type, "", currentBlock); |
| |
| Value* args[2] = { intrinsics->constantZero, LFI->getOffset() }; |
| Value* ptr = llvm::GetElementPtrInst::Create(objectConvert, args, "", |
| currentBlock); |
| return ptr; |
| } |
| |
| Type* Pty = intrinsics->arrayPtrType; |
| Constant* zero = intrinsics->constantZero; |
| |
| Function* func = stat ? intrinsics->StaticFieldLookupFunction : |
| intrinsics->VirtualFieldLookupFunction; |
| |
| Type* returnType = NULL; |
| if (stat) { |
| returnType = intrinsics->ptrType; |
| } else { |
| returnType = Type::getInt32Ty(*llvmContext); |
| } |
| |
| Value* ptr = getConstantPoolAt(index, func, returnType, 0, true); |
| if (!stat) { |
| object = new LoadInst( |
| object, "", false, currentBlock); |
| if (!thisReference) JITVerifyNull(object); |
| Value* tmp = new BitCastInst(object, Pty, "", currentBlock); |
| Value* args[2] = { zero, ptr }; |
| ptr = GetElementPtrInst::Create(tmp, args, "", currentBlock); |
| } |
| |
| return new BitCastInst(ptr, fieldTypePtr, "", currentBlock); |
| } |
| |
| void JavaJIT::convertValue(Value*& val, Type* t1, BasicBlock* currentBlock, |
| bool usign) { |
| Type* t2 = val->getType(); |
| if (t1 != t2) { |
| if (t1->isIntegerTy() && t2->isIntegerTy()) { |
| if (t2->getPrimitiveSizeInBits() < t1->getPrimitiveSizeInBits()) { |
| if (usign) { |
| val = new ZExtInst(val, t1, "", currentBlock); |
| } else { |
| val = new SExtInst(val, t1, "", currentBlock); |
| } |
| } else { |
| val = new TruncInst(val, t1, "", currentBlock); |
| } |
| } else if (t1->isFloatTy() && t2->isFloatTy()) { |
| if (t2->getPrimitiveSizeInBits() < t1->getPrimitiveSizeInBits()) { |
| val = new FPExtInst(val, t1, "", currentBlock); |
| } else { |
| val = new FPTruncInst(val, t1, "", currentBlock); |
| } |
| } else if (isa<PointerType>(t1) && isa<PointerType>(t2)) { |
| val = new BitCastInst(val, t1, "", currentBlock); |
| } |
| } |
| } |
| |
| |
| void JavaJIT::setStaticField(uint16 index) { |
| Typedef* sign = compilingClass->ctpInfo->infoOfField(index); |
| LLVMAssessorInfo& LAI = TheCompiler->getTypedefInfo(sign); |
| Type* type = LAI.llvmType; |
| |
| Value* ptr = ldResolved(index, true, NULL, LAI.llvmTypePtr); |
| |
| Value* val = pop(); |
| if (type == Type::getInt64Ty(*llvmContext) || |
| type == Type::getDoubleTy(*llvmContext)) { |
| val = pop(); |
| } |
| |
| if (type != val->getType()) { // int1, int8, int16 |
| convertValue(val, type, currentBlock, false); |
| } |
| |
| if (vmkit::Collector::needsNonHeapWriteBarrier() && type == intrinsics->JavaObjectType) { |
| ptr = new BitCastInst(ptr, intrinsics->ptrPtrType, "", currentBlock); |
| val = new BitCastInst(val, intrinsics->ptrType, "", currentBlock); |
| Value* args[2] = { ptr, val }; |
| CallInst::Create(intrinsics->NonHeapWriteBarrierFunction, args, "", currentBlock); |
| } else { |
| new StoreInst(val, ptr, false, currentBlock); |
| } |
| } |
| |
| void JavaJIT::getStaticField(uint16 index) { |
| JavaObject* val = 0; |
| llvm_gcroot(val, 0); |
| |
| Typedef* sign = compilingClass->ctpInfo->infoOfField(index); |
| LLVMAssessorInfo& LAI = TheCompiler->getTypedefInfo(sign); |
| Type* type = LAI.llvmType; |
| |
| Value* ptr = ldResolved(index, true, NULL, LAI.llvmTypePtr); |
| |
| bool final = false; |
| JnjvmBootstrapLoader* JBL = compilingClass->classLoader->bootstrapLoader; |
| if (!compilingMethod->name->equals(JBL->clinitName)) { |
| JavaField* field = compilingClass->ctpInfo->lookupField(index, true); |
| if (field && field->classDef->isReady()) final = isFinal(field->access); |
| if (final) { |
| if (sign->isPrimitive()) { |
| const PrimitiveTypedef* prim = (PrimitiveTypedef*)sign; |
| if (prim->isInt()) { |
| sint32 val = field->getStaticInt32Field(); |
| push(ConstantInt::get(Type::getInt32Ty(*llvmContext), val), false); |
| } else if (prim->isByte()) { |
| sint8 val = (sint8)field->getStaticInt8Field(); |
| push(ConstantInt::get(Type::getInt8Ty(*llvmContext), val), false); |
| } else if (prim->isBool()) { |
| uint8 val = (uint8)field->getStaticInt8Field(); |
| push(ConstantInt::get(Type::getInt8Ty(*llvmContext), val), true); |
| } else if (prim->isShort()) { |
| sint16 val = (sint16)field->getStaticInt16Field(); |
| push(ConstantInt::get(Type::getInt16Ty(*llvmContext), val), false); |
| } else if (prim->isChar()) { |
| uint16 val = (uint16)field->getStaticInt16Field(); |
| push(ConstantInt::get(Type::getInt16Ty(*llvmContext), val), true); |
| } else if (prim->isLong()) { |
| sint64 val = (sint64)field->getStaticLongField(); |
| push(ConstantInt::get(Type::getInt64Ty(*llvmContext), val), false); |
| } else if (prim->isFloat()) { |
| float val = (float)field->getStaticFloatField(); |
| push(ConstantFP::get(Type::getFloatTy(*llvmContext), val), false); |
| } else if (prim->isDouble()) { |
| double val = (double)field->getStaticDoubleField(); |
| push(ConstantFP::get(Type::getDoubleTy(*llvmContext), val), false); |
| } else { |
| abort(); |
| } |
| } else { |
| if (TheCompiler->isStaticCompiling() && !TheCompiler->useCooperativeGC()) { |
| val = field->getStaticObjectField(); |
| JnjvmClassLoader* JCL = field->classDef->classLoader; |
| Value* V = TheCompiler->getFinalObject(val, sign->assocClass(JCL)); |
| CommonClass* cl = vmkit::Collector::begOf(val) ? |
| JavaObject::getClass(val) : NULL; |
| push(V, false, cl); |
| } else { |
| // Do not call getFinalObject, as the object may move in-between two |
| // loads of this static. |
| Value* V = new LoadInst(ptr, "", currentBlock); |
| JnjvmClassLoader* JCL = compilingClass->classLoader; |
| push(V, false, sign->findAssocClass(JCL)); |
| } |
| } |
| } |
| } |
| |
| if (!final) { |
| JnjvmClassLoader* JCL = compilingClass->classLoader; |
| CommonClass* cl = sign->findAssocClass(JCL); |
| push(new LoadInst(ptr, "", currentBlock), sign->isUnsigned(), cl); |
| } |
| if (type == Type::getInt64Ty(*llvmContext) || |
| type == Type::getDoubleTy(*llvmContext)) { |
| push(intrinsics->constantZero, false); |
| } |
| } |
| |
| void JavaJIT::setVirtualField(uint16 index) { |
| Typedef* sign = compilingClass->ctpInfo->infoOfField(index); |
| LLVMAssessorInfo& LAI = TheCompiler->getTypedefInfo(sign); |
| Type* type = LAI.llvmType; |
| int stackIndex = currentStackIndex - 2; |
| if (type == Type::getInt64Ty(*llvmContext) || |
| type == Type::getDoubleTy(*llvmContext)) { |
| stackIndex--; |
| } |
| Value* object = objectStack[stackIndex]; |
| bool thisReference = isThisReference(stackIndex); |
| Value* ptr = ldResolved(index, false, object, LAI.llvmTypePtr, thisReference); |
| |
| Value* val = pop(); |
| if (type == Type::getInt64Ty(*llvmContext) || |
| type == Type::getDoubleTy(*llvmContext)) { |
| val = pop(); |
| } |
| pop(); // Pop the object |
| |
| if (type != val->getType()) { // int1, int8, int16 |
| convertValue(val, type, currentBlock, false); |
| } |
| |
| if (vmkit::Collector::needsWriteBarrier() && type == intrinsics->JavaObjectType) { |
| ptr = new BitCastInst(ptr, intrinsics->ptrPtrType, "", currentBlock); |
| val = new BitCastInst(val, intrinsics->ptrType, "", currentBlock); |
| object = new LoadInst(object, "", false, currentBlock); |
| object = new BitCastInst(object, intrinsics->ptrType, "", currentBlock); |
| Value* args[3] = { object, ptr, val }; |
| CallInst::Create(intrinsics->FieldWriteBarrierFunction, args, "", currentBlock); |
| } else { |
| new StoreInst(val, ptr, false, currentBlock); |
| } |
| } |
| |
| void JavaJIT::getVirtualField(uint16 index) { |
| Typedef* sign = compilingClass->ctpInfo->infoOfField(index); |
| JnjvmClassLoader* JCL = compilingClass->classLoader; |
| CommonClass* cl = sign->findAssocClass(JCL); |
| |
| LLVMAssessorInfo& LAI = TheCompiler->getTypedefInfo(sign); |
| Type* type = LAI.llvmType; |
| Value* obj = objectStack[currentStackIndex - 1]; |
| bool thisReference = isThisReference(currentStackIndex - 1); |
| pop(); // Pop the object |
| |
| Value* ptr = ldResolved(index, false, obj, LAI.llvmTypePtr, thisReference); |
| |
| JnjvmBootstrapLoader* JBL = compilingClass->classLoader->bootstrapLoader; |
| bool final = false; |
| |
| // In init methods, the fields have not been set yet. |
| if (!compilingMethod->name->equals(JBL->initName)) { |
| JavaField* field = compilingClass->ctpInfo->lookupField(index, false); |
| if (field) { |
| final = isFinal(field->access) && sign->isPrimitive(); |
| } |
| if (final) { |
| Function* F = 0; |
| assert(sign->isPrimitive()); |
| const PrimitiveTypedef* prim = (PrimitiveTypedef*)sign; |
| if (prim->isInt()) { |
| F = intrinsics->GetFinalInt32FieldFunction; |
| } else if (prim->isByte()) { |
| F = intrinsics->GetFinalInt8FieldFunction; |
| } else if (prim->isBool()) { |
| F = intrinsics->GetFinalInt8FieldFunction; |
| } else if (prim->isShort()) { |
| F = intrinsics->GetFinalInt16FieldFunction; |
| } else if (prim->isChar()) { |
| F = intrinsics->GetFinalInt16FieldFunction; |
| } else if (prim->isLong()) { |
| F = intrinsics->GetFinalLongFieldFunction; |
| } else if (prim->isFloat()) { |
| F = intrinsics->GetFinalFloatFieldFunction; |
| } else if (prim->isDouble()) { |
| F = intrinsics->GetFinalDoubleFieldFunction; |
| } else { |
| abort(); |
| } |
| push(CallInst::Create(F, ptr, "", currentBlock), sign->isUnsigned(), cl); |
| } |
| } |
| |
| if (!final) push(new LoadInst(ptr, "", currentBlock), sign->isUnsigned(), cl); |
| if (type == Type::getInt64Ty(*llvmContext) || |
| type == Type::getDoubleTy(*llvmContext)) { |
| push(intrinsics->constantZero, false); |
| } |
| } |
| |
| |
| void JavaJIT::invokeInterface(uint16 index) { |
| |
| // Do the usual |
| JavaConstantPool* ctpInfo = compilingClass->ctpInfo; |
| const UTF8* name = 0; |
| Signdef* signature = ctpInfo->infoOfInterfaceOrVirtualMethod(index, name); |
| bool thisReference = |
| isThisReference(stackSize() - signature->getNumberOfSlots() - 1); |
| |
| LLVMSignatureInfo* LSI = TheCompiler->getSignatureInfo(signature); |
| FunctionType* virtualType = LSI->getVirtualType(); |
| PointerType* virtualPtrType = LSI->getVirtualPtrType(); |
| |
| Type* retType = virtualType->getReturnType(); |
| |
| CommonClass* cl = 0; |
| JavaMethod* meth = 0; |
| ctpInfo->infoOfMethod(index, ACC_VIRTUAL, cl, meth); |
| Value* Meth = 0; |
| |
| if (meth) { |
| Meth = TheCompiler->getMethodInClass(meth); |
| } else { |
| Meth = getConstantPoolAt(index, intrinsics->InterfaceLookupFunction, |
| intrinsics->JavaMethodType, 0, true); |
| } |
| |
| uint32_t tableIndex = InterfaceMethodTable::getIndex(name, signature->keyName); |
| Constant* Index = ConstantInt::get(Type::getInt32Ty(*llvmContext), |
| tableIndex); |
| Value* targetObject = getTarget(signature); |
| targetObject = new LoadInst( |
| targetObject, "", false, currentBlock); |
| if (!thisReference) JITVerifyNull(targetObject); |
| // TODO: The following code needs more testing. |
| #if 0 |
| BasicBlock* endBlock = createBasicBlock("end interface invoke"); |
| PHINode * node = PHINode::Create(virtualPtrType, "", endBlock); |
| |
| BasicBlock* label_bb = createBasicBlock("bb"); |
| BasicBlock* label_bb4 = createBasicBlock("bb4"); |
| BasicBlock* label_bb6 = createBasicBlock("bb6"); |
| BasicBlock* label_bb7 = createBasicBlock("bb7"); |
| |
| // Block entry (label_entry) |
| Value* VT = CallInst::Create(intrinsics->GetVTFunction, targetObject, "", |
| currentBlock); |
| Value* IMT = CallInst::Create(intrinsics->GetIMTFunction, VT, "", |
| currentBlock); |
| |
| |
| Value* indices[2] = { intrinsics->constantZero, Index }; |
| Instruction* ptr_18 = GetElementPtrInst::Create(IMT, indices, "", |
| currentBlock); |
| Instruction* int32_19 = new LoadInst(ptr_18, "", false, currentBlock); |
| int32_19 = new PtrToIntInst(int32_19, intrinsics->pointerSizeType, "", |
| currentBlock); |
| Value* one = ConstantInt::get(intrinsics->pointerSizeType, 1); |
| Value* zero = ConstantInt::get(intrinsics->pointerSizeType, 0); |
| BinaryOperator* int32_20 = BinaryOperator::Create(Instruction::And, int32_19, |
| one, "", currentBlock); |
| ICmpInst* int1_toBool = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, |
| int32_20, zero, "toBool"); |
| BranchInst::Create(label_bb, label_bb4, int1_toBool, currentBlock); |
| |
| // Block bb (label_bb) |
| currentBlock = label_bb; |
| CastInst* ptr_22 = new IntToPtrInst(int32_19, virtualPtrType, "", currentBlock); |
| |
| node->addIncoming(ptr_22, currentBlock); |
| BranchInst::Create(endBlock, currentBlock); |
| |
| // Block bb4 (label_bb4) |
| currentBlock = label_bb4; |
| Constant* MinusTwo = ConstantInt::get(intrinsics->pointerSizeType, -2); |
| BinaryOperator* int32_25 = BinaryOperator::Create(Instruction::And, int32_19, |
| MinusTwo, "", currentBlock); |
| PointerType* Ty = PointerType::getUnqual(intrinsics->JavaMethodType); |
| CastInst* ptr_26 = new IntToPtrInst(int32_25, Ty, "", currentBlock); |
| LoadInst* int32_27 = new LoadInst(ptr_26, "", false, currentBlock); |
| ICmpInst* int1_28 = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, int32_27, |
| Meth, ""); |
| BranchInst::Create(label_bb6, label_bb7, int1_28, currentBlock); |
| |
| // Block bb6 (label_bb6) |
| currentBlock = label_bb6; |
| PHINode* ptr_table_0_lcssa = PHINode::Create(Ty, 2, "table.0.lcssa", |
| currentBlock); |
| ptr_table_0_lcssa->reserveOperandSpace(2); |
| ptr_table_0_lcssa->addIncoming(ptr_26, label_bb4); |
| |
| GetElementPtrInst* ptr_31 = GetElementPtrInst::Create(ptr_table_0_lcssa, |
| intrinsics->constantOne, "", |
| currentBlock); |
| |
| LoadInst* int32_32 = new LoadInst(ptr_31, "", false, currentBlock); |
| CastInst* ptr_33 = new BitCastInst(int32_32, virtualPtrType, "", |
| currentBlock); |
| node->addIncoming(ptr_33, currentBlock); |
| |
| BranchInst::Create(endBlock, currentBlock); |
| |
| // Block bb7 (label_bb7) |
| currentBlock = label_bb7; |
| PHINode* int32_indvar = PHINode::Create(Type::getInt32Ty(*llvmContext), |
| "indvar", currentBlock); |
| int32_indvar->reserveOperandSpace(2); |
| int32_indvar->addIncoming(intrinsics->constantZero, label_bb4); |
| |
| BinaryOperator* int32_table_010_rec = |
| BinaryOperator::Create(Instruction::Shl, int32_indvar, intrinsics->constantOne, |
| "table.010.rec", currentBlock); |
| |
| BinaryOperator* int32__rec = |
| BinaryOperator::Create(Instruction::Add, int32_table_010_rec, |
| intrinsics->constantTwo, ".rec", currentBlock); |
| GetElementPtrInst* ptr_37 = GetElementPtrInst::Create(ptr_26, int32__rec, "", |
| currentBlock); |
| LoadInst* int32_38 = new LoadInst(ptr_37, "", false, currentBlock); |
| ICmpInst* int1_39 = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, int32_38, |
| Meth, ""); |
| BinaryOperator* int32_indvar_next = |
| BinaryOperator::Create(Instruction::Add, int32_indvar, intrinsics->constantOne, |
| "indvar.next", currentBlock); |
| BranchInst::Create(label_bb6, label_bb7, int1_39, currentBlock); |
| |
| int32_indvar->addIncoming(int32_indvar_next, currentBlock); |
| ptr_table_0_lcssa->addIncoming(ptr_37, currentBlock); |
| |
| currentBlock = endBlock; |
| #else |
| std::vector<Value*> Args; |
| Args.push_back(targetObject); |
| Args.push_back(Meth); |
| Args.push_back(Index); |
| Value* node = invoke(intrinsics->ResolveInterfaceFunction, |
| Args, "invokeinterface", currentBlock); |
| node = new BitCastInst(node, virtualPtrType, "", currentBlock); |
| #endif |
| |
| std::vector<Value*> args; // size = [signature->nbIn + 3]; |
| FunctionType::param_iterator it = virtualType->param_end(); |
| makeArgs(it, index, args, signature->nbArguments + 1); |
| Value* ret = invoke(node, args, "", currentBlock); |
| if (retType != Type::getVoidTy(*llvmContext)) { |
| if (ret->getType() == intrinsics->JavaObjectType) { |
| JnjvmClassLoader* JCL = compilingClass->classLoader; |
| push(ret, false, signature->getReturnType()->findAssocClass(JCL)); |
| } else { |
| push(ret, signature->getReturnType()->isUnsigned()); |
| if (retType == Type::getDoubleTy(*llvmContext) || |
| retType == Type::getInt64Ty(*llvmContext)) { |
| push(intrinsics->constantZero, false); |
| } |
| } |
| } |
| } |
| |
| DebugLoc JavaJIT::CreateLocation() { |
| DebugLoc DL = DebugLoc::get(currentBytecodeIndex, 0, DbgSubprogram); |
| return DL; |
| } |
| |
| Instruction* JavaJIT::invoke(Value *F, std::vector<llvm::Value*>& args, |
| const char* Name, |
| BasicBlock *InsertAtEnd) { |
| assert(!inlining); |
| |
| BasicBlock* ifException = NULL; |
| if (jmpBuffer != NULL) { |
| BasicBlock* doCall = createBasicBlock("Perform call"); |
| ifException = createBasicBlock("Exception thrown"); |
| Instruction* check = CallInst::Create(intrinsics->SetjmpFunction, jmpBuffer, "", currentBlock); |
| check = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, check, intrinsics->constantZero, ""); |
| BranchInst::Create(doCall, ifException, check, currentBlock); |
| currentBlock = doCall; |
| CallInst::Create(intrinsics->RegisterSetjmpFunction, jmpBuffer, "", currentBlock); |
| } |
| |
| Instruction* res = CallInst::Create(F, args, Name, currentBlock); |
| DebugLoc DL = CreateLocation(); |
| res->setDebugLoc(DL); |
| |
| if (jmpBuffer != NULL) { |
| CallInst::Create(intrinsics->UnregisterSetjmpFunction, jmpBuffer, "", currentBlock); |
| BasicBlock* ifNormal = createBasicBlock("no exception block"); |
| BranchInst::Create(ifNormal, currentBlock); |
| |
| currentBlock = ifException; |
| CallInst::Create(intrinsics->UnregisterSetjmpFunction, jmpBuffer, "", currentBlock); |
| BranchInst::Create(currentExceptionBlock, currentBlock); |
| currentBlock = ifNormal; |
| } |
| |
| return res; |
| } |
| |
| Instruction* JavaJIT::invoke(Value *F, Value* arg1, const char* Name, |
| BasicBlock *InsertAtEnd) { |
| std::vector<Value*> args; |
| args.push_back(arg1); |
| return invoke(F, args, Name, InsertAtEnd); |
| } |
| |
| Instruction* JavaJIT::invoke(Value *F, Value* arg1, Value* arg2, |
| const char* Name, BasicBlock *InsertAtEnd) { |
| std::vector<Value*> args; |
| args.push_back(arg1); |
| args.push_back(arg2); |
| return invoke(F, args, Name, InsertAtEnd); |
| } |
| |
| Instruction* JavaJIT::invoke(Value *F, const char* Name, |
| BasicBlock *InsertAtEnd) { |
| std::vector<Value*> args; |
| return invoke(F, args, Name, InsertAtEnd); |
| } |
| |
| void JavaJIT::throwException(Value* obj, bool checkNull) { |
| if (checkNull) JITVerifyNull(obj); |
| if (nbHandlers == 0) { |
| CallInst::Create(intrinsics->ThrowExceptionFunction, obj, "", currentBlock); |
| new UnreachableInst(*llvmContext, currentBlock); |
| } else { |
| Value* javaExceptionPtr = getJavaExceptionPtr(getJavaThreadPtr(getMutatorThreadPtr())); |
| if (vmkit::Collector::needsNonHeapWriteBarrier()) { |
| Instruction* ptr = new BitCastInst(javaExceptionPtr, intrinsics->ptrPtrType, "", currentBlock); |
| Instruction* val = new BitCastInst(obj, intrinsics->ptrType, "", currentBlock); |
| Value* args[2] = { ptr, val }; |
| CallInst::Create(intrinsics->NonHeapWriteBarrierFunction, args, "", currentBlock); |
| } else { |
| new StoreInst(obj, javaExceptionPtr, currentBlock); |
| } |
| |
| BranchInst::Create(currentExceptionBlock, currentBlock); |
| } |
| } |
| |
| void JavaJIT::throwRuntimeException(llvm::Function* F, Value* arg1) { |
| Value* args[1] = { arg1 }; |
| throwRuntimeException(F, args, 1); |
| } |
| |
| void JavaJIT::throwRuntimeException(llvm::Function* F, Value** args, uint32 nbArgs) { |
| Instruction* obj = CallInst::Create(F, ArrayRef<Value*>(args, nbArgs), "exceptionObject", currentBlock); |
| DebugLoc DL = CreateLocation(); |
| obj->setDebugLoc(DL); |
| throwException(obj, false); |
| } |
| |
| /// Handler - This class represents an exception handler. It is only needed |
| /// when parsing the .class file in the JIT, therefore it is only defined |
| /// here. The readExceptionTable function is the only function that makes |
| /// use of this class. |
| struct Handler { |
| |
| /// startpc - The bytecode number that begins the try clause. |
| uint32 startpc; |
| |
| /// endpc - The bytecode number that ends the try clause. |
| uint32 endpc; |
| |
| /// handlerpc - The bytecode number where the handler code starts. |
| uint32 handlerpc; |
| |
| /// catche - Index in the constant pool of the exception class. |
| uint16 catche; |
| |
| /// catchClass - The class of the exception: it must always be loaded before |
| /// reading the exception table so that we do not throw an exception |
| /// when compiling. |
| UserClass* catchClass; |
| |
| /// tester - The basic block that tests if the exception is handled by this |
| /// handler. If the handler is not the first of a list of handlers with the |
| /// same range, than this block is the catcher block. Otherwise, it is the |
| /// destination of the catcher block and of the handlers that do not handler |
| /// the exception. |
| llvm::BasicBlock* tester; |
| |
| /// javaHandler - The Java code that handles the exception. At this point, we |
| /// know we have caught and are handling the exception. The Java exception |
| /// object is the PHI node that begins this block. |
| llvm::BasicBlock* javaHandler; |
| |
| }; |
| |
| unsigned JavaJIT::readExceptionTable(Reader& reader, uint32 codeLen) { |
| |
| // This function uses currentBlock to simplify things. We save the current |
| // value of currentBlock to restore it at the end of the function |
| BasicBlock* temp = currentBlock; |
| |
| sint16 nbe = reader.readU2(); |
| sint16 sync = isSynchro(compilingMethod->access) ? 1 : 0; |
| nbe += sync; |
| |
| vmkit::ThreadAllocator allocator; |
| // Loop over all handlers in the bytecode to initialize their values. |
| Handler* handlers = |
| (Handler*)allocator.Allocate(sizeof(Handler) * (nbe - sync)); |
| for (uint16 i = 0; i < nbe - sync; ++i) { |
| Handler* ex = &handlers[i]; |
| ex->startpc = reader.readU2(); |
| ex->endpc = reader.readU2(); |
| ex->handlerpc = reader.readU2(); |
| |
| opcodeInfos[ex->handlerpc].isReachable = true; |
| |
| ex->catche = reader.readU2(); |
| |
| if (ex->catche) { |
| UserClass* cl = |
| (UserClass*)(compilingClass->ctpInfo->isClassLoaded(ex->catche)); |
| // When loading the class, we made sure that all exception classes |
| // were loaded, so cl must have a value. |
| assert(cl && "exception class has not been loaded"); |
| ex->catchClass = cl; |
| } else { |
| ex->catchClass = Classpath::newThrowable; |
| } |
| |
| ex->tester = createBasicBlock("testException"); |
| |
| // Set the unwind destination of the instructions in the range of this |
| // handler to the test block of the handler. If an instruction already has |
| // a handler and thus is not the synchronize or regular end handler block, |
| // leave it as-is. |
| for (uint16 i = ex->startpc; i < ex->endpc; ++i) { |
| if (opcodeInfos[i].exceptionBlock == endExceptionBlock) { |
| opcodeInfos[i].exceptionBlock = ex->tester; |
| //opcodeInfos[i].handlerPC = ex->handlerpc; |
| } |
| } |
| |
| // If the handler pc does not already have a block, create a new one. |
| if (!(opcodeInfos[ex->handlerpc].newBlock)) { |
| opcodeInfos[ex->handlerpc].newBlock = createBasicBlock("javaHandler"); |
| } |
| |
| // Set the Java handler for this exception. |
| ex->javaHandler = opcodeInfos[ex->handlerpc].newBlock; |
| opcodeInfos[ex->handlerpc].handler = true; |
| } |
| |
| // Loop over all handlers to implement their tester. |
| for (sint16 i = 0; i < nbe - sync; ++i) { |
| Handler* cur = &handlers[i]; |
| BasicBlock* bbNext = 0; |
| currentExceptionBlock = opcodeInfos[cur->handlerpc].exceptionBlock; |
| |
| // Look out where we go if we're not the handler for the exception. |
| if (i + 1 != nbe - sync) { |
| Handler* next = &handlers[i + 1]; |
| if (!(cur->startpc >= next->startpc && cur->endpc <= next->endpc)) { |
| // If there is no handler to go to (either one that has the same range |
| // or one that contains the range), then we jump to the end handler. |
| bbNext = endExceptionBlock; |
| } else { |
| // If there's a handler to goto, we jump to its tester block and record |
| // the exception PHI node to give our exception to the tester. |
| bbNext = next->tester; |
| } |
| } else { |
| // If there's no handler after us, we jump to the end handler. |
| bbNext = endExceptionBlock; |
| } |
| |
| currentBlock = cur->tester; |
| |
| assert(cur->catchClass && |
| "Class not loaded when reading the exception table"); |
| |
| Value* VTVar = TheCompiler->getVirtualTable(cur->catchClass->virtualVT); |
| |
| // Get the Java exception. |
| Value* javaExceptionPtr = getJavaExceptionPtr(getJavaThreadPtr(getMutatorThreadPtr())); |
| Value* obj = new LoadInst(javaExceptionPtr, "pendingException", currentBlock); |
| |
| Value* objVT = CallInst::Create(intrinsics->GetVTFunction, obj, "objectVT", |
| currentBlock); |
| |
| uint32 depth = cur->catchClass->virtualVT->depth; |
| Value* depthCl = ConstantInt::get(Type::getInt32Ty(*llvmContext), depth); |
| Value* cmp = 0; |
| |
| if (depth >= JavaVirtualTable::getDisplayLength()) { |
| Value* classArgs[2] = { objVT, VTVar }; |
| |
| if (TheCompiler->isStaticCompiling()) |
| cmp = CallInst::Create(intrinsics->IsSecondaryClassFunction, |
| classArgs, "isSecondaryClass", currentBlock); |
| else |
| cmp = CallInst::Create(intrinsics->IsSecondaryClassFunctionInner, |
| classArgs, "isSecondaryClass", currentBlock); |
| |
| } else { |
| |
| Value* inDisplay = CallInst::Create(intrinsics->GetDisplayFunction, |
| objVT, "objectDisplay", currentBlock); |
| |
| Value* displayArgs[2] = { inDisplay, depthCl }; |
| Value* VTInDisplay = CallInst::Create(intrinsics->GetVTInDisplayFunction, |
| displayArgs, "objectVTInDisplay", currentBlock); |
| |
| cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, VTInDisplay, VTVar, |
| ""); |
| } |
| |
| // If we are catching this exception, then jump to the Java Handler, |
| // otherwise jump to our next handler. |
| BranchInst::Create(cur->javaHandler, bbNext, cmp, currentBlock); |
| |
| currentBlock = cur->javaHandler; |
| } |
| |
| // Restore currentBlock. |
| currentBlock = temp; |
| return nbe; |
| } |
| |
| void JavaJIT::finishExceptions() { |
| pred_iterator PI = pred_begin(endExceptionBlock); |
| pred_iterator PE = pred_end(endExceptionBlock); |
| if (PI == PE) { |
| endExceptionBlock->eraseFromParent(); |
| } else { |
| if (endNode) { |
| endNode->addIncoming(Constant::getNullValue(endNode->getType()), |
| endExceptionBlock); |
| } |
| BranchInst::Create(endBlock, endExceptionBlock); |
| } |
| |
| |
| PI = pred_begin(unifiedUnreachable); |
| PE = pred_end(unifiedUnreachable); |
| if (PI == PE) { |
| unifiedUnreachable->eraseFromParent(); |
| } else { |
| new UnreachableInst(*llvmContext, unifiedUnreachable); |
| } |
| |
| for (Function::iterator BI = llvmFunction->begin(), BE = llvmFunction->end(); |
| BI != BE; BI++) { |
| PI = pred_begin(BI); |
| PE = pred_end(BI); |
| if (PI == PE) { |
| Instruction* insn = BI->begin(); |
| PHINode* node = dyn_cast<PHINode>(insn); |
| if (node) { |
| node->replaceAllUsesWith(Constant::getNullValue(node->getType())); |
| node->eraseFromParent(); |
| } |
| } |
| } |
| } |
| |
| |
| #ifdef USE_OPENJDK |
| #include "JavaJITOpenJDK.inc" |
| #else |
| #include "JavaJITClasspath.inc" |
| #endif |