Optimizer - Dead store elimination
Works for variables and assets. Does not cover call arguments yet.
This commit is contained in:
parent
ab279857fa
commit
ff50ba561a
4 changed files with 75 additions and 20 deletions
3
TODO
3
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
|
||||
|
|
|
@ -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
|
||||
|
|
87
c-opopt.cc
87
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 <anon>
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
@ -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 );
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue