Bug 1526870 - Part 3: Handle BigInts via vm-call in UnaryArithIRGenerator. r=jandem
authorAndré Bargull <andre.bargull@gmail.com>
Wed, 30 Oct 2019 09:46:54 +0000
changeset 500187 59e32702b1a62adff0c021bbc9f43762e885a60b
parent 500186 ccdc3d34f617718e04685aa91b2d073be9f82177
child 500188 c1b72e0bcfc1cfa37e24ea7d663e032bce3ca2e9
push id114164
push useraiakab@mozilla.com
push dateTue, 05 Nov 2019 10:06:15 +0000
treeherdermozilla-inbound@4d585c7edc76 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1526870
milestone72.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 1526870 - Part 3: Handle BigInts via vm-call in UnaryArithIRGenerator. r=jandem Adds new CacheIR ops to support unary operations with BigInt operands. All methods simply call into the VM to perform the actual operation. Parts 14/15 move the separate Baseline and Ion implementations into a single implementation in CacheIRCompiler. Differential Revision: https://phabricator.services.mozilla.com/D49909
js/src/jit-test/tests/cacheir/bigint-unary.js
js/src/jit/BaselineCacheIRCompiler.cpp
js/src/jit/BaselineCacheIRCompiler.h
js/src/jit/BaselineInspector.cpp
js/src/jit/CacheIR.cpp
js/src/jit/CacheIR.h
js/src/jit/IonCacheIRCompiler.cpp
js/src/jit/IonCacheIRCompiler.h
js/src/jit/VMFunctionList-inl.h
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bigint-unary.js
@@ -0,0 +1,135 @@
+var xs = [
+  // Definitely heap digits.
+  -(2n ** 1000n),
+
+  // -(2n**64n)
+  -18446744073709551617n,
+  -18446744073709551616n,
+  -18446744073709551615n,
+
+  // -(2n**63n)
+  -9223372036854775809n,
+  -9223372036854775808n,
+  -9223372036854775807n,
+
+  // -(2**32)
+  -4294967297n,
+  -4294967296n,
+  -4294967295n,
+
+  // -(2**31)
+  -2147483649n,
+  -2147483648n,
+  -2147483647n,
+
+  -1n,
+  0n,
+  1n,
+
+  // 2**31
+  2147483647n,
+  2147483648n,
+  2147483649n,
+
+  // 2**32
+  4294967295n,
+  4294967296n,
+  4294967297n,
+
+  // 2n**63n
+  9223372036854775807n,
+  9223372036854775808n,
+  9223372036854775809n,
+
+  // 2n**64n
+  18446744073709551615n,
+  18446744073709551616n,
+  18446744073709551617n,
+
+  // Definitely heap digits.
+  2n ** 1000n,
+];
+
+function testNeg() {
+  for (var i = 0; i < 100; ++i) {
+    var j = i % xs.length;
+    var x = xs[j];
+    var y = xs[xs.length - 1 - j];
+
+    assertEq(-x, y);
+  }
+}
+testNeg();
+
+function testBitNot() {
+  var ys = xs.map(x => -(x + 1n));
+
+  for (var i = 0; i < 100; ++i) {
+    var j = i % xs.length;
+    var x = xs[j];
+    var y = ys[j];
+
+    assertEq(~x, y);
+  }
+}
+testBitNot();
+
+function testPreInc() {
+  var ys = xs.map(x => x + 1n);
+
+  for (var i = 0; i < 100; ++i) {
+    var j = i % xs.length;
+    var x = xs[j];
+    var y = ys[j];
+
+    var r = ++x;
+    assertEq(x, y);
+    assertEq(r, y);
+  }
+}
+testPostInc();
+
+function testPostInc() {
+  var ys = xs.map(x => x + 1n);
+
+  for (var i = 0; i < 100; ++i) {
+    var j = i % xs.length;
+    var x = xs[j];
+    var y = ys[j];
+
+    var r = x++;
+    assertEq(x, y);
+    assertEq(r, xs[j]);
+  }
+}
+testPostInc();
+
+function testPreDec() {
+  var ys = xs.map(x => x - 1n);
+
+  for (var i = 0; i < 100; ++i) {
+    var j = i % xs.length;
+    var x = xs[j];
+    var y = ys[j];
+
+    var r = --x;
+    assertEq(x, y);
+    assertEq(r, y);
+  }
+}
+testPostDec();
+
+function testPostDec() {
+  var ys = xs.map(x => x - 1n);
+
+  for (var i = 0; i < 100; ++i) {
+    var j = i % xs.length;
+    var x = xs[j];
+    var y = ys[j];
+
+    var r = x--;
+    assertEq(x, y);
+    assertEq(r, xs[j]);
+  }
+}
+testPostDec();
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -2142,16 +2142,70 @@ bool BaselineCacheIRCompiler::emitCallSt
   masm.pushValue(lhs);
 
   using Fn = bool (*)(JSContext*, HandleValue, HandleValue, MutableHandleValue);
   tailCallVM<Fn, DoConcatStringObject>(masm);
 
   return true;
 }
 
