+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.
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;
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;
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;
}
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)
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);
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;
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()) {
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())
// 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;
}
{
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());
}
// 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);
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);
//
// 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; }
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);
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)
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) \
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);
}
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;
} 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;
}
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());;
}
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;
}
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;
}