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

nickg / nvc / 13887457702

16 Mar 2025 09:12PM UTC coverage: 92.285% (-0.04%) from 92.324%
13887457702

push

github

nickg
Tag Docker images on releases. Fixes #1165

68273 of 73981 relevant lines covered (92.28%)

423265.54 hits per line

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

98.01
/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 "vcode.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
static A(char *) link_args;
60
static A(char *) cleanup_files = AINIT;
61

62
#define UNITS_PER_JOB 25
63

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

71
static bool cgen_is_preload(ident_t name)
9,896✔
72
{
73
   const char *preload[] = {
9,896✔
74
      "STD.STANDARD",
75
      "STD.TEXTIO",
76
      "STD.ENV",
77
      "STD.REFLECTION",
78
      "IEEE.STD_LOGIC",
79
      "IEEE.NUMERIC",
80
      "IEEE.MATH",
81
      "IEEE.FLOAT",
82
      "IEEE.FIXED",
83
      "NVC."
84
   };
85
   const char *str = istr(name);
9,896✔
86
   for (int i = 0; i < ARRAY_LEN(preload); i++) {
44,581✔
87
      if (strncmp(str, preload[i], strlen(preload[i])) == 0)
42,641✔
88
         return true;
89
   }
90

91
   return false;
92
}
93

94
static void cgen_find_dependencies(mir_context_t *mc, unit_registry_t *ur,
14,454✔
95
                                   unit_list_t *units, hset_t *seen,
96
                                   ident_t name, bool preload)
97
{
98
   mir_unit_t *mu = mir_get_unit(mc, name);
14,454✔
99
   if (mu == NULL) {
14,454✔
100
      vcode_unit_t vu = unit_registry_get(ur, name);
10,859✔
101
      if (vu == NULL)
10,859✔
102
         fatal_trace("missing vcode for %s", istr(name));
103

104
      mu = mir_import(mc, vu);
10,859✔
105
      mir_put_unit(mc, mu);
10,859✔
106
   }
107

108
   const int nlink = mir_count_linkage(mu);
14,454✔
109
   for (int i = 0; i < nlink; i++) {
42,799✔
110
      ident_t link = mir_get_linkage(mu, i);
28,345✔
111
      if (hset_contains(seen, link))
28,345✔
112
         continue;
18,203✔
113
      else if (preload || !cgen_is_preload(link)) {
10,142✔
114
         APUSH(*units, link);
2,255✔
115
         hset_insert(seen, link);
2,255✔
116
      }
117
   }
118
}
14,454✔
119

120
static void cgen_walk_hier(unit_list_t *units, hset_t *seen, tree_t block,
3,136✔
121
                           ident_t prefix)
122
{
123
   assert(tree_kind(block) == T_BLOCK);
3,136✔
124

125
   ident_t unit_name = ident_prefix(prefix, tree_ident(block), '.');
3,136✔
126
   APUSH(*units, unit_name);
3,136✔
127
   hset_insert(seen, unit_name);
3,136✔
128

129
   const int nstmts = tree_stmts(block);
3,136✔
130
   for (int i = 0; i < nstmts; i++) {
8,288✔
131
      tree_t s = tree_stmt(block, i);
5,152✔
132
      switch (tree_kind(s)) {
5,152✔
133
      case T_BLOCK:
1,920✔
134
         cgen_walk_hier(units, seen, s, unit_name);
1,920✔
135
         break;
1,920✔
136
      case T_PROCESS:
3,159✔
137
      case T_PSL_DIRECT:
138
         {
139
            ident_t proc_name = ident_prefix(unit_name, tree_ident(s), '.');
3,159✔
140
            APUSH(*units, proc_name);
3,159✔
141
            hset_insert(seen, proc_name);
3,159✔
142
         }
143
         break;
3,159✔
144
      default:
145
         break;
146
      }
147
   }
148
}
3,136✔
149

150
static void cleanup_temp_dll(void)
962✔
151
{
152
   for (int i = 0; i < cleanup_files.count; i++) {
1,924✔
153
      if (remove(cleanup_files.items[i]) == -1)
962✔
154
         warnf("cannot remove %s: %s", cleanup_files.items[i], last_os_error());
×
155

156
      free(cleanup_files.items[i]);
962✔
157
   }
158

159
   ACLEAR(cleanup_files);
962✔
160
}
962✔
161

