/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef jit_CompileInfo_h #define jit_CompileInfo_h #include "mozilla/Assertions.h" // MOZ_ASSERT #include "mozilla/Maybe.h" // mozilla::Maybe, mozilla::Some #include // std::max #include // uint32_t #include "jit/CompileWrappers.h" // CompileRuntime #include "jit/JitFrames.h" // MinJITStackSize #include "jit/shared/Assembler-shared.h" #include "js/TypeDecls.h" // jsbytecode #include "vm/BindingKind.h" // BindingLocation #include "vm/JSAtomState.h" // JSAtomState #include "vm/JSFunction.h" // JSFunction #include "vm/JSScript.h" // JSScript #include "vm/Opcodes.h" // JSOp #include "vm/Scope.h" // BindingIter namespace js { class ModuleObject; namespace jit { class InlineScriptTree; inline unsigned StartArgSlot(JSScript* script) { // Reserved slots: // Slot 0: Environment chain. // Slot 1: Return value. // When needed: // Slot 2: Argumentsobject. // Note: when updating this, please also update the assert in // SnapshotWriter::startFrame return 2 + (script->needsArgsObj() ? 1 : 0); } inline unsigned CountArgSlots(JSScript* script, JSFunction* fun) { // Slot x + 0: This value. // Slot x + 1: Argument 1. // ... // Slot x + n: Argument n. // Note: when updating this, please also update the assert in // SnapshotWriter::startFrame return StartArgSlot(script) + (fun ? fun->nargs() + 1 : 0); } inline unsigned CountArgSlots(JSScript* script, bool hasFun, uint32_t funArgCount) { // Same as the previous function, for use when the JSFunction is not // available. return StartArgSlot(script) + (hasFun ? funArgCount + 1 : 0); } // Contains information about the compilation source for IR being generated. class CompileInfo { public: CompileInfo(CompileRuntime* runtime, JSScript* script, JSFunction* fun, jsbytecode* osrPc, bool scriptNeedsArgsObj, InlineScriptTree* inlineScriptTree) : script_(script), fun_(fun), osrPc_(osrPc), scriptNeedsArgsObj_(scriptNeedsArgsObj), hadEagerTruncationBailout_(script->hadEagerTruncationBailout()), hadSpeculativePhiBailout_(script->hadSpeculativePhiBailout()), hadLICMInvalidation_(script->hadLICMInvalidation()), hadReorderingBailout_(script->hadReorderingBailout()), hadBoundsCheckBailout_(script->failedBoundsCheck()), hadUnboxFoldingBailout_(script->hadUnboxFoldingBailout()), mayReadFrameArgsDirectly_(script->mayReadFrameArgsDirectly()), anyFormalIsForwarded_(script->anyFormalIsForwarded()), isDerivedClassConstructor_(script->isDerivedClassConstructor()), inlineScriptTree_(inlineScriptTree) { MOZ_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOp::LoopHead); // The function here can flow in from anywhere so look up the canonical // function to ensure that we do not try to embed a nursery pointer in // jit-code. Precisely because it can flow in from anywhere, it's not // guaranteed to be non-lazy. Hence, don't access its script! if (fun_) { fun_ = fun_->baseScript()->function(); MOZ_ASSERT(fun_->isTenured()); } nimplicit_ = StartArgSlot(script) /* env chain and argument obj */ + (fun ? 1 : 0); /* this */ nargs_ = fun ? fun->nargs() : 0; nlocals_ = script->nfixed(); // An extra slot is needed for global scopes because InitGLexical (stack // depth 1) is compiled as a SetProp (stack depth 2) on the global lexical // scope. uint32_t extra = script->isGlobalCode() ? 1 : 0; nstack_ = std::max(script->nslots() - script->nfixed(), MinJITStackSize) + extra; nslots_ = nimplicit_ + nargs_ + nlocals_ + nstack_; // For derived class constructors, find and cache the frame slot for // the .this binding. This slot is assumed to be always // observable. See isObservableFrameSlot. if (script->isDerivedClassConstructor()) { MOZ_ASSERT(script->functionHasThisBinding()); for (BindingIter bi(script); bi; bi++) { if (bi.name() != runtime->names().dotThis) { continue; } BindingLocation loc = bi.location(); if (loc.kind() == BindingLocation::Kind::Frame) { thisSlotForDerivedClassConstructor_ = mozilla::Some(localSlot(loc.slot())); break; } } } // If the script uses an environment in body, the environment chain // will need to be observable. needsBodyEnvironmentObject_ = script->needsBodyEnvironment(); funNeedsSomeEnvironmentObject_ = fun ? fun->needsSomeEnvironmentObject() : false; } explicit CompileInfo(unsigned nlocals) : script_(nullptr), fun_(nullptr), osrPc_(nullptr), scriptNeedsArgsObj_(false), hadEagerTruncationBailout_(false), hadSpeculativePhiBailout_(false), hadLICMInvalidation_(false), hadReorderingBailout_(false), hadBoundsCheckBailout_(false), hadUnboxFoldingBailout_(false), mayReadFrameArgsDirectly_(false), anyFormalIsForwarded_(false), inlineScriptTree_(nullptr), needsBodyEnvironmentObject_(false), funNeedsSomeEnvironmentObject_(false) { nimplicit_ = 0; nargs_ = 0; nlocals_ = nlocals; nstack_ = 1; /* For FunctionCompiler::pushPhiInput/popPhiOutput */ nslots_ = nlocals_ + nstack_; } JSScript* script() const { return script_; } bool compilingWasm() const { return script() == nullptr; } ModuleObject* module() const { return script_->module(); } jsbytecode* osrPc() const { return osrPc_; } InlineScriptTree* inlineScriptTree() const { return inlineScriptTree_; } // It's not safe to access the JSFunction off main thread. bool hasFunMaybeLazy() const { return fun_; } ImmGCPtr funMaybeLazy() const { return ImmGCPtr(fun_); } const char* filename() const { return script_->filename(); } unsigned lineno() const { return script_->lineno(); } // Total number of slots: args, locals, and stack. unsigned nslots() const { return nslots_; } // Number of slots needed for env chain, return value, // maybe argumentsobject and this value. unsigned nimplicit() const { return nimplicit_; } // Number of arguments (without counting this value). unsigned nargs() const { return nargs_; } // Number of slots needed for all local variables. This includes "fixed // vars" (see above) and also block-scoped locals. unsigned nlocals() const { return nlocals_; } unsigned ninvoke() const { return nslots_ - nstack_; } uint32_t environmentChainSlot() const { MOZ_ASSERT(script()); return 0; } uint32_t returnValueSlot() const { MOZ_ASSERT(script()); return 1; } uint32_t argsObjSlot() const { MOZ_ASSERT(needsArgsObj()); return 2; } uint32_t thisSlot() const { MOZ_ASSERT(hasFunMaybeLazy()); MOZ_ASSERT(nimplicit_ > 0); return nimplicit_ - 1; } uint32_t firstArgSlot() const { return nimplicit_; } uint32_t argSlotUnchecked(uint32_t i) const { // During initialization, some routines need to get at arg // slots regardless of how regular argument access is done. MOZ_ASSERT(i < nargs_); return nimplicit_ + i; } uint32_t argSlot(uint32_t i) const { // This should only be accessed when compiling functions for // which argument accesses don't need to go through the // argument object. MOZ_ASSERT(!argsObjAliasesFormals()); return argSlotUnchecked(i); } uint32_t firstLocalSlot() const { return nimplicit_ + nargs_; } uint32_t localSlot(uint32_t i) const { return firstLocalSlot() + i; } uint32_t firstStackSlot() const { return firstLocalSlot() + nlocals(); } uint32_t stackSlot(uint32_t i) const { return firstStackSlot() + i; } uint32_t totalSlots() const { MOZ_ASSERT(script() && hasFunMaybeLazy()); return nimplicit() + nargs() + nlocals(); } bool hasMappedArgsObj() const { return script()->hasMappedArgsObj(); } bool needsArgsObj() const { return scriptNeedsArgsObj_; } bool argsObjAliasesFormals() const { return scriptNeedsArgsObj_ && script()->hasMappedArgsObj(); } bool needsBodyEnvironmentObject() const { return needsBodyEnvironmentObject_; } enum class SlotObservableKind { // This slot must be preserved because it's observable outside SSA uses. // It can't be recovered before or during bailout. ObservableNotRecoverable, // This slot must be preserved because it's observable, but it can be // recovered. ObservableRecoverable, // This slot is not observable outside SSA uses. NotObservable, }; inline SlotObservableKind getSlotObservableKind(uint32_t slot) const { // Locals and expression stack slots. if (slot >= firstLocalSlot()) { // The |this| slot for a derived class constructor is a local slot. // It should never be optimized out, as a Debugger might need to perform // TDZ checks on it via, e.g., an exceptionUnwind handler. The TDZ check // is required for correctness if the handler decides to continue // execution. if (thisSlotForDerivedClassConstructor_ && *thisSlotForDerivedClassConstructor_ == slot) { return SlotObservableKind::ObservableNotRecoverable; } return SlotObservableKind::NotObservable; } // Formal argument slots. if (slot >= firstArgSlot()) { MOZ_ASSERT(hasFunMaybeLazy()); MOZ_ASSERT(slot - firstArgSlot() < nargs()); // Preserve formal arguments if they might be read when creating a rest or // arguments object. In non-strict scripts, Function.arguments can create // an arguments object dynamically so we always preserve the arguments. if (mayReadFrameArgsDirectly_ || !script()->strict()) { return SlotObservableKind::ObservableRecoverable; } return SlotObservableKind::NotObservable; } // |this| slot is observable but it can be recovered. if (hasFunMaybeLazy() && slot == thisSlot()) { return SlotObservableKind::ObservableRecoverable; } // Environment chain slot. if (slot == environmentChainSlot()) { // If environments can be added in the body (after the prologue) we need // to preserve the environment chain slot. It can't be recovered. if (needsBodyEnvironmentObject()) { return SlotObservableKind::ObservableNotRecoverable; } // If the function may need an arguments object, also preserve the // environment chain because it may be needed to reconstruct the arguments // object during bailout. if (funNeedsSomeEnvironmentObject_ || needsArgsObj()) { return SlotObservableKind::ObservableRecoverable; } return SlotObservableKind::NotObservable; } // The arguments object is observable. If it does not escape, it can // be recovered. if (needsArgsObj() && slot == argsObjSlot()) { MOZ_ASSERT(hasFunMaybeLazy()); return SlotObservableKind::ObservableRecoverable; } MOZ_ASSERT(slot == returnValueSlot()); return SlotObservableKind::NotObservable; } // Returns true if a slot can be observed out-side the current frame while // the frame is active on the stack. This implies that these definitions // would have to be executed and that they cannot be removed even if they // are unused. inline bool isObservableSlot(uint32_t slot) const { SlotObservableKind kind = getSlotObservableKind(slot); return (kind == SlotObservableKind::ObservableNotRecoverable || kind == SlotObservableKind::ObservableRecoverable); } // Returns true if a slot can be recovered before or during a bailout. A // definition which can be observed and recovered, implies that this // definition can be optimized away as long as we can compute its values. bool isRecoverableOperand(uint32_t slot) const { SlotObservableKind kind = getSlotObservableKind(slot); return (kind == SlotObservableKind::ObservableRecoverable || kind == SlotObservableKind::NotObservable); } // Check previous bailout states to prevent doing the same bailout in the // next compilation. bool hadEagerTruncationBailout() const { return hadEagerTruncationBailout_; } bool hadSpeculativePhiBailout() const { return hadSpeculativePhiBailout_; } bool hadLICMInvalidation() const { return hadLICMInvalidation_; } bool hadReorderingBailout() const { return hadReorderingBailout_; } bool hadBoundsCheckBailout() const { return hadBoundsCheckBailout_; } bool hadUnboxFoldingBailout() const { return hadUnboxFoldingBailout_; } bool mayReadFrameArgsDirectly() const { return mayReadFrameArgsDirectly_; } bool anyFormalIsForwarded() const { return anyFormalIsForwarded_; } bool isDerivedClassConstructor() const { return isDerivedClassConstructor_; } private: unsigned nimplicit_; unsigned nargs_; unsigned nlocals_; unsigned nstack_; unsigned nslots_; mozilla::Maybe thisSlotForDerivedClassConstructor_; JSScript* script_; JSFunction* fun_; jsbytecode* osrPc_; bool scriptNeedsArgsObj_; // Record the state of previous bailouts in order to prevent compiling the // same function identically the next time. bool hadEagerTruncationBailout_; bool hadSpeculativePhiBailout_; bool hadLICMInvalidation_; bool hadReorderingBailout_; bool hadBoundsCheckBailout_; bool hadUnboxFoldingBailout_; bool mayReadFrameArgsDirectly_; bool anyFormalIsForwarded_; bool isDerivedClassConstructor_; InlineScriptTree* inlineScriptTree_; // Whether a script needs environments within its body. This informs us // that the environment chain is not easy to reconstruct. bool needsBodyEnvironmentObject_; bool funNeedsSomeEnvironmentObject_; }; } // namespace jit } // namespace js #endif /* jit_CompileInfo_h */