From 61cd7719f110c45dda155e9b248ddf3b48da86bd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= <tseeker@nocternity.net>
Date: Fri, 8 Dec 2017 07:38:59 +0100
Subject: [PATCH] Optimizer - WIP UD chain construction

---
 c-opopt.cc | 245 +++++++++++++++++++++++++++++++++++++++--------------
 c-opopt.hh |   6 +-
 2 files changed, 182 insertions(+), 69 deletions(-)

diff --git a/c-opopt.cc b/c-opopt.cc
index c61de72..f474d9f 100644
--- a/c-opopt.cc
+++ b/c-opopt.cc
@@ -27,6 +27,9 @@ uint32_t opopt::ComputeHash(
 		^ ( ( oh << 29 ) | ( oh >> 3 ) );
 }
 
+constexpr uint32_t T_OptData::CFG_ENTER;
+constexpr uint32_t T_OptData::CFG_MAINLOOP;
+constexpr uint32_t T_OptData::CFG_END;
 
 /*= T_OptData - INPUT DECLARATIONS ===========================================*/
 
@@ -123,7 +126,6 @@ namespace {
 // CFG type shortcuts
 using T_CFN_ = T_OptData::T_CtrlFlowNode;
 using P_CFN_ = T_OptData::P_CtrlFlowNode;
-using RP_CFN_ = T_OptData::RP_CtrlFlowNode;
 
 // Helpers to create or re-use CFG nodes
 T_OptData::P_CtrlFlowNode BCFGNewNode_(
@@ -143,7 +145,9 @@ T_OptData::P_CtrlFlowNode BCFGNewNode_(
 
 #define M_NEWNODE_() BCFGNewNode_( old )
 #define M_ADDNEW_() \
-	ctrlFlowGraph[ ctrlFlowGraph.add( M_NEWNODE_( ) ) ].get( )
+	ctrlFlowGraph.add( M_NEWNODE_( ) )
+#define M_NODE_(i) \
+	ctrlFlowGraph[i]
 
 } // namespace <anon>
 
@@ -158,29 +162,29 @@ void T_OptData::buildControlFlowGraph(
 
 	// Create special nodes
 	M_ADDNEW_( );
-	const RP_CFN_ nMainLoop	{ M_ADDNEW_( ) };
-	const RP_CFN_ nExit	{ M_ADDNEW_( ) };
-	nMainLoop->outbound.add( nExit );
-	nExit->inbound.add( nMainLoop );
+	M_ADDNEW_( );
+	M_ADDNEW_( );
+	M_NODE_( CFG_MAINLOOP )->outbound.add( CFG_END );
+	M_NODE_( CFG_END )->inbound.add( CFG_MAINLOOP );
 
 	// Data structure to handle conditionals
 	struct T_StackEntry_ {
-		RP_CFN_ condBlock;
+		uint32_t condBlock;
 		bool hasDefault{ false };
-		T_AutoArray< RP_CFN_ , 8 > caseBlocks;
+		T_AutoArray< uint32_t , 8 > caseBlocks;
 	};
 
 	// Data structure for call sites
 	struct T_CallSite_ {
 		T_String name;
-		RP_CFN_ callBlock;
-		RP_CFN_ retBlock;
+		uint32_t callBlock;
+		uint32_t retBlock;
 	};
 
 	// Generate control flow graph for each function
 	T_AutoArray< T_StackEntry_ , 8 > stack;
 	T_Array< T_CallSite_ > callSites;
-	RP_CFN_ cNode{ nullptr };
+	T_Optional< uint32_t > cNode{ };
 	visitor.visit( program.root , [&]( A_Node& node , const bool exit ) {
 		const auto nt{ node.type( ) };
 
@@ -194,16 +198,16 @@ void T_OptData::buildControlFlowGraph(
 				logger( [&](){
 					T_StringBuilder sb;
 					sb << "Function ended; last block had "
-						<< ( cNode->instructions
-								? cNode->instructions->count
-								: 0 )
+						<< ( M_NODE_( *cNode )->instructions
+							? M_NODE_( *cNode )->instructions->count
+							: 0 )
 						<< " instructions";
 					return sb;
 				} , LL1 );
 				auto* frec{ cfgFunctions.get( fn ) };
 				assert( frec );
 				frec->count = ctrlFlowGraph.size( ) - frec->first;
-				cNode = nullptr;
+				cNode.clear( );
 			} else {
 				logger( [&](){
 					T_StringBuilder sb;
@@ -221,10 +225,11 @@ void T_OptData::buildControlFlowGraph(
 		auto* const iptr{ dynamic_cast< A_InstructionNode* >( &node ) };
 		if ( iptr && !exit ) {
 			assert( cNode );
-			if ( cNode->instructions ) {
-				cNode->instructions->count ++;
+			auto& n{ *M_NODE_( *cNode ) };
+			if ( n.instructions ) {
+				n.instructions->count ++;
 			} else {
-				cNode->instructions = T_BasicBlock{ indexOf( *iptr ) };
+				n.instructions = T_BasicBlock{ indexOf( *iptr ) };
 			}
 		}
 
@@ -238,15 +243,16 @@ void T_OptData::buildControlFlowGraph(
 				// and the next block
 				const auto ncb{ se.caseBlocks.size( ) };
 				for ( auto i = 0u ; i < ncb ; i ++ ) {
-					auto* cb{ se.caseBlocks[ i ] };
-					cb->inbound.add( se.condBlock );
-					se.condBlock->outbound.add( cb );
-					cb->outbound.add( cNode );
-					cNode->inbound.add( cb );
+					auto& cbi{ se.caseBlocks[ i ] };
+					auto& cb{ *M_NODE_( cbi ) };
+					cb.inbound.add( se.condBlock );
+					M_NODE_( se.condBlock )->outbound.add( cbi );
+					cb.outbound.add( cNode );
+					M_NODE_( *cNode )->inbound.add( cbi );
 				}
 				if ( !se.hasDefault ) {
-					cNode->inbound.add( se.condBlock );
-					se.condBlock->outbound.add( cNode );
+					M_NODE_( *cNode )->inbound.add( se.condBlock );
+					M_NODE_( se.condBlock )->outbound.add( *cNode );
 				}
 
 				stack.removeLast( );
@@ -258,16 +264,16 @@ void T_OptData::buildControlFlowGraph(
 				} , LL2 );
 			} else {
 				auto& se{ stack.addNew( ) };
-				se.condBlock = cNode;
-				cNode = nullptr;
+				se.condBlock = *cNode;
+				cNode.clear( );
 				logger( [&](){
 					T_StringBuilder sb;
 					sb << "Entering conditional instruction, stack size "
 						<< stack.size( )
 						<< ", block had "
-						<< ( se.condBlock->instructions
-								? se.condBlock->instructions->count
-								: 0 )
+						<< ( M_NODE_( se.condBlock )->instructions
+							? M_NODE_( se.condBlock )->instructions->count
+							: 0 )
 						<< " instructions";
 					return sb;
 				} , LL2 );
@@ -281,9 +287,9 @@ void T_OptData::buildControlFlowGraph(
 			logger( [&](){
 				T_StringBuilder sb;
 				sb << "Call to " << ci.id( ) << ", block had "
-					<< ( cNode->instructions
-							? cNode->instructions->count
-							: 0 )
+					<< ( M_NODE_( *cNode )->instructions
+						? M_NODE_( *cNode )->instructions->count
+						: 0 )
 					<< " instructions";
 				return sb;
 			} , LL2 );
@@ -301,13 +307,13 @@ void T_OptData::buildControlFlowGraph(
 				logger( [&](){
 					T_StringBuilder sb;
 					sb << "Case block added ("
-						<< ( cNode->instructions
-								? cNode->instructions->count
-								: 0 )
+						<< ( M_NODE_( *cNode )->instructions
+							? M_NODE_( *cNode )->instructions->count
+							: 0 )
 						<< " instructions)";
 					return sb;
 				} , LL2 );
-				cNode = nullptr;
+				cNode.clear( );
 			} else {
 				stack.last( ).hasDefault = stack.last( ).hasDefault
 					|| ( nt == A_Node::TN_DEFAULT );
@@ -323,14 +329,14 @@ void T_OptData::buildControlFlowGraph(
 	// Add fake call sites for *init* and *frame*
 	{
 		auto& cs{ callSites.addNew( ) };
-		cs.callBlock = ctrlFlowGraph[ CFG_ENTER ].get( );
-		cs.retBlock = ctrlFlowGraph[ CFG_MAINLOOP ].get( );
+		cs.callBlock = CFG_ENTER;
+		cs.retBlock = CFG_MAINLOOP;
 		cs.name = "*init*";
 	}
 	{
 		auto& cs{ callSites.addNew( ) };
-		cs.callBlock = ctrlFlowGraph[ CFG_MAINLOOP ].get( );
-		cs.retBlock = ctrlFlowGraph[ CFG_MAINLOOP ].get( );
+		cs.callBlock = CFG_MAINLOOP;
+		cs.retBlock = CFG_MAINLOOP;
 		cs.name = "*frame*";
 	}
 
@@ -341,18 +347,20 @@ void T_OptData::buildControlFlowGraph(
 		{
 			auto& entry{ ctrlFlowGraph[ frec->first ] };
 			entry->inbound.add( cs.callBlock );
-			cs.callBlock->outbound.add( entry.get( ) );
+			M_NODE_( cs.callBlock )->outbound.add( frec->first );
 		}
 		{
-			auto& exit{ ctrlFlowGraph[ frec->first + frec->count - 1 ] };
+			const auto n{ frec->first + frec->count - 1 };
+			auto& exit{ ctrlFlowGraph[ n ] };
 			exit->outbound.add( cs.retBlock );
-			cs.retBlock->inbound.add( exit.get( ) );
+			M_NODE_( cs.retBlock )->outbound.add( n );
 		}
 	}
 }
 
 #undef M_ADDNEW_
 #undef M_NEWNODE_
+#undef M_NODE_
 
 
 /*= T_OptData - USE/DEFINE CHAINS ============================================*/
@@ -552,24 +560,6 @@ void BUDCAddEntries_(
 	}
 }
 
-/*----------------------------------------------------------------------------*/
-
-struct T_VarEvt_
-{
-	enum E_VarEvt {
-		DEF , USE , KILL
-	};
-
-	uint32_t instrId;
-	E_VarEvt evt;
-	T_OptData::T_VarId var;
-
-	T_VarEvt_( uint32_t instrId , E_VarEvt evt ,
-			T_OptData::T_VarId const& var )
-		: instrId( instrId ) , evt( evt ) , var( var )
-	{ }
-};
-
 
 } // namespace <anon>
 
@@ -599,9 +589,134 @@ void T_OptData::buildUseDefineChains(
 		BUDCAddEntries_( udPerInstr , i , false , r.defines );
 	}
 
-	// For each node of the CFG graph, we need to generate a sequence that
-	// indicates what gets defined, used or killed.
-	T_MultiArray< T_VarEvt_ > cfgVarEvents;
+	// Walk the graph from the entry point until all reachable nodes
+	// have been covered and keeping track of active definitions. When
+	// the flow diverges, we need to store the state before the
+	// divergence.
+	const uint32_t nNodes{ ctrlFlowGraph.size( ) };
+	uint32_t nProcessed{ 0 };
+	uint32_t node{ CFG_ENTER };
+
+	bool processedNodes[ nNodes ];
+	memset( processedNodes , 0 , sizeof( processedNodes ) );
+
+	using T_ActDefs_ = T_Array< uint32_t >;
+	using P_ActDefs_ = T_OwnPtr< T_ActDefs_ >;
+	P_ActDefs_ activeDefs{ NewOwned< T_ActDefs_ >( ) };
+	activeDefs->resize( varUDChains.size( ) , T_HashIndex::INVALID_INDEX );
+
+	struct T_StackEntry_ {
+		P_ActDefs_ def;
+		uint32_t node;
+
+		T_StackEntry_( P_ActDefs_ const& src , const uint32_t n ) noexcept
+			: def{ NewOwned< T_ActDefs_ >( *src ) } , node( n )
+		{}
+	};
+	T_AutoArray< T_StackEntry_ , 32 > stack;
+
+	while ( nProcessed < nNodes ) {
+		assert( !processedNodes[ node ] );
+		auto const& cn{ *ctrlFlowGraph[ node ] };
+		processedNodes[ node ] = true;
+		logger( [=]() {
+			T_StringBuilder sb;
+			sb << "processing node " << node;
+			return sb;
+		} , LL2 );
+		nProcessed ++;
+
+		if ( cn.instructions ) {
+			// Check for uses and 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( ) };
+
+				// Handle uses first
+				for ( auto j = 0u ; j < nrec ; j ++ ) {
+					auto const& rec{ (*irec)[ j ] };
+					if ( !rec.isUse ) {
+						continue;
+					}
+
+					auto& resource{ varUDChains[ rec.entry ] };
+					const auto defId{ (*activeDefs)[ rec.entry ] };
+					// FIXME: must be defined
+					resource.defines[ defId ].refs.add( rec.index );
+					resource.uses[ rec.index ].refs.add( defId );
+					logger( [&](){
+						T_StringBuilder sb;
+						sb << "USE " << resource.var.name
+							<< " @ instr #" << ii
+							<< ", def " << defId;
+						return sb;
+					} , LL2 );
+				}
+
+				// Handle defines
+				for ( auto j = 0u ; j < nrec ; j ++ ) {
+					auto const& rec{ (*irec)[ j ] };
+					if ( rec.isUse ) {
+						continue;
+					}
+					(*activeDefs)[ rec.entry ] = rec.index;
+					logger( [&](){
+						T_StringBuilder sb;
+						sb << "DEF " << rec.index
+							<< varUDChains[ rec.entry ].var.name
+							<< " @ instr #" << ii;
+						return sb;
+					} , LL2 );
+				}
+			}
+		}
+
+		if ( nProcessed == nNodes ) {
+			break;
+		}
+
+		// Check for possible next nodes
+		//	-> no output nodes left -> pop stack and keep trying,
+		//		unless we've already processed all nodes
+		// 	-> if there's only one, use it and don't push to stack
+		// 	-> otherwise push to stack, use first available node
+		do {
+			auto const& rcn{ *ctrlFlowGraph[ node ] };
+			const uint32_t nSuccs{ [&](){
+				const auto no{ rcn.outbound.size( ) };
+				uint32_t c{ 0 };
+				for ( auto i = 0u ; i < no ; i ++ ) {
+					if ( !processedNodes[ rcn.outbound[ i ] ] ) {
+						c ++;
+					}
+				}
+				return c;
+			}() };
+			logger( [=]() {
+				T_StringBuilder sb;
+				sb << "node " << node << ": " << nSuccs
+					<< " successor(s) left";
+				return sb;
+			} , LL2 );
+
+			if ( nSuccs == 0 ) {
+				assert( !stack.empty( ) );
+				node = stack.last( ).node;
+				activeDefs = std::move( stack.last( ).def );
+				stack.removeLast( );
+				continue;
+			}
+
+			// TODO cases in which there is one or more successors
+		} while ( 0 ); // XXX
+
+		// 30 if next block is an end of function, kill locals
+	}
 }
 
 
diff --git a/c-opopt.hh b/c-opopt.hh
index 2603497..649f8af 100644
--- a/c-opopt.hh
+++ b/c-opopt.hh
@@ -67,13 +67,11 @@ struct T_OptData
 	};
 
 	// Control flow graph node
-	struct T_CtrlFlowNode;
-	using RP_CtrlFlowNode = T_CtrlFlowNode*;
 	struct T_CtrlFlowNode
 	{
 		T_Optional< T_BasicBlock > instructions;
-		T_AutoArray< RP_CtrlFlowNode , 16 > inbound;
-		T_AutoArray< RP_CtrlFlowNode , 16 > outbound;
+		T_AutoArray< uint32_t , 16 > inbound;
+		T_AutoArray< uint32_t , 16 > outbound;
 	};
 	using P_CtrlFlowNode = T_OwnPtr< T_CtrlFlowNode >;