xref: /aosp_15_r20/external/llvm/docs/HistoricalNotes/2001-05-18-ExceptionHandling.txt (revision 9880d6810fe72a1726cb53787c6711e909410d58)
1*9880d681SAndroid Build Coastguard WorkerMeeting notes: Implementation idea: Exception Handling in C++/Java
2*9880d681SAndroid Build Coastguard Worker
3*9880d681SAndroid Build Coastguard WorkerThe 5/18/01 meeting discussed ideas for implementing exceptions in LLVM.
4*9880d681SAndroid Build Coastguard WorkerWe decided that the best solution requires a set of library calls provided by
5*9880d681SAndroid Build Coastguard Workerthe VM, as well as an extension to the LLVM function invocation syntax.
6*9880d681SAndroid Build Coastguard Worker
7*9880d681SAndroid Build Coastguard WorkerThe LLVM function invocation instruction previously looks like this (ignoring
8*9880d681SAndroid Build Coastguard Workertypes):
9*9880d681SAndroid Build Coastguard Worker
10*9880d681SAndroid Build Coastguard Worker  call func(arg1, arg2, arg3)
11*9880d681SAndroid Build Coastguard Worker
12*9880d681SAndroid Build Coastguard WorkerThe extension discussed today adds an optional "with" clause that
13*9880d681SAndroid Build Coastguard Workerassociates a label with the call site.  The new syntax looks like this:
14*9880d681SAndroid Build Coastguard Worker
15*9880d681SAndroid Build Coastguard Worker  call func(arg1, arg2, arg3) with funcCleanup
16*9880d681SAndroid Build Coastguard Worker
17*9880d681SAndroid Build Coastguard WorkerThis funcHandler always stays tightly associated with the call site (being
18*9880d681SAndroid Build Coastguard Workerencoded directly into the call opcode itself), and should be used whenever
19*9880d681SAndroid Build Coastguard Workerthere is cleanup work that needs to be done for the current function if
20*9880d681SAndroid Build Coastguard Workeran exception is thrown by func (or if we are in a try block).
21*9880d681SAndroid Build Coastguard Worker
22*9880d681SAndroid Build Coastguard WorkerTo support this, the VM/Runtime provide the following simple library
23*9880d681SAndroid Build Coastguard Workerfunctions (all syntax in this document is very abstract):
24*9880d681SAndroid Build Coastguard Worker
25*9880d681SAndroid Build Coastguard Workertypedef struct { something } %frame;
26*9880d681SAndroid Build Coastguard Worker  The VM must export a "frame type", that is an opaque structure used to
27*9880d681SAndroid Build Coastguard Worker  implement different types of stack walking that may be used by various
28*9880d681SAndroid Build Coastguard Worker  language runtime libraries. We imagine that it would be typical to
29*9880d681SAndroid Build Coastguard Worker  represent a frame with a PC and frame pointer pair, although that is not
30*9880d681SAndroid Build Coastguard Worker  required.
31*9880d681SAndroid Build Coastguard Worker
32*9880d681SAndroid Build Coastguard Worker%frame getStackCurrentFrame();
33*9880d681SAndroid Build Coastguard Worker  Get a frame object for the current function.  Note that if the current
34*9880d681SAndroid Build Coastguard Worker  function was inlined into its caller, the "current" frame will belong to
35*9880d681SAndroid Build Coastguard Worker  the "caller".
36*9880d681SAndroid Build Coastguard Worker
37*9880d681SAndroid Build Coastguard Workerbool isFirstFrame(%frame f);
38*9880d681SAndroid Build Coastguard Worker  Returns true if the specified frame is the top level (first activated) frame
39*9880d681SAndroid Build Coastguard Worker  for this thread.  For the main thread, this corresponds to the main()
40*9880d681SAndroid Build Coastguard Worker  function, for a spawned thread, it corresponds to the thread function.
41*9880d681SAndroid Build Coastguard Worker
42*9880d681SAndroid Build Coastguard Worker%frame getNextFrame(%frame f);
43*9880d681SAndroid Build Coastguard Worker  Return the previous frame on the stack.  This function is undefined if f
44*9880d681SAndroid Build Coastguard Worker  satisfies the predicate isFirstFrame(f).
45*9880d681SAndroid Build Coastguard Worker
46*9880d681SAndroid Build Coastguard WorkerLabel *getFrameLabel(%frame f);
47*9880d681SAndroid Build Coastguard Worker  If a label was associated with f (as discussed below), this function returns
48*9880d681SAndroid Build Coastguard Worker  it.  Otherwise, it returns a null pointer.
49*9880d681SAndroid Build Coastguard Worker
50*9880d681SAndroid Build Coastguard WorkerdoNonLocalBranch(Label *L);
51*9880d681SAndroid Build Coastguard Worker  At this point, it is not clear whether this should be a function or
52*9880d681SAndroid Build Coastguard Worker  intrinsic.  It should probably be an intrinsic in LLVM, but we'll deal with
53*9880d681SAndroid Build Coastguard Worker  this issue later.
54*9880d681SAndroid Build Coastguard Worker
55*9880d681SAndroid Build Coastguard Worker
56*9880d681SAndroid Build Coastguard WorkerHere is a motivating example that illustrates how these facilities could be
57*9880d681SAndroid Build Coastguard Workerused to implement the C++ exception model:
58*9880d681SAndroid Build Coastguard Worker
59*9880d681SAndroid Build Coastguard Workervoid TestFunction(...) {
60*9880d681SAndroid Build Coastguard Worker  A a; B b;
61*9880d681SAndroid Build Coastguard Worker  foo();        // Any function call may throw
62*9880d681SAndroid Build Coastguard Worker  bar();
63*9880d681SAndroid Build Coastguard Worker  C c;
64*9880d681SAndroid Build Coastguard Worker
65*9880d681SAndroid Build Coastguard Worker  try {
66*9880d681SAndroid Build Coastguard Worker    D d;
67*9880d681SAndroid Build Coastguard Worker    baz();
68*9880d681SAndroid Build Coastguard Worker  } catch (int) {
69*9880d681SAndroid Build Coastguard Worker    ...int Stuff...
70*9880d681SAndroid Build Coastguard Worker    // execution continues after the try block: the exception is consumed
71*9880d681SAndroid Build Coastguard Worker  } catch (double) {
72*9880d681SAndroid Build Coastguard Worker    ...double stuff...
73*9880d681SAndroid Build Coastguard Worker   throw;            // Exception is propogated
74*9880d681SAndroid Build Coastguard Worker  }
75*9880d681SAndroid Build Coastguard Worker}
76*9880d681SAndroid Build Coastguard Worker
77*9880d681SAndroid Build Coastguard WorkerThis function would compile to approximately the following code (heavy
78*9880d681SAndroid Build Coastguard Workerpseudo code follows):
79*9880d681SAndroid Build Coastguard Worker
80*9880d681SAndroid Build Coastguard WorkerFunc:
81*9880d681SAndroid Build Coastguard Worker  %a = alloca A
82*9880d681SAndroid Build Coastguard Worker  A::A(%a)        // These ctors & dtors could throw, but we ignore this
83*9880d681SAndroid Build Coastguard Worker  %b = alloca B   // minor detail for this example
84*9880d681SAndroid Build Coastguard Worker  B::B(%b)
85*9880d681SAndroid Build Coastguard Worker
86*9880d681SAndroid Build Coastguard Worker  call foo() with fooCleanup // An exception in foo is propogated to fooCleanup
87*9880d681SAndroid Build Coastguard Worker  call bar() with barCleanup // An exception in bar is propogated to barCleanup
88*9880d681SAndroid Build Coastguard Worker
89*9880d681SAndroid Build Coastguard Worker  %c = alloca C
90*9880d681SAndroid Build Coastguard Worker  C::C(c)
91*9880d681SAndroid Build Coastguard Worker  %d = alloca D
92*9880d681SAndroid Build Coastguard Worker  D::D(d)
93*9880d681SAndroid Build Coastguard Worker  call baz() with bazCleanup // An exception in baz is propogated to bazCleanup
94*9880d681SAndroid Build Coastguard Worker  d->~D();
95*9880d681SAndroid Build Coastguard WorkerEndTry:                   // This label corresponds to the end of the try block
96*9880d681SAndroid Build Coastguard Worker  c->~C()       // These could also throw, these are also ignored
97*9880d681SAndroid Build Coastguard Worker  b->~B()
98*9880d681SAndroid Build Coastguard Worker  a->~A()
99*9880d681SAndroid Build Coastguard Worker  return
100*9880d681SAndroid Build Coastguard Worker
101*9880d681SAndroid Build Coastguard WorkerNote that this is a very straight forward and literal translation: exactly
102*9880d681SAndroid Build Coastguard Workerwhat we want for zero cost (when unused) exception handling.  Especially on
103*9880d681SAndroid Build Coastguard Workerplatforms with many registers (ie, the IA64) setjmp/longjmp style exception
104*9880d681SAndroid Build Coastguard Workerhandling is *very* impractical.  Also, the "with" clauses describe the
105*9880d681SAndroid Build Coastguard Workercontrol flow paths explicitly so that analysis is not adversly effected.
106*9880d681SAndroid Build Coastguard Worker
107*9880d681SAndroid Build Coastguard WorkerThe foo/barCleanup labels are implemented as:
108*9880d681SAndroid Build Coastguard Worker
109*9880d681SAndroid Build Coastguard WorkerTryCleanup:          // Executed if an exception escapes the try block
110*9880d681SAndroid Build Coastguard Worker  c->~C()
111*9880d681SAndroid Build Coastguard WorkerbarCleanup:          // Executed if an exception escapes from bar()
112*9880d681SAndroid Build Coastguard Worker  // fall through
113*9880d681SAndroid Build Coastguard WorkerfooCleanup:          // Executed if an exception escapes from foo()
114*9880d681SAndroid Build Coastguard Worker  b->~B()
115*9880d681SAndroid Build Coastguard Worker  a->~A()
116*9880d681SAndroid Build Coastguard Worker  Exception *E = getThreadLocalException()
117*9880d681SAndroid Build Coastguard Worker  call throw(E)      // Implemented by the C++ runtime, described below
118*9880d681SAndroid Build Coastguard Worker
119*9880d681SAndroid Build Coastguard WorkerWhich does the work one would expect.  getThreadLocalException is a function
120*9880d681SAndroid Build Coastguard Workerimplemented by the C++ support library.  It returns the current exception
121*9880d681SAndroid Build Coastguard Workerobject for the current thread.  Note that we do not attempt to recycle the
122*9880d681SAndroid Build Coastguard Workershutdown code from before, because performance of the mainline code is
123*9880d681SAndroid Build Coastguard Workercritically important.  Also, obviously fooCleanup and barCleanup may be
124*9880d681SAndroid Build Coastguard Workermerged and one of them eliminated.  This just shows how the code generator
125*9880d681SAndroid Build Coastguard Workerwould most likely emit code.
126*9880d681SAndroid Build Coastguard Worker
127*9880d681SAndroid Build Coastguard WorkerThe bazCleanup label is more interesting.  Because the exception may be caught
128*9880d681SAndroid Build Coastguard Workerby the try block, we must dispatch to its handler... but it does not exist
129*9880d681SAndroid Build Coastguard Workeron the call stack (it does not have a VM Call->Label mapping installed), so
130*9880d681SAndroid Build Coastguard Workerwe must dispatch statically with a goto.  The bazHandler thus appears as:
131*9880d681SAndroid Build Coastguard Worker
132*9880d681SAndroid Build Coastguard WorkerbazHandler:
133*9880d681SAndroid Build Coastguard Worker  d->~D();    // destruct D as it goes out of scope when entering catch clauses
134*9880d681SAndroid Build Coastguard Worker  goto TryHandler
135*9880d681SAndroid Build Coastguard Worker
136*9880d681SAndroid Build Coastguard WorkerIn general, TryHandler is not the same as bazHandler, because multiple
137*9880d681SAndroid Build Coastguard Workerfunction calls could be made from the try block.  In this case, trivial
138*9880d681SAndroid Build Coastguard Workeroptimization could merge the two basic blocks.  TryHandler is the code
139*9880d681SAndroid Build Coastguard Workerthat actually determines the type of exception, based on the Exception object
140*9880d681SAndroid Build Coastguard Workeritself.  For this discussion, assume that the exception object contains *at
141*9880d681SAndroid Build Coastguard Workerleast*:
142*9880d681SAndroid Build Coastguard Worker
143*9880d681SAndroid Build Coastguard Worker1. A pointer to the RTTI info for the contained object
144*9880d681SAndroid Build Coastguard Worker2. A pointer to the dtor for the contained object
145*9880d681SAndroid Build Coastguard Worker3. The contained object itself
146*9880d681SAndroid Build Coastguard Worker
147*9880d681SAndroid Build Coastguard WorkerNote that it is necessary to maintain #1 & #2 in the exception object itself
148*9880d681SAndroid Build Coastguard Workerbecause objects without virtual function tables may be thrown (as in this
149*9880d681SAndroid Build Coastguard Workerexample).  Assuming this, TryHandler would look something like this:
150*9880d681SAndroid Build Coastguard Worker
151*9880d681SAndroid Build Coastguard WorkerTryHandler:
152*9880d681SAndroid Build Coastguard Worker  Exception *E = getThreadLocalException();
153*9880d681SAndroid Build Coastguard Worker  switch (E->RTTIType) {
154*9880d681SAndroid Build Coastguard Worker  case IntRTTIInfo:
155*9880d681SAndroid Build Coastguard Worker    ...int Stuff...       // The action to perform from the catch block
156*9880d681SAndroid Build Coastguard Worker    break;
157*9880d681SAndroid Build Coastguard Worker  case DoubleRTTIInfo:
158*9880d681SAndroid Build Coastguard Worker    ...double Stuff...    // The action to perform from the catch block
159*9880d681SAndroid Build Coastguard Worker    goto TryCleanup       // This catch block rethrows the exception
160*9880d681SAndroid Build Coastguard Worker    break;                // Redundant, eliminated by the optimizer
161*9880d681SAndroid Build Coastguard Worker  default:
162*9880d681SAndroid Build Coastguard Worker    goto TryCleanup       // Exception not caught, rethrow
163*9880d681SAndroid Build Coastguard Worker  }
164*9880d681SAndroid Build Coastguard Worker
165*9880d681SAndroid Build Coastguard Worker  // Exception was consumed
166*9880d681SAndroid Build Coastguard Worker  if (E->dtor)
167*9880d681SAndroid Build Coastguard Worker    E->dtor(E->object)    // Invoke the dtor on the object if it exists
168*9880d681SAndroid Build Coastguard Worker  goto EndTry             // Continue mainline code...
169*9880d681SAndroid Build Coastguard Worker
170*9880d681SAndroid Build Coastguard WorkerAnd that is all there is to it.
171*9880d681SAndroid Build Coastguard Worker
172*9880d681SAndroid Build Coastguard WorkerThe throw(E) function would then be implemented like this (which may be
173*9880d681SAndroid Build Coastguard Workerinlined into the caller through standard optimization):
174*9880d681SAndroid Build Coastguard Worker
175*9880d681SAndroid Build Coastguard Workerfunction throw(Exception *E) {
176*9880d681SAndroid Build Coastguard Worker  // Get the start of the stack trace...
177*9880d681SAndroid Build Coastguard Worker  %frame %f = call getStackCurrentFrame()
178*9880d681SAndroid Build Coastguard Worker
179*9880d681SAndroid Build Coastguard Worker  // Get the label information that corresponds to it
180*9880d681SAndroid Build Coastguard Worker  label * %L = call getFrameLabel(%f)
181*9880d681SAndroid Build Coastguard Worker  while (%L == 0 && !isFirstFrame(%f)) {
182*9880d681SAndroid Build Coastguard Worker    // Loop until a cleanup handler is found
183*9880d681SAndroid Build Coastguard Worker    %f = call getNextFrame(%f)
184*9880d681SAndroid Build Coastguard Worker    %L = call getFrameLabel(%f)
185*9880d681SAndroid Build Coastguard Worker  }
186*9880d681SAndroid Build Coastguard Worker
187*9880d681SAndroid Build Coastguard Worker  if (%L != 0) {
188*9880d681SAndroid Build Coastguard Worker    call setThreadLocalException(E)   // Allow handlers access to this...
189*9880d681SAndroid Build Coastguard Worker    call doNonLocalBranch(%L)
190*9880d681SAndroid Build Coastguard Worker  }
191*9880d681SAndroid Build Coastguard Worker  // No handler found!
192*9880d681SAndroid Build Coastguard Worker  call BlowUp()         // Ends up calling the terminate() method in use
193*9880d681SAndroid Build Coastguard Worker}
194*9880d681SAndroid Build Coastguard Worker
195*9880d681SAndroid Build Coastguard WorkerThat's a brief rundown of how C++ exception handling could be implemented in
196*9880d681SAndroid Build Coastguard Workerllvm.  Java would be very similar, except it only uses destructors to unlock
197*9880d681SAndroid Build Coastguard Workersynchronized blocks, not to destroy data.  Also, it uses two stack walks: a
198*9880d681SAndroid Build Coastguard Workernondestructive walk that builds a stack trace, then a destructive walk that
199*9880d681SAndroid Build Coastguard Workerunwinds the stack as shown here.
200*9880d681SAndroid Build Coastguard Worker
201*9880d681SAndroid Build Coastguard WorkerIt would be trivial to get exception interoperability between C++ and Java.
202*9880d681SAndroid Build Coastguard Worker
203