v8/test/cctest/test-concurrent-js-array.cc
Jakob Gruber 2e8443779d Reland "[compiler] Direct heap reads for JSArrayRef"
This is a reland of 76a2ab06a1

Changes since the original CL:
- Handle unserialized elements (optional result in getter).
- Merge should_access_heap and --turbo-direct-heap-access paths.
- Slightly update the serialized path in GetOwnCowElement.
- Fix the cctest, add a regression test.

Atomic JSObject::elements/JSArray::length setters are addressed
in this CL: crrev.com/c/2704076.

Original change's description:
> [compiler] Direct heap reads for JSArrayRef
>
> There are two aspects to the non-JSObject parts of JSArrayRef:
>
> - JSArrayRef::length. Relevant only in two spots, 1. when reading
> (immutable) array boilerplates and 2. for GetOwnCowElement.
>
> - JSArrayRef::GetOwnCowElement. May read into a copy-on-write backing
> store. Relies on the invariant that cow backing stores are immutable.
>
> This CL renames the length accessor to length_unsafe to make the
> danger explicit at callsites.
>
> For GetOwnCowElement the refactor is slightly larger, since we now
> need to read into the backing store while keeping full control of
> object reads (e.g. JSArray::length and JSArray::elements_kind). We
> make all reads explicit at the call site by requiring that elements,
> elements kind, and length are passed in as arguments to
> GetOwnCowElement. Inside GetOwnCowElement, consistency between these
> is *not* guaranteed due to concurrency. At runtime, consistency *is*
> guaranteed through the reference-equality check on the elements seen
> during compilation. The actual elements read is implemented in
> ConcurrentLookupIterator::GetOwnCowElement.
>
> Bug: v8:7790
> Change-Id: I9aa169ce4f2b1e2bfe1e9232007669eb7654a995
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2695403
> Commit-Queue: Jakob Gruber <jgruber@chromium.org>
> Reviewed-by: Georg Neis <neis@chromium.org>
> Reviewed-by: Igor Sheludko <ishell@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#72834}

Bug: v8:7790
Change-Id: I7577ad554992cafff81099a28c34f27db9bd8042
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2710431
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72904}
2021-02-22 12:15:50 +00:00

138 lines
4.5 KiB
C++

// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/api/api.h"
#include "src/base/platform/semaphore.h"
#include "src/handles/handles-inl.h"
#include "src/handles/local-handles-inl.h"
#include "src/handles/persistent-handles.h"
#include "src/heap/heap.h"
#include "src/heap/local-heap-inl.h"
#include "src/heap/local-heap.h"
#include "src/heap/parked-scope.h"
#include "src/objects/js-array-inl.h"
#include "test/cctest/cctest.h"
#include "test/cctest/heap/heap-utils.h"
namespace v8 {
namespace internal {
static constexpr int kNumArrays = 1024;
namespace {
class BackgroundThread final : public v8::base::Thread {
public:
BackgroundThread(Heap* heap, std::vector<Handle<JSArray>> handles,
std::unique_ptr<PersistentHandles> ph,
base::Semaphore* sema_started)
: v8::base::Thread(base::Thread::Options("ThreadWithLocalHeap")),
heap_(heap),
handles_(std::move(handles)),
ph_(std::move(ph)),
sema_started_(sema_started) {}
void Run() override {
LocalHeap local_heap(heap_, ThreadKind::kBackground, std::move(ph_));
UnparkedScope unparked_scope(&local_heap);
LocalHandleScope scope(&local_heap);
Isolate* isolate = heap_->isolate();
for (int i = 0; i < kNumArrays; i++) {
handles_[i] = local_heap.NewPersistentHandle(handles_[i]);
}
sema_started_->Signal();
// Iterate in the opposite directions as the main thread to make a race at
// some point more likely.
static constexpr int kIndex = 1;
for (int i = 0; i < kNumArrays; i++) {
Handle<JSArray> x = handles_[i];
Handle<FixedArrayBase> elements =
local_heap.NewPersistentHandle(x->elements(isolate, kRelaxedLoad));
ElementsKind elements_kind = x->map(isolate).elements_kind();
// Mirroring the conditions in JSArrayRef::GetOwnCowElement.
if (!IsSmiOrObjectElementsKind(elements_kind)) continue;
if (elements->map() != ReadOnlyRoots(isolate).fixed_cow_array_map()) {
continue;
}
base::Optional<Object> result =
ConcurrentLookupIterator::TryGetOwnCowElement(
isolate, FixedArray::cast(*elements), elements_kind,
Smi::ToInt(x->length(isolate, kRelaxedLoad)), kIndex);
if (result.has_value()) {
// On any success, the elements at index 1 must be the original value
// Smi(1).
CHECK(result.value().IsSmi());
CHECK_EQ(Smi::ToInt(result.value()), 1);
}
}
}
private:
Heap* heap_;
std::vector<Handle<JSArray>> handles_;
std::unique_ptr<PersistentHandles> ph_;
base::Semaphore* sema_started_;
};
TEST(ArrayWithCowElements) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
std::unique_ptr<PersistentHandles> ph = isolate->NewPersistentHandles();
std::vector<Handle<JSArray>> handles;
std::vector<Handle<JSArray>> persistent_handles;
HandleScope handle_scope(isolate);
// Create kNumArrays arrays with COW backing stores.
CompileRun(
"function f() { return [0,1,2,3,4]; }\n"
"const xs = [];\n"
"let i = 0;\n");
for (int i = 0; i < kNumArrays; i++) {
Handle<JSArray> x = Handle<JSArray>::cast(Utils::OpenHandle(
*CompileRunChecked(CcTest::isolate(), "xs[i++] = f();")));
CHECK_EQ(x->elements().map(), ReadOnlyRoots(isolate).fixed_cow_array_map());
handles.push_back(x);
persistent_handles.push_back(ph->NewHandle(x));
}
base::Semaphore sema_started(0);
// Pass persistent handles to background thread.
std::unique_ptr<BackgroundThread> thread(new BackgroundThread(
isolate->heap(), persistent_handles, std::move(ph), &sema_started));
CHECK(thread->Start());
sema_started.Wait();
// On the main thread, mutate the arrays, converting to a non-COW backing
// store.
static const char* const kMutators[] = {
"xs[--i].length--;", "xs[--i].length++;", "xs[--i].length = 0;",
"xs[--i][1] = 42;", "delete xs[--i][1];", "xs[--i].push(42);",
"xs[--i].pop();", "xs[--i][1] = 1.5;", "xs[--i][1] = {};",
};
static const int kNumMutators = arraysize(kMutators);
for (int i = kNumArrays - 1; i >= 0; i--) {
CompileRunChecked(CcTest::isolate(), kMutators[i % kNumMutators]);
CHECK_NE(handles[i]->elements().map(),
ReadOnlyRoots(isolate).fixed_cow_array_map());
}
thread->Join();
}
} // anonymous namespace
} // namespace internal
} // namespace v8