bug 1490387 - Part 3: Implement BigInt support for bitwise operators. r=jandem
authorRobin Templeton <robin@igalia.com>
Tue, 18 Sep 2018 04:04:53 +0000
changeset 436995 d5c22661c86025e0098509d8d6183c4af99214ce
parent 436994 b74cf577c4015db47ad19fca177beb7f9221c8aa
child 436996 391f1d4d5cda5e493caefa9d78bc665452499e07
push id34667
push useraiakab@mozilla.com
push dateWed, 19 Sep 2018 02:13:23 +0000
treeherdermozilla-central@3857cbe7b717 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1490387
milestone64.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 1490387 - Part 3: Implement BigInt support for bitwise operators. r=jandem Differential Revision: https://phabricator.services.mozilla.com/D5557
js/src/jit/BaselineIC.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/IonIC.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/Recover.cpp
js/src/jit/TypePolicy.cpp
js/src/jit/shared/LIR-shared.h
js/src/vm/Interpreter-inl.h
js/src/vm/Interpreter.cpp
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -6029,21 +6029,20 @@ DoUnaryArithFallback(JSContext* cx, Base
 
     RootedScript script(cx, frame->script());
     jsbytecode* pc = stub->icEntry()->pc(script);
     JSOp op = JSOp(*pc);
     FallbackICSpew(cx, stub, "UnaryArith(%s)", CodeName[op]);
 
     switch (op) {
       case JSOP_BITNOT: {
-        int32_t result;
-        if (!BitNot(cx, val, &result)) {
+        RootedValue valCopy(cx, val);
+        if (!BitNot(cx, &valCopy, res)) {
             return false;
         }
-        res.setInt32(result);
         break;
       }
       case JSOP_NEG: {
         // We copy val here because the original value is needed below.
         RootedValue valCopy(cx, val);
         if (!NegOperation(cx, &valCopy, res)) {
             return false;
         }
@@ -6160,57 +6159,47 @@ DoBinaryArithFallback(JSContext* cx, Bas
         }
         break;
       case JSOP_POW:
         if (!PowValues(cx, &lhsCopy, &rhsCopy, ret)) {
             return false;
         }
         break;
       case JSOP_BITOR: {
-        int32_t result;
-        if (!BitOr(cx, lhs, rhs, &result)) {
+        if (!BitOr(cx, &lhsCopy, &rhsCopy, ret)) {
             return false;
         }
-        ret.setInt32(result);
         break;
       }
       case JSOP_BITXOR: {
-        int32_t result;
-        if (!BitXor(cx, lhs, rhs, &result)) {
+        if (!BitXor(cx, &lhsCopy, &rhsCopy, ret)) {
             return false;
         }
-        ret.setInt32(result);
         break;
       }
       case JSOP_BITAND: {
-        int32_t result;
-        if (!BitAnd(cx, lhs, rhs, &result)) {
+        if (!BitAnd(cx, &lhsCopy, &rhsCopy, ret)) {
             return false;
         }
-        ret.setInt32(result);
         break;
       }
       case JSOP_LSH: {
-        int32_t result;
-        if (!BitLsh(cx, lhs, rhs, &result)) {
+        if (!BitLsh(cx, &lhsCopy, &rhsCopy, ret)) {
             return false;
         }
-        ret.setInt32(result);
         break;
       }
       case JSOP_RSH: {
-        int32_t result;
-        if (!BitRsh(cx, lhs, rhs, &result)) {
+        if (!BitRsh(cx, &lhsCopy, &rhsCopy, ret)) {
             return false;
         }
-        ret.setInt32(result);
         break;
       }
       case JSOP_URSH: {
-        if (!UrshOperation(cx, lhs, rhs, ret)) {
+        if (!UrshOperation(cx, &lhsCopy, &rhsCopy, ret)) {
             return false;
         }
         break;
       }
       default:
         MOZ_CRASH("Unhandled baseline arith op");
     }
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -11293,27 +11293,27 @@ static const VMFunction ThrowInfoCodeGen
 
 void
 CodeGenerator::visitThrow(LThrow* lir)
 {
     pushArg(ToValue(lir, LThrow::Value));
     callVM(ThrowInfoCodeGen, lir);
 }
 
-typedef bool (*BitNotFn)(JSContext*, HandleValue, int* p);
+typedef bool (*BitNotFn)(JSContext*, MutableHandleValue, MutableHandleValue);
 static const VMFunction BitNotInfo = FunctionInfo<BitNotFn>(BitNot, "BitNot");
 
 void
 CodeGenerator::visitBitNotV(LBitNotV* lir)
 {
     pushArg(ToValue(lir, LBitNotV::Input));
     callVM(BitNotInfo, lir);
 }
 
-typedef bool (*BitopFn)(JSContext*, HandleValue, HandleValue, int* p);
+typedef bool (*BitopFn)(JSContext*, MutableHandleValue, MutableHandleValue, MutableHandleValue);
 static const VMFunction BitAndInfo = FunctionInfo<BitopFn>(BitAnd, "BitAnd");
 static const VMFunction BitOrInfo = FunctionInfo<BitopFn>(BitOr, "BitOr");
 static const VMFunction BitXorInfo = FunctionInfo<BitopFn>(BitXor, "BitXor");
 static const VMFunction BitLhsInfo = FunctionInfo<BitopFn>(BitLsh, "BitLsh");
 static const VMFunction BitRhsInfo = FunctionInfo<BitopFn>(BitRsh, "BitRsh");
 
 void
 CodeGenerator::visitBitOpV(LBitOpV* lir)
--- a/js/src/jit/IonIC.cpp
+++ b/js/src/jit/IonIC.cpp
@@ -562,21 +562,20 @@ IonUnaryArithIC::update(JSContext* cx, H
 {
     IonScript* ionScript = outerScript->ionScript();
     RootedScript script(cx, ic->script());
     jsbytecode* pc = ic->pc();
     JSOp op = JSOp(*pc);
 
     switch (op) {
       case JSOP_BITNOT: {
-        int32_t result;
-        if (!BitNot(cx, val, &result)) {
+        RootedValue valCopy(cx, val);
+        if (!BitNot(cx, &valCopy, res)) {
             return false;
         }
-        res.setInt32(result);
         break;
       }
       case JSOP_NEG: {
         // We copy val here because the original value is needed below.
         RootedValue valCopy(cx, val);
         if (!NegOperation(cx, &valCopy, res)) {
             return false;
         }
@@ -644,37 +643,31 @@ IonBinaryArithIC::update(JSContext* cx, 
         }
         break;
       case JSOP_MOD:
         if (!ModValues(cx, &lhsCopy, &rhsCopy, ret)) {
             return false;
         }
         break;
       case JSOP_BITOR: {
-        int32_t result;
-        if (!BitOr(cx, lhs, rhs, &result)) {
+        if (!BitOr(cx, &lhsCopy, &rhsCopy, ret)) {
             return false;
         }
-        ret.setInt32(result);
         break;
       }
       case JSOP_BITXOR: {
-        int32_t result;
-        if (!BitXor(cx, lhs, rhs, &result)) {
+        if (!BitXor(cx, &lhsCopy, &rhsCopy, ret)) {
             return false;
         }
-        ret.setInt32(result);
         break;
       }
       case JSOP_BITAND: {
-        int32_t result;
-        if (!BitAnd(cx, lhs, rhs, &result)) {
+        if (!BitAnd(cx, &lhsCopy, &rhsCopy, ret)) {
             return false;
         }
-        ret.setInt32(result);
         break;
       }
      default:
         MOZ_CRASH("Unhandled binary arith op");
     }
 
     if (ic->state().maybeTransition()) {
         ic->discardStubs(cx->zone());
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -2672,43 +2672,47 @@ MBinaryBitwiseInstruction::foldUnnecessa
 
 void
 MBinaryBitwiseInstruction::infer(BaselineInspector*, jsbytecode*)
 {
     if (getOperand(0)->mightBeType(MIRType::Object) || getOperand(0)->mightBeType(MIRType::Symbol) ||
         getOperand(1)->mightBeType(MIRType::Object) || getOperand(1)->mightBeType(MIRType::Symbol))
     {
         specialization_ = MIRType::None;
+        setResultType(MIRType::Value);
     } else {
         specializeAs(MIRType::Int32);
     }
 }
 
 void
 MBinaryBitwiseInstruction::specializeAs(MIRType type)
 {
     MOZ_ASSERT(type == MIRType::Int32 || type == MIRType::Int64);
-    MOZ_ASSERT(this->type() == type);
+    MOZ_ASSERT(this->type() == MIRType::Value || this->type() == type);
 
     specialization_ = type;
+    setResultType(type);
 
     if (isBitOr() || isBitAnd() || isBitXor()) {
         setCommutative();
     }
 }
 
 void
 MShiftInstruction::infer(BaselineInspector*, jsbytecode*)
 {
     if (getOperand(0)->mightBeType(MIRType::Object) || getOperand(1)->mightBeType(MIRType::Object) ||
         getOperand(0)->mightBeType(MIRType::Symbol) || getOperand(1)->mightBeType(MIRType::Symbol))
     {
         specialization_ = MIRType::None;
+        setResultType(MIRType::Value);
     } else {
         specialization_ = MIRType::Int32;
+        setResultType(MIRType::Int32);
     }
 }
 
 void
 MUrsh::infer(BaselineInspector* inspector, jsbytecode* pc)
 {
     if (getOperand(0)->mightBeType(MIRType::Object) || getOperand(1)->mightBeType(MIRType::Object) ||
         getOperand(0)->mightBeType(MIRType::Symbol) || getOperand(1)->mightBeType(MIRType::Symbol))
@@ -3788,17 +3792,17 @@ MCompare::cacheOperandMightEmulateUndefi
     markNoOperandEmulatesUndefined();
 }
 
 MBitNot*
 MBitNot::NewInt32(TempAllocator& alloc, MDefinition* input)
 {
     MBitNot* ins = new(alloc) MBitNot(input);
     ins->specialization_ = MIRType::Int32;
-    MOZ_ASSERT(ins->type() == MIRType::Int32);
+    ins->setResultType(MIRType::Int32);
     return ins;
 }
 
 MDefinition*
 MBitNot::foldsTo(TempAllocator& alloc)
 {
     if (specialization_ != MIRType::Int32) {
         return this;
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -4836,17 +4836,17 @@ class MBitNot
   : public MUnaryInstruction,
     public BitwisePolicy::Data
 {
   protected:
     explicit MBitNot(MDefinition* input)
       : MUnaryInstruction(classOpcode, input)
     {
         specialization_ = MIRType::None;
-        setResultType(MIRType::Int32);
+        setResultType(MIRType::Value);
         setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(BitNot)
     TRIVIAL_NEW_WRAPPERS
 
     static MBitNot* NewInt32(TempAllocator& alloc, MDefinition* input);
@@ -5000,17 +5000,17 @@ class MBinaryBitwiseInstruction
     public BitwisePolicy::Data
 {
   protected:
     MBinaryBitwiseInstruction(Opcode op, MDefinition* left, MDefinition* right, MIRType type)
       : MBinaryInstruction(op, left, right), maskMatchesLeftRange(false),
         maskMatchesRightRange(false)
     {
         MOZ_ASSERT(type == MIRType::Int32 || type == MIRType::Int64);
-        setResultType(type);
+        setResultType(MIRType::Value);
         setMovable();
     }
 
     void specializeAs(MIRType type);
     bool maskMatchesLeftRange;
     bool maskMatchesRightRange;
 
   public:
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -158,24 +158,23 @@ MBitNot::writeRecoverData(CompactBufferW
 
 RBitNot::RBitNot(CompactBufferReader& reader)
 { }
 
 bool
 RBitNot::recover(JSContext* cx, SnapshotIterator& iter) const
 {
     RootedValue operand(cx, iter.read());
+    RootedValue result(cx);
 
-    int32_t result;
-    if (!js::BitNot(cx, operand, &result)) {
+    if (!js::BitNot(cx, &operand, &result)) {
         return false;
     }
 
-    RootedValue rootedResult(cx, js::Int32Value(result));
-    iter.storeInstructionResult(rootedResult);
+    iter.storeInstructionResult(result);
     return true;
 }
 
 bool
 MBitAnd::writeRecoverData(CompactBufferWriter& writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
     writer.writeUnsigned(uint32_t(RInstruction::Recover_BitAnd));
@@ -185,25 +184,24 @@ MBitAnd::writeRecoverData(CompactBufferW
 RBitAnd::RBitAnd(CompactBufferReader& reader)
 { }
 
 bool
 RBitAnd::recover(JSContext* cx, SnapshotIterator& iter) const
 {
     RootedValue lhs(cx, iter.read());
     RootedValue rhs(cx, iter.read());
-    int32_t result;
+    RootedValue result(cx);
     MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
 
-    if (!js::BitAnd(cx, lhs, rhs, &result)) {
+    if (!js::BitAnd(cx, &lhs, &rhs, &result)) {
         return false;
     }
 
-    RootedValue rootedResult(cx, js::Int32Value(result));
-    iter.storeInstructionResult(rootedResult);
+    iter.storeInstructionResult(result);
     return true;
 }
 
 bool
 MBitOr::writeRecoverData(CompactBufferWriter& writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
     writer.writeUnsigned(uint32_t(RInstruction::Recover_BitOr));
@@ -213,25 +211,24 @@ MBitOr::writeRecoverData(CompactBufferWr
 RBitOr::RBitOr(CompactBufferReader& reader)
 {}
 
 bool
 RBitOr::recover(JSContext* cx, SnapshotIterator& iter) const
 {
     RootedValue lhs(cx, iter.read());
     RootedValue rhs(cx, iter.read());
-    int32_t result;
+    RootedValue result(cx);
     MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
 
-    if (!js::BitOr(cx, lhs, rhs, &result)) {
+    if (!js::BitOr(cx, &lhs, &rhs, &result)) {
         return false;
     }
 
-    RootedValue asValue(cx, js::Int32Value(result));
-    iter.storeInstructionResult(asValue);
+    iter.storeInstructionResult(result);
     return true;
 }
 
 bool
 MBitXor::writeRecoverData(CompactBufferWriter& writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
     writer.writeUnsigned(uint32_t(RInstruction::Recover_BitXor));
@@ -241,24 +238,23 @@ MBitXor::writeRecoverData(CompactBufferW
 RBitXor::RBitXor(CompactBufferReader& reader)
 { }
 
 bool
 RBitXor::recover(JSContext* cx, SnapshotIterator& iter) const
 {
     RootedValue lhs(cx, iter.read());
     RootedValue rhs(cx, iter.read());
+    RootedValue result(cx);
 
-    int32_t result;
-    if (!js::BitXor(cx, lhs, rhs, &result)) {
+    if (!js::BitXor(cx, &lhs, &rhs, &result)) {
         return false;
     }
 
-    RootedValue rootedResult(cx, js::Int32Value(result));
-    iter.storeInstructionResult(rootedResult);
+    iter.storeInstructionResult(result);
     return true;
 }
 
 bool
 MLsh::writeRecoverData(CompactBufferWriter& writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
     writer.writeUnsigned(uint32_t(RInstruction::Recover_Lsh));
@@ -268,25 +264,24 @@ MLsh::writeRecoverData(CompactBufferWrit
 RLsh::RLsh(CompactBufferReader& reader)
 {}
 
 bool
 RLsh::recover(JSContext* cx, SnapshotIterator& iter) const
 {
     RootedValue lhs(cx, iter.read());
     RootedValue rhs(cx, iter.read());
-    int32_t result;
+    RootedValue result(cx);
     MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
 
-    if (!js::BitLsh(cx, lhs, rhs, &result)) {
+    if (!js::BitLsh(cx, &lhs, &rhs, &result)) {
         return false;
     }
 
-    RootedValue asValue(cx, js::Int32Value(result));
-    iter.storeInstructionResult(asValue);
+    iter.storeInstructionResult(result);
     return true;
 }
 
 bool
 MRsh::writeRecoverData(CompactBufferWriter& writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
     writer.writeUnsigned(uint32_t(RInstruction::Recover_Rsh));
@@ -296,25 +291,24 @@ MRsh::writeRecoverData(CompactBufferWrit
 RRsh::RRsh(CompactBufferReader& reader)
 { }
 
 bool
 RRsh::recover(JSContext* cx, SnapshotIterator& iter) const
 {
     RootedValue lhs(cx, iter.read());
     RootedValue rhs(cx, iter.read());
+    RootedValue result(cx);
     MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
 
-    int32_t result;
-    if (!js::BitRsh(cx, lhs, rhs, &result)) {
+    if (!js::BitRsh(cx, &lhs, &rhs, &result)) {
         return false;
     }
 
-    RootedValue rootedResult(cx, js::Int32Value(result));
-    iter.storeInstructionResult(rootedResult);
+    iter.storeInstructionResult(result);
     return true;
 }
 
 bool
 MUrsh::writeRecoverData(CompactBufferWriter& writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
     writer.writeUnsigned(uint32_t(RInstruction::Recover_Ursh));
@@ -327,17 +321,17 @@ RUrsh::RUrsh(CompactBufferReader& reader
 bool
 RUrsh::recover(JSContext* cx, SnapshotIterator& iter) const
 {
     RootedValue lhs(cx, iter.read());
     RootedValue rhs(cx, iter.read());
     MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
 
     RootedValue result(cx);
-    if (!js::UrshOperation(cx, lhs, rhs, &result)) {
+    if (!js::UrshOperation(cx, &lhs, &rhs, &result)) {
         return false;
     }
 
     iter.storeInstructionResult(result);
     return true;
 }
 
 bool
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -409,17 +409,17 @@ TestPolicy::adjustInputs(TempAllocator& 
 bool
 BitwisePolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const
 {
     MIRType specialization = ins->typePolicySpecialization();
     if (specialization == MIRType::None) {
         return BoxInputsPolicy::staticAdjustInputs(alloc, ins);
     }
 
-    MOZ_ASSERT(ins->type() == specialization);
+    MOZ_ASSERT(ins->type() == MIRType::Value || ins->type() == specialization);
     MOZ_ASSERT(specialization == MIRType::Int32 || specialization == MIRType::Double);
 
     // This policy works for both unary and binary bitwise operations.
     for (size_t i = 0, e = ins->numOperands(); i < e; i++) {
         MDefinition* in = ins->getOperand(i);
         if (in->type() == MIRType::Int32) {
             continue;
         }
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -2857,17 +2857,17 @@ class LBitNotI : public LInstructionHelp
     LIR_HEADER(BitNotI)
 
     LBitNotI()
       : LInstructionHelper(classOpcode)
     {}
 };
 
 // Call a VM function to perform a BITNOT operation.
-class LBitNotV : public LCallInstructionHelper<1, BOX_PIECES, 0>
+class LBitNotV : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES, 0>
 {
   public:
     LIR_HEADER(BitNotV)
 
     static const size_t Input = 0;
 
     explicit LBitNotV(const LBoxAllocation& input)
       : LCallInstructionHelper(classOpcode)
@@ -2922,17 +2922,17 @@ class LBitOpI64 : public LInstructionHel
     }
 
     JSOp bitop() const {
         return op_;
     }
 };
 
 // Call a VM function to perform a bitwise operation.
-class LBitOpV : public LCallInstructionHelper<1, 2 * BOX_PIECES, 0>
+class LBitOpV : public LCallInstructionHelper<BOX_PIECES, 2 * BOX_PIECES, 0>
 {
     JSOp jsop_;
 
   public:
     LIR_HEADER(BitOpV)
 
     LBitOpV(JSOp jsop, const LBoxAllocation& lhs, const LBoxAllocation& rhs)
       : LCallInstructionHelper(classOpcode),
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -798,86 +798,134 @@ GreaterThanOperation(JSContext* cx, Muta
 }
 
 static MOZ_ALWAYS_INLINE bool
 GreaterThanOrEqualOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res) {
     RELATIONAL_OP(>=);
 }
 
 static MOZ_ALWAYS_INLINE bool
-BitNot(JSContext* cx, HandleValue in, int* out)
+BitNot(JSContext* cx, MutableHandleValue in, MutableHandleValue out)
 {
-    int i;
-    if (!ToInt32(cx, in, &i)) {
+    if (!ToInt32OrBigInt(cx, in)) {
         return false;
     }
-    *out = ~i;
+
+#ifdef ENABLE_BIGINT
+    if (in.isBigInt()) {
+        return BigInt::bitNot(cx, in, out);
+    }
+#endif
+
+    out.setInt32(~in.toInt32());
     return true;
 }
 
 static MOZ_ALWAYS_INLINE bool
-BitXor(JSContext* cx, HandleValue lhs, HandleValue rhs, int* out)
+BitXor(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue out)
 {
-    int left, right;
-    if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) {
+    if (!ToInt32OrBigInt(cx, lhs) || !ToInt32OrBigInt(cx, rhs)) {
         return false;
     }
-    *out = left ^ right;
+
+#ifdef ENABLE_BIGINT
+    if (lhs.isBigInt() || rhs.isBigInt()) {
+        return BigInt::bitXor(cx, lhs, rhs, out);
+    }
+#endif
+
+    out.setInt32(lhs.toInt32() ^ rhs.toInt32());
     return true;
 }
 
 static MOZ_ALWAYS_INLINE bool
-BitOr(JSContext* cx, HandleValue lhs, HandleValue rhs, int* out)
+BitOr(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue out)
 {
-    int left, right;
-    if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) {
+    if (!ToInt32OrBigInt(cx, lhs) || !ToInt32OrBigInt(cx, rhs)) {
         return false;
     }
-    *out = left | right;
+
+#ifdef ENABLE_BIGINT
+    if (lhs.isBigInt() || rhs.isBigInt()) {
+        return BigInt::bitOr(cx, lhs, rhs, out);
+    }
+#endif
+
+    out.setInt32(lhs.toInt32() | rhs.toInt32());
     return true;
 }
 
 static MOZ_ALWAYS_INLINE bool
-BitAnd(JSContext* cx, HandleValue lhs, HandleValue rhs, int* out)
+BitAnd(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue out)
 {
-    int left, right;
-    if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) {
+    if (!ToInt32OrBigInt(cx, lhs) || !ToInt32OrBigInt(cx, rhs)) {
         return false;
     }
-    *out = left & right;
+
+#ifdef ENABLE_BIGINT
+    if (lhs.isBigInt() || rhs.isBigInt()) {
+        return BigInt::bitAnd(cx, lhs, rhs, out);
+    }
+#endif
+
+    out.setInt32(lhs.toInt32() & rhs.toInt32());
+    return true;
+}
+
+static MOZ_ALWAYS_INLINE bool
+BitLsh(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue out)
+{
+    if (!ToInt32OrBigInt(cx, lhs) || !ToInt32OrBigInt(cx, rhs)) {
+        return false;
+    }
+
+#ifdef ENABLE_BIGINT
+    if (lhs.isBigInt() || rhs.isBigInt()) {
+        return BigInt::lsh(cx, lhs, rhs, out);
+    }
+#endif
+
+    out.setInt32(lhs.toInt32() << (rhs.toInt32() & 31));
     return true;
 }
 
 static MOZ_ALWAYS_INLINE bool
-BitLsh(JSContext* cx, HandleValue lhs, HandleValue rhs, int* out)
+BitRsh(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue out)
 {
-    int32_t left, right;
-    if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) {
+    if (!ToInt32OrBigInt(cx, lhs) || !ToInt32OrBigInt(cx, rhs)) {
         return false;
     }
-    *out = uint32_t(left) << (right & 31);
+
+#ifdef ENABLE_BIGINT
+    if (lhs.isBigInt() || rhs.isBigInt()) {
+        return BigInt::rsh(cx, lhs, rhs, out);
+    }
+#endif
+
+    out.setInt32(lhs.toInt32() >> (rhs.toInt32() & 31));
     return true;
 }
 
 static MOZ_ALWAYS_INLINE bool
-BitRsh(JSContext* cx, HandleValue lhs, HandleValue rhs, int* out)
+UrshOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue out)
 {
-    int32_t left, right;
-    if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) {
+#ifdef ENABLE_BIGINT
+    if (!ToNumeric(cx, lhs) || !ToNumeric(cx, rhs)) {
         return false;
     }
-    *out = left >> (right & 31);
-    return true;
-}
 
-static MOZ_ALWAYS_INLINE bool
-UrshOperation(JSContext* cx, HandleValue lhs, HandleValue rhs, MutableHandleValue out)
-{
+    if (lhs.isBigInt() || rhs.isBigInt()) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                                  JSMSG_BIGINT_TO_NUMBER);
+        return false;
+    }
+#endif
+
     uint32_t left;
-    int32_t  right;
+    int32_t right;
     if (!ToUint32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) {
         return false;
     }
     left >>= right & 31;
     out.setNumber(uint32_t(left));
     return true;
 }
 
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -2695,42 +2695,52 @@ CASE(JSOP_BINDNAME)
 END_CASE(JSOP_BINDNAME)
 
 CASE(JSOP_BINDVAR)
 {
     PUSH_OBJECT(REGS.fp()->varObj());
 }
 END_CASE(JSOP_BINDVAR)
 
-#define BITWISE_OP(OP)                                                        \
-    JS_BEGIN_MACRO                                                            \
-        int32_t i, j;                                                         \
-        if (!ToInt32(cx, REGS.stackHandleAt(-2), &i))                         \
-            goto error;                                                       \
-        if (!ToInt32(cx, REGS.stackHandleAt(-1), &j))                         \
-            goto error;                                                       \
-        i = i OP j;                                                           \
-        REGS.sp--;                                                            \
-        REGS.sp[-1].setInt32(i);                                              \
-    JS_END_MACRO
-
 CASE(JSOP_BITOR)
-    BITWISE_OP(|);
+{
+    MutableHandleValue lhs = REGS.stackHandleAt(-2);
+    MutableHandleValue rhs = REGS.stackHandleAt(-1);
+    MutableHandleValue res = REGS.stackHandleAt(-2);
+    if (!BitOr(cx, lhs, rhs, res)) {
+        goto error;
+    }
+    REGS.sp--;
+}
 END_CASE(JSOP_BITOR)
 
 CASE(JSOP_BITXOR)
-    BITWISE_OP(^);
+{
+    MutableHandleValue lhs = REGS.stackHandleAt(-2);
+    MutableHandleValue rhs = REGS.stackHandleAt(-1);
+    MutableHandleValue res = REGS.stackHandleAt(-2);
+    if (!BitXor(cx, lhs, rhs, res)) {
+        goto error;
+    }
+    REGS.sp--;
+}
 END_CASE(JSOP_BITXOR)
 
 CASE(JSOP_BITAND)
-    BITWISE_OP(&);
+{
+    MutableHandleValue lhs = REGS.stackHandleAt(-2);
+    MutableHandleValue rhs = REGS.stackHandleAt(-1);
+    MutableHandleValue res = REGS.stackHandleAt(-2);
+    if (!BitAnd(cx, lhs, rhs, res)) {
+        goto error;
+    }
+    REGS.sp--;
+}
 END_CASE(JSOP_BITAND)
 
-#undef BITWISE_OP
-
 CASE(JSOP_EQ)
     if (!LooseEqualityOp<true>(cx, REGS)) {
         goto error;
     }
 END_CASE(JSOP_EQ)
 
 CASE(JSOP_NE)
     if (!LooseEqualityOp<false>(cx, REGS)) {
@@ -2829,44 +2839,46 @@ CASE(JSOP_GE)
         goto error;
     }
     TRY_BRANCH_AFTER_COND(cond, 2);
     REGS.sp[-2].setBoolean(cond);
     REGS.sp--;
 }
 END_CASE(JSOP_GE)
 
-#define SIGNED_SHIFT_OP(OP, TYPE)                                             \
-    JS_BEGIN_MACRO                                                            \
-        int32_t i, j;                                                         \
-        if (!ToInt32(cx, REGS.stackHandleAt(-2), &i))                         \
-            goto error;                                                       \
-        if (!ToInt32(cx, REGS.stackHandleAt(-1), &j))                         \
-            goto error;                                                       \
-        i = TYPE(i) OP (j & 31);                                              \
-        REGS.sp--;                                                            \
-        REGS.sp[-1].setInt32(i);                                              \
-    JS_END_MACRO
-
 CASE(JSOP_LSH)
-    SIGNED_SHIFT_OP(<<, uint32_t);
+{
+    MutableHandleValue lhs = REGS.stackHandleAt(-2);
+    MutableHandleValue rhs = REGS.stackHandleAt(-1);
+    MutableHandleValue res = REGS.stackHandleAt(-2);
+    if (!BitLsh(cx, lhs, rhs, res)) {
+        goto error;
+    }
+    REGS.sp--;
+}
 END_CASE(JSOP_LSH)
 
 CASE(JSOP_RSH)
-    SIGNED_SHIFT_OP(>>, int32_t);
+{
+    MutableHandleValue lhs = REGS.stackHandleAt(-2);
+    MutableHandleValue rhs = REGS.stackHandleAt(-1);
+    MutableHandleValue res = REGS.stackHandleAt(-2);
+    if (!BitRsh(cx, lhs, rhs, res)) {
+        goto error;
+    }
+    REGS.sp--;
+}
 END_CASE(JSOP_RSH)
 
-#undef SIGNED_SHIFT_OP
-
 CASE(JSOP_URSH)
 {
-    HandleValue lval = REGS.stackHandleAt(-2);
-    HandleValue rval = REGS.stackHandleAt(-1);
+    MutableHandleValue lhs = REGS.stackHandleAt(-2);
+    MutableHandleValue rhs = REGS.stackHandleAt(-1);
     MutableHandleValue res = REGS.stackHandleAt(-2);
-    if (!UrshOperation(cx, lval, rval, res)) {
+    if (!UrshOperation(cx, lhs, rhs, res)) {
         goto error;
     }
     REGS.sp--;
 }
 END_CASE(JSOP_URSH)
 
 CASE(JSOP_ADD)
 {
@@ -2945,22 +2957,21 @@ CASE(JSOP_NOT)
     bool cond = ToBoolean(REGS.stackHandleAt(-1));
     REGS.sp--;
     PUSH_BOOLEAN(!cond);
 }
 END_CASE(JSOP_NOT)
 
 CASE(JSOP_BITNOT)
 {
-    int32_t i;
-    HandleValue value = REGS.stackHandleAt(-1);
-    if (!BitNot(cx, value, &i)) {
+    MutableHandleValue value = REGS.stackHandleAt(-1);
+    MutableHandleValue res = REGS.stackHandleAt(-1);
+    if (!BitNot(cx, value, res)) {
         goto error;
     }
-    REGS.sp[-1].setInt32(i);
 }
 END_CASE(JSOP_BITNOT)
 
 CASE(JSOP_NEG)
 {
     ReservedRooted<Value> val(&rootValue0, REGS.sp[-1]);
     MutableHandleValue res = REGS.stackHandleAt(-1);
     if (!NegOperation(cx, &val, res)) {