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.Temp = type { i8 } 9 10@_ZTIi = external constant i8* 11 12; CHECK-LABEL: test_throw: 13; CHECK: throw __cpp_exception, $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; Simple test with a try-catch 29; 30; void foo(); 31; void test_catch() { 32; try { 33; foo(); 34; } catch (int) { 35; } 36; } 37 38; CHECK-LABEL: test_catch: 39; CHECK: global.get ${{.+}}=, __stack_pointer 40; CHECK: try 41; CHECK: call foo 42; CHECK: catch $[[EXCEPT_REF:[0-9]+]]= 43; CHECK: global.set __stack_pointer 44; CHECK: block i32 45; CHECK: br_on_exn 0, __cpp_exception, $[[EXCEPT_REF]] 46; CHECK: rethrow 47; CHECK: end_block 48; CHECK: extract_exception $[[EXN:[0-9]+]]= 49; CHECK-DAG: i32.store __wasm_lpad_context 50; CHECK-DAG: i32.store __wasm_lpad_context+4 51; CHECK: i32.call $drop=, _Unwind_CallPersonality, $[[EXN]] 52; CHECK: block 53; CHECK: br_if 0 54; CHECK: i32.call $drop=, __cxa_begin_catch 55; CHECK: call __cxa_end_catch 56; CHECK: br 1 57; CHECK: end_block 58; CHECK: call __cxa_rethrow 59; CHECK: end_try 60define void @test_catch() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 61entry: 62 invoke void @foo() 63 to label %try.cont unwind label %catch.dispatch 64 65catch.dispatch: ; preds = %entry 66 %0 = catchswitch within none [label %catch.start] unwind to caller 67 68catch.start: ; preds = %catch.dispatch 69 %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*)] 70 %2 = call i8* @llvm.wasm.get.exception(token %1) 71 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 72 %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) 73 %matches = icmp eq i32 %3, %4 74 br i1 %matches, label %catch, label %rethrow 75 76catch: ; preds = %catch.start 77 %5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 78 call void @__cxa_end_catch() [ "funclet"(token %1) ] 79 catchret from %1 to label %try.cont 80 81rethrow: ; preds = %catch.start 82 call void @__cxa_rethrow() [ "funclet"(token %1) ] 83 unreachable 84 85try.cont: ; preds = %entry, %catch 86 ret void 87} 88 89; Destructor (cleanup) test 90; 91; void foo(); 92; struct Temp { 93; ~Temp() {} 94; }; 95; void test_cleanup() { 96; Temp t; 97; foo(); 98; } 99 100; CHECK-LABEL: test_cleanup: 101; CHECK: try 102; CHECK: call foo 103; CHECK: catch 104; CHECK: global.set __stack_pointer 105; CHECK: i32.call $drop=, _ZN4TempD2Ev 106; CHECK: rethrow 107; CHECK: end_try 108define void @test_cleanup() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 109entry: 110 %t = alloca %struct.Temp, align 1 111 invoke void @foo() 112 to label %invoke.cont unwind label %ehcleanup 113 114invoke.cont: ; preds = %entry 115 %call = call %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* %t) 116 ret void 117 118ehcleanup: ; preds = %entry 119 %0 = cleanuppad within none [] 120 %call1 = call %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* %t) [ "funclet"(token %0) ] 121 cleanupret from %0 unwind to caller 122} 123 124; Calling a function that may throw within a 'catch (...)' generates a 125; temrinatepad, because __cxa_end_catch() also can throw within 'catch (...)'. 126; 127; void foo(); 128; void test_terminatepad() { 129; try { 130; foo(); 131; } catch (...) { 132; foo(); 133; } 134; } 135 136; CHECK-LABEL: test_terminatepad 137; CHECK: try 138; CHECK: call foo 139; CHECK: catch 140; CHECK: i32.call $drop=, __cxa_begin_catch 141; CHECK: try 142; CHECK: call foo 143; CHECK: catch 144; CHECK: try 145; CHECK: call __cxa_end_catch 146; CHECK: catch 147; CHECK: block i32 148; CHECK: br_on_exn 0, __cpp_exception 149; CHECK: call __clang_call_terminate, 0 150; CHECK: unreachable 151; CHECK: end_block 152; CHECK: call __clang_call_terminate 153; CHECK: unreachable 154; CHECK: end_try 155; CHECK: rethrow 156; CHECK: end_try 157; CHECK: call __cxa_end_catch 158; CHECK: end_try 159define void @test_terminatepad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 160entry: 161 invoke void @foo() 162 to label %try.cont unwind label %catch.dispatch 163 164catch.dispatch: ; preds = %entry 165 %0 = catchswitch within none [label %catch.start] unwind to caller 166 167catch.start: ; preds = %catch.dispatch 168 %1 = catchpad within %0 [i8* null] 169 %2 = call i8* @llvm.wasm.get.exception(token %1) 170 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 171 %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 172 invoke void @foo() [ "funclet"(token %1) ] 173 to label %invoke.cont1 unwind label %ehcleanup 174 175invoke.cont1: ; preds = %catch.start 176 call void @__cxa_end_catch() [ "funclet"(token %1) ] 177 catchret from %1 to label %try.cont 178 179try.cont: ; preds = %entry, %invoke.cont1 180 ret void 181 182ehcleanup: ; preds = %catch.start 183 %5 = cleanuppad within %1 [] 184 invoke void @__cxa_end_catch() [ "funclet"(token %5) ] 185 to label %invoke.cont2 unwind label %terminate 186 187invoke.cont2: ; preds = %ehcleanup 188 cleanupret from %5 unwind to caller 189 190terminate: ; preds = %ehcleanup 191 %6 = cleanuppad within %5 [] 192 %7 = call i8* @llvm.wasm.get.exception(token %6) 193 call void @__clang_call_terminate(i8* %7) [ "funclet"(token %6) ] 194 unreachable 195} 196 197; Tests prologues and epilogues are not generated within EH scopes. 198; They should not be treated as funclets; BBs starting with a catch instruction 199; should not have a prologue, and BBs ending with a catchret/cleanupret should 200; not have an epilogue. This is separate from __stack_pointer restoring 201; instructions after a catch instruction. 202; 203; void bar(int) noexcept; 204; void test_no_prolog_epilog_in_ehpad() { 205; int stack_var = 0; 206; bar(stack_var); 207; try { 208; foo(); 209; } catch (int) { 210; foo(); 211; } 212; } 213 214; CHECK-LABEL: test_no_prolog_epilog_in_ehpad 215; CHECK: try 216; CHECK: call foo 217; CHECK: catch 218; CHECK-NOT: global.get $push{{.+}}=, __stack_pointer 219; CHECK: global.set __stack_pointer 220; CHECK: block 221; CHECK: block 222; CHECK: br_if 0 223; CHECK: i32.call $drop=, __cxa_begin_catch 224; CHECK: try 225; CHECK: call foo 226; CHECK: catch 227; CHECK-NOT: global.get $push{{.+}}=, __stack_pointer 228; CHECK: global.set __stack_pointer 229; CHECK: call __cxa_end_catch 230; CHECK: rethrow 231; CHECK-NOT: global.set __stack_pointer, $pop{{.+}} 232; CHECK: end_try 233; CHECK: end_block 234; CHECK: call __cxa_rethrow 235; CHECK: end_block 236; CHECK-NOT: global.set __stack_pointer, $pop{{.+}} 237; CHECK: call __cxa_end_catch 238; CHECK: end_try 239define void @test_no_prolog_epilog_in_ehpad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 240entry: 241 %stack_var = alloca i32, align 4 242 call void @bar(i32* %stack_var) 243 invoke void @foo() 244 to label %try.cont unwind label %catch.dispatch 245 246catch.dispatch: ; preds = %entry 247 %0 = catchswitch within none [label %catch.start] unwind to caller 248 249catch.start: ; preds = %catch.dispatch 250 %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*)] 251 %2 = call i8* @llvm.wasm.get.exception(token %1) 252 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 253 %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) 254 %matches = icmp eq i32 %3, %4 255 br i1 %matches, label %catch, label %rethrow 256 257catch: ; preds = %catch.start 258 %5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 259 %6 = bitcast i8* %5 to float* 260 %7 = load float, float* %6, align 4 261 invoke void @foo() [ "funclet"(token %1) ] 262 to label %invoke.cont1 unwind label %ehcleanup 263 264invoke.cont1: ; preds = %catch 265 call void @__cxa_end_catch() [ "funclet"(token %1) ] 266 catchret from %1 to label %try.cont 267 268rethrow: ; preds = %catch.start 269 call void @__cxa_rethrow() [ "funclet"(token %1) ] 270 unreachable 271 272try.cont: ; preds = %entry, %invoke.cont1 273 ret void 274 275ehcleanup: ; preds = %catch 276 %8 = cleanuppad within %1 [] 277 call void @__cxa_end_catch() [ "funclet"(token %8) ] 278 cleanupret from %8 unwind to caller 279} 280 281; When a function does not have stack-allocated objects, it does not need to 282; store SP back to __stack_pointer global at the epilog. 283; 284; void foo(); 285; void test_no_sp_writeback() { 286; try { 287; foo(); 288; } catch (...) { 289; } 290; } 291 292; CHECK-LABEL: test_no_sp_writeback 293; CHECK: try 294; CHECK: call foo 295; CHECK: catch 296; CHECK: i32.call $drop=, __cxa_begin_catch 297; CHECK: call __cxa_end_catch 298; CHECK: end_try 299; CHECK-NOT: global.set __stack_pointer 300; CHECK: return 301define void @test_no_sp_writeback() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 302entry: 303 invoke void @foo() 304 to label %try.cont unwind label %catch.dispatch 305 306catch.dispatch: ; preds = %entry 307 %0 = catchswitch within none [label %catch.start] unwind to caller 308 309catch.start: ; preds = %catch.dispatch 310 %1 = catchpad within %0 [i8* null] 311 %2 = call i8* @llvm.wasm.get.exception(token %1) 312 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 313 %4 = call i8* @__cxa_begin_catch(i8* %2) #2 [ "funclet"(token %1) ] 314 call void @__cxa_end_catch() [ "funclet"(token %1) ] 315 catchret from %1 to label %try.cont 316 317try.cont: ; preds = %entry, %catch.start 318 ret void 319} 320 321; When the result of @llvm.wasm.get.exception is not used. This is created to 322; fix a bug in LateEHPrepare and this should not crash. 323define void @test_get_exception_wo_use() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 324entry: 325 invoke void @foo() 326 to label %try.cont unwind label %catch.dispatch 327 328catch.dispatch: ; preds = %entry 329 %0 = catchswitch within none [label %catch.start] unwind to caller 330 331catch.start: ; preds = %catch.dispatch 332 %1 = catchpad within %0 [i8* null] 333 %2 = call i8* @llvm.wasm.get.exception(token %1) 334 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 335 catchret from %1 to label %try.cont 336 337try.cont: ; preds = %entry, %catch.start 338 ret void 339} 340 341declare void @foo() 342declare void @bar(i32*) 343declare i32 @__gxx_wasm_personality_v0(...) 344declare void @llvm.wasm.throw(i32, i8*) 345declare void @llvm.wasm.rethrow() 346declare i8* @llvm.wasm.get.exception(token) 347declare i32 @llvm.wasm.get.ehselector(token) 348declare i32 @llvm.eh.typeid.for(i8*) 349declare i8* @__cxa_begin_catch(i8*) 350declare void @__cxa_end_catch() 351declare void @__cxa_rethrow() 352declare void @__clang_call_terminate(i8*) 353declare %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* returned) 354 355; CHECK: __cpp_exception: 356; CHECK: .eventtype __cpp_exception i32 357