Bug 1709216: Optimize polymorphic Object.is in CacheIR r=jandem
authorIain Ireland <iireland@mozilla.com>
Wed, 12 May 2021 01:54:38 +0000
changeset 579435 d10063d1349d834c15ccdc11bdac3ba0363ab083
parent 579434 f99981eb9e49a1da41854ce510165cc573ec80f6
child 579436 31d61c8494d04d2d76aa2087b6e1279b26f4a2f1
push id38456
push usernbeleuzu@mozilla.com
push dateWed, 12 May 2021 09:42:13 +0000
treeherdermozilla-central@02aeefba2bce [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1709216
milestone90.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 1709216: Optimize polymorphic Object.is in CacheIR r=jandem Differential Revision: https://phabricator.services.mozilla.com/D114257
js/src/jit-test/tests/basic/object-is-polymorphic.js
js/src/jit/BaselineCacheIRCompiler.cpp
js/src/jit/CacheIR.cpp
js/src/jit/CacheIROps.yaml
js/src/jit/IonCacheIRCompiler.cpp
js/src/jit/VMFunctionList-inl.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/object-is-polymorphic.js
@@ -0,0 +1,20 @@
+function foo(arr) {
+  var result = 0;
+  for (var i = 0; i < arr.length; i++) {
+    for (var j = 0; j < arr.length; j++) {
+      if (Object.is(arr[i], arr[j])) {
+        result++;
+      }
+    }
+  }
+  return result;
+}
+
+var input = [1,"one",{x:1},
+	     1,"one",{x:1}];
+
+var sum = 0;
+for (var i = 0; i < 100; i++) {
+  sum += foo(input);
+}
+assertEq(sum, 1000);
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -749,16 +749,59 @@ bool BaselineCacheIRCompiler::emitCompar
     stubFrame.leave(masm);
     masm.mov(ReturnReg, scratch);
   }
   masm.bind(&done);
   masm.tagValue(JSVAL_TYPE_BOOLEAN, scratch, output.valueReg());
   return true;
 }
 
