Bug 607692 - Inline parseInt(<int|double>, <0|10>) in JM. r=bhackett
authorTom Schuster <evilpies@gmail.com>
Wed, 04 Jan 2012 21:29:54 +0100
changeset 83762 db8ea632731159dd69fc17e6181c5e7ed0ba732e
parent 83761 783ecbf07090d79f2096b5916ab3482156771b72
child 83763 bc1196840c427c6212743e79f71c066fbed69f47
push id21790
push userbmo@edmorley.co.uk
push dateThu, 05 Jan 2012 01:00:04 +0000
treeherdermozilla-central@0cdaf0773073 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs607692
milestone12.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 607692 - Inline parseInt(<int|double>, <0|10>) in JM. r=bhackett
js/src/jit-test/tests/basic/testParseInt.js
js/src/jsnum.cpp
js/src/jsnum.h
js/src/methodjit/Compiler.h
js/src/methodjit/FastBuiltins.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/testParseInt.js
@@ -0,0 +1,48 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+function testInt(n, result) {
+    var x = 0;
+    for (var i = 0; i < 15; i++) {
+        assertEq(parseInt(n, 10), result);
+        assertEq(parseInt(n, 0), result);
+        assertEq(parseInt(n), result);
+        assertEq(parseInt(n, x), result);
+
+        if (x % 2 == 0)
+            x = 10;
+        else
+            x = 0;
+    }
+}
+
+function testDouble(n, result) {
+    var x = 0;
+    for (var i = 0; i < 15; i++) {
+        assertEq(parseInt(n, 10), result);
+        assertEq(parseInt(n, 0), result);
+        assertEq(parseInt(n), result);
+        assertEq(parseInt(n, x), result);
+
+        if (x % 2 == 0)
+            x = 10;
+        else
+            x = 0;
+    }
+}
+
+testInt(2147483647, 2147483647);
+testInt(-2147483648, -2147483648);
+testInt(17, 17);
+testInt(-1, -1);
+testInt(0, 0);
+
+testDouble(1e21, 1);
+testDouble(-5.7, -5);
+testDouble(1.7, 1);
+testDouble(1.0e-6, 0);
+testDouble(1.0e-7, 1);
+testDouble(NaN, NaN);
+testDouble(1e20, 1e20);
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -360,18 +360,18 @@ ParseIntStringHelper(JSContext *cx, cons
     if (s == actualEnd)
         *dp = js_NaN;
     else if (negative)
         *dp = -*dp;
     return true;
 }
 
 /* See ECMA 15.1.2.2. */
-static JSBool
-num_parseInt(JSContext *cx, uintN argc, Value *vp)
+JSBool
+js::num_parseInt(JSContext *cx, uintN argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Fast paths and exceptional cases. */
     if (args.length() == 0) {
         args.rval().setDouble(js_NaN);
         return true;
     }
--- a/js/src/jsnum.h
+++ b/js/src/jsnum.h
@@ -326,16 +326,19 @@ ValueToUint16(JSContext *cx, const js::V
     if (v.isInt32()) {
         *out = uint16_t(v.toInt32());
         return true;
     }
     extern bool ValueToUint16Slow(JSContext *cx, const js::Value &v, uint16_t *out);
     return ValueToUint16Slow(cx, v, out);
 }
 
+JSBool
+num_parseInt(JSContext *cx, uintN argc, Value *vp);
+
 }  /* namespace js */
 
 /*
  * Specialized ToInt32 and ToUint32 converters for doubles.
  */
 /*
  * From the ES3 spec, 9.5
  *  2.  If Result(1) is NaN, +0, -0, +Inf, or -Inf, return +0.
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -765,16 +765,17 @@ private:
 
     enum RoundingMode { Floor, Round };
     CompileStatus compileRound(FrameEntry *arg, RoundingMode mode);
 
     enum GetCharMode { GetChar, GetCharCode };
     CompileStatus compileGetChar(FrameEntry *thisValue, FrameEntry *arg, GetCharMode mode);
     
     CompileStatus compileStringFromCode(FrameEntry *arg);
+    CompileStatus compileParseInt(JSValueType argType, uint32_t argc);
 
     void prepareStubCall(Uses uses);
     Call emitStubCall(void *ptr, DataLabelPtr *pinline);
 };
 
 // Given a stub call, emits the call into the inline assembly path. rejoin
 // indicates how to rejoin should this call trigger expansion/discarding.
 #define INLINE_STUBCALL(stub, rejoin)                                       \
--- a/js/src/methodjit/FastBuiltins.cpp
+++ b/js/src/methodjit/FastBuiltins.cpp
@@ -793,17 +793,17 @@ mjit::Compiler::compileArrayWithArgs(uin
     Jump emptyFreeList = masm.getNewObject(cx, result, templateObject);
     stubcc.linkExit(emptyFreeList, Uses(0));
 
     int offset = JSObject::offsetOfFixedElements();
     masm.store32(Imm32(argc),
                  Address(result, offset + ObjectElements::offsetOfInitializedLength()));
 
     for (unsigned i = 0; i < argc; i++) {
-        FrameEntry *arg = frame.peek(-(int)argc + i);
+        FrameEntry *arg = frame.peek(-(int32_t)argc + i);
         frame.storeTo(arg, Address(result, offset), /* popped = */ true);
         offset += sizeof(Value);
     }
 
     stubcc.leave();
 
     stubcc.masm.move(Imm32(argc), Registers::ArgReg1);
     OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH);
