[wasm] Implement memory.copy and memory.fill
This implementation currently only supports the optimized tier. Bug: v8:7747 Change-Id: Ia1af29b11a5d3e8a48b122f6cf3240c9f5948bfb Reviewed-on: https://chromium-review.googlesource.com/c/1364710 Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> Reviewed-by: Clemens Hammacher <clemensh@chromium.org> Commit-Queue: Ben Smith <binji@chromium.org> Cr-Commit-Position: refs/heads/master@{#58137}
This commit is contained in:
parent
44b1b245c5
commit
e151479972
@ -3284,6 +3284,34 @@ Node* WasmGraphBuilder::BoundsCheckMem(uint8_t access_size, Node* index,
|
|||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node* WasmGraphBuilder::BoundsCheckMemRange(Node* start, Node* size,
|
||||||
|
wasm::WasmCodePosition position) {
|
||||||
|
// TODO(binji): Support trap handler.
|
||||||
|
auto m = mcgraph()->machine();
|
||||||
|
if (!FLAG_wasm_no_bounds_checks) {
|
||||||
|
// The accessed memory is [start, end), where {end} is {start + size}.
|
||||||
|
// We want to check that {start + size <= mem_size}, making sure that
|
||||||
|
// {start + size} doesn't overflow. This can be expressed as
|
||||||
|
// {start <= mem_size - size} as long as {mem_size - size} isn't negative,
|
||||||
|
// which is true if {size <= mem_size}.
|
||||||
|
Node* mem_size = instance_cache_->mem_size;
|
||||||
|
Node* cond = graph()->NewNode(m->Uint32LessThanOrEqual(), size, mem_size);
|
||||||
|
TrapIfFalse(wasm::kTrapMemOutOfBounds, cond, position);
|
||||||
|
|
||||||
|
// This produces a positive number, since {size <= mem_size}.
|
||||||
|
Node* effective_size = graph()->NewNode(m->Int32Sub(), mem_size, size);
|
||||||
|
|
||||||
|
// Introduce the actual bounds check.
|
||||||
|
Node* check =
|
||||||
|
graph()->NewNode(m->Uint32LessThanOrEqual(), start, effective_size);
|
||||||
|
TrapIfFalse(wasm::kTrapMemOutOfBounds, check, position);
|
||||||
|
|
||||||
|
// TODO(binji): Does this need addtional untrusted_code_mitigations_ mask
|
||||||
|
// like BoundsCheckMem above?
|
||||||
|
}
|
||||||
|
return graph()->NewNode(m->IntAdd(), MemBuffer(0), Uint32ToUintptr(start));
|
||||||
|
}
|
||||||
|
|
||||||
const Operator* WasmGraphBuilder::GetSafeLoadOperator(int offset,
|
const Operator* WasmGraphBuilder::GetSafeLoadOperator(int offset,
|
||||||
wasm::ValueType type) {
|
wasm::ValueType type) {
|
||||||
int alignment = offset % (wasm::ValueTypes::ElementSizeInBytes(type));
|
int alignment = offset % (wasm::ValueTypes::ElementSizeInBytes(type));
|
||||||
@ -4204,6 +4232,29 @@ Node* WasmGraphBuilder::AtomicOp(wasm::WasmOpcode opcode, Node* const* inputs,
|
|||||||
#undef ATOMIC_LOAD_LIST
|
#undef ATOMIC_LOAD_LIST
|
||||||
#undef ATOMIC_STORE_LIST
|
#undef ATOMIC_STORE_LIST
|
||||||
|
|
||||||
|
Node* WasmGraphBuilder::MemoryCopy(Node* dst, Node* src, Node* size,
|
||||||
|
wasm::WasmCodePosition position) {
|
||||||
|
dst = BoundsCheckMemRange(dst, size, position);
|
||||||
|
src = BoundsCheckMemRange(src, size, position);
|
||||||
|
Node* function = graph()->NewNode(mcgraph()->common()->ExternalConstant(
|
||||||
|
ExternalReference::wasm_memory_copy()));
|
||||||
|
MachineType sig_types[] = {MachineType::Pointer(), MachineType::Pointer(),
|
||||||
|
MachineType::Uint32()};
|
||||||
|
MachineSignature sig(0, 3, sig_types);
|
||||||
|
return BuildCCall(&sig, function, dst, src, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node* WasmGraphBuilder::MemoryFill(Node* dst, Node* value, Node* size,
|
||||||
|
wasm::WasmCodePosition position) {
|
||||||
|
dst = BoundsCheckMemRange(dst, size, position);
|
||||||
|
Node* function = graph()->NewNode(mcgraph()->common()->ExternalConstant(
|
||||||
|
ExternalReference::wasm_memory_fill()));
|
||||||
|
MachineType sig_types[] = {MachineType::Pointer(), MachineType::Uint32(),
|
||||||
|
MachineType::Uint32()};
|
||||||
|
MachineSignature sig(0, 3, sig_types);
|
||||||
|
return BuildCCall(&sig, function, dst, value, size);
|
||||||
|
}
|
||||||
|
|
||||||
class WasmDecorator final : public GraphDecorator {
|
class WasmDecorator final : public GraphDecorator {
|
||||||
public:
|
public:
|
||||||
explicit WasmDecorator(NodeOriginTable* origins, wasm::Decoder* decoder)
|
explicit WasmDecorator(NodeOriginTable* origins, wasm::Decoder* decoder)
|
||||||
|
@ -349,6 +349,11 @@ class WasmGraphBuilder {
|
|||||||
uint32_t alignment, uint32_t offset,
|
uint32_t alignment, uint32_t offset,
|
||||||
wasm::WasmCodePosition position);
|
wasm::WasmCodePosition position);
|
||||||
|
|
||||||
|
Node* MemoryCopy(Node* dst, Node* src, Node* size,
|
||||||
|
wasm::WasmCodePosition position);
|
||||||
|
Node* MemoryFill(Node* dst, Node* fill, Node* size,
|
||||||
|
wasm::WasmCodePosition position);
|
||||||
|
|
||||||
bool has_simd() const { return has_simd_; }
|
bool has_simd() const { return has_simd_; }
|
||||||
|
|
||||||
const wasm::WasmModule* module() { return env_ ? env_->module : nullptr; }
|
const wasm::WasmModule* module() { return env_ ? env_->module : nullptr; }
|
||||||
@ -401,6 +406,9 @@ class WasmGraphBuilder {
|
|||||||
// BoundsCheckMem receives a uint32 {index} node and returns a ptrsize index.
|
// BoundsCheckMem receives a uint32 {index} node and returns a ptrsize index.
|
||||||
Node* BoundsCheckMem(uint8_t access_size, Node* index, uint32_t offset,
|
Node* BoundsCheckMem(uint8_t access_size, Node* index, uint32_t offset,
|
||||||
wasm::WasmCodePosition, EnforceBoundsCheck);
|
wasm::WasmCodePosition, EnforceBoundsCheck);
|
||||||
|
// BoundsCheckMemRange receives a uint32 {index} and {size} and returns
|
||||||
|
// a pointer into memory at that index, if it is in bounds.
|
||||||
|
Node* BoundsCheckMemRange(Node* index, Node* size, wasm::WasmCodePosition);
|
||||||
Node* CheckBoundsAndAlignment(uint8_t access_size, Node* index,
|
Node* CheckBoundsAndAlignment(uint8_t access_size, Node* index,
|
||||||
uint32_t offset, wasm::WasmCodePosition);
|
uint32_t offset, wasm::WasmCodePosition);
|
||||||
Node* Uint32ToUintptr(Node*);
|
Node* Uint32ToUintptr(Node*);
|
||||||
|
@ -307,6 +307,8 @@ FUNCTION_REFERENCE(wasm_word32_popcnt, wasm::word32_popcnt_wrapper)
|
|||||||
FUNCTION_REFERENCE(wasm_word64_popcnt, wasm::word64_popcnt_wrapper)
|
FUNCTION_REFERENCE(wasm_word64_popcnt, wasm::word64_popcnt_wrapper)
|
||||||
FUNCTION_REFERENCE(wasm_word32_rol, wasm::word32_rol_wrapper)
|
FUNCTION_REFERENCE(wasm_word32_rol, wasm::word32_rol_wrapper)
|
||||||
FUNCTION_REFERENCE(wasm_word32_ror, wasm::word32_ror_wrapper)
|
FUNCTION_REFERENCE(wasm_word32_ror, wasm::word32_ror_wrapper)
|
||||||
|
FUNCTION_REFERENCE(wasm_memory_copy, wasm::memory_copy_wrapper)
|
||||||
|
FUNCTION_REFERENCE(wasm_memory_fill, wasm::memory_fill_wrapper)
|
||||||
|
|
||||||
static void f64_acos_wrapper(Address data) {
|
static void f64_acos_wrapper(Address data) {
|
||||||
double input = ReadUnalignedValue<double>(data);
|
double input = ReadUnalignedValue<double>(data);
|
||||||
|
@ -183,6 +183,8 @@ class StatsCounter;
|
|||||||
V(wasm_word32_ror, "wasm::word32_ror") \
|
V(wasm_word32_ror, "wasm::word32_ror") \
|
||||||
V(wasm_word64_ctz, "wasm::word64_ctz") \
|
V(wasm_word64_ctz, "wasm::word64_ctz") \
|
||||||
V(wasm_word64_popcnt, "wasm::word64_popcnt") \
|
V(wasm_word64_popcnt, "wasm::word64_popcnt") \
|
||||||
|
V(wasm_memory_copy, "wasm::memory_copy") \
|
||||||
|
V(wasm_memory_fill, "wasm::memory_fill") \
|
||||||
V(call_enqueue_microtask_function, "MicrotaskQueue::CallEnqueueMicrotask") \
|
V(call_enqueue_microtask_function, "MicrotaskQueue::CallEnqueueMicrotask") \
|
||||||
V(atomic_pair_load_function, "atomic_pair_load_function") \
|
V(atomic_pair_load_function, "atomic_pair_load_function") \
|
||||||
V(atomic_pair_store_function, "atomic_pair_store_function") \
|
V(atomic_pair_store_function, "atomic_pair_store_function") \
|
||||||
|
@ -1865,13 +1865,13 @@ class LiftoffCompiler {
|
|||||||
unsupported(decoder, "memory.drop");
|
unsupported(decoder, "memory.drop");
|
||||||
}
|
}
|
||||||
void MemoryCopy(FullDecoder* decoder,
|
void MemoryCopy(FullDecoder* decoder,
|
||||||
const MemoryIndexImmediate<validate>& imm,
|
const MemoryIndexImmediate<validate>& imm, const Value& dst,
|
||||||
Vector<Value> args) {
|
const Value& src, const Value& size) {
|
||||||
unsupported(decoder, "memory.copy");
|
unsupported(decoder, "memory.copy");
|
||||||
}
|
}
|
||||||
void MemoryFill(FullDecoder* decoder,
|
void MemoryFill(FullDecoder* decoder,
|
||||||
const MemoryIndexImmediate<validate>& imm,
|
const MemoryIndexImmediate<validate>& imm, const Value& dst,
|
||||||
Vector<Value> args) {
|
const Value& value, const Value& size) {
|
||||||
unsupported(decoder, "memory.fill");
|
unsupported(decoder, "memory.fill");
|
||||||
}
|
}
|
||||||
void TableInit(FullDecoder* decoder, const TableInitImmediate<validate>& imm,
|
void TableInit(FullDecoder* decoder, const TableInitImmediate<validate>& imm,
|
||||||
|
@ -665,77 +665,79 @@ struct ControlWithNamedConstructors : public ControlBase<Value> {
|
|||||||
// This is the list of callback functions that an interface for the
|
// This is the list of callback functions that an interface for the
|
||||||
// WasmFullDecoder should implement.
|
// WasmFullDecoder should implement.
|
||||||
// F(Name, args...)
|
// F(Name, args...)
|
||||||
#define INTERFACE_FUNCTIONS(F) \
|
#define INTERFACE_FUNCTIONS(F) \
|
||||||
/* General: */ \
|
/* General: */ \
|
||||||
F(StartFunction) \
|
F(StartFunction) \
|
||||||
F(StartFunctionBody, Control* block) \
|
F(StartFunctionBody, Control* block) \
|
||||||
F(FinishFunction) \
|
F(FinishFunction) \
|
||||||
F(OnFirstError) \
|
F(OnFirstError) \
|
||||||
F(NextInstruction, WasmOpcode) \
|
F(NextInstruction, WasmOpcode) \
|
||||||
/* Control: */ \
|
/* Control: */ \
|
||||||
F(Block, Control* block) \
|
F(Block, Control* block) \
|
||||||
F(Loop, Control* block) \
|
F(Loop, Control* block) \
|
||||||
F(Try, Control* block) \
|
F(Try, Control* block) \
|
||||||
F(If, const Value& cond, Control* if_block) \
|
F(If, const Value& cond, Control* if_block) \
|
||||||
F(FallThruTo, Control* c) \
|
F(FallThruTo, Control* c) \
|
||||||
F(PopControl, Control* block) \
|
F(PopControl, Control* block) \
|
||||||
F(EndControl, Control* block) \
|
F(EndControl, Control* block) \
|
||||||
/* Instructions: */ \
|
/* Instructions: */ \
|
||||||
F(UnOp, WasmOpcode opcode, FunctionSig*, const Value& value, Value* result) \
|
F(UnOp, WasmOpcode opcode, FunctionSig*, const Value& value, Value* result) \
|
||||||
F(BinOp, WasmOpcode opcode, FunctionSig*, const Value& lhs, \
|
F(BinOp, WasmOpcode opcode, FunctionSig*, const Value& lhs, \
|
||||||
const Value& rhs, Value* result) \
|
const Value& rhs, Value* result) \
|
||||||
F(I32Const, Value* result, int32_t value) \
|
F(I32Const, Value* result, int32_t value) \
|
||||||
F(I64Const, Value* result, int64_t value) \
|
F(I64Const, Value* result, int64_t value) \
|
||||||
F(F32Const, Value* result, float value) \
|
F(F32Const, Value* result, float value) \
|
||||||
F(F64Const, Value* result, double value) \
|
F(F64Const, Value* result, double value) \
|
||||||
F(RefNull, Value* result) \
|
F(RefNull, Value* result) \
|
||||||
F(Drop, const Value& value) \
|
F(Drop, const Value& value) \
|
||||||
F(DoReturn, Vector<Value> values, bool implicit) \
|
F(DoReturn, Vector<Value> values, bool implicit) \
|
||||||
F(GetLocal, Value* result, const LocalIndexImmediate<validate>& imm) \
|
F(GetLocal, Value* result, const LocalIndexImmediate<validate>& imm) \
|
||||||
F(SetLocal, const Value& value, const LocalIndexImmediate<validate>& imm) \
|
F(SetLocal, const Value& value, const LocalIndexImmediate<validate>& imm) \
|
||||||
F(TeeLocal, const Value& value, Value* result, \
|
F(TeeLocal, const Value& value, Value* result, \
|
||||||
const LocalIndexImmediate<validate>& imm) \
|
const LocalIndexImmediate<validate>& imm) \
|
||||||
F(GetGlobal, Value* result, const GlobalIndexImmediate<validate>& imm) \
|
F(GetGlobal, Value* result, const GlobalIndexImmediate<validate>& imm) \
|
||||||
F(SetGlobal, const Value& value, const GlobalIndexImmediate<validate>& imm) \
|
F(SetGlobal, const Value& value, const GlobalIndexImmediate<validate>& imm) \
|
||||||
F(Unreachable) \
|
F(Unreachable) \
|
||||||
F(Select, const Value& cond, const Value& fval, const Value& tval, \
|
F(Select, const Value& cond, const Value& fval, const Value& tval, \
|
||||||
Value* result) \
|
Value* result) \
|
||||||
F(Br, Control* target) \
|
F(Br, Control* target) \
|
||||||
F(BrIf, const Value& cond, Control* target) \
|
F(BrIf, const Value& cond, Control* target) \
|
||||||
F(BrTable, const BranchTableImmediate<validate>& imm, const Value& key) \
|
F(BrTable, const BranchTableImmediate<validate>& imm, const Value& key) \
|
||||||
F(Else, Control* if_block) \
|
F(Else, Control* if_block) \
|
||||||
F(LoadMem, LoadType type, const MemoryAccessImmediate<validate>& imm, \
|
F(LoadMem, LoadType type, const MemoryAccessImmediate<validate>& imm, \
|
||||||
const Value& index, Value* result) \
|
const Value& index, Value* result) \
|
||||||
F(StoreMem, StoreType type, const MemoryAccessImmediate<validate>& imm, \
|
F(StoreMem, StoreType type, const MemoryAccessImmediate<validate>& imm, \
|
||||||
const Value& index, const Value& value) \
|
const Value& index, const Value& value) \
|
||||||
F(CurrentMemoryPages, Value* result) \
|
F(CurrentMemoryPages, Value* result) \
|
||||||
F(MemoryGrow, const Value& value, Value* result) \
|
F(MemoryGrow, const Value& value, Value* result) \
|
||||||
F(CallDirect, const CallFunctionImmediate<validate>& imm, \
|
F(CallDirect, const CallFunctionImmediate<validate>& imm, \
|
||||||
const Value args[], Value returns[]) \
|
const Value args[], Value returns[]) \
|
||||||
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(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) \
|
||||||
F(SimdShiftOp, WasmOpcode opcode, const SimdShiftImmediate<validate>& imm, \
|
F(SimdShiftOp, WasmOpcode opcode, const SimdShiftImmediate<validate>& imm, \
|
||||||
const Value& input, Value* result) \
|
const Value& input, Value* result) \
|
||||||
F(Simd8x16ShuffleOp, const Simd8x16ShuffleImmediate<validate>& imm, \
|
F(Simd8x16ShuffleOp, const Simd8x16ShuffleImmediate<validate>& imm, \
|
||||||
const Value& input0, const Value& input1, Value* result) \
|
const Value& input0, const Value& input1, Value* result) \
|
||||||
F(Throw, const ExceptionIndexImmediate<validate>& imm, \
|
F(Throw, const ExceptionIndexImmediate<validate>& imm, \
|
||||||
const Vector<Value>& args) \
|
const Vector<Value>& args) \
|
||||||
F(Rethrow, Control* block) \
|
F(Rethrow, Control* block) \
|
||||||
F(CatchException, const ExceptionIndexImmediate<validate>& imm, \
|
F(CatchException, const ExceptionIndexImmediate<validate>& imm, \
|
||||||
Control* block, Vector<Value> caught_values) \
|
Control* block, Vector<Value> caught_values) \
|
||||||
F(CatchAll, Control* block) \
|
F(CatchAll, Control* block) \
|
||||||
F(AtomicOp, WasmOpcode opcode, Vector<Value> args, \
|
F(AtomicOp, WasmOpcode opcode, Vector<Value> args, \
|
||||||
const MemoryAccessImmediate<validate>& imm, Value* result) \
|
const MemoryAccessImmediate<validate>& imm, Value* result) \
|
||||||
F(MemoryInit, const MemoryInitImmediate<validate>& imm, Vector<Value> args) \
|
F(MemoryInit, const MemoryInitImmediate<validate>& imm, Vector<Value> args) \
|
||||||
F(MemoryDrop, const MemoryDropImmediate<validate>& imm) \
|
F(MemoryDrop, const MemoryDropImmediate<validate>& imm) \
|
||||||
F(MemoryCopy, const MemoryIndexImmediate<validate>& imm, Vector<Value> args) \
|
F(MemoryCopy, const MemoryIndexImmediate<validate>& imm, const Value& dst, \
|
||||||
F(MemoryFill, const MemoryIndexImmediate<validate>& imm, Vector<Value> args) \
|
const Value& src, const Value& size) \
|
||||||
F(TableInit, const TableInitImmediate<validate>& imm, Vector<Value> args) \
|
F(MemoryFill, const MemoryIndexImmediate<validate>& imm, const Value& dst, \
|
||||||
F(TableDrop, const TableDropImmediate<validate>& imm) \
|
const Value& value, const Value& size) \
|
||||||
|
F(TableInit, const TableInitImmediate<validate>& imm, Vector<Value> args) \
|
||||||
|
F(TableDrop, const TableDropImmediate<validate>& imm) \
|
||||||
F(TableCopy, const TableIndexImmediate<validate>& imm, Vector<Value> args)
|
F(TableCopy, const TableIndexImmediate<validate>& imm, Vector<Value> args)
|
||||||
|
|
||||||
// Generic Wasm bytecode decoder with utilities for decoding immediates,
|
// Generic Wasm bytecode decoder with utilities for decoding immediates,
|
||||||
@ -2520,16 +2522,20 @@ class WasmFullDecoder : public WasmDecoder<validate> {
|
|||||||
MemoryIndexImmediate<validate> imm(this, this->pc_ + 1);
|
MemoryIndexImmediate<validate> imm(this, this->pc_ + 1);
|
||||||
if (!this->Validate(imm)) break;
|
if (!this->Validate(imm)) break;
|
||||||
len += imm.length;
|
len += imm.length;
|
||||||
PopArgs(sig);
|
auto size = Pop(2, sig->GetParam(2));
|
||||||
CALL_INTERFACE_IF_REACHABLE(MemoryCopy, imm, VectorOf(args_));
|
auto src = Pop(1, sig->GetParam(1));
|
||||||
|
auto dst = Pop(0, sig->GetParam(0));
|
||||||
|
CALL_INTERFACE_IF_REACHABLE(MemoryCopy, imm, dst, src, size);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case kExprMemoryFill: {
|
case kExprMemoryFill: {
|
||||||
MemoryIndexImmediate<validate> imm(this, this->pc_ + 1);
|
MemoryIndexImmediate<validate> imm(this, this->pc_ + 1);
|
||||||
if (!this->Validate(imm)) break;
|
if (!this->Validate(imm)) break;
|
||||||
len += imm.length;
|
len += imm.length;
|
||||||
PopArgs(sig);
|
auto size = Pop(2, sig->GetParam(2));
|
||||||
CALL_INTERFACE_IF_REACHABLE(MemoryFill, imm, VectorOf(args_));
|
auto value = Pop(1, sig->GetParam(1));
|
||||||
|
auto dst = Pop(0, sig->GetParam(0));
|
||||||
|
CALL_INTERFACE_IF_REACHABLE(MemoryFill, imm, dst, value, size);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case kExprTableInit: {
|
case kExprTableInit: {
|
||||||
|
@ -512,14 +512,14 @@ class WasmGraphBuildingInterface {
|
|||||||
BUILD(Unreachable, decoder->position());
|
BUILD(Unreachable, decoder->position());
|
||||||
}
|
}
|
||||||
void MemoryCopy(FullDecoder* decoder,
|
void MemoryCopy(FullDecoder* decoder,
|
||||||
const MemoryIndexImmediate<validate>& imm,
|
const MemoryIndexImmediate<validate>& imm, const Value& dst,
|
||||||
Vector<Value> args) {
|
const Value& src, const Value& size) {
|
||||||
BUILD(Unreachable, decoder->position());
|
BUILD(MemoryCopy, dst.node, src.node, size.node, decoder->position());
|
||||||
}
|
}
|
||||||
void MemoryFill(FullDecoder* decoder,
|
void MemoryFill(FullDecoder* decoder,
|
||||||
const MemoryIndexImmediate<validate>& imm,
|
const MemoryIndexImmediate<validate>& imm, const Value& dst,
|
||||||
Vector<Value> args) {
|
const Value& value, const Value& size) {
|
||||||
BUILD(Unreachable, decoder->position());
|
BUILD(MemoryFill, dst.node, value.node, size.node, decoder->position());
|
||||||
}
|
}
|
||||||
void TableInit(FullDecoder* decoder, const TableInitImmediate<validate>& imm,
|
void TableInit(FullDecoder* decoder, const TableInitImmediate<validate>& imm,
|
||||||
Vector<Value> args) {
|
Vector<Value> args) {
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "include/v8config.h"
|
#include "include/v8config.h"
|
||||||
|
|
||||||
#include "src/base/bits.h"
|
#include "src/base/bits.h"
|
||||||
|
#include "src/memcopy.h"
|
||||||
#include "src/utils.h"
|
#include "src/utils.h"
|
||||||
#include "src/v8memory.h"
|
#include "src/v8memory.h"
|
||||||
#include "src/wasm/wasm-external-refs.h"
|
#include "src/wasm/wasm-external-refs.h"
|
||||||
@ -247,6 +248,14 @@ void float64_pow_wrapper(Address data) {
|
|||||||
WriteUnalignedValue<double>(data, Pow(x, y));
|
WriteUnalignedValue<double>(data, Pow(x, y));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void memory_copy_wrapper(Address dst, Address src, uint32_t size) {
|
||||||
|
MemMove(reinterpret_cast<void*>(dst), reinterpret_cast<void*>(src), size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory_fill_wrapper(Address dst, uint32_t value, uint32_t size) {
|
||||||
|
memset(reinterpret_cast<void*>(dst), value, size);
|
||||||
|
}
|
||||||
|
|
||||||
static WasmTrapCallbackForTesting wasm_trap_callback_for_testing = nullptr;
|
static WasmTrapCallbackForTesting wasm_trap_callback_for_testing = nullptr;
|
||||||
|
|
||||||
void set_trap_callback_for_testing(WasmTrapCallbackForTesting callback) {
|
void set_trap_callback_for_testing(WasmTrapCallbackForTesting callback) {
|
||||||
|
@ -67,6 +67,10 @@ uint32_t word32_ror_wrapper(Address data);
|
|||||||
|
|
||||||
void float64_pow_wrapper(Address data);
|
void float64_pow_wrapper(Address data);
|
||||||
|
|
||||||
|
void memory_copy_wrapper(Address dst, Address src, uint32_t size);
|
||||||
|
|
||||||
|
void memory_fill_wrapper(Address dst, uint32_t value, uint32_t size);
|
||||||
|
|
||||||
typedef void (*WasmTrapCallbackForTesting)();
|
typedef void (*WasmTrapCallbackForTesting)();
|
||||||
|
|
||||||
void set_trap_callback_for_testing(WasmTrapCallbackForTesting callback);
|
void set_trap_callback_for_testing(WasmTrapCallbackForTesting callback);
|
||||||
|
@ -14,7 +14,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
|||||||
builder.addPassiveDataSegment([3, 4]);
|
builder.addPassiveDataSegment([3, 4]);
|
||||||
|
|
||||||
// Should not throw.
|
// Should not throw.
|
||||||
const module = builder.instantiate();
|
builder.instantiate();
|
||||||
})();
|
})();
|
||||||
|
|
||||||
(function TestPassiveElementSegment() {
|
(function TestPassiveElementSegment() {
|
||||||
@ -25,5 +25,156 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
|||||||
builder.addPassiveElementSegment([0, 0]);
|
builder.addPassiveElementSegment([0, 0]);
|
||||||
|
|
||||||
// Should not throw.
|
// Should not throw.
|
||||||
const module = builder.instantiate();
|
builder.instantiate();
|
||||||
|
})();
|
||||||
|
|
||||||
|
function assertBufferContents(buf, expected) {
|
||||||
|
for (let i = 0; i < expected.length; ++i) {
|
||||||
|
assertEquals(expected[i], buf[i]);
|
||||||
|
}
|
||||||
|
for (let i = expected.length; i < buf.length; ++i) {
|
||||||
|
assertEquals(0, buf[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMemoryCopy(mem) {
|
||||||
|
const builder = new WasmModuleBuilder();
|
||||||
|
builder.addImportedMemory("", "mem", 0);
|
||||||
|
builder.addFunction("copy", kSig_v_iii).addBody([
|
||||||
|
kExprGetLocal, 0, // Dest.
|
||||||
|
kExprGetLocal, 1, // Source.
|
||||||
|
kExprGetLocal, 2, // Size in bytes.
|
||||||
|
kNumericPrefix, kExprMemoryCopy, 0,
|
||||||
|
]).exportAs("copy");
|
||||||
|
return builder.instantiate({'': {mem}}).exports.copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
(function TestMemoryCopy() {
|
||||||
|
const mem = new WebAssembly.Memory({initial: 1});
|
||||||
|
const memoryCopy = getMemoryCopy(mem);
|
||||||
|
|
||||||
|
const u8a = new Uint8Array(mem.buffer);
|
||||||
|
u8a.set([0, 11, 22, 33, 44, 55, 66, 77]);
|
||||||
|
|
||||||
|
memoryCopy(10, 1, 8);
|
||||||
|
|
||||||
|
assertBufferContents(u8a, [0, 11, 22, 33, 44, 55, 66, 77, 0, 0,
|
||||||
|
11, 22, 33, 44, 55, 66, 77]);
|
||||||
|
|
||||||
|
// Copy 0 bytes does nothing.
|
||||||
|
memoryCopy(10, 1, 0);
|
||||||
|
assertBufferContents(u8a, [0, 11, 22, 33, 44, 55, 66, 77, 0, 0,
|
||||||
|
11, 22, 33, 44, 55, 66, 77]);
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function TestMemoryCopyOverlapping() {
|
||||||
|
const mem = new WebAssembly.Memory({initial: 1});
|
||||||
|
const memoryCopy = getMemoryCopy(mem);
|
||||||
|
|
||||||
|
const u8a = new Uint8Array(mem.buffer);
|
||||||
|
u8a.set([10, 20, 30]);
|
||||||
|
|
||||||
|
// Copy from [0, 3] -> [2, 5]. The copy must not overwrite 30 before copying
|
||||||
|
// it (i.e. cannot copy forward in this case).
|
||||||
|
memoryCopy(2, 0, 3);
|
||||||
|
assertBufferContents(u8a, [10, 20, 10, 20, 30]);
|
||||||
|
|
||||||
|
// Copy from [2, 5] -> [0, 3]. The copy must not write the first 10 (i.e.
|
||||||
|
// cannot copy backward in this case).
|
||||||
|
memoryCopy(0, 2, 3);
|
||||||
|
assertBufferContents(u8a, [10, 20, 30, 20, 30]);
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function TestMemoryCopyOutOfBounds() {
|
||||||
|
const mem = new WebAssembly.Memory({initial: 1});
|
||||||
|
const memoryCopy = getMemoryCopy(mem);
|
||||||
|
|
||||||
|
memoryCopy(0, 0, kPageSize);
|
||||||
|
|
||||||
|
// Source range must not be out of bounds.
|
||||||
|
assertTraps(kTrapMemOutOfBounds, () => memoryCopy(0, 1, kPageSize));
|
||||||
|
assertTraps(kTrapMemOutOfBounds, () => memoryCopy(0, 1000, kPageSize));
|
||||||
|
assertTraps(kTrapMemOutOfBounds, () => memoryCopy(0, kPageSize, 1));
|
||||||
|
|
||||||
|
// Destination range must not be out of bounds.
|
||||||
|
assertTraps(kTrapMemOutOfBounds, () => memoryCopy(1, 0, kPageSize));
|
||||||
|
assertTraps(kTrapMemOutOfBounds, () => memoryCopy(1000, 0, kPageSize));
|
||||||
|
assertTraps(kTrapMemOutOfBounds, () => memoryCopy(kPageSize, 0, 1));
|
||||||
|
|
||||||
|
// Make sure bounds aren't checked with 32-bit wrapping.
|
||||||
|
assertTraps(kTrapMemOutOfBounds, () => memoryCopy(1, 1, -1));
|
||||||
|
|
||||||
|
mem.grow(1);
|
||||||
|
|
||||||
|
// Works properly after grow.
|
||||||
|
memoryCopy(0, kPageSize, 1000);
|
||||||
|
|
||||||
|
// Traps at new boundary.
|
||||||
|
assertTraps(
|
||||||
|
kTrapMemOutOfBounds, () => memoryCopy(0, kPageSize + 1, kPageSize));
|
||||||
|
})();
|
||||||
|
|
||||||
|
function getMemoryFill(mem) {
|
||||||
|
const builder = new WasmModuleBuilder();
|
||||||
|
builder.addImportedMemory("", "mem", 0);
|
||||||
|
builder.addFunction("fill", kSig_v_iii).addBody([
|
||||||
|
kExprGetLocal, 0, // Dest.
|
||||||
|
kExprGetLocal, 1, // Byte value.
|
||||||
|
kExprGetLocal, 2, // Size.
|
||||||
|
kNumericPrefix, kExprMemoryFill, 0,
|
||||||
|
]).exportAs("fill");
|
||||||
|
return builder.instantiate({'': {mem}}).exports.fill;
|
||||||
|
}
|
||||||
|
|
||||||
|
(function TestMemoryFill() {
|
||||||
|
const mem = new WebAssembly.Memory({initial: 1});
|
||||||
|
const memoryFill = getMemoryFill(mem);
|
||||||
|
|
||||||
|
const u8a = new Uint8Array(mem.buffer);
|
||||||
|
|
||||||
|
memoryFill(1, 33, 5);
|
||||||
|
assertBufferContents(u8a, [0, 33, 33, 33, 33, 33]);
|
||||||
|
|
||||||
|
memoryFill(4, 66, 4);
|
||||||
|
assertBufferContents(u8a, [0, 33, 33, 33, 66, 66, 66, 66]);
|
||||||
|
|
||||||
|
// Fill 0 bytes does nothing.
|
||||||
|
memoryFill(4, 66, 0);
|
||||||
|
assertBufferContents(u8a, [0, 33, 33, 33, 66, 66, 66, 66]);
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function TestMemoryFillValueWrapsToByte() {
|
||||||
|
const mem = new WebAssembly.Memory({initial: 1});
|
||||||
|
const memoryFill = getMemoryFill(mem);
|
||||||
|
|
||||||
|
const u8a = new Uint8Array(mem.buffer);
|
||||||
|
|
||||||
|
memoryFill(0, 1000, 3);
|
||||||
|
const expected = 1000 & 255;
|
||||||
|
assertBufferContents(u8a, [expected, expected, expected]);
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function TestMemoryFillOutOfBounds() {
|
||||||
|
const mem = new WebAssembly.Memory({initial: 1});
|
||||||
|
const memoryFill = getMemoryFill(mem);
|
||||||
|
const v = 123;
|
||||||
|
|
||||||
|
memoryFill(0, 0, kPageSize);
|
||||||
|
|
||||||
|
// Destination range must not be out of bounds.
|
||||||
|
assertTraps(kTrapMemOutOfBounds, () => memoryFill(1, v, kPageSize));
|
||||||
|
assertTraps(kTrapMemOutOfBounds, () => memoryFill(1000, v, kPageSize));
|
||||||
|
assertTraps(kTrapMemOutOfBounds, () => memoryFill(kPageSize, v, 1));
|
||||||
|
|
||||||
|
// Make sure bounds aren't checked with 32-bit wrapping.
|
||||||
|
assertTraps(kTrapMemOutOfBounds, () => memoryFill(1, v, -1));
|
||||||
|
|
||||||
|
mem.grow(1);
|
||||||
|
|
||||||
|
// Works properly after grow.
|
||||||
|
memoryFill(kPageSize, v, 1000);
|
||||||
|
|
||||||
|
// Traps at new boundary.
|
||||||
|
assertTraps(
|
||||||
|
kTrapMemOutOfBounds, () => memoryFill(kPageSize + 1, v, kPageSize));
|
||||||
})();
|
})();
|
||||||
|
@ -353,8 +353,19 @@ let kExprF32ReinterpretI32 = 0xbe;
|
|||||||
let kExprF64ReinterpretI64 = 0xbf;
|
let kExprF64ReinterpretI64 = 0xbf;
|
||||||
|
|
||||||
// Prefix opcodes
|
// Prefix opcodes
|
||||||
|
let kNumericPrefix = 0xfc;
|
||||||
let kAtomicPrefix = 0xfe;
|
let kAtomicPrefix = 0xfe;
|
||||||
|
|
||||||
|
// Numeric opcodes.
|
||||||
|
let kExprMemoryInit = 0x08;
|
||||||
|
let kExprMemoryDrop = 0x09;
|
||||||
|
let kExprMemoryCopy = 0x0a;
|
||||||
|
let kExprMemoryFill = 0x0b;
|
||||||
|
let kExprTableInit = 0x0c;
|
||||||
|
let kExprTableDrop = 0x0d;
|
||||||
|
let kExprTableCopy = 0x0e;
|
||||||
|
|
||||||
|
// Atomic opcodes.
|
||||||
let kExprAtomicWake = 0x00;
|
let kExprAtomicWake = 0x00;
|
||||||
let kExprI32AtomicWait = 0x01;
|
let kExprI32AtomicWait = 0x01;
|
||||||
let kExprI64AtomicWait = 0x02;
|
let kExprI64AtomicWait = 0x02;
|
||||||
|
Loading…
Reference in New Issue
Block a user