diff --git a/c-buildcfg.cc b/c-buildcfg.cc index 947f95f..c641fcb 100644 --- a/c-buildcfg.cc +++ b/c-buildcfg.cc @@ -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" ) diff --git a/c-buildcfg.hh b/c-buildcfg.hh index a6ca6aa..654df85 100644 --- a/c-buildcfg.hh +++ b/c-buildcfg.hh @@ -34,6 +34,10 @@ struct T_BuildConfiguration // Dead code elimination ----------------------------------------------- bool deadCodeElimination{ false }; + + // Function inlining --------------------------------------------------- + + bool inlineFunctions{ false }; }; // Table of avaiable build configurations diff --git a/c-opopt.cc b/c-opopt.cc index 9c1078c..4f514f3 100644 --- a/c-opopt.cc +++ b/c-opopt.cc @@ -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 +/*----------------------------------------------------------------------------*/ + +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; +} diff --git a/c-opopt.hh b/c-opopt.hh index 126b9f5..3383938 100644 --- a/c-opopt.hh +++ b/c-opopt.hh @@ -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 diff --git a/m-builder.cc b/m-builder.cc index 2664a30..0273929 100644 --- a/m-builder.cc +++ b/m-builder.cc @@ -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 diff --git a/test/build.srd b/test/build.srd index 221bc78..05488c7 100644 --- a/test/build.srd +++ b/test/build.srd @@ -9,6 +9,7 @@ ) (constant-propagation on) (dead-code-elimination on) + (function-inlining on) ) ) diff --git a/test/demo.srd b/test/demo.srd index 358f820..1ccbad3 100644 --- a/test/demo.srd +++ b/test/demo.srd @@ -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)