Permalink
Browse files

Add simplification pass

  • Loading branch information...
1 parent 3e8552b commit 96a94ccf0b05209d5833561899bb0c7aa4f3db09 @nickg committed Sep 18, 2011
Showing with 314 additions and 2 deletions.
  1. +1 −1 src/Makefile.am
  2. +1 −0 src/nhdl.c
  3. +1 −0 src/phase.h
  4. +194 −0 src/simp.c
  5. +11 −0 src/tree.c
  6. +1 −0 src/tree.h
  7. +9 −0 src/type.c
  8. +1 −0 src/type.h
  9. +2 −1 test/Makefile.am
  10. +9 −0 test/simp/cfold.vhd
  11. +84 −0 test/test_simp.c
View
@@ -10,7 +10,7 @@ AM_LDFLAGS = -rdynamic $(LLVM_LDFLAGS)
BUILT_SOURCES = parse.h
libnhdl_a_SOURCES = lib.c util.c ident.c parse.y lexer.l tree.c type.c \
- sem.c elab.c
+ sem.c elab.c simp.c
libcgen_a_SOURCES = cgen.c
libcgen_a_CFLAGS = $(AM_CFLAGS) $(LLVM_CFLAGS)
View
@@ -86,6 +86,7 @@ static int analyse(int argc, char **argv)
tree_t unit;
while ((unit = parse())) {
sem_check(unit);
+ simplify(unit);
}
}
View
@@ -20,6 +20,7 @@
#include "tree.h"
+void simplify(tree_t top);
tree_t elab(tree_t top);
void cgen(tree_t top);
View
@@ -0,0 +1,194 @@
+//
+// Copyright (C) 2011 Nick Gasson
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program 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.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "phase.h"
+#include "util.h"
+
+#include <assert.h>
+#include <string.h>
+
+#define MAX_BUILTIN_ARGS 2
+
+#define FOLD_BINARY(args, l, op) \
+ if (args[0].kind == L_INT && args[1].kind == L_INT) { \
+ l.i = args[0].i op args[1].i; \
+ l.kind = L_INT; \
+ } \
+ else \
+ assert(false); \
+
+#define FOLD_UNARY(args, l, op) \
+ if (args[0].kind == L_INT) { \
+ l.i = op args[0].i; \
+ l.kind = L_INT; \
+ } \
+ else \
+ assert(false); \
+
+static tree_t simp_expr(tree_t t);
+
+static bool folded(tree_t t, literal_t *l)
+{
+ if (tree_kind(t) == T_LITERAL) {
+ *l = tree_literal(t);
+ return true;
+ }
+ else
+ return false;
+}
+
+static tree_t simp_fcall(tree_t t)
+{
+ tree_t decl = tree_ref(t);
+ assert(tree_kind(decl) == T_FUNC_DECL);
+
+ const char *builtin = tree_attr_str(decl, ident_new("builtin"));
+ if (builtin == NULL)
+ return t; // TODO: expand pure function calls
+
+ if (tree_params(t) > MAX_BUILTIN_ARGS)
+ return t;
+
+ bool can_fold = true;
+ literal_t args[MAX_BUILTIN_ARGS];
+ for (unsigned i = 0; i < tree_params(t); i++) {
+ tree_t p = simp_expr(tree_param(t, i));
+ tree_change_param(t, i, p);
+ can_fold = can_fold && folded(p, &args[i]);
+ }
+
+ if (!can_fold)
+ return t;
+
+ literal_t l;
+ if (strcmp(builtin, "mul") == 0) {
+ FOLD_BINARY(args, l, *);
+ }
+ else if (strcmp(builtin, "add") == 0) {
+ FOLD_BINARY(args, l, +);
+ }
+ else if (strcmp(builtin, "sub") == 0) {
+ FOLD_BINARY(args, l, -);
+ }
+ else if (strcmp(builtin, "neg") == 0) {
+ FOLD_UNARY(args, l, -);
+ }
+ else
+ fatal("cannot fold builtin %s", builtin);
+
+ tree_t f = tree_new(T_LITERAL);
+ tree_set_loc(f, tree_loc(t));
+ tree_set_literal(f, l);
+
+ return f;
+}
+
+static tree_t simp_expr(tree_t t)
+{
+ switch (tree_kind(t)) {
+ case T_LITERAL:
+ return t;
+
+ case T_FCALL:
+ return simp_fcall(t);
+
+ default:
+ assert(false);
+ }
+}
+
+static void simp_type_decl(tree_t t)
+{
+ type_t type = tree_type(t);
+
+ switch (type_kind(type)) {
+ case T_INTEGER:
+ case T_PHYSICAL:
+ case T_CARRAY:
+ {
+ for (unsigned i = 0; i < type_dims(type); i++) {
+ range_t r = type_dim(type, i);
+ r.left = simp_expr(r.left);
+ r.right = simp_expr(r.right);
+ type_change_dim(type, i, r);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void simp_decl(tree_t t)
+{
+ switch (tree_kind(t)) {
+ case T_SIGNAL_DECL:
+ case T_VAR_DECL:
+ if (tree_has_value(t))
+ tree_set_value(t, simp_expr(tree_value(t)));
+ break;
+
+ case T_TYPE_DECL:
+ simp_type_decl(t);
+ break;
+
+ case T_FUNC_DECL:
+ break;
+
+ default:
+ assert(false);
+ }
+}
+
+static void simp_entity(tree_t t)
+{
+ for (unsigned i = 0; i < tree_generics(t); i++)
+ simp_decl(tree_generic(t, i));
+
+ for (unsigned i = 0; i < tree_ports(t); i++)
+ simp_decl(tree_port(t, i));
+}
+
+static void simp_arch(tree_t t)
+{
+ for (unsigned i = 0; i < tree_decls(t); i++)
+ simp_decl(tree_decl(t, i));
+}
+
+static void simp_package(tree_t t)
+{
+ for (unsigned i = 0; i < tree_decls(t); i++)
+ simp_decl(tree_decl(t, i));
+}
+
+void simplify(tree_t top)
+{
+ switch (tree_kind(top)) {
+ case T_ENTITY:
+ simp_entity(top);
+ break;
+ case T_ARCH:
+ simp_arch(top);
+ break;
+ case T_PACKAGE:
+ simp_package(top);
+ break;
+ default:
+ assert(false);
+ }
+}
View
@@ -457,6 +457,17 @@ void tree_add_param(tree_t t, tree_t e)
tree_array_add(&t->params, e);
}
+void tree_change_param(tree_t t, unsigned n, tree_t e)
+{
+ assert(t != NULL);
+ assert(e != NULL);
+ assert(HAS_PARAMS(t));
+ assert(IS_EXPR(e));
+ assert(n < t->params.count);
+
+ t->params.items[n] = e;
+}
+
void tree_set_literal(tree_t t, literal_t lit)
{
assert(t != NULL);
View
@@ -122,6 +122,7 @@ unsigned tree_params(tree_t t);
tree_t tree_param(tree_t t, unsigned n);
// TODO: what about named association? Add an ident parameter.
void tree_add_param(tree_t t, tree_t e);
+void tree_change_param(tree_t t, unsigned n, tree_t e);
// T_LITERAL
literal_t tree_literal(tree_t t);
View
@@ -206,6 +206,15 @@ void type_add_dim(type_t t, range_t r)
t->dims[t->n_dims++] = r;
}
+void type_change_dim(type_t t, unsigned n, range_t r)
+{
+ assert(t != NULL);
+ assert(HAS_DIMS(t));
+ assert(n < t->n_dims);
+
+ t->dims[n] = r;
+}
+
type_t type_base(type_t t)
{
assert(t != NULL);
View
@@ -75,6 +75,7 @@ void type_set_base(type_t t, type_t b);
unsigned type_dims(type_t t);
range_t type_dim(type_t t, unsigned n);
void type_add_dim(type_t t, range_t r);
+void type_change_dim(type_t t, unsigned n, range_t r);
// T_ENUM
unsigned type_enum_literals(type_t t);
View
@@ -1,4 +1,4 @@
-check_PROGRAMS = test_lib test_ident test_parse test_sem
+check_PROGRAMS = test_lib test_ident test_parse test_sem test_simp
TESTS = $(check_PROGRAMS)
src = $(top_srcdir)/src
@@ -13,3 +13,4 @@ test_lib_SOURCES = test_lib.c
test_ident_SOURCES = test_ident.c
test_parse_SOURCES = test_parse.c
test_sem_SOURCES = test_sem.c
+test_simp_SOURCES = test_simp.c
View
@@ -0,0 +1,9 @@
+entity e is
+end entity;
+
+architecture a of e is
+ signal x : integer := -3 * 4 + 2;
+ type t is range -5 to 11 - 3;
+begin
+
+end architecture;
View
@@ -0,0 +1,84 @@
+#include "parse.h"
+#include "type.h"
+#include "util.h"
+#include "sem.h"
+#include "phase.h"
+
+#include <check.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+static void setup(void)
+{
+ lib_set_work(lib_tmp());
+}
+
+static void teardown(void)
+{
+ lib_free(lib_work());
+}
+
+static bool folded_i(tree_t t, int64_t i)
+{
+ if (tree_kind(t) != T_LITERAL)
+ return false;
+
+ literal_t l = tree_literal(t);
+ if (l.kind != L_INT)
+ return false;
+
+ return l.i == i;
+}
+
+START_TEST(test_cfold)
+{
+ tree_t e, a;
+
+ fail_unless(input_from_file(TESTDIR "/simp/cfold.vhd"));
+
+ e = parse();
+ fail_if(e == NULL);
+ fail_unless(tree_kind(e) == T_ENTITY);
+ sem_check(e);
+
+ a = parse();
+ fail_if(a == NULL);
+ fail_unless(tree_kind(a) == T_ARCH);
+ sem_check(a);
+
+ fail_unless(parse() == NULL);
+ fail_unless(parse_errors() == 0);
+ fail_unless(sem_errors() == 0);
+
+ simplify(a);
+
+ fail_unless(folded_i(tree_value(tree_decl(a, 0)), -10));
+
+ range_t r = type_dim(tree_type(tree_decl(a, 1)), 0);
+ fail_unless(folded_i(r.left, -5));
+ fail_unless(folded_i(r.right, 8));
+}
+END_TEST
+
+int main(void)
+{
+ register_trace_signal_handlers();
+
+ Suite *s = suite_create("simplify");
+
+ TCase *tc_core = tcase_create("Core");
+ tcase_add_unchecked_fixture(tc_core, setup, teardown);
+ tcase_add_test(tc_core, test_cfold);
+ suite_add_tcase(s, tc_core);
+
+ SRunner *sr = srunner_create(s);
+ srunner_run_all(sr, CK_NORMAL);
+
+ int nfail = srunner_ntests_failed(sr);
+
+ srunner_free(sr);
+
+ return nfail == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+

0 comments on commit 96a94cc

Please sign in to comment.