Add optimised access to known properties on the global object.
authoroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 10 Sep 2008 09:23:35 +0000 (09:23 +0000)
committeroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 10 Sep 2008 09:23:35 +0000 (09:23 +0000)
Reviewed by Maciej Stachowiak

Improve cross scope access to the global object by emitting
code to access it directly rather than by walking the scope chain.

This is a 0.8% win in SunSpider and a 1.7% win in the v8 benchmarks.

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

JavaScriptCore/ChangeLog
JavaScriptCore/VM/CTI.cpp
JavaScriptCore/VM/CTI.h
JavaScriptCore/VM/CodeBlock.cpp
JavaScriptCore/VM/CodeGenerator.cpp
JavaScriptCore/VM/CodeGenerator.h
JavaScriptCore/VM/Machine.cpp
JavaScriptCore/VM/Opcode.h
JavaScriptCore/kjs/nodes.cpp

index a43dcb2..ec1cd2f 100644 (file)
@@ -1,3 +1,37 @@
+2008-09-09  Oliver Hunt  <oliver@apple.com>
+
+        Reviewed by Maciej Stachowiak.
+
+        Add optimised access to known properties on the global object.
+
+        Improve cross scope access to the global object by emitting
+        code to access it directly rather than by walking the scope chain.
+
+        This is a 0.8% win in SunSpider and a 1.7% win in the v8 benchmarks.
+
+        * VM/CTI.cpp:
+        (JSC::CTI::privateCompileMainPass):
+        (JSC::CTI::emitGetVariableObjectRegister):
+        (JSC::CTI::emitPutVariableObjectRegister):
+        * VM/CTI.h:
+        * VM/CodeBlock.cpp:
+        (JSC::CodeBlock::dump):
+        * VM/CodeGenerator.cpp:
+        (JSC::CodeGenerator::findScopedProperty):
+        (JSC::CodeGenerator::emitResolve):
+        (JSC::CodeGenerator::emitGetScopedVar):
+        (JSC::CodeGenerator::emitPutScopedVar):
+        * VM/CodeGenerator.h:
+        * VM/Machine.cpp:
+        (JSC::Machine::privateExecute):
+        * VM/Opcode.h:
+        * kjs/nodes.cpp:
+        (JSC::FunctionCallResolveNode::emitCode):
+        (JSC::PostfixResolveNode::emitCode):
+        (JSC::PrefixResolveNode::emitCode):
+        (JSC::ReadModifyResolveNode::emitCode):
+        (JSC::AssignResolveNode::emitCode):
+
 2008-09-10  Maciej Stachowiak  <mjs@apple.com>
 
         Reviewed by Oliver.
index cab6818..1f60a72 100644 (file)
@@ -628,6 +628,22 @@ void CTI::privateCompileMainPass()
             i += 6;
             break;
         }
+        case op_get_global_var: {
+            JSVariableObject* globalObject = static_cast<JSVariableObject*>(instruction[i + 2].u.jsCell);
+            m_jit.movl_i32r(reinterpret_cast<unsigned>(globalObject), X86::eax);
+            emitGetVariableObjectRegister(X86::eax, instruction[i + 3].u.operand, X86::eax);
+            emitPutResult(instruction[i + 1].u.operand, X86::eax);
+            i += 4;
+            break;
+        }
+        case op_put_global_var: {
+            JSVariableObject* globalObject = static_cast<JSVariableObject*>(instruction[i + 1].u.jsCell);
+            m_jit.movl_i32r(reinterpret_cast<unsigned>(globalObject), X86::eax);
+            emitGetArg(instruction[i + 3].u.operand, X86::edx);
+            emitPutVariableObjectRegister(X86::edx, X86::eax, instruction[i + 2].u.operand);
+            i += 4;
+            break;
+        }
         case op_get_scoped_var: {
             int skip = instruction[i + 3].u.operand + m_codeBlock->needsFullScopeChain;
 
@@ -636,9 +652,7 @@ void CTI::privateCompileMainPass()
                 m_jit.movl_mr(OBJECT_OFFSET(ScopeChainNode, next), X86::eax, X86::eax);
 
             m_jit.movl_mr(OBJECT_OFFSET(ScopeChainNode, object), X86::eax, X86::eax);
-            m_jit.movl_mr(JSVariableObject::offsetOf_d(), X86::eax, X86::eax);
-            m_jit.movl_mr(JSVariableObject::offsetOf_Data_registers(), X86::eax, X86::eax);
-            m_jit.movl_mr((instruction[i + 2].u.operand) * sizeof(Register), X86::eax, X86::eax);
+            emitGetVariableObjectRegister(X86::eax, instruction[i + 2].u.operand, X86::eax);
             emitPutResult(instruction[i + 1].u.operand);
             i += 4;
             break;
@@ -652,9 +666,7 @@ void CTI::privateCompileMainPass()
                 m_jit.movl_mr(OBJECT_OFFSET(ScopeChainNode, next), X86::edx, X86::edx);
 
             m_jit.movl_mr(OBJECT_OFFSET(ScopeChainNode, object), X86::edx, X86::edx);
-            m_jit.movl_mr(JSVariableObject::offsetOf_d(), X86::edx, X86::edx);
-            m_jit.movl_mr(JSVariableObject::offsetOf_Data_registers(), X86::edx, X86::edx);
-            m_jit.movl_rm(X86::eax, (instruction[i + 1].u.operand) * sizeof(Register), X86::edx);
+            emitPutVariableObjectRegister(X86::eax, X86::edx, instruction[i + 1].u.operand);
             i += 4;
             break;
         }
@@ -1830,6 +1842,20 @@ void* CTI::privateStringLengthTrampoline()
     return code;
 }
 
+void CTI::emitGetVariableObjectRegister(X86Assembler::RegisterID variableObject, int index, X86Assembler::RegisterID dst)
+{
+    m_jit.movl_mr(JSVariableObject::offsetOf_d(), variableObject, dst);
+    m_jit.movl_mr(JSVariableObject::offsetOf_Data_registers(), dst, dst);
+    m_jit.movl_mr(index * sizeof(Register), dst, dst);
+}
+
+void CTI::emitPutVariableObjectRegister(X86Assembler::RegisterID src, X86Assembler::RegisterID variableObject, int index)
+{
+    m_jit.movl_mr(JSVariableObject::offsetOf_d(), variableObject, variableObject);
+    m_jit.movl_mr(JSVariableObject::offsetOf_Data_registers(), variableObject, variableObject);
+    m_jit.movl_rm(src, index * sizeof(Register), variableObject);
+}
+
 #if ENABLE(WREC)
 
 void* CTI::compileRegExp(ExecState* exec, const UString& pattern, unsigned* numSubpatterns_ptr, const char** error_ptr, bool ignoreCase, bool multiline)
index bcb5db3..2f8f995 100644 (file)
@@ -329,7 +329,10 @@ namespace JSC {
         void emitCall(unsigned opcodeIndex, CTIHelper_b);
         void emitCall(unsigned opcodeIndex, CTIHelper_v);
         void emitCall(unsigned opcodeIndex, CTIHelper_s);
-
+        
+        void emitGetVariableObjectRegister(X86Assembler::RegisterID variableObject, int index, X86Assembler::RegisterID dst);
+        void emitPutVariableObjectRegister(X86Assembler::RegisterID src, X86Assembler::RegisterID variableObject, int index);
+        
         void emitSlowScriptCheck(unsigned opcodeIndex);
 #ifndef NDEBUG
         void printOpcodeOperandTypes(unsigned src1, unsigned src2);
index bc878ca..a8a2b6d 100644 (file)
@@ -522,6 +522,20 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator&
             printf("[%4d] put_scoped_var\t %d, %d, %s\n", location, index, skipLevels, registerName(r0).c_str());
             break;
         }
+        case op_get_global_var: {
+            int r0 = (++it)->u.operand;
+            JSValue* scope = static_cast<JSValue*>((++it)->u.jsCell);
+            int index = (++it)->u.operand;
+            printf("[%4d] get_global_var\t %s, %s, %d\n", location, registerName(r0).c_str(), valueToSourceString(exec, scope).ascii(), index);
+            break;
+        }
+        case op_put_global_var: {
+            JSValue* scope = static_cast<JSValue*>((++it)->u.jsCell);
+            int index = (++it)->u.operand;
+            int r0 = (++it)->u.operand;
+            printf("[%4d] put_global_var\t %s, %d, %s\n", location, valueToSourceString(exec, scope).ascii(), index, registerName(r0).c_str());
+            break;
+        }
         case op_resolve_base: {
             int r0 = (++it)->u.operand;
             int id0 = (++it)->u.operand;
index a18aa01..f66bd8f 100644 (file)
@@ -677,7 +677,7 @@ RegisterID* CodeGenerator::emitUnexpectedLoad(RegisterID* dst, double d)
     return dst;
 }
 
-bool CodeGenerator::findScopedProperty(const Identifier& property, int& index, size_t& stackDepth, bool forWriting)
+bool CodeGenerator::findScopedProperty(const Identifier& property, int& index, size_t& stackDepth, bool forWriting, JSValue*& globalObject)
 {
     // Cases where we cannot optimise the lookup
     if (property == propertyNames().arguments || !canOptimizeNonLocals()) {
@@ -702,10 +702,14 @@ bool CodeGenerator::findScopedProperty(const Identifier& property, int& index, s
             if (entry.isReadOnly() && forWriting) {
                 stackDepth = 0;
                 index = missingSymbolMarker();
+                if (++iter == end)
+                    globalObject = currentVariableObject;
                 return false;
             }
             stackDepth = depth;
             index = entry.getIndex();
+            if (++iter == end)
+                globalObject = currentVariableObject;
             return true;
         }
         if (currentVariableObject->isDynamicScope())
@@ -715,6 +719,9 @@ bool CodeGenerator::findScopedProperty(const Identifier& property, int& index, s
     // Can't locate the property but we're able to avoid a few lookups
     stackDepth = depth;
     index = missingSymbolMarker();
+    JSObject* scope = *iter;
+    if (++iter == end)
+        globalObject = scope;
     return true;
 }
 
@@ -722,7 +729,8 @@ RegisterID* CodeGenerator::emitResolve(RegisterID* dst, const Identifier& proper
 {
     size_t depth = 0;
     int index = 0;
-    if (!findScopedProperty(property, index, depth, false)) {
+    JSValue* globalObject = 0;
+    if (!findScopedProperty(property, index, depth, false, globalObject)) {
         // We can't optimise at all :-(
         emitOpcode(op_resolve);
         instructions().append(dst->index());
@@ -741,11 +749,19 @@ RegisterID* CodeGenerator::emitResolve(RegisterID* dst, const Identifier& proper
     }
 
     // Directly index the property lookup across multiple scopes.  Yay!
-    return emitGetScopedVar(dst, depth, index);
+    return emitGetScopedVar(dst, depth, index, globalObject);
 }
 
-RegisterID* CodeGenerator::emitGetScopedVar(RegisterID* dst, size_t depth, int index)
+RegisterID* CodeGenerator::emitGetScopedVar(RegisterID* dst, size_t depth, int index, JSValue* globalObject)
 {
+    if (globalObject) {
+        emitOpcode(op_get_global_var);
+        instructions().append(dst->index());
+        instructions().append(static_cast<JSCell*>(globalObject));
+        instructions().append(index);
+        return dst;
+    }
+
     emitOpcode(op_get_scoped_var);
     instructions().append(dst->index());
     instructions().append(index);
@@ -753,8 +769,15 @@ RegisterID* CodeGenerator::emitGetScopedVar(RegisterID* dst, size_t depth, int i
     return dst;
 }
 
-RegisterID* CodeGenerator::emitPutScopedVar(size_t depth, int index, RegisterID* value)
+RegisterID* CodeGenerator::emitPutScopedVar(size_t depth, int index, RegisterID* value, JSValue* globalObject)
 {
+    if (globalObject) {
+        emitOpcode(op_put_global_var);
+        instructions().append(static_cast<JSCell*>(globalObject));
+        instructions().append(index);
+        instructions().append(value->index());
+        return value;
+    }
     emitOpcode(op_put_scoped_var);
     instructions().append(index);
     instructions().append(depth);
index 9cf1e05..60e17bf 100644 (file)
@@ -103,7 +103,7 @@ namespace JSC {
         //
         // NB: depth does _not_ include the local scope.  eg. a depth of 0 refers
         // to the scope containing this codeblock.
-        bool findScopedProperty(const Identifier&, int& index, size_t& depth, bool forWriting);
+        bool findScopedProperty(const Identifier&, int& index, size_t& depth, bool forWriting, JSValue*& globalObject);
 
         // Returns the register storing "this"
         RegisterID* thisRegister() { return &m_thisRegister; }
@@ -256,8 +256,8 @@ namespace JSC {
         RegisterID* emitIn(RegisterID* dst, RegisterID* property, RegisterID* base) { return emitBinaryOp(op_in, dst, property, base); }
 
         RegisterID* emitResolve(RegisterID* dst, const Identifier& property);
-        RegisterID* emitGetScopedVar(RegisterID* dst, size_t skip, int index);
-        RegisterID* emitPutScopedVar(size_t skip, int index, RegisterID* value);
+        RegisterID* emitGetScopedVar(RegisterID* dst, size_t skip, int index, JSValue* globalObject);
+        RegisterID* emitPutScopedVar(size_t skip, int index, RegisterID* value, JSValue* globalObject);
 
         RegisterID* emitResolveBase(RegisterID* dst, const Identifier& property);
         RegisterID* emitResolveWithBase(RegisterID* baseDst, RegisterID* propDst, const Identifier& property);
index 3fb0152..1755731 100644 (file)
@@ -2086,6 +2086,34 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
 
         NEXT_OPCODE;
     }
+    BEGIN_OPCODE(op_get_global_var) {
+        /* get_global_var dst(r) index(n)
+
+           Gets the global var at global slot index and places it in register dst.
+         */
+        int dst = (++vPC)->u.operand;
+        JSGlobalObject* scope = static_cast<JSGlobalObject*>((++vPC)->u.jsCell);
+        ASSERT(scope->isGlobalObject());
+        int index = (++vPC)->u.operand;
+
+        r[dst] = scope->registerAt(index);
+        ++vPC;
+        NEXT_OPCODE;
+    }
+    BEGIN_OPCODE(op_put_global_var) {
+        /* put_global_var globalObject(c) index(n) value(r)
+         
+           Puts value into global slot index.
+         */
+        JSGlobalObject* scope = static_cast<JSGlobalObject*>((++vPC)->u.jsCell);
+        ASSERT(scope->isGlobalObject());
+        int index = (++vPC)->u.operand;
+        int value = (++vPC)->u.operand;
+        
+        scope->registerAt(index) = r[value].jsValue(exec);
+        ++vPC;
+        NEXT_OPCODE;
+    }            
     BEGIN_OPCODE(op_get_scoped_var) {
         /* get_scoped_var dst(r) index(n) skip(n)
 
index 29707e5..772733c 100644 (file)
@@ -84,6 +84,8 @@ namespace JSC {
         macro(op_resolve_skip) \
         macro(op_get_scoped_var) \
         macro(op_put_scoped_var) \
+        macro(op_get_global_var) \
+        macro(op_put_global_var) \
         macro(op_resolve_base) \
         macro(op_resolve_with_base) \
         macro(op_resolve_func) \
index e687416..3bfc986 100644 (file)
@@ -432,8 +432,9 @@ RegisterID* FunctionCallResolveNode::emitCode(CodeGenerator& generator, Register
 
     int index = 0;
     size_t depth = 0;
-    if (generator.findScopedProperty(m_ident, index, depth, false) && index != missingSymbolMarker()) {
-        RegisterID* func = generator.emitGetScopedVar(generator.newTemporary(), depth, index);
+    JSValue* globalObject = 0;
+    if (generator.findScopedProperty(m_ident, index, depth, false, globalObject) && index != missingSymbolMarker()) {
+        RegisterID* func = generator.emitGetScopedVar(generator.newTemporary(), depth, index, globalObject);
         return generator.emitCall(generator.finalDestination(dst), func, 0, m_args.get(), m_divot, m_startOffset, m_endOffset);
     }
 
@@ -490,8 +491,9 @@ RegisterID* PostfixResolveNode::emitCode(CodeGenerator& generator, RegisterID* d
 
     int index = 0;
     size_t depth = 0;
-    if (generator.findScopedProperty(m_ident, index, depth, true) && index != missingSymbolMarker()) {
-        RefPtr<RegisterID> value = generator.emitGetScopedVar(generator.newTemporary(), depth, index);
+    JSValue* globalObject = 0;
+    if (generator.findScopedProperty(m_ident, index, depth, true, globalObject) && index != missingSymbolMarker()) {
+        RefPtr<RegisterID> value = generator.emitGetScopedVar(generator.newTemporary(), depth, index, globalObject);
         RegisterID* oldValue;
         if (dst == ignoredResult()) {
             oldValue = 0;
@@ -499,7 +501,7 @@ RegisterID* PostfixResolveNode::emitCode(CodeGenerator& generator, RegisterID* d
         } else {
             oldValue = emitPostIncOrDec(generator, generator.finalDestination(dst), value.get(), m_operator);
         }
-        generator.emitPutScopedVar(depth, index, value.get());
+        generator.emitPutScopedVar(depth, index, value.get(), globalObject);
         return oldValue;
     }
 
@@ -673,10 +675,11 @@ RegisterID* PrefixResolveNode::emitCode(CodeGenerator& generator, RegisterID* ds
 
     int index = 0;
     size_t depth = 0;
-    if (generator.findScopedProperty(m_ident, index, depth, false) && index != missingSymbolMarker()) {
-        RefPtr<RegisterID> propDst = generator.emitGetScopedVar(generator.tempDestination(dst), depth, index);
+    JSValue* globalObject = 0;
+    if (generator.findScopedProperty(m_ident, index, depth, false, globalObject) && index != missingSymbolMarker()) {
+        RefPtr<RegisterID> propDst = generator.emitGetScopedVar(generator.tempDestination(dst), depth, index, globalObject);
         emitPreIncOrDec(generator, propDst.get(), m_operator);
-        generator.emitPutScopedVar(depth, index, propDst.get());
+        generator.emitPutScopedVar(depth, index, propDst.get(), globalObject);
         return generator.moveToDestinationIfNeeded(dst, propDst.get());;
     }
 
@@ -884,11 +887,12 @@ RegisterID* ReadModifyResolveNode::emitCode(CodeGenerator& generator, RegisterID
 
     int index = 0;
     size_t depth = 0;
-    if (generator.findScopedProperty(m_ident, index, depth, true) && index != missingSymbolMarker()) {
-        RefPtr<RegisterID> src1 = generator.emitGetScopedVar(generator.tempDestination(dst), depth, index);
+    JSValue* globalObject = 0;
+    if (generator.findScopedProperty(m_ident, index, depth, true, globalObject) && index != missingSymbolMarker()) {
+        RefPtr<RegisterID> src1 = generator.emitGetScopedVar(generator.tempDestination(dst), depth, index, globalObject);
         RegisterID* src2 = generator.emitNode(m_right.get());
         RegisterID* result = emitReadModifyAssignment(generator, generator.finalDestination(dst, src1.get()), src1.get(), src2, m_operator);
-        generator.emitPutScopedVar(depth, index, result);
+        generator.emitPutScopedVar(depth, index, result, globalObject);
         return result;
     }
 
@@ -915,11 +919,12 @@ RegisterID* AssignResolveNode::emitCode(CodeGenerator& generator, RegisterID* ds
 
     int index = 0;
     size_t depth = 0;
-    if (generator.findScopedProperty(m_ident, index, depth, true) && index != missingSymbolMarker()) {
+    JSValue* globalObject = 0;
+    if (generator.findScopedProperty(m_ident, index, depth, true, globalObject) && index != missingSymbolMarker()) {
         if (dst == ignoredResult())
             dst = 0;
         RegisterID* value = generator.emitNode(dst, m_right.get());
-        generator.emitPutScopedVar(depth, index, value);
+        generator.emitPutScopedVar(depth, index, value, globalObject);
         return value;
     }