Defragmenting C++ Making Exceptions and RTTI More Affordable and Usable
Defragmenting C++ Making Exceptions and RTTI More Affordable and Usable
Defragmenting C++ Making Exceptions and RTTI More Affordable and Usable
Herb Sutter
1
Start of CastGuard check
; rcx == The right-hand side object pointer.
; First do the nullptr check. This could be optimized away but is not today.
; N.B. If the static_cast has to adjust the pointer base, this nullptr check
; already exists.
This is a known algorithm,
4885c9 test mentioned at a previous CppCon, just in assembler.
rcx, rcx
7416 raise your hand
Please jewhen you
codegentest!DoCast+0x26
recognize it. You have 15 seconds.
; Now do the range check. Jump to the AppCompat check if the range check fail
492bc0 sub rdx, r8
4883f820 cmp rdx, 20h
7715 ja codegentest!DoCast+0x3b ; Jump to app-compat che
Core principles:
Zero-overhead abstraction — don’t pay for what you don’t use,
what you use is as efficient as you can reasonably write by hand
Determinism & control — over time & space, close to hardware,
leave no room for a lower language, trust the programmer
Link compatibility — with C95 and C++prev
Useful pragmatics for adoption and library consumption:
Backward source compat with C — mostly C95, read-only, consume headers & seamlessly call
Backward source compat with C++prev — C++98 and later, read-mostly, to use & to maintain
What is not core C++:
Specific syntax — we’ve been adding & recommending C-incompatible syntax since 1979
Tedium — most modern abstractions (e.g., range-for, override) compatible with zero-overhead
Lack of good defaults — good defaults are fine, as long as the programmer can override them
Sharp edges — e.g., brittle dangling pointers are not necessary for efficiency and control
3
2
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
Historical
simplify
simplify
A future worth considering?
5
Historical
simplify
simplify
A future worth considering?
6
3
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
We exceptions We RTTI
-fno-exceptions -fno-rtti
#define _HAS_EXCEPTIONS 0 /GR-
We exceptions We RTTI
-fno-exceptions -fno-rtti
#define _HAS_EXCEPTIONS 0 /GR-
4
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
static by default
dynamic by opt-in
10
10
5
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
11
11
12
6
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
“Pity the poor call site that uses A and B” – including all generic code:
template<typename T>
auto some_func(T& t) {
… process(t) … // ??? error handling ???
}
13
13
14
14
7
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
15
15
16
16
8
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
17
17
18
18
9
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
19
19
20
10
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
21
22
22
11
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
23
23
24
24
12
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
Run-time errors (and only those) should be reported to the calling code.
Regardless of mechanism: “Prefer exceptions” but applies to any reporting style.
25
25
26
26
13
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
27
28
28
14
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
29
29
Exceptions are great: Distinct “error” paths, can’t ignore, auto propagation.
But: Inherently not zero-overhead, not deterministic.
“Throwing objects of dynamic types… dynamic allocation + type erasure
… and catching using RTTI.” dynamic casting (special)
Proposal:
“Throwing values of static types… stack allocation, share return channel
… and catching by value.” no dynamic casting, just value comparison
Isomorphic to error codes, identical space/time overhead and predictability.
Share return channel potential for negative overhead abstraction.
If a function agrees (opts in) that any exceptions it emits are values of one statically
known type, we can implement it with zero dynamic/non-local overheads.
30
15
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
31
32
16
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
33
// … // …
// propagate // propagate
// … // …
// catch site: “catch (EBase& e) {/*…*/}” by reference // catch site: “catch (std::error e)” by value
if (auto e = special_dynamic_cast<EBase*>(pvoid); e) if (e.failed) {/*…*/} // no RTTI
{/*…*/}
34
34
17
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
35
35
36
36
18
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
— [Duffy 2016]
37
37
38
38
19
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
39
39
Today:
1. Exceptions must be dynamically allocated.
3. Dynamic allocation failures are reported
using exceptions.
40
40
20
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
Today:
1. Exceptions must be dynamically allocated.
3. Dynamic allocation failures are reported
using exceptions.
41
41
42
42
21
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
43
43
44
44
22
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
45
45
46
23
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
47
47
“One more thing”… Sets the stage for a potential new world:
2+3: Enables “~90% of all functions are noexcept.”
2+3+4: Enables “require try on every expression that can throw.”
Simplification: Enables using C code in C++ projects with confidence.
Can take any C code, compile it as C++, and (automatically) add try on
every expression that could throw feasible to inspect and validate the
code is exception-safe.
48
48
24
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
49
49
50
25
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
51
51
void myfunc(base* a) {
...
if (something) {
auto concrete = static_cast<derived*>(a);
use(concrete);
}
...
}
Q: What do you think of this code?
52
52
26
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
void myfunc(base* b) {
...
if (something) {
auto concrete = static_cast<derived*>(b);
use(concrete);
}
...
}
Q: What do you think of this code?
A: “Wait, that’s an unchecked down-cast!”
53
53
54
27
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
Many “C++” projects ban RTTI Q13: [Is RTTI] allowed in your
in whole or in part. current project? (N=2,058)
One root cause of security
No: Not allowed
vulnerabilities is “didn’t use 18%
dynamic_cast” because:
Partial: Allowed in
“We can’t, RTTI not enabled 14% some parts of the
because it’s too expensive.” code
68% Yes: Allowed
“We can’t, RTTI is enabled but pretty much
dynamic_cast is too expensive.” everywhere
55
55
56
28
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
57
57
58
58
29
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
59
60
60
30
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
61
62
62
31
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
CastGuard, simple SI
; First do the nullptr check. This could be optimized away but is not today.
; N.B. If the static_cast has to adjust the pointer base, this nullptr check
; already exists.
4885c9 test rcx, rcx
7416 je codegentest!DoCast+0x26
; Now do the range check. Jump to the AppCompat check if the range check fails.
492bc0 sub rdx, r8
4883f820 cmp rdx, 20h
7715 ja codegentest!DoCast+0x3b ; Jump to app-compat check
63
; End of CastGuard check
63 ; After the cast, use the object in some way.
; In this case, we call a virtual function as an example.
488b01 mov rax, qword ptr [rcx]
ff10 call qword ptr [rax]
Historical
simplify
simplify
A future worth considering?
64
64
32
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
Historical
simplify
simplify
A future worth considering?
65
65
static by default
dynamic by opt-in
66
66
33
De-fragmenting C++: Making exceptions 9/22/2019
and RTTI more affordable and usable
Herb Sutter
67
Herb Sutter
68
34