[WebAssembly] Improve readability of EH tests

Summary:
- Indent check lines to easily figure out try-catch-end structure
- Add the original C++ code the tests were genereated from
- Add a few more lines to make the structure more readable
- Rename a couple function / structures
- Add label and branch annotations to cfg-stackify-eh.ll
- Temporarily delete check lines for `test1` in `cfg-stackify-eh.ll`
  because it will be updated in a later CL soon and there's no point of
  making it look better here

Reviewers: dschuff

Subscribers: sunfish, sbc100, jgravelle-google, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D58562

llvm-svn: 354842
This commit is contained in:
Heejin Ahn 2019-02-26 03:29:59 +00:00
parent 1d5f8632d7
commit 7829763e49
3 changed files with 305 additions and 142 deletions

View File

@ -1,4 +1,4 @@
; RUN: llc < %s -asm-verbose=false -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 -exception-model=wasm -mattr=+exception-handling | FileCheck %s
; 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 -exception-model=wasm -mattr=+exception-handling | FileCheck %s
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"
@ -7,26 +7,37 @@ target triple = "wasm32-unknown-unknown"
@_ZTId = external constant i8*
; Simple test case with two catch clauses
;
; void foo();
; void test0() {
; try {
; foo();
; } catch (int n) {
; bar();
; } catch (double d) {
; } catch (int) {
; } catch (double) {
; }
; }
; CHECK-LABEL: test0
; CHECK: block
; CHECK: try
; CHECK: call foo
; CHECK: catch $[[EXCEPT_REF:[0-9]+]]=
; CHECK: block i32
; CHECK: br_on_exn 0, __cpp_exception, $[[EXCEPT_REF]]
; CHECK: rethrow
; CHECK: end_block
; CHECK: i32.call $drop=, _Unwind_CallPersonality
; CHECK: end_try
; CHECK: return
; CHECK: call foo
; CHECK: br 0 # 0: down to label1
; CHECK: catch
; CHECK: block
; CHECK: br_if 0, {{.*}} # 0: down to label3
; CHECK: i32.call $drop=, __cxa_begin_catch
; CHECK: call __cxa_end_catch
; CHECK: br 1 # 1: down to label1
; CHECK: end_block # label3:
; CHECK: block
; CHECK: br_if 0, {{.*}} # 0: down to label4
; CHECK: i32.call $drop=, __cxa_begin_catch
; CHECK: call __cxa_end_catch
; CHECK: br 1 # 1: down to label1
; CHECK: end_block # label4:
; CHECK: call __cxa_rethrow
; CHECK: end_try # label1:
; CHECK: end_block
define void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
entry:
invoke void @foo()
@ -45,21 +56,16 @@ catch.start: ; preds = %catch.dispatch
catch2: ; preds = %catch.start
%5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
%6 = bitcast i8* %5 to i32*
%7 = load i32, i32* %6, align 4
call void @bar() [ "funclet"(token %1) ]
call void @__cxa_end_catch() [ "funclet"(token %1) ]
catchret from %1 to label %try.cont
catch.fallthrough: ; preds = %catch.start
%8 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTId to i8*))
%matches1 = icmp eq i32 %3, %8
%6 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTId to i8*))
%matches1 = icmp eq i32 %3, %6
br i1 %matches1, label %catch, label %rethrow
catch: ; preds = %catch.fallthrough
%9 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
%10 = bitcast i8* %9 to double*
%11 = load double, double* %10, align 8
%7 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
call void @__cxa_end_catch() [ "funclet"(token %1) ]
catchret from %1 to label %try.cont
@ -75,41 +81,17 @@ try.cont: ; preds = %entry, %catch, %cat
; void test1() {
; try {
; foo();
; } catch (int n) {
; } catch (int) {
; try {
; foo();
; } catch (int n) {
; } catch (int) {
; foo();
; }
; }
; }
; CHECK-LABEL: test1
; CHECK: try
; CHECK: call foo
; CHECK: catch
; CHECK: br_on_exn 0, __cpp_exception
; CHECK: rethrow
; CHECK: i32.call $drop=, _Unwind_CallPersonality
; CHECK: try
; CHECK: call foo
; CHECK: catch
; CHECK: br_on_exn 0, __cpp_exception
; CHECK: rethrow
; CHECK: i32.call $drop=, _Unwind_CallPersonality
; CHECK: try
; CHECK: i32.call $drop=, __cxa_begin_catch
; CHECK: try
; CHECK: call foo
; CHECK: catch $drop=
; CHECK: rethrow
; CHECK: end_try
; CHECK: catch $drop=
; CHECK: rethrow
; CHECK: end_try
; CHECK: end_try
; CHECK: end_try
; CHECK: return
; TODO Fill in CHECK lines once we fix ScopeTops info bug in D58605
define void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
entry:
invoke void @foo()
@ -195,29 +177,39 @@ unreachable: ; preds = %rethrow5
; }
; CHECK-LABEL: test2
; CHECK: block
; CHECK: try
; CHECK: call foo
; CHECK: call foo
; CHECK: br 0 # 0: down to label17
; CHECK: catch
; CHECK: br_on_exn 0, __cpp_exception
; CHECK: rethrow
; CHECK: loop
; CHECK: try
; CHECK: call foo
; CHECK: catch $drop=
; CHECK: try
; CHECK: call __cxa_end_catch
; CHECK: catch
; CHECK: br_on_exn 0, __cpp_exception
; CHECK: call __clang_call_terminate, 0
; CHECK: unreachable
; CHECK: call __clang_call_terminate
; CHECK: unreachable
; CHECK: end_try
; CHECK: rethrow
; CHECK: end_try
; CHECK: end_loop
; CHECK: end_try
; CHECK: return
; CHECK: i32.call $drop=, __cxa_begin_catch
; CHECK: loop # label19:
; CHECK: block
; CHECK: block
; CHECK: br_if 0, {{.*}} # 0: down to label21
; CHECK: try
; CHECK: call foo
; CHECK: br 2 # 2: down to label20
; CHECK: catch
; CHECK: block
; CHECK: try
; CHECK: call __cxa_end_catch
; CHECK: br 0 # 0: down to label24
; CHECK: catch
; CHECK: call __clang_call_terminate
; CHECK: unreachable
; CHECK: end_try # label24:
; CHECK: end_block
; CHECK: rethrow # to caller
; CHECK: end_try
; CHECK: end_block # label21:
; CHECK: call __cxa_end_catch
; CHECK: br 2 # 2: down to label17
; CHECK: end_block # label20:
; CHECK: br 0 # 0: up to label19
; CHECK: end_loop
; CHECK: end_try # label17:
; CHECK: end_block
define void @test2() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
entry:
invoke void @foo()

View File

@ -5,45 +5,62 @@
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"
%struct.Cleanup = type { i8 }
%struct.Temp = type { i8 }
@_ZTIi = external constant i8*
; CHECK-LABEL: test_throw:
; CHECK: throw __cpp_exception, $0
; CHECK-NOT: unreachable
; CHECK: throw __cpp_exception, $0
; CHECK-NOT: unreachable
define void @test_throw(i8* %p) {
call void @llvm.wasm.throw(i32 0, i8* %p)
ret void
}
; CHECK-LABEL: test_rethrow:
; CHECK: rethrow
; CHECK-NOT: unreachable
; CHECK: rethrow
; CHECK-NOT: unreachable
define void @test_rethrow(i8* %p) {
call void @llvm.wasm.rethrow()
ret void
}
; CHECK-LABEL: test_catch_rethrow:
; CHECK: global.get ${{.+}}=, __stack_pointer
; CHECK: try
; CHECK: call foo
; CHECK: catch $[[EXCEPT_REF:[0-9]+]]=
; CHECK: global.set __stack_pointer
; CHECK: block i32
; CHECK: br_on_exn 0, __cpp_exception, $[[EXCEPT_REF]]
; CHECK: rethrow
; CHECK: end_block
; CHECK: extract_exception $[[EXN:[0-9]+]]=
; CHECK-DAG: i32.store __wasm_lpad_context
; CHECK-DAG: i32.store __wasm_lpad_context+4
; CHECK: i32.call $drop=, _Unwind_CallPersonality, $[[EXN]]
; CHECK: i32.call $drop=, __cxa_begin_catch
; CHECK: call __cxa_end_catch
; CHECK: call __cxa_rethrow
; CHECK: end_try
define void @test_catch_rethrow() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
; Simple test with a try-catch
;
; void foo();
; void test_catch() {
; try {
; foo();
; } catch (int) {
; }
; }
; CHECK-LABEL: test_catch:
; CHECK: global.get ${{.+}}=, __stack_pointer
; CHECK: block
; CHECK: try
; CHECK: call foo
; CHECK: br 0
; CHECK: catch $[[EXCEPT_REF:[0-9]+]]=
; CHECK: global.set __stack_pointer
; CHECK: block i32
; CHECK: br_on_exn 0, __cpp_exception, $[[EXCEPT_REF]]
; CHECK: rethrow
; CHECK: end_block
; CHECK: extract_exception $[[EXN:[0-9]+]]=
; CHECK-DAG: i32.store __wasm_lpad_context
; CHECK-DAG: i32.store __wasm_lpad_context+4
; CHECK: i32.call $drop=, _Unwind_CallPersonality, $[[EXN]]
; CHECK: block
; CHECK: br_if 0
; CHECK: i32.call $drop=, __cxa_begin_catch
; CHECK: call __cxa_end_catch
; CHECK: br 1
; CHECK: end_block
; CHECK: call __cxa_rethrow
; CHECK: end_try
; CHECK: end_block
define void @test_catch() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
entry:
invoke void @foo()
to label %try.cont unwind label %catch.dispatch
@ -72,40 +89,88 @@ try.cont: ; preds = %entry, %catch
ret void
}
; Destructor (cleanup) test
;
; void foo();
; struct Temp {
; ~Temp() {}
; };
; void test_cleanup() {
; Temp t;
; foo();
; }
; CHECK-LABEL: test_cleanup:
; CHECK: block
; CHECK: try
; CHECK: call foo
; CHECK: call foo
; CHECK: br 0
; CHECK: catch
; CHECK: global.set __stack_pointer
; CHECK: i32.call $drop=, _ZN7CleanupD1Ev
; CHECK: rethrow
; CHECK: global.set __stack_pointer
; CHECK: i32.call $drop=, _ZN4TempD2Ev
; CHECK: rethrow
; CHECK: end_try
; CHECK: end_block
define void @test_cleanup() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
entry:
%c = alloca %struct.Cleanup, align 1
%t = alloca %struct.Temp, align 1
invoke void @foo()
to label %invoke.cont unwind label %ehcleanup
invoke.cont: ; preds = %entry
%call = call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c)
%call = call %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* %t)
ret void
ehcleanup: ; preds = %entry
%0 = cleanuppad within none []
%call1 = call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c) [ "funclet"(token %0) ]
%call1 = call %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* %t) [ "funclet"(token %0) ]
cleanupret from %0 unwind to caller
}
; Calling a function that may throw within a 'catch (...)' generates a
; temrinatepad, because __cxa_end_catch() also can throw within 'catch (...)'.
;
; void foo();
; void test_terminatepad() {
; try {
; foo();
; } catch (...) {
; foo();
; }
; }
; CHECK-LABEL: test_terminatepad
; CHECK: block
; CHECK: try
; CHECK: call foo
; CHECK: br 0
; CHECK: catch
; CHECK: block i32
; CHECK: br_on_exn 0, __cpp_exception
; CHECK: call __clang_call_terminate, 0
; CHECK: unreachable
; CHECK: end_block
; CHECK: extract_exception
; CHECK: call __clang_call_terminate
; CHECK: unreachable
; CHECK: i32.call $drop=, __cxa_begin_catch
; CHECK: block
; CHECK: try
; CHECK: call foo
; CHECK: br 0
; CHECK: catch
; CHECK: block
; CHECK: try
; CHECK: call __cxa_end_catch
; CHECK: br 0
; CHECK: catch
; CHECK: block i32
; CHECK: br_on_exn 0, __cpp_exception
; CHECK: call __clang_call_terminate, 0
; CHECK: unreachable
; CHECK: end_block
; CHECK: call __clang_call_terminate
; CHECK: unreachable
; CHECK: end_try
; CHECK: end_block
; CHECK: rethrow
; CHECK: end_try
; CHECK: end_block
; CHECK: call __cxa_end_catch
; CHECK: end_try
; CHECK: end_block
define void @test_terminatepad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
entry:
invoke void @foo()
@ -149,23 +214,47 @@ terminate: ; preds = %ehcleanup
; should not have a prologue, and BBs ending with a catchret/cleanupret should
; not have an epilogue. This is separate from __stack_pointer restoring
; instructions after a catch instruction.
;
; void bar(int) noexcept;
; void test_no_prolog_epilog_in_ehpad() {
; int stack_var = 0;
; bar(stack_var);
; try {
; foo();
; } catch (int) {
; foo();
; }
; }
; CHECK-LABEL: test_no_prolog_epilog_in_ehpad
; CHECK: try
; CHECK: call foo
; CHECK: catch
; CHECK-NOT: global.get $push{{.+}}=, __stack_pointer
; CHECK: global.set __stack_pointer
; CHECK: try
; CHECK: call foo
; CHECK: catch
; CHECK-NOT: global.get $push{{.+}}=, __stack_pointer
; CHECK: global.set __stack_pointer
; CHECK: call __cxa_end_catch
; CHECK-NOT: global.set __stack_pointer, $pop{{.+}}
; CHECK: end_try
; CHECK-NOT: global.set __stack_pointer, $pop{{.+}}
; CHECK: end_try
; CHECK: block
; CHECK: try
; CHECK: call foo
; CHECK: br 0
; CHECK: catch
; CHECK-NOT: global.get $push{{.+}}=, __stack_pointer
; CHECK: global.set __stack_pointer
; CHECK: block
; CHECK: block
; CHECK: br_if 0
; CHECK: i32.call $drop=, __cxa_begin_catch
; CHECK: try
; CHECK: call foo
; CHECK: br 2
; CHECK: catch
; CHECK-NOT: global.get $push{{.+}}=, __stack_pointer
; CHECK: global.set __stack_pointer
; CHECK: call __cxa_end_catch
; CHECK: rethrow
; CHECK-NOT: global.set __stack_pointer, $pop{{.+}}
; CHECK: end_try
; CHECK: end_block
; CHECK: call __cxa_rethrow
; CHECK: end_block
; CHECK-NOT: global.set __stack_pointer, $pop{{.+}}
; CHECK: call __cxa_end_catch
; CHECK: end_try
; CHECK: end_block
define void @test_no_prolog_epilog_in_ehpad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
entry:
%stack_var = alloca i32, align 4
@ -210,14 +299,28 @@ ehcleanup: ; preds = %catch
; When a function does not have stack-allocated objects, it does not need to
; store SP back to __stack_pointer global at the epilog.
;
; void foo();
; void test_no_sp_writeback() {
; try {
; foo();
; } catch (...) {
; }
; }
; CHECK-LABEL: no_sp_writeback
; CHECK: try
; CHECK: call foo
; CHECK: end_try
; CHECK-NOT: global.set __stack_pointer
; CHECK: return
define void @no_sp_writeback() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
; CHECK-LABEL: test_no_sp_writeback
; CHECK: block
; CHECK: try
; CHECK: call foo
; CHECK: br 0
; CHECK: catch
; CHECK: i32.call $drop=, __cxa_begin_catch
; CHECK: call __cxa_end_catch
; CHECK: end_try
; CHECK: end_block
; CHECK-NOT: global.set __stack_pointer
; CHECK: return
define void @test_no_sp_writeback() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
entry:
invoke void @foo()
to label %try.cont unwind label %catch.dispatch
@ -249,7 +352,7 @@ declare i8* @__cxa_begin_catch(i8*)
declare void @__cxa_end_catch()
declare void @__cxa_rethrow()
declare void @__clang_call_terminate(i8*)
declare %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* returned)
declare %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* returned)
; CHECK: __cpp_exception:
; CHECK: .eventtype __cpp_exception i32

