Property storage should grow in reverse address direction, to support butterflies
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 24 Jul 2012 02:13:19 +0000 (02:13 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 24 Jul 2012 02:13:19 +0000 (02:13 +0000)
https://bugs.webkit.org/show_bug.cgi?id=91788

Reviewed by Geoffrey Garen.

Changes property storage to grow to the left, and changes the property storage pointer to point
one 8-byte word (i.e. JSValue) to the right of the first value in the storage.

Also improved debug support somewhat, by adding a describe() function to the jsc command-line,
and a slow mode of object access in LLInt.

* assembler/ARMv7Assembler.h:
(JSC::ARMv7Assembler::repatchCompact):
* assembler/MacroAssemblerARMv7.h:
(MacroAssemblerARMv7):
(JSC::MacroAssemblerARMv7::isCompactPtrAlignedAddressOffset):
(JSC::MacroAssemblerARMv7::load32WithCompactAddressOffsetPatch):
* assembler/MacroAssemblerX86Common.h:
(JSC::MacroAssemblerX86Common::isCompactPtrAlignedAddressOffset):
(JSC::MacroAssemblerX86Common::repatchCompact):
* assembler/X86Assembler.h:
(JSC::X86Assembler::repatchCompact):
* bytecode/CodeBlock.cpp:
(JSC::dumpStructure):
* bytecode/GetByIdStatus.h:
(JSC::GetByIdStatus::GetByIdStatus):
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGRepatch.cpp:
(JSC::DFG::tryCacheGetByID):
(JSC::DFG::emitPutTransitionStub):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileAllocatePropertyStorage):
(JSC::DFG::SpeculativeJIT::compileReallocatePropertyStorage):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* heap/ConservativeRoots.cpp:
(JSC::ConservativeRoots::genericAddPointer):
* heap/CopiedSpace.h:
(CopiedSpace):
* heap/CopiedSpaceInlineMethods.h:
(JSC::CopiedSpace::pinIfNecessary):
(JSC):
* jit/JITPropertyAccess.cpp:
(JSC::JIT::compileGetDirectOffset):
* jit/JITPropertyAccess32_64.cpp:
(JSC::JIT::compileGetDirectOffset):
* jit/JITStubs.cpp:
(JSC::JITThunks::tryCacheGetByID):
* jsc.cpp:
(GlobalObject::finishCreation):
(functionDescribe):
* llint/LLIntCommon.h:
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/JSObject.cpp:
(JSC::JSObject::visitChildren):
(JSC::JSFinalObject::visitChildren):
(JSC::JSObject::growOutOfLineStorage):
* runtime/JSObject.h:
(JSC::JSObject::getDirectLocation):
(JSC::JSObject::offsetForLocation):
* runtime/JSValue.h:
(JSValue):
* runtime/PropertyOffset.h:
(JSC::offsetInOutOfLineStorage):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@123417 268f45cc-cd09-0410-ab3c-d52691b4dbfc

29 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/assembler/ARMv7Assembler.h
Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h
Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h
Source/JavaScriptCore/assembler/X86Assembler.h
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/GetByIdStatus.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGRepatch.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/heap/ConservativeRoots.cpp
Source/JavaScriptCore/heap/CopiedSpace.h
Source/JavaScriptCore/heap/CopiedSpaceInlineMethods.h
Source/JavaScriptCore/jit/JITPropertyAccess.cpp
Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp
Source/JavaScriptCore/jit/JITStubs.cpp
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/llint/LLIntCommon.h
Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/runtime/JSObject.cpp
Source/JavaScriptCore/runtime/JSObject.h
Source/JavaScriptCore/runtime/JSValue.h
Source/JavaScriptCore/runtime/PropertyOffset.h

index 575ebb1..1ca96a9 100644 (file)
@@ -1,5 +1,80 @@
 2012-07-23  Filip Pizlo  <fpizlo@apple.com>
 
