[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