From 480171c17d734dc81b0f1bc884f97607f7b52da2 Mon Sep 17 00:00:00 2001 From: Matthew Gaudet Date: Thu, 29 Mar 2018 09:09:58 -0400 Subject: [PATCH] Bug 1438727: [Part 1] Implement a subset of JSOP_ADD in CacheIR r=jandem This patch adds both Ion and Baseline support for ADD when the arguments are doubles or int32. This is implmented as a strangler via the SharedIC, this falls back to the shared IC if there's no attachment in CacheIR. This should allow preservation of performance throughout. To provide clobber safety to the float registers, this patch uses fixed temporaries on LBinaryCache. --HG-- extra : rebase_source : 35f32e5d0f99bb93845c41aadaf94693644192bd --- js/src/jit/BaselineCacheIRCompiler.cpp | 1 + js/src/jit/BaselineIC.cpp | 56 +++++++++++++++ js/src/jit/CacheIR.cpp | 92 +++++++++++++++++++++++++ js/src/jit/CacheIR.h | 33 ++++++++- js/src/jit/CacheIRCompiler.cpp | 89 +++++++++++++++++++++++- js/src/jit/CacheIRCompiler.h | 6 ++ js/src/jit/CacheIRSpewer.cpp | 11 +++ js/src/jit/CacheIRSpewer.h | 5 ++ js/src/jit/CodeGenerator.cpp | 34 +++++++++ js/src/jit/IonBuilder.cpp | 6 ++ js/src/jit/IonCacheIRCompiler.cpp | 14 ++++ js/src/jit/IonIC.cpp | 44 ++++++++++++ js/src/jit/IonIC.h | 33 +++++++++ js/src/jit/JitOptions.cpp | 3 + js/src/jit/JitOptions.h | 1 + js/src/jit/Lowering.cpp | 18 +++++ js/src/jit/MIR.h | 16 +++++ js/src/jit/SharedIC.cpp | 14 ++++ js/src/jit/shared/LIR-shared.h | 26 +++++++ js/src/jit/shared/Lowering-shared-inl.h | 8 +++ js/src/jit/shared/Lowering-shared.h | 5 +- js/src/shell/js.cpp | 7 +- 22 files changed, 517 insertions(+), 5 deletions(-) diff --git a/js/src/jit/BaselineCacheIRCompiler.cpp b/js/src/jit/BaselineCacheIRCompiler.cpp index 5b13c443631e..ffd691419bbc 100644 --- a/js/src/jit/BaselineCacheIRCompiler.cpp +++ b/js/src/jit/BaselineCacheIRCompiler.cpp @@ -2113,6 +2113,7 @@ BaselineCacheIRCompiler::init(CacheKind kind) case CacheKind::In: case CacheKind::HasOwn: case CacheKind::InstanceOf: + case CacheKind::BinaryArith: MOZ_ASSERT(numInputs == 2); allocator.initInputLocation(0, R0); allocator.initInputLocation(1, R1); diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 610a4ca1cb05..abeb433f02a4 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -4933,5 +4933,61 @@ ICUnaryArith_Fallback::Compiler::generateStubCode(MacroAssembler& masm) return tailCallVM(DoUnaryArithFallbackInfo, masm); } +// +// BinaryArith_Fallback +// +// This currently is fused with the SharedIC DoBinaryArithFallback, however, +// as we deprecate the SharedIC, this will be able to eventually take over. +// +// At that point the stub argument will go away, and instead be generated here. +bool +DoCacheIRBinaryArithFallback(JSContext* cx, BaselineFrame* frame, ICBinaryArith_Fallback* stub_, + HandleValue lhs, HandleValue rhs, MutableHandleValue ret, + DebugModeOSRVolatileStub& stub) +{ + RootedScript script(cx, frame->script()); + jsbytecode* pc = stub->icEntry()->pc(script); + JSOp op = JSOp(*pc); + + // Ensure we're only generating for an enabled opcode. + switch(op) { + case JSOP_ADD: + break; + default: + return false; // Fallback to shared IC. + } + + FallbackICSpew(cx, stub, "CacheIRBinaryArith(%s,%d,%d)", CodeName[op], + int(lhs.isDouble() ? JSVAL_TYPE_DOUBLE : lhs.extractNonDoubleType()), + int(rhs.isDouble() ? JSVAL_TYPE_DOUBLE : rhs.extractNonDoubleType())); + + // Check if debug mode toggling made the stub invalid. + if (stub.invalid()) + return true; + + if (ret.isDouble()) + stub->setSawDoubleResult(); + + if (stub->state().maybeTransition()) + stub->discardStubs(cx); + + if (stub->state().canAttachStub()) { + BinaryArithIRGenerator gen(cx, script, pc, stub->state().mode(), + op, lhs, rhs, ret); + if (gen.tryAttachStub()) { + bool attached = false; + ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), + BaselineCacheIRStubKind::Regular, + ICStubEngine::Baseline, script, stub, &attached); + if (newStub) + JitSpew(JitSpew_BaselineIC, " Attached BinaryArith CacheIR stub for %s", CodeName[op]); + } else { + return false; // Fallback to shared IC. + } + } + return true; +} + + } // namespace jit } // namespace js diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index 20e3f88cb375..ace03086d3e9 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -5078,3 +5078,95 @@ UnaryArithIRGenerator::tryAttachNumber() writer.returnFromIC(); return true; } + +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) +{ } + +void +BinaryArithIRGenerator::trackAttached(const char* name) +{ +#ifdef JS_CACHEIR_SPEW + if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) { + sp.opcodeProperty("op", op_); + sp.valueProperty("rhs", rhs_); + sp.valueProperty("lhs", lhs_); + } +#endif +} + +bool +BinaryArithIRGenerator::tryAttachStub() +{ + + if (tryAttachInt32()) + return true; + if (tryAttachDouble()) + return true; + + trackAttached(IRGenerator::NotAttached); + return false; +} + +bool +BinaryArithIRGenerator::tryAttachDouble() +{ + if (op_ != JSOP_ADD) + return false; + + if (!lhs_.isDouble() || !rhs_.isDouble() || !res_.isDouble()) + return false; + + if (!cx_->runtime()->jitSupportsFloatingPoint) + return false; + + ValOperandId lhsId(writer.setInputOperandId(0)); + ValOperandId rhsId(writer.setInputOperandId(1)); + + writer.guardIsNumber(lhsId); + writer.guardIsNumber(rhsId); + + switch (op_) { + case JSOP_ADD: + writer.doubleAddResult(lhsId, rhsId); + trackAttached("BinaryArith.Double.Add"); + break; + default: + MOZ_CRASH("Unhandled Op"); + } + writer.returnFromIC(); + return true; +} + +bool +BinaryArithIRGenerator::tryAttachInt32() +{ + if (op_ != JSOP_ADD) + return false; + + if (!lhs_.isInt32() || !rhs_.isInt32()) + return false; + + ValOperandId lhsId(writer.setInputOperandId(0)); + ValOperandId rhsId(writer.setInputOperandId(1)); + + Int32OperandId lhsIntId = writer.guardIsInt32(lhsId); + Int32OperandId rhsIntId = writer.guardIsInt32(rhsId); + + switch (op_) { + case JSOP_ADD: + writer.int32AddResult(lhsIntId, rhsIntId); + trackAttached("BinaryArith.Int32.Add"); + break; + default: + MOZ_CRASH("Unhandled op in tryAttachInt32"); + } + + writer.returnFromIC(); + return true; +} diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h index 995c6ac3a0c7..030a27f484dd 100644 --- a/js/src/jit/CacheIR.h +++ b/js/src/jit/CacheIR.h @@ -166,7 +166,8 @@ class TypedOperandId : public OperandId _(Compare) \ _(ToBool) \ _(Call) \ - _(UnaryArith) + _(UnaryArith) \ + _(BinaryArith) enum class CacheKind : uint8_t { @@ -290,6 +291,8 @@ extern const char* CacheKindNames[]; _(LoadStringResult) \ _(LoadInstanceOfObjectResult) \ _(LoadTypeOfObjectResult) \ + _(DoubleAddResult) \ + _(Int32AddResult) \ _(Int32NotResult) \ _(Int32NegationResult) \ _(DoubleNegationResult) \ @@ -989,6 +992,14 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter buffer_.writeByte(uint32_t(hasOwn)); } + void doubleAddResult(ValOperandId lhsId, ValOperandId rhsId) { + writeOpWithOperandId(CacheOp::DoubleAddResult, lhsId); + writeOperandId(rhsId); + } + void int32AddResult(Int32OperandId lhs, Int32OperandId rhs) { + writeOpWithOperandId(CacheOp::Int32AddResult, lhs); + writeOperandId(rhs); + } void int32NotResult(Int32OperandId id) { writeOpWithOperandId(CacheOp::Int32NotResult, id); } @@ -1777,6 +1788,26 @@ class MOZ_RAII UnaryArithIRGenerator : public IRGenerator bool tryAttachStub(); }; +class MOZ_RAII BinaryArithIRGenerator : public IRGenerator +{ + JSOp op_; + HandleValue lhs_; + HandleValue rhs_; + HandleValue res_; + + void trackAttached(const char* name); + + bool tryAttachInt32(); + bool tryAttachDouble(); + + public: + BinaryArithIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState::Mode, + JSOp op, HandleValue lhs, HandleValue rhs, HandleValue res); + + bool tryAttachStub(); + +}; + } // namespace jit } // namespace js diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp index b9a6ed56b273..9ceb53a119dd 100644 --- a/js/src/jit/CacheIRCompiler.cpp +++ b/js/src/jit/CacheIRCompiler.cpp @@ -88,6 +88,51 @@ CacheRegisterAllocator::useValueRegister(MacroAssembler& masm, ValOperandId op) MOZ_CRASH(); } +// Load a value operand directly into a float register. Caller must have +// guarded isNumber on the provided val. +void +CacheRegisterAllocator::loadDouble(MacroAssembler& masm, ValOperandId op, FloatRegister dest) +{ + OperandLocation& loc = operandLocations_[op.id()]; + + Label failure, done; + switch (loc.kind()) { + case OperandLocation::ValueReg: { + masm.ensureDouble(loc.valueReg(), dest, &failure); + break; + } + + case OperandLocation::ValueStack: { + masm.ensureDouble(valueAddress(masm, &loc), dest, &failure); + break; + } + + case OperandLocation::BaselineFrame: { + Address addr = addressOf(masm, loc.baselineFrameSlot()); + masm.ensureDouble(addr, dest, &failure); + break; + } + + case OperandLocation::DoubleReg: { + masm.moveDouble(loc.doubleReg(), dest); + loc.setDoubleReg(dest); + return; + } + + case OperandLocation::Constant: + case OperandLocation::PayloadStack: + case OperandLocation::PayloadReg: + case OperandLocation::Uninitialized: + MOZ_CRASH("Unhandled operand type in loadDouble"); + return; + } + masm.jump(&done); + masm.bind(&failure); + masm.assumeUnreachable("Missing guard allowed non-number to hit loadDouble"); + masm.bind(&done); +} + + ValueOperand CacheRegisterAllocator::useFixedValueRegister(MacroAssembler& masm, ValOperandId valId, ValueOperand reg) @@ -668,6 +713,13 @@ CacheRegisterAllocator::popPayload(MacroAssembler& masm, OperandLocation* loc, R loc->setPayloadReg(dest, loc->payloadType()); } +Address +CacheRegisterAllocator::valueAddress(MacroAssembler& masm, OperandLocation* loc) +{ + MOZ_ASSERT(loc >= operandLocations_.begin() && loc < operandLocations_.end()); + return Address(masm.getStackPointer(), stackPushed_ - loc->valueStack()); +} + void CacheRegisterAllocator::popValue(MacroAssembler& masm, OperandLocation* loc, ValueOperand dest) { @@ -777,9 +829,11 @@ CacheRegisterAllocator::restoreInputState(MacroAssembler& masm, bool shouldDisca case OperandLocation::ValueStack: popValue(masm, &cur, dest.valueReg()); continue; + case OperandLocation::DoubleReg: + masm.boxDouble(cur.doubleReg(), dest.valueReg(), cur.doubleReg()); + continue; case OperandLocation::Constant: case OperandLocation::BaselineFrame: - case OperandLocation::DoubleReg: case OperandLocation::Uninitialized: break; } @@ -1867,6 +1921,39 @@ CacheIRCompiler::emitLoadInt32ArrayLengthResult() EmitStoreResult(masm, scratch, JSVAL_TYPE_INT32, output); return true; } +bool +CacheIRCompiler::emitDoubleAddResult() +{ + AutoOutputRegister output(*this); + + // Float register must be preserved. The BinaryArith ICs use + // the fact that baseline has them available, as well as fixed temps on + // LBinaryCache. + allocator.loadDouble(masm, reader.valOperandId(), FloatReg0); + allocator.loadDouble(masm, reader.valOperandId(), FloatReg1); + + masm.addDouble(FloatReg1, FloatReg0); + masm.boxDouble(FloatReg0, output.valueReg(), FloatReg0); + + return true; +} + +bool +CacheIRCompiler::emitInt32AddResult() +{ + AutoOutputRegister output(*this); + Register lhs = allocator.useRegister(masm, reader.int32OperandId()); + Register rhs = allocator.useRegister(masm, reader.int32OperandId()); + + FailurePath* failure; + if (!addFailurePath(&failure)) + return false; + + masm.branchAdd32(Assembler::Overflow, lhs, rhs, failure->label()); + EmitStoreResult(masm, rhs, JSVAL_TYPE_INT32, output); + + return true; +} bool CacheIRCompiler::emitInt32NegationResult() diff --git a/js/src/jit/CacheIRCompiler.h b/js/src/jit/CacheIRCompiler.h index dbf19f1b2853..18a816fc6ad5 100644 --- a/js/src/jit/CacheIRCompiler.h +++ b/js/src/jit/CacheIRCompiler.h @@ -51,6 +51,8 @@ namespace jit { _(LoadUndefinedResult) \ _(LoadBooleanResult) \ _(LoadInt32ArrayLengthResult) \ + _(Int32AddResult) \ + _(DoubleAddResult) \ _(Int32NegationResult) \ _(Int32NotResult) \ _(DoubleNegationResult) \ @@ -329,6 +331,7 @@ class MOZ_RAII CacheRegisterAllocator void popPayload(MacroAssembler& masm, OperandLocation* loc, Register dest); void popValue(MacroAssembler& masm, OperandLocation* loc, ValueOperand dest); + Address valueAddress(MacroAssembler& masm, OperandLocation* loc); #ifdef DEBUG void assertValidState() const; @@ -469,6 +472,9 @@ class MOZ_RAII CacheRegisterAllocator Register defineRegister(MacroAssembler& masm, TypedOperandId typedId); ValueOperand defineValueRegister(MacroAssembler& masm, ValOperandId val); + // Loads (and unboxes) a value into a float register (caller guarded) + void loadDouble(MacroAssembler&, ValOperandId, FloatRegister); + // Returns |val|'s JSValueType or JSVAL_TYPE_UNKNOWN. JSValueType knownType(ValOperandId val) const; diff --git a/js/src/jit/CacheIRSpewer.cpp b/js/src/jit/CacheIRSpewer.cpp index 79a8f8fbca10..62ecdd4d797f 100644 --- a/js/src/jit/CacheIRSpewer.cpp +++ b/js/src/jit/CacheIRSpewer.cpp @@ -156,6 +156,17 @@ CacheIRSpewer::valueProperty(const char* name, const Value& v) j.endObject(); } +void +CacheIRSpewer::opcodeProperty(const char* name, const JSOp op) +{ + MOZ_ASSERT(enabled()); + JSONPrinter& j = json.ref(); + + j.beginStringProperty(name); + output.put(CodeName[op]); + j.endStringProperty(); +} + void CacheIRSpewer::attached(const char* name) { diff --git a/js/src/jit/CacheIRSpewer.h b/js/src/jit/CacheIRSpewer.h index fc0adbdefb9a..7b0fffa17e8e 100644 --- a/js/src/jit/CacheIRSpewer.h +++ b/js/src/jit/CacheIRSpewer.h @@ -37,6 +37,7 @@ class CacheIRSpewer void beginCache(const IRGenerator& generator); void valueProperty(const char* name, const Value& v); + void opcodeProperty(const char* name, const JSOp op); void attached(const char* name); void endCache(); @@ -74,6 +75,10 @@ class CacheIRSpewer sp_.valueProperty(name, v); } + void opcodeProperty(const char* name, const JSOp op) const { + sp_.opcodeProperty(name, op); + } + explicit operator bool() const { return sp_.enabled(); } diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 491097ce4138..6324178f2ca9 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -188,6 +188,11 @@ typedef bool (*IonUnaryArithICFn)(JSContext* cx, HandleScript outerScript, IonUn static const VMFunction IonUnaryArithICInfo = FunctionInfo(IonUnaryArithIC::update, "IonUnaryArithIC::update"); +typedef bool (*IonBinaryArithICFn)(JSContext* cx, HandleScript outerScript, IonBinaryArithIC* stub, + HandleValue lhs, HandleValue rhs, MutableHandleValue res); +static const VMFunction IonBinaryArithICInfo = + FunctionInfo(IonBinaryArithIC::update, "IonBinaryArithIC::update"); + void CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool) { @@ -380,6 +385,23 @@ CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool) masm.jump(ool->rejoin()); return; } + case CacheKind::BinaryArith: { + IonBinaryArithIC* binaryArithIC = ic->asBinaryArithIC(); + + saveLive(lir); + + pushArg(binaryArithIC->rhs()); + pushArg(binaryArithIC->lhs()); + icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); + pushArg(ImmGCPtr(gen->info().script())); + callVM(IonBinaryArithICInfo, lir); + + StoreValueTo(binaryArithIC->output()).generate(this); + restoreLiveIgnore(lir, StoreValueTo(binaryArithIC->output()).clobbered()); + + masm.jump(ool->rejoin()); + return; + } case CacheKind::Call: case CacheKind::Compare: case CacheKind::TypeOf: @@ -2760,6 +2782,18 @@ CodeGenerator::emitSharedStub(ICStub::Kind kind, LInstruction* lir) markSafepointAt(callOffset, lir); } +void +CodeGenerator::visitBinaryCache(LBinaryCache* lir) +{ + LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); + TypedOrValueRegister lhs = TypedOrValueRegister(ToValue(lir, LBinaryCache::LhsInput)); + TypedOrValueRegister rhs = TypedOrValueRegister(ToValue(lir, LBinaryCache::RhsInput)); + ValueOperand output = ToOutValue(lir); + + IonBinaryArithIC ic(liveRegs, lhs, rhs, output); + addIC(lir, allocateIC(ic)); +} + void CodeGenerator::visitBinarySharedStub(LBinarySharedStub* lir) { diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index cf952a5c0ae4..cb51dfe84e09 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -3570,6 +3570,12 @@ IonBuilder::arithTrySharedStub(bool* emitted, JSOp op, stub = MUnaryCache::New(alloc(), right); break; case JSOP_ADD: + // If not disabled, prefer the cache IR stub. + if (!JitOptions.disableCacheIRBinaryArith) { + stub = MBinaryCache::New(alloc(), left, right); + break; + } + MOZ_FALLTHROUGH; case JSOP_SUB: case JSOP_MUL: case JSOP_DIV: diff --git a/js/src/jit/IonCacheIRCompiler.cpp b/js/src/jit/IonCacheIRCompiler.cpp index da10b7d689be..72045711914b 100644 --- a/js/src/jit/IonCacheIRCompiler.cpp +++ b/js/src/jit/IonCacheIRCompiler.cpp @@ -523,6 +523,20 @@ IonCacheIRCompiler::init() allocator.initInputLocation(0, ic->input()); break; } + case CacheKind::BinaryArith: { + IonBinaryArithIC* ic = ic_->asBinaryArithIC(); + ValueOperand output = ic->output(); + + available.add(output); + + liveRegs_.emplace(ic->liveRegs()); + outputUnchecked_.emplace(TypedOrValueRegister(output)); + + MOZ_ASSERT(numInputs == 2); + allocator.initInputLocation(0, ic->lhs()); + allocator.initInputLocation(1, ic->rhs()); + break; + } case CacheKind::Call: case CacheKind::Compare: case CacheKind::TypeOf: diff --git a/js/src/jit/IonIC.cpp b/js/src/jit/IonIC.cpp index 7b62f91b7d4d..3a2ace5a7040 100644 --- a/js/src/jit/IonIC.cpp +++ b/js/src/jit/IonIC.cpp @@ -58,6 +58,8 @@ IonIC::scratchRegisterForEntryJump() return asInstanceOfIC()->output(); case CacheKind::UnaryArith: return asUnaryArithIC()->output().scratchReg(); + case CacheKind::BinaryArith: + return asBinaryArithIC()->output().scratchReg(); case CacheKind::Call: case CacheKind::Compare: case CacheKind::TypeOf: @@ -548,6 +550,48 @@ IonUnaryArithIC::update(JSContext* cx, HandleScript outerScript, IonUnaryArithIC return true; } +/* static */ bool +IonBinaryArithIC::update(JSContext* cx, HandleScript outerScript, IonBinaryArithIC* ic, + HandleValue lhs, HandleValue rhs, MutableHandleValue ret) +{ + IonScript* ionScript = outerScript->ionScript(); + RootedScript script(cx, ic->script()); + jsbytecode* pc = ic->pc(); + JSOp op = JSOp(*pc); + + // Don't pass lhs/rhs directly, we need the original values when + // generating stubs. + RootedValue lhsCopy(cx, lhs); + RootedValue rhsCopy(cx, rhs); + + // Perform the compare operation. + switch(op) { + case JSOP_ADD: + // Do an add. + if (!AddValues(cx, &lhsCopy, &rhsCopy, ret)) + return false; + break; + default: + MOZ_CRASH("Unhandled binary arith op"); + } + + if (ic->state().maybeTransition()) + ic->discardStubs(cx->zone()); + + if (ic->state().canAttachStub()) { + bool attached = false; + BinaryArithIRGenerator gen(cx, script, pc, ic->state().mode(), + op, lhs, rhs, ret); + if (gen.tryAttachStub()) { + ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached); + + if (!attached) + ic->state().trackNotAttached(); + } + } + return true; +} + uint8_t* IonICStub::stubDataStart() { diff --git a/js/src/jit/IonIC.h b/js/src/jit/IonIC.h index fc02c593f632..ec7548fa2637 100644 --- a/js/src/jit/IonIC.h +++ b/js/src/jit/IonIC.h @@ -66,6 +66,7 @@ class IonHasOwnIC; class IonInIC; class IonInstanceOfIC; class IonUnaryArithIC; +class IonBinaryArithIC; class IonIC { @@ -177,6 +178,10 @@ class IonIC MOZ_ASSERT(kind_ == CacheKind::UnaryArith); return (IonUnaryArithIC*)this; } + IonBinaryArithIC* asBinaryArithIC() { + MOZ_ASSERT(kind_ == CacheKind::BinaryArith); + return (IonBinaryArithIC*)this; + } void updateBaseAddress(JitCode* code); @@ -504,6 +509,34 @@ class IonUnaryArithIC : public IonIC HandleValue val, MutableHandleValue res); }; +class IonBinaryArithIC : public IonIC +{ + LiveRegisterSet liveRegs_; + + TypedOrValueRegister lhs_; + TypedOrValueRegister rhs_; + ValueOperand output_; + + public: + + IonBinaryArithIC(LiveRegisterSet liveRegs, TypedOrValueRegister lhs, TypedOrValueRegister rhs, ValueOperand output) + : IonIC(CacheKind::BinaryArith), + liveRegs_(liveRegs), + lhs_(lhs), + rhs_(rhs), + output_(output) + { } + + LiveRegisterSet liveRegs() const { return liveRegs_; } + TypedOrValueRegister lhs() const { return lhs_; } + TypedOrValueRegister rhs() const { return rhs_; } + ValueOperand output() const { return output_; } + + static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonBinaryArithIC* stub, + HandleValue lhs, HandleValue rhs, MutableHandleValue res); +}; + + } // namespace jit } // namespace js diff --git a/js/src/jit/JitOptions.cpp b/js/src/jit/JitOptions.cpp index d9b8ae0da4d7..b7f5230445e2 100644 --- a/js/src/jit/JitOptions.cpp +++ b/js/src/jit/JitOptions.cpp @@ -125,6 +125,9 @@ DefaultJitOptions::DefaultJitOptions() // Toggles whether CacheIR stubs are used. SET_DEFAULT(disableCacheIR, false); + // Toggles whether CacheIR stubs for binary arith operations are used + SET_DEFAULT(disableCacheIRBinaryArith, false); + // Toggles whether shared stubs are used in Ionmonkey. SET_DEFAULT(disableSharedStubs, false); diff --git a/js/src/jit/JitOptions.h b/js/src/jit/JitOptions.h index 35b60949d512..ed645eaf7305 100644 --- a/js/src/jit/JitOptions.h +++ b/js/src/jit/JitOptions.h @@ -63,6 +63,7 @@ struct DefaultJitOptions bool disableRecoverIns; bool disableScalarReplacement; bool disableCacheIR; + bool disableCacheIRBinaryArith; bool disableSharedStubs; bool disableSincos; bool disableSink; diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 3b52ec58805f..96041d2b4686 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -2512,6 +2512,24 @@ LIRGenerator::visitStringReplace(MStringReplace* ins) assignSafepoint(lir, ins); } +void +LIRGenerator::visitBinaryCache(MBinaryCache* ins) +{ + MDefinition* lhs = ins->getOperand(0); + MDefinition* rhs = ins->getOperand(1); + + MOZ_ASSERT(ins->type() == MIRType::Value); + MOZ_ASSERT(ins->type() == MIRType::Value); + + LBinaryCache* lir = new(alloc()) LBinaryCache(useBox(lhs), + useBox(rhs), + tempFixed(FloatReg0), + tempFixed(FloatReg1)); + defineBox(lir, ins); + assignSafepoint(lir, ins); +} + + void LIRGenerator::visitBinarySharedStub(MBinarySharedStub* ins) { diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index da1b3729989b..403d2ae4f1c4 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -8338,6 +8338,22 @@ class MBinarySharedStub TRIVIAL_NEW_WRAPPERS }; +class MBinaryCache + : public MBinaryInstruction, + public MixPolicy, BoxPolicy<1> >::Data +{ + protected: + explicit MBinaryCache(MDefinition* left, MDefinition* right) + : MBinaryInstruction(classOpcode, left, right) + { + setResultType(MIRType::Value); + } + + public: + INSTRUCTION_HEADER(BinaryCache) + TRIVIAL_NEW_WRAPPERS +}; + class MUnaryCache : public MUnaryInstruction, public BoxPolicy<0>::Data diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp index f0984952b409..a33ae8c5ea61 100644 --- a/js/src/jit/SharedIC.cpp +++ b/js/src/jit/SharedIC.cpp @@ -673,6 +673,12 @@ SharedStubInfo::outerScript(JSContext* cx) // // BinaryArith_Fallback // +// This will be phased out in favour of the CacheIR system. + +extern bool +DoCacheIRBinaryArithFallback(JSContext* cx, BaselineFrame* frame, ICBinaryArith_Fallback* stub_, + HandleValue lhs, HandleValue rhs, MutableHandleValue ret, + DebugModeOSRVolatileStub& stub); static bool DoBinaryArithFallback(JSContext* cx, void* payload, ICBinaryArith_Fallback* stub_, @@ -690,6 +696,7 @@ DoBinaryArithFallback(JSContext* cx, void* payload, ICBinaryArith_Fallback* stub int(lhs.isDouble() ? JSVAL_TYPE_DOUBLE : lhs.extractNonDoubleType()), int(rhs.isDouble() ? JSVAL_TYPE_DOUBLE : rhs.extractNonDoubleType())); + // Don't pass lhs/rhs directly, we need the original values when // generating stubs. RootedValue lhsCopy(cx, lhs); @@ -770,6 +777,13 @@ DoBinaryArithFallback(JSContext* cx, void* payload, ICBinaryArith_Fallback* stub if (stub.invalid()) return true; + // Try to use a CacheIR stub first. If that succeeds, then we're done. Otherwise, we + // need to try to attach a shared stub. + if (engine == ICStubCompiler::Engine::Baseline && !JitOptions.disableCacheIRBinaryArith) { + if (DoCacheIRBinaryArithFallback(cx, (BaselineFrame*)payload, stub_, lhs, rhs, ret, stub)) + return true; + } + if (ret.isDouble()) stub->setSawDoubleResult(); diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 5e00d433c11e..ef6d84628a98 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -5556,6 +5556,32 @@ class LBinarySharedStub : public LCallInstructionHelper +{ + public: + LIR_HEADER(BinaryCache) + + // Takes two temps: these are intendend to be FloatReg0 and FloatReg1 + // To allow the actual cache code to safely clobber those values without + // save and restore. + LBinaryCache(const LBoxAllocation& lhs, const LBoxAllocation& rhs, + const LDefinition& temp0, const LDefinition& temp1) + : LInstructionHelper(classOpcode) + { + setBoxOperand(LhsInput, lhs); + setBoxOperand(RhsInput, rhs); + setTemp(0, temp0); + setTemp(1, temp1); + } + + const MBinaryCache* mir() const { + return mir_->toBinaryCache(); + } + + static const size_t LhsInput = 0; + static const size_t RhsInput = BOX_PIECES; +}; + class LUnaryCache : public LInstructionHelper { public: diff --git a/js/src/jit/shared/Lowering-shared-inl.h b/js/src/jit/shared/Lowering-shared-inl.h index 844d3a01ec2f..acd553ba6ee7 100644 --- a/js/src/jit/shared/Lowering-shared-inl.h +++ b/js/src/jit/shared/Lowering-shared-inl.h @@ -687,6 +687,14 @@ LIRGeneratorShared::tempFixed(Register reg) return t; } +LDefinition +LIRGeneratorShared::tempFixed(FloatRegister reg) +{ + LDefinition t = temp(LDefinition::DOUBLE); + t.setOutput(LFloatReg(reg)); + return t; +} + LDefinition LIRGeneratorShared::tempFloat32() { diff --git a/js/src/jit/shared/Lowering-shared.h b/js/src/jit/shared/Lowering-shared.h index cf5627ee485d..acaf46d443f7 100644 --- a/js/src/jit/shared/Lowering-shared.h +++ b/js/src/jit/shared/Lowering-shared.h @@ -150,8 +150,11 @@ class LIRGeneratorShared inline LDefinition tempDouble(); inline LDefinition tempCopy(MDefinition* input, uint32_t reusedInput); - // Note that the fixed register has a GENERAL type. + // Note that the fixed register has a GENERAL type, + // unless the arg is of FloatRegister type inline LDefinition tempFixed(Register reg); + inline LDefinition tempFixed(FloatRegister reg); + template inline void defineFixed(LInstructionHelper<1, Ops, Temps>* lir, MDefinition* mir, diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 8e0daad965b3..4b2a10537440 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -8645,6 +8645,8 @@ SetContextOptions(JSContext* cx, const OptionParser& op) jit::JitOptions.disableCacheIR = false; else if (strcmp(str, "off") == 0) jit::JitOptions.disableCacheIR = true; + else if (strcmp(str, "nobinary") == 0) + jit::JitOptions.disableCacheIRBinaryArith = true; else return OptionFailure("cache-ir-stubs", str); } @@ -9199,8 +9201,9 @@ main(int argc, char** argv, char** envp) #endif || !op.addStringOption('\0', "spectre-mitigations", "on/off", "Whether Spectre mitigations are enabled (default: off, on to enable)") - || !op.addStringOption('\0', "cache-ir-stubs", "on/off", - "Use CacheIR stubs (default: on, off to disable)") + || !op.addStringOption('\0', "cache-ir-stubs", "on/off/nobinary", + "Use CacheIR stubs (default: on, off to disable, nobinary to" + "just disable binary arith)") || !op.addStringOption('\0', "ion-shared-stubs", "on/off", "Use shared stubs (default: on, off to disable)") || !op.addStringOption('\0', "ion-scalar-replacement", "on/off",