• 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

90.0
/src/mir/mir-optim.c
1
//
2
//  Copyright (C) 2024-2025  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 "mask.h"
21
#include "mir/mir-node.h"
22
#include "mir/mir-priv.h"
23
#include "mir/mir-structs.h"
24
#include "option.h"
25

26
#include <assert.h>
27
#include <stdlib.h>
28
#include <string.h>
29

30
#define CFG_EMBED_EDGES 4
31

32
typedef struct {
33
   unsigned count;
34
   unsigned max;
35
   union {
36
      mir_block_t  edges[CFG_EMBED_EDGES];
37
      mir_block_t *external;
38
   } u;
39
} cfg_edge_list_t;
40

41
typedef struct {
42
   mir_block_t     block;
43
   unsigned        entry : 1;
44
   unsigned        aborts : 1;
45
   unsigned        returns : 1;
46
   cfg_edge_list_t in;
47
   cfg_edge_list_t out;
48
   bit_mask_t      dom;
49
#if 0
50
   bit_mask_t      livein;
51
   bit_mask_t      varkill;
52
   bit_mask_t      liveout;
53
#endif
54
} cfg_block_t;
55

56
typedef uint32_t valnum_t;
57
#define VN_INVALID UINT_MAX
58
#define VN_CONST   0x80000000
59

60
typedef struct {
61
   mir_value_t value;
62
   uint32_t    hash;
63
   mir_block_t block;
64
   valnum_t    vn;
65
} gvn_tab_t;
66

67
typedef struct {
68
   valnum_t    *nodevn;
69
   valnum_t    *paramvn;
70
   valnum_t     nextvn;
71
   valnum_t     maxvn;
72
   mir_value_t *canon;
73
   bit_mask_t   visited;
74
   size_t       tabsz;
75
   gvn_tab_t    hashtab[];
76
} gvn_state_t;
77

78
typedef struct {
79
   bit_mask_t visited;
80
   uint8_t    uses[];
81
} dce_state_t;
82

83
typedef struct {
84
   cfg_block_t *cfg;
85
   gvn_state_t *gvn;
86
   dce_state_t *dce;
87
} mir_optim_t;
88

89
static void mir_dump_optim(mir_unit_t *mu, mir_optim_t *an);
90

91
////////////////////////////////////////////////////////////////////////////////
92
// Control flow graph construction
93

94
static void cfg_add_one_edge(cfg_edge_list_t *list, mir_block_t edge)
18✔
95
{
96
   if (list->count < 4)
18✔
97
      list->u.edges[list->count++] = edge;
18✔
98
   else if (list->count == 4) {
×
99
      mir_block_t *ptr = xmalloc_array(16, sizeof(mir_block_t));
×
100
      memcpy(ptr, list->u.edges, 4 * sizeof(mir_block_t));
×
101

102
      list->max = 16;
×
103
      list->u.external = ptr;
×
104
      list->u.external[list->count++] = edge;
×
105
   }
106
   else if (list->count == list->max) {
×
107
      list->max *= 2;
×
108
      list->u.external =
×
109
         xrealloc_array(list->u.external, list->max, sizeof(mir_block_t));
×
110
      list->u.external[list->count++] = edge;
×
111
   }
112
   else
113
      list->u.external[list->count++] = edge;
×
114
}
18✔
115

116
static void cfg_add_edge(cfg_block_t *cfg, mir_block_t from, mir_block_t to)
9✔
117
{
118
   cfg_add_one_edge(&(cfg[from.id].out), to);
9✔
119
   cfg_add_one_edge(&(cfg[to.id].in), from);
9✔
120
}
9✔
121

122
static mir_block_t cfg_get_edge(const cfg_edge_list_t *list, int nth)
17✔
123
{
124
   assert(nth < list->count);
17✔
125
   if (list->max <= 4)
17✔
126
      return list->u.edges[nth];
17✔
127
   else
128
      return list->u.external[nth];
×
129
}
130

