demotool/c-opast.cc

801 lines
18 KiB
C++
Raw Normal View History

#include "externals.hh"
#include "c-opast.hh"
#include "c-sync.hh"
#include <ebcl/Algorithms.hh>
2017-11-06 10:15:38 +01:00
using namespace ebcl;
using namespace opast;
/*= A_Node ===================================================================*/
A_Node::A_Node( const E_Type type ,
A_Node* const parent ) noexcept
: type_( type ) , parent_( parent )
{
assert( ( type == ROOT && !parent ) || ( type != ROOT && parent ) );
}
A_Node::~A_Node( ) { }
T_RootNode& A_Node::root( ) const noexcept
{
A_Node const* node( this );
while ( node->parent_ ) {
node = node->parent_;
}
assert( node );
assert( node->type_ == ROOT );
return *dynamic_cast< T_RootNode* >(
const_cast< A_Node* >( node ) );
}
bool A_Node::replace(
A_Node const& node ,
T_OwnPtr< A_Node >& replacement
) noexcept
{
const auto nc{ size( ) };
for ( auto i = 0u ; i < nc ; i ++ ) {
if ( child( i ).get( ) == &node ) {
child( i ) = std::move( replacement );
return true;
}
}
for ( auto i = 0u ; i < nc ; i ++ ) {
if ( child( i ) && child( i )->replace( node , replacement ) ) {
return true;
}
}
return false;
}
/*----------------------------------------------------------------------------*/
A_Node* opast::ASTVisitorBrowser(
A_Node& node ,
const uint32_t child ) noexcept
{
switch ( node.type( ) ) {
// Root node
case A_Node::ROOT: {
auto& n( (T_RootNode&) node );
if ( child < n.nFunctions( ) ) {
return &n.function( child );
}
break;
}
// Functions / special blocks
case A_Node::DECL_FN: case A_Node::DECL_INIT: case A_Node::DECL_FRAME:
if ( child == 0 ) {
auto& n( (A_FuncNode&) node );
return &n.instructions( );
}
break;
// Instruction list
case A_Node::ILIST: {
auto& n( (T_InstrListNode&) node );
if ( child < n.size( ) ) {
return &n.node( child );
}
break;
}
// Unary operators
case A_Node::EXPR_NEG: case A_Node::EXPR_INV:
case A_Node::EXPR_NOT: case A_Node::EXPR_SIN:
case A_Node::EXPR_COS: case A_Node::EXPR_TAN:
case A_Node::EXPR_SQRT: case A_Node::EXPR_EXP:
case A_Node::EXPR_LN:
{
auto& n( (T_UnaryOperatorNode&) node );
if ( child == 0 && n.hasArgument( ) ) {
return &n.argument( );
}
break;
}
// Binary operators
case A_Node::EXPR_ADD: case A_Node::EXPR_SUB:
case A_Node::EXPR_MUL: case A_Node::EXPR_DIV:
case A_Node::EXPR_POW:
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:
{
auto& n( (T_BinaryOperatorNode&) node );
if ( child == 0 && n.hasLeft( ) ) {
return &n.left( );
}
if ( child < 2 && n.hasRight( ) ) {
return &n.right( );
}
break;
}
// Nodes that do not have children
case A_Node::EXPR_ID: case A_Node::EXPR_CONST:
case A_Node::EXPR_INPUT:
//
case A_Node::OP_PROGRAM: case A_Node::OP_PIPELINE:
case A_Node::OP_INPUT: case A_Node::OP_FULLSCREEN:
2017-11-10 21:01:35 +01:00
case A_Node::OP_ODBG: case A_Node::OP_LOCALS:
2017-11-17 20:33:56 +01:00
case A_Node::OP_MAINOUT: case A_Node::OP_OVERRIDES:
//
case A_Node::OP_USE_FRAMEBUFFER: case A_Node::OP_USE_PIPELINE:
case A_Node::OP_USE_PROGRAM: case A_Node::OP_USE_TEXTURE:
break;
// Profile instruction
case A_Node::OP_PROFILE:
if ( child == 0 ) {
return &( ((T_ProfileInstrNode&) node).instructions( ) );
}
break;
// Call instruction
case A_Node::OP_CALL:
{
auto& n( (T_CallInstrNode&) node );
const auto nArgs( n.size( ) );
if ( child < nArgs ) {
return &n.argument( nArgs - child - 1 );
}
break;
}
2017-11-13 10:02:16 +01:00
// Argument node
case A_Node::TN_ARG:
{
if ( child == 0 ) {
2017-11-13 10:02:16 +01:00
return &( (T_ArgumentNode&) node ).expression( );
}
break;
}
2017-12-24 11:10:38 +01:00
// Instructions that match the common case, in which chidren are
// listed directly
case A_Node::OP_CLEAR: case A_Node::OP_COMPUTE:
2017-11-10 22:09:24 +01:00
{
const auto nArgs( node.size( ) );
2017-11-13 10:02:16 +01:00
if ( child < nArgs ) {
return node.child( nArgs - child - 1 ).get( );
2017-11-10 22:09:24 +01:00
}
break;
}
2017-12-24 11:10:38 +01:00
// Instructions that may have undefined children at the end of
// their lists; we need to find the first non-NULL child in the
// list and start from there.
case A_Node::OP_IMAGE:
{
auto nArgs( node.size( ) );
while ( nArgs > 0 && !node.child( nArgs - 1 ) ) {
nArgs --;
}
if ( child < nArgs ) {
return node.child( nArgs - child - 1 ).get( );
}
break;
}
// Conditional instruction & associated technical nodes
case A_Node::OP_COND:
{
auto& n( (T_CondInstrNode&) node );
auto c = child;
if ( n.hasExpression( ) ) {
if ( c == 0 ) {
return &n.expression( );
}
c --;
}
if ( !n.cases( ).empty( ) ) {
if ( c < n.cases( ).size( ) ) {
return &n.getCase( n.cases( )[ c ] );
}
c -= n.cases( ).size( );
}
if ( n.hasDefaultCase( ) && c == 0 ) {
return &n.defaultCase( );
}
break;
}
case A_Node::TN_CONDITION:
if ( child == 0 ) {
return &( (T_CondInstrNode::T_Expression&) node ).expression( );
}
break;
case A_Node::TN_CASE:
if ( child == 0 ) {
return &( (T_CondInstrNode::T_ValuedCase&) node ).instructions( );
}
break;
case A_Node::TN_DEFAULT:
if ( child == 0 ) {
return &( (T_CondInstrNode::T_DefaultCase&) node ).instructions( );
}
break;
// Set instruction
case A_Node::OP_SET:
if ( child == 0 ) {
auto& n( (T_SetInstrNode&) node );
if ( n.hasExpression( ) ) {
return &n.expression( );
}
}
break;
// Texture instruction
case A_Node::OP_TEXTURE:
{
auto& n( (T_TextureInstrNode&) node );
auto c = child;
2017-11-13 10:02:16 +01:00
if ( n.lods( ) ) {
if ( c == 0 ) {
2017-11-13 10:02:16 +01:00
return n.lods( );
}
c --;
}
if ( n.hasHeight( ) ) {
if ( c == 0 ) {
return &n.height( );
}
c --;
}
2017-11-13 10:02:16 +01:00
if ( n.hasWidth( ) && c == 0 ) {
return &n.width( );
}
return nullptr;
}
// Framebuffer definition; may have LOD expressions
case A_Node::OP_FRAMEBUFFER:
{
auto& n( (T_FramebufferInstrNode&) node );
if ( child < n.colorAttachments( ) ) {
return &n.colorAttachment( child );
}
if ( child == n.colorAttachments( ) && n.depthAttachment( ) ) {
return n.depthAttachment( );
}
break;
}
case A_Node::TN_FBATT:
{
auto& n( (T_FramebufferInstrNode::T_Attachment&) node );
if ( n.lod( ) && child == 0 ) {
return n.lod( );
}
break;
}
// Sampler definition may have LOD expressions
case A_Node::OP_SAMPLER:
{
auto& n( (T_SamplerInstrNode&) node );
auto c = child;
if ( n.minLod( ) ) {
if ( c == 0 ) {
return n.minLod( );
}
c--;
}
if ( n.maxLod( ) && c == 0 ) {
return n.maxLod( );
}
break;
}
// Uniforms instruction
case A_Node::OP_UNIFORMS:
{
auto& n( (T_UniformsInstrNode&) node );
const auto nv( n.size( ) );
2017-11-13 17:20:45 +01:00
if ( child < nv ) {
return &n.value( nv - child - 1 );
}
break;
}
// Viewport instruction
case A_Node::OP_VIEWPORT:
{
auto& n( (T_ViewportInstrNode&) node );
auto c = child;
for ( int i = 0 ; i < 4 ; i ++ ) {
T_ViewportInstrNode::E_Parameter p{
2017-11-13 17:28:57 +01:00
T_ViewportInstrNode::E_Parameter( 3 - i ) };
if ( n.hasParameter( p ) ) {
if ( c == 0 ) {
return &n.parameter( p );
}
c --;
}
}
break;
}
}
return nullptr;
}
2017-11-06 14:06:21 +01:00
/*= E_DataType ===============================================================*/
T_StringBuilder& opast::operator<<(
T_StringBuilder& sb ,
const E_DataType dt )
{
switch ( dt ) {
case E_DataType::UNKNOWN:
sb << "unknown";
return sb;
case E_DataType::BUILTIN:
sb << "built-in variable";
return sb;
case E_DataType::VARIABLE:
sb << "variable";
return sb;
case E_DataType::FRAMEBUFFER:
sb << "framebuffer";
return sb;
case E_DataType::INPUT:
sb << "input";
return sb;
case E_DataType::PIPELINE:
sb << "pipeline";
return sb;
case E_DataType::PROGRAM:
sb << "program";
return sb;
case E_DataType::SAMPLER:
sb << "sampler";
return sb;
case E_DataType::TEXTURE:
sb << "texture";
return sb;
}
sb << "*unknown data type*";
return sb;
}
2017-11-06 10:15:38 +01:00
/*= A_FuncNode ===============================================================*/
A_FuncNode::A_FuncNode(
const bool isInit ,
T_RootNode* const root ) noexcept
: A_Node( isInit ? DECL_INIT : DECL_FRAME , root ) ,
name_( isInit ? "*init*" : "*frame*" )
2017-11-06 10:15:38 +01:00
{ }
A_FuncNode::A_FuncNode(
T_String const& name ,
T_RootNode* const root ) noexcept
: A_Node( DECL_FN , root ) , name_( name )
2017-11-06 10:15:38 +01:00
{ }
T_Optional< T_SRDLocation > A_FuncNode::addLocalVariable(
T_String const& name ,
T_SRDLocation const& location ) noexcept
{
auto const* const pnp( locals_.get( name ) );
if ( pnp ) {
return pnp->location;
}
T_Local_ lv{ name , location , false };
lv.type = E_DataType::VARIABLE;
locals_.add( std::move( lv ) );
return {};
}
2017-11-06 10:15:38 +01:00
/*= T_RootNode ===============================================================*/
T_RootNode::T_RootNode( ) noexcept
: A_Node( ROOT , nullptr )
2017-11-06 10:15:38 +01:00
{ }
T_RootNode::T_AddFunctionResult T_RootNode::addFunction(
T_OwnPtr< A_FuncNode >& function ) noexcept
{
T_String const& fn( function->name( ) );
auto const* const pf( functions_.get( fn ) );
if ( !pf ) {
auto* const rv( function.get( ) );
functions_.add( fn , children_.size( ) );
children_.add( std::move( function ) );
2017-11-06 10:15:38 +01:00
return *rv;
}
T_String dfn;
uint32_t dupCtr( 0 );
do {
T_StringBuilder fnsb;
fnsb << fn << " dup " << dupCtr ++;
dfn = std::move( fnsb );
} while ( functions_.contains( dfn ) );
T_OwnPtr< A_FuncNode > df( NewOwned< T_FuncNode >( dfn , *this ) );
auto* const rv( df.get( ) );
functions_.add( fn , children_.size( ) );
children_.add( std::move( df ) );
return T_AddFunctionResult{ *rv , child( *pf )->location( ) };
2017-11-06 10:15:38 +01:00
}
/*= T_FuncNode ===============================================================*/
T_Optional< T_SRDLocation > T_FuncNode::addArgument(
T_SRDToken const& token ) noexcept
{
assert( token.type( ) == E_SRDTokenType::WORD );
assert( token.hasLocation( ) );
const auto pnp( locals_.indexOf( token.stringValue( ) ) );
if ( pnp != T_HashIndex::INVALID_INDEX ) {
return locals_[ pnp ].location;
2017-11-06 10:15:38 +01:00
}
locals_.add( T_Local_{ token.stringValue( ) , token.location( ) , true } );
2017-11-06 10:15:38 +01:00
return {};
}
uint32_t T_FuncNode::arguments( ) const noexcept
{
const auto n( locals_.size( ) );
uint32_t na{ 0u };
for ( auto i = 0u ; i < n ; i ++ ) {
if ( locals_[ i ].argument ) {
na ++;
}
}
return na;
}
2017-12-16 10:38:17 +01:00
/*= T_InstrListNode ==========================================================*/
void T_InstrListNode::replaceMultiple(
A_InstructionNode& instruction ,
T_InstrListNode* replacement
) noexcept
{
T_OwnPtr< A_InstructionNode > oldInstr;
uint32_t index{ 0xffffffff };
for ( auto i = 0u ; i < children_.size( ) ; i ++ ) {
if ( children_[ i ].get( ) == &instruction ) {
oldInstr = std::move( children_[ i ] );
index = i;
break;
}
}
assert( oldInstr );
if ( replacement && replacement->size( ) ) {
2017-12-16 10:38:17 +01:00
auto& rep{ *replacement };
// Move existing children to the end of the list
const auto oldSize{ children_.size( ) };
for ( auto i = 1u ; i < rep.size( ) ; i ++ ) {
children_.addNew( );
}
const auto newSize{ children_.size( ) };
2017-12-23 17:18:29 +01:00
const auto nMove{ rep.size( ) > 1 ? ( oldSize - index ) : 0 };
2017-12-16 10:38:17 +01:00
for ( auto i = 1u ; i < nMove ; i ++ ) {
children_[ newSize - i ] = std::move(
children_[ oldSize - i ] );
}
// Move replacement instructions
for ( auto i = 0u ; i < rep.size( ) ; i ++ ) {
children_[ index + i ] = std::move(
rep.children_[ i ] );
children_[ index + i ]->setParent( *this );
}
2017-12-23 17:18:29 +01:00
rep.children_.clear( );
2017-12-16 10:38:17 +01:00
} else {
children_.remove( index );
}
}
2017-11-06 10:15:38 +01:00
2017-11-06 14:06:21 +01:00
/*= T_PipelineInstrNode ======================================================*/
T_Optional< T_SRDLocation > T_PipelineInstrNode::addProgram(
T_SRDToken const& pidToken ) noexcept
{
T_String const& name( pidToken.stringValue( ) );
const auto pIndex( pids_.indexOf( name ) );
if ( pIndex != -1 ) {
return pidLocations_[ pIndex ];
}
pids_.add( name );
pidLocations_.add( pidToken.location( ) );
return {};
}
/*= T_UnaryOperatorNode ======================================================*/
T_UnaryOperatorNode::T_UnaryOperatorNode(
A_Node& parent ,
const E_Operator op ) noexcept
: A_ExpressionNode( ([op]() {
switch ( op ) {
case NEG: return EXPR_NEG;
case INV: return EXPR_INV;
case NOT: return EXPR_NOT;
case SIN: return EXPR_SIN;
case COS: return EXPR_COS;
case TAN: return EXPR_TAN;
case SQRT: return EXPR_SQRT;
case EXP: return EXPR_EXP;
case LN: return EXPR_LN;
}
std::abort( );
})( ) , parent ) , op_( op )
{
children_.add( P_ExpressionNode{} );
}
/*= T_BinaryOperatorNode =====================================================*/
T_BinaryOperatorNode::T_BinaryOperatorNode(
A_Node& parent ,
const E_Operator op ) noexcept
: A_ExpressionNode( ([op]() {
switch ( op ) {
case ADD: return EXPR_ADD;
case SUB: return EXPR_SUB;
case MUL: return EXPR_MUL;
case DIV: return EXPR_DIV;
case POW: return EXPR_POW;
case CMP_EQ: return EXPR_CMP_EQ;
case CMP_NE: return EXPR_CMP_NE;
case CMP_GT: return EXPR_CMP_GT;
case CMP_GE: return EXPR_CMP_GE;
case CMP_LT: return EXPR_CMP_LT;
case CMP_LE: return EXPR_CMP_LE;
}
std::abort( );
})( ) , parent ) , op_( op )
{
children_.add( P_ExpressionNode{} );
children_.add( P_ExpressionNode{} );
}
/*= T_FramebufferInstrNode ===================================================*/
bool T_FramebufferInstrNode::hasAttachment(
T_String const& name ) const noexcept
{
const auto nc{ size( ) };
for ( auto i = 0u ; i < nc ; i ++ ) {
auto const& att{ (T_Attachment const&) *child( i ) };
if ( att.id( ) == name ) {
return true;
}
}
return false;
}
bool T_FramebufferInstrNode::addColorAttachment(
T_SRDToken const& id ,
P_ExpressionNode lod ) noexcept
{
if ( hasAttachment( id.stringValue( ) ) ) {
return false;
}
colorAttachments_.add( children_.add(
NewOwned< T_Attachment >( *this ,
false , id , std::move( lod ) ) ) );
return true;
}
bool T_FramebufferInstrNode::setDepthAttachment(
T_SRDToken const& id ,
P_ExpressionNode lod ) noexcept
{
if ( depthAttachment_ || hasAttachment( id.stringValue( ) ) ) {
return false;
}
depthAttachment_ = children_.add( NewOwned< T_Attachment >(
*this , true , id , std::move( lod ) ) );
return true;
}
2017-11-10 12:48:57 +01:00
/*= T_OutputDebugInstrNode ===================================================*/
T_OutputDebugInstrNode::T_OutputDebugInstrNode(
T_InstrListNode& parent ,
T_SRDToken const& texture ,
const E_ODbgMode mode ,
T_SRDLocation const& modeLocation ,
T_SRDToken const& description ) noexcept
: A_InstructionNode( OP_ODBG , parent ) ,
idTexture_( texture.stringValue( ) ) ,
locTexture_( texture.location( ) ) ,
mode_( mode ) , locMode_( modeLocation ) ,
description_( description.stringValue( ) ) ,
locDescription_( description.location( ) )
{ }
/*= T_SamplerInstrNode =========================================================*/
T_Optional< T_SRDLocation > T_SamplerInstrNode::setSampling(
E_TexSampling mode ,
T_SRDLocation const& location ) noexcept
{
if ( sampling_ ) {
return sampling_->location;
}
sampling_ = T_Sampling_{ location , mode };
return {};
}
T_Optional< T_SRDLocation > T_SamplerInstrNode::setMipmapSampling(
E_TexSampling mode ,
T_SRDLocation const& location ) noexcept
{
if ( mipmaps_ ) {
return mipmaps_->location;
}
mipmaps_ = T_Mipmaps_{ location , mode };
return {};
}
T_Optional< T_SRDLocation > T_SamplerInstrNode::setNoMipmap(
T_SRDLocation const& location ) noexcept
{
if ( mipmaps_ ) {
return mipmaps_->location;
}
mipmaps_ = T_Mipmaps_{ location , {} };
return {};
}
T_Optional< T_SRDLocation > T_SamplerInstrNode::setWrapping(
E_TexWrap mode ,
T_SRDLocation const& location ) noexcept
{
if ( wrapping_ ) {
return wrapping_->location;
}
wrapping_ = T_Wrapping_{ location , mode };
return {};
}
T_Optional< T_SRDLocation > T_SamplerInstrNode::setLOD(
T_SRDLocation const& location ,
P_ExpressionNode min ,
P_ExpressionNode max ) noexcept
{
if ( lod_ ) {
return lod_->location;
}
T_Optional< uint32_t > pMin , pMax;
if ( min ) {
pMin = children_.add( NewOwned< T_ArgumentNode >(
*this , std::move( min ) ) );
}
if ( max ) {
pMax = children_.add( NewOwned< T_ArgumentNode >(
*this , std::move( max ) ) );
}
lod_ = T_LOD_{ location , pMin , pMax };
return {};
}
2017-11-10 21:01:35 +01:00
2017-11-10 21:01:35 +01:00
/*= T_LocalsInstrNode ===========================================================*/
T_Optional< T_SRDLocation > T_LocalsInstrNode::addVariable(
2017-11-10 21:01:35 +01:00
T_SRDToken const& token ) noexcept
{
const auto pnp( vars_.indexOf( token.stringValue( ) ) );
if ( pnp != -1 ) {
return varLocs_[ pnp ];
2017-11-10 21:01:35 +01:00
}
vars_.add( token.stringValue( ) );
varLocs_.add( token.location( ) );
return {};
2017-11-10 21:01:35 +01:00
}
/*= T_CondInstrNode ==========================================================*/
void T_CondInstrNode::handleRemoval(
const uint32_t idx ) noexcept
{
children_.removeSwap( idx );
if ( idx >= children_.size( ) ) {
return;
}
if ( defaultCase_ && idx == *defaultCase_ ) {
defaultCase_ = idx;
} else if ( expression_ && idx == *expression_ ) {
expression_ = idx;
} else {
auto& vc{ (T_ValuedCase&) *child( idx ) };
cases_.update( vc.value( ) , idx );
}
}
void T_CondInstrNode::setExpression(
P_ExpressionNode expression ) noexcept
{
if ( !expression ) {
if ( !expression_ ) {
return;
}
const auto idx{ *expression_ };
expression_.clear( );
handleRemoval( idx );
} else if ( expression_ ) {
child( *expression_ ) = NewOwned< T_Expression >(
*this , std::move( expression ) );
} else {
expression_ = children_.size( );
children_.add( NewOwned< T_Expression >(
*this , std::move( expression ) ) );
}
}
void T_CondInstrNode::setCase(
const int64_t value ,
P_InstrListNode instrList ) noexcept
{
auto const* const pp{ cases_.get( value ) };
if ( instrList && pp ) {
child( *pp ) = NewOwned< T_ValuedCase >( *this , value ,
std::move( instrList ) );
} else if ( instrList ) {
cases_.add( value , children_.size( ) );
children_.add( NewOwned< T_ValuedCase >( *this , value ,
std::move( instrList ) ) );
} else if ( pp ) {
const auto idx{ *pp };
cases_.remove( value );
handleRemoval( idx );
}
}
void T_CondInstrNode::setDefaultCase(
P_InstrListNode defaultCase ) noexcept
{
if ( !defaultCase ) {
if ( !defaultCase_ ) {
return;
}
const auto idx{ *defaultCase_ };
defaultCase_.clear( );
handleRemoval( idx );
} else if ( defaultCase_ ) {
child( *defaultCase_ ) = NewOwned< T_DefaultCase >(
*this , std::move( defaultCase ) );
} else {
defaultCase_ = children_.size( );
children_.add( NewOwned< T_DefaultCase >(
*this , std::move( defaultCase ) ) );
}
}