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