162
static void cgen_link_arg(const char *fmt, ...)
7,404✔
163
{
164
   va_list ap;
7,404✔
165
   va_start(ap, fmt);
7,404✔
166
   char *buf = xvasprintf(fmt, ap);
7,404✔
167
   va_end(ap);
7,404✔
168

169
   APUSH(link_args, buf);
7,404✔
170
}
7,404✔
171

172
static void cgen_linker_setup(void)
1,219✔
173
{
174
#if defined LINKER_PATH
175
   cgen_link_arg("%s", LINKER_PATH);
1,219✔
176
   cgen_link_arg("--eh-frame-hdr");
1,219✔
177
#elif defined SYSTEM_CC
178
   cgen_link_arg("%s", SYSTEM_CC);
179
#elif defined BOOTSTRAP_CC
180
   cgen_link_arg("%s", BOOTSTRAP_CC);
181
#else
182
   fatal_trace("configured without external C compiler or linker");
183
#endif
184

185
#if defined __APPLE__
186
   cgen_link_arg("-bundle");
187
   cgen_link_arg("-flat_namespace");
188
   cgen_link_arg("-undefined");
189
   cgen_link_arg("dynamic_lookup");
190
#ifdef HAVE_NO_FIXUP_CHAINS
191
   cgen_link_arg("-Wl,-no_fixup_chains");
192
#endif
193
#elif defined __OpenBSD__
194
   cgen_link_arg("-Bdynamic");
195
   cgen_link_arg("-shared");
196
   cgen_link_arg("/usr/lib/crtbeginS.o");
197
#else
198
   cgen_link_arg("-shared");
1,219✔
199
#endif
200
}
1,219✔
201

202
static void cgen_link(const char *module_name, char **objs, int nobjs)
1,216✔
203
{
204
   cgen_linker_setup();
1,216✔
205

206
   LOCAL_TEXT_BUF tb = tb_new();
1,216✔
207
   tb_printf(tb, "_%s", module_name);
1,216✔
208
   if (opt_get_int(OPT_NO_SAVE))
1,216✔
209
      tb_printf(tb, ".%d", getpid());
962✔
210
   tb_cat(tb, "." DLL_EXT);
1,216✔
211

212
   char so_path[PATH_MAX];
1,216✔
213
   lib_realpath(lib_work(), tb_get(tb), so_path, PATH_MAX);
1,216✔
214

215
   if (opt_get_int(OPT_NO_SAVE)) {
1,216✔
216
      APUSH(cleanup_files, xstrdup(so_path));
962✔
217
      atexit(cleanup_temp_dll);
962✔
218
   }
219

220
   cgen_link_arg("-o");
1,216✔
221
   cgen_link_arg("%s", so_path);
1,216✔
222

223
   for (int i = 0; i < nobjs; i++)
2,522✔
224
      cgen_link_arg("%s", objs[i]);
1,306✔
225

226
#if defined LINKER_PATH && defined __OpenBSD__
227
   // Extra linker arguments to make constructors work on OpenBSD
228
   cgen_link_arg("-L/usr/lib");
229
   cgen_link_arg("-lcompiler_rt");
230
   cgen_link_arg("/usr/lib/crtendS.o");
231
#endif
232

233
#ifdef IMPLIB_REQUIRED
234
   tb_rewind(tb);
235
   const char *cyglib = getenv("NVC_IMP_LIB");
236
   if (cyglib != NULL)
237
      tb_cat(tb, cyglib);
238
   else
239
      get_lib_dir(tb);
240

241
   cgen_link_arg("-L%s", tb_get(tb));
242
   cgen_link_arg("-L%s/nvc", LIBDIR);
243
   cgen_link_arg("-lnvcimp");
244

245
   const char *preload_vers[] = { "93", "93", "93", "93", "08", "19" };
246
   cgen_link_arg("%s/preload%s.dll", tb_get(tb), preload_vers[standard()]);
247
#endif
248

249
   APUSH(link_args, NULL);
1,216✔
250

251
   run_program((const char * const *)link_args.items);
1,216✔
252

253
   for (int i = 0; i < nobjs ; i++) {
2,522✔
254
      if (unlink(objs[i]) != 0)
1,306✔
255
         fatal_errno("unlink: %s", objs[i]);
×
256
   }
257

258
   progress("linking shared library");
1,216✔
259

260
   for (size_t i = 0; i < link_args.count; i++)
9,818✔
261
      free(link_args.items[i]);
8,602✔
262
   ACLEAR(link_args);
1,216✔
263
}
1,216✔
264

