Added pruning of case statement expressions.

When unsized literals are used in case item expressions, it is likely
that the calculated expression width will be larger than necessary to
unambiguously select the correct case item (particularly when using
"strict" expression elaboration). This patch adds an optimisation
step that prunes the expressions to the minimum necessary width.
This commit is contained in:
Martin Whitaker 2014-11-02 12:37:32 +00:00
parent e38b5d9fb7
commit 0237297e93
5 changed files with 92 additions and 6 deletions

View File

@ -3110,6 +3110,8 @@ NetProc* PCase::elaborate(Design*des, NetScope*scope) const
}
}
res->prune();
return res;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000-2011 Stephen Williams (steve@icarus.com)
* Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -19,8 +19,10 @@
# include "config.h"
# include "compiler.h"
# include "netlist.h"
# include <cassert>
# include "netmisc.h"
# include "ivl_assert.h"
NetBlock::NetBlock(Type t, NetScope*ss)
: type_(t), subscope_(ss), last_(0)
@ -73,7 +75,7 @@ const NetProc* NetBlock::proc_next(const NetProc*cur) const
NetCase::NetCase(NetCase::TYPE c, NetExpr*ex, unsigned cnt)
: type_(c), expr_(ex), items_(cnt)
{
assert(expr_);
ivl_assert(*this, expr_);
}
NetCase::~NetCase()
@ -92,11 +94,73 @@ NetCase::TYPE NetCase::type() const
void NetCase::set_case(unsigned idx, NetExpr*e, NetProc*p)
{
assert(idx < items_.size());
ivl_assert(*this, idx < items_.size());
items_[idx].guard = e;
items_[idx].statement = p;
}
void NetCase::prune()
{
// Test whether the case expression has been padded out
NetESelect*padded_expr = dynamic_cast<NetESelect*>(expr_);
if ((padded_expr == 0) || (padded_expr->select() != 0))
return;
// If so, run through the case item expressions to find
// the minimum number of bits needed to unambiguously
// select the correct case item.
const NetExpr*unpadded_expr = padded_expr->sub_expr();
unsigned padded_width = padded_expr->expr_width();
unsigned prune_width = unpadded_expr->expr_width();
for (unsigned idx = 0; idx < items_.size(); idx += 1) {
// If there is no guard expression, this is the default
// case, so skip it.
if (items_[idx].guard == 0)
continue;
// If the guard expression is not constant, assume
// all bits are needed, so no pruning can be done.
NetEConst*gc = dynamic_cast<NetEConst*>(items_[idx].guard);
if (gc == 0)
return;
unsigned sig_bits = gc->value().significant_bits();
if (sig_bits > prune_width)
prune_width = sig_bits;
// If all the padding bits are needed, no pruning
// can be done.
if (prune_width >= padded_width)
return;
}
ivl_assert(*this, prune_width < padded_width);
if (debug_elaborate) {
cerr << get_fileline() << ": debug: pruning case expressions to "
<< prune_width << " bits." << endl;
}
// Prune the case expression
expr_ = pad_to_width(unpadded_expr->dup_expr(), prune_width, *expr_);
delete padded_expr;
// Prune the case item expressions
for (unsigned idx = 0; idx < items_.size(); idx += 1) {
if (items_[idx].guard == 0)
continue;
NetEConst*gc = dynamic_cast<NetEConst*>(items_[idx].guard);
ivl_assert(*this, gc);
verinum value(gc->value(), prune_width);
NetEConst*tmp = new NetEConst(value);
tmp->set_line(*gc);
delete gc;
items_[idx].guard = tmp;
}
}
NetDisable::NetDisable(NetScope*tgt)
: target_(tgt)
{
@ -175,7 +239,7 @@ NetPDelay::~NetPDelay()
uint64_t NetPDelay::delay() const
{
assert(expr_ == 0);
ivl_assert(*this, expr_ == 0);
return delay_;
}

View File

@ -2939,6 +2939,8 @@ class NetCase : public NetProc {
void set_case(unsigned idx, NetExpr*ex, NetProc*st);
void prune();
TYPE type() const;
const NetExpr*expr() const { return expr_; }
inline unsigned nitems() const { return items_.size(); }

View File

@ -587,6 +587,21 @@ bool verinum::is_negative() const
return (bits_[nbits_-1] == V1) && has_sign();
}
unsigned verinum::significant_bits() const
{
unsigned sbits = nbits_;
if (has_sign_) {
V sign_bit = bits_[sbits-1];
while ((sbits > 1) && (bits_[sbits-2] == sign_bit))
sbits -= 1;
} else {
while ((sbits > 1) && (bits_[sbits-1] == verinum::V0))
sbits -= 1;
}
return sbits;
}
void verinum::cast_to_int2()
{
for (unsigned idx = 0 ; idx < nbits_ ; idx += 1) {

View File

@ -59,7 +59,7 @@ class verinum {
~verinum();
verinum& operator= (const verinum&);
// Number of significant bits in this number.
// Number of stored bits in this number.
unsigned len() const { return nbits_; }
// A number "has a length" if the length was specified fixed
@ -86,6 +86,9 @@ class verinum {
// Comparison for use in sorting algorithms.
bool is_before(const verinum&that) const;
// Number of significant bits in this number.
unsigned significant_bits() const;
// Convert 4-state to 2-state
void cast_to_int2();