From dc6dc022e740cb81d06c92aa89da54a90721097b Mon Sep 17 00:00:00 2001 From: Jordan DeLong Date: Fri, 31 May 2013 17:10:47 -0700 Subject: [PATCH] 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. --- hphp/runtime/base/runtime_option.h | 1 + hphp/runtime/vm/hhbc.cpp | 8 + hphp/runtime/vm/hhbc.h | 1 + hphp/runtime/vm/jit/region_method.cpp | 112 ++++++++++ hphp/runtime/vm/jit/region_onebc.cpp | 43 ++++ hphp/runtime/vm/jit/region_selection.cpp | 267 +++++++++++++++++++++++ hphp/runtime/vm/jit/region_selection.h | 231 ++++++++++++++++++++ hphp/runtime/vm/jit/translator-x64.cpp | 54 +++++ hphp/runtime/vm/jit/type.cpp | 10 + hphp/runtime/vm/jit/type.h | 6 + hphp/runtime/vm/unit.h | 2 + hphp/runtime/vm/verifier/cfg.h | 4 +- hphp/util/trace.h | 1 + 13 files changed, 737 insertions(+), 3 deletions(-) create mode 100644 hphp/runtime/vm/jit/region_method.cpp create mode 100644 hphp/runtime/vm/jit/region_onebc.cpp create mode 100644 hphp/runtime/vm/jit/region_selection.cpp create mode 100644 hphp/runtime/vm/jit/region_selection.h diff --git a/hphp/runtime/base/runtime_option.h b/hphp/runtime/base/runtime_option.h index eeef17d1a..e6199f070 100644 --- a/hphp/runtime/base/runtime_option.h +++ b/hphp/runtime/base/runtime_option.h @@ -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) \ diff --git a/hphp/runtime/vm/hhbc.cpp b/hphp/runtime/vm/hhbc.cpp index 3e76b55f8..7f9de561d 100644 --- a/hphp/runtime/vm/hhbc.cpp +++ b/hphp/runtime/vm/hhbc.cpp @@ -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; diff --git a/hphp/runtime/vm/hhbc.h b/hphp/runtime/vm/hhbc.h index 5bab8ef1a..6495a38bd 100644 --- a/hphp/runtime/vm/hhbc.h +++ b/hphp/runtime/vm/hhbc.h @@ -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); diff --git a/hphp/runtime/vm/jit/region_method.cpp b/hphp/runtime/vm/jit/region_method.cpp new file mode 100644 index 000000000..aad244a6d --- /dev/null +++ b/hphp/runtime/vm/jit/region_method.cpp @@ -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(); + + 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(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; +} + +////////////////////////////////////////////////////////////////////// + +}} + diff --git a/hphp/runtime/vm/jit/region_onebc.cpp b/hphp/runtime/vm/jit/region_onebc.cpp new file mode 100644 index 000000000..f2fd37ae2 --- /dev/null +++ b/hphp/runtime/vm/jit/region_onebc.cpp @@ -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(); + auto blk = smart::make_unique(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; +} + +////////////////////////////////////////////////////////////////////// + +}} diff --git a/hphp/runtime/vm/jit/region_selection.cpp b/hphp/runtime/vm/jit/region_selection.cpp new file mode 100644 index 000000000..21c6f40f9 --- /dev/null +++ b/hphp/runtime/vm/jit/region_selection.cpp @@ -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 + +#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 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(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(); +} + +////////////////////////////////////////////////////////////////////// + +}} diff --git a/hphp/runtime/vm/jit/region_selection.h b/hphp/runtime/vm/jit/region_selection.h new file mode 100644 index 000000000..c9047c4ad --- /dev/null +++ b/hphp/runtime/vm/jit/region_selection.h @@ -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 +#include +#include + +#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::type BlockPtr; + + smart::vector blocks; +}; +typedef smart::unique_ptr::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> 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 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 liveTypes; + smart::vector 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 diff --git a/hphp/runtime/vm/jit/translator-x64.cpp b/hphp/runtime/vm/jit/translator-x64.cpp index 28595f276..232286e27 100644 --- a/hphp/runtime/vm/jit/translator-x64.cpp +++ b/hphp/runtime/vm/jit/translator-x64.cpp @@ -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); diff --git a/hphp/runtime/vm/jit/type.cpp b/hphp/runtime/vm/jit/type.cpp index a9438b23d..37517ea8f 100644 --- a/hphp/runtime/vm/jit/type.cpp +++ b/hphp/runtime/vm/jit/type.cpp @@ -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 { diff --git a/hphp/runtime/vm/jit/type.h b/hphp/runtime/vm/jit/type.h index 11c98100b..c6d08b22b 100644 --- a/hphp/runtime/vm/jit/type.h +++ b/hphp/runtime/vm/jit/type.h @@ -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 diff --git a/hphp/runtime/vm/unit.h b/hphp/runtime/vm/unit.h index 2e3ebb9b8..f9cfac1e5 100644 --- a/hphp/runtime/vm/unit.h +++ b/hphp/runtime/vm/unit.h @@ -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; diff --git a/hphp/runtime/vm/verifier/cfg.h b/hphp/runtime/vm/verifier/cfg.h index 0e64dcdc7..34b972d9f 100644 --- a/hphp/runtime/vm/verifier/cfg.h +++ b/hphp/runtime/vm/verifier/cfg.h @@ -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) { diff --git a/hphp/util/trace.h b/hphp/util/trace.h index 5f6da5506..d845faf63 100644 --- a/hphp/util/trace.h +++ b/hphp/util/trace.h @@ -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) \