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

nickg / nvc / 13775379932

10 Mar 2025 09:11PM UTC coverage: 92.318% (-0.001%) from 92.319%
13775379932

push

github

nickg
Fix crash when nvc -i called with no arguments

4 of 6 new or added lines in 1 file covered. (66.67%)

34 existing lines in 2 files now uncovered.

68075 of 73740 relevant lines covered (92.32%)

432883.26 hits per line

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

98.7
/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(vcode_unit_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 void cgen_find_children(vcode_unit_t root, unit_list_t *units)
7,442✔
72
{
73
   const vunit_kind_t kind = vcode_unit_kind(root);
7,442✔
74
   if (kind != VCODE_UNIT_INSTANCE && kind != VCODE_UNIT_PROCESS
7,442✔
75
       && kind != VCODE_UNIT_PROPERTY)
1,194✔
76
      return;
77

78
   for (vcode_unit_t it = vcode_unit_child(root);
6,326✔
79
        it != NULL;
12,553✔
80
        it = vcode_unit_next(it)) {
6,227✔
81
      cgen_find_children(it, units);
6,227✔
82
   }
83

84
   APUSH(*units, root);
6,326✔
85
}
86

87
static bool cgen_is_preload(ident_t name)
16,471✔
88
{
89
   const char *preload[] = {
16,471✔
90
      "STD.STANDARD",
91
      "STD.TEXTIO",
92
      "STD.ENV",
93
      "STD.REFLECTION",
94
      "IEEE.STD_LOGIC",
95
      "IEEE.NUMERIC",
96
      "IEEE.MATH",
97
      "IEEE.FLOAT",
98
      "IEEE.FIXED",
99
      "NVC."
100
   };
101
   const char *str = istr(name);
16,471✔
102
   for (int i = 0; i < ARRAY_LEN(preload); i++) {
95,679✔
103
      if (strncmp(str, preload[i], strlen(preload[i])) == 0)
90,109✔
104
         return true;
105
   }
106

107
   return false;
108
}
109

110
static void cgen_add_dependency(ident_t name, discover_args_t *args)
26,927✔
111
{
112
   if (hset_contains(args->filter, name))
26,927✔
113
      return;
114

115
   vcode_unit_t vu = unit_registry_get(args->registry, name);
8,063✔
116
   if (vu == NULL)
8,063✔
117
      fatal_trace("missing vcode unit %s", istr(name));
118

119
   APUSH(*args->units, vu);
8,063✔
120
   hset_insert(args->filter, name);
8,063✔
121
}
122

123
static void cgen_dep_cb(ident_t name, void *ctx)
16,396✔
124
{
125
   discover_args_t *args = ctx;
16,396✔
126

127
   if (cgen_is_preload(name))
16,396✔
128
      return;
129

130
   cgen_add_dependency(name, args);
5,564✔
131
}
132

133
static void cgen_find_units(vcode_unit_t root, unit_registry_t *ur,
1,215✔
134
                            unit_list_t *units)
135
{
136
   cgen_find_children(root, units);
1,215✔
137

138
   discover_args_t args = {
1,215✔
139
      .units    = units,
140
      .registry = ur,
141
      .filter   = hset_new(64),
1,215✔
142
   };
143

144
   for (unsigned i = 0; i < units->count; i++)
9,431✔
145
      vcode_walk_dependencies(units->items[i], cgen_dep_cb, &args);
8,216✔
146

147
   hset_free(args.filter);
1,215✔
148
}
1,215✔
149

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

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

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

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

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

172
static void cgen_linker_setup(void)
1,218✔
173
{
174
#if defined LINKER_PATH
175
   cgen_link_arg("%s", LINKER_PATH);
1,218✔
176
   cgen_link_arg("--eh-frame-hdr");
1,218✔
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,218✔
199
#endif
200
}
1,218✔
201

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

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

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

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

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

223
   for (int i = 0; i < nobjs; i++)
2,520✔
224
      cgen_link_arg("%s", objs[i]);
1,305✔
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,215✔
250

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

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

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

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

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

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

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

275
   for (int i = 0; i < job->units.count; i++) {
9,521✔
276
      vcode_unit_t vu = job->units.items[i];
8,216✔
277

278
      jit_handle_t handle = jit_lazy_compile(jit, vcode_unit_name(vu));
8,216✔
279
      assert(handle != JIT_HANDLE_INVALID);
8,216✔
280

281
      llvm_aot_compile(obj, jit, handle);
8,216✔
282
   }
283

284
   llvm_obj_finalise(obj, LLVM_O0);
1,305✔
285
   llvm_obj_emit(obj, job->obj_path);
1,305✔
286

287
   ACLEAR(job->units);
1,305✔
288
   free(job->module_name);
1,305✔
289
   free(job);
1,305✔
290
}
1,305✔
291

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

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

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

