[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:
parent
c73c753efa
commit
50798d6028
@ -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_;
|
||||
|
@ -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);
|
||||
|
@ -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_;
|
||||
|
@ -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")
|
||||
|
@ -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) \
|
||||
|
@ -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.
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -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() {}
|
||||
|
Loading…
Reference in New Issue
Block a user