265
static void cgen_async_work(void *context, void *arg)
1,306✔
266
{
267
   jit_t *jit = context;
1,306✔
268
   cgen_job_t *job = arg;
1,306✔
269

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

272
   if (job->index == 0)
1,306✔
273
      llvm_add_abi_version(obj);
1,216✔
274

275
   for (int i = 0; i < job->units.count; i++) {
9,535✔
276
      jit_handle_t handle = jit_lazy_compile(jit, job->units.items[i]);
8,229✔
277
      assert(handle != JIT_HANDLE_INVALID);
8,229✔
278

279
      llvm_aot_compile(obj, jit, handle);
8,229✔
280
   }
281

282
   llvm_obj_finalise(obj, LLVM_O0);
1,306✔
283
   llvm_obj_emit(obj, job->obj_path);
1,306✔
284

285
   ACLEAR(job->units);
1,306✔
286
   free(job->module_name);
1,306✔
287
   free(job);
1,306✔
288
}
1,306✔
289

290
static void cgen_partition_jobs(unit_list_t *units, workq_t *wq,
1,216✔
291
                                const char *base_name, int units_per_job,
292
                                tree_t top, obj_list_t *objs)
293
{
294
   int counter = 0;
1,216✔
295

296
   // Adjust units_per_job to ensure that each job has a roughly equal
297
   // number of units
298
   const int njobs = (units->count + units_per_job - 1) / units_per_job;
1,216✔
299
   const int clamped = MIN(njobs, MAX_JOBS);
1,216✔
300
   units_per_job = (units->count + clamped - 1) / clamped;
1,216✔
301

302
   for (unsigned i = 0; i < units->count; i += units_per_job, counter++) {
2,522✔
303
      char *module_name = xasprintf("%s.%d", base_name, counter);
1,306✔
304
      char *obj_name LOCAL =
2,612✔
305
         xasprintf("_%s.%d." LLVM_OBJ_EXT, module_name, getpid());
1,306✔
306

307
      char obj_path[PATH_MAX];
1,306✔
308
      lib_realpath(lib_work(), obj_name, obj_path, sizeof(obj_path));
1,306✔
309

310
      cgen_job_t *job = xcalloc(sizeof(cgen_job_t));
1,306✔
311
      job->module_name = module_name;
1,306✔
312
      job->obj_path    = xstrdup(obj_path);
1,306✔
313
      job->index       = counter;
1,306✔
314

315
      for (unsigned j = i; j < units->count && j < i + units_per_job; j++)
9,535✔
316
         APUSH(job->units, units->items[j]);
8,229✔
317

318
      APUSH(*objs, job->obj_path);
1,306✔
319

320
      workq_do(wq, cgen_async_work, job);
1,306✔
321
   }
322
}
1,216✔
323

324
void cgen(tree_t top, unit_registry_t *ur, mir_context_t *mc, jit_t *jit)
1,216✔
325
{
326
   assert(tree_kind(top) == T_ELAB);
1,216✔
327

328
   ident_t b0_name = tree_ident(tree_stmt(top, 0));
1,216✔
329
   ident_t work_name = lib_name(lib_work());
1,216✔
330
   ident_t unit_name = ident_prefix(work_name, b0_name, '.');
1,216✔
331
   vcode_unit_t vu = unit_registry_get(ur, unit_name);
1,216✔
332
   if (vu == NULL)
1,216✔
333
      fatal_trace("missing vcode for %s", istr(unit_name));
334

335
   hset_t *seen = hset_new(16);
1,216✔
336
   unit_list_t units = AINIT;
1,216✔
337

338
   cgen_walk_hier(&units, seen, tree_stmt(top, 0), work_name);
1,216✔
339

340
   for (int i = 0; i < units.count; i++)
9,445✔
341
      cgen_find_dependencies(mc, ur, &units, seen, units.items[i], false);
8,229✔
342

343
   hset_free(seen);
1,216✔
344
   seen = NULL;
1,216✔
345

346
   workq_t *wq = workq_new(jit);
1,216✔
347

348
   ident_t name = tree_ident(top);
1,216✔
349

350
   obj_list_t objs = AINIT;
1,216✔
351
   cgen_partition_jobs(&units, wq, istr(name), UNITS_PER_JOB, top, &objs);
1,216✔
352

353
   workq_start(wq);
1,216✔
354
   workq_drain(wq);
1,216✔
355

356
   progress("code generation for %d units", units.count);
1,216✔
357

358
   cgen_link(istr(name), objs.items, objs.count);
1,216✔
359

360
   for (unsigned i = 0; i < objs.count; i++)
2,522✔
361
      free(objs.items[i]);
1,306✔
362
   ACLEAR(objs);
1,216✔
363

364
   ACLEAR(units);
1,216✔
365
   workq_free(wq);
1,216✔
366
}
1,216✔
367

368
static void preload_walk_index(lib_t lib, ident_t ident, int kind, void *ctx)
144✔
369
{
370
   discover_args_t *args = ctx;
144✔
371

372
   if (kind != T_PACKAGE && kind != T_PACK_INST)
144✔
373
      return;
374
   else if (!cgen_is_preload(ident))
75✔
375
      return;
376

377
   tree_t unit = lib_get(lib, ident);
69✔
378

379
   if (is_uninstantiated_package(unit))
69✔
380
      return;
381

382
   APUSH(*args->units, ident);
65✔
383

384
   const int ndecls = tree_decls(unit);
65✔
385
   for (int i = 0; i < ndecls; i++) {
9,177✔
386
      tree_t d = tree_decl(unit, i);
9,112✔
387
      switch (tree_kind(d)) {
9,112✔
388
      case T_FUNC_DECL:
6,607✔
389
      case T_FUNC_INST:
390
      case T_PROC_DECL:
391
      case T_PROC_INST:
392
         {
393
            const subprogram_kind_t kind = tree_subkind(d);
6,607✔
394
            if (!is_open_coded_builtin(kind))
6,607✔
395
               APUSH(*args->units, tree_ident2(d));
5,355✔
396
         }
397
         break;
398
      case T_PROT_DECL:
20✔
399
         {
400
            type_t type = tree_type(d);
20✔
401
            ident_t id = type_ident(type);
20✔
402

403
            APUSH(*args->units, id);
20✔
404

405
            const int nmeth = tree_decls(d);
20✔
406
            for (int i = 0; i < nmeth; i++) {
143✔
407
               tree_t m = tree_decl(d, i);
123✔
408
               if (is_subprogram(m))
123✔
409
                  APUSH(*args->units, tree_ident2(m));
123✔
410
            }
411
         }
412
         break;
413
      case T_TYPE_DECL:
217✔
414
         {
415
            type_t type = tree_type(d);
217✔
416
            ident_t id = type_ident(type);
217✔
417

418
            if (type_is_representable(type)) {
217✔
419
               ident_t image = ident_prefix(id, ident_new("image"), '$');
138✔
420
               APUSH(*args->units, image);
138✔
421

422
               ident_t value = ident_prefix(id, ident_new("value"), '$');
138✔
423
               APUSH(*args->units, value);
138✔
424
            }
425

426
            if (type_is_record(type) && !type_const_bounds(type)) {
217✔
427
               ident_t new = ident_prefix(id, ident_new("new"), '$');
×
428
               APUSH(*args->units, new);
×
429
            }
430

431
            if (!type_is_homogeneous(type) && can_be_signal(type)) {
217✔
432
               ident_t resolved = ident_prefix(id, ident_new("resolved"), '$');
13✔
433
               APUSH(*args->units, resolved);
13✔
434

435
               ident_t last_value = ident_sprintf("%s$last_value", istr(id));
13✔
436
               APUSH(*args->units, last_value);
13✔
437

438
               ident_t last_event = ident_sprintf("%s$last_event", istr(id));
13✔
439
               APUSH(*args->units, last_event);
13✔
440

441
               ident_t last_active = ident_sprintf("%s$last_active", istr(id));
13✔
442
               APUSH(*args->units, last_active);
13✔
443

444
               ident_t driving = ident_prefix(id, ident_new("driving"), '$');
13✔
445
               APUSH(*args->units, driving);
13✔
446
            }
447
         }
448
         break;
449
      default:
450
         break;
451
      }
452
   }
453
}
454

455
static void preload_do_link(const char *so_name, const char *obj_file)
3✔
456
{
457
   cgen_linker_setup();
3✔
458

459
   cgen_link_arg("-o");
3✔
460
   cgen_link_arg("%s", so_name);
3✔
461

462
   cgen_link_arg("%s", obj_file);
3✔
463

464
#if defined LINKER_PATH && defined __OpenBSD__
465
   // Extra linker arguments to make constructors work on OpenBSD
466
   cgen_link_arg("-L/usr/lib");
467
   cgen_link_arg("-lcompiler_rt");
468
   cgen_link_arg("/usr/lib/crtendS.o");
469
#endif
470

471
#ifdef IMPLIB_REQUIRED
472
   LOCAL_TEXT_BUF tb = tb_new();
473
   const char *cyglib = getenv("NVC_IMP_LIB");
474
   if (cyglib != NULL)
475
      tb_cat(tb, cyglib);
476
   else
477
      get_lib_dir(tb);
478

479
   cgen_link_arg("-L%s", tb_get(tb));
480
   cgen_link_arg("-lnvcimp");
481
#endif
482

483
   APUSH(link_args, NULL);
3✔
484

485
   run_program((const char * const *)link_args.items);
3✔
486

487
   progress("linking shared library");
3✔
488

489
   for (size_t i = 0; i < link_args.count; i++)
24✔
490
      free(link_args.items[i]);
21✔
491
   ACLEAR(link_args);
3✔
492
}
3✔
493

494
void aotgen(const char *outfile, char **argv, int argc)
3✔
495
{
496
   unit_list_t initial = AINIT;
3✔
497
   unit_list_t units = AINIT;
3✔
498
   mir_context_t *mc = mir_context_new();
3✔
499
   unit_registry_t *ur = unit_registry_new();
3✔
500

501
   discover_args_t args = {
3✔
502
      .registry = ur,
503
      .units    = &units,
504
      .filter   = hset_new(64),
3✔
505
   };
506

507
   for (int i = 0; i < argc; i++) {
12✔
508
      for (char *p = argv[i]; *p; p++)
39✔
509
         *p = toupper((int)*p);
30✔
510

511
      lib_t lib = lib_require(ident_new(argv[i]));
9✔
512
      lib_walk_index(lib, preload_walk_index, &args);
9✔
513
   }
514

515
   hset_t *seen = hset_new(128);
3✔
516

517
   for (int i = 0; i < units.count; i++)
5,907✔
518
      hset_insert(seen, units.items[i]);
5,904✔
519

520
   for (int i = 0; i < units.count; i++)
6,228✔
521
      cgen_find_dependencies(mc, ur, &units, seen, units.items[i], true);
6,225✔
522

523
   ACLEAR(initial);
3✔
524
   hset_free(seen);
3✔
525
   seen = NULL;
3✔
526
   hset_free(args.filter);
3✔
527
   args.filter = NULL;
3✔
528

529
   jit_t *jit = jit_new(ur, mc);
3✔
530

531
   progress("initialising");
3✔
532

533
   llvm_obj_t *obj = llvm_obj_new("preload");
3✔
534
   llvm_add_abi_version(obj);
3✔
535

536
   for (int i = 0; i < units.count; i++) {
6,228✔
537
      jit_handle_t handle = jit_lazy_compile(jit, units.items[i]);
6,225✔
538
      assert(handle != JIT_HANDLE_INVALID);
6,225✔
539

540
      llvm_aot_compile(obj, jit, handle);
6,225✔
541
   }
542

543
   progress("code generation for %d units", units.count);
3✔
544

545
   llvm_opt_level_t olevel = opt_get_int(OPT_OPTIMISE);
3✔
546
   llvm_obj_finalise(obj, olevel);
3✔
547

548
   progress("LLVM module optimisation passes");
3✔
549

550
   char *objfile LOCAL = nvc_temp_file();
6✔
551
   llvm_obj_emit(obj, objfile);
3✔
552

553
   progress("native code generation");
3✔
554

555
   preload_do_link(outfile, objfile);
3✔
556

557
   if (remove(objfile) != 0)
3✔
558
      warnf("remove: %s: %s", objfile, last_os_error());
×
559

560
   ACLEAR(units);
3✔
561
   jit_free(jit);
3✔
562
   unit_registry_free(ur);
3✔
563
   mir_context_free(mc);
3✔
564
}
3✔
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

© 2025 Coveralls, Inc