2017-11-12 10:50:05 +01:00
|
|
|
#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_
|
|
|
|
{
|
2017-11-12 17:01:13 +01:00
|
|
|
P_OpProgram compile(
|
|
|
|
T_ParserOutput const& input ) noexcept;
|
|
|
|
|
|
|
|
private:
|
2017-11-12 10:50:05 +01:00
|
|
|
T_Visitor< A_Node > astVisitor{ ASTVisitorBrowser };
|
|
|
|
T_Set< uint32_t > constants{ UseTag< IndexBacked< > >( ) };
|
2017-11-12 17:01:13 +01:00
|
|
|
T_KeyValueTable< T_String , uint32_t > locations;
|
2017-11-12 10:50:05 +01:00
|
|
|
|
|
|
|
T_ParserOutput* input;
|
|
|
|
P_OpProgram output;
|
|
|
|
|
|
|
|
uint32_t fiVariables , fiFramebuffers ,
|
|
|
|
fiPipelines , fiPrograms ,
|
|
|
|
fiSamplers , fiTextures;
|
|
|
|
|
2017-11-12 17:01:13 +01:00
|
|
|
uint32_t sdMain , sdFPU;
|
2017-11-12 22:20:11 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
2017-11-12 10:50:05 +01:00
|
|
|
|
2017-11-13 14:41:03 +01:00
|
|
|
uint32_t fbCurrent; // Current framebuffer attachment
|
|
|
|
|
2017-11-12 10:50:05 +01:00
|
|
|
void gatherConstants( ) noexcept;
|
|
|
|
void countAssets( ) noexcept;
|
|
|
|
|
|
|
|
bool compileNode( uint32_t funcIndex ,
|
|
|
|
A_Node& node ,
|
|
|
|
bool exit ) noexcept;
|
2017-11-12 17:01:13 +01:00
|
|
|
void processFunction(
|
|
|
|
bool exit ,
|
|
|
|
uint32_t args ,
|
|
|
|
uint32_t lvars ,
|
|
|
|
T_SRDLocation const& location ) noexcept;
|
2017-11-13 09:25:32 +01:00
|
|
|
|
2017-11-12 17:01:13 +01:00
|
|
|
bool processIdentifier(
|
|
|
|
uint32_t funcIndex ,
|
|
|
|
T_IdentifierExprNode const& node ) noexcept;
|
2017-11-13 09:25:32 +01:00
|
|
|
bool processIdentifier(
|
|
|
|
uint32_t funcIndex ,
|
|
|
|
T_String const& id ,
|
|
|
|
T_SRDLocation const& location ) noexcept;
|
2017-11-12 17:01:13 +01:00
|
|
|
|
|
|
|
void addInstruction(
|
|
|
|
E_OpType op ,
|
2017-11-12 17:46:47 +01:00
|
|
|
T_SRDLocation const& location ) noexcept;
|
|
|
|
void addInstruction(
|
|
|
|
E_OpType op ,
|
|
|
|
uint32_t arg0 ,
|
|
|
|
T_SRDLocation const& location ) noexcept;
|
|
|
|
void addInstruction(
|
|
|
|
E_OpType op ,
|
2017-11-12 22:20:11 +01:00
|
|
|
std::initializer_list< uint32_t > args ,
|
2017-11-12 17:46:47 +01:00
|
|
|
T_SRDLocation const& location ) noexcept;
|
2017-11-12 18:38:31 +01:00
|
|
|
void applyStackEffects(
|
|
|
|
T_Op const& op ) noexcept;
|
2017-11-12 10:50:05 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
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
|
2017-11-12 17:01:13 +01:00
|
|
|
#ifdef INVASIVE_TRACES
|
|
|
|
uint32_t nInstr = 0;
|
|
|
|
#endif
|
2017-11-12 10:50:05 +01:00
|
|
|
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
|
2017-11-12 17:01:13 +01:00
|
|
|
sdMain = sdFPU = 0;
|
2017-11-12 10:50:05 +01:00
|
|
|
astVisitor.visit( func ,
|
|
|
|
[=]( A_Node& node , const bool exit ) -> bool {
|
|
|
|
return compileNode( cfi , node , exit );
|
|
|
|
} );
|
2017-11-12 17:01:13 +01:00
|
|
|
#ifdef INVASIVE_TRACES
|
2017-11-12 17:46:47 +01:00
|
|
|
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( ) );
|
2017-11-12 17:01:13 +01:00
|
|
|
nInstr += output->ops.sizeOf( cfi );
|
|
|
|
#endif
|
2017-11-12 10:50:05 +01:00
|
|
|
}
|
2017-11-12 17:01:13 +01:00
|
|
|
#ifdef INVASIVE_TRACES
|
|
|
|
printf( "total %d instructions\n" , nInstr );
|
|
|
|
#endif
|
2017-11-12 10:50:05 +01:00
|
|
|
|
|
|
|
return std::move( output );
|
|
|
|
}
|
|
|
|
|
|
|
|
void T_CompilerImpl_::gatherConstants( ) noexcept
|
|
|
|
{
|
|
|
|
constants.clear( );
|
|
|
|
astVisitor.visit( input->root , [&]( A_Node& node , const bool exit ) {
|
|
|
|
if ( exit && node.type( ) == A_Node::EXPR_CONST ) {
|
|
|
|
T_OpValue value;
|
|
|
|
value.f = dynamic_cast< T_ConstantExprNode& >( node ).floatValue( );
|
2017-11-12 17:01:13 +01:00
|
|
|
constants.add( value.u );
|
2017-11-12 10:50:05 +01:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
} );
|
|
|
|
|
|
|
|
#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
|
|
|
|
{
|
2017-11-12 17:01:13 +01:00
|
|
|
locations.clear( );
|
|
|
|
locations.add( T_String::Pooled( "time" ) , 0u );
|
|
|
|
locations.add( T_String::Pooled( "width" ) , 1u );
|
|
|
|
locations.add( T_String::Pooled( "height" ) , 2u );
|
|
|
|
|
2017-11-12 10:50:05 +01:00
|
|
|
auto const nt{ input->types.size( ) };
|
|
|
|
for ( auto i = 0u ; i < nt ; i ++ ) {
|
|
|
|
const auto t{ input->types.values( )[ i ] };
|
2017-11-12 17:01:13 +01:00
|
|
|
auto const& n{ input->types.keys( )[ i ] };
|
2017-11-12 10:50:05 +01:00
|
|
|
switch ( t ) {
|
|
|
|
case E_DataType::FRAMEBUFFER:
|
2017-11-12 17:01:13 +01:00
|
|
|
locations.add( n , output->nFramebuffers );
|
2017-11-12 10:50:05 +01:00
|
|
|
output->nFramebuffers ++;
|
|
|
|
break;
|
|
|
|
case E_DataType::PIPELINE:
|
2017-11-12 17:01:13 +01:00
|
|
|
locations.add( n , output->nPipelines );
|
2017-11-12 10:50:05 +01:00
|
|
|
output->nPipelines ++;
|
|
|
|
break;
|
|
|
|
case E_DataType::PROGRAM:
|
2017-11-12 17:01:13 +01:00
|
|
|
locations.add( n , output->nPrograms );
|
2017-11-12 10:50:05 +01:00
|
|
|
output->nPrograms ++;
|
|
|
|
break;
|
|
|
|
case E_DataType::SAMPLER:
|
2017-11-12 17:01:13 +01:00
|
|
|
locations.add( n , output->nSamplers );
|
2017-11-12 10:50:05 +01:00
|
|
|
output->nSamplers ++;
|
|
|
|
break;
|
|
|
|
case E_DataType::TEXTURE:
|
2017-11-12 17:01:13 +01:00
|
|
|
locations.add( n , output->nTextures );
|
2017-11-12 10:50:05 +01:00
|
|
|
output->nTextures ++;
|
|
|
|
break;
|
|
|
|
case E_DataType::VARIABLE:
|
2017-11-12 17:01:13 +01:00
|
|
|
locations.add( n , output->nVariables + 3 );
|
2017-11-12 10:50:05 +01:00
|
|
|
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;
|
|
|
|
|
2017-11-12 17:01:13 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-12 10:50:05 +01:00
|
|
|
#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 );
|
2017-11-12 17:01:13 +01:00
|
|
|
|
|
|
|
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( ) );
|
2017-11-12 10:50:05 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
bool T_CompilerImpl_::compileNode(
|
|
|
|
const uint32_t funcIndex ,
|
|
|
|
A_Node& node ,
|
|
|
|
const bool exit ) noexcept
|
|
|
|
{
|
2017-11-12 17:01:13 +01:00
|
|
|
switch ( node.type( ) ) {
|
|
|
|
|
2017-11-13 09:25:32 +01:00
|
|
|
//- PROGRAM STRUCTURE -----------------------------------------------------------------
|
|
|
|
|
2017-11-12 17:01:13 +01:00
|
|
|
case A_Node::ROOT:
|
|
|
|
fprintf( stderr , "Internal error: root node found during compilation\n" );
|
|
|
|
std::abort( );
|
|
|
|
break;
|
|
|
|
|
2017-11-13 09:25:32 +01:00
|
|
|
case A_Node::ILIST: break;
|
2017-11-13 16:58:38 +01:00
|
|
|
case A_Node::OP_LOCALS: break;
|
2017-11-13 09:25:32 +01:00
|
|
|
|
2017-11-12 17:01:13 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-11-13 10:02:16 +01:00
|
|
|
|
2017-11-13 09:25:32 +01:00
|
|
|
//- GENERAL / FLOW CONTROL INSTRUCTIONS -----------------------------------------------
|
|
|
|
|
2017-11-12 17:01:13 +01:00
|
|
|
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 );
|
2017-11-12 17:46:47 +01:00
|
|
|
addInstruction( OP_CALL , fi , node.location( ) );
|
2017-11-12 17:01:13 +01:00
|
|
|
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 );
|
2017-11-12 17:46:47 +01:00
|
|
|
addInstruction( OP_FP_SSTORE , sdMain - pos - 1 ,
|
|
|
|
node.location( ) );
|
2017-11-12 17:01:13 +01:00
|
|
|
} else {
|
2017-11-12 17:46:47 +01:00
|
|
|
addInstruction( OP_FP_STORE ,
|
|
|
|
*locations.get( ((T_SetInstrNode&)node).id( ) ) ,
|
|
|
|
node.location( ) );
|
2017-11-12 17:01:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case A_Node::OP_COND:
|
|
|
|
if ( exit ) {
|
2017-11-12 22:20:11 +01:00
|
|
|
// 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 );
|
|
|
|
}
|
|
|
|
|
2017-11-12 17:46:47 +01:00
|
|
|
addInstruction( OP_POP , 0 , node.location( ) );
|
2017-11-12 17:01:13 +01:00
|
|
|
sdMain --;
|
2017-11-12 22:20:11 +01:00
|
|
|
condJumps.removeLast( );
|
|
|
|
} else {
|
|
|
|
condJumps.addNew( );
|
2017-11-12 17:01:13 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case A_Node::TN_CONDITION:
|
|
|
|
if ( exit ) {
|
|
|
|
addInstruction( OP_PUSH , node.location( ) );
|
2017-11-12 17:46:47 +01:00
|
|
|
addInstruction( OP_FP_SSTORE_INT , 0 , node.location( ) );
|
2017-11-12 17:01:13 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case A_Node::TN_CASE:
|
|
|
|
if ( exit ) {
|
2017-11-12 22:20:11 +01:00
|
|
|
// Store a potential skip location
|
|
|
|
condJumps.last( ).caseEnds.add( output->ops.sizeOf( funcIndex ) );
|
|
|
|
|
|
|
|
// Update the initial skip
|
2017-11-12 17:01:13 +01:00
|
|
|
const auto cpos( output->ops.sizeOf( funcIndex ) );
|
2017-11-12 22:20:11 +01:00
|
|
|
const auto ppos( condJumps.last( ).prevCase );
|
|
|
|
const auto diff( cpos - ppos );
|
|
|
|
assert( diff > 1 );
|
|
|
|
output->ops.get( funcIndex , ppos ).args[ 1 ] = diff - 1;
|
2017-11-12 17:01:13 +01:00
|
|
|
} else {
|
2017-11-12 22:20:11 +01:00
|
|
|
// 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[ 1 ] ++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the conditional skip
|
2017-11-12 17:01:13 +01:00
|
|
|
auto& c( (T_CondInstrNode::T_ValuedCase&) node );
|
2017-11-12 22:20:11 +01:00
|
|
|
condJumps.last( ).prevCase = output->ops.sizeOf( funcIndex );
|
|
|
|
addInstruction( OP_COND_SKIP , { 0 , uint32_t( c.value( ) ) } ,
|
2017-11-12 17:46:47 +01:00
|
|
|
node.location( ) );
|
2017-11-12 17:01:13 +01:00
|
|
|
}
|
|
|
|
break;
|
2017-11-12 22:20:11 +01:00
|
|
|
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[ 1 ] ++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2017-11-13 09:25:32 +01:00
|
|
|
|
|
|
|
//- 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( ) , pn.location( ) );
|
|
|
|
sdMain -= pn.size( );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case A_Node::OP_PROGRAM:
|
|
|
|
if ( exit ) {
|
|
|
|
auto& pn( (T_ProgramInstrNode&) node );
|
|
|
|
processIdentifier( funcIndex , pn.id( ) , pn.idLocation( ) );
|
|
|
|
if ( !output->progNames.contains( pn.path( ) ) ) {
|
|
|
|
output->progNames.add( pn.path( ) );
|
|
|
|
}
|
2017-11-13 10:02:16 +01:00
|
|
|
addInstruction( OP_INIT_PROGRAM , output->progNames.indexOf( pn.path( ) ) ,
|
2017-11-13 09:25:32 +01:00
|
|
|
pn.location( ) );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2017-11-13 10:02:16 +01:00
|
|
|
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;
|
|
|
|
|
2017-11-13 14:41:03 +01:00
|
|
|
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 } , fan.location( ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
//- STATE -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2017-11-13 16:58:38 +01:00
|
|
|
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;
|
|
|
|
|
2017-11-13 17:20:45 +01:00
|
|
|
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( ) , un.integers( ) ? 1u : 0u } ,
|
|
|
|
un.location( ) );
|
|
|
|
sdMain -= un.values( );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2017-11-13 17:28:57 +01:00
|
|
|
case A_Node::OP_VIEWPORT:
|
|
|
|
if ( exit ) {
|
|
|
|
addInstruction( OP_VIEWPORT , node.location( ) );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2017-11-13 10:02:16 +01:00
|
|
|
|
2017-11-13 09:25:32 +01:00
|
|
|
//- RENDERING -------------------------------------------------------------------------
|
|
|
|
|
|
|
|
case A_Node::OP_FULLSCREEN:
|
|
|
|
if ( exit ) {
|
|
|
|
addInstruction( OP_FULLSCREEN , node.location( ) );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2017-11-13 10:02:16 +01:00
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
|
//- 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-13 09:25:32 +01:00
|
|
|
//- EXPRESSIONS - OPERATORS -----------------------------------------------------------
|
|
|
|
|
2017-11-12 17:01:13 +01:00
|
|
|
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 );
|
2017-11-12 17:46:47 +01:00
|
|
|
addInstruction( OP_FP_CMP , op , node.location( ) );
|
2017-11-12 17:01:13 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2017-11-13 16:58:38 +01:00
|
|
|
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;
|
2017-11-12 17:01:13 +01:00
|
|
|
|
2017-11-13 10:02:16 +01:00
|
|
|
|
2017-11-13 09:25:32 +01:00
|
|
|
//- EXPRESSIONS - TERMINAL NODES ------------------------------------------------------
|
|
|
|
|
2017-11-12 17:01:13 +01:00
|
|
|
case A_Node::EXPR_CONST:
|
|
|
|
if ( !exit ) {
|
|
|
|
T_OpValue value;
|
|
|
|
value.f = dynamic_cast< T_ConstantExprNode& >( node ).floatValue( );
|
2017-11-12 17:46:47 +01:00
|
|
|
addInstruction( OP_FP_LOAD ,
|
|
|
|
constants.indexOf( value.u ) + 3 ,
|
|
|
|
node.location( ) );
|
2017-11-12 17:01:13 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case A_Node::EXPR_ID:
|
|
|
|
if ( !exit ) {
|
|
|
|
processIdentifier( funcIndex ,
|
|
|
|
dynamic_cast< T_IdentifierExprNode& >( node ) );
|
|
|
|
}
|
|
|
|
break;
|
2017-11-13 09:25:32 +01:00
|
|
|
|
|
|
|
case A_Node::EXPR_INPUT:
|
|
|
|
if ( !exit ) {
|
|
|
|
}
|
|
|
|
break;
|
2017-11-12 17:01:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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 ) {
|
2017-11-12 17:46:47 +01:00
|
|
|
addInstruction( OP_POP , lvars - 1 , location );
|
2017-11-12 17:01:13 +01:00
|
|
|
sdMain -= lvars;
|
|
|
|
}
|
2017-11-12 18:38:31 +01:00
|
|
|
sdMain -= args;
|
2017-11-12 17:46:47 +01:00
|
|
|
addInstruction( OP_RET , args , location );
|
2017-11-12 17:01:13 +01:00
|
|
|
assert( sdMain == 0 );
|
|
|
|
} else {
|
|
|
|
if ( lvars ) {
|
2017-11-12 17:46:47 +01:00
|
|
|
addInstruction( OP_RES_STACK , lvars - 1 , location );
|
2017-11-12 17:01:13 +01:00
|
|
|
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
|
2017-11-13 09:25:32 +01:00
|
|
|
{
|
|
|
|
return processIdentifier( funcIndex , node.id( ) , node.location( ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool T_CompilerImpl_::processIdentifier(
|
|
|
|
uint32_t funcIndex ,
|
|
|
|
T_String const& id ,
|
|
|
|
T_SRDLocation const& location ) noexcept
|
2017-11-12 17:01:13 +01:00
|
|
|
{
|
|
|
|
auto& func{ input->root.function( funcIndex ) };
|
2017-11-13 09:25:32 +01:00
|
|
|
if ( func.hasLocal( id ) ) {
|
|
|
|
const E_DataType dt{ func.getLocalType( id ) };
|
2017-11-12 17:01:13 +01:00
|
|
|
assert( dt != E_DataType::UNKNOWN );
|
|
|
|
|
|
|
|
uint32_t stackPos;
|
2017-11-13 09:25:32 +01:00
|
|
|
if ( func.isArgument( id ) ) {
|
2017-11-12 17:01:13 +01:00
|
|
|
auto const& fn( (T_FuncNode&) func );
|
|
|
|
const auto nArgs( fn.arguments( ) );
|
2017-11-13 09:25:32 +01:00
|
|
|
stackPos = nArgs - 1 - func.getLocalIndex( id );
|
2017-11-12 17:01:13 +01:00
|
|
|
} else {
|
2017-11-13 09:25:32 +01:00
|
|
|
stackPos = func.getLocalIndex( id ) + 1;
|
2017-11-12 17:01:13 +01:00
|
|
|
}
|
|
|
|
assert( stackPos < sdMain );
|
|
|
|
|
|
|
|
const auto p( sdMain - ( stackPos + 1 ) );
|
|
|
|
if ( dt == E_DataType::VARIABLE ) {
|
2017-11-13 09:25:32 +01:00
|
|
|
addInstruction( OP_FP_SLOAD , p , location );
|
2017-11-12 17:01:13 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-11-13 09:25:32 +01:00
|
|
|
addInstruction( OP_SLOAD , p , location );
|
|
|
|
addInstruction( OP_PUSH , location );
|
2017-11-12 17:01:13 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-11-13 09:25:32 +01:00
|
|
|
assert( input->types.contains( id ) );
|
|
|
|
const E_DataType dt{ *( input->types.get( id ) ) };
|
2017-11-12 17:01:13 +01:00
|
|
|
assert( dt != E_DataType::UNKNOWN );
|
2017-11-13 09:25:32 +01:00
|
|
|
assert( locations.contains( id ) );
|
2017-11-12 17:01:13 +01:00
|
|
|
if ( dt == E_DataType::VARIABLE || dt == E_DataType::BUILTIN ) {
|
2017-11-13 09:25:32 +01:00
|
|
|
addInstruction( OP_FP_LOAD , *locations.get( id ) ,
|
|
|
|
location );
|
2017-11-12 17:01:13 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-11-13 09:25:32 +01:00
|
|
|
addInstruction( OP_LOAD , *locations.get( id ) ,
|
|
|
|
location );
|
|
|
|
addInstruction( OP_PUSH , location );
|
2017-11-12 17:01:13 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void T_CompilerImpl_::addInstruction(
|
|
|
|
const E_OpType op ,
|
2017-11-12 17:46:47 +01:00
|
|
|
T_SRDLocation const& location ) noexcept
|
|
|
|
{
|
|
|
|
assert( ArgumentsFor( op ) == 0 );
|
2017-11-12 18:38:31 +01:00
|
|
|
applyStackEffects( output->ops.addNew( op , location ) );
|
2017-11-12 17:46:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void T_CompilerImpl_::addInstruction(
|
|
|
|
const E_OpType op ,
|
2017-11-12 17:01:13 +01:00
|
|
|
const uint32_t arg0 ,
|
2017-11-12 17:46:47 +01:00
|
|
|
T_SRDLocation const& location ) noexcept
|
2017-11-12 17:01:13 +01:00
|
|
|
{
|
2017-11-12 17:46:47 +01:00
|
|
|
assert( ArgumentsFor( op ) == 1 );
|
2017-11-12 18:38:31 +01:00
|
|
|
applyStackEffects( output->ops.addNew( op , location , arg0 ) );
|
2017-11-12 17:46:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void T_CompilerImpl_::addInstruction(
|
|
|
|
const E_OpType op ,
|
2017-11-12 22:20:11 +01:00
|
|
|
std::initializer_list< uint32_t > args ,
|
2017-11-12 17:46:47 +01:00
|
|
|
T_SRDLocation const& location ) noexcept
|
|
|
|
{
|
2017-11-12 22:20:11 +01:00
|
|
|
assert( ArgumentsFor( op ) == args.size( ) );
|
|
|
|
applyStackEffects( output->ops.addNew( op , location , args ) );
|
2017-11-12 18:38:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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 >= uint32_t( -m ) );
|
|
|
|
sdFPU += m;
|
|
|
|
}
|
2017-11-12 10:50:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*= 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 );
|
|
|
|
}
|