• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nickg / nvc / 20682185998

03 Jan 2026 07:14PM UTC coverage: 92.592% (-0.06%) from 92.65%
20682185998

push

github

nickg
Lazy lowering for processes

63 of 64 new or added lines in 4 files covered. (98.44%)

197 existing lines in 6 files now uncovered.

76009 of 82090 relevant lines covered (92.59%)

475414.95 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.14
/src/driver.c
1
//
2
//  Copyright (C) 2023-2024  Nick Gasson
3
//
4
//  This program is free software: you can redistribute it and/or modify
5
//  it under the terms of the GNU General Public License as published by
6
//  the Free Software Foundation, either version 3 of the License, or
7
//  (at your option) any later version.
8
//
9
//  This program is distributed in the hope that it will be useful,
10
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
//  GNU General Public License for more details.
13
//
14
//  You should have received a copy of the GNU General Public License
15
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
//
17

18
#include "util.h"
19
#include "common.h"
20
#include "diag.h"
21
#include "driver.h"
22
#include "hash.h"
23
#include "ident.h"
24
#include "mask.h"
25
#include "option.h"
26
#include "phase.h"
27
#include "tree.h"
28
#include "type.h"
29

30
#include <assert.h>
31
#include <stdlib.h>
32

33
#define DRIVER_PAGE_SIZE 128
34

35
typedef struct _driver_page driver_page_t;
36

37
typedef struct _driver_page {
38
   driver_page_t *next;
39
   unsigned       count;
40
   driver_info_t  members[DRIVER_PAGE_SIZE];
41
} driver_page_t;
42

43
typedef struct _driver_set {
44
   hash_t        *map;
45
   driver_page_t *pages;
46
} driver_set_t;
47

48
typedef struct {
49
   driver_set_t *ds;
50
   tree_t        proc;
51
   bool          tentative;
52
} proc_params_t;
53

54
static driver_info_t *alloc_driver_info(driver_set_t *ds)
5,656✔
55
{
56
   if (ds->pages == NULL || ds->pages->count == DRIVER_PAGE_SIZE) {
5,656✔
57
      driver_page_t *p = xmalloc(sizeof(driver_page_t));
4,490✔
58
      p->next = ds->pages;
4,490✔
59
      p->count = 0;
4,490✔
60

61
      ds->pages = p;
4,490✔
62
   }
63

64
   return &(ds->pages->members[ds->pages->count++]);
5,656✔
65
}
66

67
static void drives_signal(driver_set_t *ds, tree_t where, tree_t expr,
8,147✔
68
                          tree_t view, bool tentative)
69
{
70
   if (tree_kind(expr) == T_AGGREGATE) {
8,147✔
71
      const int nassocs = tree_assocs(expr);
77✔
72
      for (int i = 0; i < nassocs; i++) {
270✔
73
         tree_t e = tree_value(tree_assoc(expr, i));
193✔
74
         drives_signal(ds, where, e, NULL, tentative);
193✔
75
      }
76

77
      return;
78
   }
79

80
   tree_t prefix = longest_static_prefix(expr);
8,070✔
81

82
   driver_info_t *last = NULL;
8,070✔
83
   tree_t ref = name_to_ref(prefix), decl = NULL;
8,070✔
84
   if (ref != NULL) {
8,070✔
85
      if (tree_kind((decl = tree_ref(ref))) == T_PARAM_DECL) {
8,060✔
86
         // Assignment to procedure parameter: this is handled at the
87
         // call site instead
88
         return;
89
      }
90

91
      driver_info_t *chain = hash_get(ds->map, where);
8,050✔
92
      for (; chain; last = chain, chain = chain->chain_proc) {
19,310✔
93
         if (chain->where != where || chain->decl != decl)
5,614✔
94
            continue;
2,987✔
95
         else if (tree_kind(chain->prefix) == T_REF)
2,627✔
96
            return;   // Already driving full signal
97
         else if (same_tree(prefix, chain->prefix))
410✔
98
            return;
99
      }
100
   }
101
   else if (tree_kind(prefix) != T_EXTERNAL_NAME)
10✔
102
      return;
103

104
   driver_info_t *di = alloc_driver_info(ds);
5,656✔
105
   di->chain_decl = NULL;
5,656✔
106
   di->chain_proc = NULL;
5,656✔
107
   di->decl       = decl;
5,656✔
108
   di->prefix     = prefix;
5,656✔
109
   di->where      = where;
5,656✔
110
   di->view       = view;
5,656✔
111
   di->tentative  = tentative;
5,656✔
112

113
   if (last == NULL)
5,656✔
114
      hash_put(ds->map, where, di);
4,513✔
115
   else
116
      last->chain_proc = di;
1,143✔
117

118
   if (decl != NULL) {
5,656✔
119
      driver_info_t *decl_head = hash_get(ds->map, decl);
5,646✔
120
      if (decl_head == NULL)
5,646✔
121
         hash_put(ds->map, decl, di);
5,477✔
122
      else {
123
         for (; decl_head->chain_decl; decl_head = decl_head->chain_decl);
223✔
124
         decl_head->chain_decl = di;
169✔
125
      }
126
   }
127
}
128

129
static void driver_proc_cb(tree_t t, void *ctx)
459,647✔
130
{
131
   proc_params_t *params = ctx;
459,647✔
132

133
   switch (tree_kind(t)) {
459,647✔
134
   case T_SIGNAL_ASSIGN:
7,699✔
135
   case T_DUMMY_DRIVER:
136
      drives_signal(params->ds, params->proc, tree_target(t),
15,398✔
137
                    NULL, params->tentative);
7,699✔
138
      break;
7,699✔
139

140
   case T_PCALL:
4,800✔
141
   case T_PROT_PCALL:
142
      // LRM 08 section 4.2.2.3: a process statement contains a driver
143
      // for each actual signal associated with a formal signal
144
      // parameter of mode out or inout in a subprogram call.
145
      {
146
         tree_t decl = tree_ref(t);
4,800✔
147
         const int nports = tree_ports(decl);
4,800✔
148
         for (int i = 0; i < nports; i++) {
16,614✔
149
            tree_t p = tree_port(decl, i);
11,814✔
150
            if (tree_class(p) != C_SIGNAL)
11,814✔
151
               continue;
11,467✔
152

153
            const port_mode_t mode = tree_subkind(p);
347✔
154
            if (mode == PORT_OUT || mode == PORT_INOUT) {
347✔
155
               tree_t arg = tree_param(t, i);
237✔
156
               assert(tree_subkind(arg) == P_POS);
237✔
157
               drives_signal(params->ds, params->proc, tree_value(arg),
237✔
158
                             NULL, params->tentative);
237✔
159
            }
160
            else if (mode == PORT_ARRAY_VIEW || mode == PORT_RECORD_VIEW) {
110✔
161
               tree_t arg = tree_param(t, i);
15✔
162
               assert(tree_subkind(arg) == P_POS);
15✔
163
               drives_signal(params->ds, params->proc, tree_value(arg),
15✔
164
                             tree_value(p), params->tentative);
15✔
165
            }
166
         }
167
      }
168
      break;
169

170
   default:
171
      break;
172
   }
173
}
459,647✔
174

175
static void visit_port_map(driver_set_t *ds, tree_t unit, tree_t inst,
3✔
176
                           bool tentative)
177
{
178
   const int nparams = tree_params(inst);
3✔
179
   for (int i = 0; i < nparams; i++) {
6✔
180
      tree_t map = tree_param(inst, i);
3✔
181

182
      tree_t port;
3✔
183
      if (tree_subkind(map) == P_POS)
3✔
184
         port = tree_port(unit, tree_pos(map));
3✔
185
      else {
UNCOV
186
         tree_t name = tree_name(map);
×
UNCOV
187
         switch (tree_kind(name)) {
×
UNCOV
188
         case T_CONV_FUNC:
×
189
         case T_TYPE_CONV:
UNCOV
190
            name = tree_value(name);
×
UNCOV
191
            break;
×
192
         default:
193
            break;
194
         }
195

UNCOV
196
         port = tree_ref(name_to_ref(name));
×
197
      }
198

199
      switch (tree_subkind(port)) {
3✔
200
      case PORT_OUT:
2✔
201
      case PORT_INOUT:
202
         drives_signal(ds, inst, tree_value(map), NULL, tentative);
2✔
203
         break;
2✔
204
      case PORT_ARRAY_VIEW:
1✔
205
      case PORT_RECORD_VIEW:
206
         drives_signal(ds, inst, tree_value(map), tree_value(port), tentative);
1✔
207
         break;
1✔
208
      }
209
   }
210
}
3✔
211

212
static void visit_instance(driver_set_t *ds, tree_t inst, bool tentative)
1✔
213
{
214
   tree_t unit = primary_unit_of(tree_ref(inst));
1✔
215
   visit_port_map(ds, unit, inst, tentative);
1✔
216
}
1✔
217

