1; RUN: not llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -exception-model=wasm 2; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -exception-model=wasm -mattr=+exception-handling -verify-machineinstrs | FileCheck -allow-deprecated-dag-overlap %s 3; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-keep-registers -exception-model=wasm -mattr=+exception-handling 4 5target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" 6target triple = "wasm32-unknown-unknown" 7 8%struct.Cleanup = type { i8 } 9 10@_ZTIi = external constant i8* 11 12; CHECK-LABEL: test_throw: 13; CHECK: throw __cpp_exception@EVENT, $0 14; CHECK-NOT: unreachable 15define void @test_throw(i8* %p) { 16 call void @llvm.wasm.throw(i32 0, i8* %p) 17 ret void 18} 19 20; CHECK-LABEL: test_rethrow: 21; CHECK: rethrow 22; CHECK-NOT: unreachable 23define void @test_rethrow(i8* %p) { 24 call void @llvm.wasm.rethrow() 25 ret void 26} 27 28; CHECK-LABEL: test_catch_rethrow: 29; CHECK: global.get ${{.+}}=, __stack_pointer@GLOBAL 30; CHECK: try 31; CHECK: call foo@FUNCTION 32; CHECK: catch $[[EXCEPT_REF:[0-9]+]]= 33; CHECK: global.set __stack_pointer@GLOBAL 34; CHECK: block i32 35; CHECK: br_on_exn 0, __cpp_exception@EVENT, $[[EXCEPT_REF]] 36; CHECK: rethrow 37; CHECK: end_block 38; CHECK: extract_exception $[[EXN:[0-9]+]]= 39; CHECK-DAG: i32.store __wasm_lpad_context 40; CHECK-DAG: i32.store __wasm_lpad_context+4 41; CHECK: i32.call $drop=, _Unwind_CallPersonality@FUNCTION, $[[EXN]] 42; CHECK: i32.call $drop=, __cxa_begin_catch@FUNCTION 43; CHECK: call __cxa_end_catch@FUNCTION 44; CHECK: call __cxa_rethrow@FUNCTION 45; CHECK: end_try 46define void @test_catch_rethrow() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 47entry: 48 invoke void @foo() 49 to label %try.cont unwind label %catch.dispatch 50 51catch.dispatch: ; preds = %entry 52 %0 = catchswitch within none [label %catch.start] unwind to caller 53 54catch.start: ; preds = %catch.dispatch 55 %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*)] 56 %2 = call i8* @llvm.wasm.get.exception(token %1) 57 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 58 %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) 59 %matches = icmp eq i32 %3, %4 60 br i1 %matches, label %catch, label %rethrow 61 62catch: ; preds = %catch.start 63 %5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 64 call void @__cxa_end_catch() [ "funclet"(token %1) ] 65 catchret from %1 to label %try.cont 66 67rethrow: ; preds = %catch.start 68 call void @__cxa_rethrow() [ "funclet"(token %1) ] 69 unreachable 70 71try.cont: ; preds = %entry, %catch 72 ret void 73} 74 75; CHECK-LABEL: test_cleanup: 76; CHECK: try 77; CHECK: call foo@FUNCTION 78; CHECK: catch 79; CHECK: global.set __stack_pointer@GLOBAL 80; CHECK: i32.call $drop=, _ZN7CleanupD1Ev@FUNCTION 81; CHECK: rethrow 82; CHECK: end_try 83define void @test_cleanup() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 84entry: 85 %c = alloca %struct.Cleanup, align 1 86 invoke void @foo() 87 to label %invoke.cont unwind label %ehcleanup 88 89invoke.cont: ; preds = %entry 90 %call = call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c) 91 ret void 92 93ehcleanup: ; preds = %entry 94 %0 = cleanuppad within none [] 95 %call1 = call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c) [ "funclet"(token %0) ] 96 cleanupret from %0 unwind to caller 97} 98 99; CHECK-LABEL: test_terminatepad 100; CHECK: catch 101; CHECK: block i32 102; CHECK: br_on_exn 0, __cpp_exception@EVENT 103; CHECK: call __clang_call_terminate@FUNCTION, 0 104; CHECK: unreachable 105; CHECK: end_block 106; CHECK: extract_exception 107; CHECK: call __clang_call_terminate@FUNCTION 108; CHECK: unreachable 109define void @test_terminatepad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 110entry: 111 invoke void @foo() 112 to label %try.cont unwind label %catch.dispatch 113 114catch.dispatch: ; preds = %entry 115 %0 = catchswitch within none [label %catch.start] unwind to caller 116 117catch.start: ; preds = %catch.dispatch 118 %1 = catchpad within %0 [i8* null] 119 %2 = call i8* @llvm.wasm.get.exception(token %1) 120 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 121 %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 122 invoke void @foo() [ "funclet"(token %1) ] 123 to label %invoke.cont1 unwind label %ehcleanup 124 125invoke.cont1: ; preds = %catch.start 126 call void @__cxa_end_catch() [ "funclet"(token %1) ] 127 catchret from %1 to label %try.cont 128 129try.cont: ; preds = %entry, %invoke.cont1 130 ret void 131 132ehcleanup: ; preds = %catch.start 133 %5 = cleanuppad within %1 [] 134 invoke void @__cxa_end_catch() [ "funclet"(token %5) ] 135 to label %invoke.cont2 unwind label %terminate 136 137invoke.cont2: ; preds = %ehcleanup 138 cleanupret from %5 unwind to caller 139 140terminate: ; preds = %ehcleanup 141 %6 = cleanuppad within %5 [] 142 %7 = call i8* @llvm.wasm.get.exception(token %6) 143 call void @__clang_call_terminate(i8* %7) [ "funclet"(token %6) ] 144 unreachable 145} 146 147; Tests prologues and epilogues are not generated within EH scopes. 148; They should not be treated as funclets; BBs starting with a catch instruction 149; should not have a prologue, and BBs ending with a catchret/cleanupret should 150; not have an epilogue. This is separate from __stack_pointer restoring 151; instructions after a catch instruction. 152 153; CHECK-LABEL: test_no_prolog_epilog_in_ehpad 154; CHECK: try 155; CHECK: call foo@FUNCTION 156; CHECK: catch 157; CHECK-NOT: global.get $push{{.+}}=, __stack_pointer@GLOBAL 158; CHECK: global.set __stack_pointer@GLOBAL 159; CHECK: try 160; CHECK: call foo@FUNCTION 161; CHECK: catch 162; CHECK-NOT: global.get $push{{.+}}=, __stack_pointer@GLOBAL 163; CHECK: global.set __stack_pointer@GLOBAL 164; CHECK: call __cxa_end_catch@FUNCTION 165; CHECK-NOT: global.set __stack_pointer@GLOBAL, $pop{{.+}} 166; CHECK: end_try 167; CHECK-NOT: global.set __stack_pointer@GLOBAL, $pop{{.+}} 168; CHECK: end_try 169define void @test_no_prolog_epilog_in_ehpad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 170entry: 171 %stack_var = alloca i32, align 4 172 call void @bar(i32* %stack_var) 173 invoke void @foo() 174 to label %try.cont unwind label %catch.dispatch 175 176catch.dispatch: ; preds = %entry 177 %0 = catchswitch within none [label %catch.start] unwind to caller 178 179catch.start: ; preds = %catch.dispatch 180 %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*)] 181 %2 = call i8* @llvm.wasm.get.exception(token %1) 182 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 183 %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) 184 %matches = icmp eq i32 %3, %4 185 br i1 %matches, label %catch, label %rethrow 186 187catch: ; preds = %catch.start 188 %5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 189 %6 = bitcast i8* %5 to float* 190 %7 = load float, float* %6, align 4 191 invoke void @foo() [ "funclet"(token %1) ] 192 to label %invoke.cont1 unwind label %ehcleanup 193 194invoke.cont1: ; preds = %catch 195 call void @__cxa_end_catch() [ "funclet"(token %1) ] 196 catchret from %1 to label %try.cont 197 198rethrow: ; preds = %catch.start 199 call void @__cxa_rethrow() [ "funclet"(token %1) ] 200 unreachable 201 202try.cont: ; preds = %entry, %invoke.cont1 203 ret void 204 205ehcleanup: ; preds = %catch 206 %8 = cleanuppad within %1 [] 207 call void @__cxa_end_catch() [ "funclet"(token %8) ] 208 cleanupret from %8 unwind to caller 209} 210 211; When a function does not have stack-allocated objects, it does not need to 212; store SP back to __stack_pointer global at the epilog. 213 214; CHECK-LABEL: no_sp_writeback 215; CHECK: try 216; CHECK: call foo@FUNCTION 217; CHECK: end_try 218; CHECK-NOT: global.set __stack_pointer@GLOBAL 219; CHECK: return 220define void @no_sp_writeback() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 221entry: 222 invoke void @foo() 223 to label %try.cont unwind label %catch.dispatch 224 225catch.dispatch: ; preds = %entry 226 %0 = catchswitch within none [label %catch.start] unwind to caller 227 228catch.start: ; preds = %catch.dispatch 229 %1 = catchpad within %0 [i8* null] 230 %2 = call i8* @llvm.wasm.get.exception(token %1) 231 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 232 %4 = call i8* @__cxa_begin_catch(i8* %2) #2 [ "funclet"(token %1) ] 233 call void @__cxa_end_catch() [ "funclet"(token %1) ] 234 catchret from %1 to label %try.cont 235 236try.cont: ; preds = %entry, %catch.start 237 ret void 238} 239 240declare void @foo() 241declare void @bar(i32*) 242declare i32 @__gxx_wasm_personality_v0(...) 243declare void @llvm.wasm.throw(i32, i8*) 244declare void @llvm.wasm.rethrow() 245declare i8* @llvm.wasm.get.exception(token) 246declare i32 @llvm.wasm.get.ehselector(token) 247declare i32 @llvm.eh.typeid.for(i8*) 248declare i8* @__cxa_begin_catch(i8*) 249declare void @__cxa_end_catch() 250declare void @__cxa_rethrow() 251declare void @__clang_call_terminate(i8*) 252declare %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* returned) 253 254; CHECK: __cpp_exception: 255; CHECK: .eventtype __cpp_exception i32 256