Commit 0988e0d6 authored by Francis McCabe's avatar Francis McCabe Committed by Commit Bot

[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: 's avatarClemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59582}
parent 6ee9ec5c
...@@ -1854,7 +1854,17 @@ class LiftoffCompiler { ...@@ -1854,7 +1854,17 @@ class LiftoffCompiler {
__ FinishCall(imm.sig, call_descriptor); __ 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, void SimdOp(FullDecoder* decoder, WasmOpcode opcode, Vector<Value> args,
Value* result) { Value* result) {
unsupported(decoder, "simd"); unsupported(decoder, "simd");
......
...@@ -697,6 +697,10 @@ struct ControlBase { ...@@ -697,6 +697,10 @@ struct ControlBase {
F(CallIndirect, const Value& index, \ F(CallIndirect, const Value& index, \
const CallIndirectImmediate<validate>& imm, const Value args[], \ const CallIndirectImmediate<validate>& imm, const Value args[], \
Value returns[]) \ 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(SimdOp, WasmOpcode opcode, Vector<Value> args, Value* result) \
F(SimdLaneOp, WasmOpcode opcode, const SimdLaneImmediate<validate>& imm, \ F(SimdLaneOp, WasmOpcode opcode, const SimdLaneImmediate<validate>& imm, \
const Vector<Value> inputs, Value* result) \ const Vector<Value> inputs, Value* result) \
...@@ -864,6 +868,8 @@ class WasmDecoder : public Decoder { ...@@ -864,6 +868,8 @@ class WasmDecoder : public Decoder {
case kExprMemoryGrow: case kExprMemoryGrow:
case kExprCallFunction: case kExprCallFunction:
case kExprCallIndirect: case kExprCallIndirect:
case kExprReturnCall:
case kExprReturnCallIndirect:
// Add instance cache nodes to the assigned set. // Add instance cache nodes to the assigned set.
// TODO(titzer): make this more clear. // TODO(titzer): make this more clear.
assigned->Add(locals_count - 1); assigned->Add(locals_count - 1);
...@@ -918,6 +924,16 @@ class WasmDecoder : public Decoder { ...@@ -918,6 +924,16 @@ class WasmDecoder : public Decoder {
return true; 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) { inline bool Complete(const byte* pc, CallFunctionImmediate<validate>& imm) {
if (!VALIDATE(module_ != nullptr && if (!VALIDATE(module_ != nullptr &&
imm.index < module_->functions.size())) { imm.index < module_->functions.size())) {
...@@ -1164,11 +1180,13 @@ class WasmDecoder : public Decoder { ...@@ -1164,11 +1180,13 @@ class WasmDecoder : public Decoder {
TableIndexImmediate<validate> imm(decoder, pc); TableIndexImmediate<validate> imm(decoder, pc);
return 1 + imm.length; return 1 + imm.length;
} }
case kExprCallFunction: { case kExprCallFunction:
case kExprReturnCall: {
CallFunctionImmediate<validate> imm(decoder, pc); CallFunctionImmediate<validate> imm(decoder, pc);
return 1 + imm.length; return 1 + imm.length;
} }
case kExprCallIndirect: { case kExprCallIndirect:
case kExprReturnCallIndirect: {
CallIndirectImmediate<validate> imm(decoder, pc); CallIndirectImmediate<validate> imm(decoder, pc);
return 1 + imm.length; return 1 + imm.length;
} }
...@@ -1390,6 +1408,8 @@ class WasmDecoder : public Decoder { ...@@ -1390,6 +1408,8 @@ class WasmDecoder : public Decoder {
case kExprBrOnExn: case kExprBrOnExn:
case kExprNop: case kExprNop:
case kExprReturn: case kExprReturn:
case kExprReturnCall:
case kExprReturnCallIndirect:
case kExprUnreachable: case kExprUnreachable:
return {0, 0}; return {0, 0};
case kNumericPrefix: case kNumericPrefix:
...@@ -2150,6 +2170,39 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2150,6 +2170,39 @@ class WasmFullDecoder : public WasmDecoder<validate> {
returns); returns);
break; 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: { case kNumericPrefix: {
++len; ++len;
byte numeric_index = byte numeric_index =
......
...@@ -405,12 +405,24 @@ class WasmGraphBuildingInterface { ...@@ -405,12 +405,24 @@ class WasmGraphBuildingInterface {
DoCall(decoder, nullptr, imm.sig, imm.index, args, returns); 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, void CallIndirect(FullDecoder* decoder, const Value& index,
const CallIndirectImmediate<validate>& imm, const CallIndirectImmediate<validate>& imm,
const Value args[], Value returns[]) { const Value args[], Value returns[]) {
DoCall(decoder, index.node, imm.sig, imm.sig_index, args, 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, void SimdOp(FullDecoder* decoder, WasmOpcode opcode, Vector<Value> args,
Value* result) { Value* result) {
TFNode** inputs = GetNodes(args); TFNode** inputs = GetNodes(args);
......
...@@ -23,6 +23,8 @@ ...@@ -23,6 +23,8 @@
SEPARATOR \ SEPARATOR \
V(bigint, "JS BigInt support", false) \ V(bigint, "JS BigInt support", false) \
SEPARATOR \ 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_ #endif // V8_WASM_WASM_FEATURE_FLAGS_H_
...@@ -137,6 +137,8 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) { ...@@ -137,6 +137,8 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_OP(Return, "return") CASE_OP(Return, "return")
CASE_OP(CallFunction, "call") CASE_OP(CallFunction, "call")
CASE_OP(CallIndirect, "call_indirect") CASE_OP(CallIndirect, "call_indirect")
CASE_OP(ReturnCall, "return_call")
CASE_OP(ReturnCallIndirect, "return_call_indirect")
CASE_OP(Drop, "drop") CASE_OP(Drop, "drop")
CASE_OP(Select, "select") CASE_OP(Select, "select")
CASE_OP(GetLocal, "get_local") CASE_OP(GetLocal, "get_local")
......
...@@ -38,22 +38,24 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, bool hasBigIntFeature); ...@@ -38,22 +38,24 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, bool hasBigIntFeature);
V(Return, 0x0f, _) V(Return, 0x0f, _)
// Constants, locals, globals, and calls. // Constants, locals, globals, and calls.
#define FOREACH_MISC_OPCODE(V) \ #define FOREACH_MISC_OPCODE(V) \
V(CallFunction, 0x10, _) \ V(CallFunction, 0x10, _) \
V(CallIndirect, 0x11, _) \ V(CallIndirect, 0x11, _) \
V(Drop, 0x1a, _) \ V(ReturnCall, 0x12, _) \
V(Select, 0x1b, _) \ V(ReturnCallIndirect, 0x13, _) \
V(GetLocal, 0x20, _) \ V(Drop, 0x1a, _) \
V(SetLocal, 0x21, _) \ V(Select, 0x1b, _) \
V(TeeLocal, 0x22, _) \ V(GetLocal, 0x20, _) \
V(GetGlobal, 0x23, _) \ V(SetLocal, 0x21, _) \
V(SetGlobal, 0x24, _) \ V(TeeLocal, 0x22, _) \
V(GetTable, 0x25, _) \ V(GetGlobal, 0x23, _) \
V(SetTable, 0x26, _) \ V(SetGlobal, 0x24, _) \
V(I32Const, 0x41, _) \ V(GetTable, 0x25, _) \
V(I64Const, 0x42, _) \ V(SetTable, 0x26, _) \
V(F32Const, 0x43, _) \ V(I32Const, 0x41, _) \
V(F64Const, 0x44, _) \ V(I64Const, 0x42, _) \
V(F32Const, 0x43, _) \
V(F64Const, 0x44, _) \
V(RefNull, 0xd0, _) V(RefNull, 0xd0, _)
// Load memory expressions. // Load memory expressions.
......
...@@ -387,6 +387,11 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) { ...@@ -387,6 +387,11 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_CALL_FUNCTION(index, ...) \ #define WASM_CALL_FUNCTION(index, ...) \
__VA_ARGS__, kExprCallFunction, static_cast<byte>(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 #define TABLE_ZERO 0
// TODO(titzer): change usages of these macros to put func last. // TODO(titzer): change usages of these macros to put func last.
...@@ -405,6 +410,12 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) { ...@@ -405,6 +410,12 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_CALL_INDIRECTN(arity, index, func, ...) \ #define WASM_CALL_INDIRECTN(arity, index, func, ...) \
__VA_ARGS__, func, kExprCallIndirect, static_cast<byte>(index), TABLE_ZERO __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_NOT(x) x, kExprI32Eqz
#define WASM_SEQ(...) __VA_ARGS__ #define WASM_SEQ(...) __VA_ARGS__
......
...@@ -1568,6 +1568,158 @@ TEST_F(FunctionBodyDecoderTest, CallsWithMismatchedSigs3) { ...@@ -1568,6 +1568,158 @@ TEST_F(FunctionBodyDecoderTest, CallsWithMismatchedSigs3) {
ExpectFailure(sig, {WASM_CALL_FUNCTION(1, WASM_F32(17.6))}); 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) { TEST_F(FunctionBodyDecoderTest, MultiReturn) {
WASM_FEATURE_SCOPE(mv); WASM_FEATURE_SCOPE(mv);
ValueType storage[] = {kWasmI32, kWasmI32}; ValueType storage[] = {kWasmI32, kWasmI32};
......
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