#include "execute.h"
using namespace empathy;
using namespace empathy::execute;
using namespace empathy::builtins;
uint32_t VM::ms_remainingBranchInstExecutions = 1024;
optional< Value > VM::execute( CFG& cfg )
{
return execute( cfg.entryBB() );
}
optional< Value > VM::execute( ptr< BasicBlock > bb )
{
auto pbbBackup = m_pPreviousBB;
while( bb )
{
for( auto&& instr : *bb )
execute( instr );
if( !bb->terminator() )
break;
m_pPreviousBB = bb;
bb = execute( *bb->terminator() );
}
m_pPreviousBB = pbbBackup;
return m_frame.retVal();
}
optional< Value > VM::execute( const llr::Instruction& instr )
{
return visit( [&]( auto&& e )
{
return execute( e );
}, instr.content() );
}
ptr< BasicBlock > VM::execute( const llr::Terminator& terminator )
{
return visit( [&]( auto&& e )
{
return execute( e );
}, terminator.content() );
}
optional< Value > VM::execute( const ptr< CFG >& ic )
{
return execute( *ic );
}
optional< Value > VM::execute( const llr::Call& call )
{
if( !( --ms_remainingBranchInstExecutions ) )
return nullopt;
auto func = Evaluate( call.func(), *this );
if( !func || !func->isConstant() )
{
cout << "Execute: function evaluation failed.\n";
return nullopt;
}
const auto& vec = *get< pvec >( call.args() );
if( IsBuiltinFunc( *func ) )
{
auto newVec = vec.transform( [&]( auto&& x ) -> optional< Term >
{
auto val = ValueFromIRExpr( x );
assert( val );
auto newVal = Evaluate( *val, *this );
if( !newVal || !newVal->isConstant() )
return nullopt;
return ValueToIRExpr( *newVal );
} );
if( !newVec )
{
cout << "Execute: args evaluation failed.\n";
return nullopt;
}
return ExecuteBuiltinFuncCall( *func, TERM( newVec ) );
}
const auto* pFunc = GetFuncLLR( *func );
if( !pFunc || !pFunc->isValid() )
return nullopt;
Frame f;
llvm::SmallVector< optional< Value >, 8 > args;
args.reserve( vec.terms().size() );
for( auto&& a : vec.terms() )
{
auto val = ValueFromIRExpr( a );
assert( val );
auto newVal = Evaluate( *val, *this );
if( !newVal|| !newVal->isConstant() )
{
cout << "Execute: args evaluation failed.\n";
return nullopt;
}
args.emplace_back( move( *newVal ) );
}
f.setArgs( move( args ) );
swap( m_frame, f );
auto result = execute( *pFunc->body() );
swap( m_frame, f );
return result;
}
optional< Value > VM::ExecuteBuiltinFuncCall( const Value& func, const Term& args )
{
const auto& f = GetBuiltinFuncWrapper( func );
return f( args );
}
ptr< BasicBlock > VM::execute( const llr::Ret& r )
{
if( r.value() )
m_frame.setRetVal( *Evaluate( *r.value(), *this ) );
return nullptr;
}
optional< Value > VM::execute( const llr::CreateTemporary& ct )
{
m_frame.setTemporary( ct.cfgId(), ct.index(), *Evaluate( ct.value(), *this ) );
return nullopt;
}
optional< Value > VM::execute( const llr::GetTemporary& gt )
{
const auto* pVal = m_frame.getTemporary( gt.cfgId(), gt.index() );
assert( pVal );
if( !pVal )
return nullopt;
return *pVal;
}
optional< Value > VM::execute( const llr::GetVar& gv )
{
const auto* pVal = m_frame.getTemporary( gv.cfgId(), gv.index() );
assert( pVal );
if( !pVal )
return nullopt;
auto result = Evaluate( **pVal, *this );
if( !result || !result->isConstant() )
return nullopt;
return result;
}
optional< Value > VM::execute( const llr::SetVar& sv )
{
m_frame.setTemporary( sv.cfgId(), sv.index(), sv.value() );
return nullopt;
}
optional< Value > VM::execute( const llr::Phi& p )
{
p.forAllIncomings( [&]( auto&& bb, auto&& val )
{
if( bb == m_pPreviousBB )
{
m_frame.setTemporary( p.destCfgId(), p.destIndex(), *Evaluate( val, *this ) );
return false;
}
return true;
} );
return nullopt;
}
optional< Value > VM::execute( const llr::LoadConstInt& lci )
{
return ToValue( lci.value() );
}
ptr< BasicBlock > VM::execute( const llr::Branch& b )
{
if( !( --ms_remainingBranchInstExecutions ) )
return nullptr;
return b.dest().lock();
}
ptr< BasicBlock > VM::execute( const llr::CondBranch& cb )
{
if( !( --ms_remainingBranchInstExecutions ) )
return nullptr;
auto cond = Evaluate( cb.cond(), *this );
if( !cond || !cond->isConstant() )
{
cout << "Execute: branch condition evaluation failed.\n";
return nullptr;
}
if( *FromValue< bool >( *cond ) )
return cb.trueDest().lock();
return cb.falseDest().lock();
}