⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 236496 in webkit


Ignore:
Timestamp:
Sep 25, 2018, 10:16:22 PM (8 years ago)
Author:
yusukesuzuki@slowstart.org
Message:

[JSC] Optimize Array#lastIndexOf
https://bugs.webkit.org/show_bug.cgi?id=189780

Reviewed by Saam Barati.

JSTests:

  • stress/array-lastindexof-array-prototype-trap.js: Added.

(shouldBe):
(AncestorArray.prototype.get 2):
(AncestorArray):

  • stress/array-lastindexof-have-a-bad-time-c-runtime.js: Added.

(shouldBe):

  • stress/array-lastindexof-hole-nan.js: Added.

(shouldBe):
(throw.new.Error):

  • stress/array-lastindexof-infinity.js: Added.

(shouldBe):
(throw.new.Error):

  • stress/array-lastindexof-negative-zero.js: Added.

(shouldBe):
(throw.new.Error):

  • stress/array-lastindexof-own-getter.js: Added.

(shouldBe):
(throw.new.Error.get array):
(get array):

  • stress/array-lastindexof-prototype-trap.js: Added.

(shouldBe):
(DerivedArray.prototype.get 2):
(DerivedArray):

Source/JavaScriptCore:

Optimize Array#lastIndexOf as the same to Array#indexOf. We add a fast path
for JSArray with contiguous storage.

  • runtime/ArrayPrototype.cpp:

(JSC::arrayProtoFuncLastIndexOf):

