Arquivos
hhvm/hphp/runtime/vm/jit/region_selection.cpp
T
bsimmers 26c74bf8e9 Pass known Func*s through in the region translator
Pretty straightforward, since the hard part of figuring out
when the Func* is known is still handled in Translator::analyze.
2013-06-27 10:38:22 -07:00

427 linhas
12 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2013 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 <boost/range/adaptors.hpp>
#include "folly/Memory.h"
#include "folly/Conv.h"
#include "hphp/util/assertions.h"
#include "hphp/util/map_walker.h"
#include "hphp/runtime/base/runtime_option.h"
#include "hphp/runtime/vm/jit/translator.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,
Tracelet,
};
RegionMode regionMode() {
auto& s = RuntimeOption::EvalJitRegionSelector;
if (s == "") return RegionMode::None;
if (s == "onebc") return RegionMode::OneBC;
if (s == "method") return RegionMode::Method;
if (s == "tracelet") return RegionMode::Tracelet;
FTRACE(1, "unknown region mode {}: using none\n", s);
if (debug) abort();
return RegionMode::None;
}
}
//////////////////////////////////////////////////////////////////////
void RegionDesc::Block::addPredicted(SrcKey sk, TypePred pred) {
assert(pred.type.subtypeOf(Type::Gen | Type::Cls));
m_typePreds.insert(std::make_pair(sk, pred));
checkInvariants();
}
void RegionDesc::Block::setParamByRef(SrcKey sk, ParamByRef byRef) {
assert(m_byRefs.find(sk) == m_byRefs.end());
m_byRefs.insert(std::make_pair(sk, byRef));
checkInvariants();
}
void RegionDesc::Block::addReffinessPred(SrcKey sk, const ReffinessPred& pred) {
m_refPreds.insert(std::make_pair(sk, pred));
checkInvariants();
}
void RegionDesc::Block::setKnownFunc(SrcKey sk, const Func* func) {
assert(m_knownFuncs.find(sk) == m_knownFuncs.end());
m_knownFuncs.insert(std::make_pair(sk, func));
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. Each SrcKey in m_typePreds, m_byRefs, m_refPreds, and m_knownFuncs is
* within the bounds of the block.
*
* 3. Each local id referred to in the type prediction list is valid.
*
* 4. (Unchecked) each stack offset in the type prediction list is
* valid.
*/
void RegionDesc::Block::checkInvariants() const {
if (!debug || length() == 0) return;
smart::set<SrcKey> keysInRange;
auto firstKey = [&] { return *keysInRange.begin(); };
auto lastKey = [&] {
assert(!keysInRange.empty());
return *--keysInRange.end();
};
keysInRange.insert(start());
for (int i = 1; i < length(); ++i) {
if (i != length() - 1) {
auto const pc = unit()->at(lastKey().offset());
if (instrFlags(toOp(*pc)) & TF) {
FTRACE(1, "Bad block: {}\n", show(*this));
assert(!"Block may not contain non-fallthrough instruction unless "
"they are last");
}
if (instrIsNonCallControlFlow(toOp(*pc))) {
FTRACE(1, "Bad block: {}\n", show(*this));
assert(!"Block may not contain control flow instructions unless "
"they are last");
}
}
keysInRange.insert(lastKey().advanced(unit()));
}
assert(keysInRange.size() == length());
auto rangeCheck = [&](const char* type, SrcKey sk) {
if (!keysInRange.count(sk)) {
std::cerr << folly::format("{} at {} outside range [{}, {}]\n",
type, show(sk),
show(firstKey()), show(lastKey()));
assert(!"Region::Block contained out-of-range metadata");
}
};
for (auto& tpred : m_typePreds) {
rangeCheck("type prediction", tpred.first);
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;
}
}
for (auto& byRef : m_byRefs) {
rangeCheck("parameter reference flag", byRef.first);
}
for (auto& refPred : m_refPreds) {
rangeCheck("reffiness prediction", refPred.first);
}
for (auto& func : m_knownFuncs) {
rangeCheck("known Func*", func.first);
}
}
//////////////////////////////////////////////////////////////////////
namespace {
RegionDescPtr createRegion(const Transl::Tracelet& tlet) {
typedef Transl::NormalizedInstruction NI;
typedef RegionDesc::Block Block;
auto region = smart::make_unique<RegionDesc>();
SrcKey sk(tlet.m_sk);
assert(sk == tlet.m_instrStream.first->source);
auto unit = tlet.m_instrStream.first->unit();
const Func* topFunc = nullptr;
Block* curBlock;
auto newBlock = [&] {
region->blocks.push_back(
smart::make_unique<Block>(tlet.m_func, sk.offset(), 0));
curBlock = region->blocks.back().get();
};
newBlock();
for (auto ni = tlet.m_instrStream.first; ni; ni = ni->next) {
assert(sk == ni->source);
assert(ni->unit() == unit);
curBlock->addInstruction();
if (curBlock->length() == 1 || ni->funcd != topFunc) {
topFunc = ni->funcd;
curBlock->setKnownFunc(sk, topFunc);
}
if (!ni->noOp && isFPassStar(ni->op())) {
curBlock->setParamByRef(
sk, ni->preppedByRef ? RegionDesc::ParamByRef::Yes
: RegionDesc::ParamByRef::No);
}
if (ni->op() == OpJmp && ni->next) {
// A Jmp that isn't the final instruction in a Tracelet means we traced
// through a forward jump in analyze. Update sk to point to the next NI
// in the stream.
auto dest = ni->offset() + ni->imm[0].u_BA;
assert(dest > sk.offset()); // We only trace for forward Jmps for now.
sk.setOffset(dest);
// The Jmp terminates this block.
newBlock();
} else {
sk.advance(unit);
}
}
auto& frontBlock = *region->blocks.front();
// Add tracelet guards as predictions on the first instruction. Predictions
// and known types from static analysis will be applied by
// Translator::translateRegion.
for (auto const& dep : tlet.m_dependencies) {
if (dep.second->rtt.isVagueValue() ||
dep.second->location.isThis()) continue;
typedef RegionDesc R;
auto addPred = [&](const R::Location& loc) {
auto type = Type::fromRuntimeType(dep.second->rtt);
frontBlock.addPredicted(tlet.m_sk, {loc, type});
};
switch (dep.first.space) {
case Transl::Location::Stack:
addPred(R::Location::Stack{uint32_t(-dep.first.offset - 1)});
break;
case Transl::Location::Local:
addPred(R::Location::Local{uint32_t(dep.first.offset)});
break;
default: not_reached();
}
}
// Add reffiness dependencies as predictions on the first instruction.
for (auto const& dep : tlet.m_refDeps.m_arMap) {
RegionDesc::ReffinessPred pred{dep.second.m_mask,
dep.second.m_vals,
dep.first};
frontBlock.addReffinessPred(tlet.m_sk, pred);
}
FTRACE(2, "Converted Tracelet:\n{}\nInto RegionDesc:\n{}\n",
tlet.toString(), show(*region));
return region;
}
}
RegionDescPtr selectRegion(const RegionContext& context,
const Transl::Tracelet* t) {
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);
case RegionMode::Tracelet: always_assert(t); return createRegion(*t);
}
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(const RegionDesc::ReffinessPred& pred) {
std::ostringstream out;
out << "offset: " << pred.arSpOffset << " mask: ";
for (auto const bit : pred.mask) out << (bit ? '1' : '0');
out << " vals: ";
for (auto const bit : pred.vals) out << (bit ? '1' : '0');
return out.str();
}
std::string show(RegionDesc::ParamByRef byRef) {
switch (byRef) {
case RegionDesc::ParamByRef::Yes: return "by value";
case RegionDesc::ParamByRef::No: return "by reference";
}
not_reached();
}
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 typePreds = makeMapWalker(b.typePreds());
auto byRefs = makeMapWalker(b.paramByRefs());
auto refPreds = makeMapWalker(b.reffinessPreds());
auto knownFuncs= makeMapWalker(b.knownFuncs());
auto skIter = b.start();
const Func* currentFunc = nullptr;
for (int i = 0; i < b.length(); ++i) {
while (typePreds.hasNext(skIter)) {
folly::toAppend(" predict: ", show(typePreds.next()), "\n", &ret);
}
while (refPreds.hasNext(skIter)) {
folly::toAppend(" predict reffiness: ", show(refPreds.next()), "\n",
&ret);
}
std::string knownFunc;
if (knownFuncs.hasNext(skIter)) {
currentFunc = knownFuncs.next();
}
if (currentFunc) {
knownFunc = folly::format(" (top func: {})",
currentFunc->fullName()->data()).str();
}
std::string byRef;
if (byRefs.hasNext(skIter)) {
byRef = folly::format(" (passed {})", show(byRefs.next())).str();
}
folly::toAppend(
" ",
skIter.offset(),
" ",
instrToString((Op*)b.unit()->at(skIter.offset()), b.unit()),
knownFunc,
byRef,
"\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();
}
//////////////////////////////////////////////////////////////////////
}}