Allocate local ScopeChain register
authormsaboff@apple.com <msaboff@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 21 Nov 2014 23:41:26 +0000 (23:41 +0000)
committermsaboff@apple.com <msaboff@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 21 Nov 2014 23:41:26 +0000 (23:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=138793

Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

Now we allocate the scope register as a local.  The allocated register is stored in the
CodeBlock for use by other components.  Update the DFG to work with a local scope register.
Changed usage of JSStack::ScopeChain access to the CallFrame header to use the allocated
local register.

* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
Updated to properly represent the operand inputs and bytecode result.

* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::CodeBlock):
* bytecode/CodeBlock.h:
(JSC::CodeBlock::setScopeRegister):
(JSC::CodeBlock::scopeRegister):
* bytecode/UnlinkedCodeBlock.h:
(JSC::UnlinkedCodeBlock::setScopeRegister):
(JSC::UnlinkedCodeBlock::scopeRegister):
Added scope register member and accessors.

* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::allocateAndEmitScope):
* bytecompiler/BytecodeGenerator.h:
(JSC::BytecodeGenerator::scopeRegister):
Change m_scopeRegister to an allocated register.  Added allocateAndEmitScope helper to
allocate the scope register, set the CodeBlock with its value and emit op_get_scope.

* debugger/DebuggerCallFrame.cpp:
(JSC::DebuggerCallFrame::scope): Changed to access the scope using the new convention.

* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::get):
(JSC::DFG::ByteCodeParser::flush):
(JSC::DFG::ByteCodeParser::inlineCall):
(JSC::DFG::ByteCodeParser::parseBlock):
Changed op_create_lexical_environment to set the scope VirtualRegister operand.
Filled out op_get_scope processing to emit a GetScope node putting the result in
the scope VirtualRegister result operand.
Added Phantoms where appropriate to keep the Scope register alive in places where
it use is optimized away, but where the baseline JIT would need to use its value.
Eliminated uses of JSStack::ScopeChain.

* dfg/DFGStackLayoutPhase.cpp:
(JSC::DFG::StackLayoutPhase::run):
Make sure that the scope register stack location is allocated using the same place
that the codeBlock expects.

* dfg/DFGStrengthReductionPhase.cpp:
(JSC::DFG::StrengthReductionPhase::handleNode):
Allow strength reduction of Flush to skip of GetScope nodes looking for a prior
corresponding SetLocal.

* interpreter/CallFrame.h:
(JSC::ExecState::scope):
(JSC::ExecState::setScope):
Added new scope() and setScope() helpers that take a VirtualRegister offset.

* interpreter/Interpreter.cpp:
(JSC::eval):
Changed eval() to get the scope from the caller's scope register instead of from the
temporary frame created for eval.

* interpreter/Interpreter.cpp:
(JSC::Interpreter::unwind):
Changed unwind() to manipulate the scope n the allocated register instead of from the
call frame slot.

* interpreter/StackVisitor.cpp:
(JSC::StackVisitor::readNonInlinedFrame):
(JSC::StackVisitor::readInlinedFrame):
* interpreter/StackVisitor.h:
(JSC::StackVisitor::Frame::callee):
(JSC::StackVisitor::Frame::scope): Deleted.
Eliminated the scope member as it needed to change and no StackVisitor users use it.

* jit/JITOperations.cpp:
(JSC::operationPushNameScope):
(JSC::operationPushWithScope):
* runtime/JSNameScope.h:
(JSC::JSNameScope::create):
* runtime/JSWithScope.h:
(JSC::JSWithScope::create): Deleted.
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
Deleted JSNameScope::create() and JSWithScope::create() flavors tht used the ScopeChain slot
in the CallFrame header.  Changed the only user of these function, op_push_name_scope and
op_push_with_scope helpers, to use the remaining create variants that require explicit scope.
Those operations get the scope from the register pointed to by their scope operands.

* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
Changed resolveScope to use the allocated register.

LayoutTests:

New test that sets a breakpoint in a callee of a DFG caller.  While stopped in the
breakpoint, it modifies a global via the scope chain of the DFG caller as well as
a local of the DFG caller.

* inspector-protocol/debugger/resources/breakpoint.js:
(notInlineable3):
(dfgWithoutInline3):
* inspector-protocol/debugger/setBreakpoint-dfg-callee-and-examine-dfg-local-expected.txt: Added.
* inspector-protocol/debugger/setBreakpoint-dfg-callee-and-examine-dfg-local.html: Added.

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

25 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector-protocol/debugger/resources/breakpoint.js
LayoutTests/inspector-protocol/debugger/setBreakpoint-dfg-callee-and-examine-dfg-local-expected.txt [new file with mode: 0644]
LayoutTests/inspector-protocol/debugger/setBreakpoint-dfg-callee-and-examine-dfg-local.html [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/BytecodeUseDef.h
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/CodeBlock.h
Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGStackLayoutPhase.cpp
Source/JavaScriptCore/dfg/DFGStrengthReductionPhase.cpp
Source/JavaScriptCore/interpreter/CallFrame.h
Source/JavaScriptCore/interpreter/Interpreter.cpp
Source/JavaScriptCore/interpreter/StackVisitor.cpp
Source/JavaScriptCore/interpreter/StackVisitor.h
Source/JavaScriptCore/jit/JITOperations.cpp
Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/runtime/JSNameScope.h
Source/JavaScriptCore/runtime/JSWithScope.h

index f4f1275..484ce68 100644 (file)
@@ -1,3 +1,20 @@
+2014-11-21  Michael Saboff  <msaboff@apple.com>
+
+        Allocate local ScopeChain register
+        https://bugs.webkit.org/show_bug.cgi?id=138793
+
+        Reviewed by Geoffrey Garen.
+
+        New test that sets a breakpoint in a callee of a DFG caller.  While stopped in the
+        breakpoint, it modifies a global via the scope chain of the DFG caller as well as
+        a local of the DFG caller.
+
+        * inspector-protocol/debugger/resources/breakpoint.js:
+        (notInlineable3):
+        (dfgWithoutInline3):
+        * inspector-protocol/debugger/setBreakpoint-dfg-callee-and-examine-dfg-local-expected.txt: Added.
+        * inspector-protocol/debugger/setBreakpoint-dfg-callee-and-examine-dfg-local.html: Added.
+
 2014-11-21  Glenn Adams  <glenn@skynav.com> and Myles C. Maxfield  <mmaxfield@apple.com>
 
         CSS3: line-break property support
index cc52c6b..1ce0520 100644 (file)
@@ -91,3 +91,25 @@ function debuggerStatement(x)
     return x + 3;
 }
 
