Skip to content

Commit

Permalink
wasm2c: Implement EHv4 (#2513)
Browse files Browse the repository at this point in the history
Continuation of #2470 / #2512
  • Loading branch information
SoniEx2 authored Jan 18, 2025
1 parent d1c9d90 commit 46648b0
Show file tree
Hide file tree
Showing 16 changed files with 295 additions and 37 deletions.
177 changes: 150 additions & 27 deletions src/c-writer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ class CWriter {
bool IsTopLabelUsed() const;
void PopLabel();

static constexpr bool AreInitializersAlwaysNull(Type);
static constexpr char MangleType(Type);
static constexpr char MangleField(ModuleFieldType);
static std::string MangleTypes(const TypeVector&);
Expand Down Expand Up @@ -483,10 +484,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 @@ -635,6 +638,19 @@ void CWriter::PopLabel() {
label_stack_.pop_back();
}

// static
constexpr bool CWriter::AreInitializersAlwaysNull(Type type) {
// clang-format off
switch (type) {
case Type::FuncRef: return false;
case Type::ExternRef: return true;
case Type::ExnRef: return true;
default:
WABT_UNREACHABLE;
}
// clang-format on
}

// static
constexpr char CWriter::MangleType(Type type) {
// clang-format off
Expand All @@ -646,6 +662,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 +1235,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 +1256,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 @@ -2301,9 +2320,8 @@ void CWriter::WriteElemInitializerDecls() {
continue;
}

if (elem_segment->elem_type == Type::ExternRef) {
// no need to store externref elem initializers because only
// ref.null is possible
if (AreInitializersAlwaysNull(elem_segment->elem_type)) {
// no need to store these initializers because only ref.null is possible
continue;
}

Expand Down Expand Up @@ -2373,9 +2391,8 @@ void CWriter::WriteElemInitializers() {
continue;
}

if (elem_segment->elem_type == Type::ExternRef) {
// no need to store externref elem initializers because only
// ref.null is possible
if (AreInitializersAlwaysNull(elem_segment->elem_type)) {
// no need to store these initializers because only ref.null is possible
continue;
}

Expand Down Expand Up @@ -2476,15 +2493,15 @@ void CWriter::WriteElemInitializers() {
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);
assert(dst_table->elem_type.IsRef() &&
dst_table->elem_type != Type::Reference);
assert(dst_table->elem_type == src_segment->elem_type);

Write(GetReferenceTypeName(dst_table->elem_type), "_table_init(",
ExternalInstancePtr(ModuleFieldType::Table, dst_table->name), ", ");

// elem segment exprs needed only for funcref tables
// because externref tables can only be initialized with ref.null
// because externref and exnref tables can only be initialized with ref.null
if (dst_table->elem_type == Type::FuncRef) {
if (src_segment->elem_exprs.empty()) {
Write("NULL, ");
Expand Down Expand Up @@ -3125,7 +3142,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 @@ -3269,7 +3286,7 @@ 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.IsRef()) {
Write(GetReferenceNullValue(local_type));
} else if (local_type == Type::V128) {
Write("simde_wasm_i64x2_make(0, 0)");
Expand Down Expand Up @@ -3301,27 +3318,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 @@ -3332,7 +3349,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 @@ -3430,7 +3447,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 @@ -3477,6 +3494,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 @@ -3879,6 +3975,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 @@ -3995,7 +4095,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 @@ -4022,6 +4134,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 @@ -4146,8 +4267,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 @@ -5998,6 +6117,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 @@ -6010,6 +6131,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
Loading

0 comments on commit 46648b0

Please sign in to comment.