Initial tracelet region selector
This diff renames the Tracelet -> RegionDesc conversion mode to "legacy" (since it's going away eventually) and changes "tracelet" to use the new region selection mode. It attempts to select a region that will be the same length as what Translator::analyze would come up with, using HhbcTranslator for all of the type flow logic. It generates longer tracelets in some cases due to more precise type information. Once this new mode is no longer a perf regression it can become the new default, replacing all the code in Translator::analyze and the "legacy" region mode. This version doesn't support inlining or tracking of known Func*s; those will come in later diffs.
Esse commit está contido em:
@@ -832,6 +832,35 @@ inline bool isFPassStar(Op opcode) {
|
||||
}
|
||||
}
|
||||
|
||||
inline bool isLiteral(Op op) {
|
||||
switch (op) {
|
||||
case OpNull:
|
||||
case OpNullUninit:
|
||||
case OpTrue:
|
||||
case OpFalse:
|
||||
case OpInt:
|
||||
case OpDouble:
|
||||
case OpString:
|
||||
case OpArray:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool isThisSelfOrParent(Op op) {
|
||||
switch (op) {
|
||||
case OpThis:
|
||||
case OpSelf:
|
||||
case OpParent:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool isSwitch(Op op) {
|
||||
return op == Op::Switch || op == Op::SSwitch;
|
||||
}
|
||||
|
||||
@@ -5483,8 +5483,10 @@ void CodeGenerator::cgMIterInitCommon(IRInstruction* inst) {
|
||||
args.addr(fpReg, iterOffset).ssa(src);
|
||||
|
||||
assert(src->type().isBoxed());
|
||||
auto innerType = src->type().innerType();
|
||||
assert(innerType.isKnownDataType());
|
||||
|
||||
if (src->type().innerType().isArray()) {
|
||||
if (innerType.isArray()) {
|
||||
args.addr(fpReg, valLocalOffset);
|
||||
if (inst->op() == MIterInitK) {
|
||||
args.addr(fpReg, localOffset(inst->src(4)));
|
||||
@@ -5493,7 +5495,7 @@ void CodeGenerator::cgMIterInitCommon(IRInstruction* inst) {
|
||||
}
|
||||
cgCallHelper(m_as, (TCA)new_miter_array_key, inst->dst(),
|
||||
SyncOptions::kSyncPoint, args);
|
||||
} else if (src->type() == Type::BoxedObj) {
|
||||
} else if (innerType.isObj()) {
|
||||
args.immPtr(curClass()).addr(fpReg, valLocalOffset);
|
||||
if (inst->op() == MIterInitK) {
|
||||
args.addr(fpReg, localOffset(inst->src(4)));
|
||||
|
||||
@@ -3501,7 +3501,7 @@ Type setOpResult(Type locType, Type valType, SetOpOp op) {
|
||||
not_reached();
|
||||
}
|
||||
|
||||
uint32_t localOutputId(const NormalizedInstruction& inst) {
|
||||
uint32_t localInputId(const NormalizedInstruction& inst) {
|
||||
switch (inst.op()) {
|
||||
case OpUnpackCont:
|
||||
case OpContSuspend:
|
||||
@@ -3522,7 +3522,7 @@ uint32_t localOutputId(const NormalizedInstruction& inst) {
|
||||
Type HhbcTranslator::interpOutputType(const NormalizedInstruction& inst) const {
|
||||
using namespace Transl::InstrFlags;
|
||||
auto localType = [&]{
|
||||
auto locId = localOutputId(inst);
|
||||
auto locId = localInputId(inst);
|
||||
assert(locId >= 0 && locId < curFunc()->numLocals());
|
||||
auto t = m_tb->getLocalType(locId);
|
||||
return t.equals(Type::None) ? Type::Gen : t;
|
||||
@@ -3611,8 +3611,9 @@ void HhbcTranslator::interpOutputLocals(const NormalizedInstruction& inst) {
|
||||
|
||||
case OpSetOpL:
|
||||
case OpIncDecL: {
|
||||
auto locType = m_tb->getLocalType(inst.imm[0].u_HA);
|
||||
auto stackType = topType(0);
|
||||
auto locType = m_tb->getLocalType(localInputId(inst));
|
||||
assert(!locType.equals(Type::None));
|
||||
auto stackType = inst.outputPredicted ? inst.outPred : topType(0);
|
||||
setImmLocType(0, locType.isBoxed() ? stackType.box() : stackType);
|
||||
break;
|
||||
}
|
||||
@@ -3736,6 +3737,10 @@ void HhbcTranslator::emitInterpOneCF(int numPopped) {
|
||||
m_hasExit = true;
|
||||
}
|
||||
|
||||
void HhbcTranslator::emitSmashLocals() {
|
||||
gen(SmashLocals, m_tb->fp());
|
||||
}
|
||||
|
||||
std::string HhbcTranslator::showStack() const {
|
||||
if (isInlining()) {
|
||||
return folly::format("{:*^60}\n",
|
||||
|
||||
@@ -157,6 +157,7 @@ struct HhbcTranslator {
|
||||
void emitInterpOne(Type t, int popped, int pushed);
|
||||
void emitInterpOne(const NormalizedInstruction&);
|
||||
void emitInterpOneCF(int numPopped);
|
||||
void emitSmashLocals();
|
||||
std::string showStack() const;
|
||||
bool hasExit() const {
|
||||
return m_hasExit;
|
||||
|
||||
@@ -57,7 +57,7 @@ int numInstrs(PC start, PC end) {
|
||||
RegionDescPtr regionMethod(const RegionContext& context) {
|
||||
using namespace HPHP::Verifier;
|
||||
|
||||
if (!isFuncEntry(context.func, context.offset)) return nullptr;
|
||||
if (!isFuncEntry(context.func, context.bcOffset)) return nullptr;
|
||||
FTRACE(1, "function entry for {}: using regionMethod\n",
|
||||
context.func->fullName()->data());
|
||||
|
||||
|
||||
@@ -27,10 +27,10 @@ namespace HPHP { namespace JIT {
|
||||
*/
|
||||
RegionDescPtr regionOneBC(const RegionContext& ctx) {
|
||||
auto ret = smart::make_unique<RegionDesc>();
|
||||
auto blk = smart::make_unique<RegionDesc::Block>(ctx.func, ctx.offset, 1);
|
||||
auto blk = smart::make_unique<RegionDesc::Block>(ctx.func, ctx.bcOffset, 1);
|
||||
|
||||
for (auto& live : ctx.liveTypes) {
|
||||
blk->addPredicted(SrcKey{ctx.func, ctx.offset},
|
||||
blk->addPredicted(SrcKey{ctx.func, ctx.bcOffset},
|
||||
{live.location, live.type});
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ TRACE_SET_MOD(region);
|
||||
|
||||
extern RegionDescPtr regionMethod(const RegionContext&);
|
||||
extern RegionDescPtr regionOneBC(const RegionContext&);
|
||||
extern RegionDescPtr regionTracelet(const RegionContext&);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -44,6 +45,7 @@ enum class RegionMode {
|
||||
OneBC,
|
||||
Method,
|
||||
Tracelet,
|
||||
Legacy,
|
||||
};
|
||||
|
||||
RegionMode regionMode() {
|
||||
@@ -52,6 +54,7 @@ RegionMode regionMode() {
|
||||
if (s == "onebc") return RegionMode::OneBC;
|
||||
if (s == "method") return RegionMode::Method;
|
||||
if (s == "tracelet") return RegionMode::Tracelet;
|
||||
if (s == "legacy") return RegionMode::Legacy;
|
||||
FTRACE(1, "unknown region mode {}: using none\n", s);
|
||||
if (debug) abort();
|
||||
return RegionMode::None;
|
||||
@@ -257,7 +260,7 @@ RegionDescPtr selectRegion(const RegionContext& context,
|
||||
FTRACE(1,
|
||||
"Select region: {}@{} mode={} context:\n{}{}",
|
||||
context.func->fullName()->data(),
|
||||
context.offset,
|
||||
context.bcOffset,
|
||||
static_cast<int>(mode),
|
||||
[&]{
|
||||
std::string ret;
|
||||
@@ -281,7 +284,8 @@ RegionDescPtr selectRegion(const RegionContext& context,
|
||||
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);
|
||||
case RegionMode::Tracelet: return regionTracelet(context);
|
||||
case RegionMode::Legacy: always_assert(t); return createRegion(*t);
|
||||
}
|
||||
not_reached();
|
||||
} catch (const std::exception& e) {
|
||||
|
||||
@@ -58,6 +58,13 @@ struct RegionDesc {
|
||||
No,
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
Block* addBlock(Args&&... args) {
|
||||
blocks.push_back(
|
||||
smart::make_unique<Block>(std::forward<Args>(args)...));
|
||||
return blocks.back().get();
|
||||
}
|
||||
|
||||
smart::vector<BlockPtr> blocks;
|
||||
};
|
||||
typedef smart::unique_ptr<RegionDesc>::type RegionDescPtr;
|
||||
@@ -234,7 +241,8 @@ struct RegionContext {
|
||||
struct PreLiveAR;
|
||||
|
||||
const Func* func;
|
||||
Offset offset;
|
||||
Offset bcOffset;
|
||||
Offset spOffset;
|
||||
smart::vector<LiveType> liveTypes;
|
||||
smart::vector<PreLiveAR> preLiveARs;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| 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/util/trace.h"
|
||||
#include "hphp/runtime/vm/jit/hhbc-translator.h"
|
||||
#include "hphp/runtime/vm/jit/ir-translator.h"
|
||||
#include "hphp/runtime/vm/jit/region-selection.h"
|
||||
#include "hphp/runtime/vm/jit/translator.h"
|
||||
|
||||
namespace HPHP { namespace JIT {
|
||||
|
||||
using Transl::DynLocation;
|
||||
|
||||
TRACE_SET_MOD(region);
|
||||
|
||||
typedef hphp_hash_set<SrcKey, SrcKey::Hasher> InterpSet;
|
||||
|
||||
namespace {
|
||||
struct State {
|
||||
const Unit* curUnit;
|
||||
const SrcKey startSk;
|
||||
HhbcTranslator& ht;
|
||||
Unit::MetaHandle metaHand;
|
||||
};
|
||||
|
||||
/*
|
||||
* Populate most fields of the NormalizedInstruciton, assuming its sk
|
||||
* has already been set. Returns false iff the region should be
|
||||
* truncated before inst's SrcKey.
|
||||
*/
|
||||
bool prepareInstruction(NormalizedInstruction& inst, State& state) {
|
||||
inst.m_unit = state.curUnit;
|
||||
inst.breaksTracelet = false;
|
||||
inst.changesPC = Transl::opcodeChangesPC(inst.op());
|
||||
inst.funcd = nullptr;
|
||||
Transl::populateImmediates(inst);
|
||||
Transl::preInputApplyMetaData(state.metaHand, &inst);
|
||||
|
||||
Transl::InputInfos inputInfos;
|
||||
getInputs(state.startSk, inst, inputInfos, [&](int i) {
|
||||
return state.ht.traceBuilder()->getLocalType(i);
|
||||
});
|
||||
|
||||
auto newDynLoc = [&](const Transl::InputInfo& ii) {
|
||||
auto dl = inst.newDynLoc(ii.loc, state.ht.rttFromLocation(ii.loc));
|
||||
FTRACE(2, "rttFromLocation: {} -> {}\n",
|
||||
ii.loc.pretty(), dl->rtt.pretty());
|
||||
return dl;
|
||||
};
|
||||
|
||||
for (auto const& ii : inputInfos) {
|
||||
auto* dl = newDynLoc(ii);
|
||||
auto const& rtt = dl->rtt;
|
||||
inst.inputs.push_back(dl);
|
||||
|
||||
if (ii.dontBreak || ii.dontGuard) continue;
|
||||
if (rtt.isVagueValue()) {
|
||||
// Trying to consume a value without a precise enough type.
|
||||
return false;
|
||||
}
|
||||
if (inst.ignoreInnerType || ii.dontGuardInner) continue;
|
||||
if (rtt.isValue() && rtt.isRef() &&
|
||||
(rtt.innerType() == KindOfInvalid || rtt.innerType() == KindOfAny)) {
|
||||
// Trying to consume a boxed value without a guess for the inner type.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
readMetaData(state.metaHand, inst, state.ht);
|
||||
if (!inst.noOp && inputInfos.needsRefCheck) {
|
||||
// not supported yet
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
RegionDescPtr regionTraceletImpl(const RegionContext& ctx,
|
||||
InterpSet& toInterp) {
|
||||
IRTranslator irTrans(ctx.bcOffset, ctx.spOffset, ctx.func);
|
||||
auto* curFunc = ctx.func;
|
||||
State state{
|
||||
curFunc->unit(),
|
||||
{ctx.func, ctx.bcOffset},
|
||||
irTrans.hhbcTrans(),
|
||||
};
|
||||
auto& ht = state.ht;
|
||||
uint32_t numJmps = 0;
|
||||
|
||||
SrcKey sk(state.startSk);
|
||||
auto region = smart::make_unique<RegionDesc>();
|
||||
auto* curBlock = region->addBlock(curFunc, sk.offset(), 0);
|
||||
bool blockFinished = false;
|
||||
uint32_t pendingLiterals = 0;
|
||||
|
||||
auto addInstruction = [&] {
|
||||
if (blockFinished) {
|
||||
curBlock = region->addBlock(curFunc, sk.offset(), 0);
|
||||
blockFinished = false;
|
||||
}
|
||||
|
||||
auto op = toOp(*state.curUnit->at(sk.offset()));
|
||||
// Don't finish a region with literal values or values that have a class
|
||||
// related to the current context class. They produce valuable information
|
||||
// for optimizations that's lost across region boundaries.
|
||||
if (isLiteral(op) || isThisSelfOrParent(op)) {
|
||||
++pendingLiterals;
|
||||
return;
|
||||
}
|
||||
|
||||
// This op isn't a literal so add any that are pending before the current
|
||||
// instruction.
|
||||
for (; pendingLiterals; --pendingLiterals) curBlock->addInstruction();
|
||||
curBlock->addInstruction();
|
||||
};
|
||||
|
||||
for (auto const& lt : ctx.liveTypes) {
|
||||
auto t = lt.type;
|
||||
if (t.strictSubtypeOf(Type::Obj)) t = t.unspecialize();
|
||||
|
||||
ht.guardTypeLocation(lt.location, t);
|
||||
curBlock->addPredicted(sk, RegionDesc::TypePred{lt.location, t});
|
||||
}
|
||||
|
||||
while (true) {
|
||||
NormalizedInstruction inst;
|
||||
inst.source = sk;
|
||||
if (!prepareInstruction(inst, state)) return region;
|
||||
|
||||
// Before doing the translation, check for tracelet-ending control flow.
|
||||
if (inst.op() == OpJmp && inst.imm[0].u_BA > 0 &&
|
||||
numJmps < Transl::Translator::MaxJmpsTracedThrough) {
|
||||
// Include the Jmp in the region and continue to its destination.
|
||||
++numJmps;
|
||||
addInstruction();
|
||||
sk.setOffset(sk.offset() + inst.imm[0].u_BA);
|
||||
blockFinished = true;
|
||||
|
||||
ht.setBcOff(sk.offset(), false);
|
||||
continue;
|
||||
} else if (Transl::opcodeBreaksBB(inst.op()) ||
|
||||
(Transl::dontGuardAnyInputs(inst.op()) &&
|
||||
Transl::opcodeChangesPC(inst.op()))) {
|
||||
// This instruction ends the tracelet. Include it in the region and
|
||||
// return.
|
||||
addInstruction();
|
||||
return region;
|
||||
}
|
||||
|
||||
inst.interp = toInterp.count(sk);
|
||||
auto const doPrediction = Transl::outputIsPredicted(state.startSk, inst);
|
||||
|
||||
try {
|
||||
irTrans.translateInstr(inst);
|
||||
} catch (const FailedIRGen& exn) {
|
||||
FTRACE(1, "ir generation for {} failed with {}\n",
|
||||
inst.toString(), exn.what());
|
||||
always_assert(!toInterp.count(sk));
|
||||
toInterp.insert(sk);
|
||||
return RegionDescPtr{ nullptr };
|
||||
}
|
||||
|
||||
if (isFCallStar(inst.op()) || inst.op() == OpFCallBuiltin) {
|
||||
// This is much more conservative than it needs to be.
|
||||
ht.emitSmashLocals();
|
||||
}
|
||||
|
||||
if (doPrediction) {
|
||||
ht.checkTypeStack(0, inst.outPred,
|
||||
sk.advanced(curBlock->unit()).offset());
|
||||
}
|
||||
|
||||
addInstruction();
|
||||
sk.advance(curBlock->unit());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Region selector that attempts to form the longest possible region using the
|
||||
* given context. The region will be broken before the first instruction that
|
||||
* attempts to consume an input with an insufficiently precise type.
|
||||
*
|
||||
*/
|
||||
RegionDescPtr regionTracelet(const RegionContext& ctx) {
|
||||
InterpSet interp;
|
||||
RegionDescPtr region;
|
||||
uint32_t tries = 1;
|
||||
|
||||
while (!(region = regionTraceletImpl(ctx, interp))) {
|
||||
++tries;
|
||||
}
|
||||
FTRACE(1, "regionTracelet returning after {} tries:\n{}\n",
|
||||
tries, show(*region));
|
||||
|
||||
if (region->blocks.size() == 1 && region->blocks.front()->length() == 0) {
|
||||
// If we had to break the region at the first instruction, punt.
|
||||
return RegionDescPtr{ nullptr };
|
||||
} else if (region->blocks.back()->length() == 0) {
|
||||
// If the final block is empty because it would've only contained
|
||||
// instructions producing literal values, kill it.
|
||||
region->blocks.pop_back();
|
||||
}
|
||||
return region;
|
||||
}
|
||||
|
||||
} }
|
||||
@@ -374,7 +374,7 @@ string RuntimeType::pretty() const {
|
||||
if (valueType() == KindOfObject) {
|
||||
if (valueClass() != nullptr) {
|
||||
retval += folly::format("(OfClass {})",
|
||||
knownClass()->name()->data()).str();
|
||||
valueClass()->name()->data()).str();
|
||||
} else if (hasKnownType()) {
|
||||
retval += folly::format("(Known Class {})",
|
||||
knownClass()->name()->data()).str();
|
||||
@@ -382,7 +382,7 @@ string RuntimeType::pretty() const {
|
||||
}
|
||||
if (valueType() == KindOfClass && valueClass() != nullptr) {
|
||||
retval += folly::format("(Class {})",
|
||||
knownClass()->name()->data()).str();
|
||||
valueClass()->name()->data()).str();
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -598,8 +598,16 @@ SSATmp* TraceBuilder::preOptimizeAssertLoc(IRInstruction* inst) {
|
||||
auto const typeParam = inst->typeParam();
|
||||
|
||||
if (!prevType.equals(Type::None) && !typeParam.strictSubtypeOf(prevType)) {
|
||||
assert(prevType.subtypeOf(typeParam));
|
||||
inst->convertToNop();
|
||||
if (!prevType.subtypeOf(typeParam)) {
|
||||
static auto const error =
|
||||
StringData::GetStaticString("Internal error: static analysis was "
|
||||
"wrong about a local variable's type.");
|
||||
auto* errorInst = m_irFactory.gen(RaiseError, inst->marker(), cns(error));
|
||||
inst->become(&m_irFactory, errorInst);
|
||||
assert(false && "Incorrect local type from static analysis");
|
||||
} else {
|
||||
inst->convertToNop();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -725,7 +725,7 @@ static void populateLiveContext(JIT::RegionContext& ctx) {
|
||||
|
||||
uint32_t stackOff = 0;
|
||||
visitStackElems(
|
||||
fp, sp, ctx.offset,
|
||||
fp, sp, ctx.bcOffset,
|
||||
[&](const ActRec* ar) {
|
||||
// TODO(#2466980): when it's a Cls, we should pass the Class* in
|
||||
// the Type.
|
||||
@@ -3183,9 +3183,9 @@ TranslatorX64::translateWork(const TranslArgs& args) {
|
||||
assert(srcRec.inProgressTailJumps().empty());
|
||||
};
|
||||
|
||||
if (!args.m_interp && !checkTranslationLimit(t.m_sk, srcRec)) {
|
||||
if (!args.m_interp && !checkTranslationLimit(sk, srcRec)) {
|
||||
// Attempt to create a region at this SrcKey
|
||||
JIT::RegionContext rContext { curFunc(), args.m_sk.offset() };
|
||||
JIT::RegionContext rContext { curFunc(), args.m_sk.offset(), curSpOff() };
|
||||
FTRACE(2, "populating live context for region\n");
|
||||
populateLiveContext(rContext);
|
||||
auto region = JIT::selectRegion(rContext, &t);
|
||||
@@ -3216,7 +3216,7 @@ TranslatorX64::translateWork(const TranslArgs& args) {
|
||||
if (!region || result == Failure) {
|
||||
FTRACE(1, "trying irTranslateTracelet\n");
|
||||
assertCleanState();
|
||||
result = translateTracelet(*tp);
|
||||
result = translateTracelet(t);
|
||||
DEBUG_ONLY static const bool reqRegion = getenv("HHVM_REQUIRE_REGION");
|
||||
assert(IMPLIES(region && reqRegion, result != Success));
|
||||
}
|
||||
@@ -3256,7 +3256,7 @@ TranslatorX64::translateWork(const TranslArgs& args) {
|
||||
}
|
||||
m_pendingFixups.clear();
|
||||
|
||||
addTranslation(TransRec(t.m_sk, curUnit()->md5(), transKind, t, start,
|
||||
addTranslation(TransRec(sk, curUnit()->md5(), transKind, t, start,
|
||||
a.frontier() - start, stubStart,
|
||||
astubs.frontier() - stubStart,
|
||||
counterStart, counterLen,
|
||||
|
||||
@@ -3474,29 +3474,13 @@ std::unique_ptr<Tracelet> Translator::analyze(SrcKey sk,
|
||||
breakBB:
|
||||
NormalizedInstruction* ni = t.m_instrStream.last;
|
||||
while (ni) {
|
||||
switch (ni->op()) {
|
||||
// We dont want to end a tracelet with a literal;
|
||||
// it will cause the literal to be pushed on the
|
||||
// stack, and the next tracelet will have to guard
|
||||
// on the type.
|
||||
case OpNull:
|
||||
case OpNullUninit:
|
||||
case OpTrue:
|
||||
case OpFalse:
|
||||
case OpInt:
|
||||
case OpDouble:
|
||||
case OpString:
|
||||
case OpArray:
|
||||
// Similarly, This, Self and Parent will lose
|
||||
// type information thats only useful in the
|
||||
// following tracelet.
|
||||
case OpThis:
|
||||
case OpSelf:
|
||||
case OpParent:
|
||||
ni = ni->prev;
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
// We dont want to end a tracelet with a literal; it will cause the literal
|
||||
// to be pushed on the stack, and the next tracelet will have to guard on
|
||||
// the type. Similarly, This, Self and Parent will lose type information
|
||||
// thats only useful in the following tracelet.
|
||||
if (isLiteral(ni->op()) || isThisSelfOrParent(ni->op())) {
|
||||
ni = ni->prev;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -3894,6 +3878,11 @@ Translator::translateRegion(const RegionDesc& region,
|
||||
return Retry;
|
||||
}
|
||||
|
||||
if (isFCallStar(inst.op()) || inst.op() == OpFCallBuiltin) {
|
||||
// This is much more conservative than it needs to be.
|
||||
ht.emitSmashLocals();
|
||||
}
|
||||
|
||||
// Check the prediction. If the predicted type is less specific than what
|
||||
// is currently on the eval stack, checkTypeLocation won't emit any code.
|
||||
if (doPrediction) {
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "hphp/util/hash.h"
|
||||
#include "hphp/util/timer.h"
|
||||
#include "hphp/runtime/base/execution_context.h"
|
||||
#include "hphp/runtime/base/smart_containers.h"
|
||||
#include "hphp/runtime/vm/bytecode.h"
|
||||
#include "hphp/runtime/vm/jit/fixup.h"
|
||||
#include "hphp/runtime/vm/jit/region-selection.h"
|
||||
@@ -197,7 +198,8 @@ class NormalizedInstruction {
|
||||
// For FCall's, an opaque identifier that is either null, or uniquely
|
||||
// identifies the (functionName, -arity) pair of this call site.
|
||||
const Unit* m_unit;
|
||||
vector<DynLocation*> inputs;
|
||||
|
||||
std::vector<DynLocation*> inputs;
|
||||
DynLocation* outStack;
|
||||
DynLocation* outLocal;
|
||||
DynLocation* outLocal2; // Used for IterInitK, MIterInitK, IterNextK,
|
||||
@@ -336,6 +338,18 @@ class NormalizedInstruction {
|
||||
bool isAnyOutputUsed() const;
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
// Returns a DynLocation that will be destroyed with this
|
||||
// NormalizedInstruction.
|
||||
template<typename... Args>
|
||||
DynLocation* newDynLoc(Args&&... args) {
|
||||
m_dynLocs.push_back(
|
||||
smart::make_unique<DynLocation>(std::forward<Args>(args)...));
|
||||
return m_dynLocs.back().get();
|
||||
}
|
||||
|
||||
private:
|
||||
smart::vector<smart::unique_ptr<DynLocation>::type> m_dynLocs;
|
||||
};
|
||||
|
||||
// Return a summary string of the bytecode in a tracelet.
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "hphp/util/trace.h"
|
||||
#include "hphp/runtime/vm/jit/ir.h"
|
||||
#include "hphp/runtime/vm/jit/ir-instruction.h"
|
||||
#include "hphp/runtime/vm/jit/runtime-type.h"
|
||||
#include "hphp/runtime/vm/jit/ssa-tmp.h"
|
||||
#include "hphp/runtime/vm/jit/translator.h"
|
||||
|
||||
@@ -122,7 +123,16 @@ Type liveTVType(const TypedValue* tv) {
|
||||
return Type::fromDataType(KindOfObject, KindOfInvalid,
|
||||
tv->m_data.pobj->getVMClass());
|
||||
}
|
||||
return Type::fromDataType(tv->m_type);
|
||||
|
||||
auto outer = tv->m_type;
|
||||
auto inner = KindOfInvalid;
|
||||
|
||||
if (outer == KindOfStaticString) outer = KindOfString;
|
||||
if (outer == KindOfRef) {
|
||||
inner = tv->m_data.pref->tv()->m_type;
|
||||
if (inner == KindOfStaticString) inner = KindOfString;
|
||||
}
|
||||
return Type::fromDataType(outer, inner);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -439,6 +439,11 @@ public:
|
||||
return Type(m_bits, klass);
|
||||
}
|
||||
|
||||
Type unspecialize() const {
|
||||
assert(isObj() && m_class != nullptr);
|
||||
return Type(m_bits, nullptr);
|
||||
}
|
||||
|
||||
bool canRunDtor() const {
|
||||
return
|
||||
(*this & (Obj | CountedArr | BoxedObj | BoxedCountedArr))
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário