Skip to content

Commit

Permalink
Add basic exception handling support (#2282)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
aheejin authored Aug 12, 2019
1 parent 69ad1e8 commit e2f49d8
Show file tree
Hide file tree
Showing 52 changed files with 6,080 additions and 1,382 deletions.
25 changes: 25 additions & 0 deletions build-js.sh
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,10 @@ export_function "_BinaryenMemoryInitId"
export_function "_BinaryenDataDropId"
export_function "_BinaryenMemoryCopyId"
export_function "_BinaryenMemoryFillId"
export_function "_BinaryenTryId"
export_function "_BinaryenThrowId"
export_function "_BinaryenRethrowId"
export_function "_BinaryenBrOnExnId"
export_function "_BinaryenPushId"
export_function "_BinaryenPopId"

Expand Down Expand Up @@ -579,6 +583,10 @@ export_function "_BinaryenMemoryInit"
export_function "_BinaryenDataDrop"
export_function "_BinaryenMemoryCopy"
export_function "_BinaryenMemoryFill"
export_function "_BinaryenTry"
export_function "_BinaryenThrow"
export_function "_BinaryenRethrow"
export_function "_BinaryenBrOnExn"
export_function "_BinaryenPush"
export_function "_BinaryenPop"

Expand Down Expand Up @@ -757,6 +765,23 @@ export_function "_BinaryenMemoryFillGetDest"
export_function "_BinaryenMemoryFillGetValue"
export_function "_BinaryenMemoryFillGetSize"

# 'Try' expression operations
export_function "_BinaryenTryGetBody"
export_function "_BinaryenTryGetCatchBody"

# 'Throw' expression operations
export_function "_BinaryenThrowGetEvent"
export_function "_BinaryenThrowGetNumOperands"
export_function "_BinaryenThrowGetOperand"

# 'Rethrow' expression operations
export_function "_BinaryenRethrowGetExnref"

# 'BrOnExn' expression operations
export_function "_BinaryenBrOnExnGetEvent"
export_function "_BinaryenBrOnExnGetName"
export_function "_BinaryenBrOnExnGetExnref"

# 'Push' expression operations
export_function "_BinaryenPushGetValue"

Expand Down
2 changes: 1 addition & 1 deletion check.py
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ def run_gcc_tests():
print('link: ', ' '.join(cmd))
subprocess.check_call(cmd)
print('run...', output_file)
actual = subprocess.check_output([os.path.abspath(output_file)])
actual = subprocess.check_output([os.path.abspath(output_file)]).decode('utf-8')
os.remove(output_file)
if sys.platform == 'darwin':
# Also removes debug directory produced on Mac OS
Expand Down
8 changes: 7 additions & 1 deletion scripts/gen-s-parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,13 @@
("f32x4.convert_i32x4_s", "makeUnary(s, UnaryOp::ConvertSVecI32x4ToVecF32x4)"),
("f32x4.convert_i32x4_u", "makeUnary(s, UnaryOp::ConvertUVecI32x4ToVecF32x4)"),
("f64x2.convert_i64x2_s", "makeUnary(s, UnaryOp::ConvertSVecI64x2ToVecF64x2)"),
("f64x2.convert_i64x2_u", "makeUnary(s, UnaryOp::ConvertUVecI64x2ToVecF64x2)")
("f64x2.convert_i64x2_u", "makeUnary(s, UnaryOp::ConvertUVecI64x2ToVecF64x2)"),
# exception handling instructions
("try", "makeTry(s)"),
("throw", "makeThrow(s)"),
("catch", "makeCatch(s)"),
("rethrow", "makeRethrow(s)"),
("br_on_exn", "makeBrOnExn(s)")
]


