Skip to content

Commit

Permalink
[Parser] Parse tags and throw (#6126)
Browse files Browse the repository at this point in the history
Also fix the parser to correctly error if an imported item appears after a
non-imported item and make the corresponding fix to the test.
  • Loading branch information
tlively authored Nov 21, 2023
1 parent beb816b commit cccc7a6
Show file tree
Hide file tree
Showing 11 changed files with 359 additions and 183 deletions.
44 changes: 35 additions & 9 deletions src/parser/context-decls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,7 @@ Result<> ParseDeclsCtx::addFunc(Name name,
TypeUseT type,
std::optional<LocalsT>,
Index pos) {
if (import && hasNonImport) {
return in.err(pos, "import after non-import");
}
CHECK_ERR(checkImport(pos, import));
auto f = addFuncDecl(pos, name, import);
CHECK_ERR(f);
CHECK_ERR(addExports(in, wasm, *f, exports, ExternalKind::Function));
Expand Down Expand Up @@ -109,9 +107,7 @@ Result<> ParseDeclsCtx::addMemory(Name name,
ImportNames* import,
MemType type,
Index pos) {
if (import && hasNonImport) {
return in.err(pos, "import after non-import");
}
CHECK_ERR(checkImport(pos, import));
auto m = addMemoryDecl(pos, name, import, type);
CHECK_ERR(m);
CHECK_ERR(addExports(in, wasm, *m, exports, ExternalKind::Memory));
Expand Down Expand Up @@ -156,9 +152,7 @@ Result<> ParseDeclsCtx::addGlobal(Name name,
GlobalTypeT,
std::optional<ExprT>,
Index pos) {
if (import && hasNonImport) {
return in.err(pos, "import after non-import");
}
CHECK_ERR(checkImport(pos, import));
auto g = addGlobalDecl(pos, name, import);
CHECK_ERR(g);
CHECK_ERR(addExports(in, wasm, *g, exports, ExternalKind::Global));
Expand Down Expand Up @@ -190,4 +184,36 @@ Result<> ParseDeclsCtx::addData(Name name,
return Ok{};
}

Result<Tag*>
ParseDeclsCtx::addTagDecl(Index pos, Name name, ImportNames* importNames) {
auto t = std::make_unique<Tag>();
if (name) {
if (wasm.getTagOrNull(name)) {
// TODO: if the existing tag is not explicitly named, fix its name and
// continue.
return in.err(pos, "repeated tag name");
}
t->setExplicitName(name);
} else {
name = (importNames ? "timport$" : "") + std::to_string(tagCounter++);
name = Names::getValidTagName(wasm, name);
t->name = name;
}
applyImportNames(*t, importNames);
return wasm.addTag(std::move(t));
}

Result<> ParseDeclsCtx::addTag(Name name,
const std::vector<Name>& exports,
ImportNames* import,
TypeUseT type,
Index pos) {
CHECK_ERR(checkImport(pos, import));
auto t = addTagDecl(pos, name, import);
CHECK_ERR(t);
CHECK_ERR(addExports(in, wasm, *t, exports, ExternalKind::Tag));
tagDefs.push_back({name, pos, Index(tagDefs.size())});
return Ok{};
}

} // namespace wasm::WATParser
55 changes: 55 additions & 0 deletions src/parser/contexts.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ struct NullInstrParserCtx {
using MemoryIdxT = Ok;
using DataIdxT = Ok;
using LabelIdxT = Ok;
using TagIdxT = Ok;

using MemargT = Ok;

Expand All @@ -309,6 +310,8 @@ struct NullInstrParserCtx {
DataIdxT getDataFromName(Name) { return Ok{}; }
LabelIdxT getLabelFromIdx(uint32_t) { return Ok{}; }
LabelIdxT getLabelFromName(Name) { return Ok{}; }
TagIdxT getTagFromIdx(uint32_t) { return Ok{}; }
TagIdxT getTagFromName(Name) { return Ok{}; }

MemargT getMemarg(uint64_t, uint32_t) { return Ok{}; }

Expand Down Expand Up @@ -393,6 +396,7 @@ struct NullInstrParserCtx {
Result<> makeRefIsNull(Index) { return Ok{}; }
Result<> makeRefFunc(Index, FuncIdxT) { return Ok{}; }
Result<> makeRefEq(Index) { return Ok{}; }
Result<> makeThrow(Index, TagIdxT) { return Ok{}; }
template<typename HeapTypeT> Result<> makeCallRef(Index, HeapTypeT, bool) {
return Ok{};
}
Expand Down Expand Up @@ -480,6 +484,7 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
std::vector<DefPos> memoryDefs;
std::vector<DefPos> globalDefs;
std::vector<DefPos> dataDefs;
std::vector<DefPos> tagDefs;

// Positions of typeuses that might implicitly define new types.
std::vector<Index> implicitTypeDefs;
Expand All @@ -489,10 +494,22 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
int memoryCounter = 0;
int globalCounter = 0;
int dataCounter = 0;
int tagCounter = 0;

// Used to verify that all imports come before all non-imports.
bool hasNonImport = false;

Result<> checkImport(Index pos, ImportNames* import) {
if (import) {
if (hasNonImport) {
return in.err(pos, "import after non-import");
}
} else {
hasNonImport = true;
}
return Ok{};
}

ParseDeclsCtx(std::string_view in, Module& wasm) : in(in), wasm(wasm) {}

void addFuncType(SignatureT) {}
Expand Down Expand Up @@ -568,6 +585,14 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
std::optional<ExprT>,
std::vector<char>&& data,
Index pos);

Result<Tag*> addTagDecl(Index pos, Name name, ImportNames* importNames);

Result<> addTag(Name name,
const std::vector<Name>& exports,
ImportNames* import,
TypeUseT type,
Index pos);
};

// Phase 2: Parse type definitions into a TypeBuilder.
Expand Down Expand Up @@ -814,6 +839,16 @@ struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>,
g->type = type.type;
return Ok{};
}

Result<>
addTag(Name, const std::vector<Name>&, ImportNames*, TypeUse use, Index pos) {
auto& t = wasm.tags[index];
if (!use.type.isSignature()) {
return in.err(pos, "tag type must be a signature");
}
t->sig = use.type.getSignature();
return Ok{};
}
};

// Phase 5: Parse module element definitions, including instructions.
Expand All @@ -830,6 +865,7 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
using GlobalIdxT = Name;
using MemoryIdxT = Name;
using DataIdxT = Name;
using TagIdxT = Name;

using MemargT = Memarg;

Expand Down Expand Up @@ -990,6 +1026,20 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
return irBuilder.getLabelIndex(name);
}

Result<Name> getTagFromIdx(uint32_t idx) {
if (idx >= wasm.tags.size()) {
return in.err("tag index out of bounds");
}
return wasm.tags[idx]->name;
}

Result<Name> getTagFromName(Name name) {
if (!wasm.getTagOrNull(name)) {
return in.err("tag $" + name.toString() + " does not exist");
}
return name;
}

Result<TypeUseT> makeTypeUse(Index pos,
std::optional<HeapTypeT> type,
ParamsT* params,
Expand All @@ -1009,6 +1059,7 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
Index);
Result<>
addData(Name, Name* mem, std::optional<ExprT> offset, DataStringT, Index pos);

Result<Index> addScratchLocal(Index pos, Type type) {
if (!func) {
return in.err(pos,
Expand Down Expand Up @@ -1288,6 +1339,10 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {

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

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

Result<> makeCallRef(Index pos, HeapType type, bool isReturn) {
return withLoc(pos, irBuilder.makeCallRef(type, isReturn));
}
Expand Down
53 changes: 52 additions & 1 deletion src/parser/parsers.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ 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::TagIdxT> tagidx(Ctx&);
template<typename Ctx> Result<typename Ctx::TypeUseT> typeuse(Ctx&);
MaybeResult<ImportNames> inlineImport(ParseInput&);
Result<std::vector<Name>> inlineExports(ParseInput&);
Expand All @@ -186,6 +187,7 @@ template<typename Ctx> MaybeResult<> memory(Ctx&);
template<typename Ctx> MaybeResult<> global(Ctx&);
template<typename Ctx> Result<typename Ctx::DataStringT> datastring(Ctx&);
template<typename Ctx> MaybeResult<> data(Ctx&);
template<typename Ctx> MaybeResult<> tag(Ctx&);
template<typename Ctx> MaybeResult<> modulefield(Ctx&);
template<typename Ctx> Result<> module(Ctx&);

Expand Down Expand Up @@ -1274,7 +1276,9 @@ Result<> makeTryOrCatchBody(Ctx& ctx, Index pos, Type type, bool isTry) {
}

template<typename Ctx> Result<> makeThrow(Ctx& ctx, Index pos) {
return ctx.in.err("unimplemented instruction");
auto tag = tagidx(ctx);
CHECK_ERR(tag);
return ctx.makeThrow(pos, *tag);
}

template<typename Ctx> Result<> makeRethrow(Ctx& ctx, Index pos) {
Expand Down Expand Up @@ -1627,6 +1631,18 @@ template<typename Ctx> Result<typename Ctx::LabelIdxT> labelidx(Ctx& ctx) {
return ctx.in.err("expected label index or identifier");
}

// tagidx ::= x:u32 => x
// | v:id => x (if tags[x] = v)
template<typename Ctx> Result<typename Ctx::TagIdxT> tagidx(Ctx& ctx) {
if (auto x = ctx.in.takeU32()) {
return ctx.getTagFromIdx(*x);
}
if (auto id = ctx.in.takeID()) {
return ctx.getTagFromName(*id);
}
return ctx.in.err("expected tag index or identifier");
}

// typeuse ::= '(' 'type' x:typeidx ')' => x, []
// (if typedefs[x] = [t1*] -> [t2*]
// | '(' 'type' x:typeidx ')' ((t1,IDs):param)* (t2:result)* => x, IDs
Expand Down Expand Up @@ -2011,6 +2027,36 @@ template<typename Ctx> MaybeResult<> data(Ctx& ctx) {
return Ok{};
}

// tag ::= '(' 'tag' id? ('(' 'export' name ')')*
// ('(' 'import' mod:name nm:name ')')? typeuse ')'
template<typename Ctx> MaybeResult<> tag(Ctx& ctx) {
auto pos = ctx.in.getPos();
if (!ctx.in.takeSExprStart("tag"sv)) {
return {};
}

Name name;
if (auto id = ctx.in.takeID()) {
name = *id;
}

auto exports = inlineExports(ctx.in);
CHECK_ERR(exports);

auto import = inlineImport(ctx.in);
CHECK_ERR(import);

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

if (!ctx.in.takeRParen()) {
return ctx.in.err("expected end of tag");
}

CHECK_ERR(ctx.addTag(name, *exports, import.getPtr(), *type, pos));
return Ok{};
}

// modulefield ::= deftype
// | import
// | func
Expand All @@ -2021,6 +2067,7 @@ template<typename Ctx> MaybeResult<> data(Ctx& ctx) {
// | start
// | elem
// | data
// | tag
template<typename Ctx> MaybeResult<> modulefield(Ctx& ctx) {
if (auto t = ctx.in.peek(); !t || t->isRParen()) {
return {};
Expand All @@ -2045,6 +2092,10 @@ template<typename Ctx> MaybeResult<> modulefield(Ctx& ctx) {
CHECK_ERR(res);
return Ok{};
}
if (auto res = tag(ctx)) {
CHECK_ERR(res);
return Ok{};
}
return ctx.in.err("unrecognized module field");
}

Expand Down
1 change: 1 addition & 0 deletions src/parser/wat-parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ Result<> parseModule(Module& wasm, std::string_view input) {
CHECK_ERR(parseDefs(ctx, decls.funcDefs, func));
CHECK_ERR(parseDefs(ctx, decls.memoryDefs, memory));
CHECK_ERR(parseDefs(ctx, decls.globalDefs, global));
CHECK_ERR(parseDefs(ctx, decls.tagDefs, tag));
// TODO: Parse types of other module elements.
}
{
Expand Down
2 changes: 1 addition & 1 deletion src/parser/wat-parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ Result<> parseModule(Module& wasm, std::string_view in);

} // namespace wasm::WATParser

#endif // paser_wat_parser_h
#endif // parser_wat_parser_h
2 changes: 1 addition & 1 deletion src/wasm-builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -849,7 +849,7 @@ class Builder {
Throw* makeThrow(Tag* tag, const std::vector<Expression*>& args) {
return makeThrow(tag->name, args);
}
Throw* makeThrow(Name tag, const std::vector<Expression*>& args) {
template<typename T> Throw* makeThrow(Name tag, const T& args) {
auto* ret = wasm.allocator.alloc<Throw>();
ret->tag = tag;
ret->operands.set(args);
Expand Down
3 changes: 2 additions & 1 deletion src/wasm-ir-builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
// [[nodiscard]] Result<> makeTableFill();
// [[nodiscard]] Result<> makeTableCopy();
// [[nodiscard]] Result<> makeTry();
// [[nodiscard]] Result<> makeThrow();
[[nodiscard]] Result<> makeThrow(Name tag);
// [[nodiscard]] Result<> makeRethrow();
// [[nodiscard]] Result<> makeTupleMake();
// [[nodiscard]] Result<> makeTupleExtract();
Expand Down Expand Up @@ -203,6 +203,7 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
[[nodiscard]] Result<> visitCall(Call*);
[[nodiscard]] Result<> visitCallIndirect(CallIndirect*);
[[nodiscard]] Result<> visitCallRef(CallRef*);
[[nodiscard]] Result<> visitThrow(Throw*);

private:
Module& wasm;
Expand Down
3 changes: 1 addition & 2 deletions src/wasm/wasm-io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,11 @@
//

#include "wasm-io.h"
#include "parser/wat-parser.h"
#include "support/debug.h"
#include "support/path.h"
#include "wasm-binary.h"
#include "wasm-s-parser.h"
#include "wat-parser.h"


namespace wasm {

Expand Down
19 changes: 18 additions & 1 deletion src/wasm/wasm-ir-builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,17 @@ Result<> IRBuilder::visitCallRef(CallRef* curr) {
return Ok{};
}

Result<> IRBuilder::visitThrow(Throw* curr) {
auto numArgs = wasm.getTag(curr->tag)->sig.params.size();
curr->operands.resize(numArgs);
for (size_t i = 0; i < numArgs; ++i) {
auto arg = pop();
CHECK_ERR(arg);
curr->operands[numArgs - 1 - i] = *arg;
}
return Ok{};
}

Result<> IRBuilder::visitFunctionStart(Function* func) {
if (!scopeStack.empty()) {
return Err{"unexpected start of function"};
Expand Down Expand Up @@ -1005,7 +1016,13 @@ Result<> IRBuilder::makeRefEq() {

// Result<> IRBuilder::makeTry() {}

// Result<> IRBuilder::makeThrow() {}
Result<> IRBuilder::makeThrow(Name tag) {
Throw curr(wasm.allocator);
curr.tag = tag;
CHECK_ERR(visitThrow(&curr));
push(builder.makeThrow(tag, curr.operands));
return Ok{};
}

// Result<> IRBuilder::makeRethrow() {}

Expand Down
Loading

0 comments on commit cccc7a6

Please sign in to comment.