[wasm] Decode bulk memory instructions

These instructions aren't implemented yet in TF or in Liftoff, but they
are properly decoded.

The table instructions (i.e. `table.{init,drop,copy}`) are validated,
since the table and element sections occur before the code section. The
memory instructions (i.e. `memory.{init,drop,copy,fill}`) are not
validated because the data section occurs after the code section, so it
can't be verified in one pass (without throwing a validation error
later).

There is currently a discussion about whether to add a new section
(similar to `func`) that predefines the number of expected data
segments. If we add this, then we can validate in one pass. For now,
we'll leave it unimplemented.

Bug: v8:7747
Change-Id: I839edf51721105a47a1fa8dd5e5e1bd855e72447
Reviewed-on: https://chromium-review.googlesource.com/c/1339241
Commit-Queue: Ben Smith <binji@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57622}
This commit is contained in:
Ben Smith 2018-11-19 11:30:04 -08:00 committed by Commit Bot
parent c73c753efa
commit 50798d6028
7 changed files with 540 additions and 117 deletions

View File

@ -1817,6 +1817,37 @@ class LiftoffCompiler {
const MemoryAccessImmediate<validate>& imm, Value* result) {
unsupported(decoder, "atomicop");
}
void MemoryInit(FullDecoder* decoder,
const MemoryInitImmediate<validate>& imm,
Vector<Value> args) {
unsupported(decoder, "memory.init");
}
void MemoryDrop(FullDecoder* decoder,
const MemoryDropImmediate<validate>& imm) {
unsupported(decoder, "memory.drop");
}
void MemoryCopy(FullDecoder* decoder,
const MemoryIndexImmediate<validate>& imm,
Vector<Value> args) {
unsupported(decoder, "memory.copy");
}
void MemoryFill(FullDecoder* decoder,
const MemoryIndexImmediate<validate>& imm,
Vector<Value> args) {
unsupported(decoder, "memory.fill");
}
void TableInit(FullDecoder* decoder, const TableInitImmediate<validate>& imm,
Vector<Value> args) {
unsupported(decoder, "table.init");
}
void TableDrop(FullDecoder* decoder,
const TableDropImmediate<validate>& imm) {
unsupported(decoder, "table.drop");
}
void TableCopy(FullDecoder* decoder, const TableIndexImmediate<validate>& imm,
Vector<Value> args) {
unsupported(decoder, "table.copy");
}
private:
LiftoffAssembler asm_;

View File

