Implement LBitNotV (bug 725000, r=sstangl).
authorDavid Anderson <danderson@mozilla.com>
Thu, 09 Feb 2012 15:28:15 -0800
changeset 87452 1ae79ea16a74e68564cfc3d7dadb0d476f4fb9c7
parent 87451 de33951b455d4a71e2d5fa8ab747c26c417f153d
child 87453 fc94aa84b3caf16c16b61fb9b9554eafb60ad221
push id590
push userdanderson@mozilla.com
push dateThu, 09 Feb 2012 23:31:02 +0000
reviewerssstangl
bugs725000
milestone13.0a1
Implement LBitNotV (bug 725000, r=sstangl).
js/src/ion/CodeGenerator.cpp
js/src/ion/CodeGenerator.h
js/src/ion/IonBuilder.cpp
js/src/ion/LIR-Common.h
js/src/ion/LOpcodes.h
js/src/ion/Lowering.cpp
js/src/ion/MIR.h
js/src/ion/VMFunctions.h
js/src/ion/arm/CodeGenerator-arm.cpp
js/src/ion/arm/CodeGenerator-arm.h
js/src/ion/arm/Trampoline-arm.cpp
js/src/ion/shared/CodeGenerator-x86-shared.cpp
js/src/ion/shared/CodeGenerator-x86-shared.h
js/src/ion/x64/Trampoline-x64.cpp
js/src/ion/x86/Trampoline-x86.cpp
js/src/jit-test/tests/ion/bug725000.js
js/src/jsinterp.cpp
js/src/jsinterpinlines.h
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -40,16 +40,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "CodeGenerator.h"
 #include "IonLinker.h"
 #include "IonSpewer.h"
 #include "MIRGenerator.h"
 #include "shared/CodeGenerator-shared-inl.h"
 #include "jsnum.h"
+#include "jsinterpinlines.h"
 
 using namespace js;
 using namespace js::ion;
 
 namespace js {
 namespace ion {
 
 CodeGenerator::CodeGenerator(MIRGenerator *gen, LIRGraph &graph)
@@ -1747,16 +1748,26 @@ CodeGenerator::visitThrow(LThrow *lir)
     typedef bool (*pf)(JSContext *, const Value &);
     static const VMFunction ThrowInfo = FunctionInfo<pf>(js::Throw);
 
     pushArg(ToValue(lir, LThrow::Value));
     return callVM(ThrowInfo, lir);
 }
 
 bool
+CodeGenerator::visitBitNotV(LBitNotV *lir)
+{
+    typedef bool (*pf)(JSContext *, const Value &, int *p);
+    static const VMFunction info = FunctionInfo<pf>(BitNot);
+
+    pushArg(ToValue(lir, LBitNotV::Input));
+    return callVM(info, lir);
+}
+
+bool
 CodeGenerator::visitLoadElementV(LLoadElementV *load)
 {
     Register elements = ToRegister(load->elements());
     const ValueOperand out = ToOutValue(load);
 
     if (load->index()->isConstant())
         masm.loadValue(Address(elements, ToInt32(load->index()) * sizeof(Value)), out);
     else
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -127,16 +127,17 @@ class CodeGenerator : public CodeGenerat
     bool visitStoreElementV(LStoreElementV *lir);
     bool visitStoreElementHoleT(LStoreElementHoleT *lir);
     bool visitStoreElementHoleV(LStoreElementHoleV *lir);
     bool visitCallIteratorStart(LCallIteratorStart *lir);
     bool visitCallIteratorNext(LCallIteratorNext *lir);
     bool visitCallIteratorMore(LCallIteratorMore *lir);
     bool visitCallIteratorEnd(LCallIteratorEnd *lir);
     bool visitCallSetProperty(LCallSetProperty *ins);
+    bool visitBitNotV(LBitNotV *lir);
 
     bool visitCheckOverRecursed(LCheckOverRecursed *lir);
     bool visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool);
 
     bool visitUnboxDouble(LUnboxDouble *lir);
     bool visitOutOfLineUnboxDouble(OutOfLineUnboxDouble *ool);
     bool visitOutOfLineCacheGetProperty(OutOfLineCache *ool);
     bool visitOutOfLineSetPropertyCache(OutOfLineCache *ool);
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -2010,16 +2010,18 @@ IonBuilder::jsop_bitnot()
 {
     MDefinition *input = current->pop();
     MBitNot *ins = MBitNot::New(input);
 
     current->add(ins);
     ins->infer(oracle->unaryOp(script, pc));
 
     current->push(ins);
+    if (ins->isEffectful() && !resumeAfter(ins))
+        return false;
     return true;
 }
 bool
 IonBuilder::jsop_bitop(JSOp op)
 {
     // Pop inputs.
     MDefinition *right = current->pop();
     MDefinition *left = current->pop();
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -610,20 +610,30 @@ class LCompareDAndBranch : public LInstr
     }
     const LAllocation *right() {
         return getOperand(1);
     }
 };
 
 // Bitwise not operation, takes a 32-bit integer as input and returning
 // a 32-bit integer result as an output.
