summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/parser/contexts.h16
-rw-r--r--src/parser/parsers.h17
-rw-r--r--src/wasm-ir-builder.h77
-rw-r--r--src/wasm/wasm-ir-builder.cpp94
-rw-r--r--src/wasm/wasm-validator.cpp7
-rw-r--r--test/lit/wat-kitchen-sink.wast248
6 files changed, 395 insertions, 64 deletions
diff --git a/src/parser/contexts.h b/src/parser/contexts.h
index 7eedcccc2..862d9772d 100644
--- a/src/parser/contexts.h
+++ b/src/parser/contexts.h
@@ -279,6 +279,7 @@ struct NullInstrParserCtx {
using GlobalIdxT = Ok;
using MemoryIdxT = Ok;
using DataIdxT = Ok;
+ using LabelIdxT = Ok;
using MemargT = Ok;
@@ -298,6 +299,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{}; }
MemargT getMemarg(uint64_t, uint32_t) { return Ok{}; }
@@ -370,7 +373,7 @@ struct NullInstrParserCtx {
Result<> makeMemoryCopy(Index, MemoryIdxT*, MemoryIdxT*) { return Ok{}; }
Result<> makeMemoryFill(Index, MemoryIdxT*) { return Ok{}; }
-
+ Result<> makeBreak(Index, LabelIdxT) { return Ok{}; }
Result<> makeReturn(Index) { return Ok{}; }
template<typename HeapTypeT> Result<> makeRefNull(Index, HeapTypeT) {
return Ok{};
@@ -793,6 +796,7 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
using FieldIdxT = Index;
using LocalIdxT = Index;
+ using LabelIdxT = Index;
using GlobalIdxT = Name;
using MemoryIdxT = Name;
using DataIdxT = Name;
@@ -936,6 +940,12 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
return name;
}
+ Result<Index> getLabelFromIdx(uint32_t idx) { return idx; }
+
+ Result<Index> getLabelFromName(Name name) {
+ return irBuilder.getLabelIndex(name);
+ }
+
Result<TypeUseT> makeTypeUse(Index pos,
std::optional<HeapTypeT> type,
ParamsT* params,
@@ -1203,6 +1213,10 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
return withLoc(pos, irBuilder.makeMemoryFill(*m));
}
+ Result<> makeBreak(Index pos, Index label) {
+ return withLoc(pos, irBuilder.makeBreak(label));
+ }
+
Result<> makeReturn(Index pos) {
return withLoc(pos, irBuilder.makeReturn());
}
diff --git a/src/parser/parsers.h b/src/parser/parsers.h
index 38005253f..32f6709df 100644
--- a/src/parser/parsers.h
+++ b/src/parser/parsers.h
@@ -177,6 +177,7 @@ 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::TypeUseT> typeuse(Ctx&);
MaybeResult<ImportNames> inlineImport(ParseInput&);
Result<std::vector<Name>> inlineExports(ParseInput&);
@@ -1195,7 +1196,9 @@ Result<> makeCallIndirect(Ctx& ctx, Index pos, bool isReturn) {
}
template<typename Ctx> Result<> makeBreak(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
+ auto label = labelidx(ctx);
+ CHECK_ERR(label);
+ return ctx.makeBreak(pos, *label);
}
template<typename Ctx> Result<> makeBreakTable(Ctx& ctx, Index pos) {
@@ -1569,6 +1572,18 @@ template<typename Ctx> Result<typename Ctx::LocalIdxT> localidx(Ctx& ctx) {
return ctx.in.err("expected local index or identifier");
}
+// labelidx ::= x:u32 => x
+// | v:id => x (if labels[x] = v)
+template<typename Ctx> Result<typename Ctx::LabelIdxT> labelidx(Ctx& ctx) {
+ if (auto x = ctx.in.takeU32()) {
+ return ctx.getLabelFromIdx(*x);
+ }
+ if (auto id = ctx.in.takeID()) {
+ return ctx.getLabelFromName(*id);
+ }
+ return ctx.in.err("expected label index or identifier");
+}
+
// typeuse ::= '(' 'type' x:typeidx ')' => x, []
// (if typedefs[x] = [t1*] -> [t2*]
// | '(' 'type' x:typeidx ')' ((t1,IDs):param)* (t2:result)* => x, IDs
diff --git a/src/wasm-ir-builder.h b/src/wasm-ir-builder.h
index 2da69fff7..b42d40892 100644
--- a/src/wasm-ir-builder.h
+++ b/src/wasm-ir-builder.h
@@ -19,6 +19,7 @@
#include <vector>
+#include "ir/names.h"
#include "support/result.h"
#include "wasm-builder.h"
#include "wasm-traversal.h"
@@ -58,14 +59,20 @@ public:
[[nodiscard]] Result<> visitLoopStart(Loop* iff);
[[nodiscard]] Result<> visitEnd();
- // Alternatively, call makeXYZ to have the IRBuilder allocate the nodes. This
- // is generally safer than calling `visit` because the function signatures
- // ensure that there are no missing fields.
+ // Binaryen IR uses names to refer to branch targets, but in general there may
+ // be branches to constructs that do not yet have names, so in IRBuilder we
+ // use indices to refer to branch targets instead, just as the binary format
+ // does. This function converts a branch target name to the correct index.
+ [[nodiscard]] Result<Index> getLabelIndex(Name label);
+
+ // Instead of calling visit, call makeXYZ to have the IRBuilder allocate the
+ // nodes. This is generally safer than calling `visit` because the function
+ // signatures ensure that there are no missing fields.
[[nodiscard]] Result<> makeNop();
[[nodiscard]] Result<> makeBlock(Name label, Type type);
[[nodiscard]] Result<> makeIf(Name label, Type type);
[[nodiscard]] Result<> makeLoop(Name label, Type type);
- // [[nodiscard]] Result<> makeBreak();
+ [[nodiscard]] Result<> makeBreak(Index label);
// [[nodiscard]] Result<> makeSwitch();
// [[nodiscard]] Result<> makeCall();
// [[nodiscard]] Result<> makeCallIndirect();
@@ -177,6 +184,8 @@ public:
[[nodiscard]] Result<> visitReturn(Return*);
[[nodiscard]] Result<> visitStructNew(StructNew*);
[[nodiscard]] Result<> visitArrayNew(ArrayNew*);
+ [[nodiscard]] Result<> visitBreak(Break*,
+ std::optional<Index> label = std::nullopt);
private:
Module& wasm;
@@ -196,11 +205,11 @@ private:
};
struct IfScope {
If* iff;
- Name label;
+ Name originalLabel;
};
struct ElseScope {
If* iff;
- Name label;
+ Name originalLabel;
};
struct LoopScope {
Loop* loop;
@@ -211,6 +220,9 @@ private:
// The control flow structure we are building expressions for.
Scope scope;
+ // The branch label name for this scope. Always fresh, never shadowed.
+ Name label;
+
std::vector<Expression*> exprStack;
// Whether we have seen an unreachable instruction and are in
// stack-polymorphic unreachable mode.
@@ -218,6 +230,7 @@ private:
ScopeCtx() : scope(NoScope{}) {}
ScopeCtx(Scope scope) : scope(scope) {}
+ ScopeCtx(Scope scope, Name label) : scope(scope), label(label) {}
static ScopeCtx makeFunc(Function* func) {
return ScopeCtx(FuncScope{func});
@@ -225,11 +238,11 @@ private:
static ScopeCtx makeBlock(Block* block) {
return ScopeCtx(BlockScope{block});
}
- static ScopeCtx makeIf(If* iff, Name label = {}) {
- return ScopeCtx(IfScope{iff, label});
+ static ScopeCtx makeIf(If* iff, Name originalLabel = {}) {
+ return ScopeCtx(IfScope{iff, originalLabel});
}
- static ScopeCtx makeElse(If* iff, Name label = {}) {
- return ScopeCtx(ElseScope{iff, label});
+ static ScopeCtx makeElse(If* iff, Name originalLabel, Name label) {
+ return ScopeCtx(ElseScope{iff, originalLabel}, label);
}
static ScopeCtx makeLoop(Loop* loop) { return ScopeCtx(LoopScope{loop}); }
@@ -282,15 +295,18 @@ private:
}
WASM_UNREACHABLE("unexpected scope kind");
}
- Name getLabel() {
+ Name getOriginalLabel() {
+ if (getFunction()) {
+ return Name{};
+ }
if (auto* block = getBlock()) {
return block->name;
}
if (auto* ifScope = std::get_if<IfScope>(&scope)) {
- return ifScope->label;
+ return ifScope->originalLabel;
}
if (auto* elseScope = std::get_if<ElseScope>(&scope)) {
- return elseScope->label;
+ return elseScope->originalLabel;
}
if (auto* loop = getLoop()) {
return loop->name;
@@ -302,6 +318,29 @@ private:
// The stack of block contexts currently being parsed.
std::vector<ScopeCtx> scopeStack;
+ // Map label names to stacks of label depths at which they appear. The
+ // relative index of a label name is the current depth minus the top depth on
+ // its stack.
+ std::unordered_map<Name, std::vector<Index>> labelDepths;
+
+ Name makeFresh(Name label) {
+ return Names::getValidName(label, [&](Name candidate) {
+ return labelDepths.insert({candidate, {}}).second;
+ });
+ }
+
+ void pushScope(ScopeCtx scope) {
+ if (auto label = scope.getOriginalLabel()) {
+ // Assign a fresh label to the scope, if necessary.
+ if (!scope.label) {
+ scope.label = makeFresh(label);
+ }
+ // Record the original label to handle references to it correctly.
+ labelDepths[label].push_back(scopeStack.size() + 1);
+ }
+ scopeStack.push_back(scope);
+ }
+
ScopeCtx& getScope() {
if (scopeStack.empty()) {
// We are not in a block context, so push a dummy scope.
@@ -310,12 +349,24 @@ private:
return scopeStack.back();
}
+ Result<ScopeCtx*> getScope(Index label) {
+ Index numLabels = scopeStack.size();
+ if (!scopeStack.empty() && scopeStack[0].isNone()) {
+ --numLabels;
+ }
+ if (label >= numLabels) {
+ return Err{"label index out of bounds"};
+ }
+ return &scopeStack[scopeStack.size() - 1 - label];
+ }
+
// 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<Name> getLabelName(Index label);
[[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 4312fefe3..4d1c3353d 100644
--- a/src/wasm/wasm-ir-builder.cpp
+++ b/src/wasm/wasm-ir-builder.cpp
@@ -181,6 +181,7 @@ Result<Expression*> IRBuilder::build() {
assert(scopeStack.back().exprStack.size() == 1);
auto* expr = scopeStack.back().exprStack.back();
scopeStack.clear();
+ labelDepths.clear();
return expr;
}
@@ -206,6 +207,10 @@ Result<> IRBuilder::visitExpression(Expression* curr) {
auto field = pop(); \
CHECK_ERR(field); \
expr->field = *field;
+#define DELEGATE_FIELD_SCOPE_NAME_DEF(id, field) \
+ if (labelDepths.count(expr->field)) { \
+ return Err{"repeated label"}; \
+ }
#define DELEGATE_END(id)
#define DELEGATE_FIELD_OPTIONAL_CHILD(id, field) \
@@ -214,15 +219,18 @@ Result<> IRBuilder::visitExpression(Expression* curr) {
#define DELEGATE_FIELD_CHILD_VECTOR(id, field) \
WASM_UNREACHABLE("should have called visit" #id " because " #id \
" has child vector " #field);
+#define DELEGATE_FIELD_SCOPE_NAME_USE(id, field) \
+ WASM_UNREACHABLE("should have called visit" #id " because " #id \
+ " has scope name use " #field);
+#define DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(id, field) \
+ WASM_UNREACHABLE("should have called visit" #id " because " #id \
+ " has scope name use vector " #field);
#define DELEGATE_FIELD_INT(id, field)
#define DELEGATE_FIELD_INT_ARRAY(id, field)
#define DELEGATE_FIELD_LITERAL(id, field)
#define DELEGATE_FIELD_NAME(id, field)
#define DELEGATE_FIELD_NAME_VECTOR(id, field)
-#define DELEGATE_FIELD_SCOPE_NAME_DEF(id, field)
-#define DELEGATE_FIELD_SCOPE_NAME_USE(id, field)
-#define DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(id, field)
#define DELEGATE_FIELD_TYPE(id, field)
#define DELEGATE_FIELD_HEAPTYPE(id, field)
#define DELEGATE_FIELD_ADDRESS(id, field)
@@ -281,6 +289,30 @@ Result<> IRBuilder::visitArrayNew(ArrayNew* curr) {
return Ok{};
}
+Result<> IRBuilder::visitBreak(Break* curr, std::optional<Index> label) {
+ if (!label) {
+ auto index = getLabelIndex(curr->name);
+ CHECK_ERR(index);
+ label = *index;
+ }
+ auto scope = getScope(*label);
+ CHECK_ERR(scope);
+ std::vector<Expression*> values((*scope)->getResultType().size());
+ for (size_t i = 0, size = values.size(); i < size; ++i) {
+ auto val = pop();
+ CHECK_ERR(val);
+ values[size - 1 - i] = *val;
+ }
+ if (values.size() == 0) {
+ curr->value = nullptr;
+ } else if (values.size() == 1) {
+ curr->value = values[0];
+ } else {
+ curr->value = builder.makeTupleMake(values);
+ }
+ return Ok{};
+}
+
Result<> IRBuilder::visitFunctionStart(Function* func) {
if (!scopeStack.empty()) {
return Err{"unexpected start of function"};
@@ -291,7 +323,7 @@ Result<> IRBuilder::visitFunctionStart(Function* func) {
}
Result<> IRBuilder::visitBlockStart(Block* curr) {
- scopeStack.push_back(ScopeCtx::makeBlock(curr));
+ pushScope(ScopeCtx::makeBlock(curr));
return Ok{};
}
@@ -299,12 +331,12 @@ Result<> IRBuilder::visitIfStart(If* iff, Name label) {
auto cond = pop();
CHECK_ERR(cond);
iff->condition = *cond;
- scopeStack.push_back(ScopeCtx::makeIf(iff, label));
+ pushScope(ScopeCtx::makeIf(iff, label));
return Ok{};
}
Result<> IRBuilder::visitLoopStart(Loop* loop) {
- scopeStack.push_back(ScopeCtx::makeLoop(loop));
+ pushScope(ScopeCtx::makeLoop(loop));
return Ok{};
}
@@ -356,6 +388,7 @@ Result<Expression*> IRBuilder::finishScope(Block* block) {
auto hoisted = hoistLastValue();
CHECK_ERR(hoisted);
}
+
Expression* ret = nullptr;
if (scope.exprStack.size() == 0) {
// No expressions for this scope, but we need something. If we were given a
@@ -385,6 +418,12 @@ Result<Expression*> IRBuilder::finishScope(Block* block) {
}
ret = block;
}
+
+ // If this scope had a label, remove it from the context.
+ if (auto label = scope.getOriginalLabel()) {
+ labelDepths.at(label).pop_back();
+ }
+
scopeStack.pop_back();
return ret;
}
@@ -395,11 +434,12 @@ Result<> IRBuilder::visitElse() {
if (!iff) {
return Err{"unexpected else"};
}
- auto label = scope.getLabel();
+ auto originalLabel = scope.getOriginalLabel();
+ auto label = scope.label;
auto expr = finishScope();
CHECK_ERR(expr);
iff->ifTrue = *expr;
- scopeStack.push_back(ScopeCtx::makeElse(iff, label));
+ pushScope(ScopeCtx::makeElse(iff, originalLabel, label));
return Ok{};
}
@@ -414,22 +454,25 @@ Result<> IRBuilder::visitEnd() {
// If the scope expression cannot be directly labeled, we may need to wrap it
// in a block.
auto maybeWrapForLabel = [&](Expression* curr) -> Expression* {
- if (auto label = scope.getLabel()) {
- return builder.makeBlock(label, {curr}, curr->type);
+ if (scope.label) {
+ return builder.makeBlock(scope.label, {curr}, scope.getResultType());
}
return curr;
};
if (auto* func = scope.getFunction()) {
- func->body = *expr;
+ func->body = maybeWrapForLabel(*expr);
+ labelDepths.clear();
} else if (auto* block = scope.getBlock()) {
assert(*expr == block);
+ block->name = scope.label;
// TODO: Track branches so we can know whether this block is a target and
// finalize more efficiently.
block->finalize(block->type);
push(block);
} else if (auto* loop = scope.getLoop()) {
loop->body = *expr;
+ loop->name = scope.label;
loop->finalize(loop->type);
push(loop);
} else if (auto* iff = scope.getIf()) {
@@ -447,6 +490,25 @@ Result<> IRBuilder::visitEnd() {
return Ok{};
}
+Result<Index> IRBuilder::getLabelIndex(Name label) {
+ auto it = labelDepths.find(label);
+ if (it == labelDepths.end() || it->second.empty()) {
+ return Err{"unexpected label '"s + label.toString()};
+ }
+ return scopeStack.size() - it->second.back();
+}
+
+Result<Name> IRBuilder::getLabelName(Index label) {
+ auto scope = getScope(label);
+ CHECK_ERR(scope);
+ auto& scopeLabel = (*scope)->label;
+ if (!scopeLabel) {
+ // The scope does not already have a name, so we need to create one.
+ scopeLabel = makeFresh("label");
+ }
+ return scopeLabel;
+}
+
Result<> IRBuilder::makeNop() {
push(builder.makeNop());
return Ok{};
@@ -472,7 +534,15 @@ Result<> IRBuilder::makeLoop(Name label, Type type) {
return visitLoopStart(loop);
}
-// Result<> IRBuilder::makeBreak() {}
+Result<> IRBuilder::makeBreak(Index label) {
+ auto name = getLabelName(label);
+ CHECK_ERR(name);
+ Break curr;
+ curr.name = *name;
+ CHECK_ERR(visitBreak(&curr, label));
+ push(builder.makeBreak(curr.name, curr.value));
+ return Ok{};
+}
// Result<> IRBuilder::makeSwitch() {}
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index a8bb4a536..337065b91 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -651,8 +651,11 @@ void FunctionValidator::visitBlock(Block* curr) {
auto iter = breakTypes.find(curr->name);
assert(iter != breakTypes.end()); // we set it ourselves
for (Type breakType : iter->second) {
- // none or unreachable means a poison value that we should ignore - if
- // consumed, it will error
+ if (breakType == Type::none && curr->type == Type::unreachable) {
+ // We allow empty breaks to unreachable blocks.
+ continue;
+ }
+
shouldBeSubType(breakType,
curr->type,
curr,
diff --git a/test/lit/wat-kitchen-sink.wast b/test/lit/wat-kitchen-sink.wast
index a3a7a6c66..8ef4f8d07 100644
--- a/test/lit/wat-kitchen-sink.wast
+++ b/test/lit/wat-kitchen-sink.wast
@@ -21,11 +21,13 @@
;; CHECK: (type $a2 (array (mut f32)))
- ;; CHECK: (type $7 (func (param i32)))
+ ;; CHECK: (type $7 (func (result i32 i64)))
- ;; CHECK: (type $8 (func (param i32 i32 i32)))
+ ;; CHECK: (type $8 (func (param i32)))
- ;; CHECK: (type $9 (func (param v128 i32) (result v128)))
+ ;; CHECK: (type $9 (func (param i32 i32 i32)))
+
+ ;; CHECK: (type $10 (func (param v128 i32) (result v128)))
;; CHECK: (type $packed-i8 (array (mut i8)))
@@ -33,31 +35,29 @@
;; CHECK: (type $many (sub (func (param i32 i64 f32 f64) (result anyref (ref func)))))
- ;; CHECK: (type $13 (func (param i32 i32)))
-
- ;; CHECK: (type $14 (func (param i32 i32 f64 f64)))
+ ;; CHECK: (type $14 (func (param i32 i32)))
- ;; CHECK: (type $15 (func (param i64)))
+ ;; CHECK: (type $15 (func (param i32 i32 f64 f64)))
- ;; CHECK: (type $16 (func (param v128) (result i32)))
+ ;; CHECK: (type $16 (func (param i64)))
- ;; CHECK: (type $17 (func (param v128 v128) (result v128)))
+ ;; CHECK: (type $17 (func (param v128) (result i32)))
- ;; CHECK: (type $18 (func (param v128 v128 v128) (result v128)))
+ ;; CHECK: (type $18 (func (param v128 v128) (result v128)))
- ;; CHECK: (type $19 (func (param i32 i64 v128)))
+ ;; CHECK: (type $19 (func (param v128 v128 v128) (result v128)))
- ;; CHECK: (type $20 (func (param i32 i32 i64 i64)))
+ ;; CHECK: (type $20 (func (param i32 i64 v128)))
- ;; CHECK: (type $21 (func (param i32) (result i32)))
+ ;; CHECK: (type $21 (func (param i32 i32 i64 i64)))
- ;; CHECK: (type $22 (func (param i32 i64) (result i32 i64)))
+ ;; CHECK: (type $22 (func (param i32) (result i32)))
- ;; CHECK: (type $23 (func (param i64) (result i32 i64)))
+ ;; CHECK: (type $23 (func (param i32 i64) (result i32 i64)))
- ;; CHECK: (type $24 (func (param i32) (result i32 i64)))
+ ;; CHECK: (type $24 (func (param i64) (result i32 i64)))
- ;; CHECK: (type $25 (func (result i32 i64)))
+ ;; CHECK: (type $25 (func (param i32) (result i32 i64)))
;; CHECK: (type $26 (func (param anyref) (result i32)))
@@ -228,11 +228,11 @@
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
- ;; CHECK: (func $f1 (type $7) (param $0 i32)
+ ;; CHECK: (func $f1 (type $8) (param $0 i32)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $f1 (param i32))
- ;; CHECK: (func $f2 (type $7) (param $x i32)
+ ;; CHECK: (func $f2 (type $8) (param $x i32)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $f2 (param $x i32))
@@ -676,7 +676,7 @@
drop
)
- ;; CHECK: (func $locals (type $13) (param $0 i32) (param $x i32)
+ ;; CHECK: (func $locals (type $14) (param $0 i32) (param $x i32)
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (local $y i32)
;; CHECK-NEXT: (drop
@@ -1425,7 +1425,185 @@
)
)
- ;; CHECK: (func $binary (type $14) (param $0 i32) (param $1 i32) (param $2 f64) (param $3 f64)
+ ;; CHECK: (func $label-siblings (type $void)
+ ;; CHECK-NEXT: (block $l
+ ;; CHECK-NEXT: (br $l)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (loop $l_0
+ ;; CHECK-NEXT: (br $l_0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block $l_1
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (br $l_1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $label-siblings
+ block $l
+ br $l
+ end
+ loop $l
+ br $l
+ end
+ i32.const 0
+ if $l
+ br $l
+ end
+ )
+
+ ;; CHECK: (func $label-shadowed (type $void)
+ ;; CHECK-NEXT: (block $l
+ ;; CHECK-NEXT: (br $l)
+ ;; CHECK-NEXT: (loop $l_0
+ ;; CHECK-NEXT: (br $l_0)
+ ;; CHECK-NEXT: (block $l_1
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (br $l_1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $l_0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $l)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $label-shadowed
+ block $l
+ br $l
+ loop $l
+ br $l
+ i32.const 0
+ if $l
+ br $l
+ end
+ br $l
+ end
+ br $l
+ end
+ )
+
+ ;; CHECK: (func $label-index (type $void)
+ ;; CHECK-NEXT: (block $label_1
+ ;; CHECK-NEXT: (block $label
+ ;; CHECK-NEXT: (block $label_0
+ ;; CHECK-NEXT: (block $l
+ ;; CHECK-NEXT: (br $label)
+ ;; CHECK-NEXT: (br $label_0)
+ ;; CHECK-NEXT: (br $l)
+ ;; CHECK-NEXT: (br $label_1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $label-index
+ block
+ block
+ block
+ block $l
+ br 2
+ br 1
+ br 0
+ br 3
+ end
+ end
+ end
+ end
+ )
+
+ ;; CHECK: (func $label-func (type $void)
+ ;; CHECK-NEXT: (block $label
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (br $label)
+ ;; CHECK-NEXT: (block $a
+ ;; CHECK-NEXT: (br $label)
+ ;; CHECK-NEXT: (block $b
+ ;; CHECK-NEXT: (br $label)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $label-func
+ br 0
+ block $a
+ br 1
+ block $b
+ br 2
+ end
+ end
+ )
+
+ ;; CHECK: (func $br-value (type $1) (result i32)
+ ;; CHECK-NEXT: (block $label (result i32)
+ ;; CHECK-NEXT: (br $label
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $br-value (result i32)
+ i32.const 0
+ br 0
+ )
+
+ ;; CHECK: (func $br-value-drop (type $1) (result i32)
+ ;; CHECK-NEXT: (block $label (result i32)
+ ;; CHECK-NEXT: (block (result i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (f32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $label
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $br-value-drop (result i32)
+ f32.const 0
+ i32.const 1
+ br 0
+ )
+
+ ;; CHECK: (func $br-multivalue (type $7) (result i32 i64)
+ ;; CHECK-NEXT: (block $label (result i32 i64)
+ ;; CHECK-NEXT: (br $label
+ ;; CHECK-NEXT: (tuple.make
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (i64.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $br-multivalue (result i32 i64)
+ i32.const 0
+ i64.const 1
+ br 0
+ )
+
+ ;; CHECK: (func $br-multivalue-drop (type $7) (result i32 i64)
+ ;; CHECK-NEXT: (block $label (result i32 i64)
+ ;; CHECK-NEXT: (block (result i32 i64)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (f32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $label
+ ;; CHECK-NEXT: (tuple.make
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: (i64.const 2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $br-multivalue-drop (result i32 i64)
+ f32.const 0
+ i32.const 1
+ i64.const 2
+ br 0
+ )
+
+ ;; CHECK: (func $binary (type $15) (param $0 i32) (param $1 i32) (param $2 f64) (param $3 f64)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $0)
@@ -1450,7 +1628,7 @@
drop
)
- ;; CHECK: (func $unary (type $15) (param $0 i64)
+ ;; CHECK: (func $unary (type $16) (param $0 i64)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i64.eqz
;; CHECK-NEXT: (local.get $0)
@@ -1463,7 +1641,7 @@
drop
)
- ;; CHECK: (func $select (type $8) (param $0 i32) (param $1 i32) (param $2 i32)
+ ;; CHECK: (func $select (type $9) (param $0 i32) (param $1 i32) (param $2 i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select
;; CHECK-NEXT: (local.get $0)
@@ -1745,7 +1923,7 @@
atomic.fence
)
- ;; CHECK: (func $simd-extract (type $16) (param $0 v128) (result i32)
+ ;; CHECK: (func $simd-extract (type $17) (param $0 v128) (result i32)
;; CHECK-NEXT: (i32x4.extract_lane 3
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
@@ -1755,7 +1933,7 @@
i32x4.extract_lane 3
)
- ;; CHECK: (func $simd-replace (type $9) (param $0 v128) (param $1 i32) (result v128)
+ ;; CHECK: (func $simd-replace (type $10) (param $0 v128) (param $1 i32) (result v128)
;; CHECK-NEXT: (i32x4.replace_lane 2
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: (local.get $1)
@@ -1767,7 +1945,7 @@
i32x4.replace_lane 2
)
- ;; CHECK: (func $simd-shuffle (type $17) (param $0 v128) (param $1 v128) (result v128)
+ ;; CHECK: (func $simd-shuffle (type $18) (param $0 v128) (param $1 v128) (result v128)
;; CHECK-NEXT: (i8x16.shuffle 0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: (local.get $1)
@@ -1779,7 +1957,7 @@
i8x16.shuffle 0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23
)
- ;; CHECK: (func $simd-ternary (type $18) (param $0 v128) (param $1 v128) (param $2 v128) (result v128)
+ ;; CHECK: (func $simd-ternary (type $19) (param $0 v128) (param $1 v128) (param $2 v128) (result v128)
;; CHECK-NEXT: (v128.bitselect
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: (local.get $1)
@@ -1793,7 +1971,7 @@
v128.bitselect
)
- ;; CHECK: (func $simd-shift (type $9) (param $0 v128) (param $1 i32) (result v128)
+ ;; CHECK: (func $simd-shift (type $10) (param $0 v128) (param $1 i32) (result v128)
;; CHECK-NEXT: (i8x16.shl
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: (local.get $1)
@@ -1826,7 +2004,7 @@
drop
)
- ;; CHECK: (func $simd-load-store-lane (type $19) (param $0 i32) (param $1 i64) (param $2 v128)
+ ;; CHECK: (func $simd-load-store-lane (type $20) (param $0 i32) (param $1 i64) (param $2 v128)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (v128.load16_lane $mem 7
;; CHECK-NEXT: (local.get $0)
@@ -1848,7 +2026,7 @@
v128.store64_lane 3 align=4 0
)
- ;; CHECK: (func $memory-init (type $8) (param $0 i32) (param $1 i32) (param $2 i32)
+ ;; CHECK: (func $memory-init (type $9) (param $0 i32) (param $1 i32) (param $2 i32)
;; CHECK-NEXT: (memory.init $mem-i32 $passive
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: (local.get $1)
@@ -1889,7 +2067,7 @@
data.drop $passive
)
- ;; CHECK: (func $memory-copy (type $20) (param $0 i32) (param $1 i32) (param $2 i64) (param $3 i64)
+ ;; CHECK: (func $memory-copy (type $21) (param $0 i32) (param $1 i32) (param $2 i64) (param $3 i64)
;; CHECK-NEXT: (memory.copy $mem $mem
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: (local.get $1)
@@ -1960,7 +2138,7 @@
return
)
- ;; CHECK: (func $return-one (type $21) (param $0 i32) (result i32)
+ ;; CHECK: (func $return-one (type $22) (param $0 i32) (result i32)
;; CHECK-NEXT: (return
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
@@ -1970,7 +2148,7 @@
return
)
- ;; CHECK: (func $return-two (type $22) (param $0 i32) (param $1 i64) (result i32 i64)
+ ;; CHECK: (func $return-two (type $23) (param $0 i32) (param $1 i64) (result i32 i64)
;; CHECK-NEXT: (return
;; CHECK-NEXT: (tuple.make
;; CHECK-NEXT: (local.get $0)
@@ -1984,7 +2162,7 @@
return
)
- ;; CHECK: (func $return-two-first-unreachable (type $23) (param $0 i64) (result i32 i64)
+ ;; CHECK: (func $return-two-first-unreachable (type $24) (param $0 i64) (result i32 i64)
;; CHECK-NEXT: (return
;; CHECK-NEXT: (tuple.make
;; CHECK-NEXT: (unreachable)
@@ -1998,7 +2176,7 @@
return
)
- ;; CHECK: (func $return-two-second-unreachable (type $24) (param $0 i32) (result i32 i64)
+ ;; CHECK: (func $return-two-second-unreachable (type $25) (param $0 i32) (result i32 i64)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )