1; 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 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 -O0 | FileCheck -allow-deprecated-dag-overlap --check-prefix=NOOPT %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 dso_local 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; Simple test with a try-catch 21; 22; void foo(); 23; void test_catch() { 24; try { 25; foo(); 26; } catch (int) { 27; } 28; } 29 30; CHECK-LABEL: test_catch: 31; CHECK: global.get ${{.+}}=, __stack_pointer 32; CHECK: try 33; CHECK: call foo 34; CHECK: catch $[[EXN:[0-9]+]]=, __cpp_exception 35; CHECK: global.set __stack_pointer 36; CHECK: i32.{{store|const}} {{.*}} __wasm_lpad_context 37; CHECK: call $drop=, _Unwind_CallPersonality, $[[EXN]] 38; CHECK: block 39; CHECK: br_if 0 40; CHECK: call $drop=, __cxa_begin_catch 41; CHECK: call __cxa_end_catch 42; CHECK: br 1 43; CHECK: end_block 44; CHECK: rethrow 0 45; CHECK: end_try 46define void @test_catch() 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 @llvm.wasm.rethrow() [ "funclet"(token %1) ] 69 unreachable 70 71try.cont: ; preds = %catch, %entry 72 ret void 73} 74 75; Destructor (cleanup) test 76; 77; void foo(); 78; struct Temp { 79; ~Temp() {} 80; }; 81; void test_cleanup() { 82; Temp t; 83; foo(); 84; } 85 86; CHECK-LABEL: test_cleanup: 87; CHECK: try 88; CHECK: call foo 89; CHECK: catch_all 90; CHECK: global.set __stack_pointer 91; CHECK: call $drop=, _ZN4TempD2Ev 92; CHECK: rethrow 0 93; CHECK: end_try 94define void @test_cleanup() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 95entry: 96 %t = alloca %struct.Temp, align 1 97 invoke void @foo() 98 to label %invoke.cont unwind label %ehcleanup 99 100invoke.cont: ; preds = %entry 101 %call = call %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* %t) 102 ret void 103 104ehcleanup: ; preds = %entry 105 %0 = cleanuppad within none [] 106 %call1 = call %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* %t) [ "funclet"(token %0) ] 107 cleanupret from %0 unwind to caller 108} 109 110; Calling a function that may throw within a 'catch (...)' generates a 111; temrinatepad, because __cxa_end_catch() also can throw within 'catch (...)'. 112; 113; void foo(); 114; void test_terminatepad() { 115; try { 116; foo(); 117; } catch (...) { 118; foo(); 119; } 120; } 121 122; CHECK-LABEL: test_terminatepad 123; CHECK: try 124; CHECK: call foo 125; CHECK: catch 126; CHECK: call $drop=, __cxa_begin_catch 127; CHECK: try 128; CHECK: call foo 129; CHECK: catch_all 130; CHECK: try 131; CHECK: call __cxa_end_catch 132; CHECK: catch $[[EXN:[0-9]+]]=, __cpp_exception 133; CHECK: call __clang_call_terminate, $[[EXN]] 134; CHECK: unreachable 135; CHECK: end_try 136; CHECK: rethrow 137; CHECK: end_try 138; CHECK: call __cxa_end_catch 139; CHECK: end_try 140define void @test_terminatepad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 141entry: 142 invoke void @foo() 143 to label %try.cont unwind label %catch.dispatch 144 145catch.dispatch: ; preds = %entry 146 %0 = catchswitch within none [label %catch.start] unwind to caller 147 148catch.start: ; preds = %catch.dispatch 149 %1 = catchpad within %0 [i8* null] 150 %2 = call i8* @llvm.wasm.get.exception(token %1) 151 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 152 %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 153 invoke void @foo() [ "funclet"(token %1) ] 154 to label %invoke.cont1 unwind label %ehcleanup 155 156invoke.cont1: ; preds = %catch.start 157 call void @__cxa_end_catch() [ "funclet"(token %1) ] 158 catchret from %1 to label %try.cont 159 160try.cont: ; preds = %invoke.cont1, %entry 161 ret void 162 163ehcleanup: ; preds = %catch.start 164 %5 = cleanuppad within %1 [] 165 invoke void @__cxa_end_catch() [ "funclet"(token %5) ] 166 to label %invoke.cont2 unwind label %terminate 167 168invoke.cont2: ; preds = %ehcleanup 169 cleanupret from %5 unwind to caller 170 171terminate: ; preds = %ehcleanup 172 %6 = cleanuppad within %5 [] 173 %7 = call i8* @llvm.wasm.get.exception(token %6) 174 call void @__clang_call_terminate(i8* %7) [ "funclet"(token %6) ] 175 unreachable 176} 177 178; Tests a case when there are multiple BBs within a terminate pad. This kind of 179; structure is not generated by clang, but can generated by code 180; transformations. After LateEHPrepare, there should be a single 'terminate' BB 181; with these instructions: 182 183; %exn = catch $__cpp_exception 184; call @__clang_call_terminate(%exn) 185; unreachable 186 187; NOOPT-LABEL: test_split_terminatepad 188define void @test_split_terminatepad(i1 %arg) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 189entry: 190 invoke void @foo() 191 to label %try.cont unwind label %catch.dispatch 192 193catch.dispatch: ; preds = %entry 194 %0 = catchswitch within none [label %catch.start] unwind to caller 195 196; NOOPT: catch 197catch.start: ; preds = %catch.dispatch 198 %1 = catchpad within %0 [i8* null] 199 %2 = call i8* @llvm.wasm.get.exception(token %1) 200 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 201 %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 202 invoke void @foo() [ "funclet"(token %1) ] 203 to label %invoke.cont1 unwind label %ehcleanup 204 205invoke.cont1: ; preds = %catch.start 206 call void @__cxa_end_catch() [ "funclet"(token %1) ] 207 catchret from %1 to label %try.cont 208 209try.cont: ; preds = %invoke.cont1, %entry 210 ret void 211 212; NOOPT: catch_all 213ehcleanup: ; preds = %catch.start 214 %5 = cleanuppad within %1 [] 215 invoke void @__cxa_end_catch() [ "funclet"(token %5) ] 216 to label %invoke.cont2 unwind label %terminate 217 218invoke.cont2: ; preds = %ehcleanup 219 cleanupret from %5 unwind to caller 220 221; This weird structure of split terminate pads are not generated by clang, but 222; we cannot guarantee this kind of multi-BB terminate pads cannot be generated 223; by code transformations. This structure is manually created for this test. 224; NOOPT: catch $[[EXN:[0-9]+]]=, __cpp_exception 225; NOOPT-NEXT: global.set __stack_pointer 226; NOOPT-NEXT: call __clang_call_terminate, $[[EXN]] 227; NOOPT-NEXT: unreachable 228 229terminate: ; preds = %ehcleanup 230 %6 = cleanuppad within %5 [] 231 %7 = call i8* @llvm.wasm.get.exception(token %6) 232 br i1 %arg, label %terminate.split1, label %terminate.split2 233 234terminate.split1: 235 call void @__clang_call_terminate(i8* %7) [ "funclet"(token %6) ] 236 unreachable 237 238terminate.split2: 239 ; This is to test a hypothetical case that a call to __clang_call_terminate is 240 ; duplicated within a terminate pad 241 call void @__clang_call_terminate(i8* %7) [ "funclet"(token %6) ] 242 unreachable 243} 244 245; Tests prologues and epilogues are not generated within EH scopes. 246; They should not be treated as funclets; BBs starting with a catch instruction 247; should not have a prologue, and BBs ending with a catchret/cleanupret should 248; not have an epilogue. This is separate from __stack_pointer restoring 249; instructions after a catch instruction. 250; 251; void bar(int) noexcept; 252; void test_no_prolog_epilog_in_ehpad() { 253; int stack_var = 0; 254; bar(stack_var); 255; try { 256; foo(); 257; } catch (int) { 258; foo(); 259; } 260; } 261 262; CHECK-LABEL: test_no_prolog_epilog_in_ehpad 263; CHECK: try 264; CHECK: call foo 265; CHECK: catch 266; CHECK-NOT: global.get $push{{.+}}=, __stack_pointer 267; CHECK: global.set __stack_pointer 268; CHECK: block 269; CHECK: block 270; CHECK: br_if 0 271; CHECK: call $drop=, __cxa_begin_catch 272; CHECK: try 273; CHECK: call foo 274; CHECK: catch 275; CHECK-NOT: global.get $push{{.+}}=, __stack_pointer 276; CHECK: global.set __stack_pointer 277; CHECK: call __cxa_end_catch 278; CHECK: rethrow 279; CHECK-NOT: global.set __stack_pointer, $pop{{.+}} 280; CHECK: end_try 281; CHECK: end_block 282; CHECK: rethrow 283; CHECK: end_block 284; CHECK-NOT: global.set __stack_pointer, $pop{{.+}} 285; CHECK: call __cxa_end_catch 286; CHECK: end_try 287define void @test_no_prolog_epilog_in_ehpad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 288entry: 289 %stack_var = alloca i32, align 4 290 call void @bar(i32* %stack_var) 291 invoke void @foo() 292 to label %try.cont unwind label %catch.dispatch 293 294catch.dispatch: ; preds = %entry 295 %0 = catchswitch within none [label %catch.start] unwind to caller 296 297catch.start: ; preds = %catch.dispatch 298 %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*)] 299 %2 = call i8* @llvm.wasm.get.exception(token %1) 300 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 301 %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) 302 %matches = icmp eq i32 %3, %4 303 br i1 %matches, label %catch, label %rethrow 304 305catch: ; preds = %catch.start 306 %5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 307 %6 = bitcast i8* %5 to float* 308 %7 = load float, float* %6, align 4 309 invoke void @foo() [ "funclet"(token %1) ] 310 to label %invoke.cont1 unwind label %ehcleanup 311 312invoke.cont1: ; preds = %catch 313 call void @__cxa_end_catch() [ "funclet"(token %1) ] 314 catchret from %1 to label %try.cont 315 316rethrow: ; preds = %catch.start 317 call void @llvm.wasm.rethrow() [ "funclet"(token %1) ] 318 unreachable 319 320try.cont: ; preds = %invoke.cont1, %entry 321 ret void 322 323ehcleanup: ; preds = %catch 324 %8 = cleanuppad within %1 [] 325 call void @__cxa_end_catch() [ "funclet"(token %8) ] 326 cleanupret from %8 unwind to caller 327} 328 329; When a function does not have stack-allocated objects, it does not need to 330; store SP back to __stack_pointer global at the epilog. 331; 332; void foo(); 333; void test_no_sp_writeback() { 334; try { 335; foo(); 336; } catch (...) { 337; } 338; } 339 340; CHECK-LABEL: test_no_sp_writeback 341; CHECK: try 342; CHECK: call foo 343; CHECK: catch 344; CHECK: call $drop=, __cxa_begin_catch 345; CHECK: call __cxa_end_catch 346; CHECK: end_try 347; CHECK-NOT: global.set __stack_pointer 348; CHECK: return 349define void @test_no_sp_writeback() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 350entry: 351 invoke void @foo() 352 to label %try.cont unwind label %catch.dispatch 353 354catch.dispatch: ; preds = %entry 355 %0 = catchswitch within none [label %catch.start] unwind to caller 356 357catch.start: ; preds = %catch.dispatch 358 %1 = catchpad within %0 [i8* null] 359 %2 = call i8* @llvm.wasm.get.exception(token %1) 360 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 361 %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 362 call void @__cxa_end_catch() [ "funclet"(token %1) ] 363 catchret from %1 to label %try.cont 364 365try.cont: ; preds = %catch.start, %entry 366 ret void 367} 368 369; When the result of @llvm.wasm.get.exception is not used. This is created to 370; fix a bug in LateEHPrepare and this should not crash. 371define void @test_get_exception_wo_use() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 372entry: 373 invoke void @foo() 374 to label %try.cont unwind label %catch.dispatch 375 376catch.dispatch: ; preds = %entry 377 %0 = catchswitch within none [label %catch.start] unwind to caller 378 379catch.start: ; preds = %catch.dispatch 380 %1 = catchpad within %0 [i8* null] 381 %2 = call i8* @llvm.wasm.get.exception(token %1) 382 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 383 catchret from %1 to label %try.cont 384 385try.cont: ; preds = %catch.start, %entry 386 ret void 387} 388 389; Tests a case when a cleanup region (cleanuppad ~ clanupret) contains another 390; catchpad 391define void @test_complex_cleanup_region() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 392entry: 393 invoke void @foo() 394 to label %invoke.cont unwind label %ehcleanup 395 396invoke.cont: ; preds = %entry 397 ret void 398 399ehcleanup: ; preds = %entry 400 %0 = cleanuppad within none [] 401 invoke void @foo() [ "funclet"(token %0) ] 402 to label %ehcleanupret unwind label %catch.dispatch 403 404catch.dispatch: ; preds = %ehcleanup 405 %1 = catchswitch within %0 [label %catch.start] unwind label %ehcleanup.1 406 407catch.start: ; preds = %catch.dispatch 408 %2 = catchpad within %1 [i8* null] 409 %3 = call i8* @llvm.wasm.get.exception(token %2) 410 %4 = call i32 @llvm.wasm.get.ehselector(token %2) 411 catchret from %2 to label %ehcleanupret 412 413ehcleanup.1: ; preds = %catch.dispatch 414 %5 = cleanuppad within %0 [] 415 unreachable 416 417ehcleanupret: ; preds = %catch.start, %ehcleanup 418 cleanupret from %0 unwind to caller 419} 420 421declare void @foo() 422declare void @bar(i32*) 423declare i32 @__gxx_wasm_personality_v0(...) 424declare void @llvm.wasm.throw(i32, i8*) 425declare i8* @llvm.wasm.get.exception(token) 426declare i32 @llvm.wasm.get.ehselector(token) 427declare void @llvm.wasm.rethrow() 428declare i32 @llvm.eh.typeid.for(i8*) 429declare i8* @__cxa_begin_catch(i8*) 430declare void @__cxa_end_catch() 431declare void @__clang_call_terminate(i8*) 432declare %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* returned) 433 434; CHECK: __cpp_exception: 435; CHECK: .eventtype __cpp_exception i32 436