+template <typename CallVM>
+bool BaselineCacheIRCompiler::emitBigIntUnaryOperationShared(
+    const CallVM& emitCallVM) {
+  AutoOutputRegister output(*this);
+  Register val = allocator.useRegister(masm, reader.bigIntOperandId());
+  AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
+
+  allocator.discardStack(masm);
+
+  AutoStubFrame stubFrame(*this);
+  stubFrame.enter(masm, scratch);
+
+  masm.push(val);
+
+  emitCallVM();
+
+  masm.tagValue(JSVAL_TYPE_BIGINT, ReturnReg, output.valueReg());
+
+  stubFrame.leave(masm);
+  return true;
+}
+
+bool BaselineCacheIRCompiler::emitBigIntNotResult() {
+  JitSpew(JitSpew_Codegen, __FUNCTION__);
+  return emitBigIntUnaryOperationShared([&]() {
+    using Fn = BigInt* (*)(JSContext*, HandleBigInt);
+    callVM<Fn, jit::BigIntBitNot>(masm);
+  });
+}
+
+bool BaselineCacheIRCompiler::emitBigIntNegationResult() {
+  JitSpew(JitSpew_Codegen, __FUNCTION__);
+  return emitBigIntUnaryOperationShared([&]() {
+    using Fn = BigInt* (*)(JSContext*, HandleBigInt);
+    callVM<Fn, jit::BigIntNeg>(masm);
+  });
+}
+
+bool BaselineCacheIRCompiler::emitBigIntIncResult() {
+  JitSpew(JitSpew_Codegen, __FUNCTION__);
+  return emitBigIntUnaryOperationShared([&]() {
+    using Fn = BigInt* (*)(JSContext*, HandleBigInt);
+    callVM<Fn, jit::BigIntInc>(masm);
+  });
+}
+
+bool BaselineCacheIRCompiler::emitBigIntDecResult() {
+  JitSpew(JitSpew_Codegen, __FUNCTION__);
+  return emitBigIntUnaryOperationShared([&]() {
+    using Fn = BigInt* (*)(JSContext*, HandleBigInt);
+    callVM<Fn, jit::BigIntDec>(masm);
+  });
+}
+
 // The value of argc entering the call IC is not always the value of
 // argc entering the callee. (For example, argc for a spread call IC
 // is always 1, but argc for the callee is the length of the array.)
 // In these cases, we update argc as part of the call op itself, to
 // avoid modifying input operands while it is still possible to fail a
 // guard. We also limit callee argc to a reasonable value to avoid
 // blowing the stack limit.
 bool BaselineCacheIRCompiler::updateArgc(CallFlags flags, Register argcReg,
--- a/js/src/jit/BaselineCacheIRCompiler.h
+++ b/js/src/jit/BaselineCacheIRCompiler.h
@@ -66,16 +66,19 @@ class MOZ_RAII BaselineCacheIRCompiler :
 
   MOZ_MUST_USE bool emitCallScriptedGetterResultShared(
       TypedOrValueRegister receiver);
 
   template <typename T, typename CallVM>
   MOZ_MUST_USE bool emitCallNativeGetterResultShared(T receiver,
                                                      const CallVM& emitCallVM);
 
+  template <typename CallVM>
+  MOZ_MUST_USE bool emitBigIntUnaryOperationShared(const CallVM& emitCallVM);
+
  public:
   friend class AutoStubFrame;
 
   BaselineCacheIRCompiler(JSContext* cx, const CacheIRWriter& writer,
                           uint32_t stubDataOffset,
                           BaselineCacheIRStubKind stubKind);
 
   MOZ_MUST_USE bool init(CacheKind kind);
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -245,17 +245,18 @@ static void SkipBinaryGuards(CacheIRRead
       reader.skip();  // Skip over operandId
       reader.skip();  // Skip over result/type.
       continue;
     }
 
     // One skip
     if (reader.matchOp(CacheOp::GuardIsNumber) ||
         reader.matchOp(CacheOp::GuardToString) ||
-        reader.matchOp(CacheOp::GuardToObject)) {
+        reader.matchOp(CacheOp::GuardToObject) ||
+        reader.matchOp(CacheOp::GuardToBigInt)) {
       reader.skip();  // Skip over operandId
       continue;
     }
     return;
   }
 }
 
 static MIRType ParseCacheIRStub(ICStub* stub) {
@@ -299,16 +300,21 @@ static MIRType ParseCacheIRStub(ICStub* 
     case CacheOp::Int32DecResult:
       return MIRType::Int32;
     // Int32URightShiftResult may return a double under some
     // circumstances.
     case CacheOp::Int32URightShiftResult:
       reader.skip();  // Skip over lhs
       reader.skip();  // Skip over rhs
       return reader.readByte() == 0 ? MIRType::Int32 : MIRType::Double;
+    case CacheOp::BigIntNotResult:
+    case CacheOp::BigIntNegationResult:
+    case CacheOp::BigIntIncResult:
+    case CacheOp::BigIntDecResult:
+      return MIRType::BigInt;
     case CacheOp::LoadValueResult:
       return MIRType::Value;
     default:
       MOZ_CRASH("Unknown op");
       return MIRType::None;
   }
 }
 
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -6159,16 +6159,17 @@ void UnaryArithIRGenerator::trackAttache
   }
 #endif
 }
 
 AttachDecision UnaryArithIRGenerator::tryAttachStub() {
   AutoAssertNoPendingException aanpe(cx_);
   TRY_ATTACH(tryAttachInt32());
   TRY_ATTACH(tryAttachNumber());
+  TRY_ATTACH(tryAttachBigInt());
 
   trackAttached(IRGenerator::NotAttached);
   return AttachDecision::NoAction;
 }
 
 AttachDecision UnaryArithIRGenerator::tryAttachInt32() {
   if (!val_.isInt32() || !res_.isInt32()) {
     return AttachDecision::NoAction;
@@ -6231,16 +6232,48 @@ AttachDecision UnaryArithIRGenerator::tr
     default:
       MOZ_CRASH("Unexpected OP");
   }
 
   writer.returnFromIC();
   return AttachDecision::Attach;
 }
 
