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

nickg / nvc / 16099227452

06 Jul 2025 12:45PM UTC coverage: 92.335% (+0.05%) from 92.284%
16099227452

push

github

nickg
Handle underscores in Verilog decimal literals

Fixes #1230

18 of 20 new or added lines in 1 file covered. (90.0%)

598 existing lines in 16 files now uncovered.

71069 of 76969 relevant lines covered (92.33%)

564781.41 hits per line

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

98.16
/src/cgen.c
1
//
2
//  Copyright (C) 2011-2021  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 "array.h"
20
#include "common.h"
21
#include "diag.h"
22
#include "hash.h"
23
#include "jit/jit-ffi.h"
24
#include "jit/jit-llvm.h"
25
#include "jit/jit.h"
26
#include "lib.h"
27
#include "lower.h"
28
#include "mir/mir-unit.h"
29
#include "option.h"
30
#include "phase.h"
31
#include "thread.h"
32
#include "type.h"
33
#include "vlog/vlog-node.h"
34

35
#include <stdlib.h>
36
#include <string.h>
37
#include <limits.h>
38
#include <unistd.h>
39
#include <ctype.h>
40

41
typedef A(ident_t) unit_list_t;
42
typedef A(char *) obj_list_t;
43

44
typedef struct {
45
   unit_list_t      units;
46
   char            *obj_path;
47
   char            *module_name;
48
   unsigned         index;
49
   cover_data_t    *cover;
50
   llvm_obj_t      *obj;
51
} cgen_job_t;
52

53
typedef struct {
54
   unit_list_t     *units;
55
   hset_t          *filter;
56
   unit_registry_t *registry;
57
} discover_args_t;
58

59
#ifdef ENABLE_LLVM
60
static A(char *) link_args;
61
static A(char *) cleanup_files = AINIT;
62
#endif
63

64
#define UNITS_PER_JOB 25
65

66
// Avoid generating excessively long linker command line
67
#ifdef __MINGW32__
68
#define MAX_JOBS 100
69
#else
70
#define MAX_JOBS 1000
71
#endif
72

73
static void cgen_find_dependencies(mir_context_t *mc, unit_registry_t *ur,
20,154✔
74
                                   unit_list_t *units, hset_t *seen,
75
                                   ident_t name)
76
{
77
   mir_unit_t *mu = mir_get_unit(mc, name);
20,154✔
78
   if (mu == NULL) {
20,154✔
79
      (void)unit_registry_get(ur, name);
6,439✔
80
      mu = mir_get_unit(mc, name);
6,439✔
81
   }
82

83
   if (mu == NULL)
20,154✔
84
      fatal_trace("missing vcode for %s", istr(name));
85

86
   const int nlink = mir_count_linkage(mu);
20,154✔
87
   for (int i = 0; i < nlink; i++) {
50,848✔
88
      ident_t link = mir_get_linkage(mu, i);
30,694✔
89
      if (hset_contains(seen, link))
30,694✔
90
         continue;
18,498✔
91
      else if (ident_char(link, 0) == '$')
12,196✔
92
         continue;   // TODO: handle VPI differently
83✔
93
      else {
94
         APUSH(*units, link);
12,113✔
95
         hset_insert(seen, link);
12,113✔
96
      }
97
   }
98
}
20,154✔
99

100
static void cgen_walk_hier(unit_list_t *units, hset_t *seen, tree_t block,
4,071✔
101
                           ident_t prefix)
102
{
103
   assert(tree_kind(block) == T_BLOCK);
4,071✔
104

105
   ident_t unit_name = ident_prefix(prefix, tree_ident(block), '.');
4,071✔
106
   APUSH(*units, unit_name);
4,071✔
107
   hset_insert(seen, unit_name);
4,071✔
108

109
   tree_t hier = tree_decl(block, 0);
4,071✔
110
   assert(tree_kind(hier) == T_HIER);
4,071✔
111

112
   const int nstmts = tree_stmts(block);
4,071✔
113
   for (int i = 0; i < nstmts; i++) {
10,482✔
114
      tree_t s = tree_stmt(block, i);
6,411✔
115
      switch (tree_kind(s)) {
6,411✔
116
      case T_BLOCK:
2,441✔
117
         cgen_walk_hier(units, seen, s, unit_name);
2,441✔
118
         break;
2,441✔
119
      case T_PROCESS:
3,875✔
120
      case T_PSL_DIRECT:
121
         {
122
            ident_t proc_name = ident_prefix(unit_name, tree_ident(s), '.');
3,875✔
123
            APUSH(*units, proc_name);
3,875✔
124
            hset_insert(seen, proc_name);
3,875✔
125
         }
126
         break;
3,875✔
127
      case T_VERILOG:
95✔
128
         {
129
            ident_t sym = ident_prefix(unit_name, tree_ident(s), '.');
95✔
130
            APUSH(*units, sym);
95✔
131
            hset_insert(seen, sym);
95✔
132
         }
133
         break;
95✔
134
      default:
135
         break;
136
      }
137
   }
138
}
4,071✔
139

140
#ifdef ENABLE_LLVM
141
static void cleanup_temp_dll(void)
962✔
142
{
143
   for (int i = 0; i < cleanup_files.count; i++) {
1,924✔
144
      if (remove(cleanup_files.items[i]) == -1)
962✔
UNCOV
145
         warnf("cannot remove %s: %s", cleanup_files.items[i], last_os_error());
×
146

147
      free(cleanup_files.items[i]);
962✔
148
   }
149

150
   ACLEAR(cleanup_files);
962✔
151
}
962✔
152

153
static void cgen_link_arg(const char *fmt, ...)
6,686✔
154
{
155
   va_list ap;
6,686✔
156
   va_start(ap, fmt);
6,686✔
157
   char *buf = xvasprintf(fmt, ap);
6,686✔
158
   va_end(ap);
6,686✔
159

160
   APUSH(link_args, buf);
6,686✔
161
}
6,686✔
162

163
static void cgen_linker_setup(void)
1,083✔
164
{
165
#if defined LINKER_PATH
166
   cgen_link_arg("%s", LINKER_PATH);
1,083✔
167
   cgen_link_arg("--eh-frame-hdr");
1,083✔
168
#elif defined SYSTEM_CC
169
   cgen_link_arg("%s", SYSTEM_CC);
170
#elif defined BOOTSTRAP_CC
171
   cgen_link_arg("%s", BOOTSTRAP_CC);
172
#else
173
   fatal_trace("configured without external C compiler or linker");
174
#endif
175

176
#if defined __APPLE__
177
   cgen_link_arg("-bundle");
178
   cgen_link_arg("-flat_namespace");
179
   cgen_link_arg("-undefined");
180
   cgen_link_arg("dynamic_lookup");
181
#ifdef HAVE_NO_FIXUP_CHAINS
182
   cgen_link_arg("-Wl,-no_fixup_chains");
183
#endif
184
#elif defined __OpenBSD__
185
   cgen_link_arg("-Bdynamic");
186
   cgen_link_arg("-shared");
187
   cgen_link_arg("/usr/lib/crtbeginS.o");
188
#else
189
   cgen_link_arg("-shared");
1,083✔
190
#endif
191
}
1,083✔
192

193
static void cgen_link(const char *module_name, char **objs, int nobjs)
1,083✔
194
{
195
   cgen_linker_setup();
1,083✔
196

197
   LOCAL_TEXT_BUF tb = tb_new();
1,083✔
198
   tb_printf(tb, "_%s", module_name);
1,083✔
199
   if (opt_get_int(OPT_NO_SAVE))
1,083✔
200
      tb_printf(tb, ".%d", getpid());
962✔
201
   tb_cat(tb, "." DLL_EXT);
1,083✔
202

203
   char so_path[PATH_MAX];
1,083✔
204
   lib_realpath(lib_work(), tb_get(tb), so_path, PATH_MAX);
1,083✔
205

206
   if (opt_get_int(OPT_NO_SAVE)) {
1,083✔
207
      APUSH(cleanup_files, xstrdup(so_path));
962✔
208
      atexit(cleanup_temp_dll);
962✔
209
   }
210

211
   cgen_link_arg("-o");
1,083✔
212
   cgen_link_arg("%s", so_path);
1,083✔
213

214
   for (int i = 0; i < nobjs; i++)
2,354✔
215
      cgen_link_arg("%s", objs[i]);
1,271✔
216

217
#if defined LINKER_PATH && defined __OpenBSD__
218
   // Extra linker arguments to make constructors work on OpenBSD
219
   cgen_link_arg("-L/usr/lib");
220
   cgen_link_arg("-lcompiler_rt");
221
   cgen_link_arg("/usr/lib/crtendS.o");
222
#endif
223

224
#ifdef IMPLIB_REQUIRED
225
   tb_rewind(tb);
226
   const char *cyglib = getenv("NVC_IMP_LIB");
227
   if (cyglib != NULL)
228
      tb_cat(tb, cyglib);
229
   else
230
      get_lib_dir(tb);
231

232
   cgen_link_arg("-L%s", tb_get(tb));
233
   cgen_link_arg("-L%s/nvc", LIBDIR);
234
   cgen_link_arg("-lnvcimp");
235
#endif
236

237
   APUSH(link_args, NULL);
1,083✔
238

239
   run_program((const char * const *)link_args.items);
1,083✔
240

241
   for (int i = 0; i < nobjs ; i++) {
2,354✔
242
      if (unlink(objs[i]) != 0)
1,271✔
UNCOV
243
         fatal_errno("unlink: %s", objs[i]);
×
244
   }
245

246
   progress("linking shared library");
1,083✔
247

248
   for (size_t i = 0; i < link_args.count; i++)
8,852✔
249
      free(link_args.items[i]);
7,769✔
250
   ACLEAR(link_args);
1,083✔
251
}
1,083✔
252

253
static void cgen_async_work(void *context, void *arg)
1,271✔
254
{
255
   jit_t *jit = context;
1,271✔
256
   cgen_job_t *job = arg;
1,271✔
257

258
   llvm_obj_t *obj = llvm_obj_new(job->module_name);
1,271✔
259

260
   if (job->index == 0)
1,271✔
261
      llvm_add_abi_version(obj);
1,083✔
262

263
   for (int i = 0; i < job->units.count; i++) {
15,648✔
264
      jit_handle_t handle = jit_lazy_compile(jit, job->units.items[i]);
14,377✔
265
      assert(handle != JIT_HANDLE_INVALID);
14,377✔
266

267
      llvm_aot_compile(obj, jit, handle);
14,377✔
268
   }
269

270
   llvm_obj_finalise(obj, LLVM_O0);
1,271✔
271
   llvm_obj_emit(obj, job->obj_path);
1,271✔
272

273
   ACLEAR(job->units);
1,271✔
274
   free(job->module_name);
1,271✔
275
   free(job);
1,271✔
276
}
1,271✔
277

278
static void cgen_partition_jobs(unit_list_t *units, workq_t *wq,
1,083✔
279
                                const char *base_name, int units_per_job,
280
                                obj_list_t *objs)
281
{
282
   int counter = 0;
1,083✔
283

284
   // Adjust units_per_job to ensure that each job has a roughly equal
285
   // number of units
286
   const int njobs = (units->count + units_per_job - 1) / units_per_job;
1,083✔
287
   const int clamped = MIN(njobs, MAX_JOBS);
1,083✔
288
   units_per_job = (units->count + clamped - 1) / clamped;
1,083✔
289

290
   for (unsigned i = 0; i < units->count; i += units_per_job, counter++) {
2,354✔
291
      char *module_name = xasprintf("%s.%d", base_name, counter);
1,271✔
292
      char *obj_name LOCAL =
2,542✔
293
         xasprintf("_%s.%d." LLVM_OBJ_EXT, module_name, getpid());
1,271✔
294

295
      char obj_path[PATH_MAX];
1,271✔
296
      lib_realpath(lib_work(), obj_name, obj_path, sizeof(obj_path));
1,271✔
297

298
      cgen_job_t *job = xcalloc(sizeof(cgen_job_t));
1,271✔
299
      job->module_name = module_name;
1,271✔
300
      job->obj_path    = xstrdup(obj_path);
1,271✔
301
      job->index       = counter;
1,271✔
302

303
      for (unsigned j = i; j < units->count && j < i + units_per_job; j++)
15,648✔
304
         APUSH(job->units, units->items[j]);
14,377✔
305

306
      APUSH(*objs, job->obj_path);
1,271✔
307

308
      workq_do(wq, cgen_async_work, job);
1,271✔
309
   }
310
}
1,083✔
311

312
static void cgen_native(ident_t name, jit_t *jit, unit_list_t *units)
1,083✔
313
{
314
   workq_t *wq = workq_new(jit);
1,083✔
315

316
   obj_list_t objs = AINIT;
1,083✔
317
   cgen_partition_jobs(units, wq, istr(name), UNITS_PER_JOB, &objs);
1,083✔
318

319
   workq_start(wq);
1,083✔
320
   workq_drain(wq);
1,083✔
321

322
   progress("code generation for %d units", units->count);
1,083✔
323

324
   cgen_link(istr(name), objs.items, objs.count);
1,083✔
325

326
   for (unsigned i = 0; i < objs.count; i++)
2,354✔
327
      free(objs.items[i]);
1,271✔
328
   ACLEAR(objs);
1,083✔
329

330
   workq_free(wq);
1,083✔
331
}
1,083✔
332
#endif   // ENABLE_LLVM
333

334
static void cgen_jit_pack(ident_t name, jit_t *jit, unit_list_t *units)
547✔
335
{
336
   const char *fname LOCAL = xasprintf("_%s.pack", istr(name));
1,094✔
337

338
   FILE *f = lib_fopen(lib_work(), fname, "wb");
547✔
339
   if (f == NULL)
547✔
UNCOV
340
      fatal_errno("fopen: %s", fname);
×
341

342
   jit_write_pack(jit, units->items, units->count, f);
547✔
343
   fclose(f);
547✔
344

345
   progress("writing JIT pack");
547✔
346
}
547✔
347

348
void cgen(tree_t top, unit_registry_t *ur, mir_context_t *mc, jit_t *jit,
1,630✔
349
          cgen_mode_t mode)
350
{
351
   assert(tree_kind(top) == T_ELAB);
1,630✔
352

353
   ident_t work_name = lib_name(lib_work());
1,630✔
354

355
   hset_t *seen = hset_new(16);
1,630✔
356
   unit_list_t units = AINIT;
1,630✔
357

358
   cgen_walk_hier(&units, seen, tree_stmt(top, 0), work_name);
1,630✔
359

360
   for (int i = 0; i < units.count; i++)
21,784✔
361
      cgen_find_dependencies(mc, ur, &units, seen, units.items[i]);
20,154✔
362

363
   hset_free(seen);
1,630✔
364
   seen = NULL;
1,630✔
365

366
   switch (mode) {
1,630✔
367
   case CGEN_NATIVE:
1,083✔
368
#ifdef ENABLE_LLVM
369
      cgen_native(tree_ident(top), jit, &units);
1,083✔
370
#else
371
      fatal("native code generation not enabled in this build");
372
#endif
373
      break;
1,083✔
374
   case CGEN_JIT_PACK:
547✔
375
      cgen_jit_pack(tree_ident(top), jit, &units);
547✔
376
      break;
547✔
377
   }
378

379
   ACLEAR(units);
1,630✔
380
}
1,630✔
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