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.
Esse commit está contido em:
Jordan DeLong
2013-05-31 17:10:47 -07:00
commit de sgolemon
commit dc6dc022e7
13 arquivos alterados com 737 adições e 3 exclusões
+1
Ver Arquivo
@@ -440,6 +440,7 @@ public:
F(bool, HHIRDeadCodeElim, true) \
F(bool, HHIRPredictionOpts, true) \
F(bool, HHIRStressCodegenBlocks, false) \
F(string, JitRegionSelector, "") \
/* DumpBytecode =1 dumps user php, =2 dumps systemlib & user php */ \
F(int32_t, DumpBytecode, 0) \
F(bool, DumpTC, false) \
+8
Ver Arquivo
@@ -893,6 +893,14 @@ bool instrIsControlFlow(Opcode opcode) {
return (opFlags & CF) != 0;
}
bool instrIsNonCallControlFlow(Opcode opcode) {
return
instrIsControlFlow(opcode) &&
!isFCallStar(opcode) &&
opcode != OpContEnter &&
opcode != OpFCallBuiltin;
}
bool instrAllowsFallThru(Opcode opcode) {
InstrFlags opFlags = instrFlags(opcode);
return (opFlags & TF) == 0;
+1
Ver Arquivo
@@ -762,6 +762,7 @@ struct StackTransInfo {
bool isValidOpcode(Opcode opcode);
bool instrIsControlFlow(Opcode opcode);
bool instrIsNonCallControlFlow(Opcode opcode);
bool instrAllowsFallThru(Opcode opcode);
bool instrReadsCurrentFpi(Opcode opcode);
+112
Ver Arquivo
@@ -0,0 +1,112 @@
/*
+----------------------------------------------------------------------+
| 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/util/arena.h"
#include "hphp/runtime/base/memory/smart_containers.h"
#include "hphp/runtime/vm/jit/region_selection.h"
#include "hphp/runtime/vm/verifier/cfg.h"
namespace HPHP { namespace JIT {
TRACE_SET_MOD(region);
//////////////////////////////////////////////////////////////////////
namespace {
bool isFuncEntry(const Func* func, Offset off) {
return off == func->base();
}
int numInstrs(PC start, PC end) {
int ret{};
for (; start != end; ++ret) {
start += instrLen(start);
}
return ret;
}
}
//////////////////////////////////////////////////////////////////////
/*
* Region-selector that unintelligently takes a whole method at a
* time. This is primarily intended for use for debugging and
* development on the JIT.
*
* Adds no type annotations to the region beyond those known from the
* context. It will list only the parameter types as guards.
*
* If the context is not a method entry point, returns nullptr to fall
* back to the tracelet compiler. (This will happen for side-exits
* from method regions, for example.)
*/
RegionDescPtr regionMethod(const RegionContext& context) {
using namespace HPHP::Verifier;
if (!isFuncEntry(context.func, context.offset)) return nullptr;
FTRACE(1, "function entry for {}: using regionMethod\n",
context.func->fullName()->data());
auto ret = smart::make_unique<RegionDesc>();
Arena arena;
GraphBuilder gb(arena, context.func);
auto const graph = gb.build();
auto const unit = context.func->unit();
/*
* Spit out the blocks in a RPO, but skip DV-initializer blocks
* (i.e. start with graph->first_linear. We don't handle those in
* our method regions for now---they'll get handled by the tracelet
* compiler and then may branch to the main entry point.
*/
sortRpo(graph);
for (Block* b = graph->first_linear; b != nullptr; b = b->next_rpo) {
auto const start = unit->offsetOf(b->start);
auto const length = numInstrs(b->start, b->end);
ret->blocks.emplace_back(
smart::make_unique<RegionDesc::Block>(context.func, start, length)
);
}
assert(!ret->blocks.empty());
auto const startSK = ret->blocks.front()->start();
for (auto& lt : context.liveTypes) {
typedef RegionDesc::Location::Tag LTag;
switch (lt.location.tag()) {
case LTag::Stack:
break;
case LTag::Local:
if (lt.location.localId() < context.func->numParams()) {
// Only predict objectness, not the specific class type.
auto const type = lt.type.strictSubtypeOf(Type::Obj)
? Type::Obj
: lt.type;
ret->blocks.front()->addPredicted(startSK, {lt.location, type});
}
break;
}
}
return ret;
}
//////////////////////////////////////////////////////////////////////
}}
+43
Ver Arquivo
@@ -0,0 +1,43 @@
/*
+----------------------------------------------------------------------+
| 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 "hphp/runtime/base/memory/smart_containers.h"
namespace HPHP { namespace JIT {
//////////////////////////////////////////////////////////////////////
/*
* A dummy (debugging) region selector that just uses a single HHBC
* opcode as the region, and guards on everything.
*/
RegionDescPtr regionOneBC(const RegionContext& ctx) {
auto ret = smart::make_unique<RegionDesc>();
auto blk = smart::make_unique<RegionDesc::Block>(ctx.func, ctx.offset, 1);
for (auto& live : ctx.liveTypes) {
blk->addPredicted(SrcKey{ctx.func, ctx.offset},
{live.location, live.type});
}
ret->blocks.emplace_back(std::move(blk));
return ret;
}
//////////////////////////////////////////////////////////////////////
}}
+267
Ver Arquivo
@@ -0,0 +1,267 @@
/*
+----------------------------------------------------------------------+
| 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();
}
//////////////////////////////////////////////////////////////////////
}}
+231
Ver Arquivo
@@ -0,0 +1,231 @@
/*
+----------------------------------------------------------------------+
| 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. |
+----------------------------------------------------------------------+
*/
#ifndef incl_HPHP_JIT_REGION_SELECTION_H_
#define incl_HPHP_JIT_REGION_SELECTION_H_
#include <memory>
#include <utility>
#include <boost/range/iterator_range.hpp>
#include "folly/Format.h"
#include "hphp/runtime/base/memory/smart_containers.h"
#include "hphp/runtime/vm/srckey.h"
#include "hphp/runtime/vm/jit/type.h"
namespace HPHP { namespace JIT {
//////////////////////////////////////////////////////////////////////
/*
* RegionDesc is a description of a code region.
*
* It consists of a list of unique SrcKey ranges, with type
* annotations that may come from profiling or other sources.
*
* The first block is the entry point, and the remaining blocks must
* be sorted in a reverse post order.
*/
struct RegionDesc {
struct Block;
struct Location;
struct TypePred;
typedef smart::unique_ptr<Block>::type BlockPtr;
smart::vector<BlockPtr> blocks;
};
typedef smart::unique_ptr<RegionDesc>::type RegionDescPtr;
/*
* Specification of an HHBC-visible location that can have a type
* hint. This is currently either local variables or stack slots.
*
* Local variables are addressed by local id, and stack slots are
* addressed by offset from the top of the stack at the HHBC opcode
* being annoted. So Stack{0} is the top of the stack, Stack{1} is
* one slot under the top, etc.
*/
struct RegionDesc::Location {
enum class Tag : uint32_t {
Local,
Stack,
};
struct Local { uint32_t locId; };
struct Stack { uint32_t offset; };
/* implicit */ Location(Local l) : m_tag{Tag::Local}, m_local(l) {}
/* implicit */ Location(Stack s) : m_tag{Tag::Stack}, m_stack(s) {}
Tag tag() const { return m_tag; };
uint32_t localId() const {
assert(m_tag == Tag::Local);
return m_local.locId;
}
uint32_t stackOffset() const {
assert(m_tag == Tag::Stack);
return m_stack.offset;
}
private:
Tag m_tag;
union {
Local m_local;
Stack m_stack;
};
};
/*
* A type prediction for somewhere in the middle of or start of a
* region.
*
* All types annotated in the RegionDesc are expected to be
* predictions, i.e. things that need to be guarded on or checked and
* then side-exited on. Getting the ahead-of-time static types
* attached is handled by a different module.
*/
struct RegionDesc::TypePred {
Location location;
Type type;
};
/*
* A basic block in the region, with type predictions for conditions
* at various execution points, including at entry to the block.
*/
class RegionDesc::Block {
typedef smart::vector<std::pair<SrcKey,TypePred>> TypePredList;
public:
explicit Block(const Func* func, Offset start, int length)
: m_func(func)
, m_start(start)
, m_length(length)
{
if (debug) checkInvariants();
}
Block(const Block&) = delete;
Block& operator=(const Block&) = delete;
/*
* Accessors for the func, unit, length (in HHBC instructions), and
* starting SrcKey of this Block.
*/
const Unit* unit() const { return m_func->unit(); }
const Func* func() const { return m_func; }
SrcKey start() const { return SrcKey { m_func, m_start }; }
int length() const { return m_length; }
/*
* Add a predicted type to this block.
*
* Pre: sk is in the region delimited by this block.
*/
void addPredicted(SrcKey sk, TypePred);
/*
* Obtain a range for the type predictions on this block. The
* elements in the range are listed in ascending SrcKey order.
*
* The caller should assume it is a SinglePassReadableRange.
*/
boost::iterator_range<TypePredList::const_iterator> typePreds() const {
return boost::make_iterator_range(m_predTypes.begin(), m_predTypes.end());
}
private:
void checkInvariants() const;
static bool typePredListCmp(TypePredList::const_reference,
TypePredList::const_reference);
private:
const Func* m_func;
const Offset m_start;
const int m_length;
TypePredList m_predTypes;
};
//////////////////////////////////////////////////////////////////////
/*
* Information about the context in which we are selecting a region.
*
* Right now this is a source location, plus information about the
* live types that we need to be compiling for. There is no
* implication that the region selected will necessarily specialize
* for those types.
*/
struct RegionContext {
struct LiveType;
struct PreLiveAR;
const Func* func;
Offset offset;
smart::vector<LiveType> liveTypes;
smart::vector<PreLiveAR> preLiveARs;
};
/*
* Live information about the type of a local or stack slot.
*/
struct RegionContext::LiveType {
RegionDesc::Location location;
Type type;
};
/*
* Pre-live ActRec information for a RegionContext. The ActRec is
* located stackOff slots above the stack at the start of the context,
* contains the supplied func, and the m_this/m_class field is of type
* objOrClass.
*/
struct RegionContext::PreLiveAR {
uint32_t stackOff;
const Func* func;
Type objOrCls;
};
//////////////////////////////////////////////////////////////////////
/*
* Define a compilation region that starts with sk.
*
* May return nullptr.
*
* For now this is hooked up in TranslatorX64::createTranslation, and
* returning nullptr causes it to use the current level 0 tracelet
* analyzer. Eventually we'd like analyze to occur underneath this as
* well.
*/
RegionDescPtr selectRegion(const RegionContext&);
/*
* Debug stringification for various things.
*/
std::string show(RegionDesc::Location);
std::string show(RegionDesc::TypePred);
std::string show(RegionContext::LiveType);
std::string show(RegionContext::PreLiveAR);
std::string show(const RegionDesc::Block&);
std::string show(const RegionDesc&);
//////////////////////////////////////////////////////////////////////
}}
#endif
+54
Ver Arquivo
@@ -88,6 +88,7 @@ typedef __sighandler_t *sighandler_t;
#include "hphp/runtime/vm/jit/abi-x64.h"
#include "hphp/runtime/base/file_repository.h"
#include "hphp/runtime/vm/jit/hhbctranslator.h"
#include "hphp/runtime/vm/jit/region_selection.h"
#include "hphp/runtime/vm/jit/translator-x64-internal.h"
@@ -754,6 +755,47 @@ TranslatorX64::numTranslations(SrcKey sk) const {
return 0;
}
static void populateLiveContext(JIT::RegionContext& ctx) {
typedef JIT::RegionDesc::Location L;
const ActRec* const fp {g_vmContext->getFP()};
const TypedValue* const sp {g_vmContext->getStack().top()};
for (uint32_t i = 0; i < fp->m_func->numLocals(); ++i) {
ctx.liveTypes.push_back(
{ L::Local{i}, JIT::liveTVType(frame_local(fp, i)) }
);
}
uint32_t stackOff = 0;
visitStackElems(
fp, sp, ctx.offset,
[&](const ActRec* ar) {
// TODO(#2466980): when it's a Cls, we should pass the Class* in
// the Type.
using JIT::Type;
auto const objOrCls =
ar->hasThis() ? Type::Obj.specialize(ar->getThis()->getVMClass()) :
ar->hasClass() ? Type::Cls
: Type::Nullptr;
ctx.preLiveARs.push_back(
{ stackOff,
ar->m_func,
objOrCls
}
);
stackOff += kNumActRecCells;
},
[&](const TypedValue* tv) {
ctx.liveTypes.push_back(
{ L::Stack{stackOff++}, JIT::liveTVType(tv) }
);
}
);
}
TCA
TranslatorX64::createTranslation(const TranslArgs& args) {
/*
@@ -783,6 +825,18 @@ TranslatorX64::createTranslation(const TranslArgs& args) {
}
}
/*
* First test if we have a region-selector that can handle this
* SrcKey.
*/
JIT::RegionContext rContext { curFunc(), sk.offset() };
populateLiveContext(rContext);
if (auto UNUSED rd = JIT::selectRegion(rContext)) {
/*
* WIP. Unimplemented.
*/
}
// We put retranslate requests at the end of our slab to more frequently
// allow conditional jump fall-throughs
AHotSelector ahs(this, curFunc()->attrs() & AttrHot);
+10
Ver Arquivo
@@ -30,6 +30,8 @@ namespace HPHP { namespace JIT {
TRACE_SET_MOD(hhir);
//////////////////////////////////////////////////////////////////////
Type Type::fromDynLocation(const Transl::DynLocation* dynLoc) {
if (!dynLoc) {
return Type::None;
@@ -41,6 +43,14 @@ Type Type::fromDynLocation(const Transl::DynLocation* dynLoc) {
return Type::fromDataType(dt, dynLoc->rtt.innerType());
}
Type liveTVType(const TypedValue* tv) {
if (tv->m_type == KindOfObject) {
return Type::fromDataType(KindOfObject, KindOfInvalid,
tv->m_data.pobj->getVMClass());
}
return Type::fromDataType(tv->m_type);
}
//////////////////////////////////////////////////////////////////////
namespace {
+6
Ver Arquivo
@@ -537,6 +537,12 @@ public:
static_assert(sizeof(Type) <= 2 * sizeof(uint64_t),
"JIT::Type should fit in (2 * sizeof(uint64_t))");
/*
* Return the most refined type that can be used to represent the type
* in a live TypedValue.
*/
Type liveTVType(const TypedValue* tv);
}}
#endif
+2
Ver Arquivo
@@ -402,10 +402,12 @@ struct Unit {
PC entry() const { return m_bc; }
Offset bclen() const { return m_bclen; }
PC at(const Offset off) const {
assert(off >= 0 && off <= Offset(m_bclen));
return m_bc + off;
}
Offset offsetOf(const Opcode* op) const {
assert(op >= m_bc && op <= (m_bc + m_bclen));
return op - m_bc;
+1 -3
Ver Arquivo
@@ -85,9 +85,7 @@ inline bool isTF(PC pc) {
}
inline bool isCF(PC pc) {
// exclude call-like opcodes marked with CF flag.
return instrIsControlFlow(*pc) && !isFCallStar(*pc) &&
*pc != OpContEnter && *pc != OpFCallBuiltin;
return instrIsNonCallControlFlow(*pc);
}
inline bool isFF(PC pc) {
+1
Ver Arquivo
@@ -94,6 +94,7 @@ namespace Trace {
TM(hhas) \
TM(statgroups) \
TM(minstr) \
TM(region) \
/* Stress categories, to exercise rare paths */ \
TM(stress_txInterpPct) \
TM(stress_txInterpSeed) \