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) \
|
#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 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue