[wasm] ReturnCall Implementation (decoder).

Focuses on decoder implementation and unittests of decoding return call instructions

Bug: v8:7431
Change-Id: Ib1351bb26f8bac0a766d633486492fcd8ead627b
Reviewed-on: https://chromium-review.googlesource.com/c/1455476
Commit-Queue: Francis McCabe <fgm@chromium.org>
Reviewed-by: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59582}
This commit is contained in:
Francis McCabe 2019-02-13 14:54:45 -08:00 committed by Commit Bot
parent 6ee9ec5ca1
commit 0988e0d647
8 changed files with 263 additions and 19 deletions

View File

@ -1854,7 +1854,17 @@ class LiftoffCompiler {
__ FinishCall(imm.sig, call_descriptor);
}
void ReturnCall(FullDecoder* decoder,
const CallFunctionImmediate<validate>& imm,
const Value args[]) {
unsupported(decoder, "return_call");
}
void ReturnCallIndirect(FullDecoder* decoder, const Value& index_val,
const CallIndirectImmediate<validate>& imm,
const Value args[]) {
unsupported(decoder, "return_call_indirect");
}
void SimdOp(FullDecoder* decoder, WasmOpcode opcode, Vector<Value> args,
Value* result) {
unsupported(decoder, "simd");

View File

@ -697,6 +697,10 @@ struct ControlBase {
F(CallIndirect, const Value& index, \
const CallIndirectImmediate<validate>& imm, const Value args[], \
Value returns[]) \
F(ReturnCall, const CallFunctionImmediate<validate>& imm, \
const Value args[]) \
F(ReturnCallIndirect, const Value& index, \
const CallIndirectImmediate<validate>& imm, const Value args[]) \
F(SimdOp, WasmOpcode opcode, Vector<Value> args, Value* result) \
F(SimdLaneOp, WasmOpcode opcode, const SimdLaneImmediate<validate>& imm, \
const Vector<Value> inputs, Value* result) \
@ -864,6 +868,8 @@ class WasmDecoder : public Decoder {
case kExprMemoryGrow:
case kExprCallFunction:
case kExprCallIndirect:
case kExprReturnCall:
case kExprReturnCallIndirect:
// Add instance cache nodes to the assigned set.
// TODO(titzer): make this more clear.
assigned->Add(locals_count - 1);
@ -918,6 +924,16 @@ class WasmDecoder : public Decoder {
return true;
}
inline bool CanTailCall(FunctionSig* tgt_sig) {
if (tgt_sig == nullptr) return false;
size_t num_returns = sig_->return_count();
if (num_returns != tgt_sig->return_count()) return false;
for (size_t i = 0; i < num_returns; ++i) {
if (sig_->GetReturn(i) != tgt_sig->GetReturn(i)) return false;
}
return true;
}
inline bool Complete(const byte* pc, CallFunctionImmediate<validate>& imm) {
if (!VALIDATE(module_ != nullptr &&
imm.index < module_->functions.size())) {
@ -1164,11 +1180,13 @@ class WasmDecoder : public Decoder {
TableIndexImmediate<validate> imm(decoder, pc);
return 1 + imm.length;
}
case kExprCallFunction: {
case kExprCallFunction:
case kExprReturnCall: {
CallFunctionImmediate<validate> imm(decoder, pc);
return 1 + imm.length;
}
case kExprCallIndirect: {
case kExprCallIndirect:
case kExprReturnCallIndirect: {
CallIndirectImmediate<validate> imm(decoder, pc);
return 1 + imm.length;
}
@ -1390,6 +1408,8 @@ class WasmDecoder : public Decoder {
case kExprBrOnExn:
case kExprNop:
case kExprReturn:
case kExprReturnCall:
case kExprReturnCallIndirect:
case kExprUnreachable:
return {0, 0};
case kNumericPrefix:
@ -2150,6 +2170,39 @@ class WasmFullDecoder : public WasmDecoder<validate> {
returns);
break;
}
case kExprReturnCall: {
CHECK_PROTOTYPE_OPCODE(return_call);
CallFunctionImmediate<validate> imm(this, this->pc_);
len = 1 + imm.length;
if (!this->Validate(this->pc_, imm)) break;
if (!this->CanTailCall(imm.sig)) {
OPCODE_ERROR(opcode, "tail call return types mismatch");
break;
}
PopArgs(imm.sig);
CALL_INTERFACE_IF_REACHABLE(ReturnCall, imm, args_.data());
EndControl();
break;
}
case kExprReturnCallIndirect: {
CHECK_PROTOTYPE_OPCODE(return_call);
CallIndirectImmediate<validate> imm(this, this->pc_);
len = 1 + imm.length;
if (!this->Validate(this->pc_, imm)) break;
if (!this->CanTailCall(imm.sig)) {
OPCODE_ERROR(opcode, "tail call return types mismatch");
break;
}
auto index = Pop(0, kWasmI32);
PopArgs(imm.sig);
CALL_INTERFACE_IF_REACHABLE(ReturnCallIndirect, index, imm,
args_.data());
EndControl();
break;
}
case kNumericPrefix: {
++len;
byte numeric_index =

View File

@ -405,12 +405,24 @@ class WasmGraphBuildingInterface {
DoCall(decoder, nullptr, imm.sig, imm.index, args, returns);
}
void ReturnCall(FullDecoder* decoder,
const CallFunctionImmediate<validate>& imm,
const Value args[]) {
UNIMPLEMENTED();
}
void CallIndirect(FullDecoder* decoder, const Value& index,
const CallIndirectImmediate<validate>& imm,
const Value args[], Value returns[]) {
DoCall(decoder, index.node, imm.sig, imm.sig_index, args, returns);
}
void ReturnCallIndirect(FullDecoder* decoder, const Value& index,
const CallIndirectImmediate<validate>& imm,
const Value args[]) {
UNIMPLEMENTED();
}
void SimdOp(FullDecoder* decoder, WasmOpcode opcode, Vector<Value> args,
Value* result) {
TFNode** inputs = GetNodes(args);

View File

@ -23,6 +23,8 @@
SEPARATOR \
V(bigint, "JS BigInt support", false) \
SEPARATOR \
V(bulk_memory, "bulk memory opcodes", false)
V(bulk_memory, "bulk memory opcodes", false) \
SEPARATOR \
V(return_call, "return call opcodes", false)
#endif // V8_WASM_WASM_FEATURE_FLAGS_H_

View File

@ -137,6 +137,8 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_OP(Return, "return")
CASE_OP(CallFunction, "call")
CASE_OP(CallIndirect, "call_indirect")
CASE_OP(ReturnCall, "return_call")
CASE_OP(ReturnCallIndirect, "return_call_indirect")
CASE_OP(Drop, "drop")
CASE_OP(Select, "select")
CASE_OP(GetLocal, "get_local")

View File

@ -38,22 +38,24 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, bool hasBigIntFeature);
V(Return, 0x0f, _)
// Constants, locals, globals, and calls.
#define FOREACH_MISC_OPCODE(V) \
V(CallFunction, 0x10, _) \
V(CallIndirect, 0x11, _) \
V(Drop, 0x1a, _) \
V(Select, 0x1b, _) \
V(GetLocal, 0x20, _) \
V(SetLocal, 0x21, _) \
V(TeeLocal, 0x22, _) \
V(GetGlobal, 0x23, _) \
V(SetGlobal, 0x24, _) \
V(GetTable, 0x25, _) \
V(SetTable, 0x26, _) \
V(I32Const, 0x41, _) \
V(I64Const, 0x42, _) \
V(F32Const, 0x43, _) \
V(F64Const, 0x44, _) \
#define FOREACH_MISC_OPCODE(V) \
V(CallFunction, 0x10, _) \
V(CallIndirect, 0x11, _) \
V(ReturnCall, 0x12, _) \
V(ReturnCallIndirect, 0x13, _) \
V(Drop, 0x1a, _) \
V(Select, 0x1b, _) \
V(GetLocal, 0x20, _) \
V(SetLocal, 0x21, _) \
V(TeeLocal, 0x22, _) \
V(GetGlobal, 0x23, _) \
V(SetGlobal, 0x24, _) \
V(GetTable, 0x25, _) \
V(SetTable, 0x26, _) \
V(I32Const, 0x41, _) \
V(I64Const, 0x42, _) \
V(F32Const, 0x43, _) \
V(F64Const, 0x44, _) \
V(RefNull, 0xd0, _)
// Load memory expressions.

View File

@ -387,6 +387,11 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_CALL_FUNCTION(index, ...) \
__VA_ARGS__, kExprCallFunction, static_cast<byte>(index)
#define WASM_RETURN_CALL_FUNCTION0(index) \
kExprReturnCall, static_cast<byte>(index)
#define WASM_RETURN_CALL_FUNCTION(index, ...) \
__VA_ARGS__, kExprReturnCall, static_cast<byte>(index)
#define TABLE_ZERO 0
// TODO(titzer): change usages of these macros to put func last.
@ -405,6 +410,12 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_CALL_INDIRECTN(arity, index, func, ...) \
__VA_ARGS__, func, kExprCallIndirect, static_cast<byte>(index), TABLE_ZERO
#define WASM_RETURN_CALL_INDIRECT0(index, func) \
func, kExprReturnCallIndirect, static_cast<byte>(index), TABLE_ZERO
#define WASM_RETURN_CALL_INDIRECT(index, func, ...) \
__VA_ARGS__, func, kExprReturnCallIndirect, static_cast<byte>(index), \
TABLE_ZERO
#define WASM_NOT(x) x, kExprI32Eqz
#define WASM_SEQ(...) __VA_ARGS__

View File

@ -1568,6 +1568,158 @@ TEST_F(FunctionBodyDecoderTest, CallsWithMismatchedSigs3) {
ExpectFailure(sig, {WASM_CALL_FUNCTION(1, WASM_F32(17.6))});
}
TEST_F(FunctionBodyDecoderTest, SimpleReturnCalls) {
WASM_FEATURE_SCOPE(return_call);
FunctionSig* sig = sigs.i_i();
TestModuleBuilder builder;
module = builder.module();
builder.AddFunction(sigs.i_v());
builder.AddFunction(sigs.i_i());
builder.AddFunction(sigs.i_ii());
ExpectValidates(sig, {WASM_RETURN_CALL_FUNCTION0(0)});
ExpectValidates(sig, {WASM_RETURN_CALL_FUNCTION(1, WASM_I32V_1(27))});
ExpectValidates(
sig, {WASM_RETURN_CALL_FUNCTION(2, WASM_I32V_1(37), WASM_I32V_2(77))});
}
TEST_F(FunctionBodyDecoderTest, ReturnCallsWithTooFewArguments) {
WASM_FEATURE_SCOPE(return_call);
FunctionSig* sig = sigs.i_i();
TestModuleBuilder builder;
module = builder.module();
builder.AddFunction(sigs.i_i());
builder.AddFunction(sigs.i_ii());
builder.AddFunction(sigs.f_ff());
ExpectFailure(sig, {WASM_RETURN_CALL_FUNCTION0(0)});
ExpectFailure(sig, {WASM_RETURN_CALL_FUNCTION(1, WASM_ZERO)});
ExpectFailure(sig, {WASM_RETURN_CALL_FUNCTION(2, WASM_GET_LOCAL(0))});
}
TEST_F(FunctionBodyDecoderTest, ReturnCallsWithMismatchedSigs) {
WASM_FEATURE_SCOPE(return_call);
FunctionSig* sig = sigs.i_i();
TestModuleBuilder builder;
module = builder.module();
builder.AddFunction(sigs.i_f());
builder.AddFunction(sigs.f_f());
ExpectFailure(sig, {WASM_RETURN_CALL_FUNCTION(0, WASM_I32V_1(17))});
ExpectFailure(sig, {WASM_RETURN_CALL_FUNCTION(0, WASM_I64V_1(27))});
ExpectFailure(sig, {WASM_RETURN_CALL_FUNCTION(0, WASM_F64(37.2))});
ExpectFailure(sig, {WASM_RETURN_CALL_FUNCTION(1, WASM_F64(37.2))});
ExpectFailure(sig, {WASM_RETURN_CALL_FUNCTION(1, WASM_F32(37.2))});
ExpectFailure(sig, {WASM_RETURN_CALL_FUNCTION(1, WASM_I32V_1(17))});
}
TEST_F(FunctionBodyDecoderTest, SimpleIndirectReturnCalls) {
WASM_FEATURE_SCOPE(return_call);
FunctionSig* sig = sigs.i_i();
TestModuleBuilder builder;
builder.InitializeTable();
module = builder.module();
byte f0 = builder.AddSignature(sigs.i_v());
byte f1 = builder.AddSignature(sigs.i_i());
byte f2 = builder.AddSignature(sigs.i_ii());
ExpectValidates(sig, {WASM_RETURN_CALL_INDIRECT0(f0, WASM_ZERO)});
ExpectValidates(sig,
{WASM_RETURN_CALL_INDIRECT(f1, WASM_ZERO, WASM_I32V_1(22))});
ExpectValidates(sig, {WASM_RETURN_CALL_INDIRECT(
f2, WASM_ZERO, WASM_I32V_1(32), WASM_I32V_2(72))});
}
TEST_F(FunctionBodyDecoderTest, IndirectReturnCallsOutOfBounds) {
WASM_FEATURE_SCOPE(return_call);
FunctionSig* sig = sigs.i_i();
TestModuleBuilder builder;
builder.InitializeTable();
module = builder.module();
ExpectFailure(sig, {WASM_RETURN_CALL_INDIRECT0(0, WASM_ZERO)});
builder.AddSignature(sigs.i_v());
ExpectValidates(sig, {WASM_RETURN_CALL_INDIRECT0(0, WASM_ZERO)});
ExpectFailure(sig,
{WASM_RETURN_CALL_INDIRECT(1, WASM_ZERO, WASM_I32V_1(22))});
builder.AddSignature(sigs.i_i());
ExpectValidates(sig,
{WASM_RETURN_CALL_INDIRECT(1, WASM_ZERO, WASM_I32V_1(27))});
ExpectFailure(sig,
{WASM_RETURN_CALL_INDIRECT(2, WASM_ZERO, WASM_I32V_1(27))});
}
TEST_F(FunctionBodyDecoderTest, IndirectReturnCallsWithMismatchedSigs3) {
WASM_FEATURE_SCOPE(return_call);
FunctionSig* sig = sigs.i_i();
TestModuleBuilder builder;
builder.InitializeTable();
module = builder.module();
byte f0 = builder.AddFunction(sigs.i_f());
ExpectFailure(sig,
{WASM_RETURN_CALL_INDIRECT(f0, WASM_ZERO, WASM_I32V_1(17))});
ExpectFailure(sig,
{WASM_RETURN_CALL_INDIRECT(f0, WASM_ZERO, WASM_I64V_1(27))});
ExpectFailure(sig,
{WASM_RETURN_CALL_INDIRECT(f0, WASM_ZERO, WASM_F64(37.2))});
ExpectFailure(sig, {WASM_RETURN_CALL_INDIRECT0(f0, WASM_I32V_1(17))});
ExpectFailure(sig, {WASM_RETURN_CALL_INDIRECT0(f0, WASM_I64V_1(27))});
ExpectFailure(sig, {WASM_RETURN_CALL_INDIRECT0(f0, WASM_F64(37.2))});
byte f1 = builder.AddFunction(sigs.i_d());
ExpectFailure(sig,
{WASM_RETURN_CALL_INDIRECT(f1, WASM_ZERO, WASM_I32V_1(16))});
ExpectFailure(sig,
{WASM_RETURN_CALL_INDIRECT(f1, WASM_ZERO, WASM_I64V_1(16))});
ExpectFailure(sig,
{WASM_RETURN_CALL_INDIRECT(f1, WASM_ZERO, WASM_F32(17.6))});
}
TEST_F(FunctionBodyDecoderTest, IndirectReturnCallsWithoutTableCrash) {
WASM_FEATURE_SCOPE(return_call);
FunctionSig* sig = sigs.i_i();
TestModuleBuilder builder;
module = builder.module();
byte f0 = builder.AddSignature(sigs.i_v());
byte f1 = builder.AddSignature(sigs.i_i());
byte f2 = builder.AddSignature(sigs.i_ii());
ExpectFailure(sig, {WASM_RETURN_CALL_INDIRECT0(f0, WASM_ZERO)});
ExpectFailure(sig,
{WASM_RETURN_CALL_INDIRECT(f1, WASM_ZERO, WASM_I32V_1(22))});
ExpectFailure(sig, {WASM_RETURN_CALL_INDIRECT(f2, WASM_ZERO, WASM_I32V_1(32),
WASM_I32V_2(72))});
}
TEST_F(FunctionBodyDecoderTest, IncompleteIndirectReturnCall) {
FunctionSig* sig = sigs.i_i();
TestModuleBuilder builder;
builder.InitializeTable();
module = builder.module();
static byte code[] = {kExprReturnCallIndirect};
ExpectFailure(sig, ArrayVector(code), kOmitEnd);
}
TEST_F(FunctionBodyDecoderTest, MultiReturn) {
WASM_FEATURE_SCOPE(mv);
ValueType storage[] = {kWasmI32, kWasmI32};