Index: test/Transforms/NaCl/pnacl-sjlj-eh.ll |
diff --git a/test/Transforms/NaCl/pnacl-sjlj-eh.ll b/test/Transforms/NaCl/pnacl-sjlj-eh.ll |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3783b08a5edc339c4762ae8710067015b311d7db |
--- /dev/null |
+++ b/test/Transforms/NaCl/pnacl-sjlj-eh.ll |
@@ -0,0 +1,127 @@ |
+; RUN: opt %s -pnacl-sjlj-eh -S | FileCheck %s |
+ |
+; This must be declared for expanding "invoke" and "landingpad" instructions. |
+@__pnacl_eh_stack = external thread_local global i8* |
+ |
+; This must be declared for expanding "resume" instructions. |
+declare void @__pnacl_eh_resume(i32* %exception) |
+ |
+declare i32 @external_func(i64 %arg) |
+ |
+ |
+; CHECK: %ExceptionFrame = type { [1024 x i8], %ExceptionFrame*, i32 } |
+ |
+define i32 @invoke_test(i64 %arg) { |
+ %result = invoke i32 @external_func(i64 %arg) |
+ to label %cont unwind label %lpad |
+cont: |
+ ret i32 %result |
+lpad: |
+ %lp = landingpad { i8*, i32 } personality i8* null cleanup |
+ ret i32 999 |
+} |
+; CHECK: define i32 @invoke_test |
+; CHECK-NEXT: %invoke_frame = alloca %ExceptionFrame, align 8 |
+; CHECK-NEXT: %exc_info_ptr = getelementptr %ExceptionFrame* %invoke_frame, i32 0, i32 2 |
+; CHECK-NEXT: %invoke_next = getelementptr %ExceptionFrame* %invoke_frame, i32 0, i32 1 |
+; CHECK-NEXT: %invoke_jmp_buf = getelementptr %ExceptionFrame* %invoke_frame, i32 0, i32 0, i32 0 |
+; CHECK-NEXT: %pnacl_eh_stack = bitcast i8** @__pnacl_eh_stack to %ExceptionFrame** |
+; CHECK-NEXT: %invoke_sj = call i32 @llvm.nacl.setjmp(i8* %invoke_jmp_buf) |
+; CHECK-NEXT: %invoke_sj_is_zero = icmp eq i32 %invoke_sj, 0 |
+; CHECK-NEXT: br i1 %invoke_sj_is_zero, label %invoke_do_call, label %lpad |
+; CHECK: invoke_do_call: |
+; CHECK-NEXT: %old_eh_stack = load %ExceptionFrame** %pnacl_eh_stack |
+; CHECK-NEXT: store %ExceptionFrame* %old_eh_stack, %ExceptionFrame** %invoke_next |
+; CHECK-NEXT: store i32 {{[0-9]+}}, i32* %exc_info_ptr |
+; CHECK-NEXT: store %ExceptionFrame* %invoke_frame, %ExceptionFrame** %pnacl_eh_stack |
+; CHECK-NEXT: %result = call i32 @external_func(i64 %arg) |
+; CHECK-NEXT: store %ExceptionFrame* %old_eh_stack, %ExceptionFrame** %pnacl_eh_stack |
+; CHECK-NEXT: br label %cont |
+; CHECK: cont: |
+; CHECK-NEXT: ret i32 %result |
+; CHECK: lpad: |
+; CHECK-NEXT: %landingpad_ptr = bitcast i8* %invoke_jmp_buf to { i8*, i32 }* |
+; CHECK-NEXT: %lp = load { i8*, i32 }* %landingpad_ptr |
+; CHECK-NEXT: ret i32 999 |
+ |
+ |
+; A landingpad block may be used by multiple "invoke" instructions. |
+define i32 @shared_landingpad(i64 %arg) { |
+ %result1 = invoke i32 @external_func(i64 %arg) |
+ to label %cont1 unwind label %lpad |
+cont1: |
+ %result2 = invoke i32 @external_func(i64 %arg) |
+ to label %cont2 unwind label %lpad |
+cont2: |
+ ret i32 %result2 |
+lpad: |
+ %lp = landingpad { i8*, i32 } personality i8* null cleanup |
+ ret i32 999 |
+} |
+; CHECK: define i32 @shared_landingpad |
+; CHECK: br i1 %invoke_sj_is_zero, label %invoke_do_call, label %lpad |
+; CHECK: br i1 %invoke_sj_is_zero2, label %invoke_do_call3, label %lpad |
+ |
+ |
+; Check that the pass can handle a landingpad appearing before an invoke. |
+define i32 @landingpad_before_invoke() { |
+ ret i32 123 |
+ |
+dead_block: |
+ %lp = landingpad i32 personality i8* null cleanup |
+ ret i32 %lp |
+} |
+; CHECK: define i32 @landingpad_before_invoke |
+; CHECK: %lp = load i32* %landingpad_ptr |
+ |
+ |
+; Test the expansion of the "resume" instruction. |
+define void @test_resume({ i8*, i32 } %arg) { |
+ resume { i8*, i32 } %arg |
+} |
+; CHECK: define void @test_resume |
+; CHECK-NEXT: %resume_exc = extractvalue { i8*, i32 } %arg, 0 |
+; CHECK-NEXT: %resume_cast = bitcast i8* %resume_exc to i32* |
+; CHECK-NEXT: call void @__pnacl_eh_resume(i32* %resume_cast) |
+; CHECK-NEXT: unreachable |
+ |
+ |
+; Check that call attributes are preserved. |
+define i32 @call_attrs(i64 %arg) { |
+ %result = invoke fastcc i32 @external_func(i64 inreg %arg) noreturn |
+ to label %cont unwind label %lpad |
+cont: |
+ ret i32 %result |
+lpad: |
+ %lp = landingpad { i8*, i32 } personality i8* null cleanup |
+ ret i32 999 |
+} |
+; CHECK: define i32 @call_attrs |
+; CHECK: %result = call fastcc i32 @external_func(i64 inreg %arg) [[NORETURN:#[0-9]+]] |
+ |
+ |
+; Check that any PHI nodes referring to the result of an "invoke" are |
+; updated to refer to the correct basic block. |
+define i32 @invoke_with_phi_nodes(i64 %arg) { |
+entry: |
+ %result = invoke i32 @external_func(i64 %arg) |
+ to label %cont unwind label %lpad |
+cont: |
+ %cont_phi = phi i32 [ 100, %entry ] |
+ ret i32 %cont_phi |
+lpad: |
+ %lpad_phi = phi i32 [ 200, %entry ] |
+ %lp = landingpad { i8*, i32 } personality i8* null cleanup |
+ ret i32 %lpad_phi |
+} |
+; CHECK: define i32 @invoke_with_phi_nodes |
+; CHECK: %result = call i32 @external_func(i64 %arg) |
+; CHECK: cont: |
+; CHECK-NEXT: %cont_phi = phi i32 [ 100, %invoke_do_call ] |
+; CHECK-NEXT: ret i32 %cont_phi |
+; CHECK: lpad: |
+; CHECK-NEXT: %lpad_phi = phi i32 [ 200, %entry ] |
+; CHECK: ret i32 %lpad_phi |
+ |
+ |
+; CHECK: attributes [[NORETURN]] = { noreturn } |