digitalmars.D - Dwarf Exception Handling question
- Walter Bright (64/64) Nov 23 2015 I'm struggling to understand dwarf EH, and figure it's a good idea to tr...
- Iain Buclaw via Digitalmars-d (15/79) Nov 23 2015 Yes, the _d_dynamic_cast is redundant. This happened because the EH
- Walter Bright (7/19) Nov 23 2015 Thanks, this helps a lot, and makes me a lot more comfortable with the n...
- Iain Buclaw via Digitalmars-d (11/40) Nov 23 2015 Actively? No. First comes 2.067, which will have to be updated *as is*
- Walter Bright (5/12) Nov 23 2015 It's the one I installed recently on Ubuntu with:
- Jacob Carlborg (5/7) Nov 23 2015 Is the idea to replace the existing exception handling mechanism for D
- deadalnix (3/9) Nov 24 2015 Walter is moving DMD to use libunwind.
- David Nadlinger (50/56) Nov 23 2015 This is indeed the case, but of course you need to know in which
- Iain Buclaw via Digitalmars-d (3/12) Nov 23 2015 We seem to be doing very similar things here.
- Walter Bright (7/42) Nov 23 2015 The code looks quite good. I've been trying to adjust things, however, s...
- deadalnix (3/7) Nov 23 2015 Wouldn't that makes unwinding slower because one need to go
- Walter Bright (2/8) Nov 23 2015 Yes. But if you're choked by unwinding speed, you're doing it wrong.
- deadalnix (2/17) Nov 23 2015 I don't think this is a good reason for making it twice as slow :)
- David Nadlinger (10/16) Nov 23 2015 Wouldn't you still need to restore the stack before leaving the
- Walter Bright (4/17) Nov 23 2015 I don't know why that would be true. gdc generates multiple landing pads...
I'm struggling to understand dwarf EH, and figure it's a good idea to try and make it binary compatible with what gdc does, or at least not gratuitously different. If I use gdc to compile this: void foo1() { abc(); try { def(); } catch(DD t) { ghi(t); } catch(CC t) { ghi(t); } catch { mno(); } jkl(); } the code generated looks like this: _D3eh54foo1FZv: push RBP mov RBP,RSP sub RSP,010h call abc call def L12: call jkl jmp short L86 cmp RDX,2 je L59 cmp RDX,3 je L7F cmp RDX,1 je L33 mov RDI,RAX call _Unwind_Resume L33: sub RAX,8 mov RAX,[RAX] mov ESI,offset _D3eh52DD7__ClassZ mov RDI,RAX call _d_dynamic_cast mov -8[RBP],RAX mov RAX,-8[RBP] mov RDI,RAX call ghi jmp short L12 L59: sub RAX,8 mov RAX,[RAX] mov ESI,offset _D3eh52CC7__ClassZ mov RDI,RAX call _d_dynamic_cast mov -010h[RBP],RAX mov RAX,-010h[RBP] mov RDI,RAX call ghi jmp short L12 L7F: call mno jmp short L12 L86: leave ret The calls to _d_dynamic cast appear to be redundant - shouldn't the value in RDX be sufficient? And why is there a 'default' call to _Unwind_Resume if RDX isn't an expected value? Shouldn't the personality routine simply not jump to the landing pad if none of the catch types are satisfied?
Nov 23 2015
On 23 November 2015 at 18:31, Walter Bright via Digitalmars-d < digitalmars-d puremagic.com> wrote:I'm struggling to understand dwarf EH, and figure it's a good idea to try and make it binary compatible with what gdc does, or at least not gratuitously different. If I use gdc to compile this: void foo1() { abc(); try { def(); } catch(DD t) { ghi(t); } catch(CC t) { ghi(t); } catch { mno(); } jkl(); } the code generated looks like this: _D3eh54foo1FZv: push RBP mov RBP,RSP sub RSP,010h call abc call def L12: call jkl jmp short L86 cmp RDX,2 je L59 cmp RDX,3 je L7F cmp RDX,1 je L33 mov RDI,RAX call _Unwind_Resume L33: sub RAX,8 mov RAX,[RAX] mov ESI,offset _D3eh52DD7__ClassZ mov RDI,RAX call _d_dynamic_cast mov -8[RBP],RAX mov RAX,-8[RBP] mov RDI,RAX call ghi jmp short L12 L59: sub RAX,8 mov RAX,[RAX] mov ESI,offset _D3eh52CC7__ClassZ mov RDI,RAX call _d_dynamic_cast mov -010h[RBP],RAX mov RAX,-010h[RBP] mov RDI,RAX call ghi jmp short L12 L7F: call mno jmp short L12 L86: leave ret The calls to _d_dynamic cast appear to be redundant - shouldn't the value in RDX be sufficient? And why is there a 'default' call to _Unwind_Resume if RDX isn't an expected value? Shouldn't the personality routine simply not jump to the landing pad if none of the catch types are satisfied?Yes, the _d_dynamic_cast is redundant. This happened because the EH pointer was treated as an Object, then upcasted to the catch type via the normal convert() routines. This is what caused the unnecessary call to _d_dynamic_cast. This switch statement is generated by GCC itself, and it tries to be accommodating for all supported languages. I imagine that the default case is there to support `catch(...)` in C++ code, however D has no notion of this construct, so what is instead generated is _Unwind_Resume to tell unwind to keep looking up the call chain. In other words, the default case should never really happen in D code except in the event of a logic bug in the unwind EH personality function (or possibly corruption). If you feel it more appropriate, I don't see the harm in replacing it with whatever HLT or abort instruction you like. :-)
Nov 23 2015
On 11/23/2015 10:07 AM, Iain Buclaw via Digitalmars-d wrote:Yes, the _d_dynamic_cast is redundant. This happened because the EH pointer was treated as an Object, then upcasted to the catch type via the normal convert() routines. This is what caused the unnecessary call to _d_dynamic_cast. This switch statement is generated by GCC itself, and it tries to be accommodating for all supported languages. I imagine that the default case is there to support `catch(...)` in C++ code, however D has no notion of this construct, so what is instead generated is _Unwind_Resume to tell unwind to keep looking up the call chain. In other words, the default case should never really happen in D code except in the event of a logic bug in the unwind EH personality function (or possibly corruption). If you feel it more appropriate, I don't see the harm in replacing it with whatever HLT or abort instruction you like. :-)Thanks, this helps a lot, and makes me a lot more comfortable with the notion that I understand what is going on. I won't generate the cast, then, and I'll use a HLT for the default. BTW, are you working on supporting catching std::exception ? My eevil plan is to get D exceptions working completely before trying to support std::exception.
Nov 23 2015
On 23 November 2015 at 19:18, Walter Bright via Digitalmars-d < digitalmars-d puremagic.com> wrote:On 11/23/2015 10:07 AM, Iain Buclaw via Digitalmars-d wrote:Actively? No. First comes 2.067, which will have to be updated *as is* without the visitor conversions. I will need to adjust *my* personality function to be more graceful for handling foreign exceptions. After that, I can look into C++ typeinfo mangling. It's just normal C++ mangling with a _ZT prefix, no? :-) Also, I notice that you are using an older version of GDC. That version you're using doesn't support exception chaining. To get that working I had to invent a callback __gdc_begin_catch() which cleans up the chained exceptions list.Yes, the _d_dynamic_cast is redundant. This happened because the EH pointer was treated as an Object, then upcasted to the catch type via the normal convert() routines. This is what caused the unnecessary call to _d_dynamic_cast. This switch statement is generated by GCC itself, and it tries to be accommodating for all supported languages. I imagine that the default case is there to support `catch(...)` in C++ code, however D has no notion of this construct, so what is instead generated is _Unwind_Resume to tell unwind to keep looking up the call chain. In other words, the default case should never really happen in D code except in the event of a logic bug in the unwind EH personality function (or possibly corruption). If you feel it more appropriate, I don't see the harm in replacing it with whatever HLT or abort instruction you like. :-)Thanks, this helps a lot, and makes me a lot more comfortable with the notion that I understand what is going on. I won't generate the cast, then, and I'll use a HLT for the default. BTW, are you working on supporting catching std::exception ? My eevil plan is to get D exceptions working completely before trying to support std::exception.
Nov 23 2015
On 11/23/2015 10:31 AM, Iain Buclaw via Digitalmars-d wrote:I will need to adjust *my* personality function to be more graceful for handling foreign exceptions. After that, I can look into C++ typeinfo mangling. It's just normal C++ mangling with a _ZT prefix, no? :-)I think so, but I haven't looked.Also, I notice that you are using an older version of GDC.It's the one I installed recently on Ubuntu with: sudo apt-get install gdcThat version you're using doesn't support exception chaining. To get that working I had to invent a callback __gdc_begin_catch() which cleans up the chained exceptions list.Could you please invent it as Boost licensed code, so I can simply incorporate it?
Nov 23 2015
On 2015-11-23 19:18, Walter Bright wrote:My eevil plan is to get D exceptions working completely before trying to support std::exception.Is the idea to replace the existing exception handling mechanism for D code that don't interact with C++ as well? -- /Jacob Carlborg
Nov 23 2015
On Tuesday, 24 November 2015 at 07:52:13 UTC, Jacob Carlborg wrote:On 2015-11-23 19:18, Walter Bright wrote:Walter is moving DMD to use libunwind.My eevil plan is to get D exceptions working completely before trying to support std::exception.Is the idea to replace the existing exception handling mechanism for D code that don't interact with C++ as well?
Nov 24 2015
On Monday, 23 November 2015 at 17:31:51 UTC, Walter Bright wrote:The calls to _d_dynamic cast appear to be redundant - shouldn't the value in RDX be sufficient?This is indeed the case, but of course you need to know in which order the compiler backend wrote the type info entries to the tables. Maybe at the time there didn't exist an intrinsic for that in GDC or something like that. This is how the function looks in LDC (on OS X, but the libunwind "client"-side code is very similar): --- 0000000100000a40 <__D5test24foo1FZv>: 100000a40: 53 push rbx 100000a41: e8 ca fe ff ff call 100000910 <__D4test3abcFZv> 100000a46: e8 d5 fe ff ff call 100000920 <__D4test3defFZv> 100000a4b: 5b pop rbx 100000a4c: e9 ef fe ff ff jmp 100000940 <__D4test3jklFZv> 100000a51: 48 89 c3 mov rbx,rax 100000a54: 83 fa 03 cmp edx,0x3 100000a57: 74 05 je 100000a5e <__D5test24foo1FZv+0x1e> 100000a59: 83 fa 02 cmp edx,0x2 100000a5c: 75 0f jne 100000a6d <__D5test24foo1FZv+0x2d> 100000a5e: e8 5d ff 00 00 call 1000109c0 <__d_eh_enter_catch> 100000a63: 48 8b 3b mov rdi,QWORD PTR [rbx] 100000a66: e8 c5 fe ff ff call 100000930 <__D4test3ghiFC4test2CCZv> 100000a6b: eb de jmp 100000a4b <__D5test24foo1FZv+0xb> 100000a6d: 83 fa 01 cmp edx,0x1 100000a70: 75 10 jne 100000a82 <__D5test24foo1FZv+0x42> 100000a72: e8 49 ff 00 00 call 1000109c0 <__d_eh_enter_catch> 100000a77: e8 d4 fe ff ff call 100000950 <__D4test3mnoFZv> 100000a7c: 5b pop rbx 100000a7d: e9 be fe ff ff jmp 100000940 <__D4test3jklFZv> 100000a82: 48 89 df mov rdi,rbx 100000a85: e8 16 ff 00 00 call 1000109a0 <__d_eh_resume_unwind> ---And why is there a 'default' call to _Unwind_Resume if RDX isn't an expected value? Shouldn't the personality routine simply not jump to the landing pad if none of the catch types are satisfied?The reason LDC emits the __d_eh_resume_unwind call all the time is because it is needed as soon as there is a cleanup involved anyway, and so far I was just too lazy to optimize the extra branch away in the special case that there are no cleanups. — David
Nov 23 2015
On 23 November 2015 at 19:32, David Nadlinger via Digitalmars-d < digitalmars-d puremagic.com> wrote:On Monday, 23 November 2015 at 17:31:51 UTC, Walter Bright wrote:We seem to be doing very similar things here.And why is there a 'default' call to _Unwind_Resume if RDX isn't an expected value? Shouldn't the personality routine simply not jump to the landing pad if none of the catch types are satisfied?The reason LDC emits the __d_eh_resume_unwind call all the time is because it is needed as soon as there is a cleanup involved anyway, and so far I was just too lazy to optimize the extra branch away in the special case that there are no cleanups.
Nov 23 2015
On 11/23/2015 10:32 AM, David Nadlinger wrote:This is how the function looks in LDC (on OS X, but the libunwind "client"-side code is very similar): --- 0000000100000a40 <__D5test24foo1FZv>: 100000a40: 53 push rbx 100000a41: e8 ca fe ff ff call 100000910 <__D4test3abcFZv> 100000a46: e8 d5 fe ff ff call 100000920 <__D4test3defFZv> 100000a4b: 5b pop rbx 100000a4c: e9 ef fe ff ff jmp 100000940 <__D4test3jklFZv> 100000a51: 48 89 c3 mov rbx,rax 100000a54: 83 fa 03 cmp edx,0x3 100000a57: 74 05 je 100000a5e <__D5test24foo1FZv+0x1e> 100000a59: 83 fa 02 cmp edx,0x2 100000a5c: 75 0f jne 100000a6d <__D5test24foo1FZv+0x2d> 100000a5e: e8 5d ff 00 00 call 1000109c0 <__d_eh_enter_catch> 100000a63: 48 8b 3b mov rdi,QWORD PTR [rbx] 100000a66: e8 c5 fe ff ff call 100000930 <__D4test3ghiFC4test2CCZv> 100000a6b: eb de jmp 100000a4b <__D5test24foo1FZv+0xb> 100000a6d: 83 fa 01 cmp edx,0x1 100000a70: 75 10 jne 100000a82 <__D5test24foo1FZv+0x42> 100000a72: e8 49 ff 00 00 call 1000109c0 <__d_eh_enter_catch> 100000a77: e8 d4 fe ff ff call 100000950 <__D4test3mnoFZv> 100000a7c: 5b pop rbx 100000a7d: e9 be fe ff ff jmp 100000940 <__D4test3jklFZv> 100000a82: 48 89 df mov rdi,rbx 100000a85: e8 16 ff 00 00 call 1000109a0 <__d_eh_resume_unwind> ---The code looks quite good. I've been trying to adjust things, however, so there are no pushes and pops in the code, trying to preallocate everything needed in the function prolog.dmd rewrites try-catch-finally into try-{try-catch}-finally, which makes it easier to generate code, because fewer special cases and fewer bugs. I've become a big fan of that technique :-)And why is there a 'default' call to _Unwind_Resume if RDX isn't an expected value? Shouldn't the personality routine simply not jump to the landing pad if none of the catch types are satisfied?The reason LDC emits the __d_eh_resume_unwind call all the time is because it is needed as soon as there is a cleanup involved anyway, and so far I was just too lazy to optimize the extra branch away in the special case that there are no cleanups.
Nov 23 2015
On Monday, 23 November 2015 at 21:05:29 UTC, Walter Bright wrote:dmd rewrites try-catch-finally into try-{try-catch}-finally, which makes it easier to generate code, because fewer special cases and fewer bugs. I've become a big fan of that technique :-)Wouldn't that makes unwinding slower because one need to go through several landing pads ?
Nov 23 2015
On 11/23/2015 1:32 PM, deadalnix wrote:On Monday, 23 November 2015 at 21:05:29 UTC, Walter Bright wrote:Yes. But if you're choked by unwinding speed, you're doing it wrong.dmd rewrites try-catch-finally into try-{try-catch}-finally, which makes it easier to generate code, because fewer special cases and fewer bugs. I've become a big fan of that technique :-)Wouldn't that makes unwinding slower because one need to go through several landing pads ?
Nov 23 2015
On Monday, 23 November 2015 at 21:38:39 UTC, Walter Bright wrote:On 11/23/2015 1:32 PM, deadalnix wrote:I don't think this is a good reason for making it twice as slow :)On Monday, 23 November 2015 at 21:05:29 UTC, Walter Bright wrote:Yes. But if you're choked by unwinding speed, you're doing it wrong.dmd rewrites try-catch-finally into try-{try-catch}-finally, which makes it easier to generate code, because fewer special cases and fewer bugs. I've become a big fan of that technique :-)Wouldn't that makes unwinding slower because one need to go through several landing pads ?
Nov 23 2015
On Monday, 23 November 2015 at 21:05:29 UTC, Walter Bright wrote:The code looks quite good. I've been trying to adjust things, however, so there are no pushes and pops in the code, trying to preallocate everything needed in the function prolog.Wouldn't you still need to restore the stack before leaving the function (tail call in this case)? For a single register, push/pop is probably still cheaper than setting up RBP and having the extra mov/sub.dmd rewrites try-catch-finally into try-{try-catch}-finally, which makes it easier to generate code, because fewer special cases and fewer bugs. I've become a big fan of that techniqueExcept that we actually need to flatten all the nesting into a single landing pad anyway. How would you do this in DMD? I didn't realize you could even have multiple EH table entries attached to a single code location. — David
Nov 23 2015
On 11/23/2015 4:36 PM, David Nadlinger wrote:On Monday, 23 November 2015 at 21:05:29 UTC, Walter Bright wrote:Yes.The code looks quite good. I've been trying to adjust things, however, so there are no pushes and pops in the code, trying to preallocate everything needed in the function prolog.Wouldn't you still need to restore the stack before leaving the function (tail call in this case)?I don't know why that would be true. gdc generates multiple landing pads.dmd rewrites try-catch-finally into try-{try-catch}-finally, which makes it easier to generate code, because fewer special cases and fewer bugs. I've become a big fan of that techniqueExcept that we actually need to flatten all the nesting into a single landing pad anyway.How would you do this in DMD? I didn't realize you could even have multiple EH table entries attached to a single code location.You can't. The most deeply nested one gets the table entry.
Nov 23 2015