diff --git a/c-opopt.cc b/c-opopt.cc index 1fff950..42e5328 100644 --- a/c-opopt.cc +++ b/c-opopt.cc @@ -761,6 +761,265 @@ void BUDCLink_( } , LL2 ); } +/*----------------------------------------------------------------------------*/ + +struct T_BUDCDefSet_ +{ + T_Set< uint32_t > set{ UseTag< ArrayBacked< 4 > >( ) }; + T_AutoArray< uint8_t , 1 > bitmap; + + explicit T_BUDCDefSet_( const uint32_t max ) noexcept + { + const auto nBytesRaw{ max >> 3 }; + const auto nBytes{ nBytesRaw + ( ( max & 7 ) != 0 ? 1 : 0 ) }; + for ( auto i = 0u ; i < nBytes ; i ++ ) { + bitmap.add( 0 ); + } + } + + void clear( ) noexcept + { + set.clear( ); + for ( auto i = 0u ; i < bitmap.size( ) ; i ++ ) { + bitmap[ i ] = 0; + } + } + + void add( const uint32_t item ) noexcept + { + const auto byte{ item >> 3 }; + const auto bit{ item & 7 }; + const auto mask{ 1 << bit }; + if ( ( bitmap[ byte ] & mask ) == 0 ) { + bitmap[ byte ] |= mask; + set.add( item ); + } + } + + bool operator ==( T_BUDCDefSet_ const& other ) noexcept + { + if ( bitmap.size( ) != other.bitmap.size( ) ) { + return false; + } + for ( auto i = 0u ; i < bitmap.size( ) ; i ++ ) { + if ( bitmap[ i ] != other.bitmap[ i ] ) { + return false; + } + } + return true; + } +}; + +void BUDCWalkGraph_( + T_OptData& data , + T_OptData::T_VarUseDefine& var , + T_UDEPerInstr_& udPerInstr , + T_Optional< uint32_t > fnIndex + ) noexcept +{ + T_Set< uint32_t > changed{ UseTag< ArrayBacked< 16 > >( ) }; // FIXME alloc + T_Array< T_BUDCDefSet_ > defines; // FIXME alloc + defines.resize( data.ctrlFlowGraph.size( ) , T_BUDCDefSet_{ 0 } ); + + data.logger( [&]() { + T_StringBuilder sb; + sb << "Walking graph for " << var.var.name << " ("; + switch ( var.var.type ) { + case T_OptData::E_UDVarType::GLOBAL: + sb << "global"; + break; + case T_OptData::E_UDVarType::ARGUMENT: + sb << "argument of " << var.var.owner; + break; + case T_OptData::E_UDVarType::LOCAL: + sb << "local variable of " << var.var.owner; + break; + } + sb << ')'; + return sb; + } , LL1 ); + + auto const* const fn{ fnIndex + ? &data.cfgFunctions[ *fnIndex ] + : nullptr }; + auto const nFirst{ fn ? fn->first : 0u }; + auto const nEnd{ fn ? ( fn->first + fn->count ) + : data.ctrlFlowGraph.size( ) }; +#if 0 + data.logger( [=](){ + T_StringBuilder sb{ "Nodes " }; + sb << nFirst << " ... " << ( nEnd - 1 );; + return sb; + } , LL2 ); +#endif + changed.add( nFirst ); + for ( auto i = nEnd ; i > nFirst + 1 ; i -- ) { + changed.add( i - 1 ); + } + + while ( changed.size( ) ) { + const auto node{ changed[ 0 ] }; +#if 0 + data.logger( [=](){ + T_StringBuilder sb{ "Checking node " }; + sb << node; + return sb; + } , LL2 ); +#endif + assert( node >= nFirst && node < nEnd ); + auto const& cn{ *data.ctrlFlowGraph[ node ] }; + changed.remove( node ); + + // Assemble the set of possible definitions at the start of + // the block. + T_BUDCDefSet_ sInput{ var.defines.size( ) }; + const auto nIn{ cn.inbound.size( ) }; + for ( auto i = 0u ; i < nIn ; i ++ ) { + auto const& ib{ cn.inbound[ i ] }; + // Make sure the inbound link type is right (FLOW always + // is, BYPASS works in functions, CALL/RET at the global + // level) + if ( ib.type != ib.FLOW && ( + ( fnIndex && ib.type != ib.BYPASS ) + || ( !fnIndex && ib.type == ib.BYPASS ) ) ) { + continue; + } + + // Get defines from node + auto const& sNode{ defines[ ib.target ] }; + const auto nIbIn{ sNode.set.size( ) }; + for ( auto j = 0u ; j < nIbIn ; j ++ ) { + sInput.add( sNode.set[ j ] ); + } + } + + if ( cn.instructions ) { + // Check for defines in the instructions + const auto is{ cn.instructions->first }; + const auto ie{ is + cn.instructions->count }; + for ( auto ii = is ; ii < ie ; ii ++ ) { + auto const* const irec{ udPerInstr.get( ii ) }; + if ( !irec ) { + continue; + } + const auto nrec{ irec->size( ) }; + + for ( auto j = 0u ; j < nrec ; j ++ ) { + auto const& rec{ (*irec)[ j ] }; + if ( rec.isUse || data.varUDChains[ rec.entry ].var != var.var ) { + continue; + } + sInput.clear( ); + sInput.add( rec.index ); + } + } + } + + auto& sOut{ defines[ node ] }; + if ( sOut == sInput ) { + continue; + } +#if 0 + data.logger( [&](){ + T_StringBuilder sb; + sb << "Node " << node << ", setting output to {"; + for ( auto i = 0u ; i < sInput.set.size( ) ; i ++ ) { + if ( i ) { + sb << " ,"; + } + sb << ' ' << sInput.set[ i ]; + } + sb << " }"; + return sb; + } , LL2 ); +#endif + sOut = sInput; + + // Add the node's successors into the changed set + const auto nOut{ cn.outbound.size( ) }; + for ( auto i = 0u ; i < nOut ; i ++ ) { + auto const& ob{ cn.outbound[ i ] }; + // Make sure the outbound link type is right (FLOW + // always is, BYPASS works in functions, CALL/RET at + // the global level) + if ( ob.type != ob.FLOW && ( + ( fnIndex && ob.type != ob.BYPASS ) + || ( !fnIndex && ob.type == ob.BYPASS ) ) ) { + continue; + } + changed.add( ob.target ); + } + } + + // Now we need to identify the blocks which contain var uses + T_Set< uint32_t > blocks{ UseTag< ArrayBacked< 16 > >( ) }; // FIXME alloc + for ( auto i = 0u ; i < var.uses.size( ) ; i ++ ) { + const auto useInstr{ var.uses[ i ].node }; + for ( auto j = nFirst ; j < nEnd ; j ++ ) { + auto const& block{ *data.ctrlFlowGraph[ j ] }; + if ( !block.instructions ) { + continue; + } + const auto f{ block.instructions->first }; + const auto e{ block.instructions->count + f - 1 }; + if ( f <= useInstr && e >= useInstr ) { + blocks.add( j ); + break; + } + } + } + + // Now go through all the blocks instruction by instruction, updating + // the active definition set if a definition is found, and associating + // defines to uses. + for ( auto i = 0u ; i < blocks.size( ) ; i ++ ) { + const auto bid{ blocks[ i ] }; + auto const& cb{ *data.ctrlFlowGraph[ bid ] }; + T_BUDCDefSet_ defs{ defines[ bid ] }; + assert( cb.instructions ); + + const auto is{ cb.instructions->first }; + const auto ie{ is + cb.instructions->count }; + for ( auto ii = is ; ii < ie ; ii ++ ) { + auto const* const irec{ udPerInstr.get( ii ) }; + if ( !irec ) { + continue; + } + const auto nrec{ irec->size( ) }; + + // Handle uses first + for ( auto j = 0u ; j < nrec ; j ++ ) { + auto const& rec{ (*irec)[ j ] }; + auto const& ud{ data.varUDChains[ rec.entry ] }; + if ( !rec.isUse || ud.var != var.var ) { + continue; + } + for ( auto di = 0u ; di < defs.set.size( ) ; di ++ ) { + BUDCLink_( var , defs.set[ di ] , + rec.index , data.logger ); + } + if ( !defs.set.size( ) ) { + // FIXME: add warning + BUDCLink_( var , T_HashIndex::INVALID_INDEX , + rec.index , data.logger ); + } + } + + // Update the set + for ( auto j = 0u ; j < nrec ; j ++ ) { + auto const& rec{ (*irec)[ j ] }; + auto const& ud{ data.varUDChains[ rec.entry ] }; + if ( rec.isUse || ud.var != var.var ) { + continue; + } + defs.clear( ); + defs.add( rec.index ); + break; + } + } + } +} + } // namespace @@ -828,7 +1087,7 @@ void T_OptData::buildUseDefineChains( for ( auto& sym : varUDChains.values( ) ) { switch ( sym.var.type ) { case T_OptData::E_UDVarType::GLOBAL: -#warning TODO + BUDCWalkGraph_( *this , sym , udPerInstr , {} ); break; case T_OptData::E_UDVarType::ARGUMENT: { @@ -842,9 +1101,12 @@ void T_OptData::buildUseDefineChains( break; } - case T_OptData::E_UDVarType::LOCAL: -#warning TODO + case T_OptData::E_UDVarType::LOCAL: { + assert( cfgFunctions.contains( sym.var.owner ) ); + BUDCWalkGraph_( *this , sym , udPerInstr , + cfgFunctions.indexOf( sym.var.owner ) ); break; + } } }