Title: [91199] trunk/Source/_javascript_Core
Revision
91199
Author
[email protected]
Date
2011-07-18 11:55:48 -0700 (Mon, 18 Jul 2011)

Log Message

JSC JIT does not inline GC allocation fast paths
https://bugs.webkit.org/show_bug.cgi?id=64582

Patch by Filip Pizlo <[email protected]> on 2011-07-18
Reviewed by Oliver Hunt.

This addresses inlining allocation for the easiest-to-allocate cases:
op_new_object and op_create_this.  Inlining GC allocation fast paths
required three changes.  First, the JSGlobalData now saves the vtable
pointer of JSFinalObject, since that's what op_new_object and
op_create_this allocate.  Second, the Heap exposes a reference to
the appropriate SizeClass, so that the JIT may inline accesses
directly to the SizeClass for JSFinalObject allocations.  And third,
the JIT is extended with code to emit inline fast paths for GC
allocation.  A stub call is emitted in the case where the inline fast
path fails.

* heap/Heap.h:
(JSC::Heap::sizeClassFor):
(JSC::Heap::allocate):
* jit/JIT.cpp:
(JSC::JIT::privateCompileSlowCases):
* jit/JIT.h:
* jit/JITInlineMethods.h:
(JSC::JIT::emitAllocateJSFinalObject):
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_new_object):
(JSC::JIT::emitSlow_op_new_object):
(JSC::JIT::emit_op_create_this):
(JSC::JIT::emitSlow_op_create_this):
* jit/JITOpcodes32_64.cpp:
(JSC::JIT::emit_op_new_object):
(JSC::JIT::emitSlow_op_new_object):
(JSC::JIT::emit_op_create_this):
(JSC::JIT::emitSlow_op_create_this):
* runtime/JSGlobalData.cpp:
(JSC::JSGlobalData::storeVPtrs):
* runtime/JSGlobalData.h:
* runtime/JSObject.h:
(JSC::JSFinalObject::JSFinalObject):
(JSC::JSObject::offsetOfInheritorID):

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (91198 => 91199)


--- trunk/Source/_javascript_Core/ChangeLog	2011-07-18 18:43:08 UTC (rev 91198)
+++ trunk/Source/_javascript_Core/ChangeLog	2011-07-18 18:55:48 UTC (rev 91199)
@@ -1,3 +1,46 @@
+2011-07-18  Filip Pizlo  <[email protected]>
+
+        JSC JIT does not inline GC allocation fast paths
+        https://bugs.webkit.org/show_bug.cgi?id=64582
+
+        Reviewed by Oliver Hunt.
+
+        This addresses inlining allocation for the easiest-to-allocate cases:
+        op_new_object and op_create_this.  Inlining GC allocation fast paths
+        required three changes.  First, the JSGlobalData now saves the vtable
+        pointer of JSFinalObject, since that's what op_new_object and
+        op_create_this allocate.  Second, the Heap exposes a reference to
+        the appropriate SizeClass, so that the JIT may inline accesses
+        directly to the SizeClass for JSFinalObject allocations.  And third,
+        the JIT is extended with code to emit inline fast paths for GC
+        allocation.  A stub call is emitted in the case where the inline fast
+        path fails.
+
+        * heap/Heap.h:
+        (JSC::Heap::sizeClassFor):
+        (JSC::Heap::allocate):
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileSlowCases):
+        * jit/JIT.h:
+        * jit/JITInlineMethods.h:
+        (JSC::JIT::emitAllocateJSFinalObject):
+        * jit/JITOpcodes.cpp:
+        (JSC::JIT::emit_op_new_object):
+        (JSC::JIT::emitSlow_op_new_object):
+        (JSC::JIT::emit_op_create_this):
+        (JSC::JIT::emitSlow_op_create_this):
+        * jit/JITOpcodes32_64.cpp:
+        (JSC::JIT::emit_op_new_object):
+        (JSC::JIT::emitSlow_op_new_object):
+        (JSC::JIT::emit_op_create_this):
+        (JSC::JIT::emitSlow_op_create_this):
+        * runtime/JSGlobalData.cpp:
+        (JSC::JSGlobalData::storeVPtrs):
+        * runtime/JSGlobalData.h:
+        * runtime/JSObject.h:
+        (JSC::JSFinalObject::JSFinalObject):
+        (JSC::JSObject::offsetOfInheritorID):
+
 2011-07-18  Mark Hahnenberg  <[email protected]>
 
         Refactor JSC to replace JSCell::operator new with static create method

