; ModuleID = 'ExceptionTest'
	%Object = type {}
	%UnwindInfo = type { i64, void (i32, %UnwindInfo *)*, i16, i16 }
	%UnwindContext = type opaque
	%Throwable = type { %Object, %UnwindInfo }

@str_fmt = internal constant [4 x i8] c"%s\0A\00"
@str_begin = internal constant [6 x i8] c"begin\00"
@str_throw = internal constant [6 x i8] c"throw\00"
@str_catch = internal constant [6 x i8] c"catch\00"
@str_nocatch = internal constant [8 x i8] c"nocatch\00"

; action codes
@UA_SEARCH_PHASE = internal constant i32 1
@UA_CLEANUP_PHASE = internal constant i32 2
@UA_HANDLER_FRAME = internal constant i32 4
@UA_FORCE_UNWIND = internal constant i32 8

; reason codes
@URC_NO_REASON = internal constant i32 0
@URC_FOREIGN_EXCEPTION_CAUGHT = internal constant i32 1
@URC_FATAL_PHASE2_ERROR = internal constant i32 2
@URC_FATAL_PHASE1_ERROR = internal constant i32 3
@URC_NORMAL_STOP = internal constant i32 4
@URC_END_OF_STACK = internal constant i32 5
@URC_HANDLER_FOUND = internal constant i32 6
@URC_INSTALL_CONTEXT = internal constant i32 7
@URC_CONTINUE_UNWIND = internal constant i32 8

declare i32 @__gcc_personality_v0(i32, i32, i64, i8*, %UnwindContext*) nounwind

define i32 @main(i32, i8**) nounwind {
entry:
	call void @print(i8* bitcast ([6 x i8]* @str_begin to i8 *))
  invoke fastcc void @throwSomething() to label %nounwind unwind label %catch;

catch:
  %eh_ptr = call i8* @llvm.eh.exception();
  %eh_select34 = call i32 (i8*, i8*, ...)*
      @llvm.eh.selector.i32 (
          i8* %eh_ptr,
          i8* bitcast (i32 (i32, i32, i64, i8*, %UnwindContext*)* @__gcc_personality_v0 to i8*),
          i32 0)

  call void @print(i8* bitcast ([6 x i8]* @str_catch to i8 *))
  ret i32 0

cc0:
  call void @print(i8* bitcast ([6 x i8]* @str_catch to i8 *))
  ret i32 0

nounwind:
  call void @print(i8* bitcast ([8 x i8]* @str_nocatch to i8 *))
	ret i32 -1
}

define internal fastcc void @throwSomething() noreturn {
entry:
	%throwable = malloc %Throwable
	call fastcc void @Throwable.construct(%Throwable* %throwable)
	%unwindInfo = getelementptr %Throwable* %throwable, i32 0, i32 1
	call void @print(i8* bitcast ([6 x i8]* @str_throw to i8 *))
	%throw = call i32 @_Unwind_RaiseException(%UnwindInfo* %unwindInfo)
	unreachable
}

define internal fastcc void @Throwable.construct(%Throwable* %self) nounwind {
entry:
	%exceptionClass = getelementptr %Throwable* %self, i32 0, i32 1, i32 0
	store i64 0, i64* %exceptionClass
	%exceptionCleanup = getelementptr %Throwable* %self, i32 0, i32 1, i32 1
	store void (i32, %UnwindInfo *)* @exceptionCleanupFn, void (i32, %UnwindInfo *)** %exceptionCleanup
	%private1 = getelementptr %Throwable* %self, i32 0, i32 1, i32 2
	store i16 0, i16* %private1
	%private2 = getelementptr %Throwable* %self, i32 0, i32 1, i32 3
	store i16 0, i16* %private2
	ret void
}

define internal void @exceptionCleanupFn(i32 %reason, %UnwindInfo * %exc) {
  ret void
}

declare i32 @_Unwind_RaiseException(%UnwindInfo*) noreturn
declare void @_Unwind_SetIP(%UnwindContext*, i8*);

declare i8* @llvm.eh.exception() nounwind
declare i32 @llvm.eh.selector.i32(i8*, i8*, ...) nounwind

declare void @printf(i8*, ...) nounwind

define i32 @print(i8* %msg) nounwind {
  call void (i8*, ...)* @printf(i8* bitcast([4 x i8]* @str_fmt to i8*), i8* %msg)
  ret void
}
