From 600cf43401771fedb8576e3c68ebc88e2389bb4a Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 11 Nov 2011 17:10:39 -0800 Subject: [PATCH] Introduce exit frames for x86 bailouts (bug 695897 part 4, r=sstangl). --- js/src/Makefile.in | 1 + js/src/ion/Bailouts.cpp | 106 +++------ js/src/ion/Bailouts.h | 27 +-- js/src/ion/Ion.cpp | 11 +- js/src/ion/IonCompartment.h | 17 +- js/src/ion/IonFrames.cpp | 141 ++++++++++++ js/src/ion/IonFrames.h | 206 +++++++++++++----- js/src/ion/IonMacroAssembler.cpp | 1 + js/src/ion/shared/CodeGenerator-shared.h | 2 +- .../ion/shared/CodeGenerator-x86-shared.cpp | 3 +- js/src/ion/shared/IonFrames-x86-shared.h | 50 ++++- js/src/ion/x86/Architecture-x86.h | 2 +- js/src/ion/x86/Assembler-x86.h | 8 +- js/src/ion/x86/Bailouts-x86.cpp | 102 ++++++--- js/src/ion/x86/Bailouts-x86.h | 145 ------------ js/src/ion/x86/MacroAssembler-x86.cpp | 19 ++ js/src/ion/x86/MacroAssembler-x86.h | 12 + js/src/ion/x86/Trampoline-x86.cpp | 139 ++++++------ js/src/jscntxt.h | 4 + js/src/jsval.h | 1 + 20 files changed, 567 insertions(+), 430 deletions(-) create mode 100644 js/src/ion/IonFrames.cpp delete mode 100644 js/src/ion/x86/Bailouts-x86.h diff --git a/js/src/Makefile.in b/js/src/Makefile.in index f17aa627108d..8ab6a93debf1 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -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))) diff --git a/js/src/ion/Bailouts.cpp b/js/src/ion/Bailouts.cpp index 3e6cda3ca418..73f5b54f4860 100644 --- a/js/src/ion/Bailouts.cpp +++ b/js/src/ion/Bailouts.cpp @@ -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; -} - diff --git a/js/src/ion/Bailouts.h b/js/src/ion/Bailouts.h index b105746073be..dd5aa492084c 100644 --- a/js/src/ion/Bailouts.h +++ b/js/src/ion/Bailouts.h @@ -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); } } diff --git a/js/src/ion/Ion.cpp b/js/src/ion/Ion.cpp index dbdd2ed288b4..936bc1d0f619 100644 --- a/js/src/ion/Ion.cpp +++ b/js/src/ion/Ion.cpp @@ -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(); } diff --git a/js/src/ion/IonCompartment.h b/js/src/ion/IonCompartment.h index a5007ac6c2e1..0e0b1d062aa4 100644 --- a/js/src/ion/IonCompartment.h +++ b/js/src/ion/IonCompartment.h @@ -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(); } - IonCode *returnError() const { - JS_ASSERT(returnError_); - return returnError_; - } IonActivation *activation() const { return active_; diff --git a/js/src/ion/IonFrames.cpp b/js/src/ion/IonFrames.cpp new file mode 100644 index 000000000000..2260e51a9e98 --- /dev/null +++ b/js/src/ion/IonFrames.cpp @@ -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 + * + * 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(); +} + diff --git a/js/src/ion/IonFrames.h b/js/src/ion/IonFrames.h index b5665c8f0729..97538c732ff1 100644 --- a/js/src/ion/IonFrames.h +++ b/js/src/ion/IonFrames.h @@ -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__ diff --git a/js/src/ion/IonMacroAssembler.cpp b/js/src/ion/IonMacroAssembler.cpp index 16f3c69d6318..4a6c01967b19 100644 --- a/js/src/ion/IonMacroAssembler.cpp +++ b/js/src/ion/IonMacroAssembler.cpp @@ -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); + diff --git a/js/src/ion/shared/CodeGenerator-shared.h b/js/src/ion/shared/CodeGenerator-shared.h index a95c06572167..1dcb5298b116 100644 --- a/js/src/ion/shared/CodeGenerator-shared.h +++ b/js/src/ion/shared/CodeGenerator-shared.h @@ -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 { diff --git a/js/src/ion/shared/CodeGenerator-x86-shared.cpp b/js/src/ion/shared/CodeGenerator-x86-shared.cpp index 9bfcbad1b394..de536ea03620 100644 --- a/js/src/ion/shared/CodeGenerator-x86-shared.cpp +++ b/js/src/ion/shared/CodeGenerator-x86-shared.cpp @@ -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) diff --git a/js/src/ion/shared/IonFrames-x86-shared.h b/js/src/ion/shared/IonFrames-x86-shared.h index 3391429a60cd..832256d92e90 100644 --- a/js/src/ion/shared/IonFrames-x86-shared.h +++ b/js/src/ion/shared/IonFrames-x86-shared.h @@ -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__ + diff --git a/js/src/ion/x86/Architecture-x86.h b/js/src/ion/x86/Architecture-x86.h index eb7ef800b809..3eade5fde786 100644 --- a/js/src/ion/x86/Architecture-x86.h +++ b/js/src/ion/x86/Architecture-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; diff --git a/js/src/ion/x86/Assembler-x86.h b/js/src/ion/x86/Assembler-x86.h index 90bc8be47f7f..128293440d60 100644 --- a/js/src/ion/x86/Assembler-x86.h +++ b/js/src/ion/x86/Assembler-x86.h @@ -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); diff --git a/js/src/ion/x86/Bailouts-x86.cpp b/js/src/ion/x86/Bailouts-x86.cpp index f90b5d5f9f82..805f0a24c399 100644 --- a/js/src/ion/x86/Bailouts-x86.cpp +++ b/js/src/ion/x86/Bailouts-x86.cpp @@ -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(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(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(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); } diff --git a/js/src/ion/x86/Bailouts-x86.h b/js/src/ion/x86/Bailouts-x86.h deleted file mode 100644 index cd7c1b2149aa..000000000000 --- a/js/src/ion/x86/Bailouts-x86.h +++ /dev/null @@ -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 - * - * 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__ - diff --git a/js/src/ion/x86/MacroAssembler-x86.cpp b/js/src/ion/x86/MacroAssembler-x86.cpp index 74e5585c9362..ab4397837b27 100644 --- a/js/src/ion/x86/MacroAssembler-x86.cpp +++ b/js/src/ion/x86/MacroAssembler-x86.cpp @@ -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(); +} + diff --git a/js/src/ion/x86/MacroAssembler-x86.h b/js/src/ion/x86/MacroAssembler-x86.h index 9155bfdb6046..07eeb16f11e7 100644 --- a/js/src/ion/x86/MacroAssembler-x86.h +++ b/js/src/ion/x86/MacroAssembler-x86.h @@ -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; diff --git a/js/src/ion/x86/Trampoline-x86.cpp b/js/src/ion/x86/Trampoline-x86.cpp index 9c70da491907..5e51e436688a 100644 --- a/js/src/ion/x86/Trampoline-x86.cpp +++ b/js/src/ion/x86/Trampoline-x86.cpp @@ -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); diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index c45be8a07818..2baf168ab0f2 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -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 diff --git a/js/src/jsval.h b/js/src/jsval.h index 405ed23b887d..7b22e3cfd4b0 100644 --- a/js/src/jsval.h +++ b/js/src/jsval.h @@ -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;