DFG OSR exit for UInt32ToNumber should roll forward, not roll backward
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 14 Dec 2011 01:46:36 +0000 (01:46 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 14 Dec 2011 01:46:36 +0000 (01:46 +0000)
https://bugs.webkit.org/show_bug.cgi?id=74463

Source/JavaScriptCore:

Reviewed by Gavin Barraclough.

Implements roll-forward OSR exit for UInt32ToNumber, which requires ValueRecoveries knowing
how to execute the slow path of UInt32ToNumber.

* bytecode/CodeBlock.h:
(JSC::CodeBlock::lastOSRExit):
* bytecode/CodeOrigin.h:
(JSC::CodeOrigin::operator!=):
* bytecode/ValueRecovery.h:
(JSC::ValueRecovery::uint32InGPR):
(JSC::ValueRecovery::gpr):
(JSC::ValueRecovery::dump):
* dfg/DFGAssemblyHelpers.cpp:
* dfg/DFGAssemblyHelpers.h:
* dfg/DFGOSRExit.h:
(JSC::DFG::OSRExit::valueRecoveryForOperand):
* dfg/DFGOSRExitCompiler32_64.cpp:
(JSC::DFG::OSRExitCompiler::compileExit):
* dfg/DFGOSRExitCompiler64.cpp:
(JSC::DFG::OSRExitCompiler::compileExit):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileUInt32ToNumber):
(JSC::DFG::SpeculativeJIT::compileGetByValOnIntTypedArray):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::nonSpeculativeUInt32ToNumber):
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::nonSpeculativeUInt32ToNumber):
(JSC::DFG::SpeculativeJIT::compile):

LayoutTests:

Reviewed by Gavin Barraclough.

* fast/js/dfg-uint32-to-number-expected.txt: Added.
* fast/js/dfg-uint32-to-number.html: Added.
* fast/js/script-tests/dfg-uint32-to-number.js: Added.
(foo):

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

17 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/js/dfg-uint32-to-number-expected.txt [new file with mode: 0644]
LayoutTests/fast/js/dfg-uint32-to-number.html [new file with mode: 0644]
LayoutTests/fast/js/script-tests/dfg-uint32-to-number.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/CodeBlock.h
Source/JavaScriptCore/bytecode/CodeOrigin.h
Source/JavaScriptCore/bytecode/ValueRecovery.h
Source/JavaScriptCore/dfg/DFGAssemblyHelpers.cpp
Source/JavaScriptCore/dfg/DFGAssemblyHelpers.h
Source/JavaScriptCore/dfg/DFGOSRExit.h
Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp
Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp

index b92a2bf..337224e 100644 (file)
@@ -1,3 +1,15 @@
+2011-12-13  Filip Pizlo  <fpizlo@apple.com>
+
+        DFG OSR exit for UInt32ToNumber should roll forward, not roll backward
+        https://bugs.webkit.org/show_bug.cgi?id=74463
+
+        Reviewed by Gavin Barraclough.
+
+        * fast/js/dfg-uint32-to-number-expected.txt: Added.
+        * fast/js/dfg-uint32-to-number.html: Added.
+        * fast/js/script-tests/dfg-uint32-to-number.js: Added.
+        (foo):
+
 2011-12-13  Dmitry Lomov  <dslomov@google.com>
 
         https://bugs.webkit.org/show_bug.cgi?id=73691
diff --git a/LayoutTests/fast/js/dfg-uint32-to-number-expected.txt b/LayoutTests/fast/js/dfg-uint32-to-number-expected.txt
new file mode 100644 (file)
index 0000000..b63c75f
--- /dev/null
@@ -0,0 +1,13 @@
+This tests that if the DFG fails to convert a uint32 to a number, it will OSR exit correctly.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS result is 124500
+PASS foo({f:2147483648}, {f:32}) is 2147483648
+PASS foo({f:2147483648}, {f:31}) is 1
+PASS foo({f:2147483648}, {f:30}) is 2
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/js/dfg-uint32-to-number.html b/LayoutTests/fast/js/dfg-uint32-to-number.html
new file mode 100644 (file)
index 0000000..182bf63
--- /dev/null
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="script-tests/dfg-uint32-to-number.js"></script>
+<script src="resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/js/script-tests/dfg-uint32-to-number.js b/LayoutTests/fast/js/script-tests/dfg-uint32-to-number.js
new file mode 100644 (file)
index 0000000..b7d859d
--- /dev/null
@@ -0,0 +1,19 @@
+description(
+"This tests that if the DFG fails to convert a uint32 to a number, it will OSR exit correctly."
+);
+
+function foo(a,b) {
+    return a.f >>> b.f;
+}
+
+var result = 0;
+for (var i = 0; i < 1000; ++i) {
+    result += foo({f:i + 0.5}, {f:2});
+}
+
+shouldBe("result", "124500");
+
+shouldBe("foo({f:2147483648}, {f:32})", "2147483648");
+shouldBe("foo({f:2147483648}, {f:31})", "1");
+shouldBe("foo({f:2147483648}, {f:30})", "2");
+
index a46b220..a410a38 100644 (file)
@@ -1,3 +1,40 @@
+2011-12-13  Filip Pizlo  <fpizlo@apple.com>
+
+        DFG OSR exit for UInt32ToNumber should roll forward, not roll backward
+        https://bugs.webkit.org/show_bug.cgi?id=74463
+
+        Reviewed by Gavin Barraclough.
+        
+        Implements roll-forward OSR exit for UInt32ToNumber, which requires ValueRecoveries knowing
+        how to execute the slow path of UInt32ToNumber.
+
+        * bytecode/CodeBlock.h:
+        (JSC::CodeBlock::lastOSRExit):
+        * bytecode/CodeOrigin.h:
+        (JSC::CodeOrigin::operator!=):
+        * bytecode/ValueRecovery.h:
+        (JSC::ValueRecovery::uint32InGPR):
+        (JSC::ValueRecovery::gpr):
+        (JSC::ValueRecovery::dump):
+        * dfg/DFGAssemblyHelpers.cpp:
+        * dfg/DFGAssemblyHelpers.h:
+        * dfg/DFGOSRExit.h:
+        (JSC::DFG::OSRExit::valueRecoveryForOperand):
+        * dfg/DFGOSRExitCompiler32_64.cpp:
+        (JSC::DFG::OSRExitCompiler::compileExit):
+        * dfg/DFGOSRExitCompiler64.cpp:
+        (JSC::DFG::OSRExitCompiler::compileExit):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileUInt32ToNumber):
+        (JSC::DFG::SpeculativeJIT::compileGetByValOnIntTypedArray):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::nonSpeculativeUInt32ToNumber):
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::nonSpeculativeUInt32ToNumber):
+        (JSC::DFG::SpeculativeJIT::compile):
+
 2011-12-13  Oliver Hunt  <oliver@apple.com>
 
         Arguments object doesn't handle mutation of length property correctly
index fdfe422..615f2b5 100644 (file)
@@ -427,6 +427,11 @@ namespace JSC {
             m_dfgData->osrExit.append(osrExit);
         }
         
