Mercurial > projects > ldc
comparison tango/lib/compiler/llvmdc/eh.d @ 314:8d98e42ece93 trunk
[svn r335] The basics of exception handling are in place.
Still need to make sure calls are turned into invokes everywhere. (NewExpression for instance)
Still some rough edges and corner cases to figure out.
Needs testing!
author | ChristianK |
---|---|
date | Wed, 02 Jul 2008 22:20:18 +0200 |
parents | 44a95ac7368a |
children | 8e570dbe4087 |
comparison
equal
deleted
inserted
replaced
313:a498b736a0bd | 314:8d98e42ece93 |
---|---|
2 * Temporary exception handling stubs | 2 * Temporary exception handling stubs |
3 */ | 3 */ |
4 | 4 |
5 import util.console; | 5 import util.console; |
6 | 6 |
7 //debug = EH_personality; | |
8 | |
7 private extern(C) void abort(); | 9 private extern(C) void abort(); |
10 private extern(C) int printf(char*, ...); | |
11 | |
12 // D runtime functions | |
13 extern(C) { | |
14 int _d_isbaseof(ClassInfo oc, ClassInfo c); | |
15 } | |
16 | |
17 // libunwind stuff | |
18 extern(C) | |
19 { | |
20 enum _Unwind_Reason_Code | |
21 { | |
22 NO_REASON = 0, | |
23 FOREIGN_EXCEPTION_CAUGHT = 1, | |
24 FATAL_PHASE2_ERROR = 2, | |
25 FATAL_PHASE1_ERROR = 3, | |
26 NORMAL_STOP = 4, | |
27 END_OF_STACK = 5, | |
28 HANDLER_FOUND = 6, | |
29 INSTALL_CONTEXT = 7, | |
30 CONTINUE_UNWIND = 8 | |
31 } | |
32 | |
33 enum _Unwind_Action | |
34 { | |
35 SEARCH_PHASE = 1, | |
36 CLEANUP_PHASE = 2, | |
37 HANDLER_PHASE = 3, | |
38 FORCE_UNWIND = 4 | |
39 } | |
40 | |
41 alias void* _Unwind_Context_Ptr; | |
42 | |
43 alias void function(_Unwind_Reason_Code, _Unwind_Exception*) _Unwind_Exception_Cleanup_Fn; | |
44 | |
45 struct _Unwind_Exception | |
46 { | |
47 char[8] exception_class; | |
48 _Unwind_Exception_Cleanup_Fn exception_cleanup; | |
49 int private_1; | |
50 int private_2; | |
51 } | |
52 | |
53 void _Unwind_Resume(_Unwind_Exception*); | |
54 _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Exception*); | |
55 ulong _Unwind_GetLanguageSpecificData(_Unwind_Context_Ptr context); | |
56 ulong _Unwind_GetIP(_Unwind_Context_Ptr context); | |
57 ulong _Unwind_SetIP(_Unwind_Context_Ptr context, ulong new_value); | |
58 ulong _Unwind_SetGR(_Unwind_Context_Ptr context, int index, ulong new_value); | |
59 ulong _Unwind_GetRegionStart(_Unwind_Context_Ptr context); | |
60 } | |
61 | |
62 | |
63 // helpers | |
64 private ubyte* get_uleb128(ubyte* addr, ref size_t res) | |
65 { | |
66 res = 0; | |
67 size_t bitsize = 0; | |
68 | |
69 // read as long as high bit is set | |
70 while(*addr & 0x80) { | |
71 res |= (*addr & 0x7f) << bitsize; | |
72 bitsize += 7; | |
73 addr += 1; | |
74 if(bitsize >= size_t.sizeof*8) | |
75 throw new Exception("tried to read uleb128 that exceeded size of size_t"); | |
76 } | |
77 // read last | |
78 if(bitsize != 0 && *addr >= 1 << size_t.sizeof*8 - bitsize) | |
79 throw new Exception("tried to read uleb128 that exceeded size of size_t"); | |
80 res |= (*addr) << bitsize; | |
81 | |
82 return addr + 1; | |
83 } | |
84 | |
85 private ubyte* get_sleb128(ubyte* addr, ref ptrdiff_t res) | |
86 { | |
87 res = 0; | |
88 size_t bitsize = 0; | |
89 | |
90 // read as long as high bit is set | |
91 while(*addr & 0x80) { | |
92 res |= (*addr & 0x7f) << bitsize; | |
93 bitsize += 7; | |
94 addr += 1; | |
95 if(bitsize >= size_t.sizeof*8) | |
96 throw new Exception("tried to read sleb128 that exceeded size of size_t"); | |
97 } | |
98 // read last | |
99 if(bitsize != 0 && *addr >= 1 << size_t.sizeof*8 - bitsize) | |
100 throw new Exception("tried to read sleb128 that exceeded size of size_t"); | |
101 res |= (*addr) << bitsize; | |
102 | |
103 // take care of sign | |
104 if(bitsize < size_t.sizeof*8 && ((*addr) & 0x40)) | |
105 res |= cast(ptrdiff_t)(-1) ^ ((1 << (bitsize+7)) - 1); | |
106 | |
107 return addr + 1; | |
108 } | |
109 | |
110 | |
111 | |
112 struct _d_exception | |
113 { | |
114 Object exception_object; | |
115 _Unwind_Exception unwind_info; | |
116 } | |
117 | |
118 char[8] _d_exception_class = "LLDCD1\0\0"; | |
119 | |
120 //TODO: cleanup handling | |
121 extern(C) _Unwind_Reason_Code _d_eh_personality(int ver, _Unwind_Action actions, ulong exception_class, _Unwind_Exception* exception_info, _Unwind_Context_Ptr context) | |
122 { | |
123 // check ver: the C++ Itanium ABI only allows ver == 1 | |
124 if(ver != 1) | |
125 return _Unwind_Reason_Code.FATAL_PHASE1_ERROR; | |
126 | |
127 // check exceptionClass | |
128 //TODO: Treat foreign exceptions with more respect | |
129 if((cast(char*)&exception_class)[0..8] != _d_exception_class) | |
130 return _Unwind_Reason_Code.FATAL_PHASE1_ERROR; | |
131 | |
132 // find call site table, action table and classinfo table | |
133 // Note: callsite and action tables do not contain static-length | |
134 // data and will be parsed as needed | |
135 // Note: classinfo_table points past the end of the table | |
136 ubyte* callsite_table; | |
137 ubyte* action_table; | |
138 ClassInfo* classinfo_table; | |
139 _d_getLanguageSpecificTables(context, callsite_table, action_table, classinfo_table); | |
140 | |
141 | |
142 /* | |
143 find landing pad and action table index belonging to ip by walking | |
144 the callsite_table | |
145 */ | |
146 ubyte* callsite_walker = callsite_table; | |
147 | |
148 // get the instruction pointer | |
149 // will be used to find the right entry in the callsite_table | |
150 // -1 because it will point past the last instruction | |
151 ulong ip = _Unwind_GetIP(context) - 1; | |
152 | |
153 // address block_start is relative to | |
154 ulong region_start = _Unwind_GetRegionStart(context); | |
155 | |
156 // table entries | |
157 uint block_start_offset, block_size; | |
158 ulong landing_pad; | |
159 size_t action_offset; | |
160 | |
161 while(true) { | |
162 // if we've gone through the list and found nothing... | |
163 if(callsite_walker >= action_table) | |
164 return _Unwind_Reason_Code.CONTINUE_UNWIND; | |
165 | |
166 block_start_offset = *cast(uint*)callsite_walker; | |
167 block_size = *(cast(uint*)callsite_walker + 1); | |
168 landing_pad = *(cast(uint*)callsite_walker + 2); | |
169 if(landing_pad) | |
170 landing_pad += region_start; | |
171 callsite_walker = get_uleb128(callsite_walker + 3*uint.sizeof, action_offset); | |
172 | |
173 debug(EH_personality_verbose) printf("%d %d %d\n", block_start_offset, block_size, landing_pad); | |
174 | |
175 // since the list is sorted, as soon as we're past the ip | |
176 // there's no handler to be found | |
177 if(ip < region_start + block_start_offset) | |
178 return _Unwind_Reason_Code.CONTINUE_UNWIND; | |
179 | |
180 // if we've found our block, exit | |
181 if(ip < region_start + block_start_offset + block_size) | |
182 break; | |
183 } | |
184 | |
185 debug(EH_personality) printf("Found correct landing pad and actionOffset %d\n", action_offset); | |
186 | |
187 // now we need the exception's classinfo to find a handler | |
188 // the exceptionObject is actually a member of a larger struct that | |
189 // the runtime allocated. get that now | |
190 _d_exception* exception_struct = cast(_d_exception*)(cast(ubyte*)exception_info - _d_exception.unwind_info.offsetof); | |
191 | |
192 // if there's no actionOffset and no landingpad, continue unwinding | |
193 if(!action_offset && !landing_pad) | |
194 return _Unwind_Reason_Code.CONTINUE_UNWIND; | |
195 | |
196 // if there's no action offset but a landing pad, this is a cleanup handler | |
197 else if(!action_offset && landing_pad) { | |
198 // but only if we're asked to! | |
199 if(!(actions & _Unwind_Action.CLEANUP_PHASE)) | |
200 return _Unwind_Reason_Code.CONTINUE_UNWIND; | |
201 | |
202 debug(EH_personality) printf("Calling cleanup routine...\n"); | |
203 | |
204 _Unwind_SetGR(context, 0, cast(ulong)exception_struct); | |
205 _Unwind_SetIP(context, landing_pad); | |
206 return _Unwind_Reason_Code.INSTALL_CONTEXT; | |
207 } | |
208 | |
209 /* | |
210 walk action table chain, comparing classinfos using _d_isbaseof | |
211 */ | |
212 ubyte* action_walker = action_table + action_offset - 1; | |
213 | |
214 ptrdiff_t ti_offset, next_action_offset; | |
215 while(true) { | |
216 action_walker = get_sleb128(action_walker, ti_offset); | |
217 // it is intentional that we not modify action_walker here | |
218 // next_action_offset is from current action_walker position | |
219 get_sleb128(action_walker, next_action_offset); | |
220 | |
221 // negative are 'filters' which we don't use | |
222 assert(ti_offset >= 0); | |
223 | |
224 //TODO: Implement cleanups | |
225 assert(ti_offset != 0); | |
226 | |
227 // get classinfo for action and check if the one in the | |
228 // exception structure is a base | |
229 ClassInfo catch_ci = classinfo_table[-ti_offset]; | |
230 debug(EH_personality) printf("Comparing catch %s to exception %s\n", catch_ci.name.ptr, exception_struct.exception_object.classinfo.name.ptr); | |
231 if(_d_isbaseof(exception_struct.exception_object.classinfo, catch_ci)) | |
232 return _d_eh_success(actions, ti_offset, landing_pad, exception_struct, context); | |
233 | |
234 // we've walked through all actions and found nothing... | |
235 if(next_action_offset == 0) | |
236 return _Unwind_Reason_Code.CONTINUE_UNWIND; | |
237 else | |
238 action_walker += next_action_offset; | |
239 } | |
240 | |
241 assert(false); | |
242 } | |
243 | |
244 private _Unwind_Reason_Code _d_eh_success(_Unwind_Action actions, ptrdiff_t switchval, ulong landing_pad, _d_exception* exception_struct, _Unwind_Context_Ptr context) | |
245 { | |
246 debug(EH_personality) printf("Found catch clause!\n"); | |
247 | |
248 if(actions & _Unwind_Action.SEARCH_PHASE) | |
249 return _Unwind_Reason_Code.HANDLER_FOUND; | |
250 else if(actions & _Unwind_Action.HANDLER_PHASE) | |
251 { | |
252 //TODO: Set sensible value for eh_ptr | |
253 _Unwind_SetGR(context, 0, cast(ulong)cast(void*)(exception_struct.exception_object)); | |
254 _Unwind_SetGR(context, 2, switchval); | |
255 _Unwind_SetIP(context, landing_pad); | |
256 return _Unwind_Reason_Code.INSTALL_CONTEXT; | |
257 } | |
258 | |
259 assert(false); | |
260 } | |
261 | |
262 private void _d_getLanguageSpecificTables(_Unwind_Context_Ptr context, ref ubyte* callsite, ref ubyte* action, ref ClassInfo* ci) | |
263 { | |
264 ubyte* data = cast(ubyte*)_Unwind_GetLanguageSpecificData(context); | |
265 | |
266 //TODO: Do proper DWARF reading here | |
267 assert(*data++ == 0xff); | |
268 | |
269 assert(*data++ == 0x00); | |
270 size_t cioffset; | |
271 data = get_uleb128(data, cioffset); | |
272 ci = cast(ClassInfo*)(data + cioffset); | |
273 | |
274 assert(*data++ == 0x03); | |
275 size_t callsitelength; | |
276 data = get_uleb128(data, callsitelength); | |
277 action = data + callsitelength; | |
278 | |
279 callsite = data; | |
280 } | |
8 | 281 |
9 extern(C) void _d_throw_exception(Object e) | 282 extern(C) void _d_throw_exception(Object e) |
10 { | 283 { |
11 console("Exception: "); | |
12 if (e !is null) | 284 if (e !is null) |
13 { | 285 { |
14 console(e.toString())("\n"); | 286 _d_exception* exc_struct = new _d_exception; |
287 exc_struct.unwind_info.exception_class[] = _d_exception_class; | |
288 exc_struct.exception_object = e; | |
289 _Unwind_Reason_Code ret = _Unwind_RaiseException(&exc_struct.unwind_info); | |
290 console("_Unwind_RaiseException failed with reason code: ")(ret)("\n"); | |
15 } | 291 } |
16 abort(); | 292 abort(); |
17 } | 293 } |
294 | |
295 extern(C) void _d_eh_resume_unwind(_d_exception* exception_struct) | |
296 { | |
297 _Unwind_Resume(&exception_struct.unwind_info); | |
298 } |