+AttachDecision UnaryArithIRGenerator::tryAttachBigInt() {
+  if (!val_.isBigInt()) {
+    return AttachDecision::NoAction;
+  }
+
+  ValOperandId valId(writer.setInputOperandId(0));
+  BigIntOperandId bigIntId = writer.guardToBigInt(valId);
+  switch (op_) {
+    case JSOP_BITNOT:
+      writer.bigIntNotResult(bigIntId);
+      trackAttached("UnaryArith.BigIntNot");
+      break;
+    case JSOP_NEG:
+      writer.bigIntNegationResult(bigIntId);
+      trackAttached("UnaryArith.BigIntNeg");
+      break;
+    case JSOP_INC:
+      writer.bigIntIncResult(bigIntId);
+      trackAttached("UnaryArith.BigIntInc");
+      break;
+    case JSOP_DEC:
+      writer.bigIntDecResult(bigIntId);
+      trackAttached("UnaryArith.BigIntDec");
+      break;
+    default:
+      MOZ_CRASH("Unexpected OP");
+  }
+
+  writer.returnFromIC();
+  return AttachDecision::Attach;
+}
+
 BinaryArithIRGenerator::BinaryArithIRGenerator(
     JSContext* cx, HandleScript script, jsbytecode* pc, ICState::Mode mode,
     JSOp op, HandleValue lhs, HandleValue rhs, HandleValue res)
     : IRGenerator(cx, script, pc, CacheKind::BinaryArith, mode),
       op_(op),
       lhs_(lhs),
       rhs_(rhs),
       res_(res) {}
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -361,22 +361,26 @@ extern const uint32_t ArgLengths[];
   _(Int32ModResult, Id, Id)                                                    \
   _(Int32BitOrResult, Id, Id)                                                  \
   _(Int32BitXorResult, Id, Id)                                                 \
   _(Int32BitAndResult, Id, Id)                                                 \
   _(Int32LeftShiftResult, Id, Id)                                              \
   _(Int32RightShiftResult, Id, Id)                                             \
   _(Int32URightShiftResult, Id, Id, Byte)                                      \
   _(Int32NotResult, Id)                                                        \
+  _(BigIntNotResult, Id)                                                       \
   _(Int32NegationResult, Id)                                                   \
   _(DoubleNegationResult, Id)                                                  \
+  _(BigIntNegationResult, Id)                                                  \
   _(Int32IncResult, Id)                                                        \
   _(Int32DecResult, Id)                                                        \
   _(DoubleIncResult, Id)                                                       \
   _(DoubleDecResult, Id)                                                       \
+  _(BigIntIncResult, Id)                                                       \
+  _(BigIntDecResult, Id)                                                       \
   _(LoadInt32TruthyResult, Id)                                                 \
   _(LoadDoubleTruthyResult, Id)                                                \
   _(LoadStringTruthyResult, Id)                                                \
   _(LoadObjectTruthyResult, Id)                                                \
   _(LoadBigIntTruthyResult, Id)                                                \
   _(LoadValueResult, Field)                                                    \
   _(LoadNewObjectFromTemplateResult, Field, UInt32, UInt32)                    \
                                                                                \
@@ -1698,16 +1702,32 @@ class MOZ_RAII CacheIRWriter : public JS
   void doubleIncResult(NumberOperandId val) {
     writeOpWithOperandId(CacheOp::DoubleIncResult, val);
   }
 
   void doubleDecResult(NumberOperandId val) {
     writeOpWithOperandId(CacheOp::DoubleDecResult, val);
   }
 
+  void bigIntNotResult(BigIntOperandId id) {
+    writeOpWithOperandId(CacheOp::BigIntNotResult, id);
+  }
+
+  void bigIntNegationResult(BigIntOperandId id) {
+    writeOpWithOperandId(CacheOp::BigIntNegationResult, id);
+  }
+
+  void bigIntIncResult(BigIntOperandId id) {
+    writeOpWithOperandId(CacheOp::BigIntIncResult, id);
+  }
+
+  void bigIntDecResult(BigIntOperandId id) {
+    writeOpWithOperandId(CacheOp::BigIntDecResult, id);
+  }
+
   void loadBooleanResult(bool val) {
     writeOp(CacheOp::LoadBooleanResult);
     buffer_.writeByte(uint32_t(val));
   }
 
   void loadUndefinedResult() { writeOp(CacheOp::LoadUndefinedResult); }
   void loadStringResult(JSString* str) {
     writeOp(CacheOp::LoadStringResult);
@@ -2700,16 +2720,17 @@ class MOZ_RAII GetIntrinsicIRGenerator :
 
 class MOZ_RAII UnaryArithIRGenerator : public IRGenerator {
   JSOp op_;
   HandleValue val_;
   HandleValue res_;
 
   AttachDecision tryAttachInt32();
   AttachDecision tryAttachNumber();
+  AttachDecision tryAttachBigInt();
 
   void trackAttached(const char* name);
 
  public:
   UnaryArithIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc,
                         ICState::Mode mode, JSOp op, HandleValue val,
                         HandleValue res);
 
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -2317,16 +2317,66 @@ bool IonCacheIRCompiler::emitCallStringO
 
   using Fn = bool (*)(JSContext*, HandleValue, HandleValue, MutableHandleValue);
   callVM<Fn, DoConcatStringObject>(masm);
 
   masm.storeCallResultValue(output);
   return true;
 }
 
+template <typename CallVM>
+bool IonCacheIRCompiler::emitBigIntUnaryOperationShared(
+    const CallVM& emitCallVM) {
+  AutoSaveLiveRegisters save(*this);
+  AutoOutputRegister output(*this);
+  Register val = allocator.useRegister(masm, reader.bigIntOperandId());
+
+  allocator.discardStack(masm);
+
+  prepareVMCall(masm, save);
+  masm.Push(val);
+
+  emitCallVM();
+
+  masm.tagValue(JSVAL_TYPE_BIGINT, ReturnReg, output.valueReg());
+  return true;
+}
+
+bool IonCacheIRCompiler::emitBigIntNotResult() {
+  JitSpew(JitSpew_Codegen, __FUNCTION__);
+  return emitBigIntUnaryOperationShared([&]() {
+    using Fn = BigInt* (*)(JSContext*, HandleBigInt);
+    callVM<Fn, jit::BigIntBitNot>(masm);
+  });
+}
+
+bool IonCacheIRCompiler::emitBigIntNegationResult() {
+  JitSpew(JitSpew_Codegen, __FUNCTION__);
+  return emitBigIntUnaryOperationShared([&]() {
+    using Fn = BigInt* (*)(JSContext*, HandleBigInt);
+    callVM<Fn, jit::BigIntNeg>(masm);
+  });
+}
+
+bool IonCacheIRCompiler::emitBigIntIncResult() {
+  JitSpew(JitSpew_Codegen, __FUNCTION__);
+  return emitBigIntUnaryOperationShared([&]() {
+    using Fn = BigInt* (*)(JSContext*, HandleBigInt);
+    callVM<Fn, jit::BigIntInc>(masm);
+  });
+}
+
+bool IonCacheIRCompiler::emitBigIntDecResult() {
+  JitSpew(JitSpew_Codegen, __FUNCTION__);
+  return emitBigIntUnaryOperationShared([&]() {
+    using Fn = BigInt* (*)(JSContext*, HandleBigInt);
+    callVM<Fn, jit::BigIntDec>(masm);
+  });
+}
+
 bool IonCacheIRCompiler::emitCallScriptedFunction() {
   MOZ_CRASH("Call ICs not used in ion");
 }
 
 bool IonCacheIRCompiler::emitCallNativeFunction() {
   MOZ_CRASH("Call ICs not used in ion");
 }
 
--- a/js/src/jit/IonCacheIRCompiler.h
+++ b/js/src/jit/IonCacheIRCompiler.h
@@ -62,16 +62,19 @@ class MOZ_RAII IonCacheIRCompiler : publ
 
   MOZ_MUST_USE bool emitAddAndStoreSlotShared(CacheOp op);
   MOZ_MUST_USE bool emitCallScriptedGetterResultShared(
       TypedOrValueRegister receiver, TypedOrValueRegister output);
   MOZ_MUST_USE bool emitCallNativeGetterResultShared(
       TypedOrValueRegister receiver, const AutoOutputRegister& output,
       AutoSaveLiveRegisters& save);
 
