Skip to content

Commit

Permalink
Bug 1827258 part 3 - Add fast path for copying elements from another …
Browse files Browse the repository at this point in the history
…object. r=jonco

Differential Revision: https://phabricator.services.mozilla.com/D175777
  • Loading branch information
jandem committed Apr 20, 2023
1 parent 96d846e commit 738ff2f
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 16 deletions.
62 changes: 46 additions & 16 deletions js/src/builtin/Array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4592,9 +4592,11 @@ static bool array_concat(JSContext* cx, unsigned argc, Value* vp) {
return false;
}

bool isArraySpecies = IsArraySpecies(cx, obj);

// Step 2.
RootedObject arr(cx);
if (IsArraySpecies(cx, obj)) {
if (isArraySpecies) {
arr = NewDenseEmptyArray(cx);
if (!arr) {
return false;
Expand Down Expand Up @@ -4639,27 +4641,55 @@ static bool array_concat(JSContext* cx, unsigned argc, Value* vp) {
uint64_t k = 0;

// Step 5.b.iv.
while (k < len) {
if (!CheckForInterrupt(cx)) {
return false;
}

// Step 5.b.iv.2.
bool hole;
if (!HasAndGetElement(cx, obj, k, &hole, &v)) {
// Try a fast path for copying dense elements directly.
bool optimized = false;
if (len > 0 && isArraySpecies &&
CanOptimizeForDenseStorage<ArrayAccess::Read>(obj, len) &&
n + len <= NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
NativeObject* nobj = &obj->as<NativeObject>();
ArrayObject* resArr = &arr->as<ArrayObject>();
uint32_t initLen =
std::min(uint32_t(len), nobj->getDenseInitializedLength());

DenseElementResult res = resArr->ensureDenseElements(cx, n, initLen);
if (res == DenseElementResult::Failure) {
return false;
}
if (!hole) {
// Step 5.b.iv.3.
if (!DefineArrayElement(cx, arr, n, v)) {
if (res == DenseElementResult::Success) {
resArr->initDenseElementRange(n, nobj);
n += len;
optimized = true;
} else {
MOZ_ASSERT(res == DenseElementResult::Incomplete);
}
}

if (!optimized) {
// Step 5.b.iv.
while (k < len) {
if (!CheckForInterrupt(cx)) {
return false;
}
}
// Step 5.b.iv.4.
n++;

// Step 5.b.iv.5.
k++;
// Step 5.b.iv.2.
bool hole;
if (!HasAndGetElement(cx, obj, k, &hole, &v)) {
return false;
}
if (!hole) {
// Step 5.b.iv.3.
if (!DefineArrayElement(cx, arr, n, v)) {
return false;
}
}

// Step 5.b.iv.4.
n++;

// Step 5.b.iv.5.
k++;
}
}
} else {
// Step 5.c.ii.
Expand Down
22 changes: 22 additions & 0 deletions js/src/vm/NativeObject-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,28 @@ inline void NativeObject::initDenseElements(const Value* src, uint32_t count) {
elementsRangePostWriteBarrier(0, count);
}

inline void NativeObject::initDenseElementRange(uint32_t destStart,
NativeObject* src) {
uint32_t count = src->getDenseInitializedLength();

// The initialized length must already be set to the correct value.
MOZ_ASSERT(destStart + count == getDenseInitializedLength());

if (!src->denseElementsArePacked()) {
markDenseElementsNotPacked();
}

const Value* vp = src->getDenseElements();
#ifdef DEBUG
for (uint32_t i = 0; i < count; ++i) {
checkStoredValue(vp[i]);
}
#endif
memcpy(reinterpret_cast<Value*>(elements_) + destStart, vp,
count * sizeof(Value));
elementsRangePostWriteBarrier(destStart, count);
}

template <typename Iter>
inline bool NativeObject::initDenseElementsFromRange(JSContext* cx, Iter begin,
Iter end) {
Expand Down
4 changes: 4 additions & 0 deletions js/src/vm/NativeObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -1451,6 +1451,10 @@ class NativeObject : public JSObject {
inline void initDenseElements(NativeObject* src, uint32_t srcStart,
uint32_t count);

// Copy all dense elements from `src` to `this`, starting at `destStart`.
// The initialized length must already include the new elements.
inline void initDenseElementRange(uint32_t destStart, NativeObject* src);

// Store the Values in the range [begin, end) as elements of this array.
//
// Preconditions: This must be a boring ArrayObject with dense initialized
Expand Down

0 comments on commit 738ff2f

Please sign in to comment.