Title: [203364] trunk
Revision
203364
Author
fpi...@apple.com
Date
2016-07-18 12:32:34 -0700 (Mon, 18 Jul 2016)

Log Message

DFG and FTL should support op_call_eval
https://bugs.webkit.org/show_bug.cgi?id=159786

Reviewed by Saam Barati.
Source/_javascript_Core:

        
This adds support for op_call_eval in DFG and FTL by brute force:
        
- There is now a CallEval() node type, which compiles exactly the same way that we do in
  baseline.
        
- We teach the DFG and bytecode liveness that the scope register and 'this' are read by
  CallEval()/op_call_eval.
        
We can compile eval quite well, except that right now we cannot inline functions that use
eval. It would be nice to do that, but the payoff is probably smaller. "Don't inline users
of eval" may even be an OK inlining heuristic. Not inlining users of eval allows me to
reuse the baseline implementation, which is really great. Otherwise, I'd have to get rid
of things like the rogue reads of scope register and 'this'.
        
The goal here is to produce speed-ups for code that has functions that do both eval and
some computational stuff. Obviously, we're not producing any benefit for the eval itself.
But now the other stuff in a function that uses eval will get to participate in
optimization.
        
This is a huge speed-up on microbenchmarks.

* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::printCallOp):
(JSC::CodeBlock::dumpBytecode):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::setLocal):
(JSC::DFG::ByteCodeParser::setArgument):
(JSC::DFG::ByteCodeParser::flush):
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGGraph.h:
(JSC::DFG::Graph::needsScopeRegister):
(JSC::DFG::Graph::needsFlushedThis):
* dfg/DFGHeapLocation.cpp:
(WTF::printInternal):
* dfg/DFGHeapLocation.h:
* dfg/DFGMayExit.cpp:
* dfg/DFGNode.h:
(JSC::DFG::Node::hasHeapPrediction):
* dfg/DFGNodeType.h:
* dfg/DFGOSRExitCompiler.cpp:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::emitCall):
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::emitCall):
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGStackLayoutPhase.cpp:
(JSC::DFG::StackLayoutPhase::run):
* dfg/DFGWatchpointCollectionPhase.cpp:
(JSC::DFG::WatchpointCollectionPhase::handle):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLCompile.cpp:
(JSC::FTL::compile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileCallOrConstructVarargs):
(JSC::FTL::DFG::LowerDFGToB3::compileCallEval):
(JSC::FTL::DFG::LowerDFGToB3::compileLoadVarargs):
* jit/AssemblyHelpers.cpp:
(JSC::AssemblyHelpers::restoreCalleeSavesFromVMEntryFrameCalleeSavesBuffer):
(JSC::AssemblyHelpers::emitDumbVirtualCall):
* jit/AssemblyHelpers.h:
(JSC::AssemblyHelpers::emitTypeOf):
* jit/JITCall.cpp:
(JSC::JIT::compileCallEvalSlowCase):
* jit/JITCall32_64.cpp:
(JSC::JIT::compileCallEvalSlowCase):
* jit/JITOperations.cpp:
* tests/stress/exit-then-eval.js: Added.
(foo):
* tests/stress/force-exit-then-eval-dfg.js: Added.
(foo):
* tests/stress/force-exit-then-eval.js: Added.
(foo):

LayoutTests:


* js/regress/eval-compute-expected.txt: Added.
* js/regress/eval-compute.html: Added.
* js/regress/eval-not-eval-compute-args-expected.txt: Added.
* js/regress/eval-not-eval-compute-args.html: Added.
* js/regress/eval-not-eval-compute-expected.txt: Added.
* js/regress/eval-not-eval-compute.html: Added.
* js/regress/script-tests/eval-compute.js: Added.
(foo):
* js/regress/script-tests/eval-not-eval-compute-args.js: Added.
(foo):
(i.result.foo):
* js/regress/script-tests/eval-not-eval-compute.js: Added.
(foo):
(i.result.foo):

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (203363 => 203364)


--- trunk/LayoutTests/ChangeLog	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/LayoutTests/ChangeLog	2016-07-18 19:32:34 UTC (rev 203364)
@@ -1,3 +1,25 @@
+2016-07-15  Filip Pizlo  <fpi...@apple.com>
+
+        DFG and FTL should support op_call_eval
+        https://bugs.webkit.org/show_bug.cgi?id=159786
+
+        Reviewed by Saam Barati.
+
+        * js/regress/eval-compute-expected.txt: Added.
+        * js/regress/eval-compute.html: Added.
+        * js/regress/eval-not-eval-compute-args-expected.txt: Added.
+        * js/regress/eval-not-eval-compute-args.html: Added.
+        * js/regress/eval-not-eval-compute-expected.txt: Added.
+        * js/regress/eval-not-eval-compute.html: Added.
+        * js/regress/script-tests/eval-compute.js: Added.
+        (foo):
+        * js/regress/script-tests/eval-not-eval-compute-args.js: Added.
+        (foo):
+        (i.result.foo):
+        * js/regress/script-tests/eval-not-eval-compute.js: Added.
+        (foo):
+        (i.result.foo):
+
 2016-07-12  Filip Pizlo  <fpi...@apple.com>
 
         DFG should really support jneq_ptr

Added: trunk/LayoutTests/js/regress/eval-compute-expected.txt (0 => 203364)


--- trunk/LayoutTests/js/regress/eval-compute-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/js/regress/eval-compute-expected.txt	2016-07-18 19:32:34 UTC (rev 203364)
@@ -0,0 +1,10 @@
+JSRegress/eval-compute
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/js/regress/eval-compute.html (0 => 203364)


--- trunk/LayoutTests/js/regress/eval-compute.html	                        (rev 0)
+++ trunk/LayoutTests/js/regress/eval-compute.html	2016-07-18 19:32:34 UTC (rev 203364)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/js/regress/eval-not-eval-compute-args-expected.txt (0 => 203364)


--- trunk/LayoutTests/js/regress/eval-not-eval-compute-args-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/js/regress/eval-not-eval-compute-args-expected.txt	2016-07-18 19:32:34 UTC (rev 203364)
@@ -0,0 +1,10 @@
+JSRegress/eval-not-eval-compute-args
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/js/regress/eval-not-eval-compute-args.html (0 => 203364)


--- trunk/LayoutTests/js/regress/eval-not-eval-compute-args.html	                        (rev 0)
+++ trunk/LayoutTests/js/regress/eval-not-eval-compute-args.html	2016-07-18 19:32:34 UTC (rev 203364)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/js/regress/eval-not-eval-compute-expected.txt (0 => 203364)


--- trunk/LayoutTests/js/regress/eval-not-eval-compute-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/js/regress/eval-not-eval-compute-expected.txt	2016-07-18 19:32:34 UTC (rev 203364)
@@ -0,0 +1,10 @@
+JSRegress/eval-not-eval-compute
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/js/regress/eval-not-eval-compute.html (0 => 203364)