@ -334,6 +334,18 @@ struct MemoryIndexImmediate {
}
};
template <Decoder::ValidateFlag validate>
struct TableIndexImmediate {
uint32_t index;
unsigned length = 1;
inline TableIndexImmediate(Decoder* decoder, const byte* pc) {
index = decoder->read_u8<validate>(pc + 1, "table index");
if (!VALIDATE(index == 0)) {
decoder->errorf(pc + 1, "expected table index 0, found %u", index);
}
}
};
template <Decoder::ValidateFlag validate>
struct BranchTableImmediate {
uint32_t table_count;
@ -445,6 +457,54 @@ struct Simd8x16ShuffleImmediate {
}
};
template <Decoder::ValidateFlag validate>
struct MemoryInitImmediate {
MemoryIndexImmediate<validate> memory;
uint32_t data_segment_index;
unsigned length;
inline MemoryInitImmediate(Decoder* decoder, const byte* pc)
: memory(decoder, pc + 1) {
data_segment_index = decoder->read_i32v<validate>(
pc + 2 + memory.length, &length, "data segment index");
length += memory.length;
}
};
template <Decoder::ValidateFlag validate>
struct MemoryDropImmediate {
uint32_t index;
unsigned length;
inline MemoryDropImmediate(Decoder* decoder, const byte* pc) {
index = decoder->read_i32v<validate>(pc + 2, &length, "data segment index");
}
};
template <Decoder::ValidateFlag validate>
struct TableInitImmediate {
TableIndexImmediate<validate> table;
uint32_t elem_segment_index;
unsigned length;
inline TableInitImmediate(Decoder* decoder, const byte* pc)
: table(decoder, pc + 1) {
elem_segment_index = decoder->read_i32v<validate>(
pc + 2 + table.length, &length, "elem segment index");
length += table.length;
}
};
template <Decoder::ValidateFlag validate>
struct TableDropImmediate {
uint32_t index;
unsigned length;
inline TableDropImmediate(Decoder* decoder, const byte* pc) {
index = decoder->read_i32v<validate>(pc + 2, &length, "elem segment index");
}
};
// An entry on the value stack.
struct ValueBase {
const byte* pc;
@ -598,71 +658,78 @@ struct ControlWithNamedConstructors : public ControlBase<Value> {
// This is the list of callback functions that an interface for the
// WasmFullDecoder should implement.
// F(Name, args...)
#define INTERFACE_FUNCTIONS(F) \
/* General: */ \
F(StartFunction) \
F(StartFunctionBody, Control* block) \
F(FinishFunction) \
F(OnFirstError) \
F(NextInstruction, WasmOpcode) \
/* Control: */ \
F(Block, Control* block) \
F(Loop, Control* block) \
F(Try, Control* block) \
F(If, const Value& cond, Control* if_block) \
F(FallThruTo, Control* c) \
F(PopControl, Control* block) \
F(EndControl, Control* block) \
/* Instructions: */ \
F(UnOp, WasmOpcode opcode, FunctionSig*, const Value& value, Value* result) \
F(BinOp, WasmOpcode opcode, FunctionSig*, const Value& lhs, \
const Value& rhs, Value* result) \
F(I32Const, Value* result, int32_t value) \
F(I64Const, Value* result, int64_t value) \
F(F32Const, Value* result, float value) \
F(F64Const, Value* result, double value) \
F(RefNull, Value* result) \
F(Drop, const Value& value) \
F(DoReturn, Vector<Value> values, bool implicit) \
F(GetLocal, Value* result, const LocalIndexImmediate<validate>& imm) \
F(SetLocal, const Value& value, const LocalIndexImmediate<validate>& imm) \
F(TeeLocal, const Value& value, Value* result, \
const LocalIndexImmediate<validate>& imm) \
F(GetGlobal, Value* result, const GlobalIndexImmediate<validate>& imm) \
F(SetGlobal, const Value& value, const GlobalIndexImmediate<validate>& imm) \
F(Unreachable) \
F(Select, const Value& cond, const Value& fval, const Value& tval, \
Value* result) \
F(Br, Control* target) \
F(BrIf, const Value& cond, Control* target) \
F(BrTable, const BranchTableImmediate<validate>& imm, const Value& key) \
F(Else, Control* if_block) \
F(LoadMem, LoadType type, const MemoryAccessImmediate<validate>& imm, \
const Value& index, Value* result) \
F(StoreMem, StoreType type, const MemoryAccessImmediate<validate>& imm, \
const Value& index, const Value& value) \
F(CurrentMemoryPages, Value* result) \
F(MemoryGrow, const Value& value, Value* result) \
F(CallDirect, const CallFunctionImmediate<validate>& imm, \
const Value args[], Value returns[]) \
F(CallIndirect, const Value& index, \
const CallIndirectImmediate<validate>& imm, const Value args[], \
Value returns[]) \
F(SimdOp, WasmOpcode opcode, Vector<Value> args, Value* result) \
F(SimdLaneOp, WasmOpcode opcode, const SimdLaneImmediate<validate>& imm, \
const Vector<Value> inputs, Value* result) \
F(SimdShiftOp, WasmOpcode opcode, const SimdShiftImmediate<validate>& imm, \
const Value& input, Value* result) \
F(Simd8x16ShuffleOp, const Simd8x16ShuffleImmediate<validate>& imm, \
const Value& input0, const Value& input1, Value* result) \
F(Throw, const ExceptionIndexImmediate<validate>& imm, \
const Vector<Value>& args) \
F(Rethrow, Control* block) \
F(CatchException, const ExceptionIndexImmediate<validate>& imm, \
Control* block, Vector<Value> caught_values) \
F(CatchAll, Control* block) \
F(AtomicOp, WasmOpcode opcode, Vector<Value> args, \
const MemoryAccessImmediate<validate>& imm, Value* result)
#define INTERFACE_FUNCTIONS(F) \
/* General: */ \
F(StartFunction) \
F(StartFunctionBody, Control* block) \
F(FinishFunction) \
F(OnFirstError) \
F(NextInstruction, WasmOpcode) \
/* Control: */ \
F(Block, Control* block) \
F(Loop, Control* block) \
F(Try, Control* block) \
F(If, const Value& cond, Control* if_block) \
F(FallThruTo, Control* c) \
F(PopControl, Control* block) \
F(EndControl, Control* block) \
/* Instructions: */ \
F(UnOp, WasmOpcode opcode, FunctionSig*, const Value& value, Value* result) \
F(BinOp, WasmOpcode opcode, FunctionSig*, const Value& lhs, \
const Value& rhs, Value* result) \
F(I32Const, Value* result, int32_t value) \
F(I64Const, Value* result, int64_t value) \
F(F32Const, Value* result, float value) \
F(F64Const, Value* result, double value) \
F(RefNull, Value* result) \
F(Drop, const Value& value) \
F(DoReturn, Vector<Value> values, bool implicit) \
F(GetLocal, Value* result, const LocalIndexImmediate<validate>& imm) \
F(SetLocal, const Value& value, const LocalIndexImmediate<validate>& imm) \
F(TeeLocal, const Value& value, Value* result, \
const LocalIndexImmediate<validate>& imm) \
F(GetGlobal, Value* result, const GlobalIndexImmediate<validate>& imm) \
F(SetGlobal, const Value& value, const GlobalIndexImmediate<validate>& imm) \
F(Unreachable) \
F(Select, const Value& cond, const Value& fval, const Value& tval, \
Value* result) \
F(Br, Control* target) \
F(BrIf, const Value& cond, Control* target) \
F(BrTable, const BranchTableImmediate<validate>& imm, const Value& key) \
F(Else, Control* if_block) \
F(LoadMem, LoadType type, const MemoryAccessImmediate<validate>& imm, \
const Value& index, Value* result) \
F(StoreMem, StoreType type, const MemoryAccessImmediate<validate>& imm, \
const Value& index, const Value& value) \
F(CurrentMemoryPages, Value* result) \
F(MemoryGrow, const Value& value, Value* result) \
F(CallDirect, const CallFunctionImmediate<validate>& imm, \
const Value args[], Value returns[]) \
F(CallIndirect, const Value& index, \
const CallIndirectImmediate<validate>& imm, const Value args[], \
Value returns[]) \
F(SimdOp, WasmOpcode opcode, Vector<Value> args, Value* result) \
F(SimdLaneOp, WasmOpcode opcode, const SimdLaneImmediate<validate>& imm, \
const Vector<Value> inputs, Value* result) \
F(SimdShiftOp, WasmOpcode opcode, const SimdShiftImmediate<validate>& imm, \
const Value& input, Value* result) \
F(Simd8x16ShuffleOp, const Simd8x16ShuffleImmediate<validate>& imm, \
const Value& input0, const Value& input1, Value* result) \
F(Throw, const ExceptionIndexImmediate<validate>& imm, \
const Vector<Value>& args) \
F(Rethrow, Control* block) \
F(CatchException, const ExceptionIndexImmediate<validate>& imm, \
Control* block, Vector<Value> caught_values) \
F(CatchAll, Control* block) \
F(AtomicOp, WasmOpcode opcode, Vector<Value> args, \
const MemoryAccessImmediate<validate>& imm, Value* result) \
F(MemoryInit, const MemoryInitImmediate<validate>& imm, Vector<Value> args) \
F(MemoryDrop, const MemoryDropImmediate<validate>& imm) \
F(MemoryCopy, const MemoryIndexImmediate<validate>& imm, Vector<Value> args) \
F(MemoryFill, const MemoryIndexImmediate<validate>& imm, Vector<Value> args) \
F(TableInit, const TableInitImmediate<validate>& imm, Vector<Value> args) \
F(TableDrop, const TableDropImmediate<validate>& imm) \
F(TableCopy, const TableIndexImmediate<validate>& imm, Vector<Value> args)
// Generic Wasm bytecode decoder with utilities for decoding immediates,
// lengths, etc.
@ -993,6 +1060,53 @@ class WasmDecoder : public Decoder {
return true;
}
inline bool Validate(MemoryIndexImmediate<validate>& imm) {
if (!VALIDATE(module_ != nullptr && module_->has_memory)) {
errorf(pc_ + 1, "memory instruction with no memory");
return false;
}
return true;
}
inline bool Validate(MemoryInitImmediate<validate>& imm) {
if (!Validate(imm.memory)) return false;
// TODO(binji): validate imm.data_segment_index
return true;
}
inline bool Validate(MemoryDropImmediate<validate>& imm) {
// TODO(binji): validate imm.data_segment_index
return true;
}
inline bool Validate(const byte* pc, TableIndexImmediate<validate>& imm) {
if (!VALIDATE(module_ != nullptr && imm.index < module_->tables.size())) {
errorf(pc_ + 1, "invalid table index: %u", imm.index);
return false;
}
return true;
}
inline bool Validate(TableInitImmediate<validate>& imm) {
if (!Validate(pc_ + 1, imm.table)) return false;
if (!VALIDATE(module_ != nullptr &&
imm.elem_segment_index < module_->table_inits.size())) {
errorf(pc_ + 2, "invalid element segment index: %u",
imm.elem_segment_index);
return false;
}
return true;
}
inline bool Validate(TableDropImmediate<validate>& imm) {
if (!VALIDATE(module_ != nullptr &&
imm.index < module_->table_inits.size())) {
errorf(pc_ + 2, "invalid element segment index: %u", imm.index);
return false;
}
return true;
}
static unsigned OpcodeLength(Decoder* decoder, const byte* pc) {
WasmOpcode opcode = static_cast<WasmOpcode>(*pc);
switch (opcode) {
@ -1070,8 +1184,51 @@ class WasmDecoder : public Decoder {
return 5;
case kExprF64Const:
return 9;
case kNumericPrefix:
return 2;
case kNumericPrefix: {
byte numeric_index =
decoder->read_u8<validate>(pc + 1, "numeric_index");
WasmOpcode opcode =
static_cast<WasmOpcode>(kNumericPrefix << 8 | numeric_index);
switch (opcode) {
case kExprI32SConvertSatF32:
case kExprI32UConvertSatF32:
case kExprI32SConvertSatF64:
case kExprI32UConvertSatF64:
case kExprI64SConvertSatF32:
case kExprI64UConvertSatF32:
case kExprI64SConvertSatF64:
case kExprI64UConvertSatF64:
return 2;
case kExprMemoryInit: {
MemoryInitImmediate<validate> imm(decoder, pc);
return 2 + imm.length;
}
case kExprMemoryDrop: {
MemoryDropImmediate<validate> imm(decoder, pc);
return 2 + imm.length;
}
case kExprMemoryCopy:
case kExprMemoryFill: {
MemoryIndexImmediate<validate> imm(decoder, pc + 1);
return 2 + imm.length;
}
case kExprTableInit: {
TableInitImmediate<validate> imm(decoder, pc);
return 2 + imm.length;
}
case kExprTableDrop: {
TableDropImmediate<validate> imm(decoder, pc);
return 2 + imm.length;
}
case kExprTableCopy: {
TableIndexImmediate<validate> imm(decoder, pc + 1);
return 2 + imm.length;
}
default:
decoder->error(pc, "invalid numeric opcode");
return 2;
}
}
case kSimdPrefix: {
byte simd_index = decoder->read_u8<validate>(pc + 1, "simd_index");
WasmOpcode opcode =
@ -1939,20 +2096,18 @@ class WasmFullDecoder : public WasmDecoder<validate> {
break;
}
case kNumericPrefix: {
CHECK_PROTOTYPE_OPCODE(sat_f2i_conversions);
++len;
byte numeric_index = this->template read_u8<validate>(
this->pc_ + 1, "numeric index");
opcode = static_cast<WasmOpcode>(opcode << 8 | numeric_index);
if (opcode < kExprMemoryInit) {
CHECK_PROTOTYPE_OPCODE(sat_f2i_conversions);
} else {
CHECK_PROTOTYPE_OPCODE(bulk_memory);
}
TRACE_PART(TRACE_INST_FORMAT, startrel(this->pc_),
WasmOpcodes::OpcodeName(opcode));
sig = WasmOpcodes::Signature(opcode);
if (sig == nullptr) {
this->errorf(this->pc_, "Unrecognized numeric opcode: %x\n",
opcode);
return;
}
BuildSimpleOperator(opcode, sig);
len += DecodeNumericOpcode(opcode);
break;
}
case kSimdPrefix: {
@ -2319,6 +2474,85 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return len;
}
unsigned DecodeNumericOpcode(WasmOpcode opcode) {
unsigned len = 0;
FunctionSig* sig = WasmOpcodes::Signature(opcode);
if (sig != nullptr) {
switch (opcode) {
case kExprI32SConvertSatF32:
case kExprI32UConvertSatF32:
case kExprI32SConvertSatF64:
case kExprI32UConvertSatF64:
case kExprI64SConvertSatF32:
case kExprI64UConvertSatF32:
case kExprI64SConvertSatF64:
case kExprI64UConvertSatF64:
BuildSimpleOperator(opcode, sig);
break;
case kExprMemoryInit: {
MemoryInitImmediate<validate> imm(this, this->pc_);
if (!this->Validate(imm)) break;
len += imm.length;
PopArgs(sig);
CALL_INTERFACE_IF_REACHABLE(MemoryInit, imm, VectorOf(args_));
break;
}
case kExprMemoryDrop: {
MemoryDropImmediate<validate> imm(this, this->pc_);
if (!this->Validate(imm)) break;
len += imm.length;
CALL_INTERFACE_IF_REACHABLE(MemoryDrop, imm);
break;
}
case kExprMemoryCopy: {
MemoryIndexImmediate<validate> imm(this, this->pc_ + 1);
if (!this->Validate(imm)) break;
len += imm.length;
PopArgs(sig);
CALL_INTERFACE_IF_REACHABLE(MemoryCopy, imm, VectorOf(args_));
break;
}
case kExprMemoryFill: {
MemoryIndexImmediate<validate> imm(this, this->pc_ + 1);
if (!this->Validate(imm)) break;
len += imm.length;
PopArgs(sig);
CALL_INTERFACE_IF_REACHABLE(MemoryFill, imm, VectorOf(args_));
break;
}
case kExprTableInit: {
TableInitImmediate<validate> imm(this, this->pc_);
if (!this->Validate(imm)) break;
len += imm.length;
PopArgs(sig);
CALL_INTERFACE_IF_REACHABLE(TableInit, imm, VectorOf(args_));
break;
}
case kExprTableDrop: {
TableDropImmediate<validate> imm(this, this->pc_);
if (!this->Validate(imm)) break;
len += imm.length;
CALL_INTERFACE_IF_REACHABLE(TableDrop, imm);
break;
}
case kExprTableCopy: {
TableIndexImmediate<validate> imm(this, this->pc_ + 1);
if (!this->Validate(this->pc_ + 1, imm)) break;
len += imm.length;
PopArgs(sig);
CALL_INTERFACE_IF_REACHABLE(TableCopy, imm, VectorOf(args_));
break;
}
default:
this->error("invalid numeric opcode");
break;
}
} else {
this->error("invalid numeric opcode");
}
return len;
}
void DoReturn(Control* c, bool implicit) {
int return_count = static_cast<int>(this->sig_->return_count());
args_.resize(return_count);

View File

@ -497,6 +497,38 @@ class WasmGraphBuildingInterface {
if (result) result->node = node;
}
void MemoryInit(FullDecoder* decoder,
const MemoryInitImmediate<validate>& imm,
Vector<Value> args) {
BUILD(Unreachable, decoder->position());
}
void MemoryDrop(FullDecoder* decoder,
const MemoryDropImmediate<validate>& imm) {
BUILD(Unreachable, decoder->position());
}
void MemoryCopy(FullDecoder* decoder,
const MemoryIndexImmediate<validate>& imm,
Vector<Value> args) {
BUILD(Unreachable, decoder->position());
}
void MemoryFill(FullDecoder* decoder,
const MemoryIndexImmediate<validate>& imm,
Vector<Value> args) {
BUILD(Unreachable, decoder->position());
}
void TableInit(FullDecoder* decoder, const TableInitImmediate<validate>& imm,
Vector<Value> args) {
BUILD(Unreachable, decoder->position());
}
void TableDrop(FullDecoder* decoder,
const TableDropImmediate<validate>& imm) {
BUILD(Unreachable, decoder->position());
}
void TableCopy(FullDecoder* decoder, const TableIndexImmediate<validate>& imm,
Vector<Value> args) {
BUILD(Unreachable, decoder->position());
}
private:
SsaEnv* ssa_env_;
compiler::WasmGraphBuilder* builder_;

View File

@ -110,12 +110,6 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_I32_OP(ConvertI64, "wrap/i64")
CASE_CONVERT_OP(Convert, INT, F32, "f32", "trunc")
CASE_CONVERT_OP(Convert, INT, F64, "f64", "trunc")
// TODO(kschimpf): Simplify after filling in other saturating operations.
CASE_CONVERT_SAT_OP(Convert, I32, F32, "f32", "trunc")
CASE_CONVERT_SAT_OP(Convert, I32, F64, "f64", "trunc")
CASE_CONVERT_SAT_OP(Convert, I64, F32, "f32", "trunc")
CASE_CONVERT_SAT_OP(Convert, I64, F64, "f64", "trunc")
CASE_CONVERT_OP(Convert, I64, I32, "i32", "extend")
CASE_CONVERT_OP(Convert, F32, I32, "i32", "convert")
CASE_CONVERT_OP(Convert, F32, I64, "i64", "convert")
@ -198,6 +192,19 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_I32_OP(AsmjsSConvertF64, "asmjs_convert_s/f64")
CASE_I32_OP(AsmjsUConvertF64, "asmjs_convert_u/f64")
// Numeric Opcodes.
CASE_CONVERT_SAT_OP(Convert, I32, F32, "f32", "trunc")
CASE_CONVERT_SAT_OP(Convert, I32, F64, "f64", "trunc")
CASE_CONVERT_SAT_OP(Convert, I64, F32, "f32", "trunc")
CASE_CONVERT_SAT_OP(Convert, I64, F64, "f64", "trunc")
CASE_OP(MemoryInit, "memory.init")
CASE_OP(MemoryDrop, "memory.drop")
CASE_OP(MemoryCopy, "memory.copy")
CASE_OP(MemoryFill, "memory.fill")
CASE_OP(TableInit, "table.init")
CASE_OP(TableDrop, "table.drop")
CASE_OP(TableCopy, "table.copy")
// SIMD opcodes.
CASE_SIMD_OP(Splat, "splat")
CASE_SIMD_OP(Neg, "neg")

View File

@ -408,7 +408,14 @@ bool IsJSCompatibleSignature(const FunctionSig* sig);
V(I64SConvertSatF32, 0xfc04, l_f) \
V(I64UConvertSatF32, 0xfc05, l_f) \
V(I64SConvertSatF64, 0xfc06, l_d) \
V(I64UConvertSatF64, 0xfc07, l_d)
V(I64UConvertSatF64, 0xfc07, l_d) \
V(MemoryInit, 0xfc08, v_iii) \
V(MemoryDrop, 0xfc09, v_v) \
V(MemoryCopy, 0xfc0a, v_iii) \
V(MemoryFill, 0xfc0b, v_iii) \
V(TableInit, 0xfc0c, v_iii) \
V(TableDrop, 0xfc0d, v_v) \
V(TableCopy, 0xfc0e, v_iii)
#define FOREACH_ATOMIC_OPCODE(V) \
V(I32AtomicLoad, 0xfe10, i_i) \
@ -493,41 +500,43 @@ bool IsJSCompatibleSignature(const FunctionSig* sig);
FOREACH_NUMERIC_OPCODE(V)
// All signatures.
#define FOREACH_SIGNATURE(V) \
FOREACH_SIMD_SIGNATURE(V) \
V(i_ii, kWasmI32, kWasmI32, kWasmI32) \
V(i_i, kWasmI32, kWasmI32) \
V(i_v, kWasmI32) \
V(i_ff, kWasmI32, kWasmF32, kWasmF32) \
V(i_f, kWasmI32, kWasmF32) \
V(i_dd, kWasmI32, kWasmF64, kWasmF64) \
V(i_d, kWasmI32, kWasmF64) \
V(i_l, kWasmI32, kWasmI64) \
V(l_ll, kWasmI64, kWasmI64, kWasmI64) \
V(i_ll, kWasmI32, kWasmI64, kWasmI64) \
V(l_l, kWasmI64, kWasmI64) \
V(l_i, kWasmI64, kWasmI32) \
V(l_f, kWasmI64, kWasmF32) \
V(l_d, kWasmI64, kWasmF64) \
V(f_ff, kWasmF32, kWasmF32, kWasmF32) \
V(f_f, kWasmF32, kWasmF32) \
V(f_d, kWasmF32, kWasmF64) \
V(f_i, kWasmF32, kWasmI32) \
V(f_l, kWasmF32, kWasmI64) \
V(d_dd, kWasmF64, kWasmF64, kWasmF64) \
V(d_d, kWasmF64, kWasmF64) \
V(d_f, kWasmF64, kWasmF32) \
V(d_i, kWasmF64, kWasmI32) \
V(d_l, kWasmF64, kWasmI64) \
V(v_ii, kWasmStmt, kWasmI32, kWasmI32) \
V(v_id, kWasmStmt, kWasmI32, kWasmF64) \
V(d_id, kWasmF64, kWasmI32, kWasmF64) \
V(v_if, kWasmStmt, kWasmI32, kWasmF32) \
V(f_if, kWasmF32, kWasmI32, kWasmF32) \
V(v_il, kWasmStmt, kWasmI32, kWasmI64) \
V(l_il, kWasmI64, kWasmI32, kWasmI64) \
V(i_iii, kWasmI32, kWasmI32, kWasmI32, kWasmI32) \
V(l_ill, kWasmI64, kWasmI32, kWasmI64, kWasmI64) \
#define FOREACH_SIGNATURE(V) \
FOREACH_SIMD_SIGNATURE(V) \
V(v_v, kWasmStmt) \
V(i_ii, kWasmI32, kWasmI32, kWasmI32) \
V(i_i, kWasmI32, kWasmI32) \
V(i_v, kWasmI32) \
V(i_ff, kWasmI32, kWasmF32, kWasmF32) \
V(i_f, kWasmI32, kWasmF32) \
V(i_dd, kWasmI32, kWasmF64, kWasmF64) \
V(i_d, kWasmI32, kWasmF64) \
V(i_l, kWasmI32, kWasmI64) \
V(l_ll, kWasmI64, kWasmI64, kWasmI64) \
V(i_ll, kWasmI32, kWasmI64, kWasmI64) \
V(l_l, kWasmI64, kWasmI64) \
V(l_i, kWasmI64, kWasmI32) \
V(l_f, kWasmI64, kWasmF32) \
V(l_d, kWasmI64, kWasmF64) \
V(f_ff, kWasmF32, kWasmF32, kWasmF32) \
V(f_f, kWasmF32, kWasmF32) \
V(f_d, kWasmF32, kWasmF64) \
V(f_i, kWasmF32, kWasmI32) \
V(f_l, kWasmF32, kWasmI64) \
V(d_dd, kWasmF64, kWasmF64, kWasmF64) \
V(d_d, kWasmF64, kWasmF64) \
V(d_f, kWasmF64, kWasmF32) \
V(d_i, kWasmF64, kWasmI32) \
V(d_l, kWasmF64, kWasmI64) \
V(v_ii, kWasmStmt, kWasmI32, kWasmI32) \
V(v_id, kWasmStmt, kWasmI32, kWasmF64) \
V(d_id, kWasmF64, kWasmI32, kWasmF64) \
V(v_if, kWasmStmt, kWasmI32, kWasmF32) \
V(f_if, kWasmF32, kWasmI32, kWasmF32) \
V(v_il, kWasmStmt, kWasmI32, kWasmI64) \
V(l_il, kWasmI64, kWasmI32, kWasmI64) \
V(v_iii, kWasmStmt, kWasmI32, kWasmI32, kWasmI32) \
V(i_iii, kWasmI32, kWasmI32, kWasmI32, kWasmI32) \
V(l_ill, kWasmI64, kWasmI32, kWasmI64, kWasmI64) \
V(i_r, kWasmI32, kWasmAnyRef)
#define FOREACH_SIMD_SIGNATURE(V) \

View File

@ -582,6 +582,21 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_I64_SCONVERT_SAT_F64(x) x, WASM_NUMERIC_OP(kExprI64SConvertSatF64)
#define WASM_I64_UCONVERT_SAT_F64(x) x, WASM_NUMERIC_OP(kExprI64UConvertSatF64)
#define MEMORY_ZERO 0
#define WASM_MEMORY_INIT(seg, dst, src, size) \
dst, src, size, WASM_NUMERIC_OP(kExprMemoryInit), MEMORY_ZERO, U32V_1(seg)
#define WASM_MEMORY_DROP(seg) WASM_NUMERIC_OP(kExprMemoryDrop), U32V_1(seg)
#define WASM_MEMORY_COPY(dst, src, size) \
dst, src, size, WASM_NUMERIC_OP(kExprMemoryCopy), MEMORY_ZERO
#define WASM_MEMORY_FILL(dst, val, size) \
dst, val, size, WASM_NUMERIC_OP(kExprMemoryFill), MEMORY_ZERO
#define WASM_TABLE_INIT(seg, dst, src, size) \
dst, src, size, WASM_NUMERIC_OP(kExprTableInit), TABLE_ZERO, U32V_1(seg)
#define WASM_TABLE_DROP(seg) WASM_NUMERIC_OP(kExprTableDrop), U32V_1(seg)
#define WASM_TABLE_COPY(dst, src, size) \
dst, src, size, WASM_NUMERIC_OP(kExprTableCopy), TABLE_ZERO
//------------------------------------------------------------------------------
// Memory Operations.
//------------------------------------------------------------------------------

View File

@ -264,6 +264,11 @@ class TestModuleBuilder {
void InitializeTable() { mod.tables.emplace_back(); }
byte AddPassiveElementSegment() {
mod.table_inits.emplace_back();
return static_cast<byte>(mod.table_inits.size() - 1);
}
WasmModule* module() { return &mod; }
private:
@ -2728,6 +2733,96 @@ TEST_F(FunctionBodyDecoderTest, Regression709741) {
}
}
TEST_F(FunctionBodyDecoderTest, MemoryInit) {
TestModuleBuilder builder;
builder.InitializeMemory();
module = builder.module();
EXPECT_FAILURE(v_v, WASM_MEMORY_INIT(0, WASM_ZERO, WASM_ZERO, WASM_ZERO));
WASM_FEATURE_SCOPE(bulk_memory);
EXPECT_VERIFIES(v_v, WASM_MEMORY_INIT(0, WASM_ZERO, WASM_ZERO, WASM_ZERO));
// TODO(binji): validate segment index.
}
TEST_F(FunctionBodyDecoderTest, MemoryDrop) {
EXPECT_FAILURE(v_v, WASM_MEMORY_DROP(0));
WASM_FEATURE_SCOPE(bulk_memory);
EXPECT_VERIFIES(v_v, WASM_MEMORY_DROP(0));
// TODO(binji): validate segment index.
}
TEST_F(FunctionBodyDecoderTest, MemoryCopy) {
TestModuleBuilder builder;
builder.InitializeMemory();
module = builder.module();
EXPECT_FAILURE(v_v, WASM_MEMORY_COPY(WASM_ZERO, WASM_ZERO, WASM_ZERO));
WASM_FEATURE_SCOPE(bulk_memory);
EXPECT_VERIFIES(v_v, WASM_MEMORY_COPY(WASM_ZERO, WASM_ZERO, WASM_ZERO));
}
TEST_F(FunctionBodyDecoderTest, MemoryFill) {
TestModuleBuilder builder;
builder.InitializeMemory();
module = builder.module();
EXPECT_FAILURE(v_v, WASM_MEMORY_FILL(WASM_ZERO, WASM_ZERO, WASM_ZERO));
WASM_FEATURE_SCOPE(bulk_memory);
EXPECT_VERIFIES(v_v, WASM_MEMORY_FILL(WASM_ZERO, WASM_ZERO, WASM_ZERO));
}
TEST_F(FunctionBodyDecoderTest, BulkMemoryOpsWithoutMemory) {
WASM_FEATURE_SCOPE(bulk_memory);
EXPECT_FAILURE(v_v, WASM_MEMORY_INIT(0, WASM_ZERO, WASM_ZERO, WASM_ZERO));
EXPECT_FAILURE(v_v, WASM_MEMORY_COPY(WASM_ZERO, WASM_ZERO, WASM_ZERO));
EXPECT_FAILURE(v_v, WASM_MEMORY_FILL(WASM_ZERO, WASM_ZERO, WASM_ZERO));
}
TEST_F(FunctionBodyDecoderTest, TableInit) {
TestModuleBuilder builder;
builder.InitializeTable();
builder.AddPassiveElementSegment();
module = builder.module();
EXPECT_FAILURE(v_v, WASM_TABLE_INIT(0, WASM_ZERO, WASM_ZERO, WASM_ZERO));
WASM_FEATURE_SCOPE(bulk_memory);
EXPECT_VERIFIES(v_v, WASM_TABLE_INIT(0, WASM_ZERO, WASM_ZERO, WASM_ZERO));
EXPECT_FAILURE(v_v, WASM_TABLE_INIT(1, WASM_ZERO, WASM_ZERO, WASM_ZERO));
}
TEST_F(FunctionBodyDecoderTest, TableDrop) {
TestModuleBuilder builder;
builder.InitializeTable();
builder.AddPassiveElementSegment();
module = builder.module();
EXPECT_FAILURE(v_v, WASM_TABLE_DROP(0));
WASM_FEATURE_SCOPE(bulk_memory);
EXPECT_VERIFIES(v_v, WASM_TABLE_DROP(0));
EXPECT_FAILURE(v_v, WASM_TABLE_DROP(1));
}
TEST_F(FunctionBodyDecoderTest, TableCopy) {
TestModuleBuilder builder;
builder.InitializeTable();
module = builder.module();
EXPECT_FAILURE(v_v, WASM_TABLE_COPY(WASM_ZERO, WASM_ZERO, WASM_ZERO));
WASM_FEATURE_SCOPE(bulk_memory);
EXPECT_VERIFIES(v_v, WASM_TABLE_COPY(WASM_ZERO, WASM_ZERO, WASM_ZERO));
}
TEST_F(FunctionBodyDecoderTest, BulkTableOpsWithoutTable) {
TestModuleBuilder builder;
builder.InitializeTable();
builder.AddPassiveElementSegment();
WASM_FEATURE_SCOPE(bulk_memory);
EXPECT_FAILURE(v_v, WASM_TABLE_INIT(0, WASM_ZERO, WASM_ZERO, WASM_ZERO));
EXPECT_FAILURE(v_v, WASM_TABLE_DROP(0));
EXPECT_FAILURE(v_v, WASM_TABLE_COPY(WASM_ZERO, WASM_ZERO, WASM_ZERO));
}
class BranchTableIteratorTest : public TestWithZone {
public:
BranchTableIteratorTest() : TestWithZone() {}