1786 lines · plain
1; REQUIRES: asserts2; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -wasm-enable-eh -wasm-use-legacy-eh -exception-model=wasm -mattr=+exception-handling,bulk-memory | FileCheck %s3; RUN: llc < %s -disable-wasm-fallthrough-return-opt -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -wasm-enable-eh -wasm-use-legacy-eh -exception-model=wasm -mattr=+exception-handling,bulk-memory4; RUN: llc < %s -O0 -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -verify-machineinstrs -wasm-enable-eh -wasm-use-legacy-eh -exception-model=wasm -mattr=+exception-handling,-bulk-memory,-bulk-memory-opt | FileCheck %s --check-prefix=NOOPT5; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -wasm-enable-eh -wasm-use-legacy-eh -exception-model=wasm -mattr=+exception-handling,-bulk-memory,-bulk-memory-opt -wasm-disable-ehpad-sort -stats 2>&1 | FileCheck %s --check-prefix=NOSORT6; RUN: llc < %s -disable-wasm-fallthrough-return-opt -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -wasm-enable-eh -wasm-use-legacy-eh -exception-model=wasm -mattr=+exception-handling,-bulk-memory,-bulk-memory-opt -wasm-disable-ehpad-sort | FileCheck %s --check-prefix=NOSORT-LOCALS7 8target triple = "wasm32-unknown-unknown"9 10@_ZTIi = external constant ptr11@_ZTId = external constant ptr12 13%class.Object = type { i8 }14%class.MyClass = type { i32 }15 16; Simple test case with two catch clauses17;18; void foo();19; void two_catches() {20; try {21; foo();22; } catch (int) {23; } catch (double) {24; }25; }26 27; CHECK-LABEL: two_catches:28; CHECK: try29; CHECK: call foo30; CHECK: catch31; CHECK: block32; CHECK: br_if 0, {{.*}} # 0: down to label[[L0:[0-9]+]]33; CHECK: call $drop=, __cxa_begin_catch34; CHECK: call __cxa_end_catch35; CHECK: br 1 # 1: down to label[[L1:[0-9]+]]36; CHECK: end_block # label[[L0]]:37; CHECK: block38; CHECK: br_if 0, {{.*}} # 0: down to label[[L2:[0-9]+]]39; CHECK: call $drop=, __cxa_begin_catch40; CHECK: call __cxa_end_catch41; CHECK: br 1 # 1: down to label[[L1]]42; CHECK: end_block # label[[L2]]:43; CHECK: rethrow 0 # to caller44; CHECK: end_try # label[[L1]]:45define void @two_catches() personality ptr @__gxx_wasm_personality_v0 {46entry:47 invoke void @foo()48 to label %try.cont unwind label %catch.dispatch49 50catch.dispatch: ; preds = %entry51 %0 = catchswitch within none [label %catch.start] unwind to caller52 53catch.start: ; preds = %catch.dispatch54 %1 = catchpad within %0 [ptr @_ZTIi, ptr @_ZTId]55 %2 = call ptr @llvm.wasm.get.exception(token %1)56 %3 = call i32 @llvm.wasm.get.ehselector(token %1)57 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi)58 %matches = icmp eq i32 %3, %459 br i1 %matches, label %catch2, label %catch.fallthrough60 61catch2: ; preds = %catch.start62 %5 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]63 call void @__cxa_end_catch() [ "funclet"(token %1) ]64 catchret from %1 to label %try.cont65 66catch.fallthrough: ; preds = %catch.start67 %6 = call i32 @llvm.eh.typeid.for(ptr @_ZTId)68 %matches1 = icmp eq i32 %3, %669 br i1 %matches1, label %catch, label %rethrow70 71catch: ; preds = %catch.fallthrough72 %7 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]73 call void @__cxa_end_catch() [ "funclet"(token %1) ]74 catchret from %1 to label %try.cont75 76rethrow: ; preds = %catch.fallthrough77 call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]78 unreachable79 80try.cont: ; preds = %catch, %catch2, %entry81 ret void82}83 84; Nested try-catches within a catch85; void nested_catch() {86; try {87; foo();88; } catch (int) {89; try {90; foo();91; } catch (int) {92; foo();93; }94; }95; }96 97; CHECK-LABEL: nested_catch:98; CHECK: try99; CHECK: call foo100; CHECK: catch101; CHECK: block102; CHECK: block103; CHECK: br_if 0, {{.*}} # 0: down to label[[L0:[0-9+]]]104; CHECK: call $drop=, __cxa_begin_catch, $0105; CHECK: try106; CHECK: try107; CHECK: call foo108; CHECK: br 3 # 3: down to label[[L1:[0-9+]]]109; CHECK: catch110; CHECK: block111; CHECK: block112; CHECK: br_if 0, {{.*}} # 0: down to label[[L2:[0-9+]]]113; CHECK: call $drop=, __cxa_begin_catch114; CHECK: try115; CHECK: call foo116; CHECK: br 2 # 2: down to label[[L3:[0-9+]]]117; CHECK: catch_all118; CHECK: call __cxa_end_catch119; CHECK: rethrow 0 # down to catch[[L4:[0-9+]]]120; CHECK: end_try121; CHECK: end_block # label[[L2]]:122; CHECK: rethrow 1 # down to catch[[L4]]123; CHECK: end_block # label[[L3]]:124; CHECK: call __cxa_end_catch125; CHECK: br 3 # 3: down to label[[L1]]126; CHECK: end_try127; CHECK: catch_all # catch[[L4]]:128; CHECK: call __cxa_end_catch129; CHECK: rethrow 0 # to caller130; CHECK: end_try131; CHECK: end_block # label[[L0]]:132; CHECK: rethrow 1 # to caller133; CHECK: end_block # label[[L1]]:134; CHECK: call __cxa_end_catch135; CHECK: end_try136define void @nested_catch() personality ptr @__gxx_wasm_personality_v0 {137entry:138 invoke void @foo()139 to label %try.cont11 unwind label %catch.dispatch140 141catch.dispatch: ; preds = %entry142 %0 = catchswitch within none [label %catch.start] unwind to caller143 144catch.start: ; preds = %catch.dispatch145 %1 = catchpad within %0 [ptr @_ZTIi]146 %2 = call ptr @llvm.wasm.get.exception(token %1)147 %3 = call i32 @llvm.wasm.get.ehselector(token %1)148 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi)149 %matches = icmp eq i32 %3, %4150 br i1 %matches, label %catch, label %rethrow151 152catch: ; preds = %catch.start153 %5 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]154 %6 = load i32, ptr %5, align 4155 invoke void @foo() [ "funclet"(token %1) ]156 to label %try.cont unwind label %catch.dispatch2157 158catch.dispatch2: ; preds = %catch159 %7 = catchswitch within %1 [label %catch.start3] unwind label %ehcleanup9160 161catch.start3: ; preds = %catch.dispatch2162 %8 = catchpad within %7 [ptr @_ZTIi]163 %9 = call ptr @llvm.wasm.get.exception(token %8)164 %10 = call i32 @llvm.wasm.get.ehselector(token %8)165 %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi)166 %matches4 = icmp eq i32 %10, %11167 br i1 %matches4, label %catch6, label %rethrow5168 169catch6: ; preds = %catch.start3170 %12 = call ptr @__cxa_begin_catch(ptr %9) [ "funclet"(token %8) ]171 %13 = load i32, ptr %12, align 4172 invoke void @foo() [ "funclet"(token %8) ]173 to label %invoke.cont8 unwind label %ehcleanup174 175invoke.cont8: ; preds = %catch6176 call void @__cxa_end_catch() [ "funclet"(token %8) ]177 catchret from %8 to label %try.cont178 179rethrow5: ; preds = %catch.start3180 invoke void @llvm.wasm.rethrow() [ "funclet"(token %8) ]181 to label %unreachable unwind label %ehcleanup9182 183try.cont: ; preds = %invoke.cont8, %catch184 call void @__cxa_end_catch() [ "funclet"(token %1) ]185 catchret from %1 to label %try.cont11186 187rethrow: ; preds = %catch.start188 call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]189 unreachable190 191try.cont11: ; preds = %try.cont, %entry192 ret void193 194ehcleanup: ; preds = %catch6195 %14 = cleanuppad within %8 []196 call void @__cxa_end_catch() [ "funclet"(token %14) ]197 cleanupret from %14 unwind label %ehcleanup9198 199ehcleanup9: ; preds = %ehcleanup, %rethrow5, %catch.dispatch2200 %15 = cleanuppad within %1 []201 call void @__cxa_end_catch() [ "funclet"(token %15) ]202 cleanupret from %15 unwind to caller203 204unreachable: ; preds = %rethrow5205 unreachable206}207 208; Nested try-catches within a try209; void nested_try() {210; try {211; try {212; foo();213; } catch (...) {214; }215; } catch (...) {216; }217; }218 219; CHECK-LABEL: nested_try:220; CHECK: try221; CHECK: try222; CHECK: call foo223; CHECK: catch224; CHECK: call $drop=, __cxa_begin_catch225; CHECK: call __cxa_end_catch226; CHECK: end_try227; CHECK: catch228; CHECK: call $drop=, __cxa_begin_catch229; CHECK: call __cxa_end_catch230; CHECK: end_try231define void @nested_try() personality ptr @__gxx_wasm_personality_v0 {232entry:233 invoke void @foo()234 to label %try.cont7 unwind label %catch.dispatch235 236catch.dispatch: ; preds = %entry237 %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch2238 239catch.start: ; preds = %catch.dispatch240 %1 = catchpad within %0 [ptr null]241 %2 = call ptr @llvm.wasm.get.exception(token %1)242 %3 = call i32 @llvm.wasm.get.ehselector(token %1)243 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]244 invoke void @__cxa_end_catch() [ "funclet"(token %1) ]245 to label %invoke.cont1 unwind label %catch.dispatch2246 247catch.dispatch2: ; preds = %catch.start, %catch.dispatch248 %5 = catchswitch within none [label %catch.start3] unwind to caller249 250catch.start3: ; preds = %catch.dispatch2251 %6 = catchpad within %5 [ptr null]252 %7 = call ptr @llvm.wasm.get.exception(token %6)253 %8 = call i32 @llvm.wasm.get.ehselector(token %6)254 %9 = call ptr @__cxa_begin_catch(ptr %7) [ "funclet"(token %6) ]255 call void @__cxa_end_catch() [ "funclet"(token %6) ]256 catchret from %6 to label %try.cont7257 258try.cont7: ; preds = %entry, %invoke.cont1, %catch.start3259 ret void260 261invoke.cont1: ; preds = %catch.start262 catchret from %1 to label %try.cont7263}264 265 266; CHECK-LABEL: loop_within_catch:267; CHECK: try268; CHECK: call foo269; CHECK: catch270; CHECK: call $drop=, __cxa_begin_catch271; CHECK: loop # label[[L0:[0-9]+]]:272; CHECK: block273; CHECK: block274; CHECK: br_if 0, {{.*}} # 0: down to label[[L1:[0-9]+]]275; CHECK: try276; CHECK: call foo277; CHECK: br 2 # 2: down to label[[L2:[0-9]+]]278; CHECK: catch279; CHECK: try280; CHECK: call __cxa_end_catch281; CHECK: catch_all282; CHECK: call _ZSt9terminatev283; CHECK: unreachable284; CHECK: end_try285; CHECK: rethrow 0 # to caller286; CHECK: end_try287; CHECK: end_block # label[[L1]]:288; CHECK: call __cxa_end_catch289; CHECK: br 2 # 2: down to label[[L3:[0-9]+]]290; CHECK: end_block # label[[L2]]:291; CHECK: br 0 # 0: up to label[[L0]]292; CHECK: end_loop293; CHECK: end_try # label[[L3]]:294define void @loop_within_catch() personality ptr @__gxx_wasm_personality_v0 {295entry:296 invoke void @foo()297 to label %try.cont unwind label %catch.dispatch298 299catch.dispatch: ; preds = %entry300 %0 = catchswitch within none [label %catch.start] unwind to caller301 302catch.start: ; preds = %catch.dispatch303 %1 = catchpad within %0 [ptr null]304 %2 = call ptr @llvm.wasm.get.exception(token %1)305 %3 = call i32 @llvm.wasm.get.ehselector(token %1)306 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]307 br label %for.cond308 309for.cond: ; preds = %for.inc, %catch.start310 %i.0 = phi i32 [ 0, %catch.start ], [ %inc, %for.inc ]311 %cmp = icmp slt i32 %i.0, 50312 br i1 %cmp, label %for.body, label %for.end313 314for.body: ; preds = %for.cond315 invoke void @foo() [ "funclet"(token %1) ]316 to label %for.inc unwind label %ehcleanup317 318for.inc: ; preds = %for.body319 %inc = add nsw i32 %i.0, 1320 br label %for.cond321 322for.end: ; preds = %for.cond323 call void @__cxa_end_catch() [ "funclet"(token %1) ]324 catchret from %1 to label %try.cont325 326try.cont: ; preds = %for.end, %entry327 ret void328 329ehcleanup: ; preds = %for.body330 %5 = cleanuppad within %1 []331 invoke void @__cxa_end_catch() [ "funclet"(token %5) ]332 to label %invoke.cont2 unwind label %terminate333 334invoke.cont2: ; preds = %ehcleanup335 cleanupret from %5 unwind to caller336 337terminate: ; preds = %ehcleanup338 %6 = cleanuppad within %5 []339 call void @_ZSt9terminatev() [ "funclet"(token %6) ]340 unreachable341}342 343; Tests if block and try markers are correctly placed. Even if two predecessors344; of the EH pad are bb2 and bb3 and their nearest common dominator is bb1, the345; TRY marker should be placed at bb0 because there's a branch from bb0 to bb2,346; and scopes cannot be interleaved.347 348; NOOPT-LABEL: block_try_markers:349; NOOPT: try350; NOOPT: block351; NOOPT: block352; NOOPT: block353; NOOPT: end_block354; NOOPT: end_block355; NOOPT: call foo356; NOOPT: end_block357; NOOPT: call bar358; NOOPT: catch {{.*}}359; NOOPT: end_try360define void @block_try_markers() personality ptr @__gxx_wasm_personality_v0 {361bb0:362 br i1 undef, label %bb1, label %bb2363 364bb1: ; preds = %bb0365 br i1 undef, label %bb3, label %bb4366 367bb2: ; preds = %bb0368 br label %try.cont369 370bb3: ; preds = %bb1371 invoke void @foo()372 to label %try.cont unwind label %catch.dispatch373 374bb4: ; preds = %bb1375 invoke void @bar()376 to label %try.cont unwind label %catch.dispatch377 378catch.dispatch: ; preds = %bb4, %bb3379 %0 = catchswitch within none [label %catch.start] unwind to caller380 381catch.start: ; preds = %catch.dispatch382 %1 = catchpad within %0 [ptr null]383 %2 = call ptr @llvm.wasm.get.exception(token %1)384 %3 = call i32 @llvm.wasm.get.ehselector(token %1)385 catchret from %1 to label %try.cont386 387try.cont: ; preds = %catch.start, %bb4, %bb3, %bb2388 ret void389}390 391; Tests if try/end_try markers are placed correctly wrt loop/end_loop markers,392; when try and loop markers are in the same BB and end_try and end_loop are in393; another BB.394; CHECK-LABEL: loop_try_markers:395; CHECK: loop396; CHECK: try397; CHECK: call foo398; CHECK: catch399; CHECK: end_try400; CHECK: end_loop401define void @loop_try_markers(ptr %p) personality ptr @__gxx_wasm_personality_v0 {402entry:403 store volatile i32 0, ptr %p404 br label %loop405 406loop: ; preds = %try.cont, %entry407 store volatile i32 1, ptr %p408 invoke void @foo()409 to label %try.cont unwind label %catch.dispatch410 411catch.dispatch: ; preds = %loop412 %0 = catchswitch within none [label %catch.start] unwind to caller413 414catch.start: ; preds = %catch.dispatch415 %1 = catchpad within %0 [ptr null]416 %2 = call ptr @llvm.wasm.get.exception(token %1)417 %3 = call i32 @llvm.wasm.get.ehselector(token %1)418 catchret from %1 to label %try.cont419 420try.cont: ; preds = %catch.start, %loop421 br label %loop422}423 424; Some of test cases below are hand-tweaked by deleting some library calls to425; simplify tests and changing the order of basic blocks to cause unwind426; destination mismatches. And we use -wasm-disable-ehpad-sort to create maximum427; number of mismatches in several tests below.428 429; - Call unwind mismatch430; 'call bar''s original unwind destination was 'C0', but after control flow431; linearization, its unwind destination incorrectly becomes 'C1'. We fix this by432; wrapping the call with a nested try-delegate that targets 'C0'.433; - Catch unwind mismatch434; If 'call foo' throws a foreign exception, it will not be caught by C1, and435; should be rethrown to the caller. But after control flow linearization, it436; will instead unwind to C0, an incorrect next EH pad. We wrap the whole437; try-catch with try-delegate that rethrows the exception to the caller to fix438; this.439 440; NOSORT-LABEL: unwind_mismatches_0:441; NOSORT: try442; --- try-delegate starts (catch unwind mismatch)443; NOSORT try444; NOSORT: try445; NOSORT: call foo446; --- try-delegate starts (call unwind mismatch)447; NOSORT: try448; NOSORT: call bar449; NOSORT: delegate 2 # label/catch{{[0-9]+}}: down to catch[[C0:[0-9]+]]450; --- try-delegate ends (call unwind mismatch)451; NOSORT: catch {{.*}} # catch[[C1:[0-9]+]]:452; NOSORT: end_try453; NOSORT: delegate 1 # label/catch{{[0-9]+}}: to caller454; --- try-delegate ends (catch unwind mismatch)455; NOSORT: catch {{.*}} # catch[[C0]]:456; NOSORT: end_try457; NOSORT: return458define void @unwind_mismatches_0() personality ptr @__gxx_wasm_personality_v0 {459bb0:460 invoke void @foo()461 to label %bb1 unwind label %catch.dispatch0462 463bb1: ; preds = %bb0464 invoke void @bar()465 to label %try.cont unwind label %catch.dispatch1466 467catch.dispatch0: ; preds = %bb0468 %0 = catchswitch within none [label %catch.start0] unwind to caller469 470catch.start0: ; preds = %catch.dispatch0471 %1 = catchpad within %0 [ptr null]472 %2 = call ptr @llvm.wasm.get.exception(token %1)473 %3 = call i32 @llvm.wasm.get.ehselector(token %1)474 catchret from %1 to label %try.cont475 476catch.dispatch1: ; preds = %bb1477 %4 = catchswitch within none [label %catch.start1] unwind to caller478 479catch.start1: ; preds = %catch.dispatch1480 %5 = catchpad within %4 [ptr null]481 %6 = call ptr @llvm.wasm.get.exception(token %5)482 %7 = call i32 @llvm.wasm.get.ehselector(token %5)483 catchret from %5 to label %try.cont484 485try.cont: ; preds = %catch.start1, %catch.start0, %bb1486 ret void487}488 489; 'call bar' and 'call baz''s original unwind destination was the caller, but490; after control flow linearization, their unwind destination incorrectly becomes491; 'C0'. We fix this by wrapping the calls with a nested try-delegate that492; rethrows the exception to the caller.493 494; And the return value of 'baz' should NOT be stackified because the BB is split495; during fixing unwind mismatches.496 497; NOSORT-LABEL: unwind_mismatches_1:498; NOSORT: try499; NOSORT: call foo500; --- try-delegate starts (call unwind mismatch)501; NOSORT: try502; NOSORT: call bar503; NOSORT: call $[[RET:[0-9]+]]=, baz504; NOSORT-NOT: call $push{{.*}}=, baz505; NOSORT: delegate 1 # label/catch{{[0-9]+}}: to caller506; --- try-delegate ends (call unwind mismatch)507; NOSORT: call nothrow, $[[RET]]508; NOSORT: return509; NOSORT: catch {{.*}} # catch[[C0:[0-9]+]]:510; NOSORT: return511; NOSORT: end_try512define void @unwind_mismatches_1() personality ptr @__gxx_wasm_personality_v0 {513bb0:514 invoke void @foo()515 to label %bb1 unwind label %catch.dispatch0516 517bb1: ; preds = %bb0518 call void @bar()519 %call = call i32 @baz()520 call void @nothrow(i32 %call) #0521 ret void522 523catch.dispatch0: ; preds = %bb0524 %0 = catchswitch within none [label %catch.start0] unwind to caller525 526catch.start0: ; preds = %catch.dispatch0527 %1 = catchpad within %0 [ptr null]528 %2 = call ptr @llvm.wasm.get.exception(token %1)529 %3 = call i32 @llvm.wasm.get.ehselector(token %1)530 catchret from %1 to label %try.cont531 532try.cont: ; preds = %catch.start0533 ret void534}535 536; The same as unwind_mismatches_0, but we have one more call 'call @foo' in bb1537; which unwinds to the caller. IN this case bb1 has two call unwind mismatches:538; 'call @foo' unwinds to the caller and 'call @bar' unwinds to catch C0.539 540; NOSORT-LABEL: unwind_mismatches_2:541; NOSORT: try542; --- try-delegate starts (catch unwind mismatch)543; NOSORT try544; NOSORT: try545; NOSORT: call foo546; --- try-delegate starts (call unwind mismatch)547; NOSORT: try548; NOSORT: call foo549; NOSORT: delegate 3 # label/catch{{[0-9]+}}: to caller550; --- try-delegate ends (call unwind mismatch)551; --- try-delegate starts (call unwind mismatch)552; NOSORT: try553; NOSORT: call bar554; NOSORT: delegate 2 # label/catch{{[0-9]+}}: down to catch[[C0:[0-9]+]]555; --- try-delegate ends (call unwind mismatch)556; NOSORT: catch {{.*}} # catch[[C1:[0-9]+]]:557; NOSORT: end_try558; NOSORT: delegate 1 # label/catch{{[0-9]+}}: to caller559; --- try-delegate ends (catch unwind mismatch)560; NOSORT: catch {{.*}} # catch[[C0]]:561; NOSORT: end_try562; NOSORT: return563define void @unwind_mismatches_2() personality ptr @__gxx_wasm_personality_v0 {564bb0:565 invoke void @foo()566 to label %bb1 unwind label %catch.dispatch0567 568bb1: ; preds = %bb0569 call void @foo()570 invoke void @bar()571 to label %try.cont unwind label %catch.dispatch1572 573catch.dispatch0: ; preds = %bb0574 %0 = catchswitch within none [label %catch.start0] unwind to caller575 576catch.start0: ; preds = %catch.dispatch0577 %1 = catchpad within %0 [ptr null]578 %2 = call ptr @llvm.wasm.get.exception(token %1)579 %3 = call i32 @llvm.wasm.get.ehselector(token %1)580 catchret from %1 to label %try.cont581 582catch.dispatch1: ; preds = %bb1583 %4 = catchswitch within none [label %catch.start1] unwind to caller584 585catch.start1: ; preds = %catch.dispatch1586 %5 = catchpad within %4 [ptr null]587 %6 = call ptr @llvm.wasm.get.exception(token %5)588 %7 = call i32 @llvm.wasm.get.ehselector(token %5)589 catchret from %5 to label %try.cont590 591try.cont: ; preds = %catch.start1, %catch.start0, %bb1592 ret void593}594 595; Similar situation as @unwind_mismatches_1. Here 'call @qux''s original unwind596; destination was the caller, but after control flow linearization, their unwind597; destination incorrectly becomes 'C0' within the function. We fix this by598; wrapping the call with a nested try-delegate that rethrows the exception to599; the caller.600 601; Because 'call @qux' pops an argument pushed by 'i32.const 5' from stack, the602; nested 'try' should be placed before `i32.const 5', not between 'i32.const 5'603; and 'call @qux'.604 605; NOSORT-LABEL: unwind_mismatches_3:606; NOSORT: try i32607; NOSORT: call foo608; --- try-delegate starts (call unwind mismatch)609; NOSORT: try610; NOSORT: i32.const $push{{[0-9]+}}=, 5611; NOSORT: call ${{[0-9]+}}=, qux612; NOSORT: delegate 1 # label/catch{{[0-9]+}}: to caller613; --- try-delegate ends (call unwind mismatch)614; NOSORT: return615; NOSORT: catch {{.*}} # catch[[C0:[0-9]+]]:616; NOSORT: return617; NOSORT: end_try618define i32 @unwind_mismatches_3() personality ptr @__gxx_wasm_personality_v0 {619bb0:620 invoke void @foo()621 to label %bb1 unwind label %catch.dispatch0622 623bb1: ; preds = %bb0624 %0 = call i32 @qux(i32 5)625 ret i32 %0626 627catch.dispatch0: ; preds = %bb0628 %1 = catchswitch within none [label %catch.start0] unwind to caller629 630catch.start0: ; preds = %catch.dispatch0631 %2 = catchpad within %1 [ptr null]632 %3 = call ptr @llvm.wasm.get.exception(token %2)633 %j = call i32 @llvm.wasm.get.ehselector(token %2)634 catchret from %2 to label %try.cont635 636try.cont: ; preds = %catch.start0637 ret i32 0638}639 640; We have two call unwind unwind mismatches:641; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the642; CFG, when it is supposed to unwind to another EH pad.643; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the644; CFG, when it is supposed to unwind to the caller.645; We also have a catch unwind mismatch: If an exception is not caught by the646; first catch because it is a non-C++ exception, it shouldn't unwind to the next647; catch, but it should unwind to the caller.648 649; NOSORT-LABEL: unwind_mismatches_4:650; NOSORT: try651; --- try-delegate starts (catch unwind mismatch)652; NOSORT: try653; NOSORT: try654; NOSORT: call foo655; --- try-delegate starts (call unwind mismatch)656; NOSORT: try657; NOSORT: call bar658; NOSORT: delegate 2 # label/catch{{[0-9]+}}: down to catch[[C0:[0-9]+]]659; --- try-delegate ends (call unwind mismatch)660; NOSORT: catch661; NOSORT: call {{.*}} __cxa_begin_catch662; --- try-delegate starts (call unwind mismatch)663; NOSORT: try664; NOSORT: call __cxa_end_catch665; NOSORT: delegate 3 # label/catch{{[0-9]+}}: to caller666; --- try-delegate ends (call unwind mismatch)667; NOSORT: end_try668; NOSORT: delegate 1 # label/catch{{[0-9]+}}: to caller669; --- try-delegate ends (catch unwind mismatch)670; NOSORT: catch {{.*}} # catch[[C0]]:671; NOSORT: call {{.*}} __cxa_begin_catch672; NOSORT: call __cxa_end_catch673; NOSORT: end_try674; NOSORT: return675define void @unwind_mismatches_4() personality ptr @__gxx_wasm_personality_v0 {676bb0:677 invoke void @foo()678 to label %bb1 unwind label %catch.dispatch0679 680bb1: ; preds = %bb0681 invoke void @bar()682 to label %try.cont unwind label %catch.dispatch1683 684catch.dispatch0: ; preds = %bb0685 %0 = catchswitch within none [label %catch.start0] unwind to caller686 687catch.start0: ; preds = %catch.dispatch0688 %1 = catchpad within %0 [ptr null]689 %2 = call ptr @llvm.wasm.get.exception(token %1)690 %3 = call i32 @llvm.wasm.get.ehselector(token %1)691 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]692 call void @__cxa_end_catch() [ "funclet"(token %1) ]693 catchret from %1 to label %try.cont694 695catch.dispatch1: ; preds = %bb1696 %5 = catchswitch within none [label %catch.start1] unwind to caller697 698catch.start1: ; preds = %catch.dispatch1699 %6 = catchpad within %5 [ptr null]700 %7 = call ptr @llvm.wasm.get.exception(token %6)701 %8 = call i32 @llvm.wasm.get.ehselector(token %6)702 %9 = call ptr @__cxa_begin_catch(ptr %7) [ "funclet"(token %6) ]703 call void @__cxa_end_catch() [ "funclet"(token %6) ]704 catchret from %6 to label %try.cont705 706try.cont: ; preds = %catch.start1, %catch.start0, %bb1707 ret void708}709 710; This crashed when updating EHPadStack within fixCallUniwindMismatch had a bug.711; This should not crash and try-delegate has to be created around 'call @baz',712; because the initial TRY placement for 'call @quux' was done before 'call @baz'713; because 'call @baz''s return value is stackified.714 715; CHECK-LABEL: unwind_mismatches_5:716; CHECK: try717; --- try-delegate starts (call unwind mismatch)718; CHECK: try719; CHECK: call $[[RET:[0-9]+]]=, baz720; CHECK: delegate 1721; --- try-delegate ends (call unwind mismatch)722; CHECK: call quux, $[[RET]]723; CHECK: catch_all724; CHECK: end_try725define void @unwind_mismatches_5() personality ptr @__gxx_wasm_personality_v0 {726entry:727 %call = call i32 @baz()728 invoke void @quux(i32 %call)729 to label %invoke.cont unwind label %ehcleanup730 731ehcleanup: ; preds = %entry732 %0 = cleanuppad within none []733 cleanupret from %0 unwind to caller734 735invoke.cont: ; preds = %entry736 unreachable737}738 739; The structure is similar to unwind_mismatches_0, where the call to 'bar''s740; original unwind destination is catch.dispatch1 but after placing markers it741; unwinds to catch.dispatch0, which we fix. This additionally has a loop before742; the real unwind destination (catch.dispatch1).743 744; NOSORT-LABEL: unwind_mismatches_with_loop:745; NOSORT: try746; NOSORT: try747; NOSORT: try748; NOSORT: call foo749; NOSORT: try750; NOSORT: call bar751; NOSORT: delegate 3 # label/catch{{[0-9]+}}: down to catch[[C0:[0-9]+]]752; NOSORT: catch $drop=, __cpp_exception753; NOSORT: end_try754; NOSORT: delegate 2 # label/catch{{[0-9]+}}: to caller755; NOSORT: loop756; NOSORT: call foo757; NOSORT: end_loop758; NOSORT: catch $drop=, __cpp_exception # catch[[C0]]:759; NOSORT: return760; NOSORT: end_try761define void @unwind_mismatches_with_loop() personality ptr @__gxx_wasm_personality_v0 {762bb0:763 invoke void @foo()764 to label %bb1 unwind label %catch.dispatch0765 766bb1: ; preds = %bb0767 invoke void @bar()768 to label %bb2 unwind label %catch.dispatch1769 770catch.dispatch0: ; preds = %bb0771 %0 = catchswitch within none [label %catch.start0] unwind to caller772 773catch.start0: ; preds = %catch.dispatch0774 %1 = catchpad within %0 [ptr null]775 %2 = call ptr @llvm.wasm.get.exception(token %1)776 %3 = call i32 @llvm.wasm.get.ehselector(token %1)777 catchret from %1 to label %bb2778 779bb2:780 invoke void @foo()781 to label %bb3 unwind label %catch.dispatch1782 783bb3: ; preds = %bb14784 br label %bb2785 786catch.dispatch1: ; preds = %bb1787 %4 = catchswitch within none [label %catch.start1] unwind to caller788 789catch.start1: ; preds = %catch.dispatch1790 %5 = catchpad within %4 [ptr null]791 %6 = call ptr @llvm.wasm.get.exception(token %5)792 %7 = call i32 @llvm.wasm.get.ehselector(token %5)793 catchret from %5 to label %try.cont794 795try.cont: ; preds = %catch.start1, %catch.start0, %bb1796 ret void797}798 799; Tests the case when TEE stackifies a register in RegStackify but it gets800; unstackified in fixCallUnwindMismatches in CFGStackify.801 802; NOSORT-LOCALS-LABEL: unstackify_when_fixing_unwind_mismatch:803define void @unstackify_when_fixing_unwind_mismatch(i32 %x) personality ptr @__gxx_wasm_personality_v0 {804bb0:805 invoke void @foo()806 to label %bb1 unwind label %catch.dispatch0807 808bb1: ; preds = %bb0809 %t = add i32 %x, 4810 ; This %addr is used in multiple places, so tee is introduced in RegStackify,811 ; which stackifies the use of %addr in store instruction. A tee has two dest812 ; registers, the first of which is stackified and the second is not.813 ; But when we introduce a nested try-delegate in fixCallUnwindMismatches in814 ; CFGStackify, we end up unstackifying the first dest register. In that case,815 ; we convert that tee into a copy.816 %addr = inttoptr i32 %t to ptr817 %load = load i32, ptr %addr818 %call = call i32 @baz()819 %add = add i32 %load, %call820 store i32 %add, ptr %addr821 ret void822; NOSORT-LOCALS: i32.add823; NOSORT-LOCALS-NOT: local.tee824; NOSORT-LOCALS-NEXT: local.set825 826catch.dispatch0: ; preds = %bb0827 %0 = catchswitch within none [label %catch.start0] unwind to caller828 829catch.start0: ; preds = %catch.dispatch0830 %1 = catchpad within %0 [ptr null]831 %2 = call ptr @llvm.wasm.get.exception(token %1)832 %3 = call i32 @llvm.wasm.get.ehselector(token %1)833 catchret from %1 to label %try.cont834 835try.cont: ; preds = %catch.start0836 ret void837}838 839; In CFGSort, EH pads should be sorted as soon as it is available and840; 'Preferred' queue and should NOT be entered into 'Ready' queue unless we are841; in the middle of sorting another region that does not contain the EH pad. In842; this example, 'catch.start' should be sorted right after 'if.then' is sorted843; (before 'cont' is sorted) and there should not be any unwind destination844; mismatches in CFGStackify.845 846; NOOPT-LABEL: cfg_sort_order:847; NOOPT: block848; NOOPT: try849; NOOPT: call foo850; NOOPT: catch851; NOOPT: end_try852; NOOPT: call foo853; NOOPT: end_block854; NOOPT: return855define void @cfg_sort_order(i32 %arg) personality ptr @__gxx_wasm_personality_v0 {856entry:857 %tobool = icmp ne i32 %arg, 0858 br i1 %tobool, label %if.then, label %if.end859 860catch.dispatch: ; preds = %if.then861 %0 = catchswitch within none [label %catch.start] unwind to caller862 863catch.start: ; preds = %catch.dispatch864 %1 = catchpad within %0 [ptr null]865 %2 = call ptr @llvm.wasm.get.exception(token %1)866 %3 = call i32 @llvm.wasm.get.ehselector(token %1)867 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]868 call void @__cxa_end_catch() [ "funclet"(token %1) ]869 catchret from %1 to label %if.end870 871if.then: ; preds = %entry872 invoke void @foo()873 to label %cont unwind label %catch.dispatch874 875cont: ; preds = %if.then876 call void @foo()877 br label %if.end878 879if.end: ; preds = %cont, %catch.start, %entry880 ret void881}882 883; Intrinsics like memcpy, memmove, and memset don't throw and are lowered into884; calls to external symbols (not global addresses) in instruction selection,885; which will be eventually lowered to library function calls.886; Because this test runs with -wasm-disable-ehpad-sort, these library calls in887; invoke.cont BB fall within try~end_try, but they shouldn't cause crashes or888; unwinding destination mismatches in CFGStackify.889 890; NOSORT-LABEL: mem_intrinsics:891; NOSORT: try892; NOSORT: call foo893; NOSORT: call {{.*}} memcpy894; NOSORT: call {{.*}} memmove895; NOSORT: call {{.*}} memset896; NOSORT: return897; NOSORT: catch_all898; NOSORT: rethrow 0899; NOSORT: end_try900define void @mem_intrinsics(ptr %a, ptr %b) personality ptr @__gxx_wasm_personality_v0 {901entry:902 %o = alloca %class.Object, align 1903 invoke void @foo()904 to label %invoke.cont unwind label %ehcleanup905 906invoke.cont: ; preds = %entry907 call void @llvm.memcpy.p0.p0.i32(ptr %a, ptr %b, i32 100, i1 false)908 call void @llvm.memmove.p0.p0.i32(ptr %a, ptr %b, i32 100, i1 false)909 call void @llvm.memset.p0.i32(ptr %a, i8 0, i32 100, i1 false)910 %call = call ptr @_ZN6ObjectD2Ev(ptr %o)911 ret void912 913ehcleanup: ; preds = %entry914 %0 = cleanuppad within none []915 %call2 = call ptr @_ZN6ObjectD2Ev(ptr %o) [ "funclet"(token %0) ]916 cleanupret from %0 unwind to caller917}918 919; Tests if 'try' marker is placed correctly. In this test, 'try' should be920; placed before the call to 'nothrow_i32' and not between the call to921; 'nothrow_i32' and 'fun', because the return value of 'nothrow_i32' is922; stackified and pushed onto the stack to be consumed by the call to 'fun'.923 924; CHECK-LABEL: try_marker_with_stackified_input:925; CHECK: try926; CHECK: call $push{{.*}}=, nothrow_i32927; CHECK: call fun, $pop{{.*}}928define void @try_marker_with_stackified_input() personality ptr @__gxx_wasm_personality_v0 {929entry:930 %call = call i32 @nothrow_i32()931 invoke void @fun(i32 %call)932 to label %invoke.cont unwind label %terminate933 934invoke.cont: ; preds = %entry935 ret void936 937terminate: ; preds = %entry938 %0 = cleanuppad within none []939 call void @_ZSt9terminatev() [ "funclet"(token %0) ]940 unreachable941}942 943; This crashed on debug mode (= when NDEBUG is not defined) when the logic for944; computing the innermost region was not correct, in which a loop region945; contains an exception region. This should pass CFGSort without crashing.946define void @loop_exception_region() personality ptr @__gxx_wasm_personality_v0 {947entry:948 %e = alloca %class.MyClass, align 4949 br label %for.cond950 951for.cond: ; preds = %for.inc, %entry952 %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]953 %cmp = icmp slt i32 %i.0, 9954 br i1 %cmp, label %for.body, label %for.end955 956for.body: ; preds = %for.cond957 invoke void @quux(i32 %i.0)958 to label %for.inc unwind label %catch.dispatch959 960catch.dispatch: ; preds = %for.body961 %0 = catchswitch within none [label %catch.start] unwind to caller962 963catch.start: ; preds = %catch.dispatch964 %1 = catchpad within %0 [ptr @_ZTI7MyClass]965 %2 = call ptr @llvm.wasm.get.exception(token %1)966 %3 = call i32 @llvm.wasm.get.ehselector(token %1)967 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTI7MyClass)968 %matches = icmp eq i32 %3, %4969 br i1 %matches, label %catch, label %rethrow970 971catch: ; preds = %catch.start972 %5 = call ptr @__cxa_get_exception_ptr(ptr %2) [ "funclet"(token %1) ]973 %call = call ptr @_ZN7MyClassC2ERKS_(ptr %e, ptr dereferenceable(4) %5) [ "funclet"(token %1) ]974 %6 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]975 %7 = load i32, ptr %e, align 4976 invoke void @quux(i32 %7) [ "funclet"(token %1) ]977 to label %invoke.cont2 unwind label %ehcleanup978 979invoke.cont2: ; preds = %catch980 %call3 = call ptr @_ZN7MyClassD2Ev(ptr %e) [ "funclet"(token %1) ]981 call void @__cxa_end_catch() [ "funclet"(token %1) ]982 catchret from %1 to label %for.inc983 984rethrow: ; preds = %catch.start985 call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]986 unreachable987 988for.inc: ; preds = %invoke.cont2, %for.body989 %inc = add nsw i32 %i.0, 1990 br label %for.cond991 992ehcleanup: ; preds = %catch993 %8 = cleanuppad within %1 []994 %call4 = call ptr @_ZN7MyClassD2Ev(ptr %e) [ "funclet"(token %8) ]995 invoke void @__cxa_end_catch() [ "funclet"(token %8) ]996 to label %invoke.cont6 unwind label %terminate7997 998invoke.cont6: ; preds = %ehcleanup999 cleanupret from %8 unwind to caller1000 1001for.end: ; preds = %for.cond1002 ret void1003 1004terminate7: ; preds = %ehcleanup1005 %9 = cleanuppad within %8 []1006 call void @_ZSt9terminatev() [ "funclet"(token %9) ]1007 unreachable1008}1009 1010; Tests if CFGStackify's removeUnnecessaryInstrs() removes unnecessary branches1011; correctly. The code is in the form below, where 'br' is unnecessary because1012; after running the 'try' body the control flow will fall through to bb2 anyway.1013 1014; bb0:1015; try1016; ...1017; br bb2 <- Not necessary1018; bb1 (ehpad):1019; catch1020; ...1021; bb2: <- Continuation BB1022; end1023; CHECK-LABEL: remove_unnecessary_instrs:1024define void @remove_unnecessary_instrs(i32 %n) personality ptr @__gxx_wasm_personality_v0 {1025entry:1026 invoke void @foo()1027 to label %for.body unwind label %catch.dispatch1028 1029for.body: ; preds = %for.end, %entry1030 %i = phi i32 [ %inc, %for.end ], [ 0, %entry ]1031 invoke void @foo()1032 to label %for.end unwind label %catch.dispatch1033 1034; Before going to CFGStackify, this BB will have a conditional branch followed1035; by an unconditional branch. CFGStackify should remove only the unconditional1036; one.1037for.end: ; preds = %for.body1038 %inc = add nuw nsw i32 %i, 11039 %exitcond = icmp eq i32 %inc, %n1040 br i1 %exitcond, label %try.cont, label %for.body1041; CHECK: br_if1042; CHECK-NOT: br1043; CHECK: end_loop1044; CHECK: catch1045 1046catch.dispatch: ; preds = %for.body, %entry1047 %0 = catchswitch within none [label %catch.start] unwind to caller1048 1049catch.start: ; preds = %catch.dispatch1050 %1 = catchpad within %0 [ptr null]1051 %2 = call ptr @llvm.wasm.get.exception(token %1)1052 %3 = call i32 @llvm.wasm.get.ehselector(token %1)1053 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]1054 call void @__cxa_end_catch() [ "funclet"(token %1) ]1055 catchret from %1 to label %try.cont1056 1057try.cont: ; preds = %catch.start, %for.end1058 ret void1059}1060 1061; void foo();1062; void remove_unnecessary_br() {1063; try {1064; foo();1065; try {1066; foo();1067; } catch (...) {1068; }1069; } catch (...) {1070; }1071; }1072;1073; This tests whether the 'br' can be removed in code in the form as follows.1074; Here 'br' is inside an inner try, whose 'end' is in another EH pad. In this1075; case, after running an inner try body, the control flow should fall through to1076; bb3, so the 'br' in the code is unnecessary.1077 1078; bb0:1079; try1080; try1081; ...1082; br bb3 <- Not necessary1083; bb1:1084; catch1085; bb2:1086; end_try1087; catch1088; ...1089; bb3: <- Continuation BB1090; end1091;1092; CHECK-LABEL: remove_unnecessary_br:1093define void @remove_unnecessary_br() personality ptr @__gxx_wasm_personality_v0 {1094; CHECK: call foo1095entry:1096 invoke void @foo()1097 to label %invoke.cont unwind label %catch.dispatch31098 1099; CHECK: call foo1100; CHECK-NOT: br1101invoke.cont: ; preds = %entry1102 invoke void @foo()1103 to label %try.cont8 unwind label %catch.dispatch1104 1105catch.dispatch: ; preds = %invoke.cont1106 %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch31107 1108; CHECK: catch1109catch.start: ; preds = %catch.dispatch1110 %1 = catchpad within %0 [ptr null]1111 %2 = call ptr @llvm.wasm.get.exception(token %1)1112 %3 = call i32 @llvm.wasm.get.ehselector(token %1)1113 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]1114 invoke void @__cxa_end_catch() [ "funclet"(token %1) ]1115 to label %invoke.cont2 unwind label %catch.dispatch31116 1117catch.dispatch3: ; preds = %catch.start, %catch.dispatch, %entry1118 %5 = catchswitch within none [label %catch.start4] unwind to caller1119 1120catch.start4: ; preds = %catch.dispatch31121 %6 = catchpad within %5 [ptr null]1122 %7 = call ptr @llvm.wasm.get.exception(token %6)1123 %8 = call i32 @llvm.wasm.get.ehselector(token %6)1124 %9 = call ptr @__cxa_begin_catch(ptr %7) [ "funclet"(token %6) ]1125 call void @__cxa_end_catch() [ "funclet"(token %6) ]1126 catchret from %6 to label %try.cont81127 1128try.cont8: ; preds = %invoke.cont2, %catch.start4, %invoke.cont1129 ret void1130 1131invoke.cont2: ; preds = %catch.start1132 catchret from %1 to label %try.cont81133}1134 1135; Here an exception is semantically contained in a loop. 'ehcleanup' BB belongs1136; to the exception, but does not belong to the loop (because it does not have a1137; path back to the loop header), and is placed after the loop latch block1138; 'invoke.cont' intentionally. This tests if 'end_loop' marker is placed1139; correctly not right after 'invoke.cont' part but after 'ehcleanup' part.1140; NOSORT-LABEL: loop_contains_exception:1141; NOSORT: loop1142; NOSORT: try1143; NOSORT: try1144; NOSORT: end_try1145; NOSORT: end_try1146; NOSORT: end_loop1147define void @loop_contains_exception(i32 %n) personality ptr @__gxx_wasm_personality_v0 {1148entry:1149 br label %while.cond1150 1151while.cond: ; preds = %invoke.cont, %entry1152 %n.addr.0 = phi i32 [ %n, %entry ], [ %dec, %invoke.cont ]1153 %tobool = icmp ne i32 %n.addr.0, 01154 br i1 %tobool, label %while.body, label %while.end1155 1156while.body: ; preds = %while.cond1157 %dec = add nsw i32 %n.addr.0, -11158 invoke void @foo()1159 to label %while.end unwind label %catch.dispatch1160 1161catch.dispatch: ; preds = %while.body1162 %0 = catchswitch within none [label %catch.start] unwind to caller1163 1164catch.start: ; preds = %catch.dispatch1165 %1 = catchpad within %0 [ptr null]1166 %2 = call ptr @llvm.wasm.get.exception(token %1)1167 %3 = call i32 @llvm.wasm.get.ehselector(token %1)1168 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]1169 invoke void @__cxa_end_catch() [ "funclet"(token %1) ]1170 to label %invoke.cont unwind label %ehcleanup1171 1172invoke.cont: ; preds = %catch.start1173 catchret from %1 to label %while.cond1174 1175ehcleanup: ; preds = %catch.start1176 %5 = cleanuppad within %1 []1177 call void @_ZSt9terminatev() [ "funclet"(token %5) ]1178 unreachable1179 1180while.end: ; preds = %while.body, %while.cond1181 ret void1182}1183 1184; When the function return type is non-void and 'end' instructions are at the1185; very end of a function, CFGStackify's fixEndsAtEndOfFunction function fixes1186; the corresponding block/loop/try's type to match the function's return type.1187; But when a `try`'s type is fixed, we should also check `end` instructions1188; before its corresponding `catch_all`, because both `try` and `catch_all` body1189; should satisfy the return type requirements.1190 1191; NOSORT-LABEL: fix_function_end_return_type_with_try_catch:1192; NOSORT: try i321193; NOSORT: loop i321194; NOSORT: end_loop1195; NOSORT: catch_all1196; NOSORT: end_try1197; NOSORT-NEXT: end_function1198define i32 @fix_function_end_return_type_with_try_catch(i32 %n) personality ptr @__gxx_wasm_personality_v0 {1199entry:1200 %t = alloca %class.Object, align 11201 br label %for.cond1202 1203for.cond: ; preds = %for.inc, %entry1204 %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]1205 %cmp = icmp slt i32 %i.0, %n1206 br label %for.body1207 1208for.body: ; preds = %for.cond1209 %div = sdiv i32 %n, 21210 %cmp1 = icmp eq i32 %i.0, %div1211 br i1 %cmp1, label %if.then, label %for.inc1212 1213if.then: ; preds = %for.body1214 %call = invoke i32 @baz()1215 to label %invoke.cont unwind label %ehcleanup1216 1217invoke.cont: ; preds = %if.then1218 %call2 = call ptr @_ZN6ObjectD2Ev(ptr %t)1219 ret i32 %call1220 1221for.inc: ; preds = %for.body1222 %inc = add nsw i32 %i.0, 11223 br label %for.cond1224 1225ehcleanup: ; preds = %if.then1226 %0 = cleanuppad within none []1227 %call3 = call ptr @_ZN6ObjectD2Ev(ptr %t) [ "funclet"(token %0) ]1228 cleanupret from %0 unwind to caller1229}1230 1231; This tests if invalidated branch destinations after fixing catch unwind1232; mismatches are correctly remapped. For example, we have this code and suppose1233; we need to wrap this try-catch-end in this code with a try-delegate to fix a1234; catch unwind mismatch:1235 ; - Before:1236; block1237; br (a)1238; try1239; catch1240; end_try1241; end_block1242; <- (a)1243;1244; - After1245; block1246; br (a)1247; try1248; try1249; catch1250; end_try1251; <- (a)1252; delegate1253; end_block1254; <- (b)1255; After adding a try-delegate, the 'br's destination BB, where (a) points,1256; becomes invalid because it incorrectly branches into an inner scope. The1257; destination should change to the BB where (b) points.1258 1259; NOSORT-LABEL: branch_remapping_after_fixing_unwind_mismatches_0:1260; NOSORT: try1261; NOSORT: br_if 01262define void @branch_remapping_after_fixing_unwind_mismatches_0(i1 %arg) personality ptr @__gxx_wasm_personality_v0 {1263entry:1264 br i1 %arg, label %bb0, label %dest1265 1266bb0: ; preds = %entry1267 invoke void @foo()1268 to label %bb1 unwind label %catch.dispatch01269 1270bb1: ; preds = %bb01271 invoke void @bar()1272 to label %try.cont unwind label %catch.dispatch11273 1274catch.dispatch0: ; preds = %bb01275 %0 = catchswitch within none [label %catch.start0] unwind to caller1276 1277catch.start0: ; preds = %catch.dispatch01278 %1 = catchpad within %0 [ptr null]1279 %2 = call ptr @llvm.wasm.get.exception(token %1)1280 %3 = call i32 @llvm.wasm.get.ehselector(token %1)1281 catchret from %1 to label %try.cont1282 1283dest: ; preds = %entry1284 ret void1285 1286catch.dispatch1: ; preds = %bb11287 %4 = catchswitch within none [label %catch.start1] unwind to caller1288 1289catch.start1: ; preds = %catch.dispatch11290 %5 = catchpad within %4 [ptr null]1291 %6 = call ptr @llvm.wasm.get.exception(token %5)1292 %7 = call i32 @llvm.wasm.get.ehselector(token %5)1293 catchret from %5 to label %try.cont1294 1295try.cont: ; preds = %catch.start1, %catch.start0, %bb11296 ret void1297}1298 1299; The similar case with branch_remapping_after_fixing_unwind_mismatches_0, but1300; multiple consecutive delegates are generated:1301; - Before:1302; block1303; br (a)1304; try1305; catch1306; end_try1307; end_block1308; <- (a)1309;1310; - After1311; block1312; br (a)1313; try1314; ...1315; try1316; try1317; catch1318; end_try1319; <- (a)1320; delegate1321; delegate1322; end_block1323; <- (b) The br destination should be remapped to here1324;1325; The test was reduced by bugpoint and should not crash in CFGStackify.1326define void @branch_remapping_after_fixing_unwind_mismatches_1() personality ptr @__gxx_wasm_personality_v0 {1327entry:1328 br i1 undef, label %if.then, label %if.end121329 1330if.then: ; preds = %entry1331 invoke void @__cxa_throw(ptr null, ptr null, ptr null) #11332 to label %unreachable unwind label %catch.dispatch1333 1334catch.dispatch: ; preds = %if.then1335 %0 = catchswitch within none [label %catch.start] unwind to caller1336 1337catch.start: ; preds = %catch.dispatch1338 %1 = catchpad within %0 [ptr @_ZTIi]1339 %2 = call ptr @llvm.wasm.get.exception(token %1)1340 %3 = call i32 @llvm.wasm.get.ehselector(token %1)1341 catchret from %1 to label %catchret.dest1342 1343catchret.dest: ; preds = %catch.start1344 invoke void @foo()1345 to label %invoke.cont unwind label %catch.dispatch41346 1347invoke.cont: ; preds = %catchret.dest1348 invoke void @__cxa_throw(ptr null, ptr null, ptr null) #11349 to label %unreachable unwind label %catch.dispatch41350 1351catch.dispatch4: ; preds = %invoke.cont, %catchret.dest1352 %4 = catchswitch within none [label %catch.start5] unwind to caller1353 1354catch.start5: ; preds = %catch.dispatch41355 %5 = catchpad within %4 [ptr @_ZTIi]1356 %6 = call ptr @llvm.wasm.get.exception(token %5)1357 %7 = call i32 @llvm.wasm.get.ehselector(token %5)1358 unreachable1359 1360if.end12: ; preds = %entry1361 invoke void @foo()1362 to label %invoke.cont14 unwind label %catch.dispatch161363 1364catch.dispatch16: ; preds = %if.end121365 %8 = catchswitch within none [label %catch.start17] unwind label %ehcleanup1366 1367catch.start17: ; preds = %catch.dispatch161368 %9 = catchpad within %8 [ptr @_ZTIi]1369 %10 = call ptr @llvm.wasm.get.exception(token %9)1370 %11 = call i32 @llvm.wasm.get.ehselector(token %9)1371 br i1 undef, label %catch20, label %rethrow191372 1373catch20: ; preds = %catch.start171374 catchret from %9 to label %catchret.dest221375 1376catchret.dest22: ; preds = %catch201377 br label %try.cont231378 1379rethrow19: ; preds = %catch.start171380 invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %9) ]1381 to label %unreachable unwind label %ehcleanup1382 1383try.cont23: ; preds = %invoke.cont14, %catchret.dest221384 invoke void @foo()1385 to label %invoke.cont24 unwind label %ehcleanup1386 1387invoke.cont24: ; preds = %try.cont231388 ret void1389 1390invoke.cont14: ; preds = %if.end121391 br label %try.cont231392 1393ehcleanup: ; preds = %try.cont23, %rethrow19, %catch.dispatch161394 %12 = cleanuppad within none []1395 cleanupret from %12 unwind to caller1396 1397unreachable: ; preds = %rethrow19, %invoke.cont, %if.then1398 unreachable1399}1400 1401; Regression test for WasmEHFuncInfo's reverse mapping bug. 'UnwindDestToSrc'1402; should return a vector and not a single BB, which was incorrect.1403; This was reduced by bugpoint and should not crash in CFGStackify.1404define void @wasm_eh_func_info_regression_test() personality ptr @__gxx_wasm_personality_v0 {1405entry:1406 invoke void @foo()1407 to label %invoke.cont unwind label %catch.dispatch1408 1409catch.dispatch: ; preds = %entry1410 %0 = catchswitch within none [label %catch.start] unwind label %ehcleanup221411 1412catch.start: ; preds = %catch.dispatch1413 %1 = catchpad within %0 [ptr @_ZTIi]1414 %2 = call ptr @llvm.wasm.get.exception(token %1)1415 %3 = call i32 @llvm.wasm.get.ehselector(token %1)1416 invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1 [ "funclet"(token %1) ]1417 to label %unreachable unwind label %catch.dispatch21418 1419catch.dispatch2: ; preds = %catch.start1420 %4 = catchswitch within %1 [label %catch.start3] unwind label %ehcleanup1421 1422catch.start3: ; preds = %catch.dispatch21423 %5 = catchpad within %4 [ptr @_ZTIi]1424 %6 = call ptr @llvm.wasm.get.exception(token %5)1425 %7 = call i32 @llvm.wasm.get.ehselector(token %5)1426 catchret from %5 to label %try.cont1427 1428try.cont: ; preds = %catch.start31429 invoke void @foo() [ "funclet"(token %1) ]1430 to label %invoke.cont8 unwind label %ehcleanup1431 1432invoke.cont8: ; preds = %try.cont1433 invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1 [ "funclet"(token %1) ]1434 to label %unreachable unwind label %catch.dispatch111435 1436catch.dispatch11: ; preds = %invoke.cont81437 %8 = catchswitch within %1 [label %catch.start12] unwind label %ehcleanup1438 1439catch.start12: ; preds = %catch.dispatch111440 %9 = catchpad within %8 [ptr @_ZTIi]1441 %10 = call ptr @llvm.wasm.get.exception(token %9)1442 %11 = call i32 @llvm.wasm.get.ehselector(token %9)1443 unreachable1444 1445invoke.cont: ; preds = %entry1446 unreachable1447 1448ehcleanup: ; preds = %catch.dispatch11, %try.cont, %catch.dispatch21449 %12 = cleanuppad within %1 []1450 cleanupret from %12 unwind label %ehcleanup221451 1452ehcleanup22: ; preds = %ehcleanup, %catch.dispatch1453 %13 = cleanuppad within none []1454 cleanupret from %13 unwind to caller1455 1456unreachable: ; preds = %invoke.cont8, %catch.start1457 unreachable1458}1459 1460; void exception_grouping_0() {1461; try {1462; try {1463; throw 0;1464; } catch (int) {1465; }1466; } catch (int) {1467; }1468; }1469;1470; Regression test for a WebAssemblyException grouping bug. After catchswitches1471; are removed, EH pad catch.start2 is dominated by catch.start, but because1472; catch.start2 is the unwind destination of catch.start, it should not be1473; included in catch.start's exception. Also, after we take catch.start2's1474; exception out of catch.start's exception, we have to take out try.cont8 out of1475; catch.start's exception, because it has a predecessor in catch.start2.1476define void @exception_grouping_0() personality ptr @__gxx_wasm_personality_v0 {1477entry:1478 %exception = call ptr @__cxa_allocate_exception(i32 4) #01479 store i32 0, ptr %exception, align 161480 invoke void @__cxa_throw(ptr %exception, ptr @_ZTIi, ptr null) #11481 to label %unreachable unwind label %catch.dispatch1482 1483catch.dispatch: ; preds = %entry1484 %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch11485 1486catch.start: ; preds = %catch.dispatch1487 %1 = catchpad within %0 [ptr @_ZTIi]1488 %2 = call ptr @llvm.wasm.get.exception(token %1)1489 %3 = call i32 @llvm.wasm.get.ehselector(token %1)1490 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #01491 %matches = icmp eq i32 %3, %41492 br i1 %matches, label %catch, label %rethrow1493 1494catch: ; preds = %catch.start1495 %5 = call ptr @__cxa_begin_catch(ptr %2) #0 [ "funclet"(token %1) ]1496 %6 = load i32, ptr %5, align 41497 call void @__cxa_end_catch() #0 [ "funclet"(token %1) ]1498 catchret from %1 to label %catchret.dest1499 1500catchret.dest: ; preds = %catch1501 br label %try.cont1502 1503rethrow: ; preds = %catch.start1504 invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %1) ]1505 to label %unreachable unwind label %catch.dispatch11506 1507catch.dispatch1: ; preds = %rethrow, %catch.dispatch1508 %7 = catchswitch within none [label %catch.start2] unwind to caller1509 1510catch.start2: ; preds = %catch.dispatch11511 %8 = catchpad within %7 [ptr @_ZTIi]1512 %9 = call ptr @llvm.wasm.get.exception(token %8)1513 %10 = call i32 @llvm.wasm.get.ehselector(token %8)1514 %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #01515 %matches3 = icmp eq i32 %10, %111516 br i1 %matches3, label %catch5, label %rethrow41517 1518catch5: ; preds = %catch.start21519 %12 = call ptr @__cxa_begin_catch(ptr %9) #0 [ "funclet"(token %8) ]1520 %13 = load i32, ptr %12, align 41521 call void @__cxa_end_catch() #0 [ "funclet"(token %8) ]1522 catchret from %8 to label %catchret.dest71523 1524catchret.dest7: ; preds = %catch51525 br label %try.cont81526 1527rethrow4: ; preds = %catch.start21528 call void @llvm.wasm.rethrow() #1 [ "funclet"(token %8) ]1529 unreachable1530 1531try.cont8: ; preds = %try.cont, %catchret.dest71532 ret void1533 1534try.cont: ; preds = %catchret.dest1535 br label %try.cont81536 1537unreachable: ; preds = %rethrow, %entry1538 unreachable1539}1540 1541; Test for WebAssemblyException grouping. This test is hand-modified to generate1542; this structure:1543; catch.start dominates catch.start4 and catch.start4 dominates catch.start12,1544; so the after dominator-based grouping, we end up with:1545; catch.start's exception > catch4.start's exception > catch12.start's exception1546; (> here represents subexception relationship)1547;1548; But the unwind destination chain is catch.start -> catch.start4 ->1549; catch.start12. So all these subexception relationship should be deconstructed.1550; We have to make sure to take out catch.start4's exception out of catch.start's1551; exception first, before taking out catch.start12's exception out of1552; catch.start4's exception; otherwise we end up with an incorrect relationship1553; of catch.start's exception > catch.start12's exception.1554define void @exception_grouping_1() personality ptr @__gxx_wasm_personality_v0 {1555entry:1556 invoke void @foo()1557 to label %invoke.cont unwind label %catch.dispatch1558 1559invoke.cont: ; preds = %entry1560 invoke void @foo()1561 to label %invoke.cont1 unwind label %catch.dispatch1562 1563invoke.cont1: ; preds = %invoke.cont1564 invoke void @foo()1565 to label %try.cont18 unwind label %catch.dispatch1566 1567catch.dispatch11: ; preds = %rethrow6, %catch.dispatch31568 %0 = catchswitch within none [label %catch.start12] unwind to caller1569 1570catch.start12: ; preds = %catch.dispatch111571 %1 = catchpad within %0 [ptr @_ZTIi]1572 %2 = call ptr @llvm.wasm.get.exception(token %1)1573 %3 = call i32 @llvm.wasm.get.ehselector(token %1)1574 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #01575 %matches13 = icmp eq i32 %3, %41576 br i1 %matches13, label %catch15, label %rethrow141577 1578catch15: ; preds = %catch.start121579 %5 = call ptr @__cxa_begin_catch(ptr %2) #0 [ "funclet"(token %1) ]1580 %6 = load i32, ptr %5, align 41581 call void @__cxa_end_catch() #0 [ "funclet"(token %1) ]1582 catchret from %1 to label %try.cont181583 1584rethrow14: ; preds = %catch.start121585 call void @llvm.wasm.rethrow() #1 [ "funclet"(token %1) ]1586 unreachable1587 1588catch.dispatch3: ; preds = %rethrow, %catch.dispatch1589 %7 = catchswitch within none [label %catch.start4] unwind label %catch.dispatch111590 1591catch.start4: ; preds = %catch.dispatch31592 %8 = catchpad within %7 [ptr @_ZTIi]1593 %9 = call ptr @llvm.wasm.get.exception(token %8)1594 %10 = call i32 @llvm.wasm.get.ehselector(token %8)1595 %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #01596 %matches5 = icmp eq i32 %10, %111597 br i1 %matches5, label %catch7, label %rethrow61598 1599catch7: ; preds = %catch.start41600 %12 = call ptr @__cxa_begin_catch(ptr %9) #0 [ "funclet"(token %8) ]1601 %13 = load i32, ptr %12, align 41602 call void @__cxa_end_catch() #0 [ "funclet"(token %8) ]1603 catchret from %8 to label %try.cont181604 1605rethrow6: ; preds = %catch.start41606 invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %8) ]1607 to label %unreachable unwind label %catch.dispatch111608 1609catch.dispatch: ; preds = %invoke.cont1, %invoke.cont, %entry1610 %14 = catchswitch within none [label %catch.start] unwind label %catch.dispatch31611 1612catch.start: ; preds = %catch.dispatch1613 %15 = catchpad within %14 [ptr @_ZTIi]1614 %16 = call ptr @llvm.wasm.get.exception(token %15)1615 %17 = call i32 @llvm.wasm.get.ehselector(token %15)1616 %18 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #01617 %matches = icmp eq i32 %17, %181618 br i1 %matches, label %catch, label %rethrow1619 1620catch: ; preds = %catch.start1621 %19 = call ptr @__cxa_begin_catch(ptr %16) #0 [ "funclet"(token %15) ]1622 %20 = load i32, ptr %19, align 41623 call void @__cxa_end_catch() #0 [ "funclet"(token %15) ]1624 catchret from %15 to label %try.cont181625 1626rethrow: ; preds = %catch.start1627 invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %15) ]1628 to label %unreachable unwind label %catch.dispatch31629 1630try.cont18: ; preds = %catch, %catch7, %catch15, %invoke.cont11631 ret void1632 1633unreachable: ; preds = %rethrow, %rethrow61634 unreachable1635}1636 1637; void exception_grouping_2() {1638; try {1639; try {1640; throw 0;1641; } catch (int) { // (a)1642; }1643; } catch (int) { // (b)1644; }1645; try {1646; foo();1647; } catch (int) { // (c)1648; }1649; }1650;1651; Regression test for an ExceptionInfo grouping bug. Because the first (inner)1652; try always throws, both EH pads (b) (catch.start2) and (c) (catch.start10) are1653; dominated by EH pad (a) (catch.start), even though they are not semantically1654; contained in (a)'s exception. Because (a)'s unwind destination is (b), (b)'s1655; exception is taken out of (a)'s. But because (c) is reachable from (b), we1656; should make sure to take out (c)'s exception out of (a)'s exception too.1657define void @exception_grouping_2() personality ptr @__gxx_wasm_personality_v0 {1658entry:1659 %exception = call ptr @__cxa_allocate_exception(i32 4) #11660 store i32 0, ptr %exception, align 161661 invoke void @__cxa_throw(ptr %exception, ptr @_ZTIi, ptr null) #31662 to label %unreachable unwind label %catch.dispatch1663 1664catch.dispatch: ; preds = %entry1665 %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch11666 1667catch.start: ; preds = %catch.dispatch1668 %1 = catchpad within %0 [ptr @_ZTIi]1669 %2 = call ptr @llvm.wasm.get.exception(token %1)1670 %3 = call i32 @llvm.wasm.get.ehselector(token %1)1671 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #11672 %matches = icmp eq i32 %3, %41673 br i1 %matches, label %catch, label %rethrow1674 1675catch: ; preds = %catch.start1676 %5 = call ptr @__cxa_begin_catch(ptr %2) #1 [ "funclet"(token %1) ]1677 %6 = load i32, ptr %5, align 41678 call void @__cxa_end_catch() #1 [ "funclet"(token %1) ]1679 catchret from %1 to label %try.cont81680 1681rethrow: ; preds = %catch.start1682 invoke void @llvm.wasm.rethrow() #3 [ "funclet"(token %1) ]1683 to label %unreachable unwind label %catch.dispatch11684 1685catch.dispatch1: ; preds = %rethrow, %catch.dispatch1686 %7 = catchswitch within none [label %catch.start2] unwind to caller1687 1688catch.start2: ; preds = %catch.dispatch11689 %8 = catchpad within %7 [ptr @_ZTIi]1690 %9 = call ptr @llvm.wasm.get.exception(token %8)1691 %10 = call i32 @llvm.wasm.get.ehselector(token %8)1692 %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #11693 %matches3 = icmp eq i32 %10, %111694 br i1 %matches3, label %catch5, label %rethrow41695 1696catch5: ; preds = %catch.start21697 %12 = call ptr @__cxa_begin_catch(ptr %9) #1 [ "funclet"(token %8) ]1698 %13 = load i32, ptr %12, align 41699 call void @__cxa_end_catch() #1 [ "funclet"(token %8) ]1700 catchret from %8 to label %try.cont81701 1702rethrow4: ; preds = %catch.start21703 call void @llvm.wasm.rethrow() #3 [ "funclet"(token %8) ]1704 unreachable1705 1706try.cont8: ; preds = %catch, %catch51707 invoke void @foo()1708 to label %try.cont16 unwind label %catch.dispatch91709 1710catch.dispatch9: ; preds = %try.cont81711 %14 = catchswitch within none [label %catch.start10] unwind to caller1712 1713catch.start10: ; preds = %catch.dispatch91714 %15 = catchpad within %14 [ptr @_ZTIi]1715 %16 = call ptr @llvm.wasm.get.exception(token %15)1716 %17 = call i32 @llvm.wasm.get.ehselector(token %15)1717 %18 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #11718 %matches11 = icmp eq i32 %17, %181719 br i1 %matches11, label %catch13, label %rethrow121720 1721catch13: ; preds = %catch.start101722 %19 = call ptr @__cxa_begin_catch(ptr %16) #1 [ "funclet"(token %15) ]1723 %20 = load i32, ptr %19, align 41724 call void @__cxa_end_catch() #1 [ "funclet"(token %15) ]1725 catchret from %15 to label %try.cont161726 1727rethrow12: ; preds = %catch.start101728 call void @llvm.wasm.rethrow() #3 [ "funclet"(token %15) ]1729 unreachable1730 1731try.cont16: ; preds = %try.cont8, %catch131732 ret void1733 1734unreachable: ; preds = %rethrow, %entry1735 unreachable1736}1737 1738; Check if the unwind destination mismatch stats are correct1739; NOSORT: 24 wasm-cfg-stackify - Number of call unwind mismatches found1740; NOSORT: 5 wasm-cfg-stackify - Number of catch unwind mismatches found1741 1742declare void @foo()1743declare void @bar()1744declare i32 @baz()1745declare i32 @qux(i32)1746declare void @quux(i32)1747declare void @fun(i32)1748; Function Attrs: nounwind1749declare void @nothrow(i32) #01750; Function Attrs: nounwind1751declare i32 @nothrow_i32() #01752 1753; Function Attrs: nounwind1754declare ptr @_ZN6ObjectD2Ev(ptr returned) #01755@_ZTI7MyClass = external constant { ptr, ptr }, align 41756; Function Attrs: nounwind1757declare ptr @_ZN7MyClassD2Ev(ptr returned) #01758; Function Attrs: nounwind1759declare ptr @_ZN7MyClassC2ERKS_(ptr returned, ptr dereferenceable(4)) #01760 1761declare i32 @__gxx_wasm_personality_v0(...)1762; Function Attrs: nounwind1763declare ptr @llvm.wasm.get.exception(token) #01764; Function Attrs: nounwind1765declare i32 @llvm.wasm.get.ehselector(token) #01766declare ptr @__cxa_allocate_exception(i32) #01767declare void @__cxa_throw(ptr, ptr, ptr)1768; Function Attrs: noreturn1769declare void @llvm.wasm.rethrow() #11770; Function Attrs: nounwind1771declare i32 @llvm.eh.typeid.for(ptr) #01772 1773declare ptr @__cxa_begin_catch(ptr)1774declare void @__cxa_end_catch()1775declare ptr @__cxa_get_exception_ptr(ptr)1776declare void @_ZSt9terminatev()1777; Function Attrs: nounwind1778declare void @llvm.memcpy.p0.p0.i32(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i32, i1 immarg) #01779; Function Attrs: nounwind1780declare void @llvm.memmove.p0.p0.i32(ptr nocapture, ptr nocapture readonly, i32, i1 immarg) #01781; Function Attrs: nounwind1782declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #01783 1784attributes #0 = { nounwind }1785attributes #1 = { noreturn }1786