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 }