Skip to content

Commit

Permalink
Constant folding for array references
Browse files Browse the repository at this point in the history
  • Loading branch information
nickg committed Oct 2, 2011
1 parent fa11422 commit b0df00a
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 4 deletions.
2 changes: 1 addition & 1 deletion src/nvc.c
Expand Up @@ -92,7 +92,7 @@ static int analyse(int argc, char **argv)

tree_gc();

if (parse_errors() > 0 || sem_errors() > 0)
if (parse_errors() + sem_errors() + simplify_errors() > 0)
return EXIT_FAILURE;

lib_save(lib_work());
Expand Down
3 changes: 3 additions & 0 deletions src/phase.h
Expand Up @@ -33,6 +33,9 @@ void sem_bootstrap_en(bool en);
// Fold all constant expressions
void simplify(tree_t top);

// Number of errors found during simplification
int simplify_errors(void);

// Find all drivers associated with signals
void driver_extract(tree_t top);

Expand Down
10 changes: 10 additions & 0 deletions src/sem.c
Expand Up @@ -1423,6 +1423,16 @@ static bool sem_check_array_ref(tree_t t)
if (type_kind(type) != T_CARRAY)
sem_error(t, "invalid array reference");

bool ok = true;
for (unsigned i = 0; i < tree_params(t); i++) {
param_t p = tree_param(t, i);
if (p.kind != P_POS)
sem_error(t, "only scalar references supported");

// TODO: push type set containing index type of array
ok = sem_check(p.value) && ok;
}

tree_set_type(t, type_base(type));
tree_set_ref(t, tree_ref(value));
return true;
Expand Down
88 changes: 88 additions & 0 deletions src/simp.c
Expand Up @@ -28,6 +28,11 @@ static tree_t simp_expr(tree_t t);
static ident_t std_bool_i = NULL;
static ident_t builtin_i = NULL;

static int errors = 0;

#define simp_error(t, __VA_ARGS__) \
{ errors++; error_at(tree_loc(t), __VA_ARGS__); return t; }

static bool folded_num(tree_t t, literal_t *l)
{
if (tree_kind(t) == T_LITERAL) {
Expand All @@ -52,6 +57,14 @@ static bool folded_bool(tree_t t, bool *b)
return false;
}

static int64_t assume_int(tree_t t)
{
assert(tree_kind(t) == T_LITERAL);
literal_t l = tree_literal(t);
assert(l.kind == L_INT);
return l.i;
}

static tree_t get_int_lit(tree_t t, int64_t i)
{
tree_t fdecl = tree_ref(t);
Expand Down Expand Up @@ -228,6 +241,73 @@ static tree_t simp_attr_ref(tree_t t)
}
}

static tree_t simp_array_ref(tree_t t)
{
tree_set_value(t, simp_expr(tree_value(t)));

literal_t indexes[tree_params(t)];
bool can_fold = true;
for (unsigned i = 0; i < tree_params(t); i++) {
param_t p = tree_param(t, i);
assert(p.kind == P_POS);
p.value = simp_expr(p.value);
tree_change_param(t, i, p);
can_fold = can_fold && folded_num(p.value, &indexes[i]);
}

if (!can_fold)
return t;

assert(tree_params(t) == 1);

tree_t decl = tree_ref(t);
// XXX: may not be decl e.g. nested array ref

switch (tree_kind(decl)) {
case T_CONST_DECL:
{
tree_t v = tree_value(decl);
assert(tree_kind(v) == T_AGGREGATE);
assert(indexes[0].kind == L_INT);

range_t bounds = type_dim(tree_type(decl), 0);
int64_t left = assume_int(bounds.left);
int64_t right = assume_int(bounds.right);

if (indexes[0].i < left || indexes[0].i > right)
simp_error(t, "array reference out of bounds");

for (unsigned i = 0; i < tree_assocs(v); i++) {
assoc_t a = tree_assoc(v, i);
switch (a.kind) {
case A_POS:
if (a.pos + left == indexes[0].i)
return a.value;
break;

case A_OTHERS:
return a.value;

case A_RANGE:
if ((indexes[0].i >= assume_int(a.range.left))
&& (indexes[0].i <= assume_int(a.range.right)))
return a.value;
break;

case A_NAMED:
if (assume_int(a.name) == indexes[0].i)
return a.value;
break;
}
}

assert(false);
}
default:
return t;
}
}

