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.
This commit is contained in:
Emmanuel BENOîT 2017-12-10 10:14:35 +01:00
parent 35d3098917
commit ddad981055
2 changed files with 319 additions and 227 deletions

View file

@ -149,52 +149,42 @@ T_OptData::P_CtrlFlowNode BCFGNewNode_(
#define M_NODE_(i) \ #define M_NODE_(i) \
ctrlFlowGraph[i] ctrlFlowGraph[i]
} // namespace <anon>
/*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/
void T_OptData::buildControlFlowGraph( using T_BCFGFunctions_ = T_KeyValueTable< T_String , T_OptData::T_BasicBlock >;
T_OpsParserOutput& program ) noexcept
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
{ {
// Keep the old array, we'll reuse its contents
T_Array< P_CtrlFlowNode > old{ std::move( ctrlFlowGraph ) };
M_LOGSTR_( "Building control flow graph" , LL1 );
// Create special nodes
M_ADDNEW_( );
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_ {
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_Optional< uint32_t > cNode{ };
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& n{ dynamic_cast< A_FuncNode& >( node ) };
auto const& fn{ n.name( ) }; auto const& fn{ n.name( ) };
if ( exit ) {
assert( stack.empty( ) );
logger( [&](){ logger( [&](){
T_StringBuilder sb; T_StringBuilder sb;
sb << "Function ended; last block had " sb << "Function ended; last block had "
@ -208,34 +198,47 @@ void T_OptData::buildControlFlowGraph(
assert( frec ); assert( frec );
frec->count = ctrlFlowGraph.size( ) - frec->first; frec->count = ctrlFlowGraph.size( ) - frec->first;
cNode.clear( ); cNode.clear( );
} else { }
/*----------------------------------------------------------------------------*/
// 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( [&](){ logger( [&](){
T_StringBuilder sb; T_StringBuilder sb;
sb << "Starting function '" << fn << "' at " sb << "Entering conditional instruction, stack size "
<< ctrlFlowGraph.size( ); << stack.size( )
<< ", block had "
<< ( M_NODE_( se.condBlock )->instructions
? M_NODE_( se.condBlock )->instructions->count
: 0 )
<< " instructions";
return sb; return sb;
} , LL1 ); } , LL2 );
cfgFunctions.add( fn , T_BasicBlock{ ctrlFlowGraph.size( ) } );
cNode = M_ADDNEW_( );
}
return true;
} }
// All instructions: continue the current basic block inline void BCFGCondExit_(
auto* const iptr{ dynamic_cast< A_InstructionNode* >( &node ) }; T_BCFGStack_& stack ,
if ( iptr && !exit ) { T_Optional< uint32_t >& cNode ,
assert( cNode ); T_Array< T_OptData::P_CtrlFlowNode >& ctrlFlowGraph ,
auto& n{ *M_NODE_( *cNode ) }; T_Array< T_OptData::P_CtrlFlowNode >& old ,
if ( n.instructions ) { F_OPLogger const& logger ) noexcept
n.instructions->count ++; {
} else {
n.instructions = T_BasicBlock{ indexOf( *iptr ) };
}
}
// Handle conditionals
if ( nt == A_Node::OP_COND ) {
if ( exit ) {
auto& se{ stack.last( ) }; auto& se{ stack.last( ) };
cNode = M_ADDNEW_( ); cNode = M_ADDNEW_( );
@ -262,21 +265,57 @@ void T_OptData::buildControlFlowGraph(
<< stack.size( ); << stack.size( );
return sb; return sb;
} , LL2 ); } , 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 { } else {
auto& se{ stack.addNew( ) }; BCFGFuncEnter_( node , cNode , data.ctrlFlowGraph ,
se.condBlock = *cNode; old , data.cfgFunctions ,
cNode.clear( ); data.logger );
logger( [&](){ }
T_StringBuilder sb; return true;
sb << "Entering conditional instruction, stack size " }
<< stack.size( )
<< ", block had " // All instructions: continue the current basic block
<< ( M_NODE_( se.condBlock )->instructions auto* const iptr{ dynamic_cast< A_InstructionNode* >( &node ) };
? M_NODE_( se.condBlock )->instructions->count if ( iptr && !exit ) {
: 0 ) assert( cNode );
<< " instructions"; auto& n{ *data.ctrlFlowGraph[ *cNode ] };
return sb; if ( n.instructions ) {
} , LL2 ); 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; return true;
} }
@ -284,31 +323,33 @@ void T_OptData::buildControlFlowGraph(
// Calls also break the flow // Calls also break the flow
if ( nt == A_Node::OP_CALL && !exit ) { if ( nt == A_Node::OP_CALL && !exit ) {
T_CallInstrNode& ci{ *dynamic_cast< T_CallInstrNode* >( iptr ) }; T_CallInstrNode& ci{ *dynamic_cast< T_CallInstrNode* >( iptr ) };
logger( [&](){ data.logger( [&](){
T_StringBuilder sb; T_StringBuilder sb;
auto const& node{ *data.ctrlFlowGraph[ *cNode ] };
sb << "Call to " << ci.id( ) << ", block had " sb << "Call to " << ci.id( ) << ", block had "
<< ( M_NODE_( *cNode )->instructions << ( node.instructions
? M_NODE_( *cNode )->instructions->count ? node.instructions->count
: 0 ) : 0 )
<< " instructions"; << " instructions";
return sb; return sb;
} , LL2 ); } , LL2 );
auto& cs{ callSites.addNew( ) }; auto& cs{ data.callSites.addNew( ) };
cs.name = ci.id( ); cs.name = ci.id( );
cs.callBlock = *cNode; cs.callBlock = *cNode;
cNode = cs.retBlock = M_ADDNEW_( ); cNode = cs.retBlock = data.ctrlFlowGraph.add( M_NEWNODE_( ) );
return true; return true;
} }
// Condition case nodes: create new basic block, add to stack's list // Condition case nodes: create new basic block, add to stack's list
if ( nt == A_Node::TN_CASE || nt == A_Node::TN_DEFAULT ) { if ( nt == A_Node::TN_CASE || nt == A_Node::TN_DEFAULT ) {
if ( exit ) { if ( exit ) {
logger( [&](){ data.logger( [&](){
T_StringBuilder sb; T_StringBuilder sb;
auto const& node{ *data.ctrlFlowGraph[ *cNode ] };
sb << "Case block added (" sb << "Case block added ("
<< ( M_NODE_( *cNode )->instructions << ( node.instructions
? M_NODE_( *cNode )->instructions->count ? node.instructions->count
: 0 ) : 0 )
<< " instructions)"; << " instructions)";
return sb; return sb;
@ -317,53 +358,62 @@ void T_OptData::buildControlFlowGraph(
} else { } else {
stack.last( ).hasDefault = stack.last( ).hasDefault stack.last( ).hasDefault = stack.last( ).hasDefault
|| ( nt == A_Node::TN_DEFAULT ); || ( nt == A_Node::TN_DEFAULT );
cNode = M_ADDNEW_( ); cNode = data.ctrlFlowGraph.add( M_NEWNODE_( ) );
stack.last( ).caseBlocks.add( *cNode ); stack.last( ).caseBlocks.add( *cNode );
} }
} }
return !dynamic_cast< A_ExpressionNode* >( &node ); return !dynamic_cast< A_ExpressionNode* >( &node );
} ); }
assert( cfgFunctions.contains( "*init*" ) && cfgFunctions.contains( "*frame*" ) );
/*----------------------------------------------------------------------------*/
inline void BCFGHandleCalls_(
T_OptData& data ) noexcept
{
// Add fake call sites for *init* and *frame* // Add fake call sites for *init* and *frame*
{ {
auto& cs{ callSites.addNew( ) }; auto& cs{ data.callSites.addNew( ) };
cs.callBlock = CFG_ENTER; cs.callBlock = T_OptData::CFG_ENTER;
cs.retBlock = CFG_MAINLOOP; cs.retBlock = T_OptData::CFG_MAINLOOP;
cs.name = "*init*"; cs.name = "*init*";
} }
{ {
auto& cs{ callSites.addNew( ) }; auto& cs{ data.callSites.addNew( ) };
cs.callBlock = CFG_MAINLOOP; cs.callBlock = T_OptData::CFG_MAINLOOP;
cs.retBlock = CFG_MAINLOOP; cs.retBlock = T_OptData::CFG_MAINLOOP;
cs.name = "*frame*"; cs.name = "*frame*";
} }
// Handle calls // Handle calls
for ( auto const& cs : callSites ) { for ( auto const& cs : data.callSites ) {
auto const* frec{ cfgFunctions.get( cs.name ) }; auto const* frec{ data.cfgFunctions.get( cs.name ) };
assert( frec ); assert( frec );
{ {
auto& entry{ ctrlFlowGraph[ frec->first ] }; auto& entry{ data.ctrlFlowGraph[ frec->first ] };
entry->inbound.add( cs.callBlock ); entry->inbound.add( cs.callBlock );
M_NODE_( cs.callBlock )->outbound.add( frec->first ); data.ctrlFlowGraph[ cs.callBlock ]->outbound.add(
frec->first );
} }
{ {
const auto n{ frec->first + frec->count - 1 }; const auto n{ frec->first + frec->count - 1 };
auto& exit{ ctrlFlowGraph[ n ] }; auto& exit{ data.ctrlFlowGraph[ n ] };
exit->outbound.add( cs.retBlock ); exit->outbound.add( cs.retBlock );
M_NODE_( cs.retBlock )->inbound.add( n ); data.ctrlFlowGraph[ cs.retBlock ]->inbound.add( n );
}
} }
} }
// Dump whole graph /*----------------------------------------------------------------------------*/
logger( [&](){
inline T_StringBuilder BCFGDumpAll_(
T_OptData const& data ) noexcept
{
T_StringBuilder dump; T_StringBuilder dump;
int i{ 0 }; int i{ 0 };
dump << "Control flow graph dump\n"; dump << "Control flow graph dump\n";
for ( auto const& p : ctrlFlowGraph ) { for ( auto const& p : data.ctrlFlowGraph ) {
auto const& e{ *p }; auto const& e{ *p };
dump << "\nNode " << i++ << "\n\t"; dump << "\nNode " << i++ << "\n\t";
if ( e.instructions ) { if ( e.instructions ) {
@ -398,6 +448,40 @@ void T_OptData::buildControlFlowGraph(
dump << '\n'; dump << '\n';
return dump; return dump;
}
} // namespace <anon>
/*----------------------------------------------------------------------------*/
void T_OptData::buildControlFlowGraph(
T_OpsParserOutput& program ) noexcept
{
// Keep the old array, we'll reuse its contents
T_Array< P_CtrlFlowNode > old{ std::move( ctrlFlowGraph ) };
M_LOGSTR_( "Building control flow graph" , LL1 );
// Create special nodes
M_ADDNEW_( );
M_ADDNEW_( );
M_ADDNEW_( );
M_NODE_( CFG_MAINLOOP )->outbound.add( CFG_END );
M_NODE_( CFG_END )->inbound.add( CFG_MAINLOOP );
// Generate control flow graph for each function
T_BCFGStack_ stack;
T_Optional< uint32_t > cNode{ };
callSites.clear( );
visitor.visit( program.root , [&]( A_Node& node , const bool exit ) {
return BCFGVisitor_( node , exit , *this ,
stack , cNode , old );
} );
assert( cfgFunctions.contains( "*init*" )
&& cfgFunctions.contains( "*frame*" ) );
BCFGHandleCalls_( *this );
logger( [this](){
return BCFGDumpAll_( *this );
} , LL2 ); } , LL2 );
} }

View file

@ -75,6 +75,13 @@ struct T_OptData
}; };
using P_CtrlFlowNode = T_OwnPtr< T_CtrlFlowNode >; 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 // Special nodes in the graph
static constexpr uint32_t CFG_ENTER = 0; static constexpr uint32_t CFG_ENTER = 0;
static constexpr uint32_t CFG_MAINLOOP = 1; static constexpr uint32_t CFG_MAINLOOP = 1;
@ -82,6 +89,7 @@ struct T_OptData
// Control flow graph // Control flow graph
T_Array< P_CtrlFlowNode > ctrlFlowGraph; T_Array< P_CtrlFlowNode > ctrlFlowGraph;
T_Array< T_CallSite > callSites;
T_KeyValueTable< T_String , T_BasicBlock > cfgFunctions; T_KeyValueTable< T_String , T_BasicBlock > cfgFunctions;
// Build the control flow graph. Instruction numbering must be // Build the control flow graph. Instruction numbering must be