summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gen-s-parser.inc56
-rw-r--r--src/parser/contexts.h19
-rw-r--r--src/parser/parsers.h122
-rw-r--r--src/wasm-ir-builder.h93
-rw-r--r--src/wasm/wasm-ir-builder.cpp123
5 files changed, 341 insertions, 72 deletions
diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc
index 416349032..5867913ea 100644
--- a/src/gen-s-parser.inc
+++ b/src/gen-s-parser.inc
@@ -3875,33 +3875,21 @@ switch (buf[0]) {
}
}
case 'e': {
- switch (buf[1]) {
- case 'l':
- if (op == "else"sv) {
- auto ret = makeThenOrElse(ctx, pos);
+ switch (buf[7]) {
+ case 'e':
+ if (op == "extern.externalize"sv) {
+ auto ret = makeRefAs(ctx, pos, ExternExternalize);
CHECK_ERR(ret);
return *ret;
}
goto parse_error;
- case 'x': {
- switch (buf[7]) {
- case 'e':
- if (op == "extern.externalize"sv) {
- auto ret = makeRefAs(ctx, pos, ExternExternalize);
- CHECK_ERR(ret);
- return *ret;
- }
- goto parse_error;
- case 'i':
- if (op == "extern.internalize"sv) {
- auto ret = makeRefAs(ctx, pos, ExternInternalize);
- CHECK_ERR(ret);
- return *ret;
- }
- goto parse_error;
- default: goto parse_error;
+ case 'i':
+ if (op == "extern.internalize"sv) {
+ auto ret = makeRefAs(ctx, pos, ExternInternalize);
+ CHECK_ERR(ret);
+ return *ret;
}
- }
+ goto parse_error;
default: goto parse_error;
}
}
@@ -9241,25 +9229,13 @@ switch (buf[0]) {
default: goto parse_error;
}
}
- case 'h': {
- switch (buf[2]) {
- case 'e':
- if (op == "then"sv) {
- auto ret = makeThenOrElse(ctx, pos);
- CHECK_ERR(ret);
- return *ret;
- }
- goto parse_error;
- case 'r':
- if (op == "throw"sv) {
- auto ret = makeThrow(ctx, pos);
- CHECK_ERR(ret);
- return *ret;
- }
- goto parse_error;
- default: goto parse_error;
+ case 'h':
+ if (op == "throw"sv) {
+ auto ret = makeThrow(ctx, pos);
+ CHECK_ERR(ret);
+ return *ret;
}
- }
+ goto parse_error;
case 'r':
if (op == "try"sv) {
auto ret = makeTry(ctx, pos);
diff --git a/src/parser/contexts.h b/src/parser/contexts.h
index 210945e8d..dae938f7f 100644
--- a/src/parser/contexts.h
+++ b/src/parser/contexts.h
@@ -312,7 +312,12 @@ struct NullInstrParserCtx {
InstrT makeBlock(Index, std::optional<Name>, BlockTypeT) {
return Ok{};
}
- InstrT finishBlock(Index, InstrsT) { return Ok{}; }
+ template<typename BlockTypeT>
+ InstrT makeIf(Index, std::optional<Name>, BlockTypeT) {
+ return Ok{};
+ }
+ InstrT visitEnd(Index, InstrsT) { return Ok{}; }
+ InstrT visitElse(Index) { return Ok{}; }
InstrT makeUnreachable(Index) { return Ok{}; }
InstrT makeNop(Index) { return Ok{}; }
@@ -994,10 +999,20 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
type.getSignature().results));
}
- Result<> finishBlock(Index pos, InstrsT) {
+ Result<> makeIf(Index pos, std::optional<Name> label, HeapType type) {
+ // TODO: validate labels?
+ // TODO: Move error on input types to here?
+ return withLoc(
+ pos,
+ irBuilder.makeIf(label ? *label : Name{}, type.getSignature().results));
+ }
+
+ Result<> visitEnd(Index pos, InstrsT) {
return withLoc(pos, irBuilder.visitEnd());
}
+ Result<> visitElse(Index pos) { return withLoc(pos, irBuilder.visitElse()); }
+
Result<> makeUnreachable(Index pos) {
return withLoc(pos, irBuilder.makeUnreachable());
}
diff --git a/src/parser/parsers.h b/src/parser/parsers.h
index 5f9f23a2a..d162de58c 100644
--- a/src/parser/parsers.h
+++ b/src/parser/parsers.h
@@ -48,11 +48,14 @@ MaybeResult<typename Ctx::InstrT> unfoldedBlockinstr(Ctx&);
template<typename Ctx> MaybeResult<typename Ctx::InstrT> blockinstr(Ctx&);
template<typename Ctx> MaybeResult<typename Ctx::InstrT> plaininstr(Ctx&);
template<typename Ctx> MaybeResult<typename Ctx::InstrT> instr(Ctx&);
-template<typename Ctx> Result<typename Ctx::InstrsT> instrs(Ctx&);
+template<typename Ctx>
+Result<typename Ctx::InstrsT> instrs(Ctx&, bool requireFolded = false);
+template<typename Ctx> Result<typename Ctx::InstrsT> foldedinstrs(Ctx&);
template<typename Ctx> Result<typename Ctx::ExprT> expr(Ctx&);
template<typename Ctx> Result<typename Ctx::MemargT> memarg(Ctx&, uint32_t);
template<typename Ctx> Result<typename Ctx::BlockTypeT> blocktype(Ctx&);
template<typename Ctx> MaybeResult<typename Ctx::InstrT> block(Ctx&, bool);
+template<typename Ctx> MaybeResult<typename Ctx::InstrT> ifelse(Ctx&, bool);
template<typename Ctx>
Result<typename Ctx::InstrT> makeUnreachable(Ctx&, Index);
template<typename Ctx> Result<typename Ctx::InstrT> makeNop(Ctx&, Index);
@@ -594,6 +597,9 @@ MaybeResult<typename Ctx::InstrT> foldedBlockinstr(Ctx& ctx) {
if (auto i = block(ctx, true)) {
return i;
}
+ if (auto i = ifelse(ctx, true)) {
+ return i;
+ }
// TODO: Other block instructions
return {};
}
@@ -603,6 +609,9 @@ MaybeResult<typename Ctx::InstrT> unfoldedBlockinstr(Ctx& ctx) {
if (auto i = block(ctx, false)) {
return i;
}
+ if (auto i = ifelse(ctx, false)) {
+ return i;
+ }
// TODO: Other block instructions
return {};
}
@@ -635,7 +644,7 @@ template<typename Ctx> MaybeResult<typename Ctx::InstrT> 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) {
+ if (keyword == "end"sv || keyword == "then"sv || keyword == "else"sv) {
return {};
}
}
@@ -650,13 +659,20 @@ template<typename Ctx> MaybeResult<typename Ctx::InstrT> instr(Ctx& ctx) {
return {};
}
-template<typename Ctx> Result<typename Ctx::InstrsT> instrs(Ctx& ctx) {
+template<typename Ctx>
+Result<typename Ctx::InstrsT> instrs(Ctx& ctx, bool requireFolded) {
auto insts = ctx.makeInstrs();
+ bool parsedFolded = false;
while (true) {
if (auto blockinst = foldedBlockinstr(ctx)) {
CHECK_ERR(blockinst);
ctx.appendInstr(insts, *blockinst);
+ parsedFolded = true;
+ if (requireFolded) {
+ // Do not continue to parse another sibling folded instruction.
+ break;
+ }
continue;
}
// Parse an arbitrary number of folded instructions.
@@ -686,6 +702,7 @@ template<typename Ctx> Result<typename Ctx::InstrsT> instrs(Ctx& ctx) {
if (auto blockinst = foldedBlockinstr(ctx)) {
CHECK_ERR(blockinst);
ctx.appendInstr(insts, *blockinst);
+ parsedFolded = true;
} else if (ctx.in.takeLParen()) {
foldedInstrs.push_back({ctx.in.getPos(), {}});
} else if (ctx.in.takeRParen()) {
@@ -697,6 +714,7 @@ template<typename Ctx> Result<typename Ctx::InstrsT> instrs(Ctx& ctx) {
if (auto inst = plaininstr(ctx)) {
CHECK_ERR(inst);
ctx.appendInstr(insts, *inst);
+ parsedFolded = true;
} else {
return ctx.in.err(start, "expected folded instruction");
}
@@ -708,9 +726,18 @@ template<typename Ctx> Result<typename Ctx::InstrsT> instrs(Ctx& ctx) {
WASM_UNREACHABLE("expected paren");
}
}
+ if (requireFolded) {
+ // Do not continue to parse another sibling folded instruction.
+ break;
+ }
continue;
}
+ if (requireFolded) {
+ // Do not continue to parse a non-folded instruction.
+ break;
+ }
+
// A non-folded instruction.
if (auto inst = instr(ctx)) {
CHECK_ERR(inst);
@@ -720,9 +747,17 @@ template<typename Ctx> Result<typename Ctx::InstrsT> instrs(Ctx& ctx) {
}
}
+ if (requireFolded && !parsedFolded) {
+ return ctx.in.err("expected folded instructions");
+ }
+
return ctx.finishInstrs(insts);
}
+template<typename Ctx> Result<typename Ctx::InstrsT> foldedinstrs(Ctx& ctx) {
+ return instrs(ctx, true);
+}
+
template<typename Ctx> Result<typename Ctx::ExprT> expr(Ctx& ctx) {
auto insts = instrs(ctx);
CHECK_ERR(insts);
@@ -807,7 +842,81 @@ MaybeResult<typename Ctx::InstrT> block(Ctx& ctx, bool folded) {
}
}
- return ctx.finishBlock(pos, std::move(*insts));
+ return ctx.visitEnd(pos, std::move(*insts));
+}
+
+// if ::= 'if' label blocktype instr1* ('else' id1? instr2*)? 'end' id2?
+// | '(' 'if' label blocktype instr* '(' 'then' instr1* ')'
+// ('(' 'else' instr2* ')')? ')'
+template<typename Ctx>
+MaybeResult<typename Ctx::InstrT> ifelse(Ctx& ctx, bool folded) {
+ auto pos = ctx.in.getPos();
+
+ if (folded) {
+ if (!ctx.in.takeSExprStart("if"sv)) {
+ return {};
+ }
+ } else {
+ if (!ctx.in.takeKeyword("if"sv)) {
+ return {};
+ }
+ }
+
+ auto label = ctx.in.takeID();
+
+ auto type = blocktype(ctx);
+ CHECK_ERR(type);
+
+ if (folded) {
+ CHECK_ERR(foldedinstrs(ctx));
+ }
+
+ ctx.makeIf(pos, label, *type);
+
+ if (folded && !ctx.in.takeSExprStart("then"sv)) {
+ return ctx.in.err("expected 'then' before if instructions");
+ }
+
+ auto insts = instrs(ctx);
+ CHECK_ERR(insts);
+
+ if (folded && !ctx.in.takeRParen()) {
+ return ctx.in.err("expected ')' at end of then block");
+ }
+
+ if ((folded && ctx.in.takeSExprStart("else"sv)) ||
+ (!folded && ctx.in.takeKeyword("else"sv))) {
+ auto id1 = ctx.in.takeID();
+ if (id1 && id1 != label) {
+ return ctx.in.err("else label does not match if label");
+ }
+
+ ctx.visitElse(pos);
+
+ auto elseInsts = instrs(ctx);
+ CHECK_ERR(elseInsts);
+
+ if (folded && !ctx.in.takeRParen()) {
+ return ctx.in.err("expected ')' at end of else block");
+ }
+ }
+
+ if (folded) {
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected ')' at end of if");
+ }
+ } else {
+ if (!ctx.in.takeKeyword("end"sv)) {
+ return ctx.in.err("expected 'end' at end of if");
+ }
+ }
+
+ auto id2 = ctx.in.takeID();
+ if (id2 && id2 != label) {
+ return ctx.in.err("end label does not match if label");
+ }
+
+ return ctx.visitEnd(pos, std::move(*insts));
}
template<typename Ctx>
@@ -897,11 +1006,6 @@ Result<typename Ctx::InstrT> makeBlock(Ctx& ctx, Index pos) {
}
template<typename Ctx>
-Result<typename Ctx::InstrT> makeThenOrElse(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
Result<typename Ctx::InstrT> makeConst(Ctx& ctx, Index pos, Type type) {
assert(type.isBasic());
switch (type.getBasic()) {
diff --git a/src/wasm-ir-builder.h b/src/wasm-ir-builder.h
index 1a19f62e8..c7185ebf0 100644
--- a/src/wasm-ir-builder.h
+++ b/src/wasm-ir-builder.h
@@ -52,6 +52,8 @@ public:
// the corresponding `makeXYZ` function below instead of `visitXYZStart`, but
// either way must call `visitEnd` and friends at the appropriate times.
[[nodiscard]] Result<> visitBlockStart(Block* block);
+ [[nodiscard]] Result<> visitIfStart(If* iff, Name label = {});
+ [[nodiscard]] Result<> visitElse();
[[nodiscard]] Result<> visitEnd();
// Alternatively, call makeXYZ to have the IRBuilder allocate the nodes. This
@@ -59,7 +61,7 @@ public:
// ensure that there are no missing fields.
[[nodiscard]] Result<> makeNop();
[[nodiscard]] Result<> makeBlock(Name label, Type type);
- // [[nodiscard]] Result<> makeIf();
+ [[nodiscard]] Result<> makeIf(Name label, Type type);
// [[nodiscard]] Result<> makeLoop();
// [[nodiscard]] Result<> makeBreak();
// [[nodiscard]] Result<> makeSwitch();
@@ -184,25 +186,104 @@ private:
// The context for a single block scope, including the instructions parsed
// inside that scope so far and the ultimate result type we expect this block
// to have.
- struct BlockCtx {
+ struct ScopeCtx {
+ struct NoScope {};
+ struct BlockScope {
+ Block* block;
+ };
+ struct IfScope {
+ If* iff;
+ Name label;
+ };
+ struct ElseScope {
+ If* iff;
+ Name label;
+ };
+ using Scope = std::variant<NoScope, BlockScope, IfScope, ElseScope>;
+
+ // The control flow structure we are building expressions for.
+ Scope scope;
+
std::vector<Expression*> exprStack;
- Block* block;
// Whether we have seen an unreachable instruction and are in
// stack-polymorphic unreachable mode.
bool unreachable = false;
+
+ ScopeCtx() : scope(NoScope{}) {}
+ ScopeCtx(Scope scope) : scope(scope) {}
+
+ static ScopeCtx makeBlock(Block* block) {
+ return ScopeCtx(BlockScope{block});
+ }
+ static ScopeCtx makeIf(If* iff, Name label = {}) {
+ return ScopeCtx(IfScope{iff, label});
+ }
+ static ScopeCtx makeElse(If* iff, Name label = {}) {
+ return ScopeCtx(ElseScope{iff, label});
+ }
+
+ bool isNone() { return std::get_if<NoScope>(&scope); }
+ Block* getBlock() {
+ if (auto* blockScope = std::get_if<BlockScope>(&scope)) {
+ return blockScope->block;
+ }
+ return nullptr;
+ }
+ If* getIf() {
+ if (auto* ifScope = std::get_if<IfScope>(&scope)) {
+ return ifScope->iff;
+ }
+ return nullptr;
+ }
+ If* getElse() {
+ if (auto* elseScope = std::get_if<ElseScope>(&scope)) {
+ return elseScope->iff;
+ }
+ return nullptr;
+ }
+ Type getResultType() {
+ if (auto* block = getBlock()) {
+ return block->type;
+ }
+ if (auto* iff = getIf()) {
+ return iff->type;
+ }
+ if (auto* iff = getElse()) {
+ return iff->type;
+ }
+ WASM_UNREACHABLE("unexpected scope kind");
+ }
+ Name getLabel() {
+ if (auto* block = getBlock()) {
+ return block->name;
+ }
+ if (auto* ifScope = std::get_if<IfScope>(&scope)) {
+ return ifScope->label;
+ }
+ if (auto* elseScope = std::get_if<ElseScope>(&scope)) {
+ return elseScope->label;
+ }
+ WASM_UNREACHABLE("unexpected scope kind");
+ }
};
// The stack of block contexts currently being parsed.
- std::vector<BlockCtx> scopeStack;
+ std::vector<ScopeCtx> scopeStack;
- BlockCtx& getScope() {
+ ScopeCtx& getScope() {
if (scopeStack.empty()) {
// We are not in a block context, so push a dummy scope.
- scopeStack.push_back({{}, nullptr});
+ scopeStack.push_back({});
}
return scopeStack.back();
}
+ // Collect the current scope into a single expression. If it has multiple
+ // top-level expressions, this requires collecting them into a block. If we
+ // are in a block context, we can collect them directly into the destination
+ // `block`, but otherwise we will have to allocate a new block.
+ Result<Expression*> finishScope(Block* block = nullptr);
+
[[nodiscard]] Result<Index> addScratchLocal(Type);
[[nodiscard]] Result<Expression*> pop();
void push(Expression*);
diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp
index bc3180423..b8d742ae8 100644
--- a/src/wasm/wasm-ir-builder.cpp
+++ b/src/wasm/wasm-ir-builder.cpp
@@ -172,7 +172,7 @@ Result<Expression*> IRBuilder::build() {
if (scopeStack.empty()) {
return builder.makeNop();
}
- if (scopeStack.size() > 1 || scopeStack.back().block != nullptr) {
+ if (scopeStack.size() > 1 || !scopeStack.back().isNone()) {
return Err{"unfinished block context"};
}
if (scopeStack.back().exprStack.size() > 1) {
@@ -282,18 +282,26 @@ Result<> IRBuilder::visitArrayNew(ArrayNew* curr) {
}
Result<> IRBuilder::visitBlockStart(Block* curr) {
- scopeStack.push_back({{}, curr});
+ scopeStack.push_back(ScopeCtx::makeBlock(curr));
return Ok{};
}
-Result<> IRBuilder::visitEnd() {
- if (scopeStack.empty() || !scopeStack.back().block) {
- return Err{"unexpected end"};
+Result<> IRBuilder::visitIfStart(If* iff, Name label) {
+ auto cond = pop();
+ CHECK_ERR(cond);
+ iff->condition = *cond;
+ scopeStack.push_back(ScopeCtx::makeIf(iff, label));
+ return Ok{};
+}
+
+Result<Expression*> IRBuilder::finishScope(Block* block) {
+ if (scopeStack.empty() || scopeStack.back().isNone()) {
+ return Err{"unexpected end of scope"};
}
auto& scope = scopeStack.back();
- Block* block = scope.block;
- if (block->type.isTuple()) {
+ auto type = scope.getResultType();
+ if (type.isTuple()) {
if (scope.unreachable) {
// We may not have enough concrete values on the stack to construct the
// full tuple, and if we tried to fill out the beginning of a tuple.make
@@ -328,18 +336,98 @@ Result<> IRBuilder::visitEnd() {
scope.exprStack.push_back(builder.makeTupleMake(std::move(elems)));
}
}
- } else if (block->type.isConcrete()) {
+ } else if (type.isConcrete()) {
// If the value is buried in none-typed expressions, we have to bring it to
// the top.
auto hoisted = hoistLastValue();
CHECK_ERR(hoisted);
}
- block->list.set(scope.exprStack);
- // TODO: Track branches so we can know whether this block is a target and
- // finalize more efficiently.
- block->finalize(block->type);
+ Expression* ret = nullptr;
+ if (scope.exprStack.size() == 0) {
+ // No expressions for this scope, but we need something. If we were given a
+ // block, we can empty it out and return it, but otherwise we need a nop.
+ if (block) {
+ block->list.clear();
+ ret = block;
+ } else {
+ ret = builder.makeNop();
+ }
+ } else if (scope.exprStack.size() == 1) {
+ // We can put our single expression directly into the surrounding scope.
+ if (block) {
+ block->list.resize(1);
+ block->list[0] = scope.exprStack.back();
+ ret = block;
+ } else {
+ ret = scope.exprStack.back();
+ }
+ } else {
+ // More than one expression, so we need a block. Allocate one if we weren't
+ // already given one.
+ if (!block) {
+ block = wasm.allocator.alloc<Block>();
+ block->type = type;
+ }
+ block->list.set(scope.exprStack);
+ ret = block;
+ }
scopeStack.pop_back();
- push(block);
+ return ret;
+}
+
+Result<> IRBuilder::visitElse() {
+ auto& scope = getScope();
+ auto* iff = scope.getIf();
+ if (!iff) {
+ return Err{"unexpected else"};
+ }
+ auto label = scope.getLabel();
+ auto expr = finishScope();
+ CHECK_ERR(expr);
+ iff->ifTrue = *expr;
+ scopeStack.push_back(ScopeCtx::makeElse(iff, label));
+ return Ok{};
+}
+
+Result<> IRBuilder::visitEnd() {
+ auto& scope = getScope();
+ if (scope.isNone()) {
+ return Err{"unexpected end"};
+ }
+ if (auto* block = scope.getBlock()) {
+ auto expr = finishScope(block);
+ CHECK_ERR(expr);
+ assert(*expr == block);
+ // TODO: Track branches so we can know whether this block is a target and
+ // finalize more efficiently.
+ block->finalize(block->type);
+ push(block);
+ return Ok{};
+ }
+ auto label = scope.getLabel();
+ Expression* scopeExpr = nullptr;
+ if (auto* iff = scope.getIf()) {
+ auto expr = finishScope();
+ CHECK_ERR(expr);
+ iff->ifTrue = *expr;
+ iff->ifFalse = nullptr;
+ iff->finalize();
+ scopeExpr = iff;
+ } else if (auto* iff = scope.getElse()) {
+ auto expr = finishScope();
+ CHECK_ERR(expr);
+ iff->ifFalse = *expr;
+ iff->finalize();
+ scopeExpr = iff;
+ }
+ assert(scopeExpr && "unexpected scope kind");
+ if (label) {
+ // We cannot directly name an If in Binaryen IR, so we need to wrap it in
+ // a block.
+ push(builder.makeBlock(label, {scopeExpr}, scopeExpr->type));
+ } else {
+ push(scopeExpr);
+ }
return Ok{};
}
@@ -352,8 +440,13 @@ Result<> IRBuilder::makeBlock(Name label, Type type) {
auto* block = wasm.allocator.alloc<Block>();
block->name = label;
block->type = type;
- CHECK_ERR(visitBlockStart(block));
- return Ok{};
+ return visitBlockStart(block);
+}
+
+Result<> IRBuilder::makeIf(Name label, Type type) {
+ auto* iff = wasm.allocator.alloc<If>();
+ iff->type = type;
+ return visitIfStart(iff, label);
}
// Result<> IRBuilder::makeIf() {}