Title: [128219] trunk/Source/_javascript_Core
Revision
128219
Author
[email protected]
Date
2012-09-11 13:00:31 -0700 (Tue, 11 Sep 2012)

Log Message

LLInt should optimize and profile array length accesses
https://bugs.webkit.org/show_bug.cgi?id=96417

Reviewed by Oliver Hunt.

This fixes the following hole in our array profiling strategy, where the array
is large (more than 1000 elements):
        
for (var i = 0; i < array.length; ++i) ...
        
The peeled use of array.length (in the array prologue) will execute only once
before DFG optimization kicks in from the loop's OSR point. Since it executed
only once, it executed in the LLInt. And prior to this patch, the LLInt did
not profile array.length accesses - so the DFG will assume, based on the lack
of profiling, that the access is in fact not an access to the JSArray length
property. That could then impede our ability to hoist the array structure
check, and may make us pessimistic in other ways as well, since the generic
GetById used for the array length access will be viewed as a side-effecting
operation.

* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::printGetByIdCacheStatus):
(JSC::CodeBlock::finalizeUnconditionally):
* bytecode/GetByIdStatus.cpp:
(JSC::GetByIdStatus::computeFromLLInt):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.h:
(JSC::DFG::canCompileOpcode):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
(JSC::JIT::privateCompileSlowCases):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* llint/LowLevelInterpreter.asm:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (128218 => 128219)


--- trunk/Source/_javascript_Core/ChangeLog	2012-09-11 19:17:37 UTC (rev 128218)
+++ trunk/Source/_javascript_Core/ChangeLog	2012-09-11 20:00:31 UTC (rev 128219)
@@ -1,3 +1,45 @@
+2012-09-11  Filip Pizlo  <[email protected]>
+
+        LLInt should optimize and profile array length accesses
+        https://bugs.webkit.org/show_bug.cgi?id=96417
+
+        Reviewed by Oliver Hunt.
+
+        This fixes the following hole in our array profiling strategy, where the array
+        is large (more than 1000 elements):
+        
+        for (var i = 0; i < array.length; ++i) ...
+        
+        The peeled use of array.length (in the array prologue) will execute only once
+        before DFG optimization kicks in from the loop's OSR point. Since it executed
+        only once, it executed in the LLInt. And prior to this patch, the LLInt did
+        not profile array.length accesses - so the DFG will assume, based on the lack
+        of profiling, that the access is in fact not an access to the JSArray length
+        property. That could then impede our ability to hoist the array structure
+        check, and may make us pessimistic in other ways as well, since the generic
+        GetById used for the array length access will be viewed as a side-effecting
+        operation.
+
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::printGetByIdCacheStatus):
+        (JSC::CodeBlock::finalizeUnconditionally):
+        * bytecode/GetByIdStatus.cpp:
+        (JSC::GetByIdStatus::computeFromLLInt):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGCapabilities.h:
+        (JSC::DFG::canCompileOpcode):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileMainPass):
+        (JSC::JIT::privateCompileSlowCases):
+        * llint/LLIntSlowPaths.cpp:
+        (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+        * llint/LowLevelInterpreter.asm:
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+
 2012-09-11  Raphael Kubo da Costa  <[email protected]>
 
         [EFL] Rewrite the EFL-related Find modules

Modified: trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp (128218 => 128219)


--- trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp	2012-09-11 19:17:37 UTC (rev 128218)
+++ trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp	2012-09-11 20:00:31 UTC (rev 128219)
@@ -291,10 +291,14 @@
     UNUSED_PARAM(ident); // tell the compiler to shut up in certain platform configurations.
     
 #if ENABLE(LLINT)
-    Structure* structure = instruction[4].u.structure.get();
-    dataLog(" llint(");
-    dumpStructure("struct", exec, structure, ident);
-    dataLog(")");
+    if (exec->interpreter()->getOpcodeID(instruction[0].u.opcode) == op_get_array_length)
+        dataLog(" llint(array_length)");
+    else {
+        Structure* structure = instruction[4].u.structure.get();
+        dataLog(" llint(");
+        dumpStructure("struct", exec, structure, ident);
+        dataLog(")");
+    }
 #endif
 
 #if ENABLE(JIT)
@@ -2081,6 +2085,8 @@
                 curInstruction[7].u.structureChain.clear();
                 curInstruction[0].u.opcode = interpreter->getOpcode(op_put_by_id);
                 break;
+            case op_get_array_length:
+                break;
             default:
                 ASSERT_NOT_REACHED();
             }

Modified: trunk/Source/_javascript_Core/bytecode/GetByIdStatus.cpp (128218 => 128219)


