From 87b46284ab9946aec649b06ef7743af113a57bc7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= <tseeker@nocternity.net>
Date: Sun, 12 Nov 2017 17:01:13 +0100
Subject: [PATCH] Compiler - Progress on bytecode generator

+ added a technical node for call arguments
---
 control.hh  |  21 ++++
 opast.cc    |   7 ++
 opast.hh    |  29 ++++-
 opcomp.cc   | 355 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 opparser.cc |   2 +-
 5 files changed, 403 insertions(+), 11 deletions(-)

diff --git a/control.hh b/control.hh
index 9c9cf1a..396200c 100644
--- a/control.hh
+++ b/control.hh
@@ -10,9 +10,30 @@ enum E_OpType
 {
 	OP_END ,
 	//
+	OP_CALL ,
+	OP_RET ,
+	OP_COND_JUMP ,
+	//
+	OP_RES_STACK ,
+	OP_PUSH ,
+	OP_POP ,
+	//
+	OP_LOAD ,
+	OP_SLOAD ,
+	//
 	OP_FP_LOAD ,
+	OP_FP_STORE ,
+	OP_FP_SLOAD ,
+	OP_FP_SSTORE ,
+	OP_FP_SSTORE_INT ,
 	//
 	OP_FP_CMP ,
+	OP_FP_ADD ,
+	OP_FP_SUB ,
+	OP_FP_MUL ,
+	OP_FP_DIV ,
+	OP_FP_NEG ,
+	OP_FP_INV ,
 };
 
 struct T_Op
diff --git a/opast.cc b/opast.cc
index 462e48f..567d71f 100644
--- a/opast.cc
+++ b/opast.cc
@@ -124,6 +124,13 @@ A_Node* opast::ASTVisitorBrowser(
 		}
 		break;
 	    }
+	    case A_Node::TN_ARG:
+	    {
+		if ( child == 0 ) {
+			return &( (T_CallInstrNode::T_Argument&) node ).expression( );
+		}
+		break;
+	    }
 
 	    // Clear instruction
 	    case A_Node::OP_CLEAR:
diff --git a/opast.hh b/opast.hh
index 4c2320a..4aec7f2 100644
--- a/opast.hh
+++ b/opast.hh
@@ -66,6 +66,7 @@ class A_Node
 		TN_CONDITION ,	// Expression for a conditional instruction
 		TN_CASE ,	// Valued case for a conditional instruction
 		TN_DEFAULT ,	// Default case for a conditional instruction
+		TN_ARG ,	// Call argument
 	};
 
     private:
@@ -249,6 +250,8 @@ class A_FuncNode : public A_Node
 			T_String const& name ,
 			T_SRDLocation const& location ) noexcept;
 
+	uint32_t locals( ) const noexcept
+		{ return locals_.size( ); }
 	bool hasLocal( T_String const& id ) const noexcept
 		{ return locals_.contains( id ); }
 
