blob: c3f9981615bbd1fe16dcb16e3763e88c80a8db1f [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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_BaselineJIT_h
#define jit_BaselineJIT_h
#ifdef JS_ION
#include "jscntxt.h"
#include "jscompartment.h"
#include "IonCode.h"
#include "IonMacroAssembler.h"
#include "Bailouts.h"
#include "ds/LifoAlloc.h"
namespace js {
namespace jit {
class StackValue;
struct ICEntry;
class ICStub;
class PCMappingSlotInfo
{
uint8_t slotInfo_;
public:
// SlotInfo encoding:
// Bits 0 & 1: number of slots at top of stack which are unsynced.
// Bits 2 & 3: SlotLocation of top slot value (only relevant if numUnsynced > 0).
// Bits 3 & 4: SlotLocation of next slot value (only relevant if numUnsynced > 1).
enum SlotLocation { SlotInR0 = 0, SlotInR1 = 1, SlotIgnore = 3 };
PCMappingSlotInfo()
: slotInfo_(0)
{ }
explicit PCMappingSlotInfo(uint8_t slotInfo)
: slotInfo_(slotInfo)
{ }
static inline bool ValidSlotLocation(SlotLocation loc) {
return (loc == SlotInR0) || (loc == SlotInR1) || (loc == SlotIgnore);
}
static SlotLocation ToSlotLocation(const StackValue *stackVal);
inline static PCMappingSlotInfo MakeSlotInfo() { return PCMappingSlotInfo(0); }
inline static PCMappingSlotInfo MakeSlotInfo(SlotLocation topSlotLoc) {
JS_ASSERT(ValidSlotLocation(topSlotLoc));
return PCMappingSlotInfo(1 | (topSlotLoc << 2));
}
inline static PCMappingSlotInfo MakeSlotInfo(SlotLocation topSlotLoc, SlotLocation nextSlotLoc) {
JS_ASSERT(ValidSlotLocation(topSlotLoc));
JS_ASSERT(ValidSlotLocation(nextSlotLoc));
return PCMappingSlotInfo(2 | (topSlotLoc << 2) | (nextSlotLoc) << 4);
}
inline unsigned numUnsynced() const {
return slotInfo_ & 0x3;
}
inline SlotLocation topSlotLocation() const {
return static_cast<SlotLocation>((slotInfo_ >> 2) & 0x3);
}
inline SlotLocation nextSlotLocation() const {
return static_cast<SlotLocation>((slotInfo_ >> 4) & 0x3);
}
inline uint8_t toByte() const {
return slotInfo_;
}
};
// A CompactBuffer is used to store native code offsets (relative to the
// previous pc) and PCMappingSlotInfo bytes. To allow binary search into this
// table, we maintain a second table of "index" entries. Every X ops, the
// compiler will add an index entry, so that from the index entry to the
// actual native code offset, we have to iterate at most X times.
struct PCMappingIndexEntry
{
// jsbytecode offset.
uint32_t pcOffset;
// Native code offset.
uint32_t nativeOffset;
// Offset in the CompactBuffer where data for pcOffset starts.
uint32_t bufferOffset;
};
struct BaselineScript
{
public:
static const uint32_t MAX_JSSCRIPT_LENGTH = 0x0fffffffu;
private:
// Code pointer containing the actual method.
HeapPtr<IonCode> method_;
// Allocated space for fallback stubs.
FallbackICStubSpace fallbackStubSpace_;
// Native code offset right before the scope chain is initialized.
uint32_t prologueOffset_;
// The offsets for the toggledJump instructions for SPS update ICs.
#ifdef DEBUG
mozilla::DebugOnly<bool> spsOn_;
#endif
uint32_t spsPushToggleOffset_;
public:
enum Flag {
// Flag set by JSScript::argumentsOptimizationFailed. Similar to
// JSScript::needsArgsObj_, but can be read from JIT code.
NEEDS_ARGS_OBJ = 1 << 0,
// Flag set when discarding JIT code, to indicate this script is
// on the stack and should not be discarded.
ACTIVE = 1 << 1
};
private:
uint32_t flags_;
private:
void trace(JSTracer *trc);
uint32_t icEntriesOffset_;
uint32_t icEntries_;
uint32_t pcMappingIndexOffset_;
uint32_t pcMappingIndexEntries_;
uint32_t pcMappingOffset_;
uint32_t pcMappingSize_;
public:
// Do not call directly, use BaselineScript::New. This is public for cx->new_.
BaselineScript(uint32_t prologueOffset, uint32_t spsPushToggleOffset);
static BaselineScript *New(JSContext *cx, uint32_t prologueOffset,
uint32_t spsPushToggleOffset, size_t icEntries,
size_t pcMappingIndexEntries, size_t pcMappingSize);
static void Trace(JSTracer *trc, BaselineScript *script);
static void Destroy(FreeOp *fop, BaselineScript *script);
void purgeOptimizedStubs(Zone *zone);
static inline size_t offsetOfMethod() {
return offsetof(BaselineScript, method_);
}
void sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *data,
size_t *fallbackStubs) const {
*data = mallocSizeOf(this);
// data already includes the ICStubSpace itself, so use
// sizeOfExcludingThis.
*fallbackStubs = fallbackStubSpace_.sizeOfExcludingThis(mallocSizeOf);
}
bool active() const {
return flags_ & ACTIVE;
}
void setActive() {
flags_ |= ACTIVE;
}
void resetActive() {
flags_ &= ~ACTIVE;
}
void setNeedsArgsObj() {
flags_ |= NEEDS_ARGS_OBJ;
}
uint32_t prologueOffset() const {
return prologueOffset_;
}
uint8_t *prologueEntryAddr() const {
return method_->raw() + prologueOffset_;
}
ICEntry *icEntryList() {
return (ICEntry *)(reinterpret_cast<uint8_t *>(this) + icEntriesOffset_);
}
PCMappingIndexEntry *pcMappingIndexEntryList() {
return (PCMappingIndexEntry *)(reinterpret_cast<uint8_t *>(this) + pcMappingIndexOffset_);
}
uint8_t *pcMappingData() {
return reinterpret_cast<uint8_t *>(this) + pcMappingOffset_;
}
FallbackICStubSpace *fallbackStubSpace() {
return &fallbackStubSpace_;
}
IonCode *method() const {
return method_;
}
void setMethod(IonCode *code) {
JS_ASSERT(!method_);
method_ = code;
}
void toggleBarriers(bool enabled) {
method()->togglePreBarriers(enabled);
}
ICEntry &icEntry(size_t index);
ICEntry *maybeICEntryFromReturnOffset(CodeOffsetLabel returnOffset);
ICEntry &icEntryFromReturnOffset(CodeOffsetLabel returnOffset);
ICEntry &icEntryFromPCOffset(uint32_t pcOffset);
ICEntry &icEntryFromPCOffset(uint32_t pcOffset, ICEntry *prevLookedUpEntry);
ICEntry *maybeICEntryFromReturnAddress(uint8_t *returnAddr);
ICEntry &icEntryFromReturnAddress(uint8_t *returnAddr);
uint8_t *returnAddressForIC(const ICEntry &ent);
size_t numICEntries() const {
return icEntries_;
}
void copyICEntries(HandleScript script, const ICEntry *entries, MacroAssembler &masm);
void adoptFallbackStubs(FallbackICStubSpace *stubSpace);
PCMappingIndexEntry &pcMappingIndexEntry(size_t index);
CompactBufferReader pcMappingReader(size_t indexEntry);
size_t numPCMappingIndexEntries() const {
return pcMappingIndexEntries_;
}
void copyPCMappingIndexEntries(const PCMappingIndexEntry *entries);
void copyPCMappingEntries(const CompactBufferWriter &entries);
uint8_t *nativeCodeForPC(JSScript *script, jsbytecode *pc, PCMappingSlotInfo *slotInfo = NULL);
jsbytecode *pcForReturnOffset(JSScript *script, uint32_t nativeOffset);
jsbytecode *pcForReturnAddress(JSScript *script, uint8_t *nativeAddress);
// Toggle debug traps (used for breakpoints and step mode) in the script.
// If |pc| is NULL, toggle traps for all ops in the script. Else, only
// toggle traps at |pc|.
void toggleDebugTraps(JSScript *script, jsbytecode *pc);
void toggleSPS(bool enable);
void noteAccessedGetter(uint32_t pcOffset);
static size_t offsetOfFlags() {
return offsetof(BaselineScript, flags_);
}
static void writeBarrierPre(Zone *zone, BaselineScript *script);
};
inline bool
IsBaselineEnabled(JSContext *cx)
{
return cx->hasOption(JSOPTION_BASELINE);
}
MethodStatus
CanEnterBaselineMethod(JSContext *cx, RunState &state);
MethodStatus
CanEnterBaselineAtBranch(JSContext *cx, StackFrame *fp, bool newType);
IonExecStatus
EnterBaselineMethod(JSContext *cx, RunState &state);
IonExecStatus
EnterBaselineAtBranch(JSContext *cx, StackFrame *fp, jsbytecode *pc);
void
FinishDiscardBaselineScript(FreeOp *fop, JSScript *script);
void
SizeOfBaselineData(JSScript *script, JSMallocSizeOfFun mallocSizeOf, size_t *data,
size_t *fallbackStubs);
void
ToggleBaselineSPS(JSRuntime *runtime, bool enable);
struct BaselineBailoutInfo
{
// Pointer into the current C stack, where overwriting will start.
uint8_t *incomingStack;
// The top and bottom heapspace addresses of the reconstructed stack
// which will be copied to the bottom.
uint8_t *copyStackTop;
uint8_t *copyStackBottom;
// Fields to store the top-of-stack baseline values that are held
// in registers. The setR0 and setR1 fields are flags indicating
// whether each one is initialized.
uint32_t setR0;
Value valueR0;
uint32_t setR1;
Value valueR1;
// The value of the frame pointer register on resume.
void *resumeFramePtr;
// The native code address to resume into.
void *resumeAddr;
// If resuming into a TypeMonitor IC chain, this field holds the
// address of the first stub in that chain. If this field is
// set, then the actual jitcode resumed into is the jitcode for
// the first stub, not the resumeAddr above. The resumeAddr
// above, in this case, is pushed onto the stack so that the
// TypeMonitor chain can tail-return into the main jitcode when done.
ICStub *monitorStub;
// Number of baseline frames to push on the stack.
uint32_t numFrames;
// The bailout kind.
BailoutKind bailoutKind;
};
uint32_t
BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIterator &iter,
bool invalidate, BaselineBailoutInfo **bailoutInfo);
// Mark baseline scripts on the stack as active, so that they are not discarded
// during GC.
void
MarkActiveBaselineScripts(Zone *zone);
} // namespace jit
} // namespace js
#endif // JS_ION
#endif /* jit_BaselineJIT_h */