OLD | NEW |
(Empty) | |
| 1 ; RUN: opt %s -pnacl-sjlj-eh -S | FileCheck %s |
| 2 |
| 3 ; This must be declared for expanding "invoke" and "landingpad" instructions. |
| 4 @__pnacl_eh_stack = external thread_local global i8* |
| 5 |
| 6 ; This must be declared for expanding "resume" instructions. |
| 7 declare void @__pnacl_eh_resume(i32* %exception) |
| 8 |
| 9 declare i32 @external_func(i64 %arg) |
| 10 declare void @external_func_void() |
| 11 declare i32 @my_setjmp() |
| 12 |
| 13 |
| 14 ; CHECK: %ExceptionFrame = type { [1024 x i8], %ExceptionFrame*, i32 } |
| 15 |
| 16 define i32 @invoke_test(i64 %arg) { |
| 17 %result = invoke i32 @external_func(i64 %arg) |
| 18 to label %cont unwind label %lpad |
| 19 cont: |
| 20 ret i32 %result |
| 21 lpad: |
| 22 %lp = landingpad { i8*, i32 } personality i8* null cleanup |
| 23 ret i32 999 |
| 24 } |
| 25 ; CHECK: define i32 @invoke_test |
| 26 ; CHECK-NEXT: %invoke_result_ptr = alloca i32 |
| 27 ; CHECK-NEXT: %invoke_frame = alloca %ExceptionFrame, align 8 |
| 28 ; CHECK-NEXT: %exc_info_ptr = getelementptr %ExceptionFrame* %invoke_frame, i32
0, i32 2 |
| 29 ; CHECK-NEXT: %invoke_next = getelementptr %ExceptionFrame* %invoke_frame, i32 0
, i32 1 |
| 30 ; CHECK-NEXT: %invoke_jmp_buf = getelementptr %ExceptionFrame* %invoke_frame, i3
2 0, i32 0, i32 0 |
| 31 ; CHECK-NEXT: %pnacl_eh_stack = bitcast i8** @__pnacl_eh_stack to %ExceptionFram
e** |
| 32 ; CHECK-NEXT: %old_eh_stack = load %ExceptionFrame** %pnacl_eh_stack |
| 33 ; CHECK-NEXT: store %ExceptionFrame* %old_eh_stack, %ExceptionFrame** %invoke_ne
xt |
| 34 ; CHECK-NEXT: store i32 {{[0-9]+}}, i32* %exc_info_ptr |
| 35 ; CHECK-NEXT: store %ExceptionFrame* %invoke_frame, %ExceptionFrame** %pnacl_eh_
stack |
| 36 ; CHECK-NEXT: %invoke_is_exc = call i32 @invoke_test_setjmp_caller(i64 %arg, i32
(i64)* @external_func, i8* %invoke_jmp_buf, i32* %invoke_result_ptr) |
| 37 ; CHECK-NEXT: %result = load i32* %invoke_result_ptr |
| 38 ; CHECK-NEXT: store %ExceptionFrame* %old_eh_stack, %ExceptionFrame** %pnacl_eh_
stack |
| 39 ; CHECK-NEXT: %invoke_sj_is_zero = icmp eq i32 %invoke_is_exc, 0 |
| 40 ; CHECK-NEXT: br i1 %invoke_sj_is_zero, label %cont, label %lpad |
| 41 ; CHECK: cont: |
| 42 ; CHECK-NEXT: ret i32 %result |
| 43 ; CHECK: lpad: |
| 44 ; CHECK-NEXT: %landingpad_ptr = bitcast i8* %invoke_jmp_buf to { i8*, i32 }* |
| 45 ; CHECK-NEXT: %lp = load { i8*, i32 }* %landingpad_ptr |
| 46 ; CHECK-NEXT: ret i32 999 |
| 47 |
| 48 ; Check definition of helper function: |
| 49 ; CHECK: define internal i32 @invoke_test_setjmp_caller(i64 %arg, i32 (i64)* %fu
nc_ptr, i8* %jmp_buf, i32* %result_ptr) { |
| 50 ; CHECK-NEXT: %invoke_sj = call i32 @llvm.nacl.setjmp(i8* %jmp_buf) [[RETURNS_TW
ICE:#[0-9]+]] |
| 51 ; CHECK-NEXT: %invoke_sj_is_zero = icmp eq i32 %invoke_sj, 0 |
| 52 ; CHECK-NEXT: br i1 %invoke_sj_is_zero, label %normal, label %exception |
| 53 ; CHECK: normal: |
| 54 ; CHECK-NEXT: %result = call i32 %func_ptr(i64 %arg) |
| 55 ; CHECK-NEXT: store i32 %result, i32* %result_ptr |
| 56 ; CHECK-NEXT: ret i32 0 |
| 57 ; CHECK: exception: |
| 58 ; CHECK-NEXT: ret i32 1 |
| 59 |
| 60 |
| 61 ; A landingpad block may be used by multiple "invoke" instructions. |
| 62 define i32 @shared_landingpad(i64 %arg) { |
| 63 %result1 = invoke i32 @external_func(i64 %arg) |
| 64 to label %cont1 unwind label %lpad |
| 65 cont1: |
| 66 %result2 = invoke i32 @external_func(i64 %arg) |
| 67 to label %cont2 unwind label %lpad |
| 68 cont2: |
| 69 ret i32 %result2 |
| 70 lpad: |
| 71 %lp = landingpad { i8*, i32 } personality i8* null cleanup |
| 72 ret i32 999 |
| 73 } |
| 74 ; CHECK: define i32 @shared_landingpad |
| 75 ; CHECK: br i1 %invoke_sj_is_zero{{[0-9]*}}, label %cont1, label %lpad |
| 76 ; CHECK: br i1 %invoke_sj_is_zero{{[0-9]*}}, label %cont2, label %lpad |
| 77 |
| 78 |
| 79 ; Check that the pass can handle a landingpad appearing before an invoke. |
| 80 define i32 @landingpad_before_invoke() { |
| 81 ret i32 123 |
| 82 |
| 83 dead_block: |
| 84 %lp = landingpad i32 personality i8* null cleanup |
| 85 ret i32 %lp |
| 86 } |
| 87 ; CHECK: define i32 @landingpad_before_invoke |
| 88 ; CHECK: %lp = load i32* %landingpad_ptr |
| 89 |
| 90 |
| 91 ; Test the expansion of the "resume" instruction. |
| 92 define void @test_resume({ i8*, i32 } %arg) { |
| 93 resume { i8*, i32 } %arg |
| 94 } |
| 95 ; CHECK: define void @test_resume |
| 96 ; CHECK-NEXT: %resume_exc = extractvalue { i8*, i32 } %arg, 0 |
| 97 ; CHECK-NEXT: %resume_cast = bitcast i8* %resume_exc to i32* |
| 98 ; CHECK-NEXT: call void @__pnacl_eh_resume(i32* %resume_cast) |
| 99 ; CHECK-NEXT: unreachable |
| 100 |
| 101 |
| 102 ; Check that call attributes are preserved. |
| 103 define i32 @call_attrs(i64 %arg) { |
| 104 %result = invoke fastcc i32 @external_func(i64 inreg %arg) noreturn |
| 105 to label %cont unwind label %lpad |
| 106 cont: |
| 107 ret i32 %result |
| 108 lpad: |
| 109 %lp = landingpad { i8*, i32 } personality i8* null cleanup |
| 110 ret i32 999 |
| 111 } |
| 112 ; CHECK: define i32 @call_attrs |
| 113 ; CHECK: %result = call fastcc i32 %func_ptr(i64 inreg %arg) [[NORETURN:#[0-9]+]
] |
| 114 |
| 115 |
| 116 ; If the PNaClSjLjEH pass needs to insert any instructions into the |
| 117 ; non-exceptional path, check that PHI nodes are updated correctly. |
| 118 ; (An earlier version needed to do this, but the current version |
| 119 ; doesn't.) |
| 120 define i32 @invoke_with_phi_nodes(i64 %arg) { |
| 121 entry: |
| 122 %result = invoke i32 @external_func(i64 %arg) |
| 123 to label %cont unwind label %lpad |
| 124 cont: |
| 125 %cont_phi = phi i32 [ 100, %entry ] |
| 126 ret i32 %cont_phi |
| 127 lpad: |
| 128 %lpad_phi = phi i32 [ 200, %entry ] |
| 129 %lp = landingpad { i8*, i32 } personality i8* null cleanup |
| 130 ret i32 %lpad_phi |
| 131 } |
| 132 ; CHECK: define i32 @invoke_with_phi_nodes |
| 133 ; CHECK: cont: |
| 134 ; CHECK-NEXT: %cont_phi = phi i32 [ 100, %entry ] |
| 135 ; CHECK-NEXT: ret i32 %cont_phi |
| 136 ; CHECK: lpad: |
| 137 ; CHECK-NEXT: %lpad_phi = phi i32 [ 200, %entry ] |
| 138 ; CHECK: ret i32 %lpad_phi |
| 139 |
| 140 |
| 141 ; Test "void" result type from "invoke". This requires special |
| 142 ; handling because void* is not a valid type. |
| 143 define void @invoke_void_result() { |
| 144 invoke void @external_func_void() to label %cont unwind label %lpad |
| 145 cont: |
| 146 ret void |
| 147 lpad: |
| 148 landingpad i32 personality i8* null cleanup |
| 149 ret void |
| 150 } |
| 151 ; CHECK: define void @invoke_void_result() |
| 152 ; "%result_ptr" argument is omitted from the helper function: |
| 153 ; CHECK: define internal i32 @invoke_void_result_setjmp_caller(void ()* %func_pt
r, i8* %jmp_buf) |
| 154 |
| 155 |
| 156 ; A call to setjmp() cannot be moved into a helper function, so test |
| 157 ; that it isn't moved. |
| 158 define void @invoke_setjmp() { |
| 159 %x = invoke i32 @my_setjmp() returns_twice to label %cont unwind label %lpad |
| 160 cont: |
| 161 ret void |
| 162 lpad: |
| 163 landingpad i32 personality i8* null cleanup |
| 164 ret void |
| 165 } |
| 166 ; CHECK: define void @invoke_setjmp() |
| 167 ; CHECK-NOT: call |
| 168 ; CHECK: %x = call i32 @my_setjmp() [[RETURNS_TWICE]] |
| 169 ; CHECK-NEXT: br label %cont |
| 170 |
| 171 |
| 172 ; CHECK: attributes [[RETURNS_TWICE]] = { returns_twice } |
| 173 ; CHECK: attributes [[NORETURN]] = { noreturn } |
OLD | NEW |