Commit 97cdf35f authored by Ben L. Titzer's avatar Ben L. Titzer Committed by Commit Bot

[wasm] Implement table.drop

The table.drop bytecode "drops" the backing storage for an element
initializer. In the V8 implementation, this is a nop, other than
updating a per-instance boolean array so that two drops of the same
segment or a drop of an active segment will trap.

This is implemented with inline code in TurboFan in order to be symmetric
to memory.drop, but could as easily be a runtime call to be supported in
Liftoff.

R=mstarzinger@chromium.org
CC=​binji@chromium.org
BUG=v8:7747

Change-Id: Ic017398eaa764dd3a9ff19523453ff7142c9abf6
Reviewed-on: https://chromium-review.googlesource.com/c/1408996Reviewed-by: 's avatarBen Smith <binji@chromium.org>
Commit-Queue: Ben Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58817}
parent 4c8dd3c9
...@@ -1259,6 +1259,7 @@ namespace internal { ...@@ -1259,6 +1259,7 @@ namespace internal {
TFS(ThrowWasmTrapFuncInvalid) \ TFS(ThrowWasmTrapFuncInvalid) \
TFS(ThrowWasmTrapFuncSigMismatch) \ TFS(ThrowWasmTrapFuncSigMismatch) \
TFS(ThrowWasmTrapDataSegmentDropped) \ TFS(ThrowWasmTrapDataSegmentDropped) \
TFS(ThrowWasmTrapElemSegmentDropped) \
TFC(BigIntToWasmI64, BigIntToWasmI64, 1) \ TFC(BigIntToWasmI64, BigIntToWasmI64, 1) \
TFC(WasmBigIntToI64, BigIntToI64, 1) \ TFC(WasmBigIntToI64, BigIntToI64, 1) \
\ \
......
...@@ -4357,6 +4357,21 @@ Node* WasmGraphBuilder::MemoryFill(Node* dst, Node* value, Node* size, ...@@ -4357,6 +4357,21 @@ Node* WasmGraphBuilder::MemoryFill(Node* dst, Node* value, Node* size,
return BuildCCall(&sig, function, dst, value, size); return BuildCCall(&sig, function, dst, value, size);
} }
Node* WasmGraphBuilder::CheckElemSegmentIsPassiveAndNotDropped(
uint32_t elem_segment_index, wasm::WasmCodePosition position) {
// The elem segment index must be in bounds since it is required by
// validation.
DCHECK_LT(elem_segment_index, env_->module->elem_segments.size());
Node* dropped_elem_segments =
LOAD_INSTANCE_FIELD(DroppedElemSegments, MachineType::Pointer());
Node* is_segment_dropped = SetEffect(graph()->NewNode(
mcgraph()->machine()->Load(MachineType::Uint8()), dropped_elem_segments,
mcgraph()->IntPtrConstant(elem_segment_index), Effect(), Control()));
TrapIfTrue(wasm::kTrapElemSegmentDropped, is_segment_dropped, position);
return dropped_elem_segments;
}
Node* WasmGraphBuilder::TableInit(uint32_t table_index, Node* WasmGraphBuilder::TableInit(uint32_t table_index,
uint32_t elem_segment_index, Node* dst, uint32_t elem_segment_index, Node* dst,
Node* src, Node* size, Node* src, Node* size,
...@@ -4378,7 +4393,14 @@ Node* WasmGraphBuilder::TableInit(uint32_t table_index, ...@@ -4378,7 +4393,14 @@ Node* WasmGraphBuilder::TableInit(uint32_t table_index,
Node* WasmGraphBuilder::TableDrop(uint32_t elem_segment_index, Node* WasmGraphBuilder::TableDrop(uint32_t elem_segment_index,
wasm::WasmCodePosition position) { wasm::WasmCodePosition position) {
UNREACHABLE(); Node* dropped_elem_segments =
CheckElemSegmentIsPassiveAndNotDropped(elem_segment_index, position);
const Operator* store_op = mcgraph()->machine()->Store(
StoreRepresentation(MachineRepresentation::kWord8, kNoWriteBarrier));
return SetEffect(
graph()->NewNode(store_op, dropped_elem_segments,
mcgraph()->IntPtrConstant(elem_segment_index),
mcgraph()->Int32Constant(1), Effect(), Control()));
} }
Node* WasmGraphBuilder::TableCopy(Node* dst, Node* src, Node* size, Node* WasmGraphBuilder::TableCopy(Node* dst, Node* src, Node* size,
......
...@@ -358,6 +358,8 @@ class WasmGraphBuilder { ...@@ -358,6 +358,8 @@ class WasmGraphBuilder {
// segment is active or has been dropped. // segment is active or has been dropped.
Node* CheckDataSegmentIsPassiveAndNotDropped(uint32_t data_segment_index, Node* CheckDataSegmentIsPassiveAndNotDropped(uint32_t data_segment_index,
wasm::WasmCodePosition position); wasm::WasmCodePosition position);
Node* CheckElemSegmentIsPassiveAndNotDropped(uint32_t elem_segment_index,
wasm::WasmCodePosition position);
Node* MemoryInit(uint32_t data_segment_index, Node* dst, Node* src, Node* MemoryInit(uint32_t data_segment_index, Node* dst, Node* src,
Node* size, wasm::WasmCodePosition position); Node* size, wasm::WasmCodePosition position);
Node* MemoryCopy(Node* dst, Node* src, Node* size, Node* MemoryCopy(Node* dst, Node* src, Node* size,
......
...@@ -1645,7 +1645,8 @@ enum class LoadSensitivity { ...@@ -1645,7 +1645,8 @@ enum class LoadSensitivity {
V(TrapFloatUnrepresentable) \ V(TrapFloatUnrepresentable) \
V(TrapFuncInvalid) \ V(TrapFuncInvalid) \
V(TrapFuncSigMismatch) \ V(TrapFuncSigMismatch) \
V(TrapDataSegmentDropped) V(TrapDataSegmentDropped) \
V(TrapElemSegmentDropped)
enum KeyedAccessLoadMode { enum KeyedAccessLoadMode {
STANDARD_LOAD, STANDARD_LOAD,
......
...@@ -516,6 +516,7 @@ namespace internal { ...@@ -516,6 +516,7 @@ namespace internal {
T(WasmTrapFuncSigMismatch, "function signature mismatch") \ T(WasmTrapFuncSigMismatch, "function signature mismatch") \
T(WasmTrapTypeError, "wasm function signature contains illegal type") \ T(WasmTrapTypeError, "wasm function signature contains illegal type") \
T(WasmTrapDataSegmentDropped, "data segment has been dropped") \ T(WasmTrapDataSegmentDropped, "data segment has been dropped") \
T(WasmTrapElemSegmentDropped, "element segment has been dropped") \
T(WasmExceptionError, "wasm exception") \ T(WasmExceptionError, "wasm exception") \
/* Asm.js validation related */ \ /* Asm.js validation related */ \
T(AsmJsInvalid, "Invalid asm.js: %") \ T(AsmJsInvalid, "Invalid asm.js: %") \
......
...@@ -203,6 +203,8 @@ PRIMITIVE_ACCESSORS(WasmInstanceObject, data_segment_sizes, uint32_t*, ...@@ -203,6 +203,8 @@ PRIMITIVE_ACCESSORS(WasmInstanceObject, data_segment_sizes, uint32_t*,
kDataSegmentSizesOffset) kDataSegmentSizesOffset)
PRIMITIVE_ACCESSORS(WasmInstanceObject, dropped_data_segments, byte*, PRIMITIVE_ACCESSORS(WasmInstanceObject, dropped_data_segments, byte*,
kDroppedDataSegmentsOffset) kDroppedDataSegmentsOffset)
PRIMITIVE_ACCESSORS(WasmInstanceObject, dropped_elem_segments, byte*,
kDroppedElemSegmentsOffset)
ACCESSORS(WasmInstanceObject, module_object, WasmModuleObject, ACCESSORS(WasmInstanceObject, module_object, WasmModuleObject,
kModuleObjectOffset) kModuleObjectOffset)
......
...@@ -67,7 +67,8 @@ class WasmInstanceNativeAllocations { ...@@ -67,7 +67,8 @@ class WasmInstanceNativeAllocations {
WasmInstanceNativeAllocations(Handle<WasmInstanceObject> instance, WasmInstanceNativeAllocations(Handle<WasmInstanceObject> instance,
size_t num_imported_functions, size_t num_imported_functions,
size_t num_imported_mutable_globals, size_t num_imported_mutable_globals,
size_t num_data_segments) { size_t num_data_segments,
size_t num_elem_segments) {
SET(instance, imported_function_targets, SET(instance, imported_function_targets,
reinterpret_cast<Address*>( reinterpret_cast<Address*>(
calloc(num_imported_functions, sizeof(Address)))); calloc(num_imported_functions, sizeof(Address))));
...@@ -81,6 +82,8 @@ class WasmInstanceNativeAllocations { ...@@ -81,6 +82,8 @@ class WasmInstanceNativeAllocations {
calloc(num_data_segments, sizeof(uint32_t)))); calloc(num_data_segments, sizeof(uint32_t))));
SET(instance, dropped_data_segments, SET(instance, dropped_data_segments,
reinterpret_cast<uint8_t*>(calloc(num_data_segments, sizeof(uint8_t)))); reinterpret_cast<uint8_t*>(calloc(num_data_segments, sizeof(uint8_t))));
SET(instance, dropped_elem_segments,
reinterpret_cast<uint8_t*>(calloc(num_elem_segments, sizeof(uint8_t))));
} }
~WasmInstanceNativeAllocations() { ~WasmInstanceNativeAllocations() {
::free(indirect_function_table_sig_ids_); ::free(indirect_function_table_sig_ids_);
...@@ -97,6 +100,8 @@ class WasmInstanceNativeAllocations { ...@@ -97,6 +100,8 @@ class WasmInstanceNativeAllocations {
data_segment_sizes_ = nullptr; data_segment_sizes_ = nullptr;
::free(dropped_data_segments_); ::free(dropped_data_segments_);
dropped_data_segments_ = nullptr; dropped_data_segments_ = nullptr;
::free(dropped_elem_segments_);
dropped_elem_segments_ = nullptr;
} }
// Resizes the indirect function table. // Resizes the indirect function table.
void resize_indirect_function_table(Isolate* isolate, void resize_indirect_function_table(Isolate* isolate,
...@@ -141,6 +146,7 @@ class WasmInstanceNativeAllocations { ...@@ -141,6 +146,7 @@ class WasmInstanceNativeAllocations {
Address* data_segment_starts_ = nullptr; Address* data_segment_starts_ = nullptr;
uint32_t* data_segment_sizes_ = nullptr; uint32_t* data_segment_sizes_ = nullptr;
uint8_t* dropped_data_segments_ = nullptr; uint8_t* dropped_data_segments_ = nullptr;
uint8_t* dropped_elem_segments_ = nullptr;
#undef SET #undef SET
}; };
...@@ -1306,7 +1312,8 @@ Handle<WasmInstanceObject> WasmInstanceObject::New( ...@@ -1306,7 +1312,8 @@ Handle<WasmInstanceObject> WasmInstanceObject::New(
size_t native_allocations_size = EstimateNativeAllocationsSize(module); size_t native_allocations_size = EstimateNativeAllocationsSize(module);
auto native_allocations = Managed<WasmInstanceNativeAllocations>::Allocate( auto native_allocations = Managed<WasmInstanceNativeAllocations>::Allocate(
isolate, native_allocations_size, instance, num_imported_functions, isolate, native_allocations_size, instance, num_imported_functions,
num_imported_mutable_globals, num_data_segments); num_imported_mutable_globals, num_data_segments,
module->elem_segments.size());
instance->set_managed_native_allocations(*native_allocations); instance->set_managed_native_allocations(*native_allocations);
Handle<FixedArray> imported_function_refs = Handle<FixedArray> imported_function_refs =
...@@ -1342,6 +1349,7 @@ Handle<WasmInstanceObject> WasmInstanceObject::New( ...@@ -1342,6 +1349,7 @@ Handle<WasmInstanceObject> WasmInstanceObject::New(
module_object->set_weak_instance_list(*weak_instance_list); module_object->set_weak_instance_list(*weak_instance_list);
InitDataSegmentArrays(instance, module_object); InitDataSegmentArrays(instance, module_object);
InitElemSegmentArrays(instance, module_object);
return instance; return instance;
} }
...@@ -1374,6 +1382,20 @@ void WasmInstanceObject::InitDataSegmentArrays( ...@@ -1374,6 +1382,20 @@ void WasmInstanceObject::InitDataSegmentArrays(
} }
} }
void WasmInstanceObject::InitElemSegmentArrays(
Handle<WasmInstanceObject> instance,
Handle<WasmModuleObject> module_object) {
auto module = module_object->module();
auto num_elem_segments = module->elem_segments.size();
for (size_t i = 0; i < num_elem_segments; ++i) {
const wasm::WasmElemSegment& segment = module->elem_segments[i];
// Set the active segments to being already dropped, since table.init on
// a dropped passive segment and an active segment have the same
// behavior.
instance->dropped_elem_segments()[i] = segment.active ? 1 : 0;
}
}
Address WasmInstanceObject::GetCallTarget(uint32_t func_index) { Address WasmInstanceObject::GetCallTarget(uint32_t func_index) {
wasm::NativeModule* native_module = module_object()->native_module(); wasm::NativeModule* native_module = module_object()->native_module();
if (func_index < native_module->num_imported_functions()) { if (func_index < native_module->num_imported_functions()) {
......
...@@ -423,6 +423,7 @@ class WasmInstanceObject : public JSObject { ...@@ -423,6 +423,7 @@ class WasmInstanceObject : public JSObject {
DECL_PRIMITIVE_ACCESSORS(data_segment_starts, Address*) DECL_PRIMITIVE_ACCESSORS(data_segment_starts, Address*)
DECL_PRIMITIVE_ACCESSORS(data_segment_sizes, uint32_t*) DECL_PRIMITIVE_ACCESSORS(data_segment_sizes, uint32_t*)
DECL_PRIMITIVE_ACCESSORS(dropped_data_segments, byte*) DECL_PRIMITIVE_ACCESSORS(dropped_data_segments, byte*)
DECL_PRIMITIVE_ACCESSORS(dropped_elem_segments, byte*)
V8_INLINE void clear_padding(); V8_INLINE void clear_padding();
...@@ -470,6 +471,7 @@ class WasmInstanceObject : public JSObject { ...@@ -470,6 +471,7 @@ class WasmInstanceObject : public JSObject {
V(kDataSegmentStartsOffset, kSystemPointerSize) \ V(kDataSegmentStartsOffset, kSystemPointerSize) \
V(kDataSegmentSizesOffset, kSystemPointerSize) \ V(kDataSegmentSizesOffset, kSystemPointerSize) \
V(kDroppedDataSegmentsOffset, kSystemPointerSize) \ V(kDroppedDataSegmentsOffset, kSystemPointerSize) \
V(kDroppedElemSegmentsOffset, kSystemPointerSize) \
/* Header size. */ \ /* Header size. */ \
V(kSize, 0) V(kSize, 0)
...@@ -505,6 +507,8 @@ class WasmInstanceObject : public JSObject { ...@@ -505,6 +507,8 @@ class WasmInstanceObject : public JSObject {
private: private:
static void InitDataSegmentArrays(Handle<WasmInstanceObject>, static void InitDataSegmentArrays(Handle<WasmInstanceObject>,
Handle<WasmModuleObject>); Handle<WasmModuleObject>);
static void InitElemSegmentArrays(Handle<WasmInstanceObject>,
Handle<WasmModuleObject>);
}; };
// Representation of WebAssembly.Exception JavaScript-level object. // Representation of WebAssembly.Exception JavaScript-level object.
......
...@@ -343,8 +343,35 @@ function getMemoryFill(mem) { ...@@ -343,8 +343,35 @@ function getMemoryFill(mem) {
// init(1, 2, 3); // init(1, 2, 3);
})(); })();
(function TestTableDrop0() { (function TestTableDropActive() {
// TODO(titzer): initial testcase for table drop const builder = new WasmModuleBuilder();
builder.setTableBounds(5, 5);
builder.addElementSegment(0, false, [0, 0, 0]);
builder.addFunction('drop', kSig_v_v)
.addBody([
kNumericPrefix, kExprTableDrop,
0, // Element segment index.
])
.exportAs('drop');
const instance = builder.instantiate();
assertTraps(kTrapElemSegmentDropped, () => instance.exports.drop());
})();
(function TestTableDropTwice() {
const builder = new WasmModuleBuilder();
builder.setTableBounds(5, 5);
builder.addPassiveElementSegment([0, 0, 0]);
builder.addFunction('drop', kSig_v_v)
.addBody([
kNumericPrefix, kExprTableDrop,
0, // Element segment index.
])
.exportAs('drop');
const instance = builder.instantiate();
instance.exports.drop();
assertTraps(kTrapElemSegmentDropped, () => instance.exports.drop());
})(); })();
(function TestTableCopy0() { (function TestTableCopy0() {
......
...@@ -457,6 +457,7 @@ let kTrapFuncSigMismatch = 7; ...@@ -457,6 +457,7 @@ let kTrapFuncSigMismatch = 7;
let kTrapTypeError = 8; let kTrapTypeError = 8;
let kTrapUnalignedAccess = 9; let kTrapUnalignedAccess = 9;
let kTrapDataSegmentDropped = 10; let kTrapDataSegmentDropped = 10;
let kTrapElemSegmentDropped = 11;
let kTrapMsgs = [ let kTrapMsgs = [
"unreachable", "unreachable",
...@@ -469,7 +470,8 @@ let kTrapMsgs = [ ...@@ -469,7 +470,8 @@ let kTrapMsgs = [
"function signature mismatch", "function signature mismatch",
"wasm function signature contains illegal type", "wasm function signature contains illegal type",
"operation does not support unaligned accesses", "operation does not support unaligned accesses",
"data segment has been dropped" "data segment has been dropped",
"element segment has been dropped"
]; ];
function assertTraps(trap, code) { function assertTraps(trap, code) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment