blob: 573d7eac5494748ee905b85bb9284d6e793199bd [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_IonFrames_h
#define jit_IonFrames_h
#ifdef JS_ION
#include "mozilla/DebugOnly.h"
#include "jsfun.h"
#include "jstypes.h"
#include "jsutil.h"
#include "Registers.h"
#include "IonCode.h"
#include "IonFrameIterator.h"
class JSFunction;
class JSScript;
namespace js {
namespace jit {
typedef void * CalleeToken;
enum CalleeTokenTag
{
CalleeToken_Function = 0x0, // untagged
CalleeToken_Script = 0x1,
CalleeToken_ParallelFunction = 0x2
};
static inline CalleeTokenTag
GetCalleeTokenTag(CalleeToken token)
{
CalleeTokenTag tag = CalleeTokenTag(uintptr_t(token) & 0x3);
JS_ASSERT(tag <= CalleeToken_ParallelFunction);
return tag;
}
static inline CalleeToken
CalleeToToken(JSFunction *fun)
{
return CalleeToken(uintptr_t(fun) | uintptr_t(CalleeToken_Function));
}
static inline CalleeToken
CalleeToParallelToken(JSFunction *fun)
{
return CalleeToken(uintptr_t(fun) | uintptr_t(CalleeToken_ParallelFunction));
}
static inline CalleeToken
CalleeToToken(JSScript *script)
{
return CalleeToken(uintptr_t(script) | uintptr_t(CalleeToken_Script));
}
static inline bool
CalleeTokenIsFunction(CalleeToken token)
{
return GetCalleeTokenTag(token) == CalleeToken_Function;
}
static inline JSFunction *
CalleeTokenToFunction(CalleeToken token)
{
JS_ASSERT(CalleeTokenIsFunction(token));
return (JSFunction *)token;
}
static inline JSFunction *
CalleeTokenToParallelFunction(CalleeToken token)
{
JS_ASSERT(GetCalleeTokenTag(token) == CalleeToken_ParallelFunction);
return (JSFunction *)(uintptr_t(token) & ~uintptr_t(0x3));
}
static inline JSScript *
CalleeTokenToScript(CalleeToken token)
{
JS_ASSERT(GetCalleeTokenTag(token) == CalleeToken_Script);
return (JSScript *)(uintptr_t(token) & ~uintptr_t(0x3));
}
static inline JSScript *
ScriptFromCalleeToken(CalleeToken token)
{
switch (GetCalleeTokenTag(token)) {
case CalleeToken_Script:
return CalleeTokenToScript(token);
case CalleeToken_Function:
return CalleeTokenToFunction(token)->nonLazyScript();
case CalleeToken_ParallelFunction:
return CalleeTokenToParallelFunction(token)->nonLazyScript();
}
JS_NOT_REACHED("invalid callee token tag");
return NULL;
}
// In between every two frames lies a small header describing both frames. This
// header, minimally, contains a returnAddress word and a descriptor word. The
// descriptor describes the size and type of the previous frame, whereas the
// returnAddress describes the address the newer frame (the callee) will return
// to. The exact mechanism in which frames are laid out is architecture
// dependent.
//
// Two special frame types exist. Entry frames begin an ion activation, and
// therefore there is exactly one per activation of jit::Cannon. Exit frames
// are necessary to leave JIT code and enter C++, and thus, C++ code will
// always begin iterating from the topmost exit frame.
class LSafepoint;
// Two-tuple that lets you look up the safepoint entry given the
// displacement of a call instruction within the JIT code.
class SafepointIndex
{
// The displacement is the distance from the first byte of the JIT'd code
// to the return address (of the call that the safepoint was generated for).
uint32_t displacement_;
union {
LSafepoint *safepoint_;
// Offset to the start of the encoded safepoint in the safepoint stream.
uint32_t safepointOffset_;
};
mozilla::DebugOnly<bool> resolved;
public:
SafepointIndex(uint32_t displacement, LSafepoint *safepoint)
: displacement_(displacement),
safepoint_(safepoint),
resolved(false)
{ }
void resolve();
LSafepoint *safepoint() {
JS_ASSERT(!resolved);
return safepoint_;
}
uint32_t displacement() const {
return displacement_;
}
uint32_t safepointOffset() const {
return safepointOffset_;
}
void adjustDisplacement(uint32_t offset) {
JS_ASSERT(offset >= displacement_);
displacement_ = offset;
}
inline SnapshotOffset snapshotOffset() const;
inline bool hasSnapshotOffset() const;
};
class MacroAssembler;
// The OSI point is patched to a call instruction. Therefore, the
// returnPoint for an OSI call is the address immediately following that
// call instruction. The displacement of that point within the assembly
// buffer is the |returnPointDisplacement|.
class OsiIndex
{
uint32_t callPointDisplacement_;
uint32_t snapshotOffset_;
public:
OsiIndex(uint32_t callPointDisplacement, uint32_t snapshotOffset)
: callPointDisplacement_(callPointDisplacement),
snapshotOffset_(snapshotOffset)
{ }
uint32_t returnPointDisplacement() const;
uint32_t callPointDisplacement() const {
return callPointDisplacement_;
}
uint32_t snapshotOffset() const {
return snapshotOffset_;
}
void fixUpOffset(MacroAssembler &masm);
};
// The layout of an Ion frame on the C stack is roughly:
// argN _
// ... \ - These are jsvals
// arg0 /
// -3 this _/
// -2 callee
// -1 descriptor
// 0 returnAddress
// .. locals ..
// The descriptor is organized into three sections:
// [ frame size | constructing bit | frame type ]
// < highest - - - - - - - - - - - - - - lowest >
static const uintptr_t FRAMESIZE_SHIFT = 4;
static const uintptr_t FRAMETYPE_BITS = 4;
static const uintptr_t FRAMETYPE_MASK = (1 << FRAMETYPE_BITS) - 1;
// Ion frames have a few important numbers associated with them:
// Local depth: The number of bytes required to spill local variables.
// Argument depth: The number of bytes required to push arguments and make
// a function call.
// Slack: A frame may temporarily use extra stack to resolve cycles.
//
// The (local + argument) depth determines the "fixed frame size". The fixed
// frame size is the distance between the stack pointer and the frame header.
// Thus, fixed >= (local + argument).
//
// In order to compress guards, we create shared jump tables that recover the
// script from the stack and recover a snapshot pointer based on which jump was
// taken. Thus, we create a jump table for each fixed frame size.
//
// Jump tables are big. To control the amount of jump tables we generate, each
// platform chooses how to segregate stack size classes based on its
// architecture.
//
// On some architectures, these jump tables are not used at all, or frame
// size segregation is not needed. Thus, there is an option for a frame to not
// have any frame size class, and to be totally dynamic.
static const uint32_t NO_FRAME_SIZE_CLASS_ID = uint32_t(-1);
class FrameSizeClass
{
uint32_t class_;
explicit FrameSizeClass(uint32_t class_) : class_(class_)
{ }
public:
FrameSizeClass()
{ }
static FrameSizeClass None() {
return FrameSizeClass(NO_FRAME_SIZE_CLASS_ID);
}
static FrameSizeClass FromClass(uint32_t class_) {
return FrameSizeClass(class_);
}
// These functions are implemented in specific CodeGenerator-* files.
static FrameSizeClass FromDepth(uint32_t frameDepth);
static FrameSizeClass ClassLimit();
uint32_t frameSize() const;
uint32_t classId() const {
JS_ASSERT(class_ != NO_FRAME_SIZE_CLASS_ID);
return class_;
}
bool operator ==(const FrameSizeClass &other) const {
return class_ == other.class_;
}
bool operator !=(const FrameSizeClass &other) const {
return class_ != other.class_;
}
};
// Data needed to recover from an exception.
struct ResumeFromException
{
static const uint32_t RESUME_ENTRY_FRAME = 0;
static const uint32_t RESUME_CATCH = 1;
static const uint32_t RESUME_FINALLY = 2;
static const uint32_t RESUME_FORCED_RETURN = 3;
uint8_t *framePointer;
uint8_t *stackPointer;
uint8_t *target;
uint32_t kind;
// Value to push when resuming into a |finally| block.
Value exception;
};
void HandleException(ResumeFromException *rfe);
void HandleParallelFailure(ResumeFromException *rfe);
void EnsureExitFrame(IonCommonFrameLayout *frame);
void MarkJitActivations(JSRuntime *rt, JSTracer *trc);
void MarkIonCompilerRoots(JSTracer *trc);
static inline uint32_t
MakeFrameDescriptor(uint32_t frameSize, FrameType type)
{
return (frameSize << FRAMESIZE_SHIFT) | type;
}
} // namespace jit
} // namespace js
#if defined(JS_CPU_X86) || defined (JS_CPU_X64)
# include "jit/shared/IonFrames-x86-shared.h"
#elif defined (JS_CPU_ARM)
# include "jit/arm/IonFrames-arm.h"
#elif defined(JS_CPU_MIPS)
# include "jit/mips/IonFrames-mips.h"
#else
# error "unsupported architecture"
#endif
namespace js {
namespace jit {
JSScript *
GetTopIonJSScript(JSContext *cx,
const SafepointIndex **safepointIndexOut = NULL,
void **returnAddrOut = NULL);
void
GetPcScript(JSContext *cx, JSScript **scriptRes, jsbytecode **pcRes);
// Given a slot index, returns the offset, in bytes, of that slot from an
// IonJSFrameLayout. Slot distances are uniform across architectures, however,
// the distance does depend on the size of the frame header.
static inline int32_t
OffsetOfFrameSlot(int32_t slot)
{
if (slot <= 0)
return -slot;
return -(slot * STACK_SLOT_SIZE);
}
static inline uintptr_t
ReadFrameSlot(IonJSFrameLayout *fp, int32_t slot)
{
return *(uintptr_t *)((char *)fp + OffsetOfFrameSlot(slot));
}
static inline double
ReadFrameDoubleSlot(IonJSFrameLayout *fp, int32_t slot)
{
return *(double *)((char *)fp + OffsetOfFrameSlot(slot));
}
CalleeToken
MarkCalleeToken(JSTracer *trc, CalleeToken token);
} /* namespace jit */
} /* namespace js */
#endif // JS_ION
#endif /* jit_IonFrames_h */