ast/simplify: Use clone_at_zero() for "at_zero" evaluations

The correct way of using the 'at_zero' regime of simplify is to perform
the simplification on a cloned AST subtree, otherwise the "at_zero"
evaluation seeps into the main tree.

Move the effect of the 'at_zero' flag to the cloning itself, so that
the simplify flag can be retired. We assume we can rely on id2ast in
the new clone method.
This commit is contained in:
Martin Povišer 2023-06-21 13:45:42 +02:00 committed by Zachary Snow
parent 77d4b5230e
commit 4fceeb3b32
3 changed files with 56 additions and 22 deletions

View File

@ -336,6 +336,10 @@ namespace AST
// Helper for looking up identifiers which are prefixed with the current module name
std::string try_pop_module_prefix() const;
// helper to clone the node with some of its subexpressions replaced with zero (this is used
// to evaluate widths of dynamic ranges)
AstNode *clone_at_zero();
// helper to print errors from simplify/genrtlil code
[[noreturn]] void input_error(const char *format, ...) const YS_ATTRIBUTE(format(printf, 2, 3));
};

View File

@ -887,10 +887,10 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
if (range->children.size() == 1)
this_width = 1;
else if (!range->range_valid) {
AstNode *left_at_zero_ast = children[0]->children[0]->clone();
AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone() : left_at_zero_ast->clone();
while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
AstNode *left_at_zero_ast = children[0]->children[0]->clone_at_zero();
AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone_at_zero() : left_at_zero_ast->clone();
while (left_at_zero_ast->simplify(true, false, false, 1, -1, false, false)) { }
while (right_at_zero_ast->simplify(true, false, false, 1, -1, false, false)) { }
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
this_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
@ -1460,10 +1460,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
}
if (!children[0]->range_valid) {
AstNode *left_at_zero_ast = children[0]->children[0]->clone();
AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone() : left_at_zero_ast->clone();
while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
AstNode *left_at_zero_ast = children[0]->children[0]->clone_at_zero();
AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone_at_zero() : left_at_zero_ast->clone();
while (left_at_zero_ast->simplify(true, false, false, 1, -1, false, false)) { }
while (right_at_zero_ast->simplify(true, false, false, 1, -1, false, false)) { }
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;

View File

@ -773,6 +773,44 @@ static IdentUsage always_asgn_before_use(const AstNode *node, const std::string
return IdentUsage::NotReferenced;
}
AstNode *AstNode::clone_at_zero()
{
int width_hint;
bool sign_hint;
AstNode *pointee;
switch (type) {
case AST_IDENTIFIER:
if (id2ast)
pointee = id2ast;
else if (current_scope.count(str))
pointee = current_scope[str];
else
break;
if (pointee->type != AST_WIRE &&
pointee->type != AST_AUTOWIRE &&
pointee->type != AST_MEMORY)
break;
YS_FALLTHROUGH;
case AST_MEMRD:
detectSignWidth(width_hint, sign_hint);
return mkconst_int(0, sign_hint, width_hint);
default:
break;
}
AstNode *that = new AstNode;
*that = *this;
for (auto &it : that->children)
it = it->clone_at_zero();
for (auto &it : that->attributes)
it.second = it.second->clone();
return that;
}
static bool try_determine_range_width(AstNode *range, int &result_width)
{
log_assert(range->type == AST_RANGE);
@ -782,11 +820,11 @@ static bool try_determine_range_width(AstNode *range, int &result_width)
return true;
}
AstNode *left_at_zero_ast = range->children[0]->clone();
AstNode *right_at_zero_ast = range->children[1]->clone();
AstNode *left_at_zero_ast = range->children[0]->clone_at_zero();
AstNode *right_at_zero_ast = range->children[1]->clone_at_zero();
while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) {}
while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) {}
while (left_at_zero_ast->simplify(true, false, false, 1, -1, false, false)) {}
while (right_at_zero_ast->simplify(true, false, false, 1, -1, false, false)) {}
bool ok = false;
if (left_at_zero_ast->type == AST_CONSTANT
@ -873,6 +911,8 @@ static void check_auto_nosync(AstNode *node)
// nodes that link to a different node using names and lexical scoping.
bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param)
{
log_assert(!at_zero);
static int recursion_counter = 0;
static bool deep_recursion_warning = false;
@ -4202,16 +4242,6 @@ replace_fcall_later:;
if (current_scope[str]->children[0]->isConst())
newNode = current_scope[str]->children[0]->clone();
}
else if (at_zero && current_scope.count(str) > 0) {
AstNode *node = current_scope[str];
if (node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_MEMORY)
newNode = mkconst_int(0, sign_hint, width_hint);
}
break;
case AST_MEMRD:
if (at_zero) {
newNode = mkconst_int(0, sign_hint, width_hint);
}
break;
case AST_BIT_NOT:
if (children[0]->type == AST_CONSTANT) {