Skip to content

Commit e2f49d8

Browse files
authored
Add basic exception handling support (#2282)
This adds basic support for exception handling instructions, according to the spec: https://github.com/WebAssembly/exception-handling/blob/master/proposals/Exceptions.md This PR includes support for: - Binary reading/writing - Wast reading/writing - Stack IR - Validation - binaryen.js + C API - Few IR routines: branch-utils, type-updating, etc - Few passes: just enough to make `wasm-opt -O` pass - Tests This PR does not include support for many optimization passes, fuzzer, or interpreter. They will be follow-up PRs. Try-catch construct is modeled in Binaryen IR in a similar manner to that of if-else: each of try body and catch body will contain a block, which can be omitted if there is only a single instruction. This block will not be emitted in wast or binary, as in if-else. As in if-else, `class Try` contains two expressions each for try body and catch body, and `catch` is not modeled as an instruction. `exnref` value pushed by `catch` is get by `pop` instruction. `br_on_exn` is special: it returns different types of values when taken and not taken. We make `exnref`, the type `br_on_exn` pushes if not taken, as `br_on_exn`'s type.
1 parent 69ad1e8 commit e2f49d8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+6080
-1382
lines changed

build-js.sh

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,10 @@ export_function "_BinaryenMemoryInitId"
227227
export_function "_BinaryenDataDropId"
228228
export_function "_BinaryenMemoryCopyId"
229229
export_function "_BinaryenMemoryFillId"
230+
export_function "_BinaryenTryId"
231+
export_function "_BinaryenThrowId"
232+
export_function "_BinaryenRethrowId"
233+
export_function "_BinaryenBrOnExnId"
230234
export_function "_BinaryenPushId"
231235
export_function "_BinaryenPopId"
232236

@@ -579,6 +583,10 @@ export_function "_BinaryenMemoryInit"
579583
export_function "_BinaryenDataDrop"
580584
export_function "_BinaryenMemoryCopy"
581585
export_function "_BinaryenMemoryFill"
586+
export_function "_BinaryenTry"
587+
export_function "_BinaryenThrow"
588+
export_function "_BinaryenRethrow"
589+
export_function "_BinaryenBrOnExn"
582590
export_function "_BinaryenPush"
583591
export_function "_BinaryenPop"
584592

@@ -757,6 +765,23 @@ export_function "_BinaryenMemoryFillGetDest"
757765
export_function "_BinaryenMemoryFillGetValue"
758766
export_function "_BinaryenMemoryFillGetSize"
759767

768+
# 'Try' expression operations
769+
export_function "_BinaryenTryGetBody"
770+
export_function "_BinaryenTryGetCatchBody"
771+
772+
# 'Throw' expression operations
773+
export_function "_BinaryenThrowGetEvent"
774+
export_function "_BinaryenThrowGetNumOperands"
775+
export_function "_BinaryenThrowGetOperand"
776+
777+
# 'Rethrow' expression operations
778+
export_function "_BinaryenRethrowGetExnref"
779+
780+
# 'BrOnExn' expression operations
781+
export_function "_BinaryenBrOnExnGetEvent"
782+
export_function "_BinaryenBrOnExnGetName"
783+
export_function "_BinaryenBrOnExnGetExnref"
784+
760785
# 'Push' expression operations
761786
export_function "_BinaryenPushGetValue"
762787

check.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,7 @@ def run_gcc_tests():
511511
print('link: ', ' '.join(cmd))
512512
subprocess.check_call(cmd)
513513
print('run...', output_file)
514-
actual = subprocess.check_output([os.path.abspath(output_file)])
514+
actual = subprocess.check_output([os.path.abspath(output_file)]).decode('utf-8')
515515
os.remove(output_file)
516516
if sys.platform == 'darwin':
517517
# Also removes debug directory produced on Mac OS

scripts/gen-s-parser.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,13 @@
423423
("f32x4.convert_i32x4_s", "makeUnary(s, UnaryOp::ConvertSVecI32x4ToVecF32x4)"),
424424
("f32x4.convert_i32x4_u", "makeUnary(s, UnaryOp::ConvertUVecI32x4ToVecF32x4)"),
425425
("f64x2.convert_i64x2_s", "makeUnary(s, UnaryOp::ConvertSVecI64x2ToVecF64x2)"),
426-
("f64x2.convert_i64x2_u", "makeUnary(s, UnaryOp::ConvertUVecI64x2ToVecF64x2)")
426+
("f64x2.convert_i64x2_u", "makeUnary(s, UnaryOp::ConvertUVecI64x2ToVecF64x2)"),
427+
# exception handling instructions
428+
("try", "makeTry(s)"),
429+
("throw", "makeThrow(s)"),
430+
("catch", "makeCatch(s)"),
431+
("rethrow", "makeRethrow(s)"),
432+
("br_on_exn", "makeBrOnExn(s)")
427433
]
428434