+        Property storage should grow in reverse address direction, to support butterflies
+        https://bugs.webkit.org/show_bug.cgi?id=91788
+
+        Reviewed by Geoffrey Garen.
+
+        Changes property storage to grow to the left, and changes the property storage pointer to point
+        one 8-byte word (i.e. JSValue) to the right of the first value in the storage.
+        
+        Also improved debug support somewhat, by adding a describe() function to the jsc command-line,
+        and a slow mode of object access in LLInt.
+
+        * assembler/ARMv7Assembler.h:
+        (JSC::ARMv7Assembler::repatchCompact):
+        * assembler/MacroAssemblerARMv7.h:
+        (MacroAssemblerARMv7):
+        (JSC::MacroAssemblerARMv7::isCompactPtrAlignedAddressOffset):
+        (JSC::MacroAssemblerARMv7::load32WithCompactAddressOffsetPatch):
+        * assembler/MacroAssemblerX86Common.h:
+        (JSC::MacroAssemblerX86Common::isCompactPtrAlignedAddressOffset):
+        (JSC::MacroAssemblerX86Common::repatchCompact):
+        * assembler/X86Assembler.h:
+        (JSC::X86Assembler::repatchCompact):
+        * bytecode/CodeBlock.cpp:
+        (JSC::dumpStructure):
+        * bytecode/GetByIdStatus.h:
+        (JSC::GetByIdStatus::GetByIdStatus):
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGRepatch.cpp:
+        (JSC::DFG::tryCacheGetByID):
+        (JSC::DFG::emitPutTransitionStub):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileAllocatePropertyStorage):
+        (JSC::DFG::SpeculativeJIT::compileReallocatePropertyStorage):
+        * dfg/DFGSpeculativeJIT.h:
+        (JSC::DFG::SpeculativeJIT::callOperation):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * heap/ConservativeRoots.cpp:
+        (JSC::ConservativeRoots::genericAddPointer):
+        * heap/CopiedSpace.h:
+        (CopiedSpace):
+        * heap/CopiedSpaceInlineMethods.h:
+        (JSC::CopiedSpace::pinIfNecessary):
+        (JSC):
+        * jit/JITPropertyAccess.cpp:
+        (JSC::JIT::compileGetDirectOffset):
+        * jit/JITPropertyAccess32_64.cpp:
+        (JSC::JIT::compileGetDirectOffset):
+        * jit/JITStubs.cpp:
+        (JSC::JITThunks::tryCacheGetByID):
+        * jsc.cpp:
+        (GlobalObject::finishCreation):
+        (functionDescribe):
+        * llint/LLIntCommon.h:
+        * llint/LLIntSlowPaths.cpp:
+        (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+        * runtime/JSObject.cpp:
+        (JSC::JSObject::visitChildren):
+        (JSC::JSFinalObject::visitChildren):
+        (JSC::JSObject::growOutOfLineStorage):
+        * runtime/JSObject.h:
+        (JSC::JSObject::getDirectLocation):
+        (JSC::JSObject::offsetForLocation):
+        * runtime/JSValue.h:
+        (JSValue):
+        * runtime/PropertyOffset.h:
+        (JSC::offsetInOutOfLineStorage):
+
+2012-07-23  Filip Pizlo  <fpizlo@apple.com>
+
         DFG is too aggressive in performing the specific value optimization on loads
         https://bugs.webkit.org/show_bug.cgi?id=92034
 
index 96c4f09..e9b9fcc 100644 (file)
@@ -2095,11 +2095,24 @@ public:
         setInt32(where, value);
     }
     
-    static void repatchCompact(void* where, int32_t value)
+    static void repatchCompact(void* where, int32_t offset)
     {
-        ASSERT(value >= 0);
-        ASSERT(ARMThumbImmediate::makeUInt12(value).isUInt7());
-        setUInt7ForLoad(where, ARMThumbImmediate::makeUInt12(value));
+        ASSERT(offset >= -255 && offset <= 255);
+
+        bool add = true;
+        if (offset < 0) {
+            add = false;
+            offset = -offset;
+        }
+        
+        offset |= (add << 9);
+        offset |= (1 << 10);
+        offset |= (1 << 11);
+
+        uint16_t* location = reinterpret_cast<uint16_t*>(where);
+        location[1] &= ~((1 << 12) - 1);
+        location[1] |= offset;
+        cacheFlush(location, sizeof(uint16_t) * 2);
     }
 
     static void repatchPointer(void* where, void* value)
index cf6f02c..183e8f9 100644 (file)
@@ -53,10 +53,12 @@ public:
     typedef ARMv7Assembler::LinkRecord LinkRecord;
     typedef ARMv7Assembler::JumpType JumpType;
     typedef ARMv7Assembler::JumpLinkType JumpLinkType;
-    // Magic number is the biggest useful offset we can get on ARMv7 with
-    // a LDR_imm_T2 encoding
-    static const int MaximumCompactPtrAlignedAddressOffset = 124;
-    
+
+    static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value)
+    {
+        return value >= -255 && value <= 255;
+    }
+
     Vector<LinkRecord>& jumpsToLink() { return m_assembler.jumpsToLink(); }
     void* unlinkedCode() { return m_assembler.unlinkedCode(); }
     bool canCompact(JumpType jumpType) { return m_assembler.canCompact(jumpType); }
@@ -642,26 +644,14 @@ public:
         return label;
     }
     
-    // FIXME: we should be able to plant a compact load relative to/from any base/dest register.
     DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest)
     {
         RegisterID base = address.base;
         
-        if (base >= ARMRegisters::r8) {
-            move(base, addressTempRegister);
-            base = addressTempRegister;
-        }
-
         DataLabelCompact label(this);
-        ASSERT(address.offset >= 0);
-        ASSERT(address.offset <= MaximumCompactPtrAlignedAddressOffset);
-        ASSERT(ARMThumbImmediate::makeUInt12(address.offset).isUInt7());
+        ASSERT(isCompactPtrAlignedAddressOffset(address.offset));
 
-        if (dest >= ARMRegisters::r8) {
-            m_assembler.ldrCompact(addressTempRegister, base, ARMThumbImmediate::makeUInt12(address.offset));
-            move(addressTempRegister, dest);
-        } else
-            m_assembler.ldrCompact(dest, base, ARMThumbImmediate::makeUInt12(address.offset));
+        m_assembler.ldr(dest, base, address.offset, true, false);
         return label;
     }
 
index 432489d..115b337 100644 (file)
@@ -47,7 +47,10 @@ public:
     typedef X86Assembler::FPRegisterID FPRegisterID;
     typedef X86Assembler::XMMRegisterID XMMRegisterID;
     
