Mercurial > projects > ldc
comparison ir/irlandingpad.cpp @ 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 | |
children | d772927ca496 |
comparison
equal
deleted
inserted
replaced
318:8e570dbe4087 | 319:e9c93739bc4c |
---|---|
1 #include "gen/llvm.h" | |
2 #include "gen/tollvm.h" | |
3 #include "gen/irstate.h" | |
4 #include "gen/runtime.h" | |
5 #include "gen/logger.h" | |
6 #include "gen/classes.h" | |
7 #include "gen/llvmhelpers.h" | |
8 #include "ir/irlandingpad.h" | |
9 | |
10 IRLandingPadInfo::IRLandingPadInfo(Catch* catchstmt, llvm::BasicBlock* end) | |
11 : finallyBody(NULL) | |
12 { | |
13 target = llvm::BasicBlock::Create("catch", gIR->topfunc(), end); | |
14 gIR->scope() = IRScope(target,end); | |
15 | |
16 // assign storage to catch var | |
17 if(catchstmt->var) { | |
18 assert(!catchstmt->var->ir.irLocal); | |
19 catchstmt->var->ir.irLocal = new IrLocal(catchstmt->var); | |
20 LLValue* catch_var = gIR->func()->landingPad.getExceptionStorage(); | |
21 catchstmt->var->ir.irLocal->value = gIR->ir->CreateBitCast(catch_var, getPtrToType(DtoType(catchstmt->var->type))); | |
22 } | |
23 | |
24 // emit handler | |
25 assert(catchstmt->handler); | |
26 catchstmt->handler->toIR(gIR); | |
27 | |
28 if (!gIR->scopereturned()) | |
29 gIR->ir->CreateBr(end); | |
30 | |
31 assert(catchstmt->type); | |
32 catchType = catchstmt->type->isClassHandle(); | |
33 DtoForceDeclareDsymbol(catchType); | |
34 assert(catchType); | |
35 } | |
36 | |
37 IRLandingPadInfo::IRLandingPadInfo(Statement* finallystmt) | |
38 : target(NULL), finallyBody(finallystmt), catchType(NULL) | |
39 { | |
40 | |
41 } | |
42 | |
43 | |
44 void IRLandingPad::addCatch(Catch* catchstmt, llvm::BasicBlock* end) | |
45 { | |
46 unpushed_infos.push_front(IRLandingPadInfo(catchstmt, end)); | |
47 } | |
48 | |
49 void IRLandingPad::addFinally(Statement* finallystmt) | |
50 { | |
51 unpushed_infos.push_front(IRLandingPadInfo(finallystmt)); | |
52 } | |
53 | |
54 void IRLandingPad::push(llvm::BasicBlock* inBB) | |
55 { | |
56 // store infos such that matches are right to left | |
57 nInfos.push(infos.size()); | |
58 infos.insert(infos.end(), unpushed_infos.begin(), unpushed_infos.end()); | |
59 unpushed_infos.clear(); | |
60 | |
61 constructLandingPad(inBB); | |
62 | |
63 // store as invoke target | |
64 padBBs.push(inBB); | |
65 } | |
66 | |
67 void IRLandingPad::pop() | |
68 { | |
69 padBBs.pop(); | |
70 | |
71 size_t n = nInfos.top(); | |
72 infos.resize(n); | |
73 nInfos.pop(); | |
74 } | |
75 | |
76 llvm::BasicBlock* IRLandingPad::get() | |
77 { | |
78 if(padBBs.size() == 0) | |
79 return NULL; | |
80 else | |
81 return padBBs.top(); | |
82 } | |
83 | |
84 void IRLandingPad::constructLandingPad(llvm::BasicBlock* inBB) | |
85 { | |
86 // save and rewrite scope | |
87 IRScope savedscope = gIR->scope(); | |
88 gIR->scope() = IRScope(inBB,savedscope.end); | |
89 | |
90 // eh_ptr = llvm.eh.exception(); | |
91 llvm::Function* eh_exception_fn = GET_INTRINSIC_DECL(eh_exception); | |
92 LLValue* eh_ptr = gIR->ir->CreateCall(eh_exception_fn); | |
93 | |
94 // build selector arguments | |
95 LLSmallVector<LLValue*, 6> selectorargs; | |
96 selectorargs.push_back(eh_ptr); | |
97 | |
98 llvm::Function* personality_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_personality"); | |
99 LLValue* personality_fn_arg = gIR->ir->CreateBitCast(personality_fn, getPtrToType(LLType::Int8Ty)); | |
100 selectorargs.push_back(personality_fn_arg); | |
101 | |
102 bool hasFinally = false; | |
103 // associate increasing ints with each unique classdecl encountered | |
104 std::map<ClassDeclaration*, int> catchToInt; | |
105 std::deque<IRLandingPadInfo>::reverse_iterator it = infos.rbegin(), end = infos.rend(); | |
106 for(size_t i = infos.size() - 1; it != end; ++it, --i) | |
107 { | |
108 if(it->finallyBody) | |
109 hasFinally = true; | |
110 else | |
111 { | |
112 if(catchToInt.find(it->catchType) == catchToInt.end()) | |
113 { | |
114 int newval = catchToInt.size(); | |
115 catchToInt[it->catchType] = newval; | |
116 } | |
117 assert(it->catchType); | |
118 assert(it->catchType->ir.irStruct); | |
119 selectorargs.push_back(it->catchType->ir.irStruct->classInfo); | |
120 } | |
121 } | |
122 // if there's a finally, the eh table has to have a 0 action | |
123 if(hasFinally) | |
124 selectorargs.push_back(llvm::ConstantInt::get(LLType::Int32Ty, 0)); | |
125 // if there is a catch, store exception object | |
126 if(catchToInt.size()) | |
127 { | |
128 const LLType* objectTy = DtoType(ClassDeclaration::object->type); | |
129 assert(catch_var); | |
130 gIR->ir->CreateStore(gIR->ir->CreateBitCast(eh_ptr, objectTy), catch_var); | |
131 } | |
132 | |
133 // eh_sel = llvm.eh.selector(eh_ptr, cast(byte*)&_d_eh_personality, <selectorargs>); | |
134 llvm::Function* eh_selector_fn; | |
135 if (global.params.is64bit) | |
136 eh_selector_fn = GET_INTRINSIC_DECL(eh_selector_i64); | |
137 else | |
138 eh_selector_fn = GET_INTRINSIC_DECL(eh_selector_i32); | |
139 LLValue* eh_sel = gIR->ir->CreateCall(eh_selector_fn, selectorargs.begin(), selectorargs.end()); | |
140 | |
141 // emit finallys and switches that branch to catches until there are no more catches | |
142 // then simply branch to the finally chain | |
143 llvm::SwitchInst* switchinst = NULL; | |
144 for(it = infos.rbegin(); it != end; ++it) | |
145 { | |
146 // if it's a finally, emit its code | |
147 if(it->finallyBody) | |
148 { | |
149 if(switchinst) | |
150 switchinst = NULL; | |
151 it->finallyBody->toIR(gIR); | |
152 } | |
153 // otherwise it's a catch and we'll add a switch case | |
154 else | |
155 { | |
156 if(!switchinst) | |
157 { | |
158 switchinst = gIR->ir->CreateSwitch(eh_sel, llvm::BasicBlock::Create("switchdefault", gIR->topfunc(), gIR->scopeend()), infos.size()); | |
159 gIR->scope() = IRScope(switchinst->getDefaultDest(), gIR->scopeend()); | |
160 } | |
161 // catches matched first get the largest switchval, so do size - unique int | |
162 llvm::ConstantInt* switchval = llvm::ConstantInt::get(LLType::Int32Ty, catchToInt.size() - catchToInt[it->catchType]); | |
163 // and make sure we don't add the same switchval twice, may happen with nested trys | |
164 if(!switchinst->findCaseValue(switchval)) | |
165 switchinst->addCase(switchval, it->target); | |
166 } | |
167 } | |
168 | |
169 // no catch matched and all finallys executed - resume unwind | |
170 llvm::Function* unwind_resume_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_resume_unwind"); | |
171 gIR->ir->CreateCall(unwind_resume_fn, eh_ptr); | |
172 gIR->ir->CreateUnreachable(); | |
173 | |
174 gIR->scope() = savedscope; | |
175 } | |
176 | |
177 LLValue* IRLandingPad::getExceptionStorage() | |
178 { | |
179 if(!catch_var) | |
180 { | |
181 Logger::println("Making new catch var"); | |
182 const LLType* objectTy = DtoType(ClassDeclaration::object->type); | |
183 catch_var = new llvm::AllocaInst(objectTy,"catchvar",gIR->topallocapoint()); | |
184 } | |
185 return catch_var; | |
186 } |