Commit 4cafdbd1 authored by fpizlo@apple.com's avatar fpizlo@apple.com
Browse files

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:



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@128219 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 72f7f916
2012-09-11 Filip Pizlo <fpizlo@apple.com>
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 <rakuco@webkit.org>
 
[EFL] Rewrite the EFL-related Find modules
......
......@@ -291,10 +291,14 @@ void CodeBlock::printGetByIdCacheStatus(ExecState* exec, int location)
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 @@ void CodeBlock::finalizeUnconditionally()
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();
}
......
......@@ -43,6 +43,9 @@ GetByIdStatus GetByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned
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)
......
......@@ -2276,7 +2276,8 @@ bool ByteCodeParser::parseBlock(unsigned limit)
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);
......
......@@ -120,6 +120,7 @@ inline CapabilityLevel canCompileOpcode(OpcodeID opcodeID, CodeBlock*, Instructi
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:
......
......@@ -92,7 +92,7 @@ private:
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());
......
......@@ -256,6 +256,7 @@ void JIT::privateCompileMainPass()
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 @@ void JIT::privateCompileMainPass()
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 @@ void JIT::privateCompileSlowCases()
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)
......
......@@ -858,7 +858,7 @@ LLINT_SLOW_PATH_DECL(slow_path_get_by_id)
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 @@ LLINT_SLOW_PATH_DECL(slow_path_get_by_id)
}
}
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
......
......@@ -845,9 +845,6 @@ macro notSupported()
end
end
_llint_op_get_array_length:
notSupported()
_llint_op_get_by_id_chain:
notSupported()
......
......@@ -1168,6 +1168,34 @@ _llint_op_get_by_id_out_of_line:
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
......
......@@ -1015,6 +1015,34 @@ _llint_op_get_by_id_out_of_line:
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
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment