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) \
ctrlFlowGraph[i]
} // namespace <anon>
/*----------------------------------------------------------------------------*/
void T_OptData::buildControlFlowGraph(
T_OpsParserOutput& program ) noexcept
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
{
// 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 const& fn{ n.name( ) };
if ( exit ) {
assert( stack.empty( ) );
logger( [&](){
T_StringBuilder sb;
sb << "Function ended; last block had "
@ -208,34 +198,47 @@ void T_OptData::buildControlFlowGraph(
assert( frec );
frec->count = ctrlFlowGraph.size( ) - frec->first;
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( [&](){
T_StringBuilder sb;
sb << "Starting function '" << fn << "' at "
<< ctrlFlowGraph.size( );
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;
} , LL1 );
cfgFunctions.add( fn , T_BasicBlock{ ctrlFlowGraph.size( ) } );
cNode = M_ADDNEW_( );
}
return true;
} , LL2 );
}
// 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 ) {
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_( );
@ -262,21 +265,57 @@ void T_OptData::buildControlFlowGraph(
<< 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 {
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 );
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;
}
@ -284,31 +323,33 @@ void T_OptData::buildControlFlowGraph(
// Calls also break the flow
if ( nt == A_Node::OP_CALL && !exit ) {
T_CallInstrNode& ci{ *dynamic_cast< T_CallInstrNode* >( iptr ) };
logger( [&](){
data.logger( [&](){
T_StringBuilder sb;
auto const& node{ *data.ctrlFlowGraph[ *cNode ] };
sb << "Call to " << ci.id( ) << ", block had "
<< ( M_NODE_( *cNode )->instructions
? M_NODE_( *cNode )->instructions->count
<< ( node.instructions
? node.instructions->count
: 0 )
<< " instructions";
return sb;
} , LL2 );
auto& cs{ callSites.addNew( ) };
auto& cs{ data.callSites.addNew( ) };
cs.name = ci.id( );
cs.callBlock = *cNode;
cNode = cs.retBlock = M_ADDNEW_( );
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 ) {
logger( [&](){
data.logger( [&](){
T_StringBuilder sb;
auto const& node{ *data.ctrlFlowGraph[ *cNode ] };
sb << "Case block added ("
<< ( M_NODE_( *cNode )->instructions
? M_NODE_( *cNode )->instructions->count
<< ( node.instructions
? node.instructions->count
: 0 )
<< " instructions)";
return sb;
@ -317,53 +358,62 @@ void T_OptData::buildControlFlowGraph(
} else {
stack.last( ).hasDefault = stack.last( ).hasDefault
|| ( nt == A_Node::TN_DEFAULT );
cNode = M_ADDNEW_( );
cNode = data.ctrlFlowGraph.add( M_NEWNODE_( ) );
stack.last( ).caseBlocks.add( *cNode );
}
}
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*
{
auto& cs{ callSites.addNew( ) };
cs.callBlock = CFG_ENTER;
cs.retBlock = CFG_MAINLOOP;
auto& cs{ data.callSites.addNew( ) };
cs.callBlock = T_OptData::CFG_ENTER;
cs.retBlock = T_OptData::CFG_MAINLOOP;
cs.name = "*init*";
}
{
auto& cs{ callSites.addNew( ) };
cs.callBlock = CFG_MAINLOOP;
cs.retBlock = CFG_MAINLOOP;
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 : callSites ) {
auto const* frec{ cfgFunctions.get( cs.name ) };
for ( auto const& cs : data.callSites ) {
auto const* frec{ data.cfgFunctions.get( cs.name ) };
assert( frec );
{
auto& entry{ ctrlFlowGraph[ frec->first ] };
auto& entry{ data.ctrlFlowGraph[ frec->first ] };
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 };
auto& exit{ ctrlFlowGraph[ n ] };
auto& exit{ data.ctrlFlowGraph[ n ] };
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;
int i{ 0 };
dump << "Control flow graph dump\n";
for ( auto const& p : ctrlFlowGraph ) {
for ( auto const& p : data.ctrlFlowGraph ) {
auto const& e{ *p };
dump << "\nNode " << i++ << "\n\t";
if ( e.instructions ) {
@ -398,6 +448,40 @@ void T_OptData::buildControlFlowGraph(
dump << '\n';
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 );
}

View file

@ -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