From 69dc698997d6c623f44f9d6a5d58d71224f423db Mon Sep 17 00:00:00 2001 From: bsimmers Date: Tue, 14 May 2013 15:20:47 -0700 Subject: [PATCH] Move parts of ir.h to trace.h and cfg.h trace.h contains the definition of the Trace class, and cfg.h contains functions for iterating over Blocks and Traces or inspecting their CFGs. --- hphp/runtime/vm/translator/hopt/cfg.h | 173 +++++++++++++ hphp/runtime/vm/translator/hopt/cse.h | 1 + hphp/runtime/vm/translator/hopt/ir.cpp | 13 +- hphp/runtime/vm/translator/hopt/ir.h | 228 +----------------- hphp/runtime/vm/translator/hopt/irfactory.cpp | 3 + hphp/runtime/vm/translator/hopt/trace.h | 98 ++++++++ 6 files changed, 288 insertions(+), 228 deletions(-) create mode 100644 hphp/runtime/vm/translator/hopt/cfg.h create mode 100644 hphp/runtime/vm/translator/hopt/trace.h diff --git a/hphp/runtime/vm/translator/hopt/cfg.h b/hphp/runtime/vm/translator/hopt/cfg.h new file mode 100644 index 000000000..8fb739732 --- /dev/null +++ b/hphp/runtime/vm/translator/hopt/cfg.h @@ -0,0 +1,173 @@ +/* + +----------------------------------------------------------------------+ + | 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_VM_CFG_H_ +#define incl_HPHP_VM_CFG_H_ + +#include "hphp/runtime/vm/translator/hopt/ir.h" +#include "hphp/runtime/vm/translator/hopt/trace.h" + +namespace HPHP { namespace JIT { + +/* + * This header contains classes and functions for iterating over and + * inspecting the control flow graph of a Trace's Blocks. + */ + +/** + * PostorderSort encapsulates a depth-first postorder walk + */ +template +struct PostorderSort { + PostorderSort(Visitor &visitor, unsigned num_blocks) : + m_visited(num_blocks), m_visitor(visitor) { + } + + void walk(Block* block) { + assert(!block->empty()); + if (m_visited.test(block->getId())) return; + m_visited.set(block->getId()); + Block* taken = block->getTaken(); + if (taken && taken->getTrace()->isMain() != block->getTrace()->isMain()) { + walk(taken); + taken = nullptr; + } + if (Block* next = block->getNext()) walk(next); + if (taken) walk(taken); + m_visitor(block); + } +private: + boost::dynamic_bitset<> m_visited; + Visitor &m_visitor; +}; + +/** + * perform a depth-first postorder walk + */ +template +void postorderWalk(Visitor visitor, unsigned num_blocks, Block* head) { + PostorderSort ps(visitor, num_blocks); + ps.walk(head); +} + +/* + * Compute the postorder number of each immediate dominator of each block, + * using the postorder numbers assigned by sortCfg(). + */ +typedef std::vector IdomVector; +IdomVector findDominators(const BlockList& blocks); + +/* + * A vector of children lists, indexed by block->postId() + */ +typedef std::vector DomChildren; + +/* + * compute the dominator tree, then populate a list of dominator children + * for each block. Note that DomChildren is indexed by block->postId(), + * not block->id(); that's why we don't use StateVector here. + */ +DomChildren findDomChildren(const BlockList& blocks); + +/* + * return true if b1 == b2 or if b1 dominates b2. + */ +bool dominates(const Block* b1, const Block* b2, const IdomVector& idoms); + +/* + * Compute a reverse postorder list of the basic blocks reachable from + * the first block in trace. + */ +BlockList sortCfg(Trace*, const IRFactory&); + +/* + * Return true if trace has internal control flow (IE it has a branch + * to itself somewhere. + */ +bool hasInternalFlow(Trace*); + +/* + * Visit basic blocks in a preorder traversal over the dominator tree. + * The state argument is passed by value (copied) as we move down the tree, + * so each child in the tree gets the state after the parent was processed. + * The body lambda should take State& (by reference) so it can modify it + * as each block is processed. + */ +template +void forPreorderDoms(Block* block, const std::vector& children, + State state, Body body) { + body(block, state); + for (Block* child : children[block->postId()]) { + forPreorderDoms(child, children, state, body); + } +} + +/* + * Visit the main trace followed by exit traces. + */ +template +void forEachTrace(Trace* main, Body body) { + body(main); + for (Trace* exit : main->getExitTraces()) { + body(exit); + } +} + +/* + * Visit the blocks in the main trace followed by exit trace blocks. + */ +template +void forEachTraceBlock(Trace* main, Body body) { + for (Block* block : main->getBlocks()) { + body(block); + } + for (Trace* exit : main->getExitTraces()) { + for (Block* block : exit->getBlocks()) { + body(block); + } + } +} + +/* + * Visit the instructions in this trace, in block order. + */ +template +void forEachInst(const BlockList& blocks, Body body) { + for (Block* block : blocks) { + for (IRInstruction& inst : *block) { + body(&inst); + } + } +} + +template +void forEachInst(Trace* trace, Body body) { + forEachInst(trace->getBlocks(), body); +} + +/* + * Visit each instruction in the main trace, then the exit traces + */ +template +void forEachTraceInst(Trace* main, Body body) { + forEachTrace(main, [=](Trace* t) { + forEachInst(t, body); + }); +} + +}} + +#endif diff --git a/hphp/runtime/vm/translator/hopt/cse.h b/hphp/runtime/vm/translator/hopt/cse.h index 57cddf6f5..a6f20febf 100644 --- a/hphp/runtime/vm/translator/hopt/cse.h +++ b/hphp/runtime/vm/translator/hopt/cse.h @@ -22,6 +22,7 @@ #include "folly/Hash.h" #include "hphp/runtime/vm/translator/hopt/ir.h" +#include "hphp/runtime/vm/translator/hopt/cfg.h" namespace HPHP { namespace JIT { diff --git a/hphp/runtime/vm/translator/hopt/ir.cpp b/hphp/runtime/vm/translator/hopt/ir.cpp index bfaf577b7..7ddb15356 100644 --- a/hphp/runtime/vm/translator/hopt/ir.cpp +++ b/hphp/runtime/vm/translator/hopt/ir.cpp @@ -30,11 +30,12 @@ #include "hphp/runtime/base/string_data.h" #include "hphp/runtime/vm/runtime.h" #include "hphp/runtime/base/stats.h" +#include "hphp/runtime/vm/translator/hopt/cse.h" #include "hphp/runtime/vm/translator/hopt/irfactory.h" #include "hphp/runtime/vm/translator/hopt/linearscan.h" -#include "hphp/runtime/vm/translator/hopt/cse.h" -#include "hphp/runtime/vm/translator/hopt/simplifier.h" #include "hphp/runtime/vm/translator/hopt/print.h" +#include "hphp/runtime/vm/translator/hopt/simplifier.h" +#include "hphp/runtime/vm/translator/hopt/trace.h" // Include last to localize effects to this file #include "hphp/util/assert_throw.h" @@ -320,6 +321,10 @@ Opcode getStackModifyingOpcode(Opcode opc) { return opc; } +Trace* IRInstruction::getTrace() const { + return m_block->getTrace(); +} + bool IRInstruction::hasExtra() const { return opcodeHasFlags(op(), HasExtra) && m_extra; } @@ -696,6 +701,10 @@ void Block::removeEdge(IRInstruction* jmp) { assert((node->next = nullptr, true)); } +bool Block::isMain() const { + return m_trace->isMain(); +} + bool IRInstruction::cseEquals(IRInstruction* inst) const { assert(canCSE()); diff --git a/hphp/runtime/vm/translator/hopt/ir.h b/hphp/runtime/vm/translator/hopt/ir.h index 14dbd7599..49bcc9a1e 100644 --- a/hphp/runtime/vm/translator/hopt/ir.h +++ b/hphp/runtime/vm/translator/hopt/ir.h @@ -2125,7 +2125,7 @@ struct Block : boost::noncopyable { void addEdge(IRInstruction* jmp); void removeEdge(IRInstruction* jmp); - inline bool isMain() const; + bool isMain() const; // return the last instruction in the block IRInstruction* back() const { @@ -2236,168 +2236,13 @@ struct Block : boost::noncopyable { Hint m_hint; // execution frequency hint }; typedef std::list BlockList; - -inline Trace* IRInstruction::getTrace() const { - return m_block->getTrace(); -} - -/* - * A Trace is a single-entry, multi-exit, sequence of blocks. Typically - * each block falls through to the next block but this is not guaranteed; - * traces may contain internal forward-only control flow. - */ -class Trace : boost::noncopyable { -public: - explicit Trace(Block* first, uint32_t bcOff) - : m_bcOff(bcOff) - , m_main(nullptr) - { - push_back(first); - } - - ~Trace() { - std::for_each(m_exitTraces.begin(), m_exitTraces.end(), - boost::checked_deleter()); - } - - std::list& getBlocks() { return m_blocks; } - const std::list& getBlocks() const { return m_blocks; } - Block* front() { return *m_blocks.begin(); } - Block* back() { auto it = m_blocks.end(); return *(--it); } - const Block* front() const { return *m_blocks.begin(); } - const Block* back() const { auto it = m_blocks.end(); return *(--it); } - - Block* push_back(Block* b) { - b->setTrace(this); - m_blocks.push_back(b); - return b; - } - - // temporary data field for use by individual passes - // - // Used by LinearScan as a "fake" instruction id, that comes - // between the id of the last instruction that branches to - // this exit trace, and the next instruction on the main trace. - uint32_t getData() const { return m_data; } - void setData(uint32_t d) { m_data = d; } - - uint32_t getBcOff() const { return m_bcOff; } - Trace* addExitTrace(Trace* exit) { - m_exitTraces.push_back(exit); - exit->setMain(this); - return exit; - } - bool isMain() const { return m_main == nullptr; } - void setMain(Trace* t) { - assert(m_main == nullptr); - m_main = t; - } - Trace* getMain() { - return m_main; - } - - typedef std::list ExitList; - typedef std::list::iterator ExitIterator; - - ExitList& getExitTraces() { return m_exitTraces; } - const ExitList& getExitTraces() const { return m_exitTraces; } - std::string toString() const; - -private: - // offset of the first bytecode in this trace; 0 if this trace doesn't - // represent a bytecode boundary. - uint32_t m_bcOff; - uint32_t m_data; - std::list m_blocks; // Blocks in main trace starting with entry block - ExitList m_exitTraces; // traces to which this trace exits - Trace* m_main; // ptr to parent trace if this is an exit trace -}; - -inline bool Block::isMain() const { return m_trace->isMain(); } - -/* - * Some utility micro-passes used from other major passes. - */ - -/** - * PostorderSort encapsulates a depth-first postorder walk - */ -template -struct PostorderSort { - PostorderSort(Visitor &visitor, unsigned num_blocks) : - m_visited(num_blocks), m_visitor(visitor) { - } - - void walk(Block* block) { - assert(!block->empty()); - if (m_visited.test(block->getId())) return; - m_visited.set(block->getId()); - Block* taken = block->getTaken(); - if (taken && taken->getTrace()->isMain() != block->getTrace()->isMain()) { - walk(taken); - taken = nullptr; - } - if (Block* next = block->getNext()) walk(next); - if (taken) walk(taken); - m_visitor(block); - } -private: - boost::dynamic_bitset<> m_visited; - Visitor &m_visitor; -}; - -/** - * perform a depth-first postorder walk - */ -template -void postorderWalk(Visitor visitor, unsigned num_blocks, Block* head) { - PostorderSort ps(visitor, num_blocks); - ps.walk(head); -} +typedef std::forward_list BlockPtrList; /* * Remove any instruction if live[iid] == false */ void removeDeadInstructions(Trace* trace, const boost::dynamic_bitset<>& live); -/* - * Compute the postorder number of each immediate dominator of each block, - * using the postorder numbers assigned by sortCfg(). - */ -typedef std::vector IdomVector; -IdomVector findDominators(const BlockList& blocks); - -typedef std::forward_list BlockPtrList; - -/* - * A vector of children lists, indexed by block->postId() - */ -typedef std::vector DomChildren; - -/* - * compute the dominator tree, then populate a list of dominator children - * for each block. Note that DomChildren is indexed by block->postId(), - * not block->id(); that's why we don't use StateVector here. - */ -DomChildren findDomChildren(const BlockList& blocks); - -/* - * return true if b1 == b2 or if b1 dominates b2. - */ -bool dominates(const Block* b1, const Block* b2, const IdomVector& idoms); - -/* - * Compute a reverse postorder list of the basic blocks reachable from - * the first block in trace. - */ -BlockList sortCfg(Trace*, const IRFactory&); - -/* - * Return true if trace has internal control flow (IE it has a branch - * to itself somewhere. - */ -bool hasInternalFlow(Trace*); - /** * Run all optimization passes on this trace */ @@ -2422,75 +2267,6 @@ inline bool isConvIntOrPtrToBool(IRInstruction* instr) { } } -/* - * Visit basic blocks in a preorder traversal over the dominator tree. - * The state argument is passed by value (copied) as we move down the tree, - * so each child in the tree gets the state after the parent was processed. - * The body lambda should take State& (by reference) so it can modify it - * as each block is processed. - */ -template -void forPreorderDoms(Block* block, const std::vector& children, - State state, Body body) { - body(block, state); - for (Block* child : children[block->postId()]) { - forPreorderDoms(child, children, state, body); - } -} - -/* - * Visit the main trace followed by exit traces. - */ -template -void forEachTrace(Trace* main, Body body) { - body(main); - for (Trace* exit : main->getExitTraces()) { - body(exit); - } -} - -/* - * Visit the blocks in the main trace followed by exit trace blocks. - */ -template -void forEachTraceBlock(Trace* main, Body body) { - for (Block* block : main->getBlocks()) { - body(block); - } - for (Trace* exit : main->getExitTraces()) { - for (Block* block : exit->getBlocks()) { - body(block); - } - } -} - -/* - * Visit the instructions in this trace, in block order. - */ -template -void forEachInst(const BlockList& blocks, Body body) { - for (Block* block : blocks) { - for (IRInstruction& inst : *block) { - body(&inst); - } - } -} - -template -void forEachInst(Trace* trace, Body body) { - forEachInst(trace->getBlocks(), body); -} - -/* - * Visit each instruction in the main trace, then the exit traces - */ -template -void forEachTraceInst(Trace* main, Body body) { - forEachTrace(main, [=](Trace* t) { - forEachInst(t, body); - }); -} - }} namespace std { diff --git a/hphp/runtime/vm/translator/hopt/irfactory.cpp b/hphp/runtime/vm/translator/hopt/irfactory.cpp index 6ab6857ef..a81cea3dd 100644 --- a/hphp/runtime/vm/translator/hopt/irfactory.cpp +++ b/hphp/runtime/vm/translator/hopt/irfactory.cpp @@ -13,8 +13,11 @@ | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ + #include "hphp/runtime/vm/translator/hopt/irfactory.h" +#include "hphp/runtime/vm/translator/hopt/cfg.h" + namespace HPHP { namespace JIT { IRInstruction* IRFactory::defLabel() { diff --git a/hphp/runtime/vm/translator/hopt/trace.h b/hphp/runtime/vm/translator/hopt/trace.h new file mode 100644 index 000000000..eef2f6292 --- /dev/null +++ b/hphp/runtime/vm/translator/hopt/trace.h @@ -0,0 +1,98 @@ +/* + +----------------------------------------------------------------------+ + | 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_VM_TRACE_H_ +#define incl_HPHP_VM_TRACE_H_ + +#include "hphp/runtime/vm/translator/hopt/ir.h" + +namespace HPHP { namespace JIT { + +/* + * A Trace is a single-entry, multi-exit, sequence of blocks. Typically + * each block falls through to the next block but this is not guaranteed; + * traces may contain internal forward-only control flow. + */ +class Trace : boost::noncopyable { +public: + explicit Trace(Block* first, uint32_t bcOff) + : m_bcOff(bcOff) + , m_main(nullptr) + { + push_back(first); + } + + ~Trace() { + std::for_each(m_exitTraces.begin(), m_exitTraces.end(), + boost::checked_deleter()); + } + + std::list& getBlocks() { return m_blocks; } + const std::list& getBlocks() const { return m_blocks; } + Block* front() { return *m_blocks.begin(); } + Block* back() { auto it = m_blocks.end(); return *(--it); } + const Block* front() const { return *m_blocks.begin(); } + const Block* back() const { auto it = m_blocks.end(); return *(--it); } + + Block* push_back(Block* b) { + b->setTrace(this); + m_blocks.push_back(b); + return b; + } + + // temporary data field for use by individual passes + // + // Used by LinearScan as a "fake" instruction id, that comes + // between the id of the last instruction that branches to + // this exit trace, and the next instruction on the main trace. + uint32_t getData() const { return m_data; } + void setData(uint32_t d) { m_data = d; } + + uint32_t getBcOff() const { return m_bcOff; } + Trace* addExitTrace(Trace* exit) { + m_exitTraces.push_back(exit); + exit->setMain(this); + return exit; + } + bool isMain() const { return m_main == nullptr; } + void setMain(Trace* t) { + assert(m_main == nullptr); + m_main = t; + } + Trace* getMain() { + return m_main; + } + + typedef std::list ExitList; + typedef std::list::iterator ExitIterator; + + ExitList& getExitTraces() { return m_exitTraces; } + const ExitList& getExitTraces() const { return m_exitTraces; } + std::string toString() const; + +private: + // offset of the first bytecode in this trace; 0 if this trace doesn't + // represent a bytecode boundary. + uint32_t m_bcOff; + uint32_t m_data; + std::list m_blocks; // Blocks in main trace starting with entry block + ExitList m_exitTraces; // traces to which this trace exits + Trace* m_main; // ptr to parent trace if this is an exit trace +}; + +}} + +#endif