--- trunk/LayoutTests/js/regress/eval-not-eval-compute.html	                        (rev 0)
+++ trunk/LayoutTests/js/regress/eval-not-eval-compute.html	2016-07-18 19:32:34 UTC (rev 203364)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/js/regress/script-tests/eval-compute.js (0 => 203364)


--- trunk/LayoutTests/js/regress/script-tests/eval-compute.js	                        (rev 0)
+++ trunk/LayoutTests/js/regress/script-tests/eval-compute.js	2016-07-18 19:32:34 UTC (rev 203364)
@@ -0,0 +1,16 @@
+function foo(string) {
+    var x = 42;
+    var result = eval(string);
+    for (var i = 0; i < 100000; ++i)
+        result++;
+    return result;
+}
+
+noInline(foo);
+
+for (var i = 0; i < 200; ++i) {
+    var result = foo("x + " + i);
+    if (result != 42 + i + 100000)
+        throw "Error: bad result: " + result;
+}
+

Added: trunk/LayoutTests/js/regress/script-tests/eval-not-eval-compute-args.js (0 => 203364)


--- trunk/LayoutTests/js/regress/script-tests/eval-not-eval-compute-args.js	                        (rev 0)
+++ trunk/LayoutTests/js/regress/script-tests/eval-not-eval-compute-args.js	2016-07-18 19:32:34 UTC (rev 203364)
@@ -0,0 +1,16 @@
+function foo(eval, a, b, c) {
+    var x = 42;
+    var result = eval(a, b, c);
+    for (var i = 0; i < 100000; ++i)
+        result++;
+    return result;
+}
+
+noInline(foo);
+
+for (var i = 0; i < 200; ++i) {
+    var result = foo(function(a, b, c) { return a + b + c; }, 42, i, 0);
+    if (result != 42 + i + 100000)
+        throw "Error: bad result: " + result;
+}
+

Added: trunk/LayoutTests/js/regress/script-tests/eval-not-eval-compute.js (0 => 203364)


--- trunk/LayoutTests/js/regress/script-tests/eval-not-eval-compute.js	                        (rev 0)
+++ trunk/LayoutTests/js/regress/script-tests/eval-not-eval-compute.js	2016-07-18 19:32:34 UTC (rev 203364)
@@ -0,0 +1,16 @@
+function foo(eval, string) {
+    var x = 42;
+    var result = eval(string);
+    for (var i = 0; i < 100000; ++i)
+        result++;
+    return result;
+}
+
+noInline(foo);
+
+for (var i = 0; i < 200; ++i) {
+    var result = foo(function() { return 42 + i; }, "x + " + i);
+    if (result != 42 + i + 100000)
+        throw "Error: bad result: " + result;
+}
+

Modified: trunk/Source/_javascript_Core/ChangeLog (203363 => 203364)


--- trunk/Source/_javascript_Core/ChangeLog	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-07-18 19:32:34 UTC (rev 203364)
@@ -1,3 +1,101 @@
+2016-07-15  Filip Pizlo  <fpi...@apple.com>
+
+        DFG and FTL should support op_call_eval
+        https://bugs.webkit.org/show_bug.cgi?id=159786
+
+        Reviewed by Saam Barati.
+        
+        This adds support for op_call_eval in DFG and FTL by brute force:
+        
+        - There is now a CallEval() node type, which compiles exactly the same way that we do in
+          baseline.
+        
+        - We teach the DFG and bytecode liveness that the scope register and 'this' are read by
+          CallEval()/op_call_eval.
+        
+        We can compile eval quite well, except that right now we cannot inline functions that use
+        eval. It would be nice to do that, but the payoff is probably smaller. "Don't inline users
+        of eval" may even be an OK inlining heuristic. Not inlining users of eval allows me to
+        reuse the baseline implementation, which is really great. Otherwise, I'd have to get rid
+        of things like the rogue reads of scope register and 'this'.
+        
+        The goal here is to produce speed-ups for code that has functions that do both eval and
+        some computational stuff. Obviously, we're not producing any benefit for the eval itself.
+        But now the other stuff in a function that uses eval will get to participate in
+        optimization.
+        
+        This is a huge speed-up on microbenchmarks.
+
+        * bytecode/BytecodeUseDef.h:
+        (JSC::computeUsesForBytecodeOffset):
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::printCallOp):
+        (JSC::CodeBlock::dumpBytecode):
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::setLocal):
+        (JSC::DFG::ByteCodeParser::setArgument):
+        (JSC::DFG::ByteCodeParser::flush):
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGCapabilities.cpp:
+        (JSC::DFG::capabilityLevel):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGGraph.h:
+        (JSC::DFG::Graph::needsScopeRegister):
+        (JSC::DFG::Graph::needsFlushedThis):
+        * dfg/DFGHeapLocation.cpp:
+        (WTF::printInternal):
+        * dfg/DFGHeapLocation.h:
+        * dfg/DFGMayExit.cpp:
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::hasHeapPrediction):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOSRExitCompiler.cpp:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::emitCall):
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::emitCall):
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGStackLayoutPhase.cpp:
+        (JSC::DFG::StackLayoutPhase::run):
+        * dfg/DFGWatchpointCollectionPhase.cpp:
+        (JSC::DFG::WatchpointCollectionPhase::handle):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLCompile.cpp:
+        (JSC::FTL::compile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCallOrConstructVarargs):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCallEval):
+        (JSC::FTL::DFG::LowerDFGToB3::compileLoadVarargs):
+        * jit/AssemblyHelpers.cpp:
+        (JSC::AssemblyHelpers::restoreCalleeSavesFromVMEntryFrameCalleeSavesBuffer):
+        (JSC::AssemblyHelpers::emitDumbVirtualCall):
+        * jit/AssemblyHelpers.h:
+        (JSC::AssemblyHelpers::emitTypeOf):
+        * jit/JITCall.cpp:
+        (JSC::JIT::compileCallEvalSlowCase):
+        * jit/JITCall32_64.cpp:
+        (JSC::JIT::compileCallEvalSlowCase):
+        * jit/JITOperations.cpp:
+        * tests/stress/exit-then-eval.js: Added.
+        (foo):
+        * tests/stress/force-exit-then-eval-dfg.js: Added.
+        (foo):
+        * tests/stress/force-exit-then-eval.js: Added.
+        (foo):
+
 2016-07-12  Filip Pizlo  <fpi...@apple.com>
 
         DFG should really support jneq_ptr

Modified: trunk/Source/_javascript_Core/bytecode/BytecodeUseDef.h (203363 => 203364)


--- trunk/Source/_javascript_Core/bytecode/BytecodeUseDef.h	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/bytecode/BytecodeUseDef.h	2016-07-18 19:32:34 UTC (rev 203364)
@@ -282,6 +282,8 @@
         int lastArg = registerOffset + CallFrame::thisArgumentOffset();
         for (int i = 0; i < argCount; i++)
             functor(codeBlock, instruction, opcodeID, lastArg + i);
