From d0dc00114b22167638b832ca1313f7026a6acdd3 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Sat, 5 Nov 2016 19:46:17 +0000 Subject: [PATCH] Rewrite the complile time evaluator to use vcode Squashed commit of the following: commit ca411a3ce23d8cde0f760d4041e64c3ebd204810 Author: Nick Gasson Date: Sat Nov 5 18:47:27 2016 +0000 Fix crash emitting undefined value commit bf49e3062bc92a1da7943050dc0126d98ed07a17 Author: Nick Gasson Date: Sat Nov 5 18:09:15 2016 +0000 Fix evaluation use-after-free commit 56190598b4d06fa844ffd082fb1543b1c0336986 Author: Nick Gasson Date: Sat Nov 5 13:30:18 2016 +0000 Fix mistake in last commit commit a0a419f45f9db1608ca1943aaf3867ec717ac711 Author: Nick Gasson Date: Sat Nov 5 13:24:37 2016 +0000 Fix confusing name mangling with record references commit 6d729bcd07b0f0ef1df9634ac30ea48a9f33b33f Author: Nick Gasson Date: Sat Nov 5 10:09:50 2016 +0000 Add vcode reference counting and fix memory leaks commit 04804d5994e5e3fedb079bbc97cc399d94919402 Author: Nick Gasson Date: Sat Nov 5 09:13:10 2016 +0000 Stop storing vcode units in trees commit 205a8f1eebd7e7091eb14c7b7cab473f1685ad95 Author: Nick Gasson Date: Fri Oct 28 18:05:39 2016 +0100 Start of attempt to get rid of tree_code commit 5be3ec3ef6d70bbc121e8195e2ce883af413994e Author: Nick Gasson Date: Fri Oct 28 14:37:02 2016 +0100 Fix invalid value kind returned from eval_op_image commit 8206e89977c4e1b9877d30de04df8a43614c43cc Author: Nick Gasson Date: Fri Oct 28 14:24:33 2016 +0100 Vcode evaluation for bit shift operator commit 23bbaaeea9dfe522e7088bc3b2ad0d59c171ceb5 Author: Nick Gasson Date: Thu Oct 27 20:54:35 2016 +0100 Fix Valgrind error commit 97e89e8c212905b9f968c3b66b7a803fe7c48ba8 Author: Nick Gasson Date: Thu Oct 27 20:28:53 2016 +0100 Evaluation for vcode memset operation commit 1a61e019a415b1b66eb73856943237ffb20e956d Author: Nick Gasson Date: Thu Oct 27 19:45:26 2016 +0100 Handle constant folding better in lower_sched_event commit 6bfeccca92375477290c304e87196a7bd2b22794 Author: Nick Gasson Date: Thu Oct 27 19:38:01 2016 +0100 Fix global naming collision commit 6279afb9bb752b1120a441e560058641ceb0d31e Author: Nick Gasson Date: Thu Oct 27 19:12:38 2016 +0100 Instance and path names are only available at elaboration time commit aebf71ce0c759a27195a874bcdb06522d8161e20 Author: Nick Gasson Date: Thu Oct 27 19:09:52 2016 +0100 Fix a few more bugs relating to "undefined" vcode operator commit 31522356e7c652ec1da50b6155c95bff66e6d5da Author: Nick Gasson Date: Thu Oct 27 18:54:05 2016 +0100 Fix regression of protected3 commit 0bacb98b869d5fb7cc8771c8b505c69418608b20 Author: Nick Gasson Date: Thu Oct 27 17:05:20 2016 +0100 Remove redundant elab_funcs function commit 92e0c1a5db3ee74981eda89700fe2dd6ad6fe510 Author: Nick Gasson Date: Tue Oct 25 16:47:42 2016 +0100 Vcode evaluation for record references and constant records commit 1e6f8ef3cbf771f02d3e5e2a45c570963e60894c Author: Nick Gasson Date: Mon Oct 24 19:13:20 2016 +0100 Store a type with vcode_bookmark_t commit 2a7b18e488ce9aa45326d9ef42f405ae0a7ce8c7 Author: Nick Gasson Date: Mon Oct 24 15:24:35 2016 +0100 Evaluating vcode procedure calls commit 7f3617736c4a62804c718c2b6d980088102085e6 Author: Nick Gasson Date: Mon Oct 24 12:51:26 2016 +0100 Rewrite ieee4 to avoid constant folding commit bc552f5b8c6228036a3a1e61483275cd90eb698b Author: Nick Gasson Date: Mon Oct 24 11:40:01 2016 +0100 Add final simplification pass after elaboration commit d8493fee1bc118d40a7ae25d35952c5eb722d2dd Author: Nick Gasson Date: Sun Oct 23 22:33:45 2016 +0100 No fatal error if evaluation causes division by zero commit d19dfccfec7593c4f02fab7c994c6a618300f39a Author: Nick Gasson Date: Sun Oct 23 21:36:59 2016 +0100 Avoid generating redundant code for always-true assertion commit 3276cc289b6ad0894e5a36e51fde1e98fe7a18a0 Author: Nick Gasson Date: Sun Oct 23 21:27:04 2016 +0100 Finish reading/writing vcode units commit c77a614354cf70cf4bbbb5c5bdd728bf1fcef0f7 Author: Nick Gasson Date: Sun Oct 23 18:34:44 2016 +0100 Complete vcode writing to disk commit 8ff29ed06f9e57d9f9c3b85b4e13f0c6d93379c9 Author: Nick Gasson Date: Sun Oct 23 16:49:49 2016 +0100 Do not save automatically after lowering commit c826c31d19626174e0671b2bc3d469c35d5ba183 Author: Nick Gasson Date: Sun Oct 23 16:44:34 2016 +0100 Start adding code to save vcode units commit e7a97c0901315510e9aa64408f84c08c6df65f69 Author: Nick Gasson Date: Sun Oct 23 11:30:34 2016 +0100 Use macros to identify which vcode ops have which fields commit 68c670a995fd1ad869f92ca96ecedba5a564e963 Author: Nick Gasson Date: Sun Oct 23 10:58:57 2016 +0100 Make EVAL_VERBOSE a little more useful commit c8e636039af6ec7402c9a7419d389ce963fb3b54 Author: Nick Gasson Date: Sun Oct 23 10:57:38 2016 +0100 Fix use-after-free in emit_index commit 1740f9c7f6010d02d7bd62bcdc35bcdb13e98fd7 Author: Nick Gasson Date: Sat Oct 22 16:46:03 2016 +0100 Evaluation for vcode op exp commit 68648d486641e0984f0927fe7c49f9d18cf0bc74 Author: Nick Gasson Date: Sat Oct 22 14:49:12 2016 +0100 Fix bug that caused real to be optimised away commit 9d6d75a8062f3d74ea3b7c33813fe90e53c7b710 Author: Nick Gasson Date: Wed Oct 19 19:39:33 2016 +0100 Vcode evaluation for uarray left/right commit a887ff99881652b3bedcc72be12d0d811a89c626 Author: Nick Gasson Date: Wed Oct 19 19:33:41 2016 +0100 No-op implementation for heap save/restore commit c8d1d6d898f942564d26b2599d1f15c0882c01b5 Author: Nick Gasson Date: Wed Oct 19 18:21:30 2016 +0100 Vcode evaluation for 'IMAGE attribute commit 9af7b3529fc0b3e6d1297c701fc714bb86ea865e Author: Nick Gasson Date: Wed Oct 19 09:29:01 2016 +0100 Bounds elision for real variables commit 5b321d2e633e1b055b7edffa5d5317f82a5ba32e Author: Nick Gasson Date: Tue Oct 18 18:23:04 2016 +0100 Implement abs in vcode evaluator commit ba17bc0fa0bf1b2444ec51bfab0c54b43954aded Author: Nick Gasson Date: Tue Oct 18 17:43:22 2016 +0100 Bounds checking at evaluation time commit 214b84da5ae2d1dc8ea6211772ed904f53619530 Author: Nick Gasson Date: Tue Oct 18 11:35:07 2016 +0100 Vcode evaluation for alloca and index check operations commit 9597ee6adfee92770b006947123c8c514f5ee598 Author: Nick Gasson Date: Tue Oct 18 10:51:43 2016 +0100 Vcode elaboration for report and assert commit 55048e269a1bfe8b0251473a01f0ac07daeb5e6a Author: Nick Gasson Date: Tue Oct 18 10:16:33 2016 +0100 Vcode bookmarks now store the tree directly commit e88a69f6ef9fc0ac8518ed21c6a1ad20ecb1c08e Author: Nick Gasson Date: Sun Oct 16 19:36:12 2016 +0100 Improve lowering of if statements where the condition is constant commit f05d644ee73c07f5d8933a18138f19412f281a07 Author: Nick Gasson Date: Sun Oct 16 19:05:36 2016 +0100 Fix evaluation of integer to integer cast commit f7a6cc267ad71195738ed7a8efb29565a22804ff Author: Nick Gasson Date: Thu Oct 13 11:55:54 2016 +0100 Evaluation of some operations on pointers and arrays commit 79d1976f0d112f6b0030aa35f465ef9bd26d7cc7 Author: Nick Gasson Date: Thu Oct 13 10:22:32 2016 +0100 Fix generation of useless heap save operations commit 2869ad50d6dc54b91dfd28b71fc4f346fc20fe8a Author: Nick Gasson Date: Thu Oct 13 10:13:33 2016 +0100 Fix vcode evaluation of mod and rem operators commit 780fc439888c7169cd70ebb85cf9d1650031e1da Author: Nick Gasson Date: Wed Oct 12 20:50:51 2016 +0100 Make fold_tree_fn also fold trivial constants commit eedee901a4a8ea31ed8ea13f25484dbb2f905266 Author: Nick Gasson Date: Wed Oct 12 20:39:50 2016 +0100 Evaluation for vcode dynamic bounds op commit 9265c1b9504336cdfb0a79094ef6c318e40ee731 Author: Nick Gasson Date: Wed Oct 12 20:34:34 2016 +0100 Remove duplicate call to lower_unit commit f54574e4828b5f85b6e57d00aa95a2b28ee29e60 Author: Nick Gasson Date: Wed Oct 12 20:30:37 2016 +0100 Split out constant folding from simplification pass commit 89a5c5053162ea060816d5fb08eee26b5f19ccdd Author: Nick Gasson Date: Wed Oct 12 20:21:54 2016 +0100 Fix naming collisions commit 0a9922a74c957c68678f0c589bf89619d8e35d5a Author: Nick Gasson Date: Wed Oct 12 14:58:56 2016 +0100 Rewrite the complile time evaluator to use vcode Squashed commit of the following: commit d6106c2eb41b98b51560b2725e783c876b2e3577 Author: Nick Gasson Date: Wed Oct 12 14:47:58 2016 +0100 Always lower before elaboration commit 87c6d715dda9b78759fd85882e55f54564663fae Author: Nick Gasson Date: Wed Oct 12 13:50:54 2016 +0100 New type vcode_bookmark_t to abstract tree index in vcode commit d0665b79b7407c379ff5333616055c5b36bcf1c9 Author: Nick Gasson Date: Tue Oct 11 14:37:20 2016 +0100 New approach to early lowering commit c8aa4b2894e78bc41ea73835e3cac1a753664ccb Author: Nick Gasson Date: Sun Oct 9 21:21:47 2016 +0100 Add an undefined operation to simplify error handling when lowering commit 27c9cec0d591b191a861578bb738bc83d848916a Author: Nick Gasson Date: Sun Oct 9 17:35:21 2016 +0100 Fix crash in eval_possible commit d6525fae819d611bc1130aeceb0fe056788e1f88 Author: Nick Gasson Date: Sun Oct 9 17:07:25 2016 +0100 Fix error when local function has not been lowered commit 50aebfe8c788997123fb3b53024ad5d3a169639b Author: Nick Gasson Date: Thu Oct 6 17:52:23 2016 +0100 The llvm.expect.i1 intrinsic breaks optimisation sometimes commit b74eea9c628694d6873b8435bdaa5bd81754c85c Merge: 24049a4 900193c Author: Nick Gasson Date: Sun Oct 9 14:23:42 2016 +0100 Merge branch 'master' into vcode-simp commit 24049a406b6d0792fa5cb06cf16c8199d52a05e9 Author: Nick Gasson Date: Thu Oct 6 10:49:43 2016 +0100 Vcode evaluation for sub operator commit fd85f6c0fb2536a4babe9712a88f61c8d4089642 Author: Nick Gasson Date: Thu Oct 6 10:32:08 2016 +0100 Fix rewriting non-scalar function calls to null commit 96aeef6acdf168d0dd0ca0d48a1bb0222d738514 Author: Nick Gasson Date: Thu Oct 6 10:09:44 2016 +0100 Couple of test fixes commit d06578c0a754ad04793d898e1a9e0dc0628ebe7f Merge: f630db0 defb3f3 Author: Nick Gasson Date: Thu Oct 6 09:53:15 2016 +0100 Merge branch 'master' into vcode-interp commit f630db0564f073025b9d6aa5821e5aa2d4d1ac5f Merge: bb247f9 116f1bf Author: Nick Gasson Date: Sat Jun 11 12:25:29 2016 +0100 Merge branch 'master' into vcode-interp commit 116f1bfd6b6b146567734cf85758a873f1591620 Author: Nick Gasson Date: Sat Jun 11 12:24:56 2016 +0100 Improvements to VHPI tracing commit 31857d6f382c49d355a07734fc072809426a3b3b Author: Nick Gasson Date: Sat Jun 11 10:14:44 2016 +0100 Add --list command to print all units in a library commit c64438e2c2751e5de171f604ae2e113cabfee6bd Author: Nick Gasson Date: Sat Jun 11 09:26:02 2016 +0100 Update HISTORY.md commit bb247f9ffd221169730c5dd785cfa8395cfa5b5c Merge: a37defa 54dd15f Author: Nick Gasson Date: Sat May 14 16:31:53 2016 +0100 Merge branch 'master' into vcode-interp commit a37defa41d5caedb0908fc54cde86346846e753d Merge: 1f40ff0 c84b298 Author: Nick Gasson Date: Sun May 1 17:59:03 2016 +0100 Merge branch 'master' into vcode-interp commit 1f40ff0fd5402dca3e5b1a39c9a5cdc116589aad Author: Nick Gasson Date: Sun Apr 24 15:42:11 2016 +0100 Keep track of heap memory allocated during evaluation commit a61890de59a762e46c9eeafae81c9e5cae88bd8d Author: Nick Gasson Date: Sun Apr 24 15:32:07 2016 +0100 Fix evaluation of uarray wrapping commit e5164ecb2323baa6a96ec3021537abf75b5c2ca3 Merge: f1d80e9 e486477 Author: Nick Gasson Date: Wed Feb 17 20:46:45 2016 +0000 Merge branch 'master' into vcode-interp commit f1d80e94881199f5f0c5366c26b11867fc837a75 Author: Nick Gasson Date: Sun Jan 31 10:56:00 2016 +0000 Improve error reporting with builtins commit 8de29b4b3d18d8f7aa6fc08231fb0cf680817d42 Author: Nick Gasson Date: Thu Dec 31 21:45:04 2015 +0000 Add missing comparison operators commit ddf0db65cf4e044743439630864b2afb279063a5 Author: Nick Gasson Date: Tue Dec 29 11:22:16 2015 +0000 Vcode interpreter improvements commit fe600f471910f888331076b13470a8161041a933 Author: Nick Gasson Date: Thu Dec 24 15:52:53 2015 +0000 Fix build after merge commit 649e18a80fce2b1b2060739208026634966bf79d Merge: 04663f1 f6cf322 Author: Nick Gasson Date: Thu Dec 24 15:49:35 2015 +0000 Merge branch 'master' into vcode-interp commit 04663f1c7cfd85f734c43f76a9f7a67dae025c53 Author: Nick Gasson Date: Sun Sep 27 18:11:57 2015 +0100 Add vcode evaluation for memcmp commit 4ee32841ac09da5fcc7f3025feff9582b207c1bc Author: Nick Gasson Date: Fri Sep 25 21:30:19 2015 +0100 Extend vcode evaluator to more operations commit 28c08a1fbefee68eadaef1adc5839c359820cbfd Merge: 82e03ac c39dd33 Author: Nick Gasson Date: Sat Sep 12 12:23:08 2015 +0100 Merge branch 'master' into vcode-interp commit 82e03acc7f6b3baa959a248ba2e079e229f8806f Author: Nick Gasson Date: Sat Sep 12 10:20:25 2015 +0100 Initial version of vcode interpreter commit 42c769c7fbccfbbd49c76fd5237d2a6e85b76792 Author: Nick Gasson Date: Sat Sep 12 09:41:34 2015 +0100 Various thunking fixes commit ddf0096362947d9bebd8efa046c1a4c41bd7167e Author: Nick Gasson Date: Tue May 12 22:50:35 2015 +0100 Start adding vcode operations for thunking Conflicts: src/lower.c commit 742772241f62e8296b36566b88cc2eebbafdbda7 Author: Nick Gasson Date: Sun Aug 9 09:06:35 2015 +0100 Use a hash table to store vcode object mappings commit af2f01dbaca3091edf498a52bd59177b367f5143 Author: Nick Gasson Date: Sun Aug 9 11:54:52 2015 +0100 Dumping for NEXT statments --- nvc.1 | 2 +- src/array.h | 11 +- src/cgen.c | 79 +- src/common.c | 28 +- src/common.h | 3 +- src/dump.c | 1 + src/elab.c | 122 +-- src/eval.c | 2028 ++++++++++++++++++++++++++++--------- src/fbuf.c | 24 +- src/fbuf.h | 5 +- src/hash.c | 5 + src/hash.h | 3 +- src/ident.c | 30 +- src/ident.h | 3 + src/lower.c | 421 +++++--- src/nvc.c | 30 +- src/object.c | 62 +- src/object.h | 5 +- src/phase.h | 28 +- src/prim.h | 3 +- src/simp.c | 26 +- src/tree.c | 37 +- src/tree.h | 5 +- src/type.c | 10 + src/type.h | 2 + src/util.c | 17 +- src/vcode.c | 1048 +++++++++++++++---- src/vcode.h | 59 +- test/elab/eval1.vhd | 33 + test/lower/assert1.vhd | 15 + test/lower/iffold.vhd | 32 + test/lower/real1.vhd | 22 + test/lower/thunk.vhd | 22 + test/lower/wait1.vhd | 2 +- test/regress/arith1.vhd | 9 +- test/regress/bitvec.vhd | 5 +- test/regress/const6.vhd | 4 +- test/regress/func8.vhd | 9 +- test/regress/ieee4.vhd | 77 +- test/regress/ieee5.vhd | 46 + test/regress/issue143.vhd | 6 +- test/regress/proc3.vhd | 4 +- test/regress/real1.vhd | 9 +- test/regress/record6.vhd | 11 +- test/regress/shift2.vhd | 69 +- test/regress/testlist.txt | 1 + test/simp/cfold.vhd | 16 +- test/simp/ffold.vhd | 143 ++- test/simp/shift2.vhd | 123 +++ test/test_elab.c | 16 + test/test_ident.c | 16 + test/test_lower.c | 286 ++++-- test/test_simp.c | 52 +- test/test_util.c | 1 + 54 files changed, 3859 insertions(+), 1267 deletions(-) create mode 100644 test/elab/eval1.vhd create mode 100644 test/lower/assert1.vhd create mode 100644 test/lower/iffold.vhd create mode 100644 test/lower/real1.vhd create mode 100644 test/lower/thunk.vhd create mode 100644 test/regress/ieee5.vhd create mode 100644 test/simp/shift2.vhd diff --git a/nvc.1 b/nvc.1 index e09e5799..0d48313b 100644 --- a/nvc.1 +++ b/nvc.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "NVC" "1" "2016-07-16" "" "NVC Manual" +.TH "NVC" "1" "October 2016" "" "NVC Manual" . .SH "NAME" \fBnvc\fR \- VHDL Compiler and Simulator diff --git a/src/array.h b/src/array.h index b7c9f894..ee15190f 100644 --- a/src/array.h +++ b/src/array.h @@ -1,5 +1,5 @@ // -// Copyright (C) 2012-2013 Nick Gasson +// Copyright (C) 2012-2016 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 @@ -48,14 +48,15 @@ } \ \ void what##_array_resize(what##_array_t *a, \ - size_t n, what##_t fill) \ + size_t n, uint8_t fill) \ { \ if (n > 0) { \ const int sz = (n <= ARRAY_BASE_SZ) \ ? ARRAY_BASE_SZ : next_power_of_2(n); \ a->items = xrealloc(a->items, sz * sizeof(what##_t)); \ - for (unsigned i = a->count; i < n; i++) \ - a->items[i] = fill; \ + if (n > a->count) \ + memset(a->items + a->count, fill, \ + (n - a->count) * sizeof(what##_t)); \ } \ a->count = n; \ } \ @@ -86,7 +87,7 @@ } \ \ void what##_array_resize(what##_array_t *a, \ - size_t n, what##_t fill); + size_t n, uint8_t fill); #define DECLARE_AND_DEFINE_ARRAY(what) \ DECLARE_ARRAY(what) \ diff --git a/src/cgen.c b/src/cgen.c index f527ce23..76197779 100644 --- a/src/cgen.c +++ b/src/cgen.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2011-2015 Nick Gasson +// Copyright (C) 2011-2016 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 @@ -350,11 +350,16 @@ static LLVMValueRef cgen_uarray_dim(LLVMValueRef meta, int dim) static LLVMTypeRef cgen_display_type(vcode_unit_t unit) { + vcode_state_t state; + vcode_state_save(&state); + vcode_select_unit(unit); const vunit_kind_t kind = vcode_unit_kind(); - if (kind == VCODE_UNIT_CONTEXT) + if (kind == VCODE_UNIT_CONTEXT) { + vcode_state_restore(&state); return NULL; + } LLVMTypeRef parent = cgen_display_type(vcode_unit_context()); vcode_select_unit(unit); @@ -381,6 +386,8 @@ static LLVMTypeRef cgen_display_type(vcode_unit_t unit) if (parent != NULL) *outptr++ = parent; + vcode_state_restore(&state); + assert(outptr == fields + nfields); return LLVMStructType(fields, nfields, false); } @@ -1308,15 +1315,15 @@ static void cgen_op_param_upref(int op, cgen_ctx_t *ctx) { const int hops = vcode_get_hops(op); - vcode_unit_t orig_unit = vcode_active_unit(); - vcode_block_t orig_bb = vcode_active_block(); + vcode_state_t state; + vcode_state_save(&state); + for (int i = 0; i < hops; i++) vcode_select_unit(vcode_unit_context()); const int nvars = vcode_count_vars(); - vcode_select_unit(orig_unit); - vcode_select_block(orig_bb); + vcode_state_restore(&state); LLVMValueRef display = cgen_display_upref(hops, ctx); @@ -2259,7 +2266,7 @@ static void cgen_op_debug_out(int op, cgen_ctx_t *ctx) static void cgen_op_cover_stmt(int op, cgen_ctx_t *ctx) { - const int cover_tag = vcode_get_index(op); + const uint32_t cover_tag = vcode_get_tag(op); LLVMValueRef cover_counts = LLVMGetNamedGlobal(module, "cover_stmts"); @@ -2275,7 +2282,7 @@ static void cgen_op_cover_stmt(int op, cgen_ctx_t *ctx) static void cgen_op_cover_cond(int op, cgen_ctx_t *ctx) { - const int cover_tag = vcode_get_index(op); + const uint32_t cover_tag = vcode_get_tag(op); const int sub_cond = vcode_get_subkind(op); LLVMValueRef cover_conds = LLVMGetNamedGlobal(module, "cover_conds"); @@ -3027,31 +3034,35 @@ static void cgen_coverage_state(tree_t t) } } -static void cgen_subprograms(tree_t t) +static void cgen_subprograms(vcode_unit_t vcode) { + vcode_select_unit(vcode); + LLVMTypeRef display = NULL; - const tree_kind_t scope_kind = tree_kind(t); - const bool needs_display = - scope_kind != T_ELAB && scope_kind != T_PACK_BODY - && scope_kind != T_PROT_BODY; - - const int ndecls = tree_decls(t); - for (int i = 0; i < ndecls; i++) { - tree_t d = tree_decl(t, i); - switch (tree_kind(d)) { - case T_FUNC_BODY: - case T_PROC_BODY: - cgen_subprograms(d); + const vunit_kind_t scope_kind = vcode_unit_kind(); + const bool needs_display = scope_kind != VCODE_UNIT_CONTEXT; + + for (vcode_unit_t it = vcode_unit_child(vcode); + it != NULL; + it = vcode_unit_next(it)) { + + vcode_select_unit(it); + + switch (vcode_unit_kind()) { + case VCODE_UNIT_PROCEDURE: + case VCODE_UNIT_FUNCTION: + cgen_subprograms(it); if (display == NULL && needs_display) - display = cgen_display_type(tree_code(t)); - vcode_select_unit(tree_code(d)); + display = cgen_display_type(vcode); + vcode_select_unit(it); if (vcode_unit_kind() == VCODE_UNIT_FUNCTION) cgen_function(display); else cgen_procedure(display); break; - case T_PROT_BODY: - cgen_subprograms(d); + case VCODE_UNIT_PROCESS: + cgen_subprograms(it); + cgen_process(it); break; default: break; @@ -3110,25 +3121,15 @@ static void cgen_signals(void) } } -static void cgen_top(tree_t t) +static void cgen_top(tree_t t, vcode_unit_t vcode) { - vcode_unit_t vcode = tree_code(t); vcode_select_unit(vcode); cgen_coverage_state(t); cgen_shared_variables(); cgen_signals(); cgen_reset_function(t); - cgen_subprograms(t); - - if (tree_kind(t) == T_ELAB) { - const int nstmts = tree_stmts(t); - for (int i = 0; i < nstmts; i++) { - tree_t p = tree_stmt(t, i); - cgen_subprograms(p); - cgen_process(tree_code(p)); - } - } + cgen_subprograms(vcode); } static void cgen_optimise(void) @@ -3490,7 +3491,7 @@ static void cgen_tmp_stack(void) LLVMSetLinkage(_tmp_alloc, LLVMExternalLinkage); } -void cgen(tree_t top) +void cgen(tree_t top, vcode_unit_t vcode) { tree_kind_t kind = tree_kind(top); if (kind != T_ELAB && kind != T_PACK_BODY && kind != T_PACKAGE) @@ -3502,7 +3503,7 @@ void cgen(tree_t top) cgen_module_name(top); cgen_tmp_stack(); - cgen_top(top); + cgen_top(top, vcode); if (opt_get_int("dump-llvm")) LLVMDumpModule(module); diff --git a/src/common.c b/src/common.c index 2379015e..4de925a2 100644 --- a/src/common.c +++ b/src/common.c @@ -19,6 +19,7 @@ #include "util.h" #include "common.h" +#include "phase.h" #include #include @@ -42,6 +43,17 @@ int64_t assume_int(tree_t t) return tree_pos(ref); } + case T_FCALL: + { + const eval_flags_t flags = + EVAL_FCALL | EVAL_BOUNDS | EVAL_WARN | EVAL_REPORT; + tree_t new = eval(t, flags); + const tree_kind_t new_kind = tree_kind(new); + if (new_kind == T_LITERAL || new_kind == T_REF) + return assume_int(new); + } + // Fall-through + default: fatal_at(tree_loc(t), "expression cannot be folded to " "an integer constant"); @@ -50,8 +62,11 @@ int64_t assume_int(tree_t t) void range_bounds(range_t r, int64_t *low, int64_t *high) { - const bool folded = folded_bounds(r, low, high); - assert(folded); + const int64_t left = assume_int(r.left); + const int64_t right = assume_int(r.right); + + *low = r.kind == RANGE_TO ? left : right; + *high = r.kind == RANGE_TO ? right : left; } tree_t call_builtin(const char *builtin, type_t type, ...) @@ -222,15 +237,15 @@ bool folded_bool(tree_t t, bool *b) return false; } -tree_t get_bool_lit(tree_t t, bool v) +tree_t get_enum_lit(tree_t t, int pos) { - type_t bool_type = tree_type(t); - tree_t lit = type_enum_literal(bool_type, v ? 1 : 0); + type_t enum_type = type_base_recur(tree_type(t)); + tree_t lit = type_enum_literal(enum_type, pos); tree_t b = tree_new(T_REF); tree_set_loc(b, tree_loc(t)); tree_set_ref(b, lit); - tree_set_type(b, bool_type); + tree_set_type(b, enum_type); tree_set_ident(b, tree_ident(lit)); return b; @@ -768,7 +783,6 @@ void intern_strings(void) signed_i = ident_new("IEEE.NUMERIC_STD.SIGNED"); unsigned_i = ident_new("IEEE.NUMERIC_STD.UNSIGNED"); foreign_i = ident_new("FOREIGN"); - vcode_obj_i = ident_new("vcode_obj"); nested_i = ident_new("nested"); drives_all_i = ident_new("drives_all"); driver_init_i = ident_new("driver_init"); diff --git a/src/common.h b/src/common.h index 168b69ec..dda11ca1 100644 --- a/src/common.h +++ b/src/common.h @@ -35,7 +35,7 @@ bool folded_enum(tree_t t, unsigned *pos); bool folded_bounds(range_t r, int64_t *low, int64_t *high); bool folded_bounds_real(range_t r, double *low, double *high); tree_t get_int_lit(tree_t t, int64_t i); -tree_t get_bool_lit(tree_t t, bool v); +tree_t get_enum_lit(tree_t t, int pos); tree_t get_real_lit(tree_t t, double r); const char *package_signal_path_name(ident_t i); bool parse_value(type_t type, const char *str, int64_t *value); @@ -168,7 +168,6 @@ GLOBAL ident_t unsigned_i; GLOBAL ident_t signed_i; GLOBAL ident_t foreign_i; GLOBAL ident_t nested_i; -GLOBAL ident_t vcode_obj_i; GLOBAL ident_t drives_all_i; GLOBAL ident_t driver_init_i; GLOBAL ident_t GLOBAL_i; diff --git a/src/dump.c b/src/dump.c index 4eab5912..3c4b9a4b 100644 --- a/src/dump.c +++ b/src/dump.c @@ -985,6 +985,7 @@ void dump(tree_t t) break; case T_FOR_GENERATE: case T_BLOCK: + case T_PROCESS: dump_stmt(t, 0); break; default: diff --git a/src/elab.c b/src/elab.c index ece7839a..50dd761c 100644 --- a/src/elab.c +++ b/src/elab.c @@ -78,7 +78,6 @@ static void elab_arch(tree_t t, const elab_ctx_t *ctx); static void elab_block(tree_t t, const elab_ctx_t *ctx); static void elab_stmts(tree_t t, const elab_ctx_t *ctx); static void elab_decls(tree_t t, const elab_ctx_t *ctx); -static void elab_funcs(tree_t arch, tree_t ent, const elab_ctx_t *ctx); static void elab_copy_context(tree_t src, const elab_ctx_t *ctx); static int errors = 0; @@ -959,6 +958,15 @@ static tree_t elab_default_binding(tree_t inst, lib_t *new_lib, return arch; } +static void elab_free_maps(map_list_t *maps) +{ + while (maps != NULL) { + map_list_t *tmp = maps->next; + free(maps); + maps = tmp; + } +} + static void elab_instance(tree_t t, const elab_ctx_t *ctx) { lib_t new_lib = NULL; @@ -1006,16 +1014,14 @@ static void elab_instance(tree_t t, const elab_ctx_t *ctx) elab_copy_context(entity, &new_ctx); elab_decls(entity, &new_ctx); - elab_funcs(arch, entity, ctx); - simplify(arch); - elab_map_nets(maps); + elab_free_maps(maps); - while (maps != NULL) { - map_list_t *tmp = maps->next; - free(maps); - maps = tmp; - } + fold(arch); + bounds_check(arch); + + if (eval_errors() > 0 || bounds_errors() > 0) + return; elab_arch(arch, &new_ctx); } @@ -1034,6 +1040,12 @@ static void elab_signal_nets(tree_t decl, const elab_ctx_t *ctx) } } +static void elab_set_subprogram_name(tree_t decl, ident_t new) +{ + tree_set_ident(decl, new); + tree_remove_attr(decl, mangled_i); +} + static void elab_prot_body_decls(tree_t body) { type_t type = tree_type(body); @@ -1043,7 +1055,7 @@ static void elab_prot_body_decls(tree_t body) tree_t d = type_decl(type, i); ident_t base = ident_rfrom(tree_ident(d), '.'); - tree_set_ident(d, ident_prefix(tree_ident(body), base, '.')); + elab_set_subprogram_name(d, ident_prefix(tree_ident(body), base, '.')); } const int ndecls = tree_decls(body); @@ -1055,8 +1067,8 @@ static void elab_prot_body_decls(tree_t body) && (kind != T_PROC_DECL) && (kind != T_PROC_BODY)) continue; - tree_set_ident(d, ident_prefix(tree_ident(body), - ident_rfrom(tree_ident(d), '.'), '.')); + ident_t base = ident_rfrom(tree_ident(d), '.'); + elab_set_subprogram_name(d, ident_prefix(tree_ident(body), base, '.')); } } @@ -1095,7 +1107,7 @@ static void elab_decls(tree_t t, const elab_ctx_t *ctx) break; case T_FUNC_DECL: case T_PROC_DECL: - tree_set_ident(d, npath); + elab_set_subprogram_name(d, npath); break; case T_CONST_DECL: tree_set_ident(d, npath); @@ -1180,7 +1192,10 @@ static void elab_for_generate(tree_t t, elab_ctx_t *ctx) .count = 1 }; tree_rewrite(copy, rewrite_refs, ¶ms); - simplify(copy); + fold(copy); + + if (eval_errors() > 0) + break; ident_t npath = hpathf(ctx->path, '\0', "[%"PRIi64"]", i); ident_t ninst = hpathf(ctx->inst, '\0', "[%"PRIi64"]", i); @@ -1435,10 +1450,12 @@ static void elab_entity_arch(tree_t t, tree_t arch, const elab_ctx_t *ctx) tree_add_attr_str(ctx->out, simple_name_i, npath); - elab_funcs(arch, t, ctx); - simplify(arch); + fold(arch); bounds_check(arch); + if (bounds_errors() > 0 || eval_errors() > 0) + return; + elab_ctx_t new_ctx = { .out = ctx->out, .path = npath, @@ -1450,76 +1467,6 @@ static void elab_entity_arch(tree_t t, tree_t arch, const elab_ctx_t *ctx) elab_arch(arch, &new_ctx); } -static tree_t rewrite_funcs(tree_t t, void *context) -{ - if (tree_kind(t) != T_FCALL) - return t; - - tree_t decl = tree_ref(t); - - ident_t name = tree_ident(decl); - for (tree_t *rlist = context; *rlist != NULL; ++rlist) { - const bool match = - (tree_ident(*rlist) == name) - && (type_eq(tree_type(*rlist), tree_type(decl))); - - if (match) { - tree_set_ref(t, *rlist); - break; - } - } - - return t; -} - -static void elab_funcs(tree_t arch, tree_t ent, const elab_ctx_t *ctx) -{ - // Look through all the package bodies required by the context and - // rewrite references to function declarations with references to - // the function body. This allows these functions to be folded by - // the simplify phase - - int nreplace = 0; - tree_t rlist[FUNC_REPLACE_MAX]; - - const int ncontext_arch = tree_contexts(arch); - const int ncontext_ent = tree_contexts(ent); - for (int i = 0; i < ncontext_arch + ncontext_ent; i++) { - tree_t c = (i < ncontext_arch) - ? tree_context(arch, i) - : tree_context(ent, i - ncontext_arch); - - if (tree_kind(c) != T_USE) - continue; - - ident_t name = tree_ident(c); - lib_t lib = elab_find_lib(name, ctx); - - ident_t body_i = ident_prefix(name, ident_new("body"), '-'); - tree_t body = lib_get_check_stale(lib, body_i); - if (body == NULL) - continue; - - const int ndecls = tree_decls(body); - for (int j = 0; j < ndecls; j++) { - tree_t decl = tree_decl(body, j); - if (tree_kind(decl) != T_FUNC_BODY) - continue; - - if (nreplace + 1 == FUNC_REPLACE_MAX) { - tree_rewrite(arch, rewrite_funcs, rlist); - nreplace = 0; - } - - rlist[nreplace++] = decl; - rlist[nreplace] = NULL; - } - } - - if (nreplace > 0) - tree_rewrite(arch, rewrite_funcs, rlist); -} - static tree_t rewrite_package_signals(tree_t t, void *ctx) { tree_t decl = ctx; @@ -1645,7 +1592,7 @@ tree_t elab(tree_t top) elab_context_signals(&ctx); - if (errors > 0) + if (errors > 0 || eval_errors() > 0) return NULL; tree_add_attr_int(e, nnets_i, next_net); @@ -1653,6 +1600,7 @@ tree_t elab(tree_t top) if (opt_get_int("cover")) cover_tag(e); + simplify(e); bounds_check(e); for (generic_list_t *it = generic_override; it != NULL; it = it->next) { diff --git a/src/eval.c b/src/eval.c index 063202d1..a825d651 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2013-2015 Nick Gasson +// Copyright (C) 2013-2016 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 @@ -18,653 +18,1693 @@ #include "phase.h" #include "util.h" #include "common.h" +#include "vcode.h" #include #include #include #include - -#define VTABLE_SZ 16 -#define MAX_ITERS 1000 - -typedef struct vtable vtable_t; -typedef struct vtframe vtframe_t; - -struct vtframe { +#include +#include +#include + +#define MAX_DIMS 4 +#define EVAL_HEAP (4 * 1024) + +typedef enum { + VALUE_INVALID, + VALUE_REAL, + VALUE_INTEGER, + VALUE_POINTER, + VALUE_UARRAY, + VALUE_CARRAY, + VALUE_RECORD, + VALUE_HEAP_PROXY +} value_kind_t; + +typedef struct value value_t; +typedef struct context context_t; + +typedef struct { struct { - ident_t name; - tree_t value; - } binding[VTABLE_SZ]; - - size_t size; - vtframe_t *down; + int64_t left; + int64_t right; + range_kind_t dir; + } dim[MAX_DIMS]; + int ndims; + value_t *data; +} uarray_t; + +struct value { + value_kind_t kind; + union { + double real; + int64_t integer; + value_t *pointer; + uarray_t *uarray; + value_t *fields; + }; }; -struct vtable { - vtframe_t *top; - bool failed; - ident_t exit; - tree_t result; +struct context { + context_t *parent; + value_t *regs; + value_t *vars; }; -static void eval_stmt(tree_t t, vtable_t *v); -static tree_t eval_expr(tree_t t, vtable_t *v); +typedef struct { + context_t *context; + int result; + tree_t fcall; + eval_flags_t flags; + bool failed; + void *heap; + size_t halloc; +} eval_state_t; + +#define EVAL_WARN(t, ...) do { \ + if (state->flags & EVAL_WARN) \ + warn_at(tree_loc(t), __VA_ARGS__); \ + } while (0) -static bool debug = true; +static int errors = 0; -#define eval_error(t, ...) do { \ - if (unlikely(debug)) \ - warn_at(tree_loc(t), __VA_ARGS__); \ - v->failed = true; \ - return; \ - } while (0) +static void eval_vcode(eval_state_t *state); +static bool eval_possible(tree_t t, eval_flags_t flags); -static void vtable_push(vtable_t *v) +static bool eval_params_possible(tree_t fcall, eval_flags_t flags) { - vtframe_t *f = xmalloc(sizeof(vtframe_t)); - f->size = 0; - f->down = v->top; + const int nparams = tree_params(fcall); + for (int i = 0; i < nparams; i++) { + tree_t p = tree_value(tree_param(fcall, i)); + const bool fcall = tree_kind(p) == T_FCALL; + if ((flags & EVAL_FOLDING) && fcall && type_is_scalar(tree_type(p))) + return false; // Would have been folded already if possible + else if (fcall && !(flags & EVAL_FCALL)) + return false; + else if (!eval_possible(p, flags)) + return false; + } - v->top = f; + return true; } -static void vtable_pop(vtable_t *v) +static bool eval_possible(tree_t t, eval_flags_t flags) { - vtframe_t *f = v->top; - v->top = f->down; - v->result = NULL; - free(f); -} + switch (tree_kind(t)) { + case T_FCALL: + { + if (!(flags & EVAL_FCALL)) + return false; + else if (tree_flags(tree_ref(t)) & TREE_F_IMPURE) + return false; -static void vtable_bind(vtable_t *v, ident_t name, tree_t value) -{ - vtframe_t *f = v->top; - if (f == NULL) - return; + return eval_params_possible(t, flags); + } - for (size_t i = 0; i < f->size; i++) { - if (f->binding[i].name == name) { - f->binding[i].value = value; - return; + case T_LITERAL: + return true; + + case T_TYPE_CONV: + return eval_possible(tree_value(tree_param(t, 0)), flags); + + case T_REF: + { + tree_t decl = tree_ref(t); + switch (tree_kind(decl)) { + case T_UNIT_DECL: + case T_ENUM_LIT: + return true; + + case T_CONST_DECL: + return eval_possible(tree_value(decl), flags); + + default: + return false; + } } - } - assert(f->size < VTABLE_SZ); - f->binding[f->size].name = name; - f->binding[f->size].value = value; - ++(f->size); + case T_RECORD_REF: + return eval_possible(tree_value(t), flags); + + default: + if (flags & EVAL_WARN) + warn_at(tree_loc(t), "expression prevents constant folding"); + return false; + } } -static tree_t vtframe_get(vtframe_t *f, ident_t name) +static void *eval_alloc(size_t nbytes, eval_state_t *state) { - if (f == NULL) + if (state->halloc + nbytes > EVAL_HEAP) { + EVAL_WARN(state->fcall, "evaluation heap exhaustion prevents " + "constant folding (%zu allocated, %zu requested)", + state->halloc, nbytes); + state->failed = true; return NULL; - else { - for (size_t i = 0; i < f->size; i++) { - if (f->binding[i].name == name) - return f->binding[i].value; - } - return vtframe_get(f->down, name); } + + if (state->heap == NULL) + state->heap = xmalloc(EVAL_HEAP); + + void *ptr = (char *)state->heap + state->halloc; + state->halloc += nbytes; + return ptr; } -static tree_t vtable_get(vtable_t *v, ident_t name) +static context_t *eval_new_context(eval_state_t *state) { - return vtframe_get(v->top, name); + const int nregs = vcode_count_regs(); + const int nvars = vcode_count_vars(); + + void *mem = xcalloc(sizeof(context_t) + sizeof(value_t) * (nregs + nvars)); + + context_t *context = mem; + context->regs = (value_t *)((uint8_t *)mem + sizeof(context_t)); + context->vars = (value_t *) + ((uint8_t *)mem + sizeof(context_t) + (nregs * sizeof(value_t))); + + for (int i = 0; i < nvars; i++) { + vcode_var_t var = vcode_var_handle(i); + vcode_type_t type = vcode_var_type(var); + + value_t *value = &(context->vars[i]); + if (vcode_var_use_heap(var)) { + value->kind = VALUE_HEAP_PROXY; + if ((value->pointer = eval_alloc(sizeof(value_t), state)) == NULL) + goto fail; + + value = value->pointer; + } + + switch (vtype_kind(type)) { + case VCODE_TYPE_CARRAY: + value->kind = VALUE_CARRAY; + value->pointer = + eval_alloc(sizeof(value_t) * vtype_size(type), state); + break; + + case VCODE_TYPE_INT: + value->kind = VALUE_INTEGER; + value->integer = 0; + break; + + case VCODE_TYPE_REAL: + value->kind = VALUE_REAL; + value->real = 0; + break; + + case VCODE_TYPE_UARRAY: + value->kind = VALUE_UARRAY; + value->uarray = NULL; + break; + + case VCODE_TYPE_RECORD: + value->kind = VALUE_RECORD; + value->fields = NULL; + break; + + default: + fatal_at(tree_loc(state->fcall), "cannot evaluate variables with " + "type %d", vtype_kind(type)); + } + } + + return context; + + fail: + free(context); + return NULL; } -static bool folded(tree_t t) +static void eval_free_context(context_t *context) { - tree_kind_t kind = tree_kind(t); - if (kind == T_LITERAL) - return true; - else if (kind == T_REF) { - bool dummy; - return folded_bool(t, &dummy); - } - else - return false; + if (context->parent) + eval_free_context(context->parent); + + context->regs = NULL; + context->vars = NULL; + free(context); } -static tree_t eval_fcall_log(tree_t t, ident_t builtin, bool *args) -{ - if (icmp(builtin, "not")) - return get_bool_lit(t, !args[0]); - else if (icmp(builtin, "and")) - return get_bool_lit(t, args[0] && args[1]); - else if (icmp(builtin, "nand")) - return get_bool_lit(t, !(args[0] && args[1])); - else if (icmp(builtin, "or")) - return get_bool_lit(t, args[0] || args[1]); - else if (icmp(builtin, "nor")) - return get_bool_lit(t, !(args[0] || args[1])); - else if (icmp(builtin, "xor")) - return get_bool_lit(t, args[0] ^ args[1]); - else if (icmp(builtin, "xnor")) - return get_bool_lit(t, !(args[0] ^ args[1])); - else if (icmp(builtin, "eq")) - return get_bool_lit(t, args[0] == args[1]); - else if (icmp(builtin, "neq")) - return get_bool_lit(t, args[0] != args[1]); - else - return t; +static value_t *eval_get_reg(vcode_reg_t reg, eval_state_t *state) +{ + return &(state->context->regs[reg]); } -static tree_t eval_fcall_real(tree_t t, ident_t builtin, double *args) -{ - if (icmp(builtin, "mul")) - return get_real_lit(t, args[0] * args[1]); - else if (icmp(builtin, "div")) - return get_real_lit(t, args[0] / args[1]); - else if (icmp(builtin, "add")) - return get_real_lit(t, args[0] + args[1]); - else if (icmp(builtin, "sub")) - return get_real_lit(t, args[0] - args[1]); - else if (icmp(builtin, "neg")) - return get_real_lit(t, -args[0]); - else if (icmp(builtin, "identity")) - return get_real_lit(t, args[0]); - else if (icmp(builtin, "eq")) - return get_bool_lit(t, args[0] == args[1]); - else if (icmp(builtin, "neq")) - return get_bool_lit(t, args[0] != args[1]); - else if (icmp(builtin, "gt")) - return get_bool_lit(t, args[0] > args[1]); - else if (icmp(builtin, "lt")) - return get_bool_lit(t, args[0] < args[1]); +static value_t *eval_get_var(vcode_var_t var, eval_state_t *state) +{ + const int var_depth = vcode_var_context(var); + + context_t *context = state->context; + for (int depth = vcode_unit_depth(); depth > var_depth; depth--) { + if (context->parent == NULL) { + assert(vcode_unit_kind() != VCODE_UNIT_THUNK); + + vcode_state_t vcode_state; + vcode_state_save(&vcode_state); + + vcode_select_unit(vcode_unit_context()); + assert(vcode_unit_kind() == VCODE_UNIT_CONTEXT); + vcode_select_block(0); + + context_t *new_context = eval_new_context(state); + context->parent = new_context; + + eval_state_t new_state = { + .context = new_context, + .result = -1, + .fcall = state->fcall, + .failed = false, + .flags = state->flags | EVAL_BOUNDS, + .heap = state->heap, + .halloc = state->halloc + }; + + eval_vcode(&new_state); + vcode_state_restore(&vcode_state); + + state->heap = new_state.heap; + state->halloc = new_state.halloc; + + if (new_state.failed) { + state->failed = true; + return NULL; + } + } + + context = context->parent; + } + + if (vcode_var_use_heap(var)) { + value_t *proxy = &(context->vars[vcode_var_index(var)]); + assert(proxy->kind == VALUE_HEAP_PROXY); + return proxy->pointer; + } else - return t; + return &(context->vars[vcode_var_index(var)]); } -static tree_t eval_fcall_int(tree_t t, ident_t builtin, int64_t *args, int n) -{ - if (icmp(builtin, "mul")) - return get_int_lit(t, args[0] * args[1]); - else if (icmp(builtin, "div")) - return get_int_lit(t, args[0] / args[1]); - else if (icmp(builtin, "add")) - return get_int_lit(t, args[0] + args[1]); - else if (icmp(builtin, "sub")) - return get_int_lit(t, args[0] - args[1]); - else if (icmp(builtin, "neg")) - return get_int_lit(t, -args[0]); - else if (icmp(builtin, "identity")) - return get_int_lit(t, args[0]); - else if (icmp(builtin, "eq")) - return get_bool_lit(t, args[0] == args[1]); - else if (icmp(builtin, "neq")) - return get_bool_lit(t, args[0] != args[1]); - else if (icmp(builtin, "gt")) - return get_bool_lit(t, args[0] > args[1]); - else if (icmp(builtin, "lt")) - return get_bool_lit(t, args[0] < args[1]); - else if (icmp(builtin, "leq")) - return get_bool_lit(t, args[0] <= args[1]); - else if (icmp(builtin, "geq")) - return get_bool_lit(t, args[0] >= args[1]); - else if (icmp(builtin, "exp")) { - int64_t result = 1; - int64_t a = args[0]; - int64_t b = args[1]; - - if (a == 0) - return get_int_lit(t, 0); - else if (b == 0) - return get_int_lit(t, 1); - else if (b < 0) - return t; - - while (b != 0) { - if (b & 1) - result *= a; - a *= a; - b >>= 1; +static int eval_value_cmp(value_t *lhs, value_t *rhs) +{ + assert(lhs->kind == rhs->kind); + switch (lhs->kind) { + case VALUE_INTEGER: + return lhs->integer - rhs->integer; + + case VALUE_REAL: + { + const double diff = lhs->real - rhs->real; + if (diff < 0.0) + return -1; + else if (diff > 0.0) + return 1; + else + return 0; } - return get_int_lit(t, result); - } - else if (icmp(builtin, "min")) { - assert(n > 0); - int64_t r = args[0]; - for (int i = 1; i < n; i++) - r = MIN(r, args[i]); - return get_int_lit(t, r); - } - else if (icmp(builtin, "max")) { - assert(n > 0); - int64_t r = args[0]; - for (int i = 1; i < n; i++) - r = MAX(r, args[i]); - return get_int_lit(t, r); - } - else if (icmp(builtin, "mod")) - return get_int_lit(t, llabs(args[0]) % llabs(args[1])); - else if (icmp(builtin, "rem")) - return get_int_lit(t, args[0] % args[1]); - else - return t; + case VALUE_POINTER: + return lhs->pointer - rhs->pointer; + + default: + fatal_trace("invalid value type %d in %s", lhs->kind, __func__); + } } -static tree_t eval_fcall_enum(tree_t t, ident_t builtin, unsigned *args, int n) +static void eval_message(value_t *text, value_t *length, value_t *severity, + const loc_t *loc, const char *prefix) { - if (icmp(builtin, "min")) { - assert(n > 0); - unsigned r = args[0]; - for (int i = 1; i < n; i++) - r = MIN(r, args[i]); - return get_int_lit(t, r); + const char *levels[] = { + "Note", "Warning", "Error", "Failure" + }; + + assert(text->kind == VALUE_POINTER); + + char *copy = NULL ; + if (length->integer > 0) { + copy = xmalloc(length->integer + 1); + for (int i = 0; i < length->integer; i++) + copy[i] = text->pointer[i].integer; + copy[length->integer] = '\0'; } - else if (icmp(builtin, "max")) { - assert(n > 0); - unsigned r = args[0]; - for (int i = 1; i < n; i++) - r = MAX(r, args[i]); - return get_int_lit(t, r); + + void (*fn)(const loc_t *loc, const char *fmt, ...) = fatal_at; + + switch (severity->integer) { + case SEVERITY_NOTE: fn = note_at; break; + case SEVERITY_WARNING: fn = warn_at; break; + case SEVERITY_ERROR: + case SEVERITY_FAILURE: fn = error_at; break; } - else if (icmp(builtin, "eq")) - return get_bool_lit(t, args[0] == args[1]); - else if (icmp(builtin, "neq")) - return get_bool_lit(t, args[0] != args[1]); - else - return t; + + (*fn)(loc, "%s %s: %s", prefix, levels[severity->integer], + copy ?: "Assertion violation"); + free(copy); +} + +static void eval_op_const(int op, eval_state_t *state) +{ + value_t *dst = eval_get_reg(vcode_get_result(op), state); + dst->kind = VALUE_INTEGER; + dst->integer = vcode_get_value(op); } -static tree_t eval_fcall_universal(tree_t t, ident_t builtin, tree_t *args) +static void eval_op_const_real(int op, eval_state_t *state) { - int64_t ival; - double rval; + value_t *dst = eval_get_reg(vcode_get_result(op), state); + dst->kind = VALUE_REAL; + dst->real = vcode_get_real(op); +} - if (icmp(builtin, "mulri") && folded_real(args[0], &rval) - && folded_int(args[1], &ival)) - return get_real_lit(t, rval * (double)ival); - else if (icmp(builtin, "mulir") && folded_real(args[1], &rval) - && folded_int(args[0], &ival)) - return get_real_lit(t, rval * (double)ival); - else if (icmp(builtin, "divri") && folded_real(args[0], &rval) - && folded_int(args[1], &ival)) - return get_real_lit(t, rval / (double)ival); - else - fatal_at(tree_loc(t), "universal expression cannot be evaluated"); +static void eval_op_return(int op, eval_state_t *state) +{ + if (vcode_count_args(op) > 0) + state->result = vcode_get_arg(op, 0); } -static tree_t eval_fcall_str(tree_t t, ident_t builtin, tree_t *args) +static void eval_op_not(int op, eval_state_t *state) { - if (icmp(builtin, "aeq") || icmp(builtin, "aneq")) { - const int lchars = tree_chars(args[0]); - const int rchars = tree_chars(args[1]); + value_t *dst = eval_get_reg(vcode_get_result(op), state); + value_t *src = eval_get_reg(vcode_get_arg(op, 0), state); + dst->kind = VALUE_INTEGER; + dst->integer = !(src->integer); +} - const bool invert = icmp(builtin, "aneq"); +static void eval_op_add(int op, eval_state_t *state) +{ + value_t *dst = eval_get_reg(vcode_get_result(op), state); + value_t *lhs = eval_get_reg(vcode_get_arg(op, 0), state); + value_t *rhs = eval_get_reg(vcode_get_arg(op, 1), state); + + switch (lhs->kind) { + case VALUE_INTEGER: + dst->kind = VALUE_INTEGER; + dst->integer = lhs->integer + rhs->integer; + break; - if (lchars != rchars) - return get_bool_lit(t, invert); + case VALUE_REAL: + dst->kind = VALUE_REAL; + dst->real = lhs->real + rhs->real; + break; - for (int i = 0; i < lchars; i++) { - tree_t c0 = tree_char(args[0], i); - tree_t c1 = tree_char(args[1], i); - if (tree_ident(c0) != tree_ident(c1)) - return get_bool_lit(t, invert); - } + case VALUE_POINTER: + assert(rhs->kind == VALUE_INTEGER); + dst->kind = VALUE_POINTER; + dst->pointer = lhs->pointer + rhs->integer; + break; - return get_bool_lit(t, !invert); + default: + fatal_trace("invalid value type in %s", __func__); } - else - return t; } -static void eval_stmts(tree_t t, unsigned (*count)(tree_t), - tree_t (*get)(tree_t, unsigned), vtable_t *v) +static void eval_op_sub(int op, eval_state_t *state) { - const int nstmts = (*count)(t); - for (int i = 0; i < nstmts; i++) { - eval_stmt((*get)(t, i), v); - if (v->failed || (v->result != NULL) || (v->exit != NULL)) - return; + value_t *dst = eval_get_reg(vcode_get_result(op), state); + value_t *lhs = eval_get_reg(vcode_get_arg(op, 0), state); + value_t *rhs = eval_get_reg(vcode_get_arg(op, 1), state); + + switch (lhs->kind) { + case VALUE_INTEGER: + dst->kind = VALUE_INTEGER; + dst->integer = lhs->integer - rhs->integer; + break; + + case VALUE_REAL: + dst->kind = VALUE_REAL; + dst->real = lhs->real - rhs->real; + break; + + default: + fatal_trace("invalid value type in %s", __func__); } } -static void eval_func_body(tree_t t, vtable_t *v) +static void eval_op_mul(int op, eval_state_t *state) { - const int ndecls = tree_decls(t); - for (int i = 0; i < ndecls; i++) { - tree_t decl = tree_decl(t, i); - if ((tree_kind(decl) == T_VAR_DECL) && tree_has_value(decl)) - vtable_bind(v, tree_ident(decl), eval_expr(tree_value(decl), v)); - } + value_t *dst = eval_get_reg(vcode_get_result(op), state); + value_t *lhs = eval_get_reg(vcode_get_arg(op, 0), state); + value_t *rhs = eval_get_reg(vcode_get_arg(op, 1), state); + + switch (lhs->kind) { + case VALUE_INTEGER: + dst->kind = VALUE_INTEGER; + dst->integer = lhs->integer * rhs->integer; + break; + + case VALUE_REAL: + dst->kind = VALUE_REAL; + dst->real = lhs->real * rhs->real; + break; - eval_stmts(t, tree_stmts, tree_stmt, v); + default: + fatal_trace("invalid value type in %s", __func__); + } } -static tree_t eval_fcall(tree_t t, vtable_t *v) +static void eval_op_div(int op, eval_state_t *state) { - tree_t decl = tree_ref(t); - assert(tree_kind(decl) == T_FUNC_DECL - || tree_kind(decl) == T_FUNC_BODY); - - ident_t builtin = tree_attr_str(decl, builtin_i); - if (builtin == NULL) { - if (tree_kind(decl) != T_FUNC_BODY) - return t; - - // Only evaluating scalar functions is supported at the moment - if (type_is_array(tree_type(t))) - return t; + value_t *dst = eval_get_reg(vcode_get_result(op), state); + value_t *lhs = eval_get_reg(vcode_get_arg(op, 0), state); + value_t *rhs = eval_get_reg(vcode_get_arg(op, 1), state); + + switch (lhs->kind) { + case VALUE_INTEGER: + if (rhs->integer == 0) { + error_at(tree_loc(state->fcall), "division by zero"); + state->failed = true; + } + else { + dst->kind = VALUE_INTEGER; + dst->integer = lhs->integer / rhs->integer; + } + break; - const int nports = tree_ports(decl); - tree_t params[nports]; + case VALUE_REAL: + dst->kind = VALUE_REAL; + dst->real = lhs->real / rhs->real; + break; - for (int i = 0; i < nports; i++) { - params[i] = eval_expr(tree_value(tree_param(t, i)), v); + default: + fatal_trace("invalid value type in %s", __func__); + } +} - if (!folded(params[i])) - return t; // Cannot fold this function call +static void eval_op_mod(int op, eval_state_t *state) +{ + value_t *dst = eval_get_reg(vcode_get_result(op), state); + value_t *lhs = eval_get_reg(vcode_get_arg(op, 0), state); + value_t *rhs = eval_get_reg(vcode_get_arg(op, 1), state); + + switch (lhs->kind) { + case VALUE_INTEGER: + if (rhs->integer == 0) { + error_at(tree_loc(state->fcall), "division by zero"); + state->failed = true; } + else { + dst->kind = VALUE_INTEGER; + dst->integer = labs(lhs->integer % rhs->integer); + } + break; - vtable_push(v); - for (int i = 0; i < nports; i++) - vtable_bind(v, tree_ident(tree_port(decl, i)), params[i]); + default: + fatal_trace("invalid value type in %s", __func__); + } +} - eval_func_body(decl, v); - tree_t result = v->result; - vtable_pop(v); +static void eval_op_rem(int op, eval_state_t *state) +{ + value_t *dst = eval_get_reg(vcode_get_result(op), state); + value_t *lhs = eval_get_reg(vcode_get_arg(op, 0), state); + value_t *rhs = eval_get_reg(vcode_get_arg(op, 1), state); + + switch (lhs->kind) { + case VALUE_INTEGER: + if (rhs->integer == 0) { + error_at(tree_loc(state->fcall), "division by zero"); + state->failed = true; + } + else { + dst->kind = VALUE_INTEGER; + dst->integer = + lhs->integer - (lhs->integer / rhs->integer) * rhs->integer; + } + break; - return ((result != NULL) && folded(result)) ? result : t; + default: + fatal_trace("invalid value type in %s", __func__); } +} - const int nparams = tree_params(t); +static void eval_op_exp(int op, eval_state_t *state) +{ + value_t *dst = eval_get_reg(vcode_get_result(op), state); + value_t *lhs = eval_get_reg(vcode_get_arg(op, 0), state); + value_t *rhs = eval_get_reg(vcode_get_arg(op, 1), state); - tree_t targs[nparams]; - for (int i = 0; i < nparams; i++) { - tree_t p = tree_param(t, i); - targs[i] = eval_expr(tree_value(p), v); - } - - if (icmp(builtin, "mulri") || icmp(builtin, "mulir") - || icmp(builtin, "divri")) - return eval_fcall_universal(t, builtin, targs); - - bool can_fold_int = true; - bool can_fold_log = true; - bool can_fold_real = true; - bool can_fold_enum = true; - bool can_fold_str = true; - int64_t iargs[nparams]; - double rargs[nparams]; - bool bargs[nparams]; - unsigned eargs[nparams]; - for (int i = 0; i < nparams; i++) { - can_fold_int = can_fold_int && folded_int(targs[i], &iargs[i]); - can_fold_log = can_fold_log && folded_bool(targs[i], &bargs[i]); - can_fold_real = can_fold_real && folded_real(targs[i], &rargs[i]); - can_fold_enum = can_fold_enum && folded_enum(targs[i], &eargs[i]); - can_fold_str = can_fold_str && tree_kind(targs[i]) == T_LITERAL - && tree_subkind(targs[i]) == L_STRING; - } - - if (can_fold_int) - return eval_fcall_int(t, builtin, iargs, nparams); - else if (can_fold_log) - return eval_fcall_log(t, builtin, bargs); - else if (can_fold_real) - return eval_fcall_real(t, builtin, rargs); - else if (can_fold_enum) - return eval_fcall_enum(t, builtin, eargs, nparams); - else if (can_fold_str) - return eval_fcall_str(t, builtin, targs); - else - return t; + dst->kind = VALUE_REAL; + dst->real = pow(lhs->real, rhs->real); } -static tree_t eval_ref(tree_t t, vtable_t *v) +static void eval_op_cmp(int op, eval_state_t *state) { - tree_t decl = tree_ref(t); - if (tree_kind(decl) == T_CONST_DECL) - return eval_expr(tree_value(decl), v); - else { - tree_t binding = vtable_get(v, tree_ident(tree_ref(t))); - return (binding != NULL) ? binding : t; + value_t *dst = eval_get_reg(vcode_get_result(op), state); + value_t *lhs = eval_get_reg(vcode_get_arg(op, 0), state); + value_t *rhs = eval_get_reg(vcode_get_arg(op, 1), state); + + dst->kind = VALUE_INTEGER; + + switch (vcode_get_cmp(op)) { + case VCODE_CMP_EQ: + dst->integer = eval_value_cmp(lhs, rhs) == 0; + break; + case VCODE_CMP_NEQ: + dst->integer = eval_value_cmp(lhs, rhs) != 0; + break; + case VCODE_CMP_GT: + dst->integer = eval_value_cmp(lhs, rhs) > 0; + break; + case VCODE_CMP_GEQ: + dst->integer = eval_value_cmp(lhs, rhs) >= 0; + break; + case VCODE_CMP_LT: + dst->integer = eval_value_cmp(lhs, rhs) < 0; + break; + case VCODE_CMP_LEQ: + dst->integer = eval_value_cmp(lhs, rhs) <= 0; + break; + default: + vcode_dump(); + fatal_trace("cannot handle comparison"); } } -static tree_t eval_type_conv(tree_t t, vtable_t *v) +static void eval_op_cast(int op, eval_state_t *state) { - tree_t value = eval_expr(tree_value(tree_param(t, 0)), v); - - type_t from = tree_type(value); - type_t to = tree_type(t); + value_t *dst = eval_get_reg(vcode_get_result(op), state); + value_t *src = eval_get_reg(vcode_get_arg(op, 0), state); + + switch (vtype_kind(vcode_get_type(op))) { + case VCODE_TYPE_INT: + case VCODE_TYPE_OFFSET: + dst->kind = VALUE_INTEGER; + switch (src->kind) { + case VALUE_INTEGER: dst->integer = src->integer; break; + case VALUE_REAL: dst->integer = (int64_t)src->real; break; + default: break; + } + break; - type_kind_t from_k = type_kind(from); - type_kind_t to_k = type_kind(to); + case VCODE_TYPE_REAL: + dst->kind = VALUE_REAL; + switch (src->kind) { + case VALUE_INTEGER: dst->real = (double)src->integer; break; + case VALUE_REAL: dst->real = src->real; break; + default: break; + } + break; - if (from_k == T_INTEGER && to_k == T_REAL) { - int64_t l; - if (folded_int(value, &l)) - return get_real_lit(t, (double)l); - } - else if (from_k == T_REAL && to_k == T_INTEGER) { - double l; - if (folded_real(value, &l)) - return get_int_lit(t, (int)l); + default: + vcode_dump(); + fatal("cannot handle destination type in cast"); } - - return t; } -static tree_t eval_expr(tree_t t, vtable_t *v) +static void eval_op_neg(int op, eval_state_t *state) { - switch (tree_kind(t)) { - case T_FCALL: - return eval_fcall(t, v); - case T_REF: - return eval_ref(t, v); - case T_TYPE_CONV: - return eval_type_conv(t, v); + value_t *dst = eval_get_reg(vcode_get_result(op), state); + value_t *src = eval_get_reg(vcode_get_arg(op, 0), state); + + switch (src->kind) { + case VALUE_INTEGER: + dst->kind = VALUE_INTEGER; + dst->integer = -(src->integer); + break; + + case VALUE_REAL: + dst->kind = VALUE_REAL; + dst->real = -(src->real); + break; + default: - return t; + fatal_trace("invalid value type in %s", __func__); } } -static void eval_return(tree_t t, vtable_t *v) +static void eval_op_abs(int op, eval_state_t *state) { - assert(tree_has_value(t)); - assert(v->result == NULL); - v->result = eval_expr(tree_value(t), v); -} + value_t *dst = eval_get_reg(vcode_get_result(op), state); + value_t *src = eval_get_reg(vcode_get_arg(op, 0), state); -static void eval_if(tree_t t, vtable_t *v) -{ - tree_t cond = eval_expr(tree_value(t), v); - bool cond_b; - if (!folded_bool(cond, &cond_b)) - eval_error(cond, "cannot constant fold expression"); + switch (src->kind) { + case VALUE_INTEGER: + dst->kind = VALUE_INTEGER; + dst->integer = llabs(src->integer); + break; - if (cond_b) - eval_stmts(t, tree_stmts, tree_stmt, v); - else - eval_stmts(t, tree_else_stmts, tree_else_stmt, v); + case VALUE_REAL: + dst->kind = VALUE_REAL; + dst->real = fabs(src->real); + break; + + default: + fatal_trace("invalid value type in %s", __func__); + } } -static void eval_case(tree_t t, vtable_t *v) +static void eval_load_vcode(lib_t lib, tree_t unit, tree_rd_ctx_t tree_ctx, + eval_state_t *state) { - tree_t value = tree_value(t); + ident_t unit_name = tree_ident(unit); - if (type_is_array(tree_type(value))) - eval_error(value, "cannot constant fold array case"); + if (state->flags & EVAL_VERBOSE) + notef("loading vcode for %s", istr(unit_name)); - int64_t value_int; - if (!folded_int(eval_expr(value, v), &value_int)) - eval_error(value, "cannot constant fold expression"); + char *name LOCAL = xasprintf("_%s.vcode", istr(unit_name)); + fbuf_t *f = lib_fbuf_open(lib, name, FBUF_IN); + if (f == NULL) { + EVAL_WARN(state->fcall, "cannot load vcode for %s", istr(unit_name)); + return; + } + + vcode_read(f, tree_ctx); + fbuf_close(f); +} - const int nassocs = tree_assocs(t); - for (int i = 0; i < nassocs; i++) { - tree_t a = tree_assoc(t, i); - switch (tree_subkind(a)) { - case A_NAMED: - { - int64_t cmp; - if (!folded_int(eval_expr(tree_name(a), v), &cmp)) - eval_error(tree_name(a), "cannot constant fold expression"); - else if (cmp == value_int) { - eval_stmt(tree_value(a), v); - return; +static void eval_op_fcall(int op, eval_state_t *state) +{ + vcode_state_t vcode_state; + vcode_state_save(&vcode_state); + + ident_t func_name = vcode_get_func(op); + vcode_unit_t vcode = vcode_find_unit(func_name); + + const int nparams = vcode_count_args(op); + value_t *params[nparams]; + for (int i = 0; i < nparams; i++) + params[i] = eval_get_reg(vcode_get_arg(op, i), state); + + if (vcode == NULL) { + ident_t unit_name = ident_runtil(vcode_get_func(op), '.'); + ident_t lib_name = ident_until(unit_name, '.'); + + lib_t lib; + if (lib_name != unit_name && (lib = lib_find(lib_name, false)) != NULL) { + tree_rd_ctx_t tree_ctx; + tree_t unit = lib_get_ctx(lib, unit_name, &tree_ctx); + if (unit != NULL) { + eval_load_vcode(lib, unit, tree_ctx, state); + + if (tree_kind(unit) == T_PACKAGE) { + ident_t body_name = + ident_prefix(unit_name, ident_new("body"), '-'); + tree_t body = lib_get_ctx(lib, body_name, &tree_ctx); + if (body != NULL) + eval_load_vcode(lib, body, tree_ctx, state); } + + vcode = vcode_find_unit(func_name); } - break; + } + } - case A_OTHERS: - eval_stmt(tree_value(a), v); - break; + if (vcode == NULL) { + EVAL_WARN(state->fcall, "function call to %s prevents " + "constant folding", istr(func_name)); + state->failed = true; + vcode_state_restore(&vcode_state); + return; + } - default: - assert(false); + vcode_select_unit(vcode); + vcode_select_block(0); + + context_t *context = eval_new_context(state); + + for (int i = 0; i < nparams; i++) + context->regs[i] = *params[i]; + + eval_state_t new = { + .context = context, + .result = -1, + .fcall = state->fcall, + .failed = false, + .flags = state->flags | EVAL_BOUNDS, + .heap = state->heap, + .halloc = state->halloc + }; + + eval_vcode(&new); + vcode_state_restore(&vcode_state); + + state->heap = new.heap; + state->halloc = new.halloc; + + if (new.failed) + state->failed = true; + else if (vcode_get_result(op) != VCODE_INVALID_REG) { + assert(new.result != -1); + value_t *dst = eval_get_reg(vcode_get_result(op), state); + assert(context->regs[new.result].kind != VALUE_INVALID); + *dst = context->regs[new.result]; + + if (state->flags & EVAL_VERBOSE) { + const char *name = istr(vcode_get_func(op)); + const char *nest = istr(tree_ident(state->fcall)); + switch (context->regs[new.result].kind) { + case VALUE_INTEGER: + notef("%s (in %s) returned %"PRIi64, name, nest, + context->regs[new.result].integer); + break; + case VALUE_REAL: + notef("%s (in %s) returned %lf", name, nest, + context->regs[new.result].real); + break; + default: + notef("%s (in %s) returned value kind %d", name, nest, + context->regs[new.result].kind); + } } } + + eval_free_context(context); } -static void eval_while(tree_t t, vtable_t *v) +static void eval_op_bounds(int op, eval_state_t *state) { - int iters = 0; - tree_t value = tree_has_value(t) ? tree_value(t) : NULL; - while (v->result == NULL) { - bool cond_b = true; - if (value != NULL) { - tree_t cond = eval_expr(value, v); - if (!folded_bool(cond, &cond_b)) - eval_error(value, "cannot constant fold expression"); + value_t *reg = eval_get_reg(vcode_get_arg(op, 0), state); + vcode_type_t bounds = vcode_get_type(op); + + switch (reg->kind) { + case VALUE_INTEGER: + { + const int64_t low = vtype_low(bounds); + const int64_t high = vtype_high(bounds); + if (low > high) + break; + else if (reg->integer < low || reg->integer > high) { + if (state->flags & EVAL_BOUNDS) { + const loc_t *loc = tree_loc(vcode_get_bookmark(op).tree); + + switch ((bounds_kind_t)vcode_get_subkind(op)) { + case BOUNDS_ARRAY_TO: + error_at(loc, "array index %"PRIi64" outside bounds %"PRIi64 + " to %"PRIi64, reg->integer, low, high); + break; + + case BOUNDS_ARRAY_DOWNTO: + error_at(loc, "array index %"PRIi64" outside bounds %"PRIi64 + " downto %"PRIi64, reg->integer, high, low); + break; + + default: + fatal_trace("unhandled bounds kind %d in %s", + vcode_get_subkind(op), __func__); + } + + errors++; + note_at(tree_loc(state->fcall), "while evaluating call to %s", + istr(tree_ident(state->fcall))); + } + state->failed = true; + } } + break; - if (!cond_b || v->failed) - break; - else if (++iters == MAX_ITERS) { - warn_at(tree_loc(t), "iteration limit exceeded"); - v->failed = true; - break; - } + case VALUE_REAL: + break; - eval_stmts(t, tree_stmts, tree_stmt, v); + default: + fatal_trace("invalid value type in %s", __func__); + } +} - if (v->exit != NULL) { - if (v->exit == tree_ident(t)) - v->exit = NULL; - break; +static void eval_op_dynamic_bounds(int op, eval_state_t *state) +{ + value_t *reg = eval_get_reg(vcode_get_arg(op, 0), state); + value_t *low = eval_get_reg(vcode_get_arg(op, 1), state); + value_t *high = eval_get_reg(vcode_get_arg(op, 2), state); + + switch (reg->kind) { + case VALUE_INTEGER: + { + if (low->integer > high->integer) + break; + else if (reg->integer < low->integer || reg->integer > high->integer) + state->failed = true; } + break; + + case VALUE_REAL: + break; + + default: + fatal_trace("invalid value type in %s", __func__); } } -static void eval_for(tree_t t, vtable_t *v) +static void eval_op_const_array(int op, eval_state_t *state) { - range_t r = tree_range(t); - if (r.kind != RANGE_TO && r.kind != RANGE_DOWNTO) { - v->failed = true; - return; + value_t *dst = eval_get_reg(vcode_get_result(op), state); + + const int nargs = vcode_count_args(op); + + dst->kind = VALUE_POINTER; + if ((dst->pointer = eval_alloc(sizeof(value_t) * nargs, state))) { + for (int i = 0; i < nargs; i++) + dst->pointer[i] = *eval_get_reg(vcode_get_arg(op, i), state); } +} + +static void eval_op_wrap(int op, eval_state_t *state) +{ + value_t *dst = eval_get_reg(vcode_get_result(op), state); + value_t *src = eval_get_reg(vcode_get_arg(op, 0), state); - tree_t left = eval_expr(r.left, v); - tree_t right = eval_expr(r.right, v); + assert(src->kind == VALUE_POINTER); - int64_t lefti, righti; - if (!folded_int(left, &lefti) || !folded_int(right, &righti)) { - v->failed = true; + dst->kind = VALUE_UARRAY; + if ((dst->uarray = eval_alloc(sizeof(uarray_t), state)) == NULL) return; + dst->uarray->data = src->pointer; + + const int ndims = (vcode_count_args(op) - 1) / 3; + if (ndims > MAX_DIMS) { + state->failed = true; + EVAL_WARN(state->fcall, "%d dimensional array prevents " + "constant folding", ndims); } + else { + dst->uarray->ndims = ndims; + for (int i = 0; i < ndims; i++) { + dst->uarray->dim[i].left = + eval_get_reg(vcode_get_arg(op, (i * 3) + 1), state)->integer; + dst->uarray->dim[i].right = + eval_get_reg(vcode_get_arg(op, (i * 3) + 2), state)->integer; + dst->uarray->dim[i].dir = + eval_get_reg(vcode_get_arg(op, (i * 3) + 3), state)->integer; + } + } +} - if ((r.kind == RANGE_TO && lefti > righti) - || (r.kind == RANGE_DOWNTO && righti < lefti)) - return; +static void eval_op_store(int op, eval_state_t *state) +{ + value_t *src = eval_get_reg(vcode_get_arg(op, 0), state); + value_t *var = eval_get_var(vcode_get_address(op), state); - tree_t idecl = tree_decl(t, 0); - int64_t ival = lefti; - do { - vtable_bind(v, tree_ident(idecl), get_int_lit(left, ival)); - eval_stmts(t, tree_stmts, tree_stmt, v); - if (ival == righti) - break; - ival += (r.kind == RANGE_TO ? 1 : -1); - } while (v->result == NULL); + if (var != NULL) + *var = *src; +} + +static void eval_op_load(int op, eval_state_t *state) +{ + value_t *dst = eval_get_reg(vcode_get_result(op), state); + value_t *var = eval_get_var(vcode_get_address(op), state); + + if (var != NULL) + *dst = *var; +} + +static void eval_op_unwrap(int op, eval_state_t *state) +{ + value_t *dst = eval_get_reg(vcode_get_result(op), state); + value_t *src = eval_get_reg(vcode_get_arg(op, 0), state); + + assert(src->kind == VALUE_UARRAY); + + dst->kind = VALUE_POINTER; + dst->pointer = src->uarray->data; } -static void eval_var_assign(tree_t t, vtable_t *v) +static void eval_op_uarray_len(int op, eval_state_t *state) { - tree_t target = tree_target(t); - if (tree_kind(target) != T_REF) - eval_error(target, "cannot evaluate this target"); + value_t *dst = eval_get_reg(vcode_get_result(op), state); + value_t *src = eval_get_reg(vcode_get_arg(op, 0), state); - tree_t value = tree_value(t); - tree_t updated = eval_expr(value, v); - if (!folded(updated)) - eval_error(value, "cannot constant fold expression"); + const int dim = vcode_get_dim(op); + const int64_t left = src->uarray->dim[dim].left; + const int64_t right = src->uarray->dim[dim].right; + const range_kind_t dir = src->uarray->dim[dim].dir; - vtable_bind(v, tree_ident(tree_ref(target)), updated); + const int64_t len = (dir == RANGE_TO ? right - left : left - right) + 1; + + dst->kind = VALUE_INTEGER; + dst->integer = MAX(len, 0); } -static void eval_block(tree_t t, vtable_t *v) +static void eval_op_uarray_dir(int op, eval_state_t *state) { - assert(tree_decls(t) == 0); - eval_stmts(t, tree_stmts, tree_stmt, v); + value_t *dst = eval_get_reg(vcode_get_result(op), state); + value_t *src = eval_get_reg(vcode_get_arg(op, 0), state); + + const int dim = vcode_get_dim(op); + const range_kind_t dir = src->uarray->dim[dim].dir; + + dst->kind = VALUE_INTEGER; + dst->integer = dir; } -static void eval_exit(tree_t t, vtable_t *v) +static void eval_op_memcmp(int op, eval_state_t *state) { - if (tree_has_value(t)) { - bool cond_b = true; - tree_t cond = eval_expr(tree_value(t), v); - if (!folded_bool(cond, &cond_b)) - eval_error(tree_value(t), "cannot constant fold expression"); - else if (!cond_b) + value_t *dst = eval_get_reg(vcode_get_result(op), state); + value_t *lhs = eval_get_reg(vcode_get_arg(op, 0), state); + value_t *rhs = eval_get_reg(vcode_get_arg(op, 1), state); + value_t *len = eval_get_reg(vcode_get_arg(op, 2), state); + + dst->kind = VALUE_INTEGER; + dst->integer = 1; + + assert(lhs->kind == VALUE_POINTER); + assert(rhs->kind == VALUE_POINTER); + + for (int i = 0; i < len->integer; i++) { + if (eval_value_cmp(&(lhs->pointer[i]), &(rhs->pointer[i]))) { + dst->integer = 0; return; + } } - - v->exit = tree_ident2(t); } -static void eval_stmt(tree_t t, vtable_t *v) +static void eval_op_and(int op, eval_state_t *state) { - switch (tree_kind(t)) { - case T_RETURN: - eval_return(t, v); - break; - case T_WHILE: - eval_while(t, v); - break; - case T_FOR: - eval_for(t, v); - break; - case T_IF: - eval_if(t, v); - break; - case T_VAR_ASSIGN: - eval_var_assign(t, v); - break; - case T_BLOCK: - eval_block(t, v); - break; - case T_EXIT: - eval_exit(t, v); - break; - case T_CASE: - eval_case(t, v); + value_t *dst = eval_get_reg(vcode_get_result(op), state); + value_t *lhs = eval_get_reg(vcode_get_arg(op, 0), state); + value_t *rhs = eval_get_reg(vcode_get_arg(op, 1), state); + + switch (lhs->kind) { + case VALUE_INTEGER: + dst->kind = VALUE_INTEGER; + dst->integer = lhs->integer & rhs->integer; break; + default: - eval_error(t, "cannot evaluate statement %s", - tree_kind_str(tree_kind(t))); + fatal_trace("invalid value type in %s", __func__); } } -tree_t eval(tree_t fcall) +static void eval_op_or(int op, eval_state_t *state) { - assert(tree_kind(fcall) == T_FCALL); + value_t *dst = eval_get_reg(vcode_get_result(op), state); + value_t *lhs = eval_get_reg(vcode_get_arg(op, 0), state); + value_t *rhs = eval_get_reg(vcode_get_arg(op, 1), state); + + switch (lhs->kind) { + case VALUE_INTEGER: + dst->kind = VALUE_INTEGER; + dst->integer = lhs->integer | rhs->integer; + break; - static bool have_debug = false; - if (!have_debug) { - debug = (getenv("NVC_EVAL_DEBUG") != NULL); - have_debug = true; + default: + fatal_trace("invalid value type in %s", __func__); } +} - vtable_t vt = { - .top = NULL, - .failed = false, - .exit = NULL, - .result = NULL - }; - tree_t r = eval_fcall(fcall, &vt); - return vt.failed ? fcall : r; +static void eval_op_jump(int op, eval_state_t *state) +{ + vcode_select_block(vcode_get_target(op, 0)); + eval_vcode(state); +} + +static void eval_op_cond(int op, eval_state_t *state) +{ + value_t *test = eval_get_reg(vcode_get_arg(op, 0), state); + + const vcode_block_t next = vcode_get_target(op, !(test->integer)); + vcode_select_block(next); + eval_vcode(state); +} + +static void eval_op_undefined(int op, eval_state_t *state) +{ + EVAL_WARN(state->fcall, "reference to object without defined " + "value in this phase prevents constant folding"); + + state->failed = true; +} + +static void eval_op_nested_fcall(int op, eval_state_t *state) +{ + // TODO + state->failed = true; +} + +static void eval_op_index(int op, eval_state_t *state) +{ + value_t *value = eval_get_var(vcode_get_address(op), state); + if (value == NULL) + return; + + value_t *dst = eval_get_reg(vcode_get_result(op), state); + dst->kind = VALUE_POINTER; + dst->pointer = (value->kind == VALUE_CARRAY) ? value->pointer : value; +} + +static void eval_op_load_indirect(int op, eval_state_t *state) +{ + value_t *dst = eval_get_reg(vcode_get_result(op), state); + value_t *src = eval_get_reg(vcode_get_arg(op, 0), state); + + assert(src->kind == VALUE_POINTER); + *dst = *(src->pointer); +} + +static void eval_op_store_indirect(int op, eval_state_t *state) +{ + value_t *dst = eval_get_reg(vcode_get_arg(op, 1), state); + value_t *src = eval_get_reg(vcode_get_arg(op, 0), state); + + assert(dst->kind == VALUE_POINTER); + *(dst->pointer) = *src; +} + +static void eval_op_case(int op, eval_state_t *state) +{ + value_t *test = eval_get_reg(vcode_get_arg(op, 0), state); + vcode_block_t target = vcode_get_target(op, 0); + + const int num_args = vcode_count_args(op); + for (int i = 1; i < num_args; i++) { + value_t *cmp = eval_get_reg(vcode_get_arg(op, i), state); + if (eval_value_cmp(test, cmp) == 0) { + target = vcode_get_target(op, i); + break; + } + } + + vcode_select_block(target); + eval_vcode(state); +} + +static void eval_op_copy(int op, eval_state_t *state) +{ + value_t *dst = eval_get_reg(vcode_get_arg(op, 0), state); + value_t *src = eval_get_reg(vcode_get_arg(op, 1), state); + value_t *count = eval_get_reg(vcode_get_arg(op, 2), state); + + assert(dst->kind = VALUE_POINTER); + assert(src->kind = VALUE_POINTER); + assert(count->kind = VALUE_INTEGER); + + for (int i = 0; i < count->integer; i++) + dst->pointer[i] = src->pointer[i]; +} + +static void eval_op_report(int op, eval_state_t *state) +{ + value_t *text = eval_get_reg(vcode_get_arg(op, 1), state); + value_t *length = eval_get_reg(vcode_get_arg(op, 2), state); + value_t *severity = eval_get_reg(vcode_get_arg(op, 0), state); + + if (state->flags & EVAL_REPORT) + eval_message(text, length, severity, + tree_loc(vcode_get_bookmark(op).tree), "Report"); + else + state->failed = true; // Cannot fold as would change runtime behaviour +} + +static void eval_op_assert(int op, eval_state_t *state) +{ + value_t *test = eval_get_reg(vcode_get_arg(op, 0), state); + value_t *text = eval_get_reg(vcode_get_arg(op, 2), state); + value_t *length = eval_get_reg(vcode_get_arg(op, 3), state); + value_t *severity = eval_get_reg(vcode_get_arg(op, 1), state); + + if (test->integer == 0) { + if (state->flags & EVAL_REPORT) + eval_message(text, length, severity, + tree_loc(vcode_get_bookmark(op).tree), "Assertion"); + state->failed = severity->integer >= SEVERITY_ERROR; + } +} + +static void eval_op_select(int op, eval_state_t *state) +{ + value_t *test = eval_get_reg(vcode_get_arg(op, 0), state); + value_t *left = eval_get_reg(vcode_get_arg(op, 1), state); + value_t *right = eval_get_reg(vcode_get_arg(op, 2), state); + value_t *result = eval_get_reg(vcode_get_result(op), state); + + assert(test->kind == VALUE_INTEGER); + + *result = test->integer ? *left : *right; +} + +static void eval_op_alloca(int op, eval_state_t *state) +{ + value_t *result = eval_get_reg(vcode_get_result(op), state); + + int length = 1; + if (vcode_count_args(op) > 0) { + value_t *length_reg = eval_get_reg(vcode_get_arg(op, 0), state); + assert(length_reg->kind == VALUE_INTEGER); + length = length_reg->integer; + } + + result->kind = VALUE_POINTER; + + vcode_type_t vtype = vcode_get_type(op); + switch (vtype_kind(vtype)) { + case VCODE_TYPE_RECORD: + if ((result->pointer = eval_alloc(sizeof(value_t), state))) { + result->pointer->kind = VALUE_RECORD; + result->pointer->fields = + eval_alloc(sizeof(value_t) * vtype_fields(vtype), state); + } + break; + + default: + result->pointer = eval_alloc(sizeof(value_t) * length, state); + break; + } +} + +static void eval_op_index_check(int op, eval_state_t *state) +{ + value_t *low = eval_get_reg(vcode_get_arg(op, 0), state); + value_t *high = eval_get_reg(vcode_get_arg(op, 1), state); + + int64_t min, max; + if (vcode_count_args(op) == 2) { + vcode_type_t bounds = vcode_get_type(op); + min = vtype_low(bounds); + max = vtype_high(bounds); + } + else { + min = eval_get_reg(vcode_get_arg(op, 2), state)->integer; + max = eval_get_reg(vcode_get_arg(op, 3), state)->integer; + } + + if (high->integer < low->integer) + return; + else if (low->integer < min) + state->failed = true; // TODO: report error here if EVAL_BOUNDS + else if (high->integer > max) + state->failed = true; +} + +static void eval_op_image(int op, eval_state_t *state) +{ + value_t *object = eval_get_reg(vcode_get_arg(op, 0), state); + tree_t where = vcode_get_bookmark(op).tree; + type_t type = type_base_recur(tree_type(where)); + + char buf[32]; + size_t len = 0; + + switch (type_kind(type)) { + case T_INTEGER: + len = snprintf(buf, sizeof(buf), "%"PRIi64, object->integer); + break; + + case T_ENUM: + { + tree_t lit = type_enum_literal(type, object->integer); + len = snprintf(buf, sizeof(buf), "%s", istr(tree_ident(lit))); + } + break; + + case T_REAL: + len = snprintf(buf, sizeof(buf), "%.*g", DBL_DIG + 3, object->real); + break; + + case T_PHYSICAL: + { + tree_t unit = type_unit(type, 0); + len = snprintf(buf, sizeof(buf), "%"PRIi64" %s", object->integer, + istr(tree_ident(unit))); + } + break; + + default: + error_at(tree_loc(where), "cannot use 'IMAGE with this type"); + state->failed = true; + return; + } + + value_t *dst = eval_get_reg(vcode_get_result(op), state); + dst->kind = VALUE_UARRAY; + if ((dst->uarray = eval_alloc(sizeof(uarray_t), state)) == NULL) + return; + if ((dst->uarray->data = eval_alloc(sizeof(value_t) * len, state)) == NULL) + return; + + dst->uarray->ndims = 1; + dst->uarray->dim[0].left = 1; + dst->uarray->dim[0].right = len; + dst->uarray->dim[0].dir = RANGE_TO; + + for (size_t i = 0; i < len; i++) { + value_t *ch = &(dst->uarray->data[i]); + ch->kind = VALUE_INTEGER; + ch->integer = buf[i]; + } +} + +static void eval_op_uarray_left(int op, eval_state_t *state) +{ + value_t *array = eval_get_reg(vcode_get_arg(op, 0), state); + value_t *dst = eval_get_reg(vcode_get_result(op), state); + + assert(array->kind == VALUE_UARRAY); + + dst->kind = VALUE_INTEGER; + dst->integer = array->uarray->dim[vcode_get_dim(op)].left; +} + +static void eval_op_uarray_right(int op, eval_state_t *state) +{ + value_t *array = eval_get_reg(vcode_get_arg(op, 0), state); + value_t *dst = eval_get_reg(vcode_get_result(op), state); + + assert(array->kind == VALUE_UARRAY); + + dst->kind = VALUE_INTEGER; + dst->integer = array->uarray->dim[vcode_get_dim(op)].right; +} + +static void eval_op_const_record(int op, eval_state_t *state) +{ + value_t *dst = eval_get_reg(vcode_get_result(op), state); + + const int nfields = vcode_count_args(op); + dst->kind = VALUE_RECORD; + if ((dst->fields = eval_alloc(nfields * sizeof(value_t), state)) == NULL) + return; + + for (int i = 0; i < nfields; i++) + dst->fields[i] = *eval_get_reg(vcode_get_arg(op, i), state); +} + +static void eval_op_record_ref(int op, eval_state_t *state) +{ + value_t *ptr = eval_get_reg(vcode_get_arg(op, 0), state); + value_t *dst = eval_get_reg(vcode_get_result(op), state); + + assert(ptr->kind == VALUE_POINTER); + assert(ptr->pointer->kind == VALUE_RECORD); + + dst->kind = VALUE_POINTER; + dst->pointer = &(ptr->pointer->fields[vcode_get_field(op)]); +} + +static void eval_op_memset(int op, eval_state_t *state) +{ + value_t *dst = eval_get_reg(vcode_get_arg(op, 0), state); + value_t *fill = eval_get_reg(vcode_get_arg(op, 1), state); + value_t *length = eval_get_reg(vcode_get_arg(op, 2), state); + + assert(dst->kind == VALUE_POINTER); + assert(length->kind == VALUE_INTEGER); + assert(fill->kind == VALUE_INTEGER); + + for (int i = 0; i < length->integer; i++) + dst->pointer[i] = *fill; +} + +static void eval_op_bit_shift(int op, eval_state_t *state) +{ + value_t *data = eval_get_reg(vcode_get_arg(op, 0), state); + value_t *length = eval_get_reg(vcode_get_arg(op, 1), state); + value_t *dir = eval_get_reg(vcode_get_arg(op, 2), state); + value_t *shift = eval_get_reg(vcode_get_arg(op, 3), state); + value_t *dst = eval_get_reg(vcode_get_result(op), state); + + bit_shift_kind_t kind = vcode_get_subkind(op); + + assert(data->kind == VALUE_POINTER); + assert(length->kind == VALUE_INTEGER); + assert(dir->kind == VALUE_INTEGER); + assert(shift->kind == VALUE_INTEGER); + + int shift_i = shift->integer; + if (shift_i < 0) { + kind = kind ^ 1; + shift_i = -shift_i; + } + + shift_i %= length->integer; + + value_t *buf = eval_alloc(sizeof(value_t) * length->integer, state); + if (buf == NULL) + return; + + for (int i = 0; i < length->integer; i++) { + buf[i].kind = VALUE_INTEGER; + switch (kind) { + case BIT_SHIFT_SLL: + buf[i].integer = (i < length->integer - shift_i) + ? data->pointer[i + shift_i].integer : 0; + break; + case BIT_SHIFT_SRL: + buf[i].integer = (i >= shift_i) + ? data->pointer[i - shift_i].integer : 0; + break; + case BIT_SHIFT_SLA: + buf[i].integer = (i < length->integer - shift_i) + ? data->pointer[i + shift_i].integer + : data->pointer[length->integer - 1].integer; + break; + case BIT_SHIFT_SRA: + buf[i].integer = (i >= shift_i) + ? data->pointer[i - shift_i].integer : data->pointer[0].integer; + break; + case BIT_SHIFT_ROL: + buf[i].integer = (i < length->integer - shift_i) + ? data->pointer[i + shift_i].integer + : data->pointer[(i + shift_i) % length->integer].integer; + break; + case BIT_SHIFT_ROR: + buf[i].integer = (i >= shift_i) + ? data->pointer[i - shift_i].integer + : data->pointer[length->integer + i - shift_i].integer; + break; + } + } + + dst->kind = VALUE_UARRAY; + if ((dst->uarray = eval_alloc(sizeof(uarray_t), state)) == NULL) + return; + dst->uarray->data = buf; + dst->uarray->ndims = 1; + dst->uarray->dim[0].left = + (dir->integer == RANGE_TO) ? 0 : length->integer - 1; + dst->uarray->dim[0].right = + (dir->integer == RANGE_TO) ? length->integer - 1 : 0; + dst->uarray->dim[0].dir = dir->integer; +} + +static void eval_vcode(eval_state_t *state) +{ + const int nops = vcode_count_ops(); + for (int i = 0; i < nops && !(state->failed); i++) { + switch (vcode_get_op(i)) { + case VCODE_OP_COMMENT: + break; + + case VCODE_OP_CONST: + eval_op_const(i, state); + break; + + case VCODE_OP_CONST_REAL: + eval_op_const_real(i, state); + break; + + case VCODE_OP_RETURN: + eval_op_return(i, state); + return; + + case VCODE_OP_NOT: + eval_op_not(i, state); + break; + + case VCODE_OP_ADD: + eval_op_add(i, state); + break; + + case VCODE_OP_SUB: + eval_op_sub(i, state); + break; + + case VCODE_OP_MUL: + eval_op_mul(i, state); + break; + + case VCODE_OP_DIV: + eval_op_div(i, state); + break; + + case VCODE_OP_CMP: + eval_op_cmp(i, state); + break; + + case VCODE_OP_CAST: + eval_op_cast(i, state); + break; + + case VCODE_OP_NEG: + eval_op_neg(i, state); + break; + + case VCODE_OP_FCALL: + if (state->flags & EVAL_FCALL) + eval_op_fcall(i, state); + else + state->failed = true; + break; + + case VCODE_OP_BOUNDS: + eval_op_bounds(i, state); + break; + + case VCODE_OP_CONST_ARRAY: + eval_op_const_array(i, state); + break; + + case VCODE_OP_WRAP: + eval_op_wrap(i, state); + break; + + case VCODE_OP_STORE: + eval_op_store(i, state); + break; + + case VCODE_OP_UNWRAP: + eval_op_unwrap(i, state); + break; + + case VCODE_OP_UARRAY_LEN: + eval_op_uarray_len(i, state); + break; + + case VCODE_OP_MEMCMP: + eval_op_memcmp(i, state); + break; + + case VCODE_OP_AND: + eval_op_and(i, state); + break; + + case VCODE_OP_OR: + eval_op_or(i, state); + break; + + case VCODE_OP_COND: + eval_op_cond(i, state); + return; + + case VCODE_OP_JUMP: + eval_op_jump(i, state); + return; + + case VCODE_OP_LOAD: + eval_op_load(i, state); + break; + + case VCODE_OP_UNDEFINED: + eval_op_undefined(i, state); + break; + + case VCODE_OP_NESTED_FCALL: + eval_op_nested_fcall(i, state); + break; + + case VCODE_OP_CASE: + eval_op_case(i, state); + return; + + case VCODE_OP_MOD: + eval_op_mod(i, state); + break; + + case VCODE_OP_REM: + eval_op_rem(i, state); + break; + + case VCODE_OP_DYNAMIC_BOUNDS: + eval_op_dynamic_bounds(i, state); + break; + + case VCODE_OP_INDEX: + eval_op_index(i, state); + break; + + case VCODE_OP_COPY: + eval_op_copy(i, state); + break; + + case VCODE_OP_LOAD_INDIRECT: + eval_op_load_indirect(i, state); + break; + + case VCODE_OP_STORE_INDIRECT: + eval_op_store_indirect(i, state); + break; + + case VCODE_OP_REPORT: + eval_op_report(i, state); + break; + + case VCODE_OP_ASSERT: + eval_op_assert(i, state); + break; + + case VCODE_OP_SELECT: + eval_op_select(i, state); + break; + + case VCODE_OP_ALLOCA: + eval_op_alloca(i, state); + break; + + case VCODE_OP_INDEX_CHECK: + eval_op_index_check(i, state); + break; + + case VCODE_OP_ABS: + eval_op_abs(i, state); + break; + + case VCODE_OP_IMAGE: + eval_op_image(i, state); + break; + + case VCODE_OP_HEAP_SAVE: + case VCODE_OP_HEAP_RESTORE: + break; + + case VCODE_OP_UARRAY_LEFT: + eval_op_uarray_left(i, state); + break; + + case VCODE_OP_UARRAY_RIGHT: + eval_op_uarray_right(i, state); + break; + + case VCODE_OP_UARRAY_DIR: + eval_op_uarray_dir(i, state); + break; + + case VCODE_OP_EXP: + eval_op_exp(i, state); + break; + + case VCODE_OP_CONST_RECORD: + eval_op_const_record(i, state); + break; + + case VCODE_OP_RECORD_REF: + eval_op_record_ref(i, state); + break; + + case VCODE_OP_MEMSET: + eval_op_memset(i, state); + break; + + case VCODE_OP_BIT_SHIFT: + eval_op_bit_shift(i, state); + break; + + default: + vcode_dump(); + fatal("cannot evaluate vcode op %s", vcode_op_string(vcode_get_op(i))); + } + } +} + +tree_t eval(tree_t fcall, eval_flags_t flags) +{ + assert(tree_kind(fcall) == T_FCALL); + + type_t type = tree_type(fcall); + if (!type_is_scalar(type)) + return fcall; + else if (tree_flags(tree_ref(fcall)) & TREE_F_IMPURE) + return fcall; + else if (!eval_params_possible(fcall, flags)) + return fcall; + + if (getenv("NVC_EVAL_VERBOSE")) + flags |= EVAL_VERBOSE; + + if (flags & EVAL_VERBOSE) + flags |= EVAL_WARN | EVAL_BOUNDS; + + vcode_unit_t thunk = lower_thunk(fcall); + if (thunk == NULL) + return fcall; + + if (flags & EVAL_VERBOSE) + note_at(tree_loc(fcall), "evaluate thunk for %s", + istr(tree_ident(fcall))); + + vcode_select_unit(thunk); + vcode_select_block(0); + + context_t *context = eval_new_context(NULL); + + eval_state_t state = { + .context = context, + .result = -1, + .fcall = fcall, + .failed = false, + .flags = flags, + }; + + eval_vcode(&state); + free(state.heap); + + vcode_unit_unref(thunk); + thunk = NULL; + + if (state.failed) { + eval_free_context(state.context); + return fcall; + } + + assert(state.result != -1); + value_t result = context->regs[state.result]; + eval_free_context(state.context); + + if (flags & EVAL_VERBOSE) { + const char *name = istr(tree_ident(fcall)); + if (result.kind == VALUE_INTEGER) + note_at(tree_loc(fcall), "%s returned %"PRIi64, name, result.integer); + else + note_at(tree_loc(fcall), "%s returned %lf", name, result.real); + } + + switch (result.kind) { + case VALUE_INTEGER: + if (type_is_enum(type)) + return get_enum_lit(fcall, result.integer); + else + return get_int_lit(fcall, result.integer); + case VALUE_REAL: + return get_real_lit(fcall, result.real); + default: + fatal_trace("eval result is not scalar"); + } +} + +int eval_errors(void) +{ + return errors; +} + +static tree_t fold_tree_fn(tree_t t, void *context) +{ + switch (tree_kind(t)) { + case T_FCALL: + return eval(t, EVAL_FCALL | EVAL_FOLDING); + + case T_REF: + { + tree_t decl = tree_ref(t); + switch (tree_kind(decl)) { + case T_CONST_DECL: + { + tree_t value = tree_value(decl); + if (tree_kind(value) == T_LITERAL) + return value; + else + return t; + } + + case T_UNIT_DECL: + return tree_value(decl); + + default: + return t; + } + } + + default: + return t; + } +} + +void fold(tree_t top) +{ + tree_rewrite(top, fold_tree_fn, NULL); } diff --git a/src/fbuf.c b/src/fbuf.c index db2383c5..74d89d71 100644 --- a/src/fbuf.c +++ b/src/fbuf.c @@ -121,12 +121,17 @@ fbuf_t *fbuf_open(const char *file, fbuf_mode_t mode) return (open_list = f); } +const char *fbuf_file_name(fbuf_t *f) +{ + return f->fname; +} + static void fbuf_maybe_flush(fbuf_t *f, size_t more, bool finish) { assert(more <= BLOCK_SIZE); if (f->wpend + more > BLOCK_SIZE) { if (f->wpend < 16) { - // Write dummy bytes at and to meet fastlz block size requirement + // Write dummy bytes at end to meet fastlz block size requirement assert(finish); f->wpend = 16; } @@ -173,6 +178,9 @@ static void fbuf_maybe_read(fbuf_t *f, size_t more) f->roff += sizeof(uint32_t); + if (f->roff + blksz > f->maplen) + fatal_trace("read past end of compressed file %s", f->fname); + const int ret = fastlz_decompress(f->rmap + f->roff, blksz, f->rbuf + overlap, @@ -260,6 +268,13 @@ void write_raw(const void *buf, size_t len, fbuf_t *f) f->wpend += len; } +void write_double(double d, fbuf_t *f) +{ + union { double d; uint64_t i; } u; + u.d = d; + write_u64(u.i, f); +} + uint32_t read_u32(fbuf_t *f) { fbuf_maybe_read(f, 4); @@ -310,3 +325,10 @@ void read_raw(void *buf, size_t len, fbuf_t *f) memcpy(buf, f->rbuf + f->rptr, len); f->rptr += len; } + +double read_double(fbuf_t *f) +{ + union { uint64_t i; double d; } u; + u.i = read_u64(f); + return u.d; +} diff --git a/src/fbuf.h b/src/fbuf.h index 802c580a..e664dbe1 100644 --- a/src/fbuf.h +++ b/src/fbuf.h @@ -1,5 +1,5 @@ // -// Copyright (C) 2013 Nick Gasson +// Copyright (C) 2013-2016 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 @@ -34,17 +34,20 @@ typedef enum { fbuf_t *fbuf_open(const char *file, fbuf_mode_t mode); void fbuf_close(fbuf_t *f); void fbuf_cleanup(void); +const char *fbuf_file_name(fbuf_t *f); void write_u32(uint32_t u, fbuf_t *f); void write_u16(uint16_t s, fbuf_t *f); void write_u64(uint64_t i, fbuf_t *f); void write_u8(uint8_t u, fbuf_t *f); void write_raw(const void *buf, size_t len, fbuf_t *f); +void write_double(double d, fbuf_t *f); uint32_t read_u32(fbuf_t *f); uint16_t read_u16(fbuf_t *f); uint64_t read_u64(fbuf_t *f); uint8_t read_u8(fbuf_t *f); void read_raw(void *buf, size_t len, fbuf_t *f); +double read_double(fbuf_t *f); #endif // _FBUF_H diff --git a/src/hash.c b/src/hash.c index afb13e5a..fc7059bb 100644 --- a/src/hash.c +++ b/src/hash.c @@ -167,3 +167,8 @@ bool hash_iter(hash_t *h, hash_iter_t *now, const void **key, void **value) *now = HASH_END; return false; } + +unsigned hash_members(hash_t *h) +{ + return h->members; +} diff --git a/src/hash.h b/src/hash.h index af0ea8a5..1b073e90 100644 --- a/src/hash.h +++ b/src/hash.h @@ -1,5 +1,5 @@ // -// Copyright (C) 2013-2014 Nick Gasson +// Copyright (C) 2013-2015 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 @@ -36,5 +36,6 @@ void *hash_get(hash_t *h, const void *key); void *hash_get_nth(hash_t *h, const void *key, int *n); void hash_replace(hash_t *h, void *value, void *with); bool hash_iter(hash_t *h, hash_iter_t *now, const void **key, void **value); +unsigned hash_members(hash_t *h); #endif // _HASH_H diff --git a/src/ident.c b/src/ident.c index 705b6e92..4800b3b3 100644 --- a/src/ident.c +++ b/src/ident.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2011-2014 Nick Gasson +// Copyright (C) 2011-2016 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 @@ -287,10 +287,11 @@ ident_t ident_read(ident_rd_ctx_t ctx) return p; } } - else { - assert(index < ctx->cache_sz); + else if (likely(index < ctx->cache_sz)) return ctx->cache[index]; - } + else + fatal("ident index in %s is corrupt: index=%d cache_sz=%d", + fbuf_file_name(ctx->file), index, (int)ctx->cache_sz); } ident_t ident_uniq(const char *prefix) @@ -386,10 +387,9 @@ ident_t ident_runtil(ident_t i, char c) { assert(i != NULL); - while (i->value != '\0') { - if (i->value == c) - return i->up; - i = i->up; + for (ident_t r = i; r->value != '\0'; r = r->up) { + if (r->value == c) + return r->up; } return i; @@ -469,6 +469,20 @@ bool ident_glob(ident_t i, const char *glob, int length) return ident_glob_walk(i, glob + length - 1, glob); } +bool ident_contains(ident_t i, const char *search) +{ + assert(i != NULL); + + for (ident_t r = i; r->value != '\0'; r = r->up) { + for (const char *p = search; *p != '\0'; p++) { + if (r->value == *p) + return true; + } + } + + return false; +} + void ident_list_add(ident_list_t **list, ident_t i) { ident_list_t *c = xmalloc(sizeof(ident_list_t)); diff --git a/src/ident.h b/src/ident.h index 158b2289..b4478cc4 100644 --- a/src/ident.h +++ b/src/ident.h @@ -68,6 +68,9 @@ bool icmp(ident_t i, const char *s); // if set to the length of glob bool ident_glob(ident_t i, const char *glob, int length); +// True if identifier contains any characters from search +bool ident_contains(ident_t i, const char *search); + // Convert an identifier reference to a NULL-terminated string. // This function is quite slow so its use should be avoided except // for printing. diff --git a/src/lower.c b/src/lower.c index 212219d0..de7304aa 100644 --- a/src/lower.c +++ b/src/lower.c @@ -20,6 +20,7 @@ #include "vcode.h" #include "common.h" #include "rt/rt.h" +#include "hash.h" #include #include @@ -64,8 +65,15 @@ struct case_state { case_arc_t arcs[MAX_CASE_ARCS]; }; -static const char *verbose = NULL; -static bool tmp_alloc_used = false; +typedef enum { + LOWER_NORMAL, + LOWER_THUNK +} lower_mode_t; + +static const char *verbose = NULL; +static bool tmp_alloc_used = false; +static lower_mode_t mode = LOWER_NORMAL; +static hash_t *vcode_objs = NULL; static vcode_reg_t lower_expr(tree_t expr, expr_ctx_t ctx); static vcode_reg_t lower_reify_expr(tree_t expr); @@ -320,15 +328,15 @@ static vcode_type_t lower_array_type(type_t type) return vtype_uarray(array_dimension(type), elem_type, elem_bounds); } -static uint32_t lower_record_index(type_t type) +static vcode_bookmark_t lower_record_bookmark(type_t type) { // If a record type is not qualified with a package name then add a unique // index to its type name to avoid collisions + vcode_bookmark_t uniq = { .type = NULL }; ident_t name = type_ident(type); - if (ident_until(name, '.') == name) - return type_index(type); - else - return UINT32_MAX; + if (ident_until(name, '.') == name && type_has_index(type)) + uniq.type = type; + return uniq; } static vcode_type_t lower_type(type_t type) @@ -349,8 +357,11 @@ static vcode_type_t lower_type(type_t type) { range_t r = type_dim(type, 0); int64_t low, high; - range_bounds(r, &low, &high); - return vtype_int(low, high); + const bool folded = folded_bounds(r, &low, &high); + if (folded) + return vtype_int(low, high); + else + return vtype_int(INT64_MIN, INT64_MAX); } case T_ENUM: @@ -359,10 +370,10 @@ static vcode_type_t lower_type(type_t type) case T_RECORD: { ident_t name = type_ident(type); - uint32_t index = lower_record_index(type); - vcode_type_t record = vtype_named_record(name, index, false); + vcode_bookmark_t uniq = lower_record_bookmark(type); + vcode_type_t record = vtype_named_record(name, uniq, false); if (record == VCODE_INVALID_TYPE) { - record = vtype_named_record(name, index, true); + record = vtype_named_record(name, uniq, true); const int nfields = type_fields(type); vcode_type_t fields[nfields]; @@ -378,10 +389,10 @@ static vcode_type_t lower_type(type_t type) case T_PROTECTED: { ident_t name = type_ident(type); - uint32_t index = lower_record_index(type); - vcode_type_t record = vtype_named_record(name, index, false); + vcode_bookmark_t uniq = lower_record_bookmark(type); + vcode_type_t record = vtype_named_record(name, uniq, false); if (record == VCODE_INVALID_TYPE) { - record = vtype_named_record(name, index, true); + record = vtype_named_record(name, uniq, true); tree_t body = type_body(type); @@ -480,6 +491,15 @@ static vcode_reg_t lower_wrap(type_t type, vcode_reg_t data) return lower_wrap_with_new_bounds(type, data, data); } +static vcode_bookmark_t lower_bookmark(tree_t where) +{ + vcode_bookmark_t b = { + .tree = where + }; + + return b; +} + static bounds_kind_t lower_type_bounds_kind(type_t type) { if (type_is_enum(type)) @@ -532,8 +552,8 @@ static bool lower_scalar_has_static_bounds(type_t type, vcode_reg_t *low_reg, static void lower_check_scalar_bounds(vcode_reg_t value, type_t type, tree_t where, tree_t hint) { - const int index1 = tree_index(where); - const int index2 = hint == NULL ? index1 : tree_index(hint); + const vcode_bookmark_t index1 = lower_bookmark(where); + const vcode_bookmark_t index2 = hint == NULL ? index1 : lower_bookmark(hint); const bounds_kind_t kind = lower_type_bounds_kind(type); @@ -883,23 +903,28 @@ static ident_t lower_mangle_func(tree_t decl, vcode_unit_t context) } LOCAL_TEXT_BUF buf = tb_new(); + ident_t name_i = tree_ident(decl); const int nest_depth = tree_attr_int(decl, nested_i, 0); - if (nest_depth > 0) { + if (nest_depth > 0 || !ident_contains(name_i, ".:")) { vcode_state_t state; vcode_state_save(&state); vcode_select_unit(context); const vunit_kind_t ckind = vcode_unit_kind(); - if (ckind != VCODE_UNIT_CONTEXT && ckind != VCODE_UNIT_PROCESS) + if (ckind == VCODE_UNIT_PROCESS) + ; + else if (ckind != VCODE_UNIT_CONTEXT) tb_printf(buf, "%s__", istr(vcode_unit_name())); + else + tb_printf(buf, "%s.", istr(vcode_unit_name())); vcode_state_restore(&state); } #if LLVM_MANGLES_NAMES - const char *name = istr(tree_ident(decl)); + const char *name = istr(name_i); char tmp[strlen(name) + 1], *p; for (p = tmp; *name != '\0'; ++name) { switch (*name) { @@ -918,7 +943,7 @@ static ident_t lower_mangle_func(tree_t decl, vcode_unit_t context) tb_printf(buf, "%s", tmp); #else - tb_printf(buf, "%s", istr(tree_ident(decl))); + tb_printf(buf, "%s", istr(name_i)); #endif const tree_kind_t kind = tree_kind(decl); @@ -969,6 +994,9 @@ static vcode_reg_t lower_min_max(vcode_cmp_t cmp, tree_t fcall) static vcode_reg_t lower_name_attr(tree_t ref, name_attr_t which) { + if (mode == LOWER_THUNK) + return emit_undefined(vtype_uarray(1, vtype_char(), vtype_char())); + tree_t decl = tree_ref(ref); ident_t i = NULL; @@ -1131,7 +1159,7 @@ static vcode_reg_t lower_builtin(tree_t fcall, ident_t builtin) if (!type_eq(r0_type, r1_type)) r1 = emit_cast(lower_type(r0_type), lower_bounds(r0_type), r1); return lower_narrow(tree_type(fcall), - emit_div(r0, r1, tree_index(fcall))); + emit_div(r0, r1, lower_bookmark(fcall))); } else if (icmp(builtin, "exp")) { if (!type_eq(r0_type, r1_type)) @@ -1250,12 +1278,12 @@ static vcode_reg_t lower_builtin(tree_t fcall, ident_t builtin) return lower_bit_shift(BIT_SHIFT_ROL, r0, r0_type, r1); else if (icmp(builtin, "ror")) return lower_bit_shift(BIT_SHIFT_ROR, r0, r0_type, r1); - else if (icmp(builtin, "mulrp")) { + else if (icmp(builtin, "mulrp") || icmp(builtin, "mulri")) { vcode_type_t vreal = vtype_real(); vcode_type_t rtype = lower_type(tree_type(fcall)); return emit_cast(rtype, rtype, emit_mul(r0, emit_cast(vreal, vreal, r1))); } - else if (icmp(builtin, "mulpr")) { + else if (icmp(builtin, "mulpr") || icmp(builtin, "mulir")) { vcode_type_t vreal = vtype_real(); vcode_type_t rtype = lower_type(tree_type(fcall)); return emit_cast(rtype, rtype, emit_mul(emit_cast(vreal, vreal, r0), r1)); @@ -1265,7 +1293,14 @@ static vcode_reg_t lower_builtin(tree_t fcall, ident_t builtin) vcode_type_t rtype = lower_type(tree_type(fcall)); return emit_cast(rtype, rtype, emit_div(emit_cast(vreal, vreal, r0), - r1, tree_index(fcall))); + r1, lower_bookmark(fcall))); + } + else if (icmp(builtin, "divri")) { + vcode_type_t vreal = vtype_real(); + vcode_type_t rtype = lower_type(tree_type(fcall)); + return emit_cast(rtype, rtype, + emit_div(r0, emit_cast(vreal, vreal, r1), + lower_bookmark(fcall))); } else fatal_at(tree_loc(fcall), "cannot lower builtin %s", istr(builtin)); @@ -1374,13 +1409,29 @@ static vcode_reg_t lower_literal(tree_t lit, expr_ctx_t ctx) } } +static int lower_get_vcode_obj(tree_t t) +{ + if (vcode_objs == NULL) + return VCODE_INVALID_REG; + else { + const void *ptr = hash_get(vcode_objs, t); + return (uintptr_t)ptr - 1; + } +} + +static void lower_put_vcode_obj(tree_t t, int obj) +{ + hash_put(vcode_objs, t, (void *)(uintptr_t)(obj + 1)); +} + static vcode_var_t lower_get_var(tree_t decl) { - vcode_var_t var = tree_attr_int(decl, vcode_obj_i, VCODE_INVALID_VAR); + vcode_var_t var = lower_get_vcode_obj(decl); const bool is_extern = var == VCODE_INVALID_VAR - && tree_attr_int(decl, prot_field_i, -1) == -1; + && tree_attr_int(decl, prot_field_i, -1) == -1 + && mode == LOWER_NORMAL; if (is_extern) { vcode_state_t state; @@ -1401,7 +1452,7 @@ static vcode_var_t lower_get_var(tree_t decl) static vcode_signal_t lower_get_signal(tree_t decl) { - vcode_signal_t sig = tree_attr_int(decl, vcode_obj_i, VCODE_INVALID_SIGNAL); + vcode_signal_t sig = lower_get_vcode_obj(decl); if (sig == VCODE_INVALID_SIGNAL) { vcode_state_t state; vcode_state_save(&state); @@ -1433,8 +1484,21 @@ static vcode_reg_t lower_var_ref(tree_t decl, expr_ctx_t ctx) type_t type = tree_type(decl); vcode_var_t var = lower_get_var(decl); - if (var == VCODE_INVALID_VAR) - return lower_protected_var(decl); + if (var == VCODE_INVALID_VAR) { + if (mode == LOWER_THUNK) { + emit_comment("Cannot resolve variable %s", istr(tree_ident(decl))); + vcode_type_t vtype = lower_type(type); + const vtype_kind_t vtkind = vtype_kind(vtype); + if (vtkind == VCODE_TYPE_CARRAY) + return emit_undefined(vtype_pointer(vtype_elem(vtype))); + else if (ctx == EXPR_LVALUE || vtkind == VCODE_TYPE_RECORD) + return emit_undefined(vtype_pointer(vtype)); + else + return emit_undefined(vtype); + } + else + return lower_protected_var(decl); + } else if (type_is_array(type) && lower_const_bounds(type)) return emit_index(var, VCODE_INVALID_REG); else if (type_is_record(type) || type_is_protected(type)) @@ -1469,10 +1533,21 @@ static vcode_reg_t lower_signal_ref(tree_t decl, expr_ctx_t ctx) static vcode_reg_t lower_param_ref(tree_t decl, expr_ctx_t ctx) { - vcode_reg_t reg = tree_attr_int(decl, vcode_obj_i, VCODE_INVALID_REG); - if (reg == VCODE_INVALID_REG && tree_class(decl) != C_SIGNAL) { - vcode_dump(); - fatal_trace("missing register for parameter %s", istr(tree_ident(decl))); + vcode_reg_t reg = lower_get_vcode_obj(decl); + if (reg == VCODE_INVALID_REG) { + if (mode == LOWER_THUNK) { + emit_comment("Cannot resolve reference to %s", istr(tree_ident(decl))); + return emit_undefined(lower_type(tree_type(decl))); + } + else if (tree_class(decl) != C_SIGNAL) { + vcode_dump(); + fatal_trace("missing register for parameter %s", + istr(tree_ident(decl))); + } + } + else if (mode == LOWER_THUNK) { + if (tree_class(decl) == C_SIGNAL || type_is_protected(tree_type(decl))) + return emit_undefined(vcode_reg_type(reg)); } const int depth = tree_attr_int(decl, nested_i, 0); @@ -1540,6 +1615,9 @@ static vcode_reg_t lower_ref(tree_t ref, expr_ctx_t ctx) else return lower_var_ref(decl, ctx); + case T_UNIT_DECL: + return lower_expr(tree_value(decl), ctx); + case T_ALIAS: return lower_alias_ref(decl, ctx); @@ -1639,9 +1717,9 @@ static void lower_check_array_bounds(type_t type, int dim, vcode_reg_t array, emit_const(kind_type, BOUNDS_ARRAY_DOWNTO), emit_const(kind_type, BOUNDS_ARRAY_TO)); - const int index = tree_index(where); + const vcode_bookmark_t index = lower_bookmark(where); emit_dynamic_bounds(value, min_reg, max_reg, kind_reg, - index, (hint == NULL ? index : tree_index(hint))); + index, (hint == NULL ? index : lower_bookmark(hint))); } static vcode_reg_t lower_array_ref_offset(tree_t ref, vcode_reg_t array) @@ -1705,6 +1783,12 @@ static vcode_reg_t lower_array_ref(tree_t ref, expr_ctx_t ctx) return array; const vtype_kind_t vtkind = vtype_kind(vcode_reg_type(array)); + if (!(vtkind == VCODE_TYPE_POINTER || vtkind == VCODE_TYPE_UARRAY + || vtkind == VCODE_TYPE_SIGNAL)) { + vcode_dump(); + fmt_loc(stdout, tree_loc(ref)); + printf("vtkind =%d\n", vtkind); + } assert(vtkind == VCODE_TYPE_POINTER || vtkind == VCODE_TYPE_UARRAY || vtkind == VCODE_TYPE_SIGNAL); @@ -2414,7 +2498,7 @@ static vcode_reg_t lower_new(tree_t expr, expr_ctx_t ctx) static vcode_reg_t lower_all(tree_t all, expr_ctx_t ctx) { vcode_reg_t access_reg = lower_reify_expr(tree_value(all)); - emit_null_check(access_reg, tree_index(all)); + emit_null_check(access_reg, lower_bookmark(all)); vcode_reg_t all_reg = emit_all(access_reg); type_t type = tree_type(all); @@ -2579,7 +2663,8 @@ static vcode_reg_t lower_attr_ref(tree_t expr, expr_ctx_t ctx) { tree_t value = tree_value(tree_param(expr, 0)); tmp_alloc_used = true; - return emit_image(lower_param(value, NULL, PORT_IN), tree_index(name)); + return emit_image(lower_param(value, NULL, PORT_IN), + lower_bookmark(name)); } case ATTR_VALUE: @@ -2589,7 +2674,7 @@ static vcode_reg_t lower_attr_ref(tree_t expr, expr_ctx_t ctx) vcode_reg_t arg = lower_param(value, NULL, PORT_IN); vcode_reg_t reg = emit_value(lower_array_data(arg), lower_array_len(tree_type(value), 0, arg), - tree_index(expr)); + lower_bookmark(expr)); lower_check_scalar_bounds(reg, name_type, expr, NULL); return emit_cast(lower_type(name_type), lower_bounds(name_type), reg); } @@ -2654,6 +2739,11 @@ static vcode_reg_t lower_attr_ref(tree_t expr, expr_ctx_t ctx) } } +static vcode_reg_t lower_qualified(tree_t expr, expr_ctx_t ctx) +{ + return lower_expr(tree_value(expr), ctx); +} + static vcode_reg_t lower_expr(tree_t expr, expr_ctx_t ctx) { switch (tree_kind(expr)) { @@ -2681,6 +2771,8 @@ static vcode_reg_t lower_expr(tree_t expr, expr_ctx_t ctx) return lower_type_conv(expr, ctx); case T_ATTR_REF: return lower_attr_ref(expr, ctx); + case T_QUALIFIED: + return lower_qualified(expr, ctx); default: fatal_at(tree_loc(expr), "cannot lower expression kind %s", tree_kind_str(tree_kind(expr))); @@ -2702,9 +2794,14 @@ static void lower_assert(tree_t stmt) vcode_reg_t severity = lower_reify_expr(tree_severity(stmt)); vcode_reg_t value = VCODE_INVALID_REG; - if (!is_report) + if (!is_report) { value = lower_reify_expr(tree_value(stmt)); + int64_t value_const; + if (vcode_reg_const(value, &value_const) && value_const) + return; + } + vcode_block_t message_bb = VCODE_INVALID_BLOCK; vcode_block_t exit_bb = VCODE_INVALID_BLOCK; @@ -2731,9 +2828,9 @@ static void lower_assert(tree_t stmt) } if (is_report) - emit_report(message, length, severity, tree_index(stmt)); + emit_report(message, length, severity, lower_bookmark(stmt)); else - emit_assert(value, message, length, severity, tree_index(stmt)); + emit_assert(value, message, length, severity, lower_bookmark(stmt)); if (saved_heap != VCODE_INVALID_REG) lower_cleanup_temp_objects(saved_heap); @@ -2762,28 +2859,24 @@ static bool lower_signal_sequential_nets(tree_t decl) static void lower_sched_event(tree_t on, bool is_static) { - tree_kind_t expr_kind = tree_kind(on); - if (expr_kind != T_REF && expr_kind != T_ARRAY_REF - && expr_kind != T_ARRAY_SLICE) { - // It is possible for constant folding to replace a signal with - // a constant which will then appear in a sensitivity list so - // just ignore it - return; - } - - tree_t decl = NULL; - switch (expr_kind) { - case T_REF: - decl = tree_ref(on); - break; + tree_t ref = on, decl = NULL; + while (decl == NULL) { + switch (tree_kind(ref)) { + case T_REF: + decl = tree_ref(ref); + break; - case T_ARRAY_REF: - case T_ARRAY_SLICE: - decl = tree_ref(tree_value(on)); - break; + case T_ARRAY_REF: + case T_ARRAY_SLICE: + ref = tree_value(ref); + break; - default: - assert(false); + default: + // It is possible for constant folding to replace a signal with + // a constant which will then appear in a sensitivity list so + // just ignore it + return; + } } tree_kind_t kind = tree_kind(decl); @@ -2805,7 +2898,7 @@ static void lower_sched_event(tree_t on, bool is_static) vcode_reg_t n_elems = VCODE_INVALID_REG, nets = VCODE_INVALID_REG; bool sequential = false; - if (expr_kind == T_REF) { + if (tree_kind(on) == T_REF) { switch (tree_kind(decl)) { case T_SIGNAL_DECL: nets = lower_signal_ref(decl, EXPR_LVALUE); @@ -2947,7 +3040,7 @@ static void lower_check_array_sizes(tree_t t, type_t ltype, type_t rtype, vcode_reg_t llen_reg = lower_array_len(ltype, 0, lval); vcode_reg_t rlen_reg = lower_array_len(rtype, 0, rval); - emit_array_size(llen_reg, rlen_reg, tree_index(t)); + emit_array_size(llen_reg, rlen_reg, lower_bookmark(t)); } static void lower_find_matching_refs(tree_t ref, void *context) @@ -3143,6 +3236,23 @@ static void lower_if(tree_t stmt, loop_stack_t *loops) vcode_reg_t test = lower_test_expr(tree_value(stmt)); const int nelses = tree_else_stmts(stmt); + const int nstmts = tree_stmts(stmt); + + int64_t cval; + if (vcode_reg_const(test, &cval)) { + emit_comment("Condition of if statement line %d is always %s", + tree_loc(stmt)->first_line, cval ? "true" : "false"); + if (cval) { + for (int i = 0; i < nstmts; i++) + lower_stmt(tree_stmt(stmt, i), loops); + } + else { + for (int i = 0; i < nelses; i++) + lower_stmt(tree_else_stmt(stmt, i), loops); + } + + return; + } vcode_block_t btrue = emit_block(); vcode_block_t bfalse = nelses > 0 ? emit_block() : VCODE_INVALID_BLOCK; @@ -3152,7 +3262,6 @@ static void lower_if(tree_t stmt, loop_stack_t *loops) vcode_select_block(btrue); - const int nstmts = tree_stmts(stmt); for (int i = 0; i < nstmts; i++) lower_stmt(tree_stmt(stmt, i), loops); @@ -3301,7 +3410,7 @@ static void lower_for(tree_t stmt, loop_stack_t *loops) tree_set_ident(idecl, ident); vcode_var_t ivar = emit_var(vtype, bounds, ident, false); - tree_add_attr_int(idecl, vcode_obj_i, ivar); + lower_put_vcode_obj(idecl, ivar); vcode_reg_t init_reg = r.kind == RANGE_RDYN ? right_reg : left_reg; emit_store(init_reg, ivar); @@ -3813,7 +3922,7 @@ static void lower_check_indexes(type_t type, vcode_reg_t array, tree_t hint) if (type_is_enum(index)) emit_index_check(left_reg, right_reg, vbounds, - BOUNDS_INDEX_TO, tree_index(hint)); + BOUNDS_INDEX_TO, lower_bookmark(hint)); else { range_t rindex = type_dim(index, 0); bounds_kind_t bkind = rindex.kind == RANGE_TO @@ -3833,7 +3942,7 @@ static void lower_check_indexes(type_t type, vcode_reg_t array, tree_t hint) if (lower_is_const(rindex.left) && lower_is_const(rindex.right)) { emit_index_check(rlow_reg, rhigh_reg, vbounds, - bkind, tree_index(hint)); + bkind, lower_bookmark(hint)); } else { vcode_reg_t bleft = lower_reify_expr(rindex.left); @@ -3843,7 +3952,7 @@ static void lower_check_indexes(type_t type, vcode_reg_t array, tree_t hint) vcode_reg_t bmax = bkind == BOUNDS_INDEX_TO ? bright : bleft; emit_dynamic_index_check(rlow_reg, rhigh_reg, bmin, bmax, - bkind, tree_index(hint)); + bkind, lower_bookmark(hint)); } } } @@ -3897,7 +4006,7 @@ static void lower_var_decl(tree_t decl) vcode_type_t vbounds = lower_bounds(type); vcode_var_t var = emit_var(vtype, vbounds, tree_ident(decl), tree_kind(decl) == T_CONST_DECL); - tree_add_attr_int(decl, vcode_obj_i, var); + lower_put_vcode_obj(decl, var); if (type_is_protected(type)) lower_protected_init(decl, emit_index(var, VCODE_INVALID_REG)); @@ -3994,7 +4103,7 @@ static void lower_signal_decl(tree_t decl) vcode_type_t stype = vtype_signal(ltype); vcode_signal_t sig = emit_signal(stype, bounds, name, shadow, nets, nnets); - tree_add_attr_int(decl, vcode_obj_i, sig); + lower_put_vcode_obj(decl, sig); if (nnets == 0) return; @@ -4025,7 +4134,7 @@ static void lower_signal_decl(tree_t decl) rtype = lower_type(rbase); } - emit_set_initial(sig, init_reg, tree_index(decl), rfunc, rtype); + emit_set_initial(sig, init_reg, lower_bookmark(decl), rfunc, rtype); } // Identify signals which potentially need 'LAST_VALUE @@ -4041,7 +4150,7 @@ static void lower_file_decl(tree_t decl) type_t type = tree_type(decl); vcode_type_t vtype = lower_type(type); vcode_var_t var = emit_var(vtype, vtype, tree_ident(decl), false); - tree_add_attr_int(decl, vcode_obj_i, var); + lower_put_vcode_obj(decl, var); emit_store(emit_null(vtype), var); @@ -4109,22 +4218,6 @@ static void lower_finished(void) } } -static void lower_cleanup(tree_t scope) -{ - if (tree_kind(scope) == T_FUNC_BODY) { - const int nports = tree_ports(scope); - for (int i = 0; i < nports; i++) - tree_remove_attr(tree_port(scope, i), vcode_obj_i); - } - - const int ndecls = tree_decls(scope); - for (int i = 0; i < ndecls; i++) { - tree_t decl = tree_decl(scope, i); - if (tree_kind(decl) != T_USE) - tree_remove_attr(decl, vcode_obj_i); - } -} - static void lower_protected_body(tree_t body) { int nvars = 0; @@ -4149,9 +4242,8 @@ static void lower_protected_body(tree_t body) static void lower_decls(tree_t scope, vcode_unit_t context) { const tree_kind_t scope_kind = tree_kind(scope); - const bool nested = - scope_kind != T_ELAB && scope_kind != T_PACK_BODY - && scope_kind != T_PROT_BODY; + const bool nested = scope_kind == T_FUNC_BODY || scope_kind == T_PROC_BODY + || scope_kind == T_PROCESS; const int nest_depth = tree_attr_int(scope, nested_i, 0); @@ -4163,7 +4255,11 @@ static void lower_decls(tree_t scope, vcode_unit_t context) for (int i = 0; i < ndecls; i++) { tree_t d = tree_decl(scope, i); const tree_kind_t kind = tree_kind(d); - if (kind == T_FUNC_BODY || kind == T_PROC_BODY || kind == T_PROT_BODY) { + if (mode == LOWER_THUNK + && (kind == T_SIGNAL_DECL || kind == T_PROT_BODY)) + continue; + else if (kind == T_FUNC_BODY || kind == T_PROC_BODY + || kind == T_PROT_BODY) { if (nested) { tree_add_attr_int(d, nested_i, nest_depth + 1); lower_mangle_func(d, context); @@ -4181,7 +4277,7 @@ static void lower_decls(tree_t scope, vcode_unit_t context) vcode_block_t bb = vcode_active_block(); - if (kind != T_PROT_BODY && tree_has_code(d)) + if (kind == T_PROT_BODY && mode == LOWER_THUNK) continue; switch (kind) { @@ -4265,12 +4361,28 @@ static void lower_subprogram_ports(tree_t body, bool has_subprograms) } vcode_reg_t reg = emit_param(vtype, vbounds, tree_ident(p)); - tree_add_attr_int(p, vcode_obj_i, reg); + lower_put_vcode_obj(p, reg); if (has_subprograms) tree_add_attr_int(p, nested_i, vcode_unit_depth()); } } +static bool lower_have_subprogram(ident_t name, vcode_unit_t context) +{ + vcode_unit_t vu = vcode_find_unit(name); + if (vu == NULL) + return false; + + vcode_state_t state; + vcode_state_save(&state); + vcode_select_unit(vu); + + const bool same_context = vcode_unit_context() == context; + + vcode_state_restore(&state); + return same_context; +} + static void lower_proc_body(tree_t body, vcode_unit_t context) { const bool never_waits = @@ -4279,6 +4391,10 @@ static void lower_proc_body(tree_t body, vcode_unit_t context) vcode_select_unit(context); ident_t name = lower_mangle_func(body, context); + if (lower_have_subprogram(name, context)) + return; + + tree_add_attr_str(body, mangled_i, name); vcode_unit_t vu; if (never_waits) @@ -4300,8 +4416,8 @@ static void lower_proc_body(tree_t body, vcode_unit_t context) lower_finished(); - tree_set_code(body, vu); - lower_cleanup(body); + if (vcode_unit_has_undefined()) + vcode_unit_unref(vu); } static void lower_func_body(tree_t body, vcode_unit_t context) @@ -4311,6 +4427,10 @@ static void lower_func_body(tree_t body, vcode_unit_t context) vcode_type_t vtype = lower_func_result_type(body); ident_t name = lower_mangle_func(body, context); + if (lower_have_subprogram(name, context)) + return; + + tree_add_attr_str(body, mangled_i, name); vcode_unit_t vu = emit_function(name, context, vtype); const bool has_subprograms = lower_has_subprograms(body); @@ -4324,10 +4444,8 @@ static void lower_func_body(tree_t body, vcode_unit_t context) lower_finished(); - assert(!tree_has_code(body)); - tree_set_code(body, vu); - - lower_cleanup(body); + if (vcode_unit_has_undefined()) + vcode_unit_unref(vu); } static bool lower_driver_nets(tree_t t, tree_t *decl, @@ -4543,17 +4661,11 @@ static void lower_process(tree_t proc, vcode_unit_t context) emit_return(VCODE_INVALID_REG); lower_finished(); - - assert(!tree_has_code(proc)); - tree_set_code(proc, vu); - - lower_cleanup(proc); } -static void lower_elab(tree_t unit) +static vcode_unit_t lower_elab(tree_t unit) { vcode_unit_t context = emit_context(tree_ident(unit)); - tree_set_code(unit, context); lower_decls(unit, context); @@ -4568,53 +4680,81 @@ static void lower_elab(tree_t unit) lower_process(s, context); } - lower_cleanup(unit); + return context; } -static void lower_pack_body(tree_t unit) +static vcode_unit_t lower_pack_body(tree_t unit) { vcode_unit_t context = emit_context(tree_ident(unit)); - tree_set_code(unit, context); lower_decls(unit, context); emit_return(VCODE_INVALID_REG); lower_finished(); + return context; +} + +static vcode_unit_t lower_package(tree_t unit) +{ + vcode_unit_t context = emit_context(tree_ident(unit)); + + lower_decls(unit, context); + + emit_return(VCODE_INVALID_REG); - lower_cleanup(unit); + lower_finished(); + return context; } -static void lower_package(tree_t unit) +static vcode_unit_t lower_arch(tree_t unit) { vcode_unit_t context = emit_context(tree_ident(unit)); - tree_set_code(unit, context); lower_decls(unit, context); emit_return(VCODE_INVALID_REG); lower_finished(); - lower_cleanup(unit); + return context; } -void lower_unit(tree_t unit) +static void lower_set_verbose(void) { - const char *venv = getenv("NVC_LOWER_VERBOSE"); - if (venv != NULL) - verbose = isalpha((int)venv[0]) || venv[0] == ':' ? venv : ""; - else - verbose = opt_get_str("dump-vcode"); + static bool set = false; + if (!set) { + const char *venv = getenv("NVC_LOWER_VERBOSE"); + if (venv != NULL) + verbose = isalpha((int)venv[0]) || venv[0] == ':' ? venv : ""; + else + verbose = opt_get_str("dump-vcode"); + } +} + +vcode_unit_t lower_unit(tree_t unit) +{ + lower_set_verbose(); + vcode_objs = hash_new(4096, true); + mode = LOWER_NORMAL; + tmp_alloc_used = false; + + vcode_unit_t context = NULL; switch (tree_kind(unit)) { case T_ELAB: - lower_elab(unit); + context = lower_elab(unit); break; case T_PACK_BODY: - lower_pack_body(unit); + context = lower_pack_body(unit); break; case T_PACKAGE: - lower_package(unit); + context = lower_package(unit); + break; + case T_ENTITY: + break; + case T_ARCH: + mode = LOWER_THUNK; + context = lower_arch(unit); break; default: fatal("cannot lower unit kind %s to vcode", @@ -4622,4 +4762,37 @@ void lower_unit(tree_t unit) } vcode_close(); + + hash_free(vcode_objs); + vcode_objs = NULL; + + return context; +} + +vcode_unit_t lower_thunk(tree_t fcall) +{ + lower_set_verbose(); + + vcode_unit_t context = emit_context(ident_new("thunk")); + + vcode_select_unit(context); + mode = LOWER_THUNK; + tmp_alloc_used = false; + + vcode_type_t vtype = lower_type(tree_type(fcall)); + vcode_unit_t thunk = emit_thunk(tree_ident(fcall), context, vtype); + + vcode_reg_t result_reg = lower_expr(fcall, EXPR_RVALUE); + emit_return(emit_cast(vtype, vtype, result_reg)); + + lower_finished(); + vcode_unit_unref(context); + + if (vcode_unit_has_undefined()) { + vcode_unit_unref(thunk); + return NULL; + } + + vcode_close(); + return thunk; } diff --git a/src/nvc.c b/src/nvc.c index 627b2ac7..b289f6dc 100644 --- a/src/nvc.c +++ b/src/nvc.c @@ -18,6 +18,7 @@ #include "util.h" #include "phase.h" #include "common.h" +#include "vcode.h" #include "rt/rt.h" #include @@ -167,22 +168,23 @@ static int analyse(int argc, char **argv) if (parse_errors() + sem_errors() + bounds_errors() > 0) return EXIT_FAILURE; - for (int i = 0; i < n_units; i++) { - tree_kind_t kind = tree_kind(units[i]); - const bool need_cgen = - (kind == T_PACK_BODY) - || ((kind == T_PACKAGE) && pack_needs_cgen(units[i])); - if (!need_cgen) - units[i] = NULL; - } - lib_save(lib_work()); for (int i = 0; i < n_units; i++) { - if (units[i] != NULL) { - lower_unit(units[i]); - cgen(units[i]); + vcode_unit_t vu = lower_unit(units[i]); + + if (tree_kind(units[i]) != T_ENTITY) { // TODO: entity should have code + char *name LOCAL = xasprintf("_%s.vcode", istr(tree_ident(units[i]))); + fbuf_t *fbuf = lib_fbuf_open(lib_work(), name, FBUF_OUT); + vcode_write(vu, fbuf); + fbuf_close(fbuf); } + + const tree_kind_t kind = tree_kind(units[i]); + const bool need_cgen = kind == T_PACK_BODY + || (kind == T_PACKAGE && pack_needs_cgen(units[i])); + if (need_cgen) + cgen(units[i], vu); } argc -= next_cmd - 1; @@ -310,10 +312,10 @@ static int elaborate(int argc, char **argv) lib_save(lib_work()); elab_verbose(verbose, "saving library"); - lower_unit(e); + vcode_unit_t vu = lower_unit(e); elab_verbose(verbose, "generating intermediate code"); - cgen(e); + cgen(e, vu); elab_verbose(verbose, "generating LLVM"); link_bc(e); diff --git a/src/object.c b/src/object.c index 61a3dbc8..d0a95929 100644 --- a/src/object.c +++ b/src/object.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2014-2015 Nick Gasson +// Copyright (C) 2014-2016 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 @@ -37,7 +37,7 @@ static const char *item_text_map[] = { "I_DVAL", "I_SPEC", "I_OPS", "I_CONSTR", "I_BASE", "I_ELEM", "I_FILE", "I_ACCESS", "I_RESOLUTION", "I_RESULT", "I_UNITS", "I_LITERALS", "I_DIMS", "I_FIELDS", "I_TEXT_BUF", - "I_ATTRS", "I_PTYPES", "I_CHARS", "I_CODE", "I_FLAGS" + "I_ATTRS", "I_PTYPES", "I_CHARS", "I_FLAGS" }; static object_class_t *classes[4]; @@ -77,6 +77,12 @@ uint32_t object_index(const object_t *object) return object->index; } +bool object_has_index(const object_t *object) +{ + assert(object != NULL); + return object->index != UINT32_MAX; +} + void object_change_kind(const object_class_t *class, object_t *object, int kind) { if (kind == object->kind) @@ -378,8 +384,6 @@ void object_visit(object_t *object, object_visit_ctx_t *ctx) } } } - else if (ITEM_CODE & mask) - ; else item_without_type(mask); } @@ -468,8 +472,6 @@ object_t *object_rewrite(object_t *object, object_rewrite_ctx_t *ctx) } else if (ITEM_TEXT_BUF & mask) ; - else if (ITEM_CODE & mask) - ; else item_without_type(mask); } @@ -620,11 +622,8 @@ void object_write(object_t *object, object_wr_ctx_t *ctx) for (unsigned i = 0; i < a->count; i++) write_u32(a->items[i], ctx->file); } - else if (ITEM_DOUBLE & mask) { - union { double d; uint64_t i; } u; - u.d = object->items[n].dval; - write_u64(u.i, ctx->file); - } + else if (ITEM_DOUBLE & mask) + write_double(object->items[n].dval, ctx->file); else if (ITEM_ATTRS & mask) { const attr_tab_t *attrs = &(object->items[n].attrs); write_u16(attrs->num, ctx->file); @@ -661,8 +660,6 @@ void object_write(object_t *object, object_wr_ctx_t *ctx) } else if (ITEM_TEXT_BUF & mask) ; - else if (ITEM_CODE & mask) - ; else item_without_type(mask); n++; @@ -774,13 +771,13 @@ object_t *object_read(object_rd_ctx_t *ctx, int tag) object->items[n].tree = (tree_t)object_read(ctx, OBJECT_TAG_TYPE); else if (ITEM_TREE_ARRAY & mask) { tree_array_t *a = &(object->items[n].tree_array); - tree_array_resize(a, read_u32(ctx->file), NULL); + tree_array_resize(a, read_u32(ctx->file), 0); for (unsigned i = 0; i < a->count; i++) a->items[i] = (tree_t)object_read(ctx, OBJECT_TAG_TREE); } else if (ITEM_TYPE_ARRAY & mask) { type_array_t *a = &(object->items[n].type_array); - type_array_resize(a, read_u16(ctx->file), NULL); + type_array_resize(a, read_u16(ctx->file), 0); for (unsigned i = 0; i < a->count; i++) a->items[i] = (type_t)object_read(ctx, OBJECT_TAG_TYPE); } @@ -801,8 +798,7 @@ object_t *object_read(object_rd_ctx_t *ctx, int tag) } else if (ITEM_RANGE_ARRAY & mask) { range_array_t *a = &(object->items[n].range_array); - range_t dummy = { NULL, NULL, 0 }; - range_array_resize(a, read_u16(ctx->file), dummy); + range_array_resize(a, read_u16(ctx->file), 0); for (unsigned i = 0; i < a->count; i++) { a->items[i].kind = read_u8(ctx->file); @@ -816,15 +812,12 @@ object_t *object_read(object_rd_ctx_t *ctx, int tag) ; else if (ITEM_NETID_ARRAY & mask) { netid_array_t *a = &(object->items[n].netid_array); - netid_array_resize(a, read_u32(ctx->file), NETID_INVALID); + netid_array_resize(a, read_u32(ctx->file), 0xff); for (unsigned i = 0; i < a->count; i++) a->items[i] = read_u32(ctx->file); } - else if (ITEM_DOUBLE & mask) { - union { uint64_t i; double d; } u; - u.i = read_u64(ctx->file); - object->items[n].dval = u.d; - } + else if (ITEM_DOUBLE & mask) + object->items[n].dval = read_double(ctx->file); else if (ITEM_ATTRS & mask) { attr_tab_t *attrs = &(object->items[n].attrs); @@ -857,8 +850,6 @@ object_t *object_read(object_rd_ctx_t *ctx, int tag) } } } - else if (ITEM_CODE & mask) - ; else item_without_type(mask); n++; @@ -1031,8 +1022,6 @@ bool object_copy_mark(object_t *object, object_copy_ctx_t *ctx) ; else if (ITEM_TEXT_BUF & mask) ; - else if (ITEM_CODE & mask) - ; else item_without_type(mask); n++; @@ -1090,7 +1079,7 @@ object_t *object_copy_sweep(object_t *object, object_copy_ctx_t *ctx) const tree_array_t *from = &(object->items[n].tree_array); tree_array_t *to = &(copy->items[n].tree_array); - tree_array_resize(to, from->count, NULL); + tree_array_resize(to, from->count, 0); for (size_t i = 0; i < from->count; i++) to->items[i] = (tree_t) @@ -1116,7 +1105,7 @@ object_t *object_copy_sweep(object_t *object, object_copy_ctx_t *ctx) const netid_array_t *from = &(object->items[n].netid_array); netid_array_t *to = &(copy->items[n].netid_array); - netid_array_resize(to, from->count, NETID_INVALID); + netid_array_resize(to, from->count, 0xff); for (unsigned i = 0; i < from->count; i++) to->items[i] = from->items[i]; @@ -1134,9 +1123,7 @@ object_t *object_copy_sweep(object_t *object, object_copy_ctx_t *ctx) else if (ITEM_RANGE_ARRAY & mask) { const range_array_t *from = &(object->items[n].range_array); range_array_t *to = &(copy->items[n].range_array); - - range_t dummy; - range_array_resize(to, from->count, dummy); + range_array_resize(to, from->count, 0); for (unsigned i = 0; i < from->count; i++) { to->items[i].kind = from->items[i].kind; @@ -1150,7 +1137,7 @@ object_t *object_copy_sweep(object_t *object, object_copy_ctx_t *ctx) const type_array_t *from = &(object->items[n].type_array); type_array_t *to = &(object->items[n].type_array); - type_array_resize(to, from->count, NULL); + type_array_resize(to, from->count, 0); for (unsigned i = 0; i < from->count; i++) to->items[i] = (type_t) @@ -1158,8 +1145,6 @@ object_t *object_copy_sweep(object_t *object, object_copy_ctx_t *ctx) } else if (ITEM_TEXT_BUF & mask) ; - else if (ITEM_CODE & mask) - copy->items[n].code = NULL; else item_without_type(mask); n++; @@ -1184,7 +1169,7 @@ void object_replace(object_t *t, object_t *a) const type_array_t *from = &(a->items[n].type_array); type_array_t *to = &(t->items[n].type_array); - type_array_resize(to, from->count, NULL); + type_array_resize(to, from->count, 0); for (unsigned i = 0; i < from->count; i++) to->items[i] = from->items[i]; @@ -1197,7 +1182,7 @@ void object_replace(object_t *t, object_t *a) const tree_array_t *from = &(a->items[n].tree_array); tree_array_t *to = &(t->items[n].tree_array); - tree_array_resize(to, from->count, NULL); + tree_array_resize(to, from->count, 0); for (size_t i = 0; i < from->count; i++) to->items[i] = from->items[i]; @@ -1206,8 +1191,7 @@ void object_replace(object_t *t, object_t *a) const range_array_t *from = &(a->items[n].range_array); range_array_t *to = &(t->items[n].range_array); - range_t dummy; - range_array_resize(to, from->count, dummy); + range_array_resize(to, from->count, 0); for (unsigned i = 0; i < from->count; i++) to->items[i] = from->items[i]; diff --git a/src/object.h b/src/object.h index d1a43363..982607ca 100644 --- a/src/object.h +++ b/src/object.h @@ -81,7 +81,7 @@ typedef uint64_t imask_t; #define I_ATTRS ONE_HOT(45) #define I_PTYPES ONE_HOT(46) #define I_CHARS ONE_HOT(47) -#define I_CODE ONE_HOT(48) +// Unused ONE_HOT(48) #define I_FLAGS ONE_HOT(49) #define ITEM_IDENT (I_IDENT | I_IDENT2) @@ -103,7 +103,6 @@ typedef uint64_t imask_t; #define ITEM_RANGE_ARRAY (I_DIMS) #define ITEM_TEXT_BUF (I_TEXT_BUF) #define ITEM_ATTRS (I_ATTRS) -#define ITEM_CODE (I_CODE) #define OBJECT_TAG_TREE 0 #define OBJECT_TAG_TYPE 1 @@ -172,7 +171,6 @@ typedef union { type_array_t type_array; attr_tab_t attrs; ident_array_t ident_array; - vcode_unit_t code; } item_t; typedef struct { @@ -252,6 +250,7 @@ void object_lookup_failed(const char *name, const char **kind_text_map, void item_without_type(imask_t mask); uint32_t object_index(const object_t *object); +bool object_has_index(const object_t *object); void object_change_kind(const object_class_t *class, object_t *object, int kind); object_t *object_new(const object_class_t *class, int kind); diff --git a/src/phase.h b/src/phase.h index 66648440..e5c17261 100644 --- a/src/phase.h +++ b/src/phase.h @@ -1,5 +1,5 @@ // -// Copyright (C) 2011-2015 Nick Gasson +// Copyright (C) 2011-2016 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 @@ -20,6 +20,15 @@ #include "tree.h" +typedef enum { + EVAL_BOUNDS = (1 << 0), + EVAL_FCALL = (1 << 1), + EVAL_WARN = (1 << 2), + EVAL_VERBOSE = (1 << 4), + EVAL_REPORT = (1 << 5), + EVAL_FOLDING = (1 << 6) +} eval_flags_t; + // Annotate types and perform other semantics checks on a tree. // Returns false on error. bool sem_check(tree_t t); @@ -28,6 +37,12 @@ bool sem_check(tree_t t); int sem_errors(void); // Fold all constant expressions +void fold(tree_t top); + +// The number of errors found while constant folding +int eval_errors(void); + +// Rewrite to simpler forms void simplify(tree_t top); // Perform static bounds checking @@ -37,7 +52,7 @@ void bounds_check(tree_t top); int bounds_errors(void); // Evaluate a function call at compile time -tree_t eval(tree_t fcall); +tree_t eval(tree_t fcall, eval_flags_t flags); // Elaborate a top level entity tree_t elab(tree_t top); @@ -45,8 +60,8 @@ tree_t elab(tree_t top); // Set the value of a top-level generic void elab_set_generic(const char *name, const char *value); -// Generate LLVM bitcode for an elaborated design -void cgen(tree_t top); +// Generate LLVM bitcode for a design unit +void cgen(tree_t top, vcode_unit_t vu); // Dump out a VHDL representation of the given unit void dump(tree_t top); @@ -80,6 +95,9 @@ tree_t parse(void); int parse_errors(void); // Generate vcode for a design unit -void lower_unit(tree_t unit); +vcode_unit_t lower_unit(tree_t unit); + +// Generate vcode for an isolated function call +vcode_unit_t lower_thunk(tree_t fcall); #endif // _PHASE_H diff --git a/src/prim.h b/src/prim.h index b2e5fe6b..78047c95 100644 --- a/src/prim.h +++ b/src/prim.h @@ -1,5 +1,5 @@ // -// Copyright (C) 2013 Nick Gasson +// Copyright (C) 2013-2016 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 @@ -19,6 +19,7 @@ #define _PRIM_H #include +#include typedef struct loc { unsigned first_line : 20; diff --git a/src/simp.c b/src/simp.c index c60cae15..0ca5350b 100644 --- a/src/simp.c +++ b/src/simp.c @@ -122,6 +122,7 @@ static tree_t simp_fcall(tree_t t, simp_ctx_t *ctx) ident_until(name, '.') == name || ident_runtil(name, '.') == ctx->prefix; + // XXX: is this still required? if (tree_kind(decl) == T_FUNC_DECL && !builtin && local) { // Try searching the current unit for the function body type_t type = tree_type(decl); @@ -135,7 +136,8 @@ static tree_t simp_fcall(tree_t t, simp_ctx_t *ctx) } } - return eval(simp_call_args(t)); + tree_t new = simp_call_args(t); + return builtin ? eval(new, 0) : new; } static tree_t simp_pcall(tree_t t) @@ -334,7 +336,7 @@ static tree_t simp_attr_ref(tree_t t, simp_ctx_t *ctx) case ATTR_HIGH: return make_ref(type_enum_literal(type, nlits - 1)); case ATTR_ASCENDING: - return get_bool_lit(t, true); + return get_enum_lit(t, true); default: fatal_trace("invalid enumeration attribute %d", predef); } @@ -377,7 +379,7 @@ static tree_t simp_attr_ref(tree_t t, simp_ctx_t *ctx) else if (predef == ATTR_RIGHT) return r.right; else if (predef == ATTR_ASCENDING && known_dir) - return get_bool_lit(t, (r.kind == RANGE_TO)); + return get_enum_lit(t, (r.kind == RANGE_TO)); else return t; } @@ -491,6 +493,9 @@ static tree_t simp_array_ref(tree_t t) switch (tree_kind(decl)) { case T_CONST_DECL: { + if (!tree_has_value(decl)) + return t; + tree_t v = tree_value(decl); if (tree_kind(v) != T_AGGREGATE) return t; @@ -856,6 +861,19 @@ static tree_t simp_context_ref(tree_t t, simp_ctx_t *ctx) return NULL; } +static tree_t simp_assert(tree_t t) +{ + bool value_b; + if (!tree_has_value(t)) + return t; + else if (folded_bool(tree_value(t), &value_b) && value_b) { + // Assertion always passes + return NULL; + } + else + return t; +} + static tree_t simp_tree(tree_t t, void *_ctx) { simp_ctx_t *ctx = _ctx; @@ -899,6 +917,8 @@ static tree_t simp_tree(tree_t t, void *_ctx) return simp_record_ref(t); case T_CTXREF: return simp_context_ref(t, ctx); + case T_ASSERT: + return simp_assert(t); default: return t; } diff --git a/src/tree.c b/src/tree.c index 494b6e49..badb4890 100644 --- a/src/tree.c +++ b/src/tree.c @@ -49,7 +49,7 @@ static const imask_t has_map[T_LAST_TREE_KIND] = { (I_IDENT | I_VALUE | I_TYPE | I_ATTRS | I_FLAGS), // T_PROCESS - (I_IDENT | I_DECLS | I_STMTS | I_TRIGGERS | I_ATTRS | I_CODE | I_FLAGS), + (I_IDENT | I_DECLS | I_STMTS | I_TRIGGERS | I_ATTRS | I_FLAGS), // T_REF (I_IDENT | I_TYPE | I_REF | I_ATTRS | I_FLAGS), @@ -64,7 +64,7 @@ static const imask_t has_map[T_LAST_TREE_KIND] = { (I_IDENT | I_VALUE | I_TARGET | I_ATTRS), // T_PACKAGE - (I_IDENT | I_DECLS | I_CONTEXT | I_ATTRS | I_CODE), + (I_IDENT | I_DECLS | I_CONTEXT | I_ATTRS), // T_SIGNAL_ASSIGN (I_IDENT | I_TARGET | I_WAVES | I_REJECT | I_ATTRS), @@ -82,7 +82,7 @@ static const imask_t has_map[T_LAST_TREE_KIND] = { (I_IDENT | I_VALUE | I_PORTS | I_TYPE | I_ATTRS | I_FLAGS), // T_ELAB - (I_IDENT | I_DECLS | I_STMTS | I_CONTEXT | I_ATTRS | I_CODE), + (I_IDENT | I_DECLS | I_STMTS | I_CONTEXT | I_ATTRS), // T_AGGREGATE (I_TYPE | I_ASSOCS | I_FLAGS), @@ -110,11 +110,10 @@ static const imask_t has_map[T_LAST_TREE_KIND] = { (I_IDENT), // T_PACK_BODY - (I_IDENT | I_DECLS | I_CONTEXT | I_ATTRS | I_CODE), + (I_IDENT | I_DECLS | I_CONTEXT | I_ATTRS), // T_FUNC_BODY - (I_IDENT | I_DECLS | I_STMTS | I_PORTS | I_TYPE | I_ATTRS | I_CODE - | I_FLAGS), + (I_IDENT | I_DECLS | I_STMTS | I_PORTS | I_TYPE | I_ATTRS | I_FLAGS), // T_RETURN (I_IDENT | I_VALUE | I_ATTRS), @@ -144,7 +143,7 @@ static const imask_t has_map[T_LAST_TREE_KIND] = { (I_IDENT | I_PORTS | I_TYPE | I_ATTRS), // T_PROC_BODY - (I_IDENT | I_DECLS | I_STMTS | I_PORTS | I_TYPE | I_ATTRS | I_CODE), + (I_IDENT | I_DECLS | I_STMTS | I_PORTS | I_TYPE | I_ATTRS), // T_EXIT (I_IDENT | I_VALUE | I_IDENT2 | I_ATTRS), @@ -786,23 +785,6 @@ void tree_set_ref(tree_t t, tree_t decl) lookup_item(&tree_object, t, I_REF)->tree = decl; } -vcode_unit_t tree_code(tree_t t) -{ - item_t *item = lookup_item(&tree_object, t, I_CODE); - assert(item->code != NULL); - return item->code; -} - -bool tree_has_code(tree_t t) -{ - return lookup_item(&tree_object, t, I_CODE)->code != NULL; -} - -void tree_set_code(tree_t t, vcode_unit_t code) -{ - lookup_item(&tree_object, t, I_CODE)->code = code; -} - tree_t tree_spec(tree_t t) { item_t *item = lookup_item(&tree_object, t, I_SPEC); @@ -884,7 +866,7 @@ void tree_change_net(tree_t t, unsigned n, netid_t i) item_t *item = lookup_item(&tree_object, t, I_NETS); if (n >= item->netid_array.count) - netid_array_resize(&(item->netid_array), n + 1, NETID_INVALID); + netid_array_resize(&(item->netid_array), n + 1, 0xff); item->netid_array.items[n] = i; } @@ -999,6 +981,11 @@ uint32_t tree_index(tree_t t) return object_index(&(t->object)); } +bool tree_has_index(tree_t t) +{ + return object_has_index(&(t->object)); +} + unsigned tree_visit(tree_t t, tree_visit_fn_t fn, void *context) { assert(t != NULL); diff --git a/src/tree.h b/src/tree.h index 904fea27..9c63df98 100644 --- a/src/tree.h +++ b/src/tree.h @@ -292,10 +292,6 @@ tree_t tree_spec(tree_t t); bool tree_has_spec(tree_t t); void tree_set_spec(tree_t t, tree_t s); -vcode_unit_t tree_code(tree_t t); -bool tree_has_code(tree_t t); -void tree_set_code(tree_t t, vcode_unit_t code); - unsigned tree_ops(tree_t t); tree_t tree_op(tree_t t, unsigned n); void tree_add_op(tree_t t, tree_t s); @@ -313,6 +309,7 @@ tree_flags_t tree_flags(tree_t t); void tree_set_flag(tree_t t, tree_flags_t mask); uint32_t tree_index(tree_t t); +bool tree_has_index(tree_t t); void tree_add_attr_str(tree_t t, ident_t name, ident_t str); ident_t tree_attr_str(tree_t t, ident_t name); diff --git a/src/type.c b/src/type.c index f46d26ad..2a2788dc 100644 --- a/src/type.c +++ b/src/type.c @@ -134,6 +134,11 @@ uint32_t type_index(type_t t) return object_index(&(t->object)); } +bool type_has_index(type_t t) +{ + return object_has_index(&(t->object)); +} + bool type_eq(type_t a, type_t b) { assert(a != NULL); @@ -761,3 +766,8 @@ unsigned type_width(type_t type) else return 1; } + +type_t type_read_recall(tree_rd_ctx_t ctx, uint32_t index) +{ + return (type_t)object_read_recall((object_rd_ctx_t *)ctx, index); +} diff --git a/src/type.h b/src/type.h index d8b2d714..574a58c3 100644 --- a/src/type.h +++ b/src/type.h @@ -50,6 +50,8 @@ type_kind_t type_kind(type_t t); const char *type_kind_str(type_kind_t t); uint32_t type_index(type_t t); +bool type_has_index(type_t t); +type_t type_read_recall(tree_rd_ctx_t ctx, uint32_t index); bool type_eq(type_t a, type_t b); diff --git a/src/util.c b/src/util.c index 18d80860..f436d3a5 100644 --- a/src/util.c +++ b/src/util.c @@ -417,19 +417,22 @@ void error_at(const loc_t *loc, const char *fmt, ...) va_end(ap); } -void warn_at(const loc_t *loc, const char *fmt, ...) +static void catch_in_unit_test(print_fn_t fn, const loc_t *loc, + const char *fmt, va_list ap) { - va_list ap; - va_start(ap, fmt); - - // Convert warnings to errors for unit tests if (opt_get_int("unit-test")) { char *strp LOCAL = prepare_msg(fmt, ap, error_force_plain); error_fn(strp, loc != NULL ? loc : &LOC_INVALID); } else - msg_at(warnf, loc, fmt, ap); + msg_at(fn, loc, fmt, ap); +} +void warn_at(const loc_t *loc, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + catch_in_unit_test(warnf, loc, fmt, ap); va_end(ap); } @@ -437,7 +440,7 @@ void note_at(const loc_t *loc, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - msg_at(notef, loc, fmt, ap); + catch_in_unit_test(notef, loc, fmt, ap); va_end(ap); } diff --git a/src/vcode.c b/src/vcode.c index 419dc17e..6142a22f 100644 --- a/src/vcode.c +++ b/src/vcode.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2014-2015 Nick Gasson +// Copyright (C) 2014-2016 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 @@ -18,37 +18,97 @@ #include "util.h" #include "vcode.h" #include "array.h" +#include "hash.h" +#include "tree.h" #include #include #include #include +#include DECLARE_AND_DEFINE_ARRAY(vcode_reg); DECLARE_AND_DEFINE_ARRAY(vcode_block); DECLARE_AND_DEFINE_ARRAY(vcode_type); +#define OP_HAS_TYPE(x) \ + (x == VCODE_OP_BOUNDS || x == VCODE_OP_ALLOCA || x == VCODE_OP_COPY \ + || x == VCODE_OP_SET_INITIAL || x == VCODE_OP_INDEX_CHECK \ + || x == VCODE_OP_CONST || x == VCODE_OP_CAST \ + || VCODE_OP_CONST_RECORD) +#define OP_HAS_ADDRESS(x) \ + (x == VCODE_OP_LOAD || x == VCODE_OP_STORE || x == VCODE_OP_INDEX \ + || x == VCODE_OP_RESOLVED_ADDRESS) +#define OP_HAS_SUBKIND(x) \ + (x == VCODE_OP_SCHED_EVENT || x == VCODE_OP_BOUNDS \ + || x == VCODE_OP_VEC_LOAD || x == VCODE_OP_BIT_VEC_OP \ + || x == VCODE_OP_INDEX_CHECK || x == VCODE_OP_BIT_SHIFT \ + || x == VCODE_OP_ALLOCA || x == VCODE_OP_COVER_COND) +#define OP_HAS_FUNC(x) \ + (x == VCODE_OP_FCALL || x == VCODE_OP_NESTED_FCALL \ + || x == VCODE_OP_PCALL || x == VCODE_OP_RESUME \ + || x == VCODE_OP_SET_INITIAL || x == VCODE_OP_NESTED_PCALL \ + || x == VCODE_OP_NESTED_RESUME) +#define OP_HAS_REAL(x) \ + (x == VCODE_OP_CONST_REAL) +#define OP_HAS_VALUE(x) \ + (x == VCODE_OP_CONST) +#define OP_HAS_SIGNAL(x) \ + (x == VCODE_OP_NETS || x == VCODE_OP_RESOLVED_ADDRESS \ + || x == VCODE_OP_SET_INITIAL || x == VCODE_OP_NEEDS_LAST_VALUE) +#define OP_HAS_DIM(x) \ + (x == VCODE_OP_UARRAY_LEFT || x == VCODE_OP_UARRAY_RIGHT \ + || x == VCODE_OP_UARRAY_DIR || x == VCODE_OP_UARRAY_LEN) +#define OP_HAS_HOPS(x) \ + (x == VCODE_OP_PARAM_UPREF || x == VCODE_OP_NESTED_FCALL \ + || x == VCODE_OP_NESTED_PCALL || x == VCODE_OP_NESTED_RESUME) +#define OP_HAS_FIELD(x) \ + (x == VCODE_OP_RECORD_REF) +#define OP_HAS_CMP(x) \ + (x == VCODE_OP_CMP) +#define OP_HAS_TAG(x) \ + (x == VCODE_OP_COVER_STMT || x == VCODE_OP_COVER_COND) +#define OP_HAS_COMMENT(x) \ + (x == VCODE_OP_COMMENT) +#define OP_HAS_BOOKMARK(x) \ + (x == VCODE_OP_ASSERT || x == VCODE_OP_REPORT \ + || x == VCODE_OP_IMAGE || x == VCODE_OP_SET_INITIAL \ + || x == VCODE_OP_DIV || x == VCODE_OP_NULL_CHECK \ + || x == VCODE_OP_VALUE || x == VCODE_OP_BOUNDS \ + || x == VCODE_OP_DYNAMIC_BOUNDS || x == VCODE_OP_ARRAY_SIZE \ + || x == VCODE_OP_INDEX_CHECK) +#define OP_HAS_HINT(x) \ + (x == VCODE_OP_BOUNDS || x == VCODE_OP_DYNAMIC_BOUNDS) +#define OP_HAS_TARGET(x) \ + (x == VCODE_OP_WAIT || x == VCODE_OP_JUMP || x == VCODE_OP_COND \ + || x == VCODE_OP_PCALL || x == VCODE_OP_CASE \ + || x == VCODE_OP_NESTED_PCALL) + typedef struct { vcode_op_t kind; vcode_reg_array_t args; vcode_reg_t result; - vcode_type_t type; - vcode_block_array_t targets; - vcode_var_t address; - uint32_t index; - ident_t func; - unsigned subkind; + vcode_type_t type; // OP_HAS_TYPE + union { + ident_t func; // OP_HAS_FUNC + vcode_var_t address; // OP_HAS_ADDRESS + unsigned subkind; // OP_HAS_SUBKIND + }; + union { + vcode_bookmark_t bookmark; // OP_HAS_BOOKMARK + vcode_block_array_t targets; // OP_HAS_TARGET + }; union { - vcode_cmp_t cmp; - int64_t value; - double real; - char *comment; - vcode_signal_t signal; - unsigned dim; - unsigned hops; - unsigned field; - unsigned elems; - uint32_t hint; + vcode_cmp_t cmp; // OP_HAS_CMP + int64_t value; // OP_HAS_VALUE + double real; // OP_HAS_REAL + char *comment; // OP_HAS_COMMENT + vcode_signal_t signal; // OP_HAS_SIGNAL + unsigned dim; // OP_HAS_DIM + unsigned hops; // OP_HAS_HOPS + unsigned field; // OP_HAS_FIELD + vcode_bookmark_t hint; // OP_HAS_HINT + uint32_t tag; // OP_HAS_TAG }; } op_t; @@ -64,45 +124,57 @@ typedef struct { } reg_t; typedef struct { - vtype_kind_t kind; + vtype_kind_t kind; union { struct { int64_t low; int64_t high; }; + struct { + double rlow; + double rhigh; + }; struct { unsigned dims; unsigned size; vcode_type_t elem; vcode_type_t bounds; }; - vcode_type_t pointed; - vcode_type_t base; + vcode_type_t pointed; + vcode_type_t base; struct { ident_t name; - uint32_t index; + vcode_bookmark_t uniq; vcode_type_array_t fields; }; }; } vtype_t; +typedef enum { + VAR_EXTERN = (1 << 0), + VAR_CONST = (1 << 1), + VAR_HEAP = (1 << 2) +} var_flags_t; + typedef struct { vcode_type_t type; vcode_type_t bounds; ident_t name; - bool is_extern; - bool is_const; - bool use_heap; + var_flags_t flags; } var_t; +typedef enum { + SIGNAL_EXTERN = (1 << 0) +} signal_flags_t; + typedef struct { - vcode_type_t type; - vcode_type_t bounds; - ident_t name; - vcode_var_t shadow; - netid_t *nets; - size_t nnets; - bool is_extern; + vcode_type_t type; + vcode_type_t bounds; + ident_t name; + vcode_var_t shadow; + netid_t *nets; + size_t nnets; + signal_flags_t flags; } signal_t; typedef struct { @@ -119,6 +191,11 @@ DECLARE_AND_DEFINE_ARRAY(block); DECLARE_AND_DEFINE_ARRAY(signal); DECLARE_AND_DEFINE_ARRAY(vtype); +typedef enum { + UNIT_PURE = (1 << 0), + UNIT_UNDEFINED = (1 << 1) +} unit_flags_t; + struct vcode_unit { vunit_kind_t kind; vcode_unit_t context; @@ -131,7 +208,10 @@ struct vcode_unit { signal_array_t signals; param_array_t params; unsigned depth; - bool pure; + unit_flags_t flags; + vcode_unit_t children; + vcode_unit_t next; + unsigned refcount; }; #define MASK_CONTEXT(x) ((x) >> 24) @@ -153,8 +233,13 @@ struct vcode_unit { #define VCODE_FOR_EACH_MATCHING_OP(name, k) \ VCODE_FOR_EACH_OP(name) if (name->kind == k) +#define VCODE_MAGIC 0x76636f64 +#define VCODE_VERSION 2 +#define VCODE_CHECK_UNIONS 0 + static vcode_unit_t active_unit = NULL; static vcode_block_t active_block = VCODE_INVALID_BLOCK; +static hash_t *registry = NULL; static inline int64_t sadd64(int64_t a, int64_t b) { @@ -310,6 +395,7 @@ void vcode_heap_allocate(vcode_reg_t reg) case VCODE_OP_CONST_REAL: case VCODE_OP_CONST_ARRAY: case VCODE_OP_NULL: + case VCODE_OP_UNDEFINED: break; case VCODE_OP_ALLOCA: @@ -317,7 +403,7 @@ void vcode_heap_allocate(vcode_reg_t reg) break; case VCODE_OP_INDEX: - vcode_var_data(defn->address)->use_heap = true; + vcode_var_data(defn->address)->flags |= VAR_HEAP; break; case VCODE_OP_WRAP: @@ -444,6 +530,74 @@ void vcode_state_restore(const vcode_state_t *state) active_block = state->block; } +void vcode_unit_unref(vcode_unit_t unit) +{ + assert(unit != NULL); + assert(unit->refcount > 0); + + if (unit == active_unit) + vcode_close(); + + if (--(unit->refcount) > 0) + return; + + assert(unit->children == NULL); + + if (unit->kind != VCODE_UNIT_CONTEXT) { + if (unit == unit->context->children) + unit->context->children = unit->next; + else { + vcode_unit_t it; + for (it = unit->context->children; + it != NULL && it->next != unit; + it = it->next) + ; + assert(it != NULL); + it->next = it->next->next; + } + + vcode_unit_unref(unit->context); + } + + hash_put(registry, unit->name, NULL); + + for (unsigned i = 0; i < unit->blocks.count; i++) { + block_t *b = &(unit->blocks.items[i]); + + for (unsigned j = 0; j < b->ops.count; j++) { + op_t *o = &(b->ops.items[j]); + if (OP_HAS_COMMENT(o->kind)) + free(o->comment); + free(o->args.items); + } + free(b->ops.items); + } + free(unit->blocks.items); + + for (unsigned i = 0; i < unit->types.count; i++) { + vtype_t *vt = &(unit->types.items[i]); + if (vt->kind == VCODE_TYPE_RECORD) + free(vt->fields.items); + } + free(unit->types.items); + + free(unit->regs.items); + free(unit->signals.items); + free(unit->vars.items); + free(unit->params.items); + free(unit); +} + +vcode_unit_t vcode_unit_next(vcode_unit_t unit) +{ + return unit->next; +} + +vcode_unit_t vcode_unit_child(vcode_unit_t unit) +{ + return unit->children; +} + int vcode_count_regs(void) { assert(active_unit != NULL); @@ -491,7 +645,7 @@ void vcode_opt(void) { // Prune assignments to unused registers - int *uses = xmalloc(active_unit->regs.count * sizeof(int)); + int *uses LOCAL = xmalloc(active_unit->regs.count * sizeof(int)); int pruned = 0; do { @@ -523,6 +677,13 @@ void vcode_opt(void) case VCODE_OP_WRAP: case VCODE_OP_VEC_LOAD: case VCODE_OP_HEAP_SAVE: + case VCODE_OP_EXP: + case VCODE_OP_UNDEFINED: + case VCODE_OP_UARRAY_LEN: + case VCODE_OP_UARRAY_DIR: + case VCODE_OP_UARRAY_LEFT: + case VCODE_OP_UARRAY_RIGHT: + case VCODE_OP_UNWRAP: if (uses[o->result] == -1) { vcode_dump(); fatal("defintion of r%d does not dominate all uses", @@ -558,8 +719,6 @@ void vcode_opt(void) } } while (pruned > 0); - - free(uses); } void vcode_close(void) @@ -627,12 +786,12 @@ vcode_type_t vcode_var_type(vcode_var_t var) bool vcode_var_extern(vcode_var_t var) { - return vcode_var_data(var)->is_extern; + return !!(vcode_var_data(var)->flags & VAR_EXTERN); } bool vcode_var_use_heap(vcode_var_t var) { - return vcode_var_data(var)->use_heap; + return !!(vcode_var_data(var)->flags & VAR_HEAP); } int vcode_count_signals(void) @@ -675,7 +834,7 @@ vcode_type_t vcode_signal_bounds(vcode_signal_t sig) bool vcode_signal_extern(vcode_signal_t sig) { - return vcode_signal_data(sig)->is_extern; + return !!(vcode_signal_data(sig)->flags & SIGNAL_EXTERN); } vcode_op_t vcode_get_op(int op) @@ -686,85 +845,70 @@ vcode_op_t vcode_get_op(int op) ident_t vcode_get_func(int op) { op_t *o = vcode_op_data(op); - assert(o->kind == VCODE_OP_FCALL || o->kind == VCODE_OP_NESTED_FCALL - || o->kind == VCODE_OP_PCALL || o->kind == VCODE_OP_RESUME - || o->kind == VCODE_OP_SET_INITIAL - || o->kind == VCODE_OP_NESTED_RESUME - || o->kind == VCODE_OP_NESTED_PCALL); + assert(OP_HAS_FUNC(o->kind)); return o->func; } unsigned vcode_get_subkind(int op) { op_t *o = vcode_op_data(op); - assert(o->kind == VCODE_OP_SCHED_EVENT || o->kind == VCODE_OP_BOUNDS - || o->kind == VCODE_OP_VEC_LOAD || o->kind == VCODE_OP_BIT_VEC_OP - || o->kind == VCODE_OP_INDEX_CHECK || o->kind == VCODE_OP_BIT_SHIFT - || o->kind == VCODE_OP_ALLOCA || o->kind == VCODE_OP_COVER_COND); + assert(OP_HAS_SUBKIND(o->kind)); return o->subkind; } int64_t vcode_get_value(int op) { op_t *o = vcode_op_data(op); - assert(o->kind == VCODE_OP_CONST); + assert(OP_HAS_VALUE(o->kind)); return o->value; } double vcode_get_real(int op) { op_t *o = vcode_op_data(op); - assert(o->kind == VCODE_OP_CONST_REAL); + assert(OP_HAS_REAL(o->kind)); return o->real; } vcode_var_t vcode_get_address(int op) { op_t *o = vcode_op_data(op); - assert(o->kind == VCODE_OP_LOAD || o->kind == VCODE_OP_STORE - || o->kind == VCODE_OP_INDEX || o->kind == VCODE_OP_RESOLVED_ADDRESS); + assert(OP_HAS_ADDRESS(o->kind)); return o->address; } vcode_signal_t vcode_get_signal(int op) { op_t *o = vcode_op_data(op); - assert(o->kind == VCODE_OP_NETS || o->kind == VCODE_OP_RESOLVED_ADDRESS - || o->kind == VCODE_OP_SET_INITIAL - || o->kind == VCODE_OP_NEEDS_LAST_VALUE); + assert(OP_HAS_SIGNAL(o->kind)); return o->signal; } unsigned vcode_get_dim(int op) { op_t *o = vcode_op_data(op); - assert(o->kind == VCODE_OP_UARRAY_LEFT || o->kind == VCODE_OP_UARRAY_RIGHT - || o->kind == VCODE_OP_UARRAY_DIR || o->kind == VCODE_OP_UARRAY_LEN); + assert(OP_HAS_DIM(o->kind)); return o->dim; } int vcode_get_hops(int op) { op_t *o = vcode_op_data(op); - assert(o->kind == VCODE_OP_PARAM_UPREF || o->kind == VCODE_OP_NESTED_FCALL - || o->kind == VCODE_OP_NESTED_PCALL || o->kind == VCODE_OP_RESUME - || o->kind == VCODE_OP_NESTED_RESUME); + assert(OP_HAS_HOPS(o->kind)); return o->hops; } int vcode_get_field(int op) { op_t *o = vcode_op_data(op); - assert(o->kind == VCODE_OP_RECORD_REF); + assert(OP_HAS_FIELD(o->kind)); return o->field; } vcode_var_t vcode_get_type(int op) { op_t *o = vcode_op_data(op); - assert(o->kind == VCODE_OP_BOUNDS || o->kind == VCODE_OP_ALLOCA - || o->kind == VCODE_OP_COPY || o->kind == VCODE_OP_SET_INITIAL - || o->kind == VCODE_OP_INDEX_CHECK); + assert(OP_HAS_TYPE(o->kind)); return o->type; } @@ -788,38 +932,42 @@ vcode_reg_t vcode_get_result(int op) vcode_cmp_t vcode_get_cmp(int op) { op_t *o = vcode_op_data(op); - assert(o->kind == VCODE_OP_CMP); + assert(OP_HAS_CMP(o->kind)); return o->cmp; } -uint32_t vcode_get_index(int op) +uint32_t vcode_get_tag(int op) +{ + op_t *o = vcode_op_data(op); + assert(OP_HAS_TAG(o->kind)); + return o->tag; +} + +vcode_bookmark_t vcode_get_bookmark(int op) { op_t *o = vcode_op_data(op); - assert(o->kind == VCODE_OP_ASSERT || o->kind == VCODE_OP_REPORT - || o->kind == VCODE_OP_IMAGE || o->kind == VCODE_OP_SET_INITIAL - || o->kind == VCODE_OP_DIV || o->kind == VCODE_OP_NULL_CHECK - || o->kind == VCODE_OP_VALUE || o->kind == VCODE_OP_BOUNDS - || o->kind == VCODE_OP_DYNAMIC_BOUNDS - || o->kind == VCODE_OP_ARRAY_SIZE || o->kind == VCODE_OP_INDEX_CHECK - || o->kind == VCODE_OP_COVER_STMT || o->kind == VCODE_OP_COVER_COND); - return o->index; + assert(OP_HAS_BOOKMARK(o->kind)); + return o->bookmark; +} + +uint32_t vcode_get_index(int op) +{ + // TODO: remove this? + return tree_index(vcode_get_bookmark(op).tree); } uint32_t vcode_get_hint(int op) { + // TODO: replace this with function returning bookmark? op_t *o = vcode_op_data(op); - assert(o->kind == VCODE_OP_BOUNDS - || o->kind == VCODE_OP_DYNAMIC_BOUNDS - || o->kind == VCODE_OP_INDEX_CHECK); - return o->hint; + assert(OP_HAS_HINT(o->kind)); + return tree_index(o->hint.tree); } vcode_block_t vcode_get_target(int op, int nth) { op_t *o = vcode_op_data(op); - assert(o->kind == VCODE_OP_WAIT || o->kind == VCODE_OP_JUMP - || o->kind == VCODE_OP_COND || o->kind == VCODE_OP_PCALL - || o->kind == VCODE_OP_CASE || o->kind == VCODE_OP_NESTED_PCALL); + assert(OP_HAS_TARGET(o->kind)); return vcode_block_array_nth(&(o->targets), nth); } @@ -864,7 +1012,7 @@ const char *vcode_op_string(vcode_op_t op) "bit vec op", "const real", "value", "last event", "needs last value", "dynamic bounds", "array size", "index check", "bit shift", "storage hint", "debug out", "nested pcall", "cover stmt", "cover cond", - "uarray len", "heap save", "heap restore" + "uarray len", "heap save", "heap restore", "nested resume", "undefined" }; if ((unsigned)op >= ARRAY_LEN(strs)) return "???"; @@ -1034,9 +1182,10 @@ void vcode_dump(void) case VCODE_UNIT_CONTEXT: printf("context"); break; case VCODE_UNIT_FUNCTION: printf("function"); break; case VCODE_UNIT_PROCEDURE: printf("procedure"); break; + case VCODE_UNIT_THUNK: printf("thunk"); break; } color_printf("$$\n"); - if (vu->kind != VCODE_UNIT_CONTEXT) + if (vu->kind != VCODE_UNIT_CONTEXT && vu->kind != VCODE_UNIT_THUNK) color_printf("Context $cyan$%s$$\n", istr(vu->context->name)); printf("Blocks %d\n", vu->blocks.count); printf("Registers %d\n", vu->regs.count); @@ -1046,8 +1195,9 @@ void vcode_dump(void) const vtype_t *t = &(vu->types.items[i]); if (t->kind == VCODE_TYPE_RECORD) { int col = 0; - if (t->index != UINT32_MAX) - col += color_printf(" $magenta$%s.%u$$", istr(t->name), t->index); + if (t->uniq.type != NULL && type_has_index(t->uniq.type)) + col += color_printf(" $magenta$%s.%u$$", istr(t->name), + type_index(t->uniq.type)); else col += color_printf(" $magenta$%s$$", istr(t->name)); vcode_dump_tab(col, 40); @@ -1068,14 +1218,14 @@ void vcode_dump(void) int col = printf(" "); col += color_printf("$magenta$%s$$", istr(v->name)); - if (v->is_extern) + if (v->flags & VAR_EXTERN) col += printf(" extern"); - if (v->use_heap) + if (v->flags & VAR_HEAP) col += printf(" heap"); - if (vu->kind == VCODE_UNIT_CONTEXT && !v->is_const) + if (vu->kind == VCODE_UNIT_CONTEXT && !(v->flags & VAR_CONST)) col += printf(" mutable"); - else if (vu->kind != VCODE_UNIT_CONTEXT && v->is_const) + else if (vu->kind != VCODE_UNIT_CONTEXT && (v->flags & VAR_CONST)) col += printf(" const"); vcode_dump_type(col, v->type, v->bounds); @@ -1088,7 +1238,7 @@ void vcode_dump(void) for (int i = 0; i < vu->signals.count; i++) { const signal_t *s = &(vu->signals.items[i]); int col = printf(" "); - if (s->is_extern) + if (s->flags & SIGNAL_EXTERN) col += printf("extern "); col += color_printf("$white$%s$$", istr(s->name)); vcode_dump_type(col, s->type, s->bounds); @@ -1102,13 +1252,15 @@ void vcode_dump(void) printf("]\n"); } } - else if (vu->kind == VCODE_UNIT_FUNCTION - || vu->kind == VCODE_UNIT_PROCEDURE) { - if (vu->result != VCODE_INVALID_TYPE) { - color_printf("Result $cyan$"); - vcode_dump_one_type(vu->result); - color_printf("$$\n"); - } + + if (vu->result != VCODE_INVALID_TYPE) { + color_printf("Result $cyan$"); + vcode_dump_one_type(vu->result); + color_printf("$$\n"); + } + + if (vu->kind == VCODE_UNIT_FUNCTION + || vu->kind == VCODE_UNIT_PROCEDURE) { printf("Parameters %d\n", vu->params.count); @@ -1890,14 +2042,15 @@ void vcode_dump(void) case VCODE_OP_COVER_STMT: { - printf("%s %u", vcode_op_string(op->kind), op->index); + printf("%s %u", vcode_op_string(op->kind), + tree_index(op->bookmark.tree)); } break; case VCODE_OP_COVER_COND: { printf("%s %u sub %u ", vcode_op_string(op->kind), - op->index, op->subkind); + tree_index(op->bookmark.tree), op->subkind); vcode_dump_reg(op->args.items[0]); } break; @@ -1916,6 +2069,14 @@ void vcode_dump(void) vcode_dump_reg(op->args.items[0]); } break; + + case VCODE_OP_UNDEFINED: + { + col += vcode_dump_reg(op->result); + col += printf(" := %s", vcode_op_string(op->kind)); + vcode_dump_result_type(col, op); + } + break; } printf("\n"); @@ -1959,7 +2120,7 @@ bool vtype_eq(vcode_type_t a, vcode_type_t b) case VCODE_TYPE_FILE: return vtype_eq(at->base, bt->base); case VCODE_TYPE_RECORD: - return at->name == bt->name && at->index == bt->index; + return at->name == bt->name && at->uniq.type == bt->uniq.type; } return false; @@ -1989,9 +2150,11 @@ bool vtype_includes(vcode_type_t type, vcode_type_t bounds) case VCODE_TYPE_ACCESS: case VCODE_TYPE_OFFSET: case VCODE_TYPE_FILE: - case VCODE_TYPE_REAL: return false; + case VCODE_TYPE_REAL: + return bt->rlow >= tt->rlow && bt->rhigh <= tt->rhigh; + case VCODE_TYPE_SIGNAL: return vtype_includes(tt->base, bt->base); } @@ -2050,23 +2213,23 @@ vcode_type_t vtype_carray(int size, vcode_type_t elem, vcode_type_t bounds) return vtype_new(n); } -vcode_type_t vtype_named_record(ident_t name, uint32_t index, bool create) +vcode_type_t vtype_named_record(ident_t name, vcode_bookmark_t uniq, bool create) { assert(active_unit != NULL); for (int i = 0; i < active_unit->types.count; i++) { vtype_t *other = &(active_unit->types.items[i]); if (other->kind == VCODE_TYPE_RECORD && other->name == name - && other->index == index) + && other->uniq.type == uniq.type) return MAKE_HANDLE(active_unit->depth, i); } if (create) { vtype_t *n = vtype_array_alloc(&(active_unit->types)); memset(n, '\0', sizeof(vtype_t)); - n->kind = VCODE_TYPE_RECORD; - n->name = name; - n->index = index; + n->kind = VCODE_TYPE_RECORD; + n->name = name; + n->uniq = uniq; return vtype_new(n); } @@ -2164,7 +2327,9 @@ vcode_type_t vtype_real(void) assert(active_unit != NULL); vtype_t *n = vtype_array_alloc(&(active_unit->types)); - n->kind = VCODE_TYPE_REAL; + n->kind = VCODE_TYPE_REAL; + n->rlow = DBL_MIN; + n->rhigh = DBL_MAX; return vtype_new(n); } @@ -2240,8 +2405,8 @@ char *vtype_record_name(vcode_type_t type) { vtype_t *vt = vcode_type_data(type); assert(vt->kind == VCODE_TYPE_RECORD); - if (vt->index != UINT32_MAX) - return xasprintf("%s.%u", istr(vt->name), vt->index); + if (vt->uniq.type != NULL && type_has_index(vt->uniq.type)) + return xasprintf("%s.%u", istr(vt->name), type_index(vt->uniq.type)); else return xasprintf("%s", istr(vt->name)); } @@ -2348,7 +2513,13 @@ ident_t vcode_unit_name(void) bool vcode_unit_pure(void) { assert(active_unit != NULL); - return active_unit->pure; + return active_unit->flags & UNIT_PURE; +} + +bool vcode_unit_has_undefined(void) +{ + assert(active_unit != NULL); + return active_unit->flags & UNIT_UNDEFINED; } int vcode_unit_depth(void) @@ -2384,6 +2555,42 @@ static unsigned vcode_unit_calc_depth(vcode_unit_t unit) return hops; } +static void vcode_registry_add(vcode_unit_t vu) +{ + if (registry == NULL) + registry = hash_new(256, true); + + assert(vu->refcount > 0); + hash_put(registry, vu->name, vu); +} + +vcode_unit_t vcode_find_unit(ident_t name) +{ + if (registry == NULL) + return NULL; + else { + vcode_unit_t vu = hash_get(registry, name); + assert(vu == NULL || vu->refcount > 0); + return vu; + } +} + +static void vcode_add_child(vcode_unit_t context, vcode_unit_t child) +{ + assert(context->refcount > 0); + context->refcount++; + + child->next = NULL; + if (context->children == NULL) + context->children = child; + else { + vcode_unit_t it; + for (it = context->children; it->next != NULL; it = it->next) + ; + it->next = child; + } +} + vcode_unit_t emit_function(ident_t name, vcode_unit_t context, vcode_type_t result) { @@ -2393,26 +2600,36 @@ vcode_unit_t emit_function(ident_t name, vcode_unit_t context, vu->context = context; vu->result = result; vu->depth = vcode_unit_calc_depth(vu); - vu->pure = true; + vu->flags = UNIT_PURE; + vu->refcount = 1; + + vcode_add_child(context, vu); active_unit = vu; vcode_select_block(emit_block()); + vcode_registry_add(vu); + return vu; } vcode_unit_t emit_procedure(ident_t name, vcode_unit_t context) { vcode_unit_t vu = xcalloc(sizeof(struct vcode_unit)); - vu->kind = VCODE_UNIT_PROCEDURE; - vu->name = name; - vu->context = context; - vu->result = VCODE_INVALID_TYPE; - vu->depth = vcode_unit_calc_depth(vu); + vu->kind = VCODE_UNIT_PROCEDURE; + vu->name = name; + vu->context = context; + vu->result = VCODE_INVALID_TYPE; + vu->depth = vcode_unit_calc_depth(vu); + vu->refcount = 1; + + vcode_add_child(context, vu); active_unit = vu; vcode_select_block(emit_block()); + vcode_registry_add(vu); + return vu; } @@ -2421,10 +2638,34 @@ vcode_unit_t emit_process(ident_t name, vcode_unit_t context) assert(context->kind == VCODE_UNIT_CONTEXT); vcode_unit_t vu = xcalloc(sizeof(struct vcode_unit)); - vu->kind = VCODE_UNIT_PROCESS; - vu->name = name; - vu->context = context; - vu->depth = vcode_unit_calc_depth(vu); + vu->kind = VCODE_UNIT_PROCESS; + vu->name = name; + vu->context = context; + vu->depth = vcode_unit_calc_depth(vu); + vu->result = VCODE_INVALID_TYPE; + vu->refcount = 1; + + vcode_add_child(context, vu); + + active_unit = vu; + vcode_select_block(emit_block()); + + vcode_registry_add(vu); + + return vu; +} + +vcode_unit_t emit_thunk(ident_t name, vcode_unit_t context, vcode_type_t type) +{ + vcode_unit_t vu = xcalloc(sizeof(struct vcode_unit)); + vu->kind = VCODE_UNIT_THUNK; + vu->name = name; + vu->context = context; + vu->result = type; + vu->depth = vcode_unit_calc_depth(vu); + vu->refcount = 1; + + vcode_add_child(context, vu); active_unit = vu; vcode_select_block(emit_block()); @@ -2435,23 +2676,27 @@ vcode_unit_t emit_process(ident_t name, vcode_unit_t context) vcode_unit_t emit_context(ident_t name) { vcode_unit_t vu = xcalloc(sizeof(struct vcode_unit)); - vu->kind = VCODE_UNIT_CONTEXT; - vu->name = name; - vu->context = vu; + vu->kind = VCODE_UNIT_CONTEXT; + vu->name = name; + vu->context = vu; + vu->result = VCODE_INVALID_TYPE; + vu->refcount = 1; active_unit = vu; vcode_select_block(emit_block()); + vcode_registry_add(vu); + return vu; } void emit_assert(vcode_reg_t value, vcode_reg_t message, vcode_reg_t length, - vcode_reg_t severity, uint32_t index) + vcode_reg_t severity, vcode_bookmark_t where) { int64_t value_const; if (vcode_reg_const(value, &value_const)) { if (value_const == 0) - active_unit->pure = false; + active_unit->flags &= ~UNIT_PURE; else { emit_comment("Always true assertion on r%d", value); return; @@ -2463,22 +2708,22 @@ void emit_assert(vcode_reg_t value, vcode_reg_t message, vcode_reg_t length, vcode_add_arg(op, severity); vcode_add_arg(op, message); vcode_add_arg(op, length); - op->index = index; + op->bookmark = where; VCODE_ASSERT(vtype_eq(vcode_reg_type(value), vtype_bool()), "value parameter to assert is not bool"); } void emit_report(vcode_reg_t message, vcode_reg_t length, vcode_reg_t severity, - uint32_t index) + vcode_bookmark_t where) { op_t *op = vcode_add_op(VCODE_OP_REPORT); vcode_add_arg(op, severity); vcode_add_arg(op, message); vcode_add_arg(op, length); - op->index = index; + op->bookmark = where; - active_unit->pure = false; + active_unit->flags &= ~UNIT_PURE; } vcode_reg_t emit_cmp(vcode_cmp_t cmp, vcode_reg_t lhs, vcode_reg_t rhs) @@ -2764,8 +3009,7 @@ vcode_var_t emit_var(vcode_type_t type, vcode_type_t bounds, ident_t name, v->type = type; v->bounds = bounds; v->name = name; - v->is_const = is_const; - v->use_heap = false; + v->flags = is_const ? VAR_CONST : 0; return MAKE_HANDLE(active_unit->depth, var); } @@ -2779,12 +3023,12 @@ vcode_var_t emit_extern_var(vcode_type_t type, vcode_type_t bounds, // Try to find an existing extern with this name for (unsigned i = 0; i < active_unit->vars.count; i++) { var_t *v = &(active_unit->vars.items[i]); - if (v->is_extern && v->name == name) + if ((v->flags & VAR_EXTERN) && v->name == name) return MAKE_HANDLE(active_unit->depth, i); } vcode_var_t var = emit_var(type, bounds, name, false); - vcode_var_data(var)->is_extern = true; + vcode_var_data(var)->flags |= VAR_EXTERN; return var; } @@ -2835,13 +3079,13 @@ vcode_signal_t emit_extern_signal(vcode_type_t type, vcode_type_t bounds, // Try to find an existing extern with this name for (unsigned i = 0; i < active_unit->vars.count; i++) { signal_t *s = &(active_unit->signals.items[i]); - if (s->is_extern && s->name == name) + if ((s->flags & SIGNAL_EXTERN) && s->name == name) return i; } vcode_signal_t sig = emit_signal(type, bounds, name, VCODE_INVALID_VAR, NULL, 0); - vcode_signal_data(sig)->is_extern = true; + vcode_signal_data(sig)->flags |= SIGNAL_EXTERN; return sig; } @@ -2866,7 +3110,7 @@ vcode_reg_t emit_load(vcode_var_t var) var_t *v = vcode_var_data(var); if (fold != VCODE_INVALID_REG && !aliased - && (v->is_const || MASK_CONTEXT(var) == active_unit->depth)) + && ((v->flags & VAR_CONST) || MASK_CONTEXT(var) == active_unit->depth)) return fold; op_t *op = vcode_add_op(VCODE_OP_LOAD); @@ -2921,6 +3165,7 @@ void emit_store(vcode_reg_t reg, vcode_var_t var) other->kind = VCODE_OP_COMMENT; other->comment = xasprintf("Dead store to %s", istr(vcode_var_name(var))); + vcode_reg_array_resize(&(other->args), 0, VCODE_INVALID_REG); } else if (other->kind == VCODE_OP_NESTED_FCALL || other->kind == VCODE_OP_NESTED_PCALL) @@ -2953,8 +3198,7 @@ void emit_store_indirect(vcode_reg_t reg, vcode_reg_t ptr) "pointer and stored value do not have same type"); } -static vcode_reg_t emit_arith(vcode_op_t kind, vcode_reg_t lhs, vcode_reg_t rhs, - uint32_t index) +static vcode_reg_t emit_arith(vcode_op_t kind, vcode_reg_t lhs, vcode_reg_t rhs) { // Reuse any previous operation in this block with the same arguments VCODE_FOR_EACH_MATCHING_OP(other, kind) { @@ -2967,7 +3211,6 @@ static vcode_reg_t emit_arith(vcode_op_t kind, vcode_reg_t lhs, vcode_reg_t rhs, vcode_add_arg(op, lhs); vcode_add_arg(op, rhs); op->result = vcode_add_reg(vcode_reg_type(lhs)); - op->index = index; vcode_type_t lhs_type = vcode_reg_type(lhs); vcode_type_t rhs_type = vcode_reg_type(rhs); @@ -2992,11 +3235,14 @@ vcode_reg_t emit_mul(vcode_reg_t lhs, vcode_reg_t rhs) if (vcode_reg_const(lhs, &lconst) && vcode_reg_const(rhs, &rconst)) return emit_const(vcode_reg_type(lhs), lconst * rconst); - vcode_reg_t reg = emit_arith(VCODE_OP_MUL, lhs, rhs, UINT32_MAX); + vcode_reg_t reg = emit_arith(VCODE_OP_MUL, lhs, rhs); vtype_t *bl = vcode_type_data(vcode_reg_data(lhs)->bounds); vtype_t *br = vcode_type_data(vcode_reg_data(rhs)->bounds); + if (bl->kind == VCODE_TYPE_REAL) + return reg; + const int64_t ll = smul64(bl->low, br->low); const int64_t lh = smul64(bl->low, br->high); const int64_t hl = smul64(bl->high, br->low); @@ -3011,13 +3257,19 @@ vcode_reg_t emit_mul(vcode_reg_t lhs, vcode_reg_t rhs) return reg; } -vcode_reg_t emit_div(vcode_reg_t lhs, vcode_reg_t rhs, uint32_t index) +vcode_reg_t emit_div(vcode_reg_t lhs, vcode_reg_t rhs, vcode_bookmark_t where) { int64_t lconst, rconst; if (vcode_reg_const(lhs, &lconst) && vcode_reg_const(rhs, &rconst)) return emit_const(vcode_reg_type(lhs), lconst / rconst); - return emit_arith(VCODE_OP_DIV, lhs, rhs, index); + vcode_reg_t result = emit_arith(VCODE_OP_DIV, lhs, rhs); + + block_t *block = vcode_block_data(); + op_t *op = op_array_nth_ptr(&(block->ops), block->ops.count - 1); + op->bookmark = where; + + return result; } vcode_reg_t emit_exp(vcode_reg_t lhs, vcode_reg_t rhs) @@ -3026,7 +3278,7 @@ vcode_reg_t emit_exp(vcode_reg_t lhs, vcode_reg_t rhs) if (vcode_reg_const(lhs, &lconst) && vcode_reg_const(rhs, &rconst)) return emit_const(vcode_reg_type(lhs), ipow(lconst, rconst)); - return emit_arith(VCODE_OP_EXP, lhs, rhs, UINT32_MAX); + return emit_arith(VCODE_OP_EXP, lhs, rhs); } vcode_reg_t emit_mod(vcode_reg_t lhs, vcode_reg_t rhs) @@ -3036,7 +3288,7 @@ vcode_reg_t emit_mod(vcode_reg_t lhs, vcode_reg_t rhs) && lconst > 0 && rconst > 0) return emit_const(vcode_reg_type(lhs), lconst % rconst); - return emit_arith(VCODE_OP_MOD, lhs, rhs, UINT32_MAX); + return emit_arith(VCODE_OP_MOD, lhs, rhs); } vcode_reg_t emit_rem(vcode_reg_t lhs, vcode_reg_t rhs) @@ -3046,7 +3298,7 @@ vcode_reg_t emit_rem(vcode_reg_t lhs, vcode_reg_t rhs) && lconst > 0 && rconst > 0) return emit_const(vcode_reg_type(lhs), lconst % rconst); - return emit_arith(VCODE_OP_REM, lhs, rhs, UINT32_MAX); + return emit_arith(VCODE_OP_REM, lhs, rhs); } vcode_reg_t emit_add(vcode_reg_t lhs, vcode_reg_t rhs) @@ -3065,12 +3317,12 @@ vcode_reg_t emit_add(vcode_reg_t lhs, vcode_reg_t rhs) else if (l_is_const && lconst == 0) return rhs; - vcode_reg_t reg = emit_arith(VCODE_OP_ADD, lhs, rhs, UINT32_MAX); + vcode_reg_t reg = emit_arith(VCODE_OP_ADD, lhs, rhs); reg_t *rr = vcode_reg_data(reg); if (is_pointer) rr->bounds = vcode_reg_data(lhs)->bounds; - else { + else if (ltypek != VCODE_TYPE_REAL) { vtype_t *bl = vcode_type_data(vcode_reg_data(lhs)->bounds); vtype_t *br = vcode_type_data(vcode_reg_data(rhs)->bounds); @@ -3093,16 +3345,15 @@ vcode_reg_t emit_sub(vcode_reg_t lhs, vcode_reg_t rhs) else if (l_is_const && lconst == 0) return rhs; - vcode_reg_t reg = emit_arith(VCODE_OP_SUB, lhs, rhs, UINT32_MAX); + vcode_reg_t reg = emit_arith(VCODE_OP_SUB, lhs, rhs); + + vtype_t *bl = vcode_type_data(vcode_reg_data(lhs)->bounds); + vtype_t *br = vcode_type_data(vcode_reg_data(rhs)->bounds); reg_t *rr = vcode_reg_data(reg); - if (vtype_kind(vcode_reg_type(reg)) == VCODE_TYPE_POINTER) + if (bl->kind == VCODE_TYPE_POINTER || bl->kind == VCODE_TYPE_SIGNAL) rr->bounds = vcode_reg_data(lhs)->bounds; - else { - vtype_t *bl = vcode_type_data(vcode_reg_data(lhs)->bounds); - vtype_t *br = vcode_type_data(vcode_reg_data(rhs)->bounds); - - reg_t *rr = vcode_reg_data(reg); + else if (bl->kind != VCODE_TYPE_REAL) { // XXX: this is wrong - see TO_UNSIGNED rr->bounds = vtype_int(sadd64(bl->low, -br->high), sadd64(bl->high, -br->low)); @@ -3112,7 +3363,7 @@ vcode_reg_t emit_sub(vcode_reg_t lhs, vcode_reg_t rhs) } void emit_bounds(vcode_reg_t reg, vcode_type_t bounds, bounds_kind_t kind, - uint32_t index, uint32_t hint) + vcode_bookmark_t where, vcode_bookmark_t hint) { if (reg == VCODE_INVALID_REG) return; @@ -3123,10 +3374,10 @@ void emit_bounds(vcode_reg_t reg, vcode_type_t bounds, bounds_kind_t kind, op_t *op = vcode_add_op(VCODE_OP_BOUNDS); vcode_add_arg(op, reg); - op->type = bounds; - op->subkind = kind; - op->index = index; - op->hint = hint; + op->type = bounds; + op->subkind = kind; + op->bookmark = where; + op->hint = hint; const vtype_kind_t tkind = vtype_kind(bounds); VCODE_ASSERT(tkind == VCODE_TYPE_INT || tkind == VCODE_TYPE_REAL, @@ -3155,12 +3406,11 @@ vcode_reg_t emit_index(vcode_var_t var, vcode_reg_t offset) vcode_add_arg(op, offset); vcode_type_t typeref = vcode_var_type(var); - vtype_t *vt = vcode_type_data(typeref); - switch (vt->kind) { + switch (vtype_kind(typeref)) { case VCODE_TYPE_CARRAY: - op->type = vtype_pointer(vt->elem); + op->type = vtype_pointer(vtype_elem(typeref)); op->result = vcode_add_reg(op->type); - vcode_reg_data(op->result)->bounds = vt->bounds; + vcode_reg_data(op->result)->bounds = vtype_bounds(typeref); break; case VCODE_TYPE_RECORD: @@ -3247,7 +3497,8 @@ void emit_return(vcode_reg_t reg) if (reg != VCODE_INVALID_REG) { vcode_add_arg(op, reg); - VCODE_ASSERT(active_unit->kind == VCODE_UNIT_FUNCTION, + VCODE_ASSERT(active_unit->kind == VCODE_UNIT_FUNCTION + || active_unit->kind == VCODE_UNIT_THUNK, "returning value fron non-function unit"); VCODE_ASSERT(vtype_eq(active_unit->result, vcode_reg_type(reg)), "return value incorrect type"); @@ -3352,11 +3603,11 @@ vcode_reg_t emit_abs(vcode_reg_t lhs) return op->result; } -vcode_reg_t emit_image(vcode_reg_t value, uint32_t index) +vcode_reg_t emit_image(vcode_reg_t value, vcode_bookmark_t where) { op_t *op = vcode_add_op(VCODE_OP_IMAGE); vcode_add_arg(op, value); - op->index = index; + op->bookmark = where; op->result = vcode_add_reg( vtype_uarray(1, vtype_char(), vtype_int(0, 127))); @@ -3447,7 +3698,7 @@ static vcode_reg_t emit_logical(vcode_op_t op, vcode_reg_t lhs, vcode_reg_t rhs) } } - vcode_reg_t result = emit_arith(op, lhs, rhs, UINT32_MAX); + vcode_reg_t result = emit_arith(op, lhs, rhs); VCODE_ASSERT(vtype_eq(vcode_reg_type(lhs), vtbool) && vtype_eq(vcode_reg_type(rhs), vtbool), @@ -3621,13 +3872,15 @@ vcode_reg_t emit_unwrap(vcode_reg_t array) VCODE_ASSERT(vt->kind == VCODE_TYPE_UARRAY, "unwrap can only only be used with uarray types"); - vcode_type_t rtype = (vtype_kind(vt->elem) == VCODE_TYPE_SIGNAL) - ? vt->elem : vtype_pointer(vt->elem); + vcode_type_t elem = vt->elem; + + vcode_type_t rtype = (vtype_kind(elem) == VCODE_TYPE_SIGNAL) + ? elem : vtype_pointer(elem); op->result = vcode_add_reg(rtype); reg_t *rr = vcode_reg_data(op->result); - rr->bounds = vt->elem; + rr->bounds = elem; return op->result; } @@ -3664,14 +3917,15 @@ void emit_resolved_address(vcode_var_t var, vcode_signal_t signal) op->address = var; } -void emit_set_initial(vcode_signal_t signal, vcode_reg_t value, uint32_t index, - ident_t resolution, vcode_type_t type) +void emit_set_initial(vcode_signal_t signal, vcode_reg_t value, + vcode_bookmark_t index, ident_t resolution, + vcode_type_t type) { op_t *op = vcode_add_op(VCODE_OP_SET_INITIAL); - op->signal = signal; - op->index = index; - op->func = resolution; - op->type = type; + op->signal = signal; + op->bookmark = index; + op->func = resolution; + op->type = type; vcode_add_arg(op, value); } @@ -3945,7 +4199,7 @@ void emit_file_write(vcode_reg_t file, vcode_reg_t value, vcode_reg_t length) VCODE_ASSERT(vtype_is_pointer(vcode_reg_type(file), VCODE_TYPE_FILE), "file write first argument must have file pointer type"); - active_unit->pure = false; + active_unit->flags &= ~UNIT_PURE; } void emit_file_close(vcode_reg_t file) @@ -3977,7 +4231,7 @@ void emit_file_read(vcode_reg_t file, vcode_reg_t ptr, || vtype_kind(vcode_reg_type(outlen)) == VCODE_TYPE_POINTER, "file read outlen argument must have pointer type"); - active_unit->pure = false; + active_unit->flags &= ~UNIT_PURE; } vcode_reg_t emit_null(vcode_type_t type) @@ -4014,7 +4268,7 @@ vcode_reg_t emit_new(vcode_type_t type, vcode_reg_t length) return (op->result = vcode_add_reg(vtype_access(type))); } -void emit_null_check(vcode_reg_t ptr, uint32_t index) +void emit_null_check(vcode_reg_t ptr, vcode_bookmark_t where) { VCODE_FOR_EACH_MATCHING_OP(other, VCODE_OP_NULL_CHECK) { if (other->args.items[0] == ptr) @@ -4023,7 +4277,7 @@ void emit_null_check(vcode_reg_t ptr, uint32_t index) op_t *op = vcode_add_op(VCODE_OP_NULL_CHECK); vcode_add_arg(op, ptr); - op->index = index; + op->bookmark = where; VCODE_ASSERT(vtype_kind(vcode_reg_type(ptr)) == VCODE_TYPE_ACCESS, "null check argument must be an access"); @@ -4096,12 +4350,13 @@ vcode_reg_t emit_bit_vec_op(bit_vec_op_kind_t kind, vcode_reg_t lhs_data, return (op->result = vcode_add_reg(result)); } -vcode_reg_t emit_value(vcode_reg_t string, vcode_reg_t len, uint32_t index) +vcode_reg_t emit_value(vcode_reg_t string, vcode_reg_t len, + vcode_bookmark_t where) { op_t *op = vcode_add_op(VCODE_OP_VALUE); vcode_add_arg(op, string); vcode_add_arg(op, len); - op->index = index; + op->bookmark = where; VCODE_ASSERT(vcode_reg_kind(string) == VCODE_TYPE_POINTER, "string argument to value must be a pointer"); @@ -4134,7 +4389,8 @@ void emit_needs_last_value(vcode_signal_t sig) } void emit_dynamic_bounds(vcode_reg_t reg, vcode_reg_t low, vcode_reg_t high, - vcode_reg_t kind, uint32_t index, uint32_t hint) + vcode_reg_t kind, vcode_bookmark_t where, + vcode_bookmark_t hint) { int64_t lconst, hconst; if (vcode_reg_const(low, &lconst) && vcode_reg_const(high, &hconst)) { @@ -4146,7 +4402,7 @@ void emit_dynamic_bounds(vcode_reg_t reg, vcode_reg_t low, vcode_reg_t high, int64_t kconst; if (vcode_reg_const(kind, &kconst)) { - emit_bounds(reg, vtype_int(lconst, hconst), kconst, index, hint); + emit_bounds(reg, vtype_int(lconst, hconst), kconst, where, hint); return; } } @@ -4160,8 +4416,8 @@ void emit_dynamic_bounds(vcode_reg_t reg, vcode_reg_t low, vcode_reg_t high, vcode_add_arg(op, low); vcode_add_arg(op, high); vcode_add_arg(op, kind); - op->index = index; - op->hint = hint; + op->bookmark = where; + op->hint = hint; VCODE_ASSERT(vtype_eq(vcode_reg_type(low), vcode_reg_type(high)), "type mismatch in dynamic bounds range"); @@ -4169,7 +4425,7 @@ void emit_dynamic_bounds(vcode_reg_t reg, vcode_reg_t low, vcode_reg_t high, "dynamic bounds kind argument must be offset"); } -void emit_array_size(vcode_reg_t llen, vcode_reg_t rlen, uint32_t index) +void emit_array_size(vcode_reg_t llen, vcode_reg_t rlen, vcode_bookmark_t where) { if (rlen == llen) return; @@ -4177,11 +4433,11 @@ void emit_array_size(vcode_reg_t llen, vcode_reg_t rlen, uint32_t index) op_t *op = vcode_add_op(VCODE_OP_ARRAY_SIZE); vcode_add_arg(op, llen); vcode_add_arg(op, rlen); - op->index = index; + op->bookmark = where; } static op_t *emit_index_check_null(vcode_reg_t rlow, vcode_reg_t rhigh, - bounds_kind_t kind, uint32_t index) + bounds_kind_t kind, vcode_bookmark_t where) { int64_t rlow_const, rhigh_const; const bool null = @@ -4197,15 +4453,15 @@ static op_t *emit_index_check_null(vcode_reg_t rlow, vcode_reg_t rhigh, op_t *op = vcode_add_op(VCODE_OP_INDEX_CHECK); vcode_add_arg(op, rlow); vcode_add_arg(op, rhigh); - op->subkind = kind; - op->index = index; - op->type = VCODE_INVALID_TYPE; + op->subkind = kind; + op->bookmark = where; + op->type = VCODE_INVALID_TYPE; return op; } void emit_index_check(vcode_reg_t rlow, vcode_reg_t rhigh, vcode_type_t bounds, - bounds_kind_t kind, uint32_t index) + bounds_kind_t kind, vcode_bookmark_t where) { if (vtype_includes(bounds, vcode_reg_data(rlow)->bounds) && vtype_includes(bounds, vcode_reg_data(rhigh)->bounds)) { @@ -4213,16 +4469,16 @@ void emit_index_check(vcode_reg_t rlow, vcode_reg_t rhigh, vcode_type_t bounds, return; } - op_t *op = emit_index_check_null(rlow, rhigh, kind, index); + op_t *op = emit_index_check_null(rlow, rhigh, kind, where); if (op != NULL) op->type = bounds; } void emit_dynamic_index_check(vcode_reg_t rlow, vcode_reg_t rhigh, vcode_reg_t blow, vcode_reg_t bhigh, - bounds_kind_t kind, uint32_t index) + bounds_kind_t kind, vcode_bookmark_t where) { - op_t *op = emit_index_check_null(rlow, rhigh, kind, index); + op_t *op = emit_index_check_null(rlow, rhigh, kind, where); if (op != NULL) { vcode_add_arg(op, blow); vcode_add_arg(op, bhigh); @@ -4279,14 +4535,14 @@ void emit_debug_out(vcode_reg_t reg) void emit_cover_stmt(uint32_t tag) { op_t *op = vcode_add_op(VCODE_OP_COVER_STMT); - op->index = tag; + op->tag = tag; } void emit_cover_cond(vcode_reg_t test, uint32_t tag, unsigned sub) { op_t *op = vcode_add_op(VCODE_OP_COVER_COND); vcode_add_arg(op, test); - op->index = tag; + op->tag = tag; op->subkind = sub; } @@ -4306,3 +4562,399 @@ void emit_heap_restore(vcode_reg_t reg) VCODE_ASSERT(vcode_find_definition(reg)->kind == VCODE_OP_HEAP_SAVE, "register for heap restore must come from heap save"); } + +vcode_reg_t emit_undefined(vcode_type_t type) +{ + active_unit->flags |= UNIT_UNDEFINED; + + op_t *op = vcode_add_op(VCODE_OP_UNDEFINED); + return (op->result = vcode_add_reg(type)); +} + +static void vcode_write_unit(vcode_unit_t unit, fbuf_t *f, + ident_wr_ctx_t ident_wr_ctx) +{ + write_u8(unit->kind, f); + ident_write(unit->name, ident_wr_ctx); + write_u32(unit->result, f); + write_u32(unit->flags, f); + write_u32(unit->depth, f); + + if (unit->kind != VCODE_UNIT_CONTEXT) { + vcode_select_unit(unit); + vcode_select_unit(vcode_unit_context()); + ident_write(vcode_unit_name(), ident_wr_ctx); + vcode_close(); + } + + write_u32(unit->blocks.count, f); + for (unsigned i = 0; i < unit->blocks.count; i++) { + const block_t *b = &(unit->blocks.items[i]); + write_u32(b->ops.count, f); + + for (unsigned j = 0; j < b->ops.count; j++) { + const op_t *op = &(b->ops.items[j]); + + write_u8(op->kind, f); + write_u32(op->result, f); + + write_u32(op->args.count, f); + for (unsigned k = 0; k < op->args.count; k++) + write_u32(op->args.items[k], f); + + if (OP_HAS_TARGET(op->kind)) { + write_u32(op->targets.count, f); + for (unsigned k = 0; k < op->targets.count; k++) + write_u32(op->targets.items[k], f); + } + + if (OP_HAS_TYPE(op->kind)) + write_u32(op->type, f); + if (OP_HAS_ADDRESS(op->kind)) + write_u32(op->address, f); + if (OP_HAS_BOOKMARK(op->kind)) + write_u32(tree_index(op->bookmark.tree), f); + if (OP_HAS_FUNC(op->kind)) + ident_write(op->func, ident_wr_ctx); + if (OP_HAS_SUBKIND(op->kind)) + write_u8(op->subkind, f); + if (OP_HAS_CMP(op->kind)) + write_u8(op->cmp, f); + if (OP_HAS_VALUE(op->kind)) + write_u64(op->value, f); + if (OP_HAS_REAL(op->kind)) + write_double(op->real, f); + if (OP_HAS_COMMENT(op->kind)) + ; // Do not save comments + if (OP_HAS_SIGNAL(op->kind)) + write_u32(op->signal, f); + if (OP_HAS_DIM(op->kind)) + write_u32(op->dim, f); + if (OP_HAS_HOPS(op->kind)) + write_u32(op->hops, f); + if (OP_HAS_FIELD(op->kind)) + write_u32(op->field, f); + if (OP_HAS_HINT(op->kind)) + write_u32(tree_index(op->hint.tree), f); + if (OP_HAS_TAG(op->kind)) + write_u32(op->tag, f); + } + } + + write_u32(unit->regs.count, f); + for (unsigned i = 0; i < unit->regs.count; i++) { + const reg_t *r = &(unit->regs.items[i]); + write_u32(r->type, f); + write_u32(r->bounds, f); + } + + write_u32(unit->types.count, f); + for (unsigned i = 0; i < unit->types.count; i++) { + const vtype_t *t = &(unit->types.items[i]); + write_u8(t->kind, f); + switch (t->kind) { + case VCODE_TYPE_INT: + case VCODE_TYPE_OFFSET: + write_u64(t->low, f); + write_u64(t->high, f); + break; + + case VCODE_TYPE_REAL: + write_double(t->rlow, f); + write_double(t->rhigh, f); + break; + + case VCODE_TYPE_CARRAY: + case VCODE_TYPE_UARRAY: + write_u8(t->dims, f); + write_u32(t->size, f); + write_u32(t->elem, f); + write_u32(t->bounds, f); + break; + + case VCODE_TYPE_ACCESS: + case VCODE_TYPE_POINTER: + write_u32(t->pointed, f); + break; + + case VCODE_TYPE_FILE: + case VCODE_TYPE_SIGNAL: + write_u32(t->base, f); + break; + + case VCODE_TYPE_RECORD: + ident_write(t->name, ident_wr_ctx); + if (t->uniq.type) + write_u32(type_index(t->uniq.type), f); + else + write_u32(0, f); + write_u32(t->fields.count, f); + for (unsigned j = 0; j < t->fields.count; j++) + write_u32(t->fields.items[j], f); + break; + } + } + + write_u32(unit->vars.count, f); + for (unsigned i = 0; i < unit->vars.count; i++) { + const var_t *v = &(unit->vars.items[i]); + write_u32(v->type, f); + write_u32(v->bounds, f); + ident_write(v->name, ident_wr_ctx); + write_u32(v->flags, f); + } + + write_u32(unit->signals.count, f); + for (unsigned i = 0; i < unit->signals.count; i++) { + const signal_t *s = &(unit->signals.items[i]); + write_u32(s->type, f); + write_u32(s->bounds, f); + ident_write(s->name, ident_wr_ctx); + write_u32(s->shadow, f); + write_u32(s->flags, f); + write_u32(s->nnets, f); + for (unsigned j = 0; j < s->nnets; j++) + write_u32(s->nets[j], f); + } + + write_u32(unit->params.count, f); + for (unsigned i = 0; i < unit->params.count; i++) { + const param_t *p = &(unit->params.items[i]); + write_u32(p->type, f); + write_u32(p->bounds, f); + ident_write(p->name, ident_wr_ctx); + write_u32(p->reg, f); + } + + if (unit->next != NULL) + vcode_write_unit(unit->next, f, ident_wr_ctx); + + if (unit->children != NULL) + vcode_write_unit(unit->children, f, ident_wr_ctx); +} + +void vcode_write(vcode_unit_t unit, fbuf_t *f) +{ + assert(unit->kind = VCODE_UNIT_CONTEXT); + + write_u32(VCODE_MAGIC, f); + write_u8(VCODE_VERSION, f); + + ident_wr_ctx_t ident_wr_ctx = ident_write_begin(f); + vcode_write_unit(unit, f, ident_wr_ctx); + write_u8(0xff, f); // End marker + ident_write_end(ident_wr_ctx); +} + +static bool vcode_read_unit(fbuf_t *f, tree_rd_ctx_t tree_ctx, + ident_rd_ctx_t ident_rd_ctx) +{ + const uint8_t marker = read_u8(f); + if (marker == 0xff) + return false; + + vcode_unit_t unit = xcalloc(sizeof(struct vcode_unit)); + unit->kind = marker; + unit->name = ident_read(ident_rd_ctx); + unit->result = read_u32(f); + unit->flags = read_u32(f); + unit->depth = read_u32(f); + unit->refcount = 1; + + if (unit->kind != VCODE_UNIT_CONTEXT) { + ident_t context_name = ident_read(ident_rd_ctx); + unit->context = vcode_find_unit(context_name); + if (unit->context == NULL) + fatal("%s references nonexistent context %s", fbuf_file_name(f), + istr(context_name)); + } + else + unit->context = unit; + + block_array_resize(&(unit->blocks), read_u32(f), 0); + for (unsigned i = 0; i < unit->blocks.count; i++) { + block_t *b = &(unit->blocks.items[i]); + op_array_resize(&(b->ops), read_u32(f), 0); + + for (unsigned j = 0; j < b->ops.count; j++) { + op_t *op = &(b->ops.items[j]); + + op->kind = read_u8(f); + op->result = read_u32(f); + + vcode_reg_array_resize(&(op->args), read_u32(f), 0); + for (unsigned k = 0; k < op->args.count; k++) + op->args.items[k] = read_u32(f); + + if (OP_HAS_TARGET(op->kind)) { + vcode_block_array_resize(&(op->targets), read_u32(f), 0); + for (unsigned k = 0; k < op->targets.count; k++) + op->targets.items[k] = read_u32(f); + } + + if (OP_HAS_TYPE(op->kind)) + op->type = read_u32(f); + if (OP_HAS_ADDRESS(op->kind)) + op->address = read_u32(f); + if (OP_HAS_BOOKMARK(op->kind)) + op->bookmark.tree = tree_read_recall(tree_ctx, read_u32(f)); + if (OP_HAS_FUNC(op->kind)) + op->func = ident_read(ident_rd_ctx); + if (OP_HAS_SUBKIND(op->kind)) + op->subkind = read_u8(f); + if (OP_HAS_CMP(op->kind)) + op->cmp = read_u8(f); + if (OP_HAS_VALUE(op->kind)) + op->value = read_u64(f); + if (OP_HAS_REAL(op->kind)) + op->real = read_double(f); + if (OP_HAS_COMMENT(op->kind)) + op->comment = NULL; + if (OP_HAS_SIGNAL(op->kind)) + op->signal = read_u32(f); + if (OP_HAS_DIM(op->kind)) + op->dim = read_u32(f); + if (OP_HAS_HOPS(op->kind)) + op->hops = read_u32(f); + if (OP_HAS_FIELD(op->kind)) + op->field = read_u32(f); + if (OP_HAS_HINT(op->kind)) + op->hint.tree = tree_read_recall(tree_ctx, read_u32(f)); + if (OP_HAS_TAG(op->kind)) + op->tag = read_u32(f); + } + } + + reg_array_resize(&(unit->regs), read_u32(f), 0); + for (unsigned i = 0; i < unit->regs.count; i++) { + reg_t *r = &(unit->regs.items[i]); + r->type = read_u32(f); + r->bounds = read_u32(f); + } + + vtype_array_resize(&(unit->types), read_u32(f), 0); + for (unsigned i = 0; i < unit->types.count; i++) { + vtype_t *t = &(unit->types.items[i]); + switch ((t->kind = read_u8(f))) { + case VCODE_TYPE_INT: + case VCODE_TYPE_OFFSET: + t->low = read_u64(f); + t->high = read_u64(f); + break; + + case VCODE_TYPE_REAL: + t->rlow = read_double(f); + t->rhigh = read_double(f); + break; + + case VCODE_TYPE_CARRAY: + case VCODE_TYPE_UARRAY: + t->dims = read_u8(f); + t->size = read_u32(f); + t->elem = read_u32(f); + t->bounds = read_u32(f); + break; + + case VCODE_TYPE_POINTER: + case VCODE_TYPE_ACCESS: + t->base = read_u32(f); + break; + + case VCODE_TYPE_FILE: + case VCODE_TYPE_SIGNAL: + t->base = read_u32(f); + break; + + case VCODE_TYPE_RECORD: + { + t->name = ident_read(ident_rd_ctx); + const uint32_t index = read_u32(f); + if (index != 0) + t->uniq.type = type_read_recall(tree_ctx, index); + else + t->uniq.type = NULL; + vcode_type_array_resize(&(t->fields), read_u32(f), 0); + for (unsigned j = 0; j < t->fields.count; j++) + t->fields.items[j] = read_u32(f); + break; + } + } + } + + var_array_resize(&(unit->vars), read_u32(f), 0); + for (unsigned i = 0; i < unit->vars.count; i++) { + var_t *v = &(unit->vars.items[i]); + v->type = read_u32(f); + v->bounds = read_u32(f); + v->name = ident_read(ident_rd_ctx); + v->flags = read_u32(f); + } + + signal_array_resize(&(unit->signals), read_u32(f), 0); + for (unsigned i = 0; i < unit->signals.count; i++) { + signal_t *s = &(unit->signals.items[i]); + s->type = read_u32(f); + s->bounds = read_u32(f); + s->name = ident_read(ident_rd_ctx); + s->shadow = read_u32(f); + s->flags = read_u32(f); + s->nnets = read_u32(f); + s->nets = xmalloc(sizeof(uint32_t) * s->nnets); + for (unsigned j = 0; j < s->nnets; j++) + s->nets[j] = read_u32(f); + } + + param_array_resize(&(unit->params), read_u32(f), 0); + for (unsigned i = 0; i < unit->params.count; i++) { + param_t *p = &(unit->params.items[i]); + p->type = read_u32(f); + p->bounds = read_u32(f); + p->name = ident_read(ident_rd_ctx); + p->reg = read_u32(f); + } + + vcode_registry_add(unit); + + return true; +} + +void vcode_read(fbuf_t *f, tree_rd_ctx_t tree_ctx) +{ + if (read_u32(f) != VCODE_MAGIC) + fatal("%s has invalid vcode header", fbuf_file_name(f)); + + const uint8_t version = read_u8(f); + if (version != VCODE_VERSION) + fatal("%s was created with vcode format version %d (expected %d)", + fbuf_file_name(f), version, VCODE_VERSION); + + ident_rd_ctx_t ident_rd_ctx = ident_read_begin(f); + + while (vcode_read_unit(f, tree_ctx, ident_rd_ctx)) + ; + + ident_read_end(ident_rd_ctx); +} + +#if VCODE_CHECK_UNIONS +#define OP_USE_COUNT_U0(x) \ + (OP_HAS_CMP(x) + OP_HAS_VALUE(x) + OP_HAS_REAL(x) + \ + OP_HAS_COMMENT(x) + OP_HAS_SIGNAL(x) + OP_HAS_DIM(x) + \ + OP_HAS_HOPS(x) + OP_HAS_FIELD(x) + OP_HAS_HINT(x) + \ + OP_HAS_TAG(x)) +#define OP_USE_COUNT_U1(x) \ + (OP_HAS_SUBKIND(x) + OP_HAS_FUNC(x) + OP_HAS_ADDRESS(x)) +#define OP_USE_COUNT_U2(x) \ + (OP_HAS_BOOKMARK(x) + OP_HAS_TARGET(x)) + +__attribute__((constructor)) +static void vcode_check_unions(void) +{ + printf("sizeof(op_t) = %ld\n", sizeof(op_t)); + for (int i = 0; i < 256; i++) { + assert(OP_USE_COUNT_U0(i) <= 1); + assert(OP_USE_COUNT_U1(i) <= 1); + assert(OP_USE_COUNT_U2(i) <= 1); + } +} +#endif // VCODE_CHECK_UNIONS diff --git a/src/vcode.h b/src/vcode.h index c28d1bbd..6c1433f0 100644 --- a/src/vcode.h +++ b/src/vcode.h @@ -1,5 +1,5 @@ // -// Copyright (C) 2014-2015 Nick Gasson +// Copyright (C) 2014-2016 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 @@ -128,6 +128,7 @@ typedef enum { VCODE_OP_HEAP_SAVE, VCODE_OP_HEAP_RESTORE, VCODE_OP_NESTED_RESUME, + VCODE_OP_UNDEFINED, } vcode_op_t; typedef enum { @@ -147,7 +148,8 @@ typedef enum { VCODE_UNIT_PROCESS, VCODE_UNIT_CONTEXT, VCODE_UNIT_FUNCTION, - VCODE_UNIT_PROCEDURE + VCODE_UNIT_PROCEDURE, + VCODE_UNIT_THUNK } vunit_kind_t; typedef enum { @@ -166,6 +168,11 @@ typedef struct { vcode_block_t block; } vcode_state_t; +typedef union { + tree_t tree; + type_t type; +} vcode_bookmark_t; + #define VCODE_INVALID_REG -1 #define VCODE_INVALID_BLOCK -1 #define VCODE_INVALID_VAR -1 @@ -183,7 +190,8 @@ vcode_type_t vtype_signal(vcode_type_t base); vcode_type_t vtype_offset(void); vcode_type_t vtype_time(void); vcode_type_t vtype_char(void); -vcode_type_t vtype_named_record(ident_t name, uint32_t index, bool create); +vcode_type_t vtype_named_record(ident_t name, vcode_bookmark_t uniq, + bool create); void vtype_set_record_fields(vcode_type_t type, const vcode_type_t *field_types, int nfields); vcode_type_t vtype_file(vcode_type_t base); @@ -202,6 +210,10 @@ vcode_type_t vtype_field(vcode_type_t type, int field); vcode_type_t vtype_base(vcode_type_t type); char *vtype_record_name(vcode_type_t type); vcode_type_t vtype_real(void); +vcode_unit_t vcode_find_unit(ident_t name); +vcode_unit_t vcode_unit_next(vcode_unit_t unit); +vcode_unit_t vcode_unit_child(vcode_unit_t unit); +void vcode_unit_unref(vcode_unit_t unit); void vcode_opt(void); void vcode_close(void); @@ -215,11 +227,16 @@ bool vcode_block_empty(void); ident_t vcode_unit_name(void); int vcode_unit_depth(void); bool vcode_unit_pure(void); +bool vcode_unit_has_undefined(void); vunit_kind_t vcode_unit_kind(void); vcode_type_t vcode_unit_result(void); vcode_block_t vcode_active_block(void); vcode_unit_t vcode_active_unit(void); vcode_unit_t vcode_unit_context(void); +void vcode_rewind(void); + +void vcode_write(vcode_unit_t unit, fbuf_t *fbuf); +void vcode_read(fbuf_t *fbuf, tree_rd_ctx_t tree_ctx); void vcode_state_save(vcode_state_t *state); void vcode_state_restore(const vcode_state_t *state); @@ -251,6 +268,7 @@ int64_t vcode_get_value(int op); double vcode_get_real(int op); vcode_cmp_t vcode_get_cmp(int op); uint32_t vcode_get_index(int op); +vcode_bookmark_t vcode_get_bookmark(int op); uint32_t vcode_get_hint(int op); vcode_block_t vcode_get_target(int op, int nth); vcode_var_t vcode_get_address(int op); @@ -263,6 +281,7 @@ unsigned vcode_get_dim(int op); int vcode_get_hops(int op); int vcode_get_field(int op); unsigned vcode_get_subkind(int op); +uint32_t vcode_get_tag(int op); int vcode_count_vars(void); vcode_var_t vcode_find_var(ident_t name); @@ -279,6 +298,7 @@ vcode_unit_t emit_function(ident_t name, vcode_unit_t context, vcode_unit_t emit_procedure(ident_t name, vcode_unit_t context); vcode_unit_t emit_process(ident_t name, vcode_unit_t context); vcode_unit_t emit_context(ident_t name); +vcode_unit_t emit_thunk(ident_t name, vcode_unit_t context, vcode_type_t type); vcode_block_t emit_block(void); vcode_var_t emit_var(vcode_type_t type, vcode_type_t bounds, ident_t name, bool is_const); @@ -300,14 +320,14 @@ vcode_reg_t emit_const_real(double value); vcode_reg_t emit_add(vcode_reg_t lhs, vcode_reg_t rhs); vcode_reg_t emit_sub(vcode_reg_t lhs, vcode_reg_t rhs); vcode_reg_t emit_mul(vcode_reg_t lhs, vcode_reg_t rhs); -vcode_reg_t emit_div(vcode_reg_t lhs, vcode_reg_t rhs, uint32_t index); +vcode_reg_t emit_div(vcode_reg_t lhs, vcode_reg_t rhs, vcode_bookmark_t where); vcode_reg_t emit_exp(vcode_reg_t lhs, vcode_reg_t rhs); vcode_reg_t emit_mod(vcode_reg_t lhs, vcode_reg_t rhs); vcode_reg_t emit_rem(vcode_reg_t lhs, vcode_reg_t rhs); void emit_assert(vcode_reg_t value, vcode_reg_t message, vcode_reg_t length, - vcode_reg_t severity, uint32_t index); + vcode_reg_t severity, vcode_bookmark_t where); void emit_report(vcode_reg_t message, vcode_reg_t length, - vcode_reg_t severity, uint32_t index); + vcode_reg_t severity, vcode_bookmark_t where); vcode_reg_t emit_cmp(vcode_cmp_t cmp, vcode_reg_t lhs, vcode_reg_t rhs); vcode_reg_t emit_fcall(ident_t func, vcode_type_t type, const vcode_reg_t *args, int nargs); @@ -321,15 +341,16 @@ vcode_reg_t emit_load(vcode_var_t var); vcode_reg_t emit_load_indirect(vcode_reg_t reg); void emit_store(vcode_reg_t reg, vcode_var_t var); void emit_store_indirect(vcode_reg_t reg, vcode_reg_t ptr); -void emit_bounds(vcode_reg_t reg, vcode_type_t bounds, - bounds_kind_t kind, uint32_t index, uint32_t hint); +void emit_bounds(vcode_reg_t reg, vcode_type_t bounds, bounds_kind_t kind, + vcode_bookmark_t where, vcode_bookmark_t hint); void emit_dynamic_bounds(vcode_reg_t reg, vcode_reg_t low, vcode_reg_t high, - vcode_reg_t kind, uint32_t index, uint32_t hint); + vcode_reg_t kind, vcode_bookmark_t where, + vcode_bookmark_t hint); void emit_index_check(vcode_reg_t rlow, vcode_reg_t rhigh, vcode_type_t bounds, - bounds_kind_t kind, uint32_t index); + bounds_kind_t kind, vcode_bookmark_t where); void emit_dynamic_index_check(vcode_reg_t rlow, vcode_reg_t rhigh, vcode_reg_t blow, vcode_reg_t bhigh, - bounds_kind_t kind, uint32_t index); + bounds_kind_t kind, vcode_bookmark_t where); vcode_reg_t emit_index(vcode_var_t var, vcode_reg_t offset); vcode_reg_t emit_cast(vcode_type_t type, vcode_reg_t bounds, vcode_reg_t reg); void emit_return(vcode_reg_t reg); @@ -340,7 +361,7 @@ void emit_sched_waveform(vcode_reg_t nets, vcode_reg_t nnets, void emit_cond(vcode_reg_t test, vcode_block_t btrue, vcode_block_t bfalse); vcode_reg_t emit_neg(vcode_reg_t lhs); vcode_reg_t emit_abs(vcode_reg_t lhs); -vcode_reg_t emit_image(vcode_reg_t value, uint32_t index); +vcode_reg_t emit_image(vcode_reg_t value, vcode_bookmark_t index); void emit_comment(const char *fmt, ...) __attribute__((format(printf, 1, 2))); vcode_reg_t emit_select(vcode_reg_t test, vcode_reg_t rtrue, vcode_reg_t rfalse); @@ -360,8 +381,9 @@ vcode_reg_t emit_array_cmp(vcode_cmp_t cmp, vcode_reg_t lhs, vcode_reg_t rhs); vcode_reg_t emit_not(vcode_reg_t arg); vcode_reg_t emit_param_upref(int hops, vcode_reg_t reg); void emit_resolved_address(vcode_var_t var, vcode_signal_t signal); -void emit_set_initial(vcode_signal_t signal, vcode_reg_t value, uint32_t index, - ident_t resolution, vcode_type_t type); +void emit_set_initial(vcode_signal_t signal, vcode_reg_t value, + vcode_bookmark_t index, ident_t resolution, + vcode_type_t type); void emit_alloc_driver(vcode_reg_t all_nets, vcode_reg_t all_length, vcode_reg_t driven_nets, vcode_reg_t driven_length, vcode_reg_t init); @@ -387,17 +409,19 @@ void emit_file_read(vcode_reg_t file, vcode_reg_t ptr, vcode_reg_t inlen, vcode_reg_t outlen); vcode_reg_t emit_null(vcode_type_t type); vcode_reg_t emit_new(vcode_type_t type, vcode_reg_t length); -void emit_null_check(vcode_reg_t ptr, uint32_t index); +void emit_null_check(vcode_reg_t ptr, vcode_bookmark_t index); void emit_deallocate(vcode_reg_t ptr); vcode_reg_t emit_all(vcode_reg_t reg); vcode_reg_t emit_bit_vec_op(bit_vec_op_kind_t kind, vcode_reg_t lhs_data, vcode_reg_t lhs_len, vcode_reg_t lhs_dir, vcode_reg_t rhs_data, vcode_reg_t rhs_len, vcode_reg_t rhs_dir, vcode_type_t result); -vcode_reg_t emit_value(vcode_reg_t string, vcode_reg_t len, uint32_t index); +vcode_reg_t emit_value(vcode_reg_t string, vcode_reg_t len, + vcode_bookmark_t index); vcode_reg_t emit_last_event(vcode_reg_t signal, vcode_reg_t len); void emit_needs_last_value(vcode_signal_t sig); -void emit_array_size(vcode_reg_t llen, vcode_reg_t rlen, uint32_t index); +void emit_array_size(vcode_reg_t llen, vcode_reg_t rlen, + vcode_bookmark_t index); vcode_reg_t emit_bit_shift(bit_shift_kind_t kind, vcode_reg_t data, vcode_reg_t len, vcode_reg_t dir, vcode_reg_t shift, vcode_type_t result); @@ -409,5 +433,6 @@ void emit_cover_stmt(uint32_t tag); void emit_cover_cond(vcode_reg_t test, uint32_t tag, unsigned sub); vcode_reg_t emit_heap_save(void); void emit_heap_restore(vcode_reg_t reg); +vcode_reg_t emit_undefined(vcode_type_t type); #endif // _VCODE_H diff --git a/test/elab/eval1.vhd b/test/elab/eval1.vhd new file mode 100644 index 00000000..aa26805a --- /dev/null +++ b/test/elab/eval1.vhd @@ -0,0 +1,33 @@ +entity sub is + generic ( + N : integer ); +end entity; + +architecture test of sub is + signal x : integer; + constant c : bit_vector(7 downto 0) := X"12"; + + function func(a : integer) return bit is + begin + return c(a); + end function; +begin + + g: if func(N) = '1' generate -- Error + x <= 5; + end generate; + +end architecture; + +------------------------------------------------------------------------------- + +entity eval1 is +end entity; + +architecture test of eval1 is +begin + + sub_i: entity work.sub + generic map (-1); + +end architecture; diff --git a/test/lower/assert1.vhd b/test/lower/assert1.vhd new file mode 100644 index 00000000..a58f9c82 --- /dev/null +++ b/test/lower/assert1.vhd @@ -0,0 +1,15 @@ +entity assert1 is +end entity; + +architecture test of assert1 is +begin + + process is + variable b : boolean; + begin + b := true; + assert b; -- Should be optimised in vcode + wait; + end process; + +end architecture; diff --git a/test/lower/iffold.vhd b/test/lower/iffold.vhd new file mode 100644 index 00000000..fa699358 --- /dev/null +++ b/test/lower/iffold.vhd @@ -0,0 +1,32 @@ +entity sub is + generic ( + N : integer ); +end entity; + +architecture test of sub is + signal x, y : integer; +begin + + process is + begin + if N > 3 then + x <= 5; + else + y <= 6; + end if; + end process; + +end architecture; + +------------------------------------------------------------------------------- + +entity iffold is +end entity; + +architecture test of iffold is +begin + + sub_i: entity work.sub + generic map ( N => 4 ); + +end architecture; diff --git a/test/lower/real1.vhd b/test/lower/real1.vhd new file mode 100644 index 00000000..70c6272f --- /dev/null +++ b/test/lower/real1.vhd @@ -0,0 +1,22 @@ +entity real1 is +end entity; + +architecture test of real1 is + + function get_neg(x : real) return real is + variable r : real; + begin + r := -x; + return r; + end function; + + function cast_mul(x : integer) return real is + variable z : integer; + begin + z := x; + return real(z) * 0.5; + end function; + +begin + +end architecture; diff --git a/test/lower/thunk.vhd b/test/lower/thunk.vhd new file mode 100644 index 00000000..168612a8 --- /dev/null +++ b/test/lower/thunk.vhd @@ -0,0 +1,22 @@ +package pack is + type rec is record + x, y : integer; + end record; + + type rec_vec is array (integer range <>) of rec; + + constant c : bit_vector(3 downto 0) := X"f"; + constant d : rec_vec(1 to 2); +end package; + +entity top is +end entity; + +use work.pack.all; + +architecture test of top is + signal x : bit := not c(1); + signal y : integer := d(1).x + 1; +begin + +end architecture; diff --git a/test/lower/wait1.vhd b/test/lower/wait1.vhd index 2334190c..caa9cd1b 100644 --- a/test/lower/wait1.vhd +++ b/test/lower/wait1.vhd @@ -5,7 +5,7 @@ end entity; architecture test of wait1 is begin - process is + p1: process is begin assert now = 0 ns; wait_1: wait for 1 ns; diff --git a/test/regress/arith1.vhd b/test/regress/arith1.vhd index 1dbb247d..027c5078 100644 --- a/test/regress/arith1.vhd +++ b/test/regress/arith1.vhd @@ -26,10 +26,11 @@ begin x := -34; assert abs x = 34; assert abs y = 12; - assert 5 mod 3 = 2; - assert 5 rem 3 = 2; - assert (-5) rem 3 = -2; - assert (-5) mod 3 = 2; + y := 3; + assert 5 mod y = 2; + assert 5 rem y = 2; + assert (-5) rem y = -2; + assert (-5) mod y = 2; assert x = +x; wait; end process; diff --git a/test/regress/bitvec.vhd b/test/regress/bitvec.vhd index 7a3e5a12..0728a88e 100644 --- a/test/regress/bitvec.vhd +++ b/test/regress/bitvec.vhd @@ -13,8 +13,11 @@ begin process is variable b : bit_vector(3 downto 0); + variable n : integer; begin b := "1101"; + n := 2; + wait for 1 ns; assert not b = "0010"; assert (b and "1010") = "1000"; assert (b or "0110") = "1111"; @@ -22,7 +25,7 @@ begin assert (b xnor "0111") = "0101"; assert (b nand "1010") = "0111"; assert (b nor "0110") = "0000"; - assert get_bitvec(1, 2) = "00"; + assert get_bitvec(1, n) = "00"; wait; end process; diff --git a/test/regress/const6.vhd b/test/regress/const6.vhd index 59193bbc..c84ef66f 100644 --- a/test/regress/const6.vhd +++ b/test/regress/const6.vhd @@ -24,8 +24,10 @@ architecture test of const6 is begin process is + variable x : bit_vector(3 downto 0) := "1010"; begin - assert get_left("1010") = '1'; + wait for 0 ns; -- Prevent constant folding + assert get_left(x) = '1'; wait; end process; diff --git a/test/regress/func8.vhd b/test/regress/func8.vhd index 70a77fd4..2c8986e8 100644 --- a/test/regress/func8.vhd +++ b/test/regress/func8.vhd @@ -15,9 +15,14 @@ architecture test of func8 is begin process is + variable x : integer; begin - assert lookup(0) = 0.62; - assert lookup(2) = 71.7; + x := 0; + wait for 0 ns; + assert lookup(x) = 0.62; -- Avoid constant folding + x := 2; + wait for 0 ns; + assert lookup(x) = 71.7; wait; end process; diff --git a/test/regress/ieee4.vhd b/test/regress/ieee4.vhd index 7a56fb0e..19466909 100644 --- a/test/regress/ieee4.vhd +++ b/test/regress/ieee4.vhd @@ -17,13 +17,27 @@ begin variable s1, s2 : integer; variable r : real; begin - assert approx(sign(6.8), 1.0); - assert approx(ceil(5.7), 6.0); - assert approx(floor(0.6), 0.0); - assert approx(round(0.5), 1.0); - assert approx(round(6.4999), 6.0); - assert approx(trunc(0.999), 0.0); - assert approx(4.6 mod 2.7, 1.9); + r := 6.8; + wait for 0 ns; -- Prevent constant folding + assert approx(sign(r), 1.0); + r := 5.7; + wait for 0 ns; + assert approx(ceil(r), 6.0); + r := 0.6; + wait for 0 ns; + assert approx(floor(r), 0.0); + r := 0.5; + wait for 0 ns; + assert approx(round(r), 1.0); + r := 6.4999; + wait for 0 ns; + assert approx(round(r), 6.0); + r := 0.999; + wait for 0 ns; + assert approx(trunc(r), 0.0); + r := 4.6; + wait for 0 ns; + assert approx(r mod 2.7, 1.9); s1 := 6; s2 := 883; @@ -37,18 +51,43 @@ begin assert approx(r, 0.472620); uniform(s1, s2, r); assert approx(r, 0.582179); - assert approx(sqrt(4.0), 2.0); - assert approx(sqrt(4.3), 2.0736); - assert approx(cbrt(612.8), 8.49388); - assert approx(5 ** 1.2, 6.8986); - assert approx(2.0 ** (-1.0), 0.5); - assert approx(exp(2.0), 7.389056); - assert approx(log(1.0), 0.0); - assert approx(log(MATH_E), 1.0); - assert approx(log(5216.72), 8.5596); - assert approx(sin(MATH_PI), 0.0); - assert approx(cos(1.15251), 0.406195); - assert approx(arctan(0.5), 0.463648); + + r := 4.0; + wait for 0 ns; + assert approx(sqrt(r), 2.0); + r := 4.3; + wait for 0 ns; + assert approx(sqrt(r), 2.0736); + r := 612.8; + wait for 0 ns; + assert approx(cbrt(r), 8.49388); + r := 1.2; + wait for 0 ns; + assert approx(5 ** r, 6.8986); + r := 2.0; + wait for 0 ns; + assert approx(r ** (-1.0), 0.5); + r := 2.0; + wait for 0 ns; + assert approx(exp(r), 7.389056); + r := 1.0; + wait for 0 ns; + assert approx(log(r), 0.0); + r := MATH_E; + wait for 0 ns; + assert approx(log(r), 1.0); + r := 5216.72; + wait for 0 ns; + assert approx(log(r), 8.5596); + r := MATH_PI; + wait for 0 ns; + assert approx(sin(r), 0.0); + r := 1.15251; + wait for 0 ns; + assert approx(cos(r), 0.406195); + r := 0.5; + wait for 0 ns; + assert approx(arctan(r), 0.463648); wait; end process; diff --git a/test/regress/ieee5.vhd b/test/regress/ieee5.vhd new file mode 100644 index 00000000..2034d2fa --- /dev/null +++ b/test/regress/ieee5.vhd @@ -0,0 +1,46 @@ +entity should_fold is + generic ( X : boolean ); +end entity; + +architecture test of should_fold is +begin + assert X; +end architecture; + +------------------------------------------------------------------------------- + +entity ieee5 is +end entity; + +library ieee; +use ieee.math_real.all; + +architecture test of ieee5 is + function approx(x, y : real; t : real := 0.001) return boolean is + begin + return abs(x - y) < t; + end function; +begin + + s1: entity work.should_fold + generic map ( approx(sign(6.8), 1.0) ); + s2: entity work.should_fold + generic map ( approx(ceil(5.7), 6.0) ); + s3: entity work.should_fold + generic map ( approx(floor(0.6), 0.0) ); + s4: entity work.should_fold + generic map ( approx(round(0.5), 1.0) ); + s5: entity work.should_fold + generic map ( approx(round(6.4999), 6.0) ); + s6: entity work.should_fold + generic map ( approx(trunc(0.999), 0.0) ); + s7: entity work.should_fold + generic map ( approx(4.6 mod 2.7, 1.9) ); + s8: entity work.should_fold + generic map ( approx(sin(MATH_PI), 0.0) ); + s9: entity work.should_fold + generic map ( approx(cos(1.15251), 0.406195) ); + s10: entity work.should_fold + generic map ( approx(arctan(0.5), 0.463648) ); + +end architecture; diff --git a/test/regress/issue143.vhd b/test/regress/issue143.vhd index 5a26bfa1..3e82864f 100644 --- a/test/regress/issue143.vhd +++ b/test/regress/issue143.vhd @@ -38,8 +38,9 @@ package body access_field_through_function_pkg is end function; function access_field_fun2 return integer is + variable x : integer := 10; begin - return fun(10).field; -- <-- works + return fun(x).field; -- <-- works end function; function access_field_fun3 return integer is @@ -60,9 +61,10 @@ architecture test of issue143 is begin process is + variable x : integer := 4; begin assert fun.field = 0; - assert fun(4).field = 4; + assert fun(x).field = 4; assert access_field_fun1 = 0; assert access_field_fun2 = 10; assert access_field_fun3 = 0; diff --git a/test/regress/proc3.vhd b/test/regress/proc3.vhd index 63c36c65..bedc0bd9 100644 --- a/test/regress/proc3.vhd +++ b/test/regress/proc3.vhd @@ -75,8 +75,8 @@ begin p4(5, x); assert x = 10; assert now = 105 ns; - x := func(9); - assert x = 10; + x := func(x); + assert x = 11; wait; end process; diff --git a/test/regress/real1.vhd b/test/regress/real1.vhd index 0711bc85..f23245c7 100644 --- a/test/regress/real1.vhd +++ b/test/regress/real1.vhd @@ -22,9 +22,12 @@ begin assert integer(r) = 6; r := real(5); report real'image(r); - report real'image(-5.262e2); - report real'image(1.23456); - report real'image(2.0 ** (-1)); + r := real(-5.262e2); + report real'image(r); + r := real(1.23456); + report real'image(r); + r := real(2.0); + report real'image(r ** (-1)); report real'image(real'low); report real'image(real'high); wait; diff --git a/test/regress/record6.vhd b/test/regress/record6.vhd index d36a3871..e2130616 100644 --- a/test/regress/record6.vhd +++ b/test/regress/record6.vhd @@ -31,20 +31,21 @@ begin process is variable r : rec; + variable one : integer := 1; -- Prevent constant folding begin r.x := "101"; r.y := 1; assert get_bit(r) = '1'; r.y := 2; assert get_bit(r) = '0'; - assert get_bit(make_rec("011", 2)) = '1'; - r.x := make_rec("010", 1).x; + assert get_bit(make_rec("011", one + 1)) = '1'; + r.x := make_rec("010", one).x; assert r.x = "010"; - r.y := make_rec("010", 1).y; + r.y := make_rec("010", one).y; assert r.y = 1; - r := make_rec("010", 1); + r := make_rec("010", one); assert make_rec_array(r, 1, 2) = ( ("010", 1), ("010", 1) ); - assert make_rec_array(("111", 5), 1, 2) = ( ("111", 5), ("111", 5) ); + assert make_rec_array(("111", 5), one, 2) = ( ("111", 5), ("111", 5) ); wait; end process; diff --git a/test/regress/shift2.vhd b/test/regress/shift2.vhd index 9701dfdb..482503fe 100644 --- a/test/regress/shift2.vhd +++ b/test/regress/shift2.vhd @@ -2,110 +2,111 @@ entity shift2 is end entity; architecture test of shift2 is + signal input : bit_vector(4 downto 0) := "11100"; begin - assert bit_vector'("11100") ror -8 = "00111" + assert bit_vector'(input) ror -8 = "00111" report "ror -8 is broken" severity error; - assert bit_vector'("11100") ror -7 = "10011" + assert bit_vector'(input) ror -7 = "10011" report "ror -7 is broken" severity error; - assert bit_vector'("11100") ror -6 = "11001" + assert bit_vector'(input) ror -6 = "11001" report "ror -6 is broken" severity error; - assert bit_vector'("11100") ror -5 = "11100" + assert bit_vector'(input) ror -5 = input report "ror -5 is broken" severity error; - assert bit_vector'("11100") ror -4 = "01110" + assert bit_vector'(input) ror -4 = "01110" report "ror -4 is broken" severity error; - assert bit_vector'("11100") ror -3 = "00111" + assert bit_vector'(input) ror -3 = "00111" report "ror -3 is broken" severity error; - assert bit_vector'("11100") ror -2 = "10011" + assert bit_vector'(input) ror -2 = "10011" report "ror -2 is broken" severity error; - assert bit_vector'("11100") ror -1 = "11001" + assert bit_vector'(input) ror -1 = "11001" report "ror -1 is broken" severity error; - assert bit_vector'("11100") ror 0 = "11100" + assert bit_vector'(input) ror 0 = input report "ror 0 is broken" severity error; - assert bit_vector'("11100") ror 1 = "01110" + assert bit_vector'(input) ror 1 = "01110" report "ror 1 is broken" severity error; - assert bit_vector'("11100") ror 2 = "00111" + assert bit_vector'(input) ror 2 = "00111" report "ror 2 is broken" severity error; - assert bit_vector'("11100") ror 3 = "10011" + assert bit_vector'(input) ror 3 = "10011" report "ror 3 is broken" severity error; - assert bit_vector'("11100") ror 4 = "11001" + assert bit_vector'(input) ror 4 = "11001" report "ror 4 is broken" severity error; - assert bit_vector'("11100" ror 5) = "11100" + assert bit_vector'(input ror 5) = input report "ror 5 is broken" severity error; - assert bit_vector'("11100") ror 6 = "01110" + assert bit_vector'(input) ror 6 = "01110" report "ror 6 is broken" severity error; - assert bit_vector'("11100") ror 7 = "00111" + assert bit_vector'(input) ror 7 = "00111" report "ror 7 is broken" severity error; - assert bit_vector'("11100") ror 8 = "10011" + assert bit_vector'(input) ror 8 = "10011" report "ror 8 is broken" severity error; -- ROL - assert bit_vector'("11100") rol -8 = "10011" + assert bit_vector'(input) rol -8 = "10011" report "rol -8 is broken" severity error; - assert bit_vector'("11100") rol -7 = "00111" + assert bit_vector'(input) rol -7 = "00111" report "rol -7 is broken" severity error; - assert bit_vector'("11100") rol -6 = "01110" + assert bit_vector'(input) rol -6 = "01110" report "rol -6 is broken" severity error; - assert bit_vector'("11100" rol -5) = "11100" + assert bit_vector'(input rol -5) = input report "rol -5 is broken" severity error; - assert bit_vector'("11100") rol -4 = "11001" + assert bit_vector'(input) rol -4 = "11001" report "rol -4 is broken" severity error; - assert bit_vector'("11100") rol -3 = "10011" + assert bit_vector'(input) rol -3 = "10011" report "rol -3 is broken" severity error; - assert bit_vector'("11100") rol -2 = "00111" + assert bit_vector'(input) rol -2 = "00111" report "rol -2 is broken" severity error; - assert bit_vector'("11100") rol -1 = "01110" + assert bit_vector'(input) rol -1 = "01110" report "rol -1 is broken" severity error; - assert bit_vector'("11100") rol 0 = "11100" + assert bit_vector'(input) rol 0 = input report "rol 0 is broken" severity error; - assert bit_vector'("11100") rol 1 = "11001" + assert bit_vector'(input) rol 1 = "11001" report "rol 1 is broken" severity error; - assert bit_vector'("11100") rol 2 = "10011" + assert bit_vector'(input) rol 2 = "10011" report "rol 2 is broken" severity error; - assert bit_vector'("11100") rol 3 = "00111" + assert bit_vector'(input) rol 3 = "00111" report "rol 3 is broken" severity error; - assert bit_vector'("11100") rol 4 = "01110" + assert bit_vector'(input) rol 4 = "01110" report "rol 4 is broken" severity error; - assert bit_vector'("11100") rol 5 = "11100" + assert bit_vector'(input) rol 5 = input report "rol 5 is broken" severity error; - assert bit_vector'("11100") rol 6 = "11001" + assert bit_vector'(input) rol 6 = "11001" report "rol 6 is broken" severity error; - assert bit_vector'("11100") rol 7 = "10011" + assert bit_vector'(input) rol 7 = "10011" report "rol 7 is broken" severity error; - assert bit_vector'("11100") rol 8 = "00111" + assert bit_vector'(input) rol 8 = "00111" report "rol 8 is broken" severity error; end architecture; diff --git a/test/regress/testlist.txt b/test/regress/testlist.txt index 899482df..fa9fbdac 100644 --- a/test/regress/testlist.txt +++ b/test/regress/testlist.txt @@ -320,3 +320,4 @@ vhpi3 normal,vhpi jcore6 normal issue106 normal,2008 case7 normal,2008 +ieee5 normal diff --git a/test/simp/cfold.vhd b/test/simp/cfold.vhd index 0e7cee4e..989639ae 100644 --- a/test/simp/cfold.vhd +++ b/test/simp/cfold.vhd @@ -83,14 +83,15 @@ begin process type int2_vec is array (66 to 67) of integer; + variable b : boolean; begin - assert a1'length = 5; - assert a1'low(1) = 1; - assert a1'high(1) = 5; - assert a1'left = 1; - assert a1'right = 5; - assert int2_vec'length = 2; - assert int2_vec'low = 66; + b := a1'length = 5; + b := a1'low(1) = 1; + b := a1'high(1) = 5; + b := a1'left = 1; + b := a1'right = 5; + b := int2_vec'length = 2; + b := int2_vec'low = 66; end process; process is @@ -107,6 +108,7 @@ begin r := 1.5 * 2; r := 3 * 0.2; r := 5.0 / 2; + r := 2.0 ** 4; end process; process is diff --git a/test/simp/ffold.vhd b/test/simp/ffold.vhd index 2a305db1..cd374205 100644 --- a/test/simp/ffold.vhd +++ b/test/simp/ffold.vhd @@ -79,14 +79,141 @@ architecture a of ffold is return r; end function; - signal s1 : integer := add1(5); - signal s2 : integer := add4(1); - signal s3 : integer := log2(11); - signal s4 : integer := log2(integer(real'(5.5))); - signal s5 : integer := case1(1); - signal s6 : integer := case1(7); - signal s7 : integer := adddef; - signal s8 : boolean := chain2("foo", "hello"); + function flip(x : bit_vector(3 downto 0)) return bit_vector is + variable r : bit_vector(3 downto 0); + begin + r(0) := x(3); + r(1) := x(2); + r(2) := x(1); + r(3) := x(0); + return r; + end function; + + type real_vector is array (natural range <>) of real; + + function lookup(index : integer) return real is + constant table : real_vector := ( + 0.62, 61.62, 71.7, 17.25, 26.15, 651.6, 0.45, 5.761 ); + begin + return table(index); + end function; + + function get_bitvec(x, y : integer) return bit_vector is + variable r : bit_vector(x to y) := "00"; + begin + return r; + end function; + + function approx(x, y : real; t : real := 0.001) return boolean is + begin + return abs(x - y) < t; + end function; + + function get_string(x : integer) return string is + begin + return integer'image(x); + end function; + + function get_string(x : real) return string is + begin + return real'image(x); + end function; + + function get_string(x : character) return string is + begin + return character'image(x); + end function; + + function get_string(x : time) return string is + begin + return time'image(x); + end function; + + function needs_heap(x : integer) return integer is + begin + if integer'image(x)'length = 2 then + return x * 2; + else + return x / 2; + end if; + end function; + + function sum_left_right(x : bit_vector) return integer is + begin + return x'left + x'right; + end function; + + procedure p5(x : in integer; y : out integer) is + variable k : integer := x + 1; + begin + y := k; + end procedure; + + function call_proc(x : in integer) return integer is + variable y : integer; + begin + p5(x, y); + return y; + end function; + + type rec is record + x : bit_vector(1 to 3); + y : integer; + end record; + + function make_rec(x : bit_vector(1 to 3); y : integer) return rec is + variable r : rec; + begin + r.x := x; + r.y := y; + return r; + end function; + + function min(x, y : integer) return integer is + begin + if x > y then + return y; + else + return x; + end if; + end function; + + function get_left(x : bit_vector) return bit is + constant l : integer := x'left; + variable v : bit_vector(1 to x'right); + constant m : integer := min(x'length, v'length) + 1; + begin + return x(l); + end function; + begin + b1: block is + signal s0 : integer := add1(5); + signal s1 : integer := add4(1); + signal s2 : integer := log2(11); + signal s3 : integer := log2(integer(real'(5.5))); + signal s4 : integer := case1(1); + signal s5 : integer := case1(7); + signal s6 : integer := adddef; + signal s7 : boolean := chain2("foo", "hello"); + signal s8 : boolean := flip("1010") = "0101"; + signal s9 : boolean := flip("1010") = "0111"; + signal s10 : real := lookup(0); -- 0.62; + signal s11 : real := lookup(2); -- 71.7; + signal s12 : boolean := get_bitvec(1, 2) = "00"; + signal s13 : boolean := approx(1.0000, 1.0001); + signal s14 : boolean := approx(1.0000, 1.01); + signal s15 : boolean := get_string(5) = "5"; + signal s16 : boolean := get_string(2.5) = "2.5"; + signal s17 : boolean := get_string('F') = "'F'"; + signal s18 : boolean := get_string(1 fs) = "1 FS"; + signal s19 : integer := needs_heap(40); + signal s20 : integer := sum_left_right("101010"); + signal s21 : integer := call_proc(1); + signal s22 : boolean := make_rec("010", 1).y = 1; + signal s23 : boolean := get_left("1010") = '1'; + begin + end block; + end architecture; diff --git a/test/simp/shift2.vhd b/test/simp/shift2.vhd new file mode 100644 index 00000000..3baa8420 --- /dev/null +++ b/test/simp/shift2.vhd @@ -0,0 +1,123 @@ +entity shift2 is +end entity; + +architecture test of shift2 is +begin + + assert bit_vector'("11100") ror -8 = "00111" + report "ror -8 is broken" severity error; + + assert bit_vector'("11100") ror -7 = "10011" + report "ror -7 is broken" severity error; + + assert bit_vector'("11100") ror -6 = "11001" + report "ror -6 is broken" severity error; + + assert bit_vector'("11100") ror -5 = "11100" + report "ror -5 is broken" severity error; + + assert bit_vector'("11100") ror -4 = "01110" + report "ror -4 is broken" severity error; + + assert bit_vector'("11100") ror -3 = "00111" + report "ror -3 is broken" severity error; + + assert bit_vector'("11100") ror -2 = "10011" + report "ror -2 is broken" severity error; + + assert bit_vector'("11100") ror -1 = "11001" + report "ror -1 is broken" severity error; + + assert bit_vector'("11100") ror 0 = "11100" + report "ror 0 is broken" severity error; + + assert bit_vector'("11100") ror 1 = "01110" + report "ror 1 is broken" severity error; + + assert bit_vector'("11100") ror 2 = "00111" + report "ror 2 is broken" severity error; + + assert bit_vector'("11100") ror 3 = "10011" + report "ror 3 is broken" severity error; + + assert bit_vector'("11100") ror 4 = "11001" + report "ror 4 is broken" severity error; + + assert bit_vector'("11100" ror 5) = "11100" + report "ror 5 is broken" severity error; + + assert bit_vector'("11100") ror 6 = "01110" + report "ror 6 is broken" severity error; + + assert bit_vector'("11100") ror 7 = "00111" + report "ror 7 is broken" severity error; + + assert bit_vector'("11100") ror 8 = "10011" + report "ror 8 is broken" severity error; + + -- ROL + + assert bit_vector'("11100") rol -8 = "10011" + report "rol -8 is broken" severity error; + + assert bit_vector'("11100") rol -7 = "00111" + report "rol -7 is broken" severity error; + + assert bit_vector'("11100") rol -6 = "01110" + report "rol -6 is broken" severity error; + + assert bit_vector'("11100" rol -5) = "11100" + report "rol -5 is broken" severity error; + + assert bit_vector'("11100") rol -4 = "11001" + report "rol -4 is broken" severity error; + + assert bit_vector'("11100") rol -3 = "10011" + report "rol -3 is broken" severity error; + + assert bit_vector'("11100") rol -2 = "00111" + report "rol -2 is broken" severity error; + + assert bit_vector'("11100") rol -1 = "01110" + report "rol -1 is broken" severity error; + + assert bit_vector'("11100") rol 0 = "11100" + report "rol 0 is broken" severity error; + + assert bit_vector'("11100") rol 1 = "11001" + report "rol 1 is broken" severity error; + + assert bit_vector'("11100") rol 2 = "10011" + report "rol 2 is broken" severity error; + + assert bit_vector'("11100") rol 3 = "00111" + report "rol 3 is broken" severity error; + + assert bit_vector'("11100") rol 4 = "01110" + report "rol 4 is broken" severity error; + + assert bit_vector'("11100") rol 5 = "11100" + report "rol 5 is broken" severity error; + + assert bit_vector'("11100") rol 6 = "11001" + report "rol 6 is broken" severity error; + + assert bit_vector'("11100") rol 7 = "10011" + report "rol 7 is broken" severity error; + + assert bit_vector'("11100") rol 8 = "00111" + report "rol 8 is broken" severity error; + + -- Misc + + assert ("1011" sll 1) = "0110"; + assert ("1011" srl 1) = "0101"; + assert ("1011" sla 1) = "0111"; + assert ("1011" sra 1) = "1101"; + + assert ("1011" srl -1) = "0110"; + assert ("1011" sll -1) = "0101"; + assert ("1011" sra -1) = "0111"; + assert ("1011" sla -1) = "1101"; + +end architecture; diff --git a/test/test_elab.c b/test/test_elab.c index 17b87987..5369b7b9 100644 --- a/test/test_elab.c +++ b/test/test_elab.c @@ -429,6 +429,21 @@ START_TEST(test_jcore1) } END_TEST +START_TEST(test_eval1) +{ + input_from_file(TESTDIR "/elab/eval1.vhd"); + + const error_t expect[] = { + { 12, "array index -1 outside bounds 7 downto 0" }, + { 16, "while evaluating call to FUNC" }, + { -1, NULL } + }; + expect_errors(expect); + + fail_unless(run_elab() == NULL); +} +END_TEST + int main(void) { Suite *s = suite_create("elab"); @@ -461,6 +476,7 @@ int main(void) tcase_add_test(tc, test_libbind3); tcase_add_test(tc, test_issue251); tcase_add_test(tc, test_jcore1); + tcase_add_test(tc, test_eval1); suite_add_tcase(s, tc); return nvc_run_test(s); diff --git a/test/test_ident.c b/test/test_ident.c index 2d5d1f97..c910b5e2 100644 --- a/test/test_ident.c +++ b/test/test_ident.c @@ -166,6 +166,8 @@ START_TEST(test_until) ident_t i; i = ident_new("aye.bee.c"); fail_unless(ident_until(i, '.') == ident_new("aye")); + i = ident_new("nodot"); + fail_unless(ident_until(i, '.') == i); } END_TEST @@ -174,6 +176,8 @@ START_TEST(test_runtil) ident_t i; i = ident_new("aye.bee.c"); fail_unless(ident_runtil(i, '.') == ident_new("aye.bee")); + i = ident_new("nodot"); + fail_unless(ident_runtil(i, '.') == i); } END_TEST @@ -240,6 +244,17 @@ START_TEST(test_interned) } END_TEST +START_TEST(test_contains) +{ + ident_t i = ident_new("cake"); + fail_unless(ident_contains(i, "k")); + fail_unless(ident_contains(i, "moa")); + fail_unless(ident_contains(i, "amo")); + fail_if(ident_contains(i, "zod")); + fail_if(ident_contains(i, "")); +} +END_TEST + int main(void) { srandom((unsigned)time(NULL)); @@ -262,6 +277,7 @@ int main(void) tcase_add_test(tc_core, test_rfrom); tcase_add_test(tc_core, test_from); tcase_add_test(tc_core, test_interned); + tcase_add_test(tc_core, test_contains); suite_add_tcase(s, tc_core); SRunner *sr = srunner_create(s); diff --git a/test/test_lower.c b/test/test_lower.c index f6de92c7..e72d99cf 100644 --- a/test/test_lower.c +++ b/test/test_lower.c @@ -22,7 +22,8 @@ typedef struct { int hops; int field; int subkind; - uint32_t index; + uint32_t tag; + double real; } check_bb_t; #define CAT(x, y) x##y @@ -82,6 +83,14 @@ static void check_bb(int bb, const check_bb_t *expect, int len) } break; + case VCODE_OP_CONST_REAL: + if (e->real != vcode_get_real(i)) { + vcode_dump(); + fail("expected op %d in block %d to have constant %lf but has %lf", + i, bb, e->real, vcode_get_real(i)); + } + break; + case VCODE_OP_CMP: if (e->cmp != vcode_get_cmp(i)) { vcode_dump(); @@ -266,10 +275,10 @@ static void check_bb(int bb, const check_bb_t *expect, int len) // Fall-through case VCODE_OP_COVER_STMT: - if (e->index != vcode_get_index(i)) { + if (e->tag != vcode_get_tag(i)) { vcode_dump(); fail("expected op %d in block %d to have cover tag %d but has %d", - i, bb, e->index, vcode_get_index(i)); + i, bb, e->tag, vcode_get_tag(i)); } break; @@ -284,6 +293,17 @@ static void check_bb(int bb, const check_bb_t *expect, int len) } } +static vcode_unit_t find_unit(tree_t t) +{ + ident_t name = tree_attr_str(t, mangled_i); + if (name == NULL) + name = tree_ident(t); + vcode_unit_t vu = vcode_find_unit(name); + if (vu == NULL) + fail("missing vcode unit for %s", istr(name)); + return vu; +} + START_TEST(test_wait1) { input_from_file(TESTDIR "/lower/wait1.vhd"); @@ -296,7 +316,7 @@ START_TEST(test_wait1) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); const check_bb_t bb0[] = { @@ -360,7 +380,7 @@ START_TEST(test_assign1) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); fail_unless(vcode_count_vars() == 2); @@ -441,7 +461,7 @@ START_TEST(test_assign2) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -507,7 +527,7 @@ START_TEST(test_signal1) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t vc = tree_code(e); + vcode_unit_t vc = find_unit(e); vcode_select_unit(vc); { @@ -526,7 +546,7 @@ START_TEST(test_signal1) fail_unless(vcode_signal_count_nets(0) == 1); fail_unless(vcode_signal_nets(0)[0] == 0); - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); { @@ -571,7 +591,7 @@ START_TEST(test_cond1) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -654,7 +674,7 @@ START_TEST(test_arith1) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -775,7 +795,7 @@ START_TEST(test_pack1) tree_t add1 = tree_decl(body, 0); fail_unless(tree_kind(add1) == T_FUNC_BODY); - vcode_unit_t v0 = tree_code(add1); + vcode_unit_t v0 = find_unit(add1); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -801,7 +821,7 @@ START_TEST(test_func1) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -815,9 +835,9 @@ START_TEST(test_func1) EXPECT_BB(1) = { { VCODE_OP_CONST, .value = 2 }, #if LLVM_MANGLES_NAMES - { VCODE_OP_FCALL, .func = ":func1:add1__II", .args = 1 }, + { VCODE_OP_FCALL, .func = "WORK.FUNC1-TEST.ADD1__II", .args = 1 }, #else - { VCODE_OP_FCALL, .func = ":func1:add1$II", .args = 1 }, + { VCODE_OP_FCALL, .func = "WORK.FUNC1-TEST.ADD1$II", .args = 1 }, #endif { VCODE_OP_STORE, .name = "R" }, { VCODE_OP_WAIT, .target = 2 } @@ -853,7 +873,7 @@ START_TEST(test_arrayop1) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -933,7 +953,7 @@ START_TEST(test_array1) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); EXPECT_BB(1) = { @@ -971,7 +991,7 @@ START_TEST(test_nest1) tree_t p = tree_stmt(e, 0); { - vcode_unit_t v0 = tree_code(p); + vcode_unit_t v0 = find_unit(p); vcode_select_unit(v0); EXPECT_BB(1) = { @@ -997,7 +1017,7 @@ START_TEST(test_nest1) fail_unless(tree_kind(f1) == T_FUNC_BODY); { - vcode_unit_t v0 = tree_code(f1); + vcode_unit_t v0 = find_unit(f1); vcode_select_unit(v0); #if LLVM_MANGLES_NAMES @@ -1026,7 +1046,7 @@ START_TEST(test_nest1) fail_unless(tree_kind(f2) == T_FUNC_BODY); { - vcode_unit_t v0 = tree_code(f2); + vcode_unit_t v0 = find_unit(f2); vcode_select_unit(v0); #if LLVM_MANGLES_NAMES @@ -1063,7 +1083,7 @@ START_TEST(test_signal2) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -1099,7 +1119,7 @@ START_TEST(test_attr1) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); EXPECT_BB(1) = { @@ -1152,7 +1172,7 @@ START_TEST(test_assign3) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); EXPECT_BB(1) = { @@ -1183,7 +1203,7 @@ START_TEST(test_record1) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -1244,7 +1264,7 @@ START_TEST(test_signal4) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); EXPECT_BB(1) = { @@ -1278,7 +1298,7 @@ START_TEST(test_staticwait) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -1317,16 +1337,16 @@ START_TEST(test_proc1) lower_unit(e); { - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); EXPECT_BB(1) = { { VCODE_OP_LOAD, .name = "A" }, { VCODE_OP_INDEX, .name = "B" }, #if LLVM_MANGLES_NAMES - { VCODE_OP_FCALL, .func = ":proc1:add1__vII", .args = 2 }, + { VCODE_OP_FCALL, .func = "WORK.PROC1-TEST.ADD1__vII", .args = 2 }, #else - { VCODE_OP_FCALL, .func = ":proc1:add1$vII", .args = 2 }, + { VCODE_OP_FCALL, .func = "WORK.PROC1-TEST.ADD1$vII", .args = 2 }, #endif { VCODE_OP_CONST, .value = 2 }, { VCODE_OP_LOAD, .name = "B" }, @@ -1335,9 +1355,9 @@ START_TEST(test_proc1) { VCODE_OP_ASSERT }, { VCODE_OP_CONST, .value = 5 }, #if LLVM_MANGLES_NAMES - { VCODE_OP_FCALL, .func = ":proc1:add1__vII", .args = 2 }, + { VCODE_OP_FCALL, .func = "WORK.PROC1-TEST.ADD1__vII", .args = 2 }, #else - { VCODE_OP_FCALL, .func = ":proc1:add1$vII", .args = 2 }, + { VCODE_OP_FCALL, .func = "WORK.PROC1-TEST.ADD1$vII", .args = 2 }, #endif { VCODE_OP_LOAD, .name = "B" }, { VCODE_OP_CONST, .value = 6 }, @@ -1350,7 +1370,7 @@ START_TEST(test_proc1) } { - vcode_unit_t v0 = tree_code(tree_decl(e, 1)); + vcode_unit_t v0 = find_unit(tree_decl(e, 1)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -1378,7 +1398,7 @@ START_TEST(test_while1) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); EXPECT_BB(2) = { @@ -1415,7 +1435,7 @@ START_TEST(test_loop1) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); EXPECT_BB(3) = { @@ -1457,7 +1477,7 @@ START_TEST(test_proc3) lower_unit(e); { - vcode_unit_t v0 = tree_code(tree_decl(e, 1)); + vcode_unit_t v0 = find_unit(tree_decl(e, 1)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -1478,15 +1498,16 @@ START_TEST(test_proc3) } { - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); EXPECT_BB(1) = { { VCODE_OP_INDEX, .name = "X" }, #if LLVM_MANGLES_NAMES - { VCODE_OP_PCALL, .func = ":proc3:p1__vI", .target = 2, .args = 1 }, + { VCODE_OP_PCALL, .func = "WORK.PROC3-TEST.P1__vI", + .target = 2, .args = 1 }, #else - { VCODE_OP_PCALL, .func = ":proc3:p1$vI", .target = 2, .args = 1 } + { VCODE_OP_PCALL, .func = "WORK.PROC3-TEST.P1$vI", .target = 2, .args = 1 } #endif }; @@ -1494,9 +1515,9 @@ START_TEST(test_proc3) EXPECT_BB(2) = { #if LLVM_MANGLES_NAMES - { VCODE_OP_RESUME, .func = ":proc3:p1__vI" }, + { VCODE_OP_RESUME, .func = "WORK.PROC3-TEST.P1__vI" }, #else - { VCODE_OP_RESUME, .func = ":proc3:p1$vI" }, + { VCODE_OP_RESUME, .func = "WORK.PROC3-TEST.P1$vI" }, #endif { VCODE_OP_WAIT, .target = 3 } }; @@ -1518,7 +1539,7 @@ START_TEST(test_loop2) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_decl(e, 1)); + vcode_unit_t v0 = find_unit(tree_decl(e, 1)); vcode_select_unit(v0); EXPECT_BB(2) = { @@ -1549,7 +1570,7 @@ START_TEST(test_slice1) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); EXPECT_BB(1) = { @@ -1593,7 +1614,7 @@ START_TEST(test_funcif) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_decl(e, 1)); + vcode_unit_t v0 = find_unit(tree_decl(e, 1)); vcode_select_unit(v0); fail_unless(vcode_count_blocks() == 3); @@ -1613,7 +1634,7 @@ START_TEST(test_memset) lower_unit(e); { - vcode_unit_t v0 = tree_code(tree_decl(e, 1)); + vcode_unit_t v0 = find_unit(tree_decl(e, 1)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -1636,7 +1657,7 @@ START_TEST(test_memset) } { - vcode_unit_t v0 = tree_code(tree_decl(e, 2)); + vcode_unit_t v0 = find_unit(tree_decl(e, 2)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -1676,7 +1697,7 @@ START_TEST(test_func5) lower_unit(e); { - vcode_unit_t v0 = tree_code(tree_decl(e, 1)); + vcode_unit_t v0 = find_unit(tree_decl(e, 1)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -1692,7 +1713,7 @@ START_TEST(test_func5) } { - vcode_unit_t v0 = tree_code(tree_decl(e, 2)); + vcode_unit_t v0 = find_unit(tree_decl(e, 2)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -1705,24 +1726,24 @@ START_TEST(test_func5) } { - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); EXPECT_BB(1) = { { VCODE_OP_CONST, .value = 2 }, { VCODE_OP_NETS, .name = ":func5:x" }, #if LLVM_MANGLES_NAMES - { VCODE_OP_FCALL, .func = ":func5:add_one_s__IsI", .args = 1 }, + { VCODE_OP_FCALL, .func = "WORK.FUNC5-TEST.ADD_ONE_S__IsI", .args = 1 }, #else - { VCODE_OP_FCALL, .func = ":func5:add_one_s$IsI", .args = 1 }, + { VCODE_OP_FCALL, .func = "WORK.FUNC5-TEST.ADD_ONE_S$IsI", .args = 1 }, #endif { VCODE_OP_CONST, .value = 6 }, { VCODE_OP_CMP, .cmp = VCODE_CMP_EQ }, { VCODE_OP_ASSERT }, #if LLVM_MANGLES_NAMES - { VCODE_OP_FCALL, .func = ":func5:event__BsI", .args = 1 }, + { VCODE_OP_FCALL, .func = "WORK.FUNC5-TEST.EVENT__BsI", .args = 1 }, #else - { VCODE_OP_FCALL, .func = ":func5:event$BsI", .args = 1 }, + { VCODE_OP_FCALL, .func = "WORK.FUNC5-TEST.EVENT$BsI", .args = 1 }, #endif { VCODE_OP_ASSERT }, { VCODE_OP_WAIT, .target = 2 } @@ -1745,7 +1766,7 @@ START_TEST(test_bounds1) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); EXPECT_BB(1) = { @@ -1784,7 +1805,7 @@ START_TEST(test_record6) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_decl(e, 1)); + vcode_unit_t v0 = find_unit(tree_decl(e, 1)); vcode_select_unit(v0); fail_unless(vcode_var_use_heap(vcode_var_handle(0))); @@ -1820,7 +1841,7 @@ START_TEST(test_proc7) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_decl(e, 1)); + vcode_unit_t v0 = find_unit(tree_decl(e, 1)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -1872,7 +1893,7 @@ START_TEST(test_issue116) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -1900,7 +1921,7 @@ START_TEST(test_mulphys) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_decl(e, 1)); + vcode_unit_t v0 = find_unit(tree_decl(e, 1)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -1928,31 +1949,31 @@ START_TEST(test_cover) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); EXPECT_BB(1) = { - { VCODE_OP_COVER_STMT, .index = 0 }, + { VCODE_OP_COVER_STMT, .tag = 0 }, { VCODE_OP_CONST, .value = 1 }, { VCODE_OP_STORE, .name = "V" }, - { VCODE_OP_COVER_STMT, .index = 2 }, + { VCODE_OP_COVER_STMT, .tag = 2 }, { VCODE_OP_LOAD, .name = "resolved_:cover:s" }, { VCODE_OP_LOAD_INDIRECT }, { VCODE_OP_CMP, .cmp = VCODE_CMP_EQ }, - { VCODE_OP_COVER_COND, .index = 0, .subkind = 1 }, + { VCODE_OP_COVER_COND, .tag = 0, .subkind = 1 }, { VCODE_OP_LOAD_INDIRECT }, { VCODE_OP_CONST, .value = 10 }, { VCODE_OP_CMP, .cmp = VCODE_CMP_GT }, - { VCODE_OP_COVER_COND, .index = 0, .subkind = 2 }, + { VCODE_OP_COVER_COND, .tag = 0, .subkind = 2 }, { VCODE_OP_OR }, - { VCODE_OP_COVER_COND, .index = 0, .subkind = 0 }, + { VCODE_OP_COVER_COND, .tag = 0, .subkind = 0 }, { VCODE_OP_COND, .target = 2, .target_else = 3 } }; CHECK_BB(1); EXPECT_BB(2) = { - { VCODE_OP_COVER_STMT, .index = 1 }, + { VCODE_OP_COVER_STMT, .tag = 1 }, { VCODE_OP_CONST, .value = 2 }, { VCODE_OP_STORE, .name = "V" }, { VCODE_OP_JUMP, .target = 3 } @@ -1974,14 +1995,16 @@ START_TEST(test_issue122) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_decl(e, 1)); + vcode_unit_t v0 = find_unit(tree_decl(e, 1)); vcode_select_unit(v0); EXPECT_BB(0) = { #if LLVM_MANGLES_NAMES - { VCODE_OP_NESTED_FCALL, .func = ":issue122:func__II__NESTED__I" }, + { VCODE_OP_NESTED_FCALL, + .func = "WORK.ISSUE122-TEST.FUNC__NESTED__II__NESTED__I" }, #else - { VCODE_OP_NESTED_FCALL, .func = ":issue122:func$II__NESTED$I" }, + { VCODE_OP_NESTED_FCALL, + .func = "WORK.ISSUE122-TEST.FUNC$II__NESTED$I" }, #endif { VCODE_OP_STORE, .name = "V" }, { VCODE_OP_RETURN } @@ -2003,7 +2026,7 @@ START_TEST(test_issue124) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_decl(e, 1)); + vcode_unit_t v0 = find_unit(tree_decl(e, 1)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -2029,7 +2052,7 @@ START_TEST(test_issue135) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_decl(e, 1)); + vcode_unit_t v0 = find_unit(tree_decl(e, 1)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -2070,6 +2093,7 @@ START_TEST(test_issue134) input_from_file(TESTDIR "/lower/issue134.vhd"); const error_t expect[] = { + { 8, "statement is unreachable" }, { 8, "statement is unreachable" }, { -1, NULL } }; @@ -2078,7 +2102,7 @@ START_TEST(test_issue134) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_decl(e, 1)); + vcode_unit_t v0 = find_unit(tree_decl(e, 1)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -2103,7 +2127,7 @@ START_TEST(test_issue136) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_decl(tree_decl(e, 1), 1)); + vcode_unit_t v0 = find_unit(tree_decl(tree_decl(e, 1), 1)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -2140,7 +2164,7 @@ START_TEST(test_rectype) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(e); + vcode_unit_t v0 = find_unit(e); vcode_select_unit(v0); fail_unless(vtype_kind(0) == VCODE_TYPE_RECORD); @@ -2161,7 +2185,7 @@ START_TEST(test_issue149) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_decl(e, 1)); + vcode_unit_t v0 = find_unit(tree_decl(e, 1)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -2200,7 +2224,7 @@ START_TEST(test_issue167) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(e); + vcode_unit_t v0 = find_unit(e); vcode_select_unit(v0); fail_unless(vtype_kind(0) == VCODE_TYPE_RECORD); @@ -2221,14 +2245,14 @@ START_TEST(test_issue164) tree_t p = parse_check_and_simplify(T_PACKAGE, T_PACK_BODY); lower_unit(p); - vcode_select_unit(tree_code(tree_decl(p, 0))); + vcode_select_unit(find_unit(tree_decl(p, 0))); #if LLVM_MANGLES_NAMES fail_unless(icmp(vcode_unit_name(), "WORK.ISSUE164.SAME_NAME__vI")); #else fail_unless(icmp(vcode_unit_name(), "WORK.ISSUE164.SAME_NAME$vI")); #endif - vcode_select_unit(tree_code(tree_decl(p, 1))); + vcode_select_unit(find_unit(tree_decl(p, 1))); #if LLVM_MANGLES_NAMES fail_unless(icmp(vcode_unit_name(), "WORK.ISSUE164.SAME_NAME__I")); #else @@ -2245,7 +2269,7 @@ START_TEST(test_sigvar) lower_unit(e); { - vcode_unit_t v0 = tree_code(tree_decl(e, 1)); + vcode_unit_t v0 = find_unit(tree_decl(e, 1)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -2283,7 +2307,7 @@ START_TEST(test_sigvar) } { - vcode_unit_t v0 = tree_code(tree_decl(e, 2)); + vcode_unit_t v0 = find_unit(tree_decl(e, 2)); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -2310,7 +2334,7 @@ START_TEST(test_issue181) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_decl(e, 1)); + vcode_unit_t v0 = find_unit(tree_decl(e, 1)); vcode_select_unit(v0); fail_unless(vcode_count_vars() == 2); @@ -2330,7 +2354,7 @@ START_TEST(test_issue203) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(e); + vcode_unit_t v0 = find_unit(e); vcode_select_unit(v0); fail_unless(vcode_count_vars() == 1); @@ -2346,7 +2370,7 @@ START_TEST(test_issue215) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(e); + vcode_unit_t v0 = find_unit(e); vcode_select_unit(v0); } @@ -2359,7 +2383,7 @@ START_TEST(test_choice1) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(tree_stmt(e, 0)); + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); vcode_select_unit(v0); EXPECT_BB(1) = { @@ -2408,7 +2432,7 @@ START_TEST(test_tag) tree_t e = run_elab(); lower_unit(e); - vcode_unit_t v0 = tree_code(e); + vcode_unit_t v0 = find_unit(e); vcode_select_unit(v0); EXPECT_BB(0) = { @@ -2432,6 +2456,100 @@ START_TEST(test_tag) } END_TEST +START_TEST(test_iffold) +{ + input_from_file(TESTDIR "/lower/iffold.vhd"); + + tree_t e = run_elab(); + lower_unit(e); + + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); + vcode_select_unit(v0); + + EXPECT_BB(1) = { + { VCODE_OP_CONST, .value = 0 }, + { VCODE_OP_NETS, .name = ":iffold:sub_i:x" }, + { VCODE_OP_CONST, .value = 5 }, + { VCODE_OP_CONST, .value = 1 }, + { VCODE_OP_SCHED_WAVEFORM }, + { VCODE_OP_JUMP, .target = 1 } + }; + + CHECK_BB(1); +} +END_TEST + +START_TEST(test_real1) +{ + input_from_file(TESTDIR "/lower/real1.vhd"); + + tree_t e = run_elab(); + lower_unit(e); + + { + vcode_unit_t v0 = find_unit(tree_decl(e, 1)); + vcode_select_unit(v0); + + EXPECT_BB(0) = { + { VCODE_OP_NEG }, + { VCODE_OP_STORE, .name = "R" }, + { VCODE_OP_RETURN } + }; + + CHECK_BB(0); + } + + { + vcode_unit_t v0 = find_unit(tree_decl(e, 2)); + vcode_select_unit(v0); + + EXPECT_BB(0) = { + { VCODE_OP_STORE, .name = "Z" }, + { VCODE_OP_CAST }, + { VCODE_OP_CONST_REAL, .real = 0.5 }, + { VCODE_OP_MUL }, + { VCODE_OP_RETURN } + }; + + CHECK_BB(0); + } +} +END_TEST + +START_TEST(test_assert1) +{ + input_from_file(TESTDIR "/lower/assert1.vhd"); + + tree_t e = run_elab(); + lower_unit(e); + + vcode_unit_t v0 = find_unit(tree_stmt(e, 0)); + vcode_select_unit(v0); + + EXPECT_BB(1) = { + { VCODE_OP_CONST, .value = 1 }, + { VCODE_OP_STORE, .name = "B" }, + { VCODE_OP_WAIT, .target = 2 } + }; + + CHECK_BB(1); +} +END_TEST + +START_TEST(test_thunk) +{ + input_from_file(TESTDIR "/lower/thunk.vhd"); + + tree_t arch = parse_check_and_simplify(T_PACKAGE, T_ENTITY, T_ARCH); + + vcode_unit_t t0 = lower_thunk(tree_value(tree_decl(arch, 0))); + fail_unless(t0 == NULL); + + vcode_unit_t t1 = lower_thunk(tree_value(tree_decl(arch, 1))); + fail_unless(t1 == NULL); +} +END_TEST + int main(void) { Suite *s = suite_create("lower"); @@ -2488,6 +2606,10 @@ int main(void) tcase_add_test(tc, test_issue215); tcase_add_test(tc, test_choice1); tcase_add_test(tc, test_tag); + tcase_add_test(tc, test_iffold); + tcase_add_test(tc, test_real1); + tcase_add_test(tc, test_assert1); + tcase_add_test(tc, test_thunk); suite_add_tcase(s, tc); return nvc_run_test(s); diff --git a/test/test_simp.c b/test/test_simp.c index 9985b6fe..cd1fdcf1 100644 --- a/test/test_simp.c +++ b/test/test_simp.c @@ -139,6 +139,7 @@ START_TEST(test_cfold) fail_unless(folded_r(tree_value(tree_stmt(p, 0)), 3.0)); fail_unless(folded_r(tree_value(tree_stmt(p, 1)), 0.6)); fail_unless(folded_r(tree_value(tree_stmt(p, 2)), 2.5)); + fail_unless(folded_r(tree_value(tree_stmt(p, 3)), 16.0)); p = tree_stmt(a, 7); fail_unless(folded_b(tree_value(tree_stmt(p, 0)), true)); @@ -280,18 +281,39 @@ START_TEST(test_ffold) { input_from_file(TESTDIR "/simp/ffold.vhd"); - tree_t a = parse_and_check(T_PACKAGE, T_PACK_BODY, T_ENTITY, T_ARCH); + tree_t a = parse_check_and_simplify(T_PACKAGE, T_PACK_BODY, + T_ENTITY, T_ARCH); fail_unless(sem_errors() == 0); - simplify(a); - - fail_unless(folded_i(tree_value(tree_decl(a, 6)), 6)); - fail_unless(folded_i(tree_value(tree_decl(a, 8)), 4)); - fail_unless(folded_i(tree_value(tree_decl(a, 9)), 3)); - fail_unless(folded_i(tree_value(tree_decl(a, 10)), 2)); - fail_unless(folded_i(tree_value(tree_decl(a, 11)), 5)); - fail_unless(folded_i(tree_value(tree_decl(a, 12)), 10)); - fail_unless(folded_b(tree_value(tree_decl(a, 13)), true)); + lower_unit(a); + fold(a); + + tree_t b = tree_stmt(a, 0); + fail_unless(tree_kind(b) == T_BLOCK); + + fail_unless(folded_i(tree_value(tree_decl(b, 0)), 6)); + fail_unless(folded_i(tree_value(tree_decl(b, 2)), 4)); + fail_unless(folded_i(tree_value(tree_decl(b, 3)), 3)); + fail_unless(folded_i(tree_value(tree_decl(b, 4)), 2)); + fail_unless(folded_i(tree_value(tree_decl(b, 5)), 5)); + fail_unless(folded_i(tree_value(tree_decl(b, 6)), 10)); + fail_unless(folded_b(tree_value(tree_decl(b, 7)), true)); + fail_unless(folded_b(tree_value(tree_decl(b, 8)), true)); + fail_unless(folded_b(tree_value(tree_decl(b, 9)), false)); + fail_unless(folded_r(tree_value(tree_decl(b, 10)), 0.62)); + fail_unless(folded_r(tree_value(tree_decl(b, 11)), 71.7)); + fail_unless(folded_b(tree_value(tree_decl(b, 12)), true)); + fail_unless(folded_b(tree_value(tree_decl(b, 13)), true)); + fail_unless(folded_b(tree_value(tree_decl(b, 14)), false)); + fail_unless(folded_b(tree_value(tree_decl(b, 15)), true)); + fail_unless(folded_b(tree_value(tree_decl(b, 16)), true)); + fail_unless(folded_b(tree_value(tree_decl(b, 17)), true)); + fail_unless(folded_b(tree_value(tree_decl(b, 18)), true)); + fail_unless(folded_i(tree_value(tree_decl(b, 19)), 80)); + fail_unless(folded_i(tree_value(tree_decl(b, 20)), 5)); + fail_unless(folded_i(tree_value(tree_decl(b, 21)), 2)); + fail_unless(folded_b(tree_value(tree_decl(b, 22)), true)); + fail_unless(folded_b(tree_value(tree_decl(b, 23)), true)); } END_TEST @@ -368,6 +390,15 @@ START_TEST(test_issue212) } END_TEST +START_TEST(test_shift2) +{ + input_from_file(TESTDIR "/simp/shift2.vhd"); + + tree_t top = run_elab(); + fail_unless(tree_stmts(top) == 0); +} +END_TEST + int main(void) { Suite *s = suite_create("simplify"); @@ -381,6 +412,7 @@ int main(void) tcase_add_test(tc_core, test_issue155); tcase_add_test(tc_core, test_context); tcase_add_test(tc_core, test_issue212); + tcase_add_test(tc_core, test_shift2); suite_add_tcase(s, tc_core); return nvc_run_test(s); diff --git a/test/test_util.c b/test/test_util.c index 942caa19..0e8fc958 100644 --- a/test/test_util.c +++ b/test/test_util.c @@ -92,6 +92,7 @@ tree_t run_elab(void) fail_if(sem_errors() > 0); simplify(t); + lower_unit(t); if (tree_kind(t) == T_ENTITY) last_ent = t; -- 2.39.2