#include "cir/cir.h"
#include "builtins/builtins.h"
using namespace goose::builtins;
namespace goose::cir
{
bool Call::canBeExecuted() const
{
if( !IsValueConstantOrExecutable( m_func ) )
return false;
if( IsExternalFunc( m_func ) )
return false;
bool argsCanBeExecuted = true;
ForEachInVectorTerm( m_args, [&]( auto&& arg )
{
if( !IsValueConstantOrExecutable( *EIRToValue( arg ) ) )
{
argsCanBeExecuted = false;
return false;
}
return true;
} );
if( !argsCanBeExecuted )
return false;
if( IsBuiltinFunc( m_func ) )
return true;
const auto* pFunc = GetFuncCIR( m_func );
// If the func is passed as a value, we might not have been able to evaluate
// it yet, so we assume it's going to be executable. If it isn't, we'll fail
// at execution time, which isn't a big deal - we should be able to avoid trying
// executing it in the first place in most cases.
return !pFunc || pFunc->canBeExecuted();
}
bool Call::canBeEagerlyEvaluated() const
{
// Functions with void return type are assumed to have side effects
// and therefore that they should never be eagerly evaluated.
// It's not like they would need to be anyway.
if( GetFuncRType( m_func ) == GetValueType< void >() )
return false;
if( IsNonEagerBuiltinFunc( m_func ) )
return false;
if( IsExternalFunc( m_func ) )
return false;
bool argsAreConstant = true;
ForEachInVectorTerm( m_args, [&]( auto&& arg )
{
if( !CanValueBeEagerlyEvaluated( *EIRToValue( arg ) ) )
{
argsAreConstant = false;
return false;
}
return true;
} );
if( !argsAreConstant )
return false;
if( IsEagerBuiltinFunc( m_func ) )
return true;
const auto* pFuncCIR = GetFuncCIR( m_func );
if( !pFuncCIR )
return false;
return pFuncCIR->canBeExecuted();
}
ostream& operator<<( ostream& out, const Call& ins )
{
return out << "CALL(" << ins.m_func << ", " << ins.m_args << ')';
}
}