-    static const int MaximumCompactPtrAlignedAddressOffset = 127;
+    static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value)
+    {
+        return value >= -128 && value <= 127;
+    }
 
     enum RelationalCondition {
         Equal = X86Assembler::ConditionE,
@@ -494,8 +497,7 @@ public:
     
     static void repatchCompact(CodeLocationDataLabelCompact dataLabelCompact, int32_t value)
     {
-        ASSERT(value >= 0);
-        ASSERT(value < MaximumCompactPtrAlignedAddressOffset);
+        ASSERT(isCompactPtrAlignedAddressOffset(value));
         AssemblerType_T::repatchCompact(dataLabelCompact.dataLocation(), value);
     }
     
index fc1c272..adaee4b 100644 (file)
@@ -1829,7 +1829,7 @@ public:
     
     static void repatchCompact(void* where, int32_t value)
     {
-        ASSERT(value >= 0);
+        ASSERT(value >= std::numeric_limits<int8_t>::min());
         ASSERT(value <= std::numeric_limits<int8_t>::max());
         setInt8(where, value);
     }
index 363efa2..5374a53 100644 (file)
@@ -255,9 +255,9 @@ static void dumpStructure(const char* name, ExecState* exec, Structure* structur
     
     dataLog("%s = %p", name, structure);
     
-    size_t offset = structure->get(exec->globalData(), ident);
-    if (offset != notFound)
-        dataLog(" (offset = %lu)", static_cast<unsigned long>(offset));
+    PropertyOffset offset = structure->get(exec->globalData(), ident);
+    if (offset != invalidOffset)
+        dataLog(" (offset = %d)", offset);
 }
 #endif
 
index 297ec33..f38a19e 100644 (file)
@@ -53,7 +53,7 @@ public:
     
     GetByIdStatus(
         State state, bool wasSeenInJIT, const StructureSet& structureSet = StructureSet(),
-        size_t offset = invalidOffset, JSValue specificValue = JSValue(), Vector<Structure*> chain = Vector<Structure*>())
+        PropertyOffset offset = invalidOffset, JSValue specificValue = JSValue(), Vector<Structure*> chain = Vector<Structure*>())
         : m_state(state)
         , m_structureSet(structureSet)
         , m_chain(chain)
@@ -61,7 +61,7 @@ public:
         , m_offset(offset)
         , m_wasSeenInJIT(wasSeenInJIT)
     {
-        ASSERT((state == Simple) == (offset != notFound));
+        ASSERT((state == Simple) == (offset != invalidOffset));
     }
     
     static GetByIdStatus computeFor(CodeBlock*, unsigned bytecodeIndex, Identifier&);
index 9050d59..94479d6 100644 (file)
@@ -1248,7 +1248,7 @@ char* DFG_OPERATION operationAllocatePropertyStorageWithInitialCapacity(ExecStat
     void* result;
     if (!globalData.heap.tryAllocateStorage(initialOutOfLineCapacity * sizeof(JSValue), &result))
         CRASH();
-    return reinterpret_cast<char*>(result);
+    return reinterpret_cast<char*>(reinterpret_cast<JSValue*>(result) + initialOutOfLineCapacity + 1);
 }
 
 char* DFG_OPERATION operationAllocatePropertyStorage(ExecState* exec, size_t newSize)
@@ -1257,7 +1257,7 @@ char* DFG_OPERATION operationAllocatePropertyStorage(ExecState* exec, size_t new
     void* result;
     if (!globalData.heap.tryAllocateStorage(newSize, &result))
         CRASH();
-    return reinterpret_cast<char*>(result);
+    return reinterpret_cast<char*>(reinterpret_cast<JSValue*>(result) + 1) + newSize;
 }
 
 double DFG_OPERATION operationFModOnInts(int32_t a, int32_t b)
