From ddad98105584fd6607de49640d2fbcc89f747478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= Date: Sun, 10 Dec 2017 10:14:35 +0100 Subject: [PATCH] Optimizer - Refactored CFG construction Split the control flow graph construction function into multiple functions. Doesn't add anything to the code, but makes it slightly less unreadable. --- c-opopt.cc | 538 +++++++++++++++++++++++++++++++---------------------- c-opopt.hh | 8 + 2 files changed, 319 insertions(+), 227 deletions(-) diff --git a/c-opopt.cc b/c-opopt.cc index c3d8bf0..779b67a 100644 --- a/c-opopt.cc +++ b/c-opopt.cc @@ -149,8 +149,309 @@ T_OptData::P_CtrlFlowNode BCFGNewNode_( #define M_NODE_(i) \ ctrlFlowGraph[i] -} // namespace +/*----------------------------------------------------------------------------*/ +using T_BCFGFunctions_ = T_KeyValueTable< T_String , T_OptData::T_BasicBlock >; + +inline void BCFGFuncEnter_( + A_Node& node , + T_Optional< uint32_t >& cNode , + T_Array< T_OptData::P_CtrlFlowNode >& ctrlFlowGraph , + T_Array< T_OptData::P_CtrlFlowNode >& old , + T_BCFGFunctions_& cfgFunctions , + F_OPLogger const& logger + ) noexcept +{ + auto& n{ dynamic_cast< A_FuncNode& >( node ) }; + auto const& fn{ n.name( ) }; + logger( [&](){ + T_StringBuilder sb; + sb << "Starting function '" << fn << "' at " + << ctrlFlowGraph.size( ); + return sb; + } , LL1 ); + cfgFunctions.add( fn , T_OptData::T_BasicBlock{ + ctrlFlowGraph.size( ) } ); + cNode = M_ADDNEW_( ); +} + +inline void BCFGFuncExit_( + A_Node& node , + T_Optional< uint32_t >& cNode , + T_Array< T_OptData::P_CtrlFlowNode >& ctrlFlowGraph , + T_BCFGFunctions_& cfgFunctions , + F_OPLogger const& logger + ) noexcept +{ + auto& n{ dynamic_cast< A_FuncNode& >( node ) }; + auto const& fn{ n.name( ) }; + logger( [&](){ + T_StringBuilder sb; + sb << "Function ended; last block had " + << ( M_NODE_( *cNode )->instructions + ? M_NODE_( *cNode )->instructions->count + : 0 ) + << " instructions"; + return sb; + } , LL1 ); + auto* frec{ cfgFunctions.get( fn ) }; + assert( frec ); + frec->count = ctrlFlowGraph.size( ) - frec->first; + cNode.clear( ); +} + +/*----------------------------------------------------------------------------*/ + +// Data structure to handle conditionals +struct T_BCFGStackEntry_ { + uint32_t condBlock; + bool hasDefault{ false }; + T_AutoArray< uint32_t , 8 > caseBlocks; +}; +using T_BCFGStack_ = T_AutoArray< T_BCFGStackEntry_ , 8 >; + +inline void BCFGCondEnter_( + T_BCFGStack_& stack , + T_Optional< uint32_t >& cNode , + T_Array< T_OptData::P_CtrlFlowNode >& ctrlFlowGraph , + F_OPLogger const& logger ) noexcept +{ + auto& se{ stack.addNew( ) }; + se.condBlock = *cNode; + cNode.clear( ); + logger( [&](){ + T_StringBuilder sb; + sb << "Entering conditional instruction, stack size " + << stack.size( ) + << ", block had " + << ( M_NODE_( se.condBlock )->instructions + ? M_NODE_( se.condBlock )->instructions->count + : 0 ) + << " instructions"; + return sb; + } , LL2 ); +} + +inline void BCFGCondExit_( + T_BCFGStack_& stack , + T_Optional< uint32_t >& cNode , + T_Array< T_OptData::P_CtrlFlowNode >& ctrlFlowGraph , + T_Array< T_OptData::P_CtrlFlowNode >& old , + F_OPLogger const& logger ) noexcept +{ + auto& se{ stack.last( ) }; + cNode = M_ADDNEW_( ); + + // Connect each case block to both the condition + // and the next block + const auto ncb{ se.caseBlocks.size( ) }; + for ( auto i = 0u ; i < ncb ; i ++ ) { + auto& cbi{ se.caseBlocks[ i ] }; + auto& cb{ *M_NODE_( cbi ) }; + cb.inbound.add( se.condBlock ); + M_NODE_( se.condBlock )->outbound.add( cbi ); + cb.outbound.add( *cNode ); + M_NODE_( *cNode )->inbound.add( cbi ); + } + if ( !se.hasDefault ) { + M_NODE_( *cNode )->inbound.add( se.condBlock ); + M_NODE_( se.condBlock )->outbound.add( *cNode ); + } + + stack.removeLast( ); + logger( [&](){ + T_StringBuilder sb; + sb << "Exiting conditional instruction, stack size " + << stack.size( ); + return sb; + } , LL2 ); +} + +/*----------------------------------------------------------------------------*/ + +inline bool BCFGVisitor_( + A_Node& node , + const bool exit , + T_OptData& data , + T_BCFGStack_& stack , + T_Optional< uint32_t >& cNode , + T_Array< T_OptData::P_CtrlFlowNode >& old + ) noexcept +{ + const auto nt{ node.type( ) }; + + // Handle start/end of functions + if ( nt == A_Node::DECL_FN || nt == A_Node::DECL_INIT + || nt == A_Node::DECL_FRAME ) { + if ( exit ) { + assert( stack.empty( ) ); + BCFGFuncExit_( node , cNode , data.ctrlFlowGraph , + data.cfgFunctions , data.logger ); + } else { + BCFGFuncEnter_( node , cNode , data.ctrlFlowGraph , + old , data.cfgFunctions , + data.logger ); + } + return true; + } + + // All instructions: continue the current basic block + auto* const iptr{ dynamic_cast< A_InstructionNode* >( &node ) }; + if ( iptr && !exit ) { + assert( cNode ); + auto& n{ *data.ctrlFlowGraph[ *cNode ] }; + if ( n.instructions ) { + n.instructions->count ++; + } else { + n.instructions = T_OptData::T_BasicBlock{ + data.indexOf( *iptr ) }; + } + } + + // Handle conditionals + if ( nt == A_Node::OP_COND ) { + if ( exit ) { + BCFGCondExit_( stack , cNode , data.ctrlFlowGraph , + old , data.logger ); + } else { + BCFGCondEnter_( stack , cNode , data.ctrlFlowGraph , + data.logger ); + } + return true; + } + + // Calls also break the flow + if ( nt == A_Node::OP_CALL && !exit ) { + T_CallInstrNode& ci{ *dynamic_cast< T_CallInstrNode* >( iptr ) }; + data.logger( [&](){ + T_StringBuilder sb; + auto const& node{ *data.ctrlFlowGraph[ *cNode ] }; + sb << "Call to " << ci.id( ) << ", block had " + << ( node.instructions + ? node.instructions->count + : 0 ) + << " instructions"; + return sb; + } , LL2 ); + + auto& cs{ data.callSites.addNew( ) }; + cs.name = ci.id( ); + cs.callBlock = *cNode; + cNode = cs.retBlock = data.ctrlFlowGraph.add( M_NEWNODE_( ) ); + return true; + } + + // Condition case nodes: create new basic block, add to stack's list + if ( nt == A_Node::TN_CASE || nt == A_Node::TN_DEFAULT ) { + if ( exit ) { + data.logger( [&](){ + T_StringBuilder sb; + auto const& node{ *data.ctrlFlowGraph[ *cNode ] }; + sb << "Case block added (" + << ( node.instructions + ? node.instructions->count + : 0 ) + << " instructions)"; + return sb; + } , LL2 ); + cNode.clear( ); + } else { + stack.last( ).hasDefault = stack.last( ).hasDefault + || ( nt == A_Node::TN_DEFAULT ); + cNode = data.ctrlFlowGraph.add( M_NEWNODE_( ) ); + stack.last( ).caseBlocks.add( *cNode ); + } + } + + return !dynamic_cast< A_ExpressionNode* >( &node ); +} + +/*----------------------------------------------------------------------------*/ + +inline void BCFGHandleCalls_( + T_OptData& data ) noexcept +{ + // Add fake call sites for *init* and *frame* + { + auto& cs{ data.callSites.addNew( ) }; + cs.callBlock = T_OptData::CFG_ENTER; + cs.retBlock = T_OptData::CFG_MAINLOOP; + cs.name = "*init*"; + } + { + auto& cs{ data.callSites.addNew( ) }; + cs.callBlock = T_OptData::CFG_MAINLOOP; + cs.retBlock = T_OptData::CFG_MAINLOOP; + cs.name = "*frame*"; + } + + // Handle calls + for ( auto const& cs : data.callSites ) { + auto const* frec{ data.cfgFunctions.get( cs.name ) }; + assert( frec ); + { + auto& entry{ data.ctrlFlowGraph[ frec->first ] }; + entry->inbound.add( cs.callBlock ); + data.ctrlFlowGraph[ cs.callBlock ]->outbound.add( + frec->first ); + } + { + const auto n{ frec->first + frec->count - 1 }; + auto& exit{ data.ctrlFlowGraph[ n ] }; + exit->outbound.add( cs.retBlock ); + data.ctrlFlowGraph[ cs.retBlock ]->inbound.add( n ); + } + } +} + +/*----------------------------------------------------------------------------*/ + +inline T_StringBuilder BCFGDumpAll_( + T_OptData const& data ) noexcept +{ + T_StringBuilder dump; + + int i{ 0 }; + dump << "Control flow graph dump\n"; + for ( auto const& p : data.ctrlFlowGraph ) { + auto const& e{ *p }; + dump << "\nNode " << i++ << "\n\t"; + if ( e.instructions ) { + dump << e.instructions->count + << " instruction(s) at index " + << e.instructions->first; + } else { + dump << "No instructions"; + } + dump << "\n\tInbound:"; + { + const auto ni{ e.inbound.size( ) }; + if ( ni == 0 ) { + dump << " NONE"; + } + for ( auto idx = 0u ; idx < ni ; idx ++ ) { + dump << ' ' << e.inbound[ idx ]; + } + } + dump << "\n\tOutbound:"; + { + const auto no{ e.outbound.size( ) }; + if ( no == 0 ) { + dump << " NONE"; + } + for ( auto idx = 0u ; idx < no ; idx ++ ) { + dump << ' ' << e.outbound[ idx ]; + } + } + dump << '\n'; + } + dump << '\n'; + + return dump; +} + + +} // namespace /*----------------------------------------------------------------------------*/ void T_OptData::buildControlFlowGraph( @@ -167,237 +468,20 @@ void T_OptData::buildControlFlowGraph( M_NODE_( CFG_MAINLOOP )->outbound.add( CFG_END ); M_NODE_( CFG_END )->inbound.add( CFG_MAINLOOP ); - // Data structure to handle conditionals - struct T_StackEntry_ { - uint32_t condBlock; - bool hasDefault{ false }; - T_AutoArray< uint32_t , 8 > caseBlocks; - }; - - // Data structure for call sites - struct T_CallSite_ { - T_String name; - uint32_t callBlock; - uint32_t retBlock; - }; - // Generate control flow graph for each function - T_AutoArray< T_StackEntry_ , 8 > stack; - T_Array< T_CallSite_ > callSites; + T_BCFGStack_ stack; T_Optional< uint32_t > cNode{ }; + callSites.clear( ); visitor.visit( program.root , [&]( A_Node& node , const bool exit ) { - const auto nt{ node.type( ) }; - - // Handle start/end of functions - if ( nt == A_Node::DECL_FN || nt == A_Node::DECL_INIT - || nt == A_Node::DECL_FRAME ) { - auto& n{ dynamic_cast< A_FuncNode& >( node ) }; - auto const& fn{ n.name( ) }; - if ( exit ) { - assert( stack.empty( ) ); - logger( [&](){ - T_StringBuilder sb; - sb << "Function ended; last block had " - << ( M_NODE_( *cNode )->instructions - ? M_NODE_( *cNode )->instructions->count - : 0 ) - << " instructions"; - return sb; - } , LL1 ); - auto* frec{ cfgFunctions.get( fn ) }; - assert( frec ); - frec->count = ctrlFlowGraph.size( ) - frec->first; - cNode.clear( ); - } else { - logger( [&](){ - T_StringBuilder sb; - sb << "Starting function '" << fn << "' at " - << ctrlFlowGraph.size( ); - return sb; - } , LL1 ); - cfgFunctions.add( fn , T_BasicBlock{ ctrlFlowGraph.size( ) } ); - cNode = M_ADDNEW_( ); - } - return true; - } - - // All instructions: continue the current basic block - auto* const iptr{ dynamic_cast< A_InstructionNode* >( &node ) }; - if ( iptr && !exit ) { - assert( cNode ); - auto& n{ *M_NODE_( *cNode ) }; - if ( n.instructions ) { - n.instructions->count ++; - } else { - n.instructions = T_BasicBlock{ indexOf( *iptr ) }; - } - } - - // Handle conditionals - if ( nt == A_Node::OP_COND ) { - if ( exit ) { - auto& se{ stack.last( ) }; - cNode = M_ADDNEW_( ); - - // Connect each case block to both the condition - // and the next block - const auto ncb{ se.caseBlocks.size( ) }; - for ( auto i = 0u ; i < ncb ; i ++ ) { - auto& cbi{ se.caseBlocks[ i ] }; - auto& cb{ *M_NODE_( cbi ) }; - cb.inbound.add( se.condBlock ); - M_NODE_( se.condBlock )->outbound.add( cbi ); - cb.outbound.add( *cNode ); - M_NODE_( *cNode )->inbound.add( cbi ); - } - if ( !se.hasDefault ) { - M_NODE_( *cNode )->inbound.add( se.condBlock ); - M_NODE_( se.condBlock )->outbound.add( *cNode ); - } - - stack.removeLast( ); - logger( [&](){ - T_StringBuilder sb; - sb << "Exiting conditional instruction, stack size " - << stack.size( ); - return sb; - } , LL2 ); - } else { - auto& se{ stack.addNew( ) }; - se.condBlock = *cNode; - cNode.clear( ); - logger( [&](){ - T_StringBuilder sb; - sb << "Entering conditional instruction, stack size " - << stack.size( ) - << ", block had " - << ( M_NODE_( se.condBlock )->instructions - ? M_NODE_( se.condBlock )->instructions->count - : 0 ) - << " instructions"; - return sb; - } , LL2 ); - } - return true; - } - - // Calls also break the flow - if ( nt == A_Node::OP_CALL && !exit ) { - T_CallInstrNode& ci{ *dynamic_cast< T_CallInstrNode* >( iptr ) }; - logger( [&](){ - T_StringBuilder sb; - sb << "Call to " << ci.id( ) << ", block had " - << ( M_NODE_( *cNode )->instructions - ? M_NODE_( *cNode )->instructions->count - : 0 ) - << " instructions"; - return sb; - } , LL2 ); - - auto& cs{ callSites.addNew( ) }; - cs.name = ci.id( ); - cs.callBlock = *cNode; - cNode = cs.retBlock = M_ADDNEW_( ); - return true; - } - - // Condition case nodes: create new basic block, add to stack's list - if ( nt == A_Node::TN_CASE || nt == A_Node::TN_DEFAULT ) { - if ( exit ) { - logger( [&](){ - T_StringBuilder sb; - sb << "Case block added (" - << ( M_NODE_( *cNode )->instructions - ? M_NODE_( *cNode )->instructions->count - : 0 ) - << " instructions)"; - return sb; - } , LL2 ); - cNode.clear( ); - } else { - stack.last( ).hasDefault = stack.last( ).hasDefault - || ( nt == A_Node::TN_DEFAULT ); - cNode = M_ADDNEW_( ); - stack.last( ).caseBlocks.add( *cNode ); - } - } - - return !dynamic_cast< A_ExpressionNode* >( &node ); + return BCFGVisitor_( node , exit , *this , + stack , cNode , old ); } ); - assert( cfgFunctions.contains( "*init*" ) && cfgFunctions.contains( "*frame*" ) ); + assert( cfgFunctions.contains( "*init*" ) + && cfgFunctions.contains( "*frame*" ) ); - // Add fake call sites for *init* and *frame* - { - auto& cs{ callSites.addNew( ) }; - cs.callBlock = CFG_ENTER; - cs.retBlock = CFG_MAINLOOP; - cs.name = "*init*"; - } - { - auto& cs{ callSites.addNew( ) }; - cs.callBlock = CFG_MAINLOOP; - cs.retBlock = CFG_MAINLOOP; - cs.name = "*frame*"; - } - - // Handle calls - for ( auto const& cs : callSites ) { - auto const* frec{ cfgFunctions.get( cs.name ) }; - assert( frec ); - { - auto& entry{ ctrlFlowGraph[ frec->first ] }; - entry->inbound.add( cs.callBlock ); - M_NODE_( cs.callBlock )->outbound.add( frec->first ); - } - { - const auto n{ frec->first + frec->count - 1 }; - auto& exit{ ctrlFlowGraph[ n ] }; - exit->outbound.add( cs.retBlock ); - M_NODE_( cs.retBlock )->inbound.add( n ); - } - } - - // Dump whole graph - logger( [&](){ - T_StringBuilder dump; - - int i{ 0 }; - dump << "Control flow graph dump\n"; - for ( auto const& p : ctrlFlowGraph ) { - auto const& e{ *p }; - dump << "\nNode " << i++ << "\n\t"; - if ( e.instructions ) { - dump << e.instructions->count - << " instruction(s) at index " - << e.instructions->first; - } else { - dump << "No instructions"; - } - dump << "\n\tInbound:"; - { - const auto ni{ e.inbound.size( ) }; - if ( ni == 0 ) { - dump << " NONE"; - } - for ( auto idx = 0u ; idx < ni ; idx ++ ) { - dump << ' ' << e.inbound[ idx ]; - } - } - dump << "\n\tOutbound:"; - { - const auto no{ e.outbound.size( ) }; - if ( no == 0 ) { - dump << " NONE"; - } - for ( auto idx = 0u ; idx < no ; idx ++ ) { - dump << ' ' << e.outbound[ idx ]; - } - } - dump << '\n'; - } - dump << '\n'; - - return dump; + BCFGHandleCalls_( *this ); + logger( [this](){ + return BCFGDumpAll_( *this ); } , LL2 ); } diff --git a/c-opopt.hh b/c-opopt.hh index 649f8af..6d72976 100644 --- a/c-opopt.hh +++ b/c-opopt.hh @@ -75,6 +75,13 @@ struct T_OptData }; using P_CtrlFlowNode = T_OwnPtr< T_CtrlFlowNode >; + // Data structure for call sites + struct T_CallSite { + T_String name; + uint32_t callBlock; + uint32_t retBlock; + }; + // Special nodes in the graph static constexpr uint32_t CFG_ENTER = 0; static constexpr uint32_t CFG_MAINLOOP = 1; @@ -82,6 +89,7 @@ struct T_OptData // Control flow graph T_Array< P_CtrlFlowNode > ctrlFlowGraph; + T_Array< T_CallSite > callSites; T_KeyValueTable< T_String , T_BasicBlock > cfgFunctions; // Build the control flow graph. Instruction numbering must be