[wasm] Make interpreter clear reference stack slots.
This makes sure the interpreter clears any stale references from the
reference stack when they are popped/dropped. Otherwise stale values
would unnecessarily increase lifetime of operand stack slots.
R=ahaas@chromium.org
BUG=v8:7581
Change-Id: I6b8be56a815327229a66ea0c97b3646ac64f6461
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1612905
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61510}
diff --git a/src/wasm/wasm-interpreter.cc b/src/wasm/wasm-interpreter.cc
index a19f3a6..831413f 100644
--- a/src/wasm/wasm-interpreter.cc
+++ b/src/wasm/wasm-interpreter.cc
@@ -1172,7 +1172,7 @@
void Reset() {
TRACE("----- RESET -----\n");
- sp_ = stack_.get();
+ ResetStack(0);
frames_.clear();
state_ = WasmInterpreter::STOPPED;
trap_reason_ = kTrapCount;
@@ -1241,7 +1241,7 @@
// first).
DCHECK_EQ(activations_.back().fp, frames_.size());
DCHECK_LE(activations_.back().sp, StackHeight());
- sp_ = stack_.get() + activations_.back().sp;
+ ResetStack(activations_.back().sp);
activations_.pop_back();
}
@@ -1315,7 +1315,7 @@
}
TRACE(" => drop frame #%zu (#%u @%zu)\n", frames_.size() - 1,
code->function->func_index, frame.pc);
- sp_ = stack_.get() + frame.sp;
+ ResetStack(frame.sp);
frames_.pop_back();
}
TRACE("----- UNWIND -----\n");
@@ -1342,8 +1342,6 @@
// kept in a separate on-heap reference stack to make the GC trace them.
// TODO(mstarzinger): Optimize simple stack operations (like "get_local",
// "set_local", and "tee_local") so that they don't require a handle scope.
- // TODO(mstarzinger): Ensure unused slots on the reference stack are cleared
- // so that they don't keep alive old/stale references unnecessarily long.
// TODO(mstarzinger): Consider optimizing activations that use no reference
// values to avoid allocating the reference stack entirely.
class StackValue {
@@ -1363,11 +1361,30 @@
int ref_index = static_cast<int>(index);
Isolate* isolate = thread->isolate_;
Handle<Object> ref(thread->reference_stack()->get(ref_index), isolate);
+ DCHECK(!ref->IsTheHole(isolate));
return WasmValue(ref);
}
bool IsReferenceValue() const { return value_.type() == kWasmAnyRef; }
+ void ClearValue(ThreadImpl* thread, sp_t index) {
+ if (!IsReferenceValue()) return;
+ int ref_index = static_cast<int>(index);
+ Isolate* isolate = thread->isolate_;
+ thread->reference_stack()->set_the_hole(isolate, ref_index);
+ }
+
+ static void ClearValues(ThreadImpl* thread, sp_t index, int count) {
+ int ref_index = static_cast<int>(index);
+ thread->reference_stack()->FillWithHoles(ref_index, ref_index + count);
+ }
+
+ static bool IsClearedValue(ThreadImpl* thread, sp_t index) {
+ int ref_index = static_cast<int>(index);
+ Isolate* isolate = thread->isolate_;
+ return thread->reference_stack()->is_the_hole(isolate, ref_index);
+ }
+
private:
WasmValue value_;
};
@@ -1608,7 +1625,9 @@
reference_stack().MoveElements(isolate_, dst, src, len,
UPDATE_WRITE_BARRIER);
}
- sp_ = dest + arity;
+ // TODO(mstarzinger): Refactor the interface so that we don't have to
+ // recompute values here which are already known at the call-site.
+ ResetStack(StackHeight() - (sp_ - dest) + arity);
}
inline Address EffectiveAddress(uint32_t index) {
@@ -3373,7 +3392,9 @@
StackValue stack_value = *--sp_;
// Note that {StackHeight} depends on the current {sp} value, hence this
// operation is split into two statements to ensure proper evaluation order.
- return stack_value.ExtractValue(this, StackHeight());
+ WasmValue val = stack_value.ExtractValue(this, StackHeight());
+ stack_value.ClearValue(this, StackHeight());
+ return val;
}
void Drop(int n = 1) {
@@ -3381,6 +3402,7 @@
DCHECK_GT(frames_.size(), 0);
// Check that we don't pop into locals.
DCHECK_GE(StackHeight() - n, frames_.back().llimit());
+ StackValue::ClearValues(this, StackHeight() - n, n);
sp_ -= n;
}
@@ -3393,6 +3415,7 @@
void Push(WasmValue val) {
DCHECK_NE(kWasmStmt, val.type());
DCHECK_LE(1, stack_limit_ - sp_);
+ DCHECK(StackValue::IsClearedValue(this, StackHeight()));
StackValue stack_value(val, this, StackHeight());
// Note that {StackHeight} depends on the current {sp} value, hence this
// operation is split into two statements to ensure proper evaluation order.
@@ -3407,6 +3430,13 @@
}
}
+ void ResetStack(sp_t new_height) {
+ DCHECK_LE(new_height, StackHeight()); // Only allowed to shrink.
+ int count = static_cast<int>(StackHeight() - new_height);
+ StackValue::ClearValues(this, new_height, count);
+ sp_ = stack_.get() + new_height;
+ }
+
void EnsureStackSpace(size_t size) {
if (V8_LIKELY(static_cast<size_t>(stack_limit_ - sp_) >= size)) return;
size_t old_size = stack_limit_ - stack_.get();
@@ -3426,6 +3456,8 @@
Handle<FixedArray> old_ref_stack(reference_stack(), isolate_);
Handle<FixedArray> new_ref_stack =
isolate_->factory()->CopyFixedArrayAndGrow(old_ref_stack, grow_by);
+ new_ref_stack->FillWithHoles(static_cast<int>(old_size),
+ static_cast<int>(new_size));
reference_stack_cell_->set_value(*new_ref_stack);
}
@@ -3505,7 +3537,7 @@
if (code->kind() == WasmCode::kWasmToJsWrapper &&
!IsJSCompatibleSignature(sig, enabled_features.bigint)) {
- sp_ -= num_args; // Pop arguments before throwing.
+ Drop(num_args); // Pop arguments before throwing.
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kWasmTrapTypeError));
return TryHandleException(isolate);
@@ -3588,7 +3620,7 @@
maybe_retval.is_null() ? " with exception" : "");
// Pop arguments off the stack.
- sp_ -= num_args;
+ Drop(num_args);
if (maybe_retval.is_null()) {
// JSEntry may throw a stack overflow before we actually get to wasm code