+  template <typename CallVM>
+  MOZ_MUST_USE bool emitBigIntUnaryOperationShared(const CallVM& emitCallVM);
+
   bool needsPostBarrier() const;
 
   void pushStubCodePointer();
 
 #define DEFINE_OP(op, ...) MOZ_MUST_USE bool emit##op();
   CACHE_IR_OPS(DEFINE_OP)
 #undef DEFINE_OP
 };
--- a/js/src/jit/VMFunctionList-inl.h
+++ b/js/src/jit/VMFunctionList-inl.h
@@ -43,16 +43,20 @@ namespace jit {
   _(AsyncFunctionAwait, js::AsyncFunctionAwait)                                \
   _(AsyncFunctionResolve, js::AsyncFunctionResolve)                            \
   _(BaselineCompileFromBaselineInterpreter,                                    \
     js::jit::BaselineCompileFromBaselineInterpreter)                           \
   _(BaselineDebugPrologue, js::jit::DebugPrologue)                             \
   _(BaselineGetFunctionThis, js::jit::BaselineGetFunctionThis)                 \
   _(BaselineThrowInitializedThis, js::jit::BaselineThrowInitializedThis)       \
   _(BaselineThrowUninitializedThis, js::jit::BaselineThrowUninitializedThis)   \
+  _(BigIntBitNot, js::jit::BigIntBitNot)                                       \
+  _(BigIntDec, js::jit::BigIntDec)                                             \
+  _(BigIntInc, js::jit::BigIntInc)                                             \
+  _(BigIntNeg, js::jit::BigIntNeg)                                             \
   _(BindVarOperation, js::BindVarOperation)                                    \
   _(BitAnd, js::BitAnd)                                                        \
   _(BitLsh, js::BitLsh)                                                        \
   _(BitNot, js::BitNot)                                                        \
   _(BitOr, js::BitOr)                                                          \
   _(BitRsh, js::BitRsh)                                                        \
   _(BitXor, js::BitXor)                                                        \
   _(BoxNonStrictThis, js::BoxNonStrictThis)                                    \
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -2080,10 +2080,20 @@ bool DoToNumeric(JSContext* cx, HandleVa
   return ToNumeric(cx, ret);
 }
 
 void* AllocateBigIntNoGC(JSContext* cx) {
   AutoUnsafeCallWithABI unsafe;
   return js::Allocate<BigInt, NoGC>(cx);
 }
 
+BigInt* BigIntBitNot(JSContext* cx, HandleBigInt x) {
+  return BigInt::bitNot(cx, x);
+}
+
+BigInt* BigIntNeg(JSContext* cx, HandleBigInt x) { return BigInt::neg(cx, x); }
+
+BigInt* BigIntInc(JSContext* cx, HandleBigInt x) { return BigInt::inc(cx, x); }
+
+BigInt* BigIntDec(JSContext* cx, HandleBigInt x) { return BigInt::dec(cx, x); }
+
 }  // namespace jit
 }  // namespace js
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -1140,16 +1140,21 @@ MOZ_MUST_USE bool TrySkipAwait(JSContext
 
 bool IsPossiblyWrappedTypedArray(JSContext* cx, JSObject* obj, bool* result);
 
 bool DoToNumber(JSContext* cx, HandleValue arg, MutableHandleValue ret);
 bool DoToNumeric(JSContext* cx, HandleValue arg, MutableHandleValue ret);
 
 void* AllocateBigIntNoGC(JSContext* cx);
 
+BigInt* BigIntBitNot(JSContext* cx, HandleBigInt x);
+BigInt* BigIntNeg(JSContext* cx, HandleBigInt x);
+BigInt* BigIntInc(JSContext* cx, HandleBigInt x);
+BigInt* BigIntDec(JSContext* cx, HandleBigInt x);
+
 enum class TailCallVMFunctionId;
 enum class VMFunctionId;
 
 extern const VMFunctionData& GetVMFunction(VMFunctionId id);
 extern const VMFunctionData& GetVMFunction(TailCallVMFunctionId id);
 
 }  // namespace jit
 }  // namespace js