Exception Handling by Propagated Return Value¶
This method is a compiler-generated way of implicitly checking each function’s return value. Its main advantage is that it is simple - at the cost of many mostly unproductive checks of return values. The great thing about this method is that it readily interfaces with a host of languages and environments - it is all a matter of returning a pointer to an exception.
The C++ example from the beginning of the section maps to the following code:
;********************* External and Utility functions *********************
declare i8* @malloc(i32) nounwind
declare void @free(i8*) nounwind
declare i32 @printf(i8* noalias nocapture, ...) nounwind
declare i32 @puts(i8* noalias nocapture) nounwind
;***************************** Object class *******************************
%Object_vtable_type = type {
%Object_vtable_type*, ; 0: above: parent class vtable pointer
i8* ; 1: class: class name (usually mangled)
; virtual methods would follow here
}
@.Object_class_name = private constant [7 x i8] c"Object\00"
@.Object_vtable = private constant %Object_vtable_type {
%Object_vtable_type* null, ; This is the root object of the object hierarchy
i8* getelementptr([7 x i8], [7 x i8]* @.Object_class_name, i32 0, i32 0)
}
%Object = type {
%Object_vtable_type* ; 0: vtable: class vtable pointer (always non-null)
; class data members would follow here
}
; returns true if the specified object is identical to or derived from the
; class with the specified name.
define i1 @Object_IsA(%Object* %object, i8* %name) nounwind {
.init:
; if (object == null) return false
%0 = icmp ne %Object* %object, null
br i1 %0, label %.once, label %.exit_false
.once:
%1 = getelementptr %Object, %Object* %object, i32 0, i32 0
br label %.body
.body:
; if (vtable->class == name)
%2 = phi %Object_vtable_type** [ %1, %.once ], [ %7, %.next]
%3 = load %Object_vtable_type*, %Object_vtable_type** %2
%4 = getelementptr %Object_vtable_type, %Object_vtable_type* %3, i32 0, i32 1
%5 = load i8*, i8** %4
%6 = icmp eq i8* %5, %name
br i1 %6, label %.exit_true, label %.next
.next:
; object = object->above
%7 = getelementptr %Object_vtable_type, %Object_vtable_type* %3, i32 0, i32 0
; while (object != null)
%8 = icmp ne %Object_vtable_type* %3, null
br i1 %8, label %.body, label %.exit_false
.exit_true:
ret i1 true
.exit_false:
ret i1 false
}
;*************************** Exception class ******************************
%Exception_vtable_type = type {
%Object_vtable_type*, ; 0: parent class vtable pointer
i8* ; 1: class name
; virtual methods would follow here.
}
@.Exception_class_name = private constant [10 x i8] c"Exception\00"
@.Exception_vtable = private constant %Exception_vtable_type {
%Object_vtable_type* @.Object_vtable, ; the parent of this class is the Object class
i8* getelementptr([10 x i8], [10 x i8]* @.Exception_class_name, i32 0, i32 0)
}
%Exception = type {
%Exception_vtable_type*, ; 0: the vtable pointer
i8* ; 1: the _text member
}
define void @Exception_Create_String(%Exception* %this, i8* %text) nounwind {
; set up vtable
%1 = getelementptr %Exception, %Exception* %this, i32 0, i32 0
store %Exception_vtable_type* @.Exception_vtable, %Exception_vtable_type** %1
; save input text string into _text
%2 = getelementptr %Exception, %Exception* %this, i32 0, i32 1
store i8* %text, i8** %2
ret void
}
define i8* @Exception_GetText(%Exception* %this) nounwind {
%1 = getelementptr %Exception, %Exception* %this, i32 0, i32 1
%2 = load i8*, i8** %1
ret i8* %2
}
;******************************* Foo class ********************************
%Foo = type { i32 }
define void @Foo_Create_Default(%Foo* %this) nounwind {
%1 = getelementptr %Foo, %Foo* %this, i32 0, i32 0
store i32 0, i32* %1
ret void
}
define i32 @Foo_GetLength(%Foo* %this) nounwind {
%1 = getelementptr %Foo, %Foo* %this, i32 0, i32 0
%2 = load i32, i32* %1
ret i32 %2
}
define void @Foo_SetLength(%Foo* %this, i32 %value) nounwind {
%1 = getelementptr %Foo, %Foo* %this, i32 0, i32 0
store i32 %value, i32* %1
ret void
}
;********************************* Foo function ***************************
@.message1 = internal constant [30 x i8] c"Exception requested by caller\00"
define %Exception* @Bar(i1 %fail, i32* %result) nounwind {
; Allocate Foo instance
%foo = alloca %Foo
call void @Foo_Create_Default(%Foo* %foo)
call void @Foo_SetLength(%Foo* %foo, i32 17)
; if (fail)
%1 = icmp eq i1 %fail, true
br i1 %1, label %.if_begin, label %.if_close
.if_begin:
; throw new Exception(...)
%2 = call i8* @malloc(i32 8)
%3 = bitcast i8* %2 to %Exception*
%4 = getelementptr [30 x i8], [30 x i8]* @.message1, i32 0, i32 0
call void @Exception_Create_String(%Exception* %3, i8* %4)
ret %Exception* %3
.if_close:
; foo.SetLength(24)
call void @Foo_SetLength(%Foo* %foo, i32 24)
%5 = call i32 @Foo_GetLength(%Foo* %foo)
store i32 %5, i32* %result
ret %Exception* null
}
;********************************* Main program ***************************
@.message2 = internal constant [11 x i8] c"Error: %s\0A\00"
@.message3 = internal constant [44 x i8] c"Internal error: Unhandled exception detectd\00"
define i32 @main(i32 %argc, i8** %argv) nounwind {
; "try" keyword expands to nothing.
; Body of try block.
; fail = (argc >= 2)
%fail = icmp uge i32 %argc, 2
; Function call.
%1 = alloca i32
%2 = call %Exception* @Bar(i1 %fail, i32* %1)
%3 = icmp ne %Exception* %2, null
br i1 %3, label %.catch_block, label %.exit
.catch_block:
%4 = bitcast %Exception* %2 to %Object*
%5 = getelementptr [10 x i8], [10 x i8]* @.Exception_class_name, i32 0, i32 0
%6 = call i1 @Object_IsA(%Object* %4, i8* %5)
br i1 %6, label %.catch_exception, label %.catch_all
.catch_exception:
%7 = getelementptr [11 x i8], [11 x i8]* @.message2, i32 0, i32 0
%8 = call i8* @Exception_GetText(%Exception* %2)
%9 = call i32 (i8*, ...) @printf(i8* %7, i8* %8)
br label %.exit
.catch_all:
%10 = getelementptr [44 x i8], [44 x i8]* @.message3, i32 0, i32 0
%11 = call i32 @puts(i8* %10)
br label %.exit
.exit:
%result = phi i32 [ 0, %0 ], [ 1, %.catch_exception ], [ 1, %.catch_all ]
ret i32 %result
}