Skip to content

Commit

Permalink
wasm2c: Implement EHv4
Browse files Browse the repository at this point in the history
  • Loading branch information
SoniEx2 committed Nov 30, 2024
1 parent 0583f0f commit 9ab1faf
Show file tree
Hide file tree
Showing 16 changed files with 279 additions and 29 deletions.
157 changes: 136 additions & 21 deletions src/c-writer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -483,10 +483,12 @@ class CWriter {
void Write(const AtomicRmwExpr& expr);
void Write(const AtomicRmwCmpxchgExpr& expr);

size_t BeginTry(const TryExpr& tryexpr);
size_t BeginTry(const Block& block);
void WriteTryCatch(const TryExpr& tryexpr);
void WriteTryDelegate(const TryExpr& tryexpr);
void Write(const TryTableExpr& try_table_expr);
void Write(const Catch& c);
void Write(const TableCatch& c);
void WriteThrow();

void PushTryCatch(const std::string& name);
Expand Down Expand Up @@ -646,6 +648,7 @@ constexpr char CWriter::MangleType(Type type) {
case Type::V128: return 'o';
case Type::FuncRef: return 'r';
case Type::ExternRef: return 'e';
case Type::ExnRef: return 'x';
default:
WABT_UNREACHABLE;
}
Expand Down Expand Up @@ -1218,6 +1221,7 @@ const char* CWriter::GetCTypeName(const Type& type) {
case Type::V128: return "v128";
case Type::FuncRef: return "wasm_rt_funcref_t";
case Type::ExternRef: return "wasm_rt_externref_t";
case Type::ExnRef: return "wasm_rt_exnref_t";
default:
WABT_UNREACHABLE;
}
Expand All @@ -1238,6 +1242,7 @@ void CWriter::Write(TypeEnum type) {
case Type::V128: Write("WASM_RT_V128"); break;
case Type::FuncRef: Write("WASM_RT_FUNCREF"); break;
case Type::ExternRef: Write("WASM_RT_EXTERNREF"); break;
case Type::ExnRef: Write("WASM_RT_EXNREF"); break;
default:
WABT_UNREACHABLE;
}
Expand Down Expand Up @@ -2284,7 +2289,8 @@ void CWriter::WriteElemInitializerDecls() {
continue;
}

if (elem_segment->elem_type == Type::ExternRef) {
if (elem_segment->elem_type == Type::ExternRef ||
elem_segment->elem_type == Type::ExnRef) {
// no need to store externref elem initializers because only
// ref.null is possible
continue;
Expand Down Expand Up @@ -2356,7 +2362,8 @@ void CWriter::WriteElemInitializers() {
continue;
}

if (elem_segment->elem_type == Type::ExternRef) {
if (elem_segment->elem_type == Type::ExternRef ||
elem_segment->elem_type == Type::ExnRef) {
// no need to store externref elem initializers because only
// ref.null is possible
continue;
Expand Down Expand Up @@ -2460,7 +2467,8 @@ void CWriter::WriteElemTableInit(bool active_initialization,
const ElemSegment* src_segment,
const Table* dst_table) {
assert(dst_table->elem_type == Type::FuncRef ||
dst_table->elem_type == Type::ExternRef);
dst_table->elem_type == Type::ExternRef ||
dst_table->elem_type == Type::ExnRef);
assert(dst_table->elem_type == src_segment->elem_type);

Write(GetReferenceTypeName(dst_table->elem_type), "_table_init(",
Expand Down Expand Up @@ -3108,7 +3116,7 @@ void CWriter::WriteVarsByType(const Vars& vars,
const ToDo& todo,
bool setjmp_safe) {
for (Type type : {Type::I32, Type::I64, Type::F32, Type::F64, Type::V128,
Type::FuncRef, Type::ExternRef}) {
Type::FuncRef, Type::ExternRef, Type::ExnRef}) {
Index var_index = 0;
size_t count = 0;
for (const auto& var : vars) {
Expand Down Expand Up @@ -3252,7 +3260,8 @@ void CWriter::WriteLocals(const std::vector<std::string>& index_to_name) {
func_->local_types, [](auto x) { return x; },
[&](Index local_index, Type local_type) {
Write(DefineParamName(index_to_name[num_params + local_index]), " = ");
if (local_type == Type::FuncRef || local_type == Type::ExternRef) {
if (local_type == Type::FuncRef || local_type == Type::ExternRef ||
local_type == Type::ExnRef) {
Write(GetReferenceNullValue(local_type));
} else if (local_type == Type::V128) {
Write("simde_wasm_i64x2_make(0, 0)");
Expand Down Expand Up @@ -3284,27 +3293,27 @@ void CWriter::Write(const Block& block) {
PushTypes(block.decl.sig.result_types);
}

size_t CWriter::BeginTry(const TryExpr& tryexpr) {
size_t CWriter::BeginTry(const Block& block) {
func_includes_.insert("exceptions");
Write(OpenBrace()); /* beginning of try-catch */
const std::string tlabel = DefineLabelName(tryexpr.block.label);
Write(OpenBrace()); /* beginning of try-catch or try_table */
const std::string tlabel = DefineLabelName(block.label);
Write("WASM_RT_UNWIND_TARGET *", tlabel,
"_outer_target = wasm_rt_get_unwind_target();", Newline());
Write("WASM_RT_UNWIND_TARGET ", tlabel, "_unwind_target;", Newline());
Write("if (!wasm_rt_try(", tlabel, "_unwind_target)) ");
Write(OpenBrace()); /* beginning of try block */
DropTypes(tryexpr.block.decl.GetNumParams());
Write(OpenBrace()); /* beginning of try or try_table block */
DropTypes(block.decl.GetNumParams());
const size_t mark = MarkTypeStack();
PushLabel(LabelType::Try, tryexpr.block.label, tryexpr.block.decl.sig);
PushTypes(tryexpr.block.decl.sig.param_types);
PushLabel(LabelType::Try, block.label, block.decl.sig);
PushTypes(block.decl.sig.param_types);
Write("wasm_rt_set_unwind_target(&", tlabel, "_unwind_target);", Newline());
PushTryCatch(tlabel);
Write(tryexpr.block.exprs);
Write(block.exprs);
ResetTypeStack(mark);
Write("wasm_rt_set_unwind_target(", tlabel, "_outer_target);", Newline());
Write(CloseBrace()); /* end of try block */
Write(CloseBrace()); /* end of try or try_table block */
Write(" else ", OpenBrace()); /* beginning of catch blocks or delegate */
assert(label_stack_.back().name == tryexpr.block.label);
assert(label_stack_.back().name == block.label);
assert(label_stack_.back().label_type == LabelType::Try);
label_stack_.back().label_type = LabelType::Catch;
if (try_catch_stack_.back().used) {
Expand All @@ -3315,7 +3324,7 @@ size_t CWriter::BeginTry(const TryExpr& tryexpr) {
}

void CWriter::WriteTryCatch(const TryExpr& tryexpr) {
const size_t mark = BeginTry(tryexpr);
const size_t mark = BeginTry(tryexpr.block);

/* exception has been thrown -- do we catch it? */

Expand Down Expand Up @@ -3413,7 +3422,7 @@ void CWriter::PopTryCatch() {
}

void CWriter::WriteTryDelegate(const TryExpr& tryexpr) {
const size_t mark = BeginTry(tryexpr);
const size_t mark = BeginTry(tryexpr.block);

/* exception has been thrown -- where do we delegate it? */

Expand Down Expand Up @@ -3460,6 +3469,85 @@ void CWriter::WriteTryDelegate(const TryExpr& tryexpr) {
PushTypes(tryexpr.block.decl.sig.result_types);
}

void CWriter::Write(const TryTableExpr& try_table_expr) {
const size_t mark = BeginTry(try_table_expr.block);

/* exception has been thrown -- do we catch it? */

const LabelName tlabel = LabelName(try_table_expr.block.label);

Write("wasm_rt_set_unwind_target(", tlabel, "_outer_target);", Newline());
PopTryCatch();

ResetTypeStack(mark);
assert(!label_stack_.empty());
assert(label_stack_.back().name == try_table_expr.block.label);
Write(LabelDecl(GetLocalName(try_table_expr.block.label, true)));
PopLabel();

assert(!try_table_expr.catches.empty());
bool has_catch_all{};
for (auto it = try_table_expr.catches.cbegin();
it != try_table_expr.catches.cend(); ++it) {
if (it == try_table_expr.catches.cbegin()) {
Write(Newline());
} else {
Write(" else ");
}
ResetTypeStack(mark);
Write(*it);
if (it->IsCatchAll()) {
has_catch_all = true;
break;
}
}
if (!has_catch_all) {
/* if not caught, rethrow */
Write(" else ", OpenBrace());
WriteThrow();
Write(CloseBrace(), Newline());
}
Write(CloseBrace(), Newline()); /* end of catch blocks */
Write(CloseBrace(), Newline()); /* end of try-catch */

ResetTypeStack(mark);
PushTypes(try_table_expr.block.decl.sig.result_types);
}

void CWriter::Write(const TableCatch& c) {
if (!c.IsCatchAll()) {
Write("if (wasm_rt_exception_tag() == ",
TagSymbol(module_->GetTag(c.tag)->name), ") ", OpenBrace());

const Tag* tag = module_->GetTag(c.tag);
const FuncDeclaration& tag_type = tag->decl;
const Index num_params = tag_type.GetNumParams();
PushTypes(tag_type.sig.param_types);
if (num_params == 1) {
Write("wasm_rt_memcpy(&", StackVar(0), ", wasm_rt_exception(), sizeof(",
tag_type.GetParamType(0), "));", Newline());
} else if (num_params > 1) {
Write(OpenBrace(), tag_type.sig.param_types, " tmp;", Newline());
Write("wasm_rt_memcpy(&tmp, wasm_rt_exception(), sizeof(tmp));",
Newline());
Unspill(tag_type.sig.param_types);
Write(CloseBrace(), Newline());
}
}
if (c.IsRef()) {
PushType(Type::ExnRef);
Write(StackVar(0), ".tag = wasm_rt_exception_tag();", Newline());
Write(StackVar(0), ".size = wasm_rt_exception_size();", Newline());
Write("wasm_rt_memcpy(&", StackVar(0),
".data, wasm_rt_exception(), wasm_rt_exception_size());", Newline());
}

Write(GotoLabel(c.target), Newline());
if (!c.IsCatchAll()) {
Write(CloseBrace());
}
}

void CWriter::Write(const ExprList& exprs) {
for (const Expr& expr : exprs) {
switch (expr.type()) {
Expand Down Expand Up @@ -3862,6 +3950,10 @@ void CWriter::Write(const ExprList& exprs) {
" == ", GetReferenceNullValue(Type::ExternRef), ");",
Newline());
break;
case Type::ExnRef:
Write(StackVar(0, Type::I32), " = (", StackVar(0), ".tag == NULL",
");", Newline());
break;
default:
WABT_UNREACHABLE;
}
Expand Down Expand Up @@ -3978,7 +4070,19 @@ void CWriter::Write(const ExprList& exprs) {
}

WriteThrow();
} break;
// Stop processing this ExprList, since the following are unreachable.
return;
}

case ExprType::ThrowRef: {
Write("if (", StackVar(0), ".tag == NULL) { TRAP(NULL_REF); }");
Write("wasm_rt_load_exception(", StackVar(0), ".tag, ", StackVar(0),
".size, ", StackVar(0), ".data);", Newline());
DropTypes(1);
WriteThrow();
// Stop processing this ExprList, since the following are unreachable.
return;
}

case ExprType::Rethrow: {
const RethrowExpr* rethrow = cast<RethrowExpr>(&expr);
Expand All @@ -4005,6 +4109,15 @@ void CWriter::Write(const ExprList& exprs) {
}
} break;

case ExprType::TryTable: {
const TryTableExpr& try_table = *cast<TryTableExpr>(&expr);
if (try_table.catches.empty()) {
Write(try_table.block);
} else {
Write(try_table);
}
} break;

case ExprType::AtomicLoad: {
Write(*cast<AtomicLoadExpr>(&expr));
break;
Expand Down Expand Up @@ -4129,8 +4242,6 @@ void CWriter::Write(const ExprList& exprs) {
case ExprType::AtomicWait:
case ExprType::AtomicNotify:
case ExprType::CallRef:
case ExprType::ThrowRef:
case ExprType::TryTable:
UNIMPLEMENTED("...");
break;
}
Expand Down Expand Up @@ -5981,6 +6092,8 @@ const char* CWriter::GetReferenceTypeName(const Type& type) {
return "funcref";
case Type::ExternRef:
return "externref";
case Type::ExnRef:
return "exnref";
default:
WABT_UNREACHABLE;
}
Expand All @@ -5993,6 +6106,8 @@ const char* CWriter::GetReferenceNullValue(const Type& type) {
return "wasm_rt_funcref_null_value";
case Type::ExternRef:
return "wasm_rt_externref_null_value";
case Type::ExnRef:
return "wasm_rt_exnref_null_value";
default:
WABT_UNREACHABLE;
}
Expand Down
8 changes: 7 additions & 1 deletion test/run-spec-wasm2c.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def F64ToC(f64_bits):

def MangleType(t):
return {'i32': 'i', 'i64': 'j', 'f32': 'f', 'f64': 'd', 'v128': 'o',
'externref': 'e', 'funcref': 'r'}[t]
'externref': 'e', 'funcref': 'r', 'exnref': 'x'}[t]


def MangleTypes(types):
Expand Down Expand Up @@ -321,6 +321,7 @@ def _WriteAssertReturnCommand(self, command):
'i64': 'ASSERT_RETURN_I64',
'f64': 'ASSERT_RETURN_F64',
'externref': 'ASSERT_RETURN_EXTERNREF',
'exnref': 'ASSERT_RETURN_EXNREF',
'funcref': 'ASSERT_RETURN_FUNCREF',
}

Expand Down Expand Up @@ -385,6 +386,11 @@ def _Constant(self, const):
return 'wasm_rt_funcref_null_value'
else:
assert False # can't make an arbitrary funcref from an integer value
elif type_ == 'exnref':
if value == 'null':
return 'wasm_rt_exnref_null_value'
else:
assert False # can't make an arbitrary exnref from an integer value
else:
assert False

Expand Down
24 changes: 24 additions & 0 deletions test/spec-wasm2c-prefix.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,24 @@ static void error(const char* file, int line, const char* format, ...) {
} \
} while (0)

#define ASSERT_RETURN_EXNREF(f, expected) \
do { \
g_tests_run++; \
int trap_code = wasm_rt_impl_try(); \
if (trap_code) { \
error(__FILE__, __LINE__, #f " trapped (%s).\n", \
wasm_rt_strerror(trap_code)); \
} else { \
wasm_rt_exnref_t actual = f; \
if (is_equal_wasm_rt_exnref_t(actual, expected)) { \
g_tests_passed++; \
} else { \
error(__FILE__, __LINE__, \
"in " #f ": mismatch between expected and actual exnref"); \
} \
} \
} while (0)

#define ASSERT_RETURN_NAN_T(type, itype, fmt, f, kind) \
do { \
g_tests_run++; \
Expand Down Expand Up @@ -273,6 +291,12 @@ static bool is_equal_wasm_rt_funcref_t(wasm_rt_funcref_t x,
(x.func == y.func) && (x.module_instance == y.module_instance);
}

#ifdef WASM_EXN_MAX_SIZE
static bool is_equal_wasm_rt_exnref_t(wasm_rt_exnref_t x, wasm_rt_exnref_t y) {
return x.tag == y.tag && x.size == y.size && !memcmp(x.data, y.data, x.size);
}
#endif

wasm_rt_externref_t spectest_make_externref(uintptr_t x) {
return (wasm_rt_externref_t)(x + 1); // externref(0) is not null
}
Expand Down
6 changes: 6 additions & 0 deletions test/wasm2c/spec/exception-handling/legacy/throw.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
;;; TOOL: run-spec-wasm2c
;;; STDIN_FILE: third_party/testsuite/proposals/exception-handling/legacy/throw.wast
;;; ARGS*: --enable-exceptions
(;; STDOUT ;;;
7/7 tests passed.
;;; STDOUT ;;)
6 changes: 6 additions & 0 deletions test/wasm2c/spec/exception-handling/ref_null.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
;;; TOOL: run-spec-wasm2c
;;; STDIN_FILE: third_party/testsuite/proposals/exception-handling/ref_null.wast
;;; ARGS*: --enable-exceptions
(;; STDOUT ;;;
3/3 tests passed.
;;; STDOUT ;;)
4 changes: 2 additions & 2 deletions test/wasm2c/spec/exception-handling/throw.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
;;; TOOL: run-spec-wasm2c
;;; STDIN_FILE: third_party/testsuite/proposals/exception-handling/legacy/throw.wast
;;; STDIN_FILE: third_party/testsuite/proposals/exception-handling/throw.wast
;;; ARGS*: --enable-exceptions
(;; STDOUT ;;;
7/7 tests passed.
9/9 tests passed.
;;; STDOUT ;;)
Loading

0 comments on commit 9ab1faf

Please sign in to comment.