Location:
trunk
Files:
9 added
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r236495 r236496  
     12018-09-20  Yusuke Suzuki  <yusukesuzuki@slowstart.org>
     2
     3        [JSC] Optimize Array#lastIndexOf
     4        https://bugs.webkit.org/show_bug.cgi?id=189780
     5
     6        Reviewed by Saam Barati.
     7
     8        * stress/array-lastindexof-array-prototype-trap.js: Added.
     9        (shouldBe):
     10        (AncestorArray.prototype.get 2):
     11        (AncestorArray):
     12        * stress/array-lastindexof-have-a-bad-time-c-runtime.js: Added.
     13        (shouldBe):
     14        * stress/array-lastindexof-hole-nan.js: Added.
     15        (shouldBe):
     16        (throw.new.Error):
     17        * stress/array-lastindexof-infinity.js: Added.
     18        (shouldBe):
     19        (throw.new.Error):
     20        * stress/array-lastindexof-negative-zero.js: Added.
     21        (shouldBe):
     22        (throw.new.Error):
     23        * stress/array-lastindexof-own-getter.js: Added.
     24        (shouldBe):
     25        (throw.new.Error.get array):
     26        (get array):
     27        * stress/array-lastindexof-prototype-trap.js: Added.
     28        (shouldBe):
     29        (DerivedArray.prototype.get 2):
     30        (DerivedArray):
     31
    1322018-09-25  Saam Barati  <sbarati@apple.com>
    233
  • trunk/Source/JavaScriptCore/ChangeLog

    r236495 r236496  
     12018-09-20  Yusuke Suzuki  <yusukesuzuki@slowstart.org>
     2
     3        [JSC] Optimize Array#lastIndexOf
     4        https://bugs.webkit.org/show_bug.cgi?id=189780
     5
     6        Reviewed by Saam Barati.
     7
     8        Optimize Array#lastIndexOf as the same to Array#indexOf. We add a fast path
     9        for JSArray with contiguous storage.
     10
     11        * runtime/ArrayPrototype.cpp:
     12        (JSC::arrayProtoFuncLastIndexOf):
     13
    1142018-09-25  Saam Barati  <sbarati@apple.com>
    215
  • trunk/Source/JavaScriptCore/runtime/ArrayPrototype.cpp

    r236437 r236496  
    11511151}
    11521152
     1153enum class IndexOfDirection { Forward, Backward };
     1154template<IndexOfDirection direction>
     1155ALWAYS_INLINE JSValue fastIndexOf(ExecState* exec, VM& vm, JSArray* array, unsigned length, JSValue searchElement, unsigned index)
     1156{
     1157    auto scope = DECLARE_THROW_SCOPE(vm);
     1158
     1159    bool canDoFastPath = array->canDoFastIndexedAccess(vm)
     1160        && array->getArrayLength() == length; // The effects in getting `index` could have changed the length of this array.
     1161    if (!canDoFastPath)
     1162        return JSValue();
     1163
     1164    switch (array->indexingType()) {
     1165    case ALL_INT32_INDEXING_TYPES: {
     1166        if (!searchElement.isNumber())
     1167            return jsNumber(-1);
     1168        JSValue searchInt32;
     1169        if (searchElement.isInt32())
     1170            searchInt32 = searchElement;
     1171        else {
     1172            double searchNumber = searchElement.asNumber();
     1173            if (!canBeInt32(searchNumber))
     1174                return jsNumber(-1);
     1175            searchInt32 = jsNumber(static_cast<int32_t>(searchNumber));
     1176        }
     1177        auto& butterfly = *array->butterfly();
     1178        auto data = butterfly.contiguous().data();
     1179        if (direction == IndexOfDirection::Forward) {
     1180            for (; index < length; ++index) {
     1181                // Array#indexOf uses `===` semantics (not HashMap isEqual semantics).
     1182                // And the hole never matches against Int32 value.
     1183                if (searchInt32 == data[index].get())
     1184                    return jsNumber(index);
     1185            }
     1186        } else {
     1187            do {
     1188                ASSERT(index < length);
     1189                // Array#lastIndexOf uses `===` semantics (not HashMap isEqual semantics).
     1190                // And the hole never matches against Int32 value.
     1191                if (searchInt32 == data[index].get())
     1192                    return jsNumber(index);
     1193            } while (index--);
     1194        }
     1195        return jsNumber(-1);
     1196    }
     1197    case ALL_CONTIGUOUS_INDEXING_TYPES: {
     1198        auto& butterfly = *array->butterfly();
     1199        auto data = butterfly.contiguous().data();
     1200
     1201        if (direction == IndexOfDirection::Forward) {
     1202            for (; index < length; ++index) {
     1203                JSValue value = data[index].get();
     1204                if (!value)
     1205                    continue;
     1206                bool isEqual = JSValue::strictEqual(exec, searchElement, value);
     1207                RETURN_IF_EXCEPTION(scope, { });
     1208                if (isEqual)
     1209                    return jsNumber(index);
     1210            }
     1211        } else {
     1212            do {
     1213                ASSERT(index < length);
     1214                JSValue value = data[index].get();
     1215                if (!value)
     1216                    continue;
     1217                bool isEqual = JSValue::strictEqual(exec, searchElement, value);
     1218                RETURN_IF_EXCEPTION(scope, { });
     1219                if (isEqual)
     1220                    return jsNumber(index);
     1221            } while (index--);
     1222        }
     1223        return jsNumber(-1);
     1224    }
     1225    case ALL_DOUBLE_INDEXING_TYPES: {
     1226        if (!searchElement.isNumber())
     1227            return jsNumber(-1);
     1228        double searchNumber = searchElement.asNumber();
     1229        auto& butterfly = *array->butterfly();
     1230        auto data = butterfly.contiguousDouble().data();
     1231        if (direction == IndexOfDirection::Forward) {
     1232            for (; index < length; ++index) {
     1233                // Array#indexOf uses `===` semantics (not HashMap isEqual semantics).
     1234                // And the hole never matches since it is NaN.
     1235                if (data[index] == searchNumber)
     1236                    return jsNumber(index);
     1237            }
     1238        } else {
     1239            do {
     1240                ASSERT(index < length);
     1241                // Array#lastIndexOf uses `===` semantics (not HashMap isEqual semantics).
     1242                // And the hole never matches since it is NaN.
     1243                if (data[index] == searchNumber)
     1244                    return jsNumber(index);
     1245            } while (index--);
     1246        }
     1247        return jsNumber(-1);
     1248    }
     1249    default:
     1250        return JSValue();
     1251    }
     1252}
     1253
    11531254EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec)
    11541255{
     
    11691270
    11701271    if (isJSArray(thisObject)) {
    1171         JSArray* array = asArray(thisObject);
    1172         bool canDoFastPath = array->canDoFastIndexedAccess(vm)
    1173             && array->getArrayLength() == length; // The effects in getting `index` could have changed the length of this array.
    1174         if (canDoFastPath) {
    1175             switch (array->indexingType()) {
    1176             case ALL_INT32_INDEXING_TYPES: {
    1177                 if (!searchElement.isNumber())
    1178                     return JSValue::encode(jsNumber(-1));
    1179                 JSValue searchInt32;
    1180                 if (searchElement.isInt32())
    1181                     searchInt32 = searchElement;
    1182                 else {
    1183                     double searchNumber = searchElement.asNumber();
    1184                     if (!canBeInt32(searchNumber))
    1185                         return JSValue::encode(jsNumber(-1));
    1186                     searchInt32 = jsNumber(static_cast<int32_t>(searchNumber));
    1187                 }
    1188                 auto& butterfly = *array->butterfly();
    1189                 auto data = butterfly.contiguous().data();
    1190                 for (; index < length; ++index) {
    1191                     // Array#indexOf uses `===` semantics (not HashMap isEqual semantics).
    1192                     // And the hole never matches against Int32 value.
    1193                     if (searchInt32 == data[index].get())
    1194                         return JSValue::encode(jsNumber(index));
    1195                 }
    1196                 return JSValue::encode(jsNumber(-1));
    1197             }
    1198             case ALL_CONTIGUOUS_INDEXING_TYPES: {
    1199                 auto& butterfly = *array->butterfly();
    1200                 auto data = butterfly.contiguous().data();
    1201                 for (; index < length; ++index) {
    1202                     JSValue value = data[index].get();
    1203                     if (!value)
    1204                         continue;
    1205                     bool isEqual = JSValue::strictEqual(exec, searchElement, value);
    1206                     RETURN_IF_EXCEPTION(scope, { });
    1207                     if (isEqual)
    1208                         return JSValue::encode(jsNumber(index));
    1209                 }
    1210                 return JSValue::encode(jsNumber(-1));
    1211             }
    1212             case ALL_DOUBLE_INDEXING_TYPES: {
    1213                 if (!searchElement.isNumber())
    1214                     return JSValue::encode(jsNumber(-1));
    1215                 double searchNumber = searchElement.asNumber();
    1216                 auto& butterfly = *array->butterfly();
    1217                 auto data = butterfly.contiguousDouble().data();
    1218                 for (; index < length; ++index) {
    1219                     // Array#indexOf uses `===` semantics (not HashMap isEqual semantics).
    1220                     // And the hole never matches since it is NaN.
    1221                     if (data[index] == searchNumber)
    1222                         return JSValue::encode(jsNumber(index));
    1223                 }
    1224                 return JSValue::encode(jsNumber(-1));
    1225             }
    1226             default:
    1227                 break;
    1228             }
    1229         }
     1272        if (JSValue result = fastIndexOf<IndexOfDirection::Forward>(exec, vm, asArray(thisObject), length, searchElement, index))
     1273            return JSValue::encode(result);
    12301274    }
    12311275
     
    12501294
    12511295    // 15.4.4.15
    1252     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
    1253     EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
    1254     if (UNLIKELY(!thisObj))
    1255         return encodedJSValue();
    1256     unsigned length = toLength(exec, thisObj);
     1296    JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
     1297    EXCEPTION_ASSERT(!!scope.exception() == !thisObject);
     1298    if (UNLIKELY(!thisObject))
     1299        return { };
     1300    unsigned length = toLength(exec, thisObject);
    12571301    if (UNLIKELY(scope.exception()) || !length)
    12581302        return JSValue::encode(jsNumber(-1));
     
    12621306        JSValue fromValue = exec->uncheckedArgument(1);
    12631307        double fromDouble = fromValue.toInteger(exec);
    1264         RETURN_IF_EXCEPTION(scope, encodedJSValue());
     1308        RETURN_IF_EXCEPTION(scope, { });
    12651309        if (fromDouble < 0) {
    12661310            fromDouble += length;
     
    12731317
    12741318    JSValue searchElement = exec->argument(0);
     1319
     1320    if (isJSArray(thisObject)) {
     1321        if (JSValue result = fastIndexOf<IndexOfDirection::Backward>(exec, vm, asArray(thisObject), length, searchElement, index))
     1322            return JSValue::encode(result);
     1323    }
     1324
    12751325    do {
    1276         RELEASE_ASSERT(index < length);
    1277         JSValue e = getProperty(exec, thisObj, index);
    1278         RETURN_IF_EXCEPTION(scope, encodedJSValue());
     1326        ASSERT(index < length);
     1327        JSValue e = getProperty(exec, thisObject, index);
     1328        RETURN_IF_EXCEPTION(scope, { });
    12791329        if (!e)
    12801330            continue;
    12811331        bool isEqual = JSValue::strictEqual(exec, searchElement, e);
    1282         RETURN_IF_EXCEPTION(scope, encodedJSValue());
     1332        RETURN_IF_EXCEPTION(scope, { });
    12831333        if (isEqual)
    12841334            return JSValue::encode(jsNumber(index));
Note: See TracChangeset for help on using the changeset viewer.