+        if (opcodeID == op_call_eval)
+            functor(codeBlock, instruction, opcodeID, codeBlock->scopeRegister().offset());
         return;
     }
     case op_save: {

Modified: trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp (203363 => 203364)


--- trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp	2016-07-18 19:32:34 UTC (rev 203364)
@@ -529,7 +529,8 @@
     int argCount = (++it)->u.operand;
     int registerOffset = (++it)->u.operand;
     printLocationAndOp(out, exec, location, it, op);
-    out.printf("%s, %s, %d, %d", registerName(dst).data(), registerName(func).data(), argCount, registerOffset);
+    out.print(registerName(dst), ", ", registerName(func), ", ", argCount, ", ", registerOffset);
+    out.print(" (this at ", virtualRegisterForArgument(0, -registerOffset), ")");
     if (cacheDumpMode == DumpCaches) {
         LLIntCallLinkInfo* callLinkInfo = it[1].u.callLinkInfo;
         if (callLinkInfo->lastSeenCallee) {
@@ -609,6 +610,7 @@
         static_cast<unsigned long>(instructions().size()),
         static_cast<unsigned long>(instructions().size() * sizeof(Instruction)),
         m_numParameters, m_numCalleeLocals, m_numVars);
+    out.print("; scope at ", scopeRegister());
     out.printf("\n");
     
     StubInfoMap stubInfos;

Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (203363 => 203364)


--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2016-07-18 19:32:34 UTC (rev 203364)
@@ -2801,7 +2801,6 @@
         forNode(node).setType(m_graph, SpecObject);
         break;
         
-    case VarInjectionWatchpoint:
     case PutGlobalVariable:
     case NotifyWrite:
         break;
@@ -2847,6 +2846,7 @@
     case ConstructVarargs:
     case ConstructForwardVarargs:
     case TailCallForwardVarargsInlinedCaller:
+    case CallEval:
         clobberWorld(node->origin.semantic, clobberLimit);
         forNode(node).makeHeapTop();
         break;

Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (203363 => 203364)


--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2016-07-18 19:32:34 UTC (rev 203364)
@@ -455,7 +455,7 @@
             ArgumentPosition* argumentPosition = findArgumentPositionForLocal(operand);
             if (argumentPosition)
                 flushDirect(operand, argumentPosition);
-            else if (m_hasDebuggerEnabled && operand == m_codeBlock->scopeRegister())
+            else if (m_graph.needsScopeRegister() && operand == m_codeBlock->scopeRegister())
                 flush(operand);
         }
 
@@ -511,10 +511,12 @@
 
         // Always flush arguments, except for 'this'. If 'this' is created by us,
         // then make sure that it's never unboxed.
-        if (argument) {
+        if (argument || m_graph.needsFlushedThis()) {
             if (setMode != ImmediateNakedSet)
                 flushDirect(operand);
-        } else if (m_codeBlock->specializationKind() == CodeForConstruct)
+        }
+        
+        if (!argument && m_codeBlock->specializationKind() == CodeForConstruct)
             variableAccessData->mergeShouldNeverUnbox(true);
         
         variableAccessData->mergeStructureCheckHoistingFailed(
@@ -604,7 +606,9 @@
             numArguments = inlineStackEntry->m_codeBlock->numParameters();
         for (unsigned argument = numArguments; argument-- > 1;)
             flushDirect(inlineStackEntry->remapOperand(virtualRegisterForArgument(argument)));
-        if (m_hasDebuggerEnabled)
+        if (!inlineStackEntry->m_inlineCallFrame && m_graph.needsFlushedThis())
+            flushDirect(virtualRegisterForArgument(0));
+        if (m_graph.needsScopeRegister())
             flush(m_codeBlock->scopeRegister());
     }
 
@@ -4564,6 +4568,15 @@
             NEXT_OPCODE(op_construct_varargs);
         }
             
