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