diff --git a/c-opopt.cc b/c-opopt.cc index 569c02c..a22a831 100644 --- a/c-opopt.cc +++ b/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 +/*----------------------------------------------------------------------------*/ + 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; diff --git a/c-opopt.hh b/c-opopt.hh index a052637..2603497 100644 --- a/c-opopt.hh +++ b/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.