[LLVMdev] RFC: Exception Handling Proposal Revised
Bill Wendling
wendling at apple.com
Tue Nov 30 23:04:36 PST 2010
This is a revision of the second exception handling proposal I sent out. You can see it here:
http://lists.cs.uiuc.edu/pipermail/llvmdev/2010-November/036484.html
After much discussion, there are some changes to the proposal – some significant and some minor. One major point, this proposal does not address the issue of catching an exception thrown from a non-invoke instruction. However if done properly, it should form the foundation for that.
General Model
=============
The unwind edge from an invoke instruction jumps to a landing pad. That landing pad contains code which performs optional cleanups, and then determines which catch handler to call (if any). If no catch handlers are applicable, the exception resumes propagation either to the next enclosing region or out of the function.
Basic Block Attributes
======================
A basic block can be given an attribute.
General Syntax:
label: attr1 attr2 ... attrn
For exception handling, we introduce the `landingpad' attribute.
lp: landingpad
A basic block that has the `landingpad' attribute is considered a (surprise!) "landing pad".
• It may be branched to only by the unwind edge of an invoke instruction or by
the resume edge of a dispatch instruction.
• A landing pad must have exactly one dispatch instruction associated with it,
and it must dominate that instruction.
• If a landing pad is split, only the section containing the start of the basic
block retains the `landingpad' attribute.
• The critical edges may not be split.
• The attribute may not be removed.
Dispatch Instruction
====================
The dispatch instruction is the work horse of the exception handling design. It replaces the llvm.eh.selector and llvm.eh.typeid.for intrinsics.
Syntax:
dispatch resume to label <resumedest>
catches [
<type> <val>, label <dest1>
...
]
catchall [ <type> <val>, label <dest> ]
personality [<type> <value>]
filters [
<type> <val>, <type> <val>, ...
]
The `catches', `catchall', and `filters' clauses are optional. If neither `catches' nor `catchall' is specified, then the landing pad is implicitly a cleanup.
• The `<resumedest>' basic block is the destination to unwind to if the
exception cannot be handled by this landing pad.
• The `catches' clause is a list of types which the landing pad can handle and
the destinations to jump to for each type.
• The `catchall' clause is the place to jump to if the exception type doesn't
match any of the types in the `catches' clause.
• The `personality' clause indicates the personality function for the landing
pad.
• The `filters' clause lists the types of exceptions which may be thrown by the
region.
Invoke Instruction
==================
The "invoke" instruction will not change. :-)
Examples
========
Consider this program:
$ cat t.cpp
#include <cstdio>
extern void foo();
struct A { ~A(); };
struct B { ~B(); };
struct C { ~C(); };
void bar() {
try {
foo();
A a;
foo();
B b;
foo();
C c;
foo();
} catch (int i) {
printf("caught int: %d\n", i);
} catch (const char *c) {
printf("caught string: %s\n", c);
} catch (...) {
printf("catchall\n");
}
}
Here's a simplified example of what code would be generated for this (there may be subtle errors...it's handcrafted):
$ llvm-g++ -o - -emit-llvm -S t.cpp -Os
@.str = private constant [16 x i8] c"caught int: %d\0A\00", align 1
@.str1 = private constant [19 x i8] c"caught string: %s\0A\00", align 1
@.str2 = private constant [9 x i8] c"catchall\00", align 1
@_ZTIi = external constant %struct.__fundamental_type_info_pseudo
@_ZTIPKc = external constant %struct.__pointer_type_info_pseudo
define void @_Z3barv() optsize ssp {
entry:
%a = alloca %struct.A, align 8
%b = alloca %struct.B, align 8
%c = alloca %struct.C, align 8
invoke void @_Z3foov()
to label %invcont unwind label %catch.handlers
invcont:
invoke void @_Z3foov()
to label %invcont1 unwind label %a.dtor
invcont1:
invoke void @_Z3foov()
to label %invcont2 unwind label %b.dtor
invcont2:
invoke void @_Z3foov()
to label %bb1 unwind label %c.dtor
bb1:
invoke void @_ZN1CD1Ev(%struct.C* %c)
to label %bb2 unwind label %b.dtor
bb2:
invoke void @_ZN1BD1Ev(%struct.B* %b)
to label %bb3 unwind label %a.dtor
bb3:
invoke void @_ZN1AD1Ev(%struct.A* %a)
to label %return unwind label %catch.handlers
return:
ret void
;; Catch Handlers
catch.handlers: landingpad
%eh_ptr = call i8* @llvm.eh.exception()
dispatch resume to label %...
catches [
%struct.__fundamental_type_info_pseudo* @_ZTIi, label %ch.int
%struct.__pointer_type_info_pseudo* @_ZTIPKc, label %ch.str
]
catchall [i8* null, label % ch.ca]
ch.int:
%t0 = call i8* @__cxa_begin_catch(i8* %eh_ptr) nounwind
%t1 = bitcast i8* %t0 to i32*
%t2 = load i32* %t1, align 4
%t3 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([16 x i8]* @.str, i64 0, i64 0), i32 %t2)
call void @__cxa_end_catch()
ret void
ch.string:
%t4 = call i8* @__cxa_begin_catch(i8* %eh_ptr) nounwind
%t5 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([19 x i8]* @.str1, i64 0, i64 0), i8* %t4)
call void @__cxa_end_catch()
ret void
ch.ca:
%t6 = call i8* @__cxa_begin_catch(i8* %eh_ptr) nounwind
%t7 = call i32 @puts(i8* getelementptr inbounds ([9 x i8]* @.str2, i64 0, i64 0))
call void @__cxa_end_catch()
ret void
;; C's d'tor
c.dtor:
invoke void @_ZN1CD1Ev(%struct.C* %c)
to label %b.dtor unwind label %lpad.c.dtor
;; B's d'tor
b.dtor:
invoke void @_ZN1BD1Ev(%struct.B* %b)
to label %a.dtor unwind label %lpad.b.dtor
;; A's d'tor
a.dtor: landingpad
invoke void @_ZN1AD1Ev(%struct.A* %a)
to label %onto.catch.handlers unwind label %lpad.a.dtor
onto.catch.handlers:
dispatch resume to %lpad
;; Desperate times...
lpad.c.dtor: landingpad
dispatch resume to %yikes
lpad.b.dtor: landingpad
dispatch resume to %yikes
lpad.a.dtor: landingpad
dispatch resume to %yikes
yikes:
call void @_ZSt9terminatev() noreturn nounwind
unreachable
}
Because of the invariant that a landing pad must dominate a dispatch and because a landing pad may be branched to only by an invoke and dispatch instructions, the CFG now has enough information in it to collect the information needed to create the exception handling tables.
Well? What do you think? Pretty cool, eh? :-)
-bw
More information about the llvm-dev
mailing list