Expand Down
172 changes: 172 additions & 0 deletions src/binaryen-c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,14 @@ BinaryenExpressionId BinaryenMemoryCopyId(void) {
BinaryenExpressionId BinaryenMemoryFillId(void) {
return Expression::Id::MemoryFillId;
}
BinaryenExpressionId BinaryenTryId(void) { return Expression::Id::TryId; }
BinaryenExpressionId BinaryenThrowId(void) { return Expression::Id::ThrowId; }
BinaryenExpressionId BinaryenRethrowId(void) {
return Expression::Id::RethrowId;
}
BinaryenExpressionId BinaryenBrOnExnId(void) {
return Expression::Id::BrOnExnId;
}
BinaryenExpressionId BinaryenPushId(void) { return Expression::Id::PushId; }
BinaryenExpressionId BinaryenPopId(void) { return Expression::Id::PopId; }

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

BinaryenExpressionRef BinaryenTry(BinaryenModuleRef module,
BinaryenExpressionRef body,
BinaryenExpressionRef catchBody) {
auto* ret = Builder(*(Module*)module)
.makeTry((Expression*)body, (Expression*)catchBody);
if (tracing) {
traceExpression(ret, "BinaryenTry", body, catchBody);
}
return static_cast<Expression*>(ret);
}

BinaryenExpressionRef BinaryenThrow(BinaryenModuleRef module,
const char* event,
BinaryenExpressionRef* operands,
BinaryenIndex numOperands) {
std::vector<Expression*> args;
for (BinaryenIndex i = 0; i < numOperands; i++) {
args.push_back((Expression*)operands[i]);
}
auto* ret = Builder(*(Module*)module).makeThrow(event, args);

if (tracing) {
std::cout << " {\n";
std::cout << " BinaryenExpressionRef operands[] = { ";
for (BinaryenIndex i = 0; i < numOperands; i++) {
if (i > 0) {
std::cout << ", ";
}
std::cout << "expressions[" << expressions[operands[i]] << "]";
}
if (numOperands == 0) {
// ensure the array is not empty, otherwise a compiler error on VS
std::cout << "0";
}
std::cout << " };\n ";
traceExpression(
ret, "BinaryenThrow", StringLit(event), "operands", numOperands);
std::cout << " }\n";
}
return static_cast<Expression*>(ret);
}

BinaryenExpressionRef BinaryenRethrow(BinaryenModuleRef module,
BinaryenExpressionRef exnref) {
auto* ret = Builder(*(Module*)module).makeRethrow((Expression*)exnref);
if (tracing) {
traceExpression(ret, "BinaryenRethrow", exnref);
}
return static_cast<Expression*>(ret);
}

BinaryenExpressionRef BinaryenBrOnExn(BinaryenModuleRef module,
const char* name,
const char* eventName,
BinaryenExpressionRef exnref) {
Module* wasm = (Module*)module;
Event* event = wasm->getEventOrNull(eventName);
assert(event && "br_on_exn's event must exist");
auto* ret = Builder(*wasm).makeBrOnExn(name, event, (Expression*)exnref);

if (tracing) {
traceExpression(
ret, "BinaryenBrOnExn", StringLit(name), StringLit(eventName), exnref);
}
return static_cast<Expression*>(ret);
}

// Expression utility

BinaryenExpressionId BinaryenExpressionGetId(BinaryenExpressionRef expr) {
Expand Down Expand Up @@ -2721,6 +2796,7 @@ BinaryenExpressionRef BinaryenMemoryFillGetSize(BinaryenExpressionRef expr) {
assert(expression->is<MemoryFill>());
return static_cast<MemoryFill*>(expression)->size;
}
// Push
BinaryenExpressionRef BinaryenPushGetValue(BinaryenExpressionRef expr) {
if (tracing) {
std::cout << " BinaryenPushGetValue(expressions[" << expressions[expr]
Expand All @@ -2731,6 +2807,102 @@ BinaryenExpressionRef BinaryenPushGetValue(BinaryenExpressionRef expr) {
assert(expression->is<Push>());
return static_cast<Push*>(expression)->value;
}
// Try
BinaryenExpressionRef BinaryenTryGetBody(BinaryenExpressionRef expr) {
if (tracing) {
std::cout << " BinaryenTryGetBody(expressions[" << expressions[expr]
<< "]);\n";
}

auto* expression = (Expression*)expr;
assert(expression->is<Try>());
return static_cast<Try*>(expression)->body;
}
BinaryenExpressionRef BinaryenTryGetCatchBody(BinaryenExpressionRef expr) {
if (tracing) {
std::cout << " BinaryenTryGetCatchBody(expressions[" << expressions[expr]
<< "]);\n";
}

auto* expression = (Expression*)expr;
assert(expression->is<Try>());
return static_cast<Try*>(expression)->catchBody;
}
// Throw
const char* BinaryenThrowGetEvent(BinaryenExpressionRef expr) {
if (tracing) {
std::cout << " BinaryenThrowGetEvent(expressions[" << expressions[expr]
<< "]);\n";
}

auto* expression = (Expression*)expr;
assert(expression->is<Throw>());
return static_cast<Throw*>(expression)->event.c_str();
}
BinaryenExpressionRef BinaryenThrowGetOperand(BinaryenExpressionRef expr,
BinaryenIndex index) {
if (tracing) {
std::cout << " BinaryenThrowGetOperand(expressions[" << expressions[expr]
<< "], " << index << ");\n";
}

auto* expression = (Expression*)expr;
assert(expression->is<Throw>());
assert(index < static_cast<Throw*>(expression)->operands.size());
return static_cast<Throw*>(expression)->operands[index];
}
BinaryenIndex BinaryenThrowGetNumOperands(BinaryenExpressionRef expr) {
if (tracing) {
std::cout << " BinaryenThrowGetNumOperands(expressions["
<< expressions[expr] << "]);\n";
}

auto* expression = (Expression*)expr;
assert(expression->is<Throw>());
return static_cast<Throw*>(expression)->operands.size();
}
// Rethrow
BinaryenExpressionRef BinaryenRethrowGetExnref(BinaryenExpressionRef expr) {
if (tracing) {
std::cout << " BinaryenRethrowGetExnref(expressions[" << expressions[expr]
<< "]);\n";
}

auto* expression = (Expression*)expr;
assert(expression->is<Rethrow>());
return static_cast<Rethrow*>(expression)->exnref;
}
// BrOnExn
const char* BinaryenBrOnExnGetEvent(BinaryenExpressionRef expr) {
if (tracing) {
std::cout << " BinaryenBrOnExnGetEvent(expressions[" << expressions[expr]
<< "]);\n";
}

auto* expression = (Expression*)expr;
assert(expression->is<BrOnExn>());
return static_cast<BrOnExn*>(expression)->event.c_str();
}
const char* BinaryenBrOnExnGetName(BinaryenExpressionRef expr) {
if (tracing) {
std::cout << " BinaryenBrOnExnGetName(expressions[" << expressions[expr]
<< "]);\n";
}

auto* expression = (Expression*)expr;
assert(expression->is<BrOnExn>());
return static_cast<BrOnExn*>(expression)->name.c_str();
}
BinaryenExpressionRef BinaryenBrOnExnGetExnref(BinaryenExpressionRef expr) {
if (tracing) {
std::cout << " BinaryenBrOnExnGetExnref(expressions[" << expressions[expr]
<< "]);\n";
}

auto* expression = (Expression*)expr;
assert(expression->is<BrOnExn>());
return static_cast<BrOnExn*>(expression)->exnref;
}

// Functions

Expand Down
31 changes: 31 additions & 0 deletions src/binaryen-c.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ BinaryenExpressionId BinaryenMemoryInitId(void);
BinaryenExpressionId BinaryenDataDropId(void);
BinaryenExpressionId BinaryenMemoryCopyId(void);
BinaryenExpressionId BinaryenMemoryFillId(void);
BinaryenExpressionId BinaryenTryId(void);
BinaryenExpressionId BinaryenThrowId(void);
BinaryenExpressionId BinaryenRethrowId(void);
BinaryenExpressionId BinaryenBrOnExnId(void);
BinaryenExpressionId BinaryenPushId(void);
BinaryenExpressionId BinaryenPopId(void);

Expand Down Expand Up @@ -701,6 +705,19 @@ BinaryenExpressionRef BinaryenMemoryFill(BinaryenModuleRef module,
BinaryenExpressionRef dest,
BinaryenExpressionRef value,
BinaryenExpressionRef size);
BinaryenExpressionRef BinaryenTry(BinaryenModuleRef module,
BinaryenExpressionRef body,
BinaryenExpressionRef catchBody);
BinaryenExpressionRef BinaryenThrow(BinaryenModuleRef module,
const char* event,
BinaryenExpressionRef* operands,
BinaryenIndex numOperands);
BinaryenExpressionRef BinaryenRethrow(BinaryenModuleRef module,
BinaryenExpressionRef exnref);
BinaryenExpressionRef BinaryenBrOnExn(BinaryenModuleRef module,
const char* name,
const char* eventName,
BinaryenExpressionRef exnref);
BinaryenExpressionRef BinaryenPush(BinaryenModuleRef module,
BinaryenExpressionRef value);
BinaryenExpressionRef BinaryenPop(BinaryenModuleRef module, BinaryenType type);
Expand Down Expand Up @@ -855,6 +872,20 @@ BinaryenExpressionRef BinaryenMemoryFillGetDest(BinaryenExpressionRef expr);
BinaryenExpressionRef BinaryenMemoryFillGetValue(BinaryenExpressionRef expr);
BinaryenExpressionRef BinaryenMemoryFillGetSize(BinaryenExpressionRef expr);

BinaryenExpressionRef BinaryenTryGetBody(BinaryenExpressionRef expr);
BinaryenExpressionRef BinaryenTryGetCatchBody(BinaryenExpressionRef expr);

const char* BinaryenThrowGetEvent(BinaryenExpressionRef expr);
BinaryenExpressionRef BinaryenThrowGetOperand(BinaryenExpressionRef expr,
BinaryenIndex index);
BinaryenIndex BinaryenThrowGetNumOperands(BinaryenExpressionRef expr);

BinaryenExpressionRef BinaryenRethrowGetExnref(BinaryenExpressionRef expr);

const char* BinaryenBrOnExnGetEvent(BinaryenExpressionRef expr);
const char* BinaryenBrOnExnGetName(BinaryenExpressionRef expr);
BinaryenExpressionRef BinaryenBrOnExnGetExnref(BinaryenExpressionRef expr);

BinaryenExpressionRef BinaryenPushGetValue(BinaryenExpressionRef expr);

// Functions
Expand Down
Loading

0 comments on commit e2f49d8

Please sign in to comment.