Introduce exit frames for x86 bailouts (bug 695897 part 4, r=sstangl).

This commit is contained in:
David Anderson 2011-11-11 17:10:39 -08:00
Родитель 11edef65af
Коммит 600cf43401
20 изменённых файлов: 567 добавлений и 430 удалений

Просмотреть файл

@ -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_;

141
js/src/ion/IonFrames.cpp Normal file
Просмотреть файл

@ -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 &reg) const {
return fpregs_[reg.code()];
}
uintptr_t readReg(const Register &reg) 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 &reg) const {
return bailout_->readReg(reg);
}
double readFloatReg(const FloatRegister &reg) 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, &copyLoopTop);
}
// 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;