131
static cfg_block_t *mir_get_cfg(mir_unit_t *mu)
186✔
132
{
133
   cfg_block_t *cfg = xcalloc_array(mu->blocks.count, sizeof(cfg_block_t));
186✔
134
   cfg[0].entry = true;
186✔
135

136
   if (mir_get_kind(mu) == MIR_UNIT_PROCESS && mu->blocks.count > 1)
186✔
137
      cfg[1].entry = true;
1✔
138

139
   for (int i = 0; i < mu->blocks.count; i++) {
380✔
140
      const block_data_t *bd = &(mu->blocks.items[i]);
194✔
141
      mir_block_t this = { .tag = MIR_TAG_BLOCK, .id = i };
194✔
142

143
      DEBUG_ONLY(mir_set_cursor(mu, this, MIR_APPEND));
194✔
144
      MIR_ASSERT(bd->num_nodes > 0, "empty basic block");
194✔
145

146
      const node_data_t *last = &(mu->nodes[bd->nodes[bd->num_nodes - 1]]);
194✔
147
      MIR_ASSERT(mir_is_terminator(last->op),
194✔
148
                 "last operation in block is not terminator");
149

150
      switch (last->op) {
194✔
151
      case MIR_OP_WAIT:
1✔
152
         assert(last->args[0].tag == MIR_TAG_BLOCK);
1✔
153
         cfg[last->args[0].id].entry = true;
1✔
154
         // Fall-through
155
      case MIR_OP_RETURN:
187✔
156
         cfg[i].returns = true;
187✔
157
         break;
187✔
158
      default:
7✔
159
         assert(last->nargs <= MIR_INLINE_ARGS);
7✔
160
         for (int j = 0; j < last->nargs; j++) {
18✔
161
            if (last->args[j].tag == MIR_TAG_BLOCK)
11✔
162
               cfg_add_edge(cfg, this, mir_cast_block(last->args[j]));
9✔
163
         }
164
      }
165
   }
166

167
   return cfg;
186✔
168
}
169

170
static void mir_free_cfg(mir_unit_t *mu, cfg_block_t *cfg)
186✔
171
{
172
   for (int i = 0; i < mu->blocks.count; i++) {
380✔
173
      cfg_block_t *cb = &(cfg[i]);
194✔
174
      if (cb->in.max > CFG_EMBED_EDGES) free(cb->in.u.external);
194✔
175
      if (cb->out.max > CFG_EMBED_EDGES) free(cb->out.u.external);
194✔
176

177
      mask_free(&cb->dom);
194✔
178
   }
179

180
   free(cfg);
186✔
181
}
186✔
182

183
////////////////////////////////////////////////////////////////////////////////
184
// Dominator tree construction
185

186
static void mir_dominator_tree(mir_unit_t *mu, cfg_block_t *cfg)
186✔
187
{
188
   // TODO: use more efficient algorithm
189

190
   for (int i = 0; i < mu->blocks.count; i++) {
380✔
191
      mask_init(&cfg[i].dom, mu->blocks.count);
194✔
192

193
      if (cfg[i].entry)
194✔
194
         mask_set(&cfg[i].dom, i);
187✔
195
      else
196
         mask_setall(&cfg[i].dom);
7✔
197
   }
198

199
   bit_mask_t tmp;
186✔
200
   mask_init(&tmp, mu->blocks.count);
186✔
201

202
   bool changes;
188✔
203
   do {
188✔
204
      changes = false;
188✔
205

206
      for (int i = 0; i < mu->blocks.count; i++) {
390✔
207
         if (cfg[i].entry) continue;
202✔
208

209
         mask_setall(&tmp);
13✔
210

211
         for (int j = 0; j < cfg[i].in.count; j++) {
30✔
212
            mir_block_t pred = cfg_get_edge(&cfg[i].in, j);
17✔
213
            mask_intersect(&tmp, &cfg[pred.id].dom);
17✔
214
         }
215

216
         mask_set(&tmp, i);
13✔
217

218
         if (!changes && !mask_eq(&tmp, &cfg[i].dom))
13✔
219
            changes = true;
2✔
220

221
         mask_copy(&cfg[i].dom, &tmp);
13✔
222
      }
223
   } while (changes);
188✔
224
}
186✔
225

226
////////////////////////////////////////////////////////////////////////////////
227
// Liveness analysis
228

229
#if 0
230
static void mir_do_liveness(mir_unit_t *mu, mir_optim_t *opt)
231
{
232
   // Algorithm from "Engineering a Compiler" chapter 8.6
233

234
   for (int i = 0; i < mu->num_blocks; i++) {
235
      cfg_block_t *cb = &(opt->cfg[i]);
236
      mask_init(&cb->livein, mu->num_nodes);
237
      mask_init(&cb->varkill, mu->num_nodes);
238
      mask_init(&cb->liveout, mu->num_nodes);
239

240
      const block_data_t *bd = mir_block_data(mu, cb->block);
241
      for (int j = 0; j < bd->num_nodes; j++) {
242
         mir_value_t node = { .tag = MIR_TAG_NODE, .id = bd->nodes[i] };
243
         const node_data_t *n = mir_node_data(mu, node);
244

245
         if (n->op == MIR_OP_CONST || n->op == MIR_OP_CONST_REAL)
246
            continue;
247
         else if (mir_is_null(n->type))
248
            continue;   // Produces no value
249

250
         const mir_value_t *args = mir_get_args(mu, n);
251
         for (int k = 0; k < n->nargs; k++) {
252
            mir_value_t arg = args[k];
253
            if (arg.tag == MIR_TAG_NODE && !mask_test(&cb->varkill, arg.id))
254
               mask_set(&cb->livein, arg.id);
255
         }
256

257
         mask_set(&cb->varkill, node.id);
258
      }
259
   }
260

261
   bit_mask_t new, tmp;
262
   mask_init(&new, mu->num_nodes);
263
   mask_init(&tmp, mu->num_nodes);
264

265
   bool changed;
266
   do {
267
      changed = false;
268

269
      for (int i = mu->num_blocks - 1; i >= 0; i--) {
270
         cfg_block_t *cb = &(opt->cfg[i]);
271
         mask_clearall(&new);
272

273
         for (int j = 0; j < cb->out.count; j++) {
274
            cfg_block_t *succ = &(opt->cfg[cfg_get_edge(&cb->out, j).id]);
275
            mask_copy(&tmp, &succ->liveout);
276
            mask_subtract(&tmp, &succ->varkill);
277
            mask_union(&tmp, &succ->livein);
278
            mask_union(&new, &tmp);
279
         }
280

281
         if (!mask_eq(&new, &cb->liveout)) {
282
            mask_copy(&cb->liveout, &new);
283
            changed = true;
284
         }
285
      }
286
   } while (changed);
287

288
   // Replaced "upward exposed variables" set with live-in
289
   for (int i = 0; i < mu->num_blocks; i++) {
290
      cfg_block_t *cb = &(opt->cfg[i]);
291
      mask_copy(&tmp, &cb->liveout);
292
      mask_subtract(&tmp, &cb->varkill);
293
      mask_union(&cb->livein, &tmp);
294
   }
295

296
   mask_free(&new);
297
   mask_free(&tmp);
298
}
299
#endif
300

301
////////////////////////////////////////////////////////////////////////////////
302
// Global value numbering
303

304
static inline valnum_t gvn_new_value(mir_value_t canon, gvn_state_t *gvn)
2,226✔
305
{
306
   assert(gvn->nextvn < gvn->maxvn);
2,226✔
307
   gvn->canon[gvn->nextvn] = canon;
2,226✔
308
   return gvn->nextvn++;
2,226✔
309
}
310

311
static inline bool gvn_tracked(mir_value_t value)
1,650✔
312
{
313
   return value.tag == MIR_TAG_NODE || value.tag == MIR_TAG_PARAM;
1,650✔
314
}
315

316
static valnum_t gvn_get_value(mir_value_t value, gvn_state_t *gvn)
375✔
317
{
318
   switch (value.tag) {
375✔
319
   case MIR_TAG_NODE:
329✔
320
      return gvn->nodevn[value.id];
329✔
321
   case MIR_TAG_PARAM:
46✔
322
      return gvn->paramvn[value.id];
46✔
UNCOV
323
   case MIR_TAG_CONST:
×
UNCOV
324
      return VN_CONST + value.id;
×
325
   default:
×
326
      should_not_reach_here();
327
   }
328
}
329

330
static uint32_t gvn_hash_node(mir_unit_t *mu, const node_data_t *n,
1,329✔
331
                              gvn_state_t *gvn)
332
{
333
   uint32_t hash = knuth_hash(n->op) + knuth_hash(n->nargs);
1,329✔
334
   hash ^= mir_type_data(mu, n->type)->hash;
1,329✔
335

336
   if (n->op == MIR_OP_CONST)
1,329✔
UNCOV
337
      return hash ^ mix_bits_64(n->iconst);
×
338
   else if (n->op == MIR_OP_CONST_REAL)
1,329✔
339
      return hash ^ mix_bits_64(FLOAT_BITS(n->dconst));
×
340
   else if (n->op == MIR_OP_CONST_VEC)
1,329✔
341
      return hash ^ mix_bits_64(n->bits[0]) ^ mix_bits_64(n->bits[1]);
16✔
342
   else if (n->op == MIR_OP_LOCUS)
1,313✔
343
      return hash ^ mix_bits_64(n->locus);
484✔
344
   else {
345
      const mir_value_t *args = mir_get_args(mu, n);
829✔
346
      for (int i = 0; i < n->nargs; i++) {
2,447✔
347
         hash <<= 1;   // Argument order should affect hash
1,618✔
348
         if (gvn_tracked(args[i]))
1,618✔
349
            hash += knuth_hash(gvn_get_value(args[i], gvn));
334✔
350
         else
351
            hash += knuth_hash(args[i].bits);
1,284✔
352
      }
353

354
      return mix_bits_32(hash);
829✔
355
   }
356
}
357

358
static bool gvn_compare(mir_unit_t *mu, const node_data_t *a,
6✔
359
                        const node_data_t *b, gvn_state_t *gvn)
360
{
361
   if (a->op != b->op || a->nargs != b->nargs || !mir_equals(a->type, b->type))
6✔
362
      return false;
363
   else if (a->op == MIR_OP_CONST)
6✔
UNCOV
364
      return a->iconst == b->iconst;
×
365
   else if (a->op == MIR_OP_CONST_REAL)
6✔
366
      return a->dconst == b->dconst;
×
367
   else if (a->op == MIR_OP_CONST_VEC)
6✔
368
      return a->bits[0] == b->bits[0] && a->bits[1] == b->bits[1];
×
369
   else if (a->op == MIR_OP_LOCUS)
6✔
370
      return a->locus == b->locus;
×
371

372
   const mir_value_t *a_args = mir_get_args(mu, a);
6✔
373
   const mir_value_t *b_args = mir_get_args(mu, b);
6✔
374

375
   for (int i = 0; i < a->nargs; i++) {
17✔
376
      if (a_args[i].tag != b_args[i].tag)
11✔
377
         return false;
378
      else if (gvn_tracked(a_args[i])) {
11✔
379
         const valnum_t avn = gvn_get_value(a_args[i], gvn);
9✔
380
         const valnum_t bvn = gvn_get_value(b_args[i], gvn);
9✔
381
         if (avn != bvn)
9✔
382
            return false;
383
      }
384
      else if (a_args[i].bits != b_args[i].bits)
2✔
385
         return false;
386
   }
387

388
   return true;
389
}
390

391
static void gvn_generic(mir_unit_t *mu, mir_value_t node, mir_block_t block,
1,329✔
392
                        mir_optim_t *opt)
393
{
394
   const node_data_t *n = mir_node_data(mu, node);
1,329✔
395
   const cfg_block_t *cb = &(opt->cfg[block.id]);
1,329✔
396
   const uint32_t hash = gvn_hash_node(mu, n, opt->gvn);
1,329✔
397

398
   for (int idx = hash & (opt->gvn->tabsz - 1), limit = 0; limit < 10;
1,444✔
399
        idx = (idx + 1) & (opt->gvn->tabsz - 1), limit++) {
115✔
400
      gvn_tab_t *tab = &(opt->gvn->hashtab[idx]);
1,444✔
401
      if (mir_is_null(tab->value)) {
1,444✔
402
         tab->value = node;
1,323✔
403
         tab->hash  = hash;
1,323✔
404
         tab->block = block;
1,323✔
405
         tab->vn = opt->gvn->nodevn[node.id] = gvn_new_value(node, opt->gvn);
1,323✔
406
         return;
1,323✔
407
      }
408
      else if (tab->hash == hash && mask_test(&cb->dom, tab->block.id)) {
121✔
409
         const node_data_t *o = mir_node_data(mu, tab->value);
6✔
410
         if (gvn_compare(mu, n, o, opt->gvn)) {
6✔
411
            opt->gvn->nodevn[node.id] = tab->vn;
6✔
412
            return;
6✔
413
         }
414
      }
415
   }
416

UNCOV
417
   opt->gvn->nodevn[node.id] = gvn_new_value(node, opt->gvn);
×
418
}
419

420
static void gvn_sub(mir_unit_t *mu, mir_value_t node, mir_block_t block,
1✔
421
                    mir_optim_t *opt)
422
{
423
   node_data_t *n = mir_node_data(mu, node);
1✔
424
   assert(n->nargs == 2);
1✔
425

426
   // (X + Y) - Y ==> X
427

428
   mir_value_t left = n->args[0], right = n->args[1];
1✔
429
   if (left.tag == MIR_TAG_NODE) {
1✔
430
      node_data_t *nl = mir_node_data(mu, left);
1✔
431
      if (nl->op == MIR_OP_ADD) {
1✔
432
         valnum_t left0_vn = gvn_get_value(nl->args[0], opt->gvn);
1✔
433
         valnum_t left1_vn = gvn_get_value(nl->args[1], opt->gvn);
1✔
434
         valnum_t right_vn = gvn_get_value(right, opt->gvn);
1✔
435
         if (left1_vn == right_vn) {
1✔
436
            opt->gvn->nodevn[node.id] = left0_vn;
1✔
437
            return;
1✔
438
         }
439
      }
440
   }
441

UNCOV
442
   gvn_generic(mu, node, block, opt);
×
443
}
444

445
static void gvn_commutative(mir_unit_t *mu, mir_value_t node, mir_block_t block,
9✔
446
                            mir_optim_t *opt, int first)
447
{
448
   node_data_t *n = mir_node_data(mu, node);
9✔
449
   assert(n->nargs == first + 2);
9✔
450
   assert(n->nargs <= MIR_INLINE_ARGS);
9✔
451

452
   mir_value_t left = n->args[first], right = n->args[first + 1];
9✔
453
   if (left.tag == MIR_TAG_CONST) {
9✔
454
      // Move constants to right hand side
455
      n->args[first] = right;
1✔
456
      n->args[first + 1] = left;
1✔
457
   }
458
   else if (gvn_tracked(left) && gvn_tracked(right)) {
8✔
459
      // Sort arguments by value number
460
      const valnum_t vn0 = gvn_get_value(left, opt->gvn);
7✔
461
      const valnum_t vn1 = gvn_get_value(right, opt->gvn);
7✔
462

463
      if (vn0 > vn1) {
7✔
464
         n->args[first] = right;
2✔
465
         n->args[first + 1] = left;
2✔
466
      }
467
   }
468

469
   gvn_generic(mu, node, block, opt);
9✔
470
}
9✔
471

472
static void gvn_cmp(mir_unit_t *mu, mir_value_t node, mir_block_t block,
2✔
473
                    mir_optim_t *opt)
474
{
475
   node_data_t *n = mir_node_data(mu, node);
2✔
476
   assert(n->nargs == 3);
2✔
477
   assert(n->nargs <= MIR_INLINE_ARGS);
2✔
478
   assert(n->args[0].tag == MIR_TAG_ENUM);
2✔
479

480
   switch (n->args[0].id) {
2✔
481
   case MIR_CMP_EQ:
2✔
482
   case MIR_CMP_NEQ:
483
      gvn_commutative(mu, node, block, opt, 1);
2✔
484
      break;
2✔
UNCOV
485
   default:
×
UNCOV
486
      gvn_generic(mu, node, block, opt);
×
487
      break;
×
488
   }
489
}
2✔
490

491
static void gvn_logical(mir_unit_t *mu, mir_value_t node, mir_block_t block,
2✔
492
                        mir_optim_t *opt)
493
{
494
   const node_data_t *n = mir_node_data(mu, node);
2✔
495
   assert(n->nargs == 2);
2✔
496
   assert(n->nargs <= MIR_INLINE_ARGS);
2✔
497

498
   if (gvn_tracked(n->args[0]) && gvn_tracked(n->args[1])) {
2✔
499
      const valnum_t vn0 = gvn_get_value(n->args[0], opt->gvn);
2✔
500
      const valnum_t vn1 = gvn_get_value(n->args[1], opt->gvn);
2✔
501
      if (vn0 == vn1) {
2✔
UNCOV
502
         switch (n->op) {
×
UNCOV
503
         case MIR_OP_AND:   // X & X ==> X
×
504
         case MIR_OP_OR:    // X | X ==> X
505
            opt->gvn->nodevn[node.id] = vn0;
×
UNCOV
506
            return;
×
507
         default:
×
508
            should_not_reach_here();
509
         }
510
      }
511
   }
512

513
   gvn_commutative(mu, node, block, opt, 0);
2✔
514
}
515

516
static void gvn_phi(mir_unit_t *mu, mir_value_t node, mir_block_t block,
1✔
517
                    mir_optim_t *opt)
518
{
519
   const node_data_t *n = mir_node_data(mu, node);
1✔
520
   assert(n->nargs >= 2);
1✔
521

522
   const mir_value_t *args = mir_get_args(mu, n);
1✔
523

524
   if (!gvn_tracked(args[1]))
1✔
525
      return;
526

527
   valnum_t vn = gvn_get_value(args[1], opt->gvn);
1✔
528

529
   for (int i = 3; i < n->nargs; i += 2) {
2✔
530
      if (gvn_get_value(args[i], opt->gvn) != vn)
1✔
531
         return;
532
   }
533

534
   opt->gvn->nodevn[node.id] = vn;
1✔
535
}
536

537
static void gvn_unpack(mir_unit_t *mu, mir_value_t node, mir_block_t block,
8✔
538
                       mir_optim_t *opt)
539
{
540
   const node_data_t *n = mir_node_data(mu, node);
8✔
541
   if (mir_is_null(n->type))
8✔
542
      return;   // Unpack into memory
543

544
   gvn_generic(mu, node, block, opt);
4✔
545
}
546

547
static void gvn_load(mir_unit_t *mu, mir_value_t node, mir_block_t block,
292✔
548
                     mir_optim_t *opt)
549
{
550
   // TODO: check if stamp is const or pointer
551

552
   opt->gvn->nodevn[node.id] = gvn_new_value(node, opt->gvn);
292✔
553
}
292✔
554

555
static void gvn_visit_block(mir_unit_t *mu, mir_block_t block,
393✔
556
                            mir_optim_t *opt)
