Commit 06061b7d authored by Andreas Rossberg's avatar Andreas Rossberg Committed by Commit Bot

[wasm] Support block parameters

This adds support for parameters on block, loop, if, cf the multi-value proposal at:
https://github.com/WebAssembly/multi-value/blob/master/proposals/multi-value/Overview.md

With this CL, we ssucceed on all tests in:
https://github.com/WebAssembly/multi-value/pull/2
except those involving multiple returns from functions.

R=titzer@chromium.org

Change-Id: I14a33e86450148f6aed2b8b8cc6bebb2303625c6
Reviewed-on: https://chromium-review.googlesource.com/712578
Commit-Queue: Andreas Rossberg <rossberg@chromium.org>
Reviewed-by: 's avatarBen Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48871}
parent d75ecf13
......@@ -191,7 +191,7 @@ class LiftoffCompiler {
}
void FallThruTo(Decoder* decoder, Control* c) {
if (c->merge.reached) {
if (c->end_merge.reached) {
__ MergeFullStackWith(c->label_state);
} else {
c->label_state.Split(*__ cache_state());
......@@ -199,7 +199,7 @@ class LiftoffCompiler {
}
void PopControl(Decoder* decoder, Control* c) {
if (!c->is_loop() && c->merge.reached) {
if (!c->is_loop() && c->end_merge.reached) {
__ cache_state()->Steal(c->label_state);
}
if (!c->label->is_bound()) {
......@@ -383,12 +383,12 @@ class LiftoffCompiler {
unsupported(decoder, "select");
}
void BreakTo(Decoder* decoder, Control* target) {
if (!target->merge.reached) {
void Br(Decoder* decoder, Control* target) {
if (!target->br_merge()->reached) {
target->label_state.InitMerge(*__ cache_state(), __ num_locals(),
target->break_arity());
target->br_merge()->arity);
}
__ MergeStackWith(target->label_state, target->break_arity());
__ MergeStackWith(target->label_state, target->br_merge()->arity);
__ jmp(target->label.get());
}
......@@ -397,7 +397,7 @@ class LiftoffCompiler {
Register value = __ PopToRegister(kWasmI32);
__ JumpIfZero(value, &cont_false);
BreakTo(decoder, target);
Br(decoder, target);
__ bind(&cont_false);
}
......
This diff is collapsed.
......@@ -27,11 +27,6 @@ namespace wasm {
namespace {
template <typename T>
Vector<T> vec2vec(ZoneVector<T>& vec) {
return Vector<T>(vec.data(), vec.size());
}
// An SsaEnv environment carries the current local variable renaming
// as well as the current effect and control dependency in the TF graph.
// It maintains a control state that tracks whether the environment
......@@ -173,6 +168,12 @@ class WasmGraphBuildingInterface {
// The continue environment is the inner environment.
SetEnv(PrepareForLoop(decoder, finish_try_env));
ssa_env_->SetNotMerged();
if (!decoder->ok()) return;
// Wrap input merge into phis.
for (unsigned i = 0; i < block->start_merge.arity; ++i) {
Value& val = block->start_merge[i];
val.node = builder_->Phi(val.type, 1, &val.node, block->end_env->control);
}
}
void Try(Decoder* decoder, Control* block) {
......@@ -206,14 +207,11 @@ class WasmGraphBuildingInterface {
void FallThruTo(Decoder* decoder, Control* c) {
DCHECK(!c->is_loop());
MergeValuesInto(decoder, c);
MergeValuesInto(decoder, c, &c->end_merge);
}
void PopControl(Decoder* decoder, Control* block) {
if (!block->is_loop()) SetEnv(block->end_env);
if (block->is_onearmed_if()) {
Goto(decoder, block->false_env, block->end_env);
}
}
void EndControl(Decoder* decoder, Control* block) { ssa_env_->Kill(); }
......@@ -304,12 +302,8 @@ class WasmGraphBuildingInterface {
ssa_env_->control = merge;
}
void BreakTo(Decoder* decoder, Control* target) {
if (target->is_loop()) {
Goto(decoder, ssa_env_, target->end_env);
} else {
MergeValuesInto(decoder, target);
}
void Br(Decoder* decoder, Control* target) {
MergeValuesInto(decoder, target, target->br_merge());
}
void BrIf(Decoder* decoder, const Value& cond, Control* target) {
......@@ -318,7 +312,7 @@ class WasmGraphBuildingInterface {
fenv->SetNotMerged();
BUILD(BranchNoHint, cond.node, &tenv->control, &fenv->control);
ssa_env_ = tenv;
BreakTo(decoder, target);
Br(decoder, target);
ssa_env_ = fenv;
}
......@@ -327,7 +321,7 @@ class WasmGraphBuildingInterface {
if (operand.table_count == 0) {
// Only a default target. Do the equivalent of br.
uint32_t target = BranchTableIterator<validate>(decoder, operand).next();
BreakTo(decoder, decoder->control_at(target));
Br(decoder, decoder->control_at(target));
return;
}
......@@ -344,7 +338,7 @@ class WasmGraphBuildingInterface {
ssa_env_ = Split(decoder, copy);
ssa_env_->control = (i == operand.table_count) ? BUILD(IfDefault, sw)
: BUILD(IfValue, i, sw);
BreakTo(decoder, decoder->control_at(target));
Br(decoder, decoder->control_at(target));
}
DCHECK(decoder->ok());
ssa_env_ = break_env;
......@@ -618,7 +612,8 @@ class WasmGraphBuildingInterface {
}
}
void MergeValuesInto(Decoder* decoder, Control* c) {
void MergeValuesInto(Decoder* decoder, Control* c, Merge<Value>* merge) {
DCHECK(merge == &c->start_merge || merge == &c->end_merge);
if (!ssa_env_->go()) return;
SsaEnv* target = c->end_env;
......@@ -627,10 +622,10 @@ class WasmGraphBuildingInterface {
uint32_t avail =
decoder->stack_size() - decoder->control_at(0)->stack_depth;
uint32_t start = avail >= c->merge.arity ? 0 : c->merge.arity - avail;
for (uint32_t i = start; i < c->merge.arity; ++i) {
auto& val = decoder->GetMergeValueFromStack(c, i);
auto& old = c->merge[i];
uint32_t start = avail >= merge->arity ? 0 : merge->arity - avail;
for (uint32_t i = start; i < merge->arity; ++i) {
auto& val = decoder->GetMergeValueFromStack(c, merge, i);
auto& old = (*merge)[i];
DCHECK_NOT_NULL(val.node);
DCHECK(val.type == old.type || val.type == kWasmVar);
old.node = first ? val.node
......
......@@ -120,7 +120,7 @@ WASM_EXEC_TEST(Int32Add_P2) {
WASM_EXEC_TEST(Int32Add_block1) {
EXPERIMENTAL_FLAG_SCOPE(mv);
static const byte code[] = {
WASM_BLOCK_TT(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)),
WASM_BLOCK_X(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)),
kExprI32Add};
RunInt32AddTest(execution_mode, code, sizeof(code));
}
......@@ -128,7 +128,7 @@ WASM_EXEC_TEST(Int32Add_block1) {
WASM_EXEC_TEST(Int32Add_block2) {
EXPERIMENTAL_FLAG_SCOPE(mv);
static const byte code[] = {
WASM_BLOCK_TT(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), kExprBr, DEPTH_0),
WASM_BLOCK_X(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), kExprBr, DEPTH_0),
kExprI32Add};
RunInt32AddTest(execution_mode, code, sizeof(code));
}
......@@ -136,9 +136,9 @@ WASM_EXEC_TEST(Int32Add_block2) {
WASM_EXEC_TEST(Int32Add_multi_if) {
EXPERIMENTAL_FLAG_SCOPE(mv);
static const byte code[] = {
WASM_IF_ELSE_TT(0, WASM_GET_LOCAL(0),
WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)),
WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))),
WASM_IF_ELSE_X(0, WASM_GET_LOCAL(0),
WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)),
WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))),
kExprI32Add};
RunInt32AddTest(execution_mode, code, sizeof(code));
}
......
......@@ -79,7 +79,7 @@
kExprBlock, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), \
__VA_ARGS__, kExprEnd
#define WASM_BLOCK_TT(index, ...) \
#define WASM_BLOCK_X(index, ...) \
kExprBlock, static_cast<byte>(index), __VA_ARGS__, kExprEnd
#define WASM_INFINITE_LOOP kExprLoop, kLocalVoid, kExprBr, DEPTH_0, kExprEnd
......@@ -94,20 +94,20 @@
kExprLoop, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), \
__VA_ARGS__, kExprEnd
#define WASM_LOOP_TT(index, ...) \
#define WASM_LOOP_X(index, ...) \
kExprLoop, static_cast<byte>(index), __VA_ARGS__, kExprEnd
#define WASM_IF(cond, tstmt) cond, kExprIf, kLocalVoid, tstmt, kExprEnd
#define WASM_IF(cond, ...) cond, kExprIf, kLocalVoid, __VA_ARGS__, kExprEnd
#define WASM_IF_ELSE(cond, tstmt, fstmt) \
cond, kExprIf, kLocalVoid, tstmt, kExprElse, fstmt, kExprEnd
#define WASM_IF_T(t, cond, ...) \
cond, kExprIf, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), \
__VA_ARGS__, kExprEnd
#define WASM_IF_ELSE_T(t, cond, tstmt, fstmt) \
cond, kExprIf, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), tstmt, \
kExprElse, fstmt, kExprEnd
#define WASM_IF_X(index, cond, ...) \
cond, kExprIf, static_cast<byte>(index), __VA_ARGS__, kExprEnd
#define WASM_IF_ELSE_TT(index, cond, tstmt, fstmt) \
cond, kExprIf, static_cast<byte>(index), tstmt, kExprElse, fstmt, kExprEnd
#define WASM_IF_ELSE(cond, tstmt, fstmt) \
cond, kExprIf, kLocalVoid, tstmt, kExprElse, fstmt, kExprEnd
#define WASM_IF_ELSE_I(cond, tstmt, fstmt) \
cond, kExprIf, kLocalI32, tstmt, kExprElse, fstmt, kExprEnd
......@@ -118,6 +118,13 @@
#define WASM_IF_ELSE_D(cond, tstmt, fstmt) \
cond, kExprIf, kLocalF64, tstmt, kExprElse, fstmt, kExprEnd
#define WASM_IF_ELSE_T(t, cond, tstmt, fstmt) \
cond, kExprIf, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), tstmt, \
kExprElse, fstmt, kExprEnd
#define WASM_IF_ELSE_X(index, cond, tstmt, fstmt) \
cond, kExprIf, static_cast<byte>(index), tstmt, kExprElse, fstmt, kExprEnd
#define WASM_SELECT(tval, fval, cond) tval, fval, cond, kExprSelect
#define WASM_RETURN0 kExprReturn
......@@ -574,7 +581,7 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define SIZEOF_SIG_ENTRY_x_xx 6
#define SIZEOF_SIG_ENTRY_x_xxx 7
#define WASM_BRV(depth, val) val, kExprBr, static_cast<byte>(depth)
#define WASM_BRV(depth, ...) __VA_ARGS__, kExprBr, static_cast<byte>(depth)
#define WASM_BRV_IF(depth, val, cond) \
val, cond, kExprBrIf, static_cast<byte>(depth)
#define WASM_BRV_IFD(depth, val, cond) \
......
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --experimental-wasm-mv
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
(function MultiBlockResultTest() {
print("MultiBlockResultTest");
let builder = new WasmModuleBuilder();
let sig_i_ii = builder.addType(kSig_i_ii);
let sig_ii_v = builder.addType(kSig_ii_v);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprBlock, sig_ii_v,
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprEnd,
kExprI32Add])
.exportAs("main");
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module);
assertEquals(instance.exports.main(1, 4), 5);
})();
(function MultiBlockParamTest() {
print("MultiBlockParamTest");
let builder = new WasmModuleBuilder();
let sig_i_ii = builder.addType(kSig_i_ii);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprBlock, sig_i_ii,
kExprI32Add,
kExprEnd])
.exportAs("main");
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module);
assertEquals(instance.exports.main(1, 4), 5);
})();
(function MultiBlockBrTest() {
print("MultiBlockBrTest");
let builder = new WasmModuleBuilder();
let sig_i_ii = builder.addType(kSig_i_ii);
let sig_ii_v = builder.addType(kSig_ii_v);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprBlock, sig_ii_v,
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprBr, 0,
kExprEnd,
kExprI32Add])
.exportAs("main");
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module);
assertEquals(instance.exports.main(1, 4), 5);
})();
(function MultiLoopResultTest() {
print("MultiLoopResultTest");
let builder = new WasmModuleBuilder();
let sig_i_ii = builder.addType(kSig_i_ii);
let sig_ii_v = builder.addType(kSig_ii_v);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprLoop, sig_ii_v,
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprEnd,
kExprI32Add])
.exportAs("main");
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module);
assertEquals(instance.exports.main(1, 4), 5);
})();
(function MultiLoopParamTest() {
print("MultiLoopParamTest");
let builder = new WasmModuleBuilder();
let sig_i_ii = builder.addType(kSig_i_ii);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprLoop, sig_i_ii,
kExprI32Add,
kExprEnd])
.exportAs("main");
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module);
assertEquals(instance.exports.main(1, 4), 5);
})();
(function MultiLoopBrTest() {
print("MultiLoopBrTest");
let builder = new WasmModuleBuilder();
let sig_i_ii = builder.addType(kSig_i_ii);
let sig_ii_i = builder.addType(kSig_ii_i);
let sig_ii_ii = builder.addType(kSig_ii_ii);
builder.addFunction("dup", kSig_ii_i)
.addBody([kExprGetLocal, 0, kExprGetLocal, 0]);
builder.addFunction("swap", kSig_ii_ii)
.addBody([kExprGetLocal, 1, kExprGetLocal, 0]);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprLoop, sig_ii_ii,
kExprCallFunction, 1, // swap
kExprCallFunction, 0, // dup
kExprI32Add,
kExprCallFunction, 0, // dup
kExprI32Const, 20,
kExprI32LeU,
kExprBrIf, 0,
kExprEnd,
kExprDrop])
.exportAs("main");
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module);
assertEquals(0, instance.exports.main(0, 1));
assertEquals(16, instance.exports.main(1, 1));
assertEquals(4, instance.exports.main(3, 1));
assertEquals(4, instance.exports.main(4, 1));
assertEquals(0, instance.exports.main(0, 2));
assertEquals(16, instance.exports.main(1, 2));
assertEquals(8, instance.exports.main(3, 2));
assertEquals(8, instance.exports.main(4, 2));
assertEquals(0, instance.exports.main(0, 3));
assertEquals(8, instance.exports.main(1, 3));
assertEquals(12, instance.exports.main(3, 3));
assertEquals(12, instance.exports.main(4, 3));
assertEquals(0, instance.exports.main(0, 4));
assertEquals(8, instance.exports.main(1, 4));
assertEquals(16, instance.exports.main(3, 4));
assertEquals(16, instance.exports.main(4, 4));
assertEquals(3, instance.exports.main(100, 3));
assertEquals(6, instance.exports.main(3, 100));
})();
(function MultiIfResultTest() {
print("MultiIfResultTest");
let builder = new WasmModuleBuilder();
let sig_i_ii = builder.addType(kSig_i_ii);
let sig_ii_v = builder.addType(kSig_ii_v);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprGetLocal, 0,
kExprIf, sig_ii_v,
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprElse,
kExprGetLocal, 1,
kExprGetLocal, 0,
kExprEnd,
kExprI32Sub])
.exportAs("main");
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module);
assertEquals(instance.exports.main(8, 3), 5);
assertEquals(instance.exports.main(0, 3), 3);
})();
(function MultiIfParamTest() {
print("MultiIfParamTest");
let builder = new WasmModuleBuilder();
let sig_i_ii = builder.addType(kSig_i_ii);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprGetLocal, 0,
kExprIf, sig_i_ii,
kExprI32Add,
kExprElse,
kExprI32Sub,
kExprEnd])
.exportAs("main");
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module);
assertEquals(instance.exports.main(1, 4), 5);
assertEquals(instance.exports.main(0, 4), -4);
})();
(function MultiIfBrTest() {
print("MultiIfBrTest");
let builder = new WasmModuleBuilder();
let sig_i_ii = builder.addType(kSig_i_ii);
let sig_ii_v = builder.addType(kSig_ii_v);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprGetLocal, 0,
kExprIf, sig_ii_v,
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprBr, 0,
kExprElse,
kExprGetLocal, 1,
kExprGetLocal, 0,
kExprBr, 0,
kExprEnd,
kExprI32Sub])
.exportAs("main");
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module);
assertEquals(instance.exports.main(8, 3), 5);
assertEquals(instance.exports.main(0, 3), 3);
})();
......@@ -123,6 +123,10 @@ let kSig_v_l = makeSig([kWasmI64], []);
let kSig_v_d = makeSig([kWasmF64], []);
let kSig_v_dd = makeSig([kWasmF64, kWasmF64], []);
let kSig_v_ddi = makeSig([kWasmF64, kWasmF64, kWasmI32], []);
let kSig_ii_v = makeSig([], [kWasmI32, kWasmI32]);
let kSig_iii_v = makeSig([], [kWasmI32, kWasmI32, kWasmI32]);
let kSig_ii_i = makeSig([kWasmI32], [kWasmI32, kWasmI32]);
let kSig_ii_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32, kWasmI32]);
let kSig_v_f = makeSig([kWasmF32], []);
let kSig_f_f = makeSig([kWasmF32], [kWasmF32]);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment