Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize TypedArrayObject.prototype.set, indexOf, lastIndexOf #1251

Merged
merged 2 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ PROJECT (ESCARGOT)

# CONFIGURATION
SET (CMAKE_VERBOSE_MAKEFILE true)
# so(dll)s are should placed on CMAKE_BINARY_DIR
SET (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
SET (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})

MESSAGE(STATUS "CMAKE_SYSTEM_NAME: " ${CMAKE_SYSTEM_NAME})
MESSAGE(STATUS "CMAKE_SYSTEM_PROCESSOR: " ${CMAKE_SYSTEM_PROCESSOR})
Expand Down
157 changes: 100 additions & 57 deletions src/builtins/BuiltinTypedArray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,98 @@ static Value builtinTypedArrayCopyWithin(ExecutionState& state, Value thisValue,
return O;
}

template <const bool isForward>
static Value fastTypedArrayIndexSearch(TypedArrayObject* arr, size_t k, size_t len, const Value& value)
{
auto type = arr->typedArrayType();
if (type == TypedArrayType::BigInt64 || type == TypedArrayType::BigUint64) {
if (!value.isBigInt()) {
return Value(-1);
}
} else if (UNLIKELY(!value.isNumber())) {
return Value(-1);
}

uint8_t* buffer = arr->rawBuffer();
auto elementSize = arr->elementSize();
int64_t byteK = static_cast<int64_t>(k * elementSize);
int64_t byteLength = static_cast<int64_t>(len * elementSize);

auto compFn = [](int64_t byteK, int64_t byteLength) -> bool {
if (isForward) {
return byteK < byteLength;
} else {
return byteK >= 0;
}
};

auto updateFn = [](int64_t& byteK, const int diff) {
if (isForward) {
byteK += diff;
} else {
byteK -= diff;
}
};

if (!isForward && byteK >= byteLength) {
return Value(-1);
}

if (type == TypedArrayType::BigInt64 || type == TypedArrayType::BigUint64) {
uint64_t argv0ToUint64 = type == TypedArrayType::BigInt64 ? bitwise_cast<uint64_t>(value.asBigInt()->toInt64()) : value.asBigInt()->toUint64();
while (compFn(byteK, byteLength)) {
if (*reinterpret_cast<uint64_t*>(&buffer[byteK]) == argv0ToUint64) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it fine to directly access array elements regardless of Endian?
What if we use BigEndian?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO We don't need to care endianness.
because two values are comes from same machine

return Value(byteK / elementSize);
}
updateFn(byteK, 8);
}
} else if (type == TypedArrayType::Float32) {
double num = value.asNumber();
if (std::isnan(num)) {
return Value(-1);
}
while (compFn(byteK, byteLength)) {
double c = *reinterpret_cast<float*>(&buffer[byteK]);
if (c == num) {
return Value(byteK / elementSize);
}
updateFn(byteK, 4);
}
} else if (type == TypedArrayType::Float64) {
double num = value.asNumber();
if (std::isnan(num)) {
return Value(-1);
}
while (compFn(byteK, byteLength)) {
const double& c = *reinterpret_cast<double*>(&buffer[byteK]);
if (c == num) {
return Value(byteK / elementSize);
}
updateFn(byteK, 8);
}
} else {
int32_t argv0ToUint32;
if (value.isDouble()) {
if (value.asDouble() == -0.0) {
argv0ToUint32 = 0;
} else {
return Value(-1);
}
} else {
argv0ToUint32 = value.asInt32();
}

while (compFn(byteK, byteLength)) {
if (memcmp(&buffer[byteK], &argv0ToUint32, elementSize) == 0) {
return Value(byteK / elementSize);
}
updateFn(byteK, elementSize);
}
}

return Value(-1);
}

static Value builtinTypedArrayIndexOf(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
// NOTE: Same algorithm as Array.prototype.indexOf
Expand Down Expand Up @@ -527,28 +619,9 @@ static Value builtinTypedArrayIndexOf(ExecutionState& state, Value thisValue, si
}
}
size_t k = (size_t)doubleK;

// Repeat, while k<len
while (k < len) {
// Let kPresent be the result of calling the [[HasProperty]] internal method of O with argument ToString(k).
ObjectGetResult kPresent = O->getIndexedProperty(state, Value(k));
// If kPresent is true, then
if (kPresent.hasValue()) {
// Let elementK be the result of calling the [[Get]] internal method of O with the argument ToString(k).
Value elementK = kPresent.value(state, O);

// Let same be the result of applying the Strict Equality Comparison Algorithm to searchElement and elementK.
if (elementK.equalsTo(state, argv[0])) {
// If same is true, return k.
return Value(k);
}
}
// Increase k by 1.
k++;
}

