/* -*- 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" // TODO: I believe this code is now obsolete and should be removed. //#ifdef VMCFG_HALFMOON #if 0 #include "CodegenLIR.h" #include "Deopt.h" #include "Deopt-CL.h" #include "Interpreter.h" namespace avmplus { // Return a generic pointer to local slot at the specified index. static void* addrOfJitSlot(void* vars, int slot_ix, int shift) { return (void*)(((uint8_t*) vars) + (slot_ix << shift)); } static const uint8_t* INVALID_PC_MARKER = (uint8_t*)0xfafafafa; CLSafepoint::CLSafepoint(MethodInfo* mi, const FrameState* state, CLSafepoint* next) : pc_(INVALID_PC_MARKER), abc_pc_(state->abc_pc), scope_depth_(state->scopeDepth), stack_depth_(state->stackDepth), next_(next) { const MethodSignature *ms = mi->getMethodSignature(); int frame_size = ms->frame_size(); // NOTE: May be padded on 64-bit platforms! int local_count = ms->local_count(); int scope_base = local_count; int stack_base = local_count + ms->max_scope(); // Set method-relative ABC instruction pointer, used by debugger. if (abc_pc_ == 0) { // Still in prologue, which we charge to the first instruction. abc_ix_ = 0; } else { // Substract absolute address of first ABC instruction. const uint8_t* codeStart = ms->abc_code_start(); abc_ix_ = uint32_t(abc_pc_ - codeStart); } // TODO: Allocate this in the correct arena. tags_ = new uint8_t[frame_size]; for (int i = 0; i < frame_size; i++) tags_[i] = 0; // Capture machine type for each frame slot. // TODO: Tags for locals need not be captured at each safepoint. // We'll need to do things differently in Halfmoon anyway, and // we *will* need to note changes in the register allocation for // locals from safepoint to safepoint. for (int i = 0; i < local_count; i++) { tags_[i] = uint8_t(slotType(state, i)); } for (int i = 0; i < state->scopeDepth; i++) { int j = scope_base + i; tags_[j] = uint8_t(slotType(state, j)); } for (int i = 0; i < state->stackDepth; i++) { int j = stack_base + i; tags_[j] = uint8_t(slotType(state, j)); } // DIRTY HACK! // We need to pass the current stack depth to the deadvars optimizer // in the LIR_safe instruction. We take advantage of the fact that we // pass the address of the pc_ slot as the payload of th LIR_safe, // to be filled in during assembly. We stash the stack depth in the // pc_ slot temporarily, where it can be accessed from the LIR_safe insn // prior to assembly, e.g., during deadvars optimization. // NOTE: We don't need this for safepoints that will be used only for // exception handling, as the stack is dead. pc_ = (uint8_t*)state->stackDepth; } SlotStorageType CLSafepoint::slotType(const FrameState* state, int i) { const FrameValue& val = state->value(i); if (exactlyOneBit(val.sst_mask)) { // Slot has a unique machine type, inferable from its traits. BuiltinType bt = Traits::getBuiltinType(val.traits); return valueStorageType(bt); } else { // Slot may have multiple machine types. Must consult runtime tag. return SlotStorageType(0xff); // Magic cookie. } } // Fill in additional safepoint information needed only for calls. // NOTE: This method violates the principle that we not assume that // safepoints are objects, permitting easy replacement with an opaque // handle, likely implemented as a scalar. The clunky way that safepoint // generation is driven by the verifier will be revisited in the context // of Halfmoon, however, so I'm just doing something simple and easy here. void CLSafepoint::finish(int argc, const uint8_t* nextpc, Traits* resultType) { // This needs to be the total number of slots that are popped off of the // operand stack by the call instruction, not including any results pushed. // Aside from the ABC call arguments, it must include the receiver, property // names, etc. pop_args_ = argc; // Capture the size of the call instruction. This will be used to adjust // the captured abc_ix_ to reflect the return address of the call. call_size_ = nextpc - abc_pc_; return_sst_ = valueStorageType(Traits::getBuiltinType(resultType)); } CLSafepoint* CLDeoptData::addSafepoint(const FrameState* state) { // TODO: Allocate this in the correct arena. CLSafepoint* sp = new CLSafepoint(methodInfo, state, safepoints); safepoints = sp; return sp; } // Return the safepoint associated with the given virtual (ABC) pc. // Unlike lookups based on machine addresses, this must always be an exact match. CLSafepoint* CLDeoptData::findSafepointAtVirtualPc(int32_t vpc) { CLSafepoint* sp = safepoints; while (sp != NULL) { if (intptr_t(sp->abc_ix_) == vpc) return sp; sp = sp->next_; } return NULL; } // Return the safepoint associated with the given native pc. // The recorded address may precede the location at which we capture the pc at runtime, // so we search for the safepoint with the largest address preceding the given pc. CLSafepoint* CLDeoptData::findSafepointAtNativePc(uint8_t* pc) { CLSafepoint* sp = safepoints; intptr_t largest = 0; CLSafepoint* candidate = NULL; while (sp != NULL) { if ((intptr_t(sp->pc_) >= largest) && sp->pc_ <= pc) { largest = intptr_t(sp->pc_); candidate = sp; } sp = sp->next_; } return candidate; } // Frame populators for compiled methods generated by CodegenLIR. class CLFramePopulator : public FramePopulator { public: CLFramePopulator(MethodInfo* method, const MethodSignature* signature, CLSafepoint* safePoint, Atom* vars, uint8_t* tags); virtual void populate(Atom* framep, int *scopeDepth, int *stackDepth); virtual ~CLFramePopulator() {} // Silence G++ warning. protected: SlotStorageType slotType(int i); void populateLocals(Atom* framep); void populateScopes(Atom* framep, int *scopeDepth); void populateStack(Atom* framep, int *stackDepth); AvmCore* core() const { return method_->pool()->core; } int varshift() const { return VARSHIFT(method_); } MethodInfo* method_; const MethodSignature* signature_; CLSafepoint* safePoint_; Atom* vars_; uint8_t* tags_; }; CLFramePopulator::CLFramePopulator(MethodInfo* method, const MethodSignature* signature, CLSafepoint* safePoint, Atom* vars, uint8_t* tags) : method_(method), signature_(signature), safePoint_(safePoint), vars_(vars), tags_(tags) {} SlotStorageType CLFramePopulator::slotType(int i) { uint32_t tag = safePoint_->tags_[i]; if (tag == 0xff) { // Fetch runtime tag value. return SlotStorageType(tags_[i]); } // Unique type is known at compile time. return SlotStorageType(tag); } void CLFramePopulator::populateLocals(Atom* framep) { int local_count = signature_->local_count(); for (int i = 0; i < local_count; i++) { void *slotAddr = addrOfJitSlot(vars_, i, varshift()); framep[i] = nativeLocalToAtom(core(), slotAddr, slotType(i)); } } void CLFramePopulator::populateScopes(Atom* framep, int *scopeDepth) { int scope_base = signature_->scope_base(); int scope_depth = safePoint_->scope_depth_; for (int i = 0; i < scope_depth; i++) { int j = scope_base + i; void *slotAddr = addrOfJitSlot(vars_, j, varshift()); framep[j] = nativeLocalToAtom(core(), slotAddr, slotType(j)); } *scopeDepth = scope_depth; } void CLFramePopulator::populateStack(Atom* framep, int *stackDepth) { int stack_base = signature_->stack_base(); int stack_depth = safePoint_->stack_depth_; for (int i = 0; i < stack_depth; i++) { int j = stack_base + i; void *slotAddr = addrOfJitSlot(vars_, j, varshift()); framep[j] = nativeLocalToAtom(core(), slotAddr, slotType(j)); } *stackDepth = stack_depth; } void CLFramePopulator::populate(Atom* framep, int *scopeDepth, int *stackDepth) { populateLocals(framep); populateScopes(framep, scopeDepth); populateStack(framep, stackDepth); } class CLHandlerFramePopulator : public CLFramePopulator { public: CLHandlerFramePopulator(MethodInfo* method, const MethodSignature* signature, CLSafepoint* safePoint, Atom* vars, uint8_t* tags, Atom exnVal); virtual void populate(Atom* framep, int *scopeDepth, int *stackDepth); virtual ~CLHandlerFramePopulator() {} // Silence G++ warning. private: Atom exnVal_; }; CLHandlerFramePopulator::CLHandlerFramePopulator(MethodInfo* method, const MethodSignature* signature, CLSafepoint* safePoint, Atom* vars, uint8_t* tags, Atom exnVal) : CLFramePopulator(method, signature, safePoint, vars, tags), exnVal_(exnVal) {} void CLHandlerFramePopulator::populate(Atom* framep, int *scopeDepth, int *stackDepth) { populateLocals(framep); *scopeDepth = 0; int stack_base = signature_->stack_base(); framep[stack_base] = exnVal_; *stackDepth = 1; } class CLReturnFramePopulator : public CLFramePopulator { public: CLReturnFramePopulator(MethodInfo* method, const MethodSignature* signature, CLSafepoint* safePoint, Atom* vars, uint8_t* tags, Atom returnVal); virtual void populate(Atom* framep, int *scopeDepth, int *stackDepth); virtual ~CLReturnFramePopulator() {} // Silence G++ warning. private: Atom returnVal_; }; CLReturnFramePopulator::CLReturnFramePopulator(MethodInfo* method, const MethodSignature* signature, CLSafepoint* safePoint, Atom* vars, uint8_t* tags, Atom returnVal) : CLFramePopulator(method, signature, safePoint, vars, tags), returnVal_(returnVal) {} void CLReturnFramePopulator::populate(Atom* framep, int *scopeDepth, int *stackDepth) { int stackIdx; populateLocals(framep); populateScopes(framep, scopeDepth); populateStack(framep, &stackIdx); stackIdx -= safePoint_->pop_args_; int stack_base = signature_->stack_base(); framep[stack_base + stackIdx] = returnVal_; *stackDepth = stackIdx + 1; } // Deoptimization context for compiled methods generated by CodegenLIR. class CLDeoptContext : public DeoptContext { public: CLDeoptContext(CLDeoptData* dd) : deoptData(dd), safePoint(NULL) {} uint8_t* frameBase(MethodFrame* methodFrame) { return (uint8_t*)methodFrame - deoptData->frame_offset_; } MethodFrame* methodFrame(uint8_t* frameBase) { return addrInFrame(frameBase, deoptData->frame_offset_); } int32_t saveEip(uint8_t* frameBase) { return *addrInFrame(frameBase, deoptData->eip_offset_); } uint8_t* calleeFrameBase(uint8_t* frameBase) { return frameBase + deoptData->nj_frame_size_; } void setSafepointFromVirtualPc(int32_t vpc) { safePoint = deoptData->findSafepointAtVirtualPc(vpc); } void setSafepointFromNativePc(uint8_t* pc) { safePoint = deoptData->findSafepointAtNativePc(pc); } int32_t safepointVpc() { AvmAssert(safePoint != NULL); return safePoint->abc_ix_; } bool isSafepointAtThrow() { AvmAssert(safePoint != NULL); return true; // NYI } bool isSafepointAtAbcCall() { AvmAssert(safePoint != NULL); return (safePoint->call_size_ != 0); } SlotStorageType safepointReturnValueSST() { AvmAssert(isSafepointAtAbcCall()); return safePoint->return_sst_; } Atom interpretFromSafepoint(uint8_t* fp, MethodEnv* env) ; Atom interpretFromCallSafepoint(uint8_t* fp, MethodEnv* env, Atom returnVal); Atom interpretFromThrowSafepoint(uint8_t* fp, MethodEnv* env, int32_t handlerVpc, Atom exnVal); private: CLDeoptData* deoptData; CLSafepoint* safePoint; }; Atom CLDeoptContext::interpretFromSafepoint(uint8_t* fp, MethodEnv* env) { // Continue at safepoint instruction. AvmAssert(safePoint != NULL); Atom* vars = addrInFrame(fp, deoptData->vars_offset_); uint8_t* tags = addrInFrame(fp, deoptData->tags_offset_); const MethodSignature* ms = env->method->getMethodSignature(); CLFramePopulator populator(env->method, ms, safePoint, vars, tags); return interpBoxedAtLocation(env, safePoint->abc_ix_, populator); } Atom CLDeoptContext::interpretFromCallSafepoint(uint8_t* fp, MethodEnv* env, Atom returnVal) { AvmAssert(safePoint != NULL); AvmAssert(isSafepointAtAbcCall()); Atom* vars = addrInFrame(fp, deoptData->vars_offset_); uint8_t* tags = addrInFrame(fp, deoptData->tags_offset_); // Interpreter frame will reconstruct state at call, with boxed result pushed on operand stack. const MethodSignature* ms = env->method->getMethodSignature(); CLReturnFramePopulator populator(env->method, ms, safePoint, vars, tags, returnVal); return interpBoxedAtLocation(env, (safePoint->abc_ix_ + safePoint->call_size_), populator); } Atom CLDeoptContext::interpretFromThrowSafepoint(uint8_t* fp, MethodEnv* env, int32_t handlerVpc, Atom exnVal) { // Continue at handler target, with stacks reset and exception pushed. AvmAssert(safePoint != NULL); AvmAssert(isSafepointAtThrow()); Atom* vars = addrInFrame(fp, deoptData->vars_offset_); uint8_t* tags = addrInFrame(fp, deoptData->tags_offset_); const MethodSignature* ms = env->method->getMethodSignature(); CLHandlerFramePopulator populator(env->method, ms, safePoint, vars, tags, exnVal); return interpBoxedAtLocation(env, handlerVpc, populator); } // Deoptimizer for compiled methods generated by CodegenLIR. typedef DeoptimizerInstance CLDeoptimizer; Deoptimizer* CLDeoptData::createDeoptimizer() { // TODO: Allocate this in the correct arena. return new CLDeoptimizer(methodInfo, this); } } // namespace avmplus { #endif // VMCFG_HALFMOON