+function notInlineable3(x)
+{
+    var func = new Function("return x + 100;");
+    if (x == 1999)
+        breakpointBasic();
+    return x + 3;
+}
+
+var globalVal3 = 0;
+
+function dfgWithoutInline3()
+{
+    globalVal3 = 0;
+    var i;
+    var result = 0;
+    var localVal3 = 0;
+    for (i = 0; i < 2000; i++)
+        result += notInlineable3(i);
+    if (globalVal3)
+        result = globalVal3 + localVal3;
+    log("result: " + result);
+}
diff --git a/LayoutTests/inspector-protocol/debugger/setBreakpoint-dfg-callee-and-examine-dfg-local-expected.txt b/LayoutTests/inspector-protocol/debugger/setBreakpoint-dfg-callee-and-examine-dfg-local-expected.txt
new file mode 100644 (file)
index 0000000..31125c2
--- /dev/null
@@ -0,0 +1,20 @@
+Debugger.evaluateOnCallFrame in a DFG compiled function from a breakpoint in a non-DFG callee.
+
+Found breakpoint.js
+inside breakpointBasic
+result: 2005000
+dfg function warmed up
+
+Breakpoint set in breakpointBasic()
+Hit Breakpoint!
+Evaluating in DFG frame at frame[2]: 'globalVal3 = 30;'
+Response value is 30
+Evaluating in DFG frame at frame[2]: 'localVal3 = 12;'
+Response value is 12
+Evaluating in DFG frame at frame[2]: 'localVal3'
+Response value is 12
+Resumed from breakpoint
+inside breakpointBasic
+result: 42
+Test complete
+
diff --git a/LayoutTests/inspector-protocol/debugger/setBreakpoint-dfg-callee-and-examine-dfg-local.html b/LayoutTests/inspector-protocol/debugger/setBreakpoint-dfg-callee-and-examine-dfg-local.html
new file mode 100644 (file)
index 0000000..f990a22
--- /dev/null
@@ -0,0 +1,113 @@
+<html>
+<head>
+<script src="../../http/tests/inspector-protocol/resources/protocol-test.js"></script>
+<script src="resources/breakpoint.js"></script>
+
+<script>
+// Put this here instead of on <body onload> to prevent an extra Debugger.scriptParsed event.
+window.onload = runTest;
+
+function test()
+{
+    // This test setting a breakpoints in DFG compiled functions callee and then modify
+    // and examine a global and local via the DFG frame.
+
+    InspectorTest.sendCommand("Debugger.enable", {});
+
+    var breakpointId = null;
+    var scriptId = 0;
+    var startLine = 0;
+
+    InspectorTest.eventHandler["Debugger.scriptParsed"] = function(messageObject)
+    {
+        if (/resources\/breakpoint\.js$/.test(messageObject.params.url)) {
+            InspectorTest.log("Found breakpoint.js");
+            scriptId = messageObject.params.scriptId;
+            startLine = messageObject.params.startLine;
+
+            InspectorTest.sendCommand("Runtime.evaluate", {
+                expression: "dfgWithoutInline3();"
+            }, function(responseObject) {
+                InspectorTest.log("dfg function warmed up\n");
+
+                var location1 = {scriptId: scriptId, lineNumber: 2, columnNumber: 0};
+
+                InspectorTest.sendCommand("Debugger.setBreakpoint", {location: location1}, function(responseObject) {
+                    InspectorTest.checkForError(responseObject);
+                    InspectorTest.log("Breakpoint set in breakpointBasic()");
+
+                    breakpointId = responseObject.result.breakpointId;
+                    InspectorTest.sendCommand("Runtime.evaluate", {
+                        expression: "dfgWithoutInline3();"
+                    }, function(responseObject) {
+                        InspectorTest.log("Test complete");
+                        InspectorTest.completeTest();
+                    });
+                });
+            });
+        }
+    }
+
+    InspectorTest.eventHandler["Debugger.paused"] = function(messageObject)
+    {
+        function dumpResponse(response)
+        {
+            try {
+                if (response.result.wasThrown) {
+                    InspectorTest.log("Exception thrown processing request");
+                    return false;
+                }
+                InspectorTest.log("Response value is " + response.result.result.value);
+                return true;
+            } catch (e) {
+                return false;
+            }
+        }
+
+        function resumeFromBreakpoint()
+        {
+            InspectorTest.sendCommand("Debugger.resume", {}, function(responseObject) {
+                InspectorTest.log("Resumed from breakpoint");
+            });
+        }
+
+        InspectorTest.log("Hit Breakpoint!");
+        var callFrames = messageObject.params.callFrames;
+        if (callFrames.length < 3) {
+            InspectorTest.log("FAIL: too few frames in stack trace");
+            resumeFromBreakpoint();
+            return;
+        }
+        var callFrameId = callFrames[2].callFrameId;
+        InspectorTest.log("Evaluating in DFG frame at frame[2]: 'globalVal3 = 30;'");
+        InspectorTest.sendCommand("Debugger.evaluateOnCallFrame", { callFrameId: callFrameId, expression: "globalVal3 = 30;" }, function(responseObject) {
+            if (!dumpResponse(responseObject)) {
+                resumeFromBreakpoint();
+                return;
+            }
+            InspectorTest.log("Evaluating in DFG frame at frame[2]: 'localVal3 = 12;'");
+            InspectorTest.sendCommand("Debugger.evaluateOnCallFrame", { callFrameId: callFrameId, expression: "localVal3 = 12;" }, function(responseObject) {
+                if (!dumpResponse(responseObject)) {
+                    resumeFromBreakpoint();
+                    return;
+                }
+                InspectorTest.log("Evaluating in DFG frame at frame[2]: 'localVal3'");
+                InspectorTest.sendCommand("Debugger.evaluateOnCallFrame", { callFrameId: callFrameId, expression: "localVal3" }, function(responseObject) {
+                    if (!dumpResponse(responseObject)) {
+                        resumeFromBreakpoint();
+                        return;
+                    }
+                    InspectorTest.sendCommand("Debugger.resume", {}, function(responseObject) {
+                        InspectorTest.log("Resumed from breakpoint");
+                    });
+                });
+            });
+        });
+    }
+}
+</script>
+</head>
+<body>
+<p>Debugger.evaluateOnCallFrame in a DFG compiled function from a breakpoint in a non-DFG callee.</p>
+</body>
+</html>
index 0aff2da..b212dc9 100644 (file)
@@ -1,3 +1,104 @@
+2014-11-21  Michael Saboff  <msaboff@apple.com>
+
+        Allocate local ScopeChain register
+        https://bugs.webkit.org/show_bug.cgi?id=138793
+
+        Reviewed by Geoffrey Garen.
+
+        Now we allocate the scope register as a local.  The allocated register is stored in the 
+        CodeBlock for use by other components.  Update the DFG to work with a local scope register.
+        Changed usage of JSStack::ScopeChain access to the CallFrame header to use the allocated
+        local register.
+
+        * bytecode/BytecodeUseDef.h:
+        (JSC::computeUsesForBytecodeOffset):
+        (JSC::computeDefsForBytecodeOffset):
+        Updated to properly represent the operand inputs and bytecode result.
+
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::CodeBlock):
+        * bytecode/CodeBlock.h:
+        (JSC::CodeBlock::setScopeRegister):
+        (JSC::CodeBlock::scopeRegister):
+        * bytecode/UnlinkedCodeBlock.h:
+        (JSC::UnlinkedCodeBlock::setScopeRegister):
+        (JSC::UnlinkedCodeBlock::scopeRegister):
+        Added scope register member and accessors.
+
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::BytecodeGenerator):
+        (JSC::BytecodeGenerator::allocateAndEmitScope):
+        * bytecompiler/BytecodeGenerator.h:
+        (JSC::BytecodeGenerator::scopeRegister):
+        Change m_scopeRegister to an allocated register.  Added allocateAndEmitScope helper to
+        allocate the scope register, set the CodeBlock with its value and emit op_get_scope.
+
+        * debugger/DebuggerCallFrame.cpp:
+        (JSC::DebuggerCallFrame::scope): Changed to access the scope using the new convention.
+
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::get):
+        (JSC::DFG::ByteCodeParser::flush):
+        (JSC::DFG::ByteCodeParser::inlineCall):
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        Changed op_create_lexical_environment to set the scope VirtualRegister operand.
+        Filled out op_get_scope processing to emit a GetScope node putting the result in
+        the scope VirtualRegister result operand.
+        Added Phantoms where appropriate to keep the Scope register alive in places where
+        it use is optimized away, but where the baseline JIT would need to use its value.
+        Eliminated uses of JSStack::ScopeChain.
+
+        * dfg/DFGStackLayoutPhase.cpp:
+        (JSC::DFG::StackLayoutPhase::run):
+        Make sure that the scope register stack location is allocated using the same place
+        that the codeBlock expects. 
+
+        * dfg/DFGStrengthReductionPhase.cpp:
+        (JSC::DFG::StrengthReductionPhase::handleNode):
+        Allow strength reduction of Flush to skip of GetScope nodes looking for a prior
+        corresponding SetLocal.
+
+        * interpreter/CallFrame.h:
+        (JSC::ExecState::scope):
+        (JSC::ExecState::setScope):
+        Added new scope() and setScope() helpers that take a VirtualRegister offset.
+
+        * interpreter/Interpreter.cpp:
+        (JSC::eval):
+        Changed eval() to get the scope from the caller's scope register instead of from the
+        temporary frame created for eval.
+
+        * interpreter/Interpreter.cpp:
+        (JSC::Interpreter::unwind):
+        Changed unwind() to manipulate the scope n the allocated register instead of from the
+        call frame slot.
+
+        * interpreter/StackVisitor.cpp:
+        (JSC::StackVisitor::readNonInlinedFrame):
+        (JSC::StackVisitor::readInlinedFrame):
+        * interpreter/StackVisitor.h:
+        (JSC::StackVisitor::Frame::callee):
+        (JSC::StackVisitor::Frame::scope): Deleted.
+        Eliminated the scope member as it needed to change and no StackVisitor users use it.
+
+        * jit/JITOperations.cpp:
+        (JSC::operationPushNameScope):
+        (JSC::operationPushWithScope):
+        * runtime/JSNameScope.h:
+        (JSC::JSNameScope::create):
+        * runtime/JSWithScope.h:
+        (JSC::JSWithScope::create): Deleted.
+        * llint/LLIntSlowPaths.cpp:
+        (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+        Deleted JSNameScope::create() and JSWithScope::create() flavors tht used the ScopeChain slot
+        in the CallFrame header.  Changed the only user of these function, op_push_name_scope and
+        op_push_with_scope helpers, to use the remaining create variants that require explicit scope.  
+        Those operations get the scope from the register pointed to by their scope operands.
+
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+        Changed resolveScope to use the allocated register.
+
 2014-11-21  Csaba Osztrogon√°c  <ossy@webkit.org>
 
         [JSC] Disable verifyHeap
index f301158..cd0848c 100644 (file)
@@ -44,10 +44,7 @@ void computeUsesForBytecodeOffset(
     case op_new_array_buffer:
     case op_throw_static_error:
     case op_debug:
-    case op_resolve_scope:
-    case op_pop_scope:
     case op_jneq_ptr:
-    case op_new_func_exp:
     case op_loop_hint:
     case op_jmp:
     case op_new_object:
@@ -57,16 +54,15 @@ void computeUsesForBytecodeOffset(
     case op_catch:
     case op_touch_entry:
         return;
-    case op_new_func:
-    case op_create_lexical_environment: 
+    case op_create_lexical_environment:
     case op_get_scope:
     case op_create_arguments:
     case op_to_this:
+    case op_pop_scope:
     case op_profile_will_call:
     case op_profile_did_call:
     case op_profile_type:
     case op_throw:
-    case op_push_with_scope:
     case op_end:
     case op_ret:
     case op_jtrue:
@@ -78,6 +74,7 @@ void computeUsesForBytecodeOffset(
         functor(codeBlock, instruction, opcodeID, instruction[1].u.operand);
         return;
     }
+    case op_new_func:
     case op_ret_object_or_this:
     case op_jlesseq:
     case op_jgreater:
@@ -117,10 +114,13 @@ void computeUsesForBytecodeOffset(
         return;
     }
     case op_get_enumerable_length:
+    case op_new_func_exp:
     case op_to_index_string:
     case op_init_global_const_nop:
     case op_init_global_const:
     case op_push_name_scope:
+    case op_push_with_scope:
+    case op_resolve_scope:
     case op_get_from_scope:
     case op_to_primitive:
     case op_get_by_id:
@@ -244,10 +244,7 @@ void computeDefsForBytecodeOffset(CodeBlock* codeBlock, unsigned bytecodeOffset,
     // These don't define anything.
     case op_init_global_const:
     case op_init_global_const_nop:
-    case op_push_name_scope:
-    case op_push_with_scope:
     case op_put_to_scope:
-    case op_pop_scope:
     case op_end:
     case op_profile_will_call:
     case op_profile_did_call:
@@ -301,6 +298,9 @@ void computeDefsForBytecodeOffset(CodeBlock* codeBlock, unsigned bytecodeOffset,
     case op_get_direct_pname:
     case op_get_structure_property_enumerator:
     case op_next_enumerator_pname:
+    case op_pop_scope:
+    case op_push_name_scope:
+    case op_push_with_scope:
     case op_resolve_scope:
     case op_strcat:
     case op_to_primitive:
@@ -365,7 +365,6 @@ void computeDefsForBytecodeOffset(CodeBlock* codeBlock, unsigned bytecodeOffset,
     case op_to_this:
     case op_get_callee:
     case op_init_lazy_reg:
-    case op_create_lexical_environment:
     case op_get_scope:
     case op_create_arguments:
     case op_del_by_id:
@@ -374,6 +373,11 @@ void computeDefsForBytecodeOffset(CodeBlock* codeBlock, unsigned bytecodeOffset,
         functor(codeBlock, instruction, opcodeID, instruction[1].u.operand);
         return;
     }
+    case op_create_lexical_environment: {
+        functor(codeBlock, instruction, opcodeID, instruction[1].u.operand);
+        functor(codeBlock, instruction, opcodeID, instruction[2].u.operand);
+        return;
+    }
     case op_enter: {
         for (unsigned i = codeBlock->m_numVars; i--;)
             functor(codeBlock, instruction, opcodeID, virtualRegisterForLocal(i).offset());
index 275ff4a..55693c8 100644 (file)
@@ -1630,6 +1630,7 @@ CodeBlock::CodeBlock(CopyParsedBlockTag, CodeBlock& other)
     , m_vm(other.m_vm)
     , m_instructions(other.m_instructions)
     , m_thisRegister(other.m_thisRegister)
+    , m_scopeRegister(other.m_scopeRegister)
     , m_argumentsRegister(other.m_argumentsRegister)
     , m_lexicalEnvironmentRegister(other.m_lexicalEnvironmentRegister)
     , m_isStrictMode(other.m_isStrictMode)
@@ -1689,6 +1690,7 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
     , m_ownerExecutable(m_globalObject->vm(), ownerExecutable, ownerExecutable)
     , m_vm(unlinkedCodeBlock->vm())
     , m_thisRegister(unlinkedCodeBlock->thisRegister())
+    , m_scopeRegister(unlinkedCodeBlock->scopeRegister())
     , m_argumentsRegister(unlinkedCodeBlock->argumentsRegister())
     , m_lexicalEnvironmentRegister(unlinkedCodeBlock->activationRegister())
     , m_isStrictMode(unlinkedCodeBlock->isStrictMode())
index 18d78a0..85e39fa 100644 (file)
@@ -323,6 +323,17 @@ public:
 
     bool usesEval() const { return m_unlinkedCode->usesEval(); }
 
+    void setScopeRegister(VirtualRegister scopeRegister)
+    {
+        m_scopeRegister = scopeRegister;
+    }
+
+    VirtualRegister scopeRegister() const
+    {
+        ASSERT(m_scopeRegister.isValid());
+        return m_scopeRegister;
+    }
+
     void setArgumentsRegister(VirtualRegister argumentsRegister)
     {
         ASSERT(argumentsRegister.isValid());
@@ -340,6 +351,7 @@ public:
             return VirtualRegister();
         return argumentsRegister();
     }
+
     void setActivationRegister(VirtualRegister activationRegister)
     {
         m_lexicalEnvironmentRegister = activationRegister;
@@ -1032,6 +1044,7 @@ private:
     RefCountedArray<Instruction> m_instructions;
     WriteBarrier<SymbolTable> m_symbolTable;
     VirtualRegister m_thisRegister;
+    VirtualRegister m_scopeRegister;
     VirtualRegister m_argumentsRegister;
     VirtualRegister m_lexicalEnvironmentRegister;
 
index 65f28bd..cd03161 100644 (file)
@@ -282,6 +282,7 @@ public:
 
     // Special registers
     void setThisRegister(VirtualRegister thisRegister) { m_thisRegister = thisRegister; }
+    void setScopeRegister(VirtualRegister scopeRegister) { m_scopeRegister = scopeRegister; }
     void setActivationRegister(VirtualRegister activationRegister) { m_lexicalEnvironmentRegister = activationRegister; }
 
     void setArgumentsRegister(VirtualRegister argumentsRegister) { m_argumentsRegister = argumentsRegister; }
@@ -429,6 +430,7 @@ public:
     CodeType codeType() const { return m_codeType; }
 
     VirtualRegister thisRegister() const { return m_thisRegister; }
+    VirtualRegister scopeRegister() const { return m_scopeRegister; }
     VirtualRegister activationRegister() const { return m_lexicalEnvironmentRegister; }
     bool hasActivationRegister() const { return m_lexicalEnvironmentRegister.isValid(); }
 
@@ -520,6 +522,7 @@ private:
 
     VirtualRegister m_thisRegister;
     VirtualRegister m_argumentsRegister;
+    VirtualRegister m_scopeRegister;
     VirtualRegister m_lexicalEnvironmentRegister;
     VirtualRegister m_globalObjectRegister;
 
index b2134ef..66de2af 100644 (file)
@@ -164,7 +164,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, ProgramNode* programNode, UnlinkedP
     , m_scopeNode(programNode)
     , m_codeBlock(vm, codeBlock)
     , m_thisRegister(CallFrame::thisArgumentOffset())
-    , m_scopeRegister(JSStack::ScopeChain)
+    , m_scopeRegister(0)
     , m_lexicalEnvironmentRegister(0)
     , m_emptyValueRegister(0)
     , m_globalObjectRegister(0)
@@ -190,7 +190,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, ProgramNode* programNode, UnlinkedP
 
     emitOpcode(op_enter);
 
-    emitGetScope();
+    allocateAndEmitScope();
 
     const VarStack& varStack = programNode->varStack();
     const FunctionStack& functionStack = programNode->functionStack();
@@ -212,7 +212,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionBodyNode* functionBody, Unl
     , m_symbolTable(codeBlock->symbolTable())
     , m_scopeNode(functionBody)
     , m_codeBlock(vm, codeBlock)
-    , m_scopeRegister(JSStack::ScopeChain)
+    , m_scopeRegister(0)
     , m_lexicalEnvironmentRegister(0)
     , m_emptyValueRegister(0)
     , m_globalObjectRegister(0)
@@ -251,7 +251,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionBodyNode* functionBody, Unl
 
     emitOpcode(op_enter);
 
-    emitGetScope();
+    allocateAndEmitScope();
 
     if (m_codeBlock->needsFullScopeChain() || m_shouldEmitDebugHooks) {
         m_lexicalEnvironmentRegister = addVar();
@@ -452,7 +452,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, EvalNode* evalNode, UnlinkedEvalCod
     , m_scopeNode(evalNode)
     , m_codeBlock(vm, codeBlock)
     , m_thisRegister(CallFrame::thisArgumentOffset())
-    , m_scopeRegister(JSStack::ScopeChain)
+    , m_scopeRegister(0)
     , m_lexicalEnvironmentRegister(0)
     , m_emptyValueRegister(0)
     , m_globalObjectRegister(0)
@@ -479,7 +479,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, EvalNode* evalNode, UnlinkedEvalCod
 
     emitOpcode(op_enter);
 
-    emitGetScope();
+    allocateAndEmitScope();
 
     const DeclarationStacks::FunctionStack& functionStack = evalNode->functionStack();
     for (size_t i = 0; i < functionStack.size(); ++i)
@@ -2218,6 +2218,14 @@ LabelScopePtr BytecodeGenerator::continueTarget(const Identifier& name)
     return LabelScopePtr::null();
 }
 
+void BytecodeGenerator::allocateAndEmitScope()
+{
+    m_scopeRegister = addVar();
+    m_scopeRegister->ref();
+    m_codeBlock->setScopeRegister(scopeRegister()->virtualRegister());
+    emitGetScope();
+}
+
 void BytecodeGenerator::emitComplexPopScopes(RegisterID* scope, ControlFlowContext* topScope, ControlFlowContext* bottomScope)
 {
     while (topScope > bottomScope) {
index 061410c..89fb2a7 100644 (file)
@@ -290,7 +290,7 @@ namespace JSC {
         // Returns the register storing "this"
         RegisterID* thisRegister() { return &m_thisRegister; }
         
-        RegisterID* scopeRegister() { return &m_scopeRegister; }
+        RegisterID* scopeRegister() { return m_scopeRegister; }
 
         // Returns the next available temporary register. Registers returned by
         // newTemporary require a modified form of reference counting: any
@@ -594,6 +594,7 @@ namespace JSC {
         ALWAYS_INLINE void rewindBinaryOp();
         ALWAYS_INLINE void rewindUnaryOp();
 
+        void allocateAndEmitScope();
         void emitComplexPopScopes(RegisterID*, ControlFlowContext* topScope, ControlFlowContext* bottomScope);
 
         typedef HashMap<double, JSValue> NumberMap;
@@ -754,7 +755,7 @@ namespace JSC {
         RegisterID m_ignoredResultRegister;
         RegisterID m_thisRegister;
         RegisterID m_calleeRegister;
-        RegisterID m_scopeRegister;
+        RegisterID* m_scopeRegister;
         RegisterID* m_lexicalEnvironmentRegister;
         RegisterID* m_emptyValueRegister;
         RegisterID* m_globalObjectRegister;
index d8f5b6b..3a2d849 100644 (file)
@@ -143,15 +143,14 @@ DebuggerScope* DebuggerCallFrame::scope()
 
     if (!m_scope) {
         VM& vm = m_callFrame->vm();
+        JSScope* scope;
         CodeBlock* codeBlock = m_callFrame->codeBlock();
-        if (codeBlock && codeBlock->needsActivation() && !m_callFrame->hasActivation()) {
-            ASSERT(!m_callFrame->scope()->isWithScope());
-            JSLexicalEnvironment* lexicalEnvironment = JSLexicalEnvironment::create(vm, m_callFrame, codeBlock);
-            m_callFrame->setActivation(lexicalEnvironment);
-            m_callFrame->setScope(lexicalEnvironment);
-        }
+        if (codeBlock && codeBlock->scopeRegister().isValid())
+            scope = m_callFrame->scope(codeBlock->scopeRegister().offset());
+        else
+            scope = jsCast<JSCallee*>(m_callFrame->callee())->scope();
 
-        m_scope.set(vm, DebuggerScope::create(vm, m_callFrame->scope()));
+        m_scope.set(vm, DebuggerScope::create(vm, scope));
     }
     return m_scope.get();
 }
index d3d83d0..d8668e0 100644 (file)
@@ -262,13 +262,11 @@ private:
                 JSFunction* callee = inlineCallFrame()->calleeConstant();
                 if (operand.offset() == JSStack::Callee)
                     return weakJSConstant(callee);
-                if (operand.offset() == JSStack::ScopeChain)
+                if (operand == m_inlineStackTop->m_codeBlock->scopeRegister())
                     return weakJSConstant(callee->scope());
             }
         } else if (operand.offset() == JSStack::Callee)
             return addToGraph(GetCallee);
-        else if (operand.offset() == JSStack::ScopeChain)
-            return addToGraph(GetMyScope);
         
         return getDirect(m_inlineStackTop->remapOperand(operand));
     }
@@ -526,10 +524,8 @@ private:
         int numArguments;
         if (InlineCallFrame* inlineCallFrame = inlineStackEntry->m_inlineCallFrame) {
             numArguments = inlineCallFrame->arguments.size();
-            if (inlineCallFrame->isClosureCall) {
+            if (inlineCallFrame->isClosureCall)
                 flushDirect(inlineStackEntry->remapOperand(VirtualRegister(JSStack::Callee)));
-                flushDirect(inlineStackEntry->remapOperand(VirtualRegister(JSStack::ScopeChain)));
-            }
         } else
             numArguments = inlineStackEntry->m_codeBlock->numParameters();
         for (unsigned argument = numArguments; argument-- > 1;)
@@ -1262,11 +1258,8 @@ void ByteCodeParser::inlineCall(Node* callTargetNode, int resultOperand, CallVar
     if (callee.isClosureCall()) {
         VariableAccessData* calleeVariable =
             set(VirtualRegister(JSStack::Callee), callTargetNode, ImmediateNakedSet)->variableAccessData();
-        VariableAccessData* scopeVariable =
-            set(VirtualRegister(JSStack::ScopeChain), addToGraph(GetScope, callTargetNode), ImmediateNakedSet)->variableAccessData();
         
         calleeVariable->mergeShouldNeverUnbox(true);
-        scopeVariable->mergeShouldNeverUnbox(true);
         
         inlineVariableData.calleeVariable = calleeVariable;
     }
@@ -3195,10 +3188,13 @@ bool ByteCodeParser::parseBlock(unsigned limit)
                 if (lexicalEnvironment
                     && lexicalEnvironment->symbolTable()->m_functionEnteredOnce.isStillValid()) {
                     addToGraph(FunctionReentryWatchpoint, OpInfo(lexicalEnvironment->symbolTable()));
+                    addToGraph(Phantom, getDirect(m_inlineStackTop->remapOperand(VirtualRegister(currentInstruction[2].u.operand))));
                     set(VirtualRegister(dst), weakJSConstant(lexicalEnvironment));
                     break;
                 }
                 set(VirtualRegister(dst), getScope(VirtualRegister(currentInstruction[2].u.operand), depth));
+                if (inlineCallFrame())
+                    addToGraph(Phantom, getDirect(m_inlineStackTop->remapOperand(VirtualRegister(currentInstruction[2].u.operand))));
                 break;
             }
             case Dynamic:
@@ -3395,11 +3391,14 @@ bool ByteCodeParser::parseBlock(unsigned limit)
         }
             
         case op_create_lexical_environment: {
-            set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(CreateActivation, get(VirtualRegister(currentInstruction[1].u.operand))));
+            Node* lexicalEnvironment = addToGraph(CreateActivation, get(VirtualRegister(currentInstruction[1].u.operand)));
+            set(VirtualRegister(currentInstruction[1].u.operand), lexicalEnvironment);
+            set(VirtualRegister(currentInstruction[2].u.operand), lexicalEnvironment);
             NEXT_OPCODE(op_create_lexical_environment);
         }
             
         case op_get_scope: {
+            set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(GetScope, get(VirtualRegister(JSStack::Callee))));
             NEXT_OPCODE(op_get_scope);
         }
             
index 8c80f28..6f0612f 100644 (file)
@@ -168,6 +168,11 @@ public:
                 virtualRegisterForLocal(allocation[codeBlock()->activationRegister().toLocal()]));
         }
         
+        if (codeBlock()->scopeRegister().isValid()) {
+            codeBlock()->setScopeRegister(
+                virtualRegisterForLocal(allocation[codeBlock()->scopeRegister().toLocal()]));
+        }
+
         for (unsigned i = m_graph.m_inlineVariableData.size(); i--;) {
             InlineVariableData data = m_graph.m_inlineVariableData[i];
             InlineCallFrame* inlineCallFrame = data.inlineCallFrame;
index 993c7e6..17564e1 100644 (file)
@@ -255,6 +255,7 @@ private:
                     case JSConstant:
                     case DoubleConstant:
                     case Int52Constant:
+                    case GetScope:
                         break;
                 
                     default:
index b80836f..0ba4bba 100644 (file)
@@ -51,6 +51,12 @@ namespace JSC  {
             return this[JSStack::ScopeChain].Register::scope();
         }
 
+        JSScope* scope(int scopeRegisterOffset) const
+        {
+            ASSERT(this[scopeRegisterOffset].Register::scope());
+            return this[scopeRegisterOffset].Register::scope();
+        }
+
         bool hasActivation() const;
         JSLexicalEnvironment* lexicalEnvironment() const;
         JSValue uncheckedActivation() const;
@@ -186,6 +192,7 @@ namespace JSC  {
 
         void setCallerFrame(CallFrame* frame) { callerFrameAndPC().callerFrame = frame; }
         void setScope(JSScope* scope) { static_cast<Register*>(this)[JSStack::ScopeChain] = scope; }
+        void setScope(int scopeRegisterOffset, JSScope* scope) { static_cast<Register*>(this)[scopeRegisterOffset] = scope; }
         void setActivation(JSLexicalEnvironment*);
 
         ALWAYS_INLINE void init(CodeBlock* codeBlock, Instruction* vPC, JSScope* scope,
index ebf39f9..8a857d3 100644 (file)
@@ -105,7 +105,7 @@ JSValue eval(CallFrame* callFrame)
     
     CallFrame* callerFrame = callFrame->callerFrame();
     CodeBlock* callerCodeBlock = callerFrame->codeBlock();
-    JSScope* callerScopeChain = callerFrame->scope();
+    JSScope* callerScopeChain = callerFrame->uncheckedR(callerCodeBlock->scopeRegister().offset()).Register::scope();
     EvalExecutable* eval = callerCodeBlock->evalCodeCache().tryGet(callerCodeBlock->isStrictMode(), programSource, callerScopeChain);
 
     if (!eval) {
@@ -726,13 +726,15 @@ NEVER_INLINE HandlerInfo* Interpreter::unwind(VMEntryFrame*& vmEntryFrame, CallF
     if (codeBlock->needsActivation() && callFrame->hasActivation())
         ++targetScopeDepth;
 
-    JSScope* scope = callFrame->scope();
+    int scopeRegisterOffset = codeBlock->scopeRegister().offset();
+    JSScope* scope = callFrame->scope(scopeRegisterOffset);
     int scopeDelta = scope->depth() - targetScopeDepth;
     RELEASE_ASSERT(scopeDelta >= 0);
 
     while (scopeDelta--)
         scope = scope->next();
-    callFrame->setScope(scope);
+
+    callFrame->setScope(scopeRegisterOffset, scope);
 
     return handler;
 }
index 942f924..6807b5a 100644 (file)
@@ -121,7 +121,6 @@ void StackVisitor::readNonInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOri
     m_frame.m_callerFrame = callFrame->callerFrame(m_frame.m_CallerVMEntryFrame);
     m_frame.m_callerIsVMEntryFrame = m_frame.m_CallerVMEntryFrame != m_frame.m_VMEntryFrame;
     m_frame.m_callee = callFrame->callee();
-    m_frame.m_scope = callFrame->scope();
     m_frame.m_codeBlock = callFrame->codeBlock();
     m_frame.m_bytecodeOffset = !m_frame.codeBlock() ? 0
         : codeOrigin ? codeOrigin->bytecodeIndex
@@ -155,9 +154,7 @@ void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin
         m_frame.m_bytecodeOffset = codeOrigin->bytecodeIndex;
 
         JSFunction* callee = inlineCallFrame->calleeForCallFrame(callFrame);
-        m_frame.m_scope = callee->scope();
         m_frame.m_callee = callee;
-        ASSERT(m_frame.scope());
         ASSERT(m_frame.callee());
 
         // The callerFrame just needs to be non-null to indicate that we
index c6e7fdf..749540f 100644 (file)
@@ -60,7 +60,6 @@ public:
         bool callerIsVMEntryFrame() const { return m_callerIsVMEntryFrame; }
         CallFrame* callerFrame() const { return m_callerFrame; }
         JSObject* callee() const { return m_callee; }
-        JSScope* scope() const { return m_scope; }
         CodeBlock* codeBlock() const { return m_codeBlock; }
         unsigned bytecodeOffset() const { return m_bytecodeOffset; }
 #if ENABLE(DFG_JIT)
@@ -101,7 +100,6 @@ public:
         VMEntryFrame* m_CallerVMEntryFrame;
         CallFrame* m_callerFrame;
         JSObject* m_callee;
-        JSScope* m_scope;
         CodeBlock* m_codeBlock;
         unsigned m_bytecodeOffset;
         bool m_callerIsVMEntryFrame;
index 6c92a44..da47df3 100644 (file)
@@ -1313,9 +1313,14 @@ void JIT_OPERATION operationPushNameScope(ExecState* exec, int32_t dst, Identifi
     VM& vm = exec->vm();
     NativeCallFrameTracer tracer(&vm, exec);
 
+    // FIXME: This won't work if this operation is called from the DFG or FTL.
+    // This should be changed to pass in the new scope.
+    JSScope* currentScope = exec->uncheckedR(dst).Register::scope();
     JSNameScope::Type scopeType = static_cast<JSNameScope::Type>(type);
-    JSNameScope* scope = JSNameScope::create(exec, *identifier, JSValue::decode(encodedValue), attibutes, scopeType);
+    JSNameScope* scope = JSNameScope::create(exec, currentScope, *identifier, JSValue::decode(encodedValue), attibutes, scopeType);
 
+    // FIXME: This won't work if this operation is called from the DFG or FTL.
+    // This should be changed to return the new scope.
     exec->uncheckedR(dst) = scope;
 }
 
@@ -1340,7 +1345,10 @@ void JIT_OPERATION operationPushWithScope(ExecState* exec, int32_t dst, EncodedJ
     if (vm.exception())
         return;
 
-    exec->uncheckedR(dst) = JSWithScope::create(exec, o);
+    // FIXME: This won't work if this operation is called from the DFG or FTL.
+    // This should be changed to pass in the old scope and return the new scope.
+    JSScope* currentScope = exec->uncheckedR(dst).Register::scope();
+    exec->uncheckedR(dst) = JSWithScope::create(exec, o, currentScope);
 }
 
 void JIT_OPERATION operationPopScope(ExecState* exec, int32_t scopeReg)
index 66942e2..7e64056 100644 (file)
@@ -1235,7 +1235,8 @@ LLINT_SLOW_PATH_DECL(slow_path_call_eval)
     execCallee->setArgumentCountIncludingThis(pc[3].u.operand);
     execCallee->setCallerFrame(exec);
     execCallee->uncheckedR(JSStack::Callee) = calleeAsValue;
-    execCallee->setScope(exec->scope());
+    JSScope* callerScope = exec->uncheckedR(exec->codeBlock()->scopeRegister().offset()).Register::scope();
+    execCallee->setScope(callerScope);
     execCallee->setReturnPC(LLInt::getCodePtr(llint_generic_return_point));
     execCallee->setCodeBlock(0);
     exec->setCurrentVPC(pc);
@@ -1275,7 +1276,9 @@ LLINT_SLOW_PATH_DECL(slow_path_push_with_scope)
     JSObject* o = v.toObject(exec);
     LLINT_CHECK_EXCEPTION();
 
-    exec->uncheckedR(pc[1].u.operand) = JSWithScope::create(exec, o);
+    int scopeReg = pc[1].u.operand;
+    JSScope* currentScope = exec->uncheckedR(scopeReg).Register::scope();
+    exec->uncheckedR(scopeReg) = JSWithScope::create(exec, o, currentScope);
     
     LLINT_END();
 }
@@ -1293,9 +1296,11 @@ LLINT_SLOW_PATH_DECL(slow_path_push_name_scope)
 {
     LLINT_BEGIN();
     CodeBlock* codeBlock = exec->codeBlock();
+    int scopeReg = pc[1].u.operand;
+    JSScope* currentScope = exec->uncheckedR(scopeReg).Register::scope();
     JSNameScope::Type type = static_cast<JSNameScope::Type>(pc[5].u.operand);
-    JSNameScope* scope = JSNameScope::create(exec, codeBlock->identifier(pc[2].u.operand), LLINT_OP(3).jsValue(), pc[4].u.operand, type);
-    exec->uncheckedR(pc[1].u.operand) = scope;
+    JSNameScope* scope = JSNameScope::create(exec, currentScope, codeBlock->identifier(pc[2].u.operand), LLINT_OP(3).jsValue(), pc[4].u.operand, type);
+    exec->uncheckedR(scopeReg) = scope;
     LLINT_END();
 }
 
index f5a84e4..4632353 100644 (file)
@@ -2171,7 +2171,8 @@ macro resolveScope()
     loadp CodeBlock[cfr], t0
     loadisFromInstruction(5, t2)
 
-    loadp ScopeChain + PayloadOffset[cfr], t0
+    loadisFromInstruction(2, t0)
+    loadp PayloadOffset[cfr, t0, 8], t0
     btiz t2, .resolveScopeLoopEnd
 
 .resolveScopeLoop:
index c4f294d..5db9dfa 100644 (file)
@@ -2000,9 +2000,9 @@ macro varInjectionCheck(slowPath)
 end
 
 macro resolveScope()
-    loadp CodeBlock[cfr], t0
     loadisFromInstruction(5, t2)
-    loadp ScopeChain[cfr], t0
+    loadisFromInstruction(2, t0)
+    loadp [cfr, t0, 8], t0
     btiz t2, .resolveScopeLoopEnd
 
 .resolveScopeLoop:
index 09d1d9b..7a7ba8e 100644 (file)
@@ -41,10 +41,10 @@ public:
         FunctionNameScope
     };
 
-    static JSNameScope* create(ExecState* exec, const Identifier& identifier, JSValue value, unsigned attributes, Type type)
+    static JSNameScope* create(ExecState* exec, JSScope* currentScope, const Identifier& identifier, JSValue value, unsigned attributes, Type type)
     {
         VM& vm = exec->vm();
-        JSNameScope* scopeObject = new (NotNull, allocateCell<JSNameScope>(vm.heap)) JSNameScope(vm, exec->lexicalGlobalObject(), exec->scope(), type);
+        JSNameScope* scopeObject = new (NotNull, allocateCell<JSNameScope>(vm.heap)) JSNameScope(vm, exec->lexicalGlobalObject(), currentScope, type);
         scopeObject->finishCreation(vm, identifier, value, attributes);
         return scopeObject;
     }
index 310de40..fc3962d 100644 (file)
@@ -34,13 +34,6 @@ class JSWithScope : public JSScope {
 public:
     typedef JSScope Base;
 
-    static JSWithScope* create(ExecState* exec, JSObject* object)
-    {
-        JSWithScope* withScope = new (NotNull, allocateCell<JSWithScope>(*exec->heap())) JSWithScope(exec, object);
-        withScope->finishCreation(exec->vm());
-        return withScope;
-    }
-
     static JSWithScope* create(ExecState* exec, JSObject* object, JSScope* next)
     {
         JSWithScope* withScope = new (NotNull, allocateCell<JSWithScope>(*exec->heap())) JSWithScope(exec, object, next);