changeset 319:e9c93739bc4c trunk

[svn r340] Rework exception handling to work with nested tryfinally and trycatch.
author ChristianK
date Sat, 05 Jul 2008 10:22:56 +0200
parents 8e570dbe4087
children d772927ca496
files gen/irstate.h gen/statements.cpp ir/irfunction.h ir/irlandingpad.cpp ir/irlandingpad.h tango/lib/compiler/llvmdc/eh.d
diffstat 6 files changed, 295 insertions(+), 136 deletions(-) [+]
line wrap: on
line diff
--- a/gen/irstate.h	Fri Jul 04 09:00:49 2008 +0200
+++ b/gen/irstate.h	Sat Jul 05 10:22:56 2008 +0200
@@ -172,10 +172,6 @@
     llvm::BasicBlock* scopeend();
     bool scopereturned();
 
-    // landing pads for try statements
-    typedef std::vector<llvm::BasicBlock*> BBVec;
-    BBVec landingPads;
-
     // create a call or invoke, depending on the landing pad info
     // the template function is defined further down in this file
     template <typename InputIterator>
@@ -229,15 +225,16 @@
 template <typename InputIterator>
 CallOrInvoke* IRState::CreateCallOrInvoke(LLValue* Callee, InputIterator ArgBegin, InputIterator ArgEnd, const char* Name)
 {
-    if(landingPads.empty())
-        return new CallOrInvoke_Call(ir->CreateCall(Callee, ArgBegin, ArgEnd, Name));
-    else
+    llvm::BasicBlock* pad;
+    if(pad = func()->landingPad.get())
     {
         llvm::BasicBlock* postinvoke = llvm::BasicBlock::Create("postinvoke", topfunc(), scopeend());
-        llvm::InvokeInst* invoke = ir->CreateInvoke(Callee, postinvoke, *landingPads.rbegin(), ArgBegin, ArgEnd, Name);
+        llvm::InvokeInst* invoke = ir->CreateInvoke(Callee, postinvoke, pad, ArgBegin, ArgEnd, Name);
         scope() = IRScope(postinvoke, scopeend());
         return new CallOrInvoke_Invoke(invoke);
     }
+    else
+        return new CallOrInvoke_Call(ir->CreateCall(Callee, ArgBegin, ArgEnd, Name));
 }
 
 #endif // LLVMDC_GEN_IRSTATE_H
--- a/gen/statements.cpp	Fri Jul 04 09:00:49 2008 +0200
+++ b/gen/statements.cpp	Sat Jul 05 10:22:56 2008 +0200
@@ -27,6 +27,7 @@
 #include "gen/dvalue.h"
 
 #include "ir/irfunction.h"
+#include "ir/irlandingpad.h"
 
 //////////////////////////////////////////////////////////////////////////////
 
@@ -497,8 +498,7 @@
     llvm::BasicBlock* trybb = llvm::BasicBlock::Create("try", p->topfunc(), oldend);
     llvm::BasicBlock* finallybb = llvm::BasicBlock::Create("finally", p->topfunc(), oldend);
     // the landing pad for statements in the try block
-    // only reached via eh-unwinding, a call to resume unwinding is appended
-    llvm::BasicBlock* unwindfinallybb = llvm::BasicBlock::Create("unwindfinally", p->topfunc(), oldend);
+    llvm::BasicBlock* landingpadbb = llvm::BasicBlock::Create("landingpad", p->topfunc(), oldend);
     llvm::BasicBlock* endbb = llvm::BasicBlock::Create("endtryfinally", p->topfunc(), oldend);
 
     // pass the previous BB into this
@@ -506,10 +506,17 @@
     llvm::BranchInst::Create(trybb, p->scopebb());
 
     //
+    // set up the landing pad
+    //
+    p->scope() = IRScope(landingpadbb, endbb);
+
+    gIR->func()->landingPad.addFinally(finalbody);
+    gIR->func()->landingPad.push(landingpadbb);
+
+    //
     // do the try block
     //
     p->scope() = IRScope(trybb,finallybb);
-    p->landingPads.push_back(unwindfinallybb);
 
     assert(body);
     body->toIR(p);
@@ -518,12 +525,12 @@
     if (!p->scopereturned())
         llvm::BranchInst::Create(finallybb, p->scopebb());
 
-    p->landingPads.pop_back();
+    gIR->func()->landingPad.pop();
 
     //
     // do finally block
     //
-    p->scope() = IRScope(finallybb,unwindfinallybb);
+    p->scope() = IRScope(finallybb,landingpadbb);
     assert(finalbody);
     finalbody->toIR(p);
 
@@ -533,39 +540,6 @@
         llvm::BranchInst::Create(endbb, p->scopebb());
     }
 
-    //
-    // do landing pad
-    //
-    p->scope() = IRScope(unwindfinallybb,endbb);
-
-    // eh_ptr = llvm.eh.exception();
-    llvm::Function* eh_exception_fn = GET_INTRINSIC_DECL(eh_exception);
-    LLValue* eh_ptr = gIR->ir->CreateCall(eh_exception_fn);
-
-    // eh_sel = llvm.eh.selector(eh_ptr, cast(byte*)&_d_eh_personality, 0);
-    llvm::Function* eh_selector_fn;
-    if (global.params.is64bit)
-        eh_selector_fn = GET_INTRINSIC_DECL(eh_selector_i64);
-    else
-        eh_selector_fn = GET_INTRINSIC_DECL(eh_selector_i32);
-    llvm::Function* personality_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_personality");
-    LLValue* personality_fn_arg = gIR->ir->CreateBitCast(personality_fn, getPtrToType(LLType::Int8Ty));
-    LLValue* eh_sel = gIR->ir->CreateCall3(eh_selector_fn, eh_ptr, personality_fn_arg, llvm::ConstantInt::get(LLType::Int32Ty, 0));
-
-    // emit finally code
-    finalbody->toIR(p);
-
-    // finally code may not be terminated!
-    if (gIR->scopereturned()) {
-        error("finally blocks may not be terminated", loc.toChars());
-        fatal();
-    }
-
-    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();
-
     // rewrite the scope
     p->scope() = IRScope(endbb,oldend);
 }
@@ -593,10 +567,23 @@
     llvm::BranchInst::Create(trybb, p->scopebb());
 
     //
+    // do catches and the landing pad
+    //
+    assert(catches);
+    gIR->scope() = IRScope(landingpadbb, endbb);
+
+    for (int i = 0; i < catches->dim; i++)
+    {
+        Catch *c = (Catch *)catches->data[i];
+        gIR->func()->landingPad.addCatch(c, endbb);
+    }
+
+    gIR->func()->landingPad.push(landingpadbb);
+
+    //
     // do the try block
     //
     p->scope() = IRScope(trybb,landingpadbb);
-    p->landingPads.push_back(landingpadbb);
 
     assert(body);
     body->toIR(p);
@@ -604,94 +591,7 @@
     if (!gIR->scopereturned())
         llvm::BranchInst::Create(endbb, p->scopebb());
 
-    p->landingPads.pop_back();
-
-    //
-    // do catches
-    //
-    assert(catches);
-
-    // get storage for exception var
-    const LLType* objectTy = DtoType(ClassDeclaration::object->type);
-    llvm::AllocaInst* catch_var = new llvm::AllocaInst(objectTy,"catchvar",p->topallocapoint());
-
-    // for further reference in landing pad
-    LLSmallVector<llvm::BasicBlock*,4> catch_bbs;
-
-    for (int i = 0; i < catches->dim; i++)
-    {
-        Catch *c = (Catch *)catches->data[i];
-
-        llvm::BasicBlock* catchbb = llvm::BasicBlock::Create("catch", p->topfunc(), oldend);
-        catch_bbs.push_back(catchbb);
-        p->scope() = IRScope(catchbb,oldend);
-
-        // assign storage to catch var
-        if(c->var) {
-            assert(!c->var->ir.irLocal);
-            c->var->ir.irLocal = new IrLocal(c->var);
-            c->var->ir.irLocal->value = gIR->ir->CreateBitCast(catch_var, getPtrToType(DtoType(c->var->type)));
-        }
-
-        // emit handler
-        assert(c->handler);
-        c->handler->toIR(p);
-
-        if (!gIR->scopereturned())
-            llvm::BranchInst::Create(endbb, p->scopebb());
-    }
-
-    //
-    // do landing pad
-    //
-    p->scope() = IRScope(landingpadbb,endbb);
-
-    // eh_ptr = llvm.eh.exception();
-    llvm::Function* eh_exception_fn = GET_INTRINSIC_DECL(eh_exception);
-    LLValue* eh_ptr = gIR->ir->CreateCall(eh_exception_fn);
-
-    // store eh_ptr in catch_var
-    gIR->ir->CreateStore(gIR->ir->CreateBitCast(eh_ptr, objectTy), catch_var);
-
-    // eh_sel = llvm.eh.selector(eh_ptr, cast(byte*)&_d_eh_personality, <classinfos>);
-    llvm::Function* eh_selector_fn;
-    if (global.params.is64bit)
-        eh_selector_fn = GET_INTRINSIC_DECL(eh_selector_i64);
-    else
-        eh_selector_fn = GET_INTRINSIC_DECL(eh_selector_i32);
-
-    LLSmallVector<LLValue*,4> args;
-    args.push_back(eh_ptr);
-    llvm::Function* personality_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_personality");
-    args.push_back(gIR->ir->CreateBitCast(personality_fn, getPtrToType(LLType::Int8Ty)));
-    for (int i = 0; i < catches->dim; i++)
-    {
-        Catch *c = (Catch *)catches->data[i];
-        assert(c->type);
-        ClassDeclaration* cdecl = c->type->isClassHandle();
-        assert(cdecl);
-        assert(cdecl->ir.irStruct);
-        args.push_back(cdecl->ir.irStruct->classInfo);
-    }
-
-    LLValue* eh_sel = gIR->ir->CreateCall(eh_selector_fn, args.begin(), args.end());
-
-    // switch on eh_sel and branch to correct case
-
-    // setup default target
-    llvm::BasicBlock* defaulttarget = llvm::BasicBlock::Create("default", p->topfunc(), oldend);
-    //TODO: Error handling?
-    new llvm::UnreachableInst(defaulttarget);
-
-    llvm::SwitchInst* sw = p->ir->CreateSwitch(eh_sel, defaulttarget, catch_bbs.size());
-
-    // add all catches as cases
-    for(unsigned int c = 0; c < catch_bbs.size(); ++c)
-    {
-        llvm::BasicBlock* casebb = llvm::BasicBlock::Create("case", p->topfunc(), oldend);
-        llvm::BranchInst::Create(catch_bbs[c], casebb);
-        sw->addCase(llvm::ConstantInt::get(LLType::Int32Ty, c+1), casebb);
-    }
+    gIR->func()->landingPad.pop();
 
     // rewrite the scope
     p->scope() = IRScope(endbb,oldend);
--- a/ir/irfunction.h	Fri Jul 04 09:00:49 2008 +0200
+++ b/ir/irfunction.h	Sat Jul 05 10:22:56 2008 +0200
@@ -2,6 +2,7 @@
 #define LLVMDC_IR_IRFUNCTION_H
 
 #include "ir/ir.h"
+#include "ir/irlandingpad.h"
 
 #include <vector>
 
@@ -25,6 +26,9 @@
     llvm::AllocaInst* srcfileArg;
     llvm::AllocaInst* msgArg;
 
+    // landing pads for try statements
+    IRLandingPad landingPad;
+
     IrFunction(FuncDeclaration* fd);
 };
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ir/irlandingpad.cpp	Sat Jul 05 10:22:56 2008 +0200
@@ -0,0 +1,186 @@
+#include "gen/llvm.h"
+#include "gen/tollvm.h"
+#include "gen/irstate.h"
+#include "gen/runtime.h"
+#include "gen/logger.h"
+#include "gen/classes.h"
+#include "gen/llvmhelpers.h"
+#include "ir/irlandingpad.h"
+
+IRLandingPadInfo::IRLandingPadInfo(Catch* catchstmt, llvm::BasicBlock* end)
+: finallyBody(NULL)
+{
+    target = llvm::BasicBlock::Create("catch", gIR->topfunc(), end);
+    gIR->scope() = IRScope(target,end);
+
+    // assign storage to catch var
+    if(catchstmt->var) {
+        assert(!catchstmt->var->ir.irLocal);
+        catchstmt->var->ir.irLocal = new IrLocal(catchstmt->var);
+        LLValue* catch_var = gIR->func()->landingPad.getExceptionStorage();
+        catchstmt->var->ir.irLocal->value = gIR->ir->CreateBitCast(catch_var, getPtrToType(DtoType(catchstmt->var->type)));
+    }
+
+    // emit handler
+    assert(catchstmt->handler);
+    catchstmt->handler->toIR(gIR);
+
+    if (!gIR->scopereturned())
+        gIR->ir->CreateBr(end);
+
+    assert(catchstmt->type);
+    catchType = catchstmt->type->isClassHandle();
+    DtoForceDeclareDsymbol(catchType);
+    assert(catchType);
+}
+
+IRLandingPadInfo::IRLandingPadInfo(Statement* finallystmt)
+: target(NULL), finallyBody(finallystmt), catchType(NULL)
+{
+
+}
+
+
+void IRLandingPad::addCatch(Catch* catchstmt, llvm::BasicBlock* end)
+{
+    unpushed_infos.push_front(IRLandingPadInfo(catchstmt, end));
+}
+
+void IRLandingPad::addFinally(Statement* finallystmt)
+{
+    unpushed_infos.push_front(IRLandingPadInfo(finallystmt));
+}
+
+void IRLandingPad::push(llvm::BasicBlock* inBB)
+{
+    // store infos such that matches are right to left
+    nInfos.push(infos.size());
+    infos.insert(infos.end(), unpushed_infos.begin(), unpushed_infos.end());
+    unpushed_infos.clear();
+
+    constructLandingPad(inBB);
+
+    // store as invoke target
+    padBBs.push(inBB);
+}
+
+void IRLandingPad::pop()
+{
+    padBBs.pop();
+
+    size_t n = nInfos.top();
+    infos.resize(n);
+    nInfos.pop();
+}
+
+llvm::BasicBlock* IRLandingPad::get()
+{
+    if(padBBs.size() == 0)
+        return NULL;
+    else
+        return padBBs.top();
+}
+
+void IRLandingPad::constructLandingPad(llvm::BasicBlock* inBB)
+{
+    // save and rewrite scope
+    IRScope savedscope = gIR->scope();
+    gIR->scope() = IRScope(inBB,savedscope.end);
+
+    // eh_ptr = llvm.eh.exception();
+    llvm::Function* eh_exception_fn = GET_INTRINSIC_DECL(eh_exception);
+    LLValue* eh_ptr = gIR->ir->CreateCall(eh_exception_fn);
+
+    // build selector arguments
+    LLSmallVector<LLValue*, 6> selectorargs;
+    selectorargs.push_back(eh_ptr);
+
+    llvm::Function* personality_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_personality");
+    LLValue* personality_fn_arg = gIR->ir->CreateBitCast(personality_fn, getPtrToType(LLType::Int8Ty));
+    selectorargs.push_back(personality_fn_arg);
+
+    bool hasFinally = false;
+    // associate increasing ints with each unique classdecl encountered
+    std::map<ClassDeclaration*, int> catchToInt;
+    std::deque<IRLandingPadInfo>::reverse_iterator it = infos.rbegin(), end = infos.rend();
+    for(size_t i = infos.size() - 1; it != end; ++it, --i)
+    {
+        if(it->finallyBody)
+            hasFinally = true;
+        else
+        {
+            if(catchToInt.find(it->catchType) == catchToInt.end())
+            {
+                int newval = catchToInt.size();
+                catchToInt[it->catchType] = newval;
+            }
+            assert(it->catchType);
+            assert(it->catchType->ir.irStruct);
+            selectorargs.push_back(it->catchType->ir.irStruct->classInfo);
+        }
+    }
+    // if there's a finally, the eh table has to have a 0 action
+    if(hasFinally)
+        selectorargs.push_back(llvm::ConstantInt::get(LLType::Int32Ty, 0));
+    // if there is a catch, store exception object
+    if(catchToInt.size()) 
+    {
+        const LLType* objectTy = DtoType(ClassDeclaration::object->type);
+        assert(catch_var);
+        gIR->ir->CreateStore(gIR->ir->CreateBitCast(eh_ptr, objectTy), catch_var);
+    }
+
+    // eh_sel = llvm.eh.selector(eh_ptr, cast(byte*)&_d_eh_personality, <selectorargs>);
+    llvm::Function* eh_selector_fn;
+    if (global.params.is64bit)
+        eh_selector_fn = GET_INTRINSIC_DECL(eh_selector_i64);
+    else
+        eh_selector_fn = GET_INTRINSIC_DECL(eh_selector_i32);
+    LLValue* eh_sel = gIR->ir->CreateCall(eh_selector_fn, selectorargs.begin(), selectorargs.end());
+
+    // emit finallys and switches that branch to catches until there are no more catches
+    // then simply branch to the finally chain
+    llvm::SwitchInst* switchinst = NULL;
+    for(it = infos.rbegin(); it != end; ++it)
+    {
+        // if it's a finally, emit its code
+        if(it->finallyBody)
+        {
+            if(switchinst)
+                switchinst = NULL;
+            it->finallyBody->toIR(gIR);
+        }
+        // otherwise it's a catch and we'll add a switch case
+        else
+        {
+            if(!switchinst)
+            {
+                switchinst = gIR->ir->CreateSwitch(eh_sel, llvm::BasicBlock::Create("switchdefault", gIR->topfunc(), gIR->scopeend()), infos.size());
+                gIR->scope() = IRScope(switchinst->getDefaultDest(), gIR->scopeend());
+            }
+            // catches matched first get the largest switchval, so do size - unique int
+            llvm::ConstantInt* switchval = llvm::ConstantInt::get(LLType::Int32Ty, catchToInt.size() - catchToInt[it->catchType]);
+            // and make sure we don't add the same switchval twice, may happen with nested trys
+            if(!switchinst->findCaseValue(switchval))
+                switchinst->addCase(switchval, it->target);
+        }
+    }
+
+    // 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();
+
+    gIR->scope() = savedscope;
+}
+
+LLValue* IRLandingPad::getExceptionStorage()
+{
+    if(!catch_var)
+    {
+        Logger::println("Making new catch var");
+        const LLType* objectTy = DtoType(ClassDeclaration::object->type);
+        catch_var = new llvm::AllocaInst(objectTy,"catchvar",gIR->topallocapoint());
+    }
+    return catch_var;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ir/irlandingpad.h	Sat Jul 05 10:22:56 2008 +0200
@@ -0,0 +1,70 @@
+#ifndef LLVMDC_IR_IRLANDINGPADINFO_H
+#define LLVMDC_IR_IRLANDINGPADINFO_H
+
+#include "ir/ir.h"
+#include "statement.h"
+
+#include <deque>
+#include <stack>
+
+struct IRLandingPadInfo
+{
+    // default constructor for being able to store in a vector
+    IRLandingPadInfo()
+    : target(NULL), finallyBody(NULL), catchType(NULL)
+    {}
+
+    IRLandingPadInfo(Catch* catchstmt, llvm::BasicBlock* end);
+    IRLandingPadInfo(Statement* finallystmt);
+
+    // the target catch bb if this is a catch
+    // or the target finally bb if this is a finally
+    llvm::BasicBlock* target;
+
+    // nonzero if this is a finally
+    Statement* finallyBody;
+
+    // nonzero if this is a catch
+    ClassDeclaration* catchType;
+};
+
+struct IRLandingPad
+{
+    IRLandingPad() : catch_var(NULL) {}
+
+    // builds a new landing pad according to given infos
+    // and the ones on the stack. also stores it as invoke target
+    void push(llvm::BasicBlock* inBB);
+
+    void addCatch(Catch* catchstmt, llvm::BasicBlock* end);
+    void addFinally(Statement* finallystmt);
+
+    // pops the most recently constructed landing pad bb
+    // and its infos
+    void pop();
+
+    // gets the current landing pad
+    llvm::BasicBlock* get();
+
+    // creates or gets storage for exception object
+    LLValue* getExceptionStorage();
+
+private:
+    // constructs the landing pad from infos
+    void constructLandingPad(llvm::BasicBlock* inBB);
+
+    // information needed to create landing pads
+    std::deque<IRLandingPadInfo> infos;
+    std::deque<IRLandingPadInfo> unpushed_infos;
+
+    // the number of infos we had before the push
+    std::stack<size_t> nInfos;
+
+    // the target for invokes
+    std::stack<llvm::BasicBlock*> padBBs;
+
+    // storage for the catch variable
+    LLValue* catch_var;
+};
+
+#endif
--- a/tango/lib/compiler/llvmdc/eh.d	Fri Jul 04 09:00:49 2008 +0200
+++ b/tango/lib/compiler/llvmdc/eh.d	Sat Jul 05 10:22:56 2008 +0200
@@ -4,7 +4,7 @@
 
 import util.console;
 
-//debug = EH_personality;
+// debug = EH_personality;
 
 private extern(C) void abort();
 private extern(C) int printf(char*, ...);
@@ -251,6 +251,7 @@
 
   else if(actions & _Unwind_Action.HANDLER_PHASE)
   {
+    debug(EH_personality) printf("Setting switch value to: %d!\n", switchval);
     _Unwind_SetGR(context, eh_exception_regno, cast(ulong)cast(void*)(exception_struct.exception_object));
     _Unwind_SetGR(context, eh_selector_regno, switchval);
     _Unwind_SetIP(context, landing_pad);
@@ -269,6 +270,7 @@
   debug(EH_personality) printf("Calling cleanup routine...\n");
 
   _Unwind_SetGR(context, eh_exception_regno, cast(ulong)exception_struct);
+  _Unwind_SetGR(context, eh_selector_regno, 0);
   _Unwind_SetIP(context, landing_pad);
   return _Unwind_Reason_Code.INSTALL_CONTEXT;
 }