[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:
parent
6ee9ec5ca1
commit
0988e0d647
@ -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");
|
||||
|
@ -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 =
|
||||
|
@ -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);
|
||||
|
@ -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_
|
||||
|
@ -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")
|
||||
|
@ -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.
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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};
|
||||
|
Loading…
Reference in New Issue
Block a user