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 ) );