273 lines · plain
1; RUN: opt < %s -wasm-lower-em-ehsjlj -enable-emscripten-cxx-exceptions -enable-emscripten-sjlj -S | FileCheck %s2; RUN: llc < %s -enable-emscripten-cxx-exceptions -enable-emscripten-sjlj -verify-machineinstrs3 4; Tests for cases when exception handling and setjmp/longjmp handling are mixed.5 6target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"7target triple = "wasm32-unknown-unknown"8 9%struct.__jmp_buf_tag = type { [6 x i32], i32, [32 x i32] }10@_ZTIi = external constant ptr11 12; There is a function call (@foo) that can either throw an exception or longjmp13; and there is also a setjmp call. When @foo throws, we have to check both for14; exception and longjmp and jump to exception or longjmp handling BB depending15; on the result.16define void @setjmp_longjmp_exception() personality ptr @__gxx_personality_v0 {17; CHECK-LABEL: @setjmp_longjmp_exception18entry:19 %buf = alloca [1 x %struct.__jmp_buf_tag], align 1620 %call = call i32 @setjmp(ptr %buf) #021 invoke void @foo()22 to label %try.cont unwind label %lpad23 24; CHECK: entry.split.split:25; CHECK: %[[CMP0:.*]] = icmp ne i32 %__THREW__.val, 026; CHECK-NEXT: %__threwValue.val = load i32, ptr @__threwValue27; CHECK-NEXT: %[[CMP1:.*]] = icmp ne i32 %__threwValue.val, 028; CHECK-NEXT: %[[CMP:.*]] = and i1 %[[CMP0]], %[[CMP1]]29; CHECK-NEXT: br i1 %[[CMP]], label %if.then1, label %if.else130 31; This is exception checking part. %if.else1 leads here32; CHECK: entry.split.split.split:33; CHECK-NEXT: %[[CMP:.*]] = icmp eq i32 %__THREW__.val, 134; CHECK-NEXT: br i1 %[[CMP]], label %lpad, label %try.cont35 36; CHECK: lpad:37lpad: ; preds = %entry38 %0 = landingpad { ptr, i32 }39 catch ptr null40 %1 = extractvalue { ptr, i32 } %0, 041 %2 = extractvalue { ptr, i32 } %0, 142; CHECK-NOT: call {{.*}} void @__invoke_void(ptr @__cxa_end_catch)43 %3 = call ptr @__cxa_begin_catch(ptr %1) #244 call void @__cxa_end_catch()45 br label %try.cont46 47try.cont: ; preds = %lpad, %entry48 ret void49 50; longjmp checking part51; CHECK: if.then1:52; CHECK: call i32 @__wasm_setjmp_test53}54 55; @foo can either throw an exception or longjmp. Because this function doesn't56; have any setjmp calls, we only handle exceptions in this function. But because57; sjlj is enabled, we check if the thrown value is longjmp and if so rethrow it58; by calling @emscripten_longjmp.59define void @rethrow_longjmp() personality ptr @__gxx_personality_v0 {60; CHECK-LABEL: @rethrow_longjmp61entry:62 invoke void @foo()63 to label %try.cont unwind label %lpad64; CHECK: entry:65; CHECK: %cmp.eq.one = icmp eq i32 %__THREW__.val, 166; CHECK-NEXT: %cmp.eq.zero = icmp eq i32 %__THREW__.val, 067; CHECK-NEXT: %or = or i1 %cmp.eq.zero, %cmp.eq.one68; CHECK-NEXT: br i1 %or, label %tail, label %rethrow.longjmp69 70; CHECK: try.cont:71; CHECK-NEXT: %phi = phi i32 [ undef, %tail ], [ undef, %lpad ]72; CHECK-NEXT: ret void73 74; CHECK: rethrow.longjmp:75; CHECK-NEXT: %threw.phi = phi i32 [ %__THREW__.val, %entry ]76; CHECK-NEXT: %__threwValue.val = load i32, ptr @__threwValue, align 477; CHECK-NEXT: call void @emscripten_longjmp(i32 %threw.phi, i32 %__threwValue.val78; CHECK-NEXT: unreachable79 80; CHECK: tail:81; CHECK-NEXT: %cmp = icmp eq i32 %__THREW__.val, 182; CHECK-NEXT: br i1 %cmp, label %lpad, label %try.cont83 84lpad: ; preds = %entry85 %0 = landingpad { ptr, i32 }86 catch ptr null87 %1 = extractvalue { ptr, i32 } %0, 088 %2 = extractvalue { ptr, i32 } %0, 189 %3 = call ptr @__cxa_begin_catch(ptr %1) #590 call void @__cxa_end_catch()91 br label %try.cont92 93try.cont: ; preds = %lpad, %entry94 %phi = phi i32 [ undef, %entry ], [ undef, %lpad ]95 ret void96}97 98; This function contains a setjmp call and no invoke, so we only handle longjmp99; here. But @foo can also throw an exception, so we check if an exception is100; thrown and if so rethrow it by calling @__resumeException. Also we have to101; free the setjmpTable buffer before calling @__resumeException.102define void @rethrow_exception() {103; CHECK-LABEL: @rethrow_exception104entry:105 %buf = alloca [1 x %struct.__jmp_buf_tag], align 16106 %call = call i32 @setjmp(ptr %buf) #0107 %cmp = icmp ne i32 %call, 0108 br i1 %cmp, label %return, label %if.end109 110if.end: ; preds = %entry111 call void @foo()112 br label %return113 114; CHECK: if.end:115; CHECK: %cmp.eq.one = icmp eq i32 %__THREW__.val, 1116; CHECK-NEXT: br i1 %cmp.eq.one, label %rethrow.exn, label %normal117 118; CHECK: rethrow.exn:119; CHECK-NEXT: %exn = call ptr @__cxa_find_matching_catch_2()120; CHECK-NEXT: call void @__resumeException(ptr %exn)121; CHECK-NEXT: unreachable122 123; CHECK: normal:124; CHECK-NEXT: icmp ne i32 %__THREW__.val, 0125 126return: ; preds = %if.end, %entry127 ret void128}129 130; The same as 'rethrow_exception' but contains a __cxa_throw call. We have to131; free the setjmpTable buffer before calling __cxa_throw.132define void @rethrow_exception2() {133; CHECK-LABEL: @rethrow_exception2134entry:135 %buf = alloca [1 x %struct.__jmp_buf_tag], align 16136 %call = call i32 @setjmp(ptr %buf) #0137 %cmp = icmp ne i32 %call, 0138 br i1 %cmp, label %throw, label %if.end139 140if.end: ; preds = %entry141 call void @foo()142 br label %throw143 144throw: ; preds = %if.end, %entry145 call void @__cxa_throw(ptr null, ptr null, ptr null) #1146 unreachable147 148; CHECK: throw:149; CHECK-NEXT: call void @__cxa_throw(ptr null, ptr null, ptr null)150; CHECK-NEXT: unreachable151}152 153; The same case with @rethrow_longjmp, but there are multiple function calls154; that can possibly longjmp (instead of throwing exception) so we have to155; rethrow them. Here we test if we correclty generate only one 'rethrow.longjmp'156; BB and share it for multiple calls.157define void @rethrow_longjmp_multi() personality ptr @__gxx_personality_v0 {158; CHECK-LABEL: @rethrow_longjmp_multi159entry:160 invoke void @foo()161 to label %bb unwind label %lpad162 163bb: ; preds = %entry164 invoke void @foo()165 to label %try.cont unwind label %lpad166 167lpad: ; preds = %bb, %entry168 %0 = landingpad { ptr, i32 }169 catch ptr null170 %1 = extractvalue { ptr, i32 } %0, 0171 %2 = extractvalue { ptr, i32 } %0, 1172 %3 = call ptr @__cxa_begin_catch(ptr %1) #5173 call void @__cxa_end_catch()174 br label %try.cont175 176try.cont: ; preds = %lpad, %bb177 %phi = phi i32 [ undef, %bb ], [ undef, %lpad ]178 ret void179 180; CHECK: rethrow.longjmp:181; CHECK-NEXT: %threw.phi = phi i32 [ %__THREW__.val, %entry ], [ %__THREW__.val1, %bb ]182; CHECK-NEXT: %__threwValue.val = load i32, ptr @__threwValue, align 4183; CHECK-NEXT: call void @emscripten_longjmp(i32 %threw.phi, i32 %__threwValue.val)184; CHECK-NEXT: unreachable185}186 187; The same case with @rethrow_exception, but there are multiple function calls188; that can possibly throw (instead of longjmping) so we have to rethrow them.189; Here we test if correctly we generate only one 'rethrow.exn' BB and share it190; for multiple calls.191define void @rethrow_exception_multi() {192; CHECK-LABEL: @rethrow_exception_multi193entry:194 %buf = alloca [1 x %struct.__jmp_buf_tag], align 16195 %call = call i32 @setjmp(ptr %buf) #0196 %cmp = icmp ne i32 %call, 0197 br i1 %cmp, label %return, label %if.end198 199if.end: ; preds = %entry200 call void @foo()201 call void @foo()202 br label %return203 204return: ; preds = %entry, %if.end205 ret void206 207; CHECK: rethrow.exn:208; CHECK-NEXT: %exn = call ptr @__cxa_find_matching_catch_2()209; CHECK-NEXT: call void @__resumeException(ptr %exn)210; CHECK-NEXT: unreachable211}212 213; int jmpval = setjmp(buf);214; if (jmpval != 0)215; return;216; try {217; throw 3;218; } catch (...) {219; }220define void @setjmp_with_throw_try_catch() personality ptr @__gxx_personality_v0 {221; CHECK-LABEL: @setjmp_with_throw_try_catch222entry:223 %buf = alloca [1 x %struct.__jmp_buf_tag], align 16224 %call = call i32 @setjmp(ptr %buf) #0225 %cmp = icmp ne i32 %call, 0226 br i1 %cmp, label %try.cont, label %if.end227 228if.end: ; preds = %entry229 %exception = call ptr @__cxa_allocate_exception(i32 4) #2230 store i32 3, ptr %exception, align 16231 invoke void @__cxa_throw(ptr %exception, ptr @_ZTIi, ptr null) #1232 to label %unreachable unwind label %lpad233; When invoke @__cxa_throw is converted to a call to the invoke wrapper,234; "noreturn" attribute should be removed, and there should be no 'free' call235; before the call. We insert a 'free' call that frees 'setjmpTable' before every236; function-exiting instruction. And invoke wrapper calls shouldn't be treated as237; noreturn instructions, because they are supposed to return.238; CHECK: if.end:239; CHECK-NOT: tail call void @free240; CHECK-NOT: call cc99 void @__invoke_void_ptr_ptr_ptr(ptr @__cxa_throw, ptr %exception, ptr @_ZTIi, ptr null) #241; CHECK: call cc99 void @__invoke_void_ptr_ptr_ptr(ptr @__cxa_throw, ptr %exception, ptr @_ZTIi, ptr null)242 243lpad: ; preds = %if.end244 %0 = landingpad { ptr, i32 }245 catch ptr null246 %1 = extractvalue { ptr, i32 } %0, 0247 %2 = extractvalue { ptr, i32 } %0, 1248 %3 = call ptr @__cxa_begin_catch(ptr %1) #2249 call void @__cxa_end_catch()250 br label %try.cont251 252try.cont: ; preds = %entry, %lpad253 ret void254 255unreachable: ; preds = %if.end256 unreachable257}258 259declare void @foo()260; Function Attrs: returns_twice261declare i32 @setjmp(ptr)262; Function Attrs: noreturn263declare void @longjmp(ptr, i32)264declare i32 @__gxx_personality_v0(...)265declare ptr @__cxa_begin_catch(ptr)266declare void @__cxa_end_catch()267declare void @__cxa_throw(ptr, ptr, ptr)268declare ptr @__cxa_allocate_exception(i32)269 270attributes #0 = { returns_twice }271attributes #1 = { noreturn }272attributes #2 = { nounwind }273