michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef jit_BaselineJIT_h michael@0: #define jit_BaselineJIT_h michael@0: michael@0: #ifdef JS_ION michael@0: michael@0: #include "mozilla/MemoryReporting.h" michael@0: michael@0: #include "jscntxt.h" michael@0: #include "jscompartment.h" michael@0: michael@0: #include "ds/LifoAlloc.h" michael@0: #include "jit/Bailouts.h" michael@0: #include "jit/IonCode.h" michael@0: #include "jit/IonMacroAssembler.h" michael@0: michael@0: namespace js { michael@0: namespace jit { michael@0: michael@0: class StackValue; michael@0: class ICEntry; michael@0: class ICStub; michael@0: michael@0: class PCMappingSlotInfo michael@0: { michael@0: uint8_t slotInfo_; michael@0: michael@0: public: michael@0: // SlotInfo encoding: michael@0: // Bits 0 & 1: number of slots at top of stack which are unsynced. michael@0: // Bits 2 & 3: SlotLocation of top slot value (only relevant if numUnsynced > 0). michael@0: // Bits 3 & 4: SlotLocation of next slot value (only relevant if numUnsynced > 1). michael@0: enum SlotLocation { SlotInR0 = 0, SlotInR1 = 1, SlotIgnore = 3 }; michael@0: michael@0: PCMappingSlotInfo() michael@0: : slotInfo_(0) michael@0: { } michael@0: michael@0: explicit PCMappingSlotInfo(uint8_t slotInfo) michael@0: : slotInfo_(slotInfo) michael@0: { } michael@0: michael@0: static inline bool ValidSlotLocation(SlotLocation loc) { michael@0: return (loc == SlotInR0) || (loc == SlotInR1) || (loc == SlotIgnore); michael@0: } michael@0: michael@0: static SlotLocation ToSlotLocation(const StackValue *stackVal); michael@0: michael@0: inline static PCMappingSlotInfo MakeSlotInfo() { return PCMappingSlotInfo(0); } michael@0: michael@0: inline static PCMappingSlotInfo MakeSlotInfo(SlotLocation topSlotLoc) { michael@0: JS_ASSERT(ValidSlotLocation(topSlotLoc)); michael@0: return PCMappingSlotInfo(1 | (topSlotLoc << 2)); michael@0: } michael@0: michael@0: inline static PCMappingSlotInfo MakeSlotInfo(SlotLocation topSlotLoc, SlotLocation nextSlotLoc) { michael@0: JS_ASSERT(ValidSlotLocation(topSlotLoc)); michael@0: JS_ASSERT(ValidSlotLocation(nextSlotLoc)); michael@0: return PCMappingSlotInfo(2 | (topSlotLoc << 2) | (nextSlotLoc) << 4); michael@0: } michael@0: michael@0: inline unsigned numUnsynced() const { michael@0: return slotInfo_ & 0x3; michael@0: } michael@0: inline SlotLocation topSlotLocation() const { michael@0: return static_cast((slotInfo_ >> 2) & 0x3); michael@0: } michael@0: inline SlotLocation nextSlotLocation() const { michael@0: return static_cast((slotInfo_ >> 4) & 0x3); michael@0: } michael@0: inline uint8_t toByte() const { michael@0: return slotInfo_; michael@0: } michael@0: }; michael@0: michael@0: // A CompactBuffer is used to store native code offsets (relative to the michael@0: // previous pc) and PCMappingSlotInfo bytes. To allow binary search into this michael@0: // table, we maintain a second table of "index" entries. Every X ops, the michael@0: // compiler will add an index entry, so that from the index entry to the michael@0: // actual native code offset, we have to iterate at most X times. michael@0: struct PCMappingIndexEntry michael@0: { michael@0: // jsbytecode offset. michael@0: uint32_t pcOffset; michael@0: michael@0: // Native code offset. michael@0: uint32_t nativeOffset; michael@0: michael@0: // Offset in the CompactBuffer where data for pcOffset starts. michael@0: uint32_t bufferOffset; michael@0: }; michael@0: michael@0: struct BaselineScript michael@0: { michael@0: public: michael@0: static const uint32_t MAX_JSSCRIPT_LENGTH = 0x0fffffffu; michael@0: michael@0: // Limit the locals on a given script so that stack check on baseline frames michael@0: // doesn't overflow a uint32_t value. michael@0: // (MAX_JSSCRIPT_SLOTS * sizeof(Value)) must fit within a uint32_t. michael@0: static const uint32_t MAX_JSSCRIPT_SLOTS = 0xffffu; michael@0: michael@0: private: michael@0: // Code pointer containing the actual method. michael@0: HeapPtr method_; michael@0: michael@0: // For heavyweight scripts, template objects to use for the call object and michael@0: // decl env object (linked via the call object's enclosing scope). michael@0: HeapPtrObject templateScope_; michael@0: michael@0: // Allocated space for fallback stubs. michael@0: FallbackICStubSpace fallbackStubSpace_; michael@0: michael@0: // Native code offset right before the scope chain is initialized. michael@0: uint32_t prologueOffset_; michael@0: michael@0: // Native code offset right before the frame is popped and the method michael@0: // returned from. michael@0: uint32_t epilogueOffset_; michael@0: michael@0: // The offsets for the toggledJump instructions for SPS update ICs. michael@0: #ifdef DEBUG michael@0: mozilla::DebugOnly spsOn_; michael@0: #endif michael@0: uint32_t spsPushToggleOffset_; michael@0: michael@0: // Native code offsets right after the debug prologue VM call returns, or michael@0: // would have returned. This offset is recorded even when debug mode is michael@0: // off to aid on-stack debug mode recompilation. michael@0: // michael@0: // We don't need one for the debug epilogue because that always happens michael@0: // right before the epilogue, so we just use the epilogue offset. michael@0: uint32_t postDebugPrologueOffset_; michael@0: michael@0: public: michael@0: enum Flag { michael@0: // Flag set by JSScript::argumentsOptimizationFailed. Similar to michael@0: // JSScript::needsArgsObj_, but can be read from JIT code. michael@0: NEEDS_ARGS_OBJ = 1 << 0, michael@0: michael@0: // Flag set when discarding JIT code, to indicate this script is michael@0: // on the stack and should not be discarded. michael@0: ACTIVE = 1 << 1, michael@0: michael@0: // Flag set when the script contains any writes to its on-stack michael@0: // (rather than call object stored) arguments. michael@0: MODIFIES_ARGUMENTS = 1 << 2, michael@0: michael@0: // Flag set when compiled for use for debug mode. Handles various michael@0: // Debugger hooks and compiles toggled calls for traps. michael@0: DEBUG_MODE = 1 << 3 michael@0: }; michael@0: michael@0: private: michael@0: uint32_t flags_; michael@0: michael@0: private: michael@0: void trace(JSTracer *trc); michael@0: michael@0: uint32_t icEntriesOffset_; michael@0: uint32_t icEntries_; michael@0: michael@0: uint32_t pcMappingIndexOffset_; michael@0: uint32_t pcMappingIndexEntries_; michael@0: michael@0: uint32_t pcMappingOffset_; michael@0: uint32_t pcMappingSize_; michael@0: michael@0: // List mapping indexes of bytecode type sets to the offset of the opcode michael@0: // they correspond to, for use by TypeScript::BytecodeTypes. michael@0: uint32_t bytecodeTypeMapOffset_; michael@0: michael@0: public: michael@0: // Do not call directly, use BaselineScript::New. This is public for cx->new_. michael@0: BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset, michael@0: uint32_t spsPushToggleOffset, uint32_t postDebugPrologueOffset); michael@0: michael@0: static BaselineScript *New(JSContext *cx, uint32_t prologueOffset, michael@0: uint32_t epilogueOffset, uint32_t postDebugPrologueOffset, michael@0: uint32_t spsPushToggleOffset, size_t icEntries, michael@0: size_t pcMappingIndexEntries, size_t pcMappingSize, michael@0: size_t bytecodeTypeMapEntries); michael@0: static void Trace(JSTracer *trc, BaselineScript *script); michael@0: static void Destroy(FreeOp *fop, BaselineScript *script); michael@0: michael@0: void purgeOptimizedStubs(Zone *zone); michael@0: michael@0: static inline size_t offsetOfMethod() { michael@0: return offsetof(BaselineScript, method_); michael@0: } michael@0: michael@0: void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t *data, michael@0: size_t *fallbackStubs) const { michael@0: *data += mallocSizeOf(this); michael@0: michael@0: // |data| already includes the ICStubSpace itself, so use michael@0: // sizeOfExcludingThis. michael@0: *fallbackStubs += fallbackStubSpace_.sizeOfExcludingThis(mallocSizeOf); michael@0: } michael@0: michael@0: bool active() const { michael@0: return flags_ & ACTIVE; michael@0: } michael@0: void setActive() { michael@0: flags_ |= ACTIVE; michael@0: } michael@0: void resetActive() { michael@0: flags_ &= ~ACTIVE; michael@0: } michael@0: michael@0: void setNeedsArgsObj() { michael@0: flags_ |= NEEDS_ARGS_OBJ; michael@0: } michael@0: michael@0: void setModifiesArguments() { michael@0: flags_ |= MODIFIES_ARGUMENTS; michael@0: } michael@0: bool modifiesArguments() { michael@0: return flags_ & MODIFIES_ARGUMENTS; michael@0: } michael@0: michael@0: void setDebugMode() { michael@0: flags_ |= DEBUG_MODE; michael@0: } michael@0: bool debugMode() const { michael@0: return flags_ & DEBUG_MODE; michael@0: } michael@0: michael@0: uint32_t prologueOffset() const { michael@0: return prologueOffset_; michael@0: } michael@0: uint8_t *prologueEntryAddr() const { michael@0: return method_->raw() + prologueOffset_; michael@0: } michael@0: michael@0: uint32_t epilogueOffset() const { michael@0: return epilogueOffset_; michael@0: } michael@0: uint8_t *epilogueEntryAddr() const { michael@0: return method_->raw() + epilogueOffset_; michael@0: } michael@0: michael@0: uint32_t postDebugPrologueOffset() const { michael@0: return postDebugPrologueOffset_; michael@0: } michael@0: uint8_t *postDebugPrologueAddr() const { michael@0: return method_->raw() + postDebugPrologueOffset_; michael@0: } michael@0: michael@0: ICEntry *icEntryList() { michael@0: return (ICEntry *)(reinterpret_cast(this) + icEntriesOffset_); michael@0: } michael@0: PCMappingIndexEntry *pcMappingIndexEntryList() { michael@0: return (PCMappingIndexEntry *)(reinterpret_cast(this) + pcMappingIndexOffset_); michael@0: } michael@0: uint8_t *pcMappingData() { michael@0: return reinterpret_cast(this) + pcMappingOffset_; michael@0: } michael@0: FallbackICStubSpace *fallbackStubSpace() { michael@0: return &fallbackStubSpace_; michael@0: } michael@0: michael@0: JitCode *method() const { michael@0: return method_; michael@0: } michael@0: void setMethod(JitCode *code) { michael@0: JS_ASSERT(!method_); michael@0: method_ = code; michael@0: } michael@0: michael@0: JSObject *templateScope() const { michael@0: return templateScope_; michael@0: } michael@0: void setTemplateScope(JSObject *templateScope) { michael@0: JS_ASSERT(!templateScope_); michael@0: templateScope_ = templateScope; michael@0: } michael@0: michael@0: void toggleBarriers(bool enabled) { michael@0: method()->togglePreBarriers(enabled); michael@0: } michael@0: michael@0: bool containsCodeAddress(uint8_t *addr) const { michael@0: return method()->raw() <= addr && addr <= method()->raw() + method()->instructionsSize(); michael@0: } michael@0: michael@0: ICEntry &icEntry(size_t index); michael@0: ICEntry *maybeICEntryFromReturnOffset(CodeOffsetLabel returnOffset); michael@0: ICEntry &icEntryFromReturnOffset(CodeOffsetLabel returnOffset); michael@0: ICEntry &icEntryFromPCOffset(uint32_t pcOffset); michael@0: ICEntry &icEntryForDebugModeRecompileFromPCOffset(uint32_t pcOffset); michael@0: ICEntry &icEntryFromPCOffset(uint32_t pcOffset, ICEntry *prevLookedUpEntry); michael@0: ICEntry *maybeICEntryFromReturnAddress(uint8_t *returnAddr); michael@0: ICEntry &icEntryFromReturnAddress(uint8_t *returnAddr); michael@0: uint8_t *returnAddressForIC(const ICEntry &ent); michael@0: michael@0: size_t numICEntries() const { michael@0: return icEntries_; michael@0: } michael@0: michael@0: void copyICEntries(JSScript *script, const ICEntry *entries, MacroAssembler &masm); michael@0: void adoptFallbackStubs(FallbackICStubSpace *stubSpace); michael@0: michael@0: PCMappingIndexEntry &pcMappingIndexEntry(size_t index); michael@0: CompactBufferReader pcMappingReader(size_t indexEntry); michael@0: michael@0: size_t numPCMappingIndexEntries() const { michael@0: return pcMappingIndexEntries_; michael@0: } michael@0: michael@0: void copyPCMappingIndexEntries(const PCMappingIndexEntry *entries); michael@0: michael@0: void copyPCMappingEntries(const CompactBufferWriter &entries); michael@0: uint8_t *nativeCodeForPC(JSScript *script, jsbytecode *pc, PCMappingSlotInfo *slotInfo = nullptr); michael@0: jsbytecode *pcForReturnOffset(JSScript *script, uint32_t nativeOffset); michael@0: jsbytecode *pcForReturnAddress(JSScript *script, uint8_t *nativeAddress); michael@0: michael@0: // Toggle debug traps (used for breakpoints and step mode) in the script. michael@0: // If |pc| is nullptr, toggle traps for all ops in the script. Else, only michael@0: // toggle traps at |pc|. michael@0: void toggleDebugTraps(JSScript *script, jsbytecode *pc); michael@0: michael@0: void toggleSPS(bool enable); michael@0: michael@0: void noteAccessedGetter(uint32_t pcOffset); michael@0: void noteArrayWriteHole(uint32_t pcOffset); michael@0: michael@0: static size_t offsetOfFlags() { michael@0: return offsetof(BaselineScript, flags_); michael@0: } michael@0: michael@0: static void writeBarrierPre(Zone *zone, BaselineScript *script); michael@0: michael@0: uint32_t *bytecodeTypeMap() { michael@0: JS_ASSERT(bytecodeTypeMapOffset_); michael@0: return reinterpret_cast(reinterpret_cast(this) + bytecodeTypeMapOffset_); michael@0: } michael@0: }; michael@0: michael@0: inline bool michael@0: IsBaselineEnabled(JSContext *cx) michael@0: { michael@0: return cx->runtime()->options().baseline(); michael@0: } michael@0: michael@0: MethodStatus michael@0: CanEnterBaselineMethod(JSContext *cx, RunState &state); michael@0: michael@0: MethodStatus michael@0: CanEnterBaselineAtBranch(JSContext *cx, InterpreterFrame *fp, bool newType); michael@0: michael@0: IonExecStatus michael@0: EnterBaselineMethod(JSContext *cx, RunState &state); michael@0: michael@0: IonExecStatus michael@0: EnterBaselineAtBranch(JSContext *cx, InterpreterFrame *fp, jsbytecode *pc); michael@0: michael@0: void michael@0: FinishDiscardBaselineScript(FreeOp *fop, JSScript *script); michael@0: michael@0: void michael@0: AddSizeOfBaselineData(JSScript *script, mozilla::MallocSizeOf mallocSizeOf, size_t *data, michael@0: size_t *fallbackStubs); michael@0: michael@0: void michael@0: ToggleBaselineSPS(JSRuntime *runtime, bool enable); michael@0: michael@0: struct BaselineBailoutInfo michael@0: { michael@0: // Pointer into the current C stack, where overwriting will start. michael@0: uint8_t *incomingStack; michael@0: michael@0: // The top and bottom heapspace addresses of the reconstructed stack michael@0: // which will be copied to the bottom. michael@0: uint8_t *copyStackTop; michael@0: uint8_t *copyStackBottom; michael@0: michael@0: // Fields to store the top-of-stack baseline values that are held michael@0: // in registers. The setR0 and setR1 fields are flags indicating michael@0: // whether each one is initialized. michael@0: uint32_t setR0; michael@0: Value valueR0; michael@0: uint32_t setR1; michael@0: Value valueR1; michael@0: michael@0: // The value of the frame pointer register on resume. michael@0: void *resumeFramePtr; michael@0: michael@0: // The native code address to resume into. michael@0: void *resumeAddr; michael@0: michael@0: // If resuming into a TypeMonitor IC chain, this field holds the michael@0: // address of the first stub in that chain. If this field is michael@0: // set, then the actual jitcode resumed into is the jitcode for michael@0: // the first stub, not the resumeAddr above. The resumeAddr michael@0: // above, in this case, is pushed onto the stack so that the michael@0: // TypeMonitor chain can tail-return into the main jitcode when done. michael@0: ICStub *monitorStub; michael@0: michael@0: // Number of baseline frames to push on the stack. michael@0: uint32_t numFrames; michael@0: michael@0: // The bailout kind. michael@0: BailoutKind bailoutKind; michael@0: }; michael@0: michael@0: uint32_t michael@0: BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIterator &iter, michael@0: bool invalidate, BaselineBailoutInfo **bailoutInfo, michael@0: const ExceptionBailoutInfo *exceptionInfo = nullptr); michael@0: michael@0: // Mark baseline scripts on the stack as active, so that they are not discarded michael@0: // during GC. michael@0: void michael@0: MarkActiveBaselineScripts(Zone *zone); michael@0: michael@0: MethodStatus michael@0: BaselineCompile(JSContext *cx, JSScript *script); michael@0: michael@0: } // namespace jit michael@0: } // namespace js michael@0: michael@0: #endif // JS_ION michael@0: michael@0: #endif /* jit_BaselineJIT_h */