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:
parent
35d3098917
commit
ddad981055
2 changed files with 319 additions and 227 deletions
294
c-opopt.cc
294
c-opopt.cc
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue