From 41946bdc30af5b9cda878e9859e26a1161529dc8 Mon Sep 17 00:00:00 2001 From: Tom Schuster Date: Thu, 28 May 2020 09:35:18 +0000 Subject: [PATCH] Bug 1641297 - Optimize Array.isArray in CacheIR and Warp. r=iain Differential Revision: https://phabricator.services.mozilla.com/D77125 --- js/src/jit/BaselineCacheIRCompiler.cpp | 50 ++++++++++++++++++++++++++ js/src/jit/CacheIR.cpp | 27 ++++++++++++++ js/src/jit/CacheIR.h | 1 + js/src/jit/CacheIROps.yaml | 6 ++++ js/src/jit/IonCacheIRCompiler.cpp | 4 +++ js/src/jit/WarpCacheIRTranspiler.cpp | 10 ++++++ 6 files changed, 98 insertions(+) diff --git a/js/src/jit/BaselineCacheIRCompiler.cpp b/js/src/jit/BaselineCacheIRCompiler.cpp index c5d16cffece3..e17c5f0bd06b 100644 --- a/js/src/jit/BaselineCacheIRCompiler.cpp +++ b/js/src/jit/BaselineCacheIRCompiler.cpp @@ -1437,6 +1437,56 @@ bool BaselineCacheIRCompiler::emitArrayPush(ObjOperandId objId, return true; } +bool BaselineCacheIRCompiler::emitIsArrayResult(ValOperandId inputId) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + + AutoOutputRegister output(*this); + AutoScratchRegister scratch1(allocator, masm); + AutoScratchRegisterMaybeOutput scratch2(allocator, masm, output); + + ValueOperand val = allocator.useValueRegister(masm, inputId); + + allocator.discardStack(masm); + + Label isNotArray; + // Primitives are never Arrays. + masm.branchTestObject(Assembler::NotEqual, val, &isNotArray); + + masm.unboxObject(val, scratch1); + + Label isArray; + masm.branchTestObjClass(Assembler::Equal, scratch1, &ArrayObject::class_, + scratch2, scratch1, &isArray); + + // isArray can also return true for Proxy wrapped Arrays. + masm.branchTestObjectIsProxy(false, scratch1, scratch2, &isNotArray); + Label done; + { + AutoStubFrame stubFrame(*this); + stubFrame.enter(masm, scratch2); + + masm.Push(scratch1); + + using Fn = bool (*)(JSContext*, HandleObject, bool*); + callVM(masm); + + stubFrame.leave(masm); + + masm.tagValue(JSVAL_TYPE_BOOLEAN, ReturnReg, output.valueReg()); + masm.jump(&done); + } + + masm.bind(&isNotArray); + masm.moveValue(BooleanValue(false), output.valueReg()); + masm.jump(&done); + + masm.bind(&isArray); + masm.moveValue(BooleanValue(true), output.valueReg()); + + masm.bind(&done); + return true; +} + bool BaselineCacheIRCompiler::emitCallNativeSetter(ObjOperandId objId, uint32_t setterOffset, ValOperandId rhsId) { diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index ba23c07a83f7..036fab66edc8 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -5000,6 +5000,31 @@ AttachDecision CallIRGenerator::tryAttachArrayJoin(HandleFunction callee) { return AttachDecision::Attach; } +AttachDecision CallIRGenerator::tryAttachArrayIsArray(HandleFunction callee) { + // Need a single argument. + if (argc_ != 1) { + return AttachDecision::NoAction; + } + + // Initialize the input operand. + Int32OperandId argcId(writer.setInputOperandId(0)); + + // Guard callee is the 'isArray' intrinsic native function. + emitNativeCalleeGuard(callee); + + // Check if the argument is an Array and return result. + ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_); + writer.isArrayResult(argId); + + // This stub does not need to be monitored, because it always + // returns a boolean. + writer.returnFromIC(); + cacheIRStubKind_ = BaselineCacheIRStubKind::Regular; + + trackAttached("ArrayIsArray"); + return AttachDecision::Attach; +} + AttachDecision CallIRGenerator::tryAttachIsSuspendedGenerator( HandleFunction callee) { // The IsSuspendedGenerator intrinsic is only called in @@ -5555,6 +5580,8 @@ AttachDecision CallIRGenerator::tryAttachInlinableNative( return tryAttachArrayPush(callee); case InlinableNative::ArrayJoin: return tryAttachArrayJoin(callee); + case InlinableNative::ArrayIsArray: + return tryAttachArrayIsArray(callee); // Intrinsics. case InlinableNative::IntrinsicIsSuspendedGenerator: diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h index 2fdddf377769..d6e91b09dee4 100644 --- a/js/src/jit/CacheIR.h +++ b/js/src/jit/CacheIR.h @@ -1520,6 +1520,7 @@ class MOZ_RAII CallIRGenerator : public IRGenerator { AttachDecision tryAttachArrayPush(HandleFunction callee); AttachDecision tryAttachArrayJoin(HandleFunction callee); + AttachDecision tryAttachArrayIsArray(HandleFunction callee); AttachDecision tryAttachIsSuspendedGenerator(HandleFunction callee); AttachDecision tryAttachToString(HandleFunction callee); AttachDecision tryAttachToObject(HandleFunction callee); diff --git a/js/src/jit/CacheIROps.yaml b/js/src/jit/CacheIROps.yaml index 329e68d22290..57602f3ee787 100644 --- a/js/src/jit/CacheIROps.yaml +++ b/js/src/jit/CacheIROps.yaml @@ -665,6 +665,12 @@ args: obj: ObjId +- name: IsArrayResult + shared: false + transpile: true + args: + input: ValId + - name: IsObjectResult shared: true transpile: true diff --git a/js/src/jit/IonCacheIRCompiler.cpp b/js/src/jit/IonCacheIRCompiler.cpp index 0c46cfc4016b..e3551f6b2889 100644 --- a/js/src/jit/IonCacheIRCompiler.cpp +++ b/js/src/jit/IonCacheIRCompiler.cpp @@ -2418,3 +2418,7 @@ bool IonCacheIRCompiler::emitGuardFunApply(Int32OperandId argcId, CallFlags flags) { MOZ_CRASH("Call ICs not used in ion"); } + +bool IonCacheIRCompiler::emitIsArrayResult(ValOperandId inputId) { + MOZ_CRASH("Call ICs not used in ion"); +} diff --git a/js/src/jit/WarpCacheIRTranspiler.cpp b/js/src/jit/WarpCacheIRTranspiler.cpp index 86a612a4277f..0471a385fb2c 100644 --- a/js/src/jit/WarpCacheIRTranspiler.cpp +++ b/js/src/jit/WarpCacheIRTranspiler.cpp @@ -1100,6 +1100,16 @@ bool WarpCacheIRTranspiler::emitArrayPush(ObjOperandId objId, return resumeAfter(ins); } +bool WarpCacheIRTranspiler::emitIsArrayResult(ValOperandId inputId) { + MDefinition* value = getOperand(inputId); + + auto* isArray = MIsArray::New(alloc(), value); + addEffectful(isArray); + pushResult(isArray); + + return resumeAfter(isArray); +} + bool WarpCacheIRTranspiler::emitIsObjectResult(ValOperandId inputId) { MDefinition* value = getOperand(inputId);