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);