summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--src/parser/contexts.h45
-rw-r--r--src/wasm-binary.h4
-rw-r--r--src/wasm-ir-builder.h95
-rw-r--r--src/wasm/wasm-binary.cpp40
-rw-r--r--src/wasm/wasm-ir-builder.cpp129
-rw-r--r--test/lit/binary/bad-multivalue-block.test16
-rw-r--r--test/lit/binary/bad-multivalue-block.test.wasmbin34 -> 0 bytes
-rw-r--r--test/lit/binary/bad-multivalue-if.test22
-rw-r--r--test/lit/binary/bad-multivalue-if.test.wasmbin38 -> 0 bytes
-rw-r--r--test/lit/control-flow-input.wast623
-rw-r--r--test/lit/control-flow-input.wast.wasmbin0 -> 793 bytes
-rw-r--r--test/lit/parse-bad-block-params.wast12
13 files changed, 825 insertions, 162 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 324c29526..52e453ee4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,7 @@ Current Trunk
- BinaryenSelect no longer takes a type parameter.
- AutoDrop APIs have been removed.
+ - Binaryen now supports parsing control flow structures with parameter types.
v120
----
diff --git a/src/parser/contexts.h b/src/parser/contexts.h
index 807b6c003..3e0bc7c40 100644
--- a/src/parser/contexts.h
+++ b/src/parser/contexts.h
@@ -1447,11 +1447,7 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
Result<HeapType> getBlockTypeFromTypeUse(Index pos, HeapType type) {
assert(type.isSignature());
- if (type.getSignature().params != Type::none) {
- return in.err(pos, "block parameters not yet supported");
- }
- // TODO: Once we support block parameters, return an error here if any of
- // them are named.
+ // TODO: Error if block parameters are named
return type;
}
@@ -1822,9 +1818,11 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
HeapType type) {
// TODO: validate labels?
// TODO: Move error on input types to here?
- return withLoc(pos,
- irBuilder.makeBlock(label ? *label : Name{},
- type.getSignature().results));
+ if (!type.isSignature()) {
+ return in.err(pos, "expected function type");
+ }
+ return withLoc(
+ pos, irBuilder.makeBlock(label ? *label : Name{}, type.getSignature()));
}
Result<> makeIf(Index pos,
@@ -1832,10 +1830,11 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
std::optional<Name> label,
HeapType type) {
// TODO: validate labels?
- // TODO: Move error on input types to here?
+ if (!type.isSignature()) {
+ return in.err(pos, "expected function type");
+ }
return withLoc(
- pos,
- irBuilder.makeIf(label ? *label : Name{}, type.getSignature().results));
+ pos, irBuilder.makeIf(label ? *label : Name{}, type.getSignature()));
}
Result<> visitElse() { return withLoc(irBuilder.visitElse()); }
@@ -1845,10 +1844,11 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
std::optional<Name> label,
HeapType type) {
// TODO: validate labels?
- // TODO: Move error on input types to here?
+ if (!type.isSignature()) {
+ return in.err(pos, "expected function type");
+ }
return withLoc(
- pos,
- irBuilder.makeLoop(label ? *label : Name{}, type.getSignature().results));
+ pos, irBuilder.makeLoop(label ? *label : Name{}, type.getSignature()));
}
Result<> makeTry(Index pos,
@@ -1856,10 +1856,11 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
std::optional<Name> label,
HeapType type) {
// TODO: validate labels?
- // TODO: Move error on input types to here?
+ if (!type.isSignature()) {
+ return in.err(pos, "expected function type");
+ }
return withLoc(
- pos,
- irBuilder.makeTry(label ? *label : Name{}, type.getSignature().results));
+ pos, irBuilder.makeTry(label ? *label : Name{}, type.getSignature()));
}
Result<> makeTryTable(Index pos,
@@ -1875,12 +1876,10 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
labels.push_back(info.label);
isRefs.push_back(info.isRef);
}
- return withLoc(pos,
- irBuilder.makeTryTable(label ? *label : Name{},
- type.getSignature().results,
- tags,
- labels,
- isRefs));
+ return withLoc(
+ pos,
+ irBuilder.makeTryTable(
+ label ? *label : Name{}, type.getSignature(), tags, labels, isRefs));
}
Result<> visitCatch(Index pos, Name tag) {
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index 163a62b1e..61f4faf69 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -1467,10 +1467,12 @@ public:
bool getBasicType(int32_t code, Type& out);
bool getBasicHeapType(int64_t code, HeapType& out);
+ // Get the signature of control flow structure.
+ Signature getBlockType();
// Read a value and get a type for it.
Type getType();
// Get a type given the initial S32LEB has already been read, and is provided.
- Type getType(int initial);
+ Type getType(int code);
HeapType getHeapType();
HeapType getIndexedHeapType();
diff --git a/src/wasm-ir-builder.h b/src/wasm-ir-builder.h
index 84ac26979..250d5d17c 100644
--- a/src/wasm-ir-builder.h
+++ b/src/wasm-ir-builder.h
@@ -80,15 +80,18 @@ public:
// the corresponding `makeXYZ` function below instead of `visitXYZStart`, but
// either way must call `visitEnd` and friends at the appropriate times.
Result<> visitFunctionStart(Function* func);
- Result<> visitBlockStart(Block* block);
- Result<> visitIfStart(If* iff, Name label = {});
+ Result<> visitBlockStart(Block* block, Type inputType = Type::none);
+ Result<> visitIfStart(If* iff, Name label = {}, Type inputType = Type::none);
Result<> visitElse();
- Result<> visitLoopStart(Loop* iff);
- Result<> visitTryStart(Try* tryy, Name label = {});
+ Result<> visitLoopStart(Loop* iff, Type inputType = Type::none);
+ Result<>
+ visitTryStart(Try* tryy, Name label = {}, Type inputType = Type::none);
Result<> visitCatch(Name tag);
Result<> visitCatchAll();
Result<> visitDelegate(Index label);
- Result<> visitTryTableStart(TryTable* trytable, Name label = {});
+ Result<> visitTryTableStart(TryTable* trytable,
+ Name label = {},
+ Type inputType = Type::none);
Result<> visitEnd();
// Used to visit break nodes when traversing a single block without its
@@ -113,9 +116,9 @@ public:
// nodes. This is generally safer than calling `visit` because the function
// signatures ensure that there are no missing fields.
Result<> makeNop();
- Result<> makeBlock(Name label, Type type);
- Result<> makeIf(Name label, Type type);
- Result<> makeLoop(Name label, Type type);
+ Result<> makeBlock(Name label, Signature sig);
+ Result<> makeIf(Name label, Signature sig);
+ Result<> makeLoop(Name label, Signature sig);
Result<> makeBreak(Index label, bool isConditional);
Result<> makeSwitch(const std::vector<Index>& labels, Index defaultLabel);
// Unlike Builder::makeCall, this assumes the function already exists.
@@ -180,9 +183,9 @@ public:
Result<> makeTableFill(Name table);
Result<> makeTableCopy(Name destTable, Name srcTable);
Result<> makeTableInit(Name elem, Name table);
- Result<> makeTry(Name label, Type type);
+ Result<> makeTry(Name label, Signature sig);
Result<> makeTryTable(Name label,
- Type type,
+ Signature sig,
const std::vector<Name>& tags,
const std::vector<Index>& labels,
const std::vector<bool>& isRefs);
@@ -323,13 +326,21 @@ private:
// The branch label name for this scope. Always fresh, never shadowed.
Name label;
+
// For Try/Catch/CatchAll scopes, we need to separately track a label used
// for branches, since the normal label is only used for delegates.
Name branchLabel;
bool labelUsed = false;
+ // If the control flow scope has an input type, we need to lower it using a
+ // scratch local because we cannot represent control flow input in the IR.
+ Type inputType;
+ Index inputLocal = -1;
+
+ // The stack of instructions being built in this scope.
std::vector<Expression*> exprStack;
+
// Whether we have seen an unreachable instruction and are in
// stack-polymorphic unreachable mode.
bool unreachable = false;
@@ -338,29 +349,39 @@ private:
size_t startPos = 0;
ScopeCtx() : scope(NoScope{}) {}
- ScopeCtx(Scope scope) : scope(scope) {}
- ScopeCtx(Scope scope, Name label, bool labelUsed)
- : scope(scope), label(label), labelUsed(labelUsed) {}
+ ScopeCtx(Scope scope, Type inputType)
+ : scope(scope), inputType(inputType) {}
+ ScopeCtx(
+ Scope scope, Name label, bool labelUsed, Type inputType, Index inputLocal)
+ : scope(scope), label(label), labelUsed(labelUsed), inputType(inputType),
+ inputLocal(inputLocal) {}
ScopeCtx(Scope scope, Name label, bool labelUsed, Name branchLabel)
: scope(scope), label(label), branchLabel(branchLabel),
labelUsed(labelUsed) {}
static ScopeCtx makeFunc(Function* func) {
- return ScopeCtx(FuncScope{func});
+ return ScopeCtx(FuncScope{func}, Type::none);
}
- static ScopeCtx makeBlock(Block* block) {
- return ScopeCtx(BlockScope{block});
+ static ScopeCtx makeBlock(Block* block, Type inputType) {
+ return ScopeCtx(BlockScope{block}, inputType);
}
- static ScopeCtx makeIf(If* iff, Name originalLabel = {}) {
- return ScopeCtx(IfScope{iff, originalLabel});
+ static ScopeCtx makeIf(If* iff, Name originalLabel, Type inputType) {
+ return ScopeCtx(IfScope{iff, originalLabel}, inputType);
}
- static ScopeCtx
- makeElse(If* iff, Name originalLabel, Name label, bool labelUsed) {
- return ScopeCtx(ElseScope{iff, originalLabel}, label, labelUsed);
+ static ScopeCtx makeElse(If* iff,
+ Name originalLabel,
+ Name label,
+ bool labelUsed,
+ Type inputType,
+ Index inputLocal) {
+ return ScopeCtx(
+ ElseScope{iff, originalLabel}, label, labelUsed, inputType, inputLocal);
}
- static ScopeCtx makeLoop(Loop* loop) { return ScopeCtx(LoopScope{loop}); }
- static ScopeCtx makeTry(Try* tryy, Name originalLabel = {}) {
- return ScopeCtx(TryScope{tryy, originalLabel});
+ static ScopeCtx makeLoop(Loop* loop, Type inputType) {
+ return ScopeCtx(LoopScope{loop}, inputType);
+ }
+ static ScopeCtx makeTry(Try* tryy, Name originalLabel, Type inputType) {
+ return ScopeCtx(TryScope{tryy, originalLabel}, inputType);
}
static ScopeCtx makeCatch(Try* tryy,
Name originalLabel,
@@ -378,8 +399,9 @@ private:
return ScopeCtx(
CatchAllScope{tryy, originalLabel}, label, labelUsed, branchLabel);
}
- static ScopeCtx makeTryTable(TryTable* trytable, Name originalLabel = {}) {
- return ScopeCtx(TryTableScope{trytable, originalLabel});
+ static ScopeCtx
+ makeTryTable(TryTable* trytable, Name originalLabel, Type inputType) {
+ return ScopeCtx(TryTableScope{trytable, originalLabel}, inputType);
}
bool isNone() { return std::get_if<NoScope>(&scope); }
@@ -518,6 +540,7 @@ private:
}
WASM_UNREACHABLE("unexpected scope kind");
}
+ bool isDelimiter() { return getElse() || getCatch() || getCatchAll(); }
};
// The stack of block contexts currently being parsed.
@@ -541,7 +564,7 @@ private:
Index blockHint = 0;
Index labelHint = 0;
- void pushScope(ScopeCtx scope) {
+ Result<> pushScope(ScopeCtx&& scope) {
if (auto label = scope.getOriginalLabel()) {
// Assign a fresh label to the scope, if necessary.
if (!scope.label) {
@@ -554,7 +577,21 @@ private:
scope.startPos = lastBinaryPos;
lastBinaryPos = *binaryPos;
}
- scopeStack.push_back(scope);
+ bool hasInput = scope.inputType != Type::none;
+ Index inputLocal = scope.inputLocal;
+ if (hasInput && !scope.isDelimiter()) {
+ if (inputLocal == Index(-1)) {
+ auto scratch = addScratchLocal(scope.inputType);
+ CHECK_ERR(scratch);
+ inputLocal = scope.inputLocal = *scratch;
+ }
+ CHECK_ERR(makeLocalSet(inputLocal));
+ }
+ scopeStack.emplace_back(std::move(scope));
+ if (hasInput) {
+ CHECK_ERR(makeLocalGet(inputLocal));
+ }
+ return Ok{};
}
ScopeCtx& getScope() {
@@ -610,6 +647,8 @@ private:
Result<Type> getLabelType(Index label);
Result<Type> getLabelType(Name labelName);
+ void fixLoopWithInput(Loop* loop, Type inputType, Index scratch);
+
void dump();
};
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 0c931f518..791dc53d7 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -2121,30 +2121,30 @@ bool WasmBinaryReader::getBasicHeapType(int64_t code, HeapType& out) {
}
}
-Type WasmBinaryReader::getType(int initial) {
- // Single value types are negative; signature indices are non-negative
- if (initial >= 0) {
- // TODO: Handle block input types properly.
- auto sig = getSignatureByTypeIndex(initial);
- if (sig.params != Type::none) {
- throwError("control flow inputs are not supported yet");
- }
- return sig.results;
+Signature WasmBinaryReader::getBlockType() {
+ // Single value types are negative; signature indices are non-negative.
+ auto code = getS32LEB();
+ if (code >= 0) {
+ return getSignatureByTypeIndex(code);
+ }
+ if (code == BinaryConsts::EncodedType::Empty) {
+ return Signature();
}
+ return Signature(Type::none, getType(code));
+}
+
+Type WasmBinaryReader::getType(int code) {
Type type;
- if (getBasicType(initial, type)) {
+ if (getBasicType(code, type)) {
return type;
}
- switch (initial) {
- // None only used for block signatures. TODO: Separate out?
- case BinaryConsts::EncodedType::Empty:
- return Type::none;
+ switch (code) {
case BinaryConsts::EncodedType::nullable:
return Type(getHeapType(), Nullable);
case BinaryConsts::EncodedType::nonnullable:
return Type(getHeapType(), NonNullable);
default:
- throwError("invalid wasm type: " + std::to_string(initial));
+ throwError("invalid wasm type: " + std::to_string(code));
}
WASM_UNREACHABLE("unexpected type");
}
@@ -2885,11 +2885,11 @@ Result<> WasmBinaryReader::readInst() {
uint8_t code = getInt8();
switch (code) {
case BinaryConsts::Block:
- return builder.makeBlock(Name(), getType());
+ return builder.makeBlock(Name(), getBlockType());
case BinaryConsts::If:
- return builder.makeIf(Name(), getType());
+ return builder.makeIf(Name(), getBlockType());
case BinaryConsts::Loop:
- return builder.makeLoop(Name(), getType());
+ return builder.makeLoop(Name(), getBlockType());
case BinaryConsts::Br:
return builder.makeBreak(getU32LEB(), false);
case BinaryConsts::BrIf:
@@ -2974,9 +2974,9 @@ Result<> WasmBinaryReader::readInst() {
case BinaryConsts::TableSet:
return builder.makeTableSet(getTableName(getU32LEB()));
case BinaryConsts::Try:
- return builder.makeTry(Name(), getType());
+ return builder.makeTry(Name(), getBlockType());
case BinaryConsts::TryTable: {
- auto type = getType();
+ auto type = getBlockType();
std::vector<Name> tags;
std::vector<Index> labels;
std::vector<bool> isRefs;
diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp
index 96212ccd7..6cd62e439 100644
--- a/src/wasm/wasm-ir-builder.cpp
+++ b/src/wasm/wasm-ir-builder.cpp
@@ -679,9 +679,8 @@ Result<> IRBuilder::visitExpression(Expression* curr) {
Result<Type> IRBuilder::getLabelType(Index label) {
auto scope = getScope(label);
CHECK_ERR(scope);
- // Loops would receive their input type rather than their output type, if we
- // supported that.
- return (*scope)->getLoop() ? Type::none : (*scope)->getResultType();
+ // Loops receive their input type rather than their output type.
+ return (*scope)->getLoop() ? (*scope)->inputType : (*scope)->getResultType();
}
Result<Type> IRBuilder::getLabelType(Name labelName) {
@@ -722,35 +721,31 @@ Result<> IRBuilder::visitFunctionStart(Function* func) {
return Ok{};
}
-Result<> IRBuilder::visitBlockStart(Block* curr) {
+Result<> IRBuilder::visitBlockStart(Block* curr, Type inputType) {
applyDebugLoc(curr);
- pushScope(ScopeCtx::makeBlock(curr));
- return Ok{};
+ return pushScope(ScopeCtx::makeBlock(curr, inputType));
}
-Result<> IRBuilder::visitIfStart(If* iff, Name label) {
+Result<> IRBuilder::visitIfStart(If* iff, Name label, Type inputType) {
applyDebugLoc(iff);
CHECK_ERR(visitIf(iff));
- pushScope(ScopeCtx::makeIf(iff, label));
- return Ok{};
+ return pushScope(ScopeCtx::makeIf(iff, label, inputType));
}
-Result<> IRBuilder::visitLoopStart(Loop* loop) {
+Result<> IRBuilder::visitLoopStart(Loop* loop, Type inputType) {
applyDebugLoc(loop);
- pushScope(ScopeCtx::makeLoop(loop));
- return Ok{};
+ return pushScope(ScopeCtx::makeLoop(loop, inputType));
}
-Result<> IRBuilder::visitTryStart(Try* tryy, Name label) {
+Result<> IRBuilder::visitTryStart(Try* tryy, Name label, Type inputType) {
applyDebugLoc(tryy);
- pushScope(ScopeCtx::makeTry(tryy, label));
- return Ok{};
+ return pushScope(ScopeCtx::makeTry(tryy, label, inputType));
}
-Result<> IRBuilder::visitTryTableStart(TryTable* trytable, Name label) {
+Result<>
+IRBuilder::visitTryTableStart(TryTable* trytable, Name label, Type inputType) {
applyDebugLoc(trytable);
- pushScope(ScopeCtx::makeTryTable(trytable, label));
- return Ok{};
+ return pushScope(ScopeCtx::makeTryTable(trytable, label, inputType));
}
Result<Expression*> IRBuilder::finishScope(Block* block) {
@@ -849,6 +844,8 @@ Result<> IRBuilder::visitElse() {
auto originalLabel = scope.getOriginalLabel();
auto label = scope.label;
auto labelUsed = scope.labelUsed;
+ auto inputType = scope.inputType;
+ auto inputLocal = scope.inputLocal;
auto expr = finishScope();
CHECK_ERR(expr);
iff->ifTrue = *expr;
@@ -858,8 +855,8 @@ Result<> IRBuilder::visitElse() {
lastBinaryPos - codeSectionOffset;
}
- pushScope(ScopeCtx::makeElse(iff, originalLabel, label, labelUsed));
- return Ok{};
+ return pushScope(ScopeCtx::makeElse(
+ iff, originalLabel, label, labelUsed, inputType, inputLocal));
}
Result<> IRBuilder::visitCatch(Name tag) {
@@ -891,8 +888,8 @@ Result<> IRBuilder::visitCatch(Name tag) {
delimiterLocs[delimiterLocs.size()] = lastBinaryPos - codeSectionOffset;
}
- pushScope(
- ScopeCtx::makeCatch(tryy, originalLabel, label, labelUsed, branchLabel));
+ CHECK_ERR(pushScope(
+ ScopeCtx::makeCatch(tryy, originalLabel, label, labelUsed, branchLabel)));
// Push a pop for the exception payload if necessary.
auto params = wasm.getTag(tag)->sig.params;
if (params != Type::none) {
@@ -933,9 +930,8 @@ Result<> IRBuilder::visitCatchAll() {
delimiterLocs[delimiterLocs.size()] = lastBinaryPos - codeSectionOffset;
}
- pushScope(
+ return pushScope(
ScopeCtx::makeCatchAll(tryy, originalLabel, label, labelUsed, branchLabel));
- return Ok{};
}
Result<> IRBuilder::visitDelegate(Index label) {
@@ -1035,11 +1031,24 @@ Result<> IRBuilder::visitEnd() {
} else if (auto* loop = scope.getLoop()) {
loop->body = *expr;
loop->name = scope.label;
+ if (scope.inputType != Type::none && scope.labelUsed) {
+ // Branches to this loop carry values, but Binaryen IR does not support
+ // that. Fix this by trampolining the branches through new code that sets
+ // the branch value to the appropriate scratch local.
+ fixLoopWithInput(loop, scope.inputType, scope.inputLocal);
+ }
loop->finalize(loop->type);
push(loop);
} else if (auto* iff = scope.getIf()) {
iff->ifTrue = *expr;
- iff->ifFalse = nullptr;
+ if (scope.inputType != Type::none) {
+ // Normally an if without an else must have type none, but if there is an
+ // input parameter, the empty else arm must propagate its value.
+ // Synthesize an else arm that loads the value from the scratch local.
+ iff->ifFalse = builder.makeLocalGet(scope.inputLocal, scope.inputType);
+ } else {
+ iff->ifFalse = nullptr;
+ }
iff->finalize(iff->type);
push(maybeWrapForLabel(iff));
} else if (auto* iff = scope.getElse()) {
@@ -1067,6 +1076,46 @@ Result<> IRBuilder::visitEnd() {
return Ok{};
}
+// Branches to this loop need to be trampolined through code that sets the value
+// carried by the branch to the appropriate scratch local before branching to
+// the loop. Transform this:
+//
+// (loop $l (param t1) (result t2) ...)
+//
+// to this:
+//
+// (loop $l0 (result t2)
+// (block $l1 (result t2)
+// (local.set $scratch ;; set the branch values to the scratch local
+// (block $l (result t1)
+// (br $l1 ;; exit the loop with the fallthrough value, if any.
+// ... ;; contains branches to $l
+// )
+// )
+// )
+// (br $l0) ;; continue the loop
+// )
+// )
+void IRBuilder::fixLoopWithInput(Loop* loop, Type inputType, Index scratch) {
+ auto l = loop->name;
+ auto l0 = makeFresh(l, 0);
+ auto l1 = makeFresh(l, 1);
+
+ Block* inner =
+ loop->type == Type::none
+ ? builder.blockifyWithName(
+ loop->body, l, builder.makeBreak(l1), inputType)
+ : builder.makeBlock(l, {builder.makeBreak(l1, loop->body)}, inputType);
+
+ Block* outer = builder.makeBlock(
+ l1,
+ {builder.makeLocalSet(scratch, inner), builder.makeBreak(l0)},
+ loop->type);
+
+ loop->body = outer;
+ loop->name = l0;
+}
+
Result<Index> IRBuilder::getLabelIndex(Name label, bool inDelegate) {
auto it = labelDepths.find(label);
if (it == labelDepths.end() || it->second.empty()) {
@@ -1128,24 +1177,24 @@ Result<> IRBuilder::makeNop() {
return Ok{};
}
-Result<> IRBuilder::makeBlock(Name label, Type type) {
+Result<> IRBuilder::makeBlock(Name label, Signature sig) {
auto* block = wasm.allocator.alloc<Block>();
block->name = label;
- block->type = type;
- return visitBlockStart(block);
+ block->type = sig.results;
+ return visitBlockStart(block, sig.params);
}
-Result<> IRBuilder::makeIf(Name label, Type type) {
+Result<> IRBuilder::makeIf(Name label, Signature sig) {
auto* iff = wasm.allocator.alloc<If>();
- iff->type = type;
- return visitIfStart(iff, label);
+ iff->type = sig.results;
+ return visitIfStart(iff, label, sig.params);
}
-Result<> IRBuilder::makeLoop(Name label, Type type) {
+Result<> IRBuilder::makeLoop(Name label, Signature sig) {
auto* loop = wasm.allocator.alloc<Loop>();
loop->name = label;
- loop->type = type;
- return visitLoopStart(loop);
+ loop->type = sig.results;
+ return visitLoopStart(loop, sig.params);
}
Result<> IRBuilder::makeBreak(Index label, bool isConditional) {
@@ -1584,19 +1633,19 @@ Result<> IRBuilder::makeTableInit(Name elem, Name table) {
return Ok{};
}
-Result<> IRBuilder::makeTry(Name label, Type type) {
+Result<> IRBuilder::makeTry(Name label, Signature sig) {
auto* tryy = wasm.allocator.alloc<Try>();
- tryy->type = type;
- return visitTryStart(tryy, label);
+ tryy->type = sig.results;
+ return visitTryStart(tryy, label, sig.params);
}
Result<> IRBuilder::makeTryTable(Name label,
- Type type,
+ Signature sig,
const std::vector<Name>& tags,
const std::vector<Index>& labels,
const std::vector<bool>& isRefs) {
auto* trytable = wasm.allocator.alloc<TryTable>();
- trytable->type = type;
+ trytable->type = sig.results;
trytable->catchTags.set(tags);
trytable->catchRefs.set(isRefs);
trytable->catchDests.reserve(labels.size());
@@ -1605,7 +1654,7 @@ Result<> IRBuilder::makeTryTable(Name label,
CHECK_ERR(name);
trytable->catchDests.push_back(*name);
}
- return visitTryTableStart(trytable, label);
+ return visitTryTableStart(trytable, label, sig.params);
}
Result<> IRBuilder::makeThrow(Name tag) {
diff --git a/test/lit/binary/bad-multivalue-block.test b/test/lit/binary/bad-multivalue-block.test
deleted file mode 100644
index 8b100fe89..000000000
--- a/test/lit/binary/bad-multivalue-block.test
+++ /dev/null
@@ -1,16 +0,0 @@
-;; Test that we error properly on a block with a bad multivalue (inputs).
-
-;; File contents:
-;;
-;; (module
-;; (func $test
-;; i32.const 0
-;; (block (param i32)
-;; drop
-;; )
-;; )
-;; )
-
-;; RUN: not wasm-opt -all %s.wasm 2>&1 | filecheck %s
-
-;; CHECK: control flow inputs are not supported yet
diff --git a/test/lit/binary/bad-multivalue-block.test.wasm b/test/lit/binary/bad-multivalue-block.test.wasm
deleted file mode 100644
index e44b9033f..000000000
--- a/test/lit/binary/bad-multivalue-block.test.wasm
+++ /dev/null
Binary files differ
diff --git a/test/lit/binary/bad-multivalue-if.test b/test/lit/binary/bad-multivalue-if.test
deleted file mode 100644
index 8fe206012..000000000
--- a/test/lit/binary/bad-multivalue-if.test
+++ /dev/null
@@ -1,22 +0,0 @@
-;; Test that we error properly on an if with a bad multivalue (inputs).
-
-;; File contents:
-;;
-;; (module
-;; (func $test
-;; i32.const 0
-;; i32.const 1
-;; (if (param i32)
-;; (then
-;; drop
-;; )
-;; (else
-;; drop
-;; )
-;; )
-;; )
-;; )
-
-;; RUN: not wasm-opt -all %s.wasm 2>&1 | filecheck %s
-
-;; CHECK: control flow inputs are not supported yet
diff --git a/test/lit/binary/bad-multivalue-if.test.wasm b/test/lit/binary/bad-multivalue-if.test.wasm
deleted file mode 100644
index baddfec4e..000000000
--- a/test/lit/binary/bad-multivalue-if.test.wasm
+++ /dev/null
Binary files differ
diff --git a/test/lit/control-flow-input.wast b/test/lit/control-flow-input.wast
new file mode 100644
index 000000000..7baae35f0
--- /dev/null
+++ b/test/lit/control-flow-input.wast
@@ -0,0 +1,623 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
+
+;; Check that control flow input is correctly parsed using scratch locals. The
+;; binary input file is generated from this file using WABT's wat2wasm
+;; --enable-all --debug-names and should be regenerated when new tests are added
+;; here.
+
+;; RUN: wasm-opt -all %s -S -o - | filecheck %s
+;; RUN: wasm-opt -all %s.wasm -S -o - | filecheck %s
+
+(module
+ (type $id (func (param i32) (result i32)))
+
+ ;; CHECK: (tag $e (param i32))
+ (tag $e (param i32))
+
+ ;; CHECK: (func $block (type $0) (result i32)
+ ;; CHECK-NEXT: (local $scratch i32)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block (result i32)
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $block (result i32)
+ i32.const 0
+ block (param i32) (result i32)
+ end
+ )
+
+ ;; CHECK: (func $block-multivalue (type $1) (result i32 i64)
+ ;; CHECK-NEXT: (local $scratch (tuple i32 i64))
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (tuple.make 2
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (i64.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block (type $1) (result i32 i64)
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $block-multivalue (result i32 i64)
+ i32.const 0
+ i64.const 1
+ block (param i32 i64) (result i32 i64)
+ end
+ )
+
+ ;; CHECK: (func $block-drop (type $2)
+ ;; CHECK-NEXT: (local $scratch i32)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $block-drop
+ i32.const 0
+ block (param i32)
+ drop
+ end
+ )
+
+ ;; CHECK: (func $block-multivalue-drop (type $2)
+ ;; CHECK-NEXT: (local $scratch (tuple i32 i64))
+ ;; CHECK-NEXT: (local $scratch_1 (tuple i32 i64))
+ ;; CHECK-NEXT: (local $scratch_2 i32)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (tuple.make 2
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (i64.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block (result i32)
+ ;; CHECK-NEXT: (local.set $scratch_2
+ ;; CHECK-NEXT: (tuple.extract 2 0
+ ;; CHECK-NEXT: (local.tee $scratch_1
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (tuple.extract 2 1
+ ;; CHECK-NEXT: (local.get $scratch_1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.get $scratch_2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $block-multivalue-drop
+ i32.const 0
+ i64.const 1
+ block (param i32 i64)
+ drop
+ drop
+ end
+ )
+
+ ;; CHECK: (func $block-passthrough-nop (type $0) (result i32)
+ ;; CHECK-NEXT: (local $scratch i32)
+ ;; CHECK-NEXT: (local $scratch_1 i32)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block (result i32)
+ ;; CHECK-NEXT: (local.set $scratch_1
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: (local.get $scratch_1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $block-passthrough-nop (result i32)
+ i32.const 0
+ block (param i32) (result i32)
+ nop
+ end
+ )
+
+ ;; CHECK: (func $block-passthrough-type (type $0) (result i32)
+ ;; CHECK-NEXT: (local $scratch i32)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block (result i32)
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $block-passthrough-type (result i32)
+ i32.const 0
+ block (type $id)
+ end
+ )
+
+ ;; CHECK: (func $loop (type $0) (result i32)
+ ;; CHECK-NEXT: (local $scratch i32)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (loop (result i32)
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $loop (result i32)
+ i32.const 0
+ loop (param i32) (result i32)
+ end
+ )
+
+ ;; CHECK: (func $loop-branch (type $2)
+ ;; CHECK-NEXT: (local $scratch i32)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (loop $label0
+ ;; CHECK-NEXT: (block $label1
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (block $label (result i32)
+ ;; CHECK-NEXT: (br $label
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $label1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $label0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $loop-branch
+ i32.const 0
+ loop (param i32)
+ br 0
+ end
+ )
+
+ ;; CHECK: (func $loop-branch-cond (type $0) (result i32)
+ ;; CHECK-NEXT: (local $scratch i32)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (loop $label0 (result i32)
+ ;; CHECK-NEXT: (block $label1 (result i32)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (block $label (result i32)
+ ;; CHECK-NEXT: (br $label1
+ ;; CHECK-NEXT: (br_if $label
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $label0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $loop-branch-cond (result i32)
+ i32.const 0
+ loop (param i32) (result i32)
+ i32.const 1
+ br_if 0
+ end
+ )
+
+ ;; CHECK: (func $loop-branch-cond-drop (type $2)
+ ;; CHECK-NEXT: (local $scratch i32)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (loop $label0
+ ;; CHECK-NEXT: (block $label1
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (block $label (result i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br_if $label
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $label1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $label0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $loop-branch-cond-drop
+ i32.const 0
+ loop (param i32)
+ i32.const 1
+ br_if 0
+ drop
+ end
+ )
+
+ ;; CHECK: (func $loop-branch-cond-new-val (type $4) (result i64)
+ ;; CHECK-NEXT: (local $scratch i32)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (loop $label0 (result i64)
+ ;; CHECK-NEXT: (block $label1 (result i64)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (block $label (result i32)
+ ;; CHECK-NEXT: (br $label1
+ ;; CHECK-NEXT: (block (result i64)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br_if $label
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i64.const 2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $label0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $loop-branch-cond-new-val (result i64)
+ i32.const 0
+ loop (param i32) (result i64)
+ i32.const 1
+ br_if 0
+ drop
+ i64.const 2
+ end
+ )
+
+ ;; CHECK: (func $nested-loops-multivalue (type $1) (result i32 i64)
+ ;; CHECK-NEXT: (local $scratch (tuple i32 i64))
+ ;; CHECK-NEXT: (local $scratch_1 (tuple i32 i64))
+ ;; CHECK-NEXT: (block $label2 (type $1) (result i32 i64)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (tuple.make 2
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (i64.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (loop $label10
+ ;; CHECK-NEXT: (block $label11
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (block $label1 (type $1) (result i32 i64)
+ ;; CHECK-NEXT: (local.set $scratch_1
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (loop $label0
+ ;; CHECK-NEXT: (block $label3
+ ;; CHECK-NEXT: (local.set $scratch_1
+ ;; CHECK-NEXT: (block $label (type $1) (result i32 i64)
+ ;; CHECK-NEXT: (br_table $label $label1 $label2
+ ;; CHECK-NEXT: (local.get $scratch_1)
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $label3)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $label0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $label11)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $label10)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $nested-loops-multivalue (result i32 i64)
+ i32.const 0
+ i64.const 1
+ loop (param i32 i64)
+ loop (param i32 i64)
+ i32.const 2
+ br_table 0 1 2
+ end
+ end
+ unreachable
+ )
+
+ ;; CHECK: (func $if (type $0) (result i32)
+ ;; CHECK-NEXT: (local $scratch i32)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (if (result i32)
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: (then
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (else
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $if (result i32)
+ i32.const 0
+ i32.const 1
+ if (param i32) (result i32)
+ end
+ )
+
+ ;; CHECK: (func $if-new-val (type $0) (result i32)
+ ;; CHECK-NEXT: (local $scratch i32)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (if (result i32)
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: (then
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (else
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $if-new-val (result i32)
+ i32.const 0
+ i32.const 1
+ if (param i32) (result i32)
+ drop
+ i32.const 2
+ end
+ )
+
+ ;; CHECK: (func $if-multivalue (type $1) (result i32 i64)
+ ;; CHECK-NEXT: (local $scratch (tuple i32 i64))
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (tuple.make 2
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (i64.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (if (type $1) (result i32 i64)
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: (then
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (else
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $if-multivalue (result i32 i64)
+ i32.const 0
+ i64.const 1
+ i32.const 2
+ if (param i32 i64) (result i32 i64)
+ end
+ )
+
+ ;; CHECK: (func $if-else (type $0) (result i32)
+ ;; CHECK-NEXT: (local $scratch i32)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (if (result i32)
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: (then
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (else
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $if-else (result i32)
+ i32.const 0
+ i32.const 1
+ if (param i32) (result i32)
+ else
+ end
+ )
+
+ ;; CHECK: (func $if-else-drop (type $2)
+ ;; CHECK-NEXT: (local $scratch i32)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: (then
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (else
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $if-else-drop
+ i32.const 0
+ i32.const 1
+ if (param i32)
+ drop
+ else
+ drop
+ end
+ )
+
+ ;; CHECK: (func $if-else-multivalue (type $5) (result f32)
+ ;; CHECK-NEXT: (local $scratch (tuple i32 i64))
+ ;; CHECK-NEXT: (local $scratch_1 (tuple i32 i64))
+ ;; CHECK-NEXT: (local $scratch_2 i32)
+ ;; CHECK-NEXT: (local $scratch_3 (tuple i32 i64))
+ ;; CHECK-NEXT: (local $scratch_4 i32)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (tuple.make 2
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (i64.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (if (result f32)
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: (then
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block (result i32)
+ ;; CHECK-NEXT: (local.set $scratch_2
+ ;; CHECK-NEXT: (tuple.extract 2 0
+ ;; CHECK-NEXT: (local.tee $scratch_1
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (tuple.extract 2 1
+ ;; CHECK-NEXT: (local.get $scratch_1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.get $scratch_2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (f32.const 3)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (else
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block (result i32)
+ ;; CHECK-NEXT: (local.set $scratch_4
+ ;; CHECK-NEXT: (tuple.extract 2 0
+ ;; CHECK-NEXT: (local.tee $scratch_3
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (tuple.extract 2 1
+ ;; CHECK-NEXT: (local.get $scratch_3)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.get $scratch_4)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (f32.const 4)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $if-else-multivalue (result f32)
+ i32.const 0
+ i64.const 1
+ i32.const 2
+ if (param i32 i64) (result f32)
+ drop
+ drop
+ f32.const 3
+ else
+ drop
+ drop
+ f32.const 4
+ end
+ )
+
+ ;; CHECK: (func $try (type $0) (result i32)
+ ;; CHECK-NEXT: (local $scratch i32)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (try (result i32)
+ ;; CHECK-NEXT: (do
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $try (result i32)
+ i32.const 0
+ try (param i32) (result i32)
+ end
+ )
+
+ ;; CHECK: (func $try-catch (type $0) (result i32)
+ ;; CHECK-NEXT: (local $scratch i32)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (try (result i32)
+ ;; CHECK-NEXT: (do
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (catch $e
+ ;; CHECK-NEXT: (pop i32)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $try-catch (result i32)
+ i32.const 0
+ try (param i32) (result i32)
+ catch $e
+ end
+ )
+
+ ;; CHECK: (func $try-catch-all (type $0) (result i32)
+ ;; CHECK-NEXT: (local $scratch i32)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (try (result i32)
+ ;; CHECK-NEXT: (do
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (catch_all
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $try-catch-all (result i32)
+ i32.const 0
+ try (param i32) (result i32)
+ catch_all
+ i32.const 1
+ end
+ )
+
+ ;; CHECK: (func $try-catch-delegate (type $0) (result i32)
+ ;; CHECK-NEXT: (local $scratch i32)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (try (result i32)
+ ;; CHECK-NEXT: (do
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (delegate 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $try-catch-delegate (result i32)
+ i32.const 0
+ try (param i32) (result i32)
+ delegate 0
+ )
+
+ ;; CHECK: (func $try-table (type $0) (result i32)
+ ;; CHECK-NEXT: (local $scratch i32)
+ ;; CHECK-NEXT: (local.set $scratch
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (try_table (result i32)
+ ;; CHECK-NEXT: (local.get $scratch)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $try-table (result i32)
+ i32.const 0
+ try_table (param i32) (result i32)
+ end
+ )
+)
diff --git a/test/lit/control-flow-input.wast.wasm b/test/lit/control-flow-input.wast.wasm
new file mode 100644
index 000000000..664d1be84
--- /dev/null
+++ b/test/lit/control-flow-input.wast.wasm
Binary files differ
diff --git a/test/lit/parse-bad-block-params.wast b/test/lit/parse-bad-block-params.wast
deleted file mode 100644
index 67e05989c..000000000
--- a/test/lit/parse-bad-block-params.wast
+++ /dev/null
@@ -1,12 +0,0 @@
-;; RUN: not wasm-opt %s -S -o - 2>&1 | filecheck %s
-
-;; CHECK: 8:11: error: block parameters not yet supported
-
-(module
- (func
- (i32.const 0)
- (block (param i32)
- (drop)
- )
- )
-)