+        case op_call_eval: {
+            int result = currentInstruction[1].u.operand;
+            int callee = currentInstruction[2].u.operand;
+            int argumentCountIncludingThis = currentInstruction[3].u.operand;
+            int registerOffset = -currentInstruction[4].u.operand;
+            addCall(result, CallEval, OpInfo(), get(VirtualRegister(callee)), argumentCountIncludingThis, registerOffset, getPrediction());
+            NEXT_OPCODE(op_call_eval);
+        }
+            
         case op_jneq_ptr: {
             Special::Pointer specialPointer = currentInstruction[2].u.specialPointer;
             ASSERT(pointerIsCell(specialPointer));
@@ -4596,7 +4609,7 @@
 
             // get_from_scope and put_to_scope depend on this watchpoint forcing OSR exit, so they don't add their own watchpoints.
             if (needsVarInjectionChecks(resolveType))
-                addToGraph(VarInjectionWatchpoint);
+                m_graph.watchpoints().addLazily(m_inlineStackTop->m_codeBlock->globalObject()->varInjectionWatchpoint());
 
             switch (resolveType) {
             case GlobalProperty:

Modified: trunk/Source/_javascript_Core/dfg/DFGCapabilities.cpp (203363 => 203364)


--- trunk/Source/_javascript_Core/dfg/DFGCapabilities.cpp	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/dfg/DFGCapabilities.cpp	2016-07-18 19:32:34 UTC (rev 203364)
@@ -244,6 +244,7 @@
 
     case op_new_regexp:
     case op_switch_string: // Don't inline because we don't want to copy string tables in the concurrent JIT.
+    case op_call_eval:
         return CanCompile;
 
     default:

Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (203363 => 203364)


--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2016-07-18 19:32:34 UTC (rev 203364)
@@ -431,11 +431,6 @@
         write(HeapObjectCount);
         return;
 
-    case VarInjectionWatchpoint:
-        read(MiscFields);
-        def(HeapLocation(VarInjectionWatchpointLoc, MiscFields), LazyNode(node));
-        return;
-
     case IsObjectOrNull:
         read(MiscFields);
         def(HeapLocation(IsObjectOrNullLoc, MiscFields, node->child1()), LazyNode(node));
@@ -484,6 +479,14 @@
         write(Heap);
         return;
 
+    case CallEval:
+        ASSERT(!node->origin.semantic.inlineCallFrame);
+        read(AbstractHeap(Stack, graph.m_codeBlock->scopeRegister()));
+        read(AbstractHeap(Stack, virtualRegisterForArgument(0)));
+        read(World);
+        write(Heap);
+        return;
+
     case TailCall:
     case TailCallVarargs:
     case TailCallForwardVarargs:

Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (203363 => 203364)


--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2016-07-18 19:32:34 UTC (rev 203364)
@@ -127,7 +127,6 @@
     case GetGlobalVar:
     case GetGlobalLexicalVariable:
     case PutGlobalVariable:
-    case VarInjectionWatchpoint:
     case CheckCell:
     case CheckNotEmpty:
     case CheckIdent:
@@ -144,6 +143,7 @@
     case TailCallInlinedCaller:
     case Construct:
     case CallVarargs:
+    case CallEval:
     case TailCallVarargsInlinedCaller:
     case ConstructVarargs:
     case LoadVarargs:

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (203363 => 203364)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2016-07-18 19:32:34 UTC (rev 203364)
@@ -1535,12 +1535,12 @@
         case GetGlobalVar:
         case GetGlobalLexicalVariable:
         case NotifyWrite:
-        case VarInjectionWatchpoint:
         case Call:
         case CheckTypeInfoFlags:
         case TailCallInlinedCaller:
         case Construct:
         case CallVarargs:
+        case CallEval:
         case TailCallVarargsInlinedCaller:
         case ConstructVarargs:
         case CallForwardVarargs:

Modified: trunk/Source/_javascript_Core/dfg/DFGGraph.h (203363 => 203364)


--- trunk/Source/_javascript_Core/dfg/DFGGraph.h	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/dfg/DFGGraph.h	2016-07-18 19:32:34 UTC (rev 203364)
@@ -813,6 +813,9 @@
     // because it queries the m_hasExceptionHandlers boolean whose value
     // is only fully determined after bytcode parsing.
     bool willCatchExceptionInMachineFrame(CodeOrigin, CodeOrigin& opCatchOriginOut, HandlerInfo*& catchHandlerOut);
+    
+    bool needsScopeRegister() const { return m_hasDebuggerEnabled || m_codeBlock->usesEval(); }
+    bool needsFlushedThis() const { return m_codeBlock->usesEval(); }
 
     VM& m_vm;
     Plan& m_plan;

Modified: trunk/Source/_javascript_Core/dfg/DFGHeapLocation.cpp (203363 => 203364)


--- trunk/Source/_javascript_Core/dfg/DFGHeapLocation.cpp	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/dfg/DFGHeapLocation.cpp	2016-07-18 19:32:34 UTC (rev 203364)
@@ -136,10 +136,6 @@
         out.print("TypedArrayByteOffsetLoc");
         return;
         
-    case VarInjectionWatchpointLoc:
-        out.print("VarInjectionWatchpointLoc");
-        return;
-        
     case StructureLoc:
         out.print("StructureLoc");
         return;

Modified: trunk/Source/_javascript_Core/dfg/DFGHeapLocation.h (203363 => 203364)


--- trunk/Source/_javascript_Core/dfg/DFGHeapLocation.h	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/dfg/DFGHeapLocation.h	2016-07-18 19:32:34 UTC (rev 203364)
@@ -57,7 +57,6 @@
     SetterLoc,
     StructureLoc,
     TypedArrayByteOffsetLoc,
-    VarInjectionWatchpointLoc,
     StackLoc,
     StackPayloadLoc
 };

Modified: trunk/Source/_javascript_Core/dfg/DFGMayExit.cpp (203363 => 203364)


--- trunk/Source/_javascript_Core/dfg/DFGMayExit.cpp	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/dfg/DFGMayExit.cpp	2016-07-18 19:32:34 UTC (rev 203364)
@@ -96,6 +96,7 @@
     case Call:
     case Construct:
     case CallVarargs:
+    case CallEval:
     case ConstructVarargs:
     case CallForwardVarargs:
     case ConstructForwardVarargs:

Modified: trunk/Source/_javascript_Core/dfg/DFGNode.h (203363 => 203364)


--- trunk/Source/_javascript_Core/dfg/DFGNode.h	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.h	2016-07-18 19:32:34 UTC (rev 203364)
@@ -1423,6 +1423,7 @@
         case TailCallInlinedCaller:
         case Construct:
         case CallVarargs:
+        case CallEval:
         case TailCallVarargsInlinedCaller:
         case ConstructVarargs:
         case CallForwardVarargs:

Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (203363 => 203364)


--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2016-07-18 19:32:34 UTC (rev 203364)
@@ -232,7 +232,6 @@
     macro(GetDynamicVar, NodeResultJS | NodeMustGenerate) \
     macro(PutDynamicVar, NodeMustGenerate) \
     macro(NotifyWrite, NodeMustGenerate) \
-    macro(VarInjectionWatchpoint, NodeMustGenerate) \
     macro(GetRegExpObjectLastIndex, NodeResultJS) \
     macro(SetRegExpObjectLastIndex, NodeMustGenerate) \
     macro(RecordRegExpCachedResult, NodeMustGenerate | NodeHasVarArgs) \
@@ -277,6 +276,7 @@
     macro(TailCallInlinedCaller, NodeResultJS | NodeMustGenerate | NodeHasVarArgs) \
     macro(TailCallVarargsInlinedCaller, NodeResultJS | NodeMustGenerate) \
     macro(TailCallForwardVarargsInlinedCaller, NodeResultJS | NodeMustGenerate) \
+    macro(CallEval, NodeResultJS | NodeMustGenerate | NodeHasVarArgs) \
     \
     /* Shadow Chicken */\
     macro(LogShadowChickenPrologue, NodeMustGenerate) \

Modified: trunk/Source/_javascript_Core/dfg/DFGOSRExitCompiler.cpp (203363 => 203364)


--- trunk/Source/_javascript_Core/dfg/DFGOSRExitCompiler.cpp	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/dfg/DFGOSRExitCompiler.cpp	2016-07-18 19:32:34 UTC (rev 203364)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2013, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-2013, 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -153,9 +153,10 @@
             // So, we must restore our call frame and stack pointer.
             jit.restoreCalleeSavesFromVMEntryFrameCalleeSavesBuffer();
             jit.loadPtr(vm->addressOfCallFrameForCatch(), GPRInfo::callFrameRegister);
-            jit.addPtr(CCallHelpers::TrustedImm32(codeBlock->stackPointerOffset() * sizeof(Register)),
-                GPRInfo::callFrameRegister, CCallHelpers::stackPointerRegister);
         }
+        jit.addPtr(
+            CCallHelpers::TrustedImm32(codeBlock->stackPointerOffset() * sizeof(Register)),
+            GPRInfo::callFrameRegister, CCallHelpers::stackPointerRegister);
 
         jit.jitAssertHasValidCallFrame();
         

Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (203363 => 203364)


--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2016-07-18 19:32:34 UTC (rev 203364)
@@ -704,6 +704,7 @@
         case TailCallInlinedCaller:
         case Construct:
         case CallVarargs:
+        case CallEval:
         case TailCallVarargsInlinedCaller:
         case ConstructVarargs:
         case CallForwardVarargs:
@@ -1047,7 +1048,6 @@
         case CheckIdent:
         case CheckBadCell:
         case PutStructure:
-        case VarInjectionWatchpoint:
         case Phantom:
         case Check:
         case PutGlobalVariable:

Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (203363 => 203364)


