Bug 794475 - Inline "new String(x)". r=dvander
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 27 Sep 2012 12:45:55 +0200
changeset 108264 385dbb23bb344d9390a756bb9422fc07b7f29699
parent 108263 978385fba015b69a5efd23763e8c6ca31007de3d
child 108265 385bc6d035973f3909fb943484d1485583199608
push id23552
push userryanvm@gmail.com
push dateFri, 28 Sep 2012 03:05:08 +0000
treeherdermozilla-central@2d96ee8d9dd4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs794475
milestone18.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 794475 - Inline "new String(x)". r=dvander
js/src/ion/CodeGenerator.cpp
js/src/ion/CodeGenerator.h
js/src/ion/IonBuilder.h
js/src/ion/IonMacroAssembler.h
js/src/ion/LIR-Common.h
js/src/ion/LOpcodes.h
js/src/ion/Lowering.cpp
js/src/ion/Lowering.h
js/src/ion/MCallOptimize.cpp
js/src/ion/MIR.h
js/src/ion/MOpcodes.h
js/src/ion/TypePolicy.cpp
js/src/ion/VMFunctions.cpp
js/src/ion/VMFunctions.h
js/src/jsinfer.cpp
js/src/methodjit/PolyIC.cpp
js/src/vm/StringObject.h
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -9,16 +9,18 @@
 #include "IonLinker.h"
 #include "IonSpewer.h"
 #include "MIRGenerator.h"
 #include "shared/CodeGenerator-shared-inl.h"
 #include "jsnum.h"
 #include "jsmath.h"
 #include "jsinterpinlines.h"
 
+#include "vm/StringObject-inl.h"
+
 using namespace js;
 using namespace js::ion;
 
 namespace js {
 namespace ion {
 
 CodeGenerator::CodeGenerator(MIRGenerator *gen, LIRGraph &graph)
   : CodeGeneratorSpecific(gen, graph)
@@ -1524,16 +1526,45 @@ CodeGenerator::visitNewCallObject(LNewCa
 
     if (lir->slots()->isRegister())
         masm.storePtr(ToRegister(lir->slots()), Address(obj, JSObject::offsetOfSlots()));
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
+CodeGenerator::visitNewStringObject(LNewStringObject *lir)
+{
+    Register input = ToRegister(lir->input());
+    Register output = ToRegister(lir->output());
+    Register temp = ToRegister(lir->temp());
+
+    typedef JSObject *(*pf)(JSContext *, HandleString);
+    static const VMFunction NewStringObjectInfo = FunctionInfo<pf>(NewStringObject);
+
+    StringObject *templateObj = lir->mir()->templateObj();
+
+    OutOfLineCode *ool = oolCallVM(NewStringObjectInfo, lir, (ArgList(), input),
+                                   StoreRegisterTo(output));
+    if (!ool)
+        return false;
+
+    masm.newGCThing(output, templateObj, ool->entry());
+    masm.initGCThing(output, templateObj);
+
+    masm.loadStringLength(input, temp);
+
+    masm.storeValue(JSVAL_TYPE_STRING, input, Address(output, StringObject::offsetOfPrimitiveValue()));
+    masm.storeValue(JSVAL_TYPE_INT32, temp, Address(output, StringObject::offsetOfLength()));
+
+    masm.bind(ool->rejoin());
+    return true;
+}
+
+bool
 CodeGenerator::visitInitProp(LInitProp *lir)
 {
     Register objReg = ToRegister(lir->getObject());
 
     typedef bool(*pf)(JSContext *, HandleObject, HandlePropertyName, HandleValue);
     static const VMFunction InitPropInfo = FunctionInfo<pf>(InitProp);
 
     pushArg(ToValue(lir, LInitProp::ValueIndex));
@@ -1647,21 +1678,20 @@ CodeGenerator::visitTypedArrayElements(L
     Register out = ToRegister(lir->output());
     masm.loadPtr(Address(obj, TypedArray::dataOffset()), out);
     return true;
 }
 
 bool
 CodeGenerator::visitStringLength(LStringLength *lir)
 {
-    Address lengthAndFlags(ToRegister(lir->string()), JSString::offsetOfLengthAndFlags());
+    Register input = ToRegister(lir->string());
     Register output = ToRegister(lir->output());
 
-    masm.loadPtr(lengthAndFlags, output);
-    masm.rshiftPtr(Imm32(JSString::LENGTH_SHIFT), output);
+    masm.loadStringLength(input, output);
     return true;
 }
 
 bool
 CodeGenerator::visitMinMaxI(LMinMaxI *ins)
 {
     Register first = ToRegister(ins->first());
     Register output = ToRegister(ins->output());
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -84,16 +84,17 @@ class CodeGenerator : public CodeGenerat
     bool visitNewSlots(LNewSlots *lir);
     bool visitNewArrayCallVM(LNewArray *lir);
     bool visitNewArray(LNewArray *lir);
     bool visitOutOfLineNewArray(OutOfLineNewArray *ool);
     bool visitNewObjectVMCall(LNewObject *lir);
     bool visitNewObject(LNewObject *lir);
     bool visitOutOfLineNewObject(OutOfLineNewObject *ool);
     bool visitNewCallObject(LNewCallObject *lir);
+    bool visitNewStringObject(LNewStringObject *lir);
     bool visitInitProp(LInitProp *lir);
     bool visitCreateThis(LCreateThis *lir);
     bool visitCreateThisVM(LCreateThisVM *lir);
     bool visitReturnFromCtor(LReturnFromCtor *lir);
     bool visitArrayLength(LArrayLength *lir);
     bool visitTypedArrayLength(LTypedArrayLength *lir);
     bool visitTypedArrayElements(LTypedArrayElements *lir);
     bool visitStringLength(LStringLength *lir);
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -377,16 +377,17 @@ class IonBuilder : public MIRGenerator
     InliningStatus inlineMathRound(uint32 argc, bool constructing);
     InliningStatus inlineMathSqrt(uint32 argc, bool constructing);
     InliningStatus inlineMathMinMax(bool max, uint32 argc, bool constructing);
     InliningStatus inlineMathPow(uint32 argc, bool constructing);
     InliningStatus inlineMathFunction(MMathFunction::Function function, uint32 argc,
                                       bool constructing);
 
     // String natives.
+    InliningStatus inlineStringObject(uint32 argc, bool constructing);
     InliningStatus inlineStrCharCodeAt(uint32 argc, bool constructing);
     InliningStatus inlineStrFromCharCode(uint32 argc, bool constructing);
     InliningStatus inlineStrCharAt(uint32 argc, bool constructing);
 
     InliningStatus inlineNativeCall(JSNative native, uint32 argc, bool constructing);
 
     bool jsop_call_inline(HandleFunction callee, uint32 argc, bool constructing,
                           MConstant *constFun, MBasicBlock *bottom,
--- a/js/src/ion/IonMacroAssembler.h
+++ b/js/src/ion/IonMacroAssembler.h
@@ -144,16 +144,21 @@ class MacroAssembler : public MacroAssem
         loadPtr(Address(obj, JSObject::getPrivateDataOffset(nfixed)), dest);
     }
 
     void loadObjProto(Register obj, Register dest) {
         loadPtr(Address(obj, JSObject::offsetOfType()), dest);
         loadPtr(Address(dest, offsetof(types::TypeObject, proto)), dest);
     }
 
+    void loadStringLength(Register str, Register dest) {
+        loadPtr(Address(str, JSString::offsetOfLengthAndFlags()), dest);
+        rshiftPtr(Imm32(JSString::LENGTH_SHIFT), dest);
+    }
+
     void loadJSContext(const Register &dest) {
         movePtr(ImmWord(GetIonContext()->cx->runtime), dest);
         loadPtr(Address(dest, offsetof(JSRuntime, ionJSContext)), dest);
     }
     void loadIonActivation(const Register &dest) {
         movePtr(ImmWord(GetIonContext()->cx->runtime), dest);
         loadPtr(Address(dest, offsetof(JSRuntime, ionActivation)), dest);
     }
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -287,16 +287,37 @@ class LNewCallObject : public LInstructi
     const LAllocation *slots() {
         return getOperand(0);
     }
     MNewCallObject *mir() const {
         return mir_->toNewCallObject();
     }
 };
 
+class LNewStringObject : public LInstructionHelper<1, 1, 1>
+{
+  public:
+    LIR_HEADER(NewStringObject);
+
+    LNewStringObject(const LAllocation &input, const LDefinition &temp) {
+        setOperand(0, input);
+        setTemp(0, temp);
+    }
+
+    const LAllocation *input() {
+        return getOperand(0);
+    }
+    const LDefinition *temp() {
+        return getTemp(0);
+    }
+    MNewStringObject *mir() const {
+        return mir_->toNewStringObject();
+    }
+};
+
 // Takes in an Object and a Value.
 class LInitProp : public LCallInstructionHelper<0, 1 + BOX_PIECES, 0>
 {
   public:
     LIR_HEADER(InitProp);
 
     LInitProp(const LAllocation &object) {
         setOperand(0, object);
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -20,16 +20,17 @@
     _(Parameter)                    \
     _(Callee)                       \
     _(TableSwitch)                  \
     _(Goto)                         \
     _(NewArray)                     \
     _(NewObject)                    \
     _(NewSlots)                     \
     _(NewCallObject)                \
+    _(NewStringObject)              \
     _(InitProp)                     \
     _(CheckOverRecursed)            \
     _(RecompileCheck)               \
     _(DefVar)                       \
     _(CallKnown)                    \
     _(CallGeneric)                  \
     _(CallNative)                   \
     _(CallConstructor)              \
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -134,16 +134,25 @@ LIRGenerator::visitNewCallObject(MNewCal
 
     if (!assignSafepoint(lir, ins))
         return false;
 
     return true;
 }
 
 bool
+LIRGenerator::visitNewStringObject(MNewStringObject *ins)
+{
+    JS_ASSERT(ins->input()->type() == MIRType_String);
+
+    LNewStringObject *lir = new LNewStringObject(useRegister(ins->input()), temp());
+    return define(lir, ins) && assignSafepoint(lir, ins);
+}
+
+bool
 LIRGenerator::visitInitProp(MInitProp *ins)
 {
     LInitProp *lir = new LInitProp(useRegisterAtStart(ins->getObject()));
     if (!useBoxAtStart(lir, LInitProp::ValueIndex, ins->getValue()))
         return false;
 
     return add(lir, ins) && assignSafepoint(lir, ins);
 }
--- a/js/src/ion/Lowering.h
+++ b/js/src/ion/Lowering.h
@@ -76,16 +76,17 @@ class LIRGenerator : public LIRGenerator
     // intercept without a bunch of explicit gunk in the .cpp.
     bool visitParameter(MParameter *param);
     bool visitCallee(MCallee *callee);
     bool visitGoto(MGoto *ins);
     bool visitNewSlots(MNewSlots *ins);
     bool visitNewArray(MNewArray *ins);
     bool visitNewObject(MNewObject *ins);
     bool visitNewCallObject(MNewCallObject *ins);
+    bool visitNewStringObject(MNewStringObject *ins);
     bool visitInitProp(MInitProp *ins);
     bool visitCheckOverRecursed(MCheckOverRecursed *ins);
     bool visitDefVar(MDefVar *ins);
     bool visitPrepareCall(MPrepareCall *ins);
     bool visitPassArg(MPassArg *arg);
     bool visitCreateThis(MCreateThis *ins);
     bool visitReturnFromCtor(MReturnFromCtor *ins);
     bool visitCall(MCall *call);
--- a/js/src/ion/MCallOptimize.cpp
+++ b/js/src/ion/MCallOptimize.cpp
@@ -7,16 +7,18 @@
 
 #include "jslibmath.h"
 #include "jsmath.h"
 
 #include "MIR.h"
 #include "MIRGraph.h"
 #include "IonBuilder.h"
 
+#include "vm/StringObject-inl.h"
+
 namespace js {
 namespace ion {
 
 IonBuilder::InliningStatus
 IonBuilder::inlineNativeCall(JSNative native, uint32 argc, bool constructing)
 {
     // Array natives.
     if (native == js_Array)
@@ -48,16 +50,18 @@ IonBuilder::inlineNativeCall(JSNative na
     if (native == js::math_cos)
         return inlineMathFunction(MMathFunction::Cos, argc, constructing);
     if (native == js::math_tan)
         return inlineMathFunction(MMathFunction::Tan, argc, constructing);
     if (native == js::math_log)
         return inlineMathFunction(MMathFunction::Log, argc, constructing);
 
     // String natives.
+    if (native == js_String)
+        return inlineStringObject(argc, constructing);
     if (native == js_str_charCodeAt)
         return inlineStrCharCodeAt(argc, constructing);
     if (native == js::str_fromCharCode)
         return inlineStrFromCharCode(argc, constructing);
     if (native == js_str_charAt)
         return inlineStrCharAt(argc, constructing);
 
     return InliningStatus_NotInlined;
@@ -552,16 +556,46 @@ IonBuilder::inlineMathMinMax(bool max, u
 
     MMinMax *ins = MMinMax::New(argv[1], argv[2], returnType, max);
     current->add(ins);
     current->push(ins);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
+IonBuilder::inlineStringObject(uint32 argc, bool constructing)
+{
+    if (argc != 1 || !constructing)
+        return InliningStatus_NotInlined;
+
+    // MToString only supports int32 or string values.
+    MIRType type = getInlineArgType(argc, 1);
+    if (type != MIRType_Int32 && type != MIRType_String)
+        return InliningStatus_NotInlined;
+
+    MDefinitionVector argv;
+    if (!discardCall(argc, argv, current))
+        return InliningStatus_Error;
+
+    RootedString emptyString(cx, cx->runtime->emptyString);
+    RootedObject templateObj(cx, StringObject::create(cx, emptyString));
+    if (!templateObj)
+        return InliningStatus_Error;
+
+    MNewStringObject *ins = MNewStringObject::New(argv[1], templateObj);
+    current->add(ins);
+    current->push(ins);
+
+    if (!resumeAfter(ins))
+        return InliningStatus_Error;
+
+    return InliningStatus_Inlined;
+}
+
+IonBuilder::InliningStatus
 IonBuilder::inlineStrCharCodeAt(uint32 argc, bool constructing)
 {
     if (argc != 1 || constructing)
         return InliningStatus_NotInlined;
 
     if (getInlineReturnType() != MIRType_Int32)
         return InliningStatus_NotInlined;
     if (getInlineArgType(argc, 0) != MIRType_String)
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -5283,16 +5283,47 @@ class MNewCallObject : public MUnaryInst
     JSObject *templateObj() {
         return templateObj_;
     }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
+class MNewStringObject :
+  public MUnaryInstruction,
+  public StringPolicy
+{
+    CompilerRootObject templateObj_;
+
+    MNewStringObject(MDefinition *input, HandleObject templateObj)
+      : MUnaryInstruction(input),
+        templateObj_(templateObj)
+    {
+        setResultType(MIRType_Object);
+    }
+
+  public:
+    INSTRUCTION_HEADER(NewStringObject);
+
+    static MNewStringObject *New(MDefinition *input, HandleObject templateObj) {
+        return new MNewStringObject(input, templateObj);
+    }
+
+    MDefinition *input() const {
+        return getOperand(0);
+    }
+    StringObject *templateObj() const {
+        return &templateObj_->asString();
+    }
+    TypePolicy *typePolicy() {
+        return this;
+    }
+};
+
 // Node that represents that a script has begun executing. This comes at the
 // start of the function and is called once per function (including inline
 // ones)
 class MFunctionBoundary : public MNullaryInstruction
 {
   public:
     enum Type {
         Enter,        // a function has begun executing and it is not inline
--- a/js/src/ion/MOpcodes.h
+++ b/js/src/ion/MOpcodes.h
@@ -65,16 +65,17 @@ namespace ion {
     _(ToDouble)                                                             \
     _(ToInt32)                                                              \
     _(TruncateToInt32)                                                      \
     _(ToString)                                                             \
     _(NewSlots)                                                             \
     _(NewArray)                                                             \
     _(NewObject)                                                            \
     _(NewCallObject)                                                        \
+    _(NewStringObject)                                                      \
     _(InitProp)                                                             \
     _(Start)                                                                \
     _(OsrEntry)                                                             \
     _(RegExp)                                                               \
     _(Lambda)                                                               \
     _(ImplicitThis)                                                         \
     _(Slots)                                                                \
     _(Elements)                                                             \
--- a/js/src/ion/TypePolicy.cpp
+++ b/js/src/ion/TypePolicy.cpp
@@ -277,17 +277,25 @@ PowPolicy::adjustInputs(MInstruction *in
 
 bool
 StringPolicy::staticAdjustInputs(MInstruction *def)
 {
     MDefinition *in = def->getOperand(0);
     if (in->type() == MIRType_String)
         return true;
 
-    MUnbox *replace = MUnbox::New(in, MIRType_String, MUnbox::Fallible);
+    MInstruction *replace;
+    if (in->type() == MIRType_Int32) {
+        replace = MToString::New(in);
+    } else {
+        if (in->type() != MIRType_Value)
+            in = boxAt(def, in);
+        replace = MUnbox::New(in, MIRType_String, MUnbox::Fallible);
+    }
+
     def->block()->insertBefore(def, replace);
     def->replaceOperand(0, replace);
     return true;
 }
 
 template <unsigned Op>
 bool
 IntPolicy<Op>::staticAdjustInputs(MInstruction *def)
--- a/js/src/ion/VMFunctions.cpp
+++ b/js/src/ion/VMFunctions.cpp
@@ -6,16 +6,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Ion.h"
 #include "IonCompartment.h"
 #include "jsinterp.h"
 #include "ion/IonFrames.h"
 #include "ion/IonFrames-inl.h" // for GetTopIonJSScript
 
+#include "vm/StringObject-inl.h"
+
 #include "jsinterpinlines.h"
 
 using namespace js;
 using namespace js::ion;
 
 namespace js {
 namespace ion {
 
@@ -380,16 +382,22 @@ NewSlots(JSRuntime *rt, unsigned nslots)
 }
 
 JSObject *
 NewCallObject(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots)
 {
     return CallObject::create(cx, shape, type, slots);
 }
 
+JSObject *
+NewStringObject(JSContext *cx, HandleString str)
+{
+    return StringObject::create(cx, str);
+}
+
 bool SPSEnter(JSContext *cx, HandleScript script)
 {
     return cx->runtime->spsProfiler.enter(cx, script, script->function());
 }
 
 bool SPSExit(JSContext *cx, HandleScript script)
 {
     cx->runtime->spsProfiler.exit(cx, script, script->function());
--- a/js/src/ion/VMFunctions.h
+++ b/js/src/ion/VMFunctions.h
@@ -434,16 +434,17 @@ JSFlatString *StringFromCharCode(JSConte
 
 bool SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value,
                  bool strict, bool isSetName);
 
 bool InterruptCheck(JSContext *cx);
 
 HeapSlot *NewSlots(JSRuntime *rt, unsigned nslots);
 JSObject *NewCallObject(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots);
+JSObject *NewStringObject(JSContext *cx, HandleString str);
 
 bool SPSEnter(JSContext *cx, HandleScript script);
 bool SPSExit(JSContext *cx, HandleScript script);
 
 
 } // namespace ion
 } // namespace js
 
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -1310,16 +1310,26 @@ TypeConstraintCall::newType(JSContext *c
                 if (callsite->argumentCount >= 2) {
                     for (unsigned i = 0; i < callsite->argumentCount; i++) {
                         PropertyAccess<PROPERTY_WRITE>(cx, script, pc, res,
                                                        callsite->argumentTypes[i], JSID_VOID);
                     }
                 }
             }
 
+            if (native == js_String && callsite->isNew) {
+                // Note that "new String()" returns a String object and "String()"
+                // returns a primitive string.
+                TypeObject *res = TypeScript::StandardType(cx, script, JSProto_String);
+                if (!res)
+                    return;
+
+                callsite->returnTypes->addType(cx, Type::ObjectType(res));
+            }
+
             return;
         }
 
         callee = obj->toFunction();
     } else if (type.isTypeObject()) {
         callee = type.typeObject()->interpretedFunction;
         if (!callee)
             return;
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -860,19 +860,17 @@ class GetPropCompiler : public PICStubCo
 
     LookupStatus generateStringObjLengthStub()
     {
         MJITInstrumentation sps(&f.cx->runtime->spsProfiler);
         Assembler masm(&sps, &f);
 
         Jump notStringObj = masm.guardShape(pic.objReg, obj);
 
-        masm.loadPayload(Address(pic.objReg, StringObject::getPrimitiveValueOffset()), pic.objReg);
-        masm.loadPtr(Address(pic.objReg, JSString::offsetOfLengthAndFlags()), pic.objReg);
-        masm.urshift32(Imm32(JSString::LENGTH_SHIFT), pic.objReg);
+        masm.loadPayload(Address(pic.objReg, StringObject::offsetOfLength()), pic.objReg);
         masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);
         Jump done = masm.jump();
 
         pic.updatePCCounters(f, masm);
 
         PICLinker buffer(masm, pic);
         if (!buffer.init(cx))
             return error();
--- a/js/src/vm/StringObject.h
+++ b/js/src/vm/StringObject.h
@@ -36,19 +36,22 @@ class StringObject : public JSObject
     JSString *unbox() const {
         return getFixedSlot(PRIMITIVE_VALUE_SLOT).toString();
     }
 
     inline size_t length() const {
         return size_t(getFixedSlot(LENGTH_SLOT).toInt32());
     }
 
-    static size_t getPrimitiveValueOffset() {
+    static size_t offsetOfPrimitiveValue() {
         return getFixedSlotOffset(PRIMITIVE_VALUE_SLOT);
     }
+    static size_t offsetOfLength() {
+        return getFixedSlotOffset(LENGTH_SLOT);
+    }
 
   private:
     inline bool init(JSContext *cx, HandleString str);
 
     void setStringThis(JSString *str) {
         JS_ASSERT(getReservedSlot(PRIMITIVE_VALUE_SLOT).isUndefined());
         setFixedSlot(PRIMITIVE_VALUE_SLOT, StringValue(str));
         setFixedSlot(LENGTH_SLOT, Int32Value(int32_t(str->length())));