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 {
TFS(ThrowWasmTrapFuncInvalid) \
TFS(ThrowWasmTrapFuncSigMismatch) \
TFS(ThrowWasmTrapDataSegmentDropped) \
TFS(ThrowWasmTrapElemSegmentDropped) \
TFC(BigIntToWasmI64, BigIntToWasmI64, 1) \
TFC(WasmBigIntToI64, BigIntToI64, 1) \
\
......
......@@ -4357,6 +4357,21 @@ Node* WasmGraphBuilder::MemoryFill(Node* dst, Node* value, Node* 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,
uint32_t elem_segment_index, Node* dst,
Node* src, Node* size,
......@@ -4378,7 +4393,14 @@ Node* WasmGraphBuilder::TableInit(uint32_t table_index,
Node* WasmGraphBuilder::TableDrop(uint32_t elem_segment_index,
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,
......
......@@ -358,6 +358,8 @@ class WasmGraphBuilder {
// segment is active or has been dropped.
Node* CheckDataSegmentIsPassiveAndNotDropped(uint32_t data_segment_index,
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* size, wasm::WasmCodePosition position);
Node* MemoryCopy(Node* dst, Node* src, Node* size,
......
......@@ -1645,7 +1645,8 @@ enum class LoadSensitivity {
V(TrapFloatUnrepresentable) \
V(TrapFuncInvalid) \
V(TrapFuncSigMismatch) \
V(TrapDataSegmentDropped)
V(TrapDataSegmentDropped) \
V(TrapElemSegmentDropped)
enum KeyedAccessLoadMode {
STANDARD_LOAD,
......
......@@ -516,6 +516,7 @@ namespace internal {
T(WasmTrapFuncSigMismatch, "function signature mismatch") \
T(WasmTrapTypeError, "wasm function signature contains illegal type") \
T(WasmTrapDataSegmentDropped, "data segment has been dropped") \
T(WasmTrapElemSegmentDropped, "element segment has been dropped") \
T(WasmExceptionError, "wasm exception") \
/* Asm.js validation related */ \
T(AsmJsInvalid, "Invalid asm.js: %") \
......
......@@ -203,6 +203,8 @@ PRIMITIVE_ACCESSORS(WasmInstanceObject, data_segment_sizes, uint32_t*,
kDataSegmentSizesOffset)
PRIMITIVE_ACCESSORS(WasmInstanceObject, dropped_data_segments, byte*,
kDroppedDataSegmentsOffset)
PRIMITIVE_ACCESSORS(WasmInstanceObject, dropped_elem_segments, byte*,
kDroppedElemSegmentsOffset)
ACCESSORS(WasmInstanceObject, module_object, WasmModuleObject,
kModuleObjectOffset)
......
......@@ -67,7 +67,8 @@ class WasmInstanceNativeAllocations {
WasmInstanceNativeAllocations(Handle<WasmInstanceObject> instance,
size_t num_imported_functions,
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,
reinterpret_cast<Address*>(
calloc(num_imported_functions, sizeof(Address))));
......@@ -81,6 +82,8 @@ class WasmInstanceNativeAllocations {
calloc(num_data_segments, sizeof(uint32_t))));
SET(instance, dropped_data_segments,
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() {
::free(indirect_function_table_sig_ids_);
......@@ -97,6 +100,8 @@ class WasmInstanceNativeAllocations {
data_segment_sizes_ = nullptr;
::free(dropped_data_segments_);
dropped_data_segments_ = nullptr;
::free(dropped_elem_segments_);
dropped_elem_segments_ = nullptr;
}
// Resizes the indirect function table.
void resize_indirect_function_table(Isolate* isolate,
......@@ -141,6 +146,7 @@ class WasmInstanceNativeAllocations {
Address* data_segment_starts_ = nullptr;
uint32_t* data_segment_sizes_ = nullptr;
uint8_t* dropped_data_segments_ = nullptr;
uint8_t* dropped_elem_segments_ = nullptr;
#undef SET
};
......@@ -1306,7 +1312,8 @@ Handle<WasmInstanceObject> WasmInstanceObject::New(
size_t native_allocations_size = EstimateNativeAllocationsSize(module);
auto native_allocations = Managed<WasmInstanceNativeAllocations>::Allocate(
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);
Handle<FixedArray> imported_function_refs =
......@@ -1342,6 +1349,7 @@ Handle<WasmInstanceObject> WasmInstanceObject::New(
module_object->set_weak_instance_list(*weak_instance_list);
InitDataSegmentArrays(instance, module_object);
InitElemSegmentArrays(instance, module_object);
return instance;
}
......@@ -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) {
wasm::NativeModule* native_module = module_object()->native_module();
if (func_index < native_module->num_imported_functions()) {
......
......@@ -423,6 +423,7 @@ class WasmInstanceObject : public JSObject {
DECL_PRIMITIVE_ACCESSORS(data_segment_starts, Address*)
DECL_PRIMITIVE_ACCESSORS(data_segment_sizes, uint32_t*)
DECL_PRIMITIVE_ACCESSORS(dropped_data_segments, byte*)
DECL_PRIMITIVE_ACCESSORS(dropped_elem_segments, byte*)
V8_INLINE void clear_padding();
......@@ -470,6 +471,7 @@ class WasmInstanceObject : public JSObject {
V(kDataSegmentStartsOffset, kSystemPointerSize) \
V(kDataSegmentSizesOffset, kSystemPointerSize) \
V(kDroppedDataSegmentsOffset, kSystemPointerSize) \
V(kDroppedElemSegmentsOffset, kSystemPointerSize) \
/* Header size. */ \
V(kSize, 0)
......@@ -505,6 +507,8 @@ class WasmInstanceObject : public JSObject {
private:
static void InitDataSegmentArrays(Handle<WasmInstanceObject>,
Handle<WasmModuleObject>);
static void InitElemSegmentArrays(Handle<WasmInstanceObject>,
Handle<WasmModuleObject>);
};
// Representation of WebAssembly.Exception JavaScript-level object.
......
......@@ -343,8 +343,35 @@ function getMemoryFill(mem) {
// init(1, 2, 3);
})();
(function TestTableDrop0() {
// TODO(titzer): initial testcase for table drop
(function TestTableDropActive() {
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() {
......
......@@ -457,6 +457,7 @@ let kTrapFuncSigMismatch = 7;
let kTrapTypeError = 8;
let kTrapUnalignedAccess = 9;
let kTrapDataSegmentDropped = 10;
let kTrapElemSegmentDropped = 11;
let kTrapMsgs = [
"unreachable",
......@@ -469,7 +470,8 @@ let kTrapMsgs = [
"function signature mismatch",
"wasm function signature contains illegal type",
"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) {
......
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