218
static void visit_process(driver_set_t *ds, tree_t proc, bool tentative)
7,391✔
219
{
220
   proc_params_t params = {
7,391✔
221
      .ds = ds,
222
      .proc = proc,
223
      .tentative = tentative,
224
   };
225
   tree_visit(proc, driver_proc_cb, &params);
7,391✔
226
}
7,391✔
227

228
static void visit_block(driver_set_t *ds, tree_t b, bool tentative)
11✔
229
{
230
   const int nstmts = tree_stmts(b);
11✔
231
   for (int i = 0; i < nstmts; i++) {
44✔
232
      tree_t s = tree_stmt(b, i);
33✔
233
      switch (tree_kind(s)) {
33✔
234
      case T_PROCESS:
28✔
235
         visit_process(ds, s, tentative);
28✔
236
         break;
28✔
237
      case T_BLOCK:
2✔
238
         visit_port_map(ds, s, s, tentative);
2✔
239
         visit_block(ds, s, tentative);
2✔
240
         break;
2✔
241
      case T_FOR_GENERATE:
1✔
242
         visit_block(ds, s, true);
1✔
243
         break;
1✔
244
      case T_IF_GENERATE:
1✔
245
         {
246
            const int nconds = tree_conds(s);
1✔
247
            for (int i = 0; i < nconds; i++)
2✔
248
               visit_block(ds, tree_cond(s, i), true);
1✔
249
         }
250
         break;
UNCOV
251
      case T_CASE_GENERATE:
×
252
         {
UNCOV
253
            const int nalts = tree_stmts(s);
×
UNCOV
254
            for (int i = 0; i < nalts; i++) {
×
UNCOV
255
               tree_t a = tree_stmt(s, i);
×
UNCOV
256
               assert(tree_kind(a) == T_ALTERNATIVE);
×
UNCOV
257
               visit_block(ds, a, true);
×
258
            }
259
         }
260
         break;
261
      case T_INSTANCE:
1✔
262
         visit_instance(ds, s, tentative);
1✔
263
         break;
1✔
264
      default:
265
         break;
266
      }
267
   }
268

269
}
11✔
270

271
driver_set_t *find_drivers(tree_t where)
7,370✔
272
{
273
   driver_set_t *ds = xcalloc(sizeof(driver_set_t));
7,370✔
274
   ds->map = hash_new(128);
7,370✔
275

276
   switch (tree_kind(where)) {
7,370✔
277
   case T_ARCH:
7✔
278
   case T_BLOCK:
279
   case T_FOR_GENERATE:
280
   case T_COND_STMT:
281
   case T_ALTERNATIVE:
282
      visit_block(ds, where, false);
7✔
283
      break;
7✔
284
   case T_BINDING:
×
UNCOV
285
      visit_port_map(ds, primary_unit_of(tree_ref(where)), where, false);
×
UNCOV
286
      break;
×
287
   case T_PROCESS:
7,363✔
288
      visit_process(ds, where, false);
7,363✔
289
      break;
7,363✔
UNCOV
290
   default:
×
291
      fatal_trace("cannot find drivers in %s", tree_kind_str(tree_kind(where)));
292
   }
293

294
   if (opt_get_int(OPT_DRIVER_VERBOSE))
7,370✔
UNCOV
295
      dump_drivers(ds);
×
296

297
   return ds;
7,370✔
298
}
299

300
void free_drivers(driver_set_t *ds)
7,370✔
301
{
302
   for (driver_page_t *p = ds->pages, *tmp; p; p = tmp) {
11,860✔
303
      tmp = p->next;
4,490✔
304
      free(p);
4,490✔
305
   }
306

307
   hash_free(ds->map);
7,370✔
308
   free(ds);
7,370✔
309
}
7,370✔
310

311
driver_info_t *get_drivers(driver_set_t *ds, tree_t what)
14,747✔
312
{
313
   return hash_get(ds->map, what);
14,747✔
314
}
315

316
bool has_unique_driver(driver_set_t *ds, tree_t what)
11✔
317
{
318
   // True if signal has exactly one driver for each sub-element
319

320
   driver_info_t *di = get_drivers(ds, what);
11✔
321

322
   if (di == NULL)
11✔
323
      return false;
324

325
   // First pass: look for assignments to the full signal
326

327
   bool saw_ref = false, saw_multiple = false;
11✔
328
   tree_t proc = di->where;
11✔
329
   for (driver_info_t *it = di; it; it = it->chain_decl) {
33✔
330
      if (it->tentative)
24✔
331
         return false;
332
      else if (it->where != proc)
22✔
333
         saw_multiple = true;
334
      else if (tree_kind(it->prefix) == T_REF)
10✔
335
         saw_ref = true;
3✔
336
   }
337

338
   if (saw_ref && !saw_multiple)
9✔
339
      return true;
340

341
   type_t type = tree_type(what);
8✔
342
   if (type_is_unconstrained(type))
8✔
343
      return false;
344

345
   int64_t length = 0, left = 0, right = 0;
7✔
346
   range_kind_t rkind = RANGE_ERROR;
7✔
347
   if (type_is_array(type) && dimension_of(type) == 1) {
7✔
348
      tree_t r = range_of(type, 0);
3✔
349
      if (folded_length(r, &length)) {
3✔
350
         left = assume_int(tree_left(r));
3✔
351
         right = assume_int(tree_right(r));
3✔
352
         rkind = tree_subkind(r);
3✔
353
      }
354
   }
355
   else if (type_is_record(type))
4✔
356
      length = type_fields(type);
3✔
357

358
   if (length == 0)
7✔
359
      return false;
360

361
   // Second pass: look for assignments to disjoint sub-elements
362

363
   bit_mask_t mask = {};
6✔
364
   mask_init(&mask, length);
6✔
365

366
   bool covered = false;
6✔
367
   for (driver_info_t *it = di; it; it = it->chain_decl) {
18✔
368
      const tree_kind_t kind = tree_kind(it->prefix);
14✔
369
      if (kind != T_ARRAY_REF && kind != T_ARRAY_SLICE && kind != T_RECORD_REF)
14✔
370
         goto out_free;
1✔
371

372
      tree_t value = tree_value(it->prefix);
13✔
373
      if (tree_kind(value) != T_REF)
13✔
374
         goto out_free;
1✔
375

376
      assert(tree_ref(value) == what);
12✔
377

378
      switch (kind) {
12✔
379
      case T_ARRAY_REF:
5✔
380
         {
381
            assert(tree_params(it->prefix) == 1);
5✔
382
            tree_t p0 = tree_value(tree_param(it->prefix, 0));
5✔
383

384
            int64_t ival;
5✔
385
            if (!folded_int(p0, &ival))
5✔
UNCOV
386
               goto out_free;
×
387

388
            const int zero = rkind == RANGE_TO ? ival - left : ival - right;
5✔
389
            if (mask_test(&mask, zero))
5✔
UNCOV
390
               goto out_free;
×
391

392
            mask_set(&mask, zero);
5✔
393
         }
394
         break;
5✔
395

396
      case T_ARRAY_SLICE:
2✔
397
         {
398
            tree_t r = tree_range(it->prefix, 0);
2✔
399

400
            int64_t low, high;
2✔
401
            if (!folded_bounds(r, &low, &high) || high < low)
2✔
402
               goto out_free;
×
403

404
            const int count = high - low + 1;
2✔
405
            const int zero = rkind == RANGE_TO ? low - left : low - right;
2✔
406

407
            if (mask_test_range(&mask, zero, count))
2✔
UNCOV
408
               goto out_free;
×
409

410
            mask_set_range(&mask, zero, count);
2✔
411
         }
412
         break;
2✔
413

414
      case T_RECORD_REF:
5✔
415
         {
416
            const int pos = tree_pos(tree_ref(it->prefix));
5✔
417

418
            if (mask_test(&mask, pos))
5✔
419
               goto out_free;
×
420

421
            mask_set(&mask, pos);
5✔
422
         }
423
         break;
424

UNCOV
425
      default:
×
UNCOV
426
         goto out_free;
×
427
      }
428
   }
429

430
   covered = (mask_popcount(&mask) == length);
4✔
431

432
 out_free:
6✔
433
   mask_free(&mask);
6✔
434
   return covered;
6✔
435
}
436

437
LCOV_EXCL_START
438
void dump_drivers(driver_set_t *ds)
439
{
440
   const void *key;
441
   void *value;
442
   for (hash_iter_t it = HASH_BEGIN; hash_iter(ds->map, &it, &key, &value); ) {
443
      tree_t tree = (tree_t)key;
444
      driver_info_t *di = value;
445

446
      const tree_kind_t kind = tree_kind(tree);
447
      if (kind != T_PROCESS && kind != T_INSTANCE)
448
         continue;
449

450
      printf("%s: { ", istr(tree_ident(tree)));
451
      for (; di; di = di->chain_proc) {
452
         vhdl_dump(di->prefix, 0);
453
         if (di->view)
454
            printf("<%s>", type_pp(tree_type(di->view)));
455
         if (di->tentative)
456
            printf("?");
457
         if (di->chain_proc)
458
            printf(", ");
459
      }
460
      printf(" }\n");
461
   }
462
}
463
LCOV_EXCL_STOP
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc