#include "externals.hh" #include "c-opopt.hh" #include "c-sync.hh" using namespace ebcl; using namespace opast; using namespace opopt; // Macro to use either the specified visitor or a visitor created just for the // occation #define M_VISITOR_( E ) \ T_OwnPtr< T_Visitor< A_Node > > localVisitor_; \ if ( !(E) ) { \ localVisitor_ = NewOwned< T_Visitor< A_Node > >( ASTVisitorBrowser ); \ } \ T_Visitor< A_Node >& visitor{ (E) ? *(E) : *localVisitor_ } /*= CONSTANT FOLDING =========================================================*/ namespace { struct T_ConstantFolder_ { // Input T_Optional< std::pair< uint32_t , uint32_t > > fixedSize; T_SyncCurves const* curves; // Result bool didFold{ false }; bool operator()( A_Node& node , bool exit ) noexcept; private: template< typename T > void handleParentNode( A_Node& node , std::function< A_ExpressionNode&( T& ) > get , std::function< void( T& , P_ExpressionNode ) > set ) noexcept; P_ExpressionNode checkExpression( A_ExpressionNode& node ) noexcept; // Handle identifiers. If the size is fixed and the identifier is // either width or height, replace it with the appropriate value. P_ExpressionNode doIdExpr( T_IdentifierExprNode& node ) noexcept; // Transform an unary operator applied to a constant into a constant. P_ExpressionNode doUnaryOp( A_Node& parent , T_UnaryOperatorNode::E_Operator op , double value ) const noexcept; // Transform a binary operator applied to a constant into a constant. P_ExpressionNode doBinaryOp( A_Node& parent , T_BinaryOperatorNode::E_Operator op , double left , double right ) const noexcept; }; /*----------------------------------------------------------------------------*/ bool T_ConstantFolder_::operator()( A_Node& node , const bool exit ) noexcept { if ( exit ) { return true; } switch ( node.type( ) ) { case A_Node::TN_ARG: handleParentNode< T_ArgumentNode >( node , []( auto& n ) -> A_ExpressionNode& { return n.expression( ); } , []( auto& n , P_ExpressionNode e ) { n.expression( std::move( e ) ); } ); return false; case A_Node::TN_CONDITION: handleParentNode< T_CondInstrNode::T_Expression >( node , []( auto& n ) -> A_ExpressionNode& { return n.expression( ); } , []( auto& n , P_ExpressionNode e ) { n.expression( std::move( e ) ); } ); return false; case A_Node::OP_SET: handleParentNode< T_SetInstrNode >( node , []( auto& n ) -> A_ExpressionNode& { return n.expression( ); } , []( auto& n , P_ExpressionNode e ) { n.setExpression( std::move( e ) ); } ); return false; default: return true; } } /*----------------------------------------------------------------------------*/ template< typename T > void T_ConstantFolder_::handleParentNode( A_Node& n , std::function< A_ExpressionNode&( T& ) > get , std::function< void( T& , P_ExpressionNode ) > set ) noexcept { auto& node{ (T&) n }; auto r{ checkExpression( get( node ) ) }; if ( r ) { r->location( ) = node.location( ); set( node , std::move( r ) ); didFold = true; } } P_ExpressionNode T_ConstantFolder_::checkExpression( A_ExpressionNode& node ) noexcept { // Already a constant if ( node.type( ) == A_Node::EXPR_CONST ) { return {}; } // Replace $width/$height with value if fixedSize if ( node.type( ) == A_Node::EXPR_ID ) { return doIdExpr( (T_IdentifierExprNode&) node ); } // Replace inputs with value if no curve/constant curve if ( node.type( ) == A_Node::EXPR_INPUT ) { if ( !curves ) { return {}; } auto& n{ (T_InputExprNode&) node }; auto const* const curve{ curves->curves.get( n.id( ) ) }; if ( curve ) { // Curve present, check if it's constant const auto cval{ curve->isConstant( ) }; if ( !cval ) { return {}; } return NewOwned< T_ConstantExprNode >( node.parent( ) , *cval ); } // TODO: check whether there's only one default value; if that's the case, // well, we got ourselves a constant. return {}; } // Replace UnOp( Cnst ) with result auto* const asUnary{ dynamic_cast< T_UnaryOperatorNode* >( &node ) }; if ( asUnary ) { handleParentNode< T_UnaryOperatorNode >( *asUnary , []( auto& n ) -> A_ExpressionNode& { return n.argument( ); } , []( auto& n , P_ExpressionNode e ) { n.setArgument( std::move( e ) ); } ); if ( asUnary->argument( ).type( ) == A_Node::EXPR_CONST ) { auto const& cn{ (T_ConstantExprNode const&) asUnary->argument( ) }; return doUnaryOp( asUnary->parent( ) , asUnary->op( ) , cn.floatValue( ) ); } return {}; } // Replace BinOp( Cnst , Cnst ) with result auto* const asBinary{ dynamic_cast< T_BinaryOperatorNode* >( &node ) }; assert( asBinary && "Missing support for some expr subtype" ); handleParentNode< T_BinaryOperatorNode >( *asBinary , []( auto& n ) -> A_ExpressionNode& { return n.left( ); } , []( auto& n , P_ExpressionNode e ) { n.setLeft( std::move( e ) ); } ); handleParentNode< T_BinaryOperatorNode >( *asBinary , []( auto& n ) -> A_ExpressionNode& { return n.right( ); } , []( auto& n , P_ExpressionNode e ) { n.setRight( std::move( e ) ); } ); if ( asBinary->left( ).type( ) == A_Node::EXPR_CONST && asBinary->right( ).type( ) == A_Node::EXPR_CONST ) { auto const& l{ (T_ConstantExprNode const&) asBinary->left( ) }; auto const& r{ (T_ConstantExprNode const&) asBinary->right( ) }; return doBinaryOp( asBinary->parent( ) , asBinary->op( ), l.floatValue( ) , r.floatValue( ) ); } return {}; } /*----------------------------------------------------------------------------*/ P_ExpressionNode T_ConstantFolder_::doIdExpr( T_IdentifierExprNode& node ) noexcept { if ( !fixedSize ) { return {}; } if ( node.id( ) == "width" ) { return NewOwned< T_ConstantExprNode >( node.parent( ) , double( fixedSize->first ) ); } if ( node.id( ) == "height" ) { return NewOwned< T_ConstantExprNode >( node.parent( ) , float( fixedSize->second ) ); } return {}; } P_ExpressionNode T_ConstantFolder_::doUnaryOp( A_Node& parent , const T_UnaryOperatorNode::E_Operator op , const double value ) const noexcept { const double rVal{ []( const auto op , const auto value ) { switch ( op ) { case T_UnaryOperatorNode::NEG: return -value; case T_UnaryOperatorNode::NOT: return value ? 0. : 1.; case T_UnaryOperatorNode::INV: // TODO check if 0 return 1. / value; case T_UnaryOperatorNode::COS: return cos( value ); case T_UnaryOperatorNode::SIN: return sin( value ); case T_UnaryOperatorNode::TAN: // TODO check if valid return tan( value ); case T_UnaryOperatorNode::SQRT: // TODO check if >= 0 return sqrt( value ); case T_UnaryOperatorNode::LN: // TODO check if > 0 return log( value ); case T_UnaryOperatorNode::EXP: return exp( value ); } fprintf( stderr , "invalid operator %d\n" , int( op ) ); std::abort( ); }( op , value ) }; return NewOwned< T_ConstantExprNode >( parent , rVal ); } P_ExpressionNode T_ConstantFolder_::doBinaryOp( A_Node& parent , const T_BinaryOperatorNode::E_Operator op , const double left , const double right ) const noexcept { const double rVal{ []( const auto op , const auto l , const auto r ) { switch ( op ) { case T_BinaryOperatorNode::ADD: return l + r; case T_BinaryOperatorNode::SUB: return l - r; case T_BinaryOperatorNode::MUL: return l * r; case T_BinaryOperatorNode::DIV: // TODO: check r != 0 return l / r; case T_BinaryOperatorNode::POW: // TODO check operands return pow( l , r ); case T_BinaryOperatorNode::CMP_EQ: return ( l == r ) ? 1. : 0.; case T_BinaryOperatorNode::CMP_NE: return ( l != r ) ? 1. : 0.; case T_BinaryOperatorNode::CMP_GT: return ( l > r ) ? 1. : 0.; case T_BinaryOperatorNode::CMP_GE: return ( l >= r ) ? 1. : 0.; case T_BinaryOperatorNode::CMP_LT: return ( l < r ) ? 1. : 0.; case T_BinaryOperatorNode::CMP_LE: return ( l <= r ) ? 1. : 0.; } fprintf( stderr , "invalid operator %d\n" , int( op ) ); std::abort( ); }( op , left , right ) }; return NewOwned< T_ConstantExprNode >( parent , rVal ); } } // namespace /*----------------------------------------------------------------------------*/ bool opopt::FoldConstants( T_RootNode& root , const T_Optional< std::pair< uint32_t , uint32_t > > fixedSize , T_SyncCurves const* curves , T_Visitor< A_Node >* const extVisitor ) noexcept { M_VISITOR_( extVisitor ); T_ConstantFolder_ folder; folder.fixedSize = fixedSize; folder.curves = curves; visitor.visit( root , folder ); return folder.didFold; } /*= DEAD CODE REMOVAL ========================================================*/ bool opopt::RemoveDeadCode( opast::T_RootNode& root , ebcl::T_Visitor< opast::A_Node >* extVisitor ) noexcept { M_VISITOR_( extVisitor ); #warning blargh return false; }