--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2016-07-18 19:32:34 UTC (rev 203364)
@@ -222,7 +222,6 @@
     case GetGlobalVar:
     case GetGlobalLexicalVariable:
     case PutGlobalVariable:
-    case VarInjectionWatchpoint:
     case CheckCell:
     case CheckBadCell:
     case CheckNotEmpty:
@@ -240,6 +239,7 @@
     case TailCallInlinedCaller:
     case Construct:
     case CallVarargs:
+    case CallEval:
     case TailCallVarargsInlinedCaller:
     case TailCallForwardVarargsInlinedCaller:
     case ConstructVarargs:

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (203363 => 203364)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2016-07-18 19:32:34 UTC (rev 203364)
@@ -660,6 +660,7 @@
     bool isEmulatedTail = false;
     switch (node->op()) {
     case Call:
+    case CallEval:
         callType = CallLinkInfo::Call;
         break;
     case TailCall:
@@ -879,7 +880,50 @@
     m_jit.emitStoreCallSiteIndex(callSite);
     
     CallLinkInfo* info = m_jit.codeBlock()->addCallLinkInfo();
+    info->setUpCall(callType, node->origin.semantic, calleePayloadGPR);
+    
+    auto setResultAndResetStack = [&] () {
+        m_jit.setupResults(resultPayloadGPR, resultTagGPR);
 
+        jsValueResult(resultTagGPR, resultPayloadGPR, node, DataFormatJS, UseChildrenCalledExplicitly);
+        // After the calls are done, we need to reestablish our stack
+        // pointer. We rely on this for varargs calls, calls with arity
+        // mismatch (the callframe is slided) and tail calls.
+        m_jit.addPtr(TrustedImm32(m_jit.graph().stackPointerOffset() * sizeof(Register)), GPRInfo::callFrameRegister, JITCompiler::stackPointerRegister);
+    };
+    
+    if (node->op() == CallEval) {
+        // We want to call operationCallEval but we don't want to overwrite the parameter area in
+        // which we have created a prototypical eval call frame. This means that we have to
+        // subtract stack to make room for the call. Lucky for us, at this point we have the whole
+        // register file to ourselves.
+        
+        m_jit.addPtr(TrustedImm32(-static_cast<ptrdiff_t>(sizeof(CallerFrameAndPC))), JITCompiler::stackPointerRegister, GPRInfo::regT0);
+        m_jit.storePtr(GPRInfo::callFrameRegister, JITCompiler::Address(GPRInfo::regT0, CallFrame::callerFrameOffset()));
+        
+        // Now we need to make room for:
+        // - The caller frame and PC of a call to operationCallEval.
+        // - Potentially two arguments on the stack.
+        unsigned requiredBytes = sizeof(CallerFrameAndPC) + sizeof(ExecState*) * 2;
+        requiredBytes = WTF::roundUpToMultipleOf(stackAlignmentBytes(), requiredBytes);
+        m_jit.subPtr(TrustedImm32(requiredBytes), JITCompiler::stackPointerRegister);
+        m_jit.setupArgumentsWithExecState(GPRInfo::regT0);
+        prepareForExternalCall();
+        m_jit.appendCall(operationCallEval);
+        m_jit.exceptionCheck();
+        JITCompiler::Jump done = m_jit.branch32(JITCompiler::NotEqual, GPRInfo::returnValueGPR2, TrustedImm32(JSValue::EmptyValueTag));
+        
+        // This is the part where we meant to make a normal call. Oops.
+        m_jit.addPtr(TrustedImm32(requiredBytes), JITCompiler::stackPointerRegister);
+        m_jit.load32(JITCompiler::calleeFrameSlot(CallFrameSlot::callee).withOffset(PayloadOffset), GPRInfo::regT0);
+        m_jit.load32(JITCompiler::calleeFrameSlot(CallFrameSlot::callee).withOffset(TagOffset), GPRInfo::regT1);
+        m_jit.emitDumbVirtualCall(info);
+        
+        done.link(&m_jit);
+        setResultAndResetStack();
+        return;
+    }
+
     slowPath.append(m_jit.branchIfNotCell(JSValueRegs(calleeTagGPR, calleePayloadGPR)));
     slowPath.append(m_jit.branchPtrWithPatch(MacroAssembler::NotEqual, calleePayloadGPR, targetToCheck));
 
@@ -929,17 +973,9 @@
 
     if (isTail)
         m_jit.abortWithReason(JITDidReturnFromTailCall);
-    else {
-        m_jit.setupResults(resultPayloadGPR, resultTagGPR);
+    else
+        setResultAndResetStack();
 
-        jsValueResult(resultTagGPR, resultPayloadGPR, node, DataFormatJS, UseChildrenCalledExplicitly);
-        // After the calls are done, we need to reestablish our stack
-        // pointer. We rely on this for varargs calls, calls with arity
-        // mismatch (the callframe is slided) and tail calls.
-        m_jit.addPtr(TrustedImm32(m_jit.graph().stackPointerOffset() * sizeof(Register)), GPRInfo::callFrameRegister, JITCompiler::stackPointerRegister);
-    }
-
-    info->setUpCall(callType, node->origin.semantic, calleePayloadGPR);
     m_jit.addJSCall(fastCall, slowCall, targetToCheck, info);
 }
 
@@ -4591,11 +4627,6 @@
         break;
     }
 
-    case VarInjectionWatchpoint: {
-        noResult(node);
-        break;
-    }
-
     case CheckTypeInfoFlags: {
         compileCheckTypeInfoFlags(node);
         break;
@@ -4803,6 +4834,7 @@
     case TailCallForwardVarargs:
     case TailCallForwardVarargsInlinedCaller:
     case ConstructForwardVarargs:
+    case CallEval:
         emitCall(node);
         break;
 

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (203363 => 203364)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2016-07-18 19:32:34 UTC (rev 203364)
@@ -635,6 +635,7 @@
     bool isEmulatedTail = false;
     switch (node->op()) {
     case Call:
+    case CallEval:
         callType = CallLinkInfo::Call;
         break;
     case TailCall:
@@ -838,8 +839,53 @@
     CallSiteIndex callSite = m_jit.recordCallSiteAndGenerateExceptionHandlingOSRExitIfNeeded(dynamicOrigin, m_stream->size());
     m_jit.emitStoreCallSiteIndex(callSite);
     
+    auto setResultAndResetStack = [&] () {
+        GPRFlushedCallResult result(this);
+        GPRReg resultGPR = result.gpr();
+        m_jit.move(GPRInfo::returnValueGPR, resultGPR);
+
+        jsValueResult(resultGPR, m_currentNode, DataFormatJS, UseChildrenCalledExplicitly);
+
+        // After the calls are done, we need to reestablish our stack
+        // pointer. We rely on this for varargs calls, calls with arity
+        // mismatch (the callframe is slided) and tail calls.
+        m_jit.addPtr(TrustedImm32(m_jit.graph().stackPointerOffset() * sizeof(Register)), GPRInfo::callFrameRegister, JITCompiler::stackPointerRegister);
+    };
+    
     CallLinkInfo* callLinkInfo = m_jit.codeBlock()->addCallLinkInfo();
+    callLinkInfo->setUpCall(callType, m_currentNode->origin.semantic, calleeGPR);
 
+    if (node->op() == CallEval) {
+        // We want to call operationCallEval but we don't want to overwrite the parameter area in
+        // which we have created a prototypical eval call frame. This means that we have to
+        // subtract stack to make room for the call. Lucky for us, at this point we have the whole
+        // register file to ourselves.
+        
+        m_jit.addPtr(TrustedImm32(-static_cast<ptrdiff_t>(sizeof(CallerFrameAndPC))), JITCompiler::stackPointerRegister, GPRInfo::regT0);
+        m_jit.storePtr(GPRInfo::callFrameRegister, JITCompiler::Address(GPRInfo::regT0, CallFrame::callerFrameOffset()));
+        
+        // Now we need to make room for:
+        // - The caller frame and PC of a call to operationCallEval.
+        // - Potentially two arguments on the stack.
+        unsigned requiredBytes = sizeof(CallerFrameAndPC) + sizeof(ExecState*) * 2;
+        requiredBytes = WTF::roundUpToMultipleOf(stackAlignmentBytes(), requiredBytes);
+        m_jit.subPtr(TrustedImm32(requiredBytes), JITCompiler::stackPointerRegister);
+        m_jit.setupArgumentsWithExecState(GPRInfo::regT0);
+        prepareForExternalCall();
+        m_jit.appendCall(operationCallEval);
+        m_jit.exceptionCheck();
+        JITCompiler::Jump done = m_jit.branchTest64(JITCompiler::NonZero, GPRInfo::returnValueGPR);
+        
+        // This is the part where we meant to make a normal call. Oops.
+        m_jit.addPtr(TrustedImm32(requiredBytes), JITCompiler::stackPointerRegister);
+        m_jit.load64(JITCompiler::calleeFrameSlot(CallFrameSlot::callee), GPRInfo::regT0);
+        m_jit.emitDumbVirtualCall(callLinkInfo);
+        
+        done.link(&m_jit);
+        setResultAndResetStack();
+        return;
+    }
+    
     JITCompiler::DataLabelPtr targetToCheck;
     JITCompiler::Jump slowPath = m_jit.branchPtrWithPatch(MacroAssembler::NotEqual, calleeGPR, targetToCheck, MacroAssembler::TrustedImmPtr(0));
 
@@ -877,20 +923,9 @@
 
     if (isTail)
         m_jit.abortWithReason(JITDidReturnFromTailCall);
-    else {
-        GPRFlushedCallResult result(this);
-        GPRReg resultGPR = result.gpr();
-        m_jit.move(GPRInfo::returnValueGPR, resultGPR);
+    else
+        setResultAndResetStack();
 
-        jsValueResult(resultGPR, m_currentNode, DataFormatJS, UseChildrenCalledExplicitly);
-
-        // After the calls are done, we need to reestablish our stack
-        // pointer. We rely on this for varargs calls, calls with arity
-        // mismatch (the callframe is slided) and tail calls.
-        m_jit.addPtr(TrustedImm32(m_jit.graph().stackPointerOffset() * sizeof(Register)), GPRInfo::callFrameRegister, JITCompiler::stackPointerRegister);
-    }
-
-    callLinkInfo->setUpCall(callType, m_currentNode->origin.semantic, calleeGPR);
     m_jit.addJSCall(fastCall, slowCall, targetToCheck, callLinkInfo);
 }
 
@@ -4506,11 +4541,6 @@
         break;
     }
 
-    case VarInjectionWatchpoint: {
-        noResult(node);
-        break;
-    }
-
     case CheckTypeInfoFlags: {
         compileCheckTypeInfoFlags(node);
         break;
@@ -4721,6 +4751,7 @@
     case ConstructForwardVarargs:
     case TailCallForwardVarargs:
     case TailCallForwardVarargsInlinedCaller:
+    case CallEval:
         emitCall(node);
         break;
 

Modified: trunk/Source/_javascript_Core/dfg/DFGStackLayoutPhase.cpp (203363 => 203364)


--- trunk/Source/_javascript_Core/dfg/DFGStackLayoutPhase.cpp	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/dfg/DFGStackLayoutPhase.cpp	2016-07-18 19:32:34 UTC (rev 203364)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2013, 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -173,7 +173,7 @@
             data->machineLocal = assign(allocation, data->local);
         }
         
-        if (LIKELY(!m_graph.hasDebuggerEnabled()))
+        if (!m_graph.needsScopeRegister())
             codeBlock()->setScopeRegister(VirtualRegister());
         else
             codeBlock()->setScopeRegister(assign(allocation, codeBlock()->scopeRegister()));

Modified: trunk/Source/_javascript_Core/dfg/DFGWatchpointCollectionPhase.cpp (203363 => 203364)


--- trunk/Source/_javascript_Core/dfg/DFGWatchpointCollectionPhase.cpp	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/dfg/DFGWatchpointCollectionPhase.cpp	2016-07-18 19:32:34 UTC (rev 203364)
@@ -97,10 +97,6 @@
             }
             break;
             
-        case VarInjectionWatchpoint:
-            addLazily(globalObject()->varInjectionWatchpoint());
-            break;
-            
         default:
             break;
         }

Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (203363 => 203364)


--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2016-07-18 19:32:34 UTC (rev 203364)
@@ -142,6 +142,7 @@
     case TailCallInlinedCaller:
     case Construct:
     case CallVarargs:
+    case CallEval:
     case TailCallVarargs:
     case TailCallVarargsInlinedCaller:
     case ConstructVarargs:

Modified: trunk/Source/_javascript_Core/ftl/FTLCompile.cpp (203363 => 203364)


--- trunk/Source/_javascript_Core/ftl/FTLCompile.cpp	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/ftl/FTLCompile.cpp	2016-07-18 19:32:34 UTC (rev 203364)
@@ -105,7 +105,10 @@
 
     }
 
-    if (graph.hasDebuggerEnabled())
+    // Note that the scope register could be invalid here if the original code had CallEval but it
+    // got killed. That's because it takes the CallEval to cause the scope register to be kept alive
+    // unless the debugger is also enabled.
+    if (graph.needsScopeRegister() && codeBlock->scopeRegister().isValid())
         codeBlock->setScopeRegister(codeBlock->scopeRegister() + localsOffset);
 
     for (OSRExitDescriptor& descriptor : state.jitCode->osrExitDescriptors) {

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (203363 => 203364)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2016-07-18 19:32:34 UTC (rev 203364)
@@ -843,6 +843,9 @@
         case ConstructForwardVarargs:
             compileCallOrConstructVarargs();
             break;
+        case CallEval:
+            compileCallEval();
+            break;
         case LoadVarargs:
             compileLoadVarargs();
             break;
@@ -5543,6 +5546,90 @@
         }
     }
     
