Commit 2e844377 authored by Jakob Gruber's avatar Jakob Gruber Committed by Commit Bot

Reland "[compiler] Direct heap reads for JSArrayRef"

This is a reland of 76a2ab06

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: 's avatarIgor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72904}
parent 1c507667
......@@ -347,7 +347,7 @@ class JSObjectRef : public JSReceiverRef {
Representation field_representation, FieldIndex index,
SerializationPolicy policy =
SerializationPolicy::kAssumeSerialized) const;
FixedArrayBaseRef elements() const;
base::Optional<FixedArrayBaseRef> elements() const;
void SerializeElements();
void EnsureElementsTenured();
ElementsKind GetElementsKind() const;
......@@ -833,13 +833,24 @@ class JSArrayRef : public JSObjectRef {
Handle<JSArray> object() const;
ObjectRef length() const;
// The `length` property of boilerplate JSArray objects. Boilerplates are
// immutable after initialization. Must not be used for non-boilerplate
// JSArrays.
ObjectRef GetBoilerplateLength() const;
// Return the element at key {index} if the array has a copy-on-write elements
// storage and {index} is known to be an own data property.
// Note the value returned by this function is only valid if we ensure at
// runtime that the backing store has not changed.
base::Optional<ObjectRef> GetOwnCowElement(
uint32_t index, SerializationPolicy policy =
SerializationPolicy::kAssumeSerialized) const;
FixedArrayBaseRef elements_ref, uint32_t index,
SerializationPolicy policy =
SerializationPolicy::kAssumeSerialized) const;
// The `JSArray::length` property; not safe to use in general, but can be
// used in some special cases that guarantee a valid `length` value despite
// concurrent reads.
ObjectRef length_unsafe() const;
};
class ScopeInfoRef : public HeapObjectRef {
......
......@@ -1735,7 +1735,7 @@ Node* JSCreateLowering::AllocateFastLiteral(Node* effect, Node* control,
JSArrayRef boilerplate_array = boilerplate.AsJSArray();
builder.Store(
AccessBuilder::ForJSArrayLength(boilerplate_array.GetElementsKind()),
boilerplate_array.length());
boilerplate_array.GetBoilerplateLength());
}
for (auto const& inobject_field : inobject_fields) {
builder.Store(inobject_field.first, inobject_field.second);
......@@ -1746,7 +1746,7 @@ Node* JSCreateLowering::AllocateFastLiteral(Node* effect, Node* control,
Node* JSCreateLowering::AllocateFastLiteralElements(Node* effect, Node* control,
JSObjectRef boilerplate,
AllocationType allocation) {
FixedArrayBaseRef boilerplate_elements = boilerplate.elements();
FixedArrayBaseRef boilerplate_elements = boilerplate.elements().value();
// Empty or copy-on-write elements just store a constant.
int const elements_length = boilerplate_elements.length();
......@@ -1754,7 +1754,7 @@ Node* JSCreateLowering::AllocateFastLiteralElements(Node* effect, Node* control,
if (boilerplate_elements.length() == 0 || elements_map.IsFixedCowArrayMap()) {
if (allocation == AllocationType::kOld) {
boilerplate.EnsureElementsTenured();
boilerplate_elements = boilerplate.elements();
boilerplate_elements = boilerplate.elements().value();
}
return jsgraph()->HeapConstant(boilerplate_elements.object());
}
......
......@@ -506,6 +506,7 @@ void JSObjectData::SerializeObjectCreateMap(JSHeapBroker* broker) {
}
namespace {
base::Optional<ObjectRef> GetOwnElementFromHeap(JSHeapBroker* broker,
Handle<Object> receiver,
uint32_t index,
......@@ -1802,7 +1803,10 @@ class JSArrayData : public JSObjectData {
Handle<JSArray> object);
void Serialize(JSHeapBroker* broker);
ObjectData* length() const { return length_; }
ObjectData* length() const {
CHECK(serialized_);
return length_;
}
ObjectData* GetOwnElement(
JSHeapBroker* broker, uint32_t index,
......@@ -1824,6 +1828,8 @@ JSArrayData::JSArrayData(JSHeapBroker* broker, ObjectData** storage,
: JSObjectData(broker, storage, object), own_elements_(broker->zone()) {}
void JSArrayData::Serialize(JSHeapBroker* broker) {
CHECK(!FLAG_turbo_direct_heap_access);
if (serialized_) return;
serialized_ = true;
......@@ -2224,7 +2230,10 @@ bool JSObjectData::cow_or_empty_elements_tenured() const {
return cow_or_empty_elements_tenured_;
}
ObjectData* JSObjectData::elements() const { return elements_; }
ObjectData* JSObjectData::elements() const {
CHECK(serialized_elements_);
return elements_;
}
void JSObjectData::SerializeAsBoilerplate(JSHeapBroker* broker) {
SerializeRecursiveAsBoilerplate(broker, kMaxFastLiteralDepth);
......@@ -2353,6 +2362,8 @@ void JSObjectData::SerializeRecursiveAsBoilerplate(JSHeapBroker* broker,
}
DCHECK_NULL(elements_);
DCHECK(!serialized_elements_);
serialized_elements_ = true;
elements_ = broker->GetOrCreateData(elements_object);
DCHECK(elements_->IsFixedArrayBase());
......@@ -2425,7 +2436,9 @@ void JSObjectData::SerializeRecursiveAsBoilerplate(JSHeapBroker* broker,
map()->AsMap()->SerializeOwnDescriptors(broker);
}
if (IsJSArray()) AsJSArray()->Serialize(broker);
if (IsJSArray() && !FLAG_turbo_direct_heap_access) {
AsJSArray()->Serialize(broker);
}
}
void RegExpBoilerplateDescriptionData::Serialize(JSHeapBroker* broker) {
......@@ -3193,7 +3206,7 @@ void JSObjectRef::SerializeElements() {
void JSObjectRef::EnsureElementsTenured() {
if (data_->should_access_heap()) {
Handle<FixedArrayBase> object_elements = elements().object();
Handle<FixedArrayBase> object_elements = elements().value().object();
if (ObjectInYoungGeneration(*object_elements)) {
// If we would like to pretenure a fixed cow array, we must ensure that
// the array is already in old space, otherwise we'll create too many
......@@ -3479,8 +3492,6 @@ BIMODAL_ACCESSOR(HeapObject, Map, map)
BIMODAL_ACCESSOR_C(HeapNumber, double, value)
BIMODAL_ACCESSOR(JSArray, Object, length)
BIMODAL_ACCESSOR(JSBoundFunction, JSReceiver, bound_target_function)
BIMODAL_ACCESSOR(JSBoundFunction, Object, bound_this)
BIMODAL_ACCESSOR(JSBoundFunction, FixedArray, bound_arguments)
......@@ -4004,25 +4015,81 @@ base::Optional<ObjectRef> JSObjectRef::GetOwnDataProperty(
return ObjectRef(broker(), property);
}
base::Optional<ObjectRef> JSArrayRef::GetOwnCowElement(
uint32_t index, SerializationPolicy policy) const {
if (data_->should_access_heap()) {
if (!object()->elements().IsCowArray()) return base::nullopt;
return GetOwnElementFromHeap(broker(), object(), index, false);
}
ObjectRef JSArrayRef::GetBoilerplateLength() const {
// Safe to read concurrently because:
// - boilerplates are immutable after initialization.
// - boilerplates are published into the feedback vector.
return length_unsafe();
}
if (policy == SerializationPolicy::kSerializeIfNeeded) {
data()->AsJSObject()->SerializeElements(broker());
} else if (!data()->AsJSObject()->serialized_elements()) {
TRACE(broker(), "'elements' on " << this);
return base::nullopt;
ObjectRef JSArrayRef::length_unsafe() const {
if (data_->should_access_heap() || FLAG_turbo_direct_heap_access) {
Object o = object()->length(broker()->isolate(), kRelaxedLoad);
return ObjectRef{broker(), broker()->CanonicalPersistentHandle(o)};
} else {
return ObjectRef{broker(), data()->AsJSArray()->length()};
}
if (!elements().map().IsFixedCowArrayMap()) return base::nullopt;
}
ObjectData* element =
data()->AsJSArray()->GetOwnElement(broker(), index, policy);
if (element == nullptr) return base::nullopt;
return ObjectRef(broker(), element);
base::Optional<ObjectRef> JSArrayRef::GetOwnCowElement(
FixedArrayBaseRef elements_ref, uint32_t index,
SerializationPolicy policy) const {
if (data_->should_access_heap() || FLAG_turbo_direct_heap_access) {
// `elements` are currently still serialized as members of JSObjectRef.
// TODO(jgruber,v8:7790): Remove the elements equality DCHECK below once
// JSObject is no longer serialized.
static_assert(std::is_base_of<JSObject, JSArray>::value, "");
STATIC_ASSERT(IsSerializedHeapObject<JSObject>());
// The elements_ref is passed in by callers to make explicit that it is
// also used outside of this function, and must match the `elements` used
// inside this function.
DCHECK(elements_ref.equals(elements().value()));
// Due to concurrency, the kind read here may not be consistent with
// `elements_ref`. But consistency is guaranteed at runtime due to the
// `elements` equality check in the caller.
ElementsKind elements_kind = GetElementsKind();
// We only inspect fixed COW arrays, which may only occur for fast
// smi/objects elements kinds.
if (!IsSmiOrObjectElementsKind(elements_kind)) return {};
DCHECK(IsFastElementsKind(elements_kind));
if (!elements_ref.map().IsFixedCowArrayMap()) return {};
// As the name says, the `length` read here is unsafe and may not match
// `elements`. We rely on the invariant that any `length` change will
// also result in an `elements` change to make this safe. The `elements`
// equality check in the caller thus also guards the value of `length`.
ObjectRef length_ref = length_unsafe();
// Likewise we only deal with smi lengths.
if (!length_ref.IsSmi()) return {};
base::Optional<Object> result =
ConcurrentLookupIterator::TryGetOwnCowElement(
broker()->isolate(), *elements_ref.AsFixedArray().object(),
elements_kind, length_ref.AsSmi(), index);
if (!result.has_value()) return {};
return ObjectRef{broker(),
broker()->CanonicalPersistentHandle(result.value())};
} else {
DCHECK(!data_->should_access_heap());
DCHECK(!FLAG_turbo_direct_heap_access);
// Just to clarify that `elements_ref` is not used on this path.
// GetOwnElement accesses the serialized `elements` field on its own.
USE(elements_ref);
if (!elements().value().map().IsFixedCowArrayMap()) return base::nullopt;
ObjectData* element =
data()->AsJSArray()->GetOwnElement(broker(), index, policy);
if (element == nullptr) return base::nullopt;
return ObjectRef(broker(), element);
}
}
base::Optional<CellRef> SourceTextModuleRef::GetCell(int cell_index) const {
......@@ -4117,12 +4184,17 @@ ElementsKind JSObjectRef::GetElementsKind() const {
return map().elements_kind();
}
FixedArrayBaseRef JSObjectRef::elements() const {
base::Optional<FixedArrayBaseRef> JSObjectRef::elements() const {
if (data_->should_access_heap()) {
return FixedArrayBaseRef(
broker(), broker()->CanonicalPersistentHandle(object()->elements()));
}
return FixedArrayBaseRef(broker(), data()->AsJSObject()->elements());
const JSObjectData* d = data()->AsJSObject();
if (!d->serialized_elements()) {
TRACE(broker(), "'elements' on " << this);
return base::nullopt;
}
return FixedArrayBaseRef(broker(), d->elements());
}
int FixedArrayBaseRef::length() const {
......
......@@ -1959,18 +1959,21 @@ Reduction JSNativeContextSpecialization::ReduceElementLoadFromHeapConstant(
// We didn't find a constant element, but if the receiver is a cow-array
// we can exploit the fact that any future write to the element will
// replace the whole elements storage.
element = receiver_ref.AsJSArray().GetOwnCowElement(index);
if (element.has_value()) {
Node* elements = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
receiver, effect, control);
FixedArrayRef array_elements =
receiver_ref.AsJSArray().elements().AsFixedArray();
Node* check = graph()->NewNode(simplified()->ReferenceEqual(), elements,
jsgraph()->Constant(array_elements));
effect = graph()->NewNode(
simplified()->CheckIf(DeoptimizeReason::kCowArrayElementsChanged),
check, effect, control);
JSArrayRef array_ref = receiver_ref.AsJSArray();
base::Optional<FixedArrayBaseRef> array_elements = array_ref.elements();
if (array_elements.has_value()) {
element = array_ref.GetOwnCowElement(*array_elements, index);
if (element.has_value()) {
Node* elements = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
receiver, effect, control);
Node* check =
graph()->NewNode(simplified()->ReferenceEqual(), elements,
jsgraph()->Constant(*array_elements));
effect = graph()->NewNode(
simplified()->CheckIf(DeoptimizeReason::kCowArrayElementsChanged),
check, effect, control);
}
}
}
if (element.has_value()) {
......
......@@ -3330,8 +3330,11 @@ void SerializerForBackgroundCompilation::ProcessElementAccess(
// We didn't find a constant element, but if the receiver is a
// cow-array we can exploit the fact that any future write to the
// element will replace the whole elements storage.
receiver_ref.AsJSArray().GetOwnCowElement(
key_ref.AsSmi(), SerializationPolicy::kSerializeIfNeeded);
JSArrayRef array_ref = receiver_ref.AsJSArray();
array_ref.SerializeElements();
array_ref.GetOwnCowElement(array_ref.elements().value(),
key_ref.AsSmi(),
SerializationPolicy::kSerializeIfNeeded);
}
}
}
......
......@@ -31,6 +31,10 @@ void JSArray::set_length(Object value, WriteBarrierMode mode) {
CONDITIONAL_WRITE_BARRIER(*this, kLengthOffset, value, mode);
}
Object JSArray::length(IsolateRoot isolate, RelaxedLoadTag tag) const {
return TaggedField<Object, kLengthOffset>::Relaxed_Load(isolate, *this);
}
void JSArray::set_length(Smi length) {
// Don't need a write barrier for a Smi.
set_length(Object(length.ptr()), SKIP_WRITE_BARRIER);
......
......@@ -25,6 +25,7 @@ class JSArray : public JSObject {
public:
// [length]: The length property.
DECL_ACCESSORS(length, Object)
DECL_RELAXED_GETTER(length, Object)
// Acquire/release semantics on this field are explicitly forbidden to avoid
// confusion, since the default setter uses relaxed semantics. If
......
......@@ -1269,5 +1269,49 @@ bool LookupIterator::LookupCachedProperty(Handle<AccessorPair> accessor_pair) {
return true;
}
// static
base::Optional<Object> ConcurrentLookupIterator::TryGetOwnCowElement(
Isolate* isolate, FixedArray array_elements, ElementsKind elements_kind,
int array_length, size_t index) {
DisallowGarbageCollection no_gc;
CHECK_EQ(array_elements.map(), ReadOnlyRoots(isolate).fixed_cow_array_map());
DCHECK(IsFastElementsKind(elements_kind) &&
IsSmiOrObjectElementsKind(elements_kind));
USE(elements_kind);
DCHECK_GE(array_length, 0);
// ________________________________________
// ( Check against both JSArray::length and )
// ( FixedArray::length. )
// ----------------------------------------
// o ^__^
// o (oo)\_______
// (__)\ )\/\
// ||----w |
// || ||
// The former is the source of truth, but due to concurrent reads it may not
// match the given `array_elements`.
if (index >= static_cast<size_t>(array_length)) return {};
if (index >= static_cast<size_t>(array_elements.length())) return {};
Object result = array_elements.get(isolate, static_cast<int>(index));
// ______________________________________
// ( Filter out holes irrespective of the )
// ( elements kind. )
// --------------------------------------
// o ^__^
// o (..)\_______
// (__)\ )\/\
// ||----w |
// || ||
// The elements kind may not be consistent with the given elements backing
// store.
if (result == ReadOnlyRoots(isolate).the_hole_value()) return {};
return result;
}
} // namespace internal
} // namespace v8
......@@ -292,6 +292,32 @@ class V8_EXPORT_PRIVATE LookupIterator final {
InternalIndex number_ = InternalIndex::NotFound();
};
// Similar to the LookupIterator, but for concurrent accesses from a background
// thread.
//
// Note: This is a work in progress, intended to bundle code related to
// concurrent lookups here. In its current state, the class is obviously not an
// 'iterator'. Still, keeping the name for now, with the intent to clarify
// names and implementation once we've gotten some experience with more
// involved logic.
// TODO(jgruber, v8:7790): Consider using a LookupIterator-style interface.
// TODO(jgruber, v8:7790): Consider merging back into the LookupIterator once
// functionality and constraints are better known.
class ConcurrentLookupIterator final : public AllStatic {
public:
// Implements the own data property lookup for the specialized case of
// fixed_cow_array backing stores (these are only in use for array literal
// boilerplates). The contract is that the elements, elements kind, and array
// length passed to this function should all be read from the same JSArray
// instance; but due to concurrency it's possible that they may not be
// consistent among themselves (e.g. the elements kind may not match the
// given elements backing store). We are thus extra-careful to handle
// exceptional situations.
V8_EXPORT_PRIVATE static base::Optional<Object> TryGetOwnCowElement(
Isolate* isolate, FixedArray array_elements, ElementsKind elements_kind,
int array_length, size_t index);
};
} // namespace internal
} // namespace v8
......
......@@ -213,6 +213,7 @@ v8_source_set("cctest_sources") {
"test-compiler.cc",
"test-concurrent-descriptor-array.cc",
"test-concurrent-feedback-vector.cc",
"test-concurrent-js-array.cc",
"test-concurrent-prototype.cc",
"test-concurrent-script-context-table.cc",
"test-concurrent-string.cc",
......
// 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
// 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.
//
// Flags: --allow-natives-syntax
var __v_10 = {};
var __v_9 = [-1];
function __f_7() {
(__v_10[65535] | 65535) / __v_9[2147483648];
}
%PrepareFunctionForOptimization(__f_7);
__f_7();
__f_7();
%OptimizeFunctionOnNextCall(__f_7);
__f_7();
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