Skip to content

Commit 46648b0

Browse files
authored
wasm2c: Implement EHv4 (#2513)
Continuation of #2470 / #2512
1 parent d1c9d90 commit 46648b0

16 files changed

+295
-37
lines changed

src/c-writer.cc

Lines changed: 150 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ class CWriter {
266266
bool IsTopLabelUsed() const;
267267
void PopLabel();
268268

269+
static constexpr bool AreInitializersAlwaysNull(Type);
269270
static constexpr char MangleType(Type);
270271
static constexpr char MangleField(ModuleFieldType);
271272
static std::string MangleTypes(const TypeVector&);
@@ -483,10 +484,12 @@ class CWriter {
483484
void Write(const AtomicRmwExpr& expr);
484485
void Write(const AtomicRmwCmpxchgExpr& expr);
485486

486-
size_t BeginTry(const TryExpr& tryexpr);
487+
size_t BeginTry(const Block& block);
487488
void WriteTryCatch(const TryExpr& tryexpr);
488489
void WriteTryDelegate(const TryExpr& tryexpr);
490+
void Write(const TryTableExpr& try_table_expr);
489491
void Write(const Catch& c);
492+
void Write(const TableCatch& c);
490493
void WriteThrow();
491494

492495
void PushTryCatch(const std::string& name);
@@ -635,6 +638,19 @@ void CWriter::PopLabel() {
635638
label_stack_.pop_back();
636639
}
637640

641+
// static
642+
constexpr bool CWriter::AreInitializersAlwaysNull(Type type) {
643+
// clang-format off
644+
switch (type) {
645+
case Type::FuncRef: return false;
646+
case Type::ExternRef: return true;
647+
case Type::ExnRef: return true;
648+
default:
649+
WABT_UNREACHABLE;
650+
}
651+
// clang-format on
652+
}
653+
638654
// static
639655
constexpr char CWriter::MangleType(Type type) {
640656
// clang-format off
@@ -646,6 +662,7 @@ constexpr char CWriter::MangleType(Type type) {
646662
case Type::V128: return 'o';
647663
case Type::FuncRef: return 'r';
648664
case Type::ExternRef: return 'e';
665+
case Type::ExnRef: return 'x';
649666
default:
650667
WABT_UNREACHABLE;
651668
}
@@ -1218,6 +1235,7 @@ const char* CWriter::GetCTypeName(const Type& type) {
12181235
case Type::V128: return "v128";
12191236
case Type::FuncRef: return "wasm_rt_funcref_t";
12201237
case Type::ExternRef: return "wasm_rt_externref_t";
1238+
case Type::ExnRef: return "wasm_rt_exnref_t";
12211239
default:
12221240
WABT_UNREACHABLE;
12231241
}
@@ -1238,6 +1256,7 @@ void CWriter::Write(TypeEnum type) {
12381256
case Type::V128: Write("WASM_RT_V128"); break;
12391257
case Type::FuncRef: Write("WASM_RT_FUNCREF"); break;
12401258
case Type::ExternRef: Write("WASM_RT_EXTERNREF"); break;
1259+
case Type::ExnRef: Write("WASM_RT_EXNREF"); break;
12411260
default:
12421261
WABT_UNREACHABLE;
12431262
}
@@ -2301,9 +2320,8 @@ void CWriter::WriteElemInitializerDecls() {
23012320
continue;
23022321
}
23032322

2304-
if (elem_segment->elem_type == Type::ExternRef) {
2305-
// no need to store externref elem initializers because only
2306-
// ref.null is possible
2323+
if (AreInitializersAlwaysNull(elem_segment->elem_type)) {
2324+
// no need to store these initializers because only ref.null is possible
23072325
continue;
23082326
}
23092327

@@ -2373,9 +2391,8 @@ void CWriter::WriteElemInitializers() {
23732391
continue;
23742392
}
23752393

2376-
if (elem_segment->elem_type == Type::ExternRef) {
2377-
// no need to store externref elem initializers because only
2378-
// ref.null is possible
2394+
if (AreInitializersAlwaysNull(elem_segment->elem_type)) {
2395+
// no need to store these initializers because only ref.null is possible
23792396
continue;
23802397
}
23812398

@@ -2476,15 +2493,15 @@ void CWriter::WriteElemInitializers() {
24762493
void CWriter::WriteElemTableInit(bool active_initialization,
24772494
const ElemSegment* src_segment,
24782495
const Table* dst_table) {
2479-
assert(dst_table->elem_type == Type::FuncRef ||
2480-
dst_table->elem_type == Type::ExternRef);
2496+
assert(dst_table->elem_type.IsRef() &&
2497+
dst_table->elem_type != Type::Reference);
24812498
assert(dst_table->elem_type == src_segment->elem_type);
24822499

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

24862503
// elem segment exprs needed only for funcref tables
2487-
// because externref tables can only be initialized with ref.null
2504+
// because externref and exnref tables can only be initialized with ref.null
24882505
if (dst_table->elem_type == Type::FuncRef) {
24892506
if (src_segment->elem_exprs.empty()) {
24902507
Write("NULL, ");
@@ -3125,7 +3142,7 @@ void CWriter::WriteVarsByType(const Vars& vars,
31253142
const ToDo& todo,
31263143
bool setjmp_safe) {
31273144
for (Type type : {Type::I32, Type::I64, Type::F32, Type::F64, Type::V128,
3128-
Type::FuncRef, Type::ExternRef}) {
3145+
Type::FuncRef, Type::ExternRef, Type::ExnRef}) {
31293146
Index var_index = 0;
31303147
size_t count = 0;
31313148
for (const auto& var : vars) {
@@ -3269,7 +3286,7 @@ void CWriter::WriteLocals(const std::vector<std::string>& index_to_name) {
32693286
func_->local_types, [](auto x) { return x; },
32703287
[&](Index local_index, Type local_type) {
32713288
Write(DefineParamName(index_to_name[num_params + local_index]), " = ");
3272-
if (local_type == Type::FuncRef || local_type == Type::ExternRef) {
3289+
if (local_type.IsRef()) {
32733290
Write(GetReferenceNullValue(local_type));
32743291
} else if (local_type == Type::V128) {
32753292
Write("simde_wasm_i64x2_make(0, 0)");
@@ -3301,27 +3318,27 @@ void CWriter::Write(const Block& block) {
33013318
PushTypes(block.decl.sig.result_types);
33023319
}
33033320

3304-
size_t CWriter::BeginTry(const TryExpr& tryexpr) {
3321+
size_t CWriter::BeginTry(const Block& block) {
33053322
func_includes_.insert("exceptions");
3306-
Write(OpenBrace()); /* beginning of try-catch */
3307-
const std::string tlabel = DefineLabelName(tryexpr.block.label);
3323+
Write(OpenBrace()); /* beginning of try-catch or try_table */
3324+
const std::string tlabel = DefineLabelName(block.label);
33083325
Write("WASM_RT_UNWIND_TARGET *", tlabel,
33093326
"_outer_target = wasm_rt_get_unwind_target();", Newline());
33103327
Write("WASM_RT_UNWIND_TARGET ", tlabel, "_unwind_target;", Newline());
33113328
Write("if (!wasm_rt_try(", tlabel, "_unwind_target)) ");
3312-
Write(OpenBrace()); /* beginning of try block */
3313-
DropTypes(tryexpr.block.decl.GetNumParams());
3329+
Write(OpenBrace()); /* beginning of try or try_table block */
3330+
DropTypes(block.decl.GetNumParams());
33143331
const size_t mark = MarkTypeStack();
3315-
PushLabel(LabelType::Try, tryexpr.block.label, tryexpr.block.decl.sig);
3316-
PushTypes(tryexpr.block.decl.sig.param_types);
3332+
PushLabel(LabelType::Try, block.label, block.decl.sig);
3333+
PushTypes(block.decl.sig.param_types);
33173334
Write("wasm_rt_set_unwind_target(&", tlabel, "_unwind_target);", Newline());
33183335
PushTryCatch(tlabel);
3319-
Write(tryexpr.block.exprs);
3336+
Write(block.exprs);
33203337
ResetTypeStack(mark);
33213338
Write("wasm_rt_set_unwind_target(", tlabel, "_outer_target);", Newline());
3322-
Write(CloseBrace()); /* end of try block */
3339+
Write(CloseBrace()); /* end of try or try_table block */
33233340
Write(" else ", OpenBrace()); /* beginning of catch blocks or delegate */
3324-
assert(label_stack_.back().name == tryexpr.block.label);
3341+
assert(label_stack_.back().name == block.label);
33253342
assert(label_stack_.back().label_type == LabelType::Try);
33263343
label_stack_.back().label_type = LabelType::Catch;
33273344
if (try_catch_stack_.back().used) {
@@ -3332,7 +3349,7 @@ size_t CWriter::BeginTry(const TryExpr& tryexpr) {
33323349
}
33333350

33343351
void CWriter::WriteTryCatch(const TryExpr& tryexpr) {
3335-
const size_t mark = BeginTry(tryexpr);
3352+
const size_t mark = BeginTry(tryexpr.block);
33363353

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

@@ -3430,7 +3447,7 @@ void CWriter::PopTryCatch() {
34303447
}
34313448

34323449
void CWriter::WriteTryDelegate(const TryExpr& tryexpr) {
3433-
const size_t mark = BeginTry(tryexpr);
3450+
const size_t mark = BeginTry(tryexpr.block);
34343451

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

@@ -3477,6 +3494,85 @@ void CWriter::WriteTryDelegate(const TryExpr& tryexpr) {
34773494
PushTypes(tryexpr.block.decl.sig.result_types);
34783495
}
34793496

3497+
void CWriter::Write(const TryTableExpr& try_table_expr) {
3498+
const size_t mark = BeginTry(try_table_expr.block);
3499+
3500+
/* exception has been thrown -- do we catch it? */
3501+
3502+
const LabelName tlabel = LabelName(try_table_expr.block.label);
3503+
3504+
Write("wasm_rt_set_unwind_target(", tlabel, "_outer_target);", Newline());
3505+
PopTryCatch();
3506+
3507+
ResetTypeStack(mark);
3508+
assert(!label_stack_.empty());
3509+
assert(label_stack_.back().name == try_table_expr.block.label);
3510+
Write(LabelDecl(GetLocalName(try_table_expr.block.label, true)));
3511+
PopLabel();
3512+
3513+
assert(!try_table_expr.catches.empty());
3514+
bool has_catch_all{};
3515+
for (auto it = try_table_expr.catches.cbegin();
3516+
it != try_table_expr.catches.cend(); ++it) {
3517+
if (it == try_table_expr.catches.cbegin()) {
3518+
Write(Newline());
3519+
} else {
3520+
Write(" else ");
3521+
}
3522+
ResetTypeStack(mark);
3523+
Write(*it);
3524+
if (it->IsCatchAll()) {
3525+
has_catch_all = true;
3526+
break;
3527+
}
3528+
}
3529+
if (!has_catch_all) {
3530+
/* if not caught, rethrow */
3531+
Write(" else ", OpenBrace());
3532+
WriteThrow();
3533+
Write(CloseBrace(), Newline());
3534+
}
3535+
Write(CloseBrace(), Newline()); /* end of catch blocks */
3536+
Write(CloseBrace(), Newline()); /* end of try-catch */
3537+
3538+
ResetTypeStack(mark);
3539+
PushTypes(try_table_expr.block.decl.sig.result_types);
3540+
}
3541+
3542+
void CWriter::Write(const TableCatch& c) {
3543+
if (!c.IsCatchAll()) {
3544+
Write("if (wasm_rt_exception_tag() == ",
3545+
TagSymbol(module_->GetTag(c.tag)->name), ") ", OpenBrace());
3546+
3547+
const Tag* tag = module_->GetTag(c.tag);
3548+
const FuncDeclaration& tag_type = tag->decl;
3549+
const Index num_params = tag_type.GetNumParams();
3550+
PushTypes(tag_type.sig.param_types);
3551+
if (num_params == 1) {
3552+
Write("wasm_rt_memcpy(&", StackVar(0), ", wasm_rt_exception(), sizeof(",
3553+
tag_type.GetParamType(0), "));", Newline());
3554+
} else if (num_params > 1) {
3555+
Write(OpenBrace(), tag_type.sig.param_types, " tmp;", Newline());
3556+
Write("wasm_rt_memcpy(&tmp, wasm_rt_exception(), sizeof(tmp));",
3557+
Newline());
3558+
Unspill(tag_type.sig.param_types);
3559+
Write(CloseBrace(), Newline());
3560+
}
3561+
}
3562+
if (c.IsRef()) {
3563+
PushType(Type::ExnRef);
3564+
Write(StackVar(0), ".tag = wasm_rt_exception_tag();", Newline());
3565+
Write(StackVar(0), ".size = wasm_rt_exception_size();", Newline());
3566+
Write("wasm_rt_memcpy(&", StackVar(0),
3567+
".data, wasm_rt_exception(), wasm_rt_exception_size());", Newline());
3568+
}
3569+
3570+
Write(GotoLabel(c.target), Newline());
3571+
if (!c.IsCatchAll()) {
3572+
Write(CloseBrace());
3573+
}
3574+
}
3575+
34803576
void CWriter::Write(const ExprList& exprs) {
34813577
for (const Expr& expr : exprs) {
34823578
switch (expr.type()) {
@@ -3879,6 +3975,10 @@ void CWriter::Write(const ExprList& exprs) {
38793975
" == ", GetReferenceNullValue(Type::ExternRef), ");",
38803976
Newline());
38813977
break;
3978+
case Type::ExnRef:
3979+
Write(StackVar(0, Type::I32), " = (", StackVar(0), ".tag == NULL",
3980+
");", Newline());
3981+
break;
38823982
default:
38833983
WABT_UNREACHABLE;
38843984
}
@@ -3995,7 +4095,19 @@ void CWriter::Write(const ExprList& exprs) {
39954095
}
39964096

39974097
WriteThrow();
3998-
} break;
4098+
// Stop processing this ExprList, since the following are unreachable.
4099+
return;
4100+
}
4101+
4102+
case ExprType::ThrowRef: {
4103+
Write("if (", StackVar(0), ".tag == NULL) { TRAP(NULL_REF); }");
4104+
Write("wasm_rt_load_exception(", StackVar(0), ".tag, ", StackVar(0),
4105+
".size, ", StackVar(0), ".data);", Newline());
4106+
DropTypes(1);
4107+
WriteThrow();
4108+
// Stop processing this ExprList, since the following are unreachable.
4109+
return;
4110+
}
39994111

40004112
case ExprType::Rethrow: {
40014113
const RethrowExpr* rethrow = cast<RethrowExpr>(&expr);
@@ -4022,6 +4134,15 @@ void CWriter::Write(const ExprList& exprs) {
40224134
}
40234135
} break;
40244136

4137+
case ExprType::TryTable: {
4138+
const TryTableExpr& try_table = *cast<TryTableExpr>(&expr);
4139+
if (try_table.catches.empty()) {
4140+
Write(try_table.block);
4141+
} else {
4142+
Write(try_table);
4143+
}
4144+
} break;
4145+
40254146
case ExprType::AtomicLoad: {
40264147
Write(*cast<AtomicLoadExpr>(&expr));
40274148
break;
@@ -4146,8 +4267,6 @@ void CWriter::Write(const ExprList& exprs) {
41464267
case ExprType::AtomicWait:
41474268
case ExprType::AtomicNotify:
41484269
case ExprType::CallRef:
4149-
case ExprType::ThrowRef:
4150-
case ExprType::TryTable:
41514270
UNIMPLEMENTED("...");
41524271
break;
41534272
}
@@ -5998,6 +6117,8 @@ const char* CWriter::GetReferenceTypeName(const Type& type) {
59986117
return "funcref";
59996118
case Type::ExternRef:
60006119
return "externref";
6120+
case Type::ExnRef:
6121+
return "exnref";
60016122
default:
60026123
WABT_UNREACHABLE;
60036124
}
@@ -6010,6 +6131,8 @@ const char* CWriter::GetReferenceNullValue(const Type& type) {
60106131
return "wasm_rt_funcref_null_value";
60116132
case Type::ExternRef:
60126133
return "wasm_rt_externref_null_value";
6134+
case Type::ExnRef:
6135+
return "wasm_rt_exnref_null_value";
60136136
default:
60146137
WABT_UNREACHABLE;
60156138
}

test/run-spec-wasm2c.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def F64ToC(f64_bits):
9090

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

9595

9696
def MangleTypes(types):
@@ -321,6 +321,7 @@ def _WriteAssertReturnCommand(self, command):
321321
'i64': 'ASSERT_RETURN_I64',
322322
'f64': 'ASSERT_RETURN_F64',
323323
'externref': 'ASSERT_RETURN_EXTERNREF',
324+
'exnref': 'ASSERT_RETURN_EXNREF',
324325
'funcref': 'ASSERT_RETURN_FUNCREF',
325326
}
326327

@@ -385,6 +386,11 @@ def _Constant(self, const):
385386
return 'wasm_rt_funcref_null_value'
386387
else:
387388
assert False # can't make an arbitrary funcref from an integer value
389+
elif type_ == 'exnref':
390+
if value == 'null':
391+
return 'wasm_rt_exnref_null_value'
392+
else:
393+
assert False # can't make an arbitrary exnref from an integer value
388394
else:
389395
assert False
390396

0 commit comments

Comments
 (0)