+    void compileCallEval()
+    {
+        Node* node = m_node;
+        unsigned numArgs = node->numChildren() - 1;
+        
+        LValue jsCallee = lowJSValue(m_graph.varArgChild(node, 0));
+        
+        unsigned frameSize = CallFrame::headerSizeInRegisters + numArgs;
+        unsigned alignedFrameSize = WTF::roundUpToMultipleOf(stackAlignmentRegisters(), frameSize);
+        
+        m_proc.requestCallArgAreaSize(alignedFrameSize);
+        
+        Vector<ConstrainedValue> arguments;
+        arguments.append(ConstrainedValue(jsCallee, ValueRep::reg(GPRInfo::regT0)));
+        
+        auto addArgument = [&] (LValue value, VirtualRegister reg, int offset) {
+            intptr_t offsetFromSP = 
+                (reg.offset() - CallerFrameAndPC::sizeInRegisters) * sizeof(EncodedJSValue) + offset;
+            arguments.append(ConstrainedValue(value, ValueRep::stackArgument(offsetFromSP)));
+        };
+        
+        addArgument(jsCallee, VirtualRegister(CallFrameSlot::callee), 0);
+        addArgument(m_out.constInt32(numArgs), VirtualRegister(CallFrameSlot::argumentCount), PayloadOffset);
+        for (unsigned i = 0; i < numArgs; ++i)
+            addArgument(lowJSValue(m_graph.varArgChild(node, 1 + i)), virtualRegisterForArgument(i), 0);
+        
+        PatchpointValue* patchpoint = m_out.patchpoint(Int64);
+        patchpoint->appendVector(arguments);
+        
+        RefPtr<PatchpointExceptionHandle> exceptionHandle = preparePatchpointForExceptions(patchpoint);
+        
+        patchpoint->append(m_tagMask, ValueRep::reg(GPRInfo::tagMaskRegister));
+        patchpoint->append(m_tagTypeNumber, ValueRep::reg(GPRInfo::tagTypeNumberRegister));
+        patchpoint->clobber(RegisterSet::macroScratchRegisters());
+        patchpoint->clobberLate(RegisterSet::volatileRegistersForJSCall());
+        patchpoint->resultConstraint = ValueRep::reg(GPRInfo::returnValueGPR);
+        
+        CodeOrigin codeOrigin = codeOriginDescriptionOfCallSite();
+        State* state = &m_ftlState;
+        patchpoint->setGenerator(
+            [=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+                AllowMacroScratchRegisterUsage allowScratch(jit);
+                CallSiteIndex callSiteIndex = state->jitCode->common.addUniqueCallSiteIndex(codeOrigin);
+                
+                Box<CCallHelpers::JumpList> exceptions = exceptionHandle->scheduleExitCreation(params)->jumps(jit);
+                
+                exceptionHandle->scheduleExitCreationForUnwind(params, callSiteIndex);
+                
+                jit.store32(
+                    CCallHelpers::TrustedImm32(callSiteIndex.bits()),
+                    CCallHelpers::tagFor(VirtualRegister(CallFrameSlot::argumentCount)));
+                
+                CallLinkInfo* callLinkInfo = jit.codeBlock()->addCallLinkInfo();
+                callLinkInfo->setUpCall(CallLinkInfo::Call, node->origin.semantic, GPRInfo::regT0);
+                
+                jit.addPtr(CCallHelpers::TrustedImm32(-static_cast<ptrdiff_t>(sizeof(CallerFrameAndPC))), CCallHelpers::stackPointerRegister, GPRInfo::regT1);
+                jit.storePtr(GPRInfo::callFrameRegister, CCallHelpers::Address(GPRInfo::regT1, CallFrame::callerFrameOffset()));
+                
+                // Now we need to make room for:
+                // - The caller frame and PC for a call to operationCallEval.
+                // - Potentially two arguments on the stack.
+                unsigned requiredBytes = sizeof(CallerFrameAndPC) + sizeof(ExecState*) * 2;
+                requiredBytes = WTF::roundUpToMultipleOf(stackAlignmentBytes(), requiredBytes);
+                jit.subPtr(CCallHelpers::TrustedImm32(requiredBytes), CCallHelpers::stackPointerRegister);
+                jit.setupArgumentsWithExecState(GPRInfo::regT1);
+                jit.move(CCallHelpers::TrustedImmPtr(bitwise_cast<void*>(operationCallEval)), GPRInfo::nonPreservedNonArgumentGPR);
+                jit.call(GPRInfo::nonPreservedNonArgumentGPR);
+                exceptions->append(jit.emitExceptionCheck(AssemblyHelpers::NormalExceptionCheck, AssemblyHelpers::FarJumpWidth));
+                
+                CCallHelpers::Jump done = jit.branchTest64(CCallHelpers::NonZero, GPRInfo::returnValueGPR);
+                
+                jit.addPtr(CCallHelpers::TrustedImm32(requiredBytes), CCallHelpers::stackPointerRegister);
+                jit.load64(CCallHelpers::calleeFrameSlot(CallFrameSlot::callee), GPRInfo::regT0);
+                jit.emitDumbVirtualCall(callLinkInfo);
+                
+                done.link(&jit);
+                jit.addPtr(
+                    CCallHelpers::TrustedImm32(-params.proc().frameSize()),
+                    GPRInfo::callFrameRegister, CCallHelpers::stackPointerRegister);
+            });
+        
+        setJSValue(patchpoint);
+    }
+    
     void compileLoadVarargs()
     {
         LoadVarargsData* data = ""

Modified: trunk/Source/_javascript_Core/jit/AssemblyHelpers.cpp (203363 => 203364)


--- trunk/Source/_javascript_Core/jit/AssemblyHelpers.cpp	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/jit/AssemblyHelpers.cpp	2016-07-18 19:32:34 UTC (rev 203364)
@@ -30,6 +30,7 @@
 
 #include "JITOperations.h"
 #include "JSCInlines.h"
+#include "LinkBuffer.h"
 
 namespace JSC {
 
@@ -613,6 +614,18 @@
 #endif
 }
 
+void AssemblyHelpers::emitDumbVirtualCall(CallLinkInfo* info)
+{
+    move(TrustedImmPtr(info), GPRInfo::regT2);
+    Call call = nearCall();
+    addLinkTask(
+        [=] (LinkBuffer& linkBuffer) {
+            MacroAssemblerCodeRef virtualThunk = virtualThunkFor(&linkBuffer.vm(), *info);
+            info->setSlowStub(createJITStubRoutine(virtualThunk, linkBuffer.vm(), nullptr, true));
+            linkBuffer.link(call, CodeLocationLabel(virtualThunk.code()));
+        });
+}
+
 } // namespace JSC
 
 #endif // ENABLE(JIT)

Modified: trunk/Source/_javascript_Core/jit/AssemblyHelpers.h (203363 => 203364)


--- trunk/Source/_javascript_Core/jit/AssemblyHelpers.h	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/jit/AssemblyHelpers.h	2016-07-18 19:32:34 UTC (rev 203364)
@@ -165,9 +165,20 @@
     void moveValueRegs(JSValueRegs srcRegs, JSValueRegs destRegs)
     {
 #if USE(JSVALUE32_64)
+        if (destRegs.tagGPR() == srcRegs.payloadGPR()) {
+            if (destRegs.payloadGPR() == srcRegs.tagGPR()) {
+                swap(srcRegs.payloadGPR(), srcRegs.tagGPR());
+                return;
+            }
+            move(srcRegs.payloadGPR(), destRegs.payloadGPR());
+            move(srcRegs.tagGPR(), destRegs.tagGPR());
+            return;
+        }
         move(srcRegs.tagGPR(), destRegs.tagGPR());
+        move(srcRegs.payloadGPR(), destRegs.payloadGPR());
+#else
+        move(srcRegs.gpr(), destRegs.gpr());
 #endif
-        move(srcRegs.payloadGPR(), destRegs.payloadGPR());
     }
 
     void moveValue(JSValue value, JSValueRegs regs)
@@ -1380,7 +1391,9 @@
         
         functor(TypeofType::Undefined, true);
     }
-
+    
+    void emitDumbVirtualCall(CallLinkInfo*);
+    
     Vector<BytecodeAndMachineOffset>& decodedCodeMapFor(CodeBlock*);
 
     void makeSpaceOnStackForCCall();

Modified: trunk/Source/_javascript_Core/jit/JITCall.cpp (203363 => 203364)


--- trunk/Source/_javascript_Core/jit/JITCall.cpp	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/jit/JITCall.cpp	2016-07-18 19:32:34 UTC (rev 203364)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2013-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2013-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -124,14 +124,7 @@
     addPtr(TrustedImm32(registerOffset * sizeof(Register) + sizeof(CallerFrameAndPC)), callFrameRegister, stackPointerRegister);
 
     load64(Address(stackPointerRegister, sizeof(Register) * CallFrameSlot::callee - sizeof(CallerFrameAndPC)), regT0);
-    move(TrustedImmPtr(info), regT2);
-    Call call = emitNakedCall();
-    addLinkTask(
-        [=] (LinkBuffer& linkBuffer) {
-            MacroAssemblerCodeRef virtualThunk = virtualThunkFor(m_vm, *info);
-            info->setSlowStub(createJITStubRoutine(virtualThunk, *m_vm, nullptr, true));
-            linkBuffer.link(call, CodeLocationLabel(virtualThunk.code()));
-        });
+    emitDumbVirtualCall(info);
     addPtr(TrustedImm32(stackPointerOffsetFor(m_codeBlock) * sizeof(Register)), callFrameRegister, stackPointerRegister);
     checkStackPointerAlignment();
 

Modified: trunk/Source/_javascript_Core/jit/JITCall32_64.cpp (203363 => 203364)


--- trunk/Source/_javascript_Core/jit/JITCall32_64.cpp	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/jit/JITCall32_64.cpp	2016-07-18 19:32:34 UTC (rev 203364)
@@ -214,8 +214,6 @@
 
     addPtr(TrustedImm32(registerOffset * sizeof(Register) + sizeof(CallerFrameAndPC)), callFrameRegister, stackPointerRegister);
 
-    loadPtr(Address(stackPointerRegister, sizeof(Register) * CallFrameSlot::callee - sizeof(CallerFrameAndPC)), regT0);
-    loadPtr(Address(stackPointerRegister, sizeof(Register) * CallFrameSlot::callee - sizeof(CallerFrameAndPC)), regT1);
     move(TrustedImmPtr(info), regT2);
 
     emitLoad(CallFrameSlot::callee, regT1, regT0);

Modified: trunk/Source/_javascript_Core/jit/JITOperations.cpp (203363 => 203364)


--- trunk/Source/_javascript_Core/jit/JITOperations.cpp	2016-07-18 19:23:41 UTC (rev 203363)
+++ trunk/Source/_javascript_Core/jit/JITOperations.cpp	2016-07-18 19:32:34 UTC (rev 203364)
@@ -772,7 +772,7 @@
     UNUSED_PARAM(exec);
 
     execCallee->setCodeBlock(0);
-
+    
     if (!isHostFunction(execCallee->calleeAsValue(), globalFuncEval))
         return JSValue::encode(JSValue());
 

Added: trunk/Source/_javascript_Core/tests/stress/exit-then-eval.js (0 => 203364)


--- trunk/Source/_javascript_Core/tests/stress/exit-then-eval.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/exit-then-eval.js	2016-07-18 19:32:34 UTC (rev 203364)
@@ -0,0 +1,17 @@
+function foo(a, b, string)
+{
+    var x = a + b;
+    return eval(string);
+}
+
+noInline(foo);
+
+for (var i = 0; i < 100000; ++i) {
+    var result = foo(1, 2, "x + 1");
+    if (result != 1 + 2 + 1)
+        throw "Error: bad result in loop: " + result;
+}
+
+var result = foo(2000000000, 2000000000, "x - 1");
+if (result != 2000000000 + 2000000000 - 1)
+    throw "Error: bad result at end: " + result;

Added: trunk/Source/_javascript_Core/tests/stress/force-exit-then-eval-dfg.js (0 => 203364)


--- trunk/Source/_javascript_Core/tests/stress/force-exit-then-eval-dfg.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/force-exit-then-eval-dfg.js	2016-07-18 19:32:34 UTC (rev 203364)
@@ -0,0 +1,14 @@
+function foo(a, b, string)
+{
+    OSRExit();
+    return eval(string);
+}
+
+noInline(foo);
+
+for (var i = 0; i < 100000; ++i) {
+    var result = foo(1, 2, "a + b + 1");
+    if (result != 1 + 2 + 1)
+        throw "Error: bad result in loop: " + result;
+}
+

Added: trunk/Source/_javascript_Core/tests/stress/force-exit-then-eval.js (0 => 203364)


--- trunk/Source/_javascript_Core/tests/stress/force-exit-then-eval.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/force-exit-then-eval.js	2016-07-18 19:32:34 UTC (rev 203364)
@@ -0,0 +1,23 @@
+var flag = true;
+flag = false;
+
+function foo(a, b, string)
+{
+    var x = a + b;
+    if (flag)
+        return eval(string);
+    return 42;
+}
+
+noInline(foo);
+
+for (var i = 0; i < 100000; ++i) {
+    var result = foo(1, 2, "x + 1");
+    if (result != 42)
+        throw "Error: bad result in loop: " + result;
+}
+
+flag = true;
+var result = foo(1, 2, "x - 1");
+if (result != 1 + 2 - 1)
+    throw "Error: bad result at end: " + result;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to