| Instruction* JavaJIT::invoke(Value *F, std::vector<llvm::Value*>& args, |
| const char* Name, |
| BasicBlock *InsertAtEnd) { |
| |
| // means: is there a handler for me? |
| if (currentExceptionBlock != endExceptionBlock) { |
| BasicBlock* ifNormal = createBasicBlock("no exception block"); |
| currentBlock = ifNormal; |
| return llvm::InvokeInst::Create(F, ifNormal, currentExceptionBlock, |
| args.begin(), |
| args.end(), Name, InsertAtEnd); |
| } else { |
| return llvm::CallInst::Create(F, args.begin(), args.end(), Name, InsertAtEnd); |
| } |
| } |
| |
| Instruction* JavaJIT::invoke(Value *F, Value* arg1, const char* Name, |
| BasicBlock *InsertAtEnd) { |
| |
| // means: is there a handler for me? |
| if (currentExceptionBlock != endExceptionBlock) { |
| BasicBlock* ifNormal = createBasicBlock("no exception block"); |
| currentBlock = ifNormal; |
| Value* arg[1] = { arg1 }; |
| return InvokeInst::Create(F, ifNormal, currentExceptionBlock, |
| arg, arg + 1, Name, InsertAtEnd); |
| } else { |
| return CallInst::Create(F, arg1, Name, InsertAtEnd); |
| } |
| } |
| |
| Instruction* JavaJIT::invoke(Value *F, Value* arg1, Value* arg2, |
| const char* Name, BasicBlock *InsertAtEnd) { |
| |
| Value* args[2] = { arg1, arg2 }; |
| |
| // means: is there a handler for me? |
| if (currentExceptionBlock != endExceptionBlock) { |
| BasicBlock* ifNormal = createBasicBlock("no exception block"); |
| currentBlock = ifNormal; |
| return InvokeInst::Create(F, ifNormal, currentExceptionBlock, |
| args, args + 2, Name, InsertAtEnd); |
| } else { |
| return CallInst::Create(F, args, args + 2, Name, InsertAtEnd); |
| } |
| } |
| |
| Instruction* JavaJIT::invoke(Value *F, const char* Name, |
| BasicBlock *InsertAtEnd) { |
| // means: is there a handler for me? |
| if (currentExceptionBlock != endExceptionBlock) { |
| BasicBlock* ifNormal = createBasicBlock("no exception block"); |
| currentBlock = ifNormal; |
| Value* args[1]; |
| return llvm::InvokeInst::Create(F, ifNormal, currentExceptionBlock, |
| args, args, Name, |
| InsertAtEnd); |
| } else { |
| return llvm::CallInst::Create(F, Name, InsertAtEnd); |
| } |
| } |
| |
| void JavaJIT::throwException(llvm::Function* F, Value* arg1) { |
| if (currentExceptionBlock != endExceptionBlock) { |
| Value* exArgs[1] = { arg1 }; |
| InvokeInst::Create(F, unifiedUnreachable, |
| currentExceptionBlock, exArgs, exArgs + 1, |
| "", currentBlock); |
| } else { |
| CallInst::Create(F, arg1, "", currentBlock); |
| new UnreachableInst(currentBlock); |
| } |
| } |
| |
| void JavaJIT::throwException(Value* obj) { |
| Function* F = module->ThrowExceptionFunction; |
| if (currentExceptionBlock != endExceptionBlock) { |
| Value* exArgs[1] = { obj }; |
| InvokeInst::Create(F, unifiedUnreachable, |
| currentExceptionBlock, exArgs, exArgs + 1, |
| "", currentBlock); |
| } else { |
| CallInst::Create(F, obj, "", currentBlock); |
| new UnreachableInst(currentBlock); |
| } |
| } |
| |
| void JavaJIT::throwException(llvm::Function* F, Value** args, |
| uint32 nbArgs) { |
| if (currentExceptionBlock != endExceptionBlock) { |
| InvokeInst::Create(F, unifiedUnreachable, |
| currentExceptionBlock, args, args + nbArgs, |
| "", currentBlock); |
| } else { |
| CallInst::Create(F, args, args + nbArgs, "", currentBlock); |
| new UnreachableInst(currentBlock); |
| } |
| } |
| |
| /// 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; |
| |
| /// catcher - The basic block that catches the exception. The catcher deals |
| /// with LLVM codegen and declares the llvm.select method. This block is the |
| /// destination of invoke instructions that are in the try clause. |
| llvm::BasicBlock* catcher; |
| |
| /// 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; |
| |
| /// nativeHandler - The CXX exception-related code that handles the exception. |
| /// The block clears the exception from the execution environment, calls |
| /// the CXX begin and end catch methods and jumps to the Java handler. |
| llvm::BasicBlock* nativeHandler; |
| |
| /// exceptionPHI - The CXX exception object for the tester block. The |
| /// tester has incoming blocks, either from the catcher or from other |
| /// handlers that don't handle the exception. Therefore each incoming block |
| /// specifies the CXX exception object that was caught. |
| llvm::PHINode* exceptionPHI; |
| }; |
| |
| 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; |
| |
| // realEndExceptionBlock is the block where handlers will resume if |
| // they don't treat the exception. realEndExceptionBlock does not |
| // have to catch the exception. |
| BasicBlock* realEndExceptionBlock = endExceptionBlock; |
| |
| // endExceptionBlockCatcher is the block where every instruction will |
| // unwind. |
| BasicBlock* endExceptionBlockCatcher = endExceptionBlock; |
| |
| if (sync) { |
| // synchronizeExceptionBlock is the the block which will release the lock |
| // on the object. trySynchronizeExceptionBlock is the block which will |
| // catch the exception if one is thrown. |
| BasicBlock* synchronizeExceptionBlock = |
| createBasicBlock("synchronizeExceptionBlock"); |
| BasicBlock* trySynchronizeExceptionBlock = |
| createBasicBlock("trySynchronizeExceptionBlock"); |
| |
| // So synchronizeExceptionBlock becomes the block where every instructions |
| // will unwind. |
| realEndExceptionBlock = synchronizeExceptionBlock; |
| endExceptionBlockCatcher = trySynchronizeExceptionBlock; |
| Value* argsSync = 0; |
| if (isVirtual(compilingMethod->access)) { |
| argsSync = llvmFunction->arg_begin(); |
| } else { |
| Value* cl = module->getJavaClass(compilingClass); |
| argsSync = cl; |
| } |
| |
| // In the synchronizeExceptionBlock: release the object and go to |
| // endExceptionBlock, which will unwind the function. |
| |
| CallInst::Create(module->ReleaseObjectFunction, argsSync, "", |
| synchronizeExceptionBlock); |
| |
| BranchInst::Create(endExceptionBlock, synchronizeExceptionBlock); |
| |
| |
| // In the trySynchronizeExceptionBlock: catch the exception and move |
| // to synchronizeExceptionBlock. |
| |
| const PointerType* PointerTy_0 = module->ptrType; |
| Instruction* ptr_eh_ptr = CallInst::Create(module->llvmGetException, |
| "eh_ptr", |
| trySynchronizeExceptionBlock); |
| Constant* C = ConstantExpr::getCast(Instruction::BitCast, |
| module->personality, PointerTy_0); |
| Value* int32_eh_select_params[3] = |
| { ptr_eh_ptr, C, module->constantPtrNull }; |
| CallInst::Create(module->exceptionSelector, int32_eh_select_params, |
| int32_eh_select_params + 3, "eh_select", |
| trySynchronizeExceptionBlock); |
| |
| BranchInst::Create(synchronizeExceptionBlock, |
| trySynchronizeExceptionBlock); |
| |
| // Now we can set the unwind destination of all instructions to |
| // the exception catcher. |
| for (uint16 i = 0; i < codeLen; ++i) { |
| if (opcodeInfos[i].exceptionBlock == endExceptionBlock) { |
| opcodeInfos[i].exceptionBlock = trySynchronizeExceptionBlock; |
| } |
| } |
| } |
| |
| // Loop over all handlers in the bytecode to initialize their values. |
| Handler* handlers = (Handler*)alloca(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(); |
| |
| ex->catche = reader.readU2(); |
| |
| #ifndef ISOLATE_SHARING |
| 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; |
| } |
| #endif |
| |
| ex->catcher = 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 == endExceptionBlockCatcher) { |
| opcodeInfos[i].exceptionBlock = ex->catcher; |
| } |
| } |
| |
| // 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; |
| |
| // Set the native handler of this exception, which will catch the exception |
| // object. |
| ex->nativeHandler = createBasicBlock("nativeHandler"); |
| } |
| |
| // Loop over all handlers to know which ones have the same range. Handlers |
| // with a same range all verify the exception's class, but only one catches |
| // the exception. This is the reason why we have a tester block |
| // and a catcher block: the first one tests the exception's class, and the |
| // second one catches the exception. |
| bool first = true; |
| for (sint16 i = 0; i < nbe - sync; ++i) { |
| Handler* cur = &handlers[i]; |
| |
| // If we are the first handler, we must have one block for catching |
| // the exception, and one block for comparing the exception. The former |
| // is catcher and the latter is tester. Handlers that live in |
| // the range of this handler will jump to tester because they |
| // have already catched the exception. The other instructions in the range |
| // of this handler will jump to catcher because the |
| // exception still has to be catched. |
| if (first) { |
| cur->tester = createBasicBlock("realTestException"); |
| } else { |
| cur->tester = cur->catcher; |
| } |
| |
| // Set the exception as a phi node. This PHI has two types of incoming |
| // nodes: |
| // - Handlers within the range: they have already catched the exception |
| // and verified its type. They are not the right handler for the |
| // exception, so they jump to this handler |
| // - The testException block of this handler (which is unique). It has |
| // catched the exception and is now jumping to perform the test. |
| cur->exceptionPHI = PHINode::Create(module->ptrType, "", cur->tester); |
| |
| // Look if the next handler has the same range or has a different range. |
| // If it's in the same range, then no need to catch the exception. |
| // Otherwise, it's a new range and we need to catch the exception. |
| if (i + 1 != nbe - sync) { |
| Handler* next = &handlers[i + 1]; |
| |
| if (cur->startpc == next->startpc && cur->endpc == next->endpc) { |
| first = false; |
| } else { |
| first = true; |
| } |
| } |
| } |
| |
| |
| // Loop over all handlers to implement their catcher and tester. |
| for (sint16 i = 0; i < nbe - sync; ++i) { |
| Handler* cur = &handlers[i]; |
| BasicBlock* bbNext = 0; |
| PHINode* nodeNext = 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 = realEndExceptionBlock; |
| } 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; |
| nodeNext = next->exceptionPHI; |
| } |
| } else { |
| // If there's no handler after us, we jump to the end handler. |
| bbNext = realEndExceptionBlock; |
| } |
| |
| // If the tester and the catcher is not the same, then we must implement |
| // the catcher. The catcher catches the exception, jumps to the tester |
| // and gives the exception as an incoming node the the exceptionPHI. |
| if (cur->tester != cur->catcher) { |
| const PointerType* PointerTy_0 = module->ptrType; |
| Instruction* ptr_eh_ptr = |
| CallInst::Create(module->llvmGetException, "eh_ptr", cur->catcher); |
| Constant* C = ConstantExpr::getCast(Instruction::BitCast, |
| module->personality, PointerTy_0); |
| Value* int32_eh_select_params[3] = |
| { ptr_eh_ptr, C, module->constantPtrNull }; |
| llvm::CallInst::Create(module->exceptionSelector, |
| int32_eh_select_params, |
| int32_eh_select_params + 3, "eh_select", |
| cur->catcher); |
| llvm::BranchInst::Create(cur->tester, cur->catcher); |
| cur->exceptionPHI->addIncoming(ptr_eh_ptr, cur->catcher); |
| } |
| |
| currentBlock = cur->tester; |
| |
| Value* clVar = 0; |
| #ifdef ISOLATE_SHARING |
| // We're dealing with exceptions, don't catch the exception if the class can |
| // not be found. |
| if (cur->catche) clVar = getResolvedClass(cur->catche, false, false, 0); |
| else clVar = CallInst::Create(module->GetJnjvmExceptionClassFunction, |
| isolateLocal, "", currentBlock); |
| #else |
| // We know catchClass exists because we have loaded all exceptions catched |
| // by the method when we loaded the class that defined this method. |
| clVar = module->getNativeClass(cur->catchClass); |
| #endif |
| if (clVar->getType() != module->JavaCommonClassType) |
| clVar = new BitCastInst(clVar, module->JavaCommonClassType, "", |
| currentBlock); |
| |
| |
| #ifdef SERVICE |
| // Verifies that the current isolate is not stopped. If it is, we don't |
| // catch the exception but resume unwinding. |
| JnjvmClassLoader* loader = compilingClass->classLoader;; |
| if (loader != loader->bootstrapLoader) { |
| Value* threadId = getCurrentThread(module->MutatorThreadType); |
| Value* Isolate = GetElementPtrInst::Create(threadId, |
| module->constantFour, "", |
| currentBlock); |
| |
| Isolate = new LoadInst(Isolate, "", currentBlock); |
| Isolate = new BitCastInst(Isolate, module->ptrPtrType, "", currentBlock); |
| Value* Status = GetElementPtrInst::Create(Isolate, module->constantOne, "", |
| currentBlock); |
| Status = new LoadInst(Status, "", currentBlock); |
| Status = new PtrToIntInst(Status, Type::Int32Ty, "", currentBlock); |
| |
| Value* stopping = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, Status, |
| module->constantOne, ""); |
| |
| BasicBlock* raiseBlock = createBasicBlock("raiseBlock"); |
| BasicBlock* continueBlock = createBasicBlock("continueBlock"); |
| BranchInst::Create(raiseBlock, continueBlock, stopping, currentBlock); |
| currentBlock = raiseBlock; |
| BranchInst::Create(endExceptionBlock, currentBlock); |
| |
| currentBlock = continueBlock; |
| } |
| #endif |
| |
| Value* threadId = getCurrentThread(module->JavaThreadType); |
| Value* geps[2] = { module->constantZero, |
| module->OffsetJavaExceptionInThreadConstant }; |
| |
| Value* javaExceptionPtr = GetElementPtrInst::Create(threadId, geps, |
| geps + 2, "", |
| currentBlock); |
| |
| // Get the Java exception. |
| Value* obj = new LoadInst(javaExceptionPtr, "", currentBlock); |
| |
| Value* objCl = CallInst::Create(module->GetClassFunction, obj, "", |
| currentBlock); |
| |
| Value* depthCl = ConstantInt::get(Type::Int32Ty, cur->catchClass->depth); |
| Value* depthClObj = CallInst::Create(module->GetDepthFunction, objCl, "", |
| currentBlock); |
| |
| // Compare the exception with the exception class we catch. |
| Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_ULE, depthCl, |
| depthClObj, ""); |
| |
| BasicBlock* supDepth = createBasicBlock("superior depth"); |
| |
| BranchInst::Create(supDepth, bbNext, cmp, currentBlock); |
| |
| if (nodeNext) |
| nodeNext->addIncoming(cur->exceptionPHI, currentBlock); |
| |
| currentBlock = supDepth; |
| Value* inDisplay = CallInst::Create(module->GetDisplayFunction, |
| objCl, "", currentBlock); |
| |
| Value* displayArgs[2] = { inDisplay, depthCl }; |
| Value* clInDisplay = CallInst::Create(module->GetClassInDisplayFunction, |
| displayArgs, displayArgs + 2, "", |
| currentBlock); |
| |
| cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, clInDisplay, clVar, |
| ""); |
| |
| // If we are catching this exception, then jump to the nativeHandler, |
| // otherwise jump to our next handler. |
| BranchInst::Create(cur->nativeHandler, bbNext, cmp, currentBlock); |
| |
| // Add the incoming value to the next handler, which is the exception we |
| // just catched. |
| if (nodeNext) |
| nodeNext->addIncoming(cur->exceptionPHI, currentBlock); |
| |
| currentBlock = cur->nativeHandler; |
| |
| threadId = getCurrentThread(module->JavaThreadType); |
| javaExceptionPtr = GetElementPtrInst::Create(threadId, geps, geps + 2, "", |
| currentBlock); |
| |
| // Get the Java exception. |
| Value* exc = new LoadInst(javaExceptionPtr, "", currentBlock); |
| |
| Value* geps2[2] = { module->constantZero, |
| module->OffsetCXXExceptionInThreadConstant }; |
| |
| Value* cxxExceptionPtr = GetElementPtrInst::Create(threadId, geps2, |
| geps2 + 2, "", |
| currentBlock); |
| |
| // Clear exceptions. |
| new StoreInst(module->constantPtrNull, cxxExceptionPtr, currentBlock); |
| new StoreInst(module->JavaObjectNullConstant, javaExceptionPtr, |
| currentBlock); |
| |
| // Call the CXX begin and end catcher. |
| CallInst::Create(module->exceptionBeginCatch, cur->exceptionPHI, |
| "tmp8", cur->nativeHandler); |
| CallInst::Create(module->exceptionEndCatch, "", cur->nativeHandler); |
| |
| // We can now jump to the Java handler! |
| BranchInst::Create(cur->javaHandler, cur->nativeHandler); |
| |
| // If the Java handler is empty, create a PHI node that will contain the |
| // exception and give our own. |
| if (cur->javaHandler->empty()) { |
| PHINode* node = PHINode::Create(JnjvmModule::JavaObjectType, "", |
| cur->javaHandler); |
| node->addIncoming(exc, cur->nativeHandler); |
| |
| } else { |
| // If the Java handler is not empty, then the first instruction is the |
| // PHI node. Give it our own. |
| Instruction* insn = cur->javaHandler->begin(); |
| PHINode* node = dyn_cast<PHINode>(insn); |
| assert(node && "malformed exceptions"); |
| node->addIncoming(exc, cur->nativeHandler); |
| } |
| |
| |
| #if defined(SERVICE) |
| |
| // Change the isolate we are currently running, now that we have catched |
| // the exception: the exception may have been thrown by another isolate. |
| Value* threadId = 0; |
| Value* OldIsolateID = 0; |
| Value* IsolateIDPtr = 0; |
| Value* OldIsolate = 0; |
| Value* NewIsolate = 0; |
| Value* IsolatePtr = 0; |
| currentBlock = cur->javaHandler; |
| if (loader != loader->bootstrapLoader) { |
| threadId = getCurrentThread(module->MutatorThreadType); |
| |
| IsolateIDPtr = GetElementPtrInst::Create(threadId, module->constantThree, |
| "", cur->javaHandler); |
| const Type* realType = PointerType::getUnqual(module->pointerSizeType); |
| IsolateIDPtr = new BitCastInst(IsolateIDPtr, realType, "", |
| cur->javaHandler); |
| OldIsolateID = new LoadInst(IsolateIDPtr, "", cur->javaHandler); |
| |
| Value* MyID = ConstantInt::get(module->pointerSizeType, |
| loader->getIsolate()->IsolateID); |
| |
| new StoreInst(MyID, IsolateIDPtr, cur->javaHandler); |
| IsolatePtr = GetElementPtrInst::Create(threadId, module->constantFour, "", |
| cur->javaHandler); |
| |
| OldIsolate = new LoadInst(IsolatePtr, "", cur->javaHandler); |
| NewIsolate = module->getIsolate(loader->getIsolate(), currentBlock); |
| new StoreInst(NewIsolate, IsolatePtr, cur->javaHandler); |
| |
| } |
| #endif |
| |
| } |
| |
| // 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 { |
| Value* threadId = getCurrentThread(module->JavaThreadType); |
| Value* geps2[2] = { module->constantZero, |
| module->OffsetCXXExceptionInThreadConstant }; |
| |
| Value* cxxExceptionPtr = GetElementPtrInst::Create(threadId, geps2, |
| geps2 + 2, "", |
| currentBlock); |
| cxxExceptionPtr = new LoadInst(cxxExceptionPtr, "", currentBlock); |
| llvm::CallInst::Create(module->unwindResume, cxxExceptionPtr, "", |
| currentBlock); |
| new UnreachableInst(currentBlock); |
| } |
| |
| PI = pred_begin(unifiedUnreachable); |
| PE = pred_end(unifiedUnreachable); |
| if (PI == PE) { |
| unifiedUnreachable->eraseFromParent(); |
| } else { |
| new UnreachableInst(unifiedUnreachable); |
| } |
| |
| } |