-class LBitNot : public LInstructionHelper<1, 1, 0>
+class LBitNotI : public LInstructionHelper<1, 1, 0>
 {
   public:
-    LIR_HEADER(BitNot);
+    LIR_HEADER(BitNotI);
+};
+
+// Call a stub to perform a binary operation.
+class LBitNotV : public LCallInstructionHelper<1, BOX_PIECES, 0>
+{
+  public:
+    LIR_HEADER(BitNotV);
+    BOX_OUTPUT_ACCESSORS();
+
+    static const size_t Input = 0;
 };
 
 // Binary bitwise operation, taking two 32-bit integers as inputs and returning
 // a 32-bit integer result as an output.
 class LBitOp : public LInstructionHelper<1, 2, 0>
 {
     JSOp op_;
 
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -56,17 +56,18 @@
     _(TableSwitch)                  \
     _(Goto)                         \
     _(NewArray)                     \
     _(CheckOverRecursed)            \
     _(RecompileCheck)               \
     _(CallGeneric)                  \
     _(CallNative)                   \
     _(StackArg)                     \
-    _(BitNot)                       \
+    _(BitNotI)                      \
+    _(BitNotV)                      \
     _(BitOp)                        \
     _(ShiftOp)                      \
     _(Return)                       \
     _(Throw)                        \
     _(Phi)                          \
     _(TestIAndBranch)               \
     _(TestDAndBranch)               \
     _(TestVAndBranch)               \
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -349,19 +349,25 @@ LIRGenerator::lowerBitOp(JSOp op, MInstr
     return false;
 }
 
 bool
 LIRGenerator::visitBitNot(MBitNot *ins)
 {
     MDefinition *input = ins->getOperand(0);
 
-    JS_ASSERT(input->type() == MIRType_Int32);
+    if (input->type() == MIRType_Int32)
+        return lowerForALU(new LBitNotI(), ins, input);
 
-    return lowerForALU(new LBitNot(), ins, input);
+    LBitNotV *lir = new LBitNotV;
+    if (!useBox(lir, LBitNotV::Input, input))
+        return false;
+    if (!defineVMReturn(lir, ins))
+        return false;
+    return assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitBitAnd(MBitAnd *ins)
 {
     return lowerBitOp(JSOP_BITAND, ins);
 }
 
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -1535,17 +1535,17 @@ class MBitNot
 
     MDefinition *foldsTo(bool useValueNumbers);
     void infer(const TypeOracle::Unary &u);
 
     bool congruentTo(MDefinition *const &ins) const {
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const {
-        if (specialization_ >= MIRType_Object)
+        if (specialization_ == MIRType_None)
             return AliasSet::Store(AliasSet::Any);
         return AliasSet::None();
     }
 };
 
 class MBinaryBitwiseInstruction
   : public MBinaryInstruction,
     public BitwisePolicy
--- a/js/src/ion/VMFunctions.h
+++ b/js/src/ion/VMFunctions.h
@@ -44,17 +44,17 @@
 #include "jspubtd.h"
 
 namespace js {
 namespace ion {
 
 enum DataType {
     Type_Void,
     Type_Bool,
-    Type_JSBool,
+    Type_Int32,
     Type_Object,
     Type_Value
 };
 
 // Contains information about a virtual machine function that can be called
 // from JIT code. Functions described in this manner must conform to a simple
 // protocol: the return type must have a special "failure" value (for example,
 // false for bool, or NULL for Objects). If the function is designed to return
@@ -184,17 +184,17 @@ template <class T> struct TypeToArgPrope
         (sizeof(T) <= sizeof(void *) ? VMFunction::Word : VMFunction::Double);
 };
 template <> struct TypeToArgProperties<const Value &> {
     static const uint32 result = TypeToArgProperties<Value>::result | VMFunction::ByRef;
 };
 
 template <class> struct OutParamToDataType { static const DataType result = Type_Void; };
 template <> struct OutParamToDataType<Value *> { static const DataType result = Type_Value; };
-template <> struct OutParamToDataType<JSBool *> { static const DataType result = Type_JSBool; };
+template <> struct OutParamToDataType<int *> { static const DataType result = Type_Int32; };
 
 #define FOR_EACH_ARGS_1(Macro, Sep, Last) Macro(1) Last(1)
 #define FOR_EACH_ARGS_2(Macro, Sep, Last) FOR_EACH_ARGS_1(Macro, Sep, Sep) Macro(2) Last(2)
 #define FOR_EACH_ARGS_3(Macro, Sep, Last) FOR_EACH_ARGS_2(Macro, Sep, Sep) Macro(3) Last(3)
 #define FOR_EACH_ARGS_4(Macro, Sep, Last) FOR_EACH_ARGS_3(Macro, Sep, Sep) Macro(4) Last(4)
 
 #define COMPUTE_INDEX(NbArg) NbArg
 #define COMPUTE_OUTPARAM_RESULT(NbArg) OutParamToDataType<A ## NbArg>::result
@@ -240,18 +240,18 @@ struct FunctionInfo<R (*)(JSContext *)> 
     }
     static inline size_t explicitArgs() {
         return 0;
     }
     static inline uint32 argumentProperties() {
         return 0;
     }
     FunctionInfo(pf fun)
-        : VMFunction(JS_FUNC_TO_DATA_PTR(void *, fun), explicitArgs(),
-                     argumentProperties(), outParam(), returnType())
+      : VMFunction(JS_FUNC_TO_DATA_PTR(void *, fun), explicitArgs(),
+                   argumentProperties(), outParam(), returnType())
     { }
 };
 
 // Specialize the class for each number of argument used by VMFunction.
 // Keep it verbose unless you find a readable macro for it.
 template <class R, class A1>
 struct FunctionInfo<R (*)(JSContext *, A1)> : public VMFunction {
     typedef R (*pf)(JSContext *, A1);
--- a/js/src/ion/arm/CodeGenerator-arm.cpp
+++ b/js/src/ion/arm/CodeGenerator-arm.cpp
@@ -509,17 +509,17 @@ CodeGeneratorARM::visitDivI(LDivI *ins)
     // and may change without notice in any one of a numebr of places.
     masm.ma_cmp(r3, Imm32(0));
     if (!bailoutIf(Assembler::NonZero, ins->snapshot()))
         return false;
     return true;
 }
 
 bool
-CodeGeneratorARM::visitBitNot(LBitNot *ins)
+CodeGeneratorARM::visitBitNotI(LBitNotI *ins)
 {
     const LAllocation *input = ins->getOperand(0);
     const LDefinition *dest = ins->getDef(0);
     // this will not actually be true on arm.
     // We can not an imm8m in order to get a wider range
     // of numbers
     JS_ASSERT(!input->isConstant());
 
--- a/js/src/ion/arm/CodeGenerator-arm.h
+++ b/js/src/ion/arm/CodeGenerator-arm.h
@@ -97,17 +97,17 @@ class CodeGeneratorARM : public CodeGene
     // true, and the false block if |cond| is false.
     void emitBranch(Assembler::Condition cond, MBasicBlock *ifTrue, MBasicBlock *ifFalse);
 
   public:
     // Instruction visitors.
     virtual bool visitAbsD(LAbsD *ins);
     virtual bool visitAddI(LAddI *ins);
     virtual bool visitSubI(LSubI *ins);
-    virtual bool visitBitNot(LBitNot *ins);
+    virtual bool visitBitNotI(LBitNotI *ins);
     virtual bool visitBitOp(LBitOp *ins);
 
     virtual bool visitMulI(LMulI *ins);
 
     virtual bool visitDivI(LDivI *ins);
     virtual bool visitMoveGroup(LMoveGroup *group);
     virtual bool visitShiftOp(LShiftOp *ins);
 
--- a/js/src/ion/arm/Trampoline-arm.cpp
+++ b/js/src/ion/arm/Trampoline-arm.cpp
@@ -582,19 +582,19 @@ IonCompartment::generateVMWrapper(JSCont
     Register outReg = InvalidReg;
     switch (f.outParam) {
       case Type_Value:
         outReg = regs.takeAny();
         masm.reserveStack(sizeof(Value));
         masm.ma_mov(sp, outReg);
         break;
 
-      case Type_JSBool:
+      case Type_Int32:
         outReg = regs.takeAny();
-        masm.reserveStack(sizeof(JSBool));
+        masm.reserveStack(sizeof(int));
         masm.ma_mov(sp, outReg);
         break;
 
       default:
         JS_ASSERT(f.outParam == Type_Void);
         break;
     }
 
@@ -652,19 +652,19 @@ IonCompartment::generateVMWrapper(JSCont
 
     // Load the outparam and free any allocated stack.
     switch (f.outParam) {
       case Type_Value:
         masm.loadValue(Address(sp, 0), JSReturnOperand);
         masm.freeStack(sizeof(Value));
         break;
 
-      case Type_JSBool:
+      case Type_Int32:
         masm.load32(Address(sp, 0), ReturnReg);
-        masm.freeStack(sizeof(JSBool));
+        masm.freeStack(sizeof(int32));
         break;
 
       default:
         JS_ASSERT(f.outParam == Type_Void);
         break;
     }
 
     masm.retn(Imm32(sizeof(IonExitFrameLayout) + argumentPadding +
--- a/js/src/ion/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/ion/shared/CodeGenerator-x86-shared.cpp
@@ -631,17 +631,17 @@ CodeGeneratorX86Shared::visitModI(LModI 
         masm.negl(remainder);
     }
 
     masm.bind(&join);
     return true;
 }
 
 bool
-CodeGeneratorX86Shared::visitBitNot(LBitNot *ins)
+CodeGeneratorX86Shared::visitBitNotI(LBitNotI *ins)
 {
     const LAllocation *input = ins->getOperand(0);
     JS_ASSERT(!input->isConstant());
 
     masm.notl(ToOperand(input));
     return true;
 }
 
--- a/js/src/ion/shared/CodeGenerator-x86-shared.h
+++ b/js/src/ion/shared/CodeGenerator-x86-shared.h
@@ -117,17 +117,17 @@ class CodeGeneratorX86Shared : public Co
   public:
     // Instruction visitors.
     virtual bool visitAbsD(LAbsD *ins);
     virtual bool visitAddI(LAddI *ins);
     virtual bool visitSubI(LSubI *ins);
     virtual bool visitMulI(LMulI *ins);
     virtual bool visitDivI(LDivI *ins);
     virtual bool visitModI(LModI *ins);
-    virtual bool visitBitNot(LBitNot *ins);
+    virtual bool visitBitNotI(LBitNotI *ins);
     virtual bool visitBitOp(LBitOp *ins);
     virtual bool visitShiftOp(LShiftOp *ins);
     virtual bool visitMoveGroup(LMoveGroup *group);
     virtual bool visitTestIAndBranch(LTestIAndBranch *test);
     virtual bool visitTestDAndBranch(LTestDAndBranch *test);
     virtual bool visitCompare(LCompare *comp);
     virtual bool visitCompareAndBranch(LCompareAndBranch *comp);
     virtual bool visitCompareD(LCompareD *comp);
--- a/js/src/ion/x64/Trampoline-x64.cpp
+++ b/js/src/ion/x64/Trampoline-x64.cpp
@@ -508,19 +508,19 @@ IonCompartment::generateVMWrapper(JSCont
     Register outReg = InvalidReg;
     switch (f.outParam) {
       case Type_Value:
         outReg = regs.takeAny();
         masm.reserveStack(sizeof(Value));
         masm.movq(esp, outReg);
         break;
 
-      case Type_JSBool:
+      case Type_Int32:
         outReg = regs.takeAny();
-        masm.reserveStack(sizeof(JSBool));
+        masm.reserveStack(sizeof(int));
         masm.movq(esp, outReg);
         break;
 
       default:
         JS_ASSERT(f.outParam == Type_Void);
         break;
     }
 
@@ -587,20 +587,19 @@ IonCompartment::generateVMWrapper(JSCont
 
     // Load the outparam and free any allocated stack.
     switch (f.outParam) {
       case Type_Value:
         masm.loadValue(Address(esp, 0), JSReturnOperand);
         masm.freeStack(sizeof(Value));
         break;
 
-      case Type_JSBool:
-        JS_STATIC_ASSERT(sizeof(JSBool) == 4);
+      case Type_Int32:
         masm.load32(Address(esp, 0), ReturnReg);
-        masm.freeStack(sizeof(JSBool));
+        masm.freeStack(sizeof(int));
         break;
 
       default:
         JS_ASSERT(f.outParam == Type_Void);
         break;
     }
 
     masm.retn(Imm32(sizeof(IonExitFrameLayout) + f.explicitStackSlots() * sizeof(void *)));
--- a/js/src/ion/x86/Trampoline-x86.cpp
+++ b/js/src/ion/x86/Trampoline-x86.cpp
@@ -526,19 +526,19 @@ IonCompartment::generateVMWrapper(JSCont
     Register outReg = InvalidReg;
     switch (f.outParam) {
       case Type_Value:
         outReg = regs.takeAny();
         masm.reserveStack(sizeof(Value));
         masm.movl(esp, outReg);
         break;
 
-      case Type_JSBool:
+      case Type_Int32:
         outReg = regs.takeAny();
-        masm.reserveStack(sizeof(JSBool));
+        masm.reserveStack(sizeof(int32));
         masm.movl(esp, outReg);
         break;
 
       default:
         JS_ASSERT(f.outParam == Type_Void);
         break;
     }
 
@@ -593,17 +593,17 @@ IonCompartment::generateVMWrapper(JSCont
 
     // Load the outparam and free any allocated stack.
     switch (f.outParam) {
       case Type_Value:
         masm.loadValue(Address(esp, 0), JSReturnOperand);
         masm.freeStack(sizeof(Value));
         break;
 
-      case Type_JSBool:
+      case Type_Int32:
         masm.load32(Address(esp, 0), ReturnReg);
         masm.freeStack(sizeof(JSBool));
         break;
 
       default:
         JS_ASSERT(f.outParam == Type_Void);
         break;
     }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug725000.js
@@ -0,0 +1,9 @@
+function f(s) {
+    var q;
+    for (var i = 0; i < 10000; i++)
+        q = ~s;
+    return q;
+}
+var obj = { valueOf: function () { return 3; } }
+assertEq(f(obj), -4);
+
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -2456,19 +2456,18 @@ BEGIN_CASE(JSOP_NOT)
     POP_BOOLEAN(cx, _, cond);
     PUSH_BOOLEAN(!cond);
 }
 END_CASE(JSOP_NOT)
 
 BEGIN_CASE(JSOP_BITNOT)
 {
     int32_t i;
-    if (!ToInt32(cx, regs.sp[-1], &i))
+    if (!BitNot(cx, regs.sp[-1], &i))
         goto error;
-    i = ~i;
     regs.sp[-1].setInt32(i);
 }
 END_CASE(JSOP_BITNOT)
 
 BEGIN_CASE(JSOP_NEG)
 {
     /*
      * When the operand is int jsval, INT32_FITS_IN_JSVAL(i) implies
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -812,13 +812,23 @@ GreaterThanOperation(JSContext *cx, cons
     RELATIONAL_OP(>);
 }
 
 static JS_ALWAYS_INLINE bool
 GreaterThanOrEqualOperation(JSContext *cx, const Value &lhs, const Value &rhs, bool *res) {
     RELATIONAL_OP(>=);
 }
 
+static JS_ALWAYS_INLINE bool
+BitNot(JSContext *cx, const Value &in, int *out)
+{
+    int i;
+    if (!ToInt32(cx, in, &i))
+        return false;
+    *out = ~i;
+    return true;
+}
+
 #undef RELATIONAL_OP
 
 }  /* namespace js */
 
 #endif /* jsinterpinlines_h__ */