Bug 1453932 - Optimize loads from CallSiteObjects for tagged template literals. r=jandem
authorTom Schuster <evilpies@gmail.com>
Fri, 13 Apr 2018 13:56:55 +0200
changeset 414514 0a19791ab7735a3b5d96a070284e9c6f3441c527
parent 414513 eebf6569ce4ad9d7025a70775c69f3c524b73201
child 414515 8ce15c227c6600eb6235a6e99c4a1f7882be3227
push id102368
push userevilpies@gmail.com
push dateThu, 19 Apr 2018 21:07:32 +0000
treeherdermozilla-inbound@0a19791ab773 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1453932
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1453932 - Optimize loads from CallSiteObjects for tagged template literals. r=jandem
js/public/TrackedOptimizationInfo.h
js/src/jit-test/tests/ion/template-tag-callsiteobject.js
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
--- a/js/public/TrackedOptimizationInfo.h
+++ b/js/public/TrackedOptimizationInfo.h
@@ -32,16 +32,17 @@ namespace JS {
     _(SetProp_CommonSetter)                             \
     _(SetProp_TypedObject)                              \
     _(SetProp_DefiniteSlot)                             \
     _(SetProp_Unboxed)                                  \
     _(SetProp_InlineAccess)                             \
     _(SetProp_InlineCache)                              \
                                                         \
     _(GetElem_TypedObject)                              \
+    _(GetElem_CallSiteObject)                           \
     _(GetElem_Dense)                                    \
     _(GetElem_TypedArray)                               \
     _(GetElem_String)                                   \
     _(GetElem_Arguments)                                \
     _(GetElem_ArgumentsInlinedConstant)                 \
     _(GetElem_ArgumentsInlinedSwitch)                   \
     _(GetElem_InlineCache)                              \
                                                         \
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/template-tag-callsiteobject.js
@@ -0,0 +1,26 @@
+function tagA(strings) {
+    assertEq(strings.length, 2);
+    assertEq(strings[0], "a");
+    assertEq(strings[1], "");
+}
+
+function tagAB(strings) {
+    assertEq(strings.length, 2);
+    assertEq(strings[0], "a");
+    assertEq(strings[1], "b");
+}
+
+var data = [1, 2, 3];
+function tag(strings, value1, value2) {
+    return strings[0] + value1 + strings[1] + value2 + strings[2];
+}
+
+function complex() {
+    return tag`${data[0]} ${data[1] + data[2]}`;
+}
+
+for (var i = 0; i < 20; i++) {
+    tagA`a${0}`;
+    tagAB`a${0}b`;
+    assertEq(complex(), "1 5");
+}
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -7791,16 +7791,21 @@ IonBuilder::jsop_getelem()
 
     if (!forceInlineCaches()) {
         // Note: no trackOptimizationAttempt call is needed, getElemTryGetProp
         // will call it.
         MOZ_TRY(getElemTryGetProp(&emitted, obj, index));
         if (emitted)
             return Ok();
 
+        trackOptimizationAttempt(TrackedStrategy::GetElem_CallSiteObject);
+        MOZ_TRY(getElemTryCallSiteObject(&emitted, obj, index));
+        if (emitted)
+            return Ok();
+
         trackOptimizationAttempt(TrackedStrategy::GetElem_Dense);
         MOZ_TRY(getElemTryDense(&emitted, obj, index));
         if (emitted)
             return Ok();
 
         trackOptimizationAttempt(TrackedStrategy::GetElem_TypedArray);
         MOZ_TRY(getElemTryTypedArray(&emitted, obj, index));
         if (emitted)
@@ -8239,16 +8244,62 @@ IonBuilder::getElemTryTypedArray(bool* e
     MOZ_TRY(jsop_getelem_typed(obj, index, arrayType));
 
     trackOptimizationSuccess();
     *emitted = true;
     return Ok();
 }
 
 AbortReasonOr<Ok>
+IonBuilder::getElemTryCallSiteObject(bool* emitted, MDefinition* obj, MDefinition* index)
+{
+    MOZ_ASSERT(*emitted == false);
+
+    if (!obj->isConstant() || obj->type() != MIRType::Object) {
+        trackOptimizationOutcome(TrackedOutcome::NotObject);
+        return Ok();
+    }
+
+    if (!index->isConstant() || index->type() != MIRType::Int32) {
+        trackOptimizationOutcome(TrackedOutcome::IndexType);
+        return Ok();
+    }
+
+    JSObject* cst = &obj->toConstant()->toObject();
+    if (!cst->is<ArrayObject>()) {
+        trackOptimizationOutcome(TrackedOutcome::GenericFailure);
+        return Ok();
+    }
+
+    // Technically this code would work with any kind of frozen array,
+    // in pratice only CallSiteObjects can be constant and frozen.
+
+    ArrayObject* array = &cst->as<ArrayObject>();
+    if (array->lengthIsWritable() || array->hasEmptyElements() || !array->denseElementsAreFrozen()) {
+        trackOptimizationOutcome(TrackedOutcome::GenericFailure);
+        return Ok();
+    }
+
+    int32_t idx = index->toConstant()->toInt32();
+    if (idx < 0 || !array->containsDenseElement(uint32_t(idx))) {
+        trackOptimizationOutcome(TrackedOutcome::OutOfBounds);
+        return Ok();
+    }
+
+    obj->setImplicitlyUsedUnchecked();
+    index->setImplicitlyUsedUnchecked();
+
+    pushConstant(array->getDenseElement(uint32_t(idx)));
+
+    trackOptimizationSuccess();
+    *emitted = true;
+    return Ok();
+}
+
+AbortReasonOr<Ok>
 IonBuilder::getElemTryString(bool* emitted, MDefinition* obj, MDefinition* index)
 {
     MOZ_ASSERT(*emitted == false);
 
     if (obj->type() != MIRType::String || !IsNumberType(index->type())) {
         trackOptimizationOutcome(TrackedOutcome::AccessNotString);
         return Ok();
     }
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -415,16 +415,17 @@ class IonBuilder
                                                         uint32_t elemSize);
     AbortReasonOr<Ok> initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value,
                                              bool addResumePointAndIncrementInitializedLength);
 
     // jsop_getelem() helpers.
     AbortReasonOr<Ok> getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index);
     AbortReasonOr<Ok> getElemTryGetProp(bool* emitted, MDefinition* obj, MDefinition* index);
     AbortReasonOr<Ok> getElemTryTypedArray(bool* emitted, MDefinition* obj, MDefinition* index);
+    AbortReasonOr<Ok> getElemTryCallSiteObject(bool* emitted, MDefinition* obj, MDefinition* index);
     AbortReasonOr<Ok> getElemTryTypedObject(bool* emitted, MDefinition* obj, MDefinition* index);
     AbortReasonOr<Ok> getElemTryString(bool* emitted, MDefinition* obj, MDefinition* index);
     AbortReasonOr<Ok> getElemTryArguments(bool* emitted, MDefinition* obj, MDefinition* index);
     AbortReasonOr<Ok> getElemTryArgumentsInlinedConstant(bool* emitted, MDefinition* obj,
                                                          MDefinition* index);
     AbortReasonOr<Ok> getElemTryArgumentsInlinedIndex(bool* emitted, MDefinition* obj,
                                                       MDefinition* index);
     AbortReasonOr<Ok> getElemAddCache(MDefinition* obj, MDefinition* index);