557
{
558
   if (mask_test(&opt->gvn->visited, block.id))
393✔
559
      return;
560

561
   mask_set(&opt->gvn->visited, block.id);
193✔
562

563
   for (size_t id = -1; mask_iter(&opt->cfg[block.id].dom, &id);) {
393✔
564
      mir_block_t dom = { .tag = MIR_TAG_BLOCK, .id = id };
200✔
565
      gvn_visit_block(mu, dom, opt);
200✔
566
   }
567

568
   const block_data_t *bd = mir_block_data(mu, block);
193✔
569
   for (int i = 0; i < bd->num_nodes; i++) {
3,598✔
570
      mir_value_t node = { .tag = MIR_TAG_NODE, .id = bd->nodes[i] };
3,405✔
571
      switch (mir_get_op(mu, node)) {
3,405✔
572
      case MIR_OP_CMP:
2✔
573
         gvn_cmp(mu, node, block, opt);
2✔
574
         break;
2✔
575
      case MIR_OP_ADD:
5✔
576
      case MIR_OP_MUL:
577
         gvn_commutative(mu, node, block, opt, 0);
5✔
578
         break;
5✔
579
      case MIR_OP_SUB:
1✔
580
         gvn_sub(mu, node, block, opt);
1✔
581
         break;
1✔
582
      case MIR_OP_NOT:
1,316✔
583
      case MIR_OP_RESOLVED:
584
      case MIR_OP_SELECT:
585
      case MIR_OP_REM:
586
      case MIR_OP_LINK_PACKAGE:
587
      case MIR_OP_LINK_VAR:
588
      case MIR_OP_PACKAGE_INIT:
589
      case MIR_OP_LOCUS:
590
      case MIR_OP_UARRAY_LEN:
591
      case MIR_OP_RANGE_LENGTH:
592
      case MIR_OP_CAST:
593
      case MIR_OP_PACK:
594
      case MIR_OP_VAR_UPREF:
595
      case MIR_OP_CONST_VEC:
596
      case MIR_OP_BINARY:
597
      case MIR_OP_UNARY:
598
      case MIR_OP_CLOSURE:
599
      case MIR_OP_CONTEXT_UPREF:
600
      case MIR_OP_UNWRAP:
601
         gvn_generic(mu, node, block, opt);
1,316✔
602
         break;
1,316✔
603
      case MIR_OP_AND:
2✔
604
      case MIR_OP_OR:
605
         gvn_logical(mu, node, block, opt);
2✔
606
         break;
2✔
607
      case MIR_OP_PHI:
1✔
608
         gvn_phi(mu, node, block, opt);
1✔
609
         break;
1✔
610
      case MIR_OP_UNPACK:
8✔
611
         gvn_unpack(mu, node, block, opt);
8✔
612
         break;
8✔
613
      case MIR_OP_LOAD:
292✔
614
         gvn_load(mu, node, block, opt);
292✔
615
         break;
292✔
616
      case MIR_OP_INIT_SIGNAL:
603✔
617
      case MIR_OP_PORT_CONVERSION:
618
         opt->gvn->nodevn[node.id] = gvn_new_value(node, opt->gvn);
603✔
619
         break;
603✔
620
      default:
621
         break;
622
      }
623
   }
624
}
625

626
static void mir_do_gvn(mir_unit_t *mu, mir_optim_t *opt)
185✔
627
{
628
   const size_t tabsz = next_power_of_2(mu->num_nodes * 2);
185✔
629
   valnum_t *vnmap = xmalloc_array(mu->num_nodes + mu->params.count,
185✔
630
                                   sizeof(gvn_tab_t));
631

632
   opt->gvn = xcalloc_flex(sizeof(gvn_state_t), tabsz, sizeof(gvn_tab_t));
185✔
633
   opt->gvn->tabsz   = tabsz;
185✔
634
   opt->gvn->nodevn  = vnmap;
185✔
635
   opt->gvn->paramvn = vnmap + mu->num_nodes;
185✔
636
   opt->gvn->maxvn   = mu->num_nodes + mu->params.count;
185✔
637
   opt->gvn->canon   = xcalloc_array(opt->gvn->maxvn, sizeof(mir_value_t));
185✔
638

639
   for (int i = 0; i < mu->num_nodes; i++)
3,590✔
640
      opt->gvn->nodevn[i] = VN_INVALID;
3,405✔
641

642
   for (int i = 0; i < mu->params.count; i++) {
193✔
643
      mir_value_t param = { .tag = MIR_TAG_PARAM, .id = i };
8✔
644
      opt->gvn->paramvn[i] = gvn_new_value(param, opt->gvn);
8✔
645
   }
646

647
   mask_init(&opt->gvn->visited, mu->blocks.count);
185✔
648

649
   for (int i = 0; i < mu->blocks.count; i++) {
378✔
650
      mir_block_t this = { .tag = MIR_TAG_BLOCK, .id = i };
193✔
651
      gvn_visit_block(mu, this, opt);
193✔
652
   }
653

654
   for (int i = 0; i < mu->num_nodes; i++) {
3,590✔
655
      node_data_t *n = &(mu->nodes[i]);
3,405✔
656
      const mir_value_t *args = mir_get_args(mu, n);
3,405✔
657
      for (int j = 0; j < n->nargs; j++) {
10,115✔
658
         if (args[j].tag == MIR_TAG_NODE) {
6,710✔
659
            const valnum_t vn = opt->gvn->nodevn[args[j].id];
2,557✔
660
            if (unlikely(vn == VN_INVALID)) {
2,557✔
UNCOV
661
               mir_dump_optim(mu, opt);
×
662
               fatal_trace("node %%%d has no value number", args[j].id);
663
            }
664
            else if (!mir_equals(opt->gvn->canon[vn], args[j]))
2,557✔
665
               mir_set_arg(mu, n, j, opt->gvn->canon[vn]);
8✔
666
         }
667
      }
668
   }
669

670
   if (opt_get_verbose(OPT_GVN_VERBOSE, istr(mu->name)))
185✔
UNCOV
671
      mir_dump_optim(mu, opt);
×
672

673
   mask_free(&opt->gvn->visited);
185✔
674
   free(vnmap);
185✔
675
   free(opt->gvn->canon);
185✔
676
   free(opt->gvn);
185✔
677
   opt->gvn = NULL;
185✔
678
}
185✔
679

