Arquivos
hhvm/hphp/compiler/analysis/peephole.cpp
T
Paul Tarjan fef62f03a2 kill VM namespace
Now that HHVM is the default runtime, this namespace doesn't mean anything.
2013-05-15 13:05:05 -07:00

181 linhas
5.9 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include "compiler/analysis/peephole.h"
#include "compiler/analysis/emitter.h"
namespace HPHP { namespace Compiler {
static void collapseJmp(Offset* offsetPtr, Opcode* instr, Opcode* start) {
if (offsetPtr) {
Opcode* dest = instr + *offsetPtr;
while (*dest == OpJmp && dest != instr) {
dest = start + instrJumpTarget(start, dest - start);
}
*offsetPtr = dest - instr;
}
}
Peephole::Peephole(UnitEmitter &ue, MetaInfoBuilder& metaInfo)
: m_ue(ue) {
// be careful about an empty input
if (ue.m_bclen == 0) {
return;
}
// We need to know which instructions may be jumped to, since this blocks
// certain optimizations
buildJumpTargets();
// Scan the bytecode linearly.
Opcode* start = ue.m_bc;
Opcode* prev = start;
Opcode* cur = prev + instrLen(prev);
Opcode* end = start + ue.m_bclen;
/*
* TODO(1086005): we should try to minimize use of CGetL2/CGetL3.
* (When they appear in an order like "CGetL; CGetL2" we can just
* switch them to "CGetL; CGetL".)
*/
while (cur < end) {
if (LIKELY(!m_jumpTargets.count(cur - start))) {
// prev and cur are always dynamically adjacent (i.e. whenever cur is
// executed, prev was always the previous instruction), so we can optimize
// them based on this assumption.
// Not, JmpZ -> Nop, JmpNZ (and vice versa)
// We replace the Not with a Nop, instead of shifting up the rest of the
// bytecode. Shifting has a lot of costs: copying bytecode around, and
// remapping jump targets and line numbers. This optimization is rare
// enough that the space savings are not worthwhile.
if (*prev == OpNot) {
if (*cur == OpJmpZ) {
*prev = OpNop;
*cur = OpJmpNZ;
} else if (*cur == OpJmpNZ) {
*prev = OpNop;
*cur = OpJmpZ;
}
metaInfo.deleteInfo(prev - start);
}
// IncDec* Post*, PopC -> IncDec* Pre*, PopC
ArgUnion* imm = 0;
if (*cur == OpPopC) {
switch (*prev) {
case OpIncDecL:
imm = getImmPtr(prev, 1);
goto incDecOp;
case OpIncDecN:
case OpIncDecG:
case OpIncDecS:
case OpIncDecM:
imm = getImmPtr(prev, 0);
// fallthrough
incDecOp:
if (imm->u_OA == PostInc) {
imm->u_OA = PreInc;
} else if (imm->u_OA == PostDec) {
imm->u_OA = PreDec;
}
break;
default:
break;
}
}
}
// Simplify jumps. Follow a jump's target until it lands on something that
// isn't an unconditional jump. Then rewrite the offset to cut out any
// intermediate jumps.
if (isSwitch(*prev)) {
foreachSwitchTarget(prev, [&](Offset& o) {
collapseJmp(&o, prev, start);
});
} else {
collapseJmp(instrJumpOffset(prev), prev, start);
}
prev = cur;
cur = cur + instrLen(cur);
}
}
void Peephole::buildFuncTargets(FuncEmitter* fe) {
m_jumpTargets.insert(fe->base());
for (FuncEmitter::EHEntVec::const_iterator it = fe->ehtab().begin();
it != fe->ehtab().end(); ++it) {
m_jumpTargets.insert(it->m_base);
m_jumpTargets.insert(it->m_past);
for (EHEnt::CatchVec::const_iterator catchIt = it->m_catches.begin();
catchIt != it->m_catches.end(); ++catchIt) {
m_jumpTargets.insert(catchIt->second);
}
m_jumpTargets.insert(it->m_fault);
}
for (uint i = 0; i < fe->params().size(); i++) {
const FuncEmitter::ParamInfo& pi = fe->params()[i];
if (pi.hasDefaultValue()) {
m_jumpTargets.insert(pi.funcletOff());
}
}
for (FuncEmitter::FPIEntVec::const_iterator it = fe->fpitab().begin();
it != fe->fpitab().end(); ++it) {
m_jumpTargets.insert(it->m_fpushOff);
m_jumpTargets.insert(it->m_fcallOff);
}
}
void Peephole::buildJumpTargets() {
// all function start locations, exception handlers, default value funclets,
// and FPI regions are targets
for (UnitEmitter::FeVec::const_iterator it = m_ue.m_fes.begin();
it != m_ue.m_fes.end(); ++it) {
FuncEmitter *fe = *it;
buildFuncTargets(fe);
}
for (UnitEmitter::PceVec::const_iterator it = m_ue.m_pceVec.begin();
it != m_ue.m_pceVec.end(); ++it) {
for (PreClassEmitter::MethodVec::const_iterator mit =
(*it)->methods().begin(); mit != (*it)->methods().end(); ++mit) {
FuncEmitter* fe = *mit;
buildFuncTargets(fe);
}
}
// all jump targets are targets
for (Offset pos = 0; pos < (Offset)m_ue.m_bclen;
pos += instrLen(&m_ue.m_bc[pos])) {
Opcode* instr = &m_ue.m_bc[pos];
if (isSwitch(*instr)) {
foreachSwitchTarget(instr, [&](Offset& o) {
m_jumpTargets.insert(pos + o);
});
} else {
Offset target = instrJumpTarget(m_ue.m_bc, pos);
if (target != InvalidAbsoluteOffset) {
m_jumpTargets.insert(target);
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
}}