+        DFG::OSRExit& lastOSRExit()
+        {
+            return m_dfgData->osrExit.last();
+        }
+        
         void appendSpeculationRecovery(const DFG::SpeculationRecovery& recovery)
         {
             createDFGDataIfNecessary();
index 0dae85a..7b6ce7d 100644 (file)
@@ -73,6 +73,8 @@ struct CodeOrigin {
     
     bool operator==(const CodeOrigin& other) const;
     
+    bool operator!=(const CodeOrigin& other) const { return !(*this == other); }
+    
 #ifndef NDEBUG
     // Get the inline stack. This is slow, and is intended for debugging only.
     Vector<CodeOrigin> inlineStack() const;
index d2bfe82..0b3cc14 100644 (file)
@@ -55,6 +55,7 @@ enum ValueRecoveryTechnique {
     InPair,
 #endif
     InFPR,
+    UInt32InGPR,
     // It's in the register file, but at a different location.
     DisplacedInRegisterFile,
     // It's in the register file, at a different location, and it's unboxed.
@@ -118,6 +119,14 @@ public:
         return result;
     }
     
+    static ValueRecovery uint32InGPR(MacroAssembler::RegisterID gpr)
+    {
+        ValueRecovery result;
+        result.m_technique = UInt32InGPR;
+        result.m_source.gpr = gpr;
+        return result;
+    }
+    
 #if USE(JSVALUE32_64)
     static ValueRecovery inPair(MacroAssembler::RegisterID tagGPR, MacroAssembler::RegisterID payloadGPR)
     {
@@ -186,7 +195,7 @@ public:
     
     MacroAssembler::RegisterID gpr() const
     {
-        ASSERT(m_technique == InGPR || m_technique == UnboxedInt32InGPR || m_technique == UnboxedBooleanInGPR);
+        ASSERT(m_technique == InGPR || m_technique == UnboxedInt32InGPR || m_technique == UnboxedBooleanInGPR || m_technique == UInt32InGPR);
         return m_source.gpr;
     }
     
@@ -247,8 +256,11 @@ public:
         case UnboxedBooleanInGPR:
             fprintf(out, "bool(%%r%d)", gpr());
             break;
+        case UInt32InGPR:
+            fprintf(out, "uint32(%%r%d)", gpr());
+            break;
         case InFPR:
-            fprintf(out, "%%r%d", fpr());
+            fprintf(out, "%%fr%d", fpr());
             break;
 #if USE(JSVALUE32_64)
         case InPair:
index 0b8fc68..969101e 100644 (file)
@@ -30,6 +30,8 @@
 
 namespace JSC { namespace DFG {
 
+const double AssemblyHelpers::twoToThe32 = (double)0x100000000ull;
+
 Vector<BytecodeAndMachineOffset>& AssemblyHelpers::decodedCodeMapFor(CodeBlock* codeBlock)
 {
     ASSERT(codeBlock == codeBlock->baselineVersion());
index e8d8d10..874d1d0 100644 (file)
@@ -305,6 +305,8 @@ public:
     }
     
     Vector<BytecodeAndMachineOffset>& decodedCodeMapFor(CodeBlock*);
+    
+    static const double twoToThe32;
 
 protected:
     JSGlobalData* m_globalData;
index fe09877..cf96f4f 100644 (file)
@@ -107,6 +107,12 @@ struct OSRExit {
             return m_arguments[index];
         return m_variables[index - m_arguments.size()];
     }
+    ValueRecovery& valueRecoveryForOperand(int operand)
+    {
+        if (operandIsArgument(operand))
+            return m_arguments[operandToArgument(operand)];
+        return m_variables[operand];
+    }
     bool isArgument(int index) const { return index < (int)m_arguments.size(); }
     bool isVariable(int index) const { return !isArgument(index); }
     int argumentForIndex(int index) const
index f9fe56c..58a5f22 100644 (file)
@@ -124,6 +124,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco
     bool haveUnboxedInt32InRegisterFile = false;
     bool haveUnboxedCellInRegisterFile = false;
     bool haveUnboxedBooleanInRegisterFile = false;
+    bool haveUInt32s = false;
     bool haveFPRs = false;
     bool haveConstants = false;
     bool haveUndefined = false;
@@ -147,6 +148,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco
                 case InGPR:
                 case UnboxedInt32InGPR:
                 case UnboxedBooleanInGPR:
+                case UInt32InGPR:
                 case InPair:
                 case InFPR:
                     if (!poisonedVirtualRegisters[recovery.virtualRegister()]) {
@@ -160,6 +162,10 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco
             }
             break;
             
+        case UInt32InGPR:
+            haveUInt32s = true;
+            break;
+
         case AlreadyInRegisterFileAsUnboxedInt32:
             haveUnboxedInt32InRegisterFile = true;
             break;
@@ -187,7 +193,8 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco
         }
     }
     
-    EncodedJSValue* scratchBuffer = static_cast<EncodedJSValue*>(m_jit.globalData()->scratchBufferForSize(sizeof(EncodedJSValue) * (numberOfPoisonedVirtualRegisters + ((numberOfDisplacedVirtualRegisters * 2) <= GPRInfo::numberOfRegisters ? 0 : numberOfDisplacedVirtualRegisters))));
+    unsigned scratchBufferLengthBeforeUInt32s = numberOfPoisonedVirtualRegisters + ((numberOfDisplacedVirtualRegisters * 2) <= GPRInfo::numberOfRegisters ? 0 : numberOfDisplacedVirtualRegisters);
+    EncodedJSValue* scratchBuffer = static_cast<EncodedJSValue*>(m_jit.globalData()->scratchBufferForSize(sizeof(EncodedJSValue) * (scratchBufferLengthBeforeUInt32s + (haveUInt32s ? 2 : 0))));
 
     // From here on, the code assumes that it is profitable to maximize the distance
     // between when something is computed and when it is stored.
@@ -252,6 +259,49 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco
                 m_jit.store32(recovery.payloadGPR(), AssemblyHelpers::payloadFor((VirtualRegister)operand));
             }
             break;
+        case UInt32InGPR: {
+            EncodedJSValue* myScratch = scratchBuffer + scratchBufferLengthBeforeUInt32s;
+            
+            GPRReg addressGPR = GPRInfo::regT0;
+            if (addressGPR == recovery.gpr())
+                addressGPR = GPRInfo::regT1;
+            
+            m_jit.storePtr(addressGPR, myScratch);
+            m_jit.move(AssemblyHelpers::TrustedImmPtr(myScratch + 1), addressGPR);
+            m_jit.storeDouble(FPRInfo::fpRegT0, addressGPR);
+            
+            AssemblyHelpers::Jump positive = m_jit.branch32(AssemblyHelpers::GreaterThanOrEqual, recovery.gpr(), AssemblyHelpers::TrustedImm32(0));
+            
+            m_jit.convertInt32ToDouble(recovery.gpr(), FPRInfo::fpRegT0);
+            m_jit.addDouble(AssemblyHelpers::AbsoluteAddress(&AssemblyHelpers::twoToThe32), FPRInfo::fpRegT0);
+            if (exit.isVariable(index) && poisonedVirtualRegisters[exit.variableForIndex(index)]) {
+                m_jit.move(AssemblyHelpers::TrustedImmPtr(scratchBuffer + scratchIndex), addressGPR);
+                m_jit.storeDouble(FPRInfo::fpRegT0, addressGPR);
+            } else
+                m_jit.storeDouble(FPRInfo::fpRegT0, AssemblyHelpers::addressFor((VirtualRegister)operand));
+            
+            AssemblyHelpers::Jump done = m_jit.jump();
+            
+            positive.link(&m_jit);
+            
+            if (exit.isVariable(index) && poisonedVirtualRegisters[exit.variableForIndex(index)]) {
+                m_jit.store32(recovery.gpr(), reinterpret_cast<char*>(scratchBuffer + scratchIndex) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload));
+                m_jit.store32(AssemblyHelpers::TrustedImm32(JSValue::Int32Tag), reinterpret_cast<char*>(scratchBuffer + scratchIndex) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag));
+            } else {
+                m_jit.store32(recovery.gpr(), AssemblyHelpers::payloadFor((VirtualRegister)operand));
+                m_jit.store32(AssemblyHelpers::TrustedImm32(JSValue::Int32Tag), AssemblyHelpers::tagFor((VirtualRegister)operand));
+            }
+            
+            done.link(&m_jit);
+            
+            m_jit.move(AssemblyHelpers::TrustedImmPtr(myScratch + 1), addressGPR);
+            m_jit.loadDouble(addressGPR, FPRInfo::fpRegT0);
+            m_jit.loadPtr(myScratch, addressGPR);
+                              
+            if (exit.isVariable(index) && poisonedVirtualRegisters[exit.variableForIndex(index)])
+                scratchIndex++;
+            break;
+        }
         default:
             break;
         }
