1; RUN: opt < %s -win-eh-prepare -demote-catchswitch-only -wasm-eh-prepare -S | FileCheck %s 2; RUN: opt < %s -win-eh-prepare -demote-catchswitch-only -wasm-eh-prepare -S --mattr=+atomics,+bulk-memory | FileCheck %s 3; RUN: opt < %s -passes='win-eh-prepare<demote-catchswitch-only>,wasm-eh-prepare' -S | FileCheck %s 4; RUN: opt < %s -passes='win-eh-prepare<demote-catchswitch-only>,wasm-eh-prepare' -S --mattr=+atomics,+bulk-memory | FileCheck %s 5; RUN: llc < %s -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling -stop-after=wasm-eh-prepare | FileCheck %s 6 7target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" 8target triple = "wasm32-unknown-unknown" 9 10; CHECK: @__wasm_lpad_context = external thread_local global { i32, ptr, i32 } 11 12@_ZTIi = external constant ptr 13%struct.Temp = type { i8 } 14 15; A single 'catch (int)' clause. 16; A wasm.catch() call, wasm.lsda() call, and personality call to generate a 17; selector should all be genereated after the catchpad. 18; 19; void foo(); 20; void test0() { 21; try { 22; foo(); 23; } catch (int) { 24; } 25; } 26define void @test0() personality ptr @__gxx_wasm_personality_v0 { 27; CHECK-LABEL: @test0() 28entry: 29 invoke void @foo() 30 to label %try.cont unwind label %catch.dispatch 31 32catch.dispatch: ; preds = %entry 33 %0 = catchswitch within none [label %catch.start] unwind to caller 34 35catch.start: ; preds = %catch.dispatch 36 %1 = catchpad within %0 [ptr @_ZTIi] 37 %2 = call ptr @llvm.wasm.get.exception(token %1) 38 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 39 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) 40 %matches = icmp eq i32 %3, %4 41 br i1 %matches, label %catch, label %rethrow 42; CHECK: catch.start: 43; CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad 44; CHECK-NEXT: %[[EXN:.*]] = call ptr @llvm.wasm.catch(i32 0) 45; CHECK-NEXT: call void @llvm.wasm.landingpad.index(token %[[CATCHPAD]], i32 0) 46; CHECK-NEXT: store i32 0, ptr @__wasm_lpad_context 47; CHECK-NEXT: %[[LSDA:.*]] = call ptr @llvm.wasm.lsda() 48; CHECK-NEXT: store ptr %[[LSDA]], ptr getelementptr inbounds ({ i32, ptr, i32 }, ptr @__wasm_lpad_context, i32 0, i32 1) 49; CHECK-NEXT: call i32 @_Unwind_CallPersonality(ptr %[[EXN]]) {{.*}} [ "funclet"(token %[[CATCHPAD]]) ] 50; CHECK-NEXT: %[[SELECTOR:.*]] = load i32, ptr getelementptr inbounds ({ i32, ptr, i32 }, ptr @__wasm_lpad_context, i32 0, i32 2) 51; CHECK: icmp eq i32 %[[SELECTOR]] 52 53catch: ; preds = %catch.start 54 %5 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ] 55 call void @__cxa_end_catch() [ "funclet"(token %1) ] 56 catchret from %1 to label %try.cont 57; CHECK: catch: 58; CHECK-NEXT: call ptr @__cxa_begin_catch(ptr %[[EXN]]) 59 60rethrow: ; preds = %catch.start 61 call void @llvm.wasm.rethrow() [ "funclet"(token %1) ] 62 unreachable 63 64try.cont: ; preds = %entry, %catch 65 ret void 66} 67 68; Two try-catches. 69; For the catchpad with a single 'catch (...)', only a wasm.catch() call should 70; be generated after the catchpad; wasm.landingpad.index() and personality call 71; should NOT be generated. For the other catchpad, the argument of 72; wasm.landingpad.index() should be not 1 but 0. 73; 74; void foo(); 75; void test1() { 76; try { 77; foo(); 78; } catch (...) { 79; } 80; try { 81; foo(); 82; } catch (int) { 83; } 84; } 85define void @test1() personality ptr @__gxx_wasm_personality_v0 { 86; CHECK-LABEL: @test1() 87entry: 88 invoke void @foo() 89 to label %try.cont unwind label %catch.dispatch 90 91catch.dispatch: ; preds = %entry 92 %0 = catchswitch within none [label %catch.start] unwind to caller 93 94catch.start: ; preds = %catch.dispatch 95 %1 = catchpad within %0 [ptr null] 96 %2 = call ptr @llvm.wasm.get.exception(token %1) 97 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 98 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ] 99 call void @__cxa_end_catch() [ "funclet"(token %1) ] 100 catchret from %1 to label %try.cont 101; CHECK: catch.start: 102; CHECK-NEXT: catchpad within %0 [ptr null] 103; CHECK-NOT: call void @llvm.wasm.landingpad.index 104; CHECK-NOT: store {{.*}} @__wasm_lpad_context 105; CHECK-NOT: call ptr @llvm.wasm.lsda() 106; CHECK-NOT: call i32 @_Unwind_CallPersonality 107; CHECK-NOT: load {{.*}} @__wasm_lpad_context 108 109try.cont: ; preds = %entry, %catch.start 110 invoke void @foo() 111 to label %try.cont7 unwind label %catch.dispatch2 112 113catch.dispatch2: ; preds = %try.cont 114 %5 = catchswitch within none [label %catch.start3] unwind to caller 115 116catch.start3: ; preds = %catch.dispatch2 117 %6 = catchpad within %5 [ptr @_ZTIi] 118 %7 = call ptr @llvm.wasm.get.exception(token %6) 119 %8 = call i32 @llvm.wasm.get.ehselector(token %6) 120 %9 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) 121 %matches = icmp eq i32 %8, %9 122 br i1 %matches, label %catch4, label %rethrow 123; CHECK: catch.start3: 124; CHECK: call void @llvm.wasm.landingpad.index(token %{{.+}}, i32 0) 125 126catch4: ; preds = %catch.start3 127 %10 = call ptr @__cxa_begin_catch(ptr %7) [ "funclet"(token %6) ] 128 call void @__cxa_end_catch() [ "funclet"(token %6) ] 129 catchret from %6 to label %try.cont7 130 131rethrow: ; preds = %catch.start3 132 call void @llvm.wasm.rethrow() [ "funclet"(token %6) ] 133 unreachable 134 135try.cont7: ; preds = %try.cont, %catch4 136 ret void 137} 138 139; PHI demotion test. Only the phi before catchswitch should be demoted; the phi 140; before cleanuppad should NOT. 141; 142; void foo(); 143; int bar(int) noexcept; 144; struct Temp { 145; ~Temp() {} 146; }; 147; 148; void test2() { 149; int num; 150; try { 151; Temp t; 152; num = 1; 153; foo(); 154; num = 2; 155; foo(); 156; } catch (...) { 157; bar(num); 158; } 159; try { 160; foo(); 161; num = 1; 162; foo(); 163; num = 2; 164; } catch (...) { 165; bar(num); 166; } 167; } 168define void @test2() personality ptr @__gxx_wasm_personality_v0 { 169; CHECK-LABEL: @test2 170entry: 171 %t = alloca %struct.Temp, align 1 172 invoke void @foo() 173 to label %invoke.cont unwind label %ehcleanup 174 175invoke.cont: ; preds = %entry 176 invoke void @foo() 177 to label %invoke.cont1 unwind label %ehcleanup 178 179invoke.cont1: ; preds = %invoke.cont 180 %call = call ptr @_ZN4TempD2Ev(ptr %t) 181 br label %try.cont 182 183ehcleanup: ; preds = %invoke.cont, %entry 184 %num.0 = phi i32 [ 2, %invoke.cont ], [ 1, %entry ] 185 %0 = cleanuppad within none [] 186 %call2 = call ptr @_ZN4TempD2Ev(ptr %t) [ "funclet"(token %0) ] 187 cleanupret from %0 unwind label %catch.dispatch 188; CHECK: ehcleanup: 189; CHECK-NEXT: = phi 190 191catch.dispatch: ; preds = %ehcleanup 192 %1 = catchswitch within none [label %catch.start] unwind to caller 193 194catch.start: ; preds = %catch.dispatch 195 %2 = catchpad within %1 [ptr null] 196 %3 = call ptr @llvm.wasm.get.exception(token %2) 197 %4 = call i32 @llvm.wasm.get.ehselector(token %2) 198 %5 = call ptr @__cxa_begin_catch(ptr %3) [ "funclet"(token %2) ] 199 call void @bar(i32 %num.0) [ "funclet"(token %2) ] 200 call void @__cxa_end_catch() [ "funclet"(token %2) ] 201 catchret from %2 to label %try.cont 202 203try.cont: ; preds = %catch.start, %invoke.cont1 204 invoke void @foo() 205 to label %invoke.cont3 unwind label %catch.dispatch5 206 207invoke.cont3: ; preds = %try.cont 208 invoke void @foo() 209 to label %try.cont10 unwind label %catch.dispatch5 210 211catch.dispatch5: ; preds = %invoke.cont3, %try.cont 212 %num.1 = phi i32 [ 2, %invoke.cont3 ], [ 1, %try.cont ] 213 %6 = catchswitch within none [label %catch.start6] unwind to caller 214; CHECK: catch.dispatch5: 215; CHECK-NOT: = phi 216 217catch.start6: ; preds = %catch.dispatch5 218 %7 = catchpad within %6 [ptr null] 219 %8 = call ptr @llvm.wasm.get.exception(token %7) 220 %9 = call i32 @llvm.wasm.get.ehselector(token %7) 221 %10 = call ptr @__cxa_begin_catch(ptr %8) [ "funclet"(token %7) ] 222 call void @bar(i32 %num.1) [ "funclet"(token %7) ] 223 call void @__cxa_end_catch() [ "funclet"(token %7) ] 224 catchret from %7 to label %try.cont10 225 226try.cont10: ; preds = %invoke.cont3, %catch.start6 227 ret void 228} 229 230; Tests if instructions after a call to @llvm.wasm.throw are deleted and the 231; BB's dead children are deleted. 232 233; CHECK-LABEL: @test3 234define i32 @test3(i1 %b, ptr %p) { 235entry: 236 br i1 %b, label %bb.true, label %bb.false 237 238; CHECK: bb.true: 239; CHECK-NEXT: call void @llvm.wasm.throw(i32 0, ptr %p) 240; CHECK-NEXT: unreachable 241bb.true: ; preds = %entry 242 call void @llvm.wasm.throw(i32 0, ptr %p) 243 br label %bb.true.0 244 245; CHECK-NOT: bb.true.0 246bb.true.0: ; preds = %bb.true 247 br label %merge 248 249bb.false: ; preds = %entry 250 br label %merge 251 252; CHECK: merge 253merge: ; preds = %bb.true.0, %bb.false 254 ret i32 0 255} 256 257declare void @foo() 258declare void @bar(i32) 259declare ptr @_ZN4TempD2Ev(ptr returned) 260declare i32 @__gxx_wasm_personality_v0(...) 261; Function Attrs: nounwind 262declare ptr @llvm.wasm.get.exception(token) #0 263; Function Attrs: nounwind 264declare i32 @llvm.wasm.get.ehselector(token) #0 265; Function Attrs: nounwind 266declare i32 @llvm.eh.typeid.for(ptr) #0 267; Function Attrs: noreturn 268declare void @llvm.wasm.throw(i32, ptr) #1 269; Function Attrs: noreturn 270declare void @llvm.wasm.rethrow() #1 271declare ptr @__cxa_begin_catch(ptr) 272declare void @__cxa_end_catch() 273declare void @_ZSt9terminatev() 274 275attributes #0 = { nounwind } 276attributes #1 = { noreturn } 277 278; CHECK-DAG: declare void @llvm.wasm.landingpad.index(token, i32 immarg) 279; CHECK-DAG: declare ptr @llvm.wasm.lsda() 280; CHECK-DAG: declare i32 @_Unwind_CallPersonality(ptr) 281