// Return -1.
return Value(-1);
// Reloading `len` because second argument can affect arrayLength
len = std::min((size_t)O->asTypedArrayObject()->arrayLength(), len);
return fastTypedArrayIndexSearch<true>(O->asTypedArrayObject(), k, len, argv[0]);
}

static Value builtinTypedArrayLastIndexOf(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
Expand Down Expand Up @@ -589,33 +662,9 @@ static Value builtinTypedArrayLastIndexOf(ExecutionState& state, Value thisValue
return Value(-1);
}
int64_t k = (int64_t)doubleK;

// Repeat, while k≥ 0
while (k >= 0) {
// Let kPresent be the result of calling the [[HasProperty]] internal method of O with argument ToString(k).
ObjectGetResult kPresent = O->getIndexedProperty(state, Value(k));
// If kPresent is true, then
if (kPresent.hasValue()) {
// Let elementK be the result of calling the [[Get]] internal method of O with the argument ToString(k).
Value elementK = kPresent.value(state, O);

// Let same be the result of applying the Strict Equality Comparison Algorithm to searchElement and elementK.
if (elementK.equalsTo(state, argv[0])) {
// If same is true, return k.
return Value(k);
}
} else {
int64_t result;
Object::nextIndexBackward(state, O, k, -1, result);
k = result;
continue;
}
// Decrease k by 1.
k--;
}

// Return -1.
return Value(-1);
// Reloading `len` because second argument can affect arrayLength
len = std::min((size_t)O->asTypedArrayObject()->arrayLength(), len);
return fastTypedArrayIndexSearch<false>(O->asTypedArrayObject(), k, len, argv[0]);
}

static Value builtinTypedArrayIncludes(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
Expand Down Expand Up @@ -759,14 +808,8 @@ static Value builtinTypedArraySet(ExecutionState& state, Value thisValue, size_t
size_t limit = targetByteIndex + targetElementSize * srcLength;

if (srcTypedArray->typedArrayType() == target->typedArrayType()) {
while (targetByteIndex < limit) {
// Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, "Uint8", true, "Unordered").
Value value = srcBuffer->getValueFromBuffer(state, srcByteIndex, TypedArrayType::Uint8);
// Perform SetValueInBuffer(targetBuffer, targetByteIndex, "Uint8", value, true, "Unordered").
targetBuffer->setValueInBuffer(state, targetByteIndex, TypedArrayType::Uint8, value);
srcByteIndex++;
targetByteIndex++;
}
memmove(targetBuffer->data() + targetByteIndex,
srcBuffer->data() + srcByteIndex, limit - targetByteIndex);
} else {
while (targetByteIndex < limit) {
// Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, srcType, true, "Unordered").
Expand Down
36 changes: 18 additions & 18 deletions src/parser/esprima_cpp/esprima.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3016,37 +3016,37 @@ class Parser {
case LogicalAnd:
return 3;
case BitwiseOr:
return 3;
case BitwiseXor:
return 4;
case BitwiseAnd:
case BitwiseXor:
return 5;
case Equal:
case BitwiseAnd:
return 6;
case Equal:
return 7;
case NotEqual:
return 6;
return 7;
case StrictEqual:
return 6;
return 7;
case NotStrictEqual:
return 6;
case RightInequality:
return 7;
case RightInequality:
return 8;
case LeftInequality:
return 7;
return 8;
case RightInequalityEqual:
return 7;
return 8;
case LeftInequalityEqual:
return 7;
case LeftShift:
return 8;
case LeftShift:
return 9;
case RightShift:
return 8;
return 9;
case UnsignedRightShift:
return 8;
case Plus:
return 9;
case Plus:
return 10;
case Minus:
return 9;
return 10;
case Multiply:
return 11;
case Divide:
Expand All @@ -3058,9 +3058,9 @@ class Parser {
}
} else if (token->type == Token::KeywordToken) {
if (token->valueKeywordKind == InKeyword) {
return this->context->allowIn ? 7 : 0;
return this->context->allowIn ? 8 : 0;
} else if (token->valueKeywordKind == InstanceofKeyword) {
return 7;
return 8;
}
} else {
return 0;
Expand Down
2 changes: 1 addition & 1 deletion test/vendortest