diff --git a/hphp/doc/ir.specification b/hphp/doc/ir.specification index 19f43414e..c62e4b979 100644 --- a/hphp/doc/ir.specification +++ b/hphp/doc/ir.specification @@ -331,6 +331,10 @@ D:T = CheckType 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 S0:PtrToGen -> L + + If the value pointed to by S0 is not type T, branch to the label L. + GuardLoc S0:FramePtr Guard that type of the given localId on the frame S0 is a subtype of @@ -644,11 +648,12 @@ D:T* = LdLocAddr S0:FramePtr Loads the address of the local slot localId from the frame S0 into D. -D:T = LdMem S0:PtrToGen [ -> L ] +D:T = LdMem 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 S0:Obj S1:ConstInt [ -> L ] diff --git a/hphp/runtime/base/runtime_option.h b/hphp/runtime/base/runtime_option.h index 01c944230..8f55f41d7 100644 --- a/hphp/runtime/base/runtime_option.h +++ b/hphp/runtime/base/runtime_option.h @@ -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) \ diff --git a/hphp/runtime/vm/translator/hopt/block.h b/hphp/runtime/vm/translator/hopt/block.h index b7f6ac438..96ef5513b 100644 --- a/hphp/runtime/vm/translator/hopt/block.h +++ b/hphp/runtime/vm/translator/hopt/block.h @@ -164,8 +164,8 @@ struct Block : boost::noncopyable { template 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: diff --git a/hphp/runtime/vm/translator/hopt/check.cpp b/hphp/runtime/vm/translator/hopt/check.cpp index 48c482ba8..c1bb69c82 100644 --- a/hphp/runtime/vm/translator/hopt/check.cpp +++ b/hphp/runtime/vm/translator/hopt/check.cpp @@ -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(); diff --git a/hphp/runtime/vm/translator/hopt/check.h b/hphp/runtime/vm/translator/hopt/check.h index 0a14e6fe8..e3627f294 100644 --- a/hphp/runtime/vm/translator/hopt/check.h +++ b/hphp/runtime/vm/translator/hopt/check.h @@ -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&); diff --git a/hphp/runtime/vm/translator/hopt/codegen.cpp b/hphp/runtime/vm/translator/hopt/codegen.cpp index be5a503b4..ce17a1304 100644 --- a/hphp/runtime/vm/translator/hopt/codegen.cpp +++ b/hphp/runtime/vm/translator/hopt/codegen.cpp @@ -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); diff --git a/hphp/runtime/vm/translator/hopt/ir.cpp b/hphp/runtime/vm/translator/hopt/ir.cpp index 93ad46944..07f8f823b 100644 --- a/hphp/runtime/vm/translator/hopt/ir.cpp +++ b/hphp/runtime/vm/translator/hopt/ir.cpp @@ -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 diff --git a/hphp/runtime/vm/translator/hopt/ir.h b/hphp/runtime/vm/translator/hopt/ir.h index 3d386c26f..c668a241b 100644 --- a/hphp/runtime/vm/translator/hopt/ir.h +++ b/hphp/runtime/vm/translator/hopt/ir.h @@ -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) \ diff --git a/hphp/runtime/vm/translator/hopt/irinstruction.h b/hphp/runtime/vm/translator/hopt/irinstruction.h index 423eba8a2..351609f0a 100644 --- a/hphp/runtime/vm/translator/hopt/irinstruction.h +++ b/hphp/runtime/vm/translator/hopt/irinstruction.h @@ -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); diff --git a/hphp/runtime/vm/translator/hopt/jumpopts.cpp b/hphp/runtime/vm/translator/hopt/jumpopts.cpp index feee695b2..fca16ec59 100644 --- a/hphp/runtime/vm/translator/hopt/jumpopts.cpp +++ b/hphp/runtime/vm/translator/hopt/jumpopts.cpp @@ -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); diff --git a/hphp/runtime/vm/translator/hopt/mutation.cpp b/hphp/runtime/vm/translator/hopt/mutation.cpp new file mode 100644 index 000000000..f2001ff26 --- /dev/null +++ b/hphp/runtime/vm/translator/hopt/mutation.cpp @@ -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 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); + } +} + +////////////////////////////////////////////////////////////////////// + +}} diff --git a/hphp/runtime/vm/translator/hopt/mutation.h b/hphp/runtime/vm/translator/hopt/mutation.h new file mode 100644 index 000000000..25cc1fe78 --- /dev/null +++ b/hphp/runtime/vm/translator/hopt/mutation.h @@ -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 diff --git a/hphp/runtime/vm/translator/hopt/opt.cpp b/hphp/runtime/vm/translator/hopt/opt.cpp index 922422e5d..6350cc2a7 100644 --- a/hphp/runtime/vm/translator/hopt/opt.cpp +++ b/hphp/runtime/vm/translator/hopt/opt.cpp @@ -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"); } } diff --git a/hphp/runtime/vm/translator/hopt/opt.h b/hphp/runtime/vm/translator/hopt/opt.h index 1649e45ba..5fe66598e 100644 --- a/hphp/runtime/vm/translator/hopt/opt.h +++ b/hphp/runtime/vm/translator/hopt/opt.h @@ -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 diff --git a/hphp/runtime/vm/translator/hopt/predictionopts.cpp b/hphp/runtime/vm/translator/hopt/predictionopts.cpp new file mode 100644 index 000000000..b9275549b --- /dev/null +++ b/hphp/runtime/vm/translator/hopt/predictionopts.cpp @@ -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 + +#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 +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; + } + } + } +} + +////////////////////////////////////////////////////////////////////// + +}} diff --git a/hphp/runtime/vm/translator/hopt/tracebuilder.cpp b/hphp/runtime/vm/translator/hopt/tracebuilder.cpp index dcbfa29e7..ed092a6e8 100644 --- a/hphp/runtime/vm/translator/hopt/tracebuilder.cpp +++ b/hphp/runtime/vm/translator/hopt/tracebuilder.cpp @@ -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]) { diff --git a/hphp/util/assertions.cpp b/hphp/util/assertions.cpp index f347593e6..6fd97ae9a 100644 --- a/hphp/util/assertions.cpp +++ b/hphp/util/assertions.cpp @@ -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) {