Restructure Nexus lists of Links to handle large net lists.

When netlists get very large, the Nexus::connect() method tickles
the O(N) performance and elaboration gets very slow. Rework the
connect method to be O(C), for a drastic performance boost for
large designs.
This commit is contained in:
Stephen Williams 2009-12-09 15:49:52 -08:00
parent 4db4a467ee
commit 782c55f7b5
3 changed files with 163 additions and 88 deletions

View File

@ -34,7 +34,7 @@ bool Nexus::drivers_constant() const
if (driven_ != NO_GUESS) if (driven_ != NO_GUESS)
return true; return true;
for (const Link*cur = list_ ; cur ; cur = cur->next_) { for (const Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) {
const NetNet*sig; const NetNet*sig;
Link::DIR cur_dir; Link::DIR cur_dir;
@ -121,7 +121,7 @@ verinum::V Nexus::driven_value() const
verinum::V val = verinum::Vz; verinum::V val = verinum::Vz;
for (cur = list_ ; cur ; cur = cur->next_) { for (cur = first_nlink() ; cur ; cur = cur->next_nlink()) {
const NetConst*obj; const NetConst*obj;
const NetNet*sig; const NetNet*sig;

View File

@ -32,7 +32,8 @@
void Nexus::connect(Link&r) void Nexus::connect(Link&r)
{ {
if (this == r.nexus_) Nexus*r_nexus = r.next_? r.find_nexus_() : 0;
if (this == r_nexus)
return; return;
if (name_) { if (name_) {
@ -40,31 +41,54 @@ void Nexus::connect(Link&r)
name_ = 0; name_ = 0;
} }
// Special case: The "r" link is connected to nothing. The // Special case: This nexus is empty. Simply copy all the
// connect becomes trivially easy. // links of the other nexus to this one, and delete the old
if (r.nexus_ == 0) { // nexus.
if (list_ == 0) {
if (r.next_ == 0) {
list_ = &r;
r.next_ = &r;
r.nexus_ = this;
driven_ = NO_GUESS;
} else {
driven_ = r_nexus->driven_;
list_ = r_nexus->list_;
list_->nexus_ = this;
r_nexus->list_ = 0;
delete r_nexus;
}
return;
}
// Special case: The Link is unconnected. Put it at the end of
// the current list and move the list_ pointer and nexus_ back
// pointer to suit.
if (r.next_ == 0) {
if (r.get_dir() != Link::INPUT) if (r.get_dir() != Link::INPUT)
driven_ = NO_GUESS; driven_ = NO_GUESS;
r.nexus_ = this; r.nexus_ = this;
r.next_ = list_; r.next_ = list_->next_;
list_->next_ = &r;
list_->nexus_ = 0;
list_ = &r; list_ = &r;
return; return;
} }
Nexus*tmp = r.nexus_; if (r_nexus->driven_ != Vz)
if (tmp->driven_ != Vz)
driven_ = NO_GUESS; driven_ = NO_GUESS;
while (Link*cur = tmp->list_) { // Splice the list of links from the "tmp" nexus to the end of
tmp->list_ = cur->next_; // this nexus. Adjust the nexus pointers as needed.
cur->nexus_ = this; Link*save_first = list_->next_;
cur->next_ = list_; list_->next_ = r_nexus->list_->next_;
list_ = cur; r_nexus->list_->next_ = save_first;
} list_->nexus_ = 0;
list_ = r_nexus->list_;
list_->nexus_ = this;
assert(tmp->list_ == 0); r_nexus->list_ = 0;
delete tmp; delete r_nexus;
} }
void connect(Link&l, Link&r) void connect(Link&l, Link&r)
@ -75,8 +99,7 @@ void connect(Link&l, Link&r)
} else if (r.nexus_ != 0) { } else if (r.nexus_ != 0) {
connect(r.nexus_, l); connect(r.nexus_, l);
} else { } else {
Nexus*tmp = new Nexus; Nexus*tmp = new Nexus(l);
tmp->connect(l);
tmp->connect(r); tmp->connect(r);
} }
} }
@ -89,25 +112,39 @@ Link::Link()
Link::~Link() Link::~Link()
{ {
if (Nexus*tmp = nexus_) { if (next_) {
nexus_->unlink(this); Nexus*tmp = nexus();
tmp->unlink(this);
if (tmp->list_ == 0) if (tmp->list_ == 0)
delete tmp; delete tmp;
} }
} }
Nexus* Link::find_nexus_() const
{
assert(next_);
if (nexus_) return nexus_;
for (Link*cur = next_ ; cur != this ; cur = cur->next_) {
if (cur->nexus_) return cur->nexus_;
}
return 0;
}
Nexus* Link::nexus() Nexus* Link::nexus()
{ {
if (nexus_ == 0) { if (next_ == 0) {
Nexus*tmp = new Nexus; assert(nexus_ == 0);
tmp->relink_(this); Nexus*tmp = new Nexus(*this);
return tmp;
} }
return nexus_;
return find_nexus_();
} }
const Nexus* Link::nexus() const const Nexus* Link::nexus() const
{ {
return nexus_; if (next_ == 0) return 0;
return find_nexus_();
} }
void Link::set_dir(DIR d) void Link::set_dir(DIR d)
@ -122,12 +159,12 @@ Link::DIR Link::get_dir() const
void Link::drivers_delays(NetExpr*rise, NetExpr*fall, NetExpr*decay) void Link::drivers_delays(NetExpr*rise, NetExpr*fall, NetExpr*decay)
{ {
nexus_->drivers_delays(rise, fall, decay); find_nexus_()->drivers_delays(rise, fall, decay);
} }
void Link::drivers_drive(strength_t drive0__, strength_t drive1__) void Link::drivers_drive(strength_t drive0__, strength_t drive1__)
{ {
nexus_->drivers_drive(drive0__, drive1__); find_nexus_()->drivers_drive(drive0__, drive1__);
} }
void Link::drive0(Link::strength_t str) void Link::drive0(Link::strength_t str)
@ -175,11 +212,10 @@ void Link::cur_link(const NetPins*&net, unsigned &pin) const
void Link::unlink() void Link::unlink()
{ {
assert(nexus_);
if (! is_linked()) if (! is_linked())
return; return;
nexus_->unlink(this); find_nexus_()->unlink(this);
} }
bool Link::is_equal(const Link&that) const bool Link::is_equal(const Link&that) const
@ -189,31 +225,44 @@ bool Link::is_equal(const Link&that) const
bool Link::is_linked() const bool Link::is_linked() const
{ {
if (nexus_ == 0) if (next_ == 0)
return false;
if (next_ == this)
return false; return false;
if (next_)
return true;
if (nexus_->first_nlink() != this)
return true;
return false; return true;
} }
bool Link::is_linked(const Link&that) const bool Link::is_linked(const Link&that) const
{ {
if (nexus_ == 0) if (next_ == 0)
return false; return false;
return nexus_ == that.nexus_; if (that.next_ == 0)
return false;
const Link*cur = next_;
while (cur != this) {
if (cur == &that) return true;
cur = cur->next_;
}
return false;
} }
/*
* If this link has a nexus_ pointer, then it is the last Link in the
* list. next_nlink() returns 0 for the last Link.
*/
Link* Link::next_nlink() Link* Link::next_nlink()
{ {
return next_; if (nexus_) return 0;
else return next_;
} }
const Link* Link::next_nlink() const const Link* Link::next_nlink() const
{ {
return next_; if (nexus_) return 0;
else return next_;
} }
const NetPins*Link::get_obj() const const NetPins*Link::get_obj() const
@ -242,12 +291,29 @@ unsigned Link::get_pin() const
return pin_; return pin_;
} }
Nexus::Nexus() Nexus::Nexus(Link&that)
{ {
name_ = 0; name_ = 0;
list_ = 0;
driven_ = NO_GUESS; driven_ = NO_GUESS;
t_cookie_ = 0; t_cookie_ = 0;
if (that.next_ == 0) {
list_ = &that;
that.next_ = &that;
that.nexus_ = this;
driven_ = NO_GUESS;
} else {
Nexus*tmp = that.find_nexus_();
list_ = tmp->list_;
list_->nexus_ = this;
driven_ = tmp->driven_;
name_ = tmp->name_;
tmp->list_ = 0;
tmp->name_ = 0;
delete tmp;
}
} }
Nexus::~Nexus() Nexus::~Nexus()
@ -259,8 +325,7 @@ Nexus::~Nexus()
verinum::V Nexus::get_init() const verinum::V Nexus::get_init() const
{ {
assert(list_); for (const Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) {
for (Link*cur = list_ ; cur ; cur = cur->next_) {
if (cur->get_dir() == Link::OUTPUT) if (cur->get_dir() == Link::OUTPUT)
return verinum::Vx; return verinum::Vx;
@ -274,8 +339,7 @@ verinum::V Nexus::get_init() const
bool Nexus::assign_lval() const bool Nexus::assign_lval() const
{ {
assert(list_); for (const Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) {
for (Link*cur = list_ ; cur ; cur = cur->next_) {
const NetPins*obj; const NetPins*obj;
unsigned pin; unsigned pin;
@ -293,8 +357,7 @@ bool Nexus::assign_lval() const
bool Nexus::drivers_present() const bool Nexus::drivers_present() const
{ {
assert(list_); for (const Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) {
for (Link*cur = list_ ; cur ; cur = cur->next_) {
if (cur->get_dir() == Link::OUTPUT) if (cur->get_dir() == Link::OUTPUT)
return true; return true;
@ -328,7 +391,7 @@ bool Nexus::drivers_present() const
void Nexus::drivers_delays(NetExpr*rise, NetExpr*fall, NetExpr*decay) void Nexus::drivers_delays(NetExpr*rise, NetExpr*fall, NetExpr*decay)
{ {
for (Link*cur = list_ ; cur ; cur = cur->next_) { for (Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) {
if (cur->get_dir() != Link::OUTPUT) if (cur->get_dir() != Link::OUTPUT)
continue; continue;
@ -344,7 +407,7 @@ void Nexus::drivers_delays(NetExpr*rise, NetExpr*fall, NetExpr*decay)
void Nexus::drivers_drive(Link::strength_t drive0, Link::strength_t drive1) void Nexus::drivers_drive(Link::strength_t drive0, Link::strength_t drive1)
{ {
for (Link*cur = list_ ; cur ; cur = cur->next_) { for (Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) {
if (cur->get_dir() != Link::OUTPUT) if (cur->get_dir() != Link::OUTPUT)
continue; continue;
@ -360,52 +423,57 @@ void Nexus::unlink(Link*that)
name_ = 0; name_ = 0;
} }
/* If the link I'm removing was a driver for this nexus, then
cancel my guess of the driven value. */
if (that->get_dir() != Link::INPUT)
driven_ = NO_GUESS;
assert(that); assert(that);
if (list_ == that) {
list_ = that->next_; // Special case: the Link is the only link in the nexus. In
that->next_ = 0; // this case, the unlink is trivial. Also clear the Nexus
// pointers.
if (that->next_ == that) {
assert(that->nexus_ == this);
assert(list_ == that);
list_ = 0;
driven_ = NO_GUESS;
that->nexus_ = 0; that->nexus_ = 0;
that->next_ = 0;
return; return;
} }
Link*cur = list_; // If the link I'm removing was a driver for this nexus, then
while (cur->next_ != that) { // cancel my guess of the driven value.
assert(cur->next_); if (that->get_dir() != Link::INPUT)
cur = cur->next_; driven_ = NO_GUESS;
// Look for the Link that points to "that". We know that there
// will be one because the list is a circle. When we find the
// prev pointer, then remove that from the list.
Link*prev = list_;
while (prev->next_ != that)
prev = prev->next_;
prev->next_ = that->next_;
// If "that" was the last item in the list, then change the
// list_ pointer to point to the new end of the list.
if (list_ == that) {
assert(that->nexus_ == this);
list_ = prev;
list_->nexus_ = this;
} }
cur->next_ = that->next_;
that->nexus_ = 0; that->nexus_ = 0;
that->next_ = 0; that->next_ = 0;
} }
void Nexus::relink_(Link*that)
{
/* If the link I'm adding is a driver for this nexus, then
cancel my guess of the driven value. */
if (that->get_dir() != Link::INPUT)
driven_ = NO_GUESS;
assert(that->nexus_ == 0);
assert(that->next_ == 0);
that->next_ = list_;
that->nexus_ = this;
list_ = that;
}
Link* Nexus::first_nlink() Link* Nexus::first_nlink()
{ {
return list_; if (list_) return list_->next_;
else return 0;
} }
const Link* Nexus::first_nlink() const const Link* Nexus::first_nlink() const
{ {
return list_; if (list_) return list_->next_;
else return 0;
} }
ivl_nexus_t Nexus::t_cookie() const ivl_nexus_t Nexus::t_cookie() const

View File

@ -168,9 +168,12 @@ class Link {
verinum::V init_ : 2; verinum::V init_ : 2;
private: private:
// The Nexus uses these to maintain a single linked list of Nexus* find_nexus_() const;
// Link objects. If this link is not connected to anything,
// then these pointers are nil. private:
// The Nexus uses these to maintain its list of Link
// objects. If this link is not connected to anything,
// then these pointers are both nil.
Link *next_; Link *next_;
Nexus*nexus_; Nexus*nexus_;
@ -313,9 +316,11 @@ class NetBranch : public NetPins, public IslandBranch {
* together. Each link has its own properties, this class holds the * together. Each link has its own properties, this class holds the
* properties of the group. * properties of the group.
* *
* The links in a nexus are grouped into a singly linked list, with * The links in a nexus are grouped into a circularly linked list,
* the nexus pointing to the first Link. Each link in turn points to * with the nexus pointing to the last Link. Each link in turn points
* the next link in the nexus, with the last link pointing to 0. * to the next link in the nexus, with the last link pointing back to
* the first. The last link also has a non-nil nexus_ pointer back to
* this nexus.
* *
* The t_cookie() is a void* that targets can use to store information * The t_cookie() is a void* that targets can use to store information
* in a Nexus. ivl guarantees that the t_cookie will be 0 when the * in a Nexus. ivl guarantees that the t_cookie will be 0 when the
@ -326,10 +331,13 @@ class Nexus {
friend void connect(Link&, Link&); friend void connect(Link&, Link&);
friend class Link; friend class Link;
public: private:
explicit Nexus(); // Only Link objects can create (or delete) Nexus objects
explicit Nexus(Link&r);
~Nexus(); ~Nexus();
public:
void connect(Link&r); void connect(Link&r);
const char* name() const; const char* name() const;
@ -371,7 +379,6 @@ class Nexus {
private: private:
Link*list_; Link*list_;
void unlink(Link*); void unlink(Link*);
void relink_(Link*);
mutable char* name_; /* Cache the calculated name for the Nexus. */ mutable char* name_; /* Cache the calculated name for the Nexus. */
mutable ivl_nexus_t t_cookie_; mutable ivl_nexus_t t_cookie_;