1; RUN: opt < %s -wasm-lower-em-ehsjlj -wasm-enable-sjlj -S | FileCheck %s -DPTR=i32 2; RUN: opt < %s -wasm-lower-em-ehsjlj -wasm-enable-sjlj -S --mattr=+atomics,+bulk-memory | FileCheck %s -DPTR=i32 3; RUN: opt < %s -wasm-lower-em-ehsjlj -wasm-enable-sjlj --mtriple=wasm64-unknown-unknown -data-layout="e-m:e-p:64:64-i64:64-n32:64-S128" -S | FileCheck %s -DPTR=i64 4 5target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" 6target triple = "wasm32-unknown-unknown" 7 8%struct.__jmp_buf_tag = type { [6 x i32], i32, [32 x i32] } 9 10; These variables are only used in Emscripten EH/SjLj, so they shouldn't be 11; generated. 12; CHECK-NOT: @__THREW__ = 13; CHECK-NOT: @__threwValue = 14 15@global_longjmp_ptr = global ptr @longjmp, align 4 16; CHECK-DAG: @global_longjmp_ptr = global ptr @__wasm_longjmp 17 18; Test a simple setjmp - longjmp sequence 19define void @setjmp_longjmp() { 20; CHECK-LABEL: @setjmp_longjmp() 21entry: 22 %buf = alloca [1 x %struct.__jmp_buf_tag], align 16 23 %call = call i32 @setjmp(ptr %buf) #0 24 call void @longjmp(ptr %buf, i32 1) #1 25 unreachable 26 27; CHECK: entry: 28; CHECK-NEXT: %functionInvocationId = alloca i32, align 4 29; CHECK-NEXT: br label %setjmp.dispatch 30 31; CHECK: setjmp.dispatch: 32; CHECK-NEXT: %[[VAL2:.*]] = phi i32 [ %val, %if.end ], [ undef, %entry ] 33; CHECK-NEXT: %[[BUF:.*]] = phi ptr [ %[[BUF2:.*]], %if.end ], [ undef, %entry ] 34; CHECK-NEXT: %label.phi = phi i32 [ %label, %if.end ], [ -1, %entry ] 35; CHECK-NEXT: switch i32 %label.phi, label %entry.split [ 36; CHECK-NEXT: i32 1, label %entry.split.split 37; CHECK-NEXT: ] 38 39; CHECK: entry.split: 40; CHECK-NEXT: %buf = alloca [1 x %struct.__jmp_buf_tag], align 16 41; CHECK-NEXT: call void @__wasm_setjmp(ptr %buf, i32 1, ptr %functionInvocationId) 42; CHECK-NEXT: br label %entry.split.split 43 44; CHECK: entry.split.split: 45; CHECK-NEXT: %[[BUF2]] = phi ptr [ %[[BUF]], %setjmp.dispatch ], [ %buf, %entry.split ] 46; CHECK-NEXT: %setjmp.ret = phi i32 [ 0, %entry.split ], [ %[[VAL2]], %setjmp.dispatch ] 47; CHECK-NEXT: invoke void @__wasm_longjmp(ptr %[[BUF2]], i32 1) 48; CHECK-NEXT: to label %.noexc unwind label %catch.dispatch.longjmp 49 50; CHECK: .noexc: 51; CHECK-NEXT: unreachable 52 53; CHECK: catch.dispatch.longjmp: 54; CHECK-NEXT: %0 = catchswitch within none [label %catch.longjmp] unwind to caller 55 56; CHECK: catch.longjmp: 57; CHECK-NEXT: %1 = catchpad within %0 [] 58; CHECK-NEXT: %thrown = call ptr @llvm.wasm.catch(i32 1) 59; CHECK-NEXT: %env_gep = getelementptr { ptr, i32 }, ptr %thrown, i32 0, i32 0 60; CHECK-NEXT: %val_gep = getelementptr { ptr, i32 }, ptr %thrown, i32 0, i32 1 61; CHECK-NEXT: %env = load ptr, ptr %env_gep, align {{.*}} 62; CHECK-NEXT: %val = load i32, ptr %val_gep, align 4 63; CHECK-NEXT: %label = call i32 @__wasm_setjmp_test(ptr %env, ptr %functionInvocationId) [ "funclet"(token %1) ] 64; CHECK-NEXT: %2 = icmp eq i32 %label, 0 65; CHECK-NEXT: br i1 %2, label %if.then, label %if.end 66 67; CHECK: if.then: 68; CHECK-NEXT: call void @__wasm_longjmp(ptr %env, i32 %val) [ "funclet"(token %1) ] 69; CHECK-NEXT: unreachable 70 71; CHECK: if.end: 72; CHECK-NEXT: catchret from %1 to label %setjmp.dispatch 73} 74 75; When there are multiple longjmpable calls after setjmp. This will turn each of 76; longjmpable call into an invoke whose unwind destination is 77; 'catch.dispatch.longjmp' BB. 78define void @setjmp_multiple_longjmpable_calls() { 79; CHECK-LABEL: @setjmp_multiple_longjmpable_calls 80entry: 81 %buf = alloca [1 x %struct.__jmp_buf_tag], align 16 82 %call = call i32 @setjmp(ptr %buf) #0 83 call void @foo() 84 call void @foo() 85 ret void 86 87; CHECK: entry.split.split: 88; CHECK: invoke void @foo() 89; CHECK: to label %{{.*}} unwind label %catch.dispatch.longjmp 90 91; CHECK: .noexc: 92; CHECK: invoke void @foo() 93; CHECK: to label %{{.*}} unwind label %catch.dispatch.longjmp 94} 95 96; Tests cases where longjmp function pointer is used in other ways than direct 97; calls. longjmps should be replaced with (void(*)(jmp_buf*, int))__wasm_longjmp. 98declare void @take_longjmp(ptr %arg_ptr) 99define void @indirect_longjmp() { 100; CHECK-LABEL: @indirect_longjmp 101entry: 102 %local_longjmp_ptr = alloca ptr, align 4 103 %buf0 = alloca [1 x %struct.__jmp_buf_tag], align 16 104 %buf1 = alloca [1 x %struct.__jmp_buf_tag], align 16 105 106 ; Store longjmp in a local variable, load it, and call it 107 store ptr @longjmp, ptr %local_longjmp_ptr, align 4 108 ; CHECK: store ptr @__wasm_longjmp, ptr %local_longjmp_ptr, align 4 109 %longjmp_from_local_ptr = load ptr, ptr %local_longjmp_ptr, align 4 110 call void %longjmp_from_local_ptr(ptr %buf0, i32 0) 111 112 ; Load longjmp from a global variable and call it 113 %longjmp_from_global_ptr = load ptr, ptr @global_longjmp_ptr, align 4 114 call void %longjmp_from_global_ptr(ptr %buf1, i32 0) 115 116 ; Pass longjmp as a function argument. This is a call but longjmp is not a 117 ; callee but an argument. 118 call void @take_longjmp(ptr @longjmp) 119 ; CHECK: call void @take_longjmp(ptr @__wasm_longjmp) 120 ret void 121} 122 123; Function Attrs: nounwind 124declare void @foo() #2 125; The pass removes the 'nounwind' attribute, so there should be no attributes 126; CHECK-NOT: declare void @foo #{{.*}} 127; Function Attrs: returns_twice 128declare i32 @setjmp(ptr) #0 129; Function Attrs: noreturn 130declare void @longjmp(ptr, i32) #1 131declare i32 @__gxx_personality_v0(...) 132declare ptr @__cxa_begin_catch(ptr) 133declare void @__cxa_end_catch() 134declare void @free(ptr) 135 136; Runtime glue function declarations 137; CHECK-DAG: declare void @__wasm_setjmp(ptr, i32, ptr) 138; CHECK-DAG: declare i32 @__wasm_setjmp_test(ptr, ptr) 139; CHECK-DAG: declare void @__wasm_longjmp(ptr, i32) 140 141attributes #0 = { returns_twice } 142attributes #1 = { noreturn } 143attributes #2 = { nounwind } 144