Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (108290 => 108291)
--- trunk/Source/_javascript_Core/ChangeLog 2012-02-21 03:19:17 UTC (rev 108290)
+++ trunk/Source/_javascript_Core/ChangeLog 2012-02-21 03:20:37 UTC (rev 108291)
@@ -1,3 +1,55 @@
+2012-02-20 Mark Hahnenberg <[email protected]>
+
+ Implement fast path for op_new_array in the baseline JIT
+ https://bugs.webkit.org/show_bug.cgi?id=78612
+
+ Reviewed by Filip Pizlo.
+
+ * heap/CopiedAllocator.h:
+ (CopiedAllocator): Friended the JIT to allow access to m_currentOffset.
+ * heap/CopiedSpace.h:
+ (CopiedSpace): Friended the JIT to allow access to
+ (JSC::CopiedSpace::allocator):
+ * heap/Heap.h:
+ (JSC::Heap::storageAllocator): Added a getter for the CopiedAllocator class so the JIT
+ can use it for simple allocation i.e. when we can just bump the offset without having to
+ do anything else.
+ * jit/JIT.cpp:
+ (JSC::JIT::privateCompileSlowCases): Added new slow case for op_new_array for when
+ we have to bail out because the fast allocation path fails for whatever reason.
+ * jit/JIT.h:
+ (JIT):
+ * jit/JITInlineMethods.h:
+ (JSC::JIT::emitAllocateBasicStorage): Added utility function that allows objects to
+ allocate generic backing stores. This function is used by emitAllocateJSArray.
+ (JSC):
+ (JSC::JIT::emitAllocateJSArray): Added utility function that allows the client to
+ more easily allocate JSArrays. This function is used by emit_op_new_array and I expect
+ it will also be used for emit_op_new_array_buffer.
+ * jit/JITOpcodes.cpp:
+ (JSC::JIT::emit_op_new_array): Changed to do inline allocation of JSArrays. Still does
+ a stub call for oversize arrays.
+ (JSC):
+ (JSC::JIT::emitSlow_op_new_array): Just bails out to a stub call if we fail in any way on
+ the fast path.
+ * runtime/JSArray.cpp:
+ (JSC):
+ * runtime/JSArray.h: Added lots of offset functions for all the fields that we need to
+ initialize in the JIT.
+ (ArrayStorage):
+ (JSC::ArrayStorage::lengthOffset):
+ (JSC::ArrayStorage::numValuesInVectorOffset):
+ (JSC::ArrayStorage::allocBaseOffset):
+ (JSC::ArrayStorage::vectorOffset):
+ (JSArray):
+ (JSC::JSArray::sparseValueMapOffset):
+ (JSC::JSArray::subclassDataOffset):
+ (JSC::JSArray::indexBiasOffset):
+ (JSC):
+ (JSC::JSArray::storageSize): Moved this function from being a static function in the cpp file
+ to being a static function in the JSArray class. This move allows the JIT to call it to
+ see what size it should allocate.
+
2012-02-20 Gavin Barraclough <[email protected]>
DefineOwnProperty fails with numeric properties & Object.prototype
Modified: trunk/Source/_javascript_Core/heap/CopiedAllocator.h (108290 => 108291)
--- trunk/Source/_javascript_Core/heap/CopiedAllocator.h 2012-02-21 03:19:17 UTC (rev 108290)
+++ trunk/Source/_javascript_Core/heap/CopiedAllocator.h 2012-02-21 03:20:37 UTC (rev 108291)
@@ -31,6 +31,7 @@
namespace JSC {
class CopiedAllocator {
+ friend class JIT;
public:
CopiedAllocator();
void* allocate(size_t);
Modified: trunk/Source/_javascript_Core/heap/CopiedSpace.h (108290 => 108291)
--- trunk/Source/_javascript_Core/heap/CopiedSpace.h 2012-02-21 03:19:17 UTC (rev 108290)
+++ trunk/Source/_javascript_Core/heap/CopiedSpace.h 2012-02-21 03:20:37 UTC (rev 108291)
@@ -46,6 +46,7 @@
class CopiedSpace {
friend class SlotVisitor;
+ friend class JIT;
public:
CopiedSpace(Heap*);
void init();
@@ -53,6 +54,8 @@
CheckedBoolean tryAllocate(size_t, void**);
CheckedBoolean tryReallocate(void**, size_t, size_t);
+ CopiedAllocator& allocator() { return m_allocator; }
+
void startedCopying();
void doneCopying();
bool isInCopyPhase() { return m_inCopyingPhase; }
Modified: trunk/Source/_javascript_Core/heap/Heap.h (108290 => 108291)
--- trunk/Source/_javascript_Core/heap/Heap.h 2012-02-21 03:19:17 UTC (rev 108290)
+++ trunk/Source/_javascript_Core/heap/Heap.h 2012-02-21 03:20:37 UTC (rev 108291)
@@ -97,6 +97,7 @@
MarkedAllocator& allocatorForObjectWithoutDestructor(size_t bytes) { return m_objectSpace.allocatorFor(bytes); }
MarkedAllocator& allocatorForObjectWithDestructor(size_t bytes) { return m_objectSpace.destructorAllocatorFor(bytes); }
+ CopiedAllocator& storageAllocator() { return m_storageSpace.allocator(); }
CheckedBoolean tryAllocateStorage(size_t, void**);
CheckedBoolean tryReallocateStorage(void**, size_t, size_t);
Modified: trunk/Source/_javascript_Core/jit/JIT.cpp (108290 => 108291)
--- trunk/Source/_javascript_Core/jit/JIT.cpp 2012-02-21 03:19:17 UTC (rev 108290)
+++ trunk/Source/_javascript_Core/jit/JIT.cpp 2012-02-21 03:20:37 UTC (rev 108291)
@@ -477,6 +477,7 @@
DEFINE_SLOWCASE_OP(op_negate)
#endif
DEFINE_SLOWCASE_OP(op_neq)
+ DEFINE_SLOWCASE_OP(op_new_array)
DEFINE_SLOWCASE_OP(op_new_object)
DEFINE_SLOWCASE_OP(op_new_func)
DEFINE_SLOWCASE_OP(op_new_func_exp)
Modified: trunk/Source/_javascript_Core/jit/JIT.h (108290 => 108291)
--- trunk/Source/_javascript_Core/jit/JIT.h 2012-02-21 03:19:17 UTC (rev 108290)
+++ trunk/Source/_javascript_Core/jit/JIT.h 2012-02-21 03:20:37 UTC (rev 108291)
@@ -336,8 +336,10 @@
void emitWriteBarrier(JSCell* owner, RegisterID value, RegisterID scratch, WriteBarrierMode, WriteBarrierUseKind);
template<typename ClassType, bool destructor, typename StructureType> void emitAllocateBasicJSObject(StructureType, RegisterID result, RegisterID storagePtr);
+ void emitAllocateBasicStorage(size_t, RegisterID result, RegisterID storagePtr);
template<typename T> void emitAllocateJSFinalObject(T structure, RegisterID result, RegisterID storagePtr);
void emitAllocateJSFunction(FunctionExecutable*, RegisterID scopeChain, RegisterID result, RegisterID storagePtr);
+ void emitAllocateJSArray(unsigned valuesRegister, unsigned length, RegisterID cellResult, RegisterID storageResult, RegisterID storagePtr);
#if ENABLE(VALUE_PROFILER)
// This assumes that the value to profile is in regT0 and that regT3 is available for
@@ -957,7 +959,7 @@
void emitSlow_op_urshift(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_new_func(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_new_func_exp(Instruction*, Vector<SlowCaseEntry>::iterator&);
-
+ void emitSlow_op_new_array(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitRightShift(Instruction*, bool isUnsigned);
void emitRightShiftSlowCase(Instruction*, Vector<SlowCaseEntry>::iterator&, bool isUnsigned);
Modified: trunk/Source/_javascript_Core/jit/JITInlineMethods.h (108290 => 108291)
--- trunk/Source/_javascript_Core/jit/JITInlineMethods.h 2012-02-21 03:19:17 UTC (rev 108290)
+++ trunk/Source/_javascript_Core/jit/JITInlineMethods.h 2012-02-21 03:20:37 UTC (rev 108291)
@@ -454,6 +454,65 @@
#endif
}
+inline void JIT::emitAllocateBasicStorage(size_t size, RegisterID result, RegisterID storagePtr)
+{
+ CopiedAllocator* allocator = &m_globalData->heap.storageAllocator();
+
+ // FIXME: We need to check for wrap-around.
+ // Check to make sure that the allocation will fit in the current block.
+ loadPtr(&allocator->m_currentOffset, result);
+ addPtr(TrustedImm32(size), result);
+ loadPtr(&allocator->m_currentBlock, storagePtr);
+ addPtr(TrustedImm32(HeapBlock::s_blockSize), storagePtr);
+ addSlowCase(branchPtr(AboveOrEqual, result, storagePtr));
+
+ // Load the original offset.
+ loadPtr(&allocator->m_currentOffset, result);
+
+ // Bump the pointer forward.
+ move(result, storagePtr);
+ addPtr(TrustedImm32(size), storagePtr);
+ storePtr(storagePtr, &allocator->m_currentOffset);
+}
+
+inline void JIT::emitAllocateJSArray(unsigned valuesRegister, unsigned length, RegisterID cellResult, RegisterID storageResult, RegisterID storagePtr)
+{
+ unsigned initialLength = std::max(length, 4U);
+ size_t initialStorage = JSArray::storageSize(initialLength);
+
+ // Allocate the cell for the array.
+ emitAllocateBasicJSObject<JSArray, false>(TrustedImmPtr(m_codeBlock->globalObject()->arrayStructure()), cellResult, storagePtr);
+
+ // Allocate the backing store for the array.
+ emitAllocateBasicStorage(initialStorage, storageResult, storagePtr);
+
+ // Store all the necessary info in the ArrayStorage.
+ storePtr(storageResult, Address(storageResult, ArrayStorage::allocBaseOffset()));
+ store32(Imm32(length), Address(storageResult, ArrayStorage::lengthOffset()));
+ store32(Imm32(length), Address(storageResult, ArrayStorage::numValuesInVectorOffset()));
+
+ // Store the newly allocated ArrayStorage.
+ storePtr(storageResult, Address(cellResult, JSArray::storageOffset()));
+
+ // Store the vector length and index bias.
+ store32(Imm32(initialLength), Address(cellResult, JSArray::vectorLengthOffset()));
+ store32(TrustedImm32(0), Address(cellResult, JSArray::indexBiasOffset()));
+
+ // Initialize the subclass data and the sparse value map.
+ storePtr(TrustedImmPtr(0), Address(cellResult, JSArray::subclassDataOffset()));
+ storePtr(TrustedImmPtr(0), Address(cellResult, JSArray::sparseValueMapOffset()));
+
+ // Store the values we have.
+ for (unsigned i = 0; i < length; i++) {
+ loadPtr(Address(callFrameRegister, (valuesRegister + i) * sizeof(Register)), storagePtr);
+ storePtr(storagePtr, Address(storageResult, ArrayStorage::vectorOffset() + sizeof(WriteBarrier<Unknown>) * i));
+ }
+
+ // Zero out the remaining slots.
+ for (unsigned i = length; i < initialLength; i++)
+ storePtr(TrustedImmPtr(0), Address(storageResult, ArrayStorage::vectorOffset() + sizeof(WriteBarrier<Unknown>) * i));
+}
+
#if ENABLE(VALUE_PROFILER)
inline void JIT::emitValueProfilingSite(ValueProfile* valueProfile)
{
Modified: trunk/Source/_javascript_Core/jit/JITOpcodes.cpp (108290 => 108291)
--- trunk/Source/_javascript_Core/jit/JITOpcodes.cpp 2012-02-21 03:19:17 UTC (rev 108290)
+++ trunk/Source/_javascript_Core/jit/JITOpcodes.cpp 2012-02-21 03:20:37 UTC (rev 108291)
@@ -29,6 +29,7 @@
#include "JIT.h"
#include "Arguments.h"
+#include "CopiedSpaceInlineMethods.h"
#include "Heap.h"
#include "JITInlineMethods.h"
#include "JITStubCall.h"
@@ -1645,6 +1646,29 @@
void JIT::emit_op_new_array(Instruction* currentInstruction)
{
+ int length = currentInstruction[3].u.operand;
+ // FIXME: Add support for non-empty arrays. This involves copying the values over.
+ if (CopiedSpace::isOversize(JSArray::storageSize(length))) {
+ JITStubCall stubCall(this, cti_op_new_array);
+ stubCall.addArgument(Imm32(currentInstruction[2].u.operand));
+ stubCall.addArgument(Imm32(currentInstruction[3].u.operand));
+ stubCall.call(currentInstruction[1].u.operand);
+ return;
+ }
+ int dst = currentInstruction[1].u.operand;
+ int values = currentInstruction[2].u.operand;
+
+ emitAllocateJSArray(values, length, regT0, regT1, regT2);
+ emitStoreCell(dst, regT0);
+}
+
+void JIT::emitSlow_op_new_array(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+ int length = currentInstruction[3].u.operand;
+ if (CopiedSpace::isOversize(JSArray::storageSize(length)))
+ return;
+ linkSlowCase(iter); // Not enough space in MarkedSpace for cell.
+ linkSlowCase(iter); // Not enough space in CopiedSpace for storage.
JITStubCall stubCall(this, cti_op_new_array);
stubCall.addArgument(Imm32(currentInstruction[2].u.operand));
stubCall.addArgument(Imm32(currentInstruction[3].u.operand));
Modified: trunk/Source/_javascript_Core/runtime/JSArray.cpp (108290 => 108291)
--- trunk/Source/_javascript_Core/runtime/JSArray.cpp 2012-02-21 03:19:17 UTC (rev 108290)
+++ trunk/Source/_javascript_Core/runtime/JSArray.cpp 2012-02-21 03:20:37 UTC (rev 108291)
@@ -104,20 +104,6 @@
// This value is capped by the constant FIRST_VECTOR_GROW defined above.
static unsigned lastArraySize = 0;
-static inline size_t storageSize(unsigned vectorLength)
-{
- ASSERT(vectorLength <= MAX_STORAGE_VECTOR_LENGTH);
-
- // MAX_STORAGE_VECTOR_LENGTH is defined such that provided (vectorLength <= MAX_STORAGE_VECTOR_LENGTH)
- // - as asserted above - the following calculation cannot overflow.
- size_t size = (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)) + (vectorLength * sizeof(WriteBarrier<Unknown>));
- // Assertion to detect integer overflow in previous calculation (should not be possible, provided that
- // MAX_STORAGE_VECTOR_LENGTH is correctly defined).
- ASSERT(((size - (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>) == vectorLength) && (size >= (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))));
-
- return size;
-}
-
static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues)
{
return length <= MIN_SPARSE_ARRAY_INDEX || length / minDensityMultiplier <= numValues;
Modified: trunk/Source/_javascript_Core/runtime/JSArray.h (108290 => 108291)
--- trunk/Source/_javascript_Core/runtime/JSArray.h 2012-02-21 03:19:17 UTC (rev 108290)
+++ trunk/Source/_javascript_Core/runtime/JSArray.h 2012-02-21 03:20:37 UTC (rev 108291)
@@ -119,10 +119,16 @@
bool m_inCompactInitialization;
#endif
WriteBarrier<Unknown> m_vector[1];
+
+ static ptrdiff_t lengthOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_length); }
+ static ptrdiff_t numValuesInVectorOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector); }
+ static ptrdiff_t allocBaseOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_allocBase); }
+ static ptrdiff_t vectorOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_vector); }
};
class JSArray : public JSNonFinalObject {
friend class Walker;
+ friend class JIT;
protected:
JS_EXPORT_PRIVATE explicit JSArray(JSGlobalData&, Structure*);
@@ -258,6 +264,7 @@
JS_EXPORT_PRIVATE void setSubclassData(void*);
private:
+ static size_t storageSize(unsigned vectorLength);
bool isLengthWritable()
{
SparseArrayValueMap* map = m_sparseValueMap;
@@ -289,6 +296,10 @@
// FIXME: Maybe SparseArrayValueMap should be put into its own JSCell?
SparseArrayValueMap* m_sparseValueMap;
void* m_subclassData; // A JSArray subclass can use this to fill the vector lazily.
+
+ static ptrdiff_t sparseValueMapOffset() { return OBJECT_OFFSETOF(JSArray, m_sparseValueMap); }
+ static ptrdiff_t subclassDataOffset() { return OBJECT_OFFSETOF(JSArray, m_subclassData); }
+ static ptrdiff_t indexBiasOffset() { return OBJECT_OFFSETOF(JSArray, m_indexBias); }
};
inline JSArray* JSArray::create(JSGlobalData& globalData, Structure* structure, unsigned initialLength)
@@ -330,6 +341,30 @@
return i;
}
-} // namespace JSC
+// The definition of MAX_STORAGE_VECTOR_LENGTH is dependant on the definition storageSize
+// function below - the MAX_STORAGE_VECTOR_LENGTH limit is defined such that the storage
+// size calculation cannot overflow. (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)) +
+// (vectorLength * sizeof(WriteBarrier<Unknown>)) must be <= 0xFFFFFFFFU (which is maximum value of size_t).
+#define MAX_STORAGE_VECTOR_LENGTH static_cast<unsigned>((0xFFFFFFFFU - (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>))
+// These values have to be macros to be used in max() and min() without introducing
+// a PIC branch in Mach-O binaries, see <rdar://problem/5971391>.
+#define MIN_SPARSE_ARRAY_INDEX 10000U
+#define MAX_STORAGE_VECTOR_INDEX (MAX_STORAGE_VECTOR_LENGTH - 1)
+ inline size_t JSArray::storageSize(unsigned vectorLength)
+ {
+ ASSERT(vectorLength <= MAX_STORAGE_VECTOR_LENGTH);
+
+ // MAX_STORAGE_VECTOR_LENGTH is defined such that provided (vectorLength <= MAX_STORAGE_VECTOR_LENGTH)
+ // - as asserted above - the following calculation cannot overflow.
+ size_t size = (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)) + (vectorLength * sizeof(WriteBarrier<Unknown>));
+ // Assertion to detect integer overflow in previous calculation (should not be possible, provided that
+ // MAX_STORAGE_VECTOR_LENGTH is correctly defined).
+ ASSERT(((size - (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>) == vectorLength) && (size >= (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))));
+
+ return size;
+ }
+
+ } // namespace JSC
+
#endif // JSArray_h