Hoist CheckType instructions above generic IncRef/LdMem @override-unit-failures
Because we normally find out about a type prediction at hhbc bounderies, sometimes we generate type checks after we've already done some generic operations on Cells. This optimizes a specific pattern of this that occurs in vector translations. The code is set up as if there is more than one since I have another case partially implemented, but it currently hits issues in both linearscan and codegen that need to be addressed first, so I split the diff for now.
Esse commit está contido em:
@@ -331,6 +331,10 @@ D:T = CheckType<T> S0:Gen -> L
|
||||
Check that the type of the src S0 is T, and if so copy it to D. If
|
||||
S0 is not type T, branch to the label L.
|
||||
|
||||
CheckTypeMem<T> S0:PtrToGen -> L
|
||||
|
||||
If the value pointed to by S0 is not type T, branch to the label L.
|
||||
|
||||
GuardLoc<T,localId> S0:FramePtr
|
||||
|
||||
Guard that type of the given localId on the frame S0 is a subtype of
|
||||
@@ -644,11 +648,12 @@ D:T* = LdLocAddr<T, localId> S0:FramePtr
|
||||
|
||||
Loads the address of the local slot localId from the frame S0 into D.
|
||||
|
||||
D:T = LdMem<T> S0:PtrToGen [ -> L ]
|
||||
D:T = LdMem<T> S0:PtrToGen S1:ConstInt [ -> L ]
|
||||
|
||||
Loads from S0 and puts the value in D. If the optional label L is
|
||||
specified and the loaded value's type does not match T, this
|
||||
instruction does not load into D and transfers control to L.
|
||||
Loads from S0 + S1 (in bytes) and puts the value in D. If the
|
||||
optional label L is specified and the loaded value's type does not
|
||||
match T, this instruction does not load into D and transfers control
|
||||
to L.
|
||||
|
||||
D:T = LdProp<T> S0:Obj S1:ConstInt [ -> L ]
|
||||
|
||||
|
||||
@@ -438,6 +438,7 @@ public:
|
||||
F(bool, HHIRGenerateAsserts, debug) \
|
||||
F(bool, HHIRDirectExit, true) \
|
||||
F(bool, HHIRDeadCodeElim, true) \
|
||||
F(bool, HHIRPredictionOpts, true) \
|
||||
/* DumpBytecode =1 dumps user php, =2 dumps systemlib & user php */ \
|
||||
F(int32_t, DumpBytecode, 0) \
|
||||
F(bool, DumpTC, false) \
|
||||
|
||||
@@ -164,8 +164,8 @@ struct Block : boost::noncopyable {
|
||||
template <class Predicate> void remove_if(Predicate p) {
|
||||
m_instrs.remove_if(p);
|
||||
}
|
||||
void erase(iterator pos) {
|
||||
m_instrs.erase(pos);
|
||||
iterator erase(iterator pos) {
|
||||
return m_instrs.erase(pos);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -167,7 +167,7 @@ bool checkTmpsSpanningCalls(Trace* trace, const IRFactory& irFactory) {
|
||||
/*
|
||||
* Php calls kill all live temporaries. We can't keep them
|
||||
* alive across the call because we currently have no
|
||||
* caller-saved registers in our abi, and all translations
|
||||
* callee-saved registers in our abi, and all translations
|
||||
* share the same spill slots.
|
||||
*/
|
||||
if (isCall(inst.op())) state.reset();
|
||||
|
||||
@@ -34,7 +34,7 @@ bool checkCfg(Trace*, const IRFactory&);
|
||||
* We can't have SSATmps spanning php-level calls, except for frame
|
||||
* pointers and constant values.
|
||||
*
|
||||
* We have no caller-saved registers in php, and there'd be nowhere to
|
||||
* We have no callee-saved registers in php, and there'd be nowhere to
|
||||
* spill these because all translations share the spill space.
|
||||
*/
|
||||
bool checkTmpsSpanningCalls(Trace*, const IRFactory&);
|
||||
|
||||
@@ -318,8 +318,9 @@ Address CodeGenerator::cgInst(IRInstruction* inst) {
|
||||
m_rScratch = selectScratchReg(inst);
|
||||
switch (opc) {
|
||||
#define O(name, dsts, srcs, flags) \
|
||||
case name: cg ## name (inst); \
|
||||
return m_as.code.frontier == start ? nullptr : start;
|
||||
case name: FTRACE(7, "cg" #name "\n"); \
|
||||
cg ## name (inst); \
|
||||
return m_as.code.frontier == start ? nullptr : start;
|
||||
IR_OPCODES
|
||||
#undef O
|
||||
|
||||
@@ -4025,6 +4026,11 @@ void CodeGenerator::cgCheckType(IRInstruction* inst) {
|
||||
}
|
||||
}
|
||||
|
||||
void CodeGenerator::cgCheckTypeMem(IRInstruction* inst) {
|
||||
auto const reg = m_regs[inst->getSrc(0)].getReg();
|
||||
emitTypeCheck(inst->getTypeParam(), reg[TVOFF(m_type)], inst->getTaken());
|
||||
}
|
||||
|
||||
void CodeGenerator::cgGuardRefs(IRInstruction* inst) {
|
||||
assert(inst->getNumSrcs() == 6);
|
||||
|
||||
|
||||
@@ -753,7 +753,6 @@ void IRInstruction::convertToMov() {
|
||||
|
||||
void IRInstruction::become(IRFactory* factory, IRInstruction* other) {
|
||||
assert(other->isTransient() || m_numDsts == other->m_numDsts);
|
||||
assert(!canCSE());
|
||||
auto& arena = factory->arena();
|
||||
|
||||
// Copy all but m_id, m_block, m_listNode, and don't clone
|
||||
|
||||
@@ -154,6 +154,7 @@ class FailedIRGen : public std::exception {
|
||||
#define IR_OPCODES \
|
||||
/* name dstinfo srcinfo flags */ \
|
||||
O(CheckType, DParam, S(Gen), C|E|CRc|PRc|P) \
|
||||
O(CheckTypeMem, ND, S(PtrToGen), E) \
|
||||
O(GuardLoc, ND, S(FramePtr), E) \
|
||||
O(GuardStk, D(StkPtr), S(StkPtr), E) \
|
||||
O(CheckLoc, ND, S(FramePtr), E) \
|
||||
|
||||
@@ -161,12 +161,7 @@ struct IRInstruction {
|
||||
* The target instruction may be transient---we'll clone anything we
|
||||
* need to keep, using factory for any needed memory.
|
||||
*
|
||||
* Note: if you want to use this to replace a CSE-able instruction
|
||||
* you're probably going to have a bad time. For now it's a
|
||||
* precondition that the current instruction can't CSE.
|
||||
*
|
||||
* Pre: other->isTransient() || numDsts() == other->numDsts()
|
||||
* Pre: !canCSE()
|
||||
*/
|
||||
void become(IRFactory*, IRInstruction* other);
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "hphp/runtime/vm/translator/hopt/opt.h"
|
||||
#include "hphp/runtime/vm/translator/hopt/irfactory.h"
|
||||
|
||||
namespace HPHP { namespace JIT {
|
||||
namespace HPHP { namespace JIT {
|
||||
|
||||
TRACE_SET_MOD(hhir);
|
||||
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| 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/translator/hopt/mutation.h"
|
||||
#include "hphp/runtime/vm/translator/hopt/state_vector.h"
|
||||
|
||||
namespace HPHP { namespace JIT {
|
||||
|
||||
TRACE_SET_MOD(hhir);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
void cloneToBlock(const BlockList& rpoBlocks,
|
||||
IRFactory* const irFactory,
|
||||
Block::iterator const first,
|
||||
Block::iterator const last,
|
||||
Block* const target) {
|
||||
assert(isRPOSorted(rpoBlocks));
|
||||
|
||||
StateVector<SSATmp,SSATmp*> rewriteMap(irFactory, nullptr);
|
||||
|
||||
auto rewriteSources = [&] (IRInstruction* inst) {
|
||||
for (int i = 0; i < inst->getNumSrcs(); ++i) {
|
||||
if (auto newTmp = rewriteMap[inst->getSrc(i)]) {
|
||||
FTRACE(5, " rewrite: {} -> {}\n",
|
||||
inst->getSrc(i)->toString(),
|
||||
newTmp->toString());
|
||||
inst->setSrc(i, newTmp);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
auto targetIt = target->skipLabel();
|
||||
for (auto it = first; it != last; ++it) {
|
||||
assert(!it->isControlFlowInstruction());
|
||||
|
||||
FTRACE(5, "cloneToBlock({}): {}\n", target->getId(), it->toString());
|
||||
auto const newInst = irFactory->cloneInstruction(&*it);
|
||||
|
||||
if (auto const numDests = newInst->getNumDsts()) {
|
||||
for (int i = 0; i < numDests; ++i) {
|
||||
FTRACE(5, " add rewrite: {} -> {}\n",
|
||||
it->getDst(i)->toString(),
|
||||
newInst->getDst(i)->toString());
|
||||
rewriteMap[it->getDst(i)] = newInst->getDst(i);
|
||||
}
|
||||
}
|
||||
|
||||
target->insert(targetIt, newInst);
|
||||
targetIt = ++target->iteratorTo(newInst);
|
||||
}
|
||||
|
||||
// TODO(#2424504): don't use linear search
|
||||
auto it = std::find(rpoBlocks.begin(), rpoBlocks.end(), target);
|
||||
for (; it != rpoBlocks.end(); ++it) {
|
||||
FTRACE(5, "cloneToBlock: rewriting block {}\n", (*it)->getId());
|
||||
for (auto& inst : **it) {
|
||||
FTRACE(5, " rewriting {}\n", inst.toString());
|
||||
rewriteSources(&inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void moveToBlock(Block::iterator const first,
|
||||
Block::iterator const last,
|
||||
Block* const target) {
|
||||
if (first == last) return;
|
||||
|
||||
auto const srcBlock = first->getBlock();
|
||||
|
||||
auto targetIt = target->skipLabel();
|
||||
for (auto it = first; it != last;) {
|
||||
auto const inst = &*it;
|
||||
assert(!inst->isControlFlowInstruction());
|
||||
|
||||
FTRACE(5, "moveToBlock({}): {}\n",
|
||||
target->getId(),
|
||||
inst->toString());
|
||||
|
||||
it = srcBlock->erase(it);
|
||||
target->insert(targetIt, inst);
|
||||
targetIt = ++target->iteratorTo(inst);
|
||||
}
|
||||
}
|
||||
|
||||
void reflowTypes(Block* const changed, const BlockList& blocks) {
|
||||
assert(isRPOSorted(blocks));
|
||||
|
||||
auto retypeDst = [&] (IRInstruction* inst, int num) {
|
||||
auto ssa = inst->getDst(num);
|
||||
|
||||
/*
|
||||
* The type of a tmp defined by DefLabel is the union of the
|
||||
* types of the tmps at each incoming Jmp.
|
||||
*/
|
||||
if (inst->op() == DefLabel) {
|
||||
Type type = Type::Bottom;
|
||||
inst->getBlock()->forEachSrc(num, [&](IRInstruction*, SSATmp* tmp) {
|
||||
type = Type::unionOf(type, tmp->type());
|
||||
});
|
||||
ssa->setType(type);
|
||||
return;
|
||||
}
|
||||
|
||||
ssa->setType(outputType(inst, num));
|
||||
};
|
||||
|
||||
auto visit = [&] (IRInstruction* inst) {
|
||||
for (int i = 0; i < inst->getNumDsts(); ++i) {
|
||||
auto const ssa = inst->getDst(i);
|
||||
auto const oldType = ssa->type();
|
||||
retypeDst(inst, i);
|
||||
if (ssa->type() != oldType) {
|
||||
FTRACE(5, "reflowTypes: retyped {} in {}\n", oldType.toString(),
|
||||
inst->toString());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// TODO(#2424504): don't use linear search
|
||||
auto it = std::find(blocks.begin(), blocks.end(), changed);
|
||||
assert(it != blocks.end());
|
||||
for (; it != blocks.end(); ++it) {
|
||||
FTRACE(5, "reflowTypes: visiting block {}\n", (*it)->getId());
|
||||
for (auto& inst : **it) visit(&inst);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| 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_MUTATION_H_
|
||||
#define incl_HPHP_JIT_MUTATION_H_
|
||||
|
||||
#include "hphp/runtime/vm/translator/hopt/ir.h"
|
||||
#include "hphp/runtime/vm/translator/hopt/cfg.h"
|
||||
|
||||
namespace HPHP { namespace JIT {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
* Utility routines for mutating the IR during optimization passes.
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
* Clone a range of IRInstructions into the front of a target block
|
||||
* (immediately after its DefLabel, but before its Marker).
|
||||
*
|
||||
* Then, for any block reachable from `target', rewrite the sources of
|
||||
* any instructions that referred to the old destinations of the
|
||||
* cloned instructions so that they refer to the new destinations this
|
||||
* function created.
|
||||
*
|
||||
* Does not unlink the instructions from the source block.
|
||||
*
|
||||
* Pre: The range [first,last) may not contain control flow
|
||||
* instructions.
|
||||
* Pre: isRPOSorted(blocks)
|
||||
*/
|
||||
void cloneToBlock(const BlockList& blocks,
|
||||
IRFactory* irFactory,
|
||||
Block::iterator first,
|
||||
Block::iterator last,
|
||||
Block* dst);
|
||||
|
||||
|
||||
/*
|
||||
* Move a range of IRInstructions to the front of a target block
|
||||
* (immediately after its DefLabel, but before its Marker).
|
||||
*
|
||||
* The instructions are unlinked from their current source block.
|
||||
*
|
||||
* Pre: The range [first,last) may not contain control flow
|
||||
* instructions.
|
||||
*/
|
||||
void moveToBlock(Block::iterator first,
|
||||
Block::iterator last,
|
||||
Block* dst);
|
||||
|
||||
/*
|
||||
* Given a block where some SSATmps have changed type, walk the
|
||||
* sub-cfg reachable from that block and recompute the types of any
|
||||
* tmps that depend on it.
|
||||
*
|
||||
* The new types of any changed SSATmps must be related to their old
|
||||
* types.
|
||||
*
|
||||
* Pre: isRPOSorted(blocks)
|
||||
*/
|
||||
void reflowTypes(Block*, const BlockList& blocks);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
}}
|
||||
|
||||
#endif
|
||||
@@ -105,24 +105,31 @@ void optimizeTrace(Trace* trace, TraceBuilder* traceBuilder) {
|
||||
IRFactory* irFactory = traceBuilder->getIrFactory();
|
||||
|
||||
auto finishPass = [&](const char* msg) {
|
||||
dumpTrace(6, trace, msg);
|
||||
dumpTrace(6, trace, folly::format("after {}", msg).str().c_str());
|
||||
assert(checkCfg(trace, *irFactory));
|
||||
assert(checkTmpsSpanningCalls(trace, *irFactory));
|
||||
if (debug) forEachTraceInst(trace, assertOperandTypes);
|
||||
};
|
||||
|
||||
auto dcePass = [&](const char* which) {
|
||||
auto doPass = [&](void (*fn)(Trace*, IRFactory*),
|
||||
const char* msg) {
|
||||
fn(trace, irFactory);
|
||||
finishPass(msg);
|
||||
};
|
||||
|
||||
auto dce = [&](const char* which) {
|
||||
if (!RuntimeOption::EvalHHIRDeadCodeElim) return;
|
||||
eliminateDeadCode(trace, irFactory);
|
||||
finishPass(folly::format("after {} DCE", which).str().c_str());
|
||||
};
|
||||
|
||||
if (false && RuntimeOption::EvalHHIRMemOpt) {
|
||||
optimizeMemoryAccesses(trace, irFactory);
|
||||
finishPass("after MemeLim");
|
||||
doPass(optimizeMemoryAccesses, "MemeLim");
|
||||
}
|
||||
dce("initial");
|
||||
if (RuntimeOption::EvalHHIRPredictionOpts) {
|
||||
doPass(optimizePredictions, "prediction opts");
|
||||
}
|
||||
|
||||
dcePass("initial");
|
||||
|
||||
if (RuntimeOption::EvalHHIRExtraOptPass
|
||||
&& (RuntimeOption::EvalHHIRCse
|
||||
@@ -132,18 +139,16 @@ void optimizeTrace(Trace* trace, TraceBuilder* traceBuilder) {
|
||||
// Cleanup any dead code left around by CSE/Simplification
|
||||
// Ideally, this would be controlled by a flag returned
|
||||
// by optimzeTrace indicating whether DCE is necessary
|
||||
dcePass("reoptimize");
|
||||
dce("reoptimize");
|
||||
}
|
||||
|
||||
if (RuntimeOption::EvalHHIRJumpOpts) {
|
||||
optimizeJumps(trace, irFactory);
|
||||
finishPass("jump opts");
|
||||
dcePass("jump opts");
|
||||
doPass(optimizeJumps, "jumpopts");
|
||||
dce("jump opts");
|
||||
}
|
||||
|
||||
if (RuntimeOption::EvalHHIRGenerateAsserts) {
|
||||
insertAsserts(trace, irFactory);
|
||||
finishPass("RefCnt asserts");
|
||||
doPass(insertAsserts, "RefCnt asserts");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,15 +18,20 @@
|
||||
|
||||
namespace HPHP { namespace JIT {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
class Trace;
|
||||
class TraceBuilder;
|
||||
class IRFactory;
|
||||
class IRInstruction;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
* The main optimization passes, in the order they run.
|
||||
*/
|
||||
void optimizeMemoryAccesses(Trace*, IRFactory*);
|
||||
void optimizePredictions(Trace*, IRFactory*);
|
||||
void eliminateDeadCode(Trace*, IRFactory*);
|
||||
void optimizeJumps(Trace*, IRFactory*);
|
||||
|
||||
@@ -35,6 +40,8 @@ void optimizeJumps(Trace*, IRFactory*);
|
||||
*/
|
||||
void optimizeTrace(Trace*, TraceBuilder*);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
}}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| 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 <boost/next_prior.hpp>
|
||||
|
||||
#include "folly/Optional.h"
|
||||
#include "folly/Lazy.h"
|
||||
|
||||
#include "hphp/runtime/vm/translator/hopt/irfactory.h"
|
||||
#include "hphp/runtime/vm/translator/hopt/state_vector.h"
|
||||
#include "hphp/runtime/vm/translator/hopt/ir.h"
|
||||
#include "hphp/runtime/vm/translator/hopt/cfg.h"
|
||||
#include "hphp/runtime/vm/translator/hopt/mutation.h"
|
||||
|
||||
namespace HPHP { namespace JIT {
|
||||
|
||||
TRACE_SET_MOD(hhir);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace {
|
||||
|
||||
template<class InputIterator>
|
||||
bool instructionsAreSinkable(InputIterator first, InputIterator last) {
|
||||
for (; first != last; ++first) {
|
||||
switch (first->op()) {
|
||||
case ReDefSP:
|
||||
case ReDefGeneratorSP:
|
||||
case DecRef:
|
||||
case DecRefNZ:
|
||||
case Marker:
|
||||
case IncRef:
|
||||
case LdMem:
|
||||
return true;
|
||||
default:
|
||||
FTRACE(5, "unsinkable: {}\n", first->toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
not_reached();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
* Optimizations that try to hoist CheckType instructions so that we
|
||||
* can specialize code earlier and avoid generic operations.
|
||||
*/
|
||||
void optimizePredictions(Trace* const trace, IRFactory* const irFactory) {
|
||||
FTRACE(5, "PredOpts:vvvvvvvvvvvvvvvvvvvvv\n");
|
||||
SCOPE_EXIT { FTRACE(5, "PredOpts:^^^^^^^^^^^^^^^^^^^^^\n"); };
|
||||
|
||||
auto const sortedBlocks = folly::lazy([&]{
|
||||
return sortCfg(trace, *irFactory);
|
||||
});
|
||||
auto const predecessors = folly::lazy([&]{
|
||||
return computePredecessors(sortedBlocks());
|
||||
});
|
||||
|
||||
/*
|
||||
* We frequently generate a generic LdMem, followed by a generic
|
||||
* IncRef, followed by a CheckType that refines that temporary or
|
||||
* otherwise branches to a block that's going to do a ReqBindJmp.
|
||||
* (This happens for e.g. in the case of object property accesses.)
|
||||
*
|
||||
* As long as the intervening instructions can be sunk into the exit
|
||||
* block this optimization will change these sequences to do the
|
||||
* type check before loading the value. If it fails, we'll do the
|
||||
* generic LdMem/IncRef on the exit block, otherwise we do
|
||||
* type-specialized versions.
|
||||
*/
|
||||
auto optLdMem = [&] (IRInstruction* checkType, IRInstruction* lastMarker) {
|
||||
auto const incRef = checkType->getSrc(0)->inst();
|
||||
if (incRef->op() != IncRef) return;
|
||||
auto const ldMem = incRef->getSrc(0)->inst();
|
||||
if (ldMem->op() != LdMem) return;
|
||||
if (ldMem->getSrc(1)->getValInt() != 0) return;
|
||||
if (!ldMem->getTypeParam().equals(Type::Cell)) return;
|
||||
|
||||
FTRACE(5, "candidate: {}\n", ldMem->toString());
|
||||
|
||||
auto const mainBlock = ldMem->getBlock();
|
||||
auto const exit = checkType->getTaken();
|
||||
auto const specialized = checkType->getBlock()->getNext();
|
||||
|
||||
if (mainBlock != checkType->getBlock()) return;
|
||||
if (predecessors()[exit->postId()].size() != 1) return;
|
||||
if (exit->isMain()) return;
|
||||
|
||||
auto const sinkFirst = mainBlock->iteratorTo(ldMem);
|
||||
auto const sinkLast = mainBlock->iteratorTo(checkType);
|
||||
if (!instructionsAreSinkable(sinkFirst, sinkLast)) return;
|
||||
|
||||
FTRACE(5, "all sinkable\n");
|
||||
auto const& rpoSort = sortedBlocks();
|
||||
|
||||
/*
|
||||
* We are going to add a new CheckTypeMem instruction in front of
|
||||
* the LdMem. Since CheckTypeMem is a control flow instruction,
|
||||
* it needs to end the block, so all the code after it has to move
|
||||
* to either the taken block (exit) or the fallthrough block
|
||||
* (specialized).
|
||||
*/
|
||||
auto const newCheckType = irFactory->gen(
|
||||
CheckTypeMem,
|
||||
checkType->getTypeParam(),
|
||||
checkType->getTaken(),
|
||||
ldMem->getSrc(0)
|
||||
);
|
||||
mainBlock->insert(mainBlock->iteratorTo(ldMem), newCheckType);
|
||||
|
||||
// Clone the instructions to the exit before specializing.
|
||||
cloneToBlock(rpoSort, irFactory, sinkFirst, sinkLast, exit);
|
||||
exit->insert(exit->skipLabel(), irFactory->cloneInstruction(lastMarker));
|
||||
|
||||
/*
|
||||
* Specialize the LdMem left on the main trace after cloning the
|
||||
* generic version to the exit. We'll reflowTypes in a sec to get
|
||||
* everything downstream specialized.
|
||||
*/
|
||||
ldMem->setTypeParam(checkType->getTypeParam());
|
||||
|
||||
/*
|
||||
* Replace the old CheckType with a Mov from the result of the
|
||||
* IncRef so that any uses of its dest point to the correct new
|
||||
* value. We'll copyProp and get rid of this in a later pass.
|
||||
*/
|
||||
irFactory->replace(
|
||||
checkType,
|
||||
Mov,
|
||||
incRef->getDst()
|
||||
);
|
||||
|
||||
// Move the fallthrough case to specialized.
|
||||
moveToBlock(sinkFirst, boost::next(sinkLast), specialized);
|
||||
specialized->insert(specialized->skipLabel(),
|
||||
irFactory->cloneInstruction(lastMarker));
|
||||
|
||||
reflowTypes(specialized, rpoSort);
|
||||
};
|
||||
|
||||
/*
|
||||
* The main loop for this pass.
|
||||
*
|
||||
* The individual optimizations called here are expected to change
|
||||
* block boundaries and potentially add new blocks. They may not
|
||||
* unlink the block containing the CheckType instruction they are
|
||||
* visiting.
|
||||
*/
|
||||
if (!trace->isMain()) return;
|
||||
for (Block* b : trace->getBlocks()) {
|
||||
IRInstruction* lastMarker = nullptr;
|
||||
for (auto& inst : *b) {
|
||||
if (inst.op() == Marker) {
|
||||
lastMarker = &inst;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inst.op() == CheckType &&
|
||||
inst.getSrc(0)->type().equals(Type::Cell)) {
|
||||
assert(lastMarker);
|
||||
optLdMem(&inst, lastMarker);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
}}
|
||||
@@ -850,7 +850,7 @@ void TraceBuilder::reoptimize() {
|
||||
Block* block = blocks.front();
|
||||
blocks.pop_front();
|
||||
assert(block->getTrace() == m_trace.get());
|
||||
FTRACE(5, "Block: {}\n", block->postId());
|
||||
FTRACE(5, "Block: {}\n", block->getId());
|
||||
|
||||
m_trace->push_back(block);
|
||||
if (m_snapshots[block]) {
|
||||
|
||||
@@ -31,6 +31,7 @@ void assert_fail_log(const char* title, const std::string& msg) {
|
||||
if (s_logger) {
|
||||
s_logger(title, msg);
|
||||
}
|
||||
fprintf(stderr, "Assertion failure: %s\n", msg.c_str());
|
||||
}
|
||||
|
||||
void register_assert_fail_logger(AssertFailLogger l) {
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário