Inline allocation of function objects
https://bugs.webkit.org/show_bug.cgi?id=65779

Reviewed by Gavin Barraclough.

Inline allocation and initilisation of function objects
in generated code.  This ended up being a 60-70% improvement
in function allocation performance.  This improvement shows
up as a ~2% improvement in 32bit sunspider and V8, but is a
wash on 64-bit.

We currently don't inline the allocation of named function
expressions, as that requires being able to gc allocate a
variable object.

* jit/JIT.cpp:
(JSC::JIT::privateCompileSlowCases):
* jit/JIT.h:
(JSC::JIT::emitStoreCell):
* jit/JITInlineMethods.h:
(JSC::JIT::emitAllocateBasicJSObject):
(JSC::JIT::emitAllocateJSFinalObject):
(JSC::JIT::emitAllocateJSFunction):
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_new_func):
(JSC::JIT::emitSlow_op_new_func):
(JSC::JIT::emit_op_new_func_exp):
(JSC::JIT::emitSlow_op_new_func_exp):
* jit/JITOpcodes32_64.cpp:
    Removed duplicate implementation of op_new_func and op_new_func_exp
* runtime/JSFunction.h:
(JSC::JSFunction::offsetOfScopeChain):
(JSC::JSFunction::offsetOfExecutable):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@92498 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 774fd32..8f81bf0 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,39 @@
+2011-08-05  Oliver Hunt  <oliver@apple.com>
+
+        Inline allocation of function objects
+        https://bugs.webkit.org/show_bug.cgi?id=65779
+
+        Reviewed by Gavin Barraclough.
+
+        Inline allocation and initilisation of function objects
+        in generated code.  This ended up being a 60-70% improvement
+        in function allocation performance.  This improvement shows
+        up as a ~2% improvement in 32bit sunspider and V8, but is a
+        wash on 64-bit.
+
+        We currently don't inline the allocation of named function
+        expressions, as that requires being able to gc allocate a
+        variable object.
+
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileSlowCases):
+        * jit/JIT.h:
+        (JSC::JIT::emitStoreCell):
+        * jit/JITInlineMethods.h:
+        (JSC::JIT::emitAllocateBasicJSObject):
+        (JSC::JIT::emitAllocateJSFinalObject):
+        (JSC::JIT::emitAllocateJSFunction):
+        * jit/JITOpcodes.cpp:
+        (JSC::JIT::emit_op_new_func):
+        (JSC::JIT::emitSlow_op_new_func):
+        (JSC::JIT::emit_op_new_func_exp):
+        (JSC::JIT::emitSlow_op_new_func_exp):
+        * jit/JITOpcodes32_64.cpp:
+            Removed duplicate implementation of op_new_func and op_new_func_exp
+        * runtime/JSFunction.h:
+        (JSC::JSFunction::offsetOfScopeChain):
+        (JSC::JSFunction::offsetOfExecutable):
+
 2011-08-04  David Levin  <levin@chromium.org>
 
         CStringBuffer should have thread safety checks turned on.
diff --git a/Source/JavaScriptCore/jit/JIT.cpp b/Source/JavaScriptCore/jit/JIT.cpp
index c60cc57..a454167 100644
--- a/Source/JavaScriptCore/jit/JIT.cpp
+++ b/Source/JavaScriptCore/jit/JIT.cpp
@@ -431,6 +431,8 @@
 #endif
         DEFINE_SLOWCASE_OP(op_neq)
         DEFINE_SLOWCASE_OP(op_new_object)
+        DEFINE_SLOWCASE_OP(op_new_func)
+        DEFINE_SLOWCASE_OP(op_new_func_exp)
         DEFINE_SLOWCASE_OP(op_not)
         DEFINE_SLOWCASE_OP(op_nstricteq)
         DEFINE_SLOWCASE_OP(op_post_dec)
diff --git a/Source/JavaScriptCore/jit/JIT.h b/Source/JavaScriptCore/jit/JIT.h
index 4ed2d38..85c4b0a 100644
--- a/Source/JavaScriptCore/jit/JIT.h
+++ b/Source/JavaScriptCore/jit/JIT.h
@@ -49,6 +49,7 @@
 namespace JSC {
 
     class CodeBlock;
+    class FunctionExecutable;
     class JIT;
     class JSPropertyNameIterator;
     class Interpreter;
@@ -299,9 +300,10 @@
         void testPrototype(JSValue, JumpList& failureCases);
 
         void emitWriteBarrier(RegisterID owner, RegisterID scratch);
-        
-        template<typename T>
-        void emitAllocateJSFinalObject(T structure, RegisterID result, RegisterID scratch);
+
+        template<typename ClassType, typename StructureType> void emitAllocateBasicJSObject(StructureType, void* vtable, RegisterID result, RegisterID storagePtr);
+        template<typename T> void emitAllocateJSFinalObject(T structure, RegisterID result, RegisterID storagePtr);
+        void emitAllocateJSFunction(FunctionExecutable*, RegisterID scopeChain, RegisterID result, RegisterID storagePtr);
 
 #if USE(JSVALUE32_64)
         bool getOperandConstantImmediateInt(unsigned op1, unsigned op2, unsigned& op, int32_t& constant);
@@ -528,6 +530,10 @@
         void emitGetVirtualRegister(int src, RegisterID dst);
         void emitGetVirtualRegisters(int src1, RegisterID dst1, int src2, RegisterID dst2);
         void emitPutVirtualRegister(unsigned dst, RegisterID from = regT0);
+        void emitStoreCell(unsigned dst, RegisterID payload, bool /* only used in JSValue32_64 */ = false)
+        {
+            emitPutVirtualRegister(dst, payload);
+        }
 
         int32_t getConstantOperandImmediateInt(unsigned src);
 
@@ -906,6 +912,8 @@
         void emitSlow_op_to_jsnumber(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_to_primitive(Instruction*, Vector<SlowCaseEntry>::iterator&);
         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 emitRightShift(Instruction*, bool isUnsigned);
diff --git a/Source/JavaScriptCore/jit/JITInlineMethods.h b/Source/JavaScriptCore/jit/JITInlineMethods.h
index 724de3a..7445994 100644
--- a/Source/JavaScriptCore/jit/JITInlineMethods.h
+++ b/Source/JavaScriptCore/jit/JITInlineMethods.h
@@ -374,29 +374,60 @@
     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)
+template <typename ClassType, typename StructureType> inline void JIT::emitAllocateBasicJSObject(StructureType structure, void* vtable, RegisterID result, RegisterID storagePtr)
 {
-    NewSpace::SizeClass* sizeClass = &m_globalData->heap.sizeClassFor(sizeof(JSFinalObject));
+    NewSpace::SizeClass* sizeClass = &m_globalData->heap.sizeClassFor(sizeof(ClassType));
     loadPtr(&sizeClass->firstFreeCell, result);
     addSlowCase(branchTestPtr(Zero, result));
-    
+
     // remove the object from the free list
-    loadPtr(Address(result), scratch);
-    storePtr(scratch, &sizeClass->firstFreeCell);
-    
+    loadPtr(Address(result), storagePtr);
+    storePtr(storagePtr, &sizeClass->firstFreeCell);
+
     // initialize the object's vtable
-    storePtr(ImmPtr(m_globalData->jsFinalObjectVPtr), Address(result));
-    
+    storePtr(TrustedImmPtr(vtable), Address(result));
+
     // initialize the object's structure
     storePtr(structure, Address(result, JSCell::structureOffset()));
-    
+
     // initialize the inheritor ID
-    storePtr(ImmPtr(0), Address(result, JSObject::offsetOfInheritorID()));
-    
+    storePtr(TrustedImmPtr(0), Address(result, JSObject::offsetOfInheritorID()));
+
     // initialize the object's property storage pointer
-    addPtr(Imm32(sizeof(JSObject)), result, scratch);
-    storePtr(scratch, Address(result, JSObject::offsetOfPropertyStorage()));
+    addPtr(TrustedImm32(sizeof(JSObject)), result, storagePtr);
+    storePtr(storagePtr, Address(result, ClassType::offsetOfPropertyStorage()));
+}
+
+template <typename T> inline void JIT::emitAllocateJSFinalObject(T structure, RegisterID result, RegisterID scratch)
+{
+    emitAllocateBasicJSObject<JSFinalObject>(structure, m_globalData->jsFinalObjectVPtr, result, scratch);
+}
+
+inline void JIT::emitAllocateJSFunction(FunctionExecutable* executable, RegisterID scopeChain, RegisterID result, RegisterID storagePtr)
+{
+    emitAllocateBasicJSObject<JSFunction>(TrustedImmPtr(m_codeBlock->globalObject()->namedFunctionStructure()), m_globalData->jsFunctionVPtr, result, storagePtr);
+
+    // store the function's scope chain
+    storePtr(scopeChain, Address(result, JSFunction::offsetOfScopeChain()));
+
+    // store the function's executable member
+    storePtr(TrustedImmPtr(executable), Address(result, JSFunction::offsetOfExecutable()));
+
+    
+    // store the function's global object
+    int globalObjectOffset = sizeof(JSValue) * JSFunction::GlobalObjectSlot;
+    storePtr(TrustedImmPtr(m_codeBlock->globalObject()), Address(regT1, globalObjectOffset + OBJECT_OFFSETOF(JSValue, u.asBits.payload)));
+#if USE(JSVALUE32_64)
+    store32(TrustedImm32(JSValue::CellTag), Address(regT1, globalObjectOffset + OBJECT_OFFSETOF(JSValue, u.asBits.tag)));
+#endif
+
+    // store the function's name
+    ASSERT(executable->nameValue());
+    int functionNameOffset = sizeof(JSValue) * m_codeBlock->globalObject()->functionNameOffset();
+    storePtr(TrustedImmPtr(executable->nameValue()), Address(regT1, functionNameOffset + OBJECT_OFFSETOF(JSValue, u.asBits.payload)));
+#if USE(JSVALUE32_64)
+    store32(TrustedImm32(JSValue::CellTag), Address(regT1, functionNameOffset + OBJECT_OFFSETOF(JSValue, u.asBits.tag)));
+#endif
 }
 
 #if USE(JSVALUE32_64)
diff --git a/Source/JavaScriptCore/jit/JITOpcodes.cpp b/Source/JavaScriptCore/jit/JITOpcodes.cpp
index b0d0cf7..84ad192 100644
--- a/Source/JavaScriptCore/jit/JITOpcodes.cpp
+++ b/Source/JavaScriptCore/jit/JITOpcodes.cpp
@@ -725,13 +725,6 @@
     stubCall.call(currentInstruction[2].u.operand);
 }
 
-void JIT::emit_op_new_func_exp(Instruction* currentInstruction)
-{
-    JITStubCall stubCall(this, cti_op_new_func_exp);
-    stubCall.addArgument(TrustedImmPtr(m_codeBlock->functionExpr(currentInstruction[2].u.operand)));
-    stubCall.call(currentInstruction[1].u.operand);
-}
-
 void JIT::emit_op_jtrue(Instruction* currentInstruction)
 {
     unsigned target = currentInstruction[2].u.operand;
@@ -1621,11 +1614,59 @@
         lazyJump = branchTestPtr(NonZero, addressFor(dst));
 #endif
     }
+
+    FunctionExecutable* executable = m_codeBlock->functionDecl(currentInstruction[2].u.operand);
+    emitGetFromCallFrameHeaderPtr(RegisterFile::ScopeChain, regT2);
+    emitAllocateJSFunction(executable, regT2, regT0, regT1);
+
+    emitStoreCell(dst, regT0);
+
+    if (currentInstruction[3].u.operand) {
+#if USE(JSVALUE32_64)        
+        unmap();
+#else
+        killLastResultRegister();
+#endif
+        lazyJump.link(this);
+    }
+}
+
+void JIT::emitSlow_op_new_func(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+    linkSlowCase(iter);
     JITStubCall stubCall(this, cti_op_new_func);
     stubCall.addArgument(TrustedImmPtr(m_codeBlock->functionDecl(currentInstruction[2].u.operand)));
     stubCall.call(currentInstruction[1].u.operand);
-    if (currentInstruction[3].u.operand)
-        lazyJump.link(this);
+}
+
+void JIT::emit_op_new_func_exp(Instruction* currentInstruction)
+{
+    FunctionExecutable* executable = m_codeBlock->functionExpr(currentInstruction[2].u.operand);
+
+    // We only inline the allocation of a anonymous function expressions
+    // If we want to be able to allocate a named function expression, we would
+    // need to be able to do inline allocation of a JSStaticScopeObject.
+    if (executable->name().isNull()) {
+        emitGetFromCallFrameHeaderPtr(RegisterFile::ScopeChain, regT2);
+        emitAllocateJSFunction(executable, regT2, regT0, regT1);
+        emitStoreCell(currentInstruction[1].u.operand, regT0);
+        return;
+    }
+
+    JITStubCall stubCall(this, cti_op_new_func_exp);
+    stubCall.addArgument(TrustedImmPtr(m_codeBlock->functionExpr(currentInstruction[2].u.operand)));
+    stubCall.call(currentInstruction[1].u.operand);
+}
+
+void JIT::emitSlow_op_new_func_exp(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+    FunctionExecutable* executable = m_codeBlock->functionExpr(currentInstruction[2].u.operand);
+    if (!executable->name().isNull())
+        return;
+    linkSlowCase(iter);
+    JITStubCall stubCall(this, cti_op_new_func_exp);
+    stubCall.addArgument(TrustedImmPtr(executable));
+    stubCall.call(currentInstruction[1].u.operand);
 }
 
 void JIT::emit_op_new_array(Instruction* currentInstruction)
diff --git a/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp b/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
index 713c6f9..abdb6f5 100644
--- a/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
+++ b/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
@@ -1118,13 +1118,6 @@
     stubCall.call(currentInstruction[2].u.operand);
 }
 
-void JIT::emit_op_new_func_exp(Instruction* currentInstruction)
-{
-    JITStubCall stubCall(this, cti_op_new_func_exp);
-    stubCall.addArgument(TrustedImmPtr(m_codeBlock->functionExpr(currentInstruction[2].u.operand)));
-    stubCall.call(currentInstruction[1].u.operand);
-}
-
 void JIT::emit_op_throw(Instruction* currentInstruction)
 {
     unsigned exception = currentInstruction[1].u.operand;
diff --git a/Source/JavaScriptCore/runtime/JSFunction.h b/Source/JavaScriptCore/runtime/JSFunction.h
index cdfad94..006a7db 100644
--- a/Source/JavaScriptCore/runtime/JSFunction.h
+++ b/Source/JavaScriptCore/runtime/JSFunction.h
@@ -110,6 +110,16 @@
         virtual ConstructType getConstructData(ConstructData&);
         virtual CallType getCallData(CallData&);
 
+        static inline size_t offsetOfScopeChain()
+        {
+            return OBJECT_OFFSETOF(JSFunction, m_scopeChain);
+        }
+
+        static inline size_t offsetOfExecutable()
+        {
+            return OBJECT_OFFSETOF(JSFunction, m_executable);
+        }
+
     protected:
         const static unsigned StructureFlags = OverridesGetOwnPropertySlot | ImplementsHasInstance | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags;