Files
dmcallis 65a0592776 Initial source code drop
Source code drop for the Actionscript virtual machine in the Adobe
Flash Player for December 2013
2013-12-02 16:04:04 -08:00

903 lines
29 KiB
C++

/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "avmplus.h"
#ifdef VMCFG_HALFMOON
#include "CodegenLIR.h"
#include "Deopt.h"
#include "Interpreter.h"
namespace framelib {
// Implementations of these functions are platform specific.
void returnFromFrame(uint8_t* frame); // void result
void returnFromFrameWithInt32(uint8_t* frame, int32_t value);
void returnFromFrameWithDouble(uint8_t* frame, double value);
void returnFromFrameWithInt64(uint8_t* frame, int64_t value);
uint8_t* getReturnAddress(uint8_t* frame);
void setReturnAddress(uint8_t* frame, uint8_t* address);
uint8_t* getPointerArgument0(uint8_t* frame);
void resumeFrameAtLocation(uint8_t* state, uint8_t* address);
void saveActivationState(uint8_t* state);
// FIXME
// FrameLib is intended to be generic, and to depend only on nanojit.
// It was formerly located in the nanojit tree, which is where it belongs.
// These functions have knowledge of the deoptimization scheme, and don't
// belong here. Also, moving them here messed up the resolution of the
// calls to the the stubs in the trampolines (asm code), which need to
// be in the same namespace.
//uint8_t* getTrampForInt32();
//uint8_t* getTrampForInt64();
//uint8_t* getTrampForDouble();
#ifdef AVMPLUS_64BIT
REALLY_INLINE void returnFromFrameWithPtr(uint8_t* frame, uintptr_t value) {
framelib::returnFromFrameWithInt64(frame, value);
}
#else
REALLY_INLINE void returnFromFrameWithPtr(uint8_t* frame, uintptr_t value) {
framelib::returnFromFrameWithInt32(frame, value);
}
#endif
}
namespace avmplus
{
#ifdef DEOPT_TRACE
#ifndef AVMPLUS_VERBOSE
#error "DEOPT_TRACE requires AVMPLUS_VERBOSE"
#endif
static bool deopt_trace = true;
static bool trace_verbose = false;
#endif
void Deoptimizer::returnFromFrameWithUnboxedValue(uint8_t* frame, Atom value,
Traits* t)
{
// Atom must be correct type already, we're just unboxing it.
AvmAssert(AvmCore::istype(value, t) || AvmCore::isNullOrUndefined(value));
switch (Traits::getBuiltinType(t)) {
case BUILTIN_boolean:
framelib::returnFromFrameWithInt32(frame, ((value >> 3) != 0));
break;
case BUILTIN_int:
framelib::returnFromFrameWithInt32(frame, AvmCore::integer_i(value));
break;
case BUILTIN_uint:
framelib::returnFromFrameWithInt32(frame, AvmCore::integer_u(value));
break;
case BUILTIN_number:
framelib::returnFromFrameWithDouble(frame, AvmCore::number_d(value));
break;
default:
// String, Namespace, ScriptObject
value = (Atom) atomPtr(value);
/* FALLTHROUGH */
case BUILTIN_void:
case BUILTIN_any:
case BUILTIN_object:
framelib::returnFromFrameWithPtr(frame, value);
break;
}
}
#ifdef linux
// GCC on Linux apparently do not mangle C-linkage names.
// Add a leading underscore here so references from assembler code will resolve.
#define deoptFrameUponReturnWithInt32 _deoptFrameUponReturnWithInt32
#define deoptFrameUponReturnWithInt64 _deoptFrameUponReturnWithInt64
#define deoptFrameUponReturnWithDouble _deoptFrameUponReturnWithDouble
#endif
// Static bridges from the assembly-language trampolines.
// The asm trampoline has captured the victim's fp.
// We use that to retrieve its first argument, the MethodEnv.
// From the MethodEnv, we can obtain the MethodInfo and the Deoptimizer.
extern "C"
void deoptFrameUponReturnWithInt32(uint8_t* fp, int32_t result)
{
MethodEnv* env = (MethodEnv*)framelib::getPointerArgument0(fp);
env->method->_armed_deoptimizers->deoptFrameUponReturnWithResult(fp, env, (void*)&result);
}
extern "C"
void deoptFrameUponReturnWithInt64(uint8_t* fp, int64_t result)
{
MethodEnv* env = (MethodEnv*)framelib::getPointerArgument0(fp);
env->method->_armed_deoptimizers->deoptFrameUponReturnWithResult(fp, env, (void*)&result);
}
extern "C"
void deoptFrameUponReturnWithDouble(uint8_t* fp, double result)
{
MethodEnv* env = (MethodEnv*)framelib::getPointerArgument0(fp);
env->method->_armed_deoptimizers->deoptFrameUponReturnWithResult(fp, env, (void*)&result);
}
#ifdef VMCFG_IA32
// TODO: These would be better off in a separate .asm file, but I don't think we
// have one for the i386 builds, and I'm not ready to get into that yet.
// Return a trampoline to capture the frame pointer and return value from a returning
// function and transfer into the appropriate static bridge above. We overwrite the
// return address of the frame we wish to "hook" in order to take control when it returns.
// We maintain 16-byte stack alignment, which is required on some platforms.
// GCC will attempt to inline these functions, but emit an out-of-line version
// as well. This results in duplicate symbol definitions due to the inline
// assembler code. We could avoid this by using numeric local labels, but there
// is no need to waste the code space, and it's good to have a symbol for the
// trampoline.
NO_INLINE
uint8_t* getTrampForInt32()
{
//using namespace framelib;
#if _MSC_VER
_asm {
jmp done
deopt_tramp_I32:
sub esp, 8
push eax
push ebp
call deoptFrameUponReturnWithInt32
/* NOTREACHED */
int 3
done:
lea eax, deopt_tramp_I32
}
#else
uint8_t* result = NULL;
asm volatile (
" jmp 0f ;\
.align 4 ;\
.globl deopt_tramp_I32 ;\
deopt_tramp_I32: ;\
subl $8, %%esp ;\
pushl %%eax ;\
pushl %%ebp ;\
call _deoptFrameUponReturnWithInt32 ;\
int3 ;\
0: ;\
movl $deopt_tramp_I32, %[result] "
: : [result] "m"(result)
);
return result;
#endif
}
NO_INLINE
uint8_t* getTrampForInt64()
{
return NULL; //nyi
}
NO_INLINE
uint8_t* getTrampForDouble()
{
//using namespace framelib;
#if _MSC_VER
_asm {
jmp done
deopt_tramp_double:
sub esp, 12
fstp qword ptr [esp]
push ebp
call deoptFrameUponReturnWithDouble
/* NOTREACHED */
int 3
done:
lea eax, deopt_tramp_double
}
#else
uint8_t* result = NULL;
asm volatile (
" jmp 0f ;\
.align 4 ;\
.globl deopt_tramp_double ;\
deopt_tramp_double: ;\
subl $12, %%esp ;\
fstpl (%%esp) ;\
pushl %%ebp ;\
call _deoptFrameUponReturnWithDouble ;\
int3 ;\
0: ;\
movl $deopt_tramp_double, %[result] "
: : [result] "m"(result)
);
return result;
#endif
}
#else
uint8_t* getTrampForInt32() {
AvmAssert(false && "not implemented");
return 0;
}
uint8_t* getTrampForInt64() {
AvmAssert(false && "not implemented");
return 0;
}
uint8_t* getTrampForDouble() {
AvmAssert(false && "not implemented");
return 0;
}
#endif // ifdef VMCFG_IA32
// Deoptimize the method associated with this Deoptimizer.
// We arrange for activations of the method to be deoptimized as the stack unwinds.
// Here, we do not actually revert the state of the method to interpreted, but only
// arrange for the deoptimization of all pending activations.
// TODO: When unwinding for exceptions, we may skip over an armed frame, and should
// then re-arm a frame that is still active. Otherwise, the deoptimize-and-rearm chain
// will be broken, and we will not deoptimize all activations.
void Deoptimizer::deoptimize(AvmCore* core)
{
MethodFrame* frame = nextActivation(methodFrame(core));
if (frame) {
Deoptimizer* armed = methodInfo->_armed_deoptimizers;
if (!armed || armed->armedFrame != frame) {
// There is an activation not handled by a pending deoptimizer.
armForLazyDeoptimization(frame);
}
}
methodInfo->_deoptimizer = NULL;
}
void Deoptimizer::armForLazyDeoptimization(DeoptContext* ctx, MethodFrame* victim)
{
// Requires fixed ESP! We expect to locate the callee's native frame by
// adding nj_frame_size_ to to the caller's native frame pointer.
// It is illegal for a deoptimizer to have more than one armed frame.
AvmAssert(resumptionPc == 0);
// Calculate base of native frame.
uint8_t* frameBase = ctx->frameBase(victim);
#ifdef DEOPT_TRACE
if (deopt_trace) {
AvmCore* core = victim->env()->core();
core->console << "$$$ deopt armed " << hexAddr((uintptr_t)this) << " @ " << hexAddr((uintptr_t)victim) << "\n";
}
#endif
uint8_t* calleeFrameBase = ctx->calleeFrameBase(frameBase);
// We assume that the victim frame is awaiting return of a
// subsidiary call, and will be resumed at the return address
// saved in the frame of its immediate callee.
uint8_t* nativePc = framelib::getReturnAddress(calleeFrameBase);
// Save the return address, because we are going to clobber it.
resumptionPc = nativePc;
// Find safepoint associated with the call.
ctx->setSafepointFromNativePc(nativePc);
// Get trampoline appropriate for the callee's return type.
uint8_t* trampoline;
switch (ctx->safepointReturnValueSST()) {
case SST_int32:
case SST_uint32:
case SST_bool32:
trampoline = getTrampForInt32();
break;
case SST_double:
trampoline = getTrampForDouble();
break;
default:
#ifdef AVMPLUS_64BIT
trampoline = getTrampForInt64();
#else
trampoline = getTrampForInt32();
#endif
break;
}
// Install the trampoline as the callee's return address.
framelib::setReturnAddress(calleeFrameBase, trampoline);
this->armedFrame = victim;
// Push ourself onto the queue of pending deoptimizers.
this->nextArmed = methodInfo->_armed_deoptimizers;
methodInfo->_armed_deoptimizers = this;
#ifdef DEOPT_TRACE
if (deopt_trace) {
AvmCore* core = victim->env()->core();
core->console << "==> ";
showArmed(core);
}
#endif
}
// We should likely not export this in its raw form,
// exposing "bogus" frames used to hold CodeContexts.
// Perhaps we want a 'currentMethodEnv()' call instead?
MethodFrame* Deoptimizer::methodFrame(AvmCore* core)
{
return core->currentMethodFrame;
}
MethodFrame* Deoptimizer::nextActivation(MethodFrame* startFrame)
{
AvmAssert(startFrame != NULL);
MethodFrame* frame = startFrame;
while (frame != NULL) {
MethodEnv *env = frame->env();
if (!env) {
// MethodFrame is holder for CodeContext, so skip it.
continue;
}
// TODO: Don't waste an entire word on this flag!
// We can probably repurpose another low-order address
// bit following existing precedent in MethodFrame.
if (frame->isInterp) {
frame = frame->next;
continue;
}
if (env->method == this->methodInfo) {
// We found another activation.
return frame;
}
frame = frame->next;
}
return NULL;
}
void Deoptimizer::deoptFrameUponReturnWithResult(DeoptContext* ctx, uint8_t* fp, MethodEnv* env, void* resultPtr)
{
AvmCore* core = env->core();
#ifdef DEOPT_TRACE
if (deopt_trace) {
core->console << "$$$ deopt fired " << hexAddr((uintptr_t)this) << "\n";
}
#endif
// The frame to deoptimize must be an activation of the method to which we belong.
AvmAssert(env->method == this->methodInfo);
// We must have previously armed this deoptimizer.
AvmAssert(resumptionPc != 0);
ctx->setSafepointFromNativePc(resumptionPc);
resumptionPc = 0;
//### Not necessarily!
AvmAssert(ctx->isSafepointAtAbcCall());
// Note that the 'fp' argument is the native frame pointer.
uint8_t* frameBase = fp;
MethodFrame* frame = ctx->methodFrame(frameBase);
// Verify that this is the correct deoptimizer for the code that
// created the frame. Note that frame->deopt is used only for
// assertion checks, and need not be maintained in production.
AvmAssert(frame->deopt == this);
// Exit the victim frame, as the interpreter will re-establish it.
frame->exit(core);
const MethodSignature *ms = methodInfo->getMethodSignature();
// Box the result returned from the callee.
Atom returnVal = nativeLocalToAtom(core, resultPtr, ctx->safepointReturnValueSST());
// Pop this deoptimizer.
armedFrame = NULL;
AvmAssert(methodInfo->_armed_deoptimizers == this);
methodInfo->_armed_deoptimizers = nextArmed;
nextArmed = NULL;
#ifdef DEOPT_TRACE
if (deopt_trace) {
core->console << "<== ";
showArmed(core);
}
#endif
// Search for the previous activation of the method, and arm for lazy deoptimization.
MethodFrame* prevFrame = nextActivation(methodFrame(core));
if (prevFrame) {
Deoptimizer* armed = methodInfo->_armed_deoptimizers;
// There is a pending activation of the method.
if (!armed || armed->armedFrame != prevFrame) {
// We are still the appropriate deoptimizer for this frame.
armForLazyDeoptimization(prevFrame);
}
}
// Interpreter frame will reconstruct state at call, with boxed result pushed on operand stack.
Atom retval = ctx->interpretFromCallSafepoint(frameBase, env, returnVal);
// Return from lazily-deoptimized frame with the unboxed value from the interpreter.
returnFromFrameWithUnboxedValue(frameBase, retval, ms->returnTraits());
}
// Begin interpretive execution at the specfied safepoint in the given frame.
// The frame must be an activation of the AS3 method to which this member belongs.
// TODO: Given the intended use of enterHandler, we could trim this down to the
// specialized case of the currently executing frame only.
void Deoptimizer::enterInterpreter(DeoptContext* ctx, MethodFrame* frame, int32_t vpc)
{
MethodEnv* env = frame->env();
AvmCore* core = env->core();
// The frame to deoptimize must be an activation of the method to which we belong.
AvmAssert(env->method == this->methodInfo);
#ifdef DEOPT_TRACE
if (deopt_trace) {
core->console << "$$$ deopt interp " << hexAddr((uintptr_t)this) << " @ " << hexAddr((uintptr_t)frame) << "\n";
}
#endif
// Unlink the intervening frames, including the victim.
frame->exit(core);
// Continue at safepoint instruction.
ctx->setSafepointFromVirtualPc(vpc);
uint8_t* frameBase = ctx->frameBase(frame);
Atom result = ctx->interpretFromSafepoint(frameBase, env);
// Return from current frame with interpreter result as our result.
const MethodSignature *ms = methodInfo->getMethodSignature();
returnFromFrameWithUnboxedValue(frameBase, result, ms->returnTraits());
}
#ifdef VMCFG_DEOPT_DIRECT_EXN
// TODO: Cribbed from jit-calls.h. Should be shared.
union AnyVal {
Atom atom; // SST_atom
const Namespace* ns; // SST_namespace
String* str; // SST_string
ScriptObject* obj; // SST_scriptobject
int32_t i; // SST_int32
uint32_t u; // SST_uint32
int32_t b; // SST_bool32
double d; // SST_double
};
// defined in jit-calls.h
extern void set_slot_from_atom(Atom atom, Traits* traits, AnyVal* slotPtr, uint8_t* tagPtr);
#endif
// Attempt to handle an exception in the method activation to which 'ef' and 'handlerFrame'
// belong. If no handler exists, we re-throw. We do not simply search up the ExceptionFrame
// chain, as each frame may follow a different protocol (e.g., interpreter vs. JIT-ed code).
// See beginCatch() in jit-calls.h
void Deoptimizer::handleException(DeoptContext* ctx, AvmCore* core, ExceptionFrame* ef, MethodFrame* handlerFrame)
{
// The ExceptionFrame 'ef' must belong to the handling method activation.
AvmAssert(ef->savedMethodFrame == handlerFrame);
// Get the exception being thrown.
Exception* exception = core->exceptionAddr;
// Restore enclosing exception frame.
ef->beginCatch();
// Calculate base of native frame.
uint8_t* frameBase = ctx->frameBase(handlerFrame);
#ifdef VMCFG_DEOPT_SAFEPOINT_NATIVE
// Reconstruct virtual ABC pc from safepoint metadata, given the native pc.
uint8_t* nativePc = framelib::getReturnAddress(ctx->calleeFrameBase(frameBase));
ctx->setSafepointFromNativePc(nativePc);
int32_t exnVpc = ctx->safepointVpc();
#else
// Extract the virtual ABC pc from the frame of the handling method activation.
int32_t exnVpc = ctx->saveEip(frameBase);
#endif
// Find handler for throw at location exnPc. Rethrows if no applicable handler exists.
ExceptionHandler* handler = core->findExceptionHandler(methodInfo, exnVpc, exception);
#ifdef VMCFG_DEOPT_DIRECT_EXN
//### This depends on CodegenLIR!
// Re-establish exception-handling context for possible exceptions raised in handler.
ef->beginDeoptUnwindTry(core);
Atom* vars = addrInFrame<Atom>(frameBase, vars_offset_);
uint8_t* tags = addrInFrame<uint8_t>(frameBase, tags_offset_);
const MethodSignature *ms = methodInfo->getMethodSignature();
int stack_base = ms->stack_base();
// Set operand_stack[0] to exception object.
set_slot_from_atom(exception->atom, handler->traits, ((AnyVal*)((uint8_t*)vars + stack_base * 8)), (tags + stack_base));
// Reset the stack and the callee-saved registers using explicitly-saved state.
// A proper unwinder that understands callee-saved registers would be more efficient.
framelib::resumeFrameAtLocation((uint8_t*)&ef->jmpbuf, handler->target_address);
#else
MethodEnv* env = handlerFrame->env();
// The frame to deoptimize must be an activation of the method to which we belong.
AvmAssert(env->method == this->methodInfo);
#ifdef DEOPT_TRACE
if (deopt_trace) {
core->console << "$$$ deopt interp " << hexAddr((uintptr_t)this) << " @ " << hexAddr((uintptr_t)handlerFrame) << "\n";
}
#endif
// Unlink the intervening frames, including the victim.
handlerFrame->exit(core);
// Continue at handler target, with stacks reset and exception pushed.
Atom result = ctx->interpretFromThrowSafepoint(frameBase, env, handler->target, exception->atom);
// Return from current frame with interpreter result as our result.
const MethodSignature *ms = methodInfo->getMethodSignature();
returnFromFrameWithUnboxedValue(frameBase, result, ms->returnTraits());
#endif
}
#ifdef DEOPT_TRACE
//static
int Deoptimizer::frameDepth = 0;
//static
void Deoptimizer::showFrames(AvmCore* core)
{
if (!deopt_trace) return;
int limit = 5;
for (MethodFrame* frame = core->methodFrame(); frame != NULL; frame = frame->next) {
if (limit-- < 0) {
core->console << "...";
break;
}
MethodInfo* m = frame->env()->method;
char mode = (frame->isInterp) ? 'I' : 'J';
core->console << " [" << m << " " << mode << " " << hexAddr((uintptr_t)frame) << " " << hexAddr((uintptr_t)frame->deopt) << "]";
}
core->console << "\n";
}
//static
void Deoptimizer::traceEnter(AvmCore* core)
{
if (!deopt_trace) return;
core->console << "[" << frameDepth++ << "]>>>";
if (trace_verbose) {
showFrames(core);
} else {
core->console << " " << hexAddr((uintptr_t)core->methodFrame()) << "\n";
}
}
//static
void Deoptimizer::traceExit(AvmCore* core)
{
if (!deopt_trace) return;
core->console << "[" << --frameDepth << "]<<<";
if (trace_verbose) {
showFrames(core);
} else {
core->console << " " << hexAddr((uintptr_t)core->methodFrame()) << "\n";
}
}
void Deoptimizer::showArmed(AvmCore* core)
{
if (!deopt_trace) return;
int limit = 5;
for (Deoptimizer* deopt = methodInfo->_armed_deoptimizers; deopt != NULL; deopt = deopt->nextArmed) {
if (limit-- < 0) {
core->console << "...";
break;
}
core->console << " {" << hexAddr((uintptr_t)deopt) << " @ " << hexAddr((uintptr_t)deopt->armedFrame) << "}";
}
core->console << "\n";
}
#endif // ifdef DEOPT_TRACE
// Debugging scaffolding.
//static
bool Deoptimizer::deoptAncestor(AvmCore* core, uint32_t k)
{
for (MethodFrame* frame = methodFrame(core); frame != NULL; frame = frame->next) {
MethodEnv *env = frame->env();
if (!env) continue; // MethodFrame is holder for CodeContext, so skip it.
if (k-- == 0) {
if (frame->isInterp)
return true;
MethodInfo* info = env->method;
if (info->_deoptimizer) {
core->exec->deoptimize(env);
}
return true;
}
}
return false;
}
} // namespace avmplus
namespace framelib {
#ifdef VMCFG_IA32
// Target architecture and backend-dependent library routines for manipulating stack frames.
// Currently aimed at supporting deoptimization in Tamarin, though debugging suggests other
// extensions to the API.
//
// TODO: This stuff doesn't belong in the code generator source files, and probably shouldn't
// be in the nanojit namespace either. Most likely, we should have a separate support library
// in a directory alongside 'nanojit'.
#if _MSC_VER
#pragma warning( push )
#pragma warning( disable : 4731 ) // Don't complain about changing FP inside function.
#endif
// We assume eager saving of the callee-saved registers, i.e., all are spilled to consecutive
// slots at method entry regardless of whether they are actually used.
#ifndef NANOJIT_EAGER_REGSAVE
#error "NANOJIT_FRAMELIB on i386 requires NANOJIT_EAGER_REGSAVE"
#endif
// In the following, we need control over register usage to satisfy
// ABI requirements. The simplest way to do this is to hardcode the
// register assignments in the inline assembly snippets, but, on GCC,
// hiding register usage from the compiler's register allocator can be
// problematic. We are relying on the fact that references to the C++
// parameters will be translated as an offset from EBP, without the need
// to allocate additional temporary registers. Note that static
// variable references in position-independent code do not translate to
// a simple absolute address, and require such a temporary register.
// Given a frame handle representing a function activation, prune the stack and
// return from the activation with the callee-saved registers properly restored.
void returnFromFrame(uint8_t* frame)
{
#if _MSC_VER
_asm {
mov eax, [frame]
mov ebx, [eax-4] // Restore EBX
mov esi, [eax-8] // Restore ESI
mov edi, [eax-12] // Restore EDI
mov esp, [frame] // Prune stack, including allocated slots of victim frame
pop ebp // Restore frame pointer of victim's caller
ret // Pop return address and return to victim's caller
}
#else
asm volatile (
"movl %[frame], %%eax \n\t"
"movl -4(%%eax), %%ebx \n\t"
"movl -8(%%eax), %%esi \n\t"
"movl -12(%%eax), %%edi \n\t"
"movl %[frame], %%esp \n\t"
"popl %%ebp \n\t"
"ret \n\t"
: : [frame] "m"(frame)
);
#endif
/*NOTREACHED*/
}
// Same as 'returnFromFrame()', but return an 32-bit integer result.
void returnFromFrameWithInt32(uint8_t* frame, int32_t value)
{
#if _MSC_VER
_asm {
mov eax, [frame]
mov ebx, [eax-4] // Restore EBX
mov esi, [eax-8] // Restore ESI
mov edi, [eax-12] // Restore EDI
mov eax, [value] // Integer result will be returned in EAX
mov esp, [frame] // Prune stack, including allocated slots of victim frame
pop ebp // Restore frame pointer of victim's caller
ret // Pop return address and return to victim's caller
}
#else
asm volatile (
"movl %[frame], %%eax \n\t"
"movl -4(%%eax), %%ebx \n\t"
"movl -8(%%eax), %%esi \n\t"
"movl -12(%%eax), %%edi \n\t"
"movl %[value], %%eax \n\t"
"movl %[frame], %%esp \n\t"
"popl %%ebp \n\t"
"ret \n\t"
: : [frame] "m"(frame), [value] "m"(value)
);
#endif
/*NOTREACHED*/
}
// Same as 'returnFromFrame()', but return a double result.
void returnFromFrameWithDouble(uint8_t* frame, double value)
{
#if _MSC_VER
_asm {
mov eax, [frame]
mov ebx, [eax-4] // Restore EBX
mov esi, [eax-8] // Restore ESI
mov edi, [eax-12] // Restore EDI
fld [value] // Double result will be returned in FST0
mov esp, [frame] // Prune stack, including allocated slots of victim frame
pop ebp // Restore frame pointer of victim's caller
ret // Pop return address and return to victim's caller
}
#else
asm volatile (
"movl %[frame], %%eax \n\t"
"movl -4(%%eax), %%ebx \n\t"
"movl -8(%%eax), %%esi \n\t"
"movl -12(%%eax), %%edi \n\t"
"fldl %[value] \n\t"
"movl %[frame], %%esp \n\t"
"popl %%ebp \n\t"
"ret \n\t"
: : [frame] "m"(frame), [value] "m"(value)
);
#endif
/*NOTREACHED*/
}
#ifdef NANOJIT_64BIT
//void returnFromFrameWithInt64(uint8_t* frame, int64_t value)
#error "unimplemented"
#endif
// Get the return address from a frame.
uint8_t* getReturnAddress(uint8_t* frame)
{
return *((uint8_t**)(frame+4));
}
// Install a new return address in a frame, e.g., to hook returns.
void setReturnAddress(uint8_t* frame, uint8_t* address)
{
*((uint8_t**)(frame+4)) = address;
}
// Get the first argument for a frame, assumed to be a pointer.
uint8_t* getPointerArgument0(uint8_t* frame)
{
return *((uint8_t**)(frame+8));
}
// Capture the frame pointer, stack pointer, and callee-saved registers.
void saveActivationState(uint8_t* state)
{
#if _MSC_VER
_asm {
mov eax, [state]
mov [eax], ebx // Save EBX
mov [eax+4], esi // Save ESI
mov [eax+8], edi // Save EDI
lea edx, [ebp+8] // Get stack pointer of our caller
mov [eax+12], edx // Save ESP of caller
mov edx, [ebp] // Get frame pointer of caller
mov [eax+16], edx // Save EBP of caller
}
#else
asm volatile (
"movl %[state], %%eax \n\t"
"movl %%ebx, (%%eax) \n\t"
"movl %%esi, 4(%%eax) \n\t"
"movl %%edi, 8(%%eax) \n\t"
"leal 8(%%ebp), %%edx \n\t"
"movl %%edx, 12(%%eax) \n\t"
"movl (%%ebp), %%edx \n\t"
"movl %%edx, 16(%%eax) \n\t"
: : [state] "m"(state)
);
#endif
}
// Restore saved frame pointer, stack pointer, and callee-saved
// registers, and resume execution at the specified address.
void resumeFrameAtLocation(uint8_t* state, uint8_t* address)
{
#if _MSC_VER
_asm {
mov eax, [address]
mov edx, [state]
mov ebx, [edx]
mov esi, [edx+4]
mov edi, [edx+8]
mov esp, [edx+12]
mov ebp, [edx+16]
jmp eax
}
#else
asm volatile (
"movl %[address], %%eax \n\t"
"movl %[state], %%edx \n\t"
"movl (%%edx), %%ebx \n\t"
"movl 4(%%edx), %%esi \n\t"
"movl 8(%%edx), %%edi \n\t"
"movl 12(%%edx), %%esp \n\t"
"movl 16(%%edx), %%ebp \n\t"
"jmp *%%eax \n\t"
: : [state] "m"(state), [address] "m"(address)
);
#endif
}
#if _MSC_VER
#pragma warning( pop )
#endif
#else
void returnFromFrame(uint8_t* /*frame*/) {
AvmAssert(false && "not implemented");
}
void returnFromFrameWithInt32(uint8_t* /*frame*/, int32_t /*value*/) {
AvmAssert(false && "not implemented");
}
void returnFromFrameWithDouble(uint8_t* /*frame*/, double /*value*/) {
AvmAssert(false && "not implemented");
}
void returnFromFrameWithInt64(uint8_t* /*frame*/, int64_t /*value*/) {
AvmAssert(false && "not implemented");
}
uint8_t* getReturnAddress(uint8_t* /*frame*/) {
AvmAssert(false && "not implemented");
return 0;
}
void setReturnAddress(uint8_t* /*frame*/, uint8_t* /*address*/) {
AvmAssert(false && "not implemented");
}
uint8_t* getPointerArgument0(uint8_t* /*frame*/) {
AvmAssert(false && "not implemented");
return 0;
}
void resumeFrameAtLocation(uint8_t* /*state*/, uint8_t* /*address*/) {
AvmAssert(false && "not implemented");
}
void saveActivationState(uint8_t* /*state*/) {
AvmAssert(false && "not implemented");
}
#endif // ifdef VMCFG_IA32, etc
} // end namespace framelib
#endif // VMCFG_HALFMOON