Skip to content

Commit d0ef732

Browse files
committed
Merge pull request #409 from AlexeyProkhin/chaining
New exception chaining implementation
2 parents 272a25c + 324ff01 commit d0ef732

File tree

5 files changed

+131
-116
lines changed

5 files changed

+131
-116
lines changed

gen/runtime.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,14 @@ static void LLVM_D_BuildRuntimeModule()
898898
llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M);
899899
}
900900

901+
// void _d_eh_handle_collision(ptr exc_struct, ptr exc_struct)
902+
{
903+
llvm::StringRef fname("_d_eh_handle_collision");
904+
LLType *types[] = { voidPtrTy, voidPtrTy };
905+
LLFunctionType* fty = llvm::FunctionType::get(voidTy, types, false);
906+
llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M);
907+
}
908+
901909
/////////////////////////////////////////////////////////////////////////////////////
902910
/////////////////////////////////////////////////////////////////////////////////////
903911
/////////////////////////////////////////////////////////////////////////////////////

ir/irlandingpad.cpp

+93-82
Original file line numberDiff line numberDiff line change
@@ -17,152 +17,134 @@
1717
#include "gen/tollvm.h"
1818
#include "ir/irlandingpad.h"
1919

20-
IRLandingPadInfo::IRLandingPadInfo(Catch* catchstmt_, llvm::BasicBlock* end_) :
21-
finallyBody(NULL), catchstmt(catchstmt_), end(end_)
20+
IRLandingPadCatchInfo::IRLandingPadCatchInfo(Catch* catchstmt_, llvm::BasicBlock* end_) :
21+
catchStmt(catchstmt_), end(end_)
2222
{
2323
target = llvm::BasicBlock::Create(gIR->context(), "catch", gIR->topfunc(), end);
2424

25-
assert(catchstmt->type);
26-
catchType = catchstmt->type->toBasetype()->isClassHandle();
25+
assert(catchStmt->type);
26+
catchType = catchStmt->type->toBasetype()->isClassHandle();
2727
assert(catchType);
2828
catchType->codegen(Type::sir);
2929

30-
if(catchstmt->var) {
31-
if(!catchstmt->var->nestedrefs.dim) {
32-
gIR->func()->gen->landingPadInfo.getExceptionStorage();
33-
}
30+
if (catchStmt->var) {
31+
if (!catchStmt->var->nestedrefs.dim) {
32+
gIR->func()->gen->landingPadInfo.getExceptionStorage();
33+
}
3434
}
3535
}
3636

37-
IRLandingPadInfo::IRLandingPadInfo(Statement* finallystmt) :
38-
target(NULL), finallyBody(finallystmt), catchstmt(NULL)
39-
{
40-
41-
}
42-
43-
void IRLandingPadInfo::toIR()
37+
void IRLandingPadCatchInfo::toIR()
4438
{
45-
if (!catchstmt)
39+
if (!catchStmt)
4640
return;
4741

4842
gIR->scope() = IRScope(target, target);
49-
DtoDwarfBlockStart(catchstmt->loc);
43+
DtoDwarfBlockStart(catchStmt->loc);
5044

5145
// assign storage to catch var
52-
if(catchstmt->var) {
46+
if (catchStmt->var) {
5347
// use the same storage for all exceptions that are not accessed in
5448
// nested functions
55-
if(!catchstmt->var->nestedrefs.dim) {
56-
assert(!catchstmt->var->ir.irLocal);
57-
catchstmt->var->ir.irLocal = new IrLocal(catchstmt->var);
49+
if (!catchStmt->var->nestedrefs.dim) {
50+
assert(!catchStmt->var->ir.irLocal);
51+
catchStmt->var->ir.irLocal = new IrLocal(catchStmt->var);
5852
LLValue* catch_var = gIR->func()->gen->landingPadInfo.getExceptionStorage();
59-
catchstmt->var->ir.irLocal->value = gIR->ir->CreateBitCast(catch_var, getPtrToType(DtoType(catchstmt->var->type)));
53+
catchStmt->var->ir.irLocal->value = gIR->ir->CreateBitCast(catch_var, getPtrToType(DtoType(catchStmt->var->type)));
6054
}
6155

6256
// this will alloca if we haven't already and take care of nested refs
63-
DtoDeclarationExp(catchstmt->var);
57+
DtoDeclarationExp(catchStmt->var);
6458

6559
// the exception will only be stored in catch_var. copy it over if necessary
66-
if(catchstmt->var->ir.irLocal->value != gIR->func()->gen->landingPadInfo.getExceptionStorage()) {
67-
LLValue* exc = gIR->ir->CreateBitCast(DtoLoad(gIR->func()->gen->landingPadInfo.getExceptionStorage()), DtoType(catchstmt->var->type));
68-
DtoStore(exc, catchstmt->var->ir.irLocal->value);
60+
if (catchStmt->var->ir.irLocal->value != gIR->func()->gen->landingPadInfo.getExceptionStorage()) {
61+
LLValue* exc = gIR->ir->CreateBitCast(DtoLoad(gIR->func()->gen->landingPadInfo.getExceptionStorage()), DtoType(catchStmt->var->type));
62+
DtoStore(exc, catchStmt->var->ir.irLocal->value);
6963
}
7064
}
7165

7266
// emit handler, if there is one
7367
// handler is zero for instance for 'catch { debug foo(); }'
74-
if(catchstmt->handler)
75-
catchstmt->handler->toIR(gIR);
68+
if (catchStmt->handler)
69+
catchStmt->handler->toIR(gIR);
7670

7771
if (!gIR->scopereturned())
7872
gIR->ir->CreateBr(end);
7973

8074
DtoDwarfBlockEnd();
8175
}
8276

83-
8477
void IRLandingPad::addCatch(Catch* catchstmt, llvm::BasicBlock* end)
8578
{
86-
unpushed_infos.push_front(IRLandingPadInfo(catchstmt, end));
79+
unpushedScope.catches.push_back(IRLandingPadCatchInfo(catchstmt, end));
8780
}
8881

8982
void IRLandingPad::addFinally(Statement* finallystmt)
9083
{
91-
unpushed_infos.push_front(IRLandingPadInfo(finallystmt));
84+
assert(unpushedScope.finallyBody == NULL && "only one finally per try-finally block");
85+
unpushedScope.finallyBody = finallystmt;
9286
}
9387

9488
void IRLandingPad::push(llvm::BasicBlock* inBB)
9589
{
96-
// store infos such that matches are right to left
97-
nInfos.push(infos.size());
98-
infos.insert(infos.end(), unpushed_infos.begin(), unpushed_infos.end());
99-
unpushed_infos.clear();
100-
101-
// store as invoke target
102-
padBBs.push(inBB);
90+
unpushedScope.target = inBB;
91+
scopeStack.push(unpushedScope);
92+
unpushedScope = IRLandingPadScope();
10393
gIR->func()->gen->landingPad = get();
10494
}
10595

10696
void IRLandingPad::pop()
10797
{
108-
llvm::BasicBlock *inBB = padBBs.top();
109-
padBBs.pop();
98+
IRLandingPadScope scope = scopeStack.top();
99+
scopeStack.pop();
110100
gIR->func()->gen->landingPad = get();
111101

112-
size_t n = nInfos.top();
113-
for (int i = n, c = infos.size(); i < c; ++i)
114-
infos.at(i).toIR();
115-
constructLandingPad(inBB);
116-
117-
infos.resize(n);
118-
nInfos.pop();
102+
std::deque<IRLandingPadCatchInfo>::iterator itr, end = scope.catches.end();
103+
for (itr = scope.catches.begin(); itr != end; ++itr)
104+
itr->toIR();
105+
constructLandingPad(scope);
119106
}
120107

121108
llvm::BasicBlock* IRLandingPad::get()
122109
{
123-
if(padBBs.size() == 0)
110+
if (scopeStack.size() == 0)
124111
return NULL;
125112
else
126-
return padBBs.top();
113+
return scopeStack.top().target;
127114
}
128115

129-
void IRLandingPad::constructLandingPad(llvm::BasicBlock* inBB)
116+
// creates new landing pad
117+
static llvm::LandingPadInst *createLandingPadInst()
118+
{
119+
llvm::Function* personality_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_personality");
120+
LLType *retType = LLStructType::get(LLType::getInt8PtrTy(gIR->context()),
121+
LLType::getInt32Ty(gIR->context()),
122+
NULL);
123+
return gIR->ir->CreateLandingPad(retType, personality_fn, 0, "landing_pad");
124+
}
125+
126+
void IRLandingPad::constructLandingPad(IRLandingPadScope scope)
130127
{
131128
// save and rewrite scope
132-
IRScope savedscope = gIR->scope();
133-
gIR->scope() = IRScope(inBB,savedscope.end);
129+
IRScope savedIRScope = gIR->scope();
130+
gIR->scope() = IRScope(scope.target, savedIRScope.end);
134131

135-
// personality fn
136-
llvm::Function* personality_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_personality");
137132
// create landingpad
138-
LLType *retType = LLStructType::get(LLType::getInt8PtrTy(gIR->context()), LLType::getInt32Ty(gIR->context()), NULL);
139-
llvm::LandingPadInst *landingPad = gIR->ir->CreateLandingPad(retType, personality_fn, 0);
133+
llvm::LandingPadInst *landingPad = createLandingPadInst();
140134
LLValue* eh_ptr = DtoExtractValue(landingPad, 0);
141135
LLValue* eh_sel = DtoExtractValue(landingPad, 1);
142136

143137
// add landingpad clauses, emit finallys and 'if' chain to catch the exception
144138
llvm::Function* eh_typeid_for_fn = GET_INTRINSIC_DECL(eh_typeid_for);
145-
std::deque<IRLandingPadInfo> infos = this->infos;
146-
std::stack<size_t> nInfos = this->nInfos;
147-
std::deque<IRLandingPadInfo>::reverse_iterator rit, rend = infos.rend();
148139
bool isFirstCatch = true;
149-
for(rit = infos.rbegin(); rit != rend; ++rit)
150-
{
151-
// if it's a finally, emit its code
152-
if(rit->finallyBody)
153-
{
154-
size_t n = this->nInfos.top();
155-
this->infos.resize(n);
156-
this->nInfos.pop();
157-
rit->finallyBody->toIR(gIR);
158-
landingPad->setCleanup(true);
159-
}
160-
// otherwise it's a catch and we'll add a if-statement
161-
else
162-
{
140+
std::stack<IRLandingPadScope> savedScopeStack = scopeStack;
141+
std::deque<IRLandingPadCatchInfo>::iterator catchItr, catchItrEnd;
142+
while (true) {
143+
catchItr = scope.catches.begin();
144+
catchItrEnd = scope.catches.end();
145+
for (; catchItr != catchItrEnd; ++catchItr) {
163146
// if it is a first catch and some catch allocated storage, store exception object
164-
if(isFirstCatch && catch_var)
165-
{
147+
if (isFirstCatch && catch_var) {
166148
// eh_ptr is a pointer to _d_exception, which has a reference
167149
// to the Throwable object at offset 0.
168150
LLType *objectPtrTy = DtoType(ClassDeclaration::object->type->pointerTo());
@@ -173,35 +155,64 @@ void IRLandingPad::constructLandingPad(llvm::BasicBlock* inBB)
173155
// create next block
174156
llvm::BasicBlock *next = llvm::BasicBlock::Create(gIR->context(), "eh.next", gIR->topfunc(), gIR->scopeend());
175157
// get class info symbol
176-
LLValue *classInfo = rit->catchType->ir.irAggr->getClassInfoSymbol();
158+
LLValue *classInfo = catchItr->catchType->ir.irAggr->getClassInfoSymbol();
177159
// add that symbol as landing pad clause
178160
landingPad->addClause(classInfo);
179161
// call llvm.eh.typeid.for to get class info index in the exception table
180162
classInfo = DtoBitCast(classInfo, getPtrToType(DtoType(Type::tint8)));
181163
LLValue *eh_id = gIR->ir->CreateCall(eh_typeid_for_fn, classInfo);
182164
// check exception selector (eh_sel) against the class info index
183-
gIR->ir->CreateCondBr(gIR->ir->CreateICmpEQ(eh_sel, eh_id), rit->target, next);
165+
gIR->ir->CreateCondBr(gIR->ir->CreateICmpEQ(eh_sel, eh_id), catchItr->target, next);
184166
gIR->scope() = IRScope(next, gIR->scopeend());
185167
}
168+
169+
if (scope.finallyBody) {
170+
// create collision landing pad that handles exceptions thrown inside the finally block
171+
llvm::BasicBlock *collision = llvm::BasicBlock::Create(gIR->context(), "eh.collision", gIR->topfunc(), gIR->scopeend());
172+
llvm::BasicBlock *bb = gIR->scopebb();
173+
gIR->scope() = IRScope(collision, gIR->scopeend());
174+
llvm::LandingPadInst *collisionLandingPad = createLandingPadInst();
175+
LLValue* collision_eh_ptr = DtoExtractValue(collisionLandingPad, 0);
176+
collisionLandingPad->setCleanup(true);
177+
llvm::Function* collision_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_handle_collision");
178+
gIR->CreateCallOrInvoke2(collision_fn, collision_eh_ptr, eh_ptr);
179+
gIR->ir->CreateUnreachable();
180+
gIR->scope() = IRScope(bb, gIR->scopeend());
181+
182+
// set collision landing pad as unwind target and emit the body of the finally
183+
DtoDwarfBlockStart(scope.finallyBody->loc);
184+
scopeStack.push(IRLandingPadScope(collision));
185+
gIR->func()->gen->landingPad = collision;
186+
scope.finallyBody->toIR(gIR);
187+
scopeStack.pop();
188+
gIR->func()->gen->landingPad = get();
189+
DtoDwarfBlockEnd();
190+
landingPad->setCleanup(true);
191+
}
192+
193+
if (scopeStack.empty())
194+
break;
195+
scope = scopeStack.top();
196+
scopeStack.pop();
197+
gIR->func()->gen->landingPad = get();
186198
}
187199

188200
// restore landing pad infos
189-
this->infos = infos;
190-
this->nInfos = nInfos;
201+
scopeStack = savedScopeStack;
202+
gIR->func()->gen->landingPad = get();
191203

192204
// no catch matched and all finallys executed - resume unwind
193205
llvm::Function* unwind_resume_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_resume_unwind");
194206
gIR->ir->CreateCall(unwind_resume_fn, eh_ptr);
195207
gIR->ir->CreateUnreachable();
196208

197209
// restore scope
198-
gIR->scope() = savedscope;
210+
gIR->scope() = savedIRScope;
199211
}
200212

201213
LLValue* IRLandingPad::getExceptionStorage()
202214
{
203-
if(!catch_var)
204-
{
215+
if (!catch_var) {
205216
Logger::println("Making new catch var");
206217
catch_var = DtoAlloca(ClassDeclaration::object->type, "catchvar");
207218
}

ir/irlandingpad.h

+28-32
Original file line numberDiff line numberDiff line change
@@ -25,37 +25,39 @@ namespace llvm {
2525
class Value;
2626
class BasicBlock;
2727
class Function;
28+
class LandingPadInst;
2829
}
2930

30-
// only to be used within IRLandingPad
31-
// holds information about a single catch or finally
32-
struct IRLandingPadInfo
31+
// holds information about a single catch
32+
struct IRLandingPadCatchInfo
3333
{
3434
// default constructor for being able to store in a vector
35-
IRLandingPadInfo() :
36-
target(NULL), finallyBody(NULL), catchstmt(NULL)
35+
IRLandingPadCatchInfo() :
36+
target(NULL), end(0), catchStmt(NULL), catchType(0)
3737
{}
3838

39-
// constructor for catch
40-
IRLandingPadInfo(Catch* catchstmt, llvm::BasicBlock* end);
41-
42-
// constructor for finally
43-
IRLandingPadInfo(Statement* finallystmt);
39+
IRLandingPadCatchInfo(Catch* catchStmt, llvm::BasicBlock* end);
4440

4541
// codegen the catch block
4642
void toIR();
4743

48-
// the target catch bb if this is a catch
49-
// or the target finally bb if this is a finally
50-
llvm::BasicBlock* target;
44+
llvm::BasicBlock *target;
45+
llvm::BasicBlock *end;
46+
Catch *catchStmt;
47+
ClassDeclaration *catchType;
48+
};
5149

52-
// nonzero if this is a finally
53-
Statement* finallyBody;
50+
// holds information about a single try-catch-inally block
51+
struct IRLandingPadScope
52+
{
53+
explicit IRLandingPadScope(llvm::BasicBlock *target_ = NULL) : target(target_), finallyBody(0) {}
5454

55-
// nonzero if this is a catch
56-
Catch* catchstmt;
57-
llvm::BasicBlock* end;
58-
ClassDeclaration* catchType;
55+
// the target for invokes
56+
llvm::BasicBlock *target;
57+
// information about catch blocks
58+
std::deque<IRLandingPadCatchInfo> catches;
59+
// the body of finally
60+
Statement *finallyBody;
5961
};
6062

6163

@@ -79,25 +81,19 @@ struct IRLandingPad
7981
// and its infos
8082
void pop();
8183

82-
// gets the current landing pad
83-
llvm::BasicBlock* get();
84-
8584
// creates or gets storage for exception object
8685
llvm::Value* getExceptionStorage();
8786

8887
private:
89-
// constructs the landing pad from infos
90-
void constructLandingPad(llvm::BasicBlock* inBB);
91-
92-
// information needed to create landing pads
93-
std::deque<IRLandingPadInfo> infos;
94-
std::deque<IRLandingPadInfo> unpushed_infos;
88+
// gets the current landing pad
89+
llvm::BasicBlock* get();
9590

96-
// the number of infos we had before the push
97-
std::stack<size_t> nInfos;
91+
// constructs the landing pad
92+
void constructLandingPad(IRLandingPadScope scope);
9893

99-
// the target for invokes
100-
std::stack<llvm::BasicBlock*> padBBs;
94+
// information about try-catch-finally blocks
95+
std::stack<IRLandingPadScope> scopeStack;
96+
IRLandingPadScope unpushedScope;
10197

10298
// storage for the catch variable
10399
llvm::Value* catch_var;

runtime/druntime

Submodule druntime updated 1 file

tests/d2/dmd-testsuite

Submodule dmd-testsuite updated 2 files

0 commit comments

Comments
 (0)