@@ -378,6 +428,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco
 
             case InFPR:
             case InPair:
+            case UInt32InGPR:
                 m_jit.load32(reinterpret_cast<char*>(scratchBuffer + scratchIndex) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload), GPRInfo::regT0);
                 m_jit.load32(reinterpret_cast<char*>(scratchBuffer + scratchIndex) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag), GPRInfo::regT1);
                 m_jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor((VirtualRegister)virtualRegister));
index 91529cc..45afac7 100644 (file)
@@ -119,6 +119,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco
     bool haveFPRs = false;
     bool haveConstants = false;
     bool haveUndefined = false;
+    bool haveUInt32s = false;
     
     for (int index = 0; index < exit.numberOfRecoveries(); ++index) {
         const ValueRecovery& recovery = exit.valueRecovery(index);
@@ -140,6 +141,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco
                 switch (exit.m_variables[recovery.virtualRegister()].technique()) {
                 case InGPR:
                 case UnboxedInt32InGPR:
+                case UInt32InGPR:
                 case InFPR:
                     if (!poisonedVirtualRegisters[recovery.virtualRegister()]) {
                         poisonedVirtualRegisters[recovery.virtualRegister()] = true;
@@ -157,6 +159,10 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco
             haveUnboxedInt32s = true;
             break;
             
+        case UInt32InGPR:
+            haveUInt32s = true;
+            break;
+            
         case InFPR:
             haveFPRs = true;
             break;
@@ -180,6 +186,8 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco
         fprintf(stderr, "Displaced=%u ", numberOfDisplacedVirtualRegisters);
     if (haveUnboxedInt32s)
         fprintf(stderr, "UnboxedInt32 ");
+    if (haveUInt32s)
+        fprintf(stderr, "UInt32 ");
     if (haveFPRs)
         fprintf(stderr, "FPR ");
     if (haveConstants)
@@ -189,14 +197,14 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco
     fprintf(stderr, " ");
 #endif
     
-    EncodedJSValue* scratchBuffer = static_cast<EncodedJSValue*>(m_jit.globalData()->scratchBufferForSize(sizeof(EncodedJSValue) * (numberOfPoisonedVirtualRegisters + (numberOfDisplacedVirtualRegisters <= GPRInfo::numberOfRegisters ? 0 : numberOfDisplacedVirtualRegisters))));
+    EncodedJSValue* scratchBuffer = static_cast<EncodedJSValue*>(m_jit.globalData()->scratchBufferForSize(sizeof(EncodedJSValue) * std::max(haveUInt32s ? 2u : 0u, numberOfPoisonedVirtualRegisters + (numberOfDisplacedVirtualRegisters <= GPRInfo::numberOfRegisters ? 0 : numberOfDisplacedVirtualRegisters))));
 
     // From here on, the code assumes that it is profitable to maximize the distance
     // between when something is computed and when it is stored.
     
     // 5) Perform all reboxing of integers.
     
-    if (haveUnboxedInt32s) {
+    if (haveUnboxedInt32s || haveUInt32s) {
         for (int index = 0; index < exit.numberOfRecoveries(); ++index) {
             const ValueRecovery& recovery = exit.valueRecovery(index);
             switch (recovery.technique()) {
@@ -209,6 +217,44 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco
                 m_jit.store32(AssemblyHelpers::Imm32(static_cast<uint32_t>(TagTypeNumber >> 32)), AssemblyHelpers::tagFor(static_cast<VirtualRegister>(exit.operandForIndex(index))));
                 break;
                 
+            case UInt32InGPR: {
+                // This occurs when the speculative JIT left an unsigned 32-bit integer
+                // in a GPR. If it's positive, we can just box the int. Otherwise we
+                // need to turn it into a boxed double.
+                
+                // We don't try to be clever with register allocation here; we assume
+                // that the program is using FPRs and we don't try to figure out which
+                // ones it is using. Instead just temporarily save fpRegT0 and then
+                // restore it. This makes sense because this path is not cheap to begin
+                // with, and should happen very rarely.
+                
+                GPRReg addressGPR = GPRInfo::regT0;
+                if (addressGPR == recovery.gpr())
+                    addressGPR = GPRInfo::regT1;
+                
+                m_jit.storePtr(addressGPR, scratchBuffer);
+                m_jit.move(AssemblyHelpers::TrustedImmPtr(scratchBuffer + 1), addressGPR);
+                m_jit.storeDouble(FPRInfo::fpRegT0, addressGPR);
+                
+                AssemblyHelpers::Jump positive = m_jit.branch32(AssemblyHelpers::GreaterThanOrEqual, recovery.gpr(), AssemblyHelpers::TrustedImm32(0));
+
+                m_jit.convertInt32ToDouble(recovery.gpr(), FPRInfo::fpRegT0);
+                m_jit.addDouble(AssemblyHelpers::AbsoluteAddress(&AssemblyHelpers::twoToThe32), FPRInfo::fpRegT0);
+                m_jit.boxDouble(FPRInfo::fpRegT0, recovery.gpr());
+                
+                AssemblyHelpers::Jump done = m_jit.jump();
+                
+                positive.link(&m_jit);
+                
+                m_jit.orPtr(GPRInfo::tagTypeNumberRegister, recovery.gpr());
+                
+                done.link(&m_jit);
+                
+                m_jit.loadDouble(addressGPR, FPRInfo::fpRegT0);
+                m_jit.loadPtr(scratchBuffer, addressGPR);
+                break;
+            }
+                
             default:
                 break;
             }
@@ -226,6 +272,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco
         switch (recovery.technique()) {
         case InGPR:
         case UnboxedInt32InGPR:
+        case UInt32InGPR:
             if (exit.isVariable(index) && poisonedVirtualRegisters[exit.variableForIndex(index)])
                 m_jit.storePtr(recovery.gpr(), scratchBuffer + scratchIndex++);
             else
@@ -395,6 +442,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco
             switch (recovery.technique()) {
             case InGPR:
             case UnboxedInt32InGPR:
+            case UInt32InGPR:
             case InFPR:
                 m_jit.loadPtr(scratchBuffer + scratchIndex++, GPRInfo::regT0);
                 m_jit.storePtr(GPRInfo::regT0, AssemblyHelpers::addressFor((VirtualRegister)virtualRegister));
index b965d3c..104fc85 100644 (file)
@@ -44,8 +44,6 @@ static double DFG_OPERATION fmodAsDFGOperation(double x, double y)
 #define fmodAsDFGOperation fmod
 #endif
 
-const double SpeculativeJIT::twoToThe32 = (double)0x100000000ull;
-
 void SpeculativeJIT::clearGenerationInfo()
 {
     for (unsigned i = 0; i < m_generationInfo.size(); ++i)
@@ -1459,6 +1457,56 @@ void SpeculativeJIT::compileValueToInt32(Node& node)
     integerResult(result.gpr(), m_compileIndex, op1.format());
 }
 
+void SpeculativeJIT::compileUInt32ToNumber(Node& node)
+{
+    if (!nodeCanSpeculateInteger(node.arithNodeFlags())) {
+        // We know that this sometimes produces doubles. So produce a double every
+        // time. This at least allows subsequent code to not have weird conditionals.
+            
+        IntegerOperand op1(this, node.child1());
+        FPRTemporary result(this);
+            
+        GPRReg inputGPR = op1.gpr();
+        FPRReg outputFPR = result.fpr();
+            
+        m_jit.convertInt32ToDouble(inputGPR, outputFPR);
+            
+        JITCompiler::Jump positive = m_jit.branch32(MacroAssembler::GreaterThanOrEqual, inputGPR, TrustedImm32(0));
+        m_jit.addDouble(JITCompiler::AbsoluteAddress(&AssemblyHelpers::twoToThe32), outputFPR);
+        positive.link(&m_jit);
+            
+        doubleResult(outputFPR, m_compileIndex);
+        return;
+    }
+
+    IntegerOperand op1(this, node.child1());
+    GPRTemporary result(this, op1);
+
+    // Test the operand is positive. This is a very special speculation check - we actually
+    // use roll-forward speculation here, where if this fails, we jump to the baseline
+    // instruction that follows us, rather than the one we're executing right now. We have
+    // to do this because by this point, the original values necessary to compile whatever
+    // operation the UInt32ToNumber originated from might be dead.
+    speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::LessThan, op1.gpr(), TrustedImm32(0)));
+        
+    // Verify that we can do roll forward.
+    ASSERT(at(m_compileIndex + 1).op == SetLocal);
+    ASSERT(at(m_compileIndex + 1).codeOrigin == node.codeOrigin);
+    ASSERT(at(m_compileIndex + 2).codeOrigin != node.codeOrigin);
+        
+    // Now do the magic.
+    OSRExit& exit = m_jit.codeBlock()->lastOSRExit();
+    Node& setLocal = at(m_compileIndex + 1);
+    exit.m_codeOrigin = at(m_compileIndex + 2).codeOrigin;
+    exit.m_lastSetOperand = setLocal.local();
+        
+    // Create the value recovery, and stuff it into the right place.
+    exit.valueRecoveryForOperand(setLocal.local()) = ValueRecovery::uint32InGPR(op1.gpr());
+
+    m_jit.move(op1.gpr(), result.gpr());
+    integerResult(result.gpr(), m_compileIndex, op1.format());
+}
+
 static void compileClampDoubleToByte(JITCompiler& jit, GPRReg result, FPRReg source, FPRReg scratch)
 {
     // Unordered compare so we pick up NaN
@@ -1640,7 +1688,7 @@ void SpeculativeJIT::compileGetByValOnIntTypedArray(const TypedArrayDescriptor&
         FPRTemporary fresult(this);
         m_jit.convertInt32ToDouble(resultReg, fresult.fpr());
         JITCompiler::Jump positive = m_jit.branch32(MacroAssembler::GreaterThanOrEqual, resultReg, TrustedImm32(0));
-        m_jit.addDouble(JITCompiler::AbsoluteAddress(&twoToThe32), fresult.fpr());
+        m_jit.addDouble(JITCompiler::AbsoluteAddress(&AssemblyHelpers::twoToThe32), fresult.fpr());
         positive.link(&m_jit);
         doubleResult(fresult.fpr(), m_compileIndex);
     }
index ffeff01..4526992 100644 (file)
@@ -170,8 +170,6 @@ private:
 
     enum UseChildrenMode { CallUseChildren, UseChildrenCalledExplicitly };
     
-    static const double twoToThe32;
-
 public:
     SpeculativeJIT(JITCompiler&);
 
@@ -1969,6 +1967,7 @@ private:
     void compileGetCharCodeAt(Node&);
     void compileGetByValOnString(Node&);
     void compileValueToInt32(Node&);
+    void compileUInt32ToNumber(Node&);
     void compileGetByValOnByteArray(Node&);
     void compilePutByValForByteArray(GPRReg base, GPRReg property, Node&);
     void compileArithMul(Node&);
index f4f4266..a660153 100644 (file)
@@ -454,7 +454,7 @@ void SpeculativeJIT::nonSpeculativeUInt32ToNumber(Node& node)
     JITCompiler::Jump positive = m_jit.branch32(MacroAssembler::GreaterThanOrEqual, op1.gpr(), TrustedImm32(0));
         
     m_jit.convertInt32ToDouble(op1.gpr(), boxer.fpr());
-    m_jit.move(JITCompiler::TrustedImmPtr(&twoToThe32), resultPayload.gpr()); // reuse resultPayload register here.
+    m_jit.move(JITCompiler::TrustedImmPtr(&AssemblyHelpers::twoToThe32), resultPayload.gpr()); // reuse resultPayload register here.
     m_jit.addDouble(JITCompiler::Address(resultPayload.gpr(), 0), boxer.fpr());
         
     boxDouble(boxer.fpr(), resultTag.gpr(), resultPayload.gpr());
@@ -2169,34 +2169,7 @@ void SpeculativeJIT::compile(Node& node)
         break;
 
     case UInt32ToNumber: {
-        if (!nodeCanSpeculateInteger(node.arithNodeFlags())) {
-            // We know that this sometimes produces doubles. So produce a double every
-            // time. This at least allows subsequent code to not have weird conditionals.
-            
-            IntegerOperand op1(this, node.child1());
-            FPRTemporary result(this);
-            
-            GPRReg inputGPR = op1.gpr();
-            FPRReg outputFPR = result.fpr();
-            
-            m_jit.convertInt32ToDouble(inputGPR, outputFPR);
-            
-            JITCompiler::Jump positive = m_jit.branch32(MacroAssembler::GreaterThanOrEqual, inputGPR, TrustedImm32(0));
-            m_jit.addDouble(JITCompiler::AbsoluteAddress(&twoToThe32), outputFPR);
-            positive.link(&m_jit);
-            
-            doubleResult(outputFPR, m_compileIndex);
-            break;
-        }
-
-        IntegerOperand op1(this, node.child1());
-        GPRTemporary result(this, op1);
-
-        // Test the operand is positive.
-        speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::LessThan, op1.gpr(), TrustedImm32(0)));
-
-        m_jit.move(op1.gpr(), result.gpr());
-        integerResult(result.gpr(), m_compileIndex, op1.format());
+        compileUInt32ToNumber(node);
         break;
     }
 
index 2fa66b0..88baf4c 100644 (file)
@@ -462,7 +462,7 @@ void SpeculativeJIT::nonSpeculativeUInt32ToNumber(Node& node)
     JITCompiler::Jump positive = m_jit.branch32(MacroAssembler::GreaterThanOrEqual, op1.gpr(), TrustedImm32(0));
     
     m_jit.convertInt32ToDouble(op1.gpr(), boxer.fpr());
-    m_jit.addDouble(JITCompiler::AbsoluteAddress(&twoToThe32), boxer.fpr());
+    m_jit.addDouble(JITCompiler::AbsoluteAddress(&AssemblyHelpers::twoToThe32), boxer.fpr());
     
     boxDouble(boxer.fpr(), result.gpr());
     
@@ -2201,34 +2201,7 @@ void SpeculativeJIT::compile(Node& node)
         break;
 
     case UInt32ToNumber: {
-        if (!nodeCanSpeculateInteger(node.arithNodeFlags())) {
-            // We know that this sometimes produces doubles. So produce a double every
-            // time. This at least allows subsequent code to not have weird conditionals.
-            
-            IntegerOperand op1(this, node.child1());
-            FPRTemporary result(this);
-            
-            GPRReg inputGPR = op1.gpr();
-            FPRReg outputFPR = result.fpr();
-            
-            m_jit.convertInt32ToDouble(inputGPR, outputFPR);
-            
-            JITCompiler::Jump positive = m_jit.branch32(MacroAssembler::GreaterThanOrEqual, inputGPR, TrustedImm32(0));
-            m_jit.addDouble(JITCompiler::AbsoluteAddress(&twoToThe32), outputFPR);
-            positive.link(&m_jit);
-            
-            doubleResult(outputFPR, m_compileIndex);
-            break;
-        }
-
-        IntegerOperand op1(this, node.child1());
-        GPRTemporary result(this, op1);
-
-        // Test the operand is positive.
-        speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::LessThan, op1.gpr(), TrustedImm32(0)));
-
-        m_jit.move(op1.gpr(), result.gpr());
-        integerResult(result.gpr(), m_compileIndex, op1.format());
+        compileUInt32ToNumber(node);
         break;
     }