//*************************************************************************
// DESCRIPTION: Verilator: Find broken links in tree
//
// Code available from: http://www.veripool.org/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2009 by Wilson Snyder.  This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
//*************************************************************************
// V3Broken's Transformations:
//
// Entire netlist
//	Mark all nodes
//	Check all links point to marked nodes
//
//*************************************************************************

#include "config_build.h"
#include "verilatedos.h"
#include <cstdio>
#include <cstdarg>
#include <unistd.h>
#include <algorithm>
#include <map>

#include "V3Global.h"
#include "V3Broken.h"
#include "V3Ast.h"

//######################################################################

class BrokenTable : public AstNVisitor {
    // Table of brokenExists node pointers
private:
    // MEMBERS
    //   For each node, we keep if it exists or not.
    typedef map<const AstNode*,int> NodeMap;
    static NodeMap s_nodes;	// Set of all nodes that exist
    // BITMASK
    static const int FLAG_ALLOCATED	= 0x01;		// new() and not delete()ed
    static const int FLAG_IN_TREE	= 0x02;		// Is in netlist tree
    static const int FLAG_LINKABLE	= 0x04;		// Is in netlist tree, can be linked to
    static const int FLAG_LEAKED	= 0x08;		// Known to have been leaked
public:
    // METHODS
    static void deleted(const AstNode* nodep) {
	// Called by operator delete on any node - only if VL_LEAK_CHECKS
	if (debug()) cout<<"-nodeDel:  "<<(void*)(nodep)<<endl;
	NodeMap::iterator iter = s_nodes.find(nodep);
	if (iter==s_nodes.end() || !(iter->second & FLAG_ALLOCATED)) {
	    ((AstNode*)(nodep))->v3fatalSrc("Deleting AstNode object that was never tracked or already deleted\n");
	}
	if (iter!=s_nodes.end()) s_nodes.erase(iter);
    }
    static void addNewed(const AstNode* nodep) {
	// Called by operator new on any node - only if VL_LEAK_CHECKS
	if (debug()) cout<<"-nodeNew:  "<<(void*)(nodep)<<endl;
	NodeMap::iterator iter = s_nodes.find(nodep);
	if (iter!=s_nodes.end() || (iter->second & FLAG_ALLOCATED)) {
	    ((AstNode*)(nodep))->v3fatalSrc("Newing AstNode object that is already allocated\n");
	}
	if (iter == s_nodes.end()) {
	    s_nodes.insert(make_pair(nodep,FLAG_ALLOCATED));
	}
    }
    static void addInTree(AstNode* nodep, bool linkable) {
#ifndef VL_LEAK_CHECKS
	if (!linkable) return;  // save some time, else the map will get huge!
#endif
	NodeMap::iterator iter = s_nodes.find(nodep);
	if (iter == s_nodes.end()) {
#ifdef VL_LEAK_CHECKS
	    nodep->v3fatalSrc("AstNode is in tree, but not allocated\n");
#endif
	} else {
	    if (!(iter->second & FLAG_ALLOCATED)) {
#ifdef VL_LEAK_CHECKS
		nodep->v3fatalSrc("AstNode is in tree, but not allocated\n");
#endif
	    }
	    if (iter->second & FLAG_IN_TREE) {
		nodep->v3fatalSrc("AstNode is already in tree at another location\n");
	    }
	}
	int or_flags = FLAG_IN_TREE | (linkable?FLAG_LINKABLE:0);
	if (iter == s_nodes.end()) {
	    s_nodes.insert(make_pair(nodep,or_flags));
	} else {
	    iter->second |= or_flags;
	}
    }
    static bool okIfLinkedTo(const AstNode* nodep) {
	// Someone has a pointer to this node.  Is it kosher?
	NodeMap::iterator iter = s_nodes.find(nodep);
	if (iter == s_nodes.end()) return false;
#ifdef VL_LEAK_CHECKS
	if (!(iter->second & FLAG_ALLOCATED)) return false;
#endif
	if (!(iter->second & FLAG_IN_TREE)) return false;
	if (!(iter->second & FLAG_LINKABLE)) return false;
	return true;
    }
    static void prepForTree() {
#ifndef VL_LEAK_CHECKS
	s_nodes.clear();
#endif
	for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
	    it->second &= ~FLAG_IN_TREE;
	    it->second &= ~FLAG_LINKABLE;
	}
    }
    static void doneWithTree() {
	for (int backs=0; backs<2; backs++) {  // Those with backp() are probably under one leaking without
	    for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
		if ((it->second & FLAG_ALLOCATED)
		    && !(it->second & FLAG_IN_TREE)
		    && !(it->second & FLAG_LEAKED)
		    && (it->first->backp() ? backs==1 : backs==0)) {
		    // Use only AstNode::dump instead of the virtual one, as there
		    // may be varp() and other cross links that are bad.
		    if (debug()) {
			cerr<<"%Error: LeakedNode"<<(it->first->backp()?"Back: ":": ");
			((AstNode*)(it->first))->AstNode::dump(cerr);
			cerr<<endl;
			V3Error::incErrors();
		    }
		    it->second |= FLAG_LEAKED;
		}
	    }
	}
    }
public:
    // CONSTUCTORS
    BrokenTable() {}
    virtual ~BrokenTable() {}
};

BrokenTable::NodeMap BrokenTable::s_nodes;

bool AstNode::brokeExists() const {
    // Called by node->broken() routines to do table lookup
    return BrokenTable::okIfLinkedTo(this);
}

//######################################################################

class BrokenMarkVisitor : public AstNVisitor {
    // Mark every node in the tree
private:
    // NODE STATE
    //  Nothing!	// This may be called deep inside other routines
    //			// so userp and friends may not be used
    // VISITORS
    virtual void visit(AstNode* nodep, AstNUser*) {
	BrokenTable::addInTree(nodep, nodep->maybePointedTo());
	nodep->iterateChildren(*this);
    }
public:
    // CONSTUCTORS
    BrokenMarkVisitor(AstNetlist* nodep) {
	nodep->accept(*this);
    }
    virtual ~BrokenMarkVisitor() {}
};

//######################################################################
// Broken state, as a visitor of each AstNode

class BrokenCheckVisitor : public AstNVisitor {
private:
    virtual void visit(AstNode* nodep, AstNUser*) {
	if (nodep->broken()) {
	    nodep->v3fatalSrc("Broken link in node (or something without maybePointedTo)\n");
	}
	if (v3Global.assertWidthsSame()) {
	    if (nodep->width() != nodep->widthMin()) {
		nodep->v3fatalSrc("Width != WidthMin\n");
	    }
	    if (!nodep->width() && nodep->castNodeMath()) {
		nodep->v3fatalSrc("Math node has no assigned width\n");
	    }
	}
	nodep->iterateChildren(*this);
    }
public:
    // CONSTUCTORS
    BrokenCheckVisitor(AstNetlist* nodep) {
	nodep->accept(*this);
    }
    virtual ~BrokenCheckVisitor() {}
};

//######################################################################
// Broken class functions

void V3Broken::brokenAll(AstNetlist* nodep) {
    //UINFO(9,__FUNCTION__<<": "<<endl);
    BrokenTable::prepForTree();
    BrokenMarkVisitor mvisitor (nodep);
    BrokenCheckVisitor cvisitor (nodep);
    BrokenTable::doneWithTree();
}

void V3Broken::addNewed(AstNode* nodep) {
    BrokenTable::addNewed(nodep);
}
void V3Broken::deleted(AstNode* nodep) {
    BrokenTable::deleted(nodep);
}