@@ -373,10 +376,29 @@ class T_FuncNode : public A_FuncNode
 // Function call
 class T_CallInstrNode : public A_InstructionNode
 {
+    public:
+	class T_Argument : public A_Node
+	{
+	    private:
+		P_ExpressionNode expr_;
+
+	    public:
+		T_Argument( T_CallInstrNode& parent ,
+				P_ExpressionNode expr ) noexcept
+			: A_Node( TN_ARG , &parent ) ,
+				expr_( std::move( expr ) )
+		{ }
+
+		A_ExpressionNode& expression( ) const noexcept
+			{ return *expr_; }
+		bool isIdentifier( ) const noexcept
+			{ return expr_->type( ) == EXPR_ID; }
+	};
+
     private:
 	T_String id_;
 	T_SRDLocation idLocation_;
-	T_AutoArray< P_ExpressionNode , 8 > arguments_;
+	T_AutoArray< T_OwnPtr< T_Argument > , 8 > arguments_;
 
     public:
 	T_CallInstrNode( T_InstrListNode& parent ,
@@ -389,7 +411,8 @@ class T_CallInstrNode : public A_InstructionNode
 	void addArgument( P_ExpressionNode expr ) noexcept
 	{
 		if ( expr ) {
-			arguments_.add( std::move( expr ) );
+			arguments_.add( NewOwned< T_Argument >(
+					*this , std::move( expr ) ) );
 		}
 	}
 
@@ -400,7 +423,7 @@ class T_CallInstrNode : public A_InstructionNode
 
 	uint32_t arguments( ) const noexcept
 		{ return arguments_.size( ); }
-	A_ExpressionNode& argument( const uint32_t index ) const noexcept
+	T_Argument& argument( const uint32_t index ) const noexcept
 		{ return *arguments_[ index ]; }
 };
 
diff --git a/opcomp.cc b/opcomp.cc
index 0d93df4..4405798 100644
--- a/opcomp.cc
+++ b/opcomp.cc
@@ -12,8 +12,13 @@ namespace {
 
 struct T_CompilerImpl_
 {
+	P_OpProgram compile(
+			T_ParserOutput const& input ) noexcept;
+
+    private:
 	T_Visitor< A_Node > astVisitor{ ASTVisitorBrowser };
 	T_Set< uint32_t > constants{ UseTag< IndexBacked< > >( ) };
+	T_KeyValueTable< T_String , uint32_t > locations;
 
 	T_ParserOutput* input;
 	P_OpProgram output;
@@ -22,16 +27,29 @@ struct T_CompilerImpl_
 		 fiPipelines , fiPrograms ,
 		 fiSamplers , fiTextures;
 
-	P_OpProgram compile(
-			T_ParserOutput const& input ) noexcept;
+	uint32_t sdMain , sdFPU;
+	T_AutoArray< uint32_t , 32 > condJumps;
 
-    private:
 	void gatherConstants( ) noexcept;
 	void countAssets( ) noexcept;
 
 	bool compileNode( uint32_t funcIndex ,
 			A_Node& node ,
 			bool exit ) noexcept;
+	void processFunction(
+			bool exit ,
+			uint32_t args ,
+			uint32_t lvars ,
+			T_SRDLocation const& location ) noexcept;
+	bool processIdentifier(
+			uint32_t funcIndex ,
+			T_IdentifierExprNode const& node ) noexcept;
+
+	void addInstruction(
+			E_OpType op ,
+			T_SRDLocation const& location ,
+			uint32_t arg0 = 0 ,
+			uint32_t arg1 = 0 ) noexcept;
 };
 
 
@@ -56,6 +74,9 @@ P_OpProgram T_CompilerImpl_::compile(
 #endif
 
 	// Compile each function
+#ifdef INVASIVE_TRACES
+	uint32_t nInstr = 0;
+#endif
 	uint32_t cfi;
 	for ( cfi = 0u ; cfi < input->root.nFunctions( ) ; cfi ++ ) {
 		output->ops.next( );
@@ -64,11 +85,19 @@ P_OpProgram T_CompilerImpl_::compile(
 		printf( "compiling function %s\n" ,
 				func.name( ).toOSString( ).data( ) );
 #endif
+		sdMain = sdFPU = 0;
 		astVisitor.visit( func ,
 			[=]( A_Node& node , const bool exit ) -> bool {
 				return compileNode( cfi , node , exit );
 			} );
+#ifdef INVASIVE_TRACES
+		printf( "\t%d instructions generated\n" , output->ops.sizeOf( cfi ) );
+		nInstr += output->ops.sizeOf( cfi );
+#endif
 	}
+#ifdef INVASIVE_TRACES
+	printf( "total %d instructions\n" , nInstr );
+#endif
 
 	return std::move( output );
 }
@@ -80,9 +109,7 @@ void T_CompilerImpl_::gatherConstants( ) noexcept
 		if ( exit && node.type( ) == A_Node::EXPR_CONST ) {
 			T_OpValue value;
 			value.f = dynamic_cast< T_ConstantExprNode& >( node ).floatValue( );
-			if ( value.f != 0 && value.f != 1 ) {
-				constants.add( value.u );
-			}
+			constants.add( value.u );
 		}
 		return true;
 	} );
@@ -103,26 +130,38 @@ void T_CompilerImpl_::gatherConstants( ) noexcept
 
 void T_CompilerImpl_::countAssets( ) noexcept
 {
+	locations.clear( );
+	locations.add( T_String::Pooled( "time" ) , 0u );
+	locations.add( T_String::Pooled( "width" ) , 1u );
+	locations.add( T_String::Pooled( "height" ) , 2u );
+
 	auto const nt{ input->types.size( ) };
 	for ( auto i = 0u ; i < nt ; i ++ ) {
 		const auto t{ input->types.values( )[ i ] };
+		auto const& n{ input->types.keys( )[ i ] };
 		switch ( t ) {
 		    case E_DataType::FRAMEBUFFER:
+			locations.add( n , output->nFramebuffers );
 			output->nFramebuffers ++;
 			break;
 		    case E_DataType::PIPELINE:
+			locations.add( n , output->nPipelines );
 			output->nPipelines ++;
 			break;
 		    case E_DataType::PROGRAM:
+			locations.add( n , output->nPrograms );
 			output->nPrograms ++;
 			break;
 		    case E_DataType::SAMPLER:
+			locations.add( n , output->nSamplers );
 			output->nSamplers ++;
 			break;
 		    case E_DataType::TEXTURE:
+			locations.add( n , output->nTextures );
 			output->nTextures ++;
 			break;
 		    case E_DataType::VARIABLE:
+			locations.add( n , output->nVariables + 3 );
 			output->nVariables ++;
 			break;
 
@@ -145,6 +184,29 @@ void T_CompilerImpl_::countAssets( ) noexcept
 	fiSamplers = fiPrograms + output->nPrograms;
 	fiTextures = fiSamplers + output->nSamplers;
 
+	for ( auto i = 0u ; i < nt ; i ++ ) {
+		const auto li{ locations.indexOf( input->types.keys( )[ i ] ) };
+		if ( li == T_HashIndex::INVALID_INDEX ) {
+			continue;
+		}
+
+		const auto t{ input->types.values( )[ i ] };
+		auto& pos{ locations[ li ] };
+		switch ( t ) {
+		    case E_DataType::FRAMEBUFFER: pos += fiFramebuffers; break;
+		    case E_DataType::PIPELINE: pos += fiPipelines; break;
+		    case E_DataType::PROGRAM: pos += fiPrograms; break;
+		    case E_DataType::SAMPLER: pos += fiSamplers; break;
+		    case E_DataType::TEXTURE: pos += fiTextures; break;
+
+		    case E_DataType::VARIABLE:
+		    case E_DataType::INPUT:
+		    case E_DataType::BUILTIN:
+		    case E_DataType::UNKNOWN:
+			break;
+		}
+	}
+
 #ifdef INVASIVE_TRACES
 	printf( "assets\n\t%d framebuffers\n\t%d pipelines\n"
 			"\t%d programs\n\t%d samplers\n\t%d textures\n"
@@ -165,6 +227,26 @@ void T_CompilerImpl_::countAssets( ) noexcept
 		fiPipelines - 1 , fiPipelines , fiPrograms - 1 , fiPrograms ,
 		fiSamplers - 1 , fiSamplers , fiTextures - 1 , fiTextures ,
 		fiTextures + output->nTextures - 1 );
+
+	T_Array< uint32_t > indices( locations.size( ) );
+	indices.ensureCapacity( locations.size( ) );
+	for ( auto i = 0u ; i < locations.size( ) ; i ++ ) {
+		indices.add( i );
+	}
+	indices.sort( [this]( uint32_t a , uint32_t b ) {
+		return T_Comparator< uint32_t >::compare(
+				locations.values( )[ a ] ,
+				locations.values( )[ b ] );
+	} );
+	T_StringBuilder lmap;
+	lmap << "location map (constants not included)\n";
+	for ( auto idx : indices ) {
+		lmap << '\t' << locations.values( )[ idx ]
+			<< '\t' << locations.keys( )[ idx ]
+			<< '\n';
+	}
+	lmap << '\0';
+	printf( "%s" , lmap.data( ) );
 #endif
 }
 
@@ -173,7 +255,266 @@ bool T_CompilerImpl_::compileNode(
 		A_Node& node ,
 		const bool exit ) noexcept
 {
-	return false;
+	switch ( node.type( ) ) {
+
+	    case A_Node::ROOT:
+		fprintf( stderr , "Internal error: root node found during compilation\n" );
+		std::abort( );
+		break;
+
+	    case A_Node::DECL_FN:
+	    {
+		T_FuncNode& fn( (T_FuncNode&) node );
+		const auto args( fn.arguments( ) );
+		processFunction( exit , args , fn.locals( ) - args , node.location( ) );
+		break;
+	    }
+
+	    case A_Node::DECL_INIT:
+	    case A_Node::DECL_FRAME:
+	    {
+		A_FuncNode& fn( (A_FuncNode&) node );
+		processFunction( exit , 0 , fn.locals( ) , node.location( ) );
+		break;
+	    }
+
+	    case A_Node::OP_CALL:
+		if ( exit ) {
+			auto& call( (T_CallInstrNode&) node );
+			const auto fi( input->root.functionIndex( call.id( ) ) );
+			assert( fi >= 0 );
+			auto& callee( input->root.function( fi ) );
+			assert( callee.type( ) == A_Node::DECL_FN );
+			auto& fcallee( (T_FuncNode&) callee );
+			const auto args( fcallee.arguments( ) );
+			assert( sdMain > args );
+			addInstruction( OP_CALL , node.location( ) , fi );
+			sdMain -= args;
+		}
+		break;
+	    case A_Node::TN_ARG:
+	    {
+		auto& n( (T_CallInstrNode::T_Argument&)node );
+		if ( n.isIdentifier( ) && !exit ) {
+			const bool main{ processIdentifier( funcIndex ,
+					(T_IdentifierExprNode&)  n.expression( ) ) };
+			if ( !main ) {
+				addInstruction( OP_PUSH , node.location( ) );
+				addInstruction( OP_FP_SSTORE , node.location( ) , 0 );
+				sdMain ++;
+				sdFPU --;
+			}
+			return false;
+		} else if ( exit && !n.isIdentifier( ) ) {
+			addInstruction( OP_PUSH , node.location( ) );
+			addInstruction( OP_FP_SSTORE , node.location( ) , 0 );
+			sdMain ++;
+			sdFPU --;
+		}
+		break;
+	    }
+
+	    case A_Node::OP_SET:
+		if ( exit ) {
+			assert( sdFPU > 0 );
+			auto& id( ((T_SetInstrNode&)node).id( ) );
+			auto& func{ input->root.function( funcIndex ) };
+			if ( func.hasLocal( id ) ) {
+				const auto pos( func.getLocalIndex( id ) + 1 );
+				addInstruction( OP_FP_SSTORE , node.location( ) ,
+						sdMain - pos - 1 );
+			} else {
+				addInstruction( OP_FP_STORE , node.location( ) ,
+						*locations.get( ((T_SetInstrNode&)node).id( ) ) );
+			}
+			sdFPU --;
+		}
+		break;
+
+	    case A_Node::OP_COND:
+		if ( exit ) {
+			assert( sdMain > 0 );
+			addInstruction( OP_POP , node.location( ) , 0 );
+			sdMain --;
+		}
+		break;
+
+	    case A_Node::TN_CONDITION:
+		if ( exit ) {
+			addInstruction( OP_PUSH , node.location( ) );
+			addInstruction( OP_FP_SSTORE_INT , node.location( ) , 0 );
+			sdFPU --;
+			sdMain ++;
+		}
+		break;
+
+	    case A_Node::TN_CASE:
+		if ( exit ) {
+			const auto cpos( output->ops.sizeOf( funcIndex ) );
+			const auto diff( cpos - condJumps.last( ) );
+			output->ops.get( funcIndex , condJumps.last( ) ).arg1 = diff - 1;
+			condJumps.removeLast( );
+#ifdef INVASIVE_TRACES
+			printf( "\tCOND JUMP UPDATED: %d\n" , diff - 1 );
+#endif
+		} else {
+			auto& c( (T_CondInstrNode::T_ValuedCase&) node );
+			condJumps.add( output->ops.sizeOf( funcIndex ) );
+			addInstruction( OP_COND_JUMP , node.location( ) ,
+					c.value( ) , 0 );
+		}
+		break;
+
+	    case A_Node::EXPR_CMP_EQ: case A_Node::EXPR_CMP_NE:
+	    case A_Node::EXPR_CMP_GT: case A_Node::EXPR_CMP_GE:
+	    case A_Node::EXPR_CMP_LT: case A_Node::EXPR_CMP_LE:
+		if ( exit ) {
+			const uint32_t op( dynamic_cast< T_BinaryOperatorNode& >( node ).op( )
+					- T_BinaryOperatorNode::CMP_EQ );
+			addInstruction( OP_FP_CMP , node.location( ) , op );
+		}
+		break;
+
+	    case A_Node::EXPR_ADD:
+		if ( exit ) {
+			assert( sdFPU > 2 );
+			addInstruction( OP_FP_ADD , node.location( ) );
+			sdFPU --;
+		}
+		break;
+	    case A_Node::EXPR_SUB:
+		if ( exit ) {
+			assert( sdFPU > 2 );
+			addInstruction( OP_FP_SUB , node.location( ) );
+			sdFPU --;
+		}
+		break;
+	    case A_Node::EXPR_MUL:
+		if ( exit ) {
+			assert( sdFPU > 2 );
+			addInstruction( OP_FP_MUL , node.location( ) );
+			sdFPU --;
+		}
+		break;
+	    case A_Node::EXPR_DIV:
+		if ( exit ) {
+			assert( sdFPU > 2 );
+			addInstruction( OP_FP_DIV , node.location( ) );
+			sdFPU --;
+		}
+		break;
+
+	    case A_Node::EXPR_CONST:
+		if ( !exit ) {
+			T_OpValue value;
+			value.f = dynamic_cast< T_ConstantExprNode& >( node ).floatValue( );
+			addInstruction( OP_FP_LOAD , node.location( ) ,
+					constants.indexOf( value.u ) + 3 );
+			sdFPU ++;
+		}
+		break;
+
+	    case A_Node::EXPR_ID:
+		if ( !exit ) {
+			processIdentifier( funcIndex ,
+					dynamic_cast< T_IdentifierExprNode& >( node ) );
+		}
+		break;
+	}
+
+	return true;
+}
+
+void T_CompilerImpl_::processFunction(
+		const bool exit ,
+		const uint32_t args ,
+		const uint32_t lvars ,
+		T_SRDLocation const& location ) noexcept
+{
+	if ( exit ) {
+		assert( sdMain == args + lvars + 1 );
+		if ( lvars ) {
+			addInstruction( OP_POP , location , lvars - 1 );
+			sdMain -= lvars;
+		}
+		sdMain -= 1 + args;
+		addInstruction( OP_RET , location , args );
+		assert( sdMain == 0 );
+	} else {
+		if ( lvars ) {
+			addInstruction( OP_RES_STACK , location , lvars - 1 );
+			sdMain += lvars;
+		}
+		sdMain += 1 + args;
+	}
+}
+
+// Returns true if the identifier caused a push to the main stack, false
+// if it pushed to the FPU stack.
+bool T_CompilerImpl_::processIdentifier(
+		const uint32_t funcIndex ,
+		T_IdentifierExprNode const& node ) noexcept
+{
+	auto& func{ input->root.function( funcIndex ) };
+	if ( func.hasLocal( node.id( ) ) ) {
+		const E_DataType dt{ func.getLocalType( node.id( ) ) };
+		assert( dt != E_DataType::UNKNOWN );
+
+		uint32_t stackPos;
+		if ( func.isArgument( node.id( ) ) ) {
+			auto const& fn( (T_FuncNode&) func );
+			const auto nArgs( fn.arguments( ) );
+			stackPos = nArgs - 1 - func.getLocalIndex( node.id( ) );
+		} else {
+			stackPos = func.getLocalIndex( node.id( ) ) + 1;
+		}
+		assert( stackPos < sdMain );
+
+		const auto p( sdMain - ( stackPos + 1 ) );
+		if ( dt == E_DataType::VARIABLE ) {
+			addInstruction( OP_FP_SLOAD , node.location( ) , p );
+			sdFPU ++;
+			return false;
+		}
+
+		addInstruction( OP_SLOAD , node.location( ) , p );
+		addInstruction( OP_PUSH , node.location( ) );
+		sdMain ++;
+		return true;
+	}
+
+	assert( input->types.contains( node.id( ) ) );
+	const E_DataType dt{ *( input->types.get( node.id( ) ) ) };
+	assert( dt != E_DataType::UNKNOWN );
+	assert( locations.contains( node.id( ) ) );
+	if ( dt == E_DataType::VARIABLE || dt == E_DataType::BUILTIN ) {
+		addInstruction( OP_FP_LOAD , node.location( ) ,
+				*locations.get( node.id( ) ) );
+		sdFPU ++;
+		return false;
+	}
+
+	addInstruction( OP_LOAD , node.location( ) ,
+			*locations.get( node.id( ) ) );
+	addInstruction( OP_PUSH , node.location( ) );
+	sdMain ++;
+	return true;
+}
+
+
+void T_CompilerImpl_::addInstruction(
+		const E_OpType op ,
+		T_SRDLocation const& location ,
+		const uint32_t arg0 ,
+		const uint32_t arg1 ) noexcept
+{
+#ifdef INVASIVE_TRACES
+	T_StringBuilder tracer;
+	tracer << "\t+I( " << int( op ) << " ; " << arg0 << " ; "
+		<< arg1 << " ) @ " << location << '\n' << '\0';
+	printf( "%s" , tracer.data( ) );
+#endif
+	output->ops.addNew( op , location , arg0 , arg1 );
 }
 
 }
diff --git a/opparser.cc b/opparser.cc
index eca1e5f..ce456c8 100644
--- a/opparser.cc
+++ b/opparser.cc
@@ -648,7 +648,7 @@ bool T_ParserImpl_::checkArgumentTypes( ) noexcept
 
 				bool ok = true;
 				for ( auto a = 0u ; a < call->arguments( ) ; a ++ ) {
-					auto& arg( call->argument( a ) );
+					auto& arg( call->argument( a ).expression( ) );
 					E_DataType ndt{ E_DataType::UNKNOWN };
 					if ( arg.type( ) == A_Node::EXPR_ID ) {
 						auto const& idn( dynamic_cast< T_IdentifierExprNode& >( arg ) );