@@ -811,16 +811,100 @@ mjit::Compiler::compileArrayWithArgs(uin
     frame.popn(argc + 2);
     frame.pushTypedPayload(JSVAL_TYPE_OBJECT, result);
 
     stubcc.rejoin(Changes(1));
     return Compile_Okay;
 }
 
 CompileStatus
+mjit::Compiler::compileParseInt(JSValueType argType, uint32_t argc)
+{
+    bool needStubCall = false;
+
+    if (argc > 1) {
+        FrameEntry *arg = frame.peek(-(int32_t)argc + 1);
+
+        if (!arg->isTypeKnown() || arg->getKnownType() != JSVAL_TYPE_INT32)
+            return Compile_InlineAbort;
+
+        if (arg->isConstant()) {
+            int32_t base = arg->getValue().toInt32();
+            if (base != 0 && base != 10)
+                return Compile_InlineAbort;
+        } else {
+            RegisterID baseReg = frame.tempRegForData(arg);
+            needStubCall = true;
+
+            Jump isTen = masm.branch32(Assembler::Equal, baseReg, Imm32(10));
+            Jump isNotZero = masm.branch32(Assembler::NotEqual, baseReg, Imm32(0));
+            stubcc.linkExit(isNotZero, Uses(2 + argc));
+
+            isTen.linkTo(masm.label(), &masm);
+        }
+    }
+
+    if (argType == JSVAL_TYPE_INT32) {
+        if (needStubCall) {
+            stubcc.leave();
+            stubcc.masm.move(Imm32(argc), Registers::ArgReg1);
+            OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH);
+        }
+
+        /* 
+         * Stack looks like callee, this, arg1, arg2, argN.
+         * First pop all args other than arg1.
+         */
+        frame.popn(argc - 1);
+        /* "Shimmy" arg1 to the callee slot and pop this + arg1. */
+        frame.shimmy(2);
+
+        if (needStubCall) {
+            stubcc.rejoin(Changes(1));
+        }        
+    } else {
+        FrameEntry *arg = frame.peek(-(int32_t)argc);
+        FPRegisterID fpScratchReg = frame.allocFPReg();
+        FPRegisterID fpReg;
+        bool allocate;
+
+        DebugOnly<MaybeJump> notNumber = loadDouble(arg, &fpReg, &allocate);
+        JS_ASSERT(!((MaybeJump)notNumber).isSet());
+
+        masm.slowLoadConstantDouble(1, fpScratchReg);
+
+        /* Slow path for NaN and numbers < 1. */
+        Jump lessThanOneOrNan = masm.branchDouble(Assembler::DoubleLessThanOrUnordered, 
+                                                  fpReg, fpScratchReg);
+        stubcc.linkExit(lessThanOneOrNan, Uses(2 + argc));
+
+        frame.freeReg(fpScratchReg);
+
+        /* Truncate to integer, slow path if this overflows. */
+        RegisterID reg = frame.allocReg();
+        Jump overflow = masm.branchTruncateDoubleToInt32(fpReg, reg);
+        stubcc.linkExit(overflow, Uses(2 + argc));
+
+        if (allocate)
+            frame.freeReg(fpReg);
+
+        stubcc.leave();
+        stubcc.masm.move(Imm32(argc), Registers::ArgReg1);
+        OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH);
+
+        frame.popn(2 + argc);
+        frame.pushTypedPayload(JSVAL_TYPE_INT32, reg);
+
+        stubcc.rejoin(Changes(1));
+    }
+
+    return Compile_Okay;   
+}
+
+CompileStatus
 mjit::Compiler::inlineNativeFunction(uint32_t argc, bool callingNew)
 {
     if (!cx->typeInferenceEnabled())
         return Compile_InlineAbort;
 
     if (applyTricks == LazyArgsObj)
         return Compile_InlineAbort;
 
@@ -864,16 +948,26 @@ mjit::Compiler::inlineNativeFunction(uin
             return compileArrayWithLength(argc);
         return compileArrayWithArgs(argc);
     }
 
     /* Remaining natives must not be called with 'new'. */
     if (callingNew)
         return Compile_InlineAbort;
 
+    if (native == js::num_parseInt && argc >= 1) {
+        FrameEntry *arg = frame.peek(-(int32_t)argc);
+        JSValueType argType = arg->isTypeKnown() ? arg->getKnownType() : JSVAL_TYPE_UNKNOWN;
+
+        if ((argType == JSVAL_TYPE_DOUBLE || argType == JSVAL_TYPE_INT32) &&
+            type == JSVAL_TYPE_INT32) {
+            return compileParseInt(argType, argc);
+        }
+    }
+
     if (argc == 0) {
         if ((native == js::array_pop || native == js::array_shift) && thisType == JSVAL_TYPE_OBJECT) {
             /*
              * Only handle pop/shift on dense arrays which have never been used
              * in an iterator --- when popping elements we don't account for
              * suppressing deleted properties in active iterators.
              *
              * Constraints propagating properties directly into the result