Emmanuel BENOîT
3de255aad0
The program is loaded and its init part is run. Many bugs were fixed in the process, including various new bugs in the shaders manager.
895 lines
26 KiB
C++
895 lines
26 KiB
C++
#include "externals.hh"
|
|
#include "control.hh"
|
|
#include <ebcl/Algorithms.hh>
|
|
|
|
#define INVASIVE_TRACES
|
|
|
|
using namespace ebcl;
|
|
using namespace ops;
|
|
using namespace opast;
|
|
|
|
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;
|
|
|
|
uint32_t fiVariables , fiFramebuffers ,
|
|
fiPipelines , fiPrograms ,
|
|
fiSamplers , fiTextures;
|
|
|
|
uint32_t sdMain , sdFPU;
|
|
|
|
/*
|
|
* Conditionals look something like this:
|
|
*
|
|
* 01 { compute expression and push it }
|
|
* 02 skip-cond value-for-case-1 2 { 5 - 2 - 1 }
|
|
* 03 { case 1 code }
|
|
* 04 skip 6 { 11 - 4 - 1 }
|
|
* 05 skip-cond value-for-case-2 3 { 9 - 5 - 1 }
|
|
* 06 { case 2 code }
|
|
* 07 { case 2 code }
|
|
* 08 skip 2 { 11 - 8 - 1 }
|
|
* 09 { default case code }
|
|
* 10 { default case code }
|
|
* 11 pop 1
|
|
*
|
|
* So, in order to adjust the skips once we know about them,
|
|
* we need to store the location of the previous skip-cond,
|
|
* and the locations of all end-of-case skips.
|
|
*/
|
|
struct T_CondInfo_ {
|
|
uint32_t prevCase;
|
|
T_AutoArray< uint32_t , 16 > caseEnds;
|
|
};
|
|
T_AutoArray< T_CondInfo_ , 16 > condJumps;
|
|
|
|
uint32_t fbCurrent; // Current framebuffer attachment
|
|
|
|
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;
|
|
bool processIdentifier(
|
|
uint32_t funcIndex ,
|
|
T_String const& id ,
|
|
T_SRDLocation const& location ) noexcept;
|
|
|
|
void addInstruction(
|
|
E_OpType op ,
|
|
T_SRDLocation const& location ) noexcept;
|
|
void addInstruction(
|
|
E_OpType op ,
|
|
uint32_t arg0 ,
|
|
T_SRDLocation const& location ) noexcept;
|
|
void addInstruction(
|
|
E_OpType op ,
|
|
std::initializer_list< uint32_t > args ,
|
|
T_SRDLocation const& location ) noexcept;
|
|
void applyStackEffects(
|
|
T_Op const& op ) noexcept;
|
|
};
|
|
|
|
|
|
P_OpProgram T_CompilerImpl_::compile(
|
|
T_ParserOutput const& in ) noexcept
|
|
{
|
|
input = const_cast< T_ParserOutput* >( &in );
|
|
output = NewOwned< T_OpProgram >( );
|
|
|
|
// Gather all constants used in expressions, count resources
|
|
gatherConstants( );
|
|
countAssets( );
|
|
|
|
// Get function indices
|
|
// FIXME ideally we should remap functions so that init is always 0
|
|
// and frame is always 1
|
|
output->init = input->root.functionIndex( "*init*" );
|
|
output->frame = input->root.functionIndex( "*frame*" );
|
|
#ifdef INVASIVE_TRACES
|
|
printf( "function indices\n\t%d\tinit\n\t%d\tframe\n" ,
|
|
output->init , output->frame );
|
|
#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( );
|
|
auto& func( input->root.function( cfi ) );
|
|
#ifdef INVASIVE_TRACES
|
|
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
|
|
T_StringBuilder dump , temp;
|
|
for ( auto i = 0u ; i < output->ops.sizeOf( cfi ) ; i ++ ) {
|
|
temp << "(" << output->ops.get( cfi , i ) << ")";
|
|
while ( temp.length( ) < 30 ) {
|
|
temp << ' ';
|
|
}
|
|
dump << "\t\t" << temp << "{ " << output->ops.get( cfi , i ).location << " }\n";
|
|
temp.clear( );
|
|
}
|
|
dump << '\t' << output->ops.sizeOf( cfi ) << " instructions\n"
|
|
<< '\0';
|
|
printf( "%s" , dump.data( ) );
|
|
nInstr += output->ops.sizeOf( cfi );
|
|
#endif
|
|
}
|
|
#ifdef INVASIVE_TRACES
|
|
printf( "total %d instructions\n" , nInstr );
|
|
#endif
|
|
|
|
return std::move( output );
|
|
}
|
|
|
|
void T_CompilerImpl_::gatherConstants( ) noexcept
|
|
{
|
|
constants.clear( );
|
|
astVisitor.visit( input->root , [&]( A_Node& node , const bool exit ) {
|
|
if ( exit ) {
|
|
return true;
|
|
}
|
|
|
|
T_OpValue value;
|
|
if ( node.type( ) == A_Node::EXPR_CONST ) {
|
|
value.f = dynamic_cast< T_ConstantExprNode& >( node ).floatValue( );
|
|
} else if ( node.type( ) == A_Node::OP_INPUT ) {
|
|
value.f = dynamic_cast< T_InputInstrNode& >( node ).defValue( );
|
|
} else {
|
|
return true;
|
|
}
|
|
constants.add( value.u );
|
|
return true;
|
|
} );
|
|
for ( auto i = 0u ; i < constants.size( ) ; i ++ ) {
|
|
output->constants.add( constants[ i ] );
|
|
}
|
|
|
|
#ifdef INVASIVE_TRACES
|
|
printf( "%d constants\n" , constants.size( ) );
|
|
for ( auto i = 0u ; i < constants.size( ) ; i ++ ) {
|
|
printf( " %08x" , constants[ i ] );
|
|
if ( i % 4 == 3 ) {
|
|
printf( "\n" );
|
|
}
|
|
}
|
|
if ( constants.size( ) % 4 ) {
|
|
printf( "\n" );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
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;
|
|
|
|
case E_DataType::INPUT:
|
|
assert( !output->inputs.contains(
|
|
input->types.keys( )[ i ] ) );
|
|
output->inputs.add( input->types.keys( )[ i ] );
|
|
break;
|
|
|
|
case E_DataType::BUILTIN:
|
|
case E_DataType::UNKNOWN:
|
|
break;
|
|
}
|
|
}
|
|
|
|
fiVariables = 3 + constants.size( );
|
|
fiFramebuffers = fiVariables + output->nVariables;
|
|
fiPipelines = fiFramebuffers + output->nFramebuffers;
|
|
fiPrograms = fiPipelines + output->nPipelines;
|
|
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"
|
|
"\t%d variables\n\t%d inputs\n" ,
|
|
output->nFramebuffers , output->nPipelines ,
|
|
output->nPrograms , output->nSamplers ,
|
|
output->nTextures , output->nVariables ,
|
|
output->inputs.size( ) );
|
|
printf( "table ranges\n\t0\t2\tBuilt-ins\n"
|
|
"\t3\t%d\tConstants\n"
|
|
"\t%d\t%d\tVariables\n"
|
|
"\t%d\t%d\tFramebuffers\n"
|
|
"\t%d\t%d\tPipelines\n"
|
|
"\t%d\t%d\tPrograms\n"
|
|
"\t%d\t%d\tSamplers\n"
|
|
"\t%d\t%d\tTextures\n" ,
|
|
fiVariables - 1 , fiVariables , fiFramebuffers - 1 , fiFramebuffers ,
|
|
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
|
|
}
|
|
|
|
bool T_CompilerImpl_::compileNode(
|
|
const uint32_t funcIndex ,
|
|
A_Node& node ,
|
|
const bool exit ) noexcept
|
|
{
|
|
switch ( node.type( ) ) {
|
|
|
|
//- PROGRAM STRUCTURE -----------------------------------------------------------------
|
|
|
|
case A_Node::ROOT:
|
|
fprintf( stderr , "Internal error: root node found during compilation\n" );
|
|
std::abort( );
|
|
break;
|
|
|
|
case A_Node::ILIST: break;
|
|
case A_Node::OP_LOCALS: 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:
|
|
{
|
|
A_FuncNode& fn( (A_FuncNode&) node );
|
|
processFunction( exit , 0 , fn.locals( ) , node.location( ) );
|
|
if ( output->nFramebuffers ) {
|
|
addInstruction( OP_OFFSET , fiFramebuffers , node.location( ) );
|
|
addInstruction( OP_GEN_ASSETS , { 0 , output->nFramebuffers } , node.location( ) );
|
|
}
|
|
if ( output->nPipelines ) {
|
|
addInstruction( OP_OFFSET , fiPipelines , node.location( ) );
|
|
addInstruction( OP_GEN_ASSETS , { 1 , output->nPipelines } , node.location( ) );
|
|
}
|
|
if ( output->nSamplers ) {
|
|
addInstruction( OP_OFFSET , fiSamplers , node.location( ) );
|
|
addInstruction( OP_GEN_ASSETS , { 2 , output->nSamplers } , node.location( ) );
|
|
}
|
|
if ( output->nTextures ) {
|
|
addInstruction( OP_OFFSET , fiTextures , node.location( ) );
|
|
addInstruction( OP_GEN_ASSETS , { 3 , output->nTextures } , node.location( ) );
|
|
}
|
|
break;
|
|
}
|
|
|
|
case A_Node::DECL_FRAME:
|
|
{
|
|
A_FuncNode& fn( (A_FuncNode&) node );
|
|
processFunction( exit , 0 , fn.locals( ) , node.location( ) );
|
|
break;
|
|
}
|
|
|
|
|
|
//- GENERAL / FLOW CONTROL INSTRUCTIONS -----------------------------------------------
|
|
|
|
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 , fi , node.location( ) );
|
|
sdMain -= args;
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_SET:
|
|
if ( exit ) {
|
|
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 , sdMain - pos - 1 ,
|
|
node.location( ) );
|
|
} else {
|
|
addInstruction( OP_FP_STORE ,
|
|
*locations.get( ((T_SetInstrNode&)node).id( ) ) ,
|
|
node.location( ) );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_COND:
|
|
if ( exit ) {
|
|
// Update all skips
|
|
const auto cpos( output->ops.sizeOf( funcIndex ) );
|
|
auto const& skips( condJumps.last( ).caseEnds );
|
|
for ( auto i = 0u ; i < skips.size( ) ; i ++ ) {
|
|
const auto skip( skips[ i ] );
|
|
assert( output->ops.get( funcIndex , skip ).op == OP_SKIP );
|
|
output->ops.get( funcIndex , skip ).args[ 0 ] = ( cpos - skip - 1 );
|
|
}
|
|
|
|
addInstruction( OP_POP , 0 , node.location( ) );
|
|
sdMain --;
|
|
condJumps.removeLast( );
|
|
} else {
|
|
condJumps.addNew( );
|
|
}
|
|
break;
|
|
case A_Node::TN_CONDITION:
|
|
if ( exit ) {
|
|
addInstruction( OP_PUSH , node.location( ) );
|
|
addInstruction( OP_FP_SSTORE_INT , 0 , node.location( ) );
|
|
}
|
|
break;
|
|
case A_Node::TN_CASE:
|
|
if ( exit ) {
|
|
// Store a potential skip location
|
|
condJumps.last( ).caseEnds.add( output->ops.sizeOf( funcIndex ) );
|
|
|
|
// Update the initial skip
|
|
const auto cpos( output->ops.sizeOf( funcIndex ) );
|
|
const auto ppos( condJumps.last( ).prevCase );
|
|
const auto diff( cpos - ppos );
|
|
assert( diff > 1 );
|
|
output->ops.get( funcIndex , ppos ).args[ 0 ] = diff - 1;
|
|
} else {
|
|
// If there is a previous skip location, insert the skip instruction
|
|
if ( !condJumps.last( ).caseEnds.empty( ) ) {
|
|
addInstruction( OP_SKIP , 0 , node.location( ) );
|
|
const auto ppos( condJumps.last( ).prevCase );
|
|
assert( output->ops.get( funcIndex , ppos ).op == OP_COND_SKIP );
|
|
output->ops.get( funcIndex , ppos ).args[ 0 ] ++;
|
|
}
|
|
|
|
// Add the conditional skip
|
|
auto& c( (T_CondInstrNode::T_ValuedCase&) node );
|
|
condJumps.last( ).prevCase = output->ops.sizeOf( funcIndex );
|
|
addInstruction( OP_COND_SKIP , { 0 , uint32_t( c.value( ) ) } ,
|
|
node.location( ) );
|
|
}
|
|
break;
|
|
case A_Node::TN_DEFAULT:
|
|
// If there is a previous skip location, insert the skip instruction
|
|
// and update the previous conditional skip
|
|
if ( !( exit || condJumps.last( ).caseEnds.empty( ) ) ) {
|
|
addInstruction( OP_SKIP , 0 , node.location( ) );
|
|
const auto ppos( condJumps.last( ).prevCase );
|
|
assert( output->ops.get( funcIndex , ppos ).op == OP_COND_SKIP );
|
|
output->ops.get( funcIndex , ppos ).args[ 0 ] ++;
|
|
}
|
|
break;
|
|
|
|
|
|
//- ASSET DECLARATIONS ----------------------------------------------------------------
|
|
|
|
case A_Node::OP_PIPELINE:
|
|
if ( exit ) {
|
|
auto& pn( (T_PipelineInstrNode&) node );
|
|
for ( auto i = 0u ; i < pn.size( ) ; i ++ ) {
|
|
processIdentifier( funcIndex , pn.program( i ) ,
|
|
pn.pLocation( i ) );
|
|
}
|
|
processIdentifier( funcIndex , pn.id( ) , pn.location( ) );
|
|
addInstruction( OP_INIT_PIPELINE , pn.size( ) - 1 , pn.location( ) );
|
|
sdMain -= pn.size( );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_PROGRAM:
|
|
if ( exit ) {
|
|
auto& pn( (T_ProgramInstrNode&) node );
|
|
if ( !output->progNames.contains( pn.path( ) ) ) {
|
|
output->progNames.add( pn.path( ) );
|
|
}
|
|
assert( locations.contains( pn.id( ) ) );
|
|
|
|
addInstruction( OP_INIT_PROGRAM , output->progNames.indexOf( pn.path( ) ) ,
|
|
pn.location( ) );
|
|
addInstruction( OP_STORE , *locations.get( pn.id( ) ) , pn.idLocation( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_SAMPLER:
|
|
if ( exit ) {
|
|
auto& sn( (T_SamplerInstrNode&) node );
|
|
if ( !sn.minLod( ) ) {
|
|
// XXX maybe use OP_RES_STACK
|
|
addInstruction( OP_CONST , 0 , node.location( ) );
|
|
addInstruction( OP_PUSH , node.location( ) );
|
|
addInstruction( OP_PUSH , node.location( ) );
|
|
}
|
|
processIdentifier( funcIndex , sn.id( ) , sn.idLocation( ) );
|
|
addInstruction( OP_INIT_SAMPLER , {
|
|
( uint32_t( sn.sampling( ) ) << 2 )
|
|
| ( ( sn.mipmap( ) ? 1 : 0 ) << 1 )
|
|
| ( sn.mipmap( ) ? uint32_t( *sn.mipmap( ) ) : 0 ) ,
|
|
uint32_t( sn.wrapping( ) )
|
|
} , node.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_TEXTURE:
|
|
if ( exit ) {
|
|
auto& tn( (T_TextureInstrNode&) node );
|
|
const auto hasLOD( tn.lods( ) );
|
|
processIdentifier( funcIndex , tn.id( ) , tn.idLocation( ) );
|
|
addInstruction( OP_INIT_TEXTURE , {
|
|
uint32_t( tn.texType( ) ) , hasLOD ? 1u : 0u
|
|
} , tn.location( ) );
|
|
if ( hasLOD ) {
|
|
sdMain --;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_FRAMEBUFFER:
|
|
if ( !exit ) {
|
|
auto& fbn( (T_FramebufferInstrNode&) node );
|
|
processIdentifier( funcIndex , fbn.id( ) , fbn.idLocation( ) );
|
|
addInstruction( OP_USE_FRAMEBUFFER , fbn.location( ) );
|
|
fbCurrent = 0;
|
|
}
|
|
break;
|
|
case A_Node::TN_FBATT:
|
|
if ( exit ) {
|
|
auto& fan( (T_FramebufferInstrNode::T_Attachment&) node );
|
|
processIdentifier( funcIndex , fan.id( ) , fan.location( ) );
|
|
const uint32_t lod( fan.lod( ) ? 1 : 0 ) ,
|
|
id( fan.isDepth( ) ? 0 : ++fbCurrent );
|
|
addInstruction( OP_FB_ATTACH , { id , lod } , fan.location( ) );
|
|
if ( lod ) {
|
|
sdMain --;
|
|
}
|
|
if ( id ) {
|
|
addInstruction( OP_FB_TOGGLE , { id - 1 , 1 } , fan.location( ) );
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
//- STATE -----------------------------------------------------------------------------
|
|
|
|
case A_Node::OP_MAINOUT:
|
|
if ( exit ) {
|
|
addInstruction( OP_CONST , 0 , node.location( ) );
|
|
addInstruction( OP_PUSH , node.location( ) );
|
|
addInstruction( OP_USE_FRAMEBUFFER , node.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_USE_FRAMEBUFFER:
|
|
if ( exit ) {
|
|
auto& fbn( (T_UseInstrNode&) node );
|
|
processIdentifier( funcIndex , fbn.id( ) , fbn.idLocation( ) );
|
|
addInstruction( OP_USE_FRAMEBUFFER , fbn.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_USE_PIPELINE:
|
|
if ( exit ) {
|
|
auto& fbn( (T_UseInstrNode&) node );
|
|
processIdentifier( funcIndex , fbn.id( ) , fbn.idLocation( ) );
|
|
addInstruction( OP_USE_PIPELINE , fbn.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_USE_PROGRAM:
|
|
if ( exit ) {
|
|
auto& fbn( (T_UseInstrNode&) node );
|
|
processIdentifier( funcIndex , fbn.id( ) , fbn.idLocation( ) );
|
|
addInstruction( OP_USE_PROGRAM , fbn.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_USE_TEXTURE:
|
|
if ( exit ) {
|
|
auto& tn( (T_UseTextureInstrNode&) node );
|
|
processIdentifier( funcIndex , tn.samplerId( ) , tn.samplerIdLocation( ) );
|
|
processIdentifier( funcIndex , tn.id( ) , tn.idLocation( ) );
|
|
addInstruction( OP_USE_TEXTURE , tn.bank( ) , tn.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_UNIFORMS:
|
|
if ( exit ) {
|
|
auto& un( (T_UniformsInstrNode&) node );
|
|
addInstruction( OP_CONST , un.uloc( ) , un.ulocLocation( ) );
|
|
addInstruction( OP_PUSH , un.ulocLocation( ) );
|
|
processIdentifier( funcIndex , un.progId( ) , un.progIdLocation( ) );
|
|
addInstruction( OP_UNIFORMS , { un.values( ) - 1 , un.integers( ) ? 1u : 0u } ,
|
|
un.location( ) );
|
|
sdMain -= un.values( );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_VIEWPORT:
|
|
if ( exit ) {
|
|
addInstruction( OP_VIEWPORT , node.location( ) );
|
|
}
|
|
break;
|
|
|
|
|
|
//- RENDERING -------------------------------------------------------------------------
|
|
|
|
case A_Node::OP_FULLSCREEN:
|
|
if ( exit ) {
|
|
addInstruction( OP_FULLSCREEN , node.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_CLEAR:
|
|
if ( exit ) {
|
|
addInstruction( OP_CLEAR , node.location( ) );
|
|
}
|
|
break;
|
|
|
|
|
|
//- DEBUGGING / UI CONTROLS -----------------------------------------------------------
|
|
|
|
case A_Node::OP_PROFILE:
|
|
if ( exit ) {
|
|
addInstruction( OP_UI_PEXIT , node.location( ) );
|
|
} else {
|
|
auto& pn( (T_ProfileInstrNode&) node );
|
|
if ( ! output->uiStrings.contains( pn.text( ) ) ) {
|
|
output->uiStrings.add( pn.text( ) );
|
|
}
|
|
addInstruction( OP_UI_PENTER , output->uiStrings.indexOf( pn.text( ) ) ,
|
|
pn.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_INPUT:
|
|
if ( exit ) {
|
|
auto& in( (T_InputInstrNode&) node );
|
|
T_OpValue value;
|
|
value.f = in.defValue( );
|
|
addInstruction( OP_UI_INPUT_DFT , {
|
|
uint32_t( output->inputs.indexOf( in.id( ) ) ) ,
|
|
uint32_t( constants.indexOf( value.u ) + 3 ) } ,
|
|
node.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_ODBG:
|
|
if ( exit ) {
|
|
auto& n( (T_OutputDebugInstrNode&) node );
|
|
if ( ! output->uiStrings.contains( n.description( ) ) ) {
|
|
output->uiStrings.add( n.description( ) );
|
|
}
|
|
processIdentifier( funcIndex , n.texture( ) , n.textureLocation( ) );
|
|
addInstruction( OP_UI_ODBG , {
|
|
uint32_t( n.mode( ) ) ,
|
|
uint32_t( output->uiStrings.indexOf( n.description( ) ) ) } ,
|
|
node.location( ) );
|
|
}
|
|
break;
|
|
|
|
|
|
//- EXPRESSIONS - ARGUMENTS -----------------------------------------------------------
|
|
|
|
case A_Node::TN_ARG:
|
|
{
|
|
auto& n( (T_ArgumentNode&)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 , 0 , node.location( ) );
|
|
}
|
|
return false;
|
|
} else if ( exit && !n.isIdentifier( ) ) {
|
|
addInstruction( OP_PUSH , node.location( ) );
|
|
addInstruction( OP_FP_SSTORE , 0 , node.location( ) );
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
//- EXPRESSIONS - OPERATORS -----------------------------------------------------------
|
|
|
|
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 , op , node.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::EXPR_ADD: if ( exit ) { addInstruction( OP_FP_ADD , node.location( ) ); } break;
|
|
case A_Node::EXPR_SUB: if ( exit ) { addInstruction( OP_FP_SUB , node.location( ) ); } break;
|
|
case A_Node::EXPR_MUL: if ( exit ) { addInstruction( OP_FP_MUL , node.location( ) ); } break;
|
|
case A_Node::EXPR_DIV: if ( exit ) { addInstruction( OP_FP_DIV , node.location( ) ); } break;
|
|
case A_Node::EXPR_POW: if ( exit ) { addInstruction( OP_FP_POW , node.location( ) ); } break;
|
|
|
|
case A_Node::EXPR_NEG: if ( exit ) { addInstruction( OP_FP_NEG , node.location( ) ); } break;
|
|
case A_Node::EXPR_INV: if ( exit ) { addInstruction( OP_FP_INV , node.location( ) ); } break;
|
|
case A_Node::EXPR_NOT: if ( exit ) { addInstruction( OP_FP_NOT , node.location( ) ); } break;
|
|
case A_Node::EXPR_SIN: if ( exit ) { addInstruction( OP_FP_SIN , node.location( ) ); } break;
|
|
case A_Node::EXPR_COS: if ( exit ) { addInstruction( OP_FP_COS , node.location( ) ); } break;
|
|
case A_Node::EXPR_TAN: if ( exit ) { addInstruction( OP_FP_TAN , node.location( ) ); } break;
|
|
case A_Node::EXPR_SQRT: if ( exit ) { addInstruction( OP_FP_SQRT , node.location( ) ); } break;
|
|
case A_Node::EXPR_EXP: if ( exit ) { addInstruction( OP_FP_EXP , node.location( ) ); } break;
|
|
case A_Node::EXPR_LN: if ( exit ) { addInstruction( OP_FP_LN , node.location( ) ); } break;
|
|
|
|
|
|
//- EXPRESSIONS - TERMINAL NODES ------------------------------------------------------
|
|
|
|
case A_Node::EXPR_CONST:
|
|
if ( !exit ) {
|
|
T_OpValue value;
|
|
value.f = dynamic_cast< T_ConstantExprNode& >( node ).floatValue( );
|
|
addInstruction( OP_FP_LOAD ,
|
|
constants.indexOf( value.u ) + 3 + output->nVariables ,
|
|
node.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::EXPR_ID:
|
|
if ( !exit ) {
|
|
processIdentifier( funcIndex ,
|
|
dynamic_cast< T_IdentifierExprNode& >( node ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::EXPR_INPUT:
|
|
if ( !exit ) {
|
|
}
|
|
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 );
|
|
assert( sdFPU == 0 );
|
|
if ( lvars ) {
|
|
addInstruction( OP_POP , lvars - 1 , location );
|
|
sdMain -= lvars;
|
|
}
|
|
sdMain -= args;
|
|
addInstruction( OP_RET , args , location );
|
|
assert( sdMain == 0 );
|
|
} else {
|
|
if ( lvars ) {
|
|
addInstruction( OP_RES_STACK , lvars - 1 , location );
|
|
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
|
|
{
|
|
return processIdentifier( funcIndex , node.id( ) , node.location( ) );
|
|
}
|
|
|
|
bool T_CompilerImpl_::processIdentifier(
|
|
uint32_t funcIndex ,
|
|
T_String const& id ,
|
|
T_SRDLocation const& location ) noexcept
|
|
{
|
|
auto& func{ input->root.function( funcIndex ) };
|
|
if ( func.hasLocal( id ) ) {
|
|
const E_DataType dt{ func.getLocalType( id ) };
|
|
assert( dt != E_DataType::UNKNOWN );
|
|
|
|
uint32_t stackPos;
|
|
if ( func.isArgument( id ) ) {
|
|
auto const& fn( (T_FuncNode&) func );
|
|
const auto nArgs( fn.arguments( ) );
|
|
stackPos = nArgs - 1 - func.getLocalIndex( id );
|
|
} else {
|
|
stackPos = func.getLocalIndex( id ) + 1;
|
|
}
|
|
assert( stackPos < sdMain );
|
|
|
|
const auto p( sdMain - ( stackPos + 1 ) );
|
|
if ( dt == E_DataType::VARIABLE ) {
|
|
addInstruction( OP_FP_SLOAD , p , location );
|
|
return false;
|
|
}
|
|
|
|
addInstruction( OP_SLOAD , p , location );
|
|
addInstruction( OP_PUSH , location );
|
|
return true;
|
|
}
|
|
|
|
assert( input->types.contains( id ) );
|
|
const E_DataType dt{ *( input->types.get( id ) ) };
|
|
assert( dt != E_DataType::UNKNOWN );
|
|
assert( locations.contains( id ) );
|
|
if ( dt == E_DataType::VARIABLE || dt == E_DataType::BUILTIN ) {
|
|
addInstruction( OP_FP_LOAD , *locations.get( id ) ,
|
|
location );
|
|
return false;
|
|
}
|
|
|
|
addInstruction( OP_LOAD , *locations.get( id ) ,
|
|
location );
|
|
addInstruction( OP_PUSH , location );
|
|
return true;
|
|
}
|
|
|
|
|
|
void T_CompilerImpl_::addInstruction(
|
|
const E_OpType op ,
|
|
T_SRDLocation const& location ) noexcept
|
|
{
|
|
assert( ArgumentsFor( op ) == 0 );
|
|
applyStackEffects( output->ops.addNew( op , location ) );
|
|
}
|
|
|
|
void T_CompilerImpl_::addInstruction(
|
|
const E_OpType op ,
|
|
const uint32_t arg0 ,
|
|
T_SRDLocation const& location ) noexcept
|
|
{
|
|
assert( ArgumentsFor( op ) == 1 );
|
|
applyStackEffects( output->ops.addNew( op , location , arg0 ) );
|
|
}
|
|
|
|
void T_CompilerImpl_::addInstruction(
|
|
const E_OpType op ,
|
|
std::initializer_list< uint32_t > args ,
|
|
T_SRDLocation const& location ) noexcept
|
|
{
|
|
assert( ArgumentsFor( op ) == args.size( ) );
|
|
applyStackEffects( output->ops.addNew( op , location , args ) );
|
|
}
|
|
|
|
void T_CompilerImpl_::applyStackEffects(
|
|
T_Op const& op ) noexcept
|
|
{
|
|
const auto m( DeltaMainStack( op.op ) );
|
|
const auto f( DeltaFPUStack( op.op ) );
|
|
|
|
#ifdef INVASIVE_TRACES
|
|
T_StringBuilder sb;
|
|
sb << "applying stack effects for (" << op << ") - sdMain " << sdMain
|
|
<< " (" << m << ") sdFPU " << sdFPU << " (" << f << ")" << '\n' << '\0';
|
|
printf( "%s" , sb.data( ) );
|
|
#endif
|
|
|
|
if ( m ) {
|
|
assert( m > 0 || sdMain >= uint32_t( -m ) );
|
|
sdMain += m;
|
|
}
|
|
|
|
if ( f ) {
|
|
assert( ( f > 0 && sdFPU + f < 8 ) || ( f < 0 && sdFPU >= uint32_t( -m ) ) );
|
|
sdFPU += m;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*= T_Compiler =================================================================*/
|
|
|
|
T_Compiler::T_Compiler( ) noexcept
|
|
: A_PrivateImplementation( new T_CompilerImpl_( ) )
|
|
{ }
|
|
|
|
P_OpProgram T_Compiler::compile(
|
|
T_ParserOutput const& input ) noexcept
|
|
{
|
|
return p< T_CompilerImpl_ >( ).compile( input );
|
|
}
|