2017-12-01 07:30:03 +01:00
|
|
|
#include "externals.hh"
|
|
|
|
|
|
|
|
#include "c-opopt.hh"
|
2017-12-02 10:07:14 +01:00
|
|
|
#include "c-ops.hh"
|
2017-12-01 07:30:03 +01:00
|
|
|
#include "c-sync.hh"
|
|
|
|
|
|
|
|
using namespace ebcl;
|
|
|
|
using namespace opast;
|
|
|
|
using namespace opopt;
|
|
|
|
|
|
|
|
|
2017-12-03 18:35:56 +01:00
|
|
|
/*= T_OptData ================================================================*/
|
|
|
|
|
2017-12-03 13:05:54 +01:00
|
|
|
#define M_LOGSTR_( S , L ) \
|
2017-12-03 18:35:56 +01:00
|
|
|
logger( [](){ return T_StringBuilder{ S }; } , L )
|
2017-12-03 13:05:54 +01:00
|
|
|
|
2017-12-03 23:15:44 +01:00
|
|
|
uint32_t opopt::ComputeHash(
|
|
|
|
T_OptData::T_VarId const& id ) noexcept
|
|
|
|
{
|
|
|
|
const uint32_t nh{ ComputeHash( id.name ) };
|
|
|
|
const uint32_t oh{ id.type != T_OptData::E_UDVarType::GLOBAL
|
|
|
|
? ComputeHash( id.name )
|
|
|
|
: 0
|
|
|
|
};
|
|
|
|
return ( uint32_t( id.type ) << 8 )
|
|
|
|
^ nh
|
|
|
|
^ ( ( oh << 29 ) | ( oh >> 3 ) );
|
|
|
|
}
|
|
|
|
|
2017-12-10 11:12:45 +01:00
|
|
|
T_StringBuilder& opopt::operator<<(
|
|
|
|
T_StringBuilder& obj ,
|
|
|
|
T_OptData::T_CtrlFlowEdge const& value ) noexcept
|
|
|
|
{
|
|
|
|
obj << value.target;
|
2017-12-10 11:57:07 +01:00
|
|
|
switch ( value.type ) {
|
|
|
|
case T_OptData::T_CtrlFlowEdge::CALL:
|
2017-12-10 11:12:45 +01:00
|
|
|
obj << "{c}";
|
2017-12-10 11:57:07 +01:00
|
|
|
break;
|
|
|
|
case T_OptData::T_CtrlFlowEdge::RET:
|
2017-12-10 11:12:45 +01:00
|
|
|
obj << "{r}";
|
2017-12-10 11:57:07 +01:00
|
|
|
break;
|
|
|
|
case T_OptData::T_CtrlFlowEdge::BYPASS:
|
|
|
|
obj << "{b}";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_OptData::T_CtrlFlowEdge::FLOW:
|
|
|
|
break;
|
2017-12-10 11:12:45 +01:00
|
|
|
}
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2017-12-08 07:38:59 +01:00
|
|
|
constexpr uint32_t T_OptData::CFG_ENTER;
|
|
|
|
constexpr uint32_t T_OptData::CFG_MAINLOOP;
|
|
|
|
constexpr uint32_t T_OptData::CFG_END;
|
2017-12-03 13:05:54 +01:00
|
|
|
|
2017-12-03 18:35:56 +01:00
|
|
|
/*= T_OptData - INPUT DECLARATIONS ===========================================*/
|
2017-12-02 10:40:47 +01:00
|
|
|
|
|
|
|
void T_OptData::findInputDecls(
|
|
|
|
T_OpsParserOutput& program ) noexcept
|
|
|
|
{
|
|
|
|
if ( inputDecls ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
inputDecls = T_KeyValueTable< T_String , T_Array< T_InputDecl > >{ };
|
|
|
|
visitor.visit( program.root , [this]( A_Node& node , const bool exit ) {
|
|
|
|
if ( exit && node.type( ) == A_Node::OP_INPUT ) {
|
|
|
|
auto& input{ (T_InputInstrNode&) node };
|
|
|
|
auto* da{ inputDecls->get( input.id( ) ) };
|
|
|
|
if ( !da ) {
|
|
|
|
inputDecls->add( input.id( ) , T_Array< T_InputDecl >{ } );
|
|
|
|
da = inputDecls->get( input.id( ) );
|
|
|
|
}
|
|
|
|
da->add( T_InputDecl{ input.location( ) , input.defValue( ) } );
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-03 18:35:56 +01:00
|
|
|
/*= T_OptData - INSTRUCTION NUMBERING ========================================*/
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
bool ODNIVisitor_(
|
|
|
|
A_Node& node ,
|
|
|
|
const bool exit ,
|
|
|
|
T_OptData& oData ,
|
|
|
|
const uint32_t fnIndex ) noexcept
|
|
|
|
{
|
|
|
|
if ( dynamic_cast< A_ExpressionNode* >( &node ) ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
auto* const iptr{
|
|
|
|
dynamic_cast< A_InstructionNode* >( &node ) };
|
|
|
|
if ( iptr && !exit ) {
|
|
|
|
auto const& il{ dynamic_cast< T_InstrListNode& >( iptr->parent( ) ) };
|
2017-12-03 23:15:44 +01:00
|
|
|
const auto hash{ ebcl::ComputeHash( (uint64_t)iptr ) };
|
2017-12-03 18:35:56 +01:00
|
|
|
oData.instrIndex.add( hash );
|
|
|
|
oData.instructions.add( T_OptData::T_InstrPos{
|
|
|
|
oData.instructions.size( ) , iptr ,
|
|
|
|
iptr == &il.node( il.size( ) - 1 ) ,
|
|
|
|
fnIndex } );
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace <anon>
|
|
|
|
|
|
|
|
void T_OptData::numberInstructions(
|
|
|
|
T_OpsParserOutput& program ) noexcept
|
|
|
|
{
|
|
|
|
instructions.clear( );
|
|
|
|
instrIndex.clear( );
|
|
|
|
|
|
|
|
const auto nf{ program.root.nFunctions( ) };
|
|
|
|
for ( auto i = 0u ; i < nf ; i ++ ) {
|
|
|
|
visitor.visit( program.root.function( i ) ,
|
|
|
|
[&]( A_Node& node , const bool exit ) {
|
|
|
|
return ODNIVisitor_( node , exit , *this , i );
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t T_OptData::indexOf(
|
|
|
|
opast::A_InstructionNode const& instr ) noexcept
|
|
|
|
{
|
2017-12-03 23:15:44 +01:00
|
|
|
const auto hash{ ebcl::ComputeHash( (uint64_t)&instr ) };
|
2017-12-03 18:35:56 +01:00
|
|
|
uint32_t existing{ instrIndex.first( hash ) };
|
|
|
|
while ( existing != T_HashIndex::INVALID_INDEX ) {
|
|
|
|
if ( &instr == instructions[ existing ].node ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
existing = instrIndex.next( existing );
|
|
|
|
}
|
|
|
|
assert( existing != T_HashIndex::INVALID_INDEX );
|
|
|
|
return existing;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-03 23:15:44 +01:00
|
|
|
/*= T_OptData - CONTROL FLOW GRAPH CONSTRUCTION ==============================*/
|
2017-12-03 18:35:56 +01:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
#warning Remove this later
|
2017-12-03 23:15:44 +01:00
|
|
|
#define LL1 2
|
|
|
|
#define LL2 2
|
2017-12-03 18:35:56 +01:00
|
|
|
|
2017-12-03 23:15:44 +01:00
|
|
|
// CFG type shortcuts
|
2017-12-03 18:35:56 +01:00
|
|
|
using T_CFN_ = T_OptData::T_CtrlFlowNode;
|
|
|
|
using P_CFN_ = T_OptData::P_CtrlFlowNode;
|
|
|
|
|
2017-12-03 23:15:44 +01:00
|
|
|
// Helpers to create or re-use CFG nodes
|
2017-12-03 18:35:56 +01:00
|
|
|
T_OptData::P_CtrlFlowNode BCFGNewNode_(
|
|
|
|
T_Array< P_CFN_ >& pool ) noexcept
|
|
|
|
{
|
|
|
|
if ( pool.empty( ) ) {
|
|
|
|
return NewOwned< T_CFN_ >( );
|
|
|
|
}
|
|
|
|
|
|
|
|
auto r{ std::move( pool.last( ) ) };
|
|
|
|
pool.removeLast( );
|
|
|
|
r->instructions.clear( );
|
|
|
|
r->inbound.clear( );
|
|
|
|
r->outbound.clear( );
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define M_NEWNODE_() BCFGNewNode_( old )
|
|
|
|
#define M_ADDNEW_() \
|
2017-12-08 07:38:59 +01:00
|
|
|
ctrlFlowGraph.add( M_NEWNODE_( ) )
|
|
|
|
#define M_NODE_(i) \
|
|
|
|
ctrlFlowGraph[i]
|
2017-12-03 18:35:56 +01:00
|
|
|
|
2017-12-10 10:14:35 +01:00
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
using T_BCFGFunctions_ = T_KeyValueTable< T_String , T_OptData::T_BasicBlock >;
|
|
|
|
|
|
|
|
inline void BCFGFuncEnter_(
|
|
|
|
A_Node& node ,
|
|
|
|
T_Optional< uint32_t >& cNode ,
|
|
|
|
T_Array< T_OptData::P_CtrlFlowNode >& ctrlFlowGraph ,
|
|
|
|
T_Array< T_OptData::P_CtrlFlowNode >& old ,
|
|
|
|
T_BCFGFunctions_& cfgFunctions ,
|
|
|
|
F_OPLogger const& logger
|
|
|
|
) noexcept
|
|
|
|
{
|
|
|
|
auto& n{ dynamic_cast< A_FuncNode& >( node ) };
|
|
|
|
auto const& fn{ n.name( ) };
|
|
|
|
logger( [&](){
|
|
|
|
T_StringBuilder sb;
|
|
|
|
sb << "Starting function '" << fn << "' at "
|
|
|
|
<< ctrlFlowGraph.size( );
|
|
|
|
return sb;
|
|
|
|
} , LL1 );
|
|
|
|
cfgFunctions.add( fn , T_OptData::T_BasicBlock{
|
|
|
|
ctrlFlowGraph.size( ) } );
|
|
|
|
cNode = M_ADDNEW_( );
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void BCFGFuncExit_(
|
|
|
|
A_Node& node ,
|
|
|
|
T_Optional< uint32_t >& cNode ,
|
|
|
|
T_Array< T_OptData::P_CtrlFlowNode >& ctrlFlowGraph ,
|
|
|
|
T_BCFGFunctions_& cfgFunctions ,
|
|
|
|
F_OPLogger const& logger
|
|
|
|
) noexcept
|
|
|
|
{
|
|
|
|
auto& n{ dynamic_cast< A_FuncNode& >( node ) };
|
|
|
|
auto const& fn{ n.name( ) };
|
|
|
|
logger( [&](){
|
|
|
|
T_StringBuilder sb;
|
|
|
|
sb << "Function ended; last block had "
|
|
|
|
<< ( M_NODE_( *cNode )->instructions
|
|
|
|
? M_NODE_( *cNode )->instructions->count
|
|
|
|
: 0 )
|
|
|
|
<< " instructions";
|
|
|
|
return sb;
|
|
|
|
} , LL1 );
|
|
|
|
auto* frec{ cfgFunctions.get( fn ) };
|
|
|
|
assert( frec );
|
|
|
|
frec->count = ctrlFlowGraph.size( ) - frec->first;
|
|
|
|
cNode.clear( );
|
|
|
|
}
|
2017-12-03 18:35:56 +01:00
|
|
|
|
2017-12-03 23:15:44 +01:00
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
2017-12-10 10:14:35 +01:00
|
|
|
// Data structure to handle conditionals
|
|
|
|
struct T_BCFGStackEntry_ {
|
|
|
|
uint32_t condBlock;
|
|
|
|
bool hasDefault{ false };
|
|
|
|
T_AutoArray< uint32_t , 8 > caseBlocks;
|
|
|
|
};
|
|
|
|
using T_BCFGStack_ = T_AutoArray< T_BCFGStackEntry_ , 8 >;
|
|
|
|
|
|
|
|
inline void BCFGCondEnter_(
|
|
|
|
T_BCFGStack_& stack ,
|
|
|
|
T_Optional< uint32_t >& cNode ,
|
|
|
|
T_Array< T_OptData::P_CtrlFlowNode >& ctrlFlowGraph ,
|
|
|
|
F_OPLogger const& logger ) noexcept
|
2017-12-03 18:35:56 +01:00
|
|
|
{
|
2017-12-10 10:14:35 +01:00
|
|
|
auto& se{ stack.addNew( ) };
|
|
|
|
se.condBlock = *cNode;
|
|
|
|
cNode.clear( );
|
|
|
|
logger( [&](){
|
|
|
|
T_StringBuilder sb;
|
|
|
|
sb << "Entering conditional instruction, stack size "
|
|
|
|
<< stack.size( )
|
|
|
|
<< ", block had "
|
|
|
|
<< ( M_NODE_( se.condBlock )->instructions
|
|
|
|
? M_NODE_( se.condBlock )->instructions->count
|
|
|
|
: 0 )
|
|
|
|
<< " instructions";
|
|
|
|
return sb;
|
|
|
|
} , LL2 );
|
|
|
|
}
|
2017-12-03 18:35:56 +01:00
|
|
|
|
2017-12-10 10:14:35 +01:00
|
|
|
inline void BCFGCondExit_(
|
|
|
|
T_BCFGStack_& stack ,
|
|
|
|
T_Optional< uint32_t >& cNode ,
|
|
|
|
T_Array< T_OptData::P_CtrlFlowNode >& ctrlFlowGraph ,
|
|
|
|
T_Array< T_OptData::P_CtrlFlowNode >& old ,
|
|
|
|
F_OPLogger const& logger ) noexcept
|
|
|
|
{
|
|
|
|
auto& se{ stack.last( ) };
|
|
|
|
cNode = M_ADDNEW_( );
|
|
|
|
|
|
|
|
// Connect each case block to both the condition
|
|
|
|
// and the next block
|
|
|
|
const auto ncb{ se.caseBlocks.size( ) };
|
|
|
|
for ( auto i = 0u ; i < ncb ; i ++ ) {
|
|
|
|
auto& cbi{ se.caseBlocks[ i ] };
|
|
|
|
auto& cb{ *M_NODE_( cbi ) };
|
|
|
|
cb.inbound.add( se.condBlock );
|
|
|
|
M_NODE_( se.condBlock )->outbound.add( cbi );
|
|
|
|
cb.outbound.add( *cNode );
|
|
|
|
M_NODE_( *cNode )->inbound.add( cbi );
|
|
|
|
}
|
|
|
|
if ( !se.hasDefault ) {
|
|
|
|
M_NODE_( *cNode )->inbound.add( se.condBlock );
|
|
|
|
M_NODE_( se.condBlock )->outbound.add( *cNode );
|
|
|
|
}
|
2017-12-03 18:35:56 +01:00
|
|
|
|
2017-12-10 10:14:35 +01:00
|
|
|
stack.removeLast( );
|
|
|
|
logger( [&](){
|
|
|
|
T_StringBuilder sb;
|
|
|
|
sb << "Exiting conditional instruction, stack size "
|
|
|
|
<< stack.size( );
|
|
|
|
return sb;
|
|
|
|
} , LL2 );
|
|
|
|
}
|
2017-12-03 18:35:56 +01:00
|
|
|
|
2017-12-10 10:14:35 +01:00
|
|
|
/*----------------------------------------------------------------------------*/
|
2017-12-03 18:35:56 +01:00
|
|
|
|
2017-12-10 10:14:35 +01:00
|
|
|
inline bool BCFGVisitor_(
|
|
|
|
A_Node& node ,
|
|
|
|
const bool exit ,
|
|
|
|
T_OptData& data ,
|
|
|
|
T_BCFGStack_& stack ,
|
|
|
|
T_Optional< uint32_t >& cNode ,
|
|
|
|
T_Array< T_OptData::P_CtrlFlowNode >& old
|
|
|
|
) noexcept
|
|
|
|
{
|
|
|
|
const auto nt{ node.type( ) };
|
|
|
|
|
|
|
|
// Handle start/end of functions
|
|
|
|
if ( nt == A_Node::DECL_FN || nt == A_Node::DECL_INIT
|
|
|
|
|| nt == A_Node::DECL_FRAME ) {
|
|
|
|
if ( exit ) {
|
|
|
|
assert( stack.empty( ) );
|
|
|
|
BCFGFuncExit_( node , cNode , data.ctrlFlowGraph ,
|
|
|
|
data.cfgFunctions , data.logger );
|
|
|
|
} else {
|
|
|
|
BCFGFuncEnter_( node , cNode , data.ctrlFlowGraph ,
|
|
|
|
old , data.cfgFunctions ,
|
|
|
|
data.logger );
|
2017-12-03 18:35:56 +01:00
|
|
|
}
|
2017-12-10 10:14:35 +01:00
|
|
|
return true;
|
|
|
|
}
|
2017-12-03 18:35:56 +01:00
|
|
|
|
2017-12-10 10:14:35 +01:00
|
|
|
// All instructions: continue the current basic block
|
|
|
|
auto* const iptr{ dynamic_cast< A_InstructionNode* >( &node ) };
|
|
|
|
if ( iptr && !exit ) {
|
|
|
|
assert( cNode );
|
|
|
|
auto& n{ *data.ctrlFlowGraph[ *cNode ] };
|
|
|
|
if ( n.instructions ) {
|
|
|
|
n.instructions->count ++;
|
|
|
|
} else {
|
|
|
|
n.instructions = T_OptData::T_BasicBlock{
|
|
|
|
data.indexOf( *iptr ) };
|
2017-12-03 18:35:56 +01:00
|
|
|
}
|
2017-12-10 10:14:35 +01:00
|
|
|
}
|
2017-12-03 18:35:56 +01:00
|
|
|
|
2017-12-10 10:14:35 +01:00
|
|
|
// Handle conditionals
|
|
|
|
if ( nt == A_Node::OP_COND ) {
|
|
|
|
if ( exit ) {
|
|
|
|
BCFGCondExit_( stack , cNode , data.ctrlFlowGraph ,
|
|
|
|
old , data.logger );
|
|
|
|
} else {
|
|
|
|
BCFGCondEnter_( stack , cNode , data.ctrlFlowGraph ,
|
|
|
|
data.logger );
|
2017-12-03 18:35:56 +01:00
|
|
|
}
|
2017-12-10 10:14:35 +01:00
|
|
|
return true;
|
|
|
|
}
|
2017-12-03 18:35:56 +01:00
|
|
|
|
2017-12-10 10:14:35 +01:00
|
|
|
// Calls also break the flow
|
|
|
|
if ( nt == A_Node::OP_CALL && !exit ) {
|
|
|
|
T_CallInstrNode& ci{ *dynamic_cast< T_CallInstrNode* >( iptr ) };
|
|
|
|
data.logger( [&](){
|
|
|
|
T_StringBuilder sb;
|
|
|
|
auto const& node{ *data.ctrlFlowGraph[ *cNode ] };
|
|
|
|
sb << "Call to " << ci.id( ) << ", block had "
|
|
|
|
<< ( node.instructions
|
|
|
|
? node.instructions->count
|
|
|
|
: 0 )
|
|
|
|
<< " instructions";
|
|
|
|
return sb;
|
|
|
|
} , LL2 );
|
|
|
|
|
|
|
|
auto& cs{ data.callSites.addNew( ) };
|
|
|
|
cs.name = ci.id( );
|
|
|
|
cs.callBlock = *cNode;
|
|
|
|
cNode = cs.retBlock = data.ctrlFlowGraph.add( M_NEWNODE_( ) );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Condition case nodes: create new basic block, add to stack's list
|
|
|
|
if ( nt == A_Node::TN_CASE || nt == A_Node::TN_DEFAULT ) {
|
|
|
|
if ( exit ) {
|
|
|
|
data.logger( [&](){
|
2017-12-03 18:35:56 +01:00
|
|
|
T_StringBuilder sb;
|
2017-12-10 10:14:35 +01:00
|
|
|
auto const& node{ *data.ctrlFlowGraph[ *cNode ] };
|
|
|
|
sb << "Case block added ("
|
|
|
|
<< ( node.instructions
|
|
|
|
? node.instructions->count
|
2017-12-08 07:38:59 +01:00
|
|
|
: 0 )
|
2017-12-10 10:14:35 +01:00
|
|
|
<< " instructions)";
|
2017-12-03 18:35:56 +01:00
|
|
|
return sb;
|
|
|
|
} , LL2 );
|
2017-12-10 10:14:35 +01:00
|
|
|
cNode.clear( );
|
|
|
|
} else {
|
|
|
|
stack.last( ).hasDefault = stack.last( ).hasDefault
|
|
|
|
|| ( nt == A_Node::TN_DEFAULT );
|
|
|
|
cNode = data.ctrlFlowGraph.add( M_NEWNODE_( ) );
|
|
|
|
stack.last( ).caseBlocks.add( *cNode );
|
2017-12-03 18:35:56 +01:00
|
|
|
}
|
2017-12-10 10:14:35 +01:00
|
|
|
}
|
2017-12-03 18:35:56 +01:00
|
|
|
|
2017-12-10 10:14:35 +01:00
|
|
|
return !dynamic_cast< A_ExpressionNode* >( &node );
|
|
|
|
}
|
2017-12-03 18:35:56 +01:00
|
|
|
|
2017-12-10 10:14:35 +01:00
|
|
|
/*----------------------------------------------------------------------------*/
|
2017-12-03 18:35:56 +01:00
|
|
|
|
2017-12-10 10:14:35 +01:00
|
|
|
inline void BCFGHandleCalls_(
|
|
|
|
T_OptData& data ) noexcept
|
|
|
|
{
|
2017-12-03 18:35:56 +01:00
|
|
|
// Add fake call sites for *init* and *frame*
|
|
|
|
{
|
2017-12-10 10:14:35 +01:00
|
|
|
auto& cs{ data.callSites.addNew( ) };
|
|
|
|
cs.callBlock = T_OptData::CFG_ENTER;
|
|
|
|
cs.retBlock = T_OptData::CFG_MAINLOOP;
|
2017-12-03 18:35:56 +01:00
|
|
|
cs.name = "*init*";
|
|
|
|
}
|
|
|
|
{
|
2017-12-10 10:14:35 +01:00
|
|
|
auto& cs{ data.callSites.addNew( ) };
|
|
|
|
cs.callBlock = T_OptData::CFG_MAINLOOP;
|
|
|
|
cs.retBlock = T_OptData::CFG_MAINLOOP;
|
2017-12-03 18:35:56 +01:00
|
|
|
cs.name = "*frame*";
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle calls
|
2017-12-10 11:12:45 +01:00
|
|
|
constexpr auto tCall{ T_OptData::T_CtrlFlowEdge::CALL };
|
|
|
|
constexpr auto tRet{ T_OptData::T_CtrlFlowEdge::RET };
|
2017-12-10 11:57:07 +01:00
|
|
|
constexpr auto tBypass{ T_OptData::T_CtrlFlowEdge::BYPASS };
|
2017-12-10 10:14:35 +01:00
|
|
|
for ( auto const& cs : data.callSites ) {
|
|
|
|
auto const* frec{ data.cfgFunctions.get( cs.name ) };
|
2017-12-03 18:35:56 +01:00
|
|
|
assert( frec );
|
2017-12-10 11:12:45 +01:00
|
|
|
|
|
|
|
|
|
|
|
const auto nExit{ frec->first + frec->count - 1 };
|
|
|
|
auto& bCall{ *data.ctrlFlowGraph[ cs.callBlock ] };
|
|
|
|
auto& bRet{ *data.ctrlFlowGraph[ cs.retBlock ] };
|
|
|
|
auto& bEntry{ *data.ctrlFlowGraph[ frec->first ] };
|
|
|
|
auto& bExit{ *data.ctrlFlowGraph[ nExit ] };
|
|
|
|
|
|
|
|
// Call
|
|
|
|
bEntry.inbound.addNew( cs.callBlock , tCall );
|
|
|
|
bCall.outbound.addNew( frec->first , tCall );
|
|
|
|
|
|
|
|
// Return
|
|
|
|
bExit.outbound.addNew( cs.retBlock , tRet );
|
|
|
|
bRet.inbound.addNew( nExit , tRet );
|
|
|
|
|
2017-12-10 11:57:07 +01:00
|
|
|
// Call bypass
|
|
|
|
bCall.outbound.addNew( cs.retBlock , tBypass );
|
|
|
|
bRet.inbound.addNew( cs.callBlock , tBypass );
|
2017-12-03 18:35:56 +01:00
|
|
|
}
|
2017-12-10 10:14:35 +01:00
|
|
|
}
|
2017-12-08 21:57:41 +01:00
|
|
|
|
2017-12-10 10:14:35 +01:00
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
inline T_StringBuilder BCFGDumpAll_(
|
|
|
|
T_OptData const& data ) noexcept
|
|
|
|
{
|
|
|
|
T_StringBuilder dump;
|
|
|
|
|
|
|
|
int i{ 0 };
|
|
|
|
dump << "Control flow graph dump\n";
|
|
|
|
for ( auto const& p : data.ctrlFlowGraph ) {
|
|
|
|
auto const& e{ *p };
|
|
|
|
dump << "\nNode " << i++ << "\n\t";
|
|
|
|
if ( e.instructions ) {
|
|
|
|
dump << e.instructions->count
|
|
|
|
<< " instruction(s) at index "
|
|
|
|
<< e.instructions->first;
|
|
|
|
} else {
|
|
|
|
dump << "No instructions";
|
|
|
|
}
|
|
|
|
dump << "\n\tInbound:";
|
|
|
|
{
|
|
|
|
const auto ni{ e.inbound.size( ) };
|
|
|
|
if ( ni == 0 ) {
|
|
|
|
dump << " NONE";
|
2017-12-08 21:57:41 +01:00
|
|
|
}
|
2017-12-10 10:14:35 +01:00
|
|
|
for ( auto idx = 0u ; idx < ni ; idx ++ ) {
|
|
|
|
dump << ' ' << e.inbound[ idx ];
|
2017-12-08 21:57:41 +01:00
|
|
|
}
|
2017-12-10 10:14:35 +01:00
|
|
|
}
|
|
|
|
dump << "\n\tOutbound:";
|
|
|
|
{
|
|
|
|
const auto no{ e.outbound.size( ) };
|
|
|
|
if ( no == 0 ) {
|
|
|
|
dump << " NONE";
|
|
|
|
}
|
|
|
|
for ( auto idx = 0u ; idx < no ; idx ++ ) {
|
|
|
|
dump << ' ' << e.outbound[ idx ];
|
2017-12-08 21:57:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
dump << '\n';
|
2017-12-10 10:14:35 +01:00
|
|
|
}
|
|
|
|
dump << '\n';
|
|
|
|
|
|
|
|
return dump;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace <anon>
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
void T_OptData::buildControlFlowGraph(
|
|
|
|
T_OpsParserOutput& program ) noexcept
|
|
|
|
{
|
|
|
|
// Keep the old array, we'll reuse its contents
|
|
|
|
T_Array< P_CtrlFlowNode > old{ std::move( ctrlFlowGraph ) };
|
|
|
|
M_LOGSTR_( "Building control flow graph" , LL1 );
|
|
|
|
|
|
|
|
// Create special nodes
|
|
|
|
M_ADDNEW_( );
|
|
|
|
M_ADDNEW_( );
|
|
|
|
M_ADDNEW_( );
|
|
|
|
M_NODE_( CFG_MAINLOOP )->outbound.add( CFG_END );
|
|
|
|
M_NODE_( CFG_END )->inbound.add( CFG_MAINLOOP );
|
|
|
|
|
|
|
|
// Generate control flow graph for each function
|
|
|
|
T_BCFGStack_ stack;
|
|
|
|
T_Optional< uint32_t > cNode{ };
|
|
|
|
callSites.clear( );
|
|
|
|
visitor.visit( program.root , [&]( A_Node& node , const bool exit ) {
|
|
|
|
return BCFGVisitor_( node , exit , *this ,
|
|
|
|
stack , cNode , old );
|
|
|
|
} );
|
|
|
|
assert( cfgFunctions.contains( "*init*" )
|
|
|
|
&& cfgFunctions.contains( "*frame*" ) );
|
2017-12-08 21:57:41 +01:00
|
|
|
|
2017-12-10 10:14:35 +01:00
|
|
|
BCFGHandleCalls_( *this );
|
|
|
|
logger( [this](){
|
|
|
|
return BCFGDumpAll_( *this );
|
2017-12-08 21:57:41 +01:00
|
|
|
} , LL2 );
|
2017-12-03 18:35:56 +01:00
|
|
|
}
|
|
|
|
|
2017-12-03 23:15:44 +01:00
|
|
|
#undef M_ADDNEW_
|
|
|
|
#undef M_NEWNODE_
|
2017-12-08 07:38:59 +01:00
|
|
|
#undef M_NODE_
|
2017-12-03 23:15:44 +01:00
|
|
|
|
|
|
|
|
|
|
|
/*= T_OptData - USE/DEFINE CHAINS ============================================*/
|
2017-12-04 07:03:54 +01:00
|
|
|
namespace {
|
2017-12-03 23:15:44 +01:00
|
|
|
|
|
|
|
#warning Remove this later
|
|
|
|
#undef LL1
|
|
|
|
#undef LL2
|
|
|
|
#define LL1 1
|
|
|
|
#define LL2 1
|
|
|
|
|
2017-12-04 07:41:09 +01:00
|
|
|
void BUDCAddRecord_(
|
2017-12-04 07:03:54 +01:00
|
|
|
A_Node& n ,
|
2017-12-04 07:16:32 +01:00
|
|
|
T_String const& id ,
|
2017-12-04 07:41:09 +01:00
|
|
|
const bool use ,
|
2017-12-04 07:03:54 +01:00
|
|
|
T_OptData& od ,
|
2017-12-04 07:41:09 +01:00
|
|
|
T_RootNode& root ,
|
|
|
|
T_OptData::T_VarId const* extVarId = nullptr ) noexcept
|
2017-12-04 07:03:54 +01:00
|
|
|
{
|
|
|
|
|
|
|
|
// Find instruction and function index
|
|
|
|
A_FuncNode* func{ nullptr };
|
|
|
|
T_Optional< uint32_t > instrId;
|
2017-12-04 07:16:32 +01:00
|
|
|
A_Node* pn{ &n };
|
2017-12-04 07:03:54 +01:00
|
|
|
while ( pn ) {
|
|
|
|
auto* const asInstr{ dynamic_cast< A_InstructionNode* >( pn ) };
|
|
|
|
func = dynamic_cast< A_FuncNode* >( pn );
|
|
|
|
if ( !instrId && asInstr ) {
|
|
|
|
instrId = od.indexOf( *asInstr );
|
|
|
|
} else if ( func ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pn = &pn->parent( );
|
|
|
|
}
|
|
|
|
assert( func && instrId );
|
|
|
|
|
|
|
|
// Generate the identifier
|
2017-12-04 07:41:09 +01:00
|
|
|
const T_OptData::T_VarId varId{ extVarId ? *extVarId : [&]() {
|
2017-12-04 07:16:32 +01:00
|
|
|
auto const& n{ id };
|
|
|
|
if ( func->hasLocal( id ) ) {
|
2017-12-04 07:03:54 +01:00
|
|
|
return T_OptData::T_VarId{ n , func->name( ) ,
|
2017-12-04 07:16:32 +01:00
|
|
|
func->isArgument( id ) };
|
2017-12-04 07:03:54 +01:00
|
|
|
}
|
|
|
|
return T_OptData::T_VarId{ n };
|
|
|
|
} () };
|
|
|
|
|
|
|
|
// Access or create the record
|
|
|
|
auto* const varRec{ [&]() {
|
|
|
|
auto* const x{ od.varUDChains.get( varId ) };
|
|
|
|
if ( x ) {
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
od.varUDChains.add( T_OptData::T_VarUseDefine{ varId } );
|
|
|
|
return od.varUDChains.get( varId );
|
|
|
|
} () };
|
|
|
|
assert( varRec );
|
|
|
|
|
2017-12-04 07:41:09 +01:00
|
|
|
// Add use/define record
|
|
|
|
auto& udRec{ use ? varRec->uses.addNew( ) : varRec->defines.addNew( ) };
|
|
|
|
udRec.node = *instrId;
|
|
|
|
udRec.fnIndex = root.functionIndex( func->name( ) );
|
2017-12-04 07:03:54 +01:00
|
|
|
|
|
|
|
od.logger( [&](){
|
|
|
|
T_StringBuilder sb;
|
2017-12-04 07:41:09 +01:00
|
|
|
sb << ( use ? "use " : "def " ) << varId.name << " at "
|
|
|
|
<< n.location( ) << " (";
|
2017-12-04 07:03:54 +01:00
|
|
|
if ( varId.type == T_OptData::E_UDVarType::GLOBAL ) {
|
|
|
|
sb << "global";
|
|
|
|
} else {
|
|
|
|
if ( varId.type == T_OptData::E_UDVarType::LOCAL ) {
|
|
|
|
sb << "local";
|
|
|
|
} else {
|
|
|
|
sb << "argument";
|
|
|
|
}
|
|
|
|
sb << " of " << varId.owner;
|
|
|
|
}
|
2017-12-04 07:41:09 +01:00
|
|
|
sb << "), instr #" << *instrId;
|
2017-12-04 07:03:54 +01:00
|
|
|
return sb;
|
|
|
|
} , LL2 );
|
|
|
|
}
|
|
|
|
|
2017-12-06 07:05:26 +01:00
|
|
|
void BUDCVisitor_(
|
|
|
|
T_RootNode& root ,
|
|
|
|
T_OptData& od ,
|
|
|
|
A_Node& n )
|
|
|
|
{
|
|
|
|
switch ( n.type( ) ) {
|
|
|
|
|
|
|
|
default: break;
|
|
|
|
|
2017-12-08 21:57:41 +01:00
|
|
|
case A_Node::EXPR_ID: {
|
|
|
|
auto const& id{ dynamic_cast< T_IdentifierExprNode& >( n ).id( ) };
|
|
|
|
if ( id != "width" && id != "height" && id != "time" ) {
|
|
|
|
BUDCAddRecord_( n , id , true , od , root );
|
|
|
|
}
|
2017-12-06 07:05:26 +01:00
|
|
|
break;
|
2017-12-08 21:57:41 +01:00
|
|
|
}
|
2017-12-06 07:05:26 +01:00
|
|
|
|
|
|
|
case A_Node::OP_UNIFORMS:
|
|
|
|
BUDCAddRecord_( n ,
|
|
|
|
dynamic_cast< T_UniformsInstrNode& >( n ).progId( ) ,
|
|
|
|
true , od , root );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case A_Node::OP_USE_TEXTURE:
|
|
|
|
BUDCAddRecord_( n ,
|
|
|
|
dynamic_cast< T_UseTextureInstrNode& >( n ).samplerId( ) ,
|
|
|
|
true , od , root );
|
|
|
|
// fallthrough
|
|
|
|
case A_Node::OP_USE_PROGRAM:
|
|
|
|
case A_Node::OP_USE_PIPELINE:
|
|
|
|
case A_Node::OP_USE_FRAMEBUFFER:
|
|
|
|
BUDCAddRecord_( n ,
|
|
|
|
dynamic_cast< T_UseInstrNode& >( n ).id( ) ,
|
|
|
|
true , od , root );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case A_Node::OP_PIPELINE: {
|
|
|
|
auto& pln{ dynamic_cast< T_PipelineInstrNode& >( n ) };
|
|
|
|
BUDCAddRecord_( n , pln.id( ) , false , od , root );
|
|
|
|
const auto np{ pln.size( ) };
|
|
|
|
for ( auto i = 0u ; i < np ; i ++ ) {
|
|
|
|
BUDCAddRecord_( n , pln.program( i ) ,
|
|
|
|
true , od , root );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case A_Node::TN_FBATT:
|
|
|
|
BUDCAddRecord_( n , dynamic_cast< T_FramebufferInstrNode::T_Attachment& >( n ).id( ) ,
|
|
|
|
true , od , root );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case A_Node::OP_FRAMEBUFFER:
|
|
|
|
case A_Node::OP_TEXTURE:
|
|
|
|
case A_Node::OP_SAMPLER:
|
|
|
|
case A_Node::OP_PROGRAM:
|
|
|
|
BUDCAddRecord_( n , dynamic_cast< A_ResourceDefInstrNode& >( n ).id( ) ,
|
|
|
|
false , od , root );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case A_Node::OP_SET:
|
|
|
|
BUDCAddRecord_( n , dynamic_cast< T_SetInstrNode& >( n ).id( ) ,
|
|
|
|
false , od , root );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case A_Node::OP_CALL: {
|
|
|
|
auto& cn{ dynamic_cast< T_CallInstrNode& >( n ) };
|
|
|
|
auto& callee{ root.function(
|
|
|
|
root.functionIndex( cn.id( ) ) ) };
|
|
|
|
const auto nlocs{ callee.locals( ) };
|
|
|
|
for ( auto i = 0u ; i < nlocs ; i ++ ) {
|
|
|
|
auto const& name{ callee.getLocalName( i ) };
|
|
|
|
if ( !callee.isArgument( name ) ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const T_OptData::T_VarId vid{ name , callee.name( ) , true };
|
|
|
|
BUDCAddRecord_( n , name , false , od ,
|
|
|
|
root , &vid );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-06 07:42:41 +01:00
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
struct T_UDEntry_
|
|
|
|
{
|
|
|
|
uint32_t entry;
|
|
|
|
bool isUse;
|
|
|
|
uint32_t index;
|
|
|
|
};
|
|
|
|
|
|
|
|
using T_UDEPerInstr_ = T_KeyValueTable< uint32_t , T_AutoArray< T_UDEntry_ , 8 > >;
|
|
|
|
|
|
|
|
template< uint32_t S >
|
|
|
|
void BUDCAddEntries_(
|
|
|
|
T_UDEPerInstr_& out ,
|
|
|
|
const uint32_t mainEntry ,
|
|
|
|
const bool isUse ,
|
|
|
|
T_AutoArray< T_OptData::T_VarUDRecord , S > const& entries ) noexcept
|
|
|
|
{
|
|
|
|
const auto na{ entries.size( ) };
|
|
|
|
for ( auto j = 0u ; j < na ; j ++ ) {
|
|
|
|
auto const& use{ entries[ j ] };
|
|
|
|
auto* rec{ out.get( use.node ) };
|
|
|
|
if ( !rec ) {
|
|
|
|
out.add( use.node , T_AutoArray< T_UDEntry_ , 8 >{ } );
|
|
|
|
rec = out.get( use.node );
|
|
|
|
}
|
|
|
|
assert( rec );
|
|
|
|
|
|
|
|
auto& ne{ rec->addNew( ) };
|
|
|
|
ne.entry = mainEntry;
|
|
|
|
ne.isUse = isUse;
|
|
|
|
ne.index = j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-04 07:03:54 +01:00
|
|
|
|
|
|
|
} // namespace <anon>
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
2017-12-03 23:15:44 +01:00
|
|
|
void T_OptData::buildUseDefineChains(
|
|
|
|
T_OpsParserOutput& program ) noexcept
|
|
|
|
{
|
|
|
|
M_LOGSTR_( "Building use/define chains" , LL1 );
|
|
|
|
varUDChains.clear( );
|
|
|
|
|
2017-12-06 07:42:41 +01:00
|
|
|
// Find all definitions and uses, add them to the table
|
2017-12-03 23:15:44 +01:00
|
|
|
visitor.visit( program.root , [&]( auto& n , const bool exit ) {
|
2017-12-06 07:05:26 +01:00
|
|
|
if ( !exit ) {
|
|
|
|
BUDCVisitor_( program.root , *this , n );
|
2017-12-04 07:16:32 +01:00
|
|
|
}
|
2017-12-03 23:15:44 +01:00
|
|
|
return true;
|
|
|
|
} );
|
2017-12-06 07:42:41 +01:00
|
|
|
|
|
|
|
// Build a table of all variable uses/defines that were identified
|
|
|
|
T_UDEPerInstr_ udPerInstr;
|
|
|
|
auto const& udcEntries{ varUDChains.values( ) };
|
|
|
|
const auto n{ udcEntries.size( ) };
|
|
|
|
for ( auto i = 0u ; i < n ; i ++ ) {
|
|
|
|
auto const& r{ udcEntries[ i ] };
|
|
|
|
BUDCAddEntries_( udPerInstr , i , true , r.uses );
|
|
|
|
BUDCAddEntries_( udPerInstr , i , false , r.defines );
|
|
|
|
}
|
|
|
|
|
2017-12-10 09:59:55 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* So this whole heap of code below is wrong. It wouldn't work correctly
|
|
|
|
* in all cases (although it does with the current test code, but that's
|
|
|
|
* irrelevant).
|
|
|
|
*
|
|
|
|
* A block B from the CFG has instr{B}+1 points (1 point before each
|
|
|
|
* instruction, and 1 point at the end of the block).
|
|
|
|
*
|
|
|
|
* Uses should be assigned to the point preceding the instruction; defs
|
|
|
|
* and kills should be assigned to the point that follows it, before
|
|
|
|
* any uses (it may be simpler to have 2 points / instruction?)
|
|
|
|
*
|
|
|
|
* defs from I0 defs from I1
|
|
|
|
* \/ \/
|
|
|
|
* P00 -> I0 -> P01 -> P10 -> I1 -> I11 -> PBE
|
|
|
|
* /\ /\ /\
|
|
|
|
* uses from I0 uses from I1 defs/kills from block end
|
|
|
|
*
|
|
|
|
* Entering/exiting functions is a bit of a PITA:
|
|
|
|
* - call instructions define function arguments;
|
|
|
|
* - return edges kill locals
|
2017-12-10 11:20:25 +01:00
|
|
|
*
|
|
|
|
* OK, so now that we have edge types, things should be easier, as we
|
|
|
|
* can:
|
|
|
|
* - handle arguments directly; they're immutable and always
|
|
|
|
* defined at the function's call site.
|
|
|
|
* - handle local variables by walking the function's graph,
|
|
|
|
* ignoring all CALL edges and terminating at the RET edge;
|
|
|
|
* - handle globals by following all edges and terminating at the
|
|
|
|
* exit node
|
2017-12-10 09:59:55 +01:00
|
|
|
*/
|
|
|
|
#if 0
|
2017-12-08 07:38:59 +01:00
|
|
|
// Walk the graph from the entry point until all reachable nodes
|
|
|
|
// have been covered and keeping track of active definitions. When
|
|
|
|
// the flow diverges, we need to store the state before the
|
|
|
|
// divergence.
|
|
|
|
const uint32_t nNodes{ ctrlFlowGraph.size( ) };
|
|
|
|
uint32_t nProcessed{ 0 };
|
|
|
|
uint32_t node{ CFG_ENTER };
|
|
|
|
|
|
|
|
bool processedNodes[ nNodes ];
|
|
|
|
memset( processedNodes , 0 , sizeof( processedNodes ) );
|
|
|
|
|
|
|
|
using T_ActDefs_ = T_Array< uint32_t >;
|
|
|
|
using P_ActDefs_ = T_OwnPtr< T_ActDefs_ >;
|
|
|
|
P_ActDefs_ activeDefs{ NewOwned< T_ActDefs_ >( ) };
|
|
|
|
activeDefs->resize( varUDChains.size( ) , T_HashIndex::INVALID_INDEX );
|
|
|
|
|
|
|
|
struct T_StackEntry_ {
|
|
|
|
P_ActDefs_ def;
|
|
|
|
uint32_t node;
|
|
|
|
|
|
|
|
T_StackEntry_( P_ActDefs_ const& src , const uint32_t n ) noexcept
|
|
|
|
: def{ NewOwned< T_ActDefs_ >( *src ) } , node( n )
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
T_AutoArray< T_StackEntry_ , 32 > stack;
|
|
|
|
|
|
|
|
while ( nProcessed < nNodes ) {
|
|
|
|
assert( !processedNodes[ node ] );
|
|
|
|
auto const& cn{ *ctrlFlowGraph[ node ] };
|
|
|
|
processedNodes[ node ] = true;
|
|
|
|
logger( [=]() {
|
|
|
|
T_StringBuilder sb;
|
|
|
|
sb << "processing node " << node;
|
|
|
|
return sb;
|
|
|
|
} , LL2 );
|
|
|
|
nProcessed ++;
|
|
|
|
|
|
|
|
if ( cn.instructions ) {
|
|
|
|
// Check for uses and defines in the instructions
|
|
|
|
const auto is{ cn.instructions->first };
|
|
|
|
const auto ie{ is + cn.instructions->count };
|
|
|
|
for ( auto ii = is ; ii < ie ; ii ++ ) {
|
|
|
|
auto const* const irec{ udPerInstr.get( ii ) };
|
|
|
|
if ( !irec ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const auto nrec{ irec->size( ) };
|
|
|
|
|
|
|
|
// Handle uses first
|
|
|
|
for ( auto j = 0u ; j < nrec ; j ++ ) {
|
|
|
|
auto const& rec{ (*irec)[ j ] };
|
|
|
|
if ( !rec.isUse ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto& resource{ varUDChains[ rec.entry ] };
|
|
|
|
const auto defId{ (*activeDefs)[ rec.entry ] };
|
|
|
|
// FIXME: must be defined
|
2017-12-08 22:00:43 +01:00
|
|
|
assert( defId != T_HashIndex::INVALID_INDEX );
|
2017-12-08 07:38:59 +01:00
|
|
|
resource.defines[ defId ].refs.add( rec.index );
|
|
|
|
resource.uses[ rec.index ].refs.add( defId );
|
|
|
|
logger( [&](){
|
|
|
|
T_StringBuilder sb;
|
|
|
|
sb << "USE " << resource.var.name
|
|
|
|
<< " @ instr #" << ii
|
|
|
|
<< ", def " << defId;
|
|
|
|
return sb;
|
|
|
|
} , LL2 );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle defines
|
|
|
|
for ( auto j = 0u ; j < nrec ; j ++ ) {
|
|
|
|
auto const& rec{ (*irec)[ j ] };
|
|
|
|
if ( rec.isUse ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
(*activeDefs)[ rec.entry ] = rec.index;
|
|
|
|
logger( [&](){
|
|
|
|
T_StringBuilder sb;
|
2017-12-08 22:00:43 +01:00
|
|
|
sb << "DEF " << rec.index << ' '
|
2017-12-08 07:38:59 +01:00
|
|
|
<< varUDChains[ rec.entry ].var.name
|
|
|
|
<< " @ instr #" << ii;
|
|
|
|
return sb;
|
|
|
|
} , LL2 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( nProcessed == nNodes ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for possible next nodes
|
|
|
|
do {
|
|
|
|
auto const& rcn{ *ctrlFlowGraph[ node ] };
|
|
|
|
const uint32_t nSuccs{ [&](){
|
|
|
|
const auto no{ rcn.outbound.size( ) };
|
|
|
|
uint32_t c{ 0 };
|
|
|
|
for ( auto i = 0u ; i < no ; i ++ ) {
|
|
|
|
if ( !processedNodes[ rcn.outbound[ i ] ] ) {
|
|
|
|
c ++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return c;
|
|
|
|
}() };
|
2017-12-08 22:00:43 +01:00
|
|
|
logger( [&]() {
|
2017-12-08 07:38:59 +01:00
|
|
|
T_StringBuilder sb;
|
|
|
|
sb << "node " << node << ": " << nSuccs
|
2017-12-08 22:00:43 +01:00
|
|
|
<< " successor(s) left (stack depth "
|
|
|
|
<< stack.size( ) << ')';
|
2017-12-08 07:38:59 +01:00
|
|
|
return sb;
|
|
|
|
} , LL2 );
|
|
|
|
|
2017-12-08 22:00:43 +01:00
|
|
|
// -> no output nodes left -> pop stack and keep trying,
|
|
|
|
// unless we've already processed all nodes
|
2017-12-08 07:38:59 +01:00
|
|
|
if ( nSuccs == 0 ) {
|
|
|
|
assert( !stack.empty( ) );
|
|
|
|
node = stack.last( ).node;
|
|
|
|
activeDefs = std::move( stack.last( ).def );
|
|
|
|
stack.removeLast( );
|
2017-12-08 22:00:43 +01:00
|
|
|
logger( [&]() {
|
|
|
|
T_StringBuilder sb;
|
|
|
|
sb << "pop stack -> next node " << node
|
|
|
|
<< " (stack depth "
|
|
|
|
<< stack.size( ) << ')';
|
|
|
|
return sb;
|
|
|
|
} , LL2 );
|
2017-12-08 07:38:59 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-12-08 22:00:43 +01:00
|
|
|
const auto no{ rcn.outbound.size( ) };
|
|
|
|
uint32_t nn{ T_HashIndex::INVALID_INDEX };
|
|
|
|
for ( auto i = 0u ; i < no ; i ++ ) {
|
|
|
|
if ( !processedNodes[ rcn.outbound[ i ] ] ) {
|
|
|
|
nn = rcn.outbound[ i ];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// More than one possible successor? Push to stack
|
|
|
|
if ( nSuccs > 1 ) {
|
|
|
|
M_LOGSTR_( "pushing node" , LL2 );
|
|
|
|
stack.addNew( activeDefs , node );
|
|
|
|
}
|
|
|
|
|
|
|
|
node = nn;
|
|
|
|
break;
|
|
|
|
} while ( 1 );
|
2017-12-08 07:38:59 +01:00
|
|
|
|
|
|
|
// 30 if next block is an end of function, kill locals
|
|
|
|
}
|
2017-12-10 09:59:55 +01:00
|
|
|
#endif
|
2017-12-03 23:15:44 +01:00
|
|
|
}
|
|
|
|
|
2017-12-03 18:35:56 +01:00
|
|
|
|
|
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
#undef M_LOGSTR_
|
|
|
|
#define M_LOGSTR_( S , L ) \
|
|
|
|
oData.logger( [](){ return T_StringBuilder{ S }; } , L )
|
|
|
|
|
|
|
|
|
2017-12-01 07:30:03 +01:00
|
|
|
/*= CONSTANT FOLDING =========================================================*/
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
struct T_ConstantFolder_
|
|
|
|
{
|
2017-12-02 10:07:14 +01:00
|
|
|
T_ConstantFolder_( T_OptData& data ) noexcept
|
|
|
|
: oData{ data }
|
|
|
|
{}
|
2017-12-01 07:30:03 +01:00
|
|
|
|
|
|
|
// Result
|
|
|
|
bool didFold{ false };
|
|
|
|
|
|
|
|
bool operator()( A_Node& node , bool exit ) noexcept;
|
|
|
|
|
|
|
|
private:
|
2017-12-02 10:07:14 +01:00
|
|
|
T_OptData& oData;
|
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
template<
|
|
|
|
typename T
|
|
|
|
> void handleParentNode(
|
2017-12-01 07:30:03 +01:00
|
|
|
A_Node& node ,
|
2017-12-01 22:10:36 +01:00
|
|
|
std::function< A_ExpressionNode&( T& ) > get ,
|
|
|
|
std::function< void( T& , P_ExpressionNode ) > set ) noexcept;
|
2017-12-01 07:30:03 +01:00
|
|
|
|
|
|
|
P_ExpressionNode checkExpression(
|
2017-12-01 22:10:36 +01:00
|
|
|
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;
|
|
|
|
|
2017-12-02 10:40:47 +01:00
|
|
|
// Handle reads from inputs. If there's a curve and it is a constant,
|
|
|
|
// or if there's no curve and only one default value, then the
|
|
|
|
// expression is constant.
|
|
|
|
P_ExpressionNode doInputExpr(
|
|
|
|
T_InputExprNode& node ) noexcept;
|
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
// Transform an unary operator applied to a constant into a constant.
|
|
|
|
P_ExpressionNode doUnaryOp(
|
2017-12-02 10:07:14 +01:00
|
|
|
T_UnaryOperatorNode& node ,
|
2017-12-01 22:10:36 +01:00
|
|
|
double value ) const noexcept;
|
|
|
|
|
|
|
|
// Transform a binary operator applied to a constant into a constant.
|
|
|
|
P_ExpressionNode doBinaryOp(
|
2017-12-02 10:07:14 +01:00
|
|
|
T_BinaryOperatorNode& node ,
|
2017-12-01 22:10:36 +01:00
|
|
|
double left ,
|
|
|
|
double right ) const noexcept;
|
2017-12-01 07:30:03 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
bool T_ConstantFolder_::operator()(
|
|
|
|
A_Node& node ,
|
|
|
|
const bool exit ) noexcept
|
|
|
|
{
|
|
|
|
if ( exit ) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ( node.type( ) ) {
|
|
|
|
|
|
|
|
case A_Node::TN_ARG:
|
2017-12-01 22:10:36 +01:00
|
|
|
handleParentNode< T_ArgumentNode >(
|
|
|
|
node ,
|
|
|
|
[]( auto& n ) -> A_ExpressionNode& { return n.expression( ); } ,
|
|
|
|
[]( auto& n , P_ExpressionNode e ) { n.expression( std::move( e ) ); }
|
|
|
|
);
|
2017-12-01 07:30:03 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
case A_Node::TN_CONDITION:
|
2017-12-01 22:10:36 +01:00
|
|
|
handleParentNode< T_CondInstrNode::T_Expression >( node ,
|
|
|
|
[]( auto& n ) -> A_ExpressionNode& { return n.expression( ); } ,
|
|
|
|
[]( auto& n , P_ExpressionNode e ) { n.expression( std::move( e ) ); }
|
|
|
|
);
|
2017-12-01 07:30:03 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
case A_Node::OP_SET:
|
2017-12-01 22:10:36 +01:00
|
|
|
handleParentNode< T_SetInstrNode >( node ,
|
|
|
|
[]( auto& n ) -> A_ExpressionNode& { return n.expression( ); } ,
|
|
|
|
[]( auto& n , P_ExpressionNode e ) { n.setExpression( std::move( e ) ); } );
|
2017-12-01 07:30:03 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
template<
|
|
|
|
typename T
|
|
|
|
> void T_ConstantFolder_::handleParentNode(
|
|
|
|
A_Node& n ,
|
|
|
|
std::function< A_ExpressionNode&( T& ) > get ,
|
|
|
|
std::function< void( T& , P_ExpressionNode ) > set ) noexcept
|
2017-12-01 07:30:03 +01:00
|
|
|
{
|
2017-12-01 22:10:36 +01:00
|
|
|
auto& node{ (T&) n };
|
2017-12-02 12:18:31 +01:00
|
|
|
auto& child{ get( node ) };
|
|
|
|
auto r{ checkExpression( child ) };
|
2017-12-01 07:30:03 +01:00
|
|
|
if ( r ) {
|
2017-12-02 12:18:31 +01:00
|
|
|
oData.logger( [&]() {
|
|
|
|
T_StringBuilder sb;
|
|
|
|
sb << "substituting node at " << child.location( );
|
|
|
|
return sb;
|
2017-12-03 13:05:54 +01:00
|
|
|
} , 3 );
|
2017-12-01 22:10:36 +01:00
|
|
|
r->location( ) = node.location( );
|
2017-12-01 07:30:03 +01:00
|
|
|
set( node , std::move( r ) );
|
|
|
|
didFold = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
P_ExpressionNode T_ConstantFolder_::checkExpression(
|
2017-12-01 22:10:36 +01:00
|
|
|
A_ExpressionNode& node ) noexcept
|
2017-12-01 07:30:03 +01:00
|
|
|
{
|
2017-12-01 22:10:36 +01:00
|
|
|
// Already a constant
|
|
|
|
if ( node.type( ) == A_Node::EXPR_CONST ) {
|
|
|
|
return {};
|
|
|
|
}
|
2017-12-01 07:30:03 +01:00
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
// Replace $width/$height with value if fixedSize
|
2017-12-01 07:30:03 +01:00
|
|
|
if ( node.type( ) == A_Node::EXPR_ID ) {
|
2017-12-01 22:10:36 +01:00
|
|
|
return doIdExpr( (T_IdentifierExprNode&) node );
|
|
|
|
}
|
2017-12-01 07:30:03 +01:00
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
// Replace inputs with value if no curve/constant curve
|
|
|
|
if ( node.type( ) == A_Node::EXPR_INPUT ) {
|
2017-12-02 10:40:47 +01:00
|
|
|
return doInputExpr( (T_InputExprNode&) node );
|
2017-12-01 22:10:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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( ) };
|
2017-12-02 10:07:14 +01:00
|
|
|
return doUnaryOp( *asUnary , cn.floatValue( ) );
|
2017-12-01 07:30:03 +01:00
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
// 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( ) };
|
2017-12-02 10:07:14 +01:00
|
|
|
return doBinaryOp( *asBinary , l.floatValue( ) , r.floatValue( ) );
|
2017-12-01 22:10:36 +01:00
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
2017-12-02 10:40:47 +01:00
|
|
|
P_ExpressionNode T_ConstantFolder_::doInputExpr(
|
|
|
|
T_InputExprNode& node ) noexcept
|
|
|
|
{
|
|
|
|
if ( !oData.curves ) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
auto const* const curve{ oData.curves->curves.get( node.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 );
|
|
|
|
}
|
|
|
|
|
|
|
|
assert( oData.inputDecls );
|
|
|
|
auto const* const dva{ oData.inputDecls->get( node.id( ) ) };
|
|
|
|
assert( dva );
|
|
|
|
if ( dva->size( ) == 1 ) {
|
|
|
|
// If there's only one default value, that's a constant.
|
|
|
|
return NewOwned< T_ConstantExprNode >( node.parent( ) ,
|
|
|
|
(*dva)[ 0 ].value );
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
P_ExpressionNode T_ConstantFolder_::doIdExpr(
|
|
|
|
T_IdentifierExprNode& node ) noexcept
|
|
|
|
{
|
2017-12-02 10:07:14 +01:00
|
|
|
if ( !oData.fixedSize ) {
|
2017-12-01 22:10:36 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( node.id( ) == "width" ) {
|
2017-12-03 13:05:54 +01:00
|
|
|
M_LOGSTR_( "replacing $width with fixed width" , 3 );
|
2017-12-01 22:10:36 +01:00
|
|
|
return NewOwned< T_ConstantExprNode >( node.parent( ) ,
|
2017-12-02 10:07:14 +01:00
|
|
|
double( oData.fixedSize->first ) );
|
2017-12-01 22:10:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( node.id( ) == "height" ) {
|
2017-12-03 13:05:54 +01:00
|
|
|
M_LOGSTR_( "replacing $height with fixed height" , 3 );
|
2017-12-01 22:10:36 +01:00
|
|
|
return NewOwned< T_ConstantExprNode >( node.parent( ) ,
|
2017-12-02 10:07:14 +01:00
|
|
|
float( oData.fixedSize->second ) );
|
2017-12-01 22:10:36 +01:00
|
|
|
}
|
|
|
|
|
2017-12-01 07:30:03 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
P_ExpressionNode T_ConstantFolder_::doUnaryOp(
|
2017-12-02 10:07:14 +01:00
|
|
|
T_UnaryOperatorNode& node ,
|
2017-12-01 22:10:36 +01:00
|
|
|
const double value ) const noexcept
|
|
|
|
{
|
2017-12-02 10:07:14 +01:00
|
|
|
const double rVal{ [this]( auto& node , const auto value ) {
|
|
|
|
switch ( node.op( ) ) {
|
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
case T_UnaryOperatorNode::NEG:
|
|
|
|
return -value;
|
2017-12-02 10:07:14 +01:00
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
case T_UnaryOperatorNode::NOT:
|
|
|
|
return value ? 0. : 1.;
|
2017-12-02 10:07:14 +01:00
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
case T_UnaryOperatorNode::INV:
|
2017-12-02 10:07:14 +01:00
|
|
|
if ( value == 0 ) {
|
|
|
|
oData.errors.addNew( "math - 1/x, x=0" , node.location( ) );
|
|
|
|
return 0.;
|
|
|
|
}
|
2017-12-01 22:10:36 +01:00
|
|
|
return 1. / value;
|
2017-12-02 10:07:14 +01:00
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
case T_UnaryOperatorNode::COS:
|
|
|
|
return cos( value );
|
2017-12-02 10:07:14 +01:00
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
case T_UnaryOperatorNode::SIN:
|
|
|
|
return sin( value );
|
2017-12-02 10:07:14 +01:00
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
case T_UnaryOperatorNode::TAN:
|
2017-12-02 10:07:14 +01:00
|
|
|
if ( fabs( value - M_PI / 2 ) <= 1e-6 ) {
|
|
|
|
oData.errors.addNew( "math - tan(x), x=~PI/2" ,
|
|
|
|
node.location( ) , E_SRDErrorType::WARNING );
|
|
|
|
}
|
2017-12-01 22:10:36 +01:00
|
|
|
return tan( value );
|
2017-12-02 10:07:14 +01:00
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
case T_UnaryOperatorNode::SQRT:
|
2017-12-02 10:07:14 +01:00
|
|
|
if ( value < 0 ) {
|
|
|
|
oData.errors.addNew( "math - sqrt(x), x<0" , node.location( ) );
|
|
|
|
return 0.;
|
|
|
|
}
|
2017-12-01 22:10:36 +01:00
|
|
|
return sqrt( value );
|
2017-12-02 10:07:14 +01:00
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
case T_UnaryOperatorNode::LN:
|
2017-12-02 10:07:14 +01:00
|
|
|
if ( value <= 0 ) {
|
|
|
|
oData.errors.addNew( "math - ln(x), x<=0" , node.location( ) );
|
|
|
|
return 0.;
|
|
|
|
}
|
2017-12-01 22:10:36 +01:00
|
|
|
return log( value );
|
2017-12-02 10:07:14 +01:00
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
case T_UnaryOperatorNode::EXP:
|
|
|
|
return exp( value );
|
|
|
|
}
|
2017-12-02 10:07:14 +01:00
|
|
|
|
|
|
|
fprintf( stderr , "invalid operator %d\n" , int( node.op( ) ) );
|
2017-12-01 22:10:36 +01:00
|
|
|
std::abort( );
|
2017-12-02 10:07:14 +01:00
|
|
|
}( node , value ) };
|
2017-12-01 22:10:36 +01:00
|
|
|
|
2017-12-02 10:07:14 +01:00
|
|
|
return NewOwned< T_ConstantExprNode >( node.parent( ) , rVal );
|
2017-12-01 22:10:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
P_ExpressionNode T_ConstantFolder_::doBinaryOp(
|
2017-12-02 10:07:14 +01:00
|
|
|
T_BinaryOperatorNode& node ,
|
2017-12-01 22:10:36 +01:00
|
|
|
const double left ,
|
|
|
|
const double right ) const noexcept
|
|
|
|
{
|
2017-12-02 10:07:14 +01:00
|
|
|
const double rVal{ [this]( auto& node , const auto l , const auto r ) {
|
|
|
|
switch ( node.op( ) ) {
|
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
case T_BinaryOperatorNode::ADD:
|
|
|
|
return l + r;
|
2017-12-02 10:07:14 +01:00
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
case T_BinaryOperatorNode::SUB:
|
|
|
|
return l - r;
|
2017-12-02 10:07:14 +01:00
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
case T_BinaryOperatorNode::MUL:
|
|
|
|
return l * r;
|
2017-12-02 10:07:14 +01:00
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
case T_BinaryOperatorNode::DIV:
|
2017-12-02 10:07:14 +01:00
|
|
|
if ( r == 0 ) {
|
|
|
|
oData.errors.addNew( "math - l/r, r=0" , node.location( ) );
|
|
|
|
return 0.;
|
|
|
|
}
|
2017-12-01 22:10:36 +01:00
|
|
|
return l / r;
|
2017-12-02 10:07:14 +01:00
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
case T_BinaryOperatorNode::POW:
|
2017-12-02 10:07:14 +01:00
|
|
|
if ( l == 0 && r == 0 ) {
|
|
|
|
oData.errors.addNew( "math - l^r, l=r=0" , node.location( ) );
|
|
|
|
return 0.;
|
|
|
|
}
|
|
|
|
if ( l == 0 && r < 0 ) {
|
|
|
|
oData.errors.addNew( "math - l^r, l=0, r<0" , node.location( ) );
|
|
|
|
return 0.;
|
|
|
|
}
|
|
|
|
if ( l < 0 && fmod( r , 1. ) != 0. ) {
|
|
|
|
oData.errors.addNew( "math - l^r, l<0, r not integer" , node.location( ) );
|
|
|
|
return 0.;
|
|
|
|
}
|
2017-12-01 22:10:36 +01:00
|
|
|
return pow( l , r );
|
2017-12-02 10:07:14 +01:00
|
|
|
|
|
|
|
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.;
|
2017-12-01 22:10:36 +01:00
|
|
|
}
|
2017-12-02 10:07:14 +01:00
|
|
|
fprintf( stderr , "invalid operator %d\n" , int( node.op( ) ) );
|
2017-12-01 22:10:36 +01:00
|
|
|
std::abort( );
|
2017-12-02 10:07:14 +01:00
|
|
|
}( node , left , right ) };
|
2017-12-01 22:10:36 +01:00
|
|
|
|
2017-12-02 10:07:14 +01:00
|
|
|
return NewOwned< T_ConstantExprNode >( node.parent( ) , rVal );
|
2017-12-01 22:10:36 +01:00
|
|
|
}
|
|
|
|
|
2017-12-01 07:30:03 +01:00
|
|
|
} // namespace <anon>
|
|
|
|
|
2017-12-01 22:10:36 +01:00
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
2017-12-01 07:30:03 +01:00
|
|
|
|
|
|
|
bool opopt::FoldConstants(
|
2017-12-02 10:07:14 +01:00
|
|
|
T_OpsParserOutput& program ,
|
|
|
|
T_OptData& oData ) noexcept
|
2017-12-01 07:30:03 +01:00
|
|
|
{
|
2017-12-02 10:07:14 +01:00
|
|
|
T_ConstantFolder_ folder{ oData };
|
2017-12-03 13:05:54 +01:00
|
|
|
M_LOGSTR_( "... Folding constants" , 2 );
|
2017-12-02 10:40:47 +01:00
|
|
|
if ( oData.curves ) {
|
|
|
|
oData.findInputDecls( program );
|
|
|
|
}
|
2017-12-02 12:18:31 +01:00
|
|
|
oData.visitor.visit( program.root , [&]( auto& n , auto x ) {
|
|
|
|
return folder( n , x );
|
|
|
|
} );
|
|
|
|
oData.logger( [&]() {
|
2017-12-03 13:05:54 +01:00
|
|
|
T_StringBuilder sb{ "...... " };
|
|
|
|
sb << ( folder.didFold
|
|
|
|
? "Some constants were folded"
|
|
|
|
: "No constants were folded" );
|
|
|
|
return sb;
|
|
|
|
} , 2 );
|
2017-12-01 07:30:03 +01:00
|
|
|
return folder.didFold;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-03 14:59:39 +01:00
|
|
|
/*= CONSTANT PROPAGATION =====================================================*/
|
|
|
|
|
|
|
|
bool opopt::PropagateConstants(
|
|
|
|
T_OpsParserOutput& program ,
|
2017-12-03 18:35:56 +01:00
|
|
|
T_OptData& oData ) noexcept
|
2017-12-03 14:59:39 +01:00
|
|
|
{
|
|
|
|
// We need to follow the general execution flow of the program. This is
|
|
|
|
// not as straightforward as it seems.
|
|
|
|
// - Handling locals is rather easy as they "die" at the end of
|
|
|
|
// the function in which they are defined.
|
|
|
|
// - Handling variables in init and functions that are called only
|
|
|
|
// from init is not too hard: once a variable is set to a constant, it
|
|
|
|
// can be substituted until the next set instruction.
|
|
|
|
// - Other variables need additional checks before propagating.
|
|
|
|
// For example, if a variable is set to a constant during init, but is
|
|
|
|
// updated at the end of the frame function, the value cannot be
|
|
|
|
// propagated.
|
2017-12-03 18:35:56 +01:00
|
|
|
|
|
|
|
oData.numberInstructions( program );
|
|
|
|
oData.buildControlFlowGraph( program );
|
2017-12-03 23:15:44 +01:00
|
|
|
oData.buildUseDefineChains( program );
|
2017-12-03 18:35:56 +01:00
|
|
|
M_LOGSTR_( "... Propagating constants" , 2 );
|
|
|
|
|
2017-12-03 14:59:39 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-01 07:30:03 +01:00
|
|
|
/*= DEAD CODE REMOVAL ========================================================*/
|
|
|
|
|
2017-12-02 10:07:14 +01:00
|
|
|
bool opopt::RemoveDeadCode(
|
|
|
|
T_OpsParserOutput& program ,
|
|
|
|
T_OptData& oData ) noexcept
|
2017-12-01 07:30:03 +01:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|