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 ) \
|
||||
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 ===========================================*/
|
||||
|
||||
|
@ -57,7 +70,7 @@ bool ODNIVisitor_(
|
|||
dynamic_cast< A_InstructionNode* >( &node ) };
|
||||
if ( iptr && !exit ) {
|
||||
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.instructions.add( T_OptData::T_InstrPos{
|
||||
oData.instructions.size( ) , iptr ,
|
||||
|
@ -87,7 +100,7 @@ void T_OptData::numberInstructions(
|
|||
uint32_t T_OptData::indexOf(
|
||||
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 ) };
|
||||
while ( existing != T_HashIndex::INVALID_INDEX ) {
|
||||
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 {
|
||||
|
||||
#warning Remove this later
|
||||
#define LL1 1
|
||||
#define LL2 1
|
||||
#define LL1 2
|
||||
#define LL2 2
|
||||
|
||||
// CFG type shortcuts
|
||||
using T_CFN_ = T_OptData::T_CtrlFlowNode;
|
||||
using P_CFN_ = T_OptData::P_CtrlFlowNode;
|
||||
using RP_CFN_ = T_OptData::RP_CtrlFlowNode;
|
||||
|
||||
// Helpers to create or re-use CFG nodes
|
||||
T_OptData::P_CtrlFlowNode BCFGNewNode_(
|
||||
T_Array< P_CFN_ >& pool ) noexcept
|
||||
{
|
||||
|
@ -132,6 +147,8 @@ T_OptData::P_CtrlFlowNode BCFGNewNode_(
|
|||
|
||||
} // namespace <anon>
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
void T_OptData::buildControlFlowGraph(
|
||||
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.buildControlFlowGraph( program );
|
||||
oData.buildUseDefineChains( program );
|
||||
M_LOGSTR_( "... Propagating constants" , 2 );
|
||||
|
||||
return false;
|
||||
|
|
76
c-opopt.hh
76
c-opopt.hh
|
@ -85,11 +85,87 @@ struct T_OptData
|
|||
// Control flow graph
|
||||
T_Array< P_CtrlFlowNode > ctrlFlowGraph;
|
||||
T_KeyValueTable< T_String , T_BasicBlock > cfgFunctions;
|
||||
|
||||
// Build the control flow graph. Instruction numbering must be
|
||||
// up-to-date before calling this.
|
||||
void buildControlFlowGraph(
|
||||
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 =================================================*/
|
||||
// All functions below return true if transformations were made, false if not.
|
||||
|
|
Loading…
Reference in a new issue