309
      char obj_path[PATH_MAX];
1,305✔
310
      lib_realpath(lib_work(), obj_name, obj_path, sizeof(obj_path));
1,305✔
311

312
      cgen_job_t *job = xcalloc(sizeof(cgen_job_t));
1,305✔
313
      job->module_name = module_name;
1,305✔
314
      job->obj_path    = xstrdup(obj_path);
1,305✔
315
      job->index       = counter;
1,305✔
316

317
      for (unsigned j = i; j < units->count && j < i + units_per_job; j++)
9,521✔
318
         APUSH(job->units, units->items[j]);
8,216✔
319

320
      APUSH(*objs, job->obj_path);
1,305✔
321

322
      workq_do(wq, cgen_async_work, job);
1,305✔
323
   }
324
}
1,215✔
325

326
void cgen(tree_t top, unit_registry_t *ur, jit_t *jit)
1,215✔
327
{
328
   assert(tree_kind(top) == T_ELAB);
1,215✔
329

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

337
   unit_list_t units = AINIT;
1,215✔
338
   cgen_find_units(vu, ur, &units);
1,215✔
339

340
   workq_t *wq = workq_new(jit);
1,215✔
341

342
   ident_t name = tree_ident(top);
1,215✔
343

344
   obj_list_t objs = AINIT;
1,215✔
345
   cgen_partition_jobs(&units, wq, istr(name), UNITS_PER_JOB, top, &objs);
1,215✔
346

347
   workq_start(wq);
1,215✔
348
   workq_drain(wq);
1,215✔
349

350
   progress("code generation for %d units", units.count);
1,215✔
351

352
   cgen_link(istr(name), objs.items, objs.count);
1,215✔
353

354
   for (unsigned i = 0; i < objs.count; i++)
2,520✔
355
      free(objs.items[i]);
1,305✔
356
   ACLEAR(objs);
1,215✔
357

358
   ACLEAR(units);
1,215✔
359
   workq_free(wq);
1,215✔
360
}
1,215✔
361

362
static void preload_walk_index(lib_t lib, ident_t ident, int kind, void *ctx)
144✔
363
{
364
   discover_args_t *args = ctx;
144✔
365

366
   if (kind != T_PACKAGE && kind != T_PACK_INST)
144✔
367
      return;
79✔
368
   else if (!cgen_is_preload(ident))
75✔
369
      return;
370

371
   tree_t unit = lib_get(lib, ident);
69✔
372

373
   if (is_uninstantiated_package(unit))
69✔
374
      return;
375

376
   cgen_add_dependency(ident, args);
65✔
377

378
   ident_t helper_suffix[] = {
65✔
379
      ident_new("image"),
65✔
380
      ident_new("value"),
65✔
381
      ident_new("resolved"),
65✔
382
      ident_new("copy"),
65✔
383
      ident_new("new"),
65✔
384
   };
385

386
   const int ndecls = tree_decls(unit);
65✔
387
   for (int i = 0; i < ndecls; i++) {
9,177✔
388
      tree_t d = tree_decl(unit, i);
9,112✔
389
      switch (tree_kind(d)) {
9,112✔
390
      case T_FUNC_DECL:
7,923✔
391
      case T_FUNC_BODY:
392
      case T_FUNC_INST:
393
      case T_PROC_DECL:
394
      case T_PROC_BODY:
395
      case T_PROC_INST:
396
         {
397
            const subprogram_kind_t kind = tree_subkind(d);
7,923✔
398
            if (!is_open_coded_builtin(kind))
7,923✔
399
               cgen_add_dependency(tree_ident2(d), args);
6,671✔
400
         }
401
         break;
402
      case T_PROT_DECL:
20✔
403
         {
404
            type_t type = tree_type(d);
20✔
405
            ident_t id = type_ident(type);
20✔
406

407
            cgen_add_dependency(id, args);
20✔
408

409
            const int nmeth = tree_decls(d);
20✔
410
            for (int i = 0; i < nmeth; i++) {
143✔
411
               tree_t m = tree_decl(d, i);
123✔
412
               if (is_subprogram(m))
123✔
413
                  cgen_add_dependency(tree_ident2(m), args);
123✔
414
            }
415
         }
416
         break;
417
      case T_TYPE_DECL:
217✔
418
         {
419
            type_t type = tree_type(d);
217✔
420
            ident_t id = type_ident(type);
217✔
421

422
            for (int i = 0; i < ARRAY_LEN(helper_suffix); i++) {
1,302✔
423
               ident_t func = ident_prefix(id, helper_suffix[i], '$');
1,085✔
424
               if (unit_registry_query(args->registry, func))
1,085✔
425
                  cgen_add_dependency(func, args);
289✔
426
            }
427
         }
428
         break;
429
      default:
430
         break;
431
      }
432
   }
433
}
434

435
static void preload_dep_cb(ident_t name, void *ctx)
14,195✔
436
{
437
   discover_args_t *args = ctx;
14,195✔
438
   cgen_add_dependency(name, args);
14,195✔
439
}
14,195✔
440

441
static void preload_do_link(const char *so_name, const char *obj_file)
3✔
442
{
443
   cgen_linker_setup();
3✔
444

445
   cgen_link_arg("-o");
3✔
446
   cgen_link_arg("%s", so_name);
3✔
447

448
   cgen_link_arg("%s", obj_file);
3✔
449

450
#if defined LINKER_PATH && defined __OpenBSD__
451
   // Extra linker arguments to make constructors work on OpenBSD
452
   cgen_link_arg("-L/usr/lib");
453
   cgen_link_arg("-lcompiler_rt");
454
   cgen_link_arg("/usr/lib/crtendS.o");
455
#endif
456

457
#ifdef IMPLIB_REQUIRED
458
   LOCAL_TEXT_BUF tb = tb_new();
459
   const char *cyglib = getenv("NVC_IMP_LIB");
460
   if (cyglib != NULL)
461
      tb_cat(tb, cyglib);
462
   else
463
      get_lib_dir(tb);
464

465
   cgen_link_arg("-L%s", tb_get(tb));
466
   cgen_link_arg("-lnvcimp");
467
#endif
468

469
   APUSH(link_args, NULL);
3✔
470

471
   run_program((const char * const *)link_args.items);
3✔
472

473
   progress("linking shared library");
3✔
474

475
   for (size_t i = 0; i < link_args.count; i++)
24✔
476
      free(link_args.items[i]);
21✔
477
   ACLEAR(link_args);
3✔
478
}
3✔
479

480
void aotgen(const char *outfile, char **argv, int argc)
3✔
481
{
482
   unit_list_t units = AINIT;
3✔
483
   mir_context_t *mc = mir_context_new();
3✔
484
   unit_registry_t *ur = unit_registry_new();
3✔
485

486
   discover_args_t args = {
3✔
487
      .registry = ur,
488
      .units    = &units,
489
      .filter   = hset_new(64),
3✔
490
   };
491

492
   for (int i = 0; i < argc; i++) {
12✔
493
      for (char *p = argv[i]; *p; p++)
39✔
494
         *p = toupper((int)*p);
30✔
495

496
      lib_t lib = lib_require(ident_new(argv[i]));
9✔
497
      lib_walk_index(lib, preload_walk_index, &args);
9✔
498
   }
499

500
   for (unsigned i = 0; i < units.count; i++)
6,176✔
501
      vcode_walk_dependencies(units.items[i], preload_dep_cb, &args);
6,173✔
502

503
   hset_free(args.filter);
3✔
504
   args.filter = NULL;
3✔
505

506
   jit_t *jit = jit_new(ur, mc);
3✔
507

508
   progress("initialising");
3✔
509

510
   llvm_obj_t *obj = llvm_obj_new("preload");
3✔
511
   llvm_add_abi_version(obj);
3✔
512

513
   for (int i = 0; i < units.count; i++) {
6,176✔
514
      vcode_unit_t vu = units.items[i];
6,173✔
515
      vcode_select_unit(vu);
6,173✔
516

517
      jit_handle_t handle = jit_lazy_compile(jit, vcode_unit_name(vu));
6,173✔
518
      assert(handle != JIT_HANDLE_INVALID);
6,173✔
519

520
      llvm_aot_compile(obj, jit, handle);
6,173✔
521
   }
522

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

525
   llvm_opt_level_t olevel = opt_get_int(OPT_OPTIMISE);
3✔
526
   llvm_obj_finalise(obj, olevel);
3✔
527

528
   progress("LLVM module optimisation passes");
3✔
529

530
   char *objfile LOCAL = nvc_temp_file();
6✔
531
   llvm_obj_emit(obj, objfile);
3✔
532

533
   progress("native code generation");
3✔
534

535
   preload_do_link(outfile, objfile);
3✔
536

537
   if (remove(objfile) != 0)
3✔
UNCOV
538
      warnf("remove: %s: %s", objfile, last_os_error());
×
539

540
   ACLEAR(units);
3✔
541
   jit_free(jit);
3✔
542
   unit_registry_free(ur);
3✔
543
   mir_context_free(mc);
3✔
544
}
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