Modified: trunk/Source/_javascript_Core/heap/Heap.h (91198 => 91199)


--- trunk/Source/_javascript_Core/heap/Heap.h	2011-07-18 18:43:08 UTC (rev 91198)
+++ trunk/Source/_javascript_Core/heap/Heap.h	2011-07-18 18:55:48 UTC (rev 91199)
@@ -82,6 +82,7 @@
         inline bool isBusy();
 
         void* allocate(size_t);
+        NewSpace::SizeClass& sizeClassFor(size_t);
         void* allocate(NewSpace::SizeClass&);
         void notifyIsSafeToCollect() { m_isSafeToCollect = true; }
         void collectAllGarbage();
@@ -289,6 +290,11 @@
         return forEachBlock(functor);
     }
     
+    inline NewSpace::SizeClass& Heap::sizeClassFor(size_t bytes)
+    {
+        return m_newSpace.sizeClassFor(bytes);
+    }
+    
     inline void* Heap::allocate(NewSpace::SizeClass& sizeClass)
     {
         // This is a light-weight fast path to cover the most common case.
@@ -303,7 +309,7 @@
     inline void* Heap::allocate(size_t bytes)
     {
         ASSERT(isValidAllocation(bytes));
-        NewSpace::SizeClass& sizeClass = m_newSpace.sizeClassFor(bytes);
+        NewSpace::SizeClass& sizeClass = sizeClassFor(bytes);
         return allocate(sizeClass);
     }
 

Modified: trunk/Source/_javascript_Core/jit/JIT.cpp (91198 => 91199)


--- trunk/Source/_javascript_Core/jit/JIT.cpp	2011-07-18 18:43:08 UTC (rev 91198)
+++ trunk/Source/_javascript_Core/jit/JIT.cpp	2011-07-18 18:55:48 UTC (rev 91199)
@@ -401,6 +401,7 @@
         DEFINE_SLOWCASE_OP(op_call_varargs)
         DEFINE_SLOWCASE_OP(op_construct)
         DEFINE_SLOWCASE_OP(op_convert_this)
+        DEFINE_SLOWCASE_OP(op_create_this)
         DEFINE_SLOWCASE_OP(op_div)
         DEFINE_SLOWCASE_OP(op_eq)
         DEFINE_SLOWCASE_OP(op_get_by_id)
@@ -435,6 +436,7 @@
         DEFINE_SLOWCASE_OP(op_negate)
 #endif
         DEFINE_SLOWCASE_OP(op_neq)
+        DEFINE_SLOWCASE_OP(op_new_object)
         DEFINE_SLOWCASE_OP(op_not)
         DEFINE_SLOWCASE_OP(op_nstricteq)
         DEFINE_SLOWCASE_OP(op_post_dec)

Modified: trunk/Source/_javascript_Core/jit/JIT.h (91198 => 91199)


--- trunk/Source/_javascript_Core/jit/JIT.h	2011-07-18 18:43:08 UTC (rev 91198)
+++ trunk/Source/_javascript_Core/jit/JIT.h	2011-07-18 18:55:48 UTC (rev 91199)
@@ -299,6 +299,9 @@
         void testPrototype(JSValue, JumpList& failureCases);
 
         void emitWriteBarrier(RegisterID owner, RegisterID scratch);
+        
+        template<typename T>
+        void emitAllocateJSFinalObject(T structure, RegisterID result, RegisterID scratch);
 
 #if USE(JSVALUE32_64)
         bool getOperandConstantImmediateInt(unsigned op1, unsigned op2, unsigned& op, int32_t& constant);
@@ -853,6 +856,7 @@
         void emitSlow_op_call_varargs(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_construct(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_convert_this(Instruction*, Vector<SlowCaseEntry>::iterator&);
+        void emitSlow_op_create_this(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_div(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_eq(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_get_by_id(Instruction*, Vector<SlowCaseEntry>::iterator&);
@@ -885,6 +889,7 @@
         void emitSlow_op_mul(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_negate(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_neq(Instruction*, Vector<SlowCaseEntry>::iterator&);
+        void emitSlow_op_new_object(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_not(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_nstricteq(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_post_dec(Instruction*, Vector<SlowCaseEntry>::iterator&);

Modified: trunk/Source/_javascript_Core/jit/JITInlineMethods.h (91198 => 91199)


--- trunk/Source/_javascript_Core/jit/JITInlineMethods.h	2011-07-18 18:43:08 UTC (rev 91198)
+++ trunk/Source/_javascript_Core/jit/JITInlineMethods.h	2011-07-18 18:55:48 UTC (rev 91199)
@@ -374,6 +374,31 @@
     return m_codeBlock->isConstantRegisterIndex(src) && getConstantOperand(src).isString() && asString(getConstantOperand(src).asCell())->length() == 1;
 }
 
+template<typename T>
+inline void JIT::emitAllocateJSFinalObject(T structure, RegisterID result, RegisterID scratch)
+{
+    NewSpace::SizeClass* sizeClass = &m_globalData->heap.sizeClassFor(sizeof(JSFinalObject));
+    loadPtr(&sizeClass->firstFreeCell, result);
+    addSlowCase(branchTestPtr(Zero, result));
+    
+    // remove the object from the free list
+    loadPtr(Address(result), scratch);
+    storePtr(scratch, &sizeClass->firstFreeCell);
+    
+    // initialize the object's vtable
+    storePtr(ImmPtr(m_globalData->jsFinalObjectVPtr), Address(result));
+    
+    // initialize the object's structure
+    storePtr(structure, Address(result, JSCell::structureOffset()));
+    
+    // initialize the inheritor ID
+    storePtr(ImmPtr(0), Address(result, JSObject::offsetOfInheritorID()));
+    
+    // initialize the object's property storage pointer
+    addPtr(Imm32(sizeof(JSObject)), result, scratch);
+    storePtr(scratch, Address(result, JSObject::offsetOfPropertyStorage()));
+}
+
 #if USE(JSVALUE32_64)
 
 inline void JIT::emitLoadTag(unsigned index, RegisterID tag)

Modified: trunk/Source/_javascript_Core/jit/JITOpcodes.cpp (91198 => 91199)


--- trunk/Source/_javascript_Core/jit/JITOpcodes.cpp	2011-07-18 18:43:08 UTC (rev 91198)
+++ trunk/Source/_javascript_Core/jit/JITOpcodes.cpp	2011-07-18 18:55:48 UTC (rev 91199)
@@ -29,6 +29,7 @@
 #include "JIT.h"
 
 #include "Arguments.h"
+#include "Heap.h"
 #include "JITInlineMethods.h"
 #include "JITStubCall.h"
 #include "JSArray.h"
@@ -330,6 +331,14 @@
 
 void JIT::emit_op_new_object(Instruction* currentInstruction)
 {
+    emitAllocateJSFinalObject(ImmPtr(m_codeBlock->globalObject()->emptyObjectStructure()), regT0, regT1);
+    
+    emitPutVirtualRegister(currentInstruction[1].u.operand);
+}
+
+void JIT::emitSlow_op_new_object(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+    linkSlowCase(iter);
     JITStubCall(this, cti_op_new_object).call(currentInstruction[1].u.operand);
 }
 
@@ -1156,6 +1165,31 @@
 
 void JIT::emit_op_create_this(Instruction* currentInstruction)
 {
+    emitGetVirtualRegister(currentInstruction[2].u.operand, regT2);
+    emitJumpSlowCaseIfNotJSCell(regT2, currentInstruction[2].u.operand);
+    loadPtr(Address(regT2, JSCell::structureOffset()), regT1);
+    addSlowCase(branch8(NotEqual, Address(regT1, Structure::typeInfoTypeOffset()), TrustedImm32(ObjectType)));
+    
+    // now we know that the prototype is an object, but we don't know if it's got an
+    // inheritor ID
+    
+    loadPtr(Address(regT2, JSObject::offsetOfInheritorID()), regT2);
+    addSlowCase(branchTestPtr(Zero, regT2));
+    
+    // now regT2 contains the inheritorID, which is the structure that the newly
+    // allocated object will have.
+    
+    emitAllocateJSFinalObject(regT2, regT0, regT1);
+    
+    emitPutVirtualRegister(currentInstruction[1].u.operand);
+}
+
+void JIT::emitSlow_op_create_this(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+    linkSlowCaseIfNotJSCell(iter, currentInstruction[2].u.operand); // not a cell
+    linkSlowCase(iter); // not an object
+    linkSlowCase(iter); // doesn't have an inheritor ID
+    linkSlowCase(iter); // allocation failed
     JITStubCall stubCall(this, cti_op_create_this);
     stubCall.addArgument(currentInstruction[2].u.operand, regT1);
     stubCall.call(currentInstruction[1].u.operand);

Modified: trunk/Source/_javascript_Core/jit/JITOpcodes32_64.cpp (91198 => 91199)


--- trunk/Source/_javascript_Core/jit/JITOpcodes32_64.cpp	2011-07-18 18:43:08 UTC (rev 91198)
+++ trunk/Source/_javascript_Core/jit/JITOpcodes32_64.cpp	2011-07-18 18:55:48 UTC (rev 91199)
@@ -485,6 +485,14 @@
 
 void JIT::emit_op_new_object(Instruction* currentInstruction)
 {
+    emitAllocateJSFinalObject(ImmPtr(m_codeBlock->globalObject()->emptyObjectStructure()), regT0, regT1);
+    
+    emitStoreCell(currentInstruction[1].u.operand, regT0);
+}
+
+void JIT::emitSlow_op_new_object(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+    linkSlowCase(iter);
     JITStubCall(this, cti_op_new_object).call(currentInstruction[1].u.operand);
 }
 
@@ -1438,6 +1446,31 @@
 
 void JIT::emit_op_create_this(Instruction* currentInstruction)
 {
+    emitLoad(currentInstruction[2].u.operand, regT1, regT0);
+    emitJumpSlowCaseIfNotJSCell(currentInstruction[2].u.operand, regT1);
+    loadPtr(Address(regT0, JSCell::structureOffset()), regT1);
+    addSlowCase(branch8(NotEqual, Address(regT1, Structure::typeInfoTypeOffset()), TrustedImm32(ObjectType)));
+    
+    // now we know that the prototype is an object, but we don't know if it's got an
+    // inheritor ID
+    
+    loadPtr(Address(regT0, JSObject::offsetOfInheritorID()), regT2);
+    addSlowCase(branchTestPtr(Zero, regT2));
+    
+    // now regT2 contains the inheritorID, which is the structure that the newly
+    // allocated object will have.
+    
+    emitAllocateJSFinalObject(regT2, regT0, regT1);
+
+    emitStoreCell(currentInstruction[1].u.operand, regT0);
+}
+
+void JIT::emitSlow_op_create_this(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+    linkSlowCaseIfNotJSCell(iter, currentInstruction[2].u.operand); // not a cell
+    linkSlowCase(iter); // not an object
+    linkSlowCase(iter); // doesn't have an inheritor ID
+    linkSlowCase(iter); // allocation failed
     unsigned protoRegister = currentInstruction[2].u.operand;
     emitLoad(protoRegister, regT1, regT0);
     JITStubCall stubCall(this, cti_op_create_this);

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalData.cpp (91198 => 91199)


--- trunk/Source/_javascript_Core/runtime/JSGlobalData.cpp	2011-07-18 18:43:08 UTC (rev 91198)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalData.cpp	2011-07-18 18:55:48 UTC (rev 91199)
@@ -113,6 +113,7 @@
 extern JSC_CONST_HASHTABLE HashTable stringTable;
 extern JSC_CONST_HASHTABLE HashTable stringConstructorTable;
 
+void* JSGlobalData::jsFinalObjectVPtr;
 void* JSGlobalData::jsArrayVPtr;
 void* JSGlobalData::jsByteArrayVPtr;
 void* JSGlobalData::jsStringVPtr;
@@ -133,6 +134,11 @@
     // COMPILE_ASSERTS below check that this is true.
     char storage[64];
 
+    COMPILE_ASSERT(sizeof(JSFinalObject) <= sizeof(storage), sizeof_JSFinalObject_must_be_less_than_storage);
+    JSCell* jsFinalObject = new (storage) JSFinalObject(JSFinalObject::VPtrStealingHack);
+    CLOBBER_MEMORY();
+    JSGlobalData::jsFinalObjectVPtr = jsFinalObject->vptr();
+
     COMPILE_ASSERT(sizeof(JSArray) <= sizeof(storage), sizeof_JSArray_must_be_less_than_storage);
     JSCell* jsArray = new (storage) JSArray(JSArray::VPtrStealingHack);
     CLOBBER_MEMORY();

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalData.h (91198 => 91199)


--- trunk/Source/_javascript_Core/runtime/JSGlobalData.h	2011-07-18 18:43:08 UTC (rev 91198)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalData.h	2011-07-18 18:55:48 UTC (rev 91199)
@@ -182,6 +182,7 @@
 #endif
 
         static void storeVPtrs();
+        static JS_EXPORTDATA void* jsFinalObjectVPtr;
         static JS_EXPORTDATA void* jsArrayVPtr;
         static JS_EXPORTDATA void* jsByteArrayVPtr;
         static JS_EXPORTDATA void* jsStringVPtr;

Modified: trunk/Source/_javascript_Core/runtime/JSObject.h (91198 => 91199)


--- trunk/Source/_javascript_Core/runtime/JSObject.h	2011-07-18 18:43:08 UTC (rev 91198)
+++ trunk/Source/_javascript_Core/runtime/JSObject.h	2011-07-18 18:55:48 UTC (rev 91199)
@@ -48,6 +48,7 @@
     
     class HashEntry;
     class InternalFunction;
+    class MarkedBlock;
     class PropertyDescriptor;
     class PropertyNameArray;
     class Structure;
@@ -75,6 +76,7 @@
         friend class BatchedTransitionOptimizer;
         friend class JIT;
         friend class JSCell;
+        friend class MarkedBlock;
         friend void setUpStaticFunctionSlot(ExecState* exec, const HashEntry* entry, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot);
 
     public:
@@ -250,6 +252,7 @@
 
         static size_t offsetOfInlineStorage();
         static size_t offsetOfPropertyStorage();
+        static size_t offsetOfInheritorID();
 
         static JS_EXPORTDATA const ClassInfo s_info;
 
@@ -357,6 +360,11 @@
         friend class JSObject;
 
     public:
+        explicit JSFinalObject(VPtrStealingHackType)
+            : JSObject(VPtrStealingHack, m_inlineStorage)
+        {
+        }
+        
         static JSFinalObject* create(ExecState* exec, Structure* structure)
         {
             return new (allocateCell<JSFinalObject>(*exec->heap())) JSFinalObject(exec->globalData(), structure);
@@ -391,6 +399,11 @@
     return OBJECT_OFFSETOF(JSObject, m_propertyStorage);
 }
 
+inline size_t JSObject::offsetOfInheritorID()
+{
+    return OBJECT_OFFSETOF(JSObject, m_inheritorID);
+}
+
 inline JSObject* constructEmptyObject(ExecState* exec, Structure* structure)
 {
     return JSFinalObject::create(exec, structure);
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to