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

nickg / nvc / 27680015718

17 Jun 2026 09:38AM UTC coverage: 92.286% (+0.02%) from 92.269%
27680015718

push

github

nickg
Constant folding for Verilog wide vector comparison

14 of 14 new or added lines in 1 file covered. (100.0%)

896 existing lines in 10 files now uncovered.

79175 of 85793 relevant lines covered (92.29%)

632516.07 hits per line

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

93.49
/src/printf.c
1
//
2
//  Copyright (C) 2025-2026  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 "ident.h"
20
#include "mir/mir-node.h"
21
#include "object.h"
22
#include "printf.h"
23
#include "thread.h"
24
#include "type.h"
25

26
#include <assert.h>
27
#include <stdio.h>
28
#include <string.h>
29
#include <stdlib.h>
30
#include <unistd.h>
31
#include <limits.h>
32

33
#define ANSI_RESET      0
34
#define ANSI_BOLD       1
35
#define ANSI_FG_BLACK   30
36
#define ANSI_FG_RED     31
37
#define ANSI_FG_GREEN   32
38
#define ANSI_FG_YELLOW  33
39
#define ANSI_FG_BLUE    34
40
#define ANSI_FG_MAGENTA 35
41
#define ANSI_FG_CYAN    36
42
#define ANSI_FG_WHITE   37
43

44
#define MAX_ARGS 25
45

46
typedef struct _printf_state printf_state_t;
47
typedef struct _printf_arg printf_arg_t;
48

49
typedef union {
50
   long long  ll;
51
   long       l;
52
   size_t     z;
53
   double     f;
54
   int        i;
55
   void      *p;
56
} printf_value_t;
57

58
typedef int (*fmt_fn_t)(ostream_t *, printf_state_t *, printf_arg_t *);
59

60
typedef struct _printf_arg {
61
   const char     *start;
62
   size_t          len;
63
   fmt_fn_t        fn;
64
   printf_value_t  value;
65
   int             width;
66
   int             precision;
67
} printf_arg_t;
68

69
typedef struct _printf_state {
70
   printf_arg_t args[MAX_ARGS];
71
   unsigned     nargs;
72
   unsigned     pos;
73
} printf_state_t;
74

75
typedef struct {
76
   const char *name;
77
   int         value;
78
} color_escape_t;
79

80
static const color_escape_t escapes[] = {
81
   { "",        ANSI_RESET },
82
   { "bold",    ANSI_BOLD },
83
   { "black",   ANSI_FG_BLACK },
84
   { "red",     ANSI_FG_RED },
85
   { "green",   ANSI_FG_GREEN },
86
   { "yellow",  ANSI_FG_YELLOW },
87
   { "blue",    ANSI_FG_BLUE },
88
   { "magenta", ANSI_FG_MAGENTA },
89
   { "cyan",    ANSI_FG_CYAN },
90
   { "white",   ANSI_FG_WHITE },
91
};
92

93
static int printf_interpret(ostream_t *os, printf_state_t *state,
94
                            const char *fmt, const char *end);
95

96
int ostream_write(ostream_t *os, const char *buf, size_t len)
2,603,877✔
97
{
98
   (*os->callback)(buf, len, os->context);
2,603,877✔
99
   return len;
2,603,877✔
100
}
101

102
int ostream_putc(ostream_t *os, char ch)
816,527✔
103
{
104
   char buf[1] = { ch };
816,527✔
105
   return ostream_write(os, buf, 1);
816,527✔
106
}
107

108
int ostream_puts(ostream_t *os, const char *str)
24,031✔
109
{
110
   if (str == NULL)
24,031✔
UNCOV
111
      return ostream_write(os, "(null)", 6);
×
112
   else
113
      return ostream_write(os, str, strlen(str));
24,031✔
114
}
115

116
static int format_ident(ostream_t *os, printf_state_t *state, printf_arg_t *arg)
413✔
117
{
118
   ident_t id = arg->value.p;
413✔
119
   return ostream_write(os, istr(id), ident_len(id));
413✔
120
}
121

122
static int format_ident_toupper(ostream_t *os, printf_state_t *state,
230✔
123
                                printf_arg_t *arg)
124
{
125
   ident_t id = arg->value.p;
230✔
126
   size_t len = ident_len(id);
230✔
127
   const char *str = istr(id);
230✔
128
   int nchars = 0;
230✔
129
   char chunk[32];
230✔
130

131
   for (int i = 0; i < len; i += sizeof(chunk)) {
460✔
132
      const int tocopy = MIN(sizeof(chunk), len - i);
230✔
133
      for (int j = 0; j < tocopy; j++)
1,100✔
134
         chunk[j] = toupper_iso88591(str[i + j]);
870✔
135

136
      nchars += ostream_write(os, chunk, tocopy);
230✔
137
   }
138

139
   return nchars;
230✔
140
}
141

142
static int format_type(ostream_t *os, printf_state_t *state, printf_arg_t *arg)
228✔
143
{
144
   type_t type = arg->value.p;
228✔
145

146
   // Print a fully qualified name if there is another type in the
147
   // argument list with the same simple name
148

149
   for (int i = 0; i < state->nargs; i++) {
443✔
150
      const printf_arg_t *other = &(state->args[i]);
353✔
151
      if (other != arg && other->fn == format_type)
353✔
152
         return ostream_puts(os, type_pp2(type, other->value.p));
138✔
153
   }
154

155
   return ostream_puts(os, type_pp(type));
90✔
156
}
157

UNCOV
158
static int format_object_kind(ostream_t *os, printf_state_t *state,
×
159
                              printf_arg_t *arg)
160
{
161
   object_t *obj = arg->value.p;
×
UNCOV
162
   return ostream_puts(os, object_kind_str(obj));
×
163
}
164

165
static int format_mir(ostream_t *os, printf_state_t *state, printf_arg_t *arg)
16✔
166
{
167
   mir_value_t *value = arg->value.p;
16✔
168
   char buf[64];
16✔
169

170
   switch (value->tag) {
16✔
171
   case MIR_TAG_NODE:
4✔
172
      checked_sprintf(buf, sizeof(buf), "%%%d", value->id);
4✔
173
      break;
4✔
174
   case MIR_TAG_CONST:
12✔
175
      checked_sprintf(buf, sizeof(buf), "#%d",
12✔
176
                      value->id - (1 << (_MIR_ID_BITS - 1)));
12✔
177
      break;
12✔
178
   default:
×
179
      checked_sprintf(buf, sizeof(buf), "{tag:%d, id:%x}",
×
180
                      value->tag, value->id);
×
UNCOV
181
      break;
×
182
   }
183

184
   return ostream_puts(os, buf);
16✔
185
}
186

187
static int delegate(ostream_t *os, printf_state_t *s, printf_arg_t *arg, ...)
869,786✔
188
{
189
   char spec[32];
869,786✔
190
   assert(arg->len + 1 < sizeof(spec));
869,786✔
191
   memcpy(spec, arg->start, arg->len);
869,786✔
192
   spec[arg->len] = '\0';
869,786✔
193

194
   va_list ap, ap2;
869,786✔
195
   va_start(ap, arg);
869,786✔
196
   va_copy(ap2, ap);
869,786✔
197

198
   char small[64];
869,786✔
199
   int req = vsnprintf(small, sizeof(small), spec, ap);
869,786✔
200

201
   if (req + 1 > sizeof(small)) {
869,786✔
202
      char *large = xmalloc(req + 1);
872✔
203
      vsnprintf(large, req + 1, spec, ap2);
872✔
204
      ostream_write(os, large, req);
872✔
205
      free(large);
872✔
206
   }
207
   else
208
      ostream_write(os, small, req);
868,914✔
209

210
   va_end(ap);
869,786✔
211
   va_end(ap2);
869,786✔
212
   return req;
869,786✔
213
}
214

215
static fmt_fn_t get_pointer_formatter(char ch)
892✔
216
{
217
   switch (ch) {
892✔
218
   case 'i': return format_ident;
219
   case 'I': return format_ident_toupper;
230✔
220
   case 'T': return format_type;
228✔
UNCOV
221
   case 'K': return format_object_kind;
×
222
   case 'M': return format_mir;
16✔
223
   default: return NULL;
5✔
224
   }
225
}
226

227
static int ansi_escape(ostream_t *os, printf_state_t *state, const char **fmt)
205,894✔
228
{
229
   bool has_format = false;
205,894✔
230
   const char *start = *fmt;
205,894✔
231
   do {
1,083,800✔
232
      has_format |= (**fmt == '%');
1,083,800✔
233
      (*fmt)++;
1,083,800✔
234
   } while (**fmt != '\0' && **fmt != '$');
1,083,800✔
235

236
   if (**fmt == '\0')
205,894✔
237
      return ostream_write(os, start, *fmt - start);
181✔
238

239
   const char *end = *fmt;
205,713✔
240
   (*fmt)++;   // Advance past final '$'
205,713✔
241

242
   size_t len = len = *fmt - start - 2;
205,713✔
243
   const char *e = e = start + 1;
205,713✔
244
   LOCAL_TEXT_BUF tb = NULL;
411,426✔
245

246
   if (has_format) {
205,713✔
247
      // Expand any embedded formatting inside the ANSI escape
248
      tb = tb_new();
22,278✔
249

250
      ostream_t aos = {
22,278✔
251
         tb_ostream_write,
252
         tb,
253
         os->charset,
22,278✔
254
      };
255

256
      printf_interpret(&aos, state, start + 1, end);
22,278✔
257

258
      e = tb_get(tb);
22,278✔
259
      len = tb_len(tb);
22,278✔
260
   }
261

262
   bool bold;
205,713✔
263
   if ((bold = (*e == '!')))
205,713✔
264
      ++e, --len;
2✔
265

266
   bool bright;
205,713✔
267
   if ((bright = (*e == '+')))
205,713✔
268
      ++e, --len;
39✔
269

270
   if (*e == '#') {
205,713✔
271
      char *eptr;
1✔
272
      int code = strtoul(e + 1, &eptr, 10);
1✔
273
      if (eptr == e + len) {
1✔
274
         char buf[16];
1✔
275
         if (bold)
1✔
UNCOV
276
            checked_sprintf(buf, sizeof(buf), "\033[1;38;5;%dm", code);
×
277
         else
278
            checked_sprintf(buf, sizeof(buf), "\033[38;5;%dm", code);
1✔
279

280
         if (os->flags & OS_COLOR)
1✔
281
            ostream_puts(os, buf);
1✔
282

283
         return 0;
1✔
284
      }
285
   }
286

287
   if (strncmp(e, "link:", 5) == 0) {
205,712✔
288
      const char *bel = strchr(e, '\07');
22,279✔
289

290
#ifndef __MINGW32__    // Winpty doesn't recognise these
291
      if (os->flags & OS_TERMINAL) {
22,279✔
292
         ostream_puts(os, "\033]8;;");
2✔
293
         ostream_write(os, e + 5, len - 5);
2✔
294
         ostream_puts(os, "\033]8;;\07");
2✔
295
         return e + len - bel - 1;
2✔
296
      }
297
#endif
298

299
      return ostream_write(os, bel + 1, e + len - bel - 1);
22,277✔
300
   }
301

302
   for (int i = 0; i < ARRAY_LEN(escapes); i++) {
693,446✔
303
      if (strncmp(e, escapes[i].name, len) == 0) {
693,444✔
304
         int code = escapes[i].value + (bright ? 60 : 0);
183,431✔
305
         char buf[16];
183,431✔
306
         if (bold)
183,431✔
307
            checked_sprintf(buf, sizeof(buf), "\033[1;%dm", code);
1✔
308
         else
309
            checked_sprintf(buf, sizeof(buf), "\033[%dm", code);
183,430✔
310

311
         if (os->flags & OS_COLOR)
183,431✔
312
            ostream_puts(os, buf);
1,839✔
313

314
         return 0;
183,431✔
315
      }
316
   }
317

318
   return ostream_write(os, start, *fmt - start);
2✔
319
}
320

321
static int format_z(ostream_t *os, printf_state_t *s, printf_arg_t *arg)
159,838✔
322
{
323
   assert(arg->precision == INT_MIN);
159,838✔
324
   return delegate(os, s, arg, arg->value.z);
159,838✔
325
}
326

UNCOV
327
static int format_ll(ostream_t *os, printf_state_t *s, printf_arg_t *arg)
×
328
{
329
   if (arg->precision != INT_MIN)
×
UNCOV
330
      return delegate(os, s, arg, arg->precision, arg->value.ll);
×
331
   else
UNCOV
332
      return delegate(os, s, arg, arg->value.ll);
×
333
}
334

335
static int format_l(ostream_t *os, printf_state_t *s, printf_arg_t *arg)
45,492✔
336
{
337
   if (arg->precision != INT_MIN)
45,492✔
338
      return delegate(os, s, arg, arg->precision, arg->value.l);
11,109✔
339
   else
340
      return delegate(os, s, arg, arg->value.l);
34,383✔
341
}
342

343
static int format_i(ostream_t *os, printf_state_t *s, printf_arg_t *arg)
127,295✔
344
{
345
   if (arg->precision != INT_MIN)
127,295✔
346
      return delegate(os, s, arg, arg->precision, arg->value.i);
13,704✔
347
   else
348
      return delegate(os, s, arg, arg->value.i);
113,591✔
349
}
350

351
static int format_f(ostream_t *os, printf_state_t *s, printf_arg_t *arg)
1,011✔
352
{
353
   if (arg->width != INT_MIN)
1,011✔
354
      return delegate(os, s, arg, arg->width, arg->precision, arg->value.f);
77✔
355
   else if (arg->precision != INT_MIN)
934✔
356
      return delegate(os, s, arg, arg->precision, arg->value.f);
9✔
357
   else
358
      return delegate(os, s, arg, arg->value.f);
925✔
359
}
360

361
static int format_s(ostream_t *os, printf_state_t *s, printf_arg_t *arg)
536,145✔
362
{
363
   if (arg->width != INT_MIN)
536,145✔
364
      return delegate(os, s, arg, arg->width, arg->precision, arg->value.p);
1✔
365
   else if (arg->precision != INT_MIN)
536,144✔
366
      return delegate(os, s, arg, arg->precision, arg->value.p);
56,764✔
367
   else
368
      return delegate(os, s, arg, arg->value.p);
479,380✔
369
}
370

371
static int format_p(ostream_t *os, printf_state_t *s, printf_arg_t *arg)
5✔
372
{
373
   return delegate(os, s, arg, arg->value.p);
5✔
374
}
375

376
static int printf_interpret(ostream_t *os, printf_state_t *state,
685,995✔
377
                            const char *fmt, const char *end)
378
{
379
   int nchars = 0;
685,995✔
380
   for (const char *p = fmt;;) {
685,995✔
381
      const char *start = p;
1,810,123✔
382
      while (p < end && *p != '%' && *p != '$')
4,929,211✔
383
         p++;
3,119,088✔
384

385
      if (start < p)
1,810,123✔
386
         nchars += ostream_write(os, start, p - start);
773,058✔
387

388
      if (p == end)
1,810,123✔
389
         return nchars;
685,995✔
390
      else if (*p == '$') {
1,124,128✔
391
         nchars += ansi_escape(os, state, &p);
205,894✔
392
         continue;
205,894✔
393
      }
394
      else if (*p == '%' && *(p + 1) == '%') {
918,234✔
395
         nchars += ostream_write(os, "%", 1);
47,561✔
396
         p += 2;
47,561✔
397
         continue;
47,561✔
398
      }
399

400
      assert(state->pos < state->nargs);
870,673✔
401

402
      printf_arg_t *arg = &(state->args[state->pos]);
870,673✔
403
      nchars += (*arg->fn)(os, state, arg);
870,673✔
404
      p += arg->len;
870,673✔
405

406
      state->pos++;
870,673✔
407
   }
408
}
409

410
int nvc_vfprintf(ostream_t *os, const char *fmt, va_list ap)
663,717✔
411
{
412
   printf_state_t state = {};
663,717✔
413

414
   const char *end = NULL;
663,717✔
415
   for (const char *p = fmt;;) {
663,717✔
416
      while (*p != '\0' && *p != '%')
5,478,171✔
417
         p++;
3,896,220✔
418

419
      if (*p == '\0') {
1,581,951✔
420
         end = p;
663,717✔
421
         break;
663,717✔
422
      }
423

424
      printf_arg_t arg = {
918,234✔
425
         .start = p,
426
         .width = INT_MIN,
427
         .precision = INT_MIN,
428
      };
429
      bool z_mod = false;
918,234✔
430
      int l_mod = 0;
918,234✔
431
   again:
1,232,817✔
432
      switch (*++p) {
1,232,817✔
433
      case 'l':
45,494✔
434
         l_mod++;
45,494✔
435
         goto again;
45,494✔
436
      case 'z':
159,838✔
437
         z_mod = true;
159,838✔
438
         goto again;
159,838✔
439
      case '-':
27,509✔
440
      case '+':
441
      case '.':
442
      case '0'...'9':
443
         goto again;
27,509✔
444
      case '*':
81,742✔
445
         arg.width = arg.precision;
81,742✔
446
         arg.precision = va_arg(ap, int);
81,742✔
447
         goto again;
81,742✔
448
      case 'd':
331,143✔
449
      case 'i':
450
      case 'x':
451
      case 'u':
452
      case 'o':
453
         if (z_mod) {
331,143✔
454
            arg.value.z = va_arg(ap, size_t);
159,838✔
455
            arg.fn = format_z;
159,838✔
456
         }
457
         else if (l_mod >= 2) {
171,305✔
UNCOV
458
            arg.value.ll = va_arg(ap, long long);
×
UNCOV
459
            arg.fn = format_ll;
×
460
         }
461
         else if (l_mod == 1) {
171,305✔
462
            arg.value.l = va_arg(ap, long);
45,492✔
463
            arg.fn = format_l;
45,492✔
464
         }
465
         else {
466
            arg.value.i = va_arg(ap, int);
125,813✔
467
            arg.fn = format_i;
125,813✔
468
         }
469
         break;
470
      case 'c':
1,482✔
471
         arg.value.i = va_arg(ap, int);
1,482✔
472
         arg.fn = format_i;
1,482✔
473
         break;
1,482✔
474
      case 'e':
1,011✔
475
      case 'f':
476
      case 'g':
477
         arg.value.f = va_arg(ap, double);
1,011✔
478
         arg.fn = format_f;
1,011✔
479
         break;
1,011✔
480
      case 's':
536,145✔
481
         arg.value.p = va_arg(ap, char *);
536,145✔
482
         arg.fn = format_s;
536,145✔
483
         break;
536,145✔
484
      case 'p':
892✔
485
         arg.value.p = va_arg(ap, void *);
892✔
486
         if ((arg.fn = get_pointer_formatter(p[1])))
892✔
487
            p++;
887✔
488
         else
489
            arg.fn = format_p;
490
         break;
491
      case '%':
47,561✔
492
         p++;
47,561✔
493
         continue;
47,561✔
UNCOV
494
      default:
×
495
         fatal_trace("unhandled character '%c' in format", *p);
496
      }
497

498
      arg.len = ++p - arg.start;
870,673✔
499

500
      if (state.nargs == MAX_ARGS)
870,673✔
501
         fatal_trace("maximum of %d printf arguments supported", MAX_ARGS);
502
      else
503
         state.args[state.nargs++] = arg;
870,673✔
504
   }
505

506
   return printf_interpret(os, &state, fmt, end);
1,327,434✔
507
}
508

509
int nvc_vprintf(const char *fmt, va_list ap)
8✔
510
{
511
   return nvc_vfprintf(nvc_stdout(), fmt, ap);
8✔
512
}
513

514
int nvc_printf(const char *fmt, ...)
8✔
515
{
516
   va_list ap;
8✔
517
   va_start(ap, fmt);
8✔
518
   const int nchars = nvc_vprintf(fmt, ap);
8✔
519
   va_end(ap);
8✔
520
   return nchars;
8✔
521
}
522

523
int nvc_fprintf(ostream_t *os, const char *fmt, ...)
180,134✔
524
{
525
   va_list ap;
180,134✔
526
   va_start(ap, fmt);
180,134✔
527
   const int nchars = nvc_vfprintf(os, fmt, ap);
180,134✔
528
   va_end(ap);
180,134✔
529
   return nchars;
180,134✔
530
}
531

532
void stdio_ostream_write(const char *buf, size_t len, void *ctx)
1,222,586✔
533
{
534
   FILE *f = ctx;
1,222,586✔
535
   fwrite(buf, len, 1, f);
1,222,586✔
536
}
1,222,586✔
537

538
static void init_terminal_ostream(ostream_t *os, FILE *f)
7,408✔
539
{
540
   os->callback = stdio_ostream_write;
7,408✔
541
   os->context = f;
7,408✔
542
   os->flags = 0;
7,408✔
543

544
   if (isatty(fileno(f))) {
7,408✔
UNCOV
545
      os->charset = utf8_terminal() ? CHARSET_UTF8 : CHARSET_ISO88591;
×
UNCOV
546
      os->flags |= OS_TERMINAL;
×
547
   }
548
   else
549
      os->charset = CHARSET_ISO88591;
7,408✔
550

551
   if (color_terminal())
7,408✔
552
      os->flags |= OS_COLOR;
4✔
553
}
7,408✔
554

555
ostream_t *nvc_stdout(void)
6,782✔
556
{
557
   static ostream_t os;
6,782✔
558
   INIT_ONCE(init_terminal_ostream(&os, stdout));
6,782✔
559
   return &os;
6,782✔
560
}
561

562
ostream_t *nvc_stderr(void)
74,326✔
563
{
564
   static ostream_t os;
74,326✔
565
   INIT_ONCE(init_terminal_ostream(&os, stderr));
74,326✔
566
   return &os;
74,326✔
567
}
568

569
char *color_asprintf(const char *fmt, ...)
31✔
570
{
571
   LOCAL_TEXT_BUF tb = tb_new();
62✔
572
   ostream_t os = { tb_ostream_write, tb, CHARSET_ISO88591, OS_COLOR };
31✔
573

574
   va_list ap;
31✔
575
   va_start(ap, fmt);
31✔
576
   nvc_vfprintf(&os, fmt, ap);
31✔
577
   va_end(ap);
31✔
578

579
   return tb_claim(tb);
31✔
580
}
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