Optimizer - Started work on function inlining

For now:
* Option that triggers inlining
* Compute list of inline-able functions
This commit is contained in:
Emmanuel BENOîT 2017-12-17 15:59:29 +01:00
parent fd84c3cae3
commit 94b01e62b4
7 changed files with 117 additions and 10 deletions

View file

@ -140,6 +140,8 @@ T_SRDParserConfig BCLInitDefinitions_( ) noexcept
<< M_BCL_TOGGLE_( constantPropagation ) )
<< ( Rule( ) << "dead-code-elimination" << Enum( "on-off" )
<< M_BCL_TOGGLE_( deadCodeElimination ) )
<< ( Rule( ) << "function-inlining" << Enum( "on-off" )
<< M_BCL_TOGGLE_( inlineFunctions ) )
;
defs.context( "opt-cf" )

View file

@ -34,6 +34,10 @@ struct T_BuildConfiguration
// Dead code elimination -----------------------------------------------
bool deadCodeElimination{ false };
// Function inlining ---------------------------------------------------
bool inlineFunctions{ false };
};
// Table of avaiable build configurations

View file

@ -1823,3 +1823,93 @@ bool opopt::RemoveDeadCode(
}
return didStuff;
}
/*= FUNCTION INLINING ========================================================*/
namespace {
uint32_t IFFindFunctionForNode_(
T_OptData const& data ,
const uint32_t cfgNode
) noexcept
{
const auto ncf{ data.cfgFunctions.size( ) };
for ( auto i = 0u ; i < ncf ; i ++ ) {
auto const& block{ data.cfgFunctions[ i ] };
if ( block.first <= cfgNode && block.first + block.count > cfgNode ) {
return i;
}
}
return T_HashIndex::INVALID_INDEX;
}
} // namespace <anon>
/*----------------------------------------------------------------------------*/
bool opopt::InlineFunctions(
T_OpsParserOutput& program ,
T_OptData& oData ) noexcept
{
oData.buildControlFlowGraph( program );
M_LOGSTR_( "... Inlining functions" , 3 );
// Find functions that can be inlined
auto const& cfg{ oData.ctrlFlowGraph };
for ( auto it = cfg.begin( ) ; it != cfg.end( ) ; it ++ ) {
auto const& node{ **it };
// We need a single inbound edge, with CALL type
if ( node.inbound.size( ) != 1 ) {
continue;
}
if ( node.inbound[ 0 ].type != T_OptData::T_CtrlFlowEdge::CALL ) {
continue;
}
// Find the function we're in and the function the call came from
const auto calleeId{ IFFindFunctionForNode_(
oData , it.pos( ) ) };
const auto callerId{ IFFindFunctionForNode_(
oData , node.inbound[ 0 ].target ) };
// Ignore recursive functions or fake calls to *init*/*frame*
if ( callerId == calleeId || callerId == T_HashIndex::INVALID_INDEX ) {
continue;
}
// Find corresponding call instruction
auto const& callNode{ *oData.ctrlFlowGraph[ node.inbound[ 0 ].target ] };
assert( callNode.instructions );
const uint32_t callInstrIdx{ callNode.instructions->first
+ callNode.instructions->count - 1 };
auto const* const instr{ oData.instructions[ callInstrIdx ].node };
assert( instr && dynamic_cast< T_CallInstrNode const* >( instr ) );
// Check call arguments
auto const& cInstr{ dynamic_cast< T_CallInstrNode const& >( *instr ) };
bool ok{ true };
for ( auto i = 0u ; i < cInstr.size( ) && ok ; i ++ ) {
auto const& arg{ cInstr.argument( i ) };
const auto aet{ arg.expression( ).type( ) };
ok = ( aet == A_Node::EXPR_CONST || aet == A_Node::EXPR_ID );
}
if ( !ok ) {
continue;
}
// Add to inlineable functions list
// TODO
oData.logger( [&](){
T_StringBuilder sb;
sb << "Will inline function "
<< program.root.function( calleeId ).name( );
return sb;
} , 4 );
}
// Order inline-able functions
// Compute extra locals required
// Merge (bottom to top)
return false;
}

View file

@ -217,11 +217,18 @@ bool PropagateConstants(
T_OptData& optData ) noexcept;
// Attempt to remove blocks of code that will not be executed because of
// constant conditions.
// constant conditions, dead stores, etc...
//
bool RemoveDeadCode(
T_OpsParserOutput& program ,
T_OptData& optData ) noexcept;
// Attempt to inline functions that are only used once and which have simple
// argument values (constants or identifiers).
//
bool InlineFunctions(
T_OpsParserOutput& program ,
T_OptData& optData ) noexcept;
} // namespace opopt

View file

@ -249,19 +249,22 @@ int main( int argc , char** argv )
od.curves = &p.curves;
}
bool doneStuff;
bool didStuff;
do {
doneStuff = false;
didStuff = false;
if ( cfg.constantFolding && opopt::FoldConstants( *parsed , od ) ) {
doneStuff = true;
didStuff = true;
}
if ( cfg.constantPropagation && opopt::PropagateConstants( *parsed , od ) ) {
doneStuff = true;
didStuff = true;
}
if ( cfg.deadCodeElimination && opopt::RemoveDeadCode( *parsed , od ) ) {
doneStuff = true;
didStuff = true;
}
} while ( doneStuff );
if ( cfg.inlineFunctions && opopt::InlineFunctions( *parsed , od ) ) {
didStuff = true;
}
} while ( didStuff );
}
// Compile

View file

@ -9,6 +9,7 @@
)
(constant-propagation on)
(dead-code-elimination on)
(function-inlining on)
)
)

View file

@ -362,9 +362,9 @@
(fn bloom-downsample (target level)
(locals m w h)
(set m (inv (pow 2 $level)))
(set w (mul $vp-width $m))
(set h (mul $vp-height $m))
(set m (pow 2 $level))
(set w (div $vp-width $m))
(set h (div $vp-height $m))
(use-pipeline pl-bloom-downsample)
(use-framebuffer target)