Title: [201456] trunk/Source/_javascript_Core
Revision
201456
Author
[email protected]
Date
2016-05-27 11:36:30 -0700 (Fri, 27 May 2016)

Log Message

get_by_id should support caching unset properties in the LLInt
https://bugs.webkit.org/show_bug.cgi?id=158136

Reviewed by Benjamin Poulain.

Recently, we started supporting prototype load caching for get_by_id
in the LLInt. This patch extends that to caching unset properties.
While it is uncommon in general for a program to see a single structure
without a given property, the Array.prototype.concat function needs to
lookup the Symbol.isConcatSpreadable property. For any existing code
That property will never be set as it did not exist prior to ES6.

Similarly to the get_by_id_proto_load bytecode, this patch adds a new
bytecode, get_by_id_unset that checks the structureID of the base and
assigns undefined to the result.

There are no new tests here since we already have many tests that
incidentally cover this change.

* bytecode/BytecodeList.json:
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::printGetByIdOp):
(JSC::CodeBlock::dumpBytecode):
(JSC::CodeBlock::finalizeLLIntInlineCaches):
* bytecode/GetByIdStatus.cpp:
(JSC::GetByIdStatus::computeFromLLInt):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
(JSC::JIT::privateCompileSlowCases):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::setupGetByIdPrototypeCache):
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* llint/LLIntSlowPaths.h:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (201455 => 201456)


--- trunk/Source/_javascript_Core/ChangeLog	2016-05-27 18:35:49 UTC (rev 201455)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-05-27 18:36:30 UTC (rev 201456)
@@ -1,3 +1,48 @@
+2016-05-27  Keith Miller  <[email protected]>
+
+        get_by_id should support caching unset properties in the LLInt
+        https://bugs.webkit.org/show_bug.cgi?id=158136
+
+        Reviewed by Benjamin Poulain.
+
+        Recently, we started supporting prototype load caching for get_by_id
+        in the LLInt. This patch extends that to caching unset properties.
+        While it is uncommon in general for a program to see a single structure
+        without a given property, the Array.prototype.concat function needs to
+        lookup the Symbol.isConcatSpreadable property. For any existing code
+        That property will never be set as it did not exist prior to ES6.
+
+        Similarly to the get_by_id_proto_load bytecode, this patch adds a new
+        bytecode, get_by_id_unset that checks the structureID of the base and
+        assigns undefined to the result.
+
+        There are no new tests here since we already have many tests that
+        incidentally cover this change.
+
+        * bytecode/BytecodeList.json:
+        * bytecode/BytecodeUseDef.h:
+        (JSC::computeUsesForBytecodeOffset):
+        (JSC::computeDefsForBytecodeOffset):
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::printGetByIdOp):
+        (JSC::CodeBlock::dumpBytecode):
+        (JSC::CodeBlock::finalizeLLIntInlineCaches):
+        * bytecode/GetByIdStatus.cpp:
+        (JSC::GetByIdStatus::computeFromLLInt):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGCapabilities.cpp:
+        (JSC::DFG::capabilityLevel):
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileMainPass):
+        (JSC::JIT::privateCompileSlowCases):
+        * llint/LLIntSlowPaths.cpp:
+        (JSC::LLInt::setupGetByIdPrototypeCache):
+        (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+        * llint/LLIntSlowPaths.h:
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+
 2016-05-26  Filip Pizlo  <[email protected]>
 
         Bogus uses of regexp matching should realize that they will OOM before they start swapping

Modified: trunk/Source/_javascript_Core/bytecode/BytecodeList.json (201455 => 201456)


--- trunk/Source/_javascript_Core/bytecode/BytecodeList.json	2016-05-27 18:35:49 UTC (rev 201455)
+++ trunk/Source/_javascript_Core/bytecode/BytecodeList.json	2016-05-27 18:36:30 UTC (rev 201456)
@@ -61,6 +61,7 @@
             { "name" : "op_get_array_length", "length" : 9 },
             { "name" : "op_get_by_id", "length" : 9  },
             { "name" : "op_get_by_id_proto_load", "length" : 9 },
+            { "name" : "op_get_by_id_unset", "length" : 9 },
             { "name" : "op_get_by_id_with_this", "length" : 5 },
             { "name" : "op_get_by_val_with_this", "length" : 5 },
             { "name" : "op_try_get_by_id", "length" : 4 },

Modified: trunk/Source/_javascript_Core/bytecode/BytecodeUseDef.h (201455 => 201456)


--- trunk/Source/_javascript_Core/bytecode/BytecodeUseDef.h	2016-05-27 18:35:49 UTC (rev 201455)
+++ trunk/Source/_javascript_Core/bytecode/BytecodeUseDef.h	2016-05-27 18:36:30 UTC (rev 201456)
@@ -159,6 +159,7 @@
     case op_try_get_by_id:
     case op_get_by_id:
     case op_get_by_id_proto_load:
+    case op_get_by_id_unset:
     case op_get_array_length:
     case op_typeof:
     case op_is_empty:
@@ -394,6 +395,7 @@
     case op_try_get_by_id:
     case op_get_by_id:
     case op_get_by_id_proto_load:
+    case op_get_by_id_unset:
     case op_get_by_id_with_this:
     case op_get_by_val_with_this:
     case op_get_array_length:

Modified: trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp (201455 => 201456)


--- trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp	2016-05-27 18:35:49 UTC (rev 201455)
+++ trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp	2016-05-27 18:36:30 UTC (rev 201456)
@@ -349,6 +349,9 @@
     case op_get_by_id_proto_load:
         op = "get_by_id_proto_load";
         break;
+    case op_get_by_id_unset:
+        op = "get_by_id_unset";
+        break;
     case op_get_array_length:
         op = "array_length";
         break;
@@ -1119,6 +1122,7 @@
         }
         case op_get_by_id:
         case op_get_by_id_proto_load:
+        case op_get_by_id_unset:
         case op_get_array_length: {
             printGetByIdOp(out, exec, location, it);
             printGetByIdCacheStatus(out, exec, location, stubInfos);
@@ -2806,7 +2810,8 @@
         Instruction* curInstruction = &instructions()[propertyAccessInstructions[i]];
         switch (interpreter->getOpcodeID(curInstruction[0].u.opcode)) {
         case op_get_by_id:
-        case op_get_by_id_proto_load: {
+        case op_get_by_id_proto_load:
+        case op_get_by_id_unset: {
             StructureID oldStructureID = curInstruction[4].u.structureID;
             if (!oldStructureID || Heap::isMarked(m_vm->heap.structureIDTable().get(oldStructureID)))
                 break;

Modified: trunk/Source/_javascript_Core/bytecode/GetByIdStatus.cpp (201455 => 201456)


--- trunk/Source/_javascript_Core/bytecode/GetByIdStatus.cpp	2016-05-27 18:35:49 UTC (rev 201455)
+++ trunk/Source/_javascript_Core/bytecode/GetByIdStatus.cpp	2016-05-27 18:36:30 UTC (rev 201456)
@@ -78,9 +78,11 @@
 
     Opcode opcode = instruction[0].u.opcode;
 
+    ASSERT(opcode == LLInt::getOpcode(op_get_array_length) || opcode == LLInt::getOpcode(op_try_get_by_id) || opcode == LLInt::getOpcode(op_get_by_id_proto_load) || opcode == LLInt::getOpcode(op_get_by_id) || opcode == LLInt::getOpcode(op_get_by_id_unset));
+
     // FIXME: We should not just bail if we see a try_get_by_id or a get_by_id_proto_load.
     // https://bugs.webkit.org/show_bug.cgi?id=158039
-    if (opcode == LLInt::getOpcode(op_get_array_length) || opcode == LLInt::getOpcode(op_try_get_by_id) || opcode == LLInt::getOpcode(op_get_by_id_proto_load))
+    if (opcode != LLInt::getOpcode(op_get_by_id))
         return GetByIdStatus(NoInformation, false);
 
     StructureID structureID = instruction[4].u.structureID;

Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (201455 => 201456)


--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2016-05-27 18:35:49 UTC (rev 201455)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2016-05-27 18:36:30 UTC (rev 201456)
@@ -4082,6 +4082,7 @@
 
         case op_get_by_id:
         case op_get_by_id_proto_load:
+        case op_get_by_id_unset:
         case op_get_array_length: {
             SpeculatedType prediction = getPrediction();
             

Modified: trunk/Source/_javascript_Core/dfg/DFGCapabilities.cpp (201455 => 201456)


--- trunk/Source/_javascript_Core/dfg/DFGCapabilities.cpp	2016-05-27 18:35:49 UTC (rev 201455)
+++ trunk/Source/_javascript_Core/dfg/DFGCapabilities.cpp	2016-05-27 18:36:30 UTC (rev 201456)
@@ -155,6 +155,7 @@
     case op_try_get_by_id:
     case op_get_by_id:
     case op_get_by_id_proto_load:
+    case op_get_by_id_unset:
     case op_get_by_id_with_this:
     case op_get_by_val_with_this:
     case op_get_array_length:

Modified: trunk/Source/_javascript_Core/jit/JIT.cpp (201455 => 201456)


--- trunk/Source/_javascript_Core/jit/JIT.cpp	2016-05-27 18:35:49 UTC (rev 201455)
+++ trunk/Source/_javascript_Core/jit/JIT.cpp	2016-05-27 18:36:30 UTC (rev 201456)
@@ -241,6 +241,7 @@
         DEFINE_OP(op_try_get_by_id)
         case op_get_array_length:
         case op_get_by_id_proto_load:
+        case op_get_by_id_unset:
         DEFINE_OP(op_get_by_id)
         DEFINE_OP(op_get_by_id_with_this)
         DEFINE_OP(op_get_by_val)
@@ -424,6 +425,7 @@
         DEFINE_SLOWCASE_OP(op_try_get_by_id)
         case op_get_array_length:
         case op_get_by_id_proto_load:
+        case op_get_by_id_unset:
         DEFINE_SLOWCASE_OP(op_get_by_id)
         DEFINE_SLOWCASE_OP(op_get_by_val)
         DEFINE_SLOWCASE_OP(op_instanceof)

Modified: trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp (201455 => 201456)


--- trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp	2016-05-27 18:35:49 UTC (rev 201455)
+++ trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp	2016-05-27 18:36:30 UTC (rev 201456)
@@ -589,7 +589,11 @@
     if (structure->typeInfo().prohibitsPropertyCaching() || structure->isDictionary())
         return;
 
-    ObjectPropertyConditionSet conditions = generateConditionsForPrototypePropertyHit(vm, codeBlock, exec, structure, slot.slotBase(), ident.impl());
+    ObjectPropertyConditionSet conditions;
+    if (slot.isUnset())
+        conditions = generateConditionsForPropertyMiss(vm, codeBlock, exec, structure, ident.impl());
+    else
+        conditions = generateConditionsForPrototypePropertyHit(vm, codeBlock, exec, structure, slot.slotBase(), ident.impl());
 
     if (!conditions.isValid())
         return;
@@ -604,16 +608,22 @@
             offset = condition.condition().offset();
         result.iterator->value.add(condition, pc)->install();
     }
-    ASSERT(offset != invalidOffset);
+    ASSERT((offset == invalidOffset) == slot.isUnset());
 
     ConcurrentJITLocker locker(codeBlock->m_lock);
 
+    if (slot.isUnset()) {
+        pc[0].u.opcode = LLInt::getOpcode(op_get_by_id_unset);
+        pc[4].u.structureID = structure->id();
+        return;
+    }
+    ASSERT(slot.isValue());
+
     pc[0].u.opcode = LLInt::getOpcode(op_get_by_id_proto_load);
     pc[4].u.structureID = structure->id();
     pc[5].u.operand = offset;
-    // We know that this pointer will remain valid because it is the prototype of some structure, s,
-    // watchpointed above. If any object with structure s were to change prototypes then the conditions
-    // for this cache would fail and this value will never be used again.
+    // We know that this pointer will remain valid because it will be cleared by either a watchpoint fire or
+    // during GC when we clear the LLInt caches.
     pc[6].u.pointer = slot.slotBase();
 }
 
@@ -632,11 +642,11 @@
     
     if (!LLINT_ALWAYS_ACCESS_SLOW
         && baseValue.isCell()
-        && slot.isCacheableValue()) {
+        && slot.isCacheable()) {
 
         JSCell* baseCell = baseValue.asCell();
         Structure* structure = baseCell->structure();
-        if (slot.slotBase() == baseValue) {
+        if (slot.isValue() && slot.slotBase() == baseValue) {
             // Start out by clearing out the old cache.
             pc[0].u.opcode = LLInt::getOpcode(op_get_by_id);
             pc[4].u.pointer = nullptr; // old structure
@@ -653,7 +663,7 @@
                 pc[4].u.structureID = structure->id();
                 pc[5].u.operand = slot.cachedOffset();
             }
-        } else if (UNLIKELY(pc[7].u.operand)) {
+        } else if (UNLIKELY(pc[7].u.operand && (slot.isValue() || slot.isUnset()))) {
             ASSERT(slot.slotBase() != baseValue);
 
             if (!(--pc[7].u.operand))

Modified: trunk/Source/_javascript_Core/llint/LLIntSlowPaths.h (201455 => 201456)


--- trunk/Source/_javascript_Core/llint/LLIntSlowPaths.h	2016-05-27 18:35:49 UTC (rev 201455)
+++ trunk/Source/_javascript_Core/llint/LLIntSlowPaths.h	2016-05-27 18:36:30 UTC (rev 201456)
@@ -71,7 +71,6 @@
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_instanceof_custom);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_try_get_by_id);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_get_by_id);
-LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_get_by_id_proto_load);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_get_arguments_length);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_put_by_id);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_del_by_id);

Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm (201455 => 201456)


--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm	2016-05-27 18:35:49 UTC (rev 201455)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm	2016-05-27 18:36:30 UTC (rev 201456)
@@ -1337,8 +1337,9 @@
 # opcode for own properties. We also allow for the cache to change anytime it fails,
 # since ping-ponging is free. At best we get lucky and the get_by_id will continue
 # to take fast path on the new cache. At worst we take slow path, which is what
-# we would have been doing anyway. For prototype properties, we will attempt to
-# convert opcode into a get_by_id_proto_load after a execution counter hits zero.
+# we would have been doing anyway. For prototype/unset properties, we will attempt to
+# convert opcode into a get_by_id_proto_load/get_by_id_unset, respectively, after an
+# execution counter hits zero.
 
 _llint_op_get_by_id:
     traceExecution()
@@ -1359,7 +1360,6 @@
     dispatch(9)
 
 
-
 _llint_op_get_by_id_proto_load:
     traceExecution()
     loadi 8[PC], t0
@@ -1380,6 +1380,23 @@
     dispatch(9)
 
 
+_llint_op_get_by_id_unset:
+    traceExecution()
+    loadi 8[PC], t0
+    loadi 16[PC], t1
+    loadConstantOrVariablePayload(t0, CellTag, t3, .opGetByIdUnsetSlow)
+    bineq JSCell::m_structureID[t3], t1, .opGetByIdUnsetSlow
+    loadi 4[PC], t2
+    storei UndefinedTag, TagOffset[cfr, t2, 8]
+    storei 0, PayloadOffset[cfr, t2, 8]
+    valueProfile(UndefinedTag, 0, 32, t2)
+    dispatch(9)
+
+.opGetByIdUnsetSlow:
+    callSlowPath(_llint_slow_path_get_by_id)
+    dispatch(9)
+
+
 _llint_op_get_array_length:
     traceExecution()
     loadi 8[PC], t0

Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm (201455 => 201456)


--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm	2016-05-27 18:35:49 UTC (rev 201455)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm	2016-05-27 18:36:30 UTC (rev 201456)
@@ -1252,6 +1252,23 @@
     dispatch(9)
 
 
+_llint_op_get_by_id_unset:
+    traceExecution()
+    loadisFromInstruction(2, t0)
+    loadConstantOrVariableCell(t0, t3, .opGetByIdUnsetSlow)
+    loadi JSCell::m_structureID[t3], t1
+    loadisFromInstruction(4, t2)
+    bineq t2, t1, .opGetByIdUnsetSlow
+    loadisFromInstruction(1, t2)
+    storeq ValueUndefined, [cfr, t2, 8]
+    valueProfile(ValueUndefined, 8, t1)
+    dispatch(9)
+
+.opGetByIdUnsetSlow:
+    callSlowPath(_llint_slow_path_get_by_id)
+    dispatch(9)
+
+
 _llint_op_get_array_length:
     traceExecution()
     loadisFromInstruction(2, t0)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to