demotool/c-opopt.cc

321 lines
8.7 KiB
C++
Raw Normal View History

#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 <anon>
/*----------------------------------------------------------------------------*/
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;
}