index d0fbf7e..0ff7212 100644 (file)
@@ -106,6 +106,7 @@ typedef void DFG_OPERATION (*V_DFGOperation_EPZJ)(ExecState*, void*, int32_t, En
 typedef void DFG_OPERATION (*V_DFGOperation_W)(WatchpointSet*);
 typedef char* DFG_OPERATION (*P_DFGOperation_E)(ExecState*);
 typedef char* DFG_OPERATION (*P_DFGOperation_ES)(ExecState*, size_t);
+typedef char* DFG_OPERATION (*P_DFGOperation_EPS)(ExecState*, void*, size_t);
 
 // These routines are provide callbacks out to C++ implementations of operations too complex to JIT.
 JSCell* DFG_OPERATION operationNewObject(ExecState*) WTF_INTERNAL;
index 19e064f..ee0c590 100644 (file)
@@ -301,7 +301,8 @@ static bool tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier
 
     // Optimize self access.
     if (slot.slotBase() == baseValue) {
-        if ((slot.cachedPropertyType() != PropertySlot::Value) || ((slot.cachedOffset() * sizeof(JSValue)) > (unsigned)MacroAssembler::MaximumCompactPtrAlignedAddressOffset)) {
+        if ((slot.cachedPropertyType() != PropertySlot::Value)
+            || !MacroAssembler::isCompactPtrAlignedAddressOffset(offsetRelativeToPatchedStorage(slot.cachedOffset()))) {
             dfgRepatchCall(codeBlock, stubInfo.callReturnLocation, operationGetByIdBuildList);
             return true;
         }
@@ -824,7 +825,7 @@ static void emitPutTransitionStub(
             stubJit.storePtr(scratchGPR1, &copiedAllocator->m_currentRemaining);
             stubJit.negPtr(scratchGPR1);
             stubJit.addPtr(MacroAssembler::AbsoluteAddress(&copiedAllocator->m_currentPayloadEnd), scratchGPR1);
-            stubJit.subPtr(MacroAssembler::TrustedImm32(newSize), scratchGPR1);
+            stubJit.addPtr(MacroAssembler::TrustedImm32(sizeof(JSValue)), scratchGPR1);
         } else {
             size_t oldSize = oldStructure->outOfLineCapacity() * sizeof(JSValue);
             ASSERT(newSize > oldSize);
@@ -835,11 +836,11 @@ static void emitPutTransitionStub(
             stubJit.storePtr(scratchGPR1, &copiedAllocator->m_currentRemaining);
             stubJit.negPtr(scratchGPR1);
             stubJit.addPtr(MacroAssembler::AbsoluteAddress(&copiedAllocator->m_currentPayloadEnd), scratchGPR1);
-            stubJit.subPtr(MacroAssembler::TrustedImm32(newSize), scratchGPR1);
+            stubJit.addPtr(MacroAssembler::TrustedImm32(sizeof(JSValue)), scratchGPR1);
             // We have scratchGPR1 = new storage, scratchGPR3 = old storage, scratchGPR2 = available
-            for (size_t offset = 0; offset < oldSize; offset += sizeof(void*)) {
-                stubJit.loadPtr(MacroAssembler::Address(scratchGPR3, offset), scratchGPR2);
-                stubJit.storePtr(scratchGPR2, MacroAssembler::Address(scratchGPR1, offset));
+            for (ptrdiff_t offset = 0; offset < static_cast<ptrdiff_t>(oldSize); offset += sizeof(void*)) {
+                stubJit.loadPtr(MacroAssembler::Address(scratchGPR3, -(offset + sizeof(JSValue) * 2)), scratchGPR2);
+                stubJit.storePtr(scratchGPR2, MacroAssembler::Address(scratchGPR1, -(offset + sizeof(JSValue) * 2)));
             }
         }
         
index 47b8f7f..875ac39 100644 (file)
@@ -3157,7 +3157,7 @@ void SpeculativeJIT::compileAllocatePropertyStorage(Node& node)
     m_jit.storePtr(scratchGPR, &copiedAllocator->m_currentRemaining);
     m_jit.negPtr(scratchGPR);
     m_jit.addPtr(JITCompiler::AbsoluteAddress(&copiedAllocator->m_currentPayloadEnd), scratchGPR);
-    m_jit.subPtr(JITCompiler::TrustedImm32(newSize), scratchGPR);
+    m_jit.addPtr(JITCompiler::TrustedImm32(sizeof(JSValue)), scratchGPR);
         
     addSlowPathGenerator(
         slowPathCall(slowPath, this, operationAllocatePropertyStorageWithInitialCapacity, scratchGPR));
@@ -3191,14 +3191,14 @@ void SpeculativeJIT::compileReallocatePropertyStorage(Node& node)
     m_jit.storePtr(scratchGPR2, &copiedAllocator->m_currentRemaining);
     m_jit.negPtr(scratchGPR2);
     m_jit.addPtr(JITCompiler::AbsoluteAddress(&copiedAllocator->m_currentPayloadEnd), scratchGPR2);
-    m_jit.subPtr(JITCompiler::TrustedImm32(newSize), scratchGPR2);
+    m_jit.addPtr(JITCompiler::TrustedImm32(sizeof(JSValue)), scratchGPR2);
         
     addSlowPathGenerator(
         slowPathCall(slowPath, this, operationAllocatePropertyStorage, scratchGPR2, newSize));
     // We have scratchGPR2 = new storage, scratchGPR1 = scratch
-    for (size_t offset = 0; offset < oldSize; offset += sizeof(void*)) {
-        m_jit.loadPtr(JITCompiler::Address(oldStorageGPR, offset), scratchGPR1);
-        m_jit.storePtr(scratchGPR1, JITCompiler::Address(scratchGPR2, offset));
+    for (ptrdiff_t offset = 0; offset < static_cast<ptrdiff_t>(oldSize); offset += sizeof(void*)) {
+        m_jit.loadPtr(JITCompiler::Address(oldStorageGPR, -(offset + sizeof(JSValue) * 2)), scratchGPR1);
+        m_jit.storePtr(scratchGPR1, JITCompiler::Address(scratchGPR2, -(offset + sizeof(JSValue) * 2)));
     }
     m_jit.storePtr(scratchGPR2, JITCompiler::Address(baseGPR, JSObject::offsetOfOutOfLineStorage()));
     
index cb7d891..0e412a1 100644 (file)
@@ -1161,6 +1161,11 @@ public:
         m_jit.setupArgumentsWithExecState(TrustedImmPtr(size));
         return appendCallWithExceptionCheckSetResult(operation, result);
     }
+    JITCompiler::Call callOperation(P_DFGOperation_EPS operation, GPRReg result, GPRReg old, size_t size)
+    {
+        m_jit.setupArgumentsWithExecState(old, TrustedImmPtr(size));
+        return appendCallWithExceptionCheckSetResult(operation, result);
+    }
     JITCompiler::Call callOperation(J_DFGOperation_E operation, GPRReg result)
     {
         m_jit.setupArgumentsExecState();
index c63ba8c..6209da4 100644 (file)
@@ -3913,8 +3913,10 @@ void SpeculativeJIT::compile(Node& node)
         m_jit.breakpoint();
         isOutOfLine.link(&m_jit);
 #endif
-        m_jit.load32(JITCompiler::BaseIndex(resultPayloadGPR, resolveInfoGPR, JITCompiler::TimesEight, OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag) - inlineStorageCapacity * static_cast<ptrdiff_t>(sizeof(JSValue))), resultTagGPR);
-        m_jit.load32(JITCompiler::BaseIndex(resultPayloadGPR, resolveInfoGPR, JITCompiler::TimesEight, OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload) - inlineStorageCapacity * static_cast<ptrdiff_t>(sizeof(JSValue))), resultPayloadGPR);
+        m_jit.neg32(resolveInfoGPR);
+        m_jit.signExtend32ToPtr(resolveInfoGPR, resolveInfoGPR);
+        m_jit.load32(JITCompiler::BaseIndex(resultPayloadGPR, resolveInfoGPR, JITCompiler::TimesEight, OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag) + (inlineStorageCapacity - 2) * static_cast<ptrdiff_t>(sizeof(JSValue))), resultTagGPR);
+        m_jit.load32(JITCompiler::BaseIndex(resultPayloadGPR, resolveInfoGPR, JITCompiler::TimesEight, OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload) + (inlineStorageCapacity - 2) * static_cast<ptrdiff_t>(sizeof(JSValue))), resultPayloadGPR);
 
         addSlowPathGenerator(
             slowPathCall(
index 6dbb8a4..39f4a07 100644 (file)
@@ -3902,8 +3902,10 @@ void SpeculativeJIT::compile(Node& node)
         m_jit.breakpoint();
         isOutOfLine.link(&m_jit);
 #endif
+        m_jit.neg32(resolveInfoGPR);
+        m_jit.signExtend32ToPtr(resolveInfoGPR, resolveInfoGPR);
         m_jit.loadPtr(JITCompiler::Address(globalObjectGPR, JSObject::offsetOfOutOfLineStorage()), resultGPR);
-        m_jit.loadPtr(JITCompiler::BaseIndex(resultGPR, resolveInfoGPR, JITCompiler::ScalePtr, -inlineStorageCapacity * static_cast<ptrdiff_t>(sizeof(JSValue))), resultGPR);
+        m_jit.loadPtr(JITCompiler::BaseIndex(resultGPR, resolveInfoGPR, JITCompiler::ScalePtr, (inlineStorageCapacity - 2) * static_cast<ptrdiff_t>(sizeof(JSValue))), resultGPR);
         
         addSlowPathGenerator(
             slowPathCall(
index 6b9cbef..7fe22df 100644 (file)
@@ -66,10 +66,8 @@ template<typename MarkHook>
 inline void ConservativeRoots::genericAddPointer(void* p, TinyBloomFilter filter, MarkHook& markHook)
 {
     markHook.mark(p);
-    
-    CopiedBlock* block;
-    if (m_copiedSpace->contains(p, block))
-        m_copiedSpace->pin(block);
+
+    m_copiedSpace->pinIfNecessary(p);
     
     MarkedBlock* candidate = MarkedBlock::blockFor(p);
     if (filter.ruleOut(reinterpret_cast<Bits>(candidate))) {
index de682a4..be3a331 100644 (file)
@@ -68,6 +68,8 @@ public:
 
     bool contains(CopiedBlock*);
     bool contains(void*, CopiedBlock*&);
+    
+    void pinIfNecessary(void* pointer);
 
     size_t size();
     size_t capacity();
index f702e1d..e2af41a 100644 (file)
@@ -57,6 +57,42 @@ inline void CopiedSpace::pin(CopiedBlock* block)
     block->m_isPinned = true;
 }
 
+inline void CopiedSpace::pinIfNecessary(void* opaquePointer)
+{
+    // Pointers into the copied space come in the following varieties:
+    // 1)  Pointers to the start of a span of memory. This is the most
+    //     natural though not necessarily the most common.
+    // 2)  Pointers to one value-sized (8 byte) word past the end of
+    //     a span of memory. This currently occurs with semi-butterflies
+    //     and should be fixed soon, once the other half of the
+    //     butterfly lands.
+    // 3)  Pointers to the innards arising from loop induction variable
+    //     optimizations (either manual ones or automatic, by the
+    //     compiler).
+    // 4)  Pointers to the end of a span of memory in arising from
+    //     induction variable optimizations combined with the
+    //     GC-to-compiler contract laid out in the C spec: a pointer to
+    //     the end of a span of memory must be considered to be a
+    //     pointer to that memory.
+    
+    EncodedJSValue* pointer = reinterpret_cast<EncodedJSValue*>(opaquePointer);
+    CopiedBlock* block;
+
+    // Handle (1) and (3).
+    if (contains(pointer, block))
+        pin(block);
+    
+    // Handle (4). We don't have to explicitly check and pin the block under this
+    // pointer because it cannot possibly point to something that cases (1) and
+    // (3) above or case (2) below wouldn't already catch.
+    pointer--;
+    
+    // Handle (2)
+    pointer--;
+    if (contains(pointer, block))
+        pin(block);
+}
+
 inline void CopiedSpace::startedCopying()
 {
     DoublyLinkedList<HeapBlock>* temp = m_fromSpace;
index 466cff7..86078fb 100644 (file)
@@ -159,9 +159,10 @@ void JIT::compileGetDirectOffset(RegisterID base, RegisterID result, RegisterID
     if (finalObjectMode == MayBeFinal) {
         Jump isInline = branch32(LessThan, offset, TrustedImm32(inlineStorageCapacity));
         loadPtr(Address(base, JSObject::offsetOfOutOfLineStorage()), scratch);
+        neg32(offset);
         Jump done = jump();
         isInline.link(this);
-        addPtr(TrustedImm32(JSObject::offsetOfInlineStorage() + inlineStorageCapacity * sizeof(EncodedJSValue)), base, scratch);
+        addPtr(TrustedImm32(JSObject::offsetOfInlineStorage() - (inlineStorageCapacity - 2) * sizeof(EncodedJSValue)), base, scratch);
         done.link(this);
     } else {
 #if !ASSERT_DISABLED
@@ -170,8 +171,10 @@ void JIT::compileGetDirectOffset(RegisterID base, RegisterID result, RegisterID
         isOutOfLine.link(this);
 #endif
         loadPtr(Address(base, JSObject::offsetOfOutOfLineStorage()), scratch);
+        neg32(offset);
     }
-    loadPtr(BaseIndex(scratch, offset, ScalePtr, -inlineStorageCapacity * static_cast<ptrdiff_t>(sizeof(JSValue))), result);
+    signExtend32ToPtr(offset, offset);
+    loadPtr(BaseIndex(scratch, offset, ScalePtr, (inlineStorageCapacity - 2) * static_cast<ptrdiff_t>(sizeof(JSValue))), result);
 }
 
 void JIT::emit_op_get_by_pname(Instruction* currentInstruction)
index 84996d9..365ecd2 100644 (file)
@@ -1018,9 +1018,10 @@ void JIT::compileGetDirectOffset(RegisterID base, RegisterID resultTag, Register
     if (finalObjectMode == MayBeFinal) {
         Jump isInline = branch32(LessThan, offset, TrustedImm32(inlineStorageCapacity));
         loadPtr(Address(base, JSObject::offsetOfOutOfLineStorage()), base);
+        neg32(offset);
         Jump done = jump();
         isInline.link(this);
-        addPtr(TrustedImmPtr(JSObject::offsetOfInlineStorage() + inlineStorageCapacity * sizeof(EncodedJSValue)), base);
+        addPtr(TrustedImmPtr(JSObject::offsetOfInlineStorage() - (inlineStorageCapacity - 2) * sizeof(EncodedJSValue)), base);
         done.link(this);
     } else {
 #if !ASSERT_DISABLED
@@ -1029,9 +1030,10 @@ void JIT::compileGetDirectOffset(RegisterID base, RegisterID resultTag, Register
         isOutOfLine.link(this);
 #endif
         loadPtr(Address(base, JSObject::offsetOfOutOfLineStorage()), base);
+        neg32(offset);
     }
-    load32(BaseIndex(base, offset, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload) - inlineStorageCapacity * sizeof(EncodedJSValue)), resultPayload);
-    load32(BaseIndex(base, offset, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag) - inlineStorageCapacity * sizeof(EncodedJSValue)), resultTag);
+    load32(BaseIndex(base, offset, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload) + (inlineStorageCapacity - 2) * sizeof(EncodedJSValue)), resultPayload);
+    load32(BaseIndex(base, offset, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag) + (inlineStorageCapacity - 2) * sizeof(EncodedJSValue)), resultTag);
 }
 
 void JIT::emit_op_get_by_pname(Instruction* currentInstruction)
index bfc4709..0f03a0a 100644 (file)
@@ -932,7 +932,8 @@ NEVER_INLINE void JITThunks::tryCacheGetByID(CallFrame* callFrame, CodeBlock* co
     if (slot.slotBase() == baseValue) {
         // set this up, so derefStructures can do it's job.
         stubInfo->initGetByIdSelf(callFrame->globalData(), codeBlock->ownerExecutable(), structure);
-        if ((slot.cachedPropertyType() != PropertySlot::Value) || ((slot.cachedOffset() * sizeof(JSValue)) > (unsigned)MacroAssembler::MaximumCompactPtrAlignedAddressOffset))
+        if ((slot.cachedPropertyType() != PropertySlot::Value)
+            || !MacroAssembler::isCompactPtrAlignedAddressOffset(offsetRelativeToPatchedStorage(slot.cachedOffset())))
             ctiPatchCallByReturnAddress(codeBlock, returnAddress, FunctionPtr(cti_op_get_by_id_self_fail));
         else
             JIT::patchGetByIdSelf(codeBlock, stubInfo, structure, slot.cachedOffset(), returnAddress);
index 00ad5d3..17ab515 100644 (file)
@@ -84,6 +84,7 @@ static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>&
 
 static EncodedJSValue JSC_HOST_CALL functionPrint(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionDebug(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionGC(ExecState*);
 #ifndef NDEBUG
@@ -191,6 +192,7 @@ protected:
         Base::finishCreation(globalData);
         
         addFunction(globalData, "debug", functionDebug, 1);
+        addFunction(globalData, "describe", functionDescribe, 1);
         addFunction(globalData, "print", functionPrint, 1);
         addFunction(globalData, "quit", functionQuit, 0);
         addFunction(globalData, "gc", functionGC, 0);
@@ -298,6 +300,12 @@ EncodedJSValue JSC_HOST_CALL functionDebug(ExecState* exec)
     return JSValue::encode(jsUndefined());
 }
 
+EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState* exec)
+{
+    fprintf(stderr, "--> %s\n", exec->argument(0).description());
+    return JSValue::encode(jsUndefined());
+}
+
 EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState* exec)
 {
     String trace = "--> Stack trace:\n";
index 6b908ea..1797ff0 100644 (file)
@@ -36,6 +36,9 @@
 // how the GC allocates.
 #define LLINT_ALWAYS_ALLOCATE_SLOW 0
 
+// Disable inline caching of get_by_id and put_by_id.
+#define LLINT_ALWAYS_ACCESS_SLOW 0
+
 // Enable OSR into the JIT. Disabling this while the LLInt is enabled effectively
 // turns off all JIT'ing, since in LLInt's parlance, OSR subsumes any form of JIT
 // invocation.
index fbc0146..d2d743e 100644 (file)
@@ -883,7 +883,8 @@ LLINT_SLOW_PATH_DECL(slow_path_get_by_id)
     LLINT_CHECK_EXCEPTION();
     LLINT_OP(1) = result;
 
-    if (baseValue.isCell()
+    if (!LLINT_ALWAYS_ACCESS_SLOW
+        && baseValue.isCell()
         && slot.isCacheable()
         && slot.slotBase() == baseValue
         && slot.cachedPropertyType() == PropertySlot::Value) {
@@ -935,7 +936,8 @@ LLINT_SLOW_PATH_DECL(slow_path_put_by_id)
         baseValue.put(exec, ident, LLINT_OP_C(3).jsValue(), slot);
     LLINT_CHECK_EXCEPTION();
     
-    if (baseValue.isCell()
+    if (!LLINT_ALWAYS_ACCESS_SLOW
+        && baseValue.isCell()
         && slot.isCacheable()) {
         
         JSCell* baseCell = baseValue.asCell();
index 513b742..b8115dd 100644 (file)
@@ -921,20 +921,22 @@ _llint_op_is_string:
 
 macro loadPropertyAtVariableOffsetKnownNotFinal(propertyOffset, objectAndStorage, tag, payload)
     assert(macro (ok) bigteq propertyOffset, InlineStorageCapacity, ok end)
+    negi propertyOffset
     loadp JSObject::m_outOfLineStorage[objectAndStorage], objectAndStorage
-    loadi TagOffset - 8 * InlineStorageCapacity[objectAndStorage, propertyOffset, 8], tag
-    loadi PayloadOffset - 8 * InlineStorageCapacity[objectAndStorage, propertyOffset, 8], payload
+    loadi TagOffset + (InlineStorageCapacity - 2) * 8[objectAndStorage, propertyOffset, 8], tag
+    loadi PayloadOffset + (InlineStorageCapacity - 2) * 8[objectAndStorage, propertyOffset, 8], payload
 end
 
 macro loadPropertyAtVariableOffset(propertyOffset, objectAndStorage, tag, payload)
     bilt propertyOffset, InlineStorageCapacity, .isInline
     loadp JSObject::m_outOfLineStorage[objectAndStorage], objectAndStorage
+    negi propertyOffset
     jmp .ready
 .isInline:
-    addp JSFinalObject::m_inlineStorage + InlineStorageCapacity * 8, objectAndStorage
+    addp JSFinalObject::m_inlineStorage - (InlineStorageCapacity - 2) * 8, objectAndStorage
 .ready:
-    loadi TagOffset - 8 * InlineStorageCapacity[objectAndStorage, propertyOffset, 8], tag
-    loadi PayloadOffset - 8 * InlineStorageCapacity[objectAndStorage, propertyOffset, 8], payload
+    loadi TagOffset + (InlineStorageCapacity - 2) * 8[objectAndStorage, propertyOffset, 8], tag
+    loadi PayloadOffset + (InlineStorageCapacity - 2) * 8[objectAndStorage, propertyOffset, 8], payload
 end
 
 macro resolveGlobal(size, slow)
index b976421..6e752a6 100644 (file)
@@ -778,20 +778,23 @@ _llint_op_is_string:
     dispatch(3)
 
 
-macro loadPropertyAtVariableOffsetKnownNotFinal(propertyOffset, objectAndStorage, value)
-    assert(macro (ok) bigteq propertyOffset, InlineStorageCapacity, ok end)
+macro loadPropertyAtVariableOffsetKnownNotFinal(propertyOffsetAsPointer, objectAndStorage, value)
+    assert(macro (ok) bigteq propertyOffsetAsPointer, InlineStorageCapacity, ok end)
+    negp propertyOffsetAsPointer
     loadp JSObject::m_outOfLineStorage[objectAndStorage], objectAndStorage
-    loadp -8 * InlineStorageCapacity[objectAndStorage, propertyOffset, 8], value
+    loadp (InlineStorageCapacity - 2) * 8[objectAndStorage, propertyOffsetAsPointer, 8], value
 end
 
-macro loadPropertyAtVariableOffset(propertyOffset, objectAndStorage, value)
-    bilt propertyOffset, InlineStorageCapacity, .isInline
+macro loadPropertyAtVariableOffset(propertyOffsetAsInt, objectAndStorage, value)
+    bilt propertyOffsetAsInt, InlineStorageCapacity, .isInline
     loadp JSObject::m_outOfLineStorage[objectAndStorage], objectAndStorage
+    negi propertyOffsetAsInt
+    sxi2p propertyOffsetAsInt, propertyOffsetAsInt
     jmp .ready
 .isInline:
-    addp JSFinalObject::m_inlineStorage + InlineStorageCapacity * 8, objectAndStorage
+    addp JSFinalObject::m_inlineStorage - (InlineStorageCapacity - 2) * 8, objectAndStorage
 .ready:
-    loadp -8 * InlineStorageCapacity[objectAndStorage, propertyOffset, 8], value
+    loadp (InlineStorageCapacity - 2) * 8[objectAndStorage, propertyOffsetAsInt, 8], value
 end
 
 macro resolveGlobal(size, slow)
index 587929f..a84597f 100644 (file)
@@ -99,10 +99,11 @@ void JSObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
     PropertyStorage storage = thisObject->outOfLineStorage();
     if (storage) {
         size_t storageSize = thisObject->structure()->outOfLineSizeForKnownNonFinalObject();
+        size_t capacity = thisObject->structure()->outOfLineCapacity();
         // We have this extra temp here to slake GCC's thirst for the blood of those who dereference type-punned pointers.
-        void* temp = storage;
-        visitor.copyAndAppend(&temp, thisObject->structure()->outOfLineCapacity() * sizeof(WriteBarrierBase<Unknown>), storage->slot(), storageSize);
-        storage = static_cast<PropertyStorage>(temp);
+        void* temp = storage - capacity - 1;
+        visitor.copyAndAppend(&temp, capacity * sizeof(WriteBarrierBase<Unknown>), (storage - storageSize - 1)->slot(), storageSize);
+        storage = static_cast<PropertyStorage>(temp) + capacity + 1;
         thisObject->m_outOfLineStorage.set(storage, StorageBarrier::Unchecked);
     }
 
@@ -128,10 +129,11 @@ void JSFinalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
     PropertyStorage storage = thisObject->outOfLineStorage();
     if (storage) {
         size_t storageSize = thisObject->structure()->outOfLineSizeForKnownFinalObject();
+        size_t capacity = thisObject->structure()->outOfLineCapacity();
         // We have this extra temp here to slake GCC's thirst for the blood of those who dereference type-punned pointers.
-        void* temp = storage;
-        visitor.copyAndAppend(&temp, thisObject->structure()->outOfLineCapacity() * sizeof(WriteBarrierBase<Unknown>), storage->slot(), storageSize);
-        storage = static_cast<PropertyStorage>(temp);
+        void* temp = storage - capacity - 1;
+        visitor.copyAndAppend(&temp, thisObject->structure()->outOfLineCapacity() * sizeof(WriteBarrierBase<Unknown>), (storage - storageSize - 1)->slot(), storageSize);
+        storage = static_cast<PropertyStorage>(temp) + capacity + 1;
         thisObject->m_outOfLineStorage.set(storage, StorageBarrier::Unchecked);
     }
 
@@ -595,7 +597,7 @@ PropertyStorage JSObject::growOutOfLineStorage(JSGlobalData& globalData, size_t
 
     // It's important that this function not rely on structure(), since
     // we might be in the middle of a transition.
-
+    
     PropertyStorage oldPropertyStorage = m_outOfLineStorage.get();
     PropertyStorage newPropertyStorage = 0;
 
@@ -603,9 +605,9 @@ PropertyStorage JSObject::growOutOfLineStorage(JSGlobalData& globalData, size_t
     void* temp = newPropertyStorage;
     if (!globalData.heap.tryAllocateStorage(sizeof(WriteBarrierBase<Unknown>) * newSize, &temp))
         CRASH();
-    newPropertyStorage = static_cast<PropertyStorage>(temp);
+    newPropertyStorage = static_cast<PropertyStorage>(temp) + newSize + 1;
     
-    memcpy(newPropertyStorage, oldPropertyStorage, sizeof(WriteBarrierBase<Unknown>) * oldSize);
+    memcpy(newPropertyStorage - oldSize - 1, oldPropertyStorage - oldSize - 1, sizeof(WriteBarrierBase<Unknown>) * oldSize);
 
     ASSERT(newPropertyStorage);
     return newPropertyStorage;
index 9972d60..cc43440 100644 (file)
@@ -173,14 +173,14 @@ namespace JSC {
         {
             PropertyOffset offset = structure()->get(globalData, propertyName);
             checkOffset(offset, structure()->typeInfo().type());
-            return offset != invalidOffset ? locationForOffset(offset) : 0;
+            return isValidOffset(offset) ? locationForOffset(offset) : 0;
         }
 
         WriteBarrierBase<Unknown>* getDirectLocation(JSGlobalData& globalData, PropertyName propertyName, unsigned& attributes)
         {
             JSCell* specificFunction;
             PropertyOffset offset = structure()->get(globalData, propertyName, attributes, specificFunction);
-            return offset != invalidOffset ? locationForOffset(offset) : 0;
+            return isValidOffset(offset) ? locationForOffset(offset) : 0;
         }
 
         bool hasInlineStorage() const { return structure()->hasInlineStorage(); }
@@ -227,7 +227,7 @@ namespace JSC {
             if (offsetInInlineStorage < static_cast<size_t>(inlineStorageCapacity))
                 result = offsetInInlineStorage;
             else
-                result = location - outOfLineStorage() + firstOutOfLineOffset;
+                result = outOfLineStorage() - location + (inlineStorageCapacity - 2);
             validateOffset(result, structure()->typeInfo().type());
             return result;
         }
index 6f98bd6..19a8c47 100644 (file)
@@ -254,7 +254,7 @@ namespace JSC {
         
         JSValue structureOrUndefined() const;
 
-        char* description() const;
+        JS_EXPORT_PRIVATE char* description() const;
 
         JS_EXPORT_PRIVATE JSObject* synthesizePrototype(ExecState*) const;
 
index c0d1316..3883d91 100644 (file)
@@ -118,7 +118,7 @@ inline size_t offsetInOutOfLineStorage(PropertyOffset offset)
 {
     validateOffset(offset);
     ASSERT(isOutOfLineOffset(offset));
-    return offset - firstOutOfLineOffset;
+    return -static_cast<ptrdiff_t>(offset - firstOutOfLineOffset) - 2;
 }
 
 inline size_t offsetInRespectiveStorage(PropertyOffset offset)