diff --git a/c-opopt.cc b/c-opopt.cc index c61de72..f474d9f 100644 --- a/c-opopt.cc +++ b/c-opopt.cc @@ -27,6 +27,9 @@ uint32_t opopt::ComputeHash( ^ ( ( oh << 29 ) | ( oh >> 3 ) ); } +constexpr uint32_t T_OptData::CFG_ENTER; +constexpr uint32_t T_OptData::CFG_MAINLOOP; +constexpr uint32_t T_OptData::CFG_END; /*= T_OptData - INPUT DECLARATIONS ===========================================*/ @@ -123,7 +126,6 @@ namespace { // CFG type shortcuts using T_CFN_ = T_OptData::T_CtrlFlowNode; using P_CFN_ = T_OptData::P_CtrlFlowNode; -using RP_CFN_ = T_OptData::RP_CtrlFlowNode; // Helpers to create or re-use CFG nodes T_OptData::P_CtrlFlowNode BCFGNewNode_( @@ -143,7 +145,9 @@ T_OptData::P_CtrlFlowNode BCFGNewNode_( #define M_NEWNODE_() BCFGNewNode_( old ) #define M_ADDNEW_() \ - ctrlFlowGraph[ ctrlFlowGraph.add( M_NEWNODE_( ) ) ].get( ) + ctrlFlowGraph.add( M_NEWNODE_( ) ) +#define M_NODE_(i) \ + ctrlFlowGraph[i] } // namespace @@ -158,29 +162,29 @@ void T_OptData::buildControlFlowGraph( // Create special nodes M_ADDNEW_( ); - const RP_CFN_ nMainLoop { M_ADDNEW_( ) }; - const RP_CFN_ nExit { M_ADDNEW_( ) }; - nMainLoop->outbound.add( nExit ); - nExit->inbound.add( nMainLoop ); + M_ADDNEW_( ); + M_ADDNEW_( ); + M_NODE_( CFG_MAINLOOP )->outbound.add( CFG_END ); + M_NODE_( CFG_END )->inbound.add( CFG_MAINLOOP ); // Data structure to handle conditionals struct T_StackEntry_ { - RP_CFN_ condBlock; + uint32_t condBlock; bool hasDefault{ false }; - T_AutoArray< RP_CFN_ , 8 > caseBlocks; + T_AutoArray< uint32_t , 8 > caseBlocks; }; // Data structure for call sites struct T_CallSite_ { T_String name; - RP_CFN_ callBlock; - RP_CFN_ retBlock; + uint32_t callBlock; + uint32_t retBlock; }; // Generate control flow graph for each function T_AutoArray< T_StackEntry_ , 8 > stack; T_Array< T_CallSite_ > callSites; - RP_CFN_ cNode{ nullptr }; + T_Optional< uint32_t > cNode{ }; visitor.visit( program.root , [&]( A_Node& node , const bool exit ) { const auto nt{ node.type( ) }; @@ -194,16 +198,16 @@ void T_OptData::buildControlFlowGraph( logger( [&](){ T_StringBuilder sb; sb << "Function ended; last block had " - << ( cNode->instructions - ? cNode->instructions->count - : 0 ) + << ( 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 = nullptr; + cNode.clear( ); } else { logger( [&](){ T_StringBuilder sb; @@ -221,10 +225,11 @@ void T_OptData::buildControlFlowGraph( auto* const iptr{ dynamic_cast< A_InstructionNode* >( &node ) }; if ( iptr && !exit ) { assert( cNode ); - if ( cNode->instructions ) { - cNode->instructions->count ++; + auto& n{ *M_NODE_( *cNode ) }; + if ( n.instructions ) { + n.instructions->count ++; } else { - cNode->instructions = T_BasicBlock{ indexOf( *iptr ) }; + n.instructions = T_BasicBlock{ indexOf( *iptr ) }; } } @@ -238,15 +243,16 @@ void T_OptData::buildControlFlowGraph( // and the next block const auto ncb{ se.caseBlocks.size( ) }; for ( auto i = 0u ; i < ncb ; i ++ ) { - auto* cb{ se.caseBlocks[ i ] }; - cb->inbound.add( se.condBlock ); - se.condBlock->outbound.add( cb ); - cb->outbound.add( cNode ); - cNode->inbound.add( cb ); + 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 ) { - cNode->inbound.add( se.condBlock ); - se.condBlock->outbound.add( cNode ); + M_NODE_( *cNode )->inbound.add( se.condBlock ); + M_NODE_( se.condBlock )->outbound.add( *cNode ); } stack.removeLast( ); @@ -258,16 +264,16 @@ void T_OptData::buildControlFlowGraph( } , LL2 ); } else { auto& se{ stack.addNew( ) }; - se.condBlock = cNode; - cNode = nullptr; + se.condBlock = *cNode; + cNode.clear( ); logger( [&](){ T_StringBuilder sb; sb << "Entering conditional instruction, stack size " << stack.size( ) << ", block had " - << ( se.condBlock->instructions - ? se.condBlock->instructions->count - : 0 ) + << ( M_NODE_( se.condBlock )->instructions + ? M_NODE_( se.condBlock )->instructions->count + : 0 ) << " instructions"; return sb; } , LL2 ); @@ -281,9 +287,9 @@ void T_OptData::buildControlFlowGraph( logger( [&](){ T_StringBuilder sb; sb << "Call to " << ci.id( ) << ", block had " - << ( cNode->instructions - ? cNode->instructions->count - : 0 ) + << ( M_NODE_( *cNode )->instructions + ? M_NODE_( *cNode )->instructions->count + : 0 ) << " instructions"; return sb; } , LL2 ); @@ -301,13 +307,13 @@ void T_OptData::buildControlFlowGraph( logger( [&](){ T_StringBuilder sb; sb << "Case block added (" - << ( cNode->instructions - ? cNode->instructions->count - : 0 ) + << ( M_NODE_( *cNode )->instructions + ? M_NODE_( *cNode )->instructions->count + : 0 ) << " instructions)"; return sb; } , LL2 ); - cNode = nullptr; + cNode.clear( ); } else { stack.last( ).hasDefault = stack.last( ).hasDefault || ( nt == A_Node::TN_DEFAULT ); @@ -323,14 +329,14 @@ void T_OptData::buildControlFlowGraph( // Add fake call sites for *init* and *frame* { auto& cs{ callSites.addNew( ) }; - cs.callBlock = ctrlFlowGraph[ CFG_ENTER ].get( ); - cs.retBlock = ctrlFlowGraph[ CFG_MAINLOOP ].get( ); + cs.callBlock = CFG_ENTER; + cs.retBlock = CFG_MAINLOOP; cs.name = "*init*"; } { auto& cs{ callSites.addNew( ) }; - cs.callBlock = ctrlFlowGraph[ CFG_MAINLOOP ].get( ); - cs.retBlock = ctrlFlowGraph[ CFG_MAINLOOP ].get( ); + cs.callBlock = CFG_MAINLOOP; + cs.retBlock = CFG_MAINLOOP; cs.name = "*frame*"; } @@ -341,18 +347,20 @@ void T_OptData::buildControlFlowGraph( { auto& entry{ ctrlFlowGraph[ frec->first ] }; entry->inbound.add( cs.callBlock ); - cs.callBlock->outbound.add( entry.get( ) ); + M_NODE_( cs.callBlock )->outbound.add( frec->first ); } { - auto& exit{ ctrlFlowGraph[ frec->first + frec->count - 1 ] }; + const auto n{ frec->first + frec->count - 1 }; + auto& exit{ ctrlFlowGraph[ n ] }; exit->outbound.add( cs.retBlock ); - cs.retBlock->inbound.add( exit.get( ) ); + M_NODE_( cs.retBlock )->outbound.add( n ); } } } #undef M_ADDNEW_ #undef M_NEWNODE_ +#undef M_NODE_ /*= T_OptData - USE/DEFINE CHAINS ============================================*/ @@ -552,24 +560,6 @@ void BUDCAddEntries_( } } -/*----------------------------------------------------------------------------*/ - -struct T_VarEvt_ -{ - enum E_VarEvt { - DEF , USE , KILL - }; - - uint32_t instrId; - E_VarEvt evt; - T_OptData::T_VarId var; - - T_VarEvt_( uint32_t instrId , E_VarEvt evt , - T_OptData::T_VarId const& var ) - : instrId( instrId ) , evt( evt ) , var( var ) - { } -}; - } // namespace @@ -599,9 +589,134 @@ void T_OptData::buildUseDefineChains( BUDCAddEntries_( udPerInstr , i , false , r.defines ); } - // For each node of the CFG graph, we need to generate a sequence that - // indicates what gets defined, used or killed. - T_MultiArray< T_VarEvt_ > cfgVarEvents; + // Walk the graph from the entry point until all reachable nodes + // have been covered and keeping track of active definitions. When + // the flow diverges, we need to store the state before the + // divergence. + const uint32_t nNodes{ ctrlFlowGraph.size( ) }; + uint32_t nProcessed{ 0 }; + uint32_t node{ CFG_ENTER }; + + bool processedNodes[ nNodes ]; + memset( processedNodes , 0 , sizeof( processedNodes ) ); + + using T_ActDefs_ = T_Array< uint32_t >; + using P_ActDefs_ = T_OwnPtr< T_ActDefs_ >; + P_ActDefs_ activeDefs{ NewOwned< T_ActDefs_ >( ) }; + activeDefs->resize( varUDChains.size( ) , T_HashIndex::INVALID_INDEX ); + + struct T_StackEntry_ { + P_ActDefs_ def; + uint32_t node; + + T_StackEntry_( P_ActDefs_ const& src , const uint32_t n ) noexcept + : def{ NewOwned< T_ActDefs_ >( *src ) } , node( n ) + {} + }; + T_AutoArray< T_StackEntry_ , 32 > stack; + + while ( nProcessed < nNodes ) { + assert( !processedNodes[ node ] ); + auto const& cn{ *ctrlFlowGraph[ node ] }; + processedNodes[ node ] = true; + logger( [=]() { + T_StringBuilder sb; + sb << "processing node " << node; + return sb; + } , LL2 ); + nProcessed ++; + + if ( cn.instructions ) { + // Check for uses and defines in the instructions + const auto is{ cn.instructions->first }; + const auto ie{ is + cn.instructions->count }; + for ( auto ii = is ; ii < ie ; ii ++ ) { + auto const* const irec{ udPerInstr.get( ii ) }; + if ( !irec ) { + continue; + } + const auto nrec{ irec->size( ) }; + + // Handle uses first + for ( auto j = 0u ; j < nrec ; j ++ ) { + auto const& rec{ (*irec)[ j ] }; + if ( !rec.isUse ) { + continue; + } + + auto& resource{ varUDChains[ rec.entry ] }; + const auto defId{ (*activeDefs)[ rec.entry ] }; + // FIXME: must be defined + resource.defines[ defId ].refs.add( rec.index ); + resource.uses[ rec.index ].refs.add( defId ); + logger( [&](){ + T_StringBuilder sb; + sb << "USE " << resource.var.name + << " @ instr #" << ii + << ", def " << defId; + return sb; + } , LL2 ); + } + + // Handle defines + for ( auto j = 0u ; j < nrec ; j ++ ) { + auto const& rec{ (*irec)[ j ] }; + if ( rec.isUse ) { + continue; + } + (*activeDefs)[ rec.entry ] = rec.index; + logger( [&](){ + T_StringBuilder sb; + sb << "DEF " << rec.index + << varUDChains[ rec.entry ].var.name + << " @ instr #" << ii; + return sb; + } , LL2 ); + } + } + } + + if ( nProcessed == nNodes ) { + break; + } + + // Check for possible next nodes + // -> no output nodes left -> pop stack and keep trying, + // unless we've already processed all nodes + // -> if there's only one, use it and don't push to stack + // -> otherwise push to stack, use first available node + do { + auto const& rcn{ *ctrlFlowGraph[ node ] }; + const uint32_t nSuccs{ [&](){ + const auto no{ rcn.outbound.size( ) }; + uint32_t c{ 0 }; + for ( auto i = 0u ; i < no ; i ++ ) { + if ( !processedNodes[ rcn.outbound[ i ] ] ) { + c ++; + } + } + return c; + }() }; + logger( [=]() { + T_StringBuilder sb; + sb << "node " << node << ": " << nSuccs + << " successor(s) left"; + return sb; + } , LL2 ); + + if ( nSuccs == 0 ) { + assert( !stack.empty( ) ); + node = stack.last( ).node; + activeDefs = std::move( stack.last( ).def ); + stack.removeLast( ); + continue; + } + + // TODO cases in which there is one or more successors + } while ( 0 ); // XXX + + // 30 if next block is an end of function, kill locals + } } diff --git a/c-opopt.hh b/c-opopt.hh index 2603497..649f8af 100644 --- a/c-opopt.hh +++ b/c-opopt.hh @@ -67,13 +67,11 @@ struct T_OptData }; // Control flow graph node - struct T_CtrlFlowNode; - using RP_CtrlFlowNode = T_CtrlFlowNode*; struct T_CtrlFlowNode { T_Optional< T_BasicBlock > instructions; - T_AutoArray< RP_CtrlFlowNode , 16 > inbound; - T_AutoArray< RP_CtrlFlowNode , 16 > outbound; + T_AutoArray< uint32_t , 16 > inbound; + T_AutoArray< uint32_t , 16 > outbound; }; using P_CtrlFlowNode = T_OwnPtr< T_CtrlFlowNode >;