Files
avmplus/halfmoon/hm-jitwriter.cpp
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

296 lines
9.3 KiB
C++

/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
/* vi: set ts=2 sw=2 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 "hm-main.h"
#ifdef VMCFG_HALFMOON
namespace halfmoon {
/**
* Notes on hooking up to CodegenLIR.
*
* Lots of code in CodegenLIR depends on having FrameState handy,
* so it can access Value.traits, etc, for stuff. We could factor
* that out so most code takes Value arguments, and a new driver_
* could provide Values that correspond to IR values.
*
* Worse: deadvars() and VarTracker assume the layout of the frame
* is whatever the verifier had. A complete boiling of the code and
* IR would destroy any such thing.
*
* In debugger mode, the generated code must have an array of tags
* and vars that the debugger can inspect. We don't want to do that at
* all for the high level JIT. Debugging will only work on the interpreter
* or on CT code, and we'll use bailout to support it.
*
* VarTracker is essential for eliminating a lot of the stack motion,
* but we'd take care of that in the high level IR. Same for null
* pointer check elimination; we'd take care of that too.
*
* deadvars() needs an equivalent pass on the high level jit that
* eliminates SetlocalInstrs that will never be read from. There are two
* interesting cases: 1) exceptions, 2) debugger. For the time being
* we can ignore both, if we count on bailing out for both.
*
* This implies we can't completely get rid of the existing jit; we will
* want to use it for any method the new jit can't compile. We might also
* need it eventually as a baseline jit if we have a 3-stage system.
*
* Strategies:
*
* 1. Drive the current jit through the CodeWriter interface. Requires
* writing a scheduling pass on the IR that generates a linear sequence
* of codewriter calls, to simulate what verifier does. This includes
* stack scheduling - sending push, pop, getlocal, etc to the jit.
*
* FrameState usage is all over the place in CodegenLIR. This could be a
* major refactoring pain in the neck. On the other hand, the fact that
* FS is used everywhere means we need its info everywhere.
*
* 2. Factor CodegenLIR into two layers. The middle layer would not have
* dependencies on FrameState or AbcOpcode, and could be driven from a
* walk of our CFG or IG.
*
* 3. Write a from-scratch emitter that drives nanojit. We can factor
* useful pieces of code out of CodegenLIR instead of copy-pasting them,
* but the overall structure of the emitter would be completely new.
*
*/
JitWriter::JitWriter(MethodInfo* method, Toplevel *tl, AbcEnv *abc_env)
: method_(method)
, console_(method->pool()->core->console)
, jit_mgr_(0)
, abc_(0)
, current_block_(0)
, abc_env_(abc_env)
, toplevel_(tl)
, calling_context_(NULL) //matz_inline_experiment
{
}
JitWriter::JitWriter(MethodInfo* method, const Context *calling_context)
: method_(method)
, console_(method->pool()->core->console)
, jit_mgr_(0)
, abc_(0)
, current_block_(0)
, abc_env_(calling_context->abc_env)
, toplevel_(calling_context->toplevel)
, calling_context_(calling_context) {
assert(calling_context_ && "calling_context cannot be null");
}
JitWriter::~JitWriter() {
abc_->~AbcGraph();
if (enable_profiler) profiled_info_->~ProfiledInformation();
cleanup();
}
void JitWriter::writePrologue(const FrameState* frame, const uint8_t* pc,
CodegenDriver*) {
assert(frame->abc_pc == pc);
(void) frame;
(void) pc;
jit_mgr_ = JitManager::init(method_->pool());
#ifdef AVMPLUS_VERBOSE
if (enable_welcome)
console_ << "analyze " << method_ << "\n";
#endif
abc_ = new (alloc_) AbcGraph(method_);
if (enable_profiler) {
profiled_info_ = new (alloc_) ProfiledInformation();
}
startBlock(frame);
}
void JitWriter::writeEpilogue(const FrameState*) {
if (enable_verbose) {
#ifdef AVMPLUS_VERBOSE
console_ << "analyze " << method_ << "\n";
#endif
}
Context cxt(method_, method_->pool()->core->console, toplevel_, abc_env_, calling_context_);
InstrGraph* ir = parseAbc(method_, jit_mgr_->lattice(), jit_mgr_->infos(),
jit_mgr_->mgr_alloc(), abc_,
toplevel_, abc_env_, profiled_info_,
cxt);
jit_mgr_->set_ir(method_, ir);
switch (enable_mode) {
case kModeInterpret:
jit_mgr_->set_interp(method_, ir);
break;
case kModeLirStubs:
case kModeLir:
jit_mgr_->set_lir(method_, ir, profiled_info_);
break;
}
}
GprMethodProc JitWriter::finish() {
return jit_mgr_->getImpl(method_);
}
InstrGraph* JitWriter::ir() {
return jit_mgr_->ir(method_);
}
void JitWriter::analyze(AbcOpcode abcop, const uint8_t* pc,
const FrameState* frame) {
assert(pc == frame->abc_pc);
(void) frame;
#ifdef AVMPLUS_VERBOSE
if (enable_verbose) {
console_ << "analyze " << opcodeInfo[abcop].name << '\n';
}
#else
(void) abcop;
#endif
if (current_block_ && pc > current_block_->start && abc_->haveBlock(pc)) {
#ifdef AVMPLUS_VERBOSE
if (enable_verbose)
console_ << "surprise\n";
#endif
abc_->analyzeEnd(current_block_, pc);
finishBlock(pc);
newBlock(pc, frame);
}
}
void JitWriter::finishBlock(const uint8_t* nextpc) {
current_block_->end = nextpc;
current_block_ = 0;
}
void JitWriter::newBlock(const uint8_t* abc_pc, const FrameState* frame) {
if (current_block_ && current_block_->start == abc_pc) {
// we just started this block. ignore.
return;
}
current_block_ = abc_->newAbcBlock(abc_pc);
current_block_->start_withbase = frame->withBase;
abc_->analyzeExceptions(current_block_);
}
void JitWriter::startBlock(const FrameState* frame) {
newBlock(frame->abc_pc, frame);
MethodSignaturep signature = method_->getMethodSignature();
const Type** types = new (abc_->alloc0()) const Type*[signature->frame_size()];
FrameRange<const FrameValue> from = range(&frame->value(0), frame, signature);
FrameRange<const Type*> t = range(types, frame, signature);
for (; !from.empty(); from.popFront(), t.popFront())
t.front() = jit_mgr_->lattice()->makeType(from.front());
current_block_->start_types = types;
}
static bool needSavedScopes(const uint8_t *pc, AbcOpcode op) {
switch ((AbcOpcode) *pc) {
case OP_getlex:
case OP_findpropstrict:
case OP_findproperty:
switch (op) {
case OP_findpropstrict:
case OP_findproperty:
case OP_getscopeobject:
case OP_getouterscope:
case OP_finddef:
case OP_findpropglobal:
case OP_findpropglobalstrict:
case OP_getglobalscope:
return true;
default:
break;
}
default:
break;
}
return false;
}
void JitWriter::write(const FrameState* frame, const uint8_t* pc,
AbcOpcode abcop, Traits*) {
analyze(abcop, pc, frame);
if (needSavedScopes(pc, abcop))
abc_->abc_instrs.put(pc, new (abc_->alloc_) AbcInstr(abcop));
if (abcop == OP_lookupswitch || isEndOpcode(abcop)) {
uint32_t imm30 = 0, imm30b = 0;
int imm8 = 0, imm24 = 0;
const uint8_t* nextpc = pc;
AvmCore::readOperands(nextpc, imm30, imm24, imm30b, imm8);
if (abcop == OP_lookupswitch)
abc_->analyzeSwitch(current_block_, pc, nextpc, imm24, imm30b + 1);
finishBlock(nextpc);
}
}
void JitWriter::writeOp1(const FrameState* frame, const uint8_t *pc,
AbcOpcode abcop, uint32_t opd1, Traits*) {
analyze(abcop, pc, frame);
if (needSavedScopes(pc, abcop))
abc_->abc_instrs.put(pc, new (abc_->alloc_) AbcInstr(abcop, opd1));
if (isBranchOpcode(abcop)) {
const uint8_t* nextpc = pc + 4;
int offset = int32_t(opd1);
if (abcop == OP_jump) {
abc_->analyzeEnd(current_block_, nextpc + offset);
finishBlock(nextpc);
} else {
abc_->analyzeBranch(current_block_, abcop, nextpc, offset);
finishBlock(nextpc);
newBlock(nextpc, frame);
}
}
}
void JitWriter::writeOp2(const FrameState* frame, const uint8_t *pc,
AbcOpcode abcop, uint32_t, uint32_t, Traits*) {
analyze(abcop, pc, frame);
}
void JitWriter::writeMethodCall(const FrameState* frame, const uint8_t *pc,
AbcOpcode abcop, MethodInfo*, uintptr_t,
uint32_t, Traits*) {
analyze(abcop, pc, frame);
}
void JitWriter::writeNip(const FrameState* frame, const uint8_t *pc,
uint32_t count) {
while (count--) {
analyze(OP_swap, pc, frame);
analyze(OP_pop, pc, frame);
}
}
void JitWriter::writeCheckNull(const FrameState* frame, uint32_t) {
analyze(OP_convert_o, frame->abc_pc, frame);
}
void JitWriter::writeCoerce(const FrameState* frame, uint32_t, Traits*) {
analyze(OP_coerce, frame->abc_pc, frame);
}
void JitWriter::writeBlockStart(const FrameState* frame) {
assert((!current_block_ || current_block_->start <= frame->abc_pc) &&
"didn't expect blocks out of order");
const uint8_t* pc = frame->abc_pc;
if (current_block_ && current_block_->start < pc) {
abc_->analyzeEnd(current_block_, pc);
finishBlock(pc);
}
startBlock(frame);
analyze(OP_label, pc, frame);
}
} // namespace avmplus
#endif // VMCFG_HALFMOON