+bool BaselineCacheIRCompiler::emitSameValueResult(ValOperandId lhsId,
+                                                  ValOperandId rhsId) {
+  JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
+
+  AutoOutputRegister output(*this);
+  AutoScratchRegister scratch(allocator, masm);
+  ValueOperand lhs = allocator.useValueRegister(masm, lhsId);
+  ValueOperand rhs = allocator.useValueRegister(masm, rhsId);
+
+  allocator.discardStack(masm);
+
+  Label done;
+  Label call;
+
+  // Check to see if the values have identical bits.
+  // This is correct for SameValue because SameValue(NaN,NaN) is true,
+  // and SameValue(0,-0) is false.
+  masm.branch64(Assembler::NotEqual, lhs.toRegister64(), rhs.toRegister64(),
+                &call);
+  masm.moveValue(BooleanValue(true), output.valueReg());
+  masm.jump(&done);
+
+  {
+    masm.bind(&call);
+
+    AutoStubFrame stubFrame(*this);
+    stubFrame.enter(masm, scratch);
+
+    masm.pushValue(lhs);
+    masm.pushValue(rhs);
+
+    using Fn = bool (*)(JSContext*, HandleValue, HandleValue, bool*);
+    callVM<Fn, SameValue>(masm);
+
+    stubFrame.leave(masm);
+    masm.mov(ReturnReg, scratch);
+    masm.tagValue(JSVAL_TYPE_BOOLEAN, scratch, output.valueReg());
+  }
+
+  masm.bind(&done);
+  return true;
+}
+
 bool BaselineCacheIRCompiler::emitStoreSlotShared(bool isFixed,
                                                   ObjOperandId objId,
                                                   uint32_t offsetOffset,
                                                   ValOperandId rhsId) {
   Register obj = allocator.useRegister(masm, objId);
   ValueOperand val = allocator.useValueRegister(masm, rhsId);
 
   AutoScratchRegister scratch1(allocator, masm);
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -7546,34 +7546,32 @@ AttachDecision CallIRGenerator::tryAttac
 }
 
 AttachDecision CallIRGenerator::tryAttachObjectIs(HandleFunction callee) {
   // Need two arguments.
   if (argc_ != 2) {
     return AttachDecision::NoAction;
   }
 
-  if (!isFirstStub_) {
-    // Attach only once to prevent slowdowns for polymorphic calls.
-    return AttachDecision::NoAction;
-  }
-
   // Initialize the input operand.
   Int32OperandId argcId(writer.setInputOperandId(0));
 
   // Guard callee is the `is` native function.
   emitNativeCalleeGuard(callee);
 
   ValOperandId lhsId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
   ValOperandId rhsId = writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
 
   HandleValue lhs = args_[0];
   HandleValue rhs = args_[1];
 
-  if (lhs.isNumber() && rhs.isNumber() && !(lhs.isInt32() && rhs.isInt32())) {
+  if (!isFirstStub_) {
+    writer.sameValueResult(lhsId, rhsId);
+  } else if (lhs.isNumber() && rhs.isNumber() &&
+             !(lhs.isInt32() && rhs.isInt32())) {
     NumberOperandId lhsNumId = writer.guardIsNumber(lhsId);
     NumberOperandId rhsNumId = writer.guardIsNumber(rhsId);
     writer.compareDoubleSameValueResult(lhsNumId, rhsNumId);
   } else if (!SameType(lhs, rhs)) {
     // Compare tags for strictly different types.
     ValueTagOperandId lhsTypeId = writer.loadValueTag(lhsId);
     ValueTagOperandId rhsTypeId = writer.loadValueTag(rhsId);
     writer.guardTagNotEqual(lhsTypeId, rhsTypeId);
--- a/js/src/jit/CacheIROps.yaml
+++ b/js/src/jit/CacheIROps.yaml
@@ -2561,16 +2561,24 @@
 - name: CompareDoubleSameValueResult
   shared: true
   transpile: true
   cost_estimate: 3
   args:
     lhs: NumberId
     rhs: NumberId
 
+- name: SameValueResult
+  shared: false
+  transpile: false
+  cost_estimate: 4
+  args:
+    lhs: ValId
+    rhs: ValId
+
 - name: IndirectTruncateInt32Result
   shared: true
   transpile: true
   cost_estimate: 1
   args:
     val: Int32Id
 
 - name: BigIntAsIntNResult
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -2009,16 +2009,21 @@ bool IonCacheIRCompiler::emitReflectGetP
   MOZ_CRASH("Call ICs not used in ion");
 }
 
 bool IonCacheIRCompiler::emitHasClassResult(ObjOperandId objId,
                                             uint32_t claspOffset) {
   MOZ_CRASH("Call ICs not used in ion");
 }
 
+bool IonCacheIRCompiler::emitSameValueResult(ValOperandId lhs,
+                                             ValOperandId rhs) {
+  MOZ_CRASH("Call ICs not used in ion");
+}
+
 bool IonCacheIRCompiler::emitNewArrayObjectResult(uint32_t arrayLength,
                                                   uint32_t shapeOffset) {
   MOZ_CRASH("NewArray ICs not used in ion");
 }
 
 bool IonCacheIRCompiler::emitNewPlainObjectResult(uint32_t numFixedSlots,
                                                   uint32_t numDynamicSlots,
                                                   gc::AllocKind allocKind,
--- a/js/src/jit/VMFunctionList-inl.h
+++ b/js/src/jit/VMFunctionList-inl.h
@@ -219,16 +219,17 @@ namespace jit {
   _(ProxySetPropertyByValue, js::ProxySetPropertyByValue)                      \
   _(PushClassBodyEnv, js::jit::PushClassBodyEnv)                               \
   _(PushLexicalEnv, js::jit::PushLexicalEnv)                                   \
   _(PushVarEnv, js::jit::PushVarEnv)                                           \
   _(RecreateLexicalEnv, js::jit::RecreateLexicalEnv)                           \
   _(RegExpMatcherRaw, js::RegExpMatcherRaw)                                    \
   _(RegExpSearcherRaw, js::RegExpSearcherRaw)                                  \
   _(RegExpTesterRaw, js::RegExpTesterRaw)                                      \
+  _(SameValue, js::SameValue)                                                  \
   _(SetArrayLength, js::jit::SetArrayLength)                                   \
   _(SetDenseElement, js::jit::SetDenseElement)                                 \
   _(SetFunctionName, js::SetFunctionName)                                      \
   _(SetIntrinsicOperation, js::SetIntrinsicOperation)                          \
   _(SetObjectElementWithReceiver, js::SetObjectElementWithReceiver)            \
   _(SetPropertySuper, js::SetPropertySuper)                                    \
   _(StartDynamicModuleImport, js::StartDynamicModuleImport)                    \
   _(StringBigIntGreaterThanOrEqual,                                            \