mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-06-06 22:29:34 +00:00
1043 lines
29 KiB
C++
1043 lines
29 KiB
C++
/* -*- 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_JitFrames_h
|
|
#define jit_JitFrames_h
|
|
|
|
#include <stdint.h>
|
|
|
|
#include "jscntxt.h"
|
|
#include "jsfun.h"
|
|
|
|
#include "jit/JitFrameIterator.h"
|
|
#include "jit/Safepoints.h"
|
|
|
|
namespace js {
|
|
namespace jit {
|
|
|
|
enum CalleeTokenTag
|
|
{
|
|
CalleeToken_Function = 0x0, // untagged
|
|
CalleeToken_FunctionConstructing = 0x1,
|
|
CalleeToken_Script = 0x2
|
|
};
|
|
|
|
static const uintptr_t CalleeTokenMask = ~uintptr_t(0x3);
|
|
|
|
static inline CalleeTokenTag
|
|
GetCalleeTokenTag(CalleeToken token)
|
|
{
|
|
CalleeTokenTag tag = CalleeTokenTag(uintptr_t(token) & 0x3);
|
|
MOZ_ASSERT(tag <= CalleeToken_Script);
|
|
return tag;
|
|
}
|
|
static inline CalleeToken
|
|
CalleeToToken(JSFunction* fun, bool constructing)
|
|
{
|
|
CalleeTokenTag tag = constructing ? CalleeToken_FunctionConstructing : CalleeToken_Function;
|
|
return CalleeToken(uintptr_t(fun) | uintptr_t(tag));
|
|
}
|
|
static inline CalleeToken
|
|
CalleeToToken(JSScript* script)
|
|
{
|
|
return CalleeToken(uintptr_t(script) | uintptr_t(CalleeToken_Script));
|
|
}
|
|
static inline bool
|
|
CalleeTokenIsFunction(CalleeToken token)
|
|
{
|
|
CalleeTokenTag tag = GetCalleeTokenTag(token);
|
|
return tag == CalleeToken_Function || tag == CalleeToken_FunctionConstructing;
|
|
}
|
|
static inline bool
|
|
CalleeTokenIsConstructing(CalleeToken token)
|
|
{
|
|
return GetCalleeTokenTag(token) == CalleeToken_FunctionConstructing;
|
|
}
|
|
static inline JSFunction*
|
|
CalleeTokenToFunction(CalleeToken token)
|
|
{
|
|
MOZ_ASSERT(CalleeTokenIsFunction(token));
|
|
return (JSFunction*)(uintptr_t(token) & CalleeTokenMask);
|
|
}
|
|
static inline JSScript*
|
|
CalleeTokenToScript(CalleeToken token)
|
|
{
|
|
MOZ_ASSERT(GetCalleeTokenTag(token) == CalleeToken_Script);
|
|
return (JSScript*)(uintptr_t(token) & CalleeTokenMask);
|
|
}
|
|
static inline bool
|
|
CalleeTokenIsModuleScript(CalleeToken token)
|
|
{
|
|
CalleeTokenTag tag = GetCalleeTokenTag(token);
|
|
return tag == CalleeToken_Script && CalleeTokenToScript(token)->module();
|
|
}
|
|
|
|
static inline JSScript*
|
|
ScriptFromCalleeToken(CalleeToken token)
|
|
{
|
|
switch (GetCalleeTokenTag(token)) {
|
|
case CalleeToken_Script:
|
|
return CalleeTokenToScript(token);
|
|
case CalleeToken_Function:
|
|
case CalleeToken_FunctionConstructing:
|
|
return CalleeTokenToFunction(token)->nonLazyScript();
|
|
}
|
|
MOZ_CRASH("invalid callee token tag");
|
|
}
|
|
|
|
JSScript* MaybeForwardedScriptFromCalleeToken(CalleeToken token);
|
|
|
|
// 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_;
|
|
};
|
|
|
|
#ifdef DEBUG
|
|
bool resolved;
|
|
#endif
|
|
|
|
public:
|
|
SafepointIndex(uint32_t displacement, LSafepoint* safepoint)
|
|
: displacement_(displacement),
|
|
safepoint_(safepoint)
|
|
#ifdef DEBUG
|
|
, resolved(false)
|
|
#endif
|
|
{ }
|
|
|
|
void resolve();
|
|
|
|
LSafepoint* safepoint() {
|
|
MOZ_ASSERT(!resolved);
|
|
return safepoint_;
|
|
}
|
|
uint32_t displacement() const {
|
|
return displacement_;
|
|
}
|
|
uint32_t safepointOffset() const {
|
|
return safepointOffset_;
|
|
}
|
|
void adjustDisplacement(uint32_t offset) {
|
|
MOZ_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_;
|
|
}
|
|
};
|
|
|
|
// 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 | has cached saved frame bit | frame type ]
|
|
// < highest - - - - - - - - - - - - - - lowest >
|
|
static const uintptr_t FRAMESIZE_SHIFT = 5;
|
|
static const uintptr_t HASCACHEDSAVEDFRAME_BIT = 1 << 4;
|
|
static const uintptr_t FRAMETYPE_BITS = 4;
|
|
|
|
// 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 {
|
|
MOZ_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_;
|
|
}
|
|
};
|
|
|
|
struct BaselineBailoutInfo;
|
|
|
|
// 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;
|
|
static const uint32_t RESUME_BAILOUT = 4;
|
|
|
|
uint8_t* framePointer;
|
|
uint8_t* stackPointer;
|
|
uint8_t* target;
|
|
uint32_t kind;
|
|
|
|
// Value to push when resuming into a |finally| block.
|
|
Value exception;
|
|
|
|
BaselineBailoutInfo* bailoutInfo;
|
|
};
|
|
|
|
void HandleException(ResumeFromException* rfe);
|
|
|
|
void EnsureExitFrame(CommonFrameLayout* frame);
|
|
|
|
void MarkJitActivations(JSRuntime* rt, JSTracer* trc);
|
|
void MarkIonCompilerRoots(JSTracer* trc);
|
|
|
|
JSCompartment*
|
|
TopmostIonActivationCompartment(JSRuntime* rt);
|
|
|
|
void UpdateJitActivationsForMinorGC(JSRuntime* rt, JSTracer* trc);
|
|
|
|
static inline uint32_t
|
|
MakeFrameDescriptor(uint32_t frameSize, FrameType type)
|
|
{
|
|
return 0 | (frameSize << FRAMESIZE_SHIFT) | type;
|
|
}
|
|
|
|
// Returns the JSScript associated with the topmost JIT frame.
|
|
inline JSScript*
|
|
GetTopJitJSScript(JSContext* cx)
|
|
{
|
|
JitFrameIterator iter(cx);
|
|
MOZ_ASSERT(iter.type() == JitFrame_Exit);
|
|
++iter;
|
|
|
|
if (iter.isBaselineStub()) {
|
|
++iter;
|
|
MOZ_ASSERT(iter.isBaselineJS());
|
|
}
|
|
|
|
MOZ_ASSERT(iter.isScripted());
|
|
return iter.script();
|
|
}
|
|
|
|
#ifdef JS_CODEGEN_MIPS32
|
|
uint8_t* alignDoubleSpillWithOffset(uint8_t* pointer, int32_t offset);
|
|
#else
|
|
inline uint8_t*
|
|
alignDoubleSpillWithOffset(uint8_t* pointer, int32_t offset)
|
|
{
|
|
// This is NO-OP on non-MIPS platforms.
|
|
return pointer;
|
|
}
|
|
#endif
|
|
|
|
// Layout of the frame prefix. This assumes the stack architecture grows down.
|
|
// If this is ever not the case, we'll have to refactor.
|
|
class CommonFrameLayout
|
|
{
|
|
uint8_t* returnAddress_;
|
|
uintptr_t descriptor_;
|
|
|
|
static const uintptr_t FrameTypeMask = (1 << FRAMETYPE_BITS) - 1;
|
|
|
|
public:
|
|
static size_t offsetOfDescriptor() {
|
|
return offsetof(CommonFrameLayout, descriptor_);
|
|
}
|
|
uintptr_t descriptor() const {
|
|
return descriptor_;
|
|
}
|
|
static size_t offsetOfReturnAddress() {
|
|
return offsetof(CommonFrameLayout, returnAddress_);
|
|
}
|
|
FrameType prevType() const {
|
|
return FrameType(descriptor_ & FrameTypeMask);
|
|
}
|
|
void changePrevType(FrameType type) {
|
|
descriptor_ &= ~FrameTypeMask;
|
|
descriptor_ |= type;
|
|
}
|
|
size_t prevFrameLocalSize() const {
|
|
return descriptor_ >> FRAMESIZE_SHIFT;
|
|
}
|
|
void setFrameDescriptor(size_t size, FrameType type) {
|
|
descriptor_ = 0 | (size << FRAMESIZE_SHIFT) | type;
|
|
}
|
|
bool hasCachedSavedFrame() const {
|
|
return descriptor_ & HASCACHEDSAVEDFRAME_BIT;
|
|
}
|
|
void setHasCachedSavedFrame() {
|
|
descriptor_ |= HASCACHEDSAVEDFRAME_BIT;
|
|
}
|
|
uint8_t* returnAddress() const {
|
|
return returnAddress_;
|
|
}
|
|
void setReturnAddress(uint8_t* addr) {
|
|
returnAddress_ = addr;
|
|
}
|
|
};
|
|
|
|
class JitFrameLayout : public CommonFrameLayout
|
|
{
|
|
CalleeToken calleeToken_;
|
|
uintptr_t numActualArgs_;
|
|
|
|
public:
|
|
CalleeToken calleeToken() const {
|
|
return calleeToken_;
|
|
}
|
|
void replaceCalleeToken(CalleeToken calleeToken) {
|
|
calleeToken_ = calleeToken;
|
|
}
|
|
|
|
static size_t offsetOfCalleeToken() {
|
|
return offsetof(JitFrameLayout, calleeToken_);
|
|
}
|
|
static size_t offsetOfNumActualArgs() {
|
|
return offsetof(JitFrameLayout, numActualArgs_);
|
|
}
|
|
static size_t offsetOfThis() {
|
|
return sizeof(JitFrameLayout);
|
|
}
|
|
static size_t offsetOfEvalNewTarget() {
|
|
return sizeof(JitFrameLayout);
|
|
}
|
|
static size_t offsetOfActualArgs() {
|
|
return offsetOfThis() + sizeof(Value);
|
|
}
|
|
static size_t offsetOfActualArg(size_t arg) {
|
|
return offsetOfActualArgs() + arg * sizeof(Value);
|
|
}
|
|
|
|
Value thisv() {
|
|
MOZ_ASSERT(CalleeTokenIsFunction(calleeToken()));
|
|
return argv()[0];
|
|
}
|
|
Value* argv() {
|
|
MOZ_ASSERT(CalleeTokenIsFunction(calleeToken()));
|
|
return (Value*)(this + 1);
|
|
}
|
|
uintptr_t numActualArgs() const {
|
|
return numActualArgs_;
|
|
}
|
|
|
|
// Computes a reference to a stack or argument slot, where a slot is a
|
|
// distance from the base frame pointer, as would be used for LStackSlot
|
|
// or LArgument.
|
|
uintptr_t* slotRef(SafepointSlotEntry where);
|
|
|
|
static inline size_t Size() {
|
|
return sizeof(JitFrameLayout);
|
|
}
|
|
};
|
|
|
|
// this is the layout of the frame that is used when we enter Ion code from platform ABI code
|
|
class EntryFrameLayout : public JitFrameLayout
|
|
{
|
|
public:
|
|
static inline size_t Size() {
|
|
return sizeof(EntryFrameLayout);
|
|
}
|
|
};
|
|
|
|
class RectifierFrameLayout : public JitFrameLayout
|
|
{
|
|
public:
|
|
static inline size_t Size() {
|
|
return sizeof(RectifierFrameLayout);
|
|
}
|
|
};
|
|
|
|
class IonAccessorICFrameLayout : public CommonFrameLayout
|
|
{
|
|
protected:
|
|
// Pointer to root the stub's JitCode.
|
|
JitCode* stubCode_;
|
|
|
|
public:
|
|
JitCode** stubCode() {
|
|
return &stubCode_;
|
|
}
|
|
static size_t Size() {
|
|
return sizeof(IonAccessorICFrameLayout);
|
|
}
|
|
};
|
|
|
|
// The callee token is now dead.
|
|
class IonUnwoundRectifierFrameLayout : public RectifierFrameLayout
|
|
{
|
|
public:
|
|
static inline size_t Size() {
|
|
// It is not necessary to accout for an extra callee token here because
|
|
// sizeof(ExitFrameLayout) == sizeof(RectifierFrameLayout) due to
|
|
// extra padding.
|
|
return sizeof(IonUnwoundRectifierFrameLayout);
|
|
}
|
|
};
|
|
|
|
// GC related data used to keep alive data surrounding the Exit frame.
|
|
class ExitFooterFrame
|
|
{
|
|
const VMFunction* function_;
|
|
JitCode* jitCode_;
|
|
|
|
public:
|
|
static inline size_t Size() {
|
|
return sizeof(ExitFooterFrame);
|
|
}
|
|
inline JitCode* jitCode() const {
|
|
return jitCode_;
|
|
}
|
|
inline JitCode** addressOfJitCode() {
|
|
return &jitCode_;
|
|
}
|
|
inline const VMFunction* function() const {
|
|
return function_;
|
|
}
|
|
|
|
// This should only be called for function()->outParam == Type_Handle
|
|
template <typename T>
|
|
T* outParam() {
|
|
uint8_t* address = reinterpret_cast<uint8_t*>(this);
|
|
address = alignDoubleSpillWithOffset(address, sizeof(intptr_t));
|
|
return reinterpret_cast<T*>(address - sizeof(T));
|
|
}
|
|
};
|
|
|
|
class NativeExitFrameLayout;
|
|
class IonOOLNativeExitFrameLayout;
|
|
class IonOOLPropertyOpExitFrameLayout;
|
|
class IonOOLProxyExitFrameLayout;
|
|
class IonDOMExitFrameLayout;
|
|
|
|
enum ExitFrameTokenValues
|
|
{
|
|
CallNativeExitFrameLayoutToken = 0x0,
|
|
ConstructNativeExitFrameLayoutToken = 0x1,
|
|
IonDOMExitFrameLayoutGetterToken = 0x2,
|
|
IonDOMExitFrameLayoutSetterToken = 0x3,
|
|
IonDOMMethodExitFrameLayoutToken = 0x4,
|
|
IonOOLNativeExitFrameLayoutToken = 0x5,
|
|
IonOOLPropertyOpExitFrameLayoutToken = 0x6,
|
|
IonOOLSetterOpExitFrameLayoutToken = 0x7,
|
|
IonOOLProxyExitFrameLayoutToken = 0x8,
|
|
LazyLinkExitFrameLayoutToken = 0xFE,
|
|
ExitFrameLayoutBareToken = 0xFF
|
|
};
|
|
|
|
// this is the frame layout when we are exiting ion code, and about to enter platform ABI code
|
|
class ExitFrameLayout : public CommonFrameLayout
|
|
{
|
|
inline uint8_t* top() {
|
|
return reinterpret_cast<uint8_t*>(this + 1);
|
|
}
|
|
|
|
public:
|
|
// Pushed for "bare" fake exit frames that have no GC things on stack to be
|
|
// marked.
|
|
static JitCode* BareToken() { return (JitCode*)ExitFrameLayoutBareToken; }
|
|
|
|
static inline size_t Size() {
|
|
return sizeof(ExitFrameLayout);
|
|
}
|
|
static inline size_t SizeWithFooter() {
|
|
return Size() + ExitFooterFrame::Size();
|
|
}
|
|
|
|
inline ExitFooterFrame* footer() {
|
|
uint8_t* sp = reinterpret_cast<uint8_t*>(this);
|
|
return reinterpret_cast<ExitFooterFrame*>(sp - ExitFooterFrame::Size());
|
|
}
|
|
|
|
// argBase targets the point which precedes the exit frame. Arguments of VM
|
|
// each wrapper are pushed before the exit frame. This correspond exactly
|
|
// to the value of the argBase register of the generateVMWrapper function.
|
|
inline uint8_t* argBase() {
|
|
MOZ_ASSERT(footer()->jitCode() != nullptr);
|
|
return top();
|
|
}
|
|
|
|
inline bool isWrapperExit() {
|
|
return footer()->function() != nullptr;
|
|
}
|
|
inline bool isBareExit() {
|
|
return footer()->jitCode() == BareToken();
|
|
}
|
|
|
|
// See the various exit frame layouts below.
|
|
template <typename T> inline bool is() {
|
|
return footer()->jitCode() == T::Token();
|
|
}
|
|
template <typename T> inline T* as() {
|
|
MOZ_ASSERT(this->is<T>());
|
|
return reinterpret_cast<T*>(footer());
|
|
}
|
|
};
|
|
|
|
// Cannot inherit implementation since we need to extend the top of
|
|
// ExitFrameLayout.
|
|
class NativeExitFrameLayout
|
|
{
|
|
protected: // only to silence a clang warning about unused private fields
|
|
ExitFooterFrame footer_;
|
|
ExitFrameLayout exit_;
|
|
uintptr_t argc_;
|
|
|
|
// We need to split the Value into 2 fields of 32 bits, otherwise the C++
|
|
// compiler may add some padding between the fields.
|
|
uint32_t loCalleeResult_;
|
|
uint32_t hiCalleeResult_;
|
|
|
|
public:
|
|
static inline size_t Size() {
|
|
return sizeof(NativeExitFrameLayout);
|
|
}
|
|
|
|
static size_t offsetOfResult() {
|
|
return offsetof(NativeExitFrameLayout, loCalleeResult_);
|
|
}
|
|
inline Value* vp() {
|
|
return reinterpret_cast<Value*>(&loCalleeResult_);
|
|
}
|
|
inline uintptr_t argc() const {
|
|
return argc_;
|
|
}
|
|
};
|
|
|
|
class CallNativeExitFrameLayout : public NativeExitFrameLayout
|
|
{
|
|
public:
|
|
static JitCode* Token() { return (JitCode*)CallNativeExitFrameLayoutToken; }
|
|
};
|
|
|
|
class ConstructNativeExitFrameLayout : public NativeExitFrameLayout
|
|
{
|
|
public:
|
|
static JitCode* Token() { return (JitCode*)ConstructNativeExitFrameLayoutToken; }
|
|
};
|
|
|
|
template<>
|
|
inline bool
|
|
ExitFrameLayout::is<NativeExitFrameLayout>()
|
|
{
|
|
return is<CallNativeExitFrameLayout>() || is<ConstructNativeExitFrameLayout>();
|
|
}
|
|
|
|
class IonOOLNativeExitFrameLayout
|
|
{
|
|
protected: // only to silence a clang warning about unused private fields
|
|
ExitFooterFrame footer_;
|
|
ExitFrameLayout exit_;
|
|
|
|
// pointer to root the stub's JitCode
|
|
JitCode* stubCode_;
|
|
|
|
uintptr_t argc_;
|
|
|
|
// We need to split the Value into 2 fields of 32 bits, otherwise the C++
|
|
// compiler may add some padding between the fields.
|
|
uint32_t loCalleeResult_;
|
|
uint32_t hiCalleeResult_;
|
|
|
|
// Split Value for |this| and args above.
|
|
uint32_t loThis_;
|
|
uint32_t hiThis_;
|
|
|
|
public:
|
|
static JitCode* Token() { return (JitCode*)IonOOLNativeExitFrameLayoutToken; }
|
|
|
|
static inline size_t Size(size_t argc) {
|
|
// The frame accounts for the callee/result and |this|, so we only need args.
|
|
return sizeof(IonOOLNativeExitFrameLayout) + (argc * sizeof(Value));
|
|
}
|
|
|
|
static size_t offsetOfResult() {
|
|
return offsetof(IonOOLNativeExitFrameLayout, loCalleeResult_);
|
|
}
|
|
|
|
inline JitCode** stubCode() {
|
|
return &stubCode_;
|
|
}
|
|
inline Value* vp() {
|
|
return reinterpret_cast<Value*>(&loCalleeResult_);
|
|
}
|
|
inline Value* thisp() {
|
|
return reinterpret_cast<Value*>(&loThis_);
|
|
}
|
|
inline uintptr_t argc() const {
|
|
return argc_;
|
|
}
|
|
};
|
|
|
|
class IonOOLPropertyOpExitFrameLayout
|
|
{
|
|
protected:
|
|
ExitFooterFrame footer_;
|
|
ExitFrameLayout exit_;
|
|
|
|
// Object for HandleObject
|
|
JSObject* obj_;
|
|
|
|
// id for HandleId
|
|
jsid id_;
|
|
|
|
// space for MutableHandleValue result
|
|
// use two uint32_t so compiler doesn't align.
|
|
uint32_t vp0_;
|
|
uint32_t vp1_;
|
|
|
|
// pointer to root the stub's JitCode
|
|
JitCode* stubCode_;
|
|
|
|
public:
|
|
static JitCode* Token() { return (JitCode*)IonOOLPropertyOpExitFrameLayoutToken; }
|
|
|
|
static inline size_t Size() {
|
|
return sizeof(IonOOLPropertyOpExitFrameLayout);
|
|
}
|
|
|
|
static size_t offsetOfObject() {
|
|
return offsetof(IonOOLPropertyOpExitFrameLayout, obj_);
|
|
}
|
|
|
|
static size_t offsetOfId() {
|
|
return offsetof(IonOOLPropertyOpExitFrameLayout, id_);
|
|
}
|
|
|
|
static size_t offsetOfResult() {
|
|
return offsetof(IonOOLPropertyOpExitFrameLayout, vp0_);
|
|
}
|
|
|
|
inline JitCode** stubCode() {
|
|
return &stubCode_;
|
|
}
|
|
inline Value* vp() {
|
|
return reinterpret_cast<Value*>(&vp0_);
|
|
}
|
|
inline jsid* id() {
|
|
return &id_;
|
|
}
|
|
inline JSObject** obj() {
|
|
return &obj_;
|
|
}
|
|
};
|
|
|
|
class IonOOLSetterOpExitFrameLayout : public IonOOLPropertyOpExitFrameLayout
|
|
{
|
|
protected: // only to silence a clang warning about unused private fields
|
|
JS::ObjectOpResult result_;
|
|
|
|
public:
|
|
static JitCode* Token() { return (JitCode*)IonOOLSetterOpExitFrameLayoutToken; }
|
|
|
|
static size_t offsetOfObjectOpResult() {
|
|
return offsetof(IonOOLSetterOpExitFrameLayout, result_);
|
|
}
|
|
|
|
static size_t Size() {
|
|
return sizeof(IonOOLSetterOpExitFrameLayout);
|
|
}
|
|
};
|
|
|
|
// ProxyGetProperty(JSContext* cx, HandleObject proxy, HandleId id, MutableHandleValue vp)
|
|
// ProxyCallProperty(JSContext* cx, HandleObject proxy, HandleId id, MutableHandleValue vp)
|
|
// ProxySetProperty(JSContext* cx, HandleObject proxy, HandleId id, MutableHandleValue vp,
|
|
// bool strict)
|
|
class IonOOLProxyExitFrameLayout
|
|
{
|
|
protected: // only to silence a clang warning about unused private fields
|
|
ExitFooterFrame footer_;
|
|
ExitFrameLayout exit_;
|
|
|
|
// The proxy object.
|
|
JSObject* proxy_;
|
|
|
|
// id for HandleId
|
|
jsid id_;
|
|
|
|
// space for MutableHandleValue result
|
|
// use two uint32_t so compiler doesn't align.
|
|
uint32_t vp0_;
|
|
uint32_t vp1_;
|
|
|
|
// pointer to root the stub's JitCode
|
|
JitCode* stubCode_;
|
|
|
|
public:
|
|
static JitCode* Token() { return (JitCode*)IonOOLProxyExitFrameLayoutToken; }
|
|
|
|
static inline size_t Size() {
|
|
return sizeof(IonOOLProxyExitFrameLayout);
|
|
}
|
|
|
|
static size_t offsetOfResult() {
|
|
return offsetof(IonOOLProxyExitFrameLayout, vp0_);
|
|
}
|
|
|
|
inline JitCode** stubCode() {
|
|
return &stubCode_;
|
|
}
|
|
inline Value* vp() {
|
|
return reinterpret_cast<Value*>(&vp0_);
|
|
}
|
|
inline jsid* id() {
|
|
return &id_;
|
|
}
|
|
inline JSObject** proxy() {
|
|
return &proxy_;
|
|
}
|
|
};
|
|
|
|
class IonDOMExitFrameLayout
|
|
{
|
|
protected: // only to silence a clang warning about unused private fields
|
|
ExitFooterFrame footer_;
|
|
ExitFrameLayout exit_;
|
|
JSObject* thisObj;
|
|
|
|
// We need to split the Value into 2 fields of 32 bits, otherwise the C++
|
|
// compiler may add some padding between the fields.
|
|
uint32_t loCalleeResult_;
|
|
uint32_t hiCalleeResult_;
|
|
|
|
public:
|
|
static JitCode* GetterToken() { return (JitCode*)IonDOMExitFrameLayoutGetterToken; }
|
|
static JitCode* SetterToken() { return (JitCode*)IonDOMExitFrameLayoutSetterToken; }
|
|
|
|
static inline size_t Size() {
|
|
return sizeof(IonDOMExitFrameLayout);
|
|
}
|
|
|
|
static size_t offsetOfResult() {
|
|
return offsetof(IonDOMExitFrameLayout, loCalleeResult_);
|
|
}
|
|
inline Value* vp() {
|
|
return reinterpret_cast<Value*>(&loCalleeResult_);
|
|
}
|
|
inline JSObject** thisObjAddress() {
|
|
return &thisObj;
|
|
}
|
|
inline bool isMethodFrame();
|
|
};
|
|
|
|
struct IonDOMMethodExitFrameLayoutTraits;
|
|
|
|
class IonDOMMethodExitFrameLayout
|
|
{
|
|
protected: // only to silence a clang warning about unused private fields
|
|
ExitFooterFrame footer_;
|
|
ExitFrameLayout exit_;
|
|
// This must be the last thing pushed, so as to stay common with
|
|
// IonDOMExitFrameLayout.
|
|
JSObject* thisObj_;
|
|
Value* argv_;
|
|
uintptr_t argc_;
|
|
|
|
// We need to split the Value into 2 fields of 32 bits, otherwise the C++
|
|
// compiler may add some padding between the fields.
|
|
uint32_t loCalleeResult_;
|
|
uint32_t hiCalleeResult_;
|
|
|
|
friend struct IonDOMMethodExitFrameLayoutTraits;
|
|
|
|
public:
|
|
static JitCode* Token() { return (JitCode*)IonDOMMethodExitFrameLayoutToken; }
|
|
|
|
static inline size_t Size() {
|
|
return sizeof(IonDOMMethodExitFrameLayout);
|
|
}
|
|
|
|
static size_t offsetOfResult() {
|
|
return offsetof(IonDOMMethodExitFrameLayout, loCalleeResult_);
|
|
}
|
|
|
|
inline Value* vp() {
|
|
// The code in visitCallDOMNative depends on this static assert holding
|
|
JS_STATIC_ASSERT(offsetof(IonDOMMethodExitFrameLayout, loCalleeResult_) ==
|
|
(offsetof(IonDOMMethodExitFrameLayout, argc_) + sizeof(uintptr_t)));
|
|
return reinterpret_cast<Value*>(&loCalleeResult_);
|
|
}
|
|
inline JSObject** thisObjAddress() {
|
|
return &thisObj_;
|
|
}
|
|
inline uintptr_t argc() {
|
|
return argc_;
|
|
}
|
|
};
|
|
|
|
inline bool
|
|
IonDOMExitFrameLayout::isMethodFrame()
|
|
{
|
|
return footer_.jitCode() == IonDOMMethodExitFrameLayout::Token();
|
|
}
|
|
|
|
template <>
|
|
inline bool
|
|
ExitFrameLayout::is<IonDOMExitFrameLayout>()
|
|
{
|
|
JitCode* code = footer()->jitCode();
|
|
return
|
|
code == IonDOMExitFrameLayout::GetterToken() ||
|
|
code == IonDOMExitFrameLayout::SetterToken() ||
|
|
code == IonDOMMethodExitFrameLayout::Token();
|
|
}
|
|
|
|
template <>
|
|
inline IonDOMExitFrameLayout*
|
|
ExitFrameLayout::as<IonDOMExitFrameLayout>()
|
|
{
|
|
MOZ_ASSERT(is<IonDOMExitFrameLayout>());
|
|
return reinterpret_cast<IonDOMExitFrameLayout*>(footer());
|
|
}
|
|
|
|
struct IonDOMMethodExitFrameLayoutTraits {
|
|
static const size_t offsetOfArgcFromArgv =
|
|
offsetof(IonDOMMethodExitFrameLayout, argc_) -
|
|
offsetof(IonDOMMethodExitFrameLayout, argv_);
|
|
};
|
|
|
|
// Cannot inherit implementation since we need to extend the top of
|
|
// ExitFrameLayout.
|
|
class LazyLinkExitFrameLayout
|
|
{
|
|
protected: // silence clang warning about unused private fields
|
|
JitCode* stubCode_;
|
|
ExitFooterFrame footer_;
|
|
JitFrameLayout exit_;
|
|
|
|
public:
|
|
static JitCode* Token() { return (JitCode*) LazyLinkExitFrameLayoutToken; }
|
|
|
|
static inline size_t Size() {
|
|
return sizeof(LazyLinkExitFrameLayout);
|
|
}
|
|
|
|
inline JitCode** stubCode() {
|
|
return &stubCode_;
|
|
}
|
|
inline JitFrameLayout* jsFrame() {
|
|
return &exit_;
|
|
}
|
|
static size_t offsetOfExitFrame() {
|
|
return offsetof(LazyLinkExitFrameLayout, exit_);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
inline LazyLinkExitFrameLayout*
|
|
ExitFrameLayout::as<LazyLinkExitFrameLayout>()
|
|
{
|
|
MOZ_ASSERT(is<LazyLinkExitFrameLayout>());
|
|
uint8_t* sp = reinterpret_cast<uint8_t*>(this);
|
|
sp -= LazyLinkExitFrameLayout::offsetOfExitFrame();
|
|
return reinterpret_cast<LazyLinkExitFrameLayout*>(sp);
|
|
}
|
|
|
|
class ICStub;
|
|
|
|
class JitStubFrameLayout : public CommonFrameLayout
|
|
{
|
|
// Info on the stack
|
|
//
|
|
// --------------------
|
|
// |JitStubFrameLayout|
|
|
// +------------------+
|
|
// | - Descriptor | => Marks end of JitFrame_IonJS
|
|
// | - returnaddres |
|
|
// +------------------+
|
|
// | - StubPtr | => First thing pushed in a stub only when the stub will do
|
|
// -------------------- a vmcall. Else we cannot have JitStubFrame. But technically
|
|
// not a member of the layout.
|
|
|
|
public:
|
|
static size_t Size() {
|
|
return sizeof(JitStubFrameLayout);
|
|
}
|
|
|
|
static inline int reverseOffsetOfStubPtr() {
|
|
return -int(sizeof(void*));
|
|
}
|
|
|
|
inline ICStub* maybeStubPtr() {
|
|
uint8_t* fp = reinterpret_cast<uint8_t*>(this);
|
|
return *reinterpret_cast<ICStub**>(fp + reverseOffsetOfStubPtr());
|
|
}
|
|
};
|
|
|
|
class BaselineStubFrameLayout : public JitStubFrameLayout
|
|
{
|
|
// Info on the stack
|
|
//
|
|
// -------------------------
|
|
// |BaselineStubFrameLayout|
|
|
// +-----------------------+
|
|
// | - Descriptor | => Marks end of JitFrame_BaselineJS
|
|
// | - returnaddres |
|
|
// +-----------------------+
|
|
// | - StubPtr | => First thing pushed in a stub only when the stub will do
|
|
// +-----------------------+ a vmcall. Else we cannot have BaselineStubFrame.
|
|
// | - FramePtr | => Baseline stubs also need to push the frame ptr when doing
|
|
// ------------------------- a vmcall.
|
|
// Technically these last two variables are not part of the
|
|
// layout.
|
|
|
|
public:
|
|
static inline size_t Size() {
|
|
return sizeof(BaselineStubFrameLayout);
|
|
}
|
|
|
|
static inline int reverseOffsetOfSavedFramePtr() {
|
|
return -int(2 * sizeof(void*));
|
|
}
|
|
|
|
void* reverseSavedFramePtr() {
|
|
uint8_t* addr = ((uint8_t*) this) + reverseOffsetOfSavedFramePtr();
|
|
return *(void**)addr;
|
|
}
|
|
|
|
inline void setStubPtr(ICStub* stub) {
|
|
uint8_t* fp = reinterpret_cast<uint8_t*>(this);
|
|
*reinterpret_cast<ICStub**>(fp + reverseOffsetOfStubPtr()) = stub;
|
|
}
|
|
};
|
|
|
|
// An invalidation bailout stack is at the stack pointer for the callee frame.
|
|
class InvalidationBailoutStack
|
|
{
|
|
RegisterDump::FPUArray fpregs_;
|
|
RegisterDump::GPRArray regs_;
|
|
IonScript* ionScript_;
|
|
uint8_t* osiPointReturnAddress_;
|
|
|
|
public:
|
|
uint8_t* sp() const {
|
|
return (uint8_t*) this + sizeof(InvalidationBailoutStack);
|
|
}
|
|
JitFrameLayout* fp() const;
|
|
MachineState machine() {
|
|
return MachineState::FromBailout(regs_, fpregs_);
|
|
}
|
|
|
|
IonScript* ionScript() const {
|
|
return ionScript_;
|
|
}
|
|
uint8_t* osiPointReturnAddress() const {
|
|
return osiPointReturnAddress_;
|
|
}
|
|
static size_t offsetOfFpRegs() {
|
|
return offsetof(InvalidationBailoutStack, fpregs_);
|
|
}
|
|
static size_t offsetOfRegs() {
|
|
return offsetof(InvalidationBailoutStack, regs_);
|
|
}
|
|
|
|
void checkInvariants() const;
|
|
};
|
|
|
|
void
|
|
GetPcScript(JSContext* cx, JSScript** scriptRes, jsbytecode** pcRes);
|
|
|
|
CalleeToken
|
|
MarkCalleeToken(JSTracer* trc, CalleeToken token);
|
|
|
|
// The minimum stack size is two. Two slots are needed because INITGLEXICAL
|
|
// (stack depth 1) is compiled as a SETPROP (stack depth 2) on the global
|
|
// lexical scope. Baseline also requires one slot for this/argument type
|
|
// checks.
|
|
static const uint32_t MinJITStackSize = 2;
|
|
|
|
} /* namespace jit */
|
|
} /* namespace js */
|
|
|
|
#endif /* jit_JitFrames_h */
|