Optimizer - Dead store elimination

Works for variables and assets. Does not cover call arguments yet.
This commit is contained in:
Emmanuel BENOîT 2017-12-17 09:33:45 +01:00
parent ab279857fa
commit ff50ba561a
4 changed files with 75 additions and 20 deletions

3
TODO
View file

@ -17,9 +17,8 @@ Scripting:
* Dead code elimination * Dead code elimination
* Local variables * Local variables
* Unused arguments * Unused arguments
* Dead store * Dead store: call arguments
* Unused functions * Unused functions
* Branches
* Common subexpressions * Common subexpressions
* Strength reduction * Strength reduction
* Loop-invariant code motion * Loop-invariant code motion

View file

@ -465,7 +465,7 @@ void T_InstrListNode::replaceMultiple(
} }
assert( oldInstr ); assert( oldInstr );
if ( replacement ) { if ( replacement && replacement->size( ) ) {
auto& rep{ *replacement }; auto& rep{ *replacement };
// Move existing children to the end of the list // Move existing children to the end of the list

View file

@ -1490,18 +1490,6 @@ bool opopt::PropagateConstants(
T_OpsParserOutput& program , T_OpsParserOutput& program ,
T_OptData& oData ) noexcept 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 ); oData.buildUseDefineChains( program );
M_LOGSTR_( "... Propagating constants" , 2 ); M_LOGSTR_( "... Propagating constants" , 2 );
@ -1559,6 +1547,16 @@ bool opopt::PropagateConstants(
CPReplaceWithConstant_( *oData.instructions[ use.node ].node , CPReplaceWithConstant_( *oData.instructions[ use.node ].node ,
udc.var , *repVal , oData ); 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 ); udc.uses.removeSwap( i );
changesMade = true; changesMade = true;
} }
@ -1652,6 +1650,58 @@ bool RDCConditionals_(
return true; 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 <anon> } // namespace <anon>
/*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/
@ -1660,14 +1710,21 @@ bool opopt::RemoveDeadCode(
T_OpsParserOutput& program , T_OpsParserOutput& program ,
T_OptData& oData ) noexcept T_OptData& oData ) noexcept
{ {
// oData.buildUseDefineChains( program );
M_LOGSTR_( "... Eliminating dead code" , 2 ); M_LOGSTR_( "... Eliminating dead code" , 2 );
bool didStuff{ false }; bool didStuff{ false };
// bool tryDeadStoresFirst{ oData.state & T_OptData::E_StateItem::UDCHAINS }; bool tryDeadStoresFirst{ oData.state & T_OptData::E_StateItem::UDCHAINS };
bool didStuffThisTime; bool didStuffThisTime;
do { 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; didStuff = didStuff || didStuffThisTime;
} while ( didStuffThisTime ); } while ( didStuffThisTime );

View file

@ -169,8 +169,7 @@ struct T_OptData
{ {
uint32_t node; // Instruction that uses or sets it uint32_t node; // Instruction that uses or sets it
uint32_t fnIndex; // Function in which the use/define is located uint32_t fnIndex; // Function in which the use/define is located
ebcl::T_Set< uint32_t > refs{ // Corresponding uses/defines T_AutoArray< uint32_t , 16 > refs; // Corresponding uses/defines
ebcl::UseTag< ebcl::ArrayBacked< 16 > >( ) };
}; };
// Use/define chains for a variable // Use/define chains for a variable