/* -*- 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_BaselineCodeGen_h #define jit_BaselineCodeGen_h #include "jit/BaselineFrameInfo.h" #include "jit/BytecodeAnalysis.h" #include "jit/FixedList.h" #include "jit/MacroAssembler.h" #include "jit/PerfSpewer.h" namespace js { namespace jit { enum class ScriptGCThingType { Atom, String, RegExp, Object, Function, Scope, BigInt }; // Base class for BaselineCompiler and BaselineInterpreterGenerator. The Handler // template is a class storing fields/methods that are interpreter or compiler // specific. This can be combined with template specialization of methods in // this class to specialize behavior. template class BaselineCodeGen { protected: Handler handler; JSContext* cx; StackMacroAssembler masm; typename Handler::FrameInfoT& frame; // Shared epilogue code to return to the caller. NonAssertingLabel return_; NonAssertingLabel postBarrierSlot_; // Prologue code where we resume for Ion prologue bailouts. NonAssertingLabel bailoutPrologue_; CodeOffset profilerEnterFrameToggleOffset_; CodeOffset profilerExitFrameToggleOffset_; // Early Ion bailouts will enter at this address. This is after frame // construction and before environment chain is initialized. CodeOffset bailoutPrologueOffset_; // Baseline Interpreter can enter Baseline Compiler code at this address. This // is right after the warm-up counter check in the prologue. CodeOffset warmUpCheckPrologueOffset_; uint32_t pushedBeforeCall_ = 0; #ifdef DEBUG bool inCall_ = false; #endif template explicit BaselineCodeGen(JSContext* cx, TempAllocator& alloc, HandlerArgs&&... args); template void pushArg(const T& t) { masm.Push(t); } // Pushes the current script as argument for a VM function. void pushScriptArg(); // Pushes the bytecode pc as argument for a VM function. void pushBytecodePCArg(); // Pushes a name/object/scope associated with the current bytecode op (and // stored in the script) as argument for a VM function. void loadScriptGCThing(ScriptGCThingType type, Register dest, Register scratch); void pushScriptGCThingArg(ScriptGCThingType type, Register scratch1, Register scratch2); void pushScriptNameArg(Register scratch1, Register scratch2); // Pushes a bytecode operand as argument for a VM function. void pushUint8BytecodeOperandArg(Register scratch); void pushUint16BytecodeOperandArg(Register scratch); void loadInt32LengthBytecodeOperand(Register dest); void loadNumFormalArguments(Register dest); // Loads the current JSScript* in dest. void loadScript(Register dest); void saveInterpreterPCReg(); void restoreInterpreterPCReg(); // Subtracts |script->nslots() * sizeof(Value)| from reg. void subtractScriptSlotsSize(Register reg, Register scratch); // Jump to the script's resume entry indicated by resumeIndex. void jumpToResumeEntry(Register resumeIndex, Register scratch1, Register scratch2); // Load the global's lexical environment. void loadGlobalLexicalEnvironment(Register dest); void pushGlobalLexicalEnvironmentValue(ValueOperand scratch); // Load the |this|-value from the global's lexical environment. void loadGlobalThisValue(ValueOperand dest); // Computes the frame size. See BaselineFrame::debugFrameSize_. void computeFrameSize(Register dest); void prepareVMCall(); void storeFrameSizeAndPushDescriptor(uint32_t argSize, Register scratch); enum class CallVMPhase { BeforePushingLocals, AfterPushingLocals }; bool callVMInternal(VMFunctionId id, RetAddrEntry::Kind kind, CallVMPhase phase); template bool callVM(RetAddrEntry::Kind kind = RetAddrEntry::Kind::CallVM, CallVMPhase phase = CallVMPhase::AfterPushingLocals); template bool callVMNonOp(CallVMPhase phase = CallVMPhase::AfterPushingLocals) { return callVM(RetAddrEntry::Kind::NonOpCallVM, phase); } // ifDebuggee should be a function emitting code for when the script is a // debuggee script. ifNotDebuggee (if present) is called to emit code for // non-debuggee scripts. template [[nodiscard]] bool emitDebugInstrumentation( const F1& ifDebuggee, const mozilla::Maybe& ifNotDebuggee); template [[nodiscard]] bool emitDebugInstrumentation(const F& ifDebuggee) { return emitDebugInstrumentation(ifDebuggee, mozilla::Maybe()); } bool emitSuspend(JSOp op); template [[nodiscard]] bool emitAfterYieldDebugInstrumentation(const F& ifDebuggee, Register scratch); // ifSet should be a function emitting code for when the script has |flag| // set. ifNotSet emits code for when the flag isn't set. template [[nodiscard]] bool emitTestScriptFlag(JSScript::ImmutableFlags flag, const F1& ifSet, const F2& ifNotSet, Register scratch); // If |script->hasFlag(flag) == value|, execute the code emitted by |emit|. template [[nodiscard]] bool emitTestScriptFlag(JSScript::ImmutableFlags flag, bool value, const F& emit, Register scratch); template [[nodiscard]] bool emitTestScriptFlag(JSScript::MutableFlags flag, bool value, const F& emit, Register scratch); [[nodiscard]] bool emitEnterGeneratorCode(Register script, Register resumeIndex, Register scratch); void emitInterpJumpToResumeEntry(Register script, Register resumeIndex, Register scratch); void emitJumpToInterpretOpLabel(); [[nodiscard]] bool emitCheckThis(ValueOperand val, bool reinit = false); void emitLoadReturnValue(ValueOperand val); void emitGetAliasedVar(ValueOperand dest); [[nodiscard]] bool emitGetAliasedDebugVar(ValueOperand dest); [[nodiscard]] bool emitNextIC(); [[nodiscard]] bool emitInterruptCheck(); [[nodiscard]] bool emitWarmUpCounterIncrement(); #define EMIT_OP(op, ...) bool emit_##op(); FOR_EACH_OPCODE(EMIT_OP) #undef EMIT_OP // JSOp::Pos, JSOp::Neg, JSOp::BitNot, JSOp::Inc, JSOp::Dec, JSOp::ToNumeric. [[nodiscard]] bool emitUnaryArith(); // JSOp::BitXor, JSOp::Lsh, JSOp::Add etc. [[nodiscard]] bool emitBinaryArith(); // Handles JSOp::Lt, JSOp::Gt, and friends [[nodiscard]] bool emitCompare(); // Handles JSOp::NewObject and JSOp::NewInit. [[nodiscard]] bool emitNewObject(); // For a JOF_JUMP op, jumps to the op's jump target. void emitJump(); // For a JOF_JUMP op, jumps to the op's jump target depending on the Value // in |val|. void emitTestBooleanTruthy(bool branchIfTrue, ValueOperand val); // Converts |val| to an index in the jump table and stores this in |dest| // or branches to the default pc if not int32 or out-of-range. void emitGetTableSwitchIndex(ValueOperand val, Register dest, Register scratch1, Register scratch2); // Jumps to the target of a table switch based on |key| and the // firstResumeIndex stored in JSOp::TableSwitch. void emitTableSwitchJump(Register key, Register scratch1, Register scratch2); [[nodiscard]] bool emitReturn(); [[nodiscard]] bool emitTest(bool branchIfTrue); [[nodiscard]] bool emitAndOr(bool branchIfTrue); [[nodiscard]] bool emitCoalesce(); [[nodiscard]] bool emitCall(JSOp op); [[nodiscard]] bool emitSpreadCall(JSOp op); [[nodiscard]] bool emitDelElem(bool strict); [[nodiscard]] bool emitDelProp(bool strict); [[nodiscard]] bool emitSetElemSuper(bool strict); [[nodiscard]] bool emitSetPropSuper(bool strict); // Try to bake in the result of BindGName instead of using an IC. // Return true if we managed to optimize the op. bool tryOptimizeBindGlobalName(); [[nodiscard]] bool emitInitPropGetterSetter(); [[nodiscard]] bool emitInitElemGetterSetter(); [[nodiscard]] bool emitFormalArgAccess(JSOp op); [[nodiscard]] bool emitUninitializedLexicalCheck(const ValueOperand& val); [[nodiscard]] bool emitIsMagicValue(); void getEnvironmentCoordinateObject(Register reg); Address getEnvironmentCoordinateAddressFromObject(Register objReg, Register reg); Address getEnvironmentCoordinateAddress(Register reg); [[nodiscard]] bool emitPrologue(); [[nodiscard]] bool emitEpilogue(); [[nodiscard]] bool emitOutOfLinePostBarrierSlot(); [[nodiscard]] bool emitStackCheck(); [[nodiscard]] bool emitDebugPrologue(); [[nodiscard]] bool emitDebugEpilogue(); [[nodiscard]] bool initEnvironmentChain(); [[nodiscard]] bool emitHandleCodeCoverageAtPrologue(); void emitInitFrameFields(Register nonFunctionEnv); [[nodiscard]] bool emitIsDebuggeeCheck(); void emitInitializeLocals(); void emitProfilerEnterFrame(); void emitProfilerExitFrame(); }; using RetAddrEntryVector = js::Vector; // Interface used by BaselineCodeGen for BaselineCompiler. class BaselineCompilerHandler { CompilerFrameInfo frame_; TempAllocator& alloc_; BytecodeAnalysis analysis_; #ifdef DEBUG const MacroAssembler& masm_; #endif FixedList