Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions scripts/gen-s-parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -709,15 +709,13 @@ def insert(self, inst, expr):
def instruction_parser(new_parser=False):
"""Build a trie out of all the instructions, then emit it as C++ code."""
global instructions
if new_parser:
# Filter out instructions that the new parser does not need.
instructions = [(inst, code) for (inst, code) in instructions
if inst not in ('block', 'loop', 'if', 'then', 'else')]
trie = Node()
inst_length = 0
for inst, expr in instructions:
if new_parser and inst in {"then", "else"}:
# These are not real instructions! skip them.
if new_parser and inst in {"block", "loop", "if", "try", "then",
"else"}:
# These are either control flow handled manually or not real
# instructions. Skip them.
continue
inst_length = max(inst_length, len(inst))
trie.insert(inst, expr)
Expand Down
6 changes: 0 additions & 6 deletions src/gen-s-parser.inc
Original file line number Diff line number Diff line change
Expand Up @@ -8649,12 +8649,6 @@ switch (buf[0]) {
return Ok{};
}
goto parse_error;
case 'r':
if (op == "try"sv) {
CHECK_ERR(makeTry(ctx, pos));
return Ok{};
}
goto parse_error;
case 'u': {
switch (buf[6]) {
case 'e':
Expand Down
37 changes: 32 additions & 5 deletions src/parser/contexts.h
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,8 @@ struct NullInstrParserCtx {
MemoryIdxT getMemoryFromName(Name) { return Ok{}; }
DataIdxT getDataFromIdx(uint32_t) { return Ok{}; }
DataIdxT getDataFromName(Name) { return Ok{}; }
LabelIdxT getLabelFromIdx(uint32_t) { return Ok{}; }
LabelIdxT getLabelFromName(Name) { return Ok{}; }
LabelIdxT getLabelFromIdx(uint32_t, bool) { return Ok{}; }
LabelIdxT getLabelFromName(Name, bool) { return Ok{}; }
TagIdxT getTagFromIdx(uint32_t) { return Ok{}; }
TagIdxT getTagFromName(Name) { return Ok{}; }

Expand All @@ -328,6 +328,13 @@ struct NullInstrParserCtx {
Result<> makeLoop(Index, std::optional<Name>, BlockTypeT) {
return Ok{};
}
template<typename BlockTypeT>
Result<> makeTry(Index, std::optional<Name>, BlockTypeT) {
return Ok{};
}
Result<> visitCatch(Index, TagIdxT) { return Ok{}; }
Result<> visitCatchAll(Index) { return Ok{}; }
Result<> visitDelegate(Index, LabelIdxT) { return Ok{}; }
Result<> visitEnd() { return Ok{}; }

Result<> makeUnreachable(Index) { return Ok{}; }
Expand Down Expand Up @@ -1020,10 +1027,10 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
return name;
}

Result<Index> getLabelFromIdx(uint32_t idx) { return idx; }
Result<Index> getLabelFromIdx(uint32_t idx, bool) { return idx; }

Result<Index> getLabelFromName(Name name) {
return irBuilder.getLabelIndex(name);
Result<Index> getLabelFromName(Name name, bool inDelegate) {
return irBuilder.getLabelIndex(name, inDelegate);
}

Result<Name> getTagFromIdx(uint32_t idx) {
Expand Down Expand Up @@ -1109,6 +1116,26 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
irBuilder.makeLoop(label ? *label : Name{}, type.getSignature().results));
}

Result<> makeTry(Index pos, std::optional<Name> label, HeapType type) {
// TODO: validate labels?
// TODO: Move error on input types to here?
return withLoc(
pos,
irBuilder.makeTry(label ? *label : Name{}, type.getSignature().results));
}

Result<> visitCatch(Index pos, Name tag) {
return withLoc(pos, irBuilder.visitCatch(tag));
}

Result<> visitCatchAll(Index pos) {
return withLoc(pos, irBuilder.visitCatchAll());
}

Result<> visitDelegate(Index pos, Index label) {
return withLoc(pos, irBuilder.visitDelegate(label));
}

Result<> visitEnd() { return withLoc(irBuilder.visitEnd()); }

Result<> makeUnreachable(Index pos) {
Expand Down
192 changes: 174 additions & 18 deletions src/parser/parsers.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ template<typename Ctx> Result<typename Ctx::BlockTypeT> blocktype(Ctx&);
template<typename Ctx> MaybeResult<> block(Ctx&, bool);
template<typename Ctx> MaybeResult<> ifelse(Ctx&, bool);
template<typename Ctx> MaybeResult<> loop(Ctx&, bool);
template<typename Ctx> MaybeResult<> trycatch(Ctx&, bool);
template<typename Ctx> Result<> makeUnreachable(Ctx&, Index);
template<typename Ctx> Result<> makeNop(Ctx&, Index);
template<typename Ctx> Result<> makeBinary(Ctx&, Index, BinaryOp op);
Expand Down Expand Up @@ -113,9 +114,6 @@ template<typename Ctx> Result<> makeTableSize(Ctx&, Index);
template<typename Ctx> Result<> makeTableGrow(Ctx&, Index);
template<typename Ctx> Result<> makeTableFill(Ctx&, Index);
template<typename Ctx> Result<> makeTableCopy(Ctx&, Index);
template<typename Ctx> Result<> makeTry(Ctx&, Index);
template<typename Ctx>
Result<> makeTryOrCatchBody(Ctx&, Index, Type type, bool isTry);
template<typename Ctx> Result<> makeThrow(Ctx&, Index);
template<typename Ctx> Result<> makeRethrow(Ctx&, Index);
template<typename Ctx> Result<> makeTupleMake(Ctx&, Index);
Expand Down Expand Up @@ -173,7 +171,8 @@ template<typename Ctx> Result<typename Ctx::MemoryIdxT> memidx(Ctx&);
template<typename Ctx> MaybeResult<typename Ctx::MemoryIdxT> maybeMemuse(Ctx&);
template<typename Ctx> Result<typename Ctx::GlobalIdxT> globalidx(Ctx&);
template<typename Ctx> Result<typename Ctx::LocalIdxT> localidx(Ctx&);
template<typename Ctx> Result<typename Ctx::LabelIdxT> labelidx(Ctx&);
template<typename Ctx>
Result<typename Ctx::LabelIdxT> labelidx(Ctx&, bool inDelegate = false);
template<typename Ctx> Result<typename Ctx::TagIdxT> tagidx(Ctx&);
template<typename Ctx> Result<typename Ctx::TypeUseT> typeuse(Ctx&);
MaybeResult<ImportNames> inlineImport(ParseInput&);
Expand Down Expand Up @@ -573,6 +572,9 @@ template<typename Ctx> MaybeResult<> foldedBlockinstr(Ctx& ctx) {
if (auto i = loop(ctx, true)) {
return i;
}
if (auto i = trycatch(ctx, true)) {
return i;
}
// TODO: Other block instructions
return {};
}
Expand All @@ -587,6 +589,9 @@ template<typename Ctx> MaybeResult<> unfoldedBlockinstr(Ctx& ctx) {
if (auto i = loop(ctx, false)) {
return i;
}
if (auto i = trycatch(ctx, false)) {
return i;
}
// TODO: Other block instructions
return {};
}
Expand Down Expand Up @@ -619,7 +624,9 @@ template<typename Ctx> MaybeResult<> instr(Ctx& ctx) {
// Check for valid strings that are not instructions.
if (auto tok = ctx.in.peek()) {
if (auto keyword = tok->getKeyword()) {
if (keyword == "end"sv || keyword == "then"sv || keyword == "else"sv) {
if (keyword == "end"sv || keyword == "then"sv || keyword == "else"sv ||
keyword == "catch"sv || keyword == "catch_all"sv ||
keyword == "delegate"sv) {
return {};
}
}
Expand Down Expand Up @@ -860,7 +867,7 @@ template<typename Ctx> MaybeResult<> ifelse(Ctx& ctx, bool folded) {
return ctx.visitEnd();
}

// loop ::= 'loop' label blocktype instr* end id?
// loop ::= 'loop' label blocktype instr* 'end' id?
// | '(' 'loop' label blocktype instr* ')'
template<typename Ctx> MaybeResult<> loop(Ctx& ctx, bool folded) {
auto pos = ctx.in.getPos();
Expand Down Expand Up @@ -895,6 +902,163 @@ template<typename Ctx> MaybeResult<> loop(Ctx& ctx, bool folded) {
return ctx.visitEnd();
}

// trycatch ::= 'try' label blocktype instr* ('catch' id? tagidx instr*)*
// ('catch_all' id? instr*)? 'end' id?
// | '(' 'try' label blocktype '(' 'do' instr* ')'
// ('(' 'catch' tagidx instr* ')')*
// ('(' 'catch_all' instr* ')')? ')'
// | 'try' label blocktype instr* 'deledate' label
// | '(' 'try' label blocktype '(' 'do' instr* ')'
// '(' 'delegate' label ')' ')'
template<typename Ctx> MaybeResult<> trycatch(Ctx& ctx, bool folded) {
auto pos = ctx.in.getPos();

if ((folded && !ctx.in.takeSExprStart("try"sv)) ||
(!folded && !ctx.in.takeKeyword("try"sv))) {
return {};
}

auto label = ctx.in.takeID();

auto type = blocktype(ctx);
CHECK_ERR(type);

CHECK_ERR(ctx.makeTry(pos, label, *type));

if (folded) {
if (!ctx.in.takeSExprStart("do"sv)) {
return ctx.in.err("expected 'do' in try");
}
}

CHECK_ERR(instrs(ctx));

if (folded) {
if (!ctx.in.takeRParen()) {
return ctx.in.err("expected ')' at end of do");
}
}

if ((folded && ctx.in.takeSExprStart("delegate")) ||
(!folded && ctx.in.takeKeyword("delegate"))) {
auto delegatePos = ctx.in.getPos();

auto label = labelidx(ctx, true);
CHECK_ERR(label);

if (folded) {
if (!ctx.in.takeRParen()) {
return ctx.in.err("expected ')' at end of delegate");
}
if (!ctx.in.takeRParen()) {
return ctx.in.err("expected ')' at end of try");
}
}

CHECK_ERR(ctx.visitDelegate(delegatePos, *label));
return Ok{};
}

while (true) {
auto catchPos = ctx.in.getPos();

if ((folded && !ctx.in.takeSExprStart("catch"sv)) ||
(!folded && !ctx.in.takeKeyword("catch"sv))) {
break;
}

// It can be ambiguous whether the name after `catch` is intended to be the
// optional ID or the tag identifier. For example:
//
// (tag $t)
// (func $ambiguous
// try $t
// catch $t
// end
// )
//
// When parsing the `catch`, the parser first tries to parse an optional ID
// that must match the label of the `try`, and it succeeds because it sees
// `$t` after the catch. However, when it then tries to parse the mandatory
// tag index, it fails because the next token is `end`. The problem is that
// the `$t` after the `catch` was the tag name and there was no optional ID
// after all. The parser sets `parseID = false` and resets to just after the
// `catch`, and now it skips parsing the optional ID so it correctly parses
// the `$t` as a tag name.
bool parseID = !folded;
auto afterCatchPos = ctx.in.getPos();
while (true) {
if (!folded && parseID) {
auto id = ctx.in.takeID();
if (id && id != label) {
// Instead of returning an error, retry without the ID.
parseID = false;
ctx.in.lexer.setIndex(afterCatchPos);
continue;
}
}

auto tag = tagidx(ctx);
if (parseID && tag.getErr()) {
// Instead of returning an error, retry without the ID.
parseID = false;
ctx.in.lexer.setIndex(afterCatchPos);
continue;
}
Comment on lines +1002 to +1007
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(While I'm not sure what the ID is in the first place...) Is this trying to parse an ID or a tag? I thought the upper if (line 967-975) parses ID (if any) and this parses tag. But this seems to set parseID to false if tag parsing went unsuccessful. Why?

And does this optional ID only exist in the non-folded format?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right that lines 967-975 parse the ID. This logic here tries to parse a tag (on line 977) and if that fails, it resets the parser to try again without trying to parse an ID.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a tag parsing fails, why do we set parseID false instead?
And what does it retry to do? Given that we set parseID false here, it doesn't retry to parse an ID. Then does it retry to parse a tag? But tag parsing just failed now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ambiguous case this is handling looks like this:

(tag $t)
(func $ambiguous
  try $t
  catch $t
  end
)

When parsing the catch, the parser first tries to parse an optional ID that must match the label of the try, and it succeeds because it sees $t after the catch. However, when it then tries to parse the mandatory tag index, it fails because the next token is end. The problem is that the $t after the catch was the tag name and there was no optional ID after all. The parser sets parseID = false and resets to just after the catch, and now it skips parsing the optional ID so it correctly parses the $t as a tag name.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see. Can you add this explanation in the comments? I think it will help reading.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done 👍

CHECK_ERR(tag);

CHECK_ERR(ctx.visitCatch(catchPos, *tag));

CHECK_ERR(instrs(ctx));

if (folded) {
if (!ctx.in.takeRParen()) {
return ctx.in.err("expected ')' at end of catch");
}
}
break;
}
}

if ((folded && ctx.in.takeSExprStart("catch_all"sv)) ||
(!folded && ctx.in.takeKeyword("catch_all"sv))) {
auto catchPos = ctx.in.getPos();

if (!folded) {
auto id = ctx.in.takeID();
if (id && id != label) {
return ctx.in.err("catch_all label does not match try label");
}
}

CHECK_ERR(ctx.visitCatchAll(catchPos));

CHECK_ERR(instrs(ctx));

if (folded) {
if (!ctx.in.takeRParen()) {
return ctx.in.err("expected ')' at end of catch_all");
}
}
}

if (folded) {
if (!ctx.in.takeRParen()) {
return ctx.in.err("expected ')' at end of try");
}
} else {
if (!ctx.in.takeKeyword("end"sv)) {
return ctx.in.err("expected 'end' at end of try");
}

auto id = ctx.in.takeID();
if (id && id != label) {
return ctx.in.err("end label does not match try label");
}
}
return ctx.visitEnd();
}

template<typename Ctx> Result<> makeUnreachable(Ctx& ctx, Index pos) {
return ctx.makeUnreachable(pos);
}
Expand Down Expand Up @@ -1266,15 +1430,6 @@ template<typename Ctx> Result<> makeTableCopy(Ctx& ctx, Index pos) {
return ctx.in.err("unimplemented instruction");
}

template<typename Ctx> Result<> makeTry(Ctx& ctx, Index pos) {
return ctx.in.err("unimplemented instruction");
}

template<typename Ctx>
Result<> makeTryOrCatchBody(Ctx& ctx, Index pos, Type type, bool isTry) {
return ctx.in.err("unimplemented instruction");
}

template<typename Ctx> Result<> makeThrow(Ctx& ctx, Index pos) {
auto tag = tagidx(ctx);
CHECK_ERR(tag);
Expand Down Expand Up @@ -1621,12 +1776,13 @@ template<typename Ctx> Result<typename Ctx::LocalIdxT> localidx(Ctx& ctx) {

// labelidx ::= x:u32 => x
// | v:id => x (if labels[x] = v)
template<typename Ctx> Result<typename Ctx::LabelIdxT> labelidx(Ctx& ctx) {
template<typename Ctx>
Result<typename Ctx::LabelIdxT> labelidx(Ctx& ctx, bool inDelegate) {
if (auto x = ctx.in.takeU32()) {
return ctx.getLabelFromIdx(*x);
return ctx.getLabelFromIdx(*x, inDelegate);
}
if (auto id = ctx.in.takeID()) {
return ctx.getLabelFromName(*id);
return ctx.getLabelFromName(*id, inDelegate);
}
return ctx.in.err("expected label index or identifier");
}
Expand Down
Loading