Optimizer - WIP UD chain construction

This commit is contained in:
Emmanuel BENOîT 2017-12-08 07:38:59 +01:00
parent be007f8f96
commit 61cd7719f1
2 changed files with 182 additions and 69 deletions

View file

@ -27,6 +27,9 @@ uint32_t opopt::ComputeHash(
^ ( ( oh << 29 ) | ( oh >> 3 ) ); ^ ( ( 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 ===========================================*/ /*= T_OptData - INPUT DECLARATIONS ===========================================*/
@ -123,7 +126,6 @@ namespace {
// CFG type shortcuts // CFG type shortcuts
using T_CFN_ = T_OptData::T_CtrlFlowNode; using T_CFN_ = T_OptData::T_CtrlFlowNode;
using P_CFN_ = T_OptData::P_CtrlFlowNode; using P_CFN_ = T_OptData::P_CtrlFlowNode;
using RP_CFN_ = T_OptData::RP_CtrlFlowNode;
// Helpers to create or re-use CFG nodes // Helpers to create or re-use CFG nodes
T_OptData::P_CtrlFlowNode BCFGNewNode_( T_OptData::P_CtrlFlowNode BCFGNewNode_(
@ -143,7 +145,9 @@ T_OptData::P_CtrlFlowNode BCFGNewNode_(
#define M_NEWNODE_() BCFGNewNode_( old ) #define M_NEWNODE_() BCFGNewNode_( old )
#define M_ADDNEW_() \ #define M_ADDNEW_() \
ctrlFlowGraph[ ctrlFlowGraph.add( M_NEWNODE_( ) ) ].get( ) ctrlFlowGraph.add( M_NEWNODE_( ) )
#define M_NODE_(i) \
ctrlFlowGraph[i]
} // namespace <anon> } // namespace <anon>
@ -158,29 +162,29 @@ void T_OptData::buildControlFlowGraph(
// Create special nodes // Create special nodes
M_ADDNEW_( ); M_ADDNEW_( );
const RP_CFN_ nMainLoop { M_ADDNEW_( ) }; M_ADDNEW_( );
const RP_CFN_ nExit { M_ADDNEW_( ) }; M_ADDNEW_( );
nMainLoop->outbound.add( nExit ); M_NODE_( CFG_MAINLOOP )->outbound.add( CFG_END );
nExit->inbound.add( nMainLoop ); M_NODE_( CFG_END )->inbound.add( CFG_MAINLOOP );
// Data structure to handle conditionals // Data structure to handle conditionals
struct T_StackEntry_ { struct T_StackEntry_ {
RP_CFN_ condBlock; uint32_t condBlock;
bool hasDefault{ false }; bool hasDefault{ false };
T_AutoArray< RP_CFN_ , 8 > caseBlocks; T_AutoArray< uint32_t , 8 > caseBlocks;
}; };
// Data structure for call sites // Data structure for call sites
struct T_CallSite_ { struct T_CallSite_ {
T_String name; T_String name;
RP_CFN_ callBlock; uint32_t callBlock;
RP_CFN_ retBlock; uint32_t retBlock;
}; };
// Generate control flow graph for each function // Generate control flow graph for each function
T_AutoArray< T_StackEntry_ , 8 > stack; T_AutoArray< T_StackEntry_ , 8 > stack;
T_Array< T_CallSite_ > callSites; T_Array< T_CallSite_ > callSites;
RP_CFN_ cNode{ nullptr }; T_Optional< uint32_t > cNode{ };
visitor.visit( program.root , [&]( A_Node& node , const bool exit ) { visitor.visit( program.root , [&]( A_Node& node , const bool exit ) {
const auto nt{ node.type( ) }; const auto nt{ node.type( ) };
@ -194,16 +198,16 @@ void T_OptData::buildControlFlowGraph(
logger( [&](){ logger( [&](){
T_StringBuilder sb; T_StringBuilder sb;
sb << "Function ended; last block had " sb << "Function ended; last block had "
<< ( cNode->instructions << ( M_NODE_( *cNode )->instructions
? cNode->instructions->count ? M_NODE_( *cNode )->instructions->count
: 0 ) : 0 )
<< " instructions"; << " instructions";
return sb; return sb;
} , LL1 ); } , LL1 );
auto* frec{ cfgFunctions.get( fn ) }; auto* frec{ cfgFunctions.get( fn ) };
assert( frec ); assert( frec );
frec->count = ctrlFlowGraph.size( ) - frec->first; frec->count = ctrlFlowGraph.size( ) - frec->first;
cNode = nullptr; cNode.clear( );
} else { } else {
logger( [&](){ logger( [&](){
T_StringBuilder sb; T_StringBuilder sb;
@ -221,10 +225,11 @@ void T_OptData::buildControlFlowGraph(
auto* const iptr{ dynamic_cast< A_InstructionNode* >( &node ) }; auto* const iptr{ dynamic_cast< A_InstructionNode* >( &node ) };
if ( iptr && !exit ) { if ( iptr && !exit ) {
assert( cNode ); assert( cNode );
if ( cNode->instructions ) { auto& n{ *M_NODE_( *cNode ) };
cNode->instructions->count ++; if ( n.instructions ) {
n.instructions->count ++;
} else { } 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 // and the next block
const auto ncb{ se.caseBlocks.size( ) }; const auto ncb{ se.caseBlocks.size( ) };
for ( auto i = 0u ; i < ncb ; i ++ ) { for ( auto i = 0u ; i < ncb ; i ++ ) {
auto* cb{ se.caseBlocks[ i ] }; auto& cbi{ se.caseBlocks[ i ] };
cb->inbound.add( se.condBlock ); auto& cb{ *M_NODE_( cbi ) };
se.condBlock->outbound.add( cb ); cb.inbound.add( se.condBlock );
cb->outbound.add( cNode ); M_NODE_( se.condBlock )->outbound.add( cbi );
cNode->inbound.add( cb ); cb.outbound.add( cNode );
M_NODE_( *cNode )->inbound.add( cbi );
} }
if ( !se.hasDefault ) { if ( !se.hasDefault ) {
cNode->inbound.add( se.condBlock ); M_NODE_( *cNode )->inbound.add( se.condBlock );
se.condBlock->outbound.add( cNode ); M_NODE_( se.condBlock )->outbound.add( *cNode );
} }
stack.removeLast( ); stack.removeLast( );
@ -258,16 +264,16 @@ void T_OptData::buildControlFlowGraph(
} , LL2 ); } , LL2 );
} else { } else {
auto& se{ stack.addNew( ) }; auto& se{ stack.addNew( ) };
se.condBlock = cNode; se.condBlock = *cNode;
cNode = nullptr; cNode.clear( );
logger( [&](){ logger( [&](){
T_StringBuilder sb; T_StringBuilder sb;
sb << "Entering conditional instruction, stack size " sb << "Entering conditional instruction, stack size "
<< stack.size( ) << stack.size( )
<< ", block had " << ", block had "
<< ( se.condBlock->instructions << ( M_NODE_( se.condBlock )->instructions
? se.condBlock->instructions->count ? M_NODE_( se.condBlock )->instructions->count
: 0 ) : 0 )
<< " instructions"; << " instructions";
return sb; return sb;
} , LL2 ); } , LL2 );
@ -281,9 +287,9 @@ void T_OptData::buildControlFlowGraph(
logger( [&](){ logger( [&](){
T_StringBuilder sb; T_StringBuilder sb;
sb << "Call to " << ci.id( ) << ", block had " sb << "Call to " << ci.id( ) << ", block had "
<< ( cNode->instructions << ( M_NODE_( *cNode )->instructions
? cNode->instructions->count ? M_NODE_( *cNode )->instructions->count
: 0 ) : 0 )
<< " instructions"; << " instructions";
return sb; return sb;
} , LL2 ); } , LL2 );
@ -301,13 +307,13 @@ void T_OptData::buildControlFlowGraph(
logger( [&](){ logger( [&](){
T_StringBuilder sb; T_StringBuilder sb;
sb << "Case block added (" sb << "Case block added ("
<< ( cNode->instructions << ( M_NODE_( *cNode )->instructions
? cNode->instructions->count ? M_NODE_( *cNode )->instructions->count
: 0 ) : 0 )
<< " instructions)"; << " instructions)";
return sb; return sb;
} , LL2 ); } , LL2 );
cNode = nullptr; cNode.clear( );
} else { } else {
stack.last( ).hasDefault = stack.last( ).hasDefault stack.last( ).hasDefault = stack.last( ).hasDefault
|| ( nt == A_Node::TN_DEFAULT ); || ( nt == A_Node::TN_DEFAULT );
@ -323,14 +329,14 @@ void T_OptData::buildControlFlowGraph(
// Add fake call sites for *init* and *frame* // Add fake call sites for *init* and *frame*
{ {
auto& cs{ callSites.addNew( ) }; auto& cs{ callSites.addNew( ) };
cs.callBlock = ctrlFlowGraph[ CFG_ENTER ].get( ); cs.callBlock = CFG_ENTER;
cs.retBlock = ctrlFlowGraph[ CFG_MAINLOOP ].get( ); cs.retBlock = CFG_MAINLOOP;
cs.name = "*init*"; cs.name = "*init*";
} }
{ {
auto& cs{ callSites.addNew( ) }; auto& cs{ callSites.addNew( ) };
cs.callBlock = ctrlFlowGraph[ CFG_MAINLOOP ].get( ); cs.callBlock = CFG_MAINLOOP;
cs.retBlock = ctrlFlowGraph[ CFG_MAINLOOP ].get( ); cs.retBlock = CFG_MAINLOOP;
cs.name = "*frame*"; cs.name = "*frame*";
} }
@ -341,18 +347,20 @@ void T_OptData::buildControlFlowGraph(
{ {
auto& entry{ ctrlFlowGraph[ frec->first ] }; auto& entry{ ctrlFlowGraph[ frec->first ] };
entry->inbound.add( cs.callBlock ); 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 ); exit->outbound.add( cs.retBlock );
cs.retBlock->inbound.add( exit.get( ) ); M_NODE_( cs.retBlock )->outbound.add( n );
} }
} }
} }
#undef M_ADDNEW_ #undef M_ADDNEW_
#undef M_NEWNODE_ #undef M_NEWNODE_
#undef M_NODE_
/*= T_OptData - USE/DEFINE CHAINS ============================================*/ /*= 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 <anon> } // namespace <anon>
@ -599,9 +589,134 @@ void T_OptData::buildUseDefineChains(
BUDCAddEntries_( udPerInstr , i , false , r.defines ); BUDCAddEntries_( udPerInstr , i , false , r.defines );
} }
// For each node of the CFG graph, we need to generate a sequence that // Walk the graph from the entry point until all reachable nodes
// indicates what gets defined, used or killed. // have been covered and keeping track of active definitions. When
T_MultiArray< T_VarEvt_ > cfgVarEvents; // 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
}
} }

View file

@ -67,13 +67,11 @@ struct T_OptData
}; };
// Control flow graph node // Control flow graph node
struct T_CtrlFlowNode;
using RP_CtrlFlowNode = T_CtrlFlowNode*;
struct T_CtrlFlowNode struct T_CtrlFlowNode
{ {
T_Optional< T_BasicBlock > instructions; T_Optional< T_BasicBlock > instructions;
T_AutoArray< RP_CtrlFlowNode , 16 > inbound; T_AutoArray< uint32_t , 16 > inbound;
T_AutoArray< RP_CtrlFlowNode , 16 > outbound; T_AutoArray< uint32_t , 16 > outbound;
}; };
using P_CtrlFlowNode = T_OwnPtr< T_CtrlFlowNode >; using P_CtrlFlowNode = T_OwnPtr< T_CtrlFlowNode >;