--- trunk/Source/_javascript_Core/bytecode/GetByIdStatus.cpp	2012-09-11 19:17:37 UTC (rev 128218)
+++ trunk/Source/_javascript_Core/bytecode/GetByIdStatus.cpp	2012-09-11 20:00:31 UTC (rev 128219)
@@ -43,6 +43,9 @@
     
     if (instruction[0].u.opcode == LLInt::getOpcode(llint_op_method_check))
         instruction++;
+    
+    if (instruction[0].u.opcode == LLInt::getOpcode(llint_op_get_array_length))
+        return GetByIdStatus(NoInformation, false);
 
     Structure* structure = instruction[4].u.structure.get();
     if (!structure)

Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (128218 => 128219)


--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2012-09-11 19:17:37 UTC (rev 128218)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2012-09-11 20:00:31 UTC (rev 128219)
@@ -2276,7 +2276,8 @@
             NEXT_OPCODE(op_put_scoped_var);
         }
         case op_get_by_id:
-        case op_get_by_id_out_of_line: {
+        case op_get_by_id_out_of_line:
+        case op_get_array_length: {
             SpeculatedType prediction = getPrediction();
             
             NodeIndex base = get(currentInstruction[2].u.operand);

Modified: trunk/Source/_javascript_Core/dfg/DFGCapabilities.h (128218 => 128219)


--- trunk/Source/_javascript_Core/dfg/DFGCapabilities.h	2012-09-11 19:17:37 UTC (rev 128218)
+++ trunk/Source/_javascript_Core/dfg/DFGCapabilities.h	2012-09-11 20:00:31 UTC (rev 128219)
@@ -120,6 +120,7 @@
     case op_put_scoped_var:
     case op_get_by_id:
     case op_get_by_id_out_of_line:
+    case op_get_array_length:
     case op_put_by_id:
     case op_put_by_id_out_of_line:
     case op_put_by_id_transition_direct:

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (128218 => 128219)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2012-09-11 19:17:37 UTC (rev 128218)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2012-09-11 20:00:31 UTC (rev 128219)
@@ -92,7 +92,7 @@
                 arrayMode = refineArrayMode(
                     fromObserved(arrayProfile->observedArrayModes(), false),
                     m_graph[node.child1()].prediction(),
-                    m_graph[m_compileIndex].prediction());                    
+                    m_graph[m_compileIndex].prediction());
                 if (modeSupportsLength(arrayMode) && arrayProfile->hasDefiniteStructure()) {
                     m_graph.ref(nodePtr->child1());
                     Node checkStructure(CheckStructure, nodePtr->codeOrigin, OpInfo(m_graph.addStructureSet(arrayProfile->expectedStructure())), nodePtr->child1().index());

Modified: trunk/Source/_javascript_Core/jit/JIT.cpp (128218 => 128219)


--- trunk/Source/_javascript_Core/jit/JIT.cpp	2012-09-11 19:17:37 UTC (rev 128218)
+++ trunk/Source/_javascript_Core/jit/JIT.cpp	2012-09-11 20:00:31 UTC (rev 128219)
@@ -256,6 +256,7 @@
         DEFINE_OP(op_eq)
         DEFINE_OP(op_eq_null)
         case op_get_by_id_out_of_line:
+        case op_get_array_length:
         DEFINE_OP(op_get_by_id)
         DEFINE_OP(op_get_arguments_length)
         DEFINE_OP(op_get_by_val)
@@ -358,7 +359,6 @@
         DEFINE_OP(op_to_jsnumber)
         DEFINE_OP(op_to_primitive)
 
-        case op_get_array_length:
         case op_get_by_id_chain:
         case op_get_by_id_generic:
         case op_get_by_id_proto:
@@ -446,6 +446,7 @@
         DEFINE_SLOWCASE_OP(op_div)
         DEFINE_SLOWCASE_OP(op_eq)
         case op_get_by_id_out_of_line:
+        case op_get_array_length:
         DEFINE_SLOWCASE_OP(op_get_by_id)
         DEFINE_SLOWCASE_OP(op_get_arguments_length)
         DEFINE_SLOWCASE_OP(op_get_by_val)

Modified: trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp (128218 => 128219)


--- trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp	2012-09-11 19:17:37 UTC (rev 128218)
+++ trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp	2012-09-11 20:00:31 UTC (rev 128219)
@@ -858,7 +858,7 @@
     JSValue result = baseValue.get(exec, ident, slot);
     LLINT_CHECK_EXCEPTION();
     LLINT_OP(1) = result;
-
+    
     if (!LLINT_ALWAYS_ACCESS_SLOW
         && baseValue.isCell()
         && slot.isCacheable()
@@ -882,6 +882,17 @@
         }
     }
 
+    if (!LLINT_ALWAYS_ACCESS_SLOW
+        && isJSArray(baseValue)
+        && ident == exec->propertyNames().length) {
+        pc[0].u.opcode = LLInt::getOpcode(llint_op_get_array_length);
+#if ENABLE(VALUE_PROFILER)
+        ArrayProfile* arrayProfile = codeBlock->getOrAddArrayProfile(pc - codeBlock->instructions().begin());
+        arrayProfile->observeStructure(baseValue.asCell()->structure());
+        pc[4].u.arrayProfile = arrayProfile;
+#endif
+    }
+
 #if ENABLE(VALUE_PROFILER)    
     pc[OPCODE_LENGTH(op_get_by_id) - 1].u.profile->m_buckets[0] = JSValue::encode(result);
 #endif

Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter.asm (128218 => 128219)


--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter.asm	2012-09-11 19:17:37 UTC (rev 128218)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter.asm	2012-09-11 20:00:31 UTC (rev 128219)
@@ -845,9 +845,6 @@
     end
 end
 
-_llint_op_get_array_length:
-    notSupported()
-
 _llint_op_get_by_id_chain:
     notSupported()
 

Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm (128218 => 128219)


--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm	2012-09-11 19:17:37 UTC (rev 128218)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm	2012-09-11 20:00:31 UTC (rev 128219)
@@ -1168,6 +1168,34 @@
     getById(withOutOfLineStorage)
 
 
+_llint_op_get_array_length:
+    traceExecution()
+    loadi 8[PC], t0
+    loadp 16[PC], t1
+    loadConstantOrVariablePayload(t0, CellTag, t3, .opGetArrayLengthSlot)
+    loadp JSCell::m_structure[t3], t2
+    if VALUE_PROFILER
+        storep t2, ArrayProfile::m_lastSeenStructure[t1]
+    end
+    loadp CodeBlock[cfr], t1
+    loadp CodeBlock::m_globalData[t1], t1
+    loadp JSGlobalData::jsArrayClassInfo[t1], t1
+    bpneq Structure::m_classInfo[t2], t1, .opGetArrayLengthSlow
+    loadi 4[PC], t1
+    loadp 32[PC], t2
+    loadp JSArray::m_storage[t3], t0
+    loadi ArrayStorage::m_length[t0], t0
+    bilt t0, 0, .opGetArrayLengthSlow
+    valueProfile(Int32Tag, t0, t2)
+    storep t0, PayloadOffset[cfr, t1, 8]
+    storep Int32Tag, TagOffset[cfr, t1, 8]
+    dispatch(9)
+
+.opGetArrayLengthSlow:
+    callSlowPath(_llint_slow_path_get_by_id)
+    dispatch(9)
+
+
 _llint_op_get_arguments_length:
     traceExecution()
     loadi 8[PC], t0

Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm (128218 => 128219)


--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm	2012-09-11 19:17:37 UTC (rev 128218)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm	2012-09-11 20:00:31 UTC (rev 128219)
@@ -1015,6 +1015,34 @@
     getById(withOutOfLineStorage)
 
 
+_llint_op_get_array_length:
+    traceExecution()
+    loadis 16[PB, PC, 8], t0
+    loadp 32[PB, PC, 8], t1
+    loadConstantOrVariableCell(t0, t3, .opGetArrayLengthSlow)
+    loadp JSCell::m_structure[t3], t2
+    if VALUE_PROFILER
+        storep t2, ArrayProfile::m_lastSeenStructure[t1]
+    end
+    loadp CodeBlock[cfr], t1
+    loadp CodeBlock::m_globalData[t1], t1
+    loadp JSGlobalData::jsArrayClassInfo[t1], t1
+    bpneq Structure::m_classInfo[t2], t1, .opGetArrayLengthSlow
+    loadis 8[PB, PC, 8], t1
+    loadp 64[PB, PC, 8], t2
+    loadp JSArray::m_storage[t3], t0
+    loadi ArrayStorage::m_length[t0], t0
+    bilt t0, 0, .opGetArrayLengthSlow
+    orp tagTypeNumber, t0
+    valueProfile(t0, t2)
+    storep t0, [cfr, t1, 8]
+    dispatch(9)
+
+.opGetArrayLengthSlow:
+    callSlowPath(_llint_slow_path_get_by_id)
+    dispatch(9)
+
+
 _llint_op_get_arguments_length:
     traceExecution()
     loadis 16[PB, PC, 8], t0
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to