View File

@ -6,11 +6,19 @@ target triple = "wasm32-unknown-unknown"
; CHECK: @__wasm_lpad_context = external global { i32, i8*, i32 }
@_ZTIi = external constant i8*
%struct.Cleanup = type { i8 }
%struct.Temp = type { i8 }
; A single 'catch (int)' clause.
; A wasm.catch() call, wasm.lsda() call, and personality call to generate a
; selector should all be genereated after the catchpad.
;
; void foo();
; void test0() {
; try {
; foo();
; } catch (int) {
; }
; }
define void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
; CHECK-LABEL: @test0()
entry:
@ -53,11 +61,23 @@ try.cont: ; preds = %entry, %catch
ret void
}
; Two try-catches, one of them is with a single 'catch (...)' clause.
; Two try-catches.
; For the catchpad with a single 'catch (...)', only a wasm.catch() call should
; be generated after the catchpad; wasm.landingpad.index() and personality call
; should NOT be generated. For the other catchpad, the argument of
; wasm.landingpad.index() should be not 1 but 0.
;
; void foo();
; void test1() {
; try {
; foo();
; } catch (...) {
; }
; try {
; foo();
; } catch (int) {
; }
; }
define void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
; CHECK-LABEL: @test1()
entry:
@ -112,8 +132,20 @@ try.cont7: ; preds = %try.cont, %catch4
ret void
}
; A nested try-catch within a catch. Within the nested catchpad, wasm.lsda()
; call should NOT be generated.
; A nested try-catch within a catch.
;
; void foo();
; void test2() {
; try {
; foo();
; } catch (int) {
; try {
; foo();
; } catch (int) {
; }
; }
; }
; Within the nested catchpad, wasm.lsda() call should NOT be generated.
define void @test2() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
; CHECK-LABEL: @test2()
entry:
@ -189,6 +221,15 @@ unreachable: ; preds = %rethrow5
; A cleanuppad with a call to __clang_call_terminate().
; A call to wasm.catch() should be generated after the cleanuppad.
;
; void foo();
; void test3() {
; try {
; foo();
; } catch (...) {
; foo();
; }
; }
define void @test3() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
; CHECK-LABEL: @test3
entry:
@ -234,10 +275,37 @@ terminate: ; preds = %ehcleanup
; PHI demotion test. Only the phi before catchswitch should be demoted; the phi
; before cleanuppad should NOT.
;
; void foo();
; int bar(int) noexcept;
; struct Temp {
; ~Temp() {}
; };
;
; void test4() {
; int num;
; try {
; Temp t;
; num = 1;
; foo();
; num = 2;
; foo();
; } catch (...) {
; bar(num);
; }
; try {
; foo();
; num = 1;
; foo();
; num = 2;
; } catch (...) {
; bar(num);
; }
; }
define void @test4() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
; CHECK-LABEL: @test4
entry:
%c = alloca %struct.Cleanup, align 1
%t = alloca %struct.Temp, align 1
invoke void @foo()
to label %invoke.cont unwind label %ehcleanup
@ -246,13 +314,13 @@ invoke.cont: ; preds = %entry
to label %invoke.cont1 unwind label %ehcleanup
invoke.cont1: ; preds = %invoke.cont
%call = call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c)
%call = call %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* %t)
br label %try.cont
ehcleanup: ; preds = %invoke.cont, %entry
%num.0 = phi i32 [ 2, %invoke.cont ], [ 1, %entry ]
%0 = cleanuppad within none []
%call2 = call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c) [ "funclet"(token %0) ]
%call2 = call %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* %t) [ "funclet"(token %0) ]
cleanupret from %0 unwind label %catch.dispatch
; CHECK: ehcleanup:
; CHECK-NEXT: = phi
@ -265,7 +333,7 @@ catch.start: ; preds = %catch.dispatch
%3 = call i8* @llvm.wasm.get.exception(token %2)
%4 = call i32 @llvm.wasm.get.ehselector(token %2)
%5 = call i8* @__cxa_begin_catch(i8* %3) [ "funclet"(token %2) ]
call void @func(i32 %num.0) [ "funclet"(token %2) ]
call void @bar(i32 %num.0) [ "funclet"(token %2) ]
call void @__cxa_end_catch() [ "funclet"(token %2) ]
catchret from %2 to label %try.cont
@ -288,7 +356,7 @@ catch.start6: ; preds = %catch.dispatch5
%8 = call i8* @llvm.wasm.get.exception(token %7)
%9 = call i32 @llvm.wasm.get.ehselector(token %7)
%10 = call i8* @__cxa_begin_catch(i8* %8) [ "funclet"(token %7) ]
call void @func(i32 %num.1) [ "funclet"(token %7) ]
call void @bar(i32 %num.1) [ "funclet"(token %7) ]
call void @__cxa_end_catch() [ "funclet"(token %7) ]
catchret from %7 to label %try.cont10
@ -353,8 +421,8 @@ merge: ; preds = %bb.true.0, %bb.fals
}
declare void @foo()
declare void @func(i32)
declare %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* returned)
declare void @bar(i32)
declare %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* returned)
declare i32 @__gxx_wasm_personality_v0(...)
declare i8* @llvm.wasm.get.exception(token)
declare i32 @llvm.wasm.get.ehselector(token)