зеркало из https://github.com/mozilla/gecko-dev.git
Introduce exit frames for x86 bailouts (bug 695897 part 4, r=sstangl).
This commit is contained in:
Родитель
11edef65af
Коммит
600cf43401
|
@ -388,6 +388,7 @@ CPPSRCS += MIR.cpp \
|
|||
IonMacroAssembler.cpp \
|
||||
Snapshots.cpp \
|
||||
Bailouts.cpp \
|
||||
IonFrames.cpp \
|
||||
$(NULL)
|
||||
endif #ENABLE_ION
|
||||
ifeq (86, $(findstring 86,$(TARGET_CPU)))
|
||||
|
|
|
@ -53,10 +53,10 @@
|
|||
using namespace js;
|
||||
using namespace js::ion;
|
||||
|
||||
class IonFrameIterator
|
||||
class IonBailoutIterator
|
||||
{
|
||||
IonScript *ionScript_;
|
||||
BailoutEnvironment *env_;
|
||||
FrameRecovery &in_;
|
||||
SnapshotReader reader_;
|
||||
|
||||
static Value FromTypedPayload(JSValueType type, uintptr_t payload)
|
||||
|
@ -78,14 +78,13 @@ class IonFrameIterator
|
|||
|
||||
uintptr_t fromLocation(const SnapshotReader::Location &loc) {
|
||||
if (loc.isStackSlot())
|
||||
return env_->readSlot(loc.stackSlot());
|
||||
return env_->readReg(loc.reg());
|
||||
return in_.readSlot(loc.stackSlot());
|
||||
return in_.machine().readReg(loc.reg());
|
||||
}
|
||||
|
||||
public:
|
||||
IonFrameIterator(IonScript *ionScript, BailoutEnvironment *env, const uint8 *start, const uint8 *end)
|
||||
: ionScript_(ionScript),
|
||||
env_(env),
|
||||
IonBailoutIterator(FrameRecovery &in, const uint8 *start, const uint8 *end)
|
||||
: in_(in),
|
||||
reader_(start, end)
|
||||
{
|
||||
}
|
||||
|
@ -94,17 +93,17 @@ class IonFrameIterator
|
|||
SnapshotReader::Slot slot = reader_.readSlot();
|
||||
switch (slot.mode()) {
|
||||
case SnapshotReader::DOUBLE_REG:
|
||||
return DoubleValue(env_->readFloatReg(slot.floatReg()));
|
||||
return DoubleValue(in_.machine().readFloatReg(slot.floatReg()));
|
||||
|
||||
case SnapshotReader::TYPED_REG:
|
||||
return FromTypedPayload(slot.knownType(), env_->readReg(slot.reg()));
|
||||
return FromTypedPayload(slot.knownType(), in_.machine().readReg(slot.reg()));
|
||||
|
||||
case SnapshotReader::TYPED_STACK:
|
||||
{
|
||||
JSValueType type = slot.knownType();
|
||||
if (type == JSVAL_TYPE_DOUBLE)
|
||||
return DoubleValue(env_->readDoubleSlot(slot.stackSlot()));
|
||||
return FromTypedPayload(type, env_->readSlot(slot.stackSlot()));
|
||||
return DoubleValue(in_.readDoubleSlot(slot.stackSlot()));
|
||||
return FromTypedPayload(type, in_.readSlot(slot.stackSlot()));
|
||||
}
|
||||
|
||||
case SnapshotReader::UNTYPED:
|
||||
|
@ -129,7 +128,7 @@ class IonFrameIterator
|
|||
return Int32Value(slot.int32Value());
|
||||
|
||||
case SnapshotReader::CONSTANT:
|
||||
return ionScript_->getConstant(slot.constantIndex());
|
||||
return in_.ionScript()->getConstant(slot.constantIndex());
|
||||
|
||||
default:
|
||||
JS_NOT_REACHED("huh?");
|
||||
|
@ -154,7 +153,7 @@ class IonFrameIterator
|
|||
};
|
||||
|
||||
static void
|
||||
RestoreOneFrame(JSContext *cx, StackFrame *fp, IonFrameIterator &iter)
|
||||
RestoreOneFrame(JSContext *cx, StackFrame *fp, IonBailoutIterator &iter)
|
||||
{
|
||||
uint32 exprStackSlots = iter.slots() - fp->script()->nfixed;
|
||||
|
||||
|
@ -184,37 +183,12 @@ RestoreOneFrame(JSContext *cx, StackFrame *fp, IonFrameIterator &iter)
|
|||
}
|
||||
|
||||
static bool
|
||||
ConvertFrames(JSContext *cx, IonActivation *activation, BailoutEnvironment *env)
|
||||
ConvertFrames(JSContext *cx, IonActivation *activation, FrameRecovery &in)
|
||||
{
|
||||
IonFramePrefix *top = env->top();
|
||||
|
||||
// Recover information about the callee.
|
||||
JSScript *script;
|
||||
IonScript *ionScript;
|
||||
JSFunction *fun = NULL;
|
||||
JSObject *callee = NULL;
|
||||
if (IsCalleeTokenFunction(top->calleeToken())) {
|
||||
callee = CalleeTokenToFunction(top->calleeToken());
|
||||
fun = callee->getFunctionPrivate();
|
||||
script = fun->script();
|
||||
} else {
|
||||
script = CalleeTokenToScript(top->calleeToken());
|
||||
}
|
||||
ionScript = script->ion;
|
||||
|
||||
// Recover the snapshot.
|
||||
uint32 snapshotOffset;
|
||||
if (env->frameClass() != FrameSizeClass::None()) {
|
||||
BailoutId id = env->bailoutId();
|
||||
snapshotOffset = ionScript->bailoutToSnapshot(id);
|
||||
} else {
|
||||
snapshotOffset = env->snapshotOffset();
|
||||
}
|
||||
|
||||
JS_ASSERT(snapshotOffset < ionScript->snapshotsSize());
|
||||
const uint8 *start = ionScript->snapshots() + snapshotOffset;
|
||||
const uint8 *end = ionScript->snapshots() + ionScript->snapshotsSize();
|
||||
IonFrameIterator iter(ionScript, env, start, end);
|
||||
JS_ASSERT(in.snapshotOffset() < in.ionScript()->snapshotsSize());
|
||||
const uint8 *start = in.ionScript()->snapshots() + in.snapshotOffset();
|
||||
const uint8 *end = in.ionScript()->snapshots() + in.ionScript()->snapshotsSize();
|
||||
IonBailoutIterator iter(in, start, end);
|
||||
|
||||
// It is critical to temporarily repoint the frame regs here, otherwise
|
||||
// pushing a new frame could clobber existing frames, since the stack code
|
||||
|
@ -229,16 +203,17 @@ ConvertFrames(JSContext *cx, IonActivation *activation, BailoutEnvironment *env)
|
|||
|
||||
// Non-function frames are not supported yet. We don't compile or enter
|
||||
// global scripts so this assert should not fire yet.
|
||||
JS_ASSERT(callee);
|
||||
JS_ASSERT(in.callee());
|
||||
|
||||
StackFrame *fp = cx->stack.pushBailoutFrame(cx, callee, fun, script, br->frameGuard());
|
||||
StackFrame *fp = cx->stack.pushBailoutFrame(cx, in.callee(), in.fun(), in.script(),
|
||||
br->frameGuard());
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
br->setEntryFrame(fp);
|
||||
|
||||
if (callee)
|
||||
fp->formalArgs()[-2].setObject(*callee);
|
||||
if (in.callee())
|
||||
fp->formalArgs()[-2].setObject(*in.callee());
|
||||
|
||||
for (;;) {
|
||||
RestoreOneFrame(cx, fp, iter);
|
||||
|
@ -272,21 +247,24 @@ ConvertFrames(JSContext *cx, IonActivation *activation, BailoutEnvironment *env)
|
|||
}
|
||||
|
||||
uint32
|
||||
ion::Bailout(void **sp)
|
||||
ion::Bailout(BailoutStack *sp)
|
||||
{
|
||||
JSContext *cx = GetIonContext()->cx;
|
||||
IonCompartment *ioncompartment = cx->compartment->ionCompartment();
|
||||
IonActivation *activation = ioncompartment->activation();
|
||||
BailoutEnvironment env(ioncompartment, sp);
|
||||
FrameRecovery in = FrameRecoveryFromBailout(ioncompartment, sp);
|
||||
|
||||
if (!ConvertFrames(cx, activation, &env))
|
||||
if (!ConvertFrames(cx, activation, in)) {
|
||||
if (BailoutClosure *br = activation->maybeTakeBailout())
|
||||
cx->delete_(br);
|
||||
return BAILOUT_RETURN_FATAL_ERROR;
|
||||
}
|
||||
|
||||
return BAILOUT_RETURN_OK;
|
||||
}
|
||||
|
||||
JSBool
|
||||
ion::ThunkToInterpreter(IonFramePrefix *top, Value *vp)
|
||||
ion::ThunkToInterpreter(Value *vp)
|
||||
{
|
||||
JSContext *cx = GetIonContext()->cx;
|
||||
IonActivation *activation = cx->compartment->ionCompartment()->activation();
|
||||
|
@ -307,29 +285,3 @@ ion::ThunkToInterpreter(IonFramePrefix *top, Value *vp)
|
|||
return ok ? JS_TRUE : JS_FALSE;
|
||||
}
|
||||
|
||||
uint32
|
||||
ion::HandleException(IonFramePrefix *top)
|
||||
{
|
||||
JSContext *cx = GetIonContext()->cx;
|
||||
IonCompartment *ioncompartment = cx->compartment->ionCompartment();
|
||||
|
||||
// Find the boundary between this IonActivation and the Interpreter.
|
||||
// Since try blocks are unsupported, exceptions propagate through Ion code.
|
||||
IonFramePrefix *entry = top;
|
||||
uint32 stack_adjust = 0;
|
||||
while (!entry->isEntryFrame()) {
|
||||
stack_adjust += sizeof(IonFramePrefix) + entry->prevFrameDepth();
|
||||
entry = entry->prev();
|
||||
}
|
||||
|
||||
// Currently, try blocks are not supported, so we don't have to implement
|
||||
// logic to bailout a bunch o' frames.
|
||||
if (BailoutClosure *closure = ioncompartment->activation()->maybeTakeBailout())
|
||||
cx->delete_(closure);
|
||||
|
||||
uint8 *returnAddress = ioncompartment->returnError()->raw();
|
||||
entry->setReturnAddress(returnAddress);
|
||||
|
||||
return stack_adjust;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,16 +44,7 @@
|
|||
|
||||
#include "jstypes.h"
|
||||
#include "vm/Stack.h"
|
||||
|
||||
#if defined(JS_CPU_X86)
|
||||
# include "ion/x86/Bailouts-x86.h"
|
||||
#elif defined(JS_CPU_X64)
|
||||
# include "ion/x64/Bailouts-x64.h"
|
||||
#elif defined(JS_CPU_ARM)
|
||||
# include "ion/arm/Bailouts-arm.h"
|
||||
#else
|
||||
# error "CPU!"
|
||||
#endif
|
||||
#include "IonFrames.h"
|
||||
|
||||
namespace js {
|
||||
namespace ion {
|
||||
|
@ -175,17 +166,27 @@ class BailoutClosure
|
|||
}
|
||||
};
|
||||
|
||||
class IonCompartment;
|
||||
|
||||
// BailoutStack is an architecture specific pointer to the stack, given by the
|
||||
// bailout handler.
|
||||
class BailoutStack;
|
||||
|
||||
// Must be implemented by each architecture.
|
||||
FrameRecovery
|
||||
FrameRecoveryFromBailout(IonCompartment *ion, BailoutStack *sp);
|
||||
|
||||
// Called from a bailout thunk. Returns a BAILOUT_* error code.
|
||||
uint32 Bailout(void **esp);
|
||||
uint32 Bailout(BailoutStack *sp);
|
||||
|
||||
// Called from a bailout thunk. Interprets the frame(s) that have been bailed
|
||||
// out.
|
||||
JSBool ThunkToInterpreter(IonFramePrefix *top, Value *vp);
|
||||
JSBool ThunkToInterpreter(Value *vp);
|
||||
|
||||
// Called when an error occurs in Ion code. Normally, exceptions are bailouts,
|
||||
// and pop the frame. This is called to propagate an exception through multiple
|
||||
// frames. The return value is how much stack to adjust before returning.
|
||||
uint32 HandleException(IonFramePrefix *top);
|
||||
uint32 HandleException(JSContext *cx);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -146,7 +146,6 @@ IonCompartment::IonCompartment()
|
|||
: execAlloc_(NULL),
|
||||
enterJIT_(NULL),
|
||||
bailoutHandler_(NULL),
|
||||
returnError_(NULL),
|
||||
argumentsRectifier_(NULL),
|
||||
functionWrappers_(NULL)
|
||||
{
|
||||
|
@ -175,8 +174,6 @@ IonCompartment::mark(JSTracer *trc, JSCompartment *compartment)
|
|||
// These must be available if we could be running JIT code.
|
||||
if (enterJIT_)
|
||||
MarkIonCode(trc, enterJIT_, "enterJIT");
|
||||
if (returnError_)
|
||||
MarkIonCode(trc, returnError_, "returnError");
|
||||
|
||||
// These need to be here until we can figure out how to make the GC
|
||||
// scan these references inside the code generator itself.
|
||||
|
@ -198,8 +195,6 @@ IonCompartment::sweep(JSContext *cx)
|
|||
enterJIT_ = NULL;
|
||||
if (bailoutHandler_ && IsAboutToBeFinalized(cx, bailoutHandler_))
|
||||
bailoutHandler_ = NULL;
|
||||
if (returnError_ && IsAboutToBeFinalized(cx, returnError_))
|
||||
returnError_ = NULL;
|
||||
if (argumentsRectifier_ && IsAboutToBeFinalized(cx, argumentsRectifier_))
|
||||
argumentsRectifier_ = NULL;
|
||||
|
||||
|
@ -649,7 +644,6 @@ ion::Cannon(JSContext *cx, StackFrame *fp)
|
|||
IonCode *code = ion->method();
|
||||
void *jitcode = code->raw();
|
||||
|
||||
JSBool ok;
|
||||
Value result;
|
||||
{
|
||||
AssertCompartmentUnchanged pcc(cx);
|
||||
|
@ -657,7 +651,8 @@ ion::Cannon(JSContext *cx, StackFrame *fp)
|
|||
IonActivation activation(cx, fp);
|
||||
JSAutoResolveFlags rf(cx, RESOLVE_INFER);
|
||||
|
||||
ok = enterJIT(jitcode, argc, argv, &result, calleeToken);
|
||||
enterJIT(jitcode, argc, argv, &result, calleeToken);
|
||||
JS_ASSERT_IF(result.isMagic(), result.isMagic(JS_ION_ERROR));
|
||||
}
|
||||
|
||||
JS_ASSERT(fp == cx->fp());
|
||||
|
@ -666,6 +661,6 @@ ion::Cannon(JSContext *cx, StackFrame *fp)
|
|||
fp->setReturnValue(result);
|
||||
fp->markFunctionEpilogueDone();
|
||||
|
||||
return !!ok;
|
||||
return !result.isMagic();
|
||||
}
|
||||
|
||||
|
|
|
@ -53,11 +53,12 @@ namespace ion {
|
|||
|
||||
class FrameSizeClass;
|
||||
|
||||
typedef JSBool (*EnterIonCode)(void *code, int argc, Value *argv, Value *vp,
|
||||
CalleeToken calleeToken);
|
||||
typedef void (*EnterIonCode)(void *code, int argc, Value *argv, Value *vp,
|
||||
CalleeToken calleeToken);
|
||||
|
||||
class IonActivation;
|
||||
struct VMFunction;
|
||||
class IonCFrame;
|
||||
|
||||
class IonCompartment
|
||||
{
|
||||
|
@ -79,9 +80,6 @@ class IonCompartment
|
|||
// Generic bailout table; used if the bailout table overflows.
|
||||
IonCode *bailoutHandler_;
|
||||
|
||||
// Error-returning thunk.
|
||||
IonCode *returnError_;
|
||||
|
||||
// Argument-rectifying thunk, in the case of insufficient arguments passed
|
||||
// to a function call site. Pads with |undefined|.
|
||||
IonCode *argumentsRectifier_;
|
||||
|
@ -143,17 +141,8 @@ class IonCompartment
|
|||
if (!enterJIT_)
|
||||
return NULL;
|
||||
}
|
||||
if (!returnError_) {
|
||||
returnError_ = generateReturnError(cx);
|
||||
if (!returnError_)
|
||||
return NULL;
|
||||
}
|
||||
return enterJIT_->as<EnterIonCode>();
|
||||
}
|
||||
IonCode *returnError() const {
|
||||
JS_ASSERT(returnError_);
|
||||
return returnError_;
|
||||
}
|
||||
|
||||
IonActivation *activation() const {
|
||||
return active_;
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=4 sw=4 et tw=79:
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code, released
|
||||
* March 31, 1998.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1998
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* David Anderson <dvander@alliedmods.net>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "Ion.h"
|
||||
#include "IonFrames.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsscript.h"
|
||||
#include "jsfun.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::ion;
|
||||
|
||||
FrameRecovery::FrameRecovery(uint8 *fp, uint8 *sp, const MachineState &machine)
|
||||
: fp_((IonJSFrameLayout *)fp),
|
||||
sp_(sp),
|
||||
machine_(machine)
|
||||
{
|
||||
if (IsCalleeTokenFunction(fp_->calleeToken())) {
|
||||
callee_ = CalleeTokenToFunction(fp_->calleeToken());
|
||||
fun_ = callee_->getFunctionPrivate();
|
||||
script_ = fun_->script();
|
||||
} else {
|
||||
script_ = CalleeTokenToScript(fp_->calleeToken());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FrameRecovery::setBailoutId(BailoutId bailoutId)
|
||||
{
|
||||
snapshotOffset_ = ionScript()->bailoutToSnapshot(bailoutId);
|
||||
}
|
||||
|
||||
FrameRecovery
|
||||
FrameRecovery::FromBailoutId(uint8 *fp, uint8 *sp, const MachineState &machine,
|
||||
BailoutId bailoutId)
|
||||
{
|
||||
FrameRecovery frame(fp, sp, machine);
|
||||
frame.setBailoutId(bailoutId);
|
||||
return frame;
|
||||
}
|
||||
|
||||
FrameRecovery
|
||||
FrameRecovery::FromSnapshot(uint8 *fp, uint8 *sp, const MachineState &machine,
|
||||
SnapshotOffset snapshotOffset)
|
||||
{
|
||||
FrameRecovery frame(fp, sp, machine);
|
||||
frame.setSnapshotOffset(snapshotOffset);
|
||||
return frame;
|
||||
}
|
||||
|
||||
IonScript *
|
||||
FrameRecovery::ionScript() const
|
||||
{
|
||||
return script_->ion;
|
||||
}
|
||||
|
||||
void
|
||||
IonFrameIterator::prev()
|
||||
{
|
||||
JS_ASSERT(type_ != IonFrame_Entry);
|
||||
|
||||
IonCommonFrameLayout *current = (IonCommonFrameLayout *)current_;
|
||||
|
||||
// If the next frame is the entry frame, just exit. Don't update current_,
|
||||
// since the entry and first frames overlap.
|
||||
if (current->prevType() == IonFrame_Entry) {
|
||||
type_ = IonFrame_Entry;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t currentSize;
|
||||
switch (type_) {
|
||||
case IonFrame_JS:
|
||||
currentSize = sizeof(IonJSFrameLayout);
|
||||
break;
|
||||
case IonFrame_Rectifier:
|
||||
currentSize = sizeof(IonRectifierFrameLayout);
|
||||
break;
|
||||
case IonFrame_Exit:
|
||||
currentSize = sizeof(IonExitFrameLayout);
|
||||
break;
|
||||
default:
|
||||
JS_NOT_REACHED("unexpected frame type");
|
||||
return;
|
||||
}
|
||||
currentSize += current->prevFrameLocalSize();
|
||||
|
||||
type_ = current->prevType();
|
||||
current_ += currentSize;
|
||||
}
|
||||
|
||||
void
|
||||
ion::HandleException(ResumeFromException *rfe)
|
||||
{
|
||||
JSContext *cx = GetIonContext()->cx;
|
||||
|
||||
IonFrameIterator iter(JS_THREAD_DATA(cx)->ionTop);
|
||||
while (iter.type() != IonFrame_Entry)
|
||||
iter.prev();
|
||||
|
||||
rfe->stackPointer = iter.fp();
|
||||
}
|
||||
|
|
@ -44,70 +44,44 @@
|
|||
|
||||
#include "jstypes.h"
|
||||
#include "jsutil.h"
|
||||
|
||||
#if defined(JS_CPU_X86) || defined (JS_CPU_X64)
|
||||
# include "ion/shared/IonFrames-x86-shared.h"
|
||||
#elif defined (JS_CPU_ARM)
|
||||
# include "ion/arm/IonFrames-arm.h"
|
||||
#else
|
||||
# error "unsupported architecture"
|
||||
#endif
|
||||
#include "IonRegisters.h"
|
||||
|
||||
struct JSFunction;
|
||||
struct JSScript;
|
||||
|
||||
namespace js {
|
||||
namespace ion {
|
||||
// The layout of an Ion frame on the C stack:
|
||||
// argN _
|
||||
// ... \ - These are jsvals
|
||||
// arg0 /
|
||||
// this _/
|
||||
// (padding) (only exists on arm)
|
||||
// calleeToken - Encodes script or JSFunction
|
||||
// sizeDescriptor - Delta between the /top/ of the current IonFramePrefix
|
||||
// and the /bottom/ of the parent IonFramePrefix.
|
||||
// The lower FrameType bits are hijacked to store type.
|
||||
// returnAddr - Return address, entering into the next call.
|
||||
|
||||
// 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 ion::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.
|
||||
|
||||
// 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 ..
|
||||
|
||||
class IonFramePrefix : protected IonFrameData
|
||||
enum FrameType
|
||||
{
|
||||
public:
|
||||
enum FrameType {
|
||||
JSFrame,
|
||||
EntryFrame,
|
||||
RectifierFrame
|
||||
};
|
||||
|
||||
// The FrameType is packed into the sizeDescriptor by left-shifting the
|
||||
// sizeDescriptor by FrameTypeBits, then ORing in the FrameType.
|
||||
static const unsigned FrameTypeBits = 2;
|
||||
|
||||
public:
|
||||
// True if this is the frame passed into EnterIonCode.
|
||||
bool isEntryFrame() const {
|
||||
return !!(sizeDescriptor_ & EntryFrame);
|
||||
}
|
||||
// Distance from top of current IonFramePrefix to parent IonFramePrefix.
|
||||
size_t prevFrameDepth() const {
|
||||
JS_ASSERT(!isEntryFrame());
|
||||
return sizeDescriptor_ >> FrameTypeBits;
|
||||
}
|
||||
IonFramePrefix *prev() const {
|
||||
JS_ASSERT(!isEntryFrame());
|
||||
return (IonFramePrefix *)((uint8 *)this +
|
||||
sizeof(IonFramePrefix) + prevFrameDepth());
|
||||
}
|
||||
void *calleeToken() const {
|
||||
return calleeToken_;
|
||||
}
|
||||
void setReturnAddress(void *address) {
|
||||
returnAddress_ = address;
|
||||
}
|
||||
IonFrame_JS,
|
||||
IonFrame_Entry,
|
||||
IonFrame_Rectifier,
|
||||
IonFrame_Exit
|
||||
};
|
||||
|
||||
static const uint32 ION_FRAME_PREFIX_SIZE = sizeof(IonFramePrefix);
|
||||
static const uint32 FRAMETYPE_BITS = 3;
|
||||
|
||||
// Ion frames have a few important numbers associated with them:
|
||||
// Local depth: The number of bytes required to spill local variables.
|
||||
|
@ -167,6 +141,122 @@ class FrameSizeClass
|
|||
}
|
||||
};
|
||||
|
||||
class IonFrameIterator
|
||||
{
|
||||
uint8 *current_;
|
||||
FrameType type_;
|
||||
|
||||
public:
|
||||
IonFrameIterator(uint8 *top)
|
||||
: current_(top),
|
||||
type_(IonFrame_Exit)
|
||||
{ }
|
||||
|
||||
FrameType type() const {
|
||||
return type_;
|
||||
}
|
||||
uint8 *fp() const {
|
||||
return current_;
|
||||
}
|
||||
|
||||
void prev();
|
||||
};
|
||||
|
||||
// Information needed to recover machine register state.
|
||||
class MachineState
|
||||
{
|
||||
uintptr_t *regs_;
|
||||
double *fpregs_;
|
||||
|
||||
public:
|
||||
MachineState()
|
||||
: regs_(NULL), fpregs_(NULL)
|
||||
{ }
|
||||
MachineState(uintptr_t *regs, double *fpregs)
|
||||
: regs_(regs), fpregs_(fpregs)
|
||||
{ }
|
||||
|
||||
double readFloatReg(FloatRegister reg) const {
|
||||
return fpregs_[reg.code()];
|
||||
}
|
||||
uintptr_t readReg(Register reg) const {
|
||||
return regs_[reg.code()];
|
||||
}
|
||||
};
|
||||
|
||||
// Duplicated from Bailouts.h, which we can't include here.
|
||||
typedef uint32 BailoutId;
|
||||
typedef uint32 SnapshotOffset;
|
||||
|
||||
class IonJSFrameLayout;
|
||||
|
||||
// Information needed to iterate the Ion stack.
|
||||
class FrameRecovery
|
||||
{
|
||||
IonJSFrameLayout *fp_;
|
||||
uint8 *sp_; // fp_ + frameSize
|
||||
|
||||
MachineState machine_;
|
||||
uint32 snapshotOffset_;
|
||||
|
||||
JSObject *callee_;
|
||||
JSFunction *fun_;
|
||||
JSScript *script_;
|
||||
|
||||
private:
|
||||
FrameRecovery(uint8 *fp, uint8 *sp, const MachineState &machine);
|
||||
|
||||
void setSnapshotOffset(uint32 snapshotOffset) {
|
||||
snapshotOffset_ = snapshotOffset;
|
||||
}
|
||||
void setBailoutId(BailoutId bailoutId);
|
||||
|
||||
public:
|
||||
static FrameRecovery FromBailoutId(uint8 *fp, uint8 *sp, const MachineState &machine,
|
||||
BailoutId bailoutId);
|
||||
static FrameRecovery FromSnapshot(uint8 *fp, uint8 *sp, const MachineState &machine,
|
||||
SnapshotOffset offset);
|
||||
|
||||
MachineState &machine() {
|
||||
return machine_;
|
||||
}
|
||||
uintptr_t readSlot(uint32 offset) const {
|
||||
JS_ASSERT((offset % STACK_SLOT_SIZE) == 0);
|
||||
return *(uintptr_t *)(sp_ + offset);
|
||||
}
|
||||
double readDoubleSlot(uint32 offset) const {
|
||||
JS_ASSERT((offset % STACK_SLOT_SIZE) == 0);
|
||||
return *(double *)(sp_ + offset);
|
||||
}
|
||||
JSObject *callee() const {
|
||||
return callee_;
|
||||
}
|
||||
JSFunction *fun() const {
|
||||
return fun_;
|
||||
}
|
||||
JSScript *script() const {
|
||||
return script_;
|
||||
}
|
||||
IonScript *ionScript() const;
|
||||
uint32 snapshotOffset() const {
|
||||
return snapshotOffset_;
|
||||
}
|
||||
};
|
||||
|
||||
// Data needed to recover from an exception.
|
||||
struct ResumeFromException
|
||||
{
|
||||
void *stackPointer;
|
||||
};
|
||||
|
||||
void HandleException(ResumeFromException *rfe);
|
||||
|
||||
static inline uint32
|
||||
MakeFrameDescriptor(uint32 frameSize, FrameType type)
|
||||
{
|
||||
return (frameSize << FRAMETYPE_BITS) | type;
|
||||
}
|
||||
|
||||
typedef void * CalleeToken;
|
||||
|
||||
static inline CalleeToken
|
||||
|
@ -200,5 +290,13 @@ CalleeTokenToScript(CalleeToken token)
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(JS_CPU_X86) || defined (JS_CPU_X64)
|
||||
# include "ion/shared/IonFrames-x86-shared.h"
|
||||
#elif defined (JS_CPU_ARM)
|
||||
# include "ion/arm/IonFrames-arm.h"
|
||||
#else
|
||||
# error "unsupported architecture"
|
||||
#endif
|
||||
|
||||
#endif // jsion_frames_h__
|
||||
|
||||
|
|
|
@ -105,3 +105,4 @@ template void MacroAssembler::guardTypeSet(const Address &address, types::TypeSe
|
|||
Register scratch, Label *mismatched);
|
||||
template void MacroAssembler::guardTypeSet(const ValueOperand &value, types::TypeSet *types,
|
||||
Register scratch, Label *mismatched);
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ class CodeGeneratorShared : public LInstructionVisitor
|
|||
// For arguments to the current function.
|
||||
inline int32 ArgToStackOffset(int32 slot) const {
|
||||
JS_ASSERT(slot >= 0);
|
||||
return masm.framePushed() + ION_FRAME_PREFIX_SIZE + slot;
|
||||
return masm.framePushed() + sizeof(IonJSFrameLayout) + slot;
|
||||
}
|
||||
|
||||
inline int32 SlotToStackOffset(int32 slot) const {
|
||||
|
|
|
@ -783,9 +783,8 @@ CodeGeneratorX86Shared::visitCallGeneric(LCallGeneric *call)
|
|||
return false;
|
||||
|
||||
// Remember the size of the frame above this point, in case of bailout.
|
||||
JS_STATIC_ASSERT(IonFramePrefix::JSFrame == 0x0);
|
||||
uint32 stack_size = masm.framePushed() - unused_stack;
|
||||
uint32 size_descriptor = stack_size << IonFramePrefix::FrameTypeBits;
|
||||
uint32 size_descriptor = (stack_size << FRAMETYPE_BITS) | IonFrame_JS;
|
||||
|
||||
// Nestle %esp up to the argument vector.
|
||||
if (unused_stack)
|
||||
|
|
|
@ -38,19 +38,58 @@
|
|||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
#ifndef js_ion_frame_layouts_x86_h__
|
||||
#define js_ion_frame_layouts_x86_h__
|
||||
|
||||
namespace js {
|
||||
namespace ion {
|
||||
|
||||
class IonFramePrefix;
|
||||
|
||||
// Layout of the frame prefix. This assumes the stack architecture grows down.
|
||||
// If this is ever not the case, we'll have to refactor.
|
||||
struct IonFrameData
|
||||
class IonCommonFrameLayout
|
||||
{
|
||||
private:
|
||||
void *returnAddress_;
|
||||
uintptr_t sizeDescriptor_;
|
||||
uintptr_t descriptor_;
|
||||
|
||||
public:
|
||||
static size_t offsetOfDescriptor() {
|
||||
return offsetof(IonCommonFrameLayout, descriptor_);
|
||||
}
|
||||
FrameType prevType() const {
|
||||
return FrameType(descriptor_ & ((1 << FRAMETYPE_BITS) - 1));
|
||||
}
|
||||
size_t prevFrameLocalSize() const {
|
||||
return descriptor_ >> FRAMETYPE_BITS;
|
||||
}
|
||||
};
|
||||
|
||||
class IonEntryFrameLayout : public IonCommonFrameLayout
|
||||
{
|
||||
private:
|
||||
};
|
||||
|
||||
class IonJSFrameLayout : public IonCommonFrameLayout
|
||||
{
|
||||
private:
|
||||
void *calleeToken_;
|
||||
|
||||
public:
|
||||
void *calleeToken() const {
|
||||
return calleeToken_;
|
||||
}
|
||||
|
||||
static size_t offsetOfCalleeToken() {
|
||||
return offsetof(IonJSFrameLayout, calleeToken_);
|
||||
}
|
||||
};
|
||||
|
||||
class IonRectifierFrameLayout : public IonJSFrameLayout
|
||||
{
|
||||
};
|
||||
|
||||
class IonExitFrameLayout : public IonCommonFrameLayout
|
||||
{
|
||||
};
|
||||
|
||||
// This Frame is constructed when JS jited code calls a C function.
|
||||
|
@ -64,3 +103,6 @@ struct IonCFrame
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // js_ion_frame_layouts_x86_h__
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ class Registers {
|
|||
static const Code Invalid = JSC::X86Registers::invalid_reg;
|
||||
|
||||
static const uint32 Total = 8;
|
||||
static const uint32 Allocatable = 6;
|
||||
static const uint32 Allocatable = 7;
|
||||
|
||||
static const uint32 AllMask = (1 << Total) - 1;
|
||||
|
||||
|
|
|
@ -236,9 +236,11 @@ class Assembler : public AssemblerX86Shared
|
|||
masm.movl_i32r(ptr.value, dest.code());
|
||||
writeDataRelocation(masm.currentOffset());
|
||||
}
|
||||
|
||||
void mov(const Imm32 &imm32, const Register &dest) {
|
||||
movl(imm32, dest);
|
||||
void movl(ImmWord imm, Register dest) {
|
||||
masm.movl_i32r(imm.value, dest.code());
|
||||
}
|
||||
void mov(Imm32 imm, Register dest) {
|
||||
movl(imm, dest);
|
||||
}
|
||||
void mov(const Operand &src, const Register &dest) {
|
||||
movl(src, dest);
|
||||
|
|
|
@ -47,47 +47,79 @@
|
|||
using namespace js;
|
||||
using namespace js::ion;
|
||||
|
||||
static const uintptr_t BAILOUT_TABLE_ENTRY_SIZE = 5;
|
||||
#if defined(_WIN32)
|
||||
# pragma pack(push, 1)
|
||||
#endif
|
||||
|
||||
JS_STATIC_ASSERT(sizeof(BailoutStack) ==
|
||||
sizeof(uintptr_t) +
|
||||
sizeof(double) * 8 +
|
||||
sizeof(uintptr_t) * 8 +
|
||||
sizeof(uintptr_t));
|
||||
namespace js {
|
||||
namespace ion {
|
||||
|
||||
JS_STATIC_ASSERT(sizeof(ExtendedBailoutStack) ==
|
||||
sizeof(BailoutStack) +
|
||||
sizeof(uintptr_t));
|
||||
|
||||
BailoutEnvironment::BailoutEnvironment(IonCompartment *ion, void **esp)
|
||||
: esp_(esp)
|
||||
class BailoutStack
|
||||
{
|
||||
bailout_ = reinterpret_cast<ExtendedBailoutStack *>(esp);
|
||||
uintptr_t frameClassId_;
|
||||
double fpregs_[FloatRegisters::Total];
|
||||
uintptr_t regs_[Registers::Total];
|
||||
union {
|
||||
uintptr_t frameSize_;
|
||||
uintptr_t tableOffset_;
|
||||
};
|
||||
uintptr_t snapshotOffset_;
|
||||
|
||||
if (bailout_->frameClass() != FrameSizeClass::None()) {
|
||||
frameSize_ = bailout_->frameSize();
|
||||
frame_ = &esp_[sizeof(BailoutStack) / STACK_SLOT_SIZE];
|
||||
|
||||
// Compute the bailout ID.
|
||||
IonCode *code = ion->getBailoutTable(bailout_->frameClass());
|
||||
uintptr_t tableOffset = bailout_->tableOffset();
|
||||
uintptr_t tableStart = reinterpret_cast<uintptr_t>(code->raw());
|
||||
|
||||
JS_ASSERT(tableOffset >= tableStart &&
|
||||
tableOffset < tableStart + code->instructionsSize());
|
||||
JS_ASSERT((tableOffset - tableStart) % BAILOUT_TABLE_ENTRY_SIZE == 0);
|
||||
|
||||
bailoutId_ = ((tableOffset - tableStart) / BAILOUT_TABLE_ENTRY_SIZE) - 1;
|
||||
JS_ASSERT(bailoutId_ < BAILOUT_TABLE_SIZE);
|
||||
} else {
|
||||
frameSize_ = bailout_->frameSize();
|
||||
frame_ = &esp_[sizeof(ExtendedBailoutStack) / STACK_SLOT_SIZE];
|
||||
public:
|
||||
FrameSizeClass frameClass() const {
|
||||
return FrameSizeClass::FromClass(frameClassId_);
|
||||
}
|
||||
}
|
||||
uintptr_t tableOffset() const {
|
||||
JS_ASSERT(frameClass() != FrameSizeClass::None());
|
||||
return tableOffset_;
|
||||
}
|
||||
uint32 frameSize() const {
|
||||
if (frameClass() == FrameSizeClass::None())
|
||||
return frameSize_;
|
||||
return frameClass().frameSize();
|
||||
}
|
||||
MachineState machine() {
|
||||
return MachineState(regs_, fpregs_);
|
||||
}
|
||||
SnapshotOffset snapshotOffset() const {
|
||||
JS_ASSERT(frameClass() == FrameSizeClass::None());
|
||||
return snapshotOffset_;
|
||||
}
|
||||
uint8 *parentStackPointer() const {
|
||||
if (frameClass() == FrameSizeClass::None())
|
||||
return (uint8 *)this + sizeof(BailoutStack);
|
||||
return (uint8 *)this + offsetof(BailoutStack, snapshotOffset_);
|
||||
}
|
||||
};
|
||||
|
||||
IonFramePrefix *
|
||||
BailoutEnvironment::top() const
|
||||
} // namespace ion
|
||||
} // namespace js
|
||||
|
||||
#if defined(_WIN32)
|
||||
# pragma pack(pop)
|
||||
#endif
|
||||
|
||||
FrameRecovery
|
||||
ion::FrameRecoveryFromBailout(IonCompartment *ion, BailoutStack *bailout)
|
||||
{
|
||||
return (IonFramePrefix *)&frame_[frameSize_ / STACK_SLOT_SIZE];
|
||||
uint8 *sp = bailout->parentStackPointer();
|
||||
uint8 *fp = sp + bailout->frameSize();
|
||||
|
||||
if (bailout->frameClass() == FrameSizeClass::None())
|
||||
return FrameRecovery::FromSnapshot(fp, sp, bailout->machine(), bailout->snapshotOffset());
|
||||
|
||||
// Compute the bailout ID.
|
||||
IonCode *code = ion->getBailoutTable(bailout->frameClass());
|
||||
uintptr_t tableOffset = bailout->tableOffset();
|
||||
uintptr_t tableStart = reinterpret_cast<uintptr_t>(code->raw());
|
||||
|
||||
JS_ASSERT(tableOffset >= tableStart &&
|
||||
tableOffset < tableStart + code->instructionsSize());
|
||||
JS_ASSERT((tableOffset - tableStart) % BAILOUT_TABLE_ENTRY_SIZE == 0);
|
||||
|
||||
uint32 bailoutId = ((tableOffset - tableStart) / BAILOUT_TABLE_ENTRY_SIZE) - 1;
|
||||
JS_ASSERT(bailoutId < BAILOUT_TABLE_SIZE);
|
||||
|
||||
return FrameRecovery::FromBailoutId(fp, sp, bailout->machine(), bailoutId);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,145 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=4 sw=4 et tw=79:
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code, released
|
||||
* March 31, 1998.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1998
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* David Anderson <dvander@alliedmods.net>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef jsion_bailouts_x86_h__
|
||||
#define jsion_bailouts_x86_h__
|
||||
|
||||
#include "ion/IonFrames.h"
|
||||
#include "ion/IonMacroAssembler.h"
|
||||
|
||||
namespace js {
|
||||
namespace ion {
|
||||
|
||||
class IonCompartment;
|
||||
|
||||
#if defined(_WIN32)
|
||||
# pragma pack(push, 1)
|
||||
#endif
|
||||
|
||||
class BailoutStack
|
||||
{
|
||||
uintptr_t frameClassId_;
|
||||
double fpregs_[FloatRegisters::Total];
|
||||
uintptr_t regs_[Registers::Total];
|
||||
union {
|
||||
uintptr_t frameSize_;
|
||||
uintptr_t tableOffset_;
|
||||
};
|
||||
|
||||
public:
|
||||
FrameSizeClass frameClass() const {
|
||||
return FrameSizeClass::FromClass(frameClassId_);
|
||||
}
|
||||
double readFloatReg(const FloatRegister ®) const {
|
||||
return fpregs_[reg.code()];
|
||||
}
|
||||
uintptr_t readReg(const Register ®) const {
|
||||
return regs_[reg.code()];
|
||||
}
|
||||
uintptr_t tableOffset() const {
|
||||
JS_ASSERT(frameClass() != FrameSizeClass::None());
|
||||
return tableOffset_;
|
||||
}
|
||||
uint32 frameSize() const {
|
||||
if (frameClass() == FrameSizeClass::None())
|
||||
return frameSize_;
|
||||
return frameClass().frameSize();
|
||||
}
|
||||
};
|
||||
|
||||
class ExtendedBailoutStack : public BailoutStack
|
||||
{
|
||||
uintptr_t snapshotOffset_;
|
||||
|
||||
public:
|
||||
SnapshotOffset snapshotOffset() const {
|
||||
JS_ASSERT(frameClass() == FrameSizeClass::None());
|
||||
return snapshotOffset_;
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(_WIN32)
|
||||
# pragma pack(pop)
|
||||
#endif
|
||||
|
||||
class BailoutEnvironment
|
||||
{
|
||||
void **esp_;
|
||||
void **frame_;
|
||||
const ExtendedBailoutStack *bailout_;
|
||||
uint32 frameSize_;
|
||||
uint32 bailoutId_;
|
||||
|
||||
public:
|
||||
BailoutEnvironment(IonCompartment *ion, void **esp);
|
||||
|
||||
IonFramePrefix *top() const;
|
||||
FrameSizeClass frameClass() const {
|
||||
return bailout_->frameClass();
|
||||
}
|
||||
uint32 bailoutId() const {
|
||||
JS_ASSERT(frameClass() != FrameSizeClass::None());
|
||||
return bailoutId_;
|
||||
}
|
||||
uint32 snapshotOffset() const {
|
||||
return bailout_->snapshotOffset();
|
||||
}
|
||||
uintptr_t readSlot(uint32 offset) const {
|
||||
JS_ASSERT(offset % STACK_SLOT_SIZE == 0);
|
||||
return *(uintptr_t *)((uint8 *)frame_ + offset);
|
||||
}
|
||||
double readDoubleSlot(uint32 offset) const {
|
||||
JS_ASSERT(offset % STACK_SLOT_SIZE == 0);
|
||||
return *(double *)((uint8 *)frame_ + offset);
|
||||
}
|
||||
uintptr_t readReg(const Register ®) const {
|
||||
return bailout_->readReg(reg);
|
||||
}
|
||||
double readFloatReg(const FloatRegister ®) const {
|
||||
return bailout_->readFloatReg(reg);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ion
|
||||
} // namespace js
|
||||
|
||||
#endif // jsion_bailouts_x86_h__
|
||||
|
|
@ -41,6 +41,7 @@
|
|||
|
||||
#include "MacroAssembler-x86.h"
|
||||
#include "ion/MoveEmitter.h"
|
||||
#include "ion/IonFrames.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::ion;
|
||||
|
@ -143,3 +144,21 @@ MacroAssemblerX86::callWithABI(void *fun)
|
|||
inCall_ = false;
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerX86::handleException()
|
||||
{
|
||||
// Reserve space for exception information.
|
||||
subl(Imm32(sizeof(ResumeFromException)), esp);
|
||||
movl(esp, eax);
|
||||
|
||||
// Ask for an exception handler.
|
||||
setupUnalignedABICall(1, ecx);
|
||||
setABIArg(0, eax);
|
||||
callWithABI(JS_FUNC_TO_DATA_PTR(void *, ion::HandleException));
|
||||
|
||||
// Load the error value, load the new stack pointer, and return.
|
||||
moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
|
||||
movl(Operand(esp, offsetof(ResumeFromException, stackPointer)), esp);
|
||||
ret();
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#define jsion_macro_assembler_x86_h__
|
||||
|
||||
#include "ion/shared/MacroAssembler-x86-shared.h"
|
||||
#include "ion/IonFrames.h"
|
||||
#include "ion/MoveResolver.h"
|
||||
|
||||
namespace js {
|
||||
|
@ -105,6 +106,9 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
|||
movl(Imm32(jv.s.tag), type);
|
||||
movl(Imm32(jv.s.payload.i32), data);
|
||||
}
|
||||
void moveValue(const Value &val, const ValueOperand &dest) {
|
||||
moveValue(val, dest.typeReg(), dest.payloadReg());
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// X86/X64-common interface.
|
||||
|
@ -389,6 +393,14 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
|||
|
||||
// Emits a call to a C/C++ function, resolving all argument moves.
|
||||
void callWithABI(void *fun);
|
||||
|
||||
// Used from within an Exit frame to handle a pending exception.
|
||||
void handleException();
|
||||
|
||||
void makeFrameDescriptor(Register frameSizeReg, FrameType type) {
|
||||
shll(Imm32(FRAMETYPE_BITS), frameSizeReg);
|
||||
orl(Imm32(type), frameSizeReg);
|
||||
}
|
||||
};
|
||||
|
||||
typedef MacroAssemblerX86 MacroAssemblerSpecific;
|
||||
|
|
|
@ -49,20 +49,6 @@
|
|||
using namespace js;
|
||||
using namespace js::ion;
|
||||
|
||||
static void
|
||||
GenerateReturn(MacroAssembler &masm, int returnCode)
|
||||
{
|
||||
// Restore non-volatile registers
|
||||
masm.pop(edi);
|
||||
masm.pop(esi);
|
||||
masm.pop(ebx);
|
||||
|
||||
// Restore old stack frame pointer
|
||||
masm.pop(ebp);
|
||||
masm.movl(Imm32(returnCode), eax);
|
||||
masm.ret();
|
||||
}
|
||||
|
||||
/* This method generates a trampoline on x86 for a c++ function with
|
||||
* the following signature:
|
||||
* JSBool blah(void *code, int argc, Value *argv, Value *vp)
|
||||
|
@ -142,8 +128,8 @@ IonCompartment::generateEnterJIT(JSContext *cx)
|
|||
masm.addl(eax, ecx);
|
||||
masm.addl(Imm32(4), ecx);
|
||||
|
||||
// Safe to take lowest bit without shifting: aligned, and no other consumers.
|
||||
masm.orl(Imm32(0x1), ecx); // Mark EntryFrame.
|
||||
// Create a frame descriptor.
|
||||
masm.makeFrameDescriptor(ecx, IonFrame_Entry);
|
||||
masm.push(ecx);
|
||||
|
||||
/***************************************************************
|
||||
|
@ -156,7 +142,7 @@ IonCompartment::generateEnterJIT(JSContext *cx)
|
|||
// Pop arguments off the stack.
|
||||
// eax <- 8*argc (size of all arugments we pushed on the stack)
|
||||
masm.pop(eax);
|
||||
masm.xorl(Imm32(0x1), eax); // Unmark EntryFrame.
|
||||
masm.shrl(Imm32(FRAMETYPE_BITS), eax); // Unmark EntryFrame.
|
||||
masm.addl(eax, esp);
|
||||
|
||||
// |ebp| could have been clobbered by the inner function. For now, re-grab
|
||||
|
@ -177,23 +163,15 @@ IonCompartment::generateEnterJIT(JSContext *cx)
|
|||
/**************************************************************
|
||||
Return stack and registers to correct state
|
||||
**************************************************************/
|
||||
GenerateReturn(masm, JS_TRUE);
|
||||
// Restore non-volatile registers
|
||||
masm.pop(edi);
|
||||
masm.pop(esi);
|
||||
masm.pop(ebx);
|
||||
|
||||
Linker linker(masm);
|
||||
return linker.newCode(cx);
|
||||
}
|
||||
// Restore old stack frame pointer
|
||||
masm.pop(ebp);
|
||||
masm.ret();
|
||||
|
||||
IonCode *
|
||||
IonCompartment::generateReturnError(JSContext *cx)
|
||||
{
|
||||
MacroAssembler masm(cx);
|
||||
|
||||
masm.pop(eax); // sizeDescriptor.
|
||||
masm.xorl(Imm32(0x1), eax); // Unmark EntryFrame.
|
||||
masm.addl(eax, esp); // Remove arguments.
|
||||
|
||||
GenerateReturn(masm, JS_FALSE);
|
||||
|
||||
Linker linker(masm);
|
||||
return linker.newCode(cx);
|
||||
}
|
||||
|
@ -208,7 +186,7 @@ IonCompartment::generateArgumentsRectifier(JSContext *cx)
|
|||
JS_ASSERT(ArgumentsRectifierReg == esi);
|
||||
|
||||
// Load the number of |undefined|s to push into %ecx.
|
||||
masm.movl(Operand(esp, offsetof(IonFrameData, calleeToken_)), eax);
|
||||
masm.movl(Operand(esp, IonJSFrameLayout::offsetOfCalleeToken()), eax);
|
||||
masm.load16(Operand(eax, offsetof(JSFunction, nargs)), ecx);
|
||||
masm.subl(esi, ecx);
|
||||
|
||||
|
@ -234,7 +212,7 @@ IonCompartment::generateArgumentsRectifier(JSContext *cx)
|
|||
masm.shll(Imm32(3), edi); // edi <- nargs * sizeof(Value);
|
||||
|
||||
masm.movl(ebp, ecx);
|
||||
masm.addl(Imm32(sizeof(IonFrameData)), ecx);
|
||||
masm.addl(Imm32(sizeof(IonRectifierFrameLayout)), ecx);
|
||||
masm.addl(edi, ecx);
|
||||
|
||||
// Push arguments, |nargs| + 1 times (to include |this|).
|
||||
|
@ -257,14 +235,13 @@ IonCompartment::generateArgumentsRectifier(JSContext *cx)
|
|||
masm.j(Assembler::NonZero, ©LoopTop);
|
||||
}
|
||||
|
||||
// Construct sizeDescriptor.
|
||||
// Construct descriptor.
|
||||
masm.subl(esp, ebp);
|
||||
masm.shll(Imm32(IonFramePrefix::FrameTypeBits), ebp);
|
||||
masm.orl(Imm32(IonFramePrefix::RectifierFrame), ebp);
|
||||
masm.makeFrameDescriptor(ebp, IonFrame_Rectifier);
|
||||
|
||||
// Construct IonFrameData.
|
||||
masm.push(eax); // calleeToken
|
||||
masm.push(ebp); // sizeDescriptor
|
||||
masm.push(ebp); // descriptor
|
||||
|
||||
// Call the target function.
|
||||
// Note that this assumes the function is JITted.
|
||||
|
@ -275,8 +252,8 @@ IonCompartment::generateArgumentsRectifier(JSContext *cx)
|
|||
masm.call(eax);
|
||||
|
||||
// Remove the rectifier frame.
|
||||
masm.pop(ebp); // ebp <- sizeDescriptor with FrameType.
|
||||
masm.shrl(Imm32(IonFramePrefix::FrameTypeBits), ebp); // ebp <- sizeDescriptor.
|
||||
masm.pop(ebp); // ebp <- descriptor with FrameType.
|
||||
masm.shrl(Imm32(FRAMETYPE_BITS), ebp); // ebp <- descriptor.
|
||||
masm.pop(edi); // Discard calleeToken.
|
||||
masm.addl(ebp, esp); // Discard pushed arguments.
|
||||
|
||||
|
@ -287,7 +264,7 @@ IonCompartment::generateArgumentsRectifier(JSContext *cx)
|
|||
}
|
||||
|
||||
static void
|
||||
GenerateBailoutThunk(MacroAssembler &masm, uint32 frameClass)
|
||||
GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32 frameClass)
|
||||
{
|
||||
// Push registers such that we can access them from [base + code].
|
||||
masm.reserveStack(Registers::Total * sizeof(void *));
|
||||
|
@ -302,62 +279,87 @@ GenerateBailoutThunk(MacroAssembler &masm, uint32 frameClass)
|
|||
// Push the bailout table number.
|
||||
masm.push(Imm32(frameClass));
|
||||
|
||||
// Get the stack pointer into a register, pre-alignment.
|
||||
// The current stack pointer is the first argument to ion::Bailout.
|
||||
masm.movl(esp, eax);
|
||||
|
||||
// Call the bailout function.
|
||||
// Call the bailout function. This will correct the size of the bailout.
|
||||
masm.setupUnalignedABICall(1, ecx);
|
||||
masm.setABIArg(0, eax);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, Bailout));
|
||||
|
||||
// Common size of a bailout frame.
|
||||
uint32 bailoutFrameSize = sizeof(void *) + // frameClass
|
||||
sizeof(double) * FloatRegisters::Total +
|
||||
sizeof(void *) * Registers::Total;
|
||||
// Remove the Ion frame.
|
||||
// Common size of stuff we've pushed.
|
||||
const uint32 BailoutDataSize = sizeof(void *) + // frameClass
|
||||
sizeof(double) * FloatRegisters::Total +
|
||||
sizeof(void *) * Registers::Total;
|
||||
|
||||
// Remove both the bailout frame and the topmost Ion frame's stack.
|
||||
if (frameClass == NO_FRAME_SIZE_CLASS_ID) {
|
||||
// Stack is:
|
||||
// We want the frameSize. Stack is:
|
||||
// ... frame ...
|
||||
// snapshotOffset
|
||||
// frameSize
|
||||
// ... bailoutFrame ...
|
||||
masm.addl(Imm32(bailoutFrameSize), esp);
|
||||
masm.addl(Imm32(BailoutDataSize), esp);
|
||||
masm.pop(ecx);
|
||||
masm.lea(Operand(esp, ecx, TimesOne, sizeof(void *)), esp);
|
||||
masm.addl(Imm32(sizeof(uint32)), esp);
|
||||
masm.addl(ecx, esp);
|
||||
} else {
|
||||
// Stack is:
|
||||
// ... frame ...
|
||||
// bailoutId
|
||||
// ... bailoutFrame ...
|
||||
uint32 frameSize = FrameSizeClass::FromClass(frameClass).frameSize();
|
||||
masm.addl(Imm32(bailoutFrameSize + sizeof(void *) + frameSize), esp);
|
||||
masm.addl(Imm32(BailoutDataSize + sizeof(void *) + frameSize), esp);
|
||||
}
|
||||
|
||||
// Convert the remaining header to an exit frame header. Stack is:
|
||||
// ...
|
||||
// +4 descriptor
|
||||
// +0 returnAddress
|
||||
//
|
||||
// Test the descriptor to see if the previous frame is a JS frame. If it
|
||||
// is, we need to rewrite it to account for the difference between
|
||||
// the sizes of an Exit and JS frame. Otherwise, IonFrameIterator will
|
||||
// assume sizeof(ExitFrame) accounts for the entire header, and end up off
|
||||
// by one word.
|
||||
Label frameFixupDone;
|
||||
masm.movl(Operand(esp, IonCommonFrameLayout::offsetOfDescriptor()), ecx);
|
||||
masm.movl(ecx, edx);
|
||||
masm.andl(Imm32(FRAMETYPE_BITS), edx);
|
||||
masm.cmpl(edx, Imm32(IonFrame_JS));
|
||||
masm.j(Assembler::NotEqual, &frameFixupDone);
|
||||
{
|
||||
JS_STATIC_ASSERT(sizeof(IonJSFrameLayout) >= sizeof(IonExitFrameLayout));
|
||||
ptrdiff_t difference = sizeof(IonJSFrameLayout) - sizeof(IonExitFrameLayout);
|
||||
masm.addl(Imm32(difference << FRAMETYPE_BITS), ecx);
|
||||
masm.movl(ecx, Operand(esp, IonCommonFrameLayout::offsetOfDescriptor()));
|
||||
}
|
||||
masm.bind(&frameFixupDone);
|
||||
|
||||
// Store to ThreadData::ionTop. Note that eax still holds the return value
|
||||
// from ion::Bailout.
|
||||
masm.movl(ImmWord(JS_THREAD_DATA(cx)), edx);
|
||||
masm.movl(esp, Operand(edx, offsetof(ThreadData, ionTop)));
|
||||
|
||||
Label exception;
|
||||
|
||||
// Either interpret or handle an exception.
|
||||
masm.testl(eax, eax);
|
||||
masm.j(Assembler::NonZero, &exception);
|
||||
|
||||
// If we're about to interpret, we've removed the depth of the local ion
|
||||
// frame, meaning we're aligned to its (callee, size, return) prefix. Save
|
||||
// this prefix in a register.
|
||||
masm.movl(esp, eax);
|
||||
|
||||
// Reserve space for Interpret() to store a Value.
|
||||
masm.subl(Imm32(sizeof(Value)), esp);
|
||||
masm.movl(esp, ecx);
|
||||
|
||||
// Call out to the interpreter.
|
||||
masm.setupUnalignedABICall(2, edx);
|
||||
masm.setABIArg(0, eax);
|
||||
masm.setABIArg(1, ecx);
|
||||
masm.setupUnalignedABICall(1, edx);
|
||||
masm.setABIArg(0, ecx);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ThunkToInterpreter));
|
||||
|
||||
// Load the value the interpreter returned.
|
||||
masm.popValue(JSReturnOperand);
|
||||
|
||||
// We're back, aligned to the frame prefix. Check for an exception.
|
||||
// Check for an exception.
|
||||
masm.testl(eax, eax);
|
||||
masm.j(Assembler::Zero, &exception);
|
||||
|
||||
|
@ -365,16 +367,7 @@ GenerateBailoutThunk(MacroAssembler &masm, uint32 frameClass)
|
|||
masm.ret();
|
||||
|
||||
masm.bind(&exception);
|
||||
|
||||
// Call into HandleException, passing in the top frame prefix.
|
||||
masm.movl(esp, eax);
|
||||
masm.setupUnalignedABICall(1, ecx);
|
||||
masm.setABIArg(0, eax);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, HandleException));
|
||||
|
||||
// The return value is how much stack to adjust before returning.
|
||||
masm.addl(eax, esp);
|
||||
masm.ret();
|
||||
masm.handleException();
|
||||
}
|
||||
|
||||
IonCode *
|
||||
|
@ -387,7 +380,7 @@ IonCompartment::generateBailoutTable(JSContext *cx, uint32 frameClass)
|
|||
masm.call(&bailout);
|
||||
masm.bind(&bailout);
|
||||
|
||||
GenerateBailoutThunk(masm, frameClass);
|
||||
GenerateBailoutThunk(cx, masm, frameClass);
|
||||
|
||||
Linker linker(masm);
|
||||
return linker.newCode(cx);
|
||||
|
@ -398,7 +391,7 @@ IonCompartment::generateBailoutHandler(JSContext *cx)
|
|||
{
|
||||
MacroAssembler masm;
|
||||
|
||||
GenerateBailoutThunk(masm, NO_FRAME_SIZE_CLASS_ID);
|
||||
GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
|
||||
|
||||
Linker linker(masm);
|
||||
return linker.newCode(cx);
|
||||
|
|
|
@ -217,6 +217,10 @@ struct ThreadData {
|
|||
|
||||
ConservativeGCThreadData conservativeGC;
|
||||
|
||||
// If Ion code is on the stack, and has called into C++, this will be
|
||||
// aligned to an Ion exit frame.
|
||||
uint8 *ionTop;
|
||||
|
||||
#ifdef DEBUG
|
||||
size_t noGCOrAllocationCheck;
|
||||
#endif
|
||||
|
|
|
@ -305,6 +305,7 @@ typedef enum JSWhyMagic
|
|||
JS_ARG_POISON, /* used in debug builds to catch tracing errors */
|
||||
JS_SERIALIZE_NO_NODE, /* an empty subnode in the AST serializer */
|
||||
JS_LAZY_ARGUMENTS, /* lazy arguments value on the stack */
|
||||
JS_ION_ERROR, /* error while running Ion code */
|
||||
JS_GENERIC_MAGIC /* for local use */
|
||||
} JSWhyMagic;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче