Arquivos
hhvm/hphp/runtime/vm/jit/region_selection.cpp
T
Jordan DeLong dc6dc022e7 Initial API for region selectors
Contains some structures to use for communicating between
compilation region selectors and the JIT.  For now includes two
debugging-oriented region selectors, one that uses a single HHBC
opcode at a time, and one that blindly pushes in the whole CFG for a
method using the Verifier::GraphBuilder.
2013-06-06 15:58:14 -07:00

268 linhas
7.6 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 "hphp/runtime/vm/jit/region_selection.h"
#include <algorithm>
#include "folly/Memory.h"
#include "folly/Conv.h"
#include "hphp/util/assertions.h"
#include "hphp/runtime/base/runtime_option.h"
namespace HPHP { namespace JIT {
TRACE_SET_MOD(region);
//////////////////////////////////////////////////////////////////////
extern RegionDescPtr regionMethod(const RegionContext&);
extern RegionDescPtr regionOneBC(const RegionContext&);
//////////////////////////////////////////////////////////////////////
namespace {
enum class RegionMode {
None,
OneBC,
Method,
};
RegionMode regionMode() {
auto& s = RuntimeOption::EvalJitRegionSelector;
if (s == "") return RegionMode::None;
if (s == "onebc") return RegionMode::OneBC;
if (s == "method") return RegionMode::Method;
FTRACE(1, "unknown region mode {}: using none\n", s);
if (debug) abort();
return RegionMode::None;
}
}
//////////////////////////////////////////////////////////////////////
void RegionDesc::Block::addPredicted(SrcKey sk, TypePred pred) {
auto const newElem = std::make_pair(sk, pred);
auto const it = std::lower_bound(m_predTypes.begin(), m_predTypes.end(),
newElem, typePredListCmp);
m_predTypes.insert(it, newElem);
checkInvariants();
}
/*
* Check invariants on a RegionDesc::Block.
*
* 1. Single entry, single exit (aside from exceptions). I.e. no
* non-fallthrough instructions mid-block and no control flow (not
* counting calls as control flow).
*
* 2. The type prediction list is ordered by increasing SrcKey.
*
* 3. Each prediction in the type prediction list is inside the range
* of this block.
*
* 4. Each local id referred to in the type prediction list is valid.
*
* 5. (Unchecked) each stack offset in the type prediction list is
* valid.
*/
void RegionDesc::Block::checkInvariants() const {
smart::vector<SrcKey> keysInRange;
keysInRange.reserve(length());
keysInRange.push_back(start());
for (int i = 1; i < length(); ++i) {
if (i != length() - 1) {
auto const pc = unit()->at(keysInRange.back().offset());
if (instrFlags(*pc) & TF) {
FTRACE(1, "Bad block: {}\n", show(*this));
assert(!"Block may not contain non-fallthrough instruction unless "
"they are last");
}
if (instrIsNonCallControlFlow(*pc)) {
FTRACE(1, "Bad block: {}\n", show(*this));
assert(!"Block may not contain control flow instructions unless "
"they are last");
}
}
keysInRange.push_back(keysInRange.back().advanced(unit()));
}
assert(keysInRange.size() == length());
assert(std::is_sorted(m_predTypes.begin(), m_predTypes.end(),
typePredListCmp));
assert(std::is_sorted(keysInRange.begin(), keysInRange.end()));
auto rangeIt = keysInRange.begin();
for (auto& tpred : m_predTypes) {
while (rangeIt != keysInRange.end() && *rangeIt < tpred.first) ++rangeIt;
assert(rangeIt != keysInRange.end() && tpred.first == *rangeIt &&
"RegionDesc::Block contained an out-of-range prediction");
auto& loc = tpred.second.location;
switch (loc.tag()) {
case Location::Tag::Local: assert(loc.localId() < m_func->numLocals());
break;
case Location::Tag::Stack: // Unchecked
break;
}
}
}
bool RegionDesc::Block::typePredListCmp(TypePredList::const_reference a,
TypePredList::const_reference b) {
return a.first < b.first;
}
//////////////////////////////////////////////////////////////////////
RegionDescPtr selectRegion(const RegionContext& context) {
auto const mode = regionMode();
FTRACE(1,
"Select region: {}@{} mode={} context:\n{}{}",
context.func->fullName()->data(),
context.offset,
static_cast<int>(mode),
[&]{
std::string ret;
for (auto& t : context.liveTypes) {
folly::toAppend(" ", show(t), "\n", &ret);
}
return ret;
}(),
[&]{
std::string ret;
for (auto& ar : context.preLiveARs) {
folly::toAppend(" ", show(ar), "\n", &ret);
}
return ret;
}()
);
auto region = [&]{
try {
switch (mode) {
case RegionMode::None: return RegionDescPtr{nullptr};
case RegionMode::OneBC: return regionOneBC(context);
case RegionMode::Method: return regionMethod(context);
}
not_reached();
} catch (const std::exception& e) {
FTRACE(1, "region selector threw: {}\n", e.what());
return RegionDescPtr{nullptr};
}
}();
if (region) {
FTRACE(3, "{}", show(*region));
} else {
FTRACE(1, "no region selectable; using tracelet compiler\n");
}
return region;
}
//////////////////////////////////////////////////////////////////////
std::string show(RegionDesc::Location l) {
switch (l.tag()) {
case RegionDesc::Location::Tag::Local:
return folly::format("Local{{{}}}", l.localId()).str();
case RegionDesc::Location::Tag::Stack:
return folly::format("Stack{{{}}}", l.stackOffset()).str();
}
not_reached();
}
std::string show(RegionDesc::TypePred ta) {
return folly::format(
"{} :: {}",
show(ta.location),
ta.type.toString()
).str();
}
std::string show(RegionContext::LiveType ta) {
return folly::format(
"{} :: {}",
show(ta.location),
ta.type.toString()
).str();
}
std::string show(RegionContext::PreLiveAR ar) {
return folly::format(
"AR@{}: {} ({})",
ar.stackOff,
ar.func->fullName()->data(),
ar.objOrCls.toString()
).str();
}
std::string show(const RegionDesc::Block& b) {
std::string ret{"Block "};
folly::toAppend(
b.func()->fullName()->data(), '@', b.start().offset(),
" length ", b.length(), '\n',
&ret
);
auto const& tpRange = b.typePreds();
auto tpIter = begin(tpRange);
auto skIter = b.start();
for (int i = 0; i < b.length(); ++i) {
while (tpIter != end(tpRange) && tpIter->first < skIter) {
++tpIter;
}
while (tpIter != end(tpRange) && tpIter->first == skIter) {
folly::toAppend(" predict: ", show(tpIter->second), "\n", &ret);
++tpIter;
}
folly::toAppend(
" ",
skIter.offset(),
" ",
instrToString(b.unit()->at(skIter.offset()), b.unit()),
'\n',
&ret
);
skIter.advance(b.unit());
}
return ret;
}
std::string show(const RegionDesc& region) {
return folly::format(
"Region ({} blocks):\n{}",
region.blocks.size(),
[&]{
std::string ret;
for (auto& b : region.blocks) {
folly::toAppend(show(*b), &ret);
}
return ret;
}()
).str();
}
//////////////////////////////////////////////////////////////////////
}}