680
////////////////////////////////////////////////////////////////////////////////
681
// Simple dead code elimination by counting uses
682

683
static void dce_visit_block(mir_unit_t *mu, mir_block_t block, mir_optim_t *opt)
184✔
684
{
685
   if (mask_test(&opt->dce->visited, block.id))
184✔
686
      return;
687

688
   mask_set(&opt->dce->visited, block.id);
184✔
689

690
   const block_data_t *bd = mir_block_data(mu, block);
184✔
691
   for (int i = 0; i < bd->num_nodes; i++) {
3,566✔
692
      mir_value_t node = { .tag = MIR_TAG_NODE, .id = bd->nodes[i] };
3,382✔
693
      node_data_t *n = mir_node_data(mu, node);
3,382✔
694
      const mir_value_t *args = mir_get_args(mu, n);
3,382✔
695

696
      for (int j = 0; j < n->nargs; j++) {
10,053✔
697
         mir_value_t arg = args[j];
6,671✔
698
         if (arg.tag == MIR_TAG_NODE)
6,671✔
699
            opt->dce->uses[arg.id] = saturate_add(opt->dce->uses[arg.id], 1);
2,542✔
700
      }
701

702
      if (mir_is_null(n->type) || n->op == MIR_OP_STORE)
3,382✔
703
         opt->dce->uses[node.id] = 1;   // Does not produce value
1,170✔
704
   }
705

706
   cfg_block_t *cb = &(opt->cfg[block.id]);
184✔
707
   for (int i = 0; i < cb->out.count; i++)
184✔
UNCOV
708
      dce_visit_block(mu, cfg_get_edge(&cb->out, i), opt);
×
709
}
710

711
static void mir_do_dce(mir_unit_t *mu, mir_optim_t *opt)
183✔
712
{
713
   opt->dce = xcalloc_flex(sizeof(dce_state_t), mu->num_nodes, sizeof(uint8_t));
183✔
714
   mask_init(&opt->dce->visited, mu->num_nodes);
183✔
715

716
   for (int i = 0; i < mu->blocks.count; i++) {
367✔
717
      if (opt->cfg[i].entry) {
184✔
718
         mir_block_t this = { .tag = MIR_TAG_BLOCK, .id = i };
184✔
719
         dce_visit_block(mu, this, opt);
184✔
720
      }
721
   }
722

723
   if (opt_get_verbose(OPT_DCE_VERBOSE, istr(mu->name)))
183✔
UNCOV
724
      mir_dump_optim(mu, opt);
×
725

726
   for (int i = mu->blocks.count - 1; i >= 0; i--) {
367✔
727
      mir_block_t this = { .tag = MIR_TAG_BLOCK, .id = i };
184✔
728
      const block_data_t *bd = mir_block_data(mu, this);
184✔
729

730
      for (int j = bd->num_nodes - 1; j >= 0; j--) {
3,566✔
731
         mir_value_t node = { .tag = MIR_TAG_NODE, .id = bd->nodes[j] };
3,382✔
732
         if (opt->dce->uses[node.id] == 0) {
3,382✔
733
            const node_data_t *nd = mir_node_data(mu, node);
293✔
734
            const mir_value_t *args = mir_get_args(mu, nd);
293✔
735
            for (int k = 0; k < nd->nargs; k++) {
721✔
736
               if (args[k].tag == MIR_TAG_NODE) {
428✔
737
                  const uint8_t uses = opt->dce->uses[args[k].id];
69✔
738
                  assert(uses > 0);
69✔
739
                  if (uses < UINT8_MAX)
69✔
740
                     opt->dce->uses[args[k].id] = uses - 1;
69✔
741
               }
742
            }
743

744
            DEBUG_ONLY(const mir_op_t op = nd->op);
293✔
745

746
            mir_set_cursor(mu, this, j);
293✔
747
            mir_delete(mu);
293✔
748

749
            DEBUG_ONLY(mir_comment(mu, "Dead %s definition of %%%u",
293✔
750
                                   mir_op_string(op), node.id));
751
         }
752
      }
753
   }
754

755
   mir_compact(mu);
183✔
756

757
   mask_free(&opt->dce->visited);
183✔
758
   free(opt->dce);
183✔
759
   opt->dce = NULL;
183✔
760
}
183✔
761

762
////////////////////////////////////////////////////////////////////////////////
763
// Debugging
764

765
// LCOV_EXCL_START /////////////////////////////////////////////////////////////
766

767
static int begin_block_cb(mir_unit_t *mu, mir_block_t block, int col, void *ctx)
768
{
769
   const mir_optim_t *opt = ctx;
770

771
   if (opt->cfg == NULL)
772
      return 0;
773

774
   const cfg_block_t *cb = &(opt->cfg[block.id]);
775
   color_printf("$cyan$//");
776

777
   if (cb->entry) printf(" entry");
778

779
   if (cb->in.count > 0) {
780
      printf(" in:");
781
      for (int i = 0; i < cb->in.count; i++)
782
         printf("%s%d", i > 0 ? "," : "", cfg_get_edge(&cb->in, i).id);
783

784
   }
785
   else if (!cb->entry)
786
      printf(" unreachable");
787

788
   if (cb->out.count > 0) {
789
      printf(" out:");
790
      for (int i = 0; i < cb->out.count; i++)
791
         printf("%s%d", i > 0 ? "," : "", cfg_get_edge(&cb->out, i).id);
792
   }
793

794
   if (cb->dom.size > 0) {
795
      printf(" dom:");
796

797
      for (size_t bit = -1, nth = 0; mask_iter(&(cb->dom), &bit); nth++)
798
         printf("%s%zd", nth > 0 ? "," : "", bit);
799
   }
800

801
   if (cb->returns) printf(" returns");
802
   if (cb->aborts) printf(" aborts");
803

804
   color_printf("$$\n%*.s", col, "");
805

806
   return 0;
807
}
808

809
static int value_cb(mir_unit_t *mu, mir_value_t value, void *ctx)
810
{
811
   const mir_optim_t *opt = ctx;
812

813
   if (opt->gvn != NULL && gvn_tracked(value)) {
814
      int col = color_printf("$!black$(");
815

816
      if (value.tag == MIR_TAG_NODE) {
817
         const node_data_t *n = mir_node_data(mu, value);
818
         col += printf("%08x:", gvn_hash_node(mu, n, opt->gvn));
819
      }
820

821
      if (opt->gvn->nodevn[value.id] == VN_INVALID)
822
         col += printf("-");
823
      else
824
         col += printf("%u", gvn_get_value(value, opt->gvn));
825

826
      col += color_printf(")$$");
827
      return col;
828
   }
829
   else if (opt->dce != NULL && value.tag == MIR_TAG_NODE)
830
      return color_printf("$!black$(%u)$$", opt->dce->uses[value.id]);
831
   else
832
      return 0;
833
}
834

835
static void mir_dump_optim(mir_unit_t *mu, mir_optim_t *an)
836
{
837
   const mir_annotate_t cb = {
838
      .begin_block = begin_block_cb,
839
      .value = value_cb,
840
   };
841

842
   mir_annotate(mu, &cb, an);
843
}
844

845
// LCOV_EXCL_STOP //////////////////////////////////////////////////////////////
846

847
////////////////////////////////////////////////////////////////////////////////
848
// Public interface
849

850
void mir_optimise(mir_unit_t *mu, mir_pass_t passes)
186✔
851
{
852
   mir_optim_t opt = {};
186✔
853

854
   const mir_pass_t need_cfg = MIR_PASS_GVN | MIR_PASS_DCE;
186✔
855

856
   if (passes & need_cfg) {
186✔
857
      opt.cfg = mir_get_cfg(mu);
186✔
858
      mir_dominator_tree(mu, opt.cfg);
186✔
859
   }
860

861
   if (passes & MIR_PASS_GVN)
186✔
862
      mir_do_gvn(mu, &opt);
185✔
863

864
   if (passes & MIR_PASS_DCE)
186✔
865
      mir_do_dce(mu, &opt);
183✔
866

867
   if (passes & need_cfg)
186✔
868
      mir_free_cfg(mu, opt.cfg);
186✔
869
}
186✔
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