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