Live data from Hacker News

C++ Exceptions: Under the Hood (2013)

monkeywritescode.blogspot.com

11–20 of 116 posts

Re: C++ Exceptions: Under the Hood (2013)

#11

[2013]ish if I remember. Also note the ABI for C++ exceptions followed by G++ et al is actually documented as part of the Itanium C++ ABI : https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html

The doc is actually incomplete, it refers to implementation defined ABI entry points for the actual unwinding. The actual unwind tables and compensation opcodes are, IIRC, part of the DWARF standard, but last time I looked at it that standard was also incomplete and left a lot unspecified. Many of the details (including bugs that have now become part of the ABI) are basically folklore. IIRC Ian LAnce Taylor had a ser…

There's essentially three separate moving pieces here.

At the bottom layer you have the unwind API. This is actually generally defined by the platform-specific ABI, although the definition here on most Unixes is "we have a .eh_frame section that's basically the same as .debug_frame in DWARF." The API is provided by some platform library (libunwind), and it's language-agnostic.

Part of the generic unwind structure is that each stack frame has a personality function and a Language-Specific Data Area, and the unwind semantics will call the personality function to figure out whether to drop off into the stack frame, and where in the function to drop off, or continue unwinding the stack. The personality function itself uses the LSDA to figure out what to do. The personality function lives in the language standard library (e.g., libstdc++), and the LSDA format is of course entirely undocumented (i.e., read Ian Lance Taylor's blog posts as the best documentation that exists).

The final level of the ABI is how you actually represent the exceptions themselves. This is provided by the Itanium ABI and describes the names of the functions needed to effect exception handling (e.g., __cxa_begin_catch), the structure layouts involved, and even some nominal data on how to handle foreign exceptions which don't really exist.

And that's not entirely true for all systems. ARM notably uses a different ABI exception handling, which is rather close to the standard Itanium exception handling except the details are different. Some operating systems choose to forgo unwinding-based exceptions in lieu of setjmp/longjmp as the standard ABI, which of course requires different versions of everything. And Windows has an entirely different form of exception handling which isn't centered around unwinding but actually calling exception handlers as new functions on the stack, requiring frame links to the original-would-be-unwound-to function.

Re: C++ Exceptions: Under the Hood (2013)

#13
Implementation in Windows is quite different. Especially x86 vs x86-64 which has much less overhead. Also gets quite complicated with the Windows built-in Structured Exceptions/ asynchronous exceptions which can be translated into c++ exceptions -- and even more complexity due to different compiler options that handle these!

Re: C++ Exceptions: Under the Hood (2013)

#14
Working with and implementing C++ exceptions for 30 years now, including implementing exception handling for Windows, DOS extenders, and Posix (all very different), and then re-implementing them for D, I have sadly come to the conclusion that exceptions are a giant mistake.

1. they are very hard to understand all the way down

2. they are largely undocumented in how they're implemented

3. they are slow when thrown

4. they are slow when not thrown

5. it is hard to write exception-safe code

6. very few understand how to write exception-safe code

7. there is no such thing as zero-cost exception handling

8. optimizers just give up trying to do flow analysis in try-exception blocks

9. consider double-fault exceptions - there's something that shows how rotten it is

10. has anyone yet found a legitimate use for throwing an `int`?

I have quit using exceptions in my own code, making everything 'nothrow'. I regret propagating exception handling into D. Constructors that may throw are an abomination. Destructors that throw are even worse.

Re: C++ Exceptions: Under the Hood (2013)

#15

Working with and implementing C++ exceptions for 30 years now, including implementing exception handling for Windows, DOS extenders, and Posix (all very different), and then re-implementing them for D, I have sadly come to the conclusion that exceptions are a giant mistake. 1. they are very hard to understand all the way down 2. they are largely undocumented in how they're implemented 3. they are slow when thrown 4.…

What do you think of languages that use sum types for error handling, but can still unwind in a few scenarios?

Reasonnable compromise, or should we get rid of all unwinding always? And if so, do we abort() or do we ask users to handle any and all possible errors.

Re: C++ Exceptions: Under the Hood (2013)

#16

Working with and implementing C++ exceptions for 30 years now, including implementing exception handling for Windows, DOS extenders, and Posix (all very different), and then re-implementing them for D, I have sadly come to the conclusion that exceptions are a giant mistake. 1. they are very hard to understand all the way down 2. they are largely undocumented in how they're implemented 3. they are slow when thrown 4.…

> they are slow when not thrown

I thought the not-thrown case was pretty much zero-cost, at least in newer versions of Clang... How much of a slow down are we talking here?

Re: C++ Exceptions: Under the Hood (2013)

#17

Working with and implementing C++ exceptions for 30 years now, including implementing exception handling for Windows, DOS extenders, and Posix (all very different), and then re-implementing them for D, I have sadly come to the conclusion that exceptions are a giant mistake. 1. they are very hard to understand all the way down 2. they are largely undocumented in how they're implemented 3. they are slow when thrown 4.…

> they are slow when not thrown I thought the not-thrown case was pretty much zero-cost, at least in newer versions of Clang... How much of a slow down are we talking here?

The main reason is the optimizer abandons trying to figure out flow-of-control when half the expressions can throw and present a path to the catch blocks. Furthermore, this all inhibits en-registering variables, because exception unwinding doesn't restore registers.

If you want your code to be fast, use 'nothrow' everywhere.

I don't know about newer versions of Clang, but I recall Chandler Carruth mentioning that LLVM abandons much optimization across EH blocks as infeasible.

Re: C++ Exceptions: Under the Hood (2013)

#18
post #15

Working with and implementing C++ exceptions for 30 years now, including implementing exception handling for Windows, DOS extenders, and Posix (all very different), and then re-implementing them for D, I have sadly come to the conclusion that exceptions are a giant mistake. 1. they are very hard to understand all the way down 2. they are largely undocumented in how they're implemented 3. they are slow when thrown 4.…

What do you think of languages that use sum types for error handling, but can still unwind in a few scenarios? Reasonnable compromise, or should we get rid of all unwinding always? And if so, do we abort() or do we ask users to handle any and all possible errors.

I've read the proposals for it. It certainly looks good, yet exception handling looked good 30 years ago, too. I haven't used sum types myself, and often it takes years to discern whether things are really good ideas or not.

What I personally use is the "poisoning" technique. This involves marking an object as being in an error state, much like a floating point value can be in a NaN state. Any operation on a poisoned object produces another poisoned object, until eventually this is dealt with at some point in the program.

I've had satisfactory success with this technique. It does have a lot of parallels with the sum type method.

Re: C++ Exceptions: Under the Hood (2013)

#19

Working with and implementing C++ exceptions for 30 years now, including implementing exception handling for Windows, DOS extenders, and Posix (all very different), and then re-implementing them for D, I have sadly come to the conclusion that exceptions are a giant mistake. 1. they are very hard to understand all the way down 2. they are largely undocumented in how they're implemented 3. they are slow when thrown 4.…

At least with D a thrown exception must be a subtype of `Throwable`. I.e. you can't throw an `int`, oh, and the incredibly confusing throwing of an object that itself can throw.

Re: C++ Exceptions: Under the Hood (2013)

#20

Working with and implementing C++ exceptions for 30 years now, including implementing exception handling for Windows, DOS extenders, and Posix (all very different), and then re-implementing them for D, I have sadly come to the conclusion that exceptions are a giant mistake. 1. they are very hard to understand all the way down 2. they are largely undocumented in how they're implemented 3. they are slow when thrown 4.…

P.S. I implemented Structured Exception Handling for Win32, with help from a couple very smart people. Microsoft completely changed it for Win64, and the documentation on it is completely unhelpful to nonexistent.[1] I simply gave up on it. D on Win64 uses the exception handling mechanism I invented for the 32 bit DOS extender from the old Zortech days. It works fine, except that it cannot interact with C++ exceptions thrown by VC++ code.

[1] I attended a presentation on it by MS soon after Win64 came out. All I could think of at the end was "what's a cubit". I understood exactly nothing about it.

Post reply on HN