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