mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-03 00:20:40 +03:00

In the 2.064 frontend, the catch variables have a zero initializer, so DtoDeclarationExp overwrote the actually caught exception with null.
240 lines
8.8 KiB
C++
240 lines
8.8 KiB
C++
//===-- irlandingpad.cpp --------------------------------------------------===//
|
||
//
|
||
// LDC – the LLVM D compiler
|
||
//
|
||
// This file is distributed under the BSD-style LDC license. See the LICENSE
|
||
// file for details.
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
#include "gen/llvm.h"
|
||
#include "gen/classes.h"
|
||
#include "gen/irstate.h"
|
||
#include "gen/llvmhelpers.h"
|
||
#include "gen/logger.h"
|
||
#include "gen/runtime.h"
|
||
#include "gen/tollvm.h"
|
||
#include "ir/irlandingpad.h"
|
||
|
||
// creates new landing pad
|
||
static llvm::LandingPadInst *createLandingPadInst()
|
||
{
|
||
llvm::Function* personality_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_personality");
|
||
LLType *retType = LLStructType::get(LLType::getInt8PtrTy(gIR->context()),
|
||
LLType::getInt32Ty(gIR->context()),
|
||
NULL);
|
||
return gIR->ir->CreateLandingPad(retType, personality_fn, 0, "landing_pad");
|
||
}
|
||
|
||
IRLandingPadCatchInfo::IRLandingPadCatchInfo(Catch* catchstmt_, llvm::BasicBlock* end_) :
|
||
end(end_), catchStmt(catchstmt_)
|
||
{
|
||
target = llvm::BasicBlock::Create(gIR->context(), "catch", gIR->topfunc(), end);
|
||
|
||
assert(catchStmt->type);
|
||
catchType = catchStmt->type->toBasetype()->isClassHandle();
|
||
assert(catchType);
|
||
DtoResolveClass(catchType);
|
||
|
||
if (catchStmt->var) {
|
||
if (!catchStmt->var->nestedrefs.dim) {
|
||
gIR->func()->gen->landingPadInfo.getExceptionStorage();
|
||
}
|
||
}
|
||
}
|
||
|
||
void IRLandingPadCatchInfo::toIR()
|
||
{
|
||
if (!catchStmt)
|
||
return;
|
||
|
||
gIR->scope() = IRScope(target, target);
|
||
gIR->DBuilder.EmitBlockStart(catchStmt->loc);
|
||
|
||
// assign storage to catch var
|
||
if (catchStmt->var) {
|
||
// use the same storage for all exceptions that are not accessed in
|
||
// nested functions
|
||
if (!catchStmt->var->nestedrefs.dim) {
|
||
assert(!catchStmt->var->ir.irLocal);
|
||
catchStmt->var->ir.irLocal = new IrLocal(catchStmt->var);
|
||
LLValue* catch_var = gIR->func()->gen->landingPadInfo.getExceptionStorage();
|
||
catchStmt->var->ir.irLocal->value = gIR->ir->CreateBitCast(catch_var, getPtrToType(DtoType(catchStmt->var->type)));
|
||
} else {
|
||
// this will alloca if we haven't already and take care of nested refs
|
||
DtoDeclarationExp(catchStmt->var);
|
||
|
||
// the exception will only be stored in catch_var. copy it over if necessary
|
||
LLValue* exc = gIR->ir->CreateBitCast(DtoLoad(gIR->func()->gen->landingPadInfo.getExceptionStorage()), DtoType(catchStmt->var->type));
|
||
DtoStore(exc, catchStmt->var->ir.irLocal->value);
|
||
}
|
||
}
|
||
|
||
// emit handler, if there is one
|
||
// handler is zero for instance for 'catch { debug foo(); }'
|
||
if (catchStmt->handler)
|
||
catchStmt->handler->toIR(gIR);
|
||
|
||
if (!gIR->scopereturned())
|
||
gIR->ir->CreateBr(end);
|
||
|
||
gIR->DBuilder.EmitBlockEnd();
|
||
}
|
||
|
||
IRLandingPadFinallyStatementInfo::IRLandingPadFinallyStatementInfo(Statement *finallyBody_) :
|
||
finallyBody(finallyBody_)
|
||
{
|
||
}
|
||
|
||
void IRLandingPadFinallyStatementInfo::toIR(LLValue *eh_ptr)
|
||
{
|
||
IRLandingPad &padInfo = gIR->func()->gen->landingPadInfo;
|
||
llvm::BasicBlock* &pad = gIR->func()->gen->landingPad;
|
||
|
||
// create collision landing pad that handles exceptions thrown inside the finally block
|
||
llvm::BasicBlock *collision = llvm::BasicBlock::Create(gIR->context(), "eh.collision", gIR->topfunc(), gIR->scopeend());
|
||
llvm::BasicBlock *bb = gIR->scopebb();
|
||
gIR->scope() = IRScope(collision, gIR->scopeend());
|
||
llvm::LandingPadInst *collisionLandingPad = createLandingPadInst();
|
||
LLValue* collision_eh_ptr = DtoExtractValue(collisionLandingPad, 0);
|
||
collisionLandingPad->setCleanup(true);
|
||
llvm::Function* collision_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_handle_collision");
|
||
gIR->CreateCallOrInvoke2(collision_fn, collision_eh_ptr, eh_ptr);
|
||
gIR->ir->CreateUnreachable();
|
||
gIR->scope() = IRScope(bb, gIR->scopeend());
|
||
|
||
// set collision landing pad as unwind target and emit the body of the finally
|
||
gIR->DBuilder.EmitBlockStart(finallyBody->loc);
|
||
padInfo.scopeStack.push(IRLandingPadScope(collision));
|
||
pad = collision;
|
||
finallyBody->toIR(gIR);
|
||
padInfo.scopeStack.pop();
|
||
pad = padInfo.get();
|
||
gIR->DBuilder.EmitBlockEnd();
|
||
}
|
||
|
||
void IRLandingPad::addCatch(Catch* catchstmt, llvm::BasicBlock* end)
|
||
{
|
||
unpushedScope.catches.push_back(IRLandingPadCatchInfo(catchstmt, end));
|
||
}
|
||
|
||
void IRLandingPad::addFinally(Statement* finallyStmt)
|
||
{
|
||
assert(unpushedScope.finally == NULL && "only one finally per try-finally block");
|
||
unpushedScope.finally = new IRLandingPadFinallyStatementInfo(finallyStmt);
|
||
unpushedScope.isFinallyCreatedInternally = true;
|
||
}
|
||
|
||
void IRLandingPad::addFinally(IRLandingPadCatchFinallyInfo *finallyInfo)
|
||
{
|
||
assert(unpushedScope.finally == NULL && "only one finally per try-finally block");
|
||
unpushedScope.finally = finallyInfo;
|
||
}
|
||
|
||
void IRLandingPad::push(llvm::BasicBlock* inBB)
|
||
{
|
||
unpushedScope.target = inBB;
|
||
scopeStack.push(unpushedScope);
|
||
unpushedScope = IRLandingPadScope();
|
||
gIR->func()->gen->landingPad = get();
|
||
}
|
||
|
||
void IRLandingPad::pop()
|
||
{
|
||
IRLandingPadScope scope = scopeStack.top();
|
||
scopeStack.pop();
|
||
gIR->func()->gen->landingPad = get();
|
||
|
||
std::deque<IRLandingPadCatchInfo>::iterator itr, end = scope.catches.end();
|
||
for (itr = scope.catches.begin(); itr != end; ++itr)
|
||
itr->toIR();
|
||
constructLandingPad(scope);
|
||
if (scope.finally && scope.isFinallyCreatedInternally)
|
||
delete scope.finally;
|
||
}
|
||
|
||
llvm::BasicBlock* IRLandingPad::get()
|
||
{
|
||
if (scopeStack.size() == 0)
|
||
return NULL;
|
||
else
|
||
return scopeStack.top().target;
|
||
}
|
||
|
||
void IRLandingPad::constructLandingPad(IRLandingPadScope scope)
|
||
{
|
||
// save and rewrite scope
|
||
IRScope savedIRScope = gIR->scope();
|
||
gIR->scope() = IRScope(scope.target, savedIRScope.end);
|
||
|
||
// create landingpad
|
||
llvm::LandingPadInst *landingPad = createLandingPadInst();
|
||
LLValue* eh_ptr = DtoExtractValue(landingPad, 0);
|
||
LLValue* eh_sel = DtoExtractValue(landingPad, 1);
|
||
|
||
// add landingpad clauses, emit finallys and 'if' chain to catch the exception
|
||
llvm::Function* eh_typeid_for_fn = GET_INTRINSIC_DECL(eh_typeid_for);
|
||
bool isFirstCatch = true;
|
||
std::stack<IRLandingPadScope> savedScopeStack = scopeStack;
|
||
std::deque<IRLandingPadCatchInfo>::iterator catchItr, catchItrEnd;
|
||
while (true) {
|
||
catchItr = scope.catches.begin();
|
||
catchItrEnd = scope.catches.end();
|
||
for (; catchItr != catchItrEnd; ++catchItr) {
|
||
// if it is a first catch and some catch allocated storage, store exception object
|
||
if (isFirstCatch && catch_var) {
|
||
// eh_ptr is a pointer to _d_exception, which has a reference
|
||
// to the Throwable object at offset 0.
|
||
LLType *objectPtrTy = DtoType(ClassDeclaration::object->type->pointerTo());
|
||
LLValue *objectPtr = gIR->ir->CreateBitCast(eh_ptr, objectPtrTy);
|
||
gIR->ir->CreateStore(gIR->ir->CreateLoad(objectPtr), catch_var);
|
||
isFirstCatch = false;
|
||
}
|
||
|
||
// create next block
|
||
llvm::BasicBlock *next = llvm::BasicBlock::Create(gIR->context(), "eh.next", gIR->topfunc(), gIR->scopeend());
|
||
// get class info symbol
|
||
LLValue *classInfo = catchItr->catchType->ir.irAggr->getClassInfoSymbol();
|
||
// add that symbol as landing pad clause
|
||
landingPad->addClause(classInfo);
|
||
// call llvm.eh.typeid.for to get class info index in the exception table
|
||
classInfo = DtoBitCast(classInfo, getPtrToType(DtoType(Type::tint8)));
|
||
LLValue *eh_id = gIR->ir->CreateCall(eh_typeid_for_fn, classInfo);
|
||
// check exception selector (eh_sel) against the class info index
|
||
gIR->ir->CreateCondBr(gIR->ir->CreateICmpEQ(eh_sel, eh_id), catchItr->target, next);
|
||
gIR->scope() = IRScope(next, gIR->scopeend());
|
||
}
|
||
|
||
if (scope.finally) {
|
||
scope.finally->toIR(eh_ptr);
|
||
landingPad->setCleanup(true);
|
||
}
|
||
|
||
if (scopeStack.empty())
|
||
break;
|
||
scope = scopeStack.top();
|
||
scopeStack.pop();
|
||
gIR->func()->gen->landingPad = get();
|
||
}
|
||
|
||
// restore landing pad infos
|
||
scopeStack = savedScopeStack;
|
||
gIR->func()->gen->landingPad = get();
|
||
|
||
// no catch matched and all finallys executed - resume unwind
|
||
llvm::Function* unwind_resume_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_resume_unwind");
|
||
gIR->ir->CreateCall(unwind_resume_fn, eh_ptr);
|
||
gIR->ir->CreateUnreachable();
|
||
|
||
// restore scope
|
||
gIR->scope() = savedIRScope;
|
||
}
|
||
|
||
LLValue* IRLandingPad::getExceptionStorage()
|
||
{
|
||
if (!catch_var) {
|
||
Logger::println("Making new catch var");
|
||
catch_var = DtoAlloca(ClassDeclaration::object->type, "catchvar");
|
||
}
|
||
return catch_var;
|
||
}
|