static tree_t simp_expr(tree_t t)
{
switch (tree_kind(t)) {
Expand Down Expand Up @@ -267,6 +347,9 @@ static tree_t simp_expr(tree_t t)
case T_ATTR_REF:
return simp_attr_ref(t);

case T_ARRAY_REF:
return simp_array_ref(t);

default:
assert(false);
}
Expand Down Expand Up @@ -407,3 +490,8 @@ void simplify(tree_t top)
assert(false);
}
}

int simplify_errors(void)
{
return errors;
}
1 change: 1 addition & 0 deletions test/regress/testlist.txt
Expand Up @@ -6,3 +6,4 @@ arith1 normal
signal1 normal
attr1 normal
signal2 normal
#signal3 normal
8 changes: 5 additions & 3 deletions test/run_regr.rb
Expand Up @@ -14,9 +14,11 @@
def read_tests
tests = []
File.open(TestDir + "regress/testlist.txt").each_line do |l|
parts = l.split /\s+/
flags = parts[1].split /,/
tests << { :name => parts[0], :flags => flags }
parts = l.gsub(/\#.*$/, '').strip.split(/\s+/)
if parts.length > 0 then
flags = parts[1].split /,/
tests << { :name => parts[0], :flags => flags }
end
end
tests
end
Expand Down
10 changes: 10 additions & 0 deletions test/simp/cfold.vhd
Expand Up @@ -6,6 +6,10 @@ architecture a of e is
type t is range -5 to 11 - 3;
constant c : integer := +4 + 1;
signal y : t;
type int_array is array (integer range <>) of integer;
constant a1 : int_array(1 to 5) := (1, 2, 3, 4, 5);
constant a2 : int_array(1 to 7) := (2 to 3 => 6, others => 5);
constant a3 : int_array(1 to 9) := (8 => 24, others => 0);
begin

process is
Expand All @@ -27,6 +31,12 @@ begin
b := false nand false;
b := false nor true;
b := 7 > 5 and 6 < 2;
x <= a1(2);
x <= a2(1);
x <= a2(3);
x <= a3(8);
x <= a3(10); -- Error!
x <= a3(-1); -- Error!
end process;

end architecture;
48 changes: 48 additions & 0 deletions test/test_simp.c
Expand Up @@ -8,6 +8,14 @@
#include <stdio.h>
#include <string.h>

typedef struct error {
int line;
const char *snippet;
} error_t;

static const error_t *error_lines = NULL;
static error_fn_t orig_error_fn = NULL;

static void setup(void)
{
lib_set_work(lib_tmp());
Expand All @@ -18,6 +26,33 @@ static void teardown(void)
lib_free(lib_work());
}

static void test_error_fn(const char *msg, const loc_t *loc)
{
fail_if(error_lines == NULL);

bool unexpected = error_lines->line == -1
|| error_lines->snippet == NULL
|| error_lines->line != loc->first_line
|| strstr(msg, error_lines->snippet) == NULL;

if (unexpected) {
orig_error_fn(msg, loc);
printf("expected line %d '%s'\n",
error_lines->line, error_lines->snippet);
}

fail_if(unexpected);

error_lines++;
}

static void expect_errors(const error_t *lines)
{
fail_unless(orig_error_fn == NULL);
orig_error_fn = set_error_fn(test_error_fn);
error_lines = lines;
}

static bool folded_i(tree_t t, int64_t i)
{
if (tree_kind(t) != T_LITERAL)
Expand Down Expand Up @@ -50,6 +85,13 @@ START_TEST(test_cfold)
tree_t e, a, p, s;
range_t r;

const error_t expect[] = {
{ 38, "array reference out of bounds" },
{ 39, "array reference out of bounds" },
{ -1, NULL }
};
expect_errors(expect);

fail_unless(input_from_file(TESTDIR "/simp/cfold.vhd"));

e = parse();
Expand Down Expand Up @@ -98,6 +140,12 @@ START_TEST(test_cfold)
fail_unless(folded_b(tree_value(tree_stmt(p, 13)), true));
fail_unless(folded_b(tree_value(tree_stmt(p, 14)), false));
fail_unless(folded_b(tree_value(tree_stmt(p, 15)), false));
fail_unless(folded_i(tree_value(tree_stmt(p, 16)), 2));
fail_unless(folded_i(tree_value(tree_stmt(p, 17)), 5));
fail_unless(folded_i(tree_value(tree_stmt(p, 18)), 6));
fail_unless(folded_i(tree_value(tree_stmt(p, 19)), 24));

fail_unless(simplify_errors() == (sizeof(expect) / sizeof(error_t)) - 1);
}
END_TEST

Expand Down

0 comments on commit b0df00a

Please sign in to comment.