blob: d3bf4fc6f4b38c169bbe5469a3064c0e44446f72 [file] [log] [blame]
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);
}
}