Optimizer - Gather variable/resource uses
First step to build the UD chains.
This commit is contained in:
parent
b6f9d06be0
commit
f1ec2cf18c
2 changed files with 202 additions and 5 deletions
131
c-opopt.cc
131
c-opopt.cc
|
@ -14,6 +14,19 @@ using namespace opopt;
|
||||||
#define M_LOGSTR_( S , L ) \
|
#define M_LOGSTR_( S , L ) \
|
||||||
logger( [](){ return T_StringBuilder{ S }; } , L )
|
logger( [](){ return T_StringBuilder{ S }; } , L )
|
||||||
|
|
||||||
|
uint32_t opopt::ComputeHash(
|
||||||
|
T_OptData::T_VarId const& id ) noexcept
|
||||||
|
{
|
||||||
|
const uint32_t nh{ ComputeHash( id.name ) };
|
||||||
|
const uint32_t oh{ id.type != T_OptData::E_UDVarType::GLOBAL
|
||||||
|
? ComputeHash( id.name )
|
||||||
|
: 0
|
||||||
|
};
|
||||||
|
return ( uint32_t( id.type ) << 8 )
|
||||||
|
^ nh
|
||||||
|
^ ( ( oh << 29 ) | ( oh >> 3 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*= T_OptData - INPUT DECLARATIONS ===========================================*/
|
/*= T_OptData - INPUT DECLARATIONS ===========================================*/
|
||||||
|
|
||||||
|
@ -57,7 +70,7 @@ bool ODNIVisitor_(
|
||||||
dynamic_cast< A_InstructionNode* >( &node ) };
|
dynamic_cast< A_InstructionNode* >( &node ) };
|
||||||
if ( iptr && !exit ) {
|
if ( iptr && !exit ) {
|
||||||
auto const& il{ dynamic_cast< T_InstrListNode& >( iptr->parent( ) ) };
|
auto const& il{ dynamic_cast< T_InstrListNode& >( iptr->parent( ) ) };
|
||||||
const auto hash{ ComputeHash( (uint64_t)iptr ) };
|
const auto hash{ ebcl::ComputeHash( (uint64_t)iptr ) };
|
||||||
oData.instrIndex.add( hash );
|
oData.instrIndex.add( hash );
|
||||||
oData.instructions.add( T_OptData::T_InstrPos{
|
oData.instructions.add( T_OptData::T_InstrPos{
|
||||||
oData.instructions.size( ) , iptr ,
|
oData.instructions.size( ) , iptr ,
|
||||||
|
@ -87,7 +100,7 @@ void T_OptData::numberInstructions(
|
||||||
uint32_t T_OptData::indexOf(
|
uint32_t T_OptData::indexOf(
|
||||||
opast::A_InstructionNode const& instr ) noexcept
|
opast::A_InstructionNode const& instr ) noexcept
|
||||||
{
|
{
|
||||||
const auto hash{ ComputeHash( (uint64_t)&instr ) };
|
const auto hash{ ebcl::ComputeHash( (uint64_t)&instr ) };
|
||||||
uint32_t existing{ instrIndex.first( hash ) };
|
uint32_t existing{ instrIndex.first( hash ) };
|
||||||
while ( existing != T_HashIndex::INVALID_INDEX ) {
|
while ( existing != T_HashIndex::INVALID_INDEX ) {
|
||||||
if ( &instr == instructions[ existing ].node ) {
|
if ( &instr == instructions[ existing ].node ) {
|
||||||
|
@ -100,17 +113,19 @@ uint32_t T_OptData::indexOf(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*= T_OptData - CONSTROL FLOW GRAPH CONSTRUCTION =============================*/
|
/*= T_OptData - CONTROL FLOW GRAPH CONSTRUCTION ==============================*/
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
#warning Remove this later
|
#warning Remove this later
|
||||||
#define LL1 1
|
#define LL1 2
|
||||||
#define LL2 1
|
#define LL2 2
|
||||||
|
|
||||||
|
// 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;
|
using RP_CFN_ = T_OptData::RP_CtrlFlowNode;
|
||||||
|
|
||||||
|
// Helpers to create or re-use CFG nodes
|
||||||
T_OptData::P_CtrlFlowNode BCFGNewNode_(
|
T_OptData::P_CtrlFlowNode BCFGNewNode_(
|
||||||
T_Array< P_CFN_ >& pool ) noexcept
|
T_Array< P_CFN_ >& pool ) noexcept
|
||||||
{
|
{
|
||||||
|
@ -132,6 +147,8 @@ T_OptData::P_CtrlFlowNode BCFGNewNode_(
|
||||||
|
|
||||||
} // namespace <anon>
|
} // namespace <anon>
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
void T_OptData::buildControlFlowGraph(
|
void T_OptData::buildControlFlowGraph(
|
||||||
T_OpsParserOutput& program ) noexcept
|
T_OpsParserOutput& program ) noexcept
|
||||||
{
|
{
|
||||||
|
@ -334,6 +351,109 @@ void T_OptData::buildControlFlowGraph(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef M_ADDNEW_
|
||||||
|
#undef M_NEWNODE_
|
||||||
|
|
||||||
|
|
||||||
|
/*= T_OptData - USE/DEFINE CHAINS ============================================*/
|
||||||
|
|
||||||
|
#warning Remove this later
|
||||||
|
#undef LL1
|
||||||
|
#undef LL2
|
||||||
|
#define LL1 1
|
||||||
|
#define LL2 1
|
||||||
|
|
||||||
|
void T_OptData::buildUseDefineChains(
|
||||||
|
T_OpsParserOutput& program ) noexcept
|
||||||
|
{
|
||||||
|
M_LOGSTR_( "Building use/define chains" , LL1 );
|
||||||
|
varUDChains.clear( );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to locate all variable uses. For each use, we will go
|
||||||
|
* backwards in the graph, until one of the following becomes true:
|
||||||
|
* - we find an instruction that sets the variable;
|
||||||
|
* - we find the initial block due to cycling back;
|
||||||
|
* - we reached the CFG_ENTER node.
|
||||||
|
* In the latter case, it's an error - the variable is never set.
|
||||||
|
*
|
||||||
|
* FIXME: Nope, actually that won't work. We need to find (set)
|
||||||
|
* instuctions, calls, and also resource initialisations as well
|
||||||
|
* when visiting the tree; *then* we can associate records from
|
||||||
|
* both lists.
|
||||||
|
*
|
||||||
|
* Note: for function arguments, most of this can be avoided by simply
|
||||||
|
* looking at the call sites.
|
||||||
|
*/
|
||||||
|
visitor.visit( program.root , [&]( auto& n , const bool exit ) {
|
||||||
|
if ( n.type( ) != A_Node::EXPR_ID || exit ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find instruction and function index
|
||||||
|
auto& nId{ dynamic_cast< T_IdentifierExprNode& >( n ) };
|
||||||
|
A_FuncNode* func{ nullptr };
|
||||||
|
T_Optional< uint32_t > instrId;
|
||||||
|
A_Node* pn{ &nId };
|
||||||
|
while ( pn ) {
|
||||||
|
auto* const asInstr{ dynamic_cast< A_InstructionNode* >( pn ) };
|
||||||
|
func = dynamic_cast< A_FuncNode* >( pn );
|
||||||
|
if ( !instrId && asInstr ) {
|
||||||
|
instrId = indexOf( *asInstr );
|
||||||
|
} else if ( func ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pn = &pn->parent( );
|
||||||
|
}
|
||||||
|
assert( func && instrId );
|
||||||
|
|
||||||
|
// Generate the identifier
|
||||||
|
const T_VarId varId{ [&]() {
|
||||||
|
auto const& n{ nId.id( ) };
|
||||||
|
if ( func->hasLocal( nId.id( ) ) ) {
|
||||||
|
return T_VarId{ n , func->name( ) ,
|
||||||
|
func->isArgument( nId.id( ) ) };
|
||||||
|
}
|
||||||
|
return T_VarId{ n };
|
||||||
|
} () };
|
||||||
|
|
||||||
|
// Access or create the record
|
||||||
|
auto* const varRec{ [&]() {
|
||||||
|
auto* const x{ varUDChains.get( varId ) };
|
||||||
|
if ( x ) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
varUDChains.add( T_VarUseDefine{ varId } );
|
||||||
|
return varUDChains.get( varId );
|
||||||
|
} () };
|
||||||
|
assert( varRec );
|
||||||
|
|
||||||
|
// Add use record
|
||||||
|
auto& useRec{ varRec->uses.addNew( ) };
|
||||||
|
useRec.node = *instrId;
|
||||||
|
useRec.fnIndex = program.root.functionIndex( func->name( ) );
|
||||||
|
|
||||||
|
logger( [&](){
|
||||||
|
T_StringBuilder sb;
|
||||||
|
sb << "use " << varId.name << " at " << nId.location( ) << " (";
|
||||||
|
if ( varId.type == E_UDVarType::GLOBAL ) {
|
||||||
|
sb << "global";
|
||||||
|
} else {
|
||||||
|
if ( varId.type == E_UDVarType::LOCAL ) {
|
||||||
|
sb << "local";
|
||||||
|
} else {
|
||||||
|
sb << "argument";
|
||||||
|
}
|
||||||
|
sb << " of " << varId.owner;
|
||||||
|
}
|
||||||
|
sb << ')';
|
||||||
|
return sb;
|
||||||
|
} , LL2 );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*============================================================================*/
|
/*============================================================================*/
|
||||||
|
|
||||||
|
@ -716,6 +836,7 @@ bool opopt::PropagateConstants(
|
||||||
|
|
||||||
oData.numberInstructions( program );
|
oData.numberInstructions( program );
|
||||||
oData.buildControlFlowGraph( program );
|
oData.buildControlFlowGraph( program );
|
||||||
|
oData.buildUseDefineChains( program );
|
||||||
M_LOGSTR_( "... Propagating constants" , 2 );
|
M_LOGSTR_( "... Propagating constants" , 2 );
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
76
c-opopt.hh
76
c-opopt.hh
|
@ -85,11 +85,87 @@ struct T_OptData
|
||||||
// Control flow graph
|
// Control flow graph
|
||||||
T_Array< P_CtrlFlowNode > ctrlFlowGraph;
|
T_Array< P_CtrlFlowNode > ctrlFlowGraph;
|
||||||
T_KeyValueTable< T_String , T_BasicBlock > cfgFunctions;
|
T_KeyValueTable< T_String , T_BasicBlock > cfgFunctions;
|
||||||
|
|
||||||
|
// Build the control flow graph. Instruction numbering must be
|
||||||
|
// up-to-date before calling this.
|
||||||
void buildControlFlowGraph(
|
void buildControlFlowGraph(
|
||||||
T_OpsParserOutput& program ) noexcept;
|
T_OpsParserOutput& program ) noexcept;
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Type of variables used for use/define chains
|
||||||
|
enum class E_UDVarType {
|
||||||
|
GLOBAL ,
|
||||||
|
LOCAL ,
|
||||||
|
ARGUMENT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Variable identifier for the U/D chains
|
||||||
|
struct T_VarId
|
||||||
|
{
|
||||||
|
E_UDVarType type; // Var type
|
||||||
|
T_String name; // Var name
|
||||||
|
T_String owner; // Function name (empty for globals)
|
||||||
|
|
||||||
|
explicit T_VarId( T_String const& name ) noexcept
|
||||||
|
: type{ E_UDVarType::GLOBAL } , name{ name } ,
|
||||||
|
owner{ }
|
||||||
|
{ }
|
||||||
|
|
||||||
|
T_VarId( T_String const& name ,
|
||||||
|
T_String const& owner ,
|
||||||
|
const bool isArgument ) noexcept
|
||||||
|
: type{ isArgument ? E_UDVarType::ARGUMENT : E_UDVarType::LOCAL } ,
|
||||||
|
name{ name } , owner{ owner }
|
||||||
|
{ assert( owner ); }
|
||||||
|
|
||||||
|
bool operator ==( T_VarId const& other ) const noexcept
|
||||||
|
{
|
||||||
|
return type == other.type && name == other.name
|
||||||
|
&& owner == other.owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator !=( T_VarId const& other ) const noexcept
|
||||||
|
{
|
||||||
|
return type != other.type || name != other.name
|
||||||
|
|| owner != other.owner;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use/define chain data
|
||||||
|
struct T_VarUDRecord
|
||||||
|
{
|
||||||
|
uint32_t node; // Instruction that uses or sets it
|
||||||
|
uint32_t fnIndex; // Function in which the use/define is located
|
||||||
|
T_AutoArray< uint32_t , 16 > refs; // Corresponding uses/defines
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use/define chains for a variable
|
||||||
|
struct T_VarUseDefine
|
||||||
|
{
|
||||||
|
T_VarId var;
|
||||||
|
T_AutoArray< T_VarUDRecord , 16 > uses;
|
||||||
|
T_AutoArray< T_VarUDRecord , 4> defines;
|
||||||
|
|
||||||
|
explicit T_VarUseDefine( T_VarId const& id )
|
||||||
|
: var{ id }
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use/define chains
|
||||||
|
T_ObjectTable< T_VarId , T_VarUseDefine > varUDChains{
|
||||||
|
[]( auto const& var ) -> T_VarId {
|
||||||
|
return var.var;
|
||||||
|
} };
|
||||||
|
|
||||||
|
// Build the use/define chains. The control flow graph must be up to
|
||||||
|
// date before calling this.
|
||||||
|
void buildUseDefineChains(
|
||||||
|
T_OpsParserOutput& program ) noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t ComputeHash( T_OptData::T_VarId const& id ) noexcept;
|
||||||
|
|
||||||
|
|
||||||
/*= INDIVIDUAL OPTIMISATIONS =================================================*/
|
/*= INDIVIDUAL OPTIMISATIONS =================================================*/
|
||||||
// All functions below return true if transformations were made, false if not.
|
// All functions below return true if transformations were made, false if not.
|
||||||
|
|
Loading…
Reference in a new issue