Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (89985 => 89986)
--- trunk/Source/_javascript_Core/ChangeLog 2011-06-29 02:41:47 UTC (rev 89985)
+++ trunk/Source/_javascript_Core/ChangeLog 2011-06-29 03:18:58 UTC (rev 89986)
@@ -1,3 +1,28 @@
+2011-06-28 Filip Pizlo <[email protected]>
+
+ Reviewed by Gavin Barraclough.
+
+ DFG JIT lacks array.length caching.
+ https://bugs.webkit.org/show_bug.cgi?id=63505
+
+ * bytecode/StructureStubInfo.h:
+ * dfg/DFGJITCodeGenerator.cpp:
+ (JSC::DFG::JITCodeGenerator::cachedGetById):
+ (JSC::DFG::JITCodeGenerator::cachedPutById):
+ * dfg/DFGJITCodeGenerator.h:
+ (JSC::DFG::JITCodeGenerator::tryAllocate):
+ (JSC::DFG::JITCodeGenerator::selectScratchGPR):
+ (JSC::DFG::JITCodeGenerator::silentSpillAllRegisters):
+ * dfg/DFGJITCompiler.cpp:
+ (JSC::DFG::JITCompiler::compileFunction):
+ * dfg/DFGJITCompiler.h:
+ (JSC::DFG::JITCompiler::addPropertyAccess):
+ (JSC::DFG::JITCompiler::PropertyAccessRecord::PropertyAccessRecord):
+ * dfg/DFGRegisterBank.h:
+ (JSC::DFG::RegisterBank::tryAllocate):
+ * dfg/DFGRepatch.cpp:
+ (JSC::DFG::tryCacheGetByID):
+
2011-06-28 Pierre Rossi <[email protected]>
Reviewed by Eric Seidel.
Modified: trunk/Source/_javascript_Core/bytecode/StructureStubInfo.h (89985 => 89986)
--- trunk/Source/_javascript_Core/bytecode/StructureStubInfo.h 2011-06-29 02:41:47 UTC (rev 89985)
+++ trunk/Source/_javascript_Core/bytecode/StructureStubInfo.h 2011-06-29 03:18:58 UTC (rev 89986)
@@ -132,8 +132,14 @@
union {
struct {
- intptr_t deltaCheckToCall;
- intptr_t deltaCallToLoadOrStore;
+ int8_t deltaCheckImmToCall;
+ int8_t deltaCallToStructCheck;
+ int8_t deltaCallToLoadOrStore;
+ int8_t deltaCallToSlowCase;
+ int8_t deltaCallToDone;
+ int8_t baseGPR;
+ int8_t valueGPR;
+ int8_t scratchGPR;
} unset;
struct {
WriteBarrierBase<Structure> baseObjectStructure;
Modified: trunk/Source/_javascript_Core/dfg/DFGJITCodeGenerator.cpp (89985 => 89986)
--- trunk/Source/_javascript_Core/dfg/DFGJITCodeGenerator.cpp 2011-06-29 02:41:47 UTC (rev 89985)
+++ trunk/Source/_javascript_Core/dfg/DFGJITCodeGenerator.cpp 2011-06-29 03:18:58 UTC (rev 89986)
@@ -322,11 +322,27 @@
void JITCodeGenerator::cachedGetById(GPRReg baseGPR, GPRReg resultGPR, unsigned identifierNumber, JITCompiler::Jump slowPathTarget)
{
+ GPRReg scratchGPR;
+
+ if (resultGPR == baseGPR)
+ scratchGPR = tryAllocate();
+ else
+ scratchGPR = resultGPR;
+
JITCompiler::DataLabelPtr structureToCompare;
- JITCompiler::Jump structureCheck = m_jit.branchPtrWithPatch(JITCompiler::Equal, JITCompiler::Address(baseGPR, JSCell::structureOffset()), structureToCompare, JITCompiler::TrustedImmPtr(reinterpret_cast<void*>(-1)));
+ JITCompiler::Jump structureCheck = m_jit.branchPtrWithPatch(JITCompiler::NotEqual, JITCompiler::Address(baseGPR, JSCell::structureOffset()), structureToCompare, JITCompiler::TrustedImmPtr(reinterpret_cast<void*>(-1)));
+ m_jit.loadPtr(JITCompiler::Address(baseGPR, JSObject::offsetOfPropertyStorage()), resultGPR);
+ JITCompiler::DataLabelCompact loadWithPatch = m_jit.loadPtrWithCompactAddressOffsetPatch(JITCompiler::Address(resultGPR, 0), resultGPR);
+
+ JITCompiler::Jump done = m_jit.jump();
+
+ structureCheck.link(&m_jit);
+
if (slowPathTarget.isSet())
slowPathTarget.link(&m_jit);
+
+ JITCompiler::Label slowCase = m_jit.label();
silentSpillAllRegisters(resultGPR, baseGPR);
m_jit.move(baseGPR, GPRInfo::argumentGPR1);
@@ -335,19 +351,21 @@
JITCompiler::Call functionCall = appendCallWithExceptionCheck(operationGetByIdOptimize);
m_jit.move(GPRInfo::returnValueGPR, resultGPR);
silentFillAllRegisters(resultGPR);
+
+ done.link(&m_jit);
+
+ JITCompiler::Label doneLabel = m_jit.label();
- JITCompiler::Jump handledByC = m_jit.jump();
- structureCheck.link(&m_jit);
-
- m_jit.loadPtr(JITCompiler::Address(baseGPR, JSObject::offsetOfPropertyStorage()), resultGPR);
- JITCompiler::DataLabelCompact loadWithPatch = m_jit.loadPtrWithCompactAddressOffsetPatch(JITCompiler::Address(resultGPR, 0), resultGPR);
-
- intptr_t checkToCall = m_jit.differenceBetween(structureToCompare, functionCall);
- intptr_t callToLoad = m_jit.differenceBetween(functionCall, loadWithPatch);
-
- handledByC.link(&m_jit);
-
- m_jit.addPropertyAccess(functionCall, checkToCall, callToLoad);
+ int8_t checkImmToCall = static_cast<int8_t>(m_jit.differenceBetween(structureToCompare, functionCall));
+ int8_t callToCheck = static_cast<int8_t>(m_jit.differenceBetween(functionCall, structureCheck));
+ int8_t callToLoad = static_cast<int8_t>(m_jit.differenceBetween(functionCall, loadWithPatch));
+ int8_t callToSlowCase = static_cast<int8_t>(m_jit.differenceBetween(functionCall, slowCase));
+ int8_t callToDone = static_cast<int8_t>(m_jit.differenceBetween(functionCall, doneLabel));
+
+ m_jit.addPropertyAccess(functionCall, checkImmToCall, callToCheck, callToLoad, callToSlowCase, callToDone, static_cast<int8_t>(baseGPR), static_cast<int8_t>(resultGPR), static_cast<int8_t>(scratchGPR));
+
+ if (scratchGPR != resultGPR && scratchGPR != InvalidGPRReg)
+ unlock(scratchGPR);
}
void JITCodeGenerator::cachedPutById(GPRReg baseGPR, GPRReg valueGPR, GPRReg scratchGPR, unsigned identifierNumber, PutKind putKind, JITCompiler::Jump slowPathTarget)
@@ -383,12 +401,13 @@
m_jit.loadPtr(JITCompiler::Address(baseGPR, JSObject::offsetOfPropertyStorage()), scratchGPR);
JITCompiler::DataLabel32 storeWithPatch = m_jit.storePtrWithAddressOffsetPatch(valueGPR, JITCompiler::Address(scratchGPR, 0));
- intptr_t checkToCall = m_jit.differenceBetween(structureToCompare, functionCall);
- intptr_t callToStore = m_jit.differenceBetween(functionCall, storeWithPatch);
+ int8_t checkImmToCall = static_cast<int8_t>(m_jit.differenceBetween(structureToCompare, functionCall));
+ int8_t callToCheck = static_cast<int8_t>(m_jit.differenceBetween(functionCall, structureCheck));
+ int8_t callToStore = static_cast<int8_t>(m_jit.differenceBetween(functionCall, storeWithPatch));
handledByC.link(&m_jit);
- m_jit.addPropertyAccess(functionCall, checkToCall, callToStore);
+ m_jit.addPropertyAccess(functionCall, checkImmToCall, callToCheck, callToStore);
}
#ifndef NDEBUG
Modified: trunk/Source/_javascript_Core/dfg/DFGJITCodeGenerator.h (89985 => 89986)
--- trunk/Source/_javascript_Core/dfg/DFGJITCodeGenerator.h 2011-06-29 02:41:47 UTC (rev 89985)
+++ trunk/Source/_javascript_Core/dfg/DFGJITCodeGenerator.h 2011-06-29 03:18:58 UTC (rev 89986)
@@ -124,6 +124,10 @@
spill(spillMe);
return specific;
}
+ GPRReg tryAllocate()
+ {
+ return m_gprs.tryAllocate();
+ }
FPRReg fprAllocate()
{
VirtualRegister spillMe;
@@ -151,6 +155,20 @@
return info.registerFormat() == DataFormatDouble;
}
+ static GPRReg selectScratchGPR(GPRReg preserve1 = InvalidGPRReg, GPRReg preserve2 = InvalidGPRReg, GPRReg preserve3 = InvalidGPRReg)
+ {
+ if (preserve1 != GPRInfo::regT0 && preserve2 != GPRInfo::regT0 && preserve3 != GPRInfo::regT0)
+ return GPRInfo::regT0;
+
+ if (preserve1 != GPRInfo::regT1 && preserve2 != GPRInfo::regT1 && preserve3 != GPRInfo::regT1)
+ return GPRInfo::regT1;
+
+ if (preserve1 != GPRInfo::regT2 && preserve2 != GPRInfo::regT2 && preserve3 != GPRInfo::regT2)
+ return GPRInfo::regT2;
+
+ return GPRInfo::regT3;
+ }
+
protected:
JITCodeGenerator(JITCompiler& jit, bool isSpeculative)
: m_jit(jit)
@@ -244,18 +262,10 @@
unboxDouble(canTrample, info.fpr());
}
}
-
+
void silentSpillAllRegisters(GPRReg exclude, GPRReg preserve1 = InvalidGPRReg, GPRReg preserve2 = InvalidGPRReg, GPRReg preserve3 = InvalidGPRReg)
{
- GPRReg canTrample = GPRInfo::regT0;
- if (preserve1 != GPRInfo::regT0 && preserve2 != GPRInfo::regT0 && preserve3 != GPRInfo::regT0)
- canTrample = GPRInfo::regT0;
- else if (preserve1 != GPRInfo::regT1 && preserve2 != GPRInfo::regT1 && preserve3 != GPRInfo::regT1)
- canTrample = GPRInfo::regT1;
- else if (preserve1 != GPRInfo::regT2 && preserve2 != GPRInfo::regT2 && preserve3 != GPRInfo::regT2)
- canTrample = GPRInfo::regT2;
- else
- canTrample = GPRInfo::regT3;
+ GPRReg canTrample = selectScratchGPR(preserve1, preserve2, preserve3);
for (gpr_iterator iter = m_gprs.begin(); iter != m_gprs.end(); ++iter) {
if (iter.name() != InvalidVirtualRegister)
Modified: trunk/Source/_javascript_Core/dfg/DFGJITCompiler.cpp (89985 => 89986)
--- trunk/Source/_javascript_Core/dfg/DFGJITCompiler.cpp 2011-06-29 02:41:47 UTC (rev 89985)
+++ trunk/Source/_javascript_Core/dfg/DFGJITCompiler.cpp 2011-06-29 03:18:58 UTC (rev 89986)
@@ -387,8 +387,14 @@
for (unsigned i = 0; i < m_propertyAccesses.size(); ++i) {
StructureStubInfo& info = m_codeBlock->structureStubInfo(i);
info.callReturnLocation = linkBuffer.locationOf(m_propertyAccesses[i].m_functionCall);
- info.u.unset.deltaCheckToCall = m_propertyAccesses[i].m_deltaCheckToCall;
+ info.u.unset.deltaCheckImmToCall = m_propertyAccesses[i].m_deltaCheckImmToCall;
+ info.u.unset.deltaCallToStructCheck = m_propertyAccesses[i].m_deltaCallToStructCheck;
info.u.unset.deltaCallToLoadOrStore = m_propertyAccesses[i].m_deltaCallToLoadOrStore;
+ info.u.unset.deltaCallToSlowCase = m_propertyAccesses[i].m_deltaCallToSlowCase;
+ info.u.unset.deltaCallToDone = m_propertyAccesses[i].m_deltaCallToDone;
+ info.u.unset.baseGPR = m_propertyAccesses[i].m_baseGPR;
+ info.u.unset.valueGPR = m_propertyAccesses[i].m_valueGPR;
+ info.u.unset.scratchGPR = m_propertyAccesses[i].m_scratchGPR;
}
// FIXME: switch the register file check & arity check over to DFGOpertaion style calls, not JIT stubs.
Modified: trunk/Source/_javascript_Core/dfg/DFGJITCompiler.h (89985 => 89986)
--- trunk/Source/_javascript_Core/dfg/DFGJITCompiler.h 2011-06-29 02:41:47 UTC (rev 89985)
+++ trunk/Source/_javascript_Core/dfg/DFGJITCompiler.h 2011-06-29 03:18:58 UTC (rev 89986)
@@ -233,9 +233,9 @@
void clearSamplingFlag(int32_t flag);
#endif
- void addPropertyAccess(JITCompiler::Call functionCall, intptr_t deltaCheckToCall, intptr_t deltaCallToLoadOrStore)
+ void addPropertyAccess(JITCompiler::Call functionCall, int16_t deltaCheckImmToCall, int16_t deltaCallToStructCheck, int16_t deltaCallToLoadOrStore, int16_t deltaCallToSlowCase = 0, int16_t deltaCallToDone = 0, int8_t baseGPR = 0, int8_t valueGPR = 0, int8_t scratchGPR = 0)
{
- m_propertyAccesses.append(PropertyAccessRecord(functionCall, deltaCheckToCall, deltaCallToLoadOrStore));
+ m_propertyAccesses.append(PropertyAccessRecord(functionCall, deltaCheckImmToCall, deltaCallToStructCheck, deltaCallToLoadOrStore, deltaCallToSlowCase, deltaCallToDone, baseGPR, valueGPR, scratchGPR));
}
private:
@@ -259,16 +259,28 @@
Vector<CallRecord> m_calls;
struct PropertyAccessRecord {
- PropertyAccessRecord(JITCompiler::Call functionCall, intptr_t deltaCheckToCall, intptr_t deltaCallToLoadOrStore)
+ PropertyAccessRecord(JITCompiler::Call functionCall, int8_t deltaCheckImmToCall, int8_t deltaCallToStructCheck, int8_t deltaCallToLoadOrStore, int8_t deltaCallToSlowCase, int8_t deltaCallToDone, int8_t baseGPR, int8_t valueGPR, int8_t scratchGPR)
: m_functionCall(functionCall)
- , m_deltaCheckToCall(deltaCheckToCall)
+ , m_deltaCheckImmToCall(deltaCheckImmToCall)
+ , m_deltaCallToStructCheck(deltaCallToStructCheck)
, m_deltaCallToLoadOrStore(deltaCallToLoadOrStore)
+ , m_deltaCallToSlowCase(deltaCallToSlowCase)
+ , m_deltaCallToDone(deltaCallToDone)
+ , m_baseGPR(baseGPR)
+ , m_valueGPR(valueGPR)
+ , m_scratchGPR(scratchGPR)
{
}
JITCompiler::Call m_functionCall;
- intptr_t m_deltaCheckToCall;
- intptr_t m_deltaCallToLoadOrStore;
+ int8_t m_deltaCheckImmToCall;
+ int8_t m_deltaCallToStructCheck;
+ int8_t m_deltaCallToLoadOrStore;
+ int8_t m_deltaCallToSlowCase;
+ int8_t m_deltaCallToDone;
+ int8_t m_baseGPR;
+ int8_t m_valueGPR;
+ int8_t m_scratchGPR;
};
Vector<PropertyAccessRecord, 4> m_propertyAccesses;
Modified: trunk/Source/_javascript_Core/dfg/DFGRegisterBank.h (89985 => 89986)
--- trunk/Source/_javascript_Core/dfg/DFGRegisterBank.h 2011-06-29 02:41:47 UTC (rev 89985)
+++ trunk/Source/_javascript_Core/dfg/DFGRegisterBank.h 2011-06-29 03:18:58 UTC (rev 89986)
@@ -79,6 +79,26 @@
{
}
+ // Attempt to allocate a register - this function finds an unlocked
+ // register, locks it, and returns it. If none can be found, this
+ // returns -1 (InvalidGPRReg or InvalidFPRReg).
+ RegID tryAllocate()
+ {
+ VirtualRegister ignored;
+
+ for (uint32_t i = m_lastAllocated + 1; i < NUM_REGS; ++i) {
+ if (!m_data[i].lockCount && m_data[i].name == InvalidVirtualRegister)
+ return allocateInternal(i, ignored);
+ }
+ // Loop over the remaining entries.
+ for (uint32_t i = 0; i <= m_lastAllocated; ++i) {
+ if (!m_data[i].lockCount && m_data[i].name == InvalidVirtualRegister)
+ return allocateInternal(i, ignored);
+ }
+
+ return (RegID)-1;
+ }
+
// Allocate a register - this function finds an unlocked register,
// locks it, and returns it. If any named registers exist, one
// of these should be selected to be allocated. If all unlocked
Modified: trunk/Source/_javascript_Core/dfg/DFGRepatch.cpp (89985 => 89986)
--- trunk/Source/_javascript_Core/dfg/DFGRepatch.cpp 2011-06-29 02:41:47 UTC (rev 89985)
+++ trunk/Source/_javascript_Core/dfg/DFGRepatch.cpp 2011-06-29 03:18:58 UTC (rev 89986)
@@ -28,6 +28,8 @@
#if ENABLE(DFG_JIT)
+#include "DFGJITCodeGenerator.h"
+#include "LinkBuffer.h"
#include "RepatchBuffer.h"
namespace JSC { namespace DFG {
@@ -46,22 +48,86 @@
repatchBuffer.relink(stubInfo.callReturnLocation, slowPathFunction);
// Patch the structure check & the offset of the load.
- repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelPtrAtOffset(-(intptr_t)stubInfo.u.unset.deltaCheckToCall), structure);
+ repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelPtrAtOffset(-(intptr_t)stubInfo.u.unset.deltaCheckImmToCall), structure);
if (compact)
repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.u.unset.deltaCallToLoadOrStore), sizeof(JSValue) * offset);
else
repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.u.unset.deltaCallToLoadOrStore), sizeof(JSValue) * offset);
}
-static bool tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier&, const PropertySlot& slot, StructureStubInfo& stubInfo)
+static bool tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo)
{
// FIXME: Write a test that proves we need to check for recursion here just
// like the interpreter does, then add a check for recursion.
CodeBlock* codeBlock = exec->codeBlock();
JSGlobalData* globalData = &exec->globalData();
+
+ if (isJSArray(globalData, baseValue) && propertyName == exec->propertyNames().length) {
+ GPRReg baseGPR = static_cast<GPRReg>(stubInfo.u.unset.baseGPR);
+ GPRReg resultGPR = static_cast<GPRReg>(stubInfo.u.unset.valueGPR);
+ GPRReg scratchGPR = static_cast<GPRReg>(stubInfo.u.unset.scratchGPR);
+ bool needToRestoreScratch = false;
+
+ MacroAssembler stubJit;
+
+ if (scratchGPR == InvalidGPRReg) {
+ scratchGPR = JITCodeGenerator::selectScratchGPR(baseGPR, resultGPR);
+ stubJit.push(scratchGPR);
+ needToRestoreScratch = true;
+ }
+
+ MacroAssembler::Jump failureCase1 = stubJit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR), MacroAssembler::TrustedImmPtr(globalData->jsArrayVPtr));
+
+ stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSArray::storageOffset()), scratchGPR);
+ stubJit.load32(MacroAssembler::Address(scratchGPR, OBJECT_OFFSETOF(ArrayStorage, m_length)), scratchGPR);
+ MacroAssembler::Jump failureCase2 = stubJit.branch32(MacroAssembler::LessThan, scratchGPR, MacroAssembler::TrustedImm32(0));
+
+ stubJit.orPtr(GPRInfo::tagTypeNumberRegister, scratchGPR, resultGPR);
- // FIXME: should support length access for Array & String.
+ MacroAssembler::Jump success, fail;
+
+ if (needToRestoreScratch) {
+ stubJit.pop(scratchGPR);
+
+ success = stubJit.jump();
+
+ // link failure cases here, so we can pop scratchGPR, and then jump back.
+ failureCase1.link(&stubJit);
+ failureCase2.link(&stubJit);
+
+ stubJit.pop(scratchGPR);
+
+ fail = stubJit.jump();
+ } else
+ success = stubJit.jump();
+
+ LinkBuffer patchBuffer(*globalData, &stubJit, codeBlock->executablePool());
+
+ CodeLocationLabel slowCaseBegin = stubInfo.callReturnLocation.labelAtOffset(stubInfo.u.unset.deltaCallToSlowCase);
+
+ patchBuffer.link(success, stubInfo.callReturnLocation.labelAtOffset(stubInfo.u.unset.deltaCallToDone));
+
+ if (needToRestoreScratch)
+ patchBuffer.link(fail, slowCaseBegin);
+ else {
+ // link failure cases directly back to normal path
+ patchBuffer.link(failureCase1, slowCaseBegin);
+ patchBuffer.link(failureCase2, slowCaseBegin);
+ }
+
+ CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
+ stubInfo.stubRoutine = entryLabel;
+
+ CodeLocationLabel hotPathBegin = stubInfo.hotPathBegin;
+ RepatchBuffer repatchBuffer(codeBlock);
+ repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.u.unset.deltaCallToStructCheck), entryLabel);
+ repatchBuffer.relink(stubInfo.callReturnLocation, operationGetById);
+
+ return true;
+ }
+
+ // FIXME: should support length access for String.
// FIXME: Cache property access for immediates.
if (!baseValue.isCell())