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
|
* 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
|
||||||
|
|
|
@ -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
|
||||||
|
|
87
c-opopt.cc
87
c-opopt.cc
|
@ -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 );
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue