mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-10-22 04:22:57 +02:00
1271da50f3
Summary: This adds support for exception handling to CFGStackify pass. This only adds TRY / END_TRY markers and DOES NOT yet fix unwind mismatches that can be created by the linearization of the CFG into the structural wasm format. The mismatch fix will be added by following patches. In detail, this patch - Added support for TRY / END_TRY markers to support EH - Changed many static functions into class member functions as they take too many arguments now - Added several more bookeeping data structures - Refactored routines that decide where to insert markers, because without refactoring this got too complicated as we added support for new kinds of markers (TRY/END_TRY). - Rewrote rethrow instructions' BB arguments to relative depths in EH pad stack. Reviewers: dschuff, sunfish Subscribers: sbc100, jgravelle-google, llvm-commits Differential Revision: https://reviews.llvm.org/D48273 llvm-svn: 339967
323 lines
13 KiB
YAML
323 lines
13 KiB
YAML
# RUN: llc -mtriple=wasm32-unknown-unknown -exception-model=wasm -run-pass wasm-cfg-stackify %s -o - | FileCheck %s
|
|
|
|
--- |
|
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
|
target triple = "wasm32-unknown-unknown"
|
|
|
|
@__wasm_lpad_context = external global { i32, i8*, i32 }
|
|
|
|
declare void @may_throw()
|
|
; Function Attrs: nounwind
|
|
declare void @dont_throw() #0
|
|
declare i8* @__cxa_begin_catch(i8*)
|
|
declare void @__cxa_end_catch()
|
|
declare void @__cxa_rethrow()
|
|
; Function Attrs: nounwind
|
|
declare i32 @__gxx_wasm_personality_v0(...)
|
|
declare i32 @_Unwind_CallPersonality(i8*) #0
|
|
|
|
define void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
|
|
unreachable
|
|
}
|
|
define void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
|
|
unreachable
|
|
}
|
|
define void @test2() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
|
|
unreachable
|
|
}
|
|
define void @test3() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
|
|
unreachable
|
|
}
|
|
|
|
attributes #0 = { nounwind }
|
|
|
|
---
|
|
# Simplest try-catch
|
|
# try {
|
|
# may_throw();
|
|
# } catch (...) {
|
|
# }
|
|
name: test0
|
|
# CHECK-LABEL: name: test0
|
|
liveins:
|
|
- { reg: '$arguments', reg: '$value_stack' }
|
|
body: |
|
|
bb.0:
|
|
successors: %bb.2, %bb.1
|
|
|
|
CALL_VOID @may_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
|
|
BR %bb.2, implicit-def $arguments
|
|
; CHECK-LABEL: bb.0:
|
|
; CHECK: TRY
|
|
; CHECK-NEXT: CALL_VOID @may_throw
|
|
|
|
bb.1 (landing-pad):
|
|
; predecessors: %bb.0
|
|
successors: %bb.2
|
|
|
|
%2:i32 = CATCH_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
%3:i32 = CALL_I32 @__cxa_begin_catch, %2:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64, implicit-def $value_stack, implicit $value_stack
|
|
DROP_I32 killed %3:i32, implicit-def $arguments
|
|
CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
|
|
|
|
bb.2:
|
|
; predecessors: %bb.0, %bb.1
|
|
|
|
RETURN_VOID implicit-def dead $arguments
|
|
; CHECK-LABEL: bb.2:
|
|
; CHECK-NEXT: END_TRY
|
|
; CHECK: RETURN_VOID
|
|
...
|
|
---
|
|
|
|
# Nested try-catch inside another catch
|
|
# try {
|
|
# may_throw();
|
|
# } catch (int n) {
|
|
# try {
|
|
# may_throw();
|
|
# } catch (int n) {
|
|
# }
|
|
# }
|
|
name: test1
|
|
# CHECK-LABEL: name: test1
|
|
liveins:
|
|
- { reg: '$arguments', reg: '$value_stack' }
|
|
body: |
|
|
bb.0:
|
|
successors: %bb.9, %bb.1
|
|
|
|
CALL_VOID @may_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
|
|
BR %bb.9, implicit-def $arguments
|
|
; CHECK-LABEL: bb.0:
|
|
; CHECK: TRY
|
|
; CHECK-NEXT: CALL_VOID @may_throw
|
|
|
|
bb.1 (landing-pad):
|
|
; predecessors: %bb.0
|
|
successors: %bb.2, %bb.7
|
|
|
|
%30:i32 = CATCH_I32 0, implicit-def dead $arguments
|
|
SET_LOCAL_I32 0, %30:i32, implicit-def $arguments
|
|
%16:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
%27:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
STORE_I32 2, @__wasm_lpad_context + 4, %16:i32, %27:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store 4 into `i8** getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 1)`)
|
|
%26:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
%25:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
STORE_I32 2, @__wasm_lpad_context, %26:i32, %25:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store 4 into `i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 0)`)
|
|
%32:i32 = GET_LOCAL_I32 0, implicit-def $arguments
|
|
%31:i32 = CALL_I32 @_Unwind_CallPersonality, %32:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64
|
|
DROP_I32 killed %31:i32, implicit-def $arguments
|
|
%24:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
%17:i32 = LOAD_I32 2, @__wasm_lpad_context + 8, %24:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (dereferenceable load 4 from `i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 2)`)
|
|
%18:i32 = CONST_I32 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
%19:i32 = NE_I32 %17:i32, %18:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
BR_IF %bb.7, %19:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
|
|
|
|
bb.2:
|
|
; predecessors: %bb.1
|
|
successors: %bb.8, %bb.3, %bb.6
|
|
|
|
%34:i32 = GET_LOCAL_I32 0, implicit-def $arguments
|
|
%33:i32 = CALL_I32 @__cxa_begin_catch, %34:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64
|
|
DROP_I32 killed %33:i32, implicit-def $arguments
|
|
CALL_VOID @may_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
|
|
BR %bb.8, implicit-def $arguments
|
|
; CHECK-LABEL: bb.2:
|
|
; CHECK: DROP_I32
|
|
; CHECK-NEXT: TRY
|
|
; CHECK-NEXT: TRY
|
|
; CHECK-NEXT: CALL_VOID @may_throw
|
|
|
|
bb.3 (landing-pad):
|
|
; predecessors: %bb.2
|
|
successors: %bb.4, %bb.5
|
|
|
|
%35:i32 = CATCH_I32 0, implicit-def dead $arguments
|
|
SET_LOCAL_I32 0, %35:i32, implicit-def $arguments
|
|
%21:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
%20:i32 = CONST_I32 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
STORE_I32 2, @__wasm_lpad_context, %21:i32, %20:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store 4 into `i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 0)`)
|
|
%37:i32 = GET_LOCAL_I32 0, implicit-def $arguments
|
|
%36:i32 = CALL_I32 @_Unwind_CallPersonality, %37:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64
|
|
DROP_I32 killed %36:i32, implicit-def $arguments
|
|
%29:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
%22:i32 = LOAD_I32 2, @__wasm_lpad_context + 8, %29:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (dereferenceable load 4 from `i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 2)`)
|
|
%28:i32 = CONST_I32 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
%23:i32 = NE_I32 %22:i32, %28:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
BR_IF %bb.5, %23:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
|
|
|
|
bb.4:
|
|
; predecessors: %bb.3
|
|
successors: %bb.8
|
|
|
|
%39:i32 = GET_LOCAL_I32 0, implicit-def $arguments
|
|
%38:i32 = CALL_I32 @__cxa_begin_catch, %39:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64
|
|
DROP_I32 killed %38:i32, implicit-def $arguments
|
|
CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
|
|
BR %bb.8, implicit-def $arguments
|
|
|
|
bb.5:
|
|
; predecessors: %bb.3
|
|
successors: %bb.6
|
|
|
|
CALL_VOID @__cxa_rethrow, implicit-def dead $arguments, implicit $sp32, implicit $sp64
|
|
RETHROW %bb.6, implicit-def $arguments
|
|
|
|
bb.6 (landing-pad):
|
|
; predecessors: %bb.2, %bb.5
|
|
|
|
CATCH_ALL implicit-def $arguments
|
|
CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
|
|
RETHROW_TO_CALLER implicit-def $arguments
|
|
; CHECK-LABEL: bb.6 (landing-pad):
|
|
; CHECK-NEXT: END_TRY
|
|
|
|
bb.7:
|
|
; predecessors: %bb.1
|
|
|
|
CALL_VOID @__cxa_rethrow, implicit-def dead $arguments, implicit $sp32, implicit $sp64
|
|
RETHROW_TO_CALLER implicit-def $arguments
|
|
; CHECK-LABEL: bb.7:
|
|
; CHECK-NEXT: END_TRY
|
|
; CHECK: RETHROW 3
|
|
|
|
bb.8:
|
|
; predecessors: %bb.2, %bb.4
|
|
successors: %bb.9
|
|
|
|
CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
|
|
|
|
bb.9:
|
|
; predecessors: %bb.0, %bb.8
|
|
|
|
RETURN_VOID implicit-def dead $arguments
|
|
; CHECK-LABEL: bb.9:
|
|
; CHECK-NEXT: END_TRY
|
|
...
|
|
---
|
|
|
|
# A loop within a try.
|
|
# try {
|
|
# for (int i = 0; i < n; ++i)
|
|
# may_throw();
|
|
# } catch (...) {
|
|
# }
|
|
name: test2
|
|
# CHECK-LABEL: name: test2
|
|
liveins:
|
|
- { reg: '$arguments', reg: '$value_stack' }
|
|
body: |
|
|
bb.0:
|
|
successors: %bb.1, %bb.4
|
|
|
|
%18:i32 = CONST_I32 0, implicit-def dead $arguments
|
|
SET_LOCAL_I32 1, %18:i32, implicit-def $arguments
|
|
%14:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
%19:i32 = GET_LOCAL_I32 0, implicit-def $arguments
|
|
%9:i32 = GE_S_I32 %14:i32, %19:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
BR_IF %bb.4, %9:i32, implicit-def $arguments
|
|
|
|
bb.1:
|
|
; predecessors: %bb.0, %bb.3
|
|
successors: %bb.3, %bb.2
|
|
|
|
CALL_VOID @may_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
|
|
BR %bb.3, implicit-def $arguments
|
|
; CHECK-LABEL: bb.1:
|
|
; CHECK: LOOP
|
|
; CHECK: TRY
|
|
; CHECK-NEXT: CALL_VOID @may_throw
|
|
|
|
bb.2 (landing-pad):
|
|
; predecessors: %bb.1
|
|
successors: %bb.4
|
|
|
|
%11:i32 = CATCH_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
%22:i32 = CALL_I32 @__cxa_begin_catch, %11:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64, implicit-def $value_stack, implicit $value_stack
|
|
DROP_I32 killed %22:i32, implicit-def $arguments
|
|
CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
|
|
BR %bb.4, implicit-def $arguments
|
|
|
|
bb.3:
|
|
; predecessors: %bb.1
|
|
successors: %bb.1, %bb.4
|
|
|
|
%20:i32 = GET_LOCAL_I32 1, implicit-def $arguments
|
|
%17:i32 = CONST_I32 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
%16:i32 = ADD_I32 %20:i32, %17:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
%15:i32 = TEE_LOCAL_I32 1, %16:i32, implicit-def $arguments
|
|
%21:i32 = GET_LOCAL_I32 0, implicit-def $arguments
|
|
%10:i32 = GE_S_I32 %15:i32, %21:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
BR_UNLESS %bb.1, %10:i32, implicit-def $arguments
|
|
; CHECK-LABEL: bb.3:
|
|
; CHECK: END_TRY
|
|
|
|
bb.4:
|
|
; predecessors: %bb.2, %bb.0, %bb.3
|
|
|
|
RETURN_VOID implicit-def dead $arguments
|
|
...
|
|
---
|
|
|
|
# A loop within a catch
|
|
# try {
|
|
# may_throw();
|
|
# } catch (...) {
|
|
# for (int i = 0; i < n; ++i)
|
|
# dont_throw();
|
|
# }
|
|
name: test3
|
|
# CHECK-LABEL: name: test3
|
|
liveins:
|
|
- { reg: '$arguments', reg: '$value_stack' }
|
|
body: |
|
|
bb.0:
|
|
successors: %bb.4, %bb.1
|
|
|
|
CALL_VOID @may_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
|
|
BR %bb.4, implicit-def $arguments
|
|
; CHECK-LABEL: bb.0:
|
|
; CHECK: TRY
|
|
; CHECK-NEXT: CALL_VOID @may_throw
|
|
|
|
bb.1 (landing-pad):
|
|
; predecessors: %bb.0
|
|
successors: %bb.2, %bb.3
|
|
|
|
%9:i32 = CATCH_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
%18:i32 = CALL_I32 @__cxa_begin_catch, %9:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64, implicit-def $value_stack, implicit $value_stack
|
|
DROP_I32 killed %18:i32, implicit-def $arguments
|
|
%19:i32 = CONST_I32 0, implicit-def dead $arguments
|
|
SET_LOCAL_I32 1, %19:i32, implicit-def $arguments
|
|
%14:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
%20:i32 = GET_LOCAL_I32 0, implicit-def $arguments
|
|
%10:i32 = GE_S_I32 %14:i32, %20:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
BR_IF %bb.3, %10:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
|
|
|
|
bb.2:
|
|
; predecessors: %bb.1, %bb.2
|
|
successors: %bb.2, %bb.3
|
|
|
|
CALL_VOID @dont_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
|
|
%21:i32 = GET_LOCAL_I32 1, implicit-def $arguments
|
|
%17:i32 = CONST_I32 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
%16:i32 = ADD_I32 %21:i32, %17:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
%15:i32 = TEE_LOCAL_I32 1, %16:i32, implicit-def $arguments
|
|
%22:i32 = GET_LOCAL_I32 0, implicit-def $arguments
|
|
%11:i32 = GE_S_I32 %15:i32, %22:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
|
|
BR_UNLESS %bb.2, %11:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
|
|
|
|
bb.3:
|
|
; predecessors: %bb.1, %bb.2
|
|
successors: %bb.4
|
|
|
|
CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
|
|
|
|
bb.4:
|
|
; predecessors: %bb.0, %bb.3
|
|
|
|
RETURN_VOID implicit-def dead $arguments
|
|
; CHECK-LABEL: bb.4:
|
|
; CHECK: END_TRY
|