Optimizer - Gather variable/resource uses

First step to build the UD chains.
This commit is contained in:
Emmanuel BENOîT 2017-12-03 23:15:44 +01:00
parent b6f9d06be0
commit f1ec2cf18c
2 changed files with 202 additions and 5 deletions

View file

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

View file

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