From ff50ba561aa13a69030cd18c6d3ab2a7c5c52342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= Date: Sun, 17 Dec 2017 09:33:45 +0100 Subject: [PATCH] Optimizer - Dead store elimination Works for variables and assets. Does not cover call arguments yet. --- TODO | 3 +- c-opast.cc | 2 +- c-opopt.cc | 87 ++++++++++++++++++++++++++++++++++++++++++++---------- c-opopt.hh | 3 +- 4 files changed, 75 insertions(+), 20 deletions(-) diff --git a/TODO b/TODO index c94bd3e..da15ec9 100644 --- a/TODO +++ b/TODO @@ -17,9 +17,8 @@ Scripting: * Dead code elimination * Local variables * Unused arguments - * Dead store + * Dead store: call arguments * Unused functions - * Branches * Common subexpressions * Strength reduction * Loop-invariant code motion diff --git a/c-opast.cc b/c-opast.cc index 788bfa9..1e3bae8 100644 --- a/c-opast.cc +++ b/c-opast.cc @@ -465,7 +465,7 @@ void T_InstrListNode::replaceMultiple( } assert( oldInstr ); - if ( replacement ) { + if ( replacement && replacement->size( ) ) { auto& rep{ *replacement }; // Move existing children to the end of the list diff --git a/c-opopt.cc b/c-opopt.cc index b7355c3..80cf7d4 100644 --- a/c-opopt.cc +++ b/c-opopt.cc @@ -1490,18 +1490,6 @@ bool opopt::PropagateConstants( T_OpsParserOutput& program , T_OptData& oData ) noexcept { - // We need to follow the general execution flow of the program. This is - // not as straightforward as it seems. - // - Handling locals is rather easy as they "die" at the end of - // the function in which they are defined. - // - Handling variables in init and functions that are called only - // from init is not too hard: once a variable is set to a constant, it - // can be substituted until the next set instruction. - // - Other variables need additional checks before propagating. - // For example, if a variable is set to a constant during init, but is - // updated at the end of the frame function, the value cannot be - // propagated. - oData.buildUseDefineChains( program ); M_LOGSTR_( "... Propagating constants" , 2 ); @@ -1559,6 +1547,16 @@ bool opopt::PropagateConstants( CPReplaceWithConstant_( *oData.instructions[ use.node ].node , udc.var , *repVal , oData ); + + for ( auto j = 0u ; j < nRefs ; j ++ ) { + auto& def{ udc.defines[ use.refs[ j ] ] }; + def.refs.remove( def.refs.indexOf( i ) ); + for ( auto k = 0u ; k < def.refs.size( ) ; k ++ ) { + if ( def.refs[ k ] > i ) { + def.refs[ k ] --; + } + } + } udc.uses.removeSwap( i ); changesMade = true; } @@ -1652,6 +1650,58 @@ bool RDCConditionals_( return true; } +/*----------------------------------------------------------------------------*/ + +bool RDCDeadStores_( + T_OpsParserOutput& program , + T_OptData& oData + ) noexcept +{ + M_LOGSTR_( "...... Removing dead stores" , 3 ); + oData.buildUseDefineChains( program ); + + T_AutoArray< A_InstructionNode* , 32 > deadSets; + for ( auto& udc : oData.varUDChains.values( ) ) { + const auto nDefs{ udc.defines.size( ) }; + for ( auto i = 0u ; i < nDefs ; i ++ ) { + auto const& def{ udc.defines[ i ] }; + if ( !def.refs.size( ) ) { + deadSets.add( oData.instructions[ def.node ].node ); + } + } + } + if ( deadSets.empty( ) ) { + return false; + } + oData.logger( [&](){ + T_StringBuilder sb; + sb << "Found " << deadSets.size( ) << " dead store candidates"; + return sb; + } , 5 ); + + const auto nds{ deadSets.size( ) }; + bool changesMade{ false }; + for ( auto i = 0u ; i < nds ; i ++ ) { + auto* const instr{ deadSets[ i ] }; + if ( instr->type( ) != A_Node::OP_CALL ) { + oData.logger( [&](){ + T_StringBuilder sb; + sb << "Removing dead store at " + << instr->location( ); + return sb; + } , 4 ); + ( (T_InstrListNode&) instr->parent( ) ).replaceMultiple( + *instr , nullptr ); + changesMade = true; + } + } + + if ( changesMade ) { + oData.state = {}; + } + return changesMade; +} + } // namespace /*----------------------------------------------------------------------------*/ @@ -1660,14 +1710,21 @@ bool opopt::RemoveDeadCode( T_OpsParserOutput& program , T_OptData& oData ) noexcept { -// oData.buildUseDefineChains( program ); M_LOGSTR_( "... Eliminating dead code" , 2 ); bool didStuff{ false }; -// bool tryDeadStoresFirst{ oData.state & T_OptData::E_StateItem::UDCHAINS }; + bool tryDeadStoresFirst{ oData.state & T_OptData::E_StateItem::UDCHAINS }; bool didStuffThisTime; do { - didStuffThisTime = RDCConditionals_( program , oData ); + if ( tryDeadStoresFirst ) { + tryDeadStoresFirst = false; + didStuff = RDCDeadStores_( program , oData ); + didStuffThisTime = true; + continue; + } + + didStuffThisTime = RDCConditionals_( program , oData ) + || RDCDeadStores_( program , oData ); didStuff = didStuff || didStuffThisTime; } while ( didStuffThisTime ); diff --git a/c-opopt.hh b/c-opopt.hh index 9830deb..126b9f5 100644 --- a/c-opopt.hh +++ b/c-opopt.hh @@ -169,8 +169,7 @@ struct T_OptData { uint32_t node; // Instruction that uses or sets it uint32_t fnIndex; // Function in which the use/define is located - ebcl::T_Set< uint32_t > refs{ // Corresponding uses/defines - ebcl::UseTag< ebcl::ArrayBacked< 16 > >( ) }; + T_AutoArray< uint32_t , 16 > refs; // Corresponding uses/defines }; // Use/define chains for a variable