429435

src/binaryen-c.cpp

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,14 @@ BinaryenExpressionId BinaryenMemoryCopyId(void) {
354354
BinaryenExpressionId BinaryenMemoryFillId(void) {
355355
return Expression::Id::MemoryFillId;
356356
}
357+
BinaryenExpressionId BinaryenTryId(void) { return Expression::Id::TryId; }
358+
BinaryenExpressionId BinaryenThrowId(void) { return Expression::Id::ThrowId; }
359+
BinaryenExpressionId BinaryenRethrowId(void) {
360+
return Expression::Id::RethrowId;
361+
}
362+
BinaryenExpressionId BinaryenBrOnExnId(void) {
363+
return Expression::Id::BrOnExnId;
364+
}
357365
BinaryenExpressionId BinaryenPushId(void) { return Expression::Id::PushId; }
358366
BinaryenExpressionId BinaryenPopId(void) { return Expression::Id::PopId; }
359367

@@ -1591,6 +1599,73 @@ BinaryenExpressionRef BinaryenPop(BinaryenModuleRef module, BinaryenType type) {
15911599
return static_cast<Expression*>(ret);
15921600
}
15931601

1602+
BinaryenExpressionRef BinaryenTry(BinaryenModuleRef module,
1603+
BinaryenExpressionRef body,
1604+
BinaryenExpressionRef catchBody) {
1605+
auto* ret = Builder(*(Module*)module)
1606+
.makeTry((Expression*)body, (Expression*)catchBody);
1607+
if (tracing) {
1608+
traceExpression(ret, "BinaryenTry", body, catchBody);
1609+
}
1610+
return static_cast<Expression*>(ret);
1611+
}
1612+
1613+
BinaryenExpressionRef BinaryenThrow(BinaryenModuleRef module,
1614+
const char* event,
1615+
BinaryenExpressionRef* operands,
1616+
BinaryenIndex numOperands) {
1617+
std::vector<Expression*> args;
1618+
for (BinaryenIndex i = 0; i < numOperands; i++) {
1619+
args.push_back((Expression*)operands[i]);
1620+
}
1621+
auto* ret = Builder(*(Module*)module).makeThrow(event, args);
1622+
1623+
if (tracing) {
1624+
std::cout << " {\n";
1625+
std::cout << " BinaryenExpressionRef operands[] = { ";
1626+
for (BinaryenIndex i = 0; i < numOperands; i++) {
1627+
if (i > 0) {
1628+
std::cout << ", ";
1629+
}
1630+
std::cout << "expressions[" << expressions[operands[i]] << "]";
1631+
}
1632+
if (numOperands == 0) {
1633+
// ensure the array is not empty, otherwise a compiler error on VS
1634+
std::cout << "0";
1635+
}
1636+
std::cout << " };\n ";
1637+
traceExpression(
1638+
ret, "BinaryenThrow", StringLit(event), "operands", numOperands);
1639+
std::cout << " }\n";
1640+
}
1641+
return static_cast<Expression*>(ret);
1642+
}
1643+
1644+
BinaryenExpressionRef BinaryenRethrow(BinaryenModuleRef module,
1645+
BinaryenExpressionRef exnref) {
1646+
auto* ret = Builder(*(Module*)module).makeRethrow((Expression*)exnref);
1647+
if (tracing) {
1648+
traceExpression(ret, "BinaryenRethrow", exnref);
1649+
}
1650+
return static_cast<Expression*>(ret);
1651+
}
1652+
1653+
BinaryenExpressionRef BinaryenBrOnExn(BinaryenModuleRef module,
1654+
const char* name,
1655+
const char* eventName,
1656+
BinaryenExpressionRef exnref) {
1657+
Module* wasm = (Module*)module;
1658+
Event* event = wasm->getEventOrNull(eventName);
1659+
assert(event && "br_on_exn's event must exist");
1660+
auto* ret = Builder(*wasm).makeBrOnExn(name, event, (Expression*)exnref);
1661+
1662+
if (tracing) {
1663+
traceExpression(
1664+
ret, "BinaryenBrOnExn", StringLit(name), StringLit(eventName), exnref);
1665+
}
1666+
return static_cast<Expression*>(ret);
1667+
}
1668+
15941669
// Expression utility
15951670

15961671
BinaryenExpressionId BinaryenExpressionGetId(BinaryenExpressionRef expr) {
@@ -2721,6 +2796,7 @@ BinaryenExpressionRef BinaryenMemoryFillGetSize(BinaryenExpressionRef expr) {
27212796
assert(expression->is<MemoryFill>());
27222797
return static_cast<MemoryFill*>(expression)->size;
27232798
}
2799+
// Push
27242800
BinaryenExpressionRef BinaryenPushGetValue(BinaryenExpressionRef expr) {
27252801
if (tracing) {
27262802
std::cout << " BinaryenPushGetValue(expressions[" << expressions[expr]
@@ -2731,6 +2807,102 @@ BinaryenExpressionRef BinaryenPushGetValue(BinaryenExpressionRef expr) {
27312807
assert(expression->is<Push>());
27322808
return static_cast<Push*>(expression)->value;
27332809
}
2810+
// Try
2811+
BinaryenExpressionRef BinaryenTryGetBody(BinaryenExpressionRef expr) {
2812+
if (tracing) {
2813+
std::cout << " BinaryenTryGetBody(expressions[" << expressions[expr]
2814+
<< "]);\n";
2815+
}
2816+
2817+
auto* expression = (Expression*)expr;
2818+
assert(expression->is<Try>());
2819+
return static_cast<Try*>(expression)->body;
2820+
}
2821+
BinaryenExpressionRef BinaryenTryGetCatchBody(BinaryenExpressionRef expr) {
2822+
if (tracing) {
2823+
std::cout << " BinaryenTryGetCatchBody(expressions[" << expressions[expr]
2824+
<< "]);\n";
2825+
}
2826+
2827+
auto* expression = (Expression*)expr;
2828+
assert(expression->is<Try>());
2829+
return static_cast<Try*>(expression)->catchBody;
2830+
}
2831+
// Throw
2832+
const char* BinaryenThrowGetEvent(BinaryenExpressionRef expr) {
2833+
if (tracing) {
2834+
std::cout << " BinaryenThrowGetEvent(expressions[" << expressions[expr]
2835+
<< "]);\n";
2836+
}
2837+
2838+
auto* expression = (Expression*)expr;
2839+
assert(expression->is<Throw>());
2840+
return static_cast<Throw*>(expression)->event.c_str();
2841+
}
2842+
BinaryenExpressionRef BinaryenThrowGetOperand(BinaryenExpressionRef expr,
2843+
BinaryenIndex index) {
2844+
if (tracing) {
2845+
std::cout << " BinaryenThrowGetOperand(expressions[" << expressions[expr]
2846+
<< "], " << index << ");\n";
2847+
}
2848+
2849+
auto* expression = (Expression*)expr;
2850+
assert(expression->is<Throw>());
2851+
assert(index < static_cast<Throw*>(expression)->operands.size());
2852+
return static_cast<Throw*>(expression)->operands[index];
2853+
}
2854+
BinaryenIndex BinaryenThrowGetNumOperands(BinaryenExpressionRef expr) {
2855+
if (tracing) {
2856+
std::cout << " BinaryenThrowGetNumOperands(expressions["
2857+
<< expressions[expr] << "]);\n";
2858+
}
2859+
2860+
auto* expression = (Expression*)expr;
2861+
assert(expression->is<Throw>());
2862+
return static_cast<Throw*>(expression)->operands.size();
2863+
}
2864+
// Rethrow
2865+
BinaryenExpressionRef BinaryenRethrowGetExnref(BinaryenExpressionRef expr) {
2866+
if (tracing) {
2867+
std::cout << " BinaryenRethrowGetExnref(expressions[" << expressions[expr]
2868+
<< "]);\n";
2869+
}
2870+
2871+
auto* expression = (Expression*)expr;
2872+
assert(expression->is<Rethrow>());
2873+
return static_cast<Rethrow*>(expression)->exnref;
2874+
}
2875+
// BrOnExn
2876+
const char* BinaryenBrOnExnGetEvent(BinaryenExpressionRef expr) {
2877+
if (tracing) {
2878+
std::cout << " BinaryenBrOnExnGetEvent(expressions[" << expressions[expr]
2879+
<< "]);\n";
2880+
}
2881+
2882+
auto* expression = (Expression*)expr;
2883+
assert(expression->is<BrOnExn>());
2884+
return static_cast<BrOnExn*>(expression)->event.c_str();
2885+
}
2886+
const char* BinaryenBrOnExnGetName(BinaryenExpressionRef expr) {
2887+
if (tracing) {
2888+
std::cout << " BinaryenBrOnExnGetName(expressions[" << expressions[expr]
2889+
<< "]);\n";
2890+
}
2891+
2892+
auto* expression = (Expression*)expr;
2893+
assert(expression->is<BrOnExn>());
2894+
return static_cast<BrOnExn*>(expression)->name.c_str();
2895+
}
2896+
BinaryenExpressionRef BinaryenBrOnExnGetExnref(BinaryenExpressionRef expr) {
2897+
if (tracing) {
2898+
std::cout << " BinaryenBrOnExnGetExnref(expressions[" << expressions[expr]
2899+
<< "]);\n";
2900+
}
2901+
2902+
auto* expression = (Expression*)expr;
2903+
assert(expression->is<BrOnExn>());
2904+
return static_cast<BrOnExn*>(expression)->exnref;
2905+
}
27342906

27352907
// Functions
27362908

src/binaryen-c.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ BinaryenExpressionId BinaryenMemoryInitId(void);
127127
BinaryenExpressionId BinaryenDataDropId(void);
128128
BinaryenExpressionId BinaryenMemoryCopyId(void);
129129
BinaryenExpressionId BinaryenMemoryFillId(void);
130+
BinaryenExpressionId BinaryenTryId(void);
131+
BinaryenExpressionId BinaryenThrowId(void);
132+
BinaryenExpressionId BinaryenRethrowId(void);
133+
BinaryenExpressionId BinaryenBrOnExnId(void);
130134
BinaryenExpressionId BinaryenPushId(void);
131135
BinaryenExpressionId BinaryenPopId(void);
132136

@@ -701,6 +705,19 @@ BinaryenExpressionRef BinaryenMemoryFill(BinaryenModuleRef module,
701705
BinaryenExpressionRef dest,
702706
BinaryenExpressionRef value,
703707
BinaryenExpressionRef size);
708+
BinaryenExpressionRef BinaryenTry(BinaryenModuleRef module,
709+
BinaryenExpressionRef body,
710+
BinaryenExpressionRef catchBody);
711+
BinaryenExpressionRef BinaryenThrow(BinaryenModuleRef module,
712+
const char* event,
713+
BinaryenExpressionRef* operands,
714+
BinaryenIndex numOperands);
715+
BinaryenExpressionRef BinaryenRethrow(BinaryenModuleRef module,
716+
BinaryenExpressionRef exnref);
717+
BinaryenExpressionRef BinaryenBrOnExn(BinaryenModuleRef module,
718+
const char* name,
719+
const char* eventName,
720+
BinaryenExpressionRef exnref);
704721
BinaryenExpressionRef BinaryenPush(BinaryenModuleRef module,
705722
BinaryenExpressionRef value);
706723
BinaryenExpressionRef BinaryenPop(BinaryenModuleRef module, BinaryenType type);
@@ -855,6 +872,20 @@ BinaryenExpressionRef BinaryenMemoryFillGetDest(BinaryenExpressionRef expr);
855872
BinaryenExpressionRef BinaryenMemoryFillGetValue(BinaryenExpressionRef expr);
856873
BinaryenExpressionRef BinaryenMemoryFillGetSize(BinaryenExpressionRef expr);
857874

875+
BinaryenExpressionRef BinaryenTryGetBody(BinaryenExpressionRef expr);
876+
BinaryenExpressionRef BinaryenTryGetCatchBody(BinaryenExpressionRef expr);
877+
878+
const char* BinaryenThrowGetEvent(BinaryenExpressionRef expr);
879+
BinaryenExpressionRef BinaryenThrowGetOperand(BinaryenExpressionRef expr,
880+
BinaryenIndex index);
881+
BinaryenIndex BinaryenThrowGetNumOperands(BinaryenExpressionRef expr);
882+
883+
BinaryenExpressionRef BinaryenRethrowGetExnref(BinaryenExpressionRef expr);
884+
885+
const char* BinaryenBrOnExnGetEvent(BinaryenExpressionRef expr);
886+
const char* BinaryenBrOnExnGetName(BinaryenExpressionRef expr);
887+
BinaryenExpressionRef BinaryenBrOnExnGetExnref(BinaryenExpressionRef expr);
888+
858889
BinaryenExpressionRef BinaryenPushGetValue(BinaryenExpressionRef expr);
859890

860891
// Functions

0 commit comments

Comments
 (0)