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",