Implement Exception Handling for classic interpreter (#3096)
This PR adds the initial support for WASM exception handling: * Inside the classic interpreter only: * Initial handling of Tags * Initial handling of Exceptions based on W3C Exception Proposal * Import and Export of Exceptions and Tags * Add `cmake -DWAMR_BUILD_EXCE_HANDLING=1/0` option to enable/disable the feature, and by default it is disabled * Update the wamr-test-suites scripts to test the feature * Additional CI/CD changes to validate the exception spec proposal cases Refer to: https://github.com/bytecodealliance/wasm-micro-runtime/issues/1884587513f3c68bebfe9ad759bccdfed8Signed-off-by: Ricardo Aguilar <ricardoaguilar@siemens.com> Co-authored-by: Chris Woods <chris.woods@siemens.com> Co-authored-by: Rene Ermler <rene.ermler@siemens.com> Co-authored-by: Trenner Thomas <trenner.thomas@siemens.com>
This commit is contained in:
@ -47,6 +47,7 @@ IWASM_CMD = get_iwasm_cmd(PLATFORM_NAME)
|
||||
IWASM_SGX_CMD = "../../../product-mini/platforms/linux-sgx/enclave-sample/iwasm"
|
||||
IWASM_QEMU_CMD = "iwasm"
|
||||
SPEC_TEST_DIR = "spec/test/core"
|
||||
EXCE_HANDLING_DIR = "exception-handling/test/core"
|
||||
WAST2WASM_CMD = exe_file_path("./wabt/out/gcc/Release/wat2wasm")
|
||||
SPEC_INTERPRETER_CMD = "spec/interpreter/wasm"
|
||||
WAMRC_CMD = "../../../wamr-compiler/build/wamrc"
|
||||
@ -78,8 +79,10 @@ def ignore_the_case(
|
||||
simd_flag=False,
|
||||
gc_flag=False,
|
||||
xip_flag=False,
|
||||
eh_flag=False,
|
||||
qemu_flag=False,
|
||||
):
|
||||
|
||||
if case_name in ["comments", "inline-module", "names"]:
|
||||
return True
|
||||
|
||||
@ -126,7 +129,7 @@ def ignore_the_case(
|
||||
return False
|
||||
|
||||
|
||||
def preflight_check(aot_flag):
|
||||
def preflight_check(aot_flag, eh_flag):
|
||||
if not pathlib.Path(SPEC_TEST_DIR).resolve().exists():
|
||||
print(f"Can not find {SPEC_TEST_DIR}")
|
||||
return False
|
||||
@ -139,6 +142,10 @@ def preflight_check(aot_flag):
|
||||
print(f"Can not find {WAMRC_CMD}")
|
||||
return False
|
||||
|
||||
if eh_flag and not pathlib.Path(EXCE_HANDLING_DIR).resolve().exists():
|
||||
print(f"Can not find {EXCE_HANDLING_DIR}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@ -151,6 +158,7 @@ def test_case(
|
||||
multi_thread_flag=False,
|
||||
simd_flag=False,
|
||||
xip_flag=False,
|
||||
eh_flag=False,
|
||||
clean_up_flag=True,
|
||||
verbose_flag=True,
|
||||
gc_flag=False,
|
||||
@ -195,6 +203,9 @@ def test_case(
|
||||
if xip_flag:
|
||||
CMD.append("--xip")
|
||||
|
||||
if eh_flag:
|
||||
CMD.append("--eh")
|
||||
|
||||
if qemu_flag:
|
||||
CMD.append("--qemu")
|
||||
CMD.append("--qemu-firmware")
|
||||
@ -268,6 +279,7 @@ def test_suite(
|
||||
multi_thread_flag=False,
|
||||
simd_flag=False,
|
||||
xip_flag=False,
|
||||
eh_flag=False,
|
||||
clean_up_flag=True,
|
||||
verbose_flag=True,
|
||||
gc_flag=False,
|
||||
@ -291,6 +303,15 @@ def test_suite(
|
||||
gc_case_list = sorted(suite_path.glob("gc/*.wast"))
|
||||
case_list.extend(gc_case_list)
|
||||
|
||||
if eh_flag:
|
||||
eh_path = pathlib.Path(EXCE_HANDLING_DIR).resolve()
|
||||
if not eh_path.exists():
|
||||
print(f"can not find spec test cases at {eh_path}")
|
||||
return False
|
||||
eh_case_list = sorted(eh_path.glob("*.wast"))
|
||||
eh_case_list_include = [test for test in eh_case_list if test.stem in ["throw", "tag", "try_catch", "rethrow", "try_delegate"]]
|
||||
case_list.extend(eh_case_list_include)
|
||||
|
||||
# ignore based on command line options
|
||||
filtered_case_list = []
|
||||
for case_path in case_list:
|
||||
@ -305,6 +326,7 @@ def test_suite(
|
||||
simd_flag,
|
||||
gc_flag,
|
||||
xip_flag,
|
||||
eh_flag,
|
||||
qemu_flag,
|
||||
):
|
||||
filtered_case_list.append(case_path)
|
||||
@ -331,6 +353,7 @@ def test_suite(
|
||||
multi_thread_flag,
|
||||
simd_flag,
|
||||
xip_flag,
|
||||
eh_flag,
|
||||
clean_up_flag,
|
||||
verbose_flag,
|
||||
gc_flag,
|
||||
@ -369,6 +392,7 @@ def test_suite(
|
||||
multi_thread_flag,
|
||||
simd_flag,
|
||||
xip_flag,
|
||||
eh_flag,
|
||||
clean_up_flag,
|
||||
verbose_flag,
|
||||
gc_flag,
|
||||
@ -428,6 +452,14 @@ def main():
|
||||
dest="xip_flag",
|
||||
help="Running with the XIP feature",
|
||||
)
|
||||
# added to support WASM_ENABLE_EXCE_HANDLING
|
||||
parser.add_argument(
|
||||
"-e",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="eh_flag",
|
||||
help="Running with the exception-handling feature",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
action="store_true",
|
||||
@ -508,7 +540,7 @@ def main():
|
||||
if options.target == "x86_32":
|
||||
options.target = "i386"
|
||||
|
||||
if not preflight_check(options.aot_flag):
|
||||
if not preflight_check(options.aot_flag, options.eh_flag):
|
||||
return False
|
||||
|
||||
if not options.cases:
|
||||
@ -527,6 +559,7 @@ def main():
|
||||
options.multi_thread_flag,
|
||||
options.simd_flag,
|
||||
options.xip_flag,
|
||||
options.eh_flag,
|
||||
options.clean_up_flag,
|
||||
options.verbose_flag,
|
||||
options.gc_flag,
|
||||
@ -552,6 +585,7 @@ def main():
|
||||
options.multi_thread_flag,
|
||||
options.simd_flag,
|
||||
options.xip_flag,
|
||||
options.eh_flag,
|
||||
options.clean_up_flag,
|
||||
options.verbose_flag,
|
||||
options.gc_flag,
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
diff --git a/test/core/try_catch.wast b/test/core/try_catch.wast
|
||||
index 2a0e9ff6..f243489d 100644
|
||||
--- a/test/core/try_catch.wast
|
||||
+++ b/test/core/try_catch.wast
|
||||
@@ -203,7 +203,6 @@
|
||||
|
||||
(assert_return (invoke "catch-param-i32" (i32.const 5)) (i32.const 5))
|
||||
|
||||
-(assert_return (invoke "catch-imported") (i32.const 2))
|
||||
|
||||
(assert_return (invoke "catchless-try" (i32.const 0)) (i32.const 0))
|
||||
(assert_return (invoke "catchless-try" (i32.const 1)) (i32.const 1))
|
||||
@@ -231,7 +230,6 @@
|
||||
)
|
||||
)
|
||||
|
||||
-(assert_return (invoke "imported-mismatch") (i32.const 3))
|
||||
|
||||
(assert_malformed
|
||||
(module quote "(module (func (catch_all)))")
|
||||
@ -301,6 +301,9 @@ parser.add_argument('--simd', default=False, action='store_true',
|
||||
parser.add_argument('--xip', default=False, action='store_true',
|
||||
help="Enable XIP")
|
||||
|
||||
parser.add_argument('--eh', default=False, action='store_true',
|
||||
help="Enable Exception Handling")
|
||||
|
||||
parser.add_argument('--multi-module', default=False, action='store_true',
|
||||
help="Enable Multi-thread")
|
||||
|
||||
@ -762,6 +765,13 @@ def test_assert(r, opts, mode, cmd, expected):
|
||||
if o.find(e) >= 0 or e.find(o) >= 0:
|
||||
return True
|
||||
|
||||
# wasm-exception thrown out of function call, not a trap
|
||||
if mode=='wasmexception':
|
||||
o = re.sub('^Exception: ', '', out)
|
||||
e = re.sub('^Exception: ', '', expected)
|
||||
if o.find(e) >= 0 or e.find(o) >= 0:
|
||||
return True
|
||||
|
||||
## 0x9:i32,-0x1:i32 -> ['0x9:i32', '-0x1:i32']
|
||||
expected_list = re.split(',', expected)
|
||||
out_list = re.split(',', out)
|
||||
@ -987,6 +997,42 @@ def test_assert_exhaustion(r,opts,form):
|
||||
expected = "Exception: %s\n" % m.group(3)
|
||||
test_assert(r, opts, "exhaustion", "%s %s" % (func, " ".join(args)), expected)
|
||||
|
||||
|
||||
# added to support WASM_ENABLE_EXCE_HANDLING
|
||||
def test_assert_wasmexception(r,opts,form):
|
||||
# params
|
||||
|
||||
# ^
|
||||
# \(assert_exception\s+
|
||||
# \(invoke\s+"([^"]+)"\s+
|
||||
# (\(.*\))\s*
|
||||
# ()
|
||||
# \)\s*
|
||||
# \)\s*
|
||||
# $
|
||||
m = re.search('^\(assert_exception\s+\(invoke\s+"([^"]+)"\s+(\(.*\))\s*\)\s*\)\s*$', form)
|
||||
if not m:
|
||||
# no params
|
||||
|
||||
# ^
|
||||
# \(assert_exception\s+
|
||||
# \(invoke\s+"([^"]+)"\s*
|
||||
# ()
|
||||
# \)\s*
|
||||
# \)\s*
|
||||
# $
|
||||
m = re.search('^\(assert_exception\s+\(invoke\s+"([^"]+)"\s*()\)\s*\)\s*$', form)
|
||||
if not m:
|
||||
raise Exception("unparsed assert_exception: '%s'" % form)
|
||||
func = m.group(1) # function name
|
||||
if m.group(2) == '': # arguments
|
||||
args = []
|
||||
else:
|
||||
args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", m.group(2)[1:-1])]
|
||||
|
||||
expected = "Exception: uncaught wasm exception\n"
|
||||
test_assert(r, opts, "wasmexception", "%s %s" % (func, " ".join(args)), expected)
|
||||
|
||||
def do_invoke(r, opts, form):
|
||||
# params
|
||||
m = re.search('^\(invoke\s+"([^"]+)"\s+(\(.*\))\s*\)\s*$', form)
|
||||
@ -1025,6 +1071,8 @@ def compile_wast_to_wasm(form, wast_tempfile, wasm_tempfile, opts):
|
||||
# default arguments
|
||||
if opts.gc:
|
||||
cmd = [opts.wast2wasm, "-u", "-d", wast_tempfile, "-o", wasm_tempfile]
|
||||
elif opts.eh:
|
||||
cmd = [opts.wast2wasm, "--enable-thread", "--no-check", "--enable-exceptions", "--enable-tail-call", wast_tempfile, "-o", wasm_tempfile ]
|
||||
else:
|
||||
cmd = [opts.wast2wasm, "--enable-thread", "--no-check",
|
||||
wast_tempfile, "-o", wasm_tempfile ]
|
||||
@ -1236,6 +1284,8 @@ if __name__ == "__main__":
|
||||
test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile if test_aot else None, opts, r)
|
||||
elif re.match("^\(assert_exhaustion\\b.*", form):
|
||||
test_assert_exhaustion(r, opts, form)
|
||||
elif re.match("^\(assert_exception\\b.*", form):
|
||||
test_assert_wasmexception(r, opts, form)
|
||||
elif re.match("^\(assert_unlinkable\\b.*", form):
|
||||
test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile if test_aot else None, opts, r, False)
|
||||
elif re.match("^\(assert_malformed\\b.*", form):
|
||||
|
||||
@ -24,6 +24,7 @@ function help()
|
||||
echo "-S enable SIMD feature"
|
||||
echo "-G enable GC feature"
|
||||
echo "-X enable XIP feature"
|
||||
echo "-e enable exception handling"
|
||||
echo "-x test SGX"
|
||||
echo "-w enable WASI threads"
|
||||
echo "-b use the wabt binary release package instead of compiling from the source code"
|
||||
@ -50,6 +51,7 @@ COLLECT_CODE_COVERAGE=0
|
||||
ENABLE_SIMD=0
|
||||
ENABLE_GC=0
|
||||
ENABLE_XIP=0
|
||||
ENABLE_EH=0
|
||||
ENABLE_DEBUG_VERSION=0
|
||||
ENABLE_GC_HEAP_VERIFY=0
|
||||
#unit test case arrary
|
||||
@ -70,7 +72,7 @@ WASI_TESTSUITE_COMMIT="ee807fc551978490bf1c277059aabfa1e589a6c2"
|
||||
TARGET_LIST=("AARCH64" "AARCH64_VFP" "ARMV7" "ARMV7_VFP" "THUMBV7" "THUMBV7_VFP" \
|
||||
"RISCV32" "RISCV32_ILP32F" "RISCV32_ILP32D" "RISCV64" "RISCV64_LP64F" "RISCV64_LP64D")
|
||||
|
||||
while getopts ":s:cabgvt:m:MCpSXxwPGQF:j:T:" opt
|
||||
while getopts ":s:cabgvt:m:MCpSXexwPGQF:j:T:" opt
|
||||
do
|
||||
OPT_PARSED="TRUE"
|
||||
case $opt in
|
||||
@ -145,6 +147,10 @@ do
|
||||
echo "enable XIP feature"
|
||||
ENABLE_XIP=1
|
||||
;;
|
||||
e)
|
||||
echo "enable exception handling feature"
|
||||
ENABLE_EH=1
|
||||
;;
|
||||
x)
|
||||
echo "test SGX"
|
||||
SGX_OPT="--sgx"
|
||||
@ -425,6 +431,26 @@ function spec_test()
|
||||
git apply ../../spec-test-script/thread_proposal_fix_atomic_case.patch
|
||||
fi
|
||||
|
||||
if [ ${ENABLE_EH} == 1 ]; then
|
||||
echo "checkout exception-handling test cases"
|
||||
popd
|
||||
if [ ! -d "exception-handling" ];then
|
||||
echo "exception-handling not exist, clone it from github"
|
||||
git clone -b master --single-branch https://github.com/WebAssembly/exception-handling
|
||||
fi
|
||||
pushd exception-handling
|
||||
|
||||
# restore and clean everything
|
||||
git reset --hard 51c721661b671bb7dc4b3a3acb9e079b49778d36
|
||||
|
||||
if [[ ${ENABLE_MULTI_MODULE} == 0 ]]; then
|
||||
git apply ../../spec-test-script/exception_handling.patch
|
||||
fi
|
||||
|
||||
popd
|
||||
echo $(pwd)
|
||||
fi
|
||||
|
||||
# update GC cases
|
||||
if [[ ${ENABLE_GC} == 1 ]]; then
|
||||
echo "checkout spec for GC proposal"
|
||||
@ -463,6 +489,10 @@ function spec_test()
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ 1 == ${ENABLE_EH} ]]; then
|
||||
ARGS_FOR_SPEC_TEST+="-e "
|
||||
fi
|
||||
|
||||
# sgx only enable in interp mode and aot mode
|
||||
if [[ ${SGX_OPT} == "--sgx" ]];then
|
||||
if [[ $1 == 'classic-interp' || $1 == 'fast-interp' || $1 == 'aot' || $1 == 'fast-jit' ]]; then
|
||||
@ -827,6 +857,10 @@ function trigger()
|
||||
EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_LIB_WASI_THREADS=1"
|
||||
fi
|
||||
|
||||
if [[ ${ENABLE_EH} == 1 ]]; then
|
||||
EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_EXCE_HANDLING=1"
|
||||
EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_TAIL_CALL=1"
|
||||
fi
|
||||
echo "SANITIZER IS" $WAMR_BUILD_SANITIZER
|
||||
|
||||
if [[ "$WAMR_BUILD_SANITIZER" == "ubsan" ]]; then
|
||||
|
||||
Reference in New Issue
Block a user