#include "verify.h"
#include "codegen/codegen.h"
// If enabled, only dump dot files for functions where an assertion
// failure was detected
#define VERIFY_VIZ_FAIL_ONLY
#ifdef VERIFY_VIZ
using namespace goose;
using namespace goose::verify;
namespace
{
ostream& indent( ostream& out, int indentation )
{
while( indentation-- )
out << '\t';
return out;
}
} // namespace
VerifyViz::~VerifyViz()
{
#ifdef VERIFY_VIZ_FAIL_ONLY
if( !m_assertionFailed )
return;
#endif
if( m_filename.empty() )
m_filename = "anon_func.dot";
ofstream out( m_filename );
out << "digraph \"goose\"\n"
"{\n"
"\tcolor=\"#"
<< colors::base05
<< "\";\n"
"\tfontcolor=\"#"
<< colors::base05
<< "\";\n"
"\tbgcolor=\"#"
<< colors::base00
<< "\";\n"
"\trankdir=TB;\n"
"\tnode [\n"
"\t\tcolor=\"#"
<< colors::base05
<< "\";\n"
"\t\tfontcolor=\"#"
<< colors::base05
<< "\";\n"
"\t];\n"
"\tedge [\n"
"\t\tcolor=\"#"
<< colors::base03
<< "\";\n"
"\t];\n";
if( m_assertionFailed )
indent( out, 1 ) << *m_assertionFailed << "[color=\"#" << colors::base08 << "\"];\n";
for( auto&& [id, oid] : m_root->m_blocks )
{
out << '\n';
if( id != oid )
indent( out, 1 ) << id << "[ label=\"" << id << ':' << oid << "\"];";
else
indent( out, 1 ) << id << ';';
}
for( auto&& x : m_root->m_children )
{
out << '\n';
GenerateSubGraph( out, x, 1 );
}
for( auto&& [from, to] : m_edges )
indent( out, 1 ) << from << "->" << to << ";\n";
out << "}\n";
}
void VerifyViz::GenerateSubGraph( ostream& out, ptr< SubGraph > sg, int indentation ) const
{
indent( out, indentation ) << "subgraph cluster_" << sg.get() << endl;
indent( out, indentation ) << "{\n";
indent( out, indentation + 1 ) << "color = \"#" << colors::base09 << "\";\n";
for( auto&& [id, oid] : sg->m_blocks )
{
out << '\n';
if( id != oid )
indent( out, indentation + 1 ) << id << "[ label=\"" << id << ':' << oid << "\"];";
else
indent( out, indentation + 1 ) << id << ';';
}
for( auto&& x : sg->m_children )
{
out << '\n';
GenerateSubGraph( out, x, indentation + 1 );
}
out << '\n';
indent( out, indentation ) << "}" << endl;
}
void VerifyViz::setFunctionIdentity( const Term& id )
{
m_filename = format( "{}.dot", *codegen::Mangle( id ) );
}
void VerifyViz::beginLoopVerification()
{
if( m_assertionFailed )
return;
pushSubGraph();
}
void VerifyViz::beginLoopInduction()
{
if( m_assertionFailed )
return;
popSubGraph();
pushSubGraph();
}
void VerifyViz::endLoopVerification()
{
if( m_assertionFailed )
return;
popSubGraph();
}
void VerifyViz::nextLoopIteration()
{
if( m_assertionFailed )
return;
popSubGraph();
pushSubGraph();
}
void VerifyViz::addBlock( uint32_t id, uint32_t originalId )
{
if( m_assertionFailed )
return;
m_currentSubGraph->m_blocks.emplace_back( id, originalId );
m_lastBlockId = id;
}
void VerifyViz::addEdge( uint32_t from, uint32_t to )
{
if( m_assertionFailed )
return;
if( to == Remapper::InvalidEdgeId )
return;
m_edges.emplace_back( from, to );
}
void VerifyViz::pushSubGraph()
{
if( m_assertionFailed )
return;
auto pNew = make_shared< SubGraph >();
m_currentSubGraph->m_children.emplace_back( pNew );
pNew->m_parent = m_currentSubGraph;
m_currentSubGraph = pNew;
}
void VerifyViz::popSubGraph()
{
if( m_assertionFailed )
return;
m_currentSubGraph = m_currentSubGraph->m_parent.lock();
assert( m_currentSubGraph );
}
VerifyViz::Guard::Guard( VerifyViz& vv ) :
m_vv( vv )
{
m_savedRoot = deepCopyGraph( m_vv.m_root );
m_savedEdges = vv.m_edges;
m_savedLastBlockId = vv.m_lastBlockId;
}
VerifyViz::Guard::~Guard()
{
if( m_commit )
return;
m_vv.m_root = move( m_savedRoot );
m_vv.m_currentSubGraph = move( m_savedCurrentSubGraph );
m_vv.m_edges = move( m_savedEdges );
m_vv.m_lastBlockId = move( m_savedLastBlockId );
}
ptr< VerifyViz::SubGraph > VerifyViz::Guard::deepCopyGraph( const ptr< SubGraph >& pNode )
{
auto pNew = make_shared< SubGraph >();
pNew->m_blocks = pNode->m_blocks;
for( const auto& x : pNode->m_children )
{
auto c = deepCopyGraph( x );
c->m_parent = pNew;
pNew->m_children.emplace_back( move( c ) );
}
if( m_vv.m_currentSubGraph == pNode )
m_savedCurrentSubGraph = pNew;
return pNew;
}
#endif