Bug 113986 - FTL should support PhantomArguments
Summary: FTL should support PhantomArguments
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: JavaScriptCore (show other bugs)
Version: 528+ (Nightly build)
Hardware: All All
: P2 Normal
Assignee: Filip Pizlo
URL:
Keywords:
Depends on: 126218
Blocks: 112840
  Show dependency treegraph
 
Reported: 2013-04-04 18:44 PDT by Filip Pizlo
Modified: 2014-11-18 13:40 PST (History)
8 users (show)

See Also:


Attachments
work in progress (20.15 KB, patch)
2014-02-28 15:13 PST, Filip Pizlo
no flags Details | Formatted Diff | Diff
the patch (20.45 KB, patch)
2014-02-28 15:23 PST, Filip Pizlo
oliver: review+
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Filip Pizlo 2013-04-04 18:44:37 PDT
I'll do this after I do OSR.
Comment 1 Filip Pizlo 2014-02-28 15:13:56 PST
Created attachment 225494 [details]
work in progress
Comment 2 Filip Pizlo 2014-02-28 15:21:24 PST
(In reply to comment #1)
> Created an attachment (id=225494) [details]
> work in progress

Shockingly, this appears to actually just work.
Comment 3 Filip Pizlo 2014-02-28 15:23:36 PST
Created attachment 225498 [details]
the patch
Comment 4 WebKit Commit Bot 2014-02-28 15:26:57 PST
Attachment 225498 [details] did not pass style-queue:


ERROR: Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp:229:  Weird number of spaces at line-start.  Are you using a 4-space indent?  [whitespace/indent] [3]
ERROR: Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp:230:  Weird number of spaces at line-start.  Are you using a 4-space indent?  [whitespace/indent] [3]
Total errors found: 2 in 10 files


If any of these errors are false positives, please file a bug against check-webkit-style.
Comment 5 Filip Pizlo 2014-03-01 11:52:25 PST
Landed in http://trac.webkit.org/changeset/164923
Comment 6 Whitehat Security 2014-10-25 19:19:52 PDT
Comment on attachment 225498 [details]
the patch

>Index: Source/JavaScriptCore/ChangeLog
>===================================================================
>--- Source/JavaScriptCore/ChangeLog	(revision 164889)
>+++ Source/JavaScriptCore/ChangeLog	(working copy)
>@@ -1,3 +1,38 @@
>+2014-02-28  Filip Pizlo  <fpizlo@apple.com>
>+
>+        FTL should support PhantomArguments
>+        https://bugs.webkit.org/show_bug.cgi?id=113986
>+
>+        Reviewed by NOBODY (OOPS!).
>+        
>+        Adding PhantomArguments to the FTL mostly means wiring the recovery of the Arguments
>+        object into the FTL's OSR exit compiler.
>+
>+        * dfg/DFGOSRExitCompiler32_64.cpp:
>+        (JSC::DFG::OSRExitCompiler::compileExit): move the recovery code to DFGOSRExitCompilerCommon.cpp
>+        * dfg/DFGOSRExitCompiler64.cpp:
>+        (JSC::DFG::OSRExitCompiler::compileExit): move the recovery code to DFGOSRExitCompilerCommon.cpp
>+        * dfg/DFGOSRExitCompilerCommon.cpp:
>+        (JSC::DFG::ArgumentsRecoveryGenerator::ArgumentsRecoveryGenerator):
>+        (JSC::DFG::ArgumentsRecoveryGenerator::~ArgumentsRecoveryGenerator):
>+        (JSC::DFG::ArgumentsRecoveryGenerator::generateFor): this is the common place for the recovery code
>+        * dfg/DFGOSRExitCompilerCommon.h:
>+        * ftl/FTLCapabilities.cpp:
>+        (JSC::FTL::canCompile):
>+        * ftl/FTLExitValue.cpp:
>+        (JSC::FTL::ExitValue::dumpInContext):
>+        * ftl/FTLExitValue.h:
>+        (JSC::FTL::ExitValue::argumentsObjectThatWasNotCreated):
>+        (JSC::FTL::ExitValue::isArgumentsObjectThatWasNotCreated):
>+        (JSC::FTL::ExitValue::valueFormat):
>+        * ftl/FTLLowerDFGToLLVM.cpp:
>+        (JSC::FTL::LowerDFGToLLVM::compileNode):
>+        (JSC::FTL::LowerDFGToLLVM::compilePhantomArguments):
>+        (JSC::FTL::LowerDFGToLLVM::buildExitArguments):
>+        (JSC::FTL::LowerDFGToLLVM::tryToSetConstantExitArgument):
>+        * ftl/FTLOSRExitCompiler.cpp:
>+        (JSC::FTL::compileStub): Call into the ArgumentsRecoveryGenerator
>+
> 2014-02-28  Oliver Hunt  <oliver@apple.com>
> 
>         REGRESSION(r164835): It broke 10 JSC stress test on 32 bit platforms
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -393,66 +393,14 @@ void OSRExitCompiler::compileExit(const 
>     //     registers.
>     
>     if (haveArguments) {
>-        HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>-            NullableHashTraits<InlineCallFrame*>> didCreateArgumentsObject;
>+        ArgumentsRecoveryGenerator argumentsRecovery;
> 
>         for (size_t index = 0; index < operands.size(); ++index) {
>             const ValueRecovery& recovery = operands[index];
>             if (recovery.technique() != ArgumentsThatWereNotCreated)
>                 continue;
>-            int operand = operands.operandForIndex(index);
>-            // Find the right inline call frame.
>-            InlineCallFrame* inlineCallFrame = 0;
>-            for (InlineCallFrame* current = exit.m_codeOrigin.inlineCallFrame;
>-                 current;
>-                 current = current->caller.inlineCallFrame) {
>-                if (current->stackOffset >= operand) {
>-                    inlineCallFrame = current;
>-                    break;
>-                }
>-            }
>-
>-            if (!m_jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>-                continue;
>-            VirtualRegister argumentsRegister = m_jit.baselineArgumentsRegisterFor(inlineCallFrame);
>-            if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>-                // We know this call frame optimized out an arguments object that
>-                // the baseline JIT would have created. Do that creation now.
>-                if (inlineCallFrame) {
>-                    m_jit.setupArgumentsWithExecState(
>-                        AssemblyHelpers::TrustedImmPtr(inlineCallFrame));
>-                    m_jit.move(
>-                        AssemblyHelpers::TrustedImmPtr(
>-                            bitwise_cast<void*>(operationCreateInlinedArguments)),
>-                        GPRInfo::nonArgGPR0);
>-                } else {
>-                    m_jit.setupArgumentsExecState();
>-                    m_jit.move(
>-                        AssemblyHelpers::TrustedImmPtr(
>-                            bitwise_cast<void*>(operationCreateArguments)),
>-                        GPRInfo::nonArgGPR0);
>-                }
>-                m_jit.call(GPRInfo::nonArgGPR0);
>-                m_jit.store32(
>-                    AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                    AssemblyHelpers::tagFor(argumentsRegister));
>-                m_jit.store32(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::payloadFor(argumentsRegister));
>-                m_jit.store32(
>-                    AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                    AssemblyHelpers::tagFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.store32(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::payloadFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>-            }
>-
>-            m_jit.load32(AssemblyHelpers::payloadFor(argumentsRegister), GPRInfo::regT0);
>-            m_jit.store32(
>-                AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                AssemblyHelpers::tagFor(operand));
>-            m_jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor(operand));
>+            argumentsRecovery.generateFor(
>+                operands.operandForIndex(index), exit.m_codeOrigin, m_jit);
>         }
>     }
> 
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -365,50 +365,14 @@ void OSRExitCompiler::compileExit(const 
>     //     registers.
>     
>     if (haveArguments) {
>-        HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>-            NullableHashTraits<InlineCallFrame*>> didCreateArgumentsObject;
>+        ArgumentsRecoveryGenerator argumentsRecovery;
> 
>         for (size_t index = 0; index < operands.size(); ++index) {
>             const ValueRecovery& recovery = operands[index];
>             if (recovery.technique() != ArgumentsThatWereNotCreated)
>                 continue;
>-            int operand = operands.operandForIndex(index);
>-            // Find the right inline call frame.
>-            InlineCallFrame* inlineCallFrame = 0;
>-            for (InlineCallFrame* current = exit.m_codeOrigin.inlineCallFrame;
>-                 current;
>-                 current = current->caller.inlineCallFrame) {
>-                if (current->stackOffset >= operand) {
>-                    inlineCallFrame = current;
>-                    break;
>-                }
>-            }
>-
>-            if (!m_jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>-                continue;
>-            VirtualRegister argumentsRegister = m_jit.baselineArgumentsRegisterFor(inlineCallFrame);
>-            if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>-                // We know this call frame optimized out an arguments object that
>-                // the baseline JIT would have created. Do that creation now.
>-                if (inlineCallFrame) {
>-                    m_jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0);
>-                    m_jit.setupArguments(GPRInfo::regT0);
>-                } else
>-                    m_jit.setupArgumentsExecState();
>-                m_jit.move(
>-                    AssemblyHelpers::TrustedImmPtr(
>-                        bitwise_cast<void*>(operationCreateArguments)),
>-                    GPRInfo::nonArgGPR0);
>-                m_jit.call(GPRInfo::nonArgGPR0);
>-                m_jit.store64(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(argumentsRegister));
>-                m_jit.store64(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>-            }
>-
>-            m_jit.load64(AssemblyHelpers::addressFor(argumentsRegister), GPRInfo::regT0);
>-            m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
>+            argumentsRecovery.generateFor(
>+                operands.operandForIndex(index), exit.m_codeOrigin, m_jit);
>         }
>     }
> 
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -217,6 +217,89 @@ void adjustAndJumpToTarget(CCallHelpers&
>     jit.jump(GPRInfo::regT2);
> }
> 
>+ArgumentsRecoveryGenerator::ArgumentsRecoveryGenerator() { }
>+ArgumentsRecoveryGenerator::~ArgumentsRecoveryGenerator() { }
>+
>+void ArgumentsRecoveryGenerator::generateFor(
>+    int operand, CodeOrigin codeOrigin, CCallHelpers& jit)
>+{
>+    // Find the right inline call frame.
>+    InlineCallFrame* inlineCallFrame = 0;
>+    for (InlineCallFrame* current = codeOrigin.inlineCallFrame;
>+         current;
>+         current = current->caller.inlineCallFrame) {
>+        if (current->stackOffset >= operand) {
>+            inlineCallFrame = current;
>+            break;
>+        }
>+    }
>+
>+    if (!jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>+        return;
>+    VirtualRegister argumentsRegister = jit.baselineArgumentsRegisterFor(inlineCallFrame);
>+    if (m_didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>+        // We know this call frame optimized out an arguments object that
>+        // the baseline JIT would have created. Do that creation now.
>+#if USE(JSVALUE64)
>+        if (inlineCallFrame) {
>+            jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0);
>+            jit.setupArguments(GPRInfo::regT0);
>+        } else
>+            jit.setupArgumentsExecState();
>+        jit.move(
>+            AssemblyHelpers::TrustedImmPtr(
>+                bitwise_cast<void*>(operationCreateArguments)),
>+            GPRInfo::nonArgGPR0);
>+        jit.call(GPRInfo::nonArgGPR0);
>+        jit.store64(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(argumentsRegister));
>+        jit.store64(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>+#else // USE(JSVALUE64) -> so the 32_64 part
>+        if (inlineCallFrame) {
>+            jit.setupArgumentsWithExecState(
>+                AssemblyHelpers::TrustedImmPtr(inlineCallFrame));
>+            jit.move(
>+                AssemblyHelpers::TrustedImmPtr(
>+                    bitwise_cast<void*>(operationCreateInlinedArguments)),
>+                GPRInfo::nonArgGPR0);
>+        } else {
>+            jit.setupArgumentsExecState();
>+            jit.move(
>+                AssemblyHelpers::TrustedImmPtr(
>+                    bitwise_cast<void*>(operationCreateArguments)),
>+                GPRInfo::nonArgGPR0);
>+        }
>+        jit.call(GPRInfo::nonArgGPR0);
>+        jit.store32(
>+            AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+            AssemblyHelpers::tagFor(argumentsRegister));
>+        jit.store32(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::payloadFor(argumentsRegister));
>+        jit.store32(
>+            AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+            AssemblyHelpers::tagFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.store32(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::payloadFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>+#endif // USE(JSVALUE64)
>+    }
>+
>+#if USE(JSVALUE64)
>+    jit.load64(AssemblyHelpers::addressFor(argumentsRegister), GPRInfo::regT0);
>+    jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
>+#else // USE(JSVALUE64) -> so the 32_64 part
>+    jit.load32(AssemblyHelpers::payloadFor(argumentsRegister), GPRInfo::regT0);
>+    jit.store32(
>+        AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+        AssemblyHelpers::tagFor(operand));
>+    jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor(operand));
>+#endif // USE(JSVALUE64)
>+}
>+    
> } } // namespace JSC::DFG
> 
> #endif // ENABLE(DFG_JIT)
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h	(working copy)
>@@ -37,6 +37,18 @@ void handleExitCounts(CCallHelpers&, con
> void reifyInlinedCallFrames(CCallHelpers&, const OSRExitBase&);
> void adjustAndJumpToTarget(CCallHelpers&, const OSRExitBase&);
> 
>+class ArgumentsRecoveryGenerator {
>+public:
>+    ArgumentsRecoveryGenerator();
>+    ~ArgumentsRecoveryGenerator();
>+    
>+    void generateFor(int operand, CodeOrigin, CCallHelpers&);
>+    
>+private:
>+    HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>+        NullableHashTraits<InlineCallFrame*>> m_didCreateArgumentsObject;
>+};
>+
> } } // namespace JSC::DFG
> 
> #endif // ENABLE(DFG_JIT)
>Index: Source/JavaScriptCore/ftl/FTLCapabilities.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLCapabilities.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLCapabilities.cpp	(working copy)
>@@ -140,6 +140,7 @@ inline CapabilityLevel canCompile(Node* 
>     case MultiGetByOffset:
>     case MultiPutByOffset:
>     case ToPrimitive:
>+    case PhantomArguments:
>         // These are OK.
>         break;
>     case PutByIdDirect:
>Index: Source/JavaScriptCore/ftl/FTLExitValue.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLExitValue.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLExitValue.cpp	(working copy)
>@@ -59,6 +59,9 @@ void ExitValue::dumpInContext(PrintStrea
>     case ExitValueInJSStackAsDouble:
>         out.print("InJSStackAsDouble:r", virtualRegister());
>         return;
>+    case ExitValueArgumentsObjectThatWasNotCreated:
>+        out.print("ArgumentsObjectThatWasNotCreated");
>+        return;
>     case ExitValueRecovery:
>         out.print("Recovery(", recoveryOpcode(), ", arg", leftRecoveryArgument(), ", arg", rightRecoveryArgument(), ", ", recoveryFormat(), ")");
>         return;
>Index: Source/JavaScriptCore/ftl/FTLExitValue.h
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLExitValue.h	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLExitValue.h	(working copy)
>@@ -51,6 +51,7 @@ enum ExitValueKind {
>     ExitValueInJSStackAsInt32,
>     ExitValueInJSStackAsInt52,
>     ExitValueInJSStackAsDouble,
>+    ExitValueArgumentsObjectThatWasNotCreated,
>     ExitValueRecovery
> };
> 
>@@ -118,6 +119,13 @@ public:
>         return result;
>     }
>     
>+    static ExitValue argumentsObjectThatWasNotCreated()
>+    {
>+        ExitValue result;
>+        result.m_kind = ExitValueArgumentsObjectThatWasNotCreated;
>+        return result;
>+    }
>+    
>     static ExitValue recovery(RecoveryOpcode opcode, unsigned leftArgument, unsigned rightArgument, ValueFormat format)
>     {
>         ExitValue result;
>@@ -146,6 +154,7 @@ public:
>     }
>     bool isConstant() const { return kind() == ExitValueConstant; }
>     bool isArgument() const { return kind() == ExitValueArgument; }
>+    bool isArgumentsObjectThatWasNotCreated() const { return kind() == ExitValueArgumentsObjectThatWasNotCreated; }
>     bool isRecovery() const { return kind() == ExitValueRecovery; }
>     
>     ExitArgument exitArgument() const
>@@ -213,6 +222,7 @@ public:
>         case ExitValueDead:
>         case ExitValueConstant:
>         case ExitValueInJSStack:
>+        case ExitValueArgumentsObjectThatWasNotCreated:
>             return ValueFormatJSValue;
>             
>         case ExitValueArgument:
>Index: Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp	(working copy)
>@@ -282,6 +282,9 @@ private:
>         case WeakJSConstant:
>             compileWeakJSConstant();
>             break;
>+        case PhantomArguments:
>+            compilePhantomArguments();
>+            break;
>         case GetArgument:
>             compileGetArgument();
>             break;
>@@ -781,6 +784,11 @@ private:
>             break;
>         }
>     }
>+
>+    void compilePhantomArguments()
>+    {
>+        setJSValue(m_out.constInt64(JSValue::encode(JSValue())));
>+    }
>     
>     void compileWeakJSConstant()
>     {
>@@ -5519,9 +5527,7 @@ private:
>                 break;
>                 
>             case FlushedArguments:
>-                // FIXME: implement PhantomArguments.
>-                // https://bugs.webkit.org/show_bug.cgi?id=113986
>-                RELEASE_ASSERT_NOT_REACHED();
>+                exit.m_values[i] = ExitValue::argumentsObjectThatWasNotCreated();
>                 break;
>             }
>         }
>@@ -5613,9 +5619,7 @@ private:
>             exit.m_values[index] = ExitValue::constant(m_graph.valueOfJSConstant(node));
>             return true;
>         case PhantomArguments:
>-            // FIXME: implement PhantomArguments.
>-            // https://bugs.webkit.org/show_bug.cgi?id=113986
>-            RELEASE_ASSERT_NOT_REACHED();
>+            exit.m_values[index] = ExitValue::argumentsObjectThatWasNotCreated();
>             return true;
>         default:
>             return false;
>Index: Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp	(working copy)
>@@ -146,6 +146,12 @@ static void compileStub(
>             jit.load64(AssemblyHelpers::addressFor(value.virtualRegister()), GPRInfo::regT0);
>             break;
>             
>+        case ExitValueArgumentsObjectThatWasNotCreated:
>+            // We can't actually recover this yet, but we can make the stack look sane. This is
>+            // a prerequisite to running the actual arguments recovery.
>+            jit.move(MacroAssembler::TrustedImm64(JSValue::encode(JSValue())), GPRInfo::regT0);
>+            break;
>+            
>         case ExitValueRecovery:
>             record->locations[value.rightRecoveryArgument()].restoreInto(
>                 jit, jitCode->stackmaps, registerScratch, GPRInfo::regT1);
>@@ -337,6 +343,15 @@ static void compileStub(
>     
>     handleExitCounts(jit, exit);
>     reifyInlinedCallFrames(jit, exit);
>+    
>+    ArgumentsRecoveryGenerator argumentsRecovery;
>+    for (unsigned index = exit.m_values.size(); index--;) {
>+        if (!exit.m_values[index].isArgumentsObjectThatWasNotCreated())
>+            continue;
>+        int operand = exit.m_values.operandForIndex(index);
>+        argumentsRecovery.generateFor(operand, exit.m_codeOrigin, jit);
>+    }
>+    
>     adjustAndJumpToTarget(jit, exit);
>     
>     LinkBuffer patchBuffer(*vm, &jit, codeBlock);
Comment 7 Whitehat Security 2014-10-25 19:19:56 PDT
Comment on attachment 225498 [details]
the patch

>Index: Source/JavaScriptCore/ChangeLog
>===================================================================
>--- Source/JavaScriptCore/ChangeLog	(revision 164889)
>+++ Source/JavaScriptCore/ChangeLog	(working copy)
>@@ -1,3 +1,38 @@
>+2014-02-28  Filip Pizlo  <fpizlo@apple.com>
>+
>+        FTL should support PhantomArguments
>+        https://bugs.webkit.org/show_bug.cgi?id=113986
>+
>+        Reviewed by NOBODY (OOPS!).
>+        
>+        Adding PhantomArguments to the FTL mostly means wiring the recovery of the Arguments
>+        object into the FTL's OSR exit compiler.
>+
>+        * dfg/DFGOSRExitCompiler32_64.cpp:
>+        (JSC::DFG::OSRExitCompiler::compileExit): move the recovery code to DFGOSRExitCompilerCommon.cpp
>+        * dfg/DFGOSRExitCompiler64.cpp:
>+        (JSC::DFG::OSRExitCompiler::compileExit): move the recovery code to DFGOSRExitCompilerCommon.cpp
>+        * dfg/DFGOSRExitCompilerCommon.cpp:
>+        (JSC::DFG::ArgumentsRecoveryGenerator::ArgumentsRecoveryGenerator):
>+        (JSC::DFG::ArgumentsRecoveryGenerator::~ArgumentsRecoveryGenerator):
>+        (JSC::DFG::ArgumentsRecoveryGenerator::generateFor): this is the common place for the recovery code
>+        * dfg/DFGOSRExitCompilerCommon.h:
>+        * ftl/FTLCapabilities.cpp:
>+        (JSC::FTL::canCompile):
>+        * ftl/FTLExitValue.cpp:
>+        (JSC::FTL::ExitValue::dumpInContext):
>+        * ftl/FTLExitValue.h:
>+        (JSC::FTL::ExitValue::argumentsObjectThatWasNotCreated):
>+        (JSC::FTL::ExitValue::isArgumentsObjectThatWasNotCreated):
>+        (JSC::FTL::ExitValue::valueFormat):
>+        * ftl/FTLLowerDFGToLLVM.cpp:
>+        (JSC::FTL::LowerDFGToLLVM::compileNode):
>+        (JSC::FTL::LowerDFGToLLVM::compilePhantomArguments):
>+        (JSC::FTL::LowerDFGToLLVM::buildExitArguments):
>+        (JSC::FTL::LowerDFGToLLVM::tryToSetConstantExitArgument):
>+        * ftl/FTLOSRExitCompiler.cpp:
>+        (JSC::FTL::compileStub): Call into the ArgumentsRecoveryGenerator
>+
> 2014-02-28  Oliver Hunt  <oliver@apple.com>
> 
>         REGRESSION(r164835): It broke 10 JSC stress test on 32 bit platforms
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -393,66 +393,14 @@ void OSRExitCompiler::compileExit(const 
>     //     registers.
>     
>     if (haveArguments) {
>-        HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>-            NullableHashTraits<InlineCallFrame*>> didCreateArgumentsObject;
>+        ArgumentsRecoveryGenerator argumentsRecovery;
> 
>         for (size_t index = 0; index < operands.size(); ++index) {
>             const ValueRecovery& recovery = operands[index];
>             if (recovery.technique() != ArgumentsThatWereNotCreated)
>                 continue;
>-            int operand = operands.operandForIndex(index);
>-            // Find the right inline call frame.
>-            InlineCallFrame* inlineCallFrame = 0;
>-            for (InlineCallFrame* current = exit.m_codeOrigin.inlineCallFrame;
>-                 current;
>-                 current = current->caller.inlineCallFrame) {
>-                if (current->stackOffset >= operand) {
>-                    inlineCallFrame = current;
>-                    break;
>-                }
>-            }
>-
>-            if (!m_jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>-                continue;
>-            VirtualRegister argumentsRegister = m_jit.baselineArgumentsRegisterFor(inlineCallFrame);
>-            if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>-                // We know this call frame optimized out an arguments object that
>-                // the baseline JIT would have created. Do that creation now.
>-                if (inlineCallFrame) {
>-                    m_jit.setupArgumentsWithExecState(
>-                        AssemblyHelpers::TrustedImmPtr(inlineCallFrame));
>-                    m_jit.move(
>-                        AssemblyHelpers::TrustedImmPtr(
>-                            bitwise_cast<void*>(operationCreateInlinedArguments)),
>-                        GPRInfo::nonArgGPR0);
>-                } else {
>-                    m_jit.setupArgumentsExecState();
>-                    m_jit.move(
>-                        AssemblyHelpers::TrustedImmPtr(
>-                            bitwise_cast<void*>(operationCreateArguments)),
>-                        GPRInfo::nonArgGPR0);
>-                }
>-                m_jit.call(GPRInfo::nonArgGPR0);
>-                m_jit.store32(
>-                    AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                    AssemblyHelpers::tagFor(argumentsRegister));
>-                m_jit.store32(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::payloadFor(argumentsRegister));
>-                m_jit.store32(
>-                    AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                    AssemblyHelpers::tagFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.store32(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::payloadFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>-            }
>-
>-            m_jit.load32(AssemblyHelpers::payloadFor(argumentsRegister), GPRInfo::regT0);
>-            m_jit.store32(
>-                AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                AssemblyHelpers::tagFor(operand));
>-            m_jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor(operand));
>+            argumentsRecovery.generateFor(
>+                operands.operandForIndex(index), exit.m_codeOrigin, m_jit);
>         }
>     }
> 
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -365,50 +365,14 @@ void OSRExitCompiler::compileExit(const 
>     //     registers.
>     
>     if (haveArguments) {
>-        HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>-            NullableHashTraits<InlineCallFrame*>> didCreateArgumentsObject;
>+        ArgumentsRecoveryGenerator argumentsRecovery;
> 
>         for (size_t index = 0; index < operands.size(); ++index) {
>             const ValueRecovery& recovery = operands[index];
>             if (recovery.technique() != ArgumentsThatWereNotCreated)
>                 continue;
>-            int operand = operands.operandForIndex(index);
>-            // Find the right inline call frame.
>-            InlineCallFrame* inlineCallFrame = 0;
>-            for (InlineCallFrame* current = exit.m_codeOrigin.inlineCallFrame;
>-                 current;
>-                 current = current->caller.inlineCallFrame) {
>-                if (current->stackOffset >= operand) {
>-                    inlineCallFrame = current;
>-                    break;
>-                }
>-            }
>-
>-            if (!m_jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>-                continue;
>-            VirtualRegister argumentsRegister = m_jit.baselineArgumentsRegisterFor(inlineCallFrame);
>-            if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>-                // We know this call frame optimized out an arguments object that
>-                // the baseline JIT would have created. Do that creation now.
>-                if (inlineCallFrame) {
>-                    m_jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0);
>-                    m_jit.setupArguments(GPRInfo::regT0);
>-                } else
>-                    m_jit.setupArgumentsExecState();
>-                m_jit.move(
>-                    AssemblyHelpers::TrustedImmPtr(
>-                        bitwise_cast<void*>(operationCreateArguments)),
>-                    GPRInfo::nonArgGPR0);
>-                m_jit.call(GPRInfo::nonArgGPR0);
>-                m_jit.store64(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(argumentsRegister));
>-                m_jit.store64(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>-            }
>-
>-            m_jit.load64(AssemblyHelpers::addressFor(argumentsRegister), GPRInfo::regT0);
>-            m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
>+            argumentsRecovery.generateFor(
>+                operands.operandForIndex(index), exit.m_codeOrigin, m_jit);
>         }
>     }
> 
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -217,6 +217,89 @@ void adjustAndJumpToTarget(CCallHelpers&
>     jit.jump(GPRInfo::regT2);
> }
> 
>+ArgumentsRecoveryGenerator::ArgumentsRecoveryGenerator() { }
>+ArgumentsRecoveryGenerator::~ArgumentsRecoveryGenerator() { }
>+
>+void ArgumentsRecoveryGenerator::generateFor(
>+    int operand, CodeOrigin codeOrigin, CCallHelpers& jit)
>+{
>+    // Find the right inline call frame.
>+    InlineCallFrame* inlineCallFrame = 0;
>+    for (InlineCallFrame* current = codeOrigin.inlineCallFrame;
>+         current;
>+         current = current->caller.inlineCallFrame) {
>+        if (current->stackOffset >= operand) {
>+            inlineCallFrame = current;
>+            break;
>+        }
>+    }
>+
>+    if (!jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>+        return;
>+    VirtualRegister argumentsRegister = jit.baselineArgumentsRegisterFor(inlineCallFrame);
>+    if (m_didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>+        // We know this call frame optimized out an arguments object that
>+        // the baseline JIT would have created. Do that creation now.
>+#if USE(JSVALUE64)
>+        if (inlineCallFrame) {
>+            jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0);
>+            jit.setupArguments(GPRInfo::regT0);
>+        } else
>+            jit.setupArgumentsExecState();
>+        jit.move(
>+            AssemblyHelpers::TrustedImmPtr(
>+                bitwise_cast<void*>(operationCreateArguments)),
>+            GPRInfo::nonArgGPR0);
>+        jit.call(GPRInfo::nonArgGPR0);
>+        jit.store64(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(argumentsRegister));
>+        jit.store64(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>+#else // USE(JSVALUE64) -> so the 32_64 part
>+        if (inlineCallFrame) {
>+            jit.setupArgumentsWithExecState(
>+                AssemblyHelpers::TrustedImmPtr(inlineCallFrame));
>+            jit.move(
>+                AssemblyHelpers::TrustedImmPtr(
>+                    bitwise_cast<void*>(operationCreateInlinedArguments)),
>+                GPRInfo::nonArgGPR0);
>+        } else {
>+            jit.setupArgumentsExecState();
>+            jit.move(
>+                AssemblyHelpers::TrustedImmPtr(
>+                    bitwise_cast<void*>(operationCreateArguments)),
>+                GPRInfo::nonArgGPR0);
>+        }
>+        jit.call(GPRInfo::nonArgGPR0);
>+        jit.store32(
>+            AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+            AssemblyHelpers::tagFor(argumentsRegister));
>+        jit.store32(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::payloadFor(argumentsRegister));
>+        jit.store32(
>+            AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+            AssemblyHelpers::tagFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.store32(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::payloadFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>+#endif // USE(JSVALUE64)
>+    }
>+
>+#if USE(JSVALUE64)
>+    jit.load64(AssemblyHelpers::addressFor(argumentsRegister), GPRInfo::regT0);
>+    jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
>+#else // USE(JSVALUE64) -> so the 32_64 part
>+    jit.load32(AssemblyHelpers::payloadFor(argumentsRegister), GPRInfo::regT0);
>+    jit.store32(
>+        AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+        AssemblyHelpers::tagFor(operand));
>+    jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor(operand));
>+#endif // USE(JSVALUE64)
>+}
>+    
> } } // namespace JSC::DFG
> 
> #endif // ENABLE(DFG_JIT)
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h	(working copy)
>@@ -37,6 +37,18 @@ void handleExitCounts(CCallHelpers&, con
> void reifyInlinedCallFrames(CCallHelpers&, const OSRExitBase&);
> void adjustAndJumpToTarget(CCallHelpers&, const OSRExitBase&);
> 
>+class ArgumentsRecoveryGenerator {
>+public:
>+    ArgumentsRecoveryGenerator();
>+    ~ArgumentsRecoveryGenerator();
>+    
>+    void generateFor(int operand, CodeOrigin, CCallHelpers&);
>+    
>+private:
>+    HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>+        NullableHashTraits<InlineCallFrame*>> m_didCreateArgumentsObject;
>+};
>+
> } } // namespace JSC::DFG
> 
> #endif // ENABLE(DFG_JIT)
>Index: Source/JavaScriptCore/ftl/FTLCapabilities.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLCapabilities.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLCapabilities.cpp	(working copy)
>@@ -140,6 +140,7 @@ inline CapabilityLevel canCompile(Node* 
>     case MultiGetByOffset:
>     case MultiPutByOffset:
>     case ToPrimitive:
>+    case PhantomArguments:
>         // These are OK.
>         break;
>     case PutByIdDirect:
>Index: Source/JavaScriptCore/ftl/FTLExitValue.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLExitValue.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLExitValue.cpp	(working copy)
>@@ -59,6 +59,9 @@ void ExitValue::dumpInContext(PrintStrea
>     case ExitValueInJSStackAsDouble:
>         out.print("InJSStackAsDouble:r", virtualRegister());
>         return;
>+    case ExitValueArgumentsObjectThatWasNotCreated:
>+        out.print("ArgumentsObjectThatWasNotCreated");
>+        return;
>     case ExitValueRecovery:
>         out.print("Recovery(", recoveryOpcode(), ", arg", leftRecoveryArgument(), ", arg", rightRecoveryArgument(), ", ", recoveryFormat(), ")");
>         return;
>Index: Source/JavaScriptCore/ftl/FTLExitValue.h
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLExitValue.h	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLExitValue.h	(working copy)
>@@ -51,6 +51,7 @@ enum ExitValueKind {
>     ExitValueInJSStackAsInt32,
>     ExitValueInJSStackAsInt52,
>     ExitValueInJSStackAsDouble,
>+    ExitValueArgumentsObjectThatWasNotCreated,
>     ExitValueRecovery
> };
> 
>@@ -118,6 +119,13 @@ public:
>         return result;
>     }
>     
>+    static ExitValue argumentsObjectThatWasNotCreated()
>+    {
>+        ExitValue result;
>+        result.m_kind = ExitValueArgumentsObjectThatWasNotCreated;
>+        return result;
>+    }
>+    
>     static ExitValue recovery(RecoveryOpcode opcode, unsigned leftArgument, unsigned rightArgument, ValueFormat format)
>     {
>         ExitValue result;
>@@ -146,6 +154,7 @@ public:
>     }
>     bool isConstant() const { return kind() == ExitValueConstant; }
>     bool isArgument() const { return kind() == ExitValueArgument; }
>+    bool isArgumentsObjectThatWasNotCreated() const { return kind() == ExitValueArgumentsObjectThatWasNotCreated; }
>     bool isRecovery() const { return kind() == ExitValueRecovery; }
>     
>     ExitArgument exitArgument() const
>@@ -213,6 +222,7 @@ public:
>         case ExitValueDead:
>         case ExitValueConstant:
>         case ExitValueInJSStack:
>+        case ExitValueArgumentsObjectThatWasNotCreated:
>             return ValueFormatJSValue;
>             
>         case ExitValueArgument:
>Index: Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp	(working copy)
>@@ -282,6 +282,9 @@ private:
>         case WeakJSConstant:
>             compileWeakJSConstant();
>             break;
>+        case PhantomArguments:
>+            compilePhantomArguments();
>+            break;
>         case GetArgument:
>             compileGetArgument();
>             break;
>@@ -781,6 +784,11 @@ private:
>             break;
>         }
>     }
>+
>+    void compilePhantomArguments()
>+    {
>+        setJSValue(m_out.constInt64(JSValue::encode(JSValue())));
>+    }
>     
>     void compileWeakJSConstant()
>     {
>@@ -5519,9 +5527,7 @@ private:
>                 break;
>                 
>             case FlushedArguments:
>-                // FIXME: implement PhantomArguments.
>-                // https://bugs.webkit.org/show_bug.cgi?id=113986
>-                RELEASE_ASSERT_NOT_REACHED();
>+                exit.m_values[i] = ExitValue::argumentsObjectThatWasNotCreated();
>                 break;
>             }
>         }
>@@ -5613,9 +5619,7 @@ private:
>             exit.m_values[index] = ExitValue::constant(m_graph.valueOfJSConstant(node));
>             return true;
>         case PhantomArguments:
>-            // FIXME: implement PhantomArguments.
>-            // https://bugs.webkit.org/show_bug.cgi?id=113986
>-            RELEASE_ASSERT_NOT_REACHED();
>+            exit.m_values[index] = ExitValue::argumentsObjectThatWasNotCreated();
>             return true;
>         default:
>             return false;
>Index: Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp	(working copy)
>@@ -146,6 +146,12 @@ static void compileStub(
>             jit.load64(AssemblyHelpers::addressFor(value.virtualRegister()), GPRInfo::regT0);
>             break;
>             
>+        case ExitValueArgumentsObjectThatWasNotCreated:
>+            // We can't actually recover this yet, but we can make the stack look sane. This is
>+            // a prerequisite to running the actual arguments recovery.
>+            jit.move(MacroAssembler::TrustedImm64(JSValue::encode(JSValue())), GPRInfo::regT0);
>+            break;
>+            
>         case ExitValueRecovery:
>             record->locations[value.rightRecoveryArgument()].restoreInto(
>                 jit, jitCode->stackmaps, registerScratch, GPRInfo::regT1);
>@@ -337,6 +343,15 @@ static void compileStub(
>     
>     handleExitCounts(jit, exit);
>     reifyInlinedCallFrames(jit, exit);
>+    
>+    ArgumentsRecoveryGenerator argumentsRecovery;
>+    for (unsigned index = exit.m_values.size(); index--;) {
>+        if (!exit.m_values[index].isArgumentsObjectThatWasNotCreated())
>+            continue;
>+        int operand = exit.m_values.operandForIndex(index);
>+        argumentsRecovery.generateFor(operand, exit.m_codeOrigin, jit);
>+    }
>+    
>     adjustAndJumpToTarget(jit, exit);
>     
>     LinkBuffer patchBuffer(*vm, &jit, codeBlock);
Comment 8 Whitehat Security 2014-10-25 19:20:00 PDT
Comment on attachment 225498 [details]
the patch

>Index: Source/JavaScriptCore/ChangeLog
>===================================================================
>--- Source/JavaScriptCore/ChangeLog	(revision 164889)
>+++ Source/JavaScriptCore/ChangeLog	(working copy)
>@@ -1,3 +1,38 @@
>+2014-02-28  Filip Pizlo  <fpizlo@apple.com>
>+
>+        FTL should support PhantomArguments
>+        https://bugs.webkit.org/show_bug.cgi?id=113986
>+
>+        Reviewed by NOBODY (OOPS!).
>+        
>+        Adding PhantomArguments to the FTL mostly means wiring the recovery of the Arguments
>+        object into the FTL's OSR exit compiler.
>+
>+        * dfg/DFGOSRExitCompiler32_64.cpp:
>+        (JSC::DFG::OSRExitCompiler::compileExit): move the recovery code to DFGOSRExitCompilerCommon.cpp
>+        * dfg/DFGOSRExitCompiler64.cpp:
>+        (JSC::DFG::OSRExitCompiler::compileExit): move the recovery code to DFGOSRExitCompilerCommon.cpp
>+        * dfg/DFGOSRExitCompilerCommon.cpp:
>+        (JSC::DFG::ArgumentsRecoveryGenerator::ArgumentsRecoveryGenerator):
>+        (JSC::DFG::ArgumentsRecoveryGenerator::~ArgumentsRecoveryGenerator):
>+        (JSC::DFG::ArgumentsRecoveryGenerator::generateFor): this is the common place for the recovery code
>+        * dfg/DFGOSRExitCompilerCommon.h:
>+        * ftl/FTLCapabilities.cpp:
>+        (JSC::FTL::canCompile):
>+        * ftl/FTLExitValue.cpp:
>+        (JSC::FTL::ExitValue::dumpInContext):
>+        * ftl/FTLExitValue.h:
>+        (JSC::FTL::ExitValue::argumentsObjectThatWasNotCreated):
>+        (JSC::FTL::ExitValue::isArgumentsObjectThatWasNotCreated):
>+        (JSC::FTL::ExitValue::valueFormat):
>+        * ftl/FTLLowerDFGToLLVM.cpp:
>+        (JSC::FTL::LowerDFGToLLVM::compileNode):
>+        (JSC::FTL::LowerDFGToLLVM::compilePhantomArguments):
>+        (JSC::FTL::LowerDFGToLLVM::buildExitArguments):
>+        (JSC::FTL::LowerDFGToLLVM::tryToSetConstantExitArgument):
>+        * ftl/FTLOSRExitCompiler.cpp:
>+        (JSC::FTL::compileStub): Call into the ArgumentsRecoveryGenerator
>+
> 2014-02-28  Oliver Hunt  <oliver@apple.com>
> 
>         REGRESSION(r164835): It broke 10 JSC stress test on 32 bit platforms
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -393,66 +393,14 @@ void OSRExitCompiler::compileExit(const 
>     //     registers.
>     
>     if (haveArguments) {
>-        HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>-            NullableHashTraits<InlineCallFrame*>> didCreateArgumentsObject;
>+        ArgumentsRecoveryGenerator argumentsRecovery;
> 
>         for (size_t index = 0; index < operands.size(); ++index) {
>             const ValueRecovery& recovery = operands[index];
>             if (recovery.technique() != ArgumentsThatWereNotCreated)
>                 continue;
>-            int operand = operands.operandForIndex(index);
>-            // Find the right inline call frame.
>-            InlineCallFrame* inlineCallFrame = 0;
>-            for (InlineCallFrame* current = exit.m_codeOrigin.inlineCallFrame;
>-                 current;
>-                 current = current->caller.inlineCallFrame) {
>-                if (current->stackOffset >= operand) {
>-                    inlineCallFrame = current;
>-                    break;
>-                }
>-            }
>-
>-            if (!m_jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>-                continue;
>-            VirtualRegister argumentsRegister = m_jit.baselineArgumentsRegisterFor(inlineCallFrame);
>-            if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>-                // We know this call frame optimized out an arguments object that
>-                // the baseline JIT would have created. Do that creation now.
>-                if (inlineCallFrame) {
>-                    m_jit.setupArgumentsWithExecState(
>-                        AssemblyHelpers::TrustedImmPtr(inlineCallFrame));
>-                    m_jit.move(
>-                        AssemblyHelpers::TrustedImmPtr(
>-                            bitwise_cast<void*>(operationCreateInlinedArguments)),
>-                        GPRInfo::nonArgGPR0);
>-                } else {
>-                    m_jit.setupArgumentsExecState();
>-                    m_jit.move(
>-                        AssemblyHelpers::TrustedImmPtr(
>-                            bitwise_cast<void*>(operationCreateArguments)),
>-                        GPRInfo::nonArgGPR0);
>-                }
>-                m_jit.call(GPRInfo::nonArgGPR0);
>-                m_jit.store32(
>-                    AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                    AssemblyHelpers::tagFor(argumentsRegister));
>-                m_jit.store32(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::payloadFor(argumentsRegister));
>-                m_jit.store32(
>-                    AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                    AssemblyHelpers::tagFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.store32(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::payloadFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>-            }
>-
>-            m_jit.load32(AssemblyHelpers::payloadFor(argumentsRegister), GPRInfo::regT0);
>-            m_jit.store32(
>-                AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                AssemblyHelpers::tagFor(operand));
>-            m_jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor(operand));
>+            argumentsRecovery.generateFor(
>+                operands.operandForIndex(index), exit.m_codeOrigin, m_jit);
>         }
>     }
> 
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -365,50 +365,14 @@ void OSRExitCompiler::compileExit(const 
>     //     registers.
>     
>     if (haveArguments) {
>-        HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>-            NullableHashTraits<InlineCallFrame*>> didCreateArgumentsObject;
>+        ArgumentsRecoveryGenerator argumentsRecovery;
> 
>         for (size_t index = 0; index < operands.size(); ++index) {
>             const ValueRecovery& recovery = operands[index];
>             if (recovery.technique() != ArgumentsThatWereNotCreated)
>                 continue;
>-            int operand = operands.operandForIndex(index);
>-            // Find the right inline call frame.
>-            InlineCallFrame* inlineCallFrame = 0;
>-            for (InlineCallFrame* current = exit.m_codeOrigin.inlineCallFrame;
>-                 current;
>-                 current = current->caller.inlineCallFrame) {
>-                if (current->stackOffset >= operand) {
>-                    inlineCallFrame = current;
>-                    break;
>-                }
>-            }
>-
>-            if (!m_jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>-                continue;
>-            VirtualRegister argumentsRegister = m_jit.baselineArgumentsRegisterFor(inlineCallFrame);
>-            if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>-                // We know this call frame optimized out an arguments object that
>-                // the baseline JIT would have created. Do that creation now.
>-                if (inlineCallFrame) {
>-                    m_jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0);
>-                    m_jit.setupArguments(GPRInfo::regT0);
>-                } else
>-                    m_jit.setupArgumentsExecState();
>-                m_jit.move(
>-                    AssemblyHelpers::TrustedImmPtr(
>-                        bitwise_cast<void*>(operationCreateArguments)),
>-                    GPRInfo::nonArgGPR0);
>-                m_jit.call(GPRInfo::nonArgGPR0);
>-                m_jit.store64(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(argumentsRegister));
>-                m_jit.store64(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>-            }
>-
>-            m_jit.load64(AssemblyHelpers::addressFor(argumentsRegister), GPRInfo::regT0);
>-            m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
>+            argumentsRecovery.generateFor(
>+                operands.operandForIndex(index), exit.m_codeOrigin, m_jit);
>         }
>     }
> 
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -217,6 +217,89 @@ void adjustAndJumpToTarget(CCallHelpers&
>     jit.jump(GPRInfo::regT2);
> }
> 
>+ArgumentsRecoveryGenerator::ArgumentsRecoveryGenerator() { }
>+ArgumentsRecoveryGenerator::~ArgumentsRecoveryGenerator() { }
>+
>+void ArgumentsRecoveryGenerator::generateFor(
>+    int operand, CodeOrigin codeOrigin, CCallHelpers& jit)
>+{
>+    // Find the right inline call frame.
>+    InlineCallFrame* inlineCallFrame = 0;
>+    for (InlineCallFrame* current = codeOrigin.inlineCallFrame;
>+         current;
>+         current = current->caller.inlineCallFrame) {
>+        if (current->stackOffset >= operand) {
>+            inlineCallFrame = current;
>+            break;
>+        }
>+    }
>+
>+    if (!jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>+        return;
>+    VirtualRegister argumentsRegister = jit.baselineArgumentsRegisterFor(inlineCallFrame);
>+    if (m_didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>+        // We know this call frame optimized out an arguments object that
>+        // the baseline JIT would have created. Do that creation now.
>+#if USE(JSVALUE64)
>+        if (inlineCallFrame) {
>+            jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0);
>+            jit.setupArguments(GPRInfo::regT0);
>+        } else
>+            jit.setupArgumentsExecState();
>+        jit.move(
>+            AssemblyHelpers::TrustedImmPtr(
>+                bitwise_cast<void*>(operationCreateArguments)),
>+            GPRInfo::nonArgGPR0);
>+        jit.call(GPRInfo::nonArgGPR0);
>+        jit.store64(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(argumentsRegister));
>+        jit.store64(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>+#else // USE(JSVALUE64) -> so the 32_64 part
>+        if (inlineCallFrame) {
>+            jit.setupArgumentsWithExecState(
>+                AssemblyHelpers::TrustedImmPtr(inlineCallFrame));
>+            jit.move(
>+                AssemblyHelpers::TrustedImmPtr(
>+                    bitwise_cast<void*>(operationCreateInlinedArguments)),
>+                GPRInfo::nonArgGPR0);
>+        } else {
>+            jit.setupArgumentsExecState();
>+            jit.move(
>+                AssemblyHelpers::TrustedImmPtr(
>+                    bitwise_cast<void*>(operationCreateArguments)),
>+                GPRInfo::nonArgGPR0);
>+        }
>+        jit.call(GPRInfo::nonArgGPR0);
>+        jit.store32(
>+            AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+            AssemblyHelpers::tagFor(argumentsRegister));
>+        jit.store32(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::payloadFor(argumentsRegister));
>+        jit.store32(
>+            AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+            AssemblyHelpers::tagFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.store32(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::payloadFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>+#endif // USE(JSVALUE64)
>+    }
>+
>+#if USE(JSVALUE64)
>+    jit.load64(AssemblyHelpers::addressFor(argumentsRegister), GPRInfo::regT0);
>+    jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
>+#else // USE(JSVALUE64) -> so the 32_64 part
>+    jit.load32(AssemblyHelpers::payloadFor(argumentsRegister), GPRInfo::regT0);
>+    jit.store32(
>+        AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+        AssemblyHelpers::tagFor(operand));
>+    jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor(operand));
>+#endif // USE(JSVALUE64)
>+}
>+    
> } } // namespace JSC::DFG
> 
> #endif // ENABLE(DFG_JIT)
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h	(working copy)
>@@ -37,6 +37,18 @@ void handleExitCounts(CCallHelpers&, con
> void reifyInlinedCallFrames(CCallHelpers&, const OSRExitBase&);
> void adjustAndJumpToTarget(CCallHelpers&, const OSRExitBase&);
> 
>+class ArgumentsRecoveryGenerator {
>+public:
>+    ArgumentsRecoveryGenerator();
>+    ~ArgumentsRecoveryGenerator();
>+    
>+    void generateFor(int operand, CodeOrigin, CCallHelpers&);
>+    
>+private:
>+    HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>+        NullableHashTraits<InlineCallFrame*>> m_didCreateArgumentsObject;
>+};
>+
> } } // namespace JSC::DFG
> 
> #endif // ENABLE(DFG_JIT)
>Index: Source/JavaScriptCore/ftl/FTLCapabilities.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLCapabilities.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLCapabilities.cpp	(working copy)
>@@ -140,6 +140,7 @@ inline CapabilityLevel canCompile(Node* 
>     case MultiGetByOffset:
>     case MultiPutByOffset:
>     case ToPrimitive:
>+    case PhantomArguments:
>         // These are OK.
>         break;
>     case PutByIdDirect:
>Index: Source/JavaScriptCore/ftl/FTLExitValue.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLExitValue.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLExitValue.cpp	(working copy)
>@@ -59,6 +59,9 @@ void ExitValue::dumpInContext(PrintStrea
>     case ExitValueInJSStackAsDouble:
>         out.print("InJSStackAsDouble:r", virtualRegister());
>         return;
>+    case ExitValueArgumentsObjectThatWasNotCreated:
>+        out.print("ArgumentsObjectThatWasNotCreated");
>+        return;
>     case ExitValueRecovery:
>         out.print("Recovery(", recoveryOpcode(), ", arg", leftRecoveryArgument(), ", arg", rightRecoveryArgument(), ", ", recoveryFormat(), ")");
>         return;
>Index: Source/JavaScriptCore/ftl/FTLExitValue.h
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLExitValue.h	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLExitValue.h	(working copy)
>@@ -51,6 +51,7 @@ enum ExitValueKind {
>     ExitValueInJSStackAsInt32,
>     ExitValueInJSStackAsInt52,
>     ExitValueInJSStackAsDouble,
>+    ExitValueArgumentsObjectThatWasNotCreated,
>     ExitValueRecovery
> };
> 
>@@ -118,6 +119,13 @@ public:
>         return result;
>     }
>     
>+    static ExitValue argumentsObjectThatWasNotCreated()
>+    {
>+        ExitValue result;
>+        result.m_kind = ExitValueArgumentsObjectThatWasNotCreated;
>+        return result;
>+    }
>+    
>     static ExitValue recovery(RecoveryOpcode opcode, unsigned leftArgument, unsigned rightArgument, ValueFormat format)
>     {
>         ExitValue result;
>@@ -146,6 +154,7 @@ public:
>     }
>     bool isConstant() const { return kind() == ExitValueConstant; }
>     bool isArgument() const { return kind() == ExitValueArgument; }
>+    bool isArgumentsObjectThatWasNotCreated() const { return kind() == ExitValueArgumentsObjectThatWasNotCreated; }
>     bool isRecovery() const { return kind() == ExitValueRecovery; }
>     
>     ExitArgument exitArgument() const
>@@ -213,6 +222,7 @@ public:
>         case ExitValueDead:
>         case ExitValueConstant:
>         case ExitValueInJSStack:
>+        case ExitValueArgumentsObjectThatWasNotCreated:
>             return ValueFormatJSValue;
>             
>         case ExitValueArgument:
>Index: Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp	(working copy)
>@@ -282,6 +282,9 @@ private:
>         case WeakJSConstant:
>             compileWeakJSConstant();
>             break;
>+        case PhantomArguments:
>+            compilePhantomArguments();
>+            break;
>         case GetArgument:
>             compileGetArgument();
>             break;
>@@ -781,6 +784,11 @@ private:
>             break;
>         }
>     }
>+
>+    void compilePhantomArguments()
>+    {
>+        setJSValue(m_out.constInt64(JSValue::encode(JSValue())));
>+    }
>     
>     void compileWeakJSConstant()
>     {
>@@ -5519,9 +5527,7 @@ private:
>                 break;
>                 
>             case FlushedArguments:
>-                // FIXME: implement PhantomArguments.
>-                // https://bugs.webkit.org/show_bug.cgi?id=113986
>-                RELEASE_ASSERT_NOT_REACHED();
>+                exit.m_values[i] = ExitValue::argumentsObjectThatWasNotCreated();
>                 break;
>             }
>         }
>@@ -5613,9 +5619,7 @@ private:
>             exit.m_values[index] = ExitValue::constant(m_graph.valueOfJSConstant(node));
>             return true;
>         case PhantomArguments:
>-            // FIXME: implement PhantomArguments.
>-            // https://bugs.webkit.org/show_bug.cgi?id=113986
>-            RELEASE_ASSERT_NOT_REACHED();
>+            exit.m_values[index] = ExitValue::argumentsObjectThatWasNotCreated();
>             return true;
>         default:
>             return false;
>Index: Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp	(working copy)
>@@ -146,6 +146,12 @@ static void compileStub(
>             jit.load64(AssemblyHelpers::addressFor(value.virtualRegister()), GPRInfo::regT0);
>             break;
>             
>+        case ExitValueArgumentsObjectThatWasNotCreated:
>+            // We can't actually recover this yet, but we can make the stack look sane. This is
>+            // a prerequisite to running the actual arguments recovery.
>+            jit.move(MacroAssembler::TrustedImm64(JSValue::encode(JSValue())), GPRInfo::regT0);
>+            break;
>+            
>         case ExitValueRecovery:
>             record->locations[value.rightRecoveryArgument()].restoreInto(
>                 jit, jitCode->stackmaps, registerScratch, GPRInfo::regT1);
>@@ -337,6 +343,15 @@ static void compileStub(
>     
>     handleExitCounts(jit, exit);
>     reifyInlinedCallFrames(jit, exit);
>+    
>+    ArgumentsRecoveryGenerator argumentsRecovery;
>+    for (unsigned index = exit.m_values.size(); index--;) {
>+        if (!exit.m_values[index].isArgumentsObjectThatWasNotCreated())
>+            continue;
>+        int operand = exit.m_values.operandForIndex(index);
>+        argumentsRecovery.generateFor(operand, exit.m_codeOrigin, jit);
>+    }
>+    
>     adjustAndJumpToTarget(jit, exit);
>     
>     LinkBuffer patchBuffer(*vm, &jit, codeBlock);
Comment 9 Whitehat Security 2014-11-07 07:47:46 PST
Comment on attachment 225498 [details]
the patch

>Index: Source/JavaScriptCore/ChangeLog
>===================================================================
>--- Source/JavaScriptCore/ChangeLog	(revision 164889)
>+++ Source/JavaScriptCore/ChangeLog	(working copy)
>@@ -1,3 +1,38 @@
>+2014-02-28  Filip Pizlo  <fpizlo@apple.com>
>+
>+        FTL should support PhantomArguments
>+        https://bugs.webkit.org/show_bug.cgi?id=113986
>+
>+        Reviewed by NOBODY (OOPS!).
>+        
>+        Adding PhantomArguments to the FTL mostly means wiring the recovery of the Arguments
>+        object into the FTL's OSR exit compiler.
>+
>+        * dfg/DFGOSRExitCompiler32_64.cpp:
>+        (JSC::DFG::OSRExitCompiler::compileExit): move the recovery code to DFGOSRExitCompilerCommon.cpp
>+        * dfg/DFGOSRExitCompiler64.cpp:
>+        (JSC::DFG::OSRExitCompiler::compileExit): move the recovery code to DFGOSRExitCompilerCommon.cpp
>+        * dfg/DFGOSRExitCompilerCommon.cpp:
>+        (JSC::DFG::ArgumentsRecoveryGenerator::ArgumentsRecoveryGenerator):
>+        (JSC::DFG::ArgumentsRecoveryGenerator::~ArgumentsRecoveryGenerator):
>+        (JSC::DFG::ArgumentsRecoveryGenerator::generateFor): this is the common place for the recovery code
>+        * dfg/DFGOSRExitCompilerCommon.h:
>+        * ftl/FTLCapabilities.cpp:
>+        (JSC::FTL::canCompile):
>+        * ftl/FTLExitValue.cpp:
>+        (JSC::FTL::ExitValue::dumpInContext):
>+        * ftl/FTLExitValue.h:
>+        (JSC::FTL::ExitValue::argumentsObjectThatWasNotCreated):
>+        (JSC::FTL::ExitValue::isArgumentsObjectThatWasNotCreated):
>+        (JSC::FTL::ExitValue::valueFormat):
>+        * ftl/FTLLowerDFGToLLVM.cpp:
>+        (JSC::FTL::LowerDFGToLLVM::compileNode):
>+        (JSC::FTL::LowerDFGToLLVM::compilePhantomArguments):
>+        (JSC::FTL::LowerDFGToLLVM::buildExitArguments):
>+        (JSC::FTL::LowerDFGToLLVM::tryToSetConstantExitArgument):
>+        * ftl/FTLOSRExitCompiler.cpp:
>+        (JSC::FTL::compileStub): Call into the ArgumentsRecoveryGenerator
>+
> 2014-02-28  Oliver Hunt  <oliver@apple.com>
> 
>         REGRESSION(r164835): It broke 10 JSC stress test on 32 bit platforms
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -393,66 +393,14 @@ void OSRExitCompiler::compileExit(const 
>     //     registers.
>     
>     if (haveArguments) {
>-        HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>-            NullableHashTraits<InlineCallFrame*>> didCreateArgumentsObject;
>+        ArgumentsRecoveryGenerator argumentsRecovery;
> 
>         for (size_t index = 0; index < operands.size(); ++index) {
>             const ValueRecovery& recovery = operands[index];
>             if (recovery.technique() != ArgumentsThatWereNotCreated)
>                 continue;
>-            int operand = operands.operandForIndex(index);
>-            // Find the right inline call frame.
>-            InlineCallFrame* inlineCallFrame = 0;
>-            for (InlineCallFrame* current = exit.m_codeOrigin.inlineCallFrame;
>-                 current;
>-                 current = current->caller.inlineCallFrame) {
>-                if (current->stackOffset >= operand) {
>-                    inlineCallFrame = current;
>-                    break;
>-                }
>-            }
>-
>-            if (!m_jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>-                continue;
>-            VirtualRegister argumentsRegister = m_jit.baselineArgumentsRegisterFor(inlineCallFrame);
>-            if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>-                // We know this call frame optimized out an arguments object that
>-                // the baseline JIT would have created. Do that creation now.
>-                if (inlineCallFrame) {
>-                    m_jit.setupArgumentsWithExecState(
>-                        AssemblyHelpers::TrustedImmPtr(inlineCallFrame));
>-                    m_jit.move(
>-                        AssemblyHelpers::TrustedImmPtr(
>-                            bitwise_cast<void*>(operationCreateInlinedArguments)),
>-                        GPRInfo::nonArgGPR0);
>-                } else {
>-                    m_jit.setupArgumentsExecState();
>-                    m_jit.move(
>-                        AssemblyHelpers::TrustedImmPtr(
>-                            bitwise_cast<void*>(operationCreateArguments)),
>-                        GPRInfo::nonArgGPR0);
>-                }
>-                m_jit.call(GPRInfo::nonArgGPR0);
>-                m_jit.store32(
>-                    AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                    AssemblyHelpers::tagFor(argumentsRegister));
>-                m_jit.store32(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::payloadFor(argumentsRegister));
>-                m_jit.store32(
>-                    AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                    AssemblyHelpers::tagFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.store32(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::payloadFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>-            }
>-
>-            m_jit.load32(AssemblyHelpers::payloadFor(argumentsRegister), GPRInfo::regT0);
>-            m_jit.store32(
>-                AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                AssemblyHelpers::tagFor(operand));
>-            m_jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor(operand));
>+            argumentsRecovery.generateFor(
>+                operands.operandForIndex(index), exit.m_codeOrigin, m_jit);
>         }
>     }
> 
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -365,50 +365,14 @@ void OSRExitCompiler::compileExit(const 
>     //     registers.
>     
>     if (haveArguments) {
>-        HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>-            NullableHashTraits<InlineCallFrame*>> didCreateArgumentsObject;
>+        ArgumentsRecoveryGenerator argumentsRecovery;
> 
>         for (size_t index = 0; index < operands.size(); ++index) {
>             const ValueRecovery& recovery = operands[index];
>             if (recovery.technique() != ArgumentsThatWereNotCreated)
>                 continue;
>-            int operand = operands.operandForIndex(index);
>-            // Find the right inline call frame.
>-            InlineCallFrame* inlineCallFrame = 0;
>-            for (InlineCallFrame* current = exit.m_codeOrigin.inlineCallFrame;
>-                 current;
>-                 current = current->caller.inlineCallFrame) {
>-                if (current->stackOffset >= operand) {
>-                    inlineCallFrame = current;
>-                    break;
>-                }
>-            }
>-
>-            if (!m_jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>-                continue;
>-            VirtualRegister argumentsRegister = m_jit.baselineArgumentsRegisterFor(inlineCallFrame);
>-            if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>-                // We know this call frame optimized out an arguments object that
>-                // the baseline JIT would have created. Do that creation now.
>-                if (inlineCallFrame) {
>-                    m_jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0);
>-                    m_jit.setupArguments(GPRInfo::regT0);
>-                } else
>-                    m_jit.setupArgumentsExecState();
>-                m_jit.move(
>-                    AssemblyHelpers::TrustedImmPtr(
>-                        bitwise_cast<void*>(operationCreateArguments)),
>-                    GPRInfo::nonArgGPR0);
>-                m_jit.call(GPRInfo::nonArgGPR0);
>-                m_jit.store64(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(argumentsRegister));
>-                m_jit.store64(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>-            }
>-
>-            m_jit.load64(AssemblyHelpers::addressFor(argumentsRegister), GPRInfo::regT0);
>-            m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
>+            argumentsRecovery.generateFor(
>+                operands.operandForIndex(index), exit.m_codeOrigin, m_jit);
>         }
>     }
> 
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -217,6 +217,89 @@ void adjustAndJumpToTarget(CCallHelpers&
>     jit.jump(GPRInfo::regT2);
> }
> 
>+ArgumentsRecoveryGenerator::ArgumentsRecoveryGenerator() { }
>+ArgumentsRecoveryGenerator::~ArgumentsRecoveryGenerator() { }
>+
>+void ArgumentsRecoveryGenerator::generateFor(
>+    int operand, CodeOrigin codeOrigin, CCallHelpers& jit)
>+{
>+    // Find the right inline call frame.
>+    InlineCallFrame* inlineCallFrame = 0;
>+    for (InlineCallFrame* current = codeOrigin.inlineCallFrame;
>+         current;
>+         current = current->caller.inlineCallFrame) {
>+        if (current->stackOffset >= operand) {
>+            inlineCallFrame = current;
>+            break;
>+        }
>+    }
>+
>+    if (!jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>+        return;
>+    VirtualRegister argumentsRegister = jit.baselineArgumentsRegisterFor(inlineCallFrame);
>+    if (m_didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>+        // We know this call frame optimized out an arguments object that
>+        // the baseline JIT would have created. Do that creation now.
>+#if USE(JSVALUE64)
>+        if (inlineCallFrame) {
>+            jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0);
>+            jit.setupArguments(GPRInfo::regT0);
>+        } else
>+            jit.setupArgumentsExecState();
>+        jit.move(
>+            AssemblyHelpers::TrustedImmPtr(
>+                bitwise_cast<void*>(operationCreateArguments)),
>+            GPRInfo::nonArgGPR0);
>+        jit.call(GPRInfo::nonArgGPR0);
>+        jit.store64(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(argumentsRegister));
>+        jit.store64(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>+#else // USE(JSVALUE64) -> so the 32_64 part
>+        if (inlineCallFrame) {
>+            jit.setupArgumentsWithExecState(
>+                AssemblyHelpers::TrustedImmPtr(inlineCallFrame));
>+            jit.move(
>+                AssemblyHelpers::TrustedImmPtr(
>+                    bitwise_cast<void*>(operationCreateInlinedArguments)),
>+                GPRInfo::nonArgGPR0);
>+        } else {
>+            jit.setupArgumentsExecState();
>+            jit.move(
>+                AssemblyHelpers::TrustedImmPtr(
>+                    bitwise_cast<void*>(operationCreateArguments)),
>+                GPRInfo::nonArgGPR0);
>+        }
>+        jit.call(GPRInfo::nonArgGPR0);
>+        jit.store32(
>+            AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+            AssemblyHelpers::tagFor(argumentsRegister));
>+        jit.store32(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::payloadFor(argumentsRegister));
>+        jit.store32(
>+            AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+            AssemblyHelpers::tagFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.store32(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::payloadFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>+#endif // USE(JSVALUE64)
>+    }
>+
>+#if USE(JSVALUE64)
>+    jit.load64(AssemblyHelpers::addressFor(argumentsRegister), GPRInfo::regT0);
>+    jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
>+#else // USE(JSVALUE64) -> so the 32_64 part
>+    jit.load32(AssemblyHelpers::payloadFor(argumentsRegister), GPRInfo::regT0);
>+    jit.store32(
>+        AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+        AssemblyHelpers::tagFor(operand));
>+    jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor(operand));
>+#endif // USE(JSVALUE64)
>+}
>+    
> } } // namespace JSC::DFG
> 
> #endif // ENABLE(DFG_JIT)
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h	(working copy)
>@@ -37,6 +37,18 @@ void handleExitCounts(CCallHelpers&, con
> void reifyInlinedCallFrames(CCallHelpers&, const OSRExitBase&);
> void adjustAndJumpToTarget(CCallHelpers&, const OSRExitBase&);
> 
>+class ArgumentsRecoveryGenerator {
>+public:
>+    ArgumentsRecoveryGenerator();
>+    ~ArgumentsRecoveryGenerator();
>+    
>+    void generateFor(int operand, CodeOrigin, CCallHelpers&);
>+    
>+private:
>+    HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>+        NullableHashTraits<InlineCallFrame*>> m_didCreateArgumentsObject;
>+};
>+
> } } // namespace JSC::DFG
> 
> #endif // ENABLE(DFG_JIT)
>Index: Source/JavaScriptCore/ftl/FTLCapabilities.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLCapabilities.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLCapabilities.cpp	(working copy)
>@@ -140,6 +140,7 @@ inline CapabilityLevel canCompile(Node* 
>     case MultiGetByOffset:
>     case MultiPutByOffset:
>     case ToPrimitive:
>+    case PhantomArguments:
>         // These are OK.
>         break;
>     case PutByIdDirect:
>Index: Source/JavaScriptCore/ftl/FTLExitValue.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLExitValue.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLExitValue.cpp	(working copy)
>@@ -59,6 +59,9 @@ void ExitValue::dumpInContext(PrintStrea
>     case ExitValueInJSStackAsDouble:
>         out.print("InJSStackAsDouble:r", virtualRegister());
>         return;
>+    case ExitValueArgumentsObjectThatWasNotCreated:
>+        out.print("ArgumentsObjectThatWasNotCreated");
>+        return;
>     case ExitValueRecovery:
>         out.print("Recovery(", recoveryOpcode(), ", arg", leftRecoveryArgument(), ", arg", rightRecoveryArgument(), ", ", recoveryFormat(), ")");
>         return;
>Index: Source/JavaScriptCore/ftl/FTLExitValue.h
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLExitValue.h	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLExitValue.h	(working copy)
>@@ -51,6 +51,7 @@ enum ExitValueKind {
>     ExitValueInJSStackAsInt32,
>     ExitValueInJSStackAsInt52,
>     ExitValueInJSStackAsDouble,
>+    ExitValueArgumentsObjectThatWasNotCreated,
>     ExitValueRecovery
> };
> 
>@@ -118,6 +119,13 @@ public:
>         return result;
>     }
>     
>+    static ExitValue argumentsObjectThatWasNotCreated()
>+    {
>+        ExitValue result;
>+        result.m_kind = ExitValueArgumentsObjectThatWasNotCreated;
>+        return result;
>+    }
>+    
>     static ExitValue recovery(RecoveryOpcode opcode, unsigned leftArgument, unsigned rightArgument, ValueFormat format)
>     {
>         ExitValue result;
>@@ -146,6 +154,7 @@ public:
>     }
>     bool isConstant() const { return kind() == ExitValueConstant; }
>     bool isArgument() const { return kind() == ExitValueArgument; }
>+    bool isArgumentsObjectThatWasNotCreated() const { return kind() == ExitValueArgumentsObjectThatWasNotCreated; }
>     bool isRecovery() const { return kind() == ExitValueRecovery; }
>     
>     ExitArgument exitArgument() const
>@@ -213,6 +222,7 @@ public:
>         case ExitValueDead:
>         case ExitValueConstant:
>         case ExitValueInJSStack:
>+        case ExitValueArgumentsObjectThatWasNotCreated:
>             return ValueFormatJSValue;
>             
>         case ExitValueArgument:
>Index: Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp	(working copy)
>@@ -282,6 +282,9 @@ private:
>         case WeakJSConstant:
>             compileWeakJSConstant();
>             break;
>+        case PhantomArguments:
>+            compilePhantomArguments();
>+            break;
>         case GetArgument:
>             compileGetArgument();
>             break;
>@@ -781,6 +784,11 @@ private:
>             break;
>         }
>     }
>+
>+    void compilePhantomArguments()
>+    {
>+        setJSValue(m_out.constInt64(JSValue::encode(JSValue())));
>+    }
>     
>     void compileWeakJSConstant()
>     {
>@@ -5519,9 +5527,7 @@ private:
>                 break;
>                 
>             case FlushedArguments:
>-                // FIXME: implement PhantomArguments.
>-                // https://bugs.webkit.org/show_bug.cgi?id=113986
>-                RELEASE_ASSERT_NOT_REACHED();
>+                exit.m_values[i] = ExitValue::argumentsObjectThatWasNotCreated();
>                 break;
>             }
>         }
>@@ -5613,9 +5619,7 @@ private:
>             exit.m_values[index] = ExitValue::constant(m_graph.valueOfJSConstant(node));
>             return true;
>         case PhantomArguments:
>-            // FIXME: implement PhantomArguments.
>-            // https://bugs.webkit.org/show_bug.cgi?id=113986
>-            RELEASE_ASSERT_NOT_REACHED();
>+            exit.m_values[index] = ExitValue::argumentsObjectThatWasNotCreated();
>             return true;
>         default:
>             return false;
>Index: Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp	(working copy)
>@@ -146,6 +146,12 @@ static void compileStub(
>             jit.load64(AssemblyHelpers::addressFor(value.virtualRegister()), GPRInfo::regT0);
>             break;
>             
>+        case ExitValueArgumentsObjectThatWasNotCreated:
>+            // We can't actually recover this yet, but we can make the stack look sane. This is
>+            // a prerequisite to running the actual arguments recovery.
>+            jit.move(MacroAssembler::TrustedImm64(JSValue::encode(JSValue())), GPRInfo::regT0);
>+            break;
>+            
>         case ExitValueRecovery:
>             record->locations[value.rightRecoveryArgument()].restoreInto(
>                 jit, jitCode->stackmaps, registerScratch, GPRInfo::regT1);
>@@ -337,6 +343,15 @@ static void compileStub(
>     
>     handleExitCounts(jit, exit);
>     reifyInlinedCallFrames(jit, exit);
>+    
>+    ArgumentsRecoveryGenerator argumentsRecovery;
>+    for (unsigned index = exit.m_values.size(); index--;) {
>+        if (!exit.m_values[index].isArgumentsObjectThatWasNotCreated())
>+            continue;
>+        int operand = exit.m_values.operandForIndex(index);
>+        argumentsRecovery.generateFor(operand, exit.m_codeOrigin, jit);
>+    }
>+    
>     adjustAndJumpToTarget(jit, exit);
>     
>     LinkBuffer patchBuffer(*vm, &jit, codeBlock);
Comment 10 Whitehat Security 2014-11-07 07:47:50 PST
Comment on attachment 225498 [details]
the patch

>Index: Source/JavaScriptCore/ChangeLog
>===================================================================
>--- Source/JavaScriptCore/ChangeLog	(revision 164889)
>+++ Source/JavaScriptCore/ChangeLog	(working copy)
>@@ -1,3 +1,38 @@
>+2014-02-28  Filip Pizlo  <fpizlo@apple.com>
>+
>+        FTL should support PhantomArguments
>+        https://bugs.webkit.org/show_bug.cgi?id=113986
>+
>+        Reviewed by NOBODY (OOPS!).
>+        
>+        Adding PhantomArguments to the FTL mostly means wiring the recovery of the Arguments
>+        object into the FTL's OSR exit compiler.
>+
>+        * dfg/DFGOSRExitCompiler32_64.cpp:
>+        (JSC::DFG::OSRExitCompiler::compileExit): move the recovery code to DFGOSRExitCompilerCommon.cpp
>+        * dfg/DFGOSRExitCompiler64.cpp:
>+        (JSC::DFG::OSRExitCompiler::compileExit): move the recovery code to DFGOSRExitCompilerCommon.cpp
>+        * dfg/DFGOSRExitCompilerCommon.cpp:
>+        (JSC::DFG::ArgumentsRecoveryGenerator::ArgumentsRecoveryGenerator):
>+        (JSC::DFG::ArgumentsRecoveryGenerator::~ArgumentsRecoveryGenerator):
>+        (JSC::DFG::ArgumentsRecoveryGenerator::generateFor): this is the common place for the recovery code
>+        * dfg/DFGOSRExitCompilerCommon.h:
>+        * ftl/FTLCapabilities.cpp:
>+        (JSC::FTL::canCompile):
>+        * ftl/FTLExitValue.cpp:
>+        (JSC::FTL::ExitValue::dumpInContext):
>+        * ftl/FTLExitValue.h:
>+        (JSC::FTL::ExitValue::argumentsObjectThatWasNotCreated):
>+        (JSC::FTL::ExitValue::isArgumentsObjectThatWasNotCreated):
>+        (JSC::FTL::ExitValue::valueFormat):
>+        * ftl/FTLLowerDFGToLLVM.cpp:
>+        (JSC::FTL::LowerDFGToLLVM::compileNode):
>+        (JSC::FTL::LowerDFGToLLVM::compilePhantomArguments):
>+        (JSC::FTL::LowerDFGToLLVM::buildExitArguments):
>+        (JSC::FTL::LowerDFGToLLVM::tryToSetConstantExitArgument):
>+        * ftl/FTLOSRExitCompiler.cpp:
>+        (JSC::FTL::compileStub): Call into the ArgumentsRecoveryGenerator
>+
> 2014-02-28  Oliver Hunt  <oliver@apple.com>
> 
>         REGRESSION(r164835): It broke 10 JSC stress test on 32 bit platforms
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -393,66 +393,14 @@ void OSRExitCompiler::compileExit(const 
>     //     registers.
>     
>     if (haveArguments) {
>-        HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>-            NullableHashTraits<InlineCallFrame*>> didCreateArgumentsObject;
>+        ArgumentsRecoveryGenerator argumentsRecovery;
> 
>         for (size_t index = 0; index < operands.size(); ++index) {
>             const ValueRecovery& recovery = operands[index];
>             if (recovery.technique() != ArgumentsThatWereNotCreated)
>                 continue;
>-            int operand = operands.operandForIndex(index);
>-            // Find the right inline call frame.
>-            InlineCallFrame* inlineCallFrame = 0;
>-            for (InlineCallFrame* current = exit.m_codeOrigin.inlineCallFrame;
>-                 current;
>-                 current = current->caller.inlineCallFrame) {
>-                if (current->stackOffset >= operand) {
>-                    inlineCallFrame = current;
>-                    break;
>-                }
>-            }
>-
>-            if (!m_jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>-                continue;
>-            VirtualRegister argumentsRegister = m_jit.baselineArgumentsRegisterFor(inlineCallFrame);
>-            if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>-                // We know this call frame optimized out an arguments object that
>-                // the baseline JIT would have created. Do that creation now.
>-                if (inlineCallFrame) {
>-                    m_jit.setupArgumentsWithExecState(
>-                        AssemblyHelpers::TrustedImmPtr(inlineCallFrame));
>-                    m_jit.move(
>-                        AssemblyHelpers::TrustedImmPtr(
>-                            bitwise_cast<void*>(operationCreateInlinedArguments)),
>-                        GPRInfo::nonArgGPR0);
>-                } else {
>-                    m_jit.setupArgumentsExecState();
>-                    m_jit.move(
>-                        AssemblyHelpers::TrustedImmPtr(
>-                            bitwise_cast<void*>(operationCreateArguments)),
>-                        GPRInfo::nonArgGPR0);
>-                }
>-                m_jit.call(GPRInfo::nonArgGPR0);
>-                m_jit.store32(
>-                    AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                    AssemblyHelpers::tagFor(argumentsRegister));
>-                m_jit.store32(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::payloadFor(argumentsRegister));
>-                m_jit.store32(
>-                    AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                    AssemblyHelpers::tagFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.store32(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::payloadFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>-            }
>-
>-            m_jit.load32(AssemblyHelpers::payloadFor(argumentsRegister), GPRInfo::regT0);
>-            m_jit.store32(
>-                AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                AssemblyHelpers::tagFor(operand));
>-            m_jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor(operand));
>+            argumentsRecovery.generateFor(
>+                operands.operandForIndex(index), exit.m_codeOrigin, m_jit);
>         }
>     }
> 
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -365,50 +365,14 @@ void OSRExitCompiler::compileExit(const 
>     //     registers.
>     
>     if (haveArguments) {
>-        HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>-            NullableHashTraits<InlineCallFrame*>> didCreateArgumentsObject;
>+        ArgumentsRecoveryGenerator argumentsRecovery;
> 
>         for (size_t index = 0; index < operands.size(); ++index) {
>             const ValueRecovery& recovery = operands[index];
>             if (recovery.technique() != ArgumentsThatWereNotCreated)
>                 continue;
>-            int operand = operands.operandForIndex(index);
>-            // Find the right inline call frame.
>-            InlineCallFrame* inlineCallFrame = 0;
>-            for (InlineCallFrame* current = exit.m_codeOrigin.inlineCallFrame;
>-                 current;
>-                 current = current->caller.inlineCallFrame) {
>-                if (current->stackOffset >= operand) {
>-                    inlineCallFrame = current;
>-                    break;
>-                }
>-            }
>-
>-            if (!m_jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>-                continue;
>-            VirtualRegister argumentsRegister = m_jit.baselineArgumentsRegisterFor(inlineCallFrame);
>-            if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>-                // We know this call frame optimized out an arguments object that
>-                // the baseline JIT would have created. Do that creation now.
>-                if (inlineCallFrame) {
>-                    m_jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0);
>-                    m_jit.setupArguments(GPRInfo::regT0);
>-                } else
>-                    m_jit.setupArgumentsExecState();
>-                m_jit.move(
>-                    AssemblyHelpers::TrustedImmPtr(
>-                        bitwise_cast<void*>(operationCreateArguments)),
>-                    GPRInfo::nonArgGPR0);
>-                m_jit.call(GPRInfo::nonArgGPR0);
>-                m_jit.store64(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(argumentsRegister));
>-                m_jit.store64(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>-            }
>-
>-            m_jit.load64(AssemblyHelpers::addressFor(argumentsRegister), GPRInfo::regT0);
>-            m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
>+            argumentsRecovery.generateFor(
>+                operands.operandForIndex(index), exit.m_codeOrigin, m_jit);
>         }
>     }
> 
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -217,6 +217,89 @@ void adjustAndJumpToTarget(CCallHelpers&
>     jit.jump(GPRInfo::regT2);
> }
> 
>+ArgumentsRecoveryGenerator::ArgumentsRecoveryGenerator() { }
>+ArgumentsRecoveryGenerator::~ArgumentsRecoveryGenerator() { }
>+
>+void ArgumentsRecoveryGenerator::generateFor(
>+    int operand, CodeOrigin codeOrigin, CCallHelpers& jit)
>+{
>+    // Find the right inline call frame.
>+    InlineCallFrame* inlineCallFrame = 0;
>+    for (InlineCallFrame* current = codeOrigin.inlineCallFrame;
>+         current;
>+         current = current->caller.inlineCallFrame) {
>+        if (current->stackOffset >= operand) {
>+            inlineCallFrame = current;
>+            break;
>+        }
>+    }
>+
>+    if (!jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>+        return;
>+    VirtualRegister argumentsRegister = jit.baselineArgumentsRegisterFor(inlineCallFrame);
>+    if (m_didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>+        // We know this call frame optimized out an arguments object that
>+        // the baseline JIT would have created. Do that creation now.
>+#if USE(JSVALUE64)
>+        if (inlineCallFrame) {
>+            jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0);
>+            jit.setupArguments(GPRInfo::regT0);
>+        } else
>+            jit.setupArgumentsExecState();
>+        jit.move(
>+            AssemblyHelpers::TrustedImmPtr(
>+                bitwise_cast<void*>(operationCreateArguments)),
>+            GPRInfo::nonArgGPR0);
>+        jit.call(GPRInfo::nonArgGPR0);
>+        jit.store64(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(argumentsRegister));
>+        jit.store64(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>+#else // USE(JSVALUE64) -> so the 32_64 part
>+        if (inlineCallFrame) {
>+            jit.setupArgumentsWithExecState(
>+                AssemblyHelpers::TrustedImmPtr(inlineCallFrame));
>+            jit.move(
>+                AssemblyHelpers::TrustedImmPtr(
>+                    bitwise_cast<void*>(operationCreateInlinedArguments)),
>+                GPRInfo::nonArgGPR0);
>+        } else {
>+            jit.setupArgumentsExecState();
>+            jit.move(
>+                AssemblyHelpers::TrustedImmPtr(
>+                    bitwise_cast<void*>(operationCreateArguments)),
>+                GPRInfo::nonArgGPR0);
>+        }
>+        jit.call(GPRInfo::nonArgGPR0);
>+        jit.store32(
>+            AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+            AssemblyHelpers::tagFor(argumentsRegister));
>+        jit.store32(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::payloadFor(argumentsRegister));
>+        jit.store32(
>+            AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+            AssemblyHelpers::tagFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.store32(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::payloadFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>+#endif // USE(JSVALUE64)
>+    }
>+
>+#if USE(JSVALUE64)
>+    jit.load64(AssemblyHelpers::addressFor(argumentsRegister), GPRInfo::regT0);
>+    jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
>+#else // USE(JSVALUE64) -> so the 32_64 part
>+    jit.load32(AssemblyHelpers::payloadFor(argumentsRegister), GPRInfo::regT0);
>+    jit.store32(
>+        AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+        AssemblyHelpers::tagFor(operand));
>+    jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor(operand));
>+#endif // USE(JSVALUE64)
>+}
>+    
> } } // namespace JSC::DFG
> 
> #endif // ENABLE(DFG_JIT)
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h	(working copy)
>@@ -37,6 +37,18 @@ void handleExitCounts(CCallHelpers&, con
> void reifyInlinedCallFrames(CCallHelpers&, const OSRExitBase&);
> void adjustAndJumpToTarget(CCallHelpers&, const OSRExitBase&);
> 
>+class ArgumentsRecoveryGenerator {
>+public:
>+    ArgumentsRecoveryGenerator();
>+    ~ArgumentsRecoveryGenerator();
>+    
>+    void generateFor(int operand, CodeOrigin, CCallHelpers&);
>+    
>+private:
>+    HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>+        NullableHashTraits<InlineCallFrame*>> m_didCreateArgumentsObject;
>+};
>+
> } } // namespace JSC::DFG
> 
> #endif // ENABLE(DFG_JIT)
>Index: Source/JavaScriptCore/ftl/FTLCapabilities.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLCapabilities.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLCapabilities.cpp	(working copy)
>@@ -140,6 +140,7 @@ inline CapabilityLevel canCompile(Node* 
>     case MultiGetByOffset:
>     case MultiPutByOffset:
>     case ToPrimitive:
>+    case PhantomArguments:
>         // These are OK.
>         break;
>     case PutByIdDirect:
>Index: Source/JavaScriptCore/ftl/FTLExitValue.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLExitValue.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLExitValue.cpp	(working copy)
>@@ -59,6 +59,9 @@ void ExitValue::dumpInContext(PrintStrea
>     case ExitValueInJSStackAsDouble:
>         out.print("InJSStackAsDouble:r", virtualRegister());
>         return;
>+    case ExitValueArgumentsObjectThatWasNotCreated:
>+        out.print("ArgumentsObjectThatWasNotCreated");
>+        return;
>     case ExitValueRecovery:
>         out.print("Recovery(", recoveryOpcode(), ", arg", leftRecoveryArgument(), ", arg", rightRecoveryArgument(), ", ", recoveryFormat(), ")");
>         return;
>Index: Source/JavaScriptCore/ftl/FTLExitValue.h
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLExitValue.h	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLExitValue.h	(working copy)
>@@ -51,6 +51,7 @@ enum ExitValueKind {
>     ExitValueInJSStackAsInt32,
>     ExitValueInJSStackAsInt52,
>     ExitValueInJSStackAsDouble,
>+    ExitValueArgumentsObjectThatWasNotCreated,
>     ExitValueRecovery
> };
> 
>@@ -118,6 +119,13 @@ public:
>         return result;
>     }
>     
>+    static ExitValue argumentsObjectThatWasNotCreated()
>+    {
>+        ExitValue result;
>+        result.m_kind = ExitValueArgumentsObjectThatWasNotCreated;
>+        return result;
>+    }
>+    
>     static ExitValue recovery(RecoveryOpcode opcode, unsigned leftArgument, unsigned rightArgument, ValueFormat format)
>     {
>         ExitValue result;
>@@ -146,6 +154,7 @@ public:
>     }
>     bool isConstant() const { return kind() == ExitValueConstant; }
>     bool isArgument() const { return kind() == ExitValueArgument; }
>+    bool isArgumentsObjectThatWasNotCreated() const { return kind() == ExitValueArgumentsObjectThatWasNotCreated; }
>     bool isRecovery() const { return kind() == ExitValueRecovery; }
>     
>     ExitArgument exitArgument() const
>@@ -213,6 +222,7 @@ public:
>         case ExitValueDead:
>         case ExitValueConstant:
>         case ExitValueInJSStack:
>+        case ExitValueArgumentsObjectThatWasNotCreated:
>             return ValueFormatJSValue;
>             
>         case ExitValueArgument:
>Index: Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp	(working copy)
>@@ -282,6 +282,9 @@ private:
>         case WeakJSConstant:
>             compileWeakJSConstant();
>             break;
>+        case PhantomArguments:
>+            compilePhantomArguments();
>+            break;
>         case GetArgument:
>             compileGetArgument();
>             break;
>@@ -781,6 +784,11 @@ private:
>             break;
>         }
>     }
>+
>+    void compilePhantomArguments()
>+    {
>+        setJSValue(m_out.constInt64(JSValue::encode(JSValue())));
>+    }
>     
>     void compileWeakJSConstant()
>     {
>@@ -5519,9 +5527,7 @@ private:
>                 break;
>                 
>             case FlushedArguments:
>-                // FIXME: implement PhantomArguments.
>-                // https://bugs.webkit.org/show_bug.cgi?id=113986
>-                RELEASE_ASSERT_NOT_REACHED();
>+                exit.m_values[i] = ExitValue::argumentsObjectThatWasNotCreated();
>                 break;
>             }
>         }
>@@ -5613,9 +5619,7 @@ private:
>             exit.m_values[index] = ExitValue::constant(m_graph.valueOfJSConstant(node));
>             return true;
>         case PhantomArguments:
>-            // FIXME: implement PhantomArguments.
>-            // https://bugs.webkit.org/show_bug.cgi?id=113986
>-            RELEASE_ASSERT_NOT_REACHED();
>+            exit.m_values[index] = ExitValue::argumentsObjectThatWasNotCreated();
>             return true;
>         default:
>             return false;
>Index: Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp	(working copy)
>@@ -146,6 +146,12 @@ static void compileStub(
>             jit.load64(AssemblyHelpers::addressFor(value.virtualRegister()), GPRInfo::regT0);
>             break;
>             
>+        case ExitValueArgumentsObjectThatWasNotCreated:
>+            // We can't actually recover this yet, but we can make the stack look sane. This is
>+            // a prerequisite to running the actual arguments recovery.
>+            jit.move(MacroAssembler::TrustedImm64(JSValue::encode(JSValue())), GPRInfo::regT0);
>+            break;
>+            
>         case ExitValueRecovery:
>             record->locations[value.rightRecoveryArgument()].restoreInto(
>                 jit, jitCode->stackmaps, registerScratch, GPRInfo::regT1);
>@@ -337,6 +343,15 @@ static void compileStub(
>     
>     handleExitCounts(jit, exit);
>     reifyInlinedCallFrames(jit, exit);
>+    
>+    ArgumentsRecoveryGenerator argumentsRecovery;
>+    for (unsigned index = exit.m_values.size(); index--;) {
>+        if (!exit.m_values[index].isArgumentsObjectThatWasNotCreated())
>+            continue;
>+        int operand = exit.m_values.operandForIndex(index);
>+        argumentsRecovery.generateFor(operand, exit.m_codeOrigin, jit);
>+    }
>+    
>     adjustAndJumpToTarget(jit, exit);
>     
>     LinkBuffer patchBuffer(*vm, &jit, codeBlock);
Comment 11 Whitehat Security 2014-11-07 07:47:54 PST
Comment on attachment 225498 [details]
the patch

>Index: Source/JavaScriptCore/ChangeLog
>===================================================================
>--- Source/JavaScriptCore/ChangeLog	(revision 164889)
>+++ Source/JavaScriptCore/ChangeLog	(working copy)
>@@ -1,3 +1,38 @@
>+2014-02-28  Filip Pizlo  <fpizlo@apple.com>
>+
>+        FTL should support PhantomArguments
>+        https://bugs.webkit.org/show_bug.cgi?id=113986
>+
>+        Reviewed by NOBODY (OOPS!).
>+        
>+        Adding PhantomArguments to the FTL mostly means wiring the recovery of the Arguments
>+        object into the FTL's OSR exit compiler.
>+
>+        * dfg/DFGOSRExitCompiler32_64.cpp:
>+        (JSC::DFG::OSRExitCompiler::compileExit): move the recovery code to DFGOSRExitCompilerCommon.cpp
>+        * dfg/DFGOSRExitCompiler64.cpp:
>+        (JSC::DFG::OSRExitCompiler::compileExit): move the recovery code to DFGOSRExitCompilerCommon.cpp
>+        * dfg/DFGOSRExitCompilerCommon.cpp:
>+        (JSC::DFG::ArgumentsRecoveryGenerator::ArgumentsRecoveryGenerator):
>+        (JSC::DFG::ArgumentsRecoveryGenerator::~ArgumentsRecoveryGenerator):
>+        (JSC::DFG::ArgumentsRecoveryGenerator::generateFor): this is the common place for the recovery code
>+        * dfg/DFGOSRExitCompilerCommon.h:
>+        * ftl/FTLCapabilities.cpp:
>+        (JSC::FTL::canCompile):
>+        * ftl/FTLExitValue.cpp:
>+        (JSC::FTL::ExitValue::dumpInContext):
>+        * ftl/FTLExitValue.h:
>+        (JSC::FTL::ExitValue::argumentsObjectThatWasNotCreated):
>+        (JSC::FTL::ExitValue::isArgumentsObjectThatWasNotCreated):
>+        (JSC::FTL::ExitValue::valueFormat):
>+        * ftl/FTLLowerDFGToLLVM.cpp:
>+        (JSC::FTL::LowerDFGToLLVM::compileNode):
>+        (JSC::FTL::LowerDFGToLLVM::compilePhantomArguments):
>+        (JSC::FTL::LowerDFGToLLVM::buildExitArguments):
>+        (JSC::FTL::LowerDFGToLLVM::tryToSetConstantExitArgument):
>+        * ftl/FTLOSRExitCompiler.cpp:
>+        (JSC::FTL::compileStub): Call into the ArgumentsRecoveryGenerator
>+
> 2014-02-28  Oliver Hunt  <oliver@apple.com>
> 
>         REGRESSION(r164835): It broke 10 JSC stress test on 32 bit platforms
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -393,66 +393,14 @@ void OSRExitCompiler::compileExit(const 
>     //     registers.
>     
>     if (haveArguments) {
>-        HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>-            NullableHashTraits<InlineCallFrame*>> didCreateArgumentsObject;
>+        ArgumentsRecoveryGenerator argumentsRecovery;
> 
>         for (size_t index = 0; index < operands.size(); ++index) {
>             const ValueRecovery& recovery = operands[index];
>             if (recovery.technique() != ArgumentsThatWereNotCreated)
>                 continue;
>-            int operand = operands.operandForIndex(index);
>-            // Find the right inline call frame.
>-            InlineCallFrame* inlineCallFrame = 0;
>-            for (InlineCallFrame* current = exit.m_codeOrigin.inlineCallFrame;
>-                 current;
>-                 current = current->caller.inlineCallFrame) {
>-                if (current->stackOffset >= operand) {
>-                    inlineCallFrame = current;
>-                    break;
>-                }
>-            }
>-
>-            if (!m_jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>-                continue;
>-            VirtualRegister argumentsRegister = m_jit.baselineArgumentsRegisterFor(inlineCallFrame);
>-            if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>-                // We know this call frame optimized out an arguments object that
>-                // the baseline JIT would have created. Do that creation now.
>-                if (inlineCallFrame) {
>-                    m_jit.setupArgumentsWithExecState(
>-                        AssemblyHelpers::TrustedImmPtr(inlineCallFrame));
>-                    m_jit.move(
>-                        AssemblyHelpers::TrustedImmPtr(
>-                            bitwise_cast<void*>(operationCreateInlinedArguments)),
>-                        GPRInfo::nonArgGPR0);
>-                } else {
>-                    m_jit.setupArgumentsExecState();
>-                    m_jit.move(
>-                        AssemblyHelpers::TrustedImmPtr(
>-                            bitwise_cast<void*>(operationCreateArguments)),
>-                        GPRInfo::nonArgGPR0);
>-                }
>-                m_jit.call(GPRInfo::nonArgGPR0);
>-                m_jit.store32(
>-                    AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                    AssemblyHelpers::tagFor(argumentsRegister));
>-                m_jit.store32(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::payloadFor(argumentsRegister));
>-                m_jit.store32(
>-                    AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                    AssemblyHelpers::tagFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.store32(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::payloadFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>-            }
>-
>-            m_jit.load32(AssemblyHelpers::payloadFor(argumentsRegister), GPRInfo::regT0);
>-            m_jit.store32(
>-                AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                AssemblyHelpers::tagFor(operand));
>-            m_jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor(operand));
>+            argumentsRecovery.generateFor(
>+                operands.operandForIndex(index), exit.m_codeOrigin, m_jit);
>         }
>     }
> 
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -365,50 +365,14 @@ void OSRExitCompiler::compileExit(const 
>     //     registers.
>     
>     if (haveArguments) {
>-        HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>-            NullableHashTraits<InlineCallFrame*>> didCreateArgumentsObject;
>+        ArgumentsRecoveryGenerator argumentsRecovery;
> 
>         for (size_t index = 0; index < operands.size(); ++index) {
>             const ValueRecovery& recovery = operands[index];
>             if (recovery.technique() != ArgumentsThatWereNotCreated)
>                 continue;
>-            int operand = operands.operandForIndex(index);
>-            // Find the right inline call frame.
>-            InlineCallFrame* inlineCallFrame = 0;
>-            for (InlineCallFrame* current = exit.m_codeOrigin.inlineCallFrame;
>-                 current;
>-                 current = current->caller.inlineCallFrame) {
>-                if (current->stackOffset >= operand) {
>-                    inlineCallFrame = current;
>-                    break;
>-                }
>-            }
>-
>-            if (!m_jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>-                continue;
>-            VirtualRegister argumentsRegister = m_jit.baselineArgumentsRegisterFor(inlineCallFrame);
>-            if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>-                // We know this call frame optimized out an arguments object that
>-                // the baseline JIT would have created. Do that creation now.
>-                if (inlineCallFrame) {
>-                    m_jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0);
>-                    m_jit.setupArguments(GPRInfo::regT0);
>-                } else
>-                    m_jit.setupArgumentsExecState();
>-                m_jit.move(
>-                    AssemblyHelpers::TrustedImmPtr(
>-                        bitwise_cast<void*>(operationCreateArguments)),
>-                    GPRInfo::nonArgGPR0);
>-                m_jit.call(GPRInfo::nonArgGPR0);
>-                m_jit.store64(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(argumentsRegister));
>-                m_jit.store64(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>-            }
>-
>-            m_jit.load64(AssemblyHelpers::addressFor(argumentsRegister), GPRInfo::regT0);
>-            m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
>+            argumentsRecovery.generateFor(
>+                operands.operandForIndex(index), exit.m_codeOrigin, m_jit);
>         }
>     }
> 
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -217,6 +217,89 @@ void adjustAndJumpToTarget(CCallHelpers&
>     jit.jump(GPRInfo::regT2);
> }
> 
>+ArgumentsRecoveryGenerator::ArgumentsRecoveryGenerator() { }
>+ArgumentsRecoveryGenerator::~ArgumentsRecoveryGenerator() { }
>+
>+void ArgumentsRecoveryGenerator::generateFor(
>+    int operand, CodeOrigin codeOrigin, CCallHelpers& jit)
>+{
>+    // Find the right inline call frame.
>+    InlineCallFrame* inlineCallFrame = 0;
>+    for (InlineCallFrame* current = codeOrigin.inlineCallFrame;
>+         current;
>+         current = current->caller.inlineCallFrame) {
>+        if (current->stackOffset >= operand) {
>+            inlineCallFrame = current;
>+            break;
>+        }
>+    }
>+
>+    if (!jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>+        return;
>+    VirtualRegister argumentsRegister = jit.baselineArgumentsRegisterFor(inlineCallFrame);
>+    if (m_didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>+        // We know this call frame optimized out an arguments object that
>+        // the baseline JIT would have created. Do that creation now.
>+#if USE(JSVALUE64)
>+        if (inlineCallFrame) {
>+            jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0);
>+            jit.setupArguments(GPRInfo::regT0);
>+        } else
>+            jit.setupArgumentsExecState();
>+        jit.move(
>+            AssemblyHelpers::TrustedImmPtr(
>+                bitwise_cast<void*>(operationCreateArguments)),
>+            GPRInfo::nonArgGPR0);
>+        jit.call(GPRInfo::nonArgGPR0);
>+        jit.store64(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(argumentsRegister));
>+        jit.store64(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>+#else // USE(JSVALUE64) -> so the 32_64 part
>+        if (inlineCallFrame) {
>+            jit.setupArgumentsWithExecState(
>+                AssemblyHelpers::TrustedImmPtr(inlineCallFrame));
>+            jit.move(
>+                AssemblyHelpers::TrustedImmPtr(
>+                    bitwise_cast<void*>(operationCreateInlinedArguments)),
>+                GPRInfo::nonArgGPR0);
>+        } else {
>+            jit.setupArgumentsExecState();
>+            jit.move(
>+                AssemblyHelpers::TrustedImmPtr(
>+                    bitwise_cast<void*>(operationCreateArguments)),
>+                GPRInfo::nonArgGPR0);
>+        }
>+        jit.call(GPRInfo::nonArgGPR0);
>+        jit.store32(
>+            AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+            AssemblyHelpers::tagFor(argumentsRegister));
>+        jit.store32(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::payloadFor(argumentsRegister));
>+        jit.store32(
>+            AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+            AssemblyHelpers::tagFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.store32(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::payloadFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>+#endif // USE(JSVALUE64)
>+    }
>+
>+#if USE(JSVALUE64)
>+    jit.load64(AssemblyHelpers::addressFor(argumentsRegister), GPRInfo::regT0);
>+    jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
>+#else // USE(JSVALUE64) -> so the 32_64 part
>+    jit.load32(AssemblyHelpers::payloadFor(argumentsRegister), GPRInfo::regT0);
>+    jit.store32(
>+        AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+        AssemblyHelpers::tagFor(operand));
>+    jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor(operand));
>+#endif // USE(JSVALUE64)
>+}
>+    
> } } // namespace JSC::DFG
> 
> #endif // ENABLE(DFG_JIT)
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h	(working copy)
>@@ -37,6 +37,18 @@ void handleExitCounts(CCallHelpers&, con
> void reifyInlinedCallFrames(CCallHelpers&, const OSRExitBase&);
> void adjustAndJumpToTarget(CCallHelpers&, const OSRExitBase&);
> 
>+class ArgumentsRecoveryGenerator {
>+public:
>+    ArgumentsRecoveryGenerator();
>+    ~ArgumentsRecoveryGenerator();
>+    
>+    void generateFor(int operand, CodeOrigin, CCallHelpers&);
>+    
>+private:
>+    HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>+        NullableHashTraits<InlineCallFrame*>> m_didCreateArgumentsObject;
>+};
>+
> } } // namespace JSC::DFG
> 
> #endif // ENABLE(DFG_JIT)
>Index: Source/JavaScriptCore/ftl/FTLCapabilities.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLCapabilities.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLCapabilities.cpp	(working copy)
>@@ -140,6 +140,7 @@ inline CapabilityLevel canCompile(Node* 
>     case MultiGetByOffset:
>     case MultiPutByOffset:
>     case ToPrimitive:
>+    case PhantomArguments:
>         // These are OK.
>         break;
>     case PutByIdDirect:
>Index: Source/JavaScriptCore/ftl/FTLExitValue.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLExitValue.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLExitValue.cpp	(working copy)
>@@ -59,6 +59,9 @@ void ExitValue::dumpInContext(PrintStrea
>     case ExitValueInJSStackAsDouble:
>         out.print("InJSStackAsDouble:r", virtualRegister());
>         return;
>+    case ExitValueArgumentsObjectThatWasNotCreated:
>+        out.print("ArgumentsObjectThatWasNotCreated");
>+        return;
>     case ExitValueRecovery:
>         out.print("Recovery(", recoveryOpcode(), ", arg", leftRecoveryArgument(), ", arg", rightRecoveryArgument(), ", ", recoveryFormat(), ")");
>         return;
>Index: Source/JavaScriptCore/ftl/FTLExitValue.h
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLExitValue.h	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLExitValue.h	(working copy)
>@@ -51,6 +51,7 @@ enum ExitValueKind {
>     ExitValueInJSStackAsInt32,
>     ExitValueInJSStackAsInt52,
>     ExitValueInJSStackAsDouble,
>+    ExitValueArgumentsObjectThatWasNotCreated,
>     ExitValueRecovery
> };
> 
>@@ -118,6 +119,13 @@ public:
>         return result;
>     }
>     
>+    static ExitValue argumentsObjectThatWasNotCreated()
>+    {
>+        ExitValue result;
>+        result.m_kind = ExitValueArgumentsObjectThatWasNotCreated;
>+        return result;
>+    }
>+    
>     static ExitValue recovery(RecoveryOpcode opcode, unsigned leftArgument, unsigned rightArgument, ValueFormat format)
>     {
>         ExitValue result;
>@@ -146,6 +154,7 @@ public:
>     }
>     bool isConstant() const { return kind() == ExitValueConstant; }
>     bool isArgument() const { return kind() == ExitValueArgument; }
>+    bool isArgumentsObjectThatWasNotCreated() const { return kind() == ExitValueArgumentsObjectThatWasNotCreated; }
>     bool isRecovery() const { return kind() == ExitValueRecovery; }
>     
>     ExitArgument exitArgument() const
>@@ -213,6 +222,7 @@ public:
>         case ExitValueDead:
>         case ExitValueConstant:
>         case ExitValueInJSStack:
>+        case ExitValueArgumentsObjectThatWasNotCreated:
>             return ValueFormatJSValue;
>             
>         case ExitValueArgument:
>Index: Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp	(working copy)
>@@ -282,6 +282,9 @@ private:
>         case WeakJSConstant:
>             compileWeakJSConstant();
>             break;
>+        case PhantomArguments:
>+            compilePhantomArguments();
>+            break;
>         case GetArgument:
>             compileGetArgument();
>             break;
>@@ -781,6 +784,11 @@ private:
>             break;
>         }
>     }
>+
>+    void compilePhantomArguments()
>+    {
>+        setJSValue(m_out.constInt64(JSValue::encode(JSValue())));
>+    }
>     
>     void compileWeakJSConstant()
>     {
>@@ -5519,9 +5527,7 @@ private:
>                 break;
>                 
>             case FlushedArguments:
>-                // FIXME: implement PhantomArguments.
>-                // https://bugs.webkit.org/show_bug.cgi?id=113986
>-                RELEASE_ASSERT_NOT_REACHED();
>+                exit.m_values[i] = ExitValue::argumentsObjectThatWasNotCreated();
>                 break;
>             }
>         }
>@@ -5613,9 +5619,7 @@ private:
>             exit.m_values[index] = ExitValue::constant(m_graph.valueOfJSConstant(node));
>             return true;
>         case PhantomArguments:
>-            // FIXME: implement PhantomArguments.
>-            // https://bugs.webkit.org/show_bug.cgi?id=113986
>-            RELEASE_ASSERT_NOT_REACHED();
>+            exit.m_values[index] = ExitValue::argumentsObjectThatWasNotCreated();
>             return true;
>         default:
>             return false;
>Index: Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp	(working copy)
>@@ -146,6 +146,12 @@ static void compileStub(
>             jit.load64(AssemblyHelpers::addressFor(value.virtualRegister()), GPRInfo::regT0);
>             break;
>             
>+        case ExitValueArgumentsObjectThatWasNotCreated:
>+            // We can't actually recover this yet, but we can make the stack look sane. This is
>+            // a prerequisite to running the actual arguments recovery.
>+            jit.move(MacroAssembler::TrustedImm64(JSValue::encode(JSValue())), GPRInfo::regT0);
>+            break;
>+            
>         case ExitValueRecovery:
>             record->locations[value.rightRecoveryArgument()].restoreInto(
>                 jit, jitCode->stackmaps, registerScratch, GPRInfo::regT1);
>@@ -337,6 +343,15 @@ static void compileStub(
>     
>     handleExitCounts(jit, exit);
>     reifyInlinedCallFrames(jit, exit);
>+    
>+    ArgumentsRecoveryGenerator argumentsRecovery;
>+    for (unsigned index = exit.m_values.size(); index--;) {
>+        if (!exit.m_values[index].isArgumentsObjectThatWasNotCreated())
>+            continue;
>+        int operand = exit.m_values.operandForIndex(index);
>+        argumentsRecovery.generateFor(operand, exit.m_codeOrigin, jit);
>+    }
>+    
>     adjustAndJumpToTarget(jit, exit);
>     
>     LinkBuffer patchBuffer(*vm, &jit, codeBlock);
Comment 12 Whitehat Security 2014-11-18 13:40:21 PST
Comment on attachment 225498 [details]
the patch

>Index: Source/JavaScriptCore/ChangeLog
>===================================================================
>--- Source/JavaScriptCore/ChangeLog	(revision 164889)
>+++ Source/JavaScriptCore/ChangeLog	(working copy)
>@@ -1,3 +1,38 @@
>+2014-02-28  Filip Pizlo  <fpizlo@apple.com>
>+
>+        FTL should support PhantomArguments
>+        https://bugs.webkit.org/show_bug.cgi?id=113986
>+
>+        Reviewed by NOBODY (OOPS!).
>+        
>+        Adding PhantomArguments to the FTL mostly means wiring the recovery of the Arguments
>+        object into the FTL's OSR exit compiler.
>+
>+        * dfg/DFGOSRExitCompiler32_64.cpp:
>+        (JSC::DFG::OSRExitCompiler::compileExit): move the recovery code to DFGOSRExitCompilerCommon.cpp
>+        * dfg/DFGOSRExitCompiler64.cpp:
>+        (JSC::DFG::OSRExitCompiler::compileExit): move the recovery code to DFGOSRExitCompilerCommon.cpp
>+        * dfg/DFGOSRExitCompilerCommon.cpp:
>+        (JSC::DFG::ArgumentsRecoveryGenerator::ArgumentsRecoveryGenerator):
>+        (JSC::DFG::ArgumentsRecoveryGenerator::~ArgumentsRecoveryGenerator):
>+        (JSC::DFG::ArgumentsRecoveryGenerator::generateFor): this is the common place for the recovery code
>+        * dfg/DFGOSRExitCompilerCommon.h:
>+        * ftl/FTLCapabilities.cpp:
>+        (JSC::FTL::canCompile):
>+        * ftl/FTLExitValue.cpp:
>+        (JSC::FTL::ExitValue::dumpInContext):
>+        * ftl/FTLExitValue.h:
>+        (JSC::FTL::ExitValue::argumentsObjectThatWasNotCreated):
>+        (JSC::FTL::ExitValue::isArgumentsObjectThatWasNotCreated):
>+        (JSC::FTL::ExitValue::valueFormat):
>+        * ftl/FTLLowerDFGToLLVM.cpp:
>+        (JSC::FTL::LowerDFGToLLVM::compileNode):
>+        (JSC::FTL::LowerDFGToLLVM::compilePhantomArguments):
>+        (JSC::FTL::LowerDFGToLLVM::buildExitArguments):
>+        (JSC::FTL::LowerDFGToLLVM::tryToSetConstantExitArgument):
>+        * ftl/FTLOSRExitCompiler.cpp:
>+        (JSC::FTL::compileStub): Call into the ArgumentsRecoveryGenerator
>+
> 2014-02-28  Oliver Hunt  <oliver@apple.com>
> 
>         REGRESSION(r164835): It broke 10 JSC stress test on 32 bit platforms
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -393,66 +393,14 @@ void OSRExitCompiler::compileExit(const 
>     //     registers.
>     
>     if (haveArguments) {
>-        HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>-            NullableHashTraits<InlineCallFrame*>> didCreateArgumentsObject;
>+        ArgumentsRecoveryGenerator argumentsRecovery;
> 
>         for (size_t index = 0; index < operands.size(); ++index) {
>             const ValueRecovery& recovery = operands[index];
>             if (recovery.technique() != ArgumentsThatWereNotCreated)
>                 continue;
>-            int operand = operands.operandForIndex(index);
>-            // Find the right inline call frame.
>-            InlineCallFrame* inlineCallFrame = 0;
>-            for (InlineCallFrame* current = exit.m_codeOrigin.inlineCallFrame;
>-                 current;
>-                 current = current->caller.inlineCallFrame) {
>-                if (current->stackOffset >= operand) {
>-                    inlineCallFrame = current;
>-                    break;
>-                }
>-            }
>-
>-            if (!m_jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>-                continue;
>-            VirtualRegister argumentsRegister = m_jit.baselineArgumentsRegisterFor(inlineCallFrame);
>-            if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>-                // We know this call frame optimized out an arguments object that
>-                // the baseline JIT would have created. Do that creation now.
>-                if (inlineCallFrame) {
>-                    m_jit.setupArgumentsWithExecState(
>-                        AssemblyHelpers::TrustedImmPtr(inlineCallFrame));
>-                    m_jit.move(
>-                        AssemblyHelpers::TrustedImmPtr(
>-                            bitwise_cast<void*>(operationCreateInlinedArguments)),
>-                        GPRInfo::nonArgGPR0);
>-                } else {
>-                    m_jit.setupArgumentsExecState();
>-                    m_jit.move(
>-                        AssemblyHelpers::TrustedImmPtr(
>-                            bitwise_cast<void*>(operationCreateArguments)),
>-                        GPRInfo::nonArgGPR0);
>-                }
>-                m_jit.call(GPRInfo::nonArgGPR0);
>-                m_jit.store32(
>-                    AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                    AssemblyHelpers::tagFor(argumentsRegister));
>-                m_jit.store32(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::payloadFor(argumentsRegister));
>-                m_jit.store32(
>-                    AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                    AssemblyHelpers::tagFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.store32(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::payloadFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>-            }
>-
>-            m_jit.load32(AssemblyHelpers::payloadFor(argumentsRegister), GPRInfo::regT0);
>-            m_jit.store32(
>-                AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                AssemblyHelpers::tagFor(operand));
>-            m_jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor(operand));
>+            argumentsRecovery.generateFor(
>+                operands.operandForIndex(index), exit.m_codeOrigin, m_jit);
>         }
>     }
> 
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -365,50 +365,14 @@ void OSRExitCompiler::compileExit(const 
>     //     registers.
>     
>     if (haveArguments) {
>-        HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>-            NullableHashTraits<InlineCallFrame*>> didCreateArgumentsObject;
>+        ArgumentsRecoveryGenerator argumentsRecovery;
> 
>         for (size_t index = 0; index < operands.size(); ++index) {
>             const ValueRecovery& recovery = operands[index];
>             if (recovery.technique() != ArgumentsThatWereNotCreated)
>                 continue;
>-            int operand = operands.operandForIndex(index);
>-            // Find the right inline call frame.
>-            InlineCallFrame* inlineCallFrame = 0;
>-            for (InlineCallFrame* current = exit.m_codeOrigin.inlineCallFrame;
>-                 current;
>-                 current = current->caller.inlineCallFrame) {
>-                if (current->stackOffset >= operand) {
>-                    inlineCallFrame = current;
>-                    break;
>-                }
>-            }
>-
>-            if (!m_jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>-                continue;
>-            VirtualRegister argumentsRegister = m_jit.baselineArgumentsRegisterFor(inlineCallFrame);
>-            if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>-                // We know this call frame optimized out an arguments object that
>-                // the baseline JIT would have created. Do that creation now.
>-                if (inlineCallFrame) {
>-                    m_jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0);
>-                    m_jit.setupArguments(GPRInfo::regT0);
>-                } else
>-                    m_jit.setupArgumentsExecState();
>-                m_jit.move(
>-                    AssemblyHelpers::TrustedImmPtr(
>-                        bitwise_cast<void*>(operationCreateArguments)),
>-                    GPRInfo::nonArgGPR0);
>-                m_jit.call(GPRInfo::nonArgGPR0);
>-                m_jit.store64(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(argumentsRegister));
>-                m_jit.store64(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>-            }
>-
>-            m_jit.load64(AssemblyHelpers::addressFor(argumentsRegister), GPRInfo::regT0);
>-            m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
>+            argumentsRecovery.generateFor(
>+                operands.operandForIndex(index), exit.m_codeOrigin, m_jit);
>         }
>     }
> 
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -217,6 +217,89 @@ void adjustAndJumpToTarget(CCallHelpers&
>     jit.jump(GPRInfo::regT2);
> }
> 
>+ArgumentsRecoveryGenerator::ArgumentsRecoveryGenerator() { }
>+ArgumentsRecoveryGenerator::~ArgumentsRecoveryGenerator() { }
>+
>+void ArgumentsRecoveryGenerator::generateFor(
>+    int operand, CodeOrigin codeOrigin, CCallHelpers& jit)
>+{
>+    // Find the right inline call frame.
>+    InlineCallFrame* inlineCallFrame = 0;
>+    for (InlineCallFrame* current = codeOrigin.inlineCallFrame;
>+         current;
>+         current = current->caller.inlineCallFrame) {
>+        if (current->stackOffset >= operand) {
>+            inlineCallFrame = current;
>+            break;
>+        }
>+    }
>+
>+    if (!jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>+        return;
>+    VirtualRegister argumentsRegister = jit.baselineArgumentsRegisterFor(inlineCallFrame);
>+    if (m_didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>+        // We know this call frame optimized out an arguments object that
>+        // the baseline JIT would have created. Do that creation now.
>+#if USE(JSVALUE64)
>+        if (inlineCallFrame) {
>+            jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0);
>+            jit.setupArguments(GPRInfo::regT0);
>+        } else
>+            jit.setupArgumentsExecState();
>+        jit.move(
>+            AssemblyHelpers::TrustedImmPtr(
>+                bitwise_cast<void*>(operationCreateArguments)),
>+            GPRInfo::nonArgGPR0);
>+        jit.call(GPRInfo::nonArgGPR0);
>+        jit.store64(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(argumentsRegister));
>+        jit.store64(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>+#else // USE(JSVALUE64) -> so the 32_64 part
>+        if (inlineCallFrame) {
>+            jit.setupArgumentsWithExecState(
>+                AssemblyHelpers::TrustedImmPtr(inlineCallFrame));
>+            jit.move(
>+                AssemblyHelpers::TrustedImmPtr(
>+                    bitwise_cast<void*>(operationCreateInlinedArguments)),
>+                GPRInfo::nonArgGPR0);
>+        } else {
>+            jit.setupArgumentsExecState();
>+            jit.move(
>+                AssemblyHelpers::TrustedImmPtr(
>+                    bitwise_cast<void*>(operationCreateArguments)),
>+                GPRInfo::nonArgGPR0);
>+        }
>+        jit.call(GPRInfo::nonArgGPR0);
>+        jit.store32(
>+            AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+            AssemblyHelpers::tagFor(argumentsRegister));
>+        jit.store32(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::payloadFor(argumentsRegister));
>+        jit.store32(
>+            AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+            AssemblyHelpers::tagFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.store32(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::payloadFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>+#endif // USE(JSVALUE64)
>+    }
>+
>+#if USE(JSVALUE64)
>+    jit.load64(AssemblyHelpers::addressFor(argumentsRegister), GPRInfo::regT0);
>+    jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
>+#else // USE(JSVALUE64) -> so the 32_64 part
>+    jit.load32(AssemblyHelpers::payloadFor(argumentsRegister), GPRInfo::regT0);
>+    jit.store32(
>+        AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+        AssemblyHelpers::tagFor(operand));
>+    jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor(operand));
>+#endif // USE(JSVALUE64)
>+}
>+    
> } } // namespace JSC::DFG
> 
> #endif // ENABLE(DFG_JIT)
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h	(working copy)
>@@ -37,6 +37,18 @@ void handleExitCounts(CCallHelpers&, con
> void reifyInlinedCallFrames(CCallHelpers&, const OSRExitBase&);
> void adjustAndJumpToTarget(CCallHelpers&, const OSRExitBase&);
> 
>+class ArgumentsRecoveryGenerator {
>+public:
>+    ArgumentsRecoveryGenerator();
>+    ~ArgumentsRecoveryGenerator();
>+    
>+    void generateFor(int operand, CodeOrigin, CCallHelpers&);
>+    
>+private:
>+    HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>+        NullableHashTraits<InlineCallFrame*>> m_didCreateArgumentsObject;
>+};
>+
> } } // namespace JSC::DFG
> 
> #endif // ENABLE(DFG_JIT)
>Index: Source/JavaScriptCore/ftl/FTLCapabilities.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLCapabilities.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLCapabilities.cpp	(working copy)
>@@ -140,6 +140,7 @@ inline CapabilityLevel canCompile(Node* 
>     case MultiGetByOffset:
>     case MultiPutByOffset:
>     case ToPrimitive:
>+    case PhantomArguments:
>         // These are OK.
>         break;
>     case PutByIdDirect:
>Index: Source/JavaScriptCore/ftl/FTLExitValue.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLExitValue.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLExitValue.cpp	(working copy)
>@@ -59,6 +59,9 @@ void ExitValue::dumpInContext(PrintStrea
>     case ExitValueInJSStackAsDouble:
>         out.print("InJSStackAsDouble:r", virtualRegister());
>         return;
>+    case ExitValueArgumentsObjectThatWasNotCreated:
>+        out.print("ArgumentsObjectThatWasNotCreated");
>+        return;
>     case ExitValueRecovery:
>         out.print("Recovery(", recoveryOpcode(), ", arg", leftRecoveryArgument(), ", arg", rightRecoveryArgument(), ", ", recoveryFormat(), ")");
>         return;
>Index: Source/JavaScriptCore/ftl/FTLExitValue.h
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLExitValue.h	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLExitValue.h	(working copy)
>@@ -51,6 +51,7 @@ enum ExitValueKind {
>     ExitValueInJSStackAsInt32,
>     ExitValueInJSStackAsInt52,
>     ExitValueInJSStackAsDouble,
>+    ExitValueArgumentsObjectThatWasNotCreated,
>     ExitValueRecovery
> };
> 
>@@ -118,6 +119,13 @@ public:
>         return result;
>     }
>     
>+    static ExitValue argumentsObjectThatWasNotCreated()
>+    {
>+        ExitValue result;
>+        result.m_kind = ExitValueArgumentsObjectThatWasNotCreated;
>+        return result;
>+    }
>+    
>     static ExitValue recovery(RecoveryOpcode opcode, unsigned leftArgument, unsigned rightArgument, ValueFormat format)
>     {
>         ExitValue result;
>@@ -146,6 +154,7 @@ public:
>     }
>     bool isConstant() const { return kind() == ExitValueConstant; }
>     bool isArgument() const { return kind() == ExitValueArgument; }
>+    bool isArgumentsObjectThatWasNotCreated() const { return kind() == ExitValueArgumentsObjectThatWasNotCreated; }
>     bool isRecovery() const { return kind() == ExitValueRecovery; }
>     
>     ExitArgument exitArgument() const
>@@ -213,6 +222,7 @@ public:
>         case ExitValueDead:
>         case ExitValueConstant:
>         case ExitValueInJSStack:
>+        case ExitValueArgumentsObjectThatWasNotCreated:
>             return ValueFormatJSValue;
>             
>         case ExitValueArgument:
>Index: Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp	(working copy)
>@@ -282,6 +282,9 @@ private:
>         case WeakJSConstant:
>             compileWeakJSConstant();
>             break;
>+        case PhantomArguments:
>+            compilePhantomArguments();
>+            break;
>         case GetArgument:
>             compileGetArgument();
>             break;
>@@ -781,6 +784,11 @@ private:
>             break;
>         }
>     }
>+
>+    void compilePhantomArguments()
>+    {
>+        setJSValue(m_out.constInt64(JSValue::encode(JSValue())));
>+    }
>     
>     void compileWeakJSConstant()
>     {
>@@ -5519,9 +5527,7 @@ private:
>                 break;
>                 
>             case FlushedArguments:
>-                // FIXME: implement PhantomArguments.
>-                // https://bugs.webkit.org/show_bug.cgi?id=113986
>-                RELEASE_ASSERT_NOT_REACHED();
>+                exit.m_values[i] = ExitValue::argumentsObjectThatWasNotCreated();
>                 break;
>             }
>         }
>@@ -5613,9 +5619,7 @@ private:
>             exit.m_values[index] = ExitValue::constant(m_graph.valueOfJSConstant(node));
>             return true;
>         case PhantomArguments:
>-            // FIXME: implement PhantomArguments.
>-            // https://bugs.webkit.org/show_bug.cgi?id=113986
>-            RELEASE_ASSERT_NOT_REACHED();
>+            exit.m_values[index] = ExitValue::argumentsObjectThatWasNotCreated();
>             return true;
>         default:
>             return false;
>Index: Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp	(working copy)
>@@ -146,6 +146,12 @@ static void compileStub(
>             jit.load64(AssemblyHelpers::addressFor(value.virtualRegister()), GPRInfo::regT0);
>             break;
>             
>+        case ExitValueArgumentsObjectThatWasNotCreated:
>+            // We can't actually recover this yet, but we can make the stack look sane. This is
>+            // a prerequisite to running the actual arguments recovery.
>+            jit.move(MacroAssembler::TrustedImm64(JSValue::encode(JSValue())), GPRInfo::regT0);
>+            break;
>+            
>         case ExitValueRecovery:
>             record->locations[value.rightRecoveryArgument()].restoreInto(
>                 jit, jitCode->stackmaps, registerScratch, GPRInfo::regT1);
>@@ -337,6 +343,15 @@ static void compileStub(
>     
>     handleExitCounts(jit, exit);
>     reifyInlinedCallFrames(jit, exit);
>+    
>+    ArgumentsRecoveryGenerator argumentsRecovery;
>+    for (unsigned index = exit.m_values.size(); index--;) {
>+        if (!exit.m_values[index].isArgumentsObjectThatWasNotCreated())
>+            continue;
>+        int operand = exit.m_values.operandForIndex(index);
>+        argumentsRecovery.generateFor(operand, exit.m_codeOrigin, jit);
>+    }
>+    
>     adjustAndJumpToTarget(jit, exit);
>     
>     LinkBuffer patchBuffer(*vm, &jit, codeBlock);
Comment 13 Whitehat Security 2014-11-18 13:40:25 PST
Comment on attachment 225498 [details]
the patch

>Index: Source/JavaScriptCore/ChangeLog
>===================================================================
>--- Source/JavaScriptCore/ChangeLog	(revision 164889)
>+++ Source/JavaScriptCore/ChangeLog	(working copy)
>@@ -1,3 +1,38 @@
>+2014-02-28  Filip Pizlo  <fpizlo@apple.com>
>+
>+        FTL should support PhantomArguments
>+        https://bugs.webkit.org/show_bug.cgi?id=113986
>+
>+        Reviewed by NOBODY (OOPS!).
>+        
>+        Adding PhantomArguments to the FTL mostly means wiring the recovery of the Arguments
>+        object into the FTL's OSR exit compiler.
>+
>+        * dfg/DFGOSRExitCompiler32_64.cpp:
>+        (JSC::DFG::OSRExitCompiler::compileExit): move the recovery code to DFGOSRExitCompilerCommon.cpp
>+        * dfg/DFGOSRExitCompiler64.cpp:
>+        (JSC::DFG::OSRExitCompiler::compileExit): move the recovery code to DFGOSRExitCompilerCommon.cpp
>+        * dfg/DFGOSRExitCompilerCommon.cpp:
>+        (JSC::DFG::ArgumentsRecoveryGenerator::ArgumentsRecoveryGenerator):
>+        (JSC::DFG::ArgumentsRecoveryGenerator::~ArgumentsRecoveryGenerator):
>+        (JSC::DFG::ArgumentsRecoveryGenerator::generateFor): this is the common place for the recovery code
>+        * dfg/DFGOSRExitCompilerCommon.h:
>+        * ftl/FTLCapabilities.cpp:
>+        (JSC::FTL::canCompile):
>+        * ftl/FTLExitValue.cpp:
>+        (JSC::FTL::ExitValue::dumpInContext):
>+        * ftl/FTLExitValue.h:
>+        (JSC::FTL::ExitValue::argumentsObjectThatWasNotCreated):
>+        (JSC::FTL::ExitValue::isArgumentsObjectThatWasNotCreated):
>+        (JSC::FTL::ExitValue::valueFormat):
>+        * ftl/FTLLowerDFGToLLVM.cpp:
>+        (JSC::FTL::LowerDFGToLLVM::compileNode):
>+        (JSC::FTL::LowerDFGToLLVM::compilePhantomArguments):
>+        (JSC::FTL::LowerDFGToLLVM::buildExitArguments):
>+        (JSC::FTL::LowerDFGToLLVM::tryToSetConstantExitArgument):
>+        * ftl/FTLOSRExitCompiler.cpp:
>+        (JSC::FTL::compileStub): Call into the ArgumentsRecoveryGenerator
>+
> 2014-02-28  Oliver Hunt  <oliver@apple.com>
> 
>         REGRESSION(r164835): It broke 10 JSC stress test on 32 bit platforms
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -393,66 +393,14 @@ void OSRExitCompiler::compileExit(const 
>     //     registers.
>     
>     if (haveArguments) {
>-        HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>-            NullableHashTraits<InlineCallFrame*>> didCreateArgumentsObject;
>+        ArgumentsRecoveryGenerator argumentsRecovery;
> 
>         for (size_t index = 0; index < operands.size(); ++index) {
>             const ValueRecovery& recovery = operands[index];
>             if (recovery.technique() != ArgumentsThatWereNotCreated)
>                 continue;
>-            int operand = operands.operandForIndex(index);
>-            // Find the right inline call frame.
>-            InlineCallFrame* inlineCallFrame = 0;
>-            for (InlineCallFrame* current = exit.m_codeOrigin.inlineCallFrame;
>-                 current;
>-                 current = current->caller.inlineCallFrame) {
>-                if (current->stackOffset >= operand) {
>-                    inlineCallFrame = current;
>-                    break;
>-                }
>-            }
>-
>-            if (!m_jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>-                continue;
>-            VirtualRegister argumentsRegister = m_jit.baselineArgumentsRegisterFor(inlineCallFrame);
>-            if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>-                // We know this call frame optimized out an arguments object that
>-                // the baseline JIT would have created. Do that creation now.
>-                if (inlineCallFrame) {
>-                    m_jit.setupArgumentsWithExecState(
>-                        AssemblyHelpers::TrustedImmPtr(inlineCallFrame));
>-                    m_jit.move(
>-                        AssemblyHelpers::TrustedImmPtr(
>-                            bitwise_cast<void*>(operationCreateInlinedArguments)),
>-                        GPRInfo::nonArgGPR0);
>-                } else {
>-                    m_jit.setupArgumentsExecState();
>-                    m_jit.move(
>-                        AssemblyHelpers::TrustedImmPtr(
>-                            bitwise_cast<void*>(operationCreateArguments)),
>-                        GPRInfo::nonArgGPR0);
>-                }
>-                m_jit.call(GPRInfo::nonArgGPR0);
>-                m_jit.store32(
>-                    AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                    AssemblyHelpers::tagFor(argumentsRegister));
>-                m_jit.store32(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::payloadFor(argumentsRegister));
>-                m_jit.store32(
>-                    AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                    AssemblyHelpers::tagFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.store32(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::payloadFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>-            }
>-
>-            m_jit.load32(AssemblyHelpers::payloadFor(argumentsRegister), GPRInfo::regT0);
>-            m_jit.store32(
>-                AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                AssemblyHelpers::tagFor(operand));
>-            m_jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor(operand));
>+            argumentsRecovery.generateFor(
>+                operands.operandForIndex(index), exit.m_codeOrigin, m_jit);
>         }
>     }
> 
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -365,50 +365,14 @@ void OSRExitCompiler::compileExit(const 
>     //     registers.
>     
>     if (haveArguments) {
>-        HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>-            NullableHashTraits<InlineCallFrame*>> didCreateArgumentsObject;
>+        ArgumentsRecoveryGenerator argumentsRecovery;
> 
>         for (size_t index = 0; index < operands.size(); ++index) {
>             const ValueRecovery& recovery = operands[index];
>             if (recovery.technique() != ArgumentsThatWereNotCreated)
>                 continue;
>-            int operand = operands.operandForIndex(index);
>-            // Find the right inline call frame.
>-            InlineCallFrame* inlineCallFrame = 0;
>-            for (InlineCallFrame* current = exit.m_codeOrigin.inlineCallFrame;
>-                 current;
>-                 current = current->caller.inlineCallFrame) {
>-                if (current->stackOffset >= operand) {
>-                    inlineCallFrame = current;
>-                    break;
>-                }
>-            }
>-
>-            if (!m_jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>-                continue;
>-            VirtualRegister argumentsRegister = m_jit.baselineArgumentsRegisterFor(inlineCallFrame);
>-            if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>-                // We know this call frame optimized out an arguments object that
>-                // the baseline JIT would have created. Do that creation now.
>-                if (inlineCallFrame) {
>-                    m_jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0);
>-                    m_jit.setupArguments(GPRInfo::regT0);
>-                } else
>-                    m_jit.setupArgumentsExecState();
>-                m_jit.move(
>-                    AssemblyHelpers::TrustedImmPtr(
>-                        bitwise_cast<void*>(operationCreateArguments)),
>-                    GPRInfo::nonArgGPR0);
>-                m_jit.call(GPRInfo::nonArgGPR0);
>-                m_jit.store64(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(argumentsRegister));
>-                m_jit.store64(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>-            }
>-
>-            m_jit.load64(AssemblyHelpers::addressFor(argumentsRegister), GPRInfo::regT0);
>-            m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
>+            argumentsRecovery.generateFor(
>+                operands.operandForIndex(index), exit.m_codeOrigin, m_jit);
>         }
>     }
> 
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -217,6 +217,89 @@ void adjustAndJumpToTarget(CCallHelpers&
>     jit.jump(GPRInfo::regT2);
> }
> 
>+ArgumentsRecoveryGenerator::ArgumentsRecoveryGenerator() { }
>+ArgumentsRecoveryGenerator::~ArgumentsRecoveryGenerator() { }
>+
>+void ArgumentsRecoveryGenerator::generateFor(
>+    int operand, CodeOrigin codeOrigin, CCallHelpers& jit)
>+{
>+    // Find the right inline call frame.
>+    InlineCallFrame* inlineCallFrame = 0;
>+    for (InlineCallFrame* current = codeOrigin.inlineCallFrame;
>+         current;
>+         current = current->caller.inlineCallFrame) {
>+        if (current->stackOffset >= operand) {
>+            inlineCallFrame = current;
>+            break;
>+        }
>+    }
>+
>+    if (!jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>+        return;
>+    VirtualRegister argumentsRegister = jit.baselineArgumentsRegisterFor(inlineCallFrame);
>+    if (m_didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>+        // We know this call frame optimized out an arguments object that
>+        // the baseline JIT would have created. Do that creation now.
>+#if USE(JSVALUE64)
>+        if (inlineCallFrame) {
>+            jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0);
>+            jit.setupArguments(GPRInfo::regT0);
>+        } else
>+            jit.setupArgumentsExecState();
>+        jit.move(
>+            AssemblyHelpers::TrustedImmPtr(
>+                bitwise_cast<void*>(operationCreateArguments)),
>+            GPRInfo::nonArgGPR0);
>+        jit.call(GPRInfo::nonArgGPR0);
>+        jit.store64(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(argumentsRegister));
>+        jit.store64(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>+#else // USE(JSVALUE64) -> so the 32_64 part
>+        if (inlineCallFrame) {
>+            jit.setupArgumentsWithExecState(
>+                AssemblyHelpers::TrustedImmPtr(inlineCallFrame));
>+            jit.move(
>+                AssemblyHelpers::TrustedImmPtr(
>+                    bitwise_cast<void*>(operationCreateInlinedArguments)),
>+                GPRInfo::nonArgGPR0);
>+        } else {
>+            jit.setupArgumentsExecState();
>+            jit.move(
>+                AssemblyHelpers::TrustedImmPtr(
>+                    bitwise_cast<void*>(operationCreateArguments)),
>+                GPRInfo::nonArgGPR0);
>+        }
>+        jit.call(GPRInfo::nonArgGPR0);
>+        jit.store32(
>+            AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+            AssemblyHelpers::tagFor(argumentsRegister));
>+        jit.store32(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::payloadFor(argumentsRegister));
>+        jit.store32(
>+            AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+            AssemblyHelpers::tagFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.store32(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::payloadFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>+#endif // USE(JSVALUE64)
>+    }
>+
>+#if USE(JSVALUE64)
>+    jit.load64(AssemblyHelpers::addressFor(argumentsRegister), GPRInfo::regT0);
>+    jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
>+#else // USE(JSVALUE64) -> so the 32_64 part
>+    jit.load32(AssemblyHelpers::payloadFor(argumentsRegister), GPRInfo::regT0);
>+    jit.store32(
>+        AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+        AssemblyHelpers::tagFor(operand));
>+    jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor(operand));
>+#endif // USE(JSVALUE64)
>+}
>+    
> } } // namespace JSC::DFG
> 
> #endif // ENABLE(DFG_JIT)
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h	(working copy)
>@@ -37,6 +37,18 @@ void handleExitCounts(CCallHelpers&, con
> void reifyInlinedCallFrames(CCallHelpers&, const OSRExitBase&);
> void adjustAndJumpToTarget(CCallHelpers&, const OSRExitBase&);
> 
>+class ArgumentsRecoveryGenerator {
>+public:
>+    ArgumentsRecoveryGenerator();
>+    ~ArgumentsRecoveryGenerator();
>+    
>+    void generateFor(int operand, CodeOrigin, CCallHelpers&);
>+    
>+private:
>+    HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>+        NullableHashTraits<InlineCallFrame*>> m_didCreateArgumentsObject;
>+};
>+
> } } // namespace JSC::DFG
> 
> #endif // ENABLE(DFG_JIT)
>Index: Source/JavaScriptCore/ftl/FTLCapabilities.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLCapabilities.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLCapabilities.cpp	(working copy)
>@@ -140,6 +140,7 @@ inline CapabilityLevel canCompile(Node* 
>     case MultiGetByOffset:
>     case MultiPutByOffset:
>     case ToPrimitive:
>+    case PhantomArguments:
>         // These are OK.
>         break;
>     case PutByIdDirect:
>Index: Source/JavaScriptCore/ftl/FTLExitValue.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLExitValue.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLExitValue.cpp	(working copy)
>@@ -59,6 +59,9 @@ void ExitValue::dumpInContext(PrintStrea
>     case ExitValueInJSStackAsDouble:
>         out.print("InJSStackAsDouble:r", virtualRegister());
>         return;
>+    case ExitValueArgumentsObjectThatWasNotCreated:
>+        out.print("ArgumentsObjectThatWasNotCreated");
>+        return;
>     case ExitValueRecovery:
>         out.print("Recovery(", recoveryOpcode(), ", arg", leftRecoveryArgument(), ", arg", rightRecoveryArgument(), ", ", recoveryFormat(), ")");
>         return;
>Index: Source/JavaScriptCore/ftl/FTLExitValue.h
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLExitValue.h	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLExitValue.h	(working copy)
>@@ -51,6 +51,7 @@ enum ExitValueKind {
>     ExitValueInJSStackAsInt32,
>     ExitValueInJSStackAsInt52,
>     ExitValueInJSStackAsDouble,
>+    ExitValueArgumentsObjectThatWasNotCreated,
>     ExitValueRecovery
> };
> 
>@@ -118,6 +119,13 @@ public:
>         return result;
>     }
>     
>+    static ExitValue argumentsObjectThatWasNotCreated()
>+    {
>+        ExitValue result;
>+        result.m_kind = ExitValueArgumentsObjectThatWasNotCreated;
>+        return result;
>+    }
>+    
>     static ExitValue recovery(RecoveryOpcode opcode, unsigned leftArgument, unsigned rightArgument, ValueFormat format)
>     {
>         ExitValue result;
>@@ -146,6 +154,7 @@ public:
>     }
>     bool isConstant() const { return kind() == ExitValueConstant; }
>     bool isArgument() const { return kind() == ExitValueArgument; }
>+    bool isArgumentsObjectThatWasNotCreated() const { return kind() == ExitValueArgumentsObjectThatWasNotCreated; }
>     bool isRecovery() const { return kind() == ExitValueRecovery; }
>     
>     ExitArgument exitArgument() const
>@@ -213,6 +222,7 @@ public:
>         case ExitValueDead:
>         case ExitValueConstant:
>         case ExitValueInJSStack:
>+        case ExitValueArgumentsObjectThatWasNotCreated:
>             return ValueFormatJSValue;
>             
>         case ExitValueArgument:
>Index: Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp	(working copy)
>@@ -282,6 +282,9 @@ private:
>         case WeakJSConstant:
>             compileWeakJSConstant();
>             break;
>+        case PhantomArguments:
>+            compilePhantomArguments();
>+            break;
>         case GetArgument:
>             compileGetArgument();
>             break;
>@@ -781,6 +784,11 @@ private:
>             break;
>         }
>     }
>+
>+    void compilePhantomArguments()
>+    {
>+        setJSValue(m_out.constInt64(JSValue::encode(JSValue())));
>+    }
>     
>     void compileWeakJSConstant()
>     {
>@@ -5519,9 +5527,7 @@ private:
>                 break;
>                 
>             case FlushedArguments:
>-                // FIXME: implement PhantomArguments.
>-                // https://bugs.webkit.org/show_bug.cgi?id=113986
>-                RELEASE_ASSERT_NOT_REACHED();
>+                exit.m_values[i] = ExitValue::argumentsObjectThatWasNotCreated();
>                 break;
>             }
>         }
>@@ -5613,9 +5619,7 @@ private:
>             exit.m_values[index] = ExitValue::constant(m_graph.valueOfJSConstant(node));
>             return true;
>         case PhantomArguments:
>-            // FIXME: implement PhantomArguments.
>-            // https://bugs.webkit.org/show_bug.cgi?id=113986
>-            RELEASE_ASSERT_NOT_REACHED();
>+            exit.m_values[index] = ExitValue::argumentsObjectThatWasNotCreated();
>             return true;
>         default:
>             return false;
>Index: Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp	(working copy)
>@@ -146,6 +146,12 @@ static void compileStub(
>             jit.load64(AssemblyHelpers::addressFor(value.virtualRegister()), GPRInfo::regT0);
>             break;
>             
>+        case ExitValueArgumentsObjectThatWasNotCreated:
>+            // We can't actually recover this yet, but we can make the stack look sane. This is
>+            // a prerequisite to running the actual arguments recovery.
>+            jit.move(MacroAssembler::TrustedImm64(JSValue::encode(JSValue())), GPRInfo::regT0);
>+            break;
>+            
>         case ExitValueRecovery:
>             record->locations[value.rightRecoveryArgument()].restoreInto(
>                 jit, jitCode->stackmaps, registerScratch, GPRInfo::regT1);
>@@ -337,6 +343,15 @@ static void compileStub(
>     
>     handleExitCounts(jit, exit);
>     reifyInlinedCallFrames(jit, exit);
>+    
>+    ArgumentsRecoveryGenerator argumentsRecovery;
>+    for (unsigned index = exit.m_values.size(); index--;) {
>+        if (!exit.m_values[index].isArgumentsObjectThatWasNotCreated())
>+            continue;
>+        int operand = exit.m_values.operandForIndex(index);
>+        argumentsRecovery.generateFor(operand, exit.m_codeOrigin, jit);
>+    }
>+    
>     adjustAndJumpToTarget(jit, exit);
>     
>     LinkBuffer patchBuffer(*vm, &jit, codeBlock);
Comment 14 Whitehat Security 2014-11-18 13:40:30 PST
Comment on attachment 225498 [details]
the patch

>Index: Source/JavaScriptCore/ChangeLog
>===================================================================
>--- Source/JavaScriptCore/ChangeLog	(revision 164889)
>+++ Source/JavaScriptCore/ChangeLog	(working copy)
>@@ -1,3 +1,38 @@
>+2014-02-28  Filip Pizlo  <fpizlo@apple.com>
>+
>+        FTL should support PhantomArguments
>+        https://bugs.webkit.org/show_bug.cgi?id=113986
>+
>+        Reviewed by NOBODY (OOPS!).
>+        
>+        Adding PhantomArguments to the FTL mostly means wiring the recovery of the Arguments
>+        object into the FTL's OSR exit compiler.
>+
>+        * dfg/DFGOSRExitCompiler32_64.cpp:
>+        (JSC::DFG::OSRExitCompiler::compileExit): move the recovery code to DFGOSRExitCompilerCommon.cpp
>+        * dfg/DFGOSRExitCompiler64.cpp:
>+        (JSC::DFG::OSRExitCompiler::compileExit): move the recovery code to DFGOSRExitCompilerCommon.cpp
>+        * dfg/DFGOSRExitCompilerCommon.cpp:
>+        (JSC::DFG::ArgumentsRecoveryGenerator::ArgumentsRecoveryGenerator):
>+        (JSC::DFG::ArgumentsRecoveryGenerator::~ArgumentsRecoveryGenerator):
>+        (JSC::DFG::ArgumentsRecoveryGenerator::generateFor): this is the common place for the recovery code
>+        * dfg/DFGOSRExitCompilerCommon.h:
>+        * ftl/FTLCapabilities.cpp:
>+        (JSC::FTL::canCompile):
>+        * ftl/FTLExitValue.cpp:
>+        (JSC::FTL::ExitValue::dumpInContext):
>+        * ftl/FTLExitValue.h:
>+        (JSC::FTL::ExitValue::argumentsObjectThatWasNotCreated):
>+        (JSC::FTL::ExitValue::isArgumentsObjectThatWasNotCreated):
>+        (JSC::FTL::ExitValue::valueFormat):
>+        * ftl/FTLLowerDFGToLLVM.cpp:
>+        (JSC::FTL::LowerDFGToLLVM::compileNode):
>+        (JSC::FTL::LowerDFGToLLVM::compilePhantomArguments):
>+        (JSC::FTL::LowerDFGToLLVM::buildExitArguments):
>+        (JSC::FTL::LowerDFGToLLVM::tryToSetConstantExitArgument):
>+        * ftl/FTLOSRExitCompiler.cpp:
>+        (JSC::FTL::compileStub): Call into the ArgumentsRecoveryGenerator
>+
> 2014-02-28  Oliver Hunt  <oliver@apple.com>
> 
>         REGRESSION(r164835): It broke 10 JSC stress test on 32 bit platforms
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -393,66 +393,14 @@ void OSRExitCompiler::compileExit(const 
>     //     registers.
>     
>     if (haveArguments) {
>-        HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>-            NullableHashTraits<InlineCallFrame*>> didCreateArgumentsObject;
>+        ArgumentsRecoveryGenerator argumentsRecovery;
> 
>         for (size_t index = 0; index < operands.size(); ++index) {
>             const ValueRecovery& recovery = operands[index];
>             if (recovery.technique() != ArgumentsThatWereNotCreated)
>                 continue;
>-            int operand = operands.operandForIndex(index);
>-            // Find the right inline call frame.
>-            InlineCallFrame* inlineCallFrame = 0;
>-            for (InlineCallFrame* current = exit.m_codeOrigin.inlineCallFrame;
>-                 current;
>-                 current = current->caller.inlineCallFrame) {
>-                if (current->stackOffset >= operand) {
>-                    inlineCallFrame = current;
>-                    break;
>-                }
>-            }
>-
>-            if (!m_jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>-                continue;
>-            VirtualRegister argumentsRegister = m_jit.baselineArgumentsRegisterFor(inlineCallFrame);
>-            if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>-                // We know this call frame optimized out an arguments object that
>-                // the baseline JIT would have created. Do that creation now.
>-                if (inlineCallFrame) {
>-                    m_jit.setupArgumentsWithExecState(
>-                        AssemblyHelpers::TrustedImmPtr(inlineCallFrame));
>-                    m_jit.move(
>-                        AssemblyHelpers::TrustedImmPtr(
>-                            bitwise_cast<void*>(operationCreateInlinedArguments)),
>-                        GPRInfo::nonArgGPR0);
>-                } else {
>-                    m_jit.setupArgumentsExecState();
>-                    m_jit.move(
>-                        AssemblyHelpers::TrustedImmPtr(
>-                            bitwise_cast<void*>(operationCreateArguments)),
>-                        GPRInfo::nonArgGPR0);
>-                }
>-                m_jit.call(GPRInfo::nonArgGPR0);
>-                m_jit.store32(
>-                    AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                    AssemblyHelpers::tagFor(argumentsRegister));
>-                m_jit.store32(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::payloadFor(argumentsRegister));
>-                m_jit.store32(
>-                    AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                    AssemblyHelpers::tagFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.store32(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::payloadFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>-            }
>-
>-            m_jit.load32(AssemblyHelpers::payloadFor(argumentsRegister), GPRInfo::regT0);
>-            m_jit.store32(
>-                AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>-                AssemblyHelpers::tagFor(operand));
>-            m_jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor(operand));
>+            argumentsRecovery.generateFor(
>+                operands.operandForIndex(index), exit.m_codeOrigin, m_jit);
>         }
>     }
> 
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -365,50 +365,14 @@ void OSRExitCompiler::compileExit(const 
>     //     registers.
>     
>     if (haveArguments) {
>-        HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>-            NullableHashTraits<InlineCallFrame*>> didCreateArgumentsObject;
>+        ArgumentsRecoveryGenerator argumentsRecovery;
> 
>         for (size_t index = 0; index < operands.size(); ++index) {
>             const ValueRecovery& recovery = operands[index];
>             if (recovery.technique() != ArgumentsThatWereNotCreated)
>                 continue;
>-            int operand = operands.operandForIndex(index);
>-            // Find the right inline call frame.
>-            InlineCallFrame* inlineCallFrame = 0;
>-            for (InlineCallFrame* current = exit.m_codeOrigin.inlineCallFrame;
>-                 current;
>-                 current = current->caller.inlineCallFrame) {
>-                if (current->stackOffset >= operand) {
>-                    inlineCallFrame = current;
>-                    break;
>-                }
>-            }
>-
>-            if (!m_jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>-                continue;
>-            VirtualRegister argumentsRegister = m_jit.baselineArgumentsRegisterFor(inlineCallFrame);
>-            if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>-                // We know this call frame optimized out an arguments object that
>-                // the baseline JIT would have created. Do that creation now.
>-                if (inlineCallFrame) {
>-                    m_jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0);
>-                    m_jit.setupArguments(GPRInfo::regT0);
>-                } else
>-                    m_jit.setupArgumentsExecState();
>-                m_jit.move(
>-                    AssemblyHelpers::TrustedImmPtr(
>-                        bitwise_cast<void*>(operationCreateArguments)),
>-                    GPRInfo::nonArgGPR0);
>-                m_jit.call(GPRInfo::nonArgGPR0);
>-                m_jit.store64(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(argumentsRegister));
>-                m_jit.store64(
>-                    GPRInfo::returnValueGPR,
>-                    AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister)));
>-                m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>-            }
>-
>-            m_jit.load64(AssemblyHelpers::addressFor(argumentsRegister), GPRInfo::regT0);
>-            m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
>+            argumentsRecovery.generateFor(
>+                operands.operandForIndex(index), exit.m_codeOrigin, m_jit);
>         }
>     }
> 
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp	(working copy)
>@@ -1,5 +1,5 @@
> /*
>- * Copyright (C) 2013 Apple Inc. All rights reserved.
>+ * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>@@ -217,6 +217,89 @@ void adjustAndJumpToTarget(CCallHelpers&
>     jit.jump(GPRInfo::regT2);
> }
> 
>+ArgumentsRecoveryGenerator::ArgumentsRecoveryGenerator() { }
>+ArgumentsRecoveryGenerator::~ArgumentsRecoveryGenerator() { }
>+
>+void ArgumentsRecoveryGenerator::generateFor(
>+    int operand, CodeOrigin codeOrigin, CCallHelpers& jit)
>+{
>+    // Find the right inline call frame.
>+    InlineCallFrame* inlineCallFrame = 0;
>+    for (InlineCallFrame* current = codeOrigin.inlineCallFrame;
>+         current;
>+         current = current->caller.inlineCallFrame) {
>+        if (current->stackOffset >= operand) {
>+            inlineCallFrame = current;
>+            break;
>+        }
>+    }
>+
>+    if (!jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
>+        return;
>+    VirtualRegister argumentsRegister = jit.baselineArgumentsRegisterFor(inlineCallFrame);
>+    if (m_didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
>+        // We know this call frame optimized out an arguments object that
>+        // the baseline JIT would have created. Do that creation now.
>+#if USE(JSVALUE64)
>+        if (inlineCallFrame) {
>+            jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0);
>+            jit.setupArguments(GPRInfo::regT0);
>+        } else
>+            jit.setupArgumentsExecState();
>+        jit.move(
>+            AssemblyHelpers::TrustedImmPtr(
>+                bitwise_cast<void*>(operationCreateArguments)),
>+            GPRInfo::nonArgGPR0);
>+        jit.call(GPRInfo::nonArgGPR0);
>+        jit.store64(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(argumentsRegister));
>+        jit.store64(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>+#else // USE(JSVALUE64) -> so the 32_64 part
>+        if (inlineCallFrame) {
>+            jit.setupArgumentsWithExecState(
>+                AssemblyHelpers::TrustedImmPtr(inlineCallFrame));
>+            jit.move(
>+                AssemblyHelpers::TrustedImmPtr(
>+                    bitwise_cast<void*>(operationCreateInlinedArguments)),
>+                GPRInfo::nonArgGPR0);
>+        } else {
>+            jit.setupArgumentsExecState();
>+            jit.move(
>+                AssemblyHelpers::TrustedImmPtr(
>+                    bitwise_cast<void*>(operationCreateArguments)),
>+                GPRInfo::nonArgGPR0);
>+        }
>+        jit.call(GPRInfo::nonArgGPR0);
>+        jit.store32(
>+            AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+            AssemblyHelpers::tagFor(argumentsRegister));
>+        jit.store32(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::payloadFor(argumentsRegister));
>+        jit.store32(
>+            AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+            AssemblyHelpers::tagFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.store32(
>+            GPRInfo::returnValueGPR,
>+            AssemblyHelpers::payloadFor(unmodifiedArgumentsRegister(argumentsRegister)));
>+        jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
>+#endif // USE(JSVALUE64)
>+    }
>+
>+#if USE(JSVALUE64)
>+    jit.load64(AssemblyHelpers::addressFor(argumentsRegister), GPRInfo::regT0);
>+    jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
>+#else // USE(JSVALUE64) -> so the 32_64 part
>+    jit.load32(AssemblyHelpers::payloadFor(argumentsRegister), GPRInfo::regT0);
>+    jit.store32(
>+        AssemblyHelpers::TrustedImm32(JSValue::CellTag),
>+        AssemblyHelpers::tagFor(operand));
>+    jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor(operand));
>+#endif // USE(JSVALUE64)
>+}
>+    
> } } // namespace JSC::DFG
> 
> #endif // ENABLE(DFG_JIT)
>Index: Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h
>===================================================================
>--- Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h	(revision 164883)
>+++ Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h	(working copy)
>@@ -37,6 +37,18 @@ void handleExitCounts(CCallHelpers&, con
> void reifyInlinedCallFrames(CCallHelpers&, const OSRExitBase&);
> void adjustAndJumpToTarget(CCallHelpers&, const OSRExitBase&);
> 
>+class ArgumentsRecoveryGenerator {
>+public:
>+    ArgumentsRecoveryGenerator();
>+    ~ArgumentsRecoveryGenerator();
>+    
>+    void generateFor(int operand, CodeOrigin, CCallHelpers&);
>+    
>+private:
>+    HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
>+        NullableHashTraits<InlineCallFrame*>> m_didCreateArgumentsObject;
>+};
>+
> } } // namespace JSC::DFG
> 
> #endif // ENABLE(DFG_JIT)
>Index: Source/JavaScriptCore/ftl/FTLCapabilities.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLCapabilities.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLCapabilities.cpp	(working copy)
>@@ -140,6 +140,7 @@ inline CapabilityLevel canCompile(Node* 
>     case MultiGetByOffset:
>     case MultiPutByOffset:
>     case ToPrimitive:
>+    case PhantomArguments:
>         // These are OK.
>         break;
>     case PutByIdDirect:
>Index: Source/JavaScriptCore/ftl/FTLExitValue.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLExitValue.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLExitValue.cpp	(working copy)
>@@ -59,6 +59,9 @@ void ExitValue::dumpInContext(PrintStrea
>     case ExitValueInJSStackAsDouble:
>         out.print("InJSStackAsDouble:r", virtualRegister());
>         return;
>+    case ExitValueArgumentsObjectThatWasNotCreated:
>+        out.print("ArgumentsObjectThatWasNotCreated");
>+        return;
>     case ExitValueRecovery:
>         out.print("Recovery(", recoveryOpcode(), ", arg", leftRecoveryArgument(), ", arg", rightRecoveryArgument(), ", ", recoveryFormat(), ")");
>         return;
>Index: Source/JavaScriptCore/ftl/FTLExitValue.h
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLExitValue.h	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLExitValue.h	(working copy)
>@@ -51,6 +51,7 @@ enum ExitValueKind {
>     ExitValueInJSStackAsInt32,
>     ExitValueInJSStackAsInt52,
>     ExitValueInJSStackAsDouble,
>+    ExitValueArgumentsObjectThatWasNotCreated,
>     ExitValueRecovery
> };
> 
>@@ -118,6 +119,13 @@ public:
>         return result;
>     }
>     
>+    static ExitValue argumentsObjectThatWasNotCreated()
>+    {
>+        ExitValue result;
>+        result.m_kind = ExitValueArgumentsObjectThatWasNotCreated;
>+        return result;
>+    }
>+    
>     static ExitValue recovery(RecoveryOpcode opcode, unsigned leftArgument, unsigned rightArgument, ValueFormat format)
>     {
>         ExitValue result;
>@@ -146,6 +154,7 @@ public:
>     }
>     bool isConstant() const { return kind() == ExitValueConstant; }
>     bool isArgument() const { return kind() == ExitValueArgument; }
>+    bool isArgumentsObjectThatWasNotCreated() const { return kind() == ExitValueArgumentsObjectThatWasNotCreated; }
>     bool isRecovery() const { return kind() == ExitValueRecovery; }
>     
>     ExitArgument exitArgument() const
>@@ -213,6 +222,7 @@ public:
>         case ExitValueDead:
>         case ExitValueConstant:
>         case ExitValueInJSStack:
>+        case ExitValueArgumentsObjectThatWasNotCreated:
>             return ValueFormatJSValue;
>             
>         case ExitValueArgument:
>Index: Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp	(working copy)
>@@ -282,6 +282,9 @@ private:
>         case WeakJSConstant:
>             compileWeakJSConstant();
>             break;
>+        case PhantomArguments:
>+            compilePhantomArguments();
>+            break;
>         case GetArgument:
>             compileGetArgument();
>             break;
>@@ -781,6 +784,11 @@ private:
>             break;
>         }
>     }
>+
>+    void compilePhantomArguments()
>+    {
>+        setJSValue(m_out.constInt64(JSValue::encode(JSValue())));
>+    }
>     
>     void compileWeakJSConstant()
>     {
>@@ -5519,9 +5527,7 @@ private:
>                 break;
>                 
>             case FlushedArguments:
>-                // FIXME: implement PhantomArguments.
>-                // https://bugs.webkit.org/show_bug.cgi?id=113986
>-                RELEASE_ASSERT_NOT_REACHED();
>+                exit.m_values[i] = ExitValue::argumentsObjectThatWasNotCreated();
>                 break;
>             }
>         }
>@@ -5613,9 +5619,7 @@ private:
>             exit.m_values[index] = ExitValue::constant(m_graph.valueOfJSConstant(node));
>             return true;
>         case PhantomArguments:
>-            // FIXME: implement PhantomArguments.
>-            // https://bugs.webkit.org/show_bug.cgi?id=113986
>-            RELEASE_ASSERT_NOT_REACHED();
>+            exit.m_values[index] = ExitValue::argumentsObjectThatWasNotCreated();
>             return true;
>         default:
>             return false;
>Index: Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp
>===================================================================
>--- Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp	(revision 164883)
>+++ Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp	(working copy)
>@@ -146,6 +146,12 @@ static void compileStub(
>             jit.load64(AssemblyHelpers::addressFor(value.virtualRegister()), GPRInfo::regT0);
>             break;
>             
>+        case ExitValueArgumentsObjectThatWasNotCreated:
>+            // We can't actually recover this yet, but we can make the stack look sane. This is
>+            // a prerequisite to running the actual arguments recovery.
>+            jit.move(MacroAssembler::TrustedImm64(JSValue::encode(JSValue())), GPRInfo::regT0);
>+            break;
>+            
>         case ExitValueRecovery:
>             record->locations[value.rightRecoveryArgument()].restoreInto(
>                 jit, jitCode->stackmaps, registerScratch, GPRInfo::regT1);
>@@ -337,6 +343,15 @@ static void compileStub(
>     
>     handleExitCounts(jit, exit);
>     reifyInlinedCallFrames(jit, exit);
>+    
>+    ArgumentsRecoveryGenerator argumentsRecovery;
>+    for (unsigned index = exit.m_values.size(); index--;) {
>+        if (!exit.m_values[index].isArgumentsObjectThatWasNotCreated())
>+            continue;
>+        int operand = exit.m_values.operandForIndex(index);
>+        argumentsRecovery.generateFor(operand, exit.m_codeOrigin, jit);
>+    }
>+    
>     adjustAndJumpToTarget(jit, exit);
>     
>     LinkBuffer patchBuffer(*vm, &jit, codeBlock);