From dcb6968e77064d78d0f77ec693f35fdbf65e9b4b Mon Sep 17 00:00:00 2001
From: tyfkda <tyfkda@gmail.com>
Date: Thu, 19 Dec 2024 09:31:30 +0900
Subject: [PATCH] Keep original VReg pointer instead of index

Also eliminate version.
---
 src/_debug/dump_ir.c      | 103 ++++++++++++++++++++------------------
 src/cc/backend/ir.h       |   3 +-
 src/cc/backend/optimize.c |  11 ++++
 src/cc/backend/regalloc.c |  12 ++---
 src/cc/backend/regalloc.h |   2 +-
 src/cc/backend/ssa.c      |  25 ++++-----
 6 files changed, 87 insertions(+), 69 deletions(-)

diff --git a/src/_debug/dump_ir.c b/src/_debug/dump_ir.c
index 6a02acbb8..a8e7a3440 100644
--- a/src/_debug/dump_ir.c
+++ b/src/_debug/dump_ir.c
@@ -22,23 +22,30 @@ static bool keep_virtual_register;
 
 extern void install_builtins(void);
 
-static void dump_vvreg(FILE *fp, VReg *vreg) {
-  if (vreg->version == 0) {
+static void dump_vvreg(FILE *fp, VReg *vreg, RegAlloc *ra) {
+  if (vreg->original == vreg) {
     fprintf(fp, "V%d", vreg->virt);
   } else {
+    Vector *versions = ra->vreg_table[vreg->original->virt];
+    int version;
+    for (version = 0; version < versions->len; ++version) {
+      if (versions->data[version] == vreg)
+        break;
+    }
+    assert(version < versions->len);
+
     char buf[16], *p = buf + sizeof(buf);
     *(--p) = '\0';
-    int version = vreg->version;
     do {
       --version;
       *(--p) = 'a' + (version % 26);
       version /= 26;
     } while (version > 0);
-    fprintf(fp, "v%d%s", vreg->orig_virt, p);
+    fprintf(fp, "v%d%s", vreg->original->virt, p);
   }
 }
 
-static void dump_vreg(FILE *fp, VReg *vreg) {
+static void dump_vreg(FILE *fp, VReg *vreg, RegAlloc *ra) {
   assert(vreg != NULL);
   assert(!(vreg->flag & VRF_SPILLED));
   static const char *kSize[] = {"b", "w", "d", ""};
@@ -56,15 +63,15 @@ static void dump_vreg(FILE *fp, VReg *vreg) {
       regtype = 'F';
     fprintf(fp, "%c%d%s<v%d>", regtype, vreg->phys, kSize[vreg->vsize], vreg->virt);
   } else {
-    dump_vvreg(fp, vreg);
+    dump_vvreg(fp, vreg, ra);
   }
 }
 
-static void dump_vreg2(FILE *fp, VReg *vreg) {
+static void dump_vreg2(FILE *fp, VReg *vreg, RegAlloc *ra) {
   if (vreg->flag & VRF_SPILLED) {
     fprintf(fp, "spilled(v%d)", vreg->virt);
   } else {
-    dump_vreg(fp, vreg);
+    dump_vreg(fp, vreg, ra);
   }
 }
 
@@ -80,7 +87,7 @@ static void dump_vregs(FILE *fp, const char *title, Vector *regs, bool newline)
     fprintf(fp, "]");
 }
 
-static void dump_ir(FILE *fp, IR *ir) {
+static void dump_ir(FILE *fp, IR *ir, RegAlloc *ra) {
   static char *kOps[] = {
     "BOFS", "IOFS", "SOFS", "LOAD", "LOAD_S", "STORE", "STORE_S",
     "ADD", "SUB", "MUL", "DIV", "MOD", "BITAND", "BITOR", "BITXOR", "LSHIFT", "RSHIFT", "COND",
@@ -105,55 +112,55 @@ static void dump_ir(FILE *fp, IR *ir) {
   }
 
   switch (ir->kind) {
-  case IR_BOFS:   { int64_t offset = ir->bofs.frameinfo->offset + ir->bofs.offset; dump_vreg(fp, ir->dst); fprintf(fp, " = &[rbp %c %" PRId64 "]\n", offset >= 0 ? '+' : '-', offset > 0 ? offset : -offset); } break;
-  case IR_IOFS:   dump_vreg(fp, ir->dst); fprintf(fp, " = &%.*s", NAMES(ir->iofs.label)); if (ir->iofs.offset != 0) { int64_t offset = ir->iofs.offset; fprintf(fp, " %c %" PRId64, offset >= 0 ? '+' : '-', offset > 0 ? offset : -offset); } fprintf(fp, "\n"); break;
-  case IR_SOFS:   dump_vreg(fp, ir->dst); fprintf(fp, " = &[rsp %c %" PRId64 "]\n", ir->opr1->fixnum >= 0 ? '+' : '-', ir->opr1->fixnum > 0 ? ir->opr1->fixnum : -ir->opr1->fixnum); break;
-  case IR_LOAD:   dump_vreg(fp, ir->dst); fprintf(fp, " = ["); dump_vreg(fp, ir->opr1); fprintf(fp, "]\n"); break;
-  case IR_LOAD_S: dump_vreg(fp, ir->dst); fprintf(fp, " = [v%d]\n", ir->opr1->virt); break;
-  case IR_STORE:  fprintf(fp, "["); dump_vreg(fp, ir->opr2); fprintf(fp, "] = "); dump_vreg(fp, ir->opr1); fprintf(fp, "\n"); break;
-  case IR_STORE_S:fprintf(fp, "[v%d] = ", ir->opr2->virt); dump_vreg(fp, ir->opr1); fprintf(fp, "\n"); break;
-  case IR_ADD:    dump_vreg(fp, ir->dst); fprintf(fp, " = "); dump_vreg(fp, ir->opr1); fprintf(fp, " + "); dump_vreg(fp, ir->opr2); fprintf(fp, "\n"); break;
-  case IR_SUB:    dump_vreg(fp, ir->dst); fprintf(fp, " = "); dump_vreg(fp, ir->opr1); fprintf(fp, " - "); dump_vreg(fp, ir->opr2); fprintf(fp, "\n"); break;
-  case IR_MUL:    dump_vreg(fp, ir->dst); fprintf(fp, " = "); dump_vreg(fp, ir->opr1); fprintf(fp, " * "); dump_vreg(fp, ir->opr2); fprintf(fp, "\n"); break;
-  case IR_DIV:    dump_vreg(fp, ir->dst); fprintf(fp, " = "); dump_vreg(fp, ir->opr1); fprintf(fp, " / "); dump_vreg(fp, ir->opr2); fprintf(fp, "\n"); break;
-  case IR_MOD:    dump_vreg(fp, ir->dst); fprintf(fp, " = "); dump_vreg(fp, ir->opr1); fprintf(fp, " %% "); dump_vreg(fp, ir->opr2); fprintf(fp, "\n"); break;
-  case IR_BITAND: dump_vreg(fp, ir->dst); fprintf(fp, " = "); dump_vreg(fp, ir->opr1); fprintf(fp, " & "); dump_vreg(fp, ir->opr2); fprintf(fp, "\n"); break;
-  case IR_BITOR:  dump_vreg(fp, ir->dst); fprintf(fp, " = "); dump_vreg(fp, ir->opr1); fprintf(fp, " | "); dump_vreg(fp, ir->opr2); fprintf(fp, "\n"); break;
-  case IR_BITXOR: dump_vreg(fp, ir->dst); fprintf(fp, " = "); dump_vreg(fp, ir->opr1); fprintf(fp, " ^ "); dump_vreg(fp, ir->opr2); fprintf(fp, "\n"); break;
-  case IR_LSHIFT: dump_vreg(fp, ir->dst); fprintf(fp, " = "); dump_vreg(fp, ir->opr1); fprintf(fp, " << "); dump_vreg(fp, ir->opr2); fprintf(fp, "\n"); break;
-  case IR_RSHIFT: dump_vreg(fp, ir->dst); fprintf(fp, " = "); dump_vreg(fp, ir->opr1); fprintf(fp, " >> "); dump_vreg(fp, ir->opr2); fprintf(fp, "\n"); break;
-  case IR_COND:   dump_vreg(fp, ir->dst); fprintf(fp, " = "); if (ir->cond.kind != COND_ANY && ir->cond.kind != COND_NONE) {dump_vreg(fp, ir->opr1); fprintf(fp, " %s ", kCond2[ir->cond.kind & (COND_MASK | COND_UNSIGNED)]); dump_vreg(fp, ir->opr2);} fprintf(fp, "\n"); break;
-  case IR_NEG:    dump_vreg(fp, ir->dst); fprintf(fp, " = -"); dump_vreg(fp, ir->opr1); fprintf(fp, "\n"); break;
-  case IR_BITNOT: dump_vreg(fp, ir->dst); fprintf(fp, " = ~"); dump_vreg(fp, ir->opr1); fprintf(fp, "\n"); break;
-  case IR_CAST:   dump_vreg(fp, ir->dst); fprintf(fp, " = "); dump_vreg(fp, ir->opr1); fprintf(fp, "\n"); break;
-  case IR_MOV:    dump_vreg(fp, ir->dst); fprintf(fp, " = "); dump_vreg(fp, ir->opr1); fprintf(fp, "\n"); break;
-  case IR_RESULT: if (ir->dst != NULL) { dump_vreg(fp, ir->dst); fprintf(fp, " = "); } dump_vreg(fp, ir->opr1); fprintf(fp, "\n"); break;
-  case IR_JMP:    if (ir->jmp.cond != COND_ANY && ir->jmp.cond != COND_NONE) {dump_vreg(fp, ir->opr1); fprintf(fp, ", "); dump_vreg(fp, ir->opr2); fprintf(fp, ", ");} fprintf(fp, "%.*s\n", NAMES(ir->jmp.bb->label)); break;
+  case IR_BOFS:   { int64_t offset = ir->bofs.frameinfo->offset + ir->bofs.offset; dump_vreg(fp, ir->dst, ra); fprintf(fp, " = &[rbp %c %" PRId64 "]\n", offset >= 0 ? '+' : '-', offset > 0 ? offset : -offset); } break;
+  case IR_IOFS:   dump_vreg(fp, ir->dst, ra); fprintf(fp, " = &%.*s", NAMES(ir->iofs.label)); if (ir->iofs.offset != 0) { int64_t offset = ir->iofs.offset; fprintf(fp, " %c %" PRId64, offset >= 0 ? '+' : '-', offset > 0 ? offset : -offset); } fprintf(fp, "\n"); break;
+  case IR_SOFS:   dump_vreg(fp, ir->dst, ra); fprintf(fp, " = &[rsp %c %" PRId64 "]\n", ir->opr1->fixnum >= 0 ? '+' : '-', ir->opr1->fixnum > 0 ? ir->opr1->fixnum : -ir->opr1->fixnum); break;
+  case IR_LOAD:   dump_vreg(fp, ir->dst, ra); fprintf(fp, " = ["); dump_vreg(fp, ir->opr1, ra); fprintf(fp, "]\n"); break;
+  case IR_LOAD_S: dump_vreg(fp, ir->dst, ra); fprintf(fp, " = [v%d]\n", ir->opr1->virt); break;
+  case IR_STORE:  fprintf(fp, "["); dump_vreg(fp, ir->opr2, ra); fprintf(fp, "] = "); dump_vreg(fp, ir->opr1, ra); fprintf(fp, "\n"); break;
+  case IR_STORE_S:fprintf(fp, "[v%d] = ", ir->opr2->virt); dump_vreg(fp, ir->opr1, ra); fprintf(fp, "\n"); break;
+  case IR_ADD:    dump_vreg(fp, ir->dst, ra); fprintf(fp, " = "); dump_vreg(fp, ir->opr1, ra); fprintf(fp, " + "); dump_vreg(fp, ir->opr2, ra); fprintf(fp, "\n"); break;
+  case IR_SUB:    dump_vreg(fp, ir->dst, ra); fprintf(fp, " = "); dump_vreg(fp, ir->opr1, ra); fprintf(fp, " - "); dump_vreg(fp, ir->opr2, ra); fprintf(fp, "\n"); break;
+  case IR_MUL:    dump_vreg(fp, ir->dst, ra); fprintf(fp, " = "); dump_vreg(fp, ir->opr1, ra); fprintf(fp, " * "); dump_vreg(fp, ir->opr2, ra); fprintf(fp, "\n"); break;
+  case IR_DIV:    dump_vreg(fp, ir->dst, ra); fprintf(fp, " = "); dump_vreg(fp, ir->opr1, ra); fprintf(fp, " / "); dump_vreg(fp, ir->opr2, ra); fprintf(fp, "\n"); break;
+  case IR_MOD:    dump_vreg(fp, ir->dst, ra); fprintf(fp, " = "); dump_vreg(fp, ir->opr1, ra); fprintf(fp, " %% "); dump_vreg(fp, ir->opr2, ra); fprintf(fp, "\n"); break;
+  case IR_BITAND: dump_vreg(fp, ir->dst, ra); fprintf(fp, " = "); dump_vreg(fp, ir->opr1, ra); fprintf(fp, " & "); dump_vreg(fp, ir->opr2, ra); fprintf(fp, "\n"); break;
+  case IR_BITOR:  dump_vreg(fp, ir->dst, ra); fprintf(fp, " = "); dump_vreg(fp, ir->opr1, ra); fprintf(fp, " | "); dump_vreg(fp, ir->opr2, ra); fprintf(fp, "\n"); break;
+  case IR_BITXOR: dump_vreg(fp, ir->dst, ra); fprintf(fp, " = "); dump_vreg(fp, ir->opr1, ra); fprintf(fp, " ^ "); dump_vreg(fp, ir->opr2, ra); fprintf(fp, "\n"); break;
+  case IR_LSHIFT: dump_vreg(fp, ir->dst, ra); fprintf(fp, " = "); dump_vreg(fp, ir->opr1, ra); fprintf(fp, " << "); dump_vreg(fp, ir->opr2, ra); fprintf(fp, "\n"); break;
+  case IR_RSHIFT: dump_vreg(fp, ir->dst, ra); fprintf(fp, " = "); dump_vreg(fp, ir->opr1, ra); fprintf(fp, " >> "); dump_vreg(fp, ir->opr2, ra); fprintf(fp, "\n"); break;
+  case IR_COND:   dump_vreg(fp, ir->dst, ra); fprintf(fp, " = "); if (ir->cond.kind != COND_ANY && ir->cond.kind != COND_NONE) {dump_vreg(fp, ir->opr1, ra); fprintf(fp, " %s ", kCond2[ir->cond.kind & (COND_MASK | COND_UNSIGNED)]); dump_vreg(fp, ir->opr2, ra);} fprintf(fp, "\n"); break;
+  case IR_NEG:    dump_vreg(fp, ir->dst, ra); fprintf(fp, " = -"); dump_vreg(fp, ir->opr1, ra); fprintf(fp, "\n"); break;
+  case IR_BITNOT: dump_vreg(fp, ir->dst, ra); fprintf(fp, " = ~"); dump_vreg(fp, ir->opr1, ra); fprintf(fp, "\n"); break;
+  case IR_CAST:   dump_vreg(fp, ir->dst, ra); fprintf(fp, " = "); dump_vreg(fp, ir->opr1, ra); fprintf(fp, "\n"); break;
+  case IR_MOV:    dump_vreg(fp, ir->dst, ra); fprintf(fp, " = "); dump_vreg(fp, ir->opr1, ra); fprintf(fp, "\n"); break;
+  case IR_RESULT: if (ir->dst != NULL) { dump_vreg(fp, ir->dst, ra); fprintf(fp, " = "); } dump_vreg(fp, ir->opr1, ra); fprintf(fp, "\n"); break;
+  case IR_JMP:    if (ir->jmp.cond != COND_ANY && ir->jmp.cond != COND_NONE) {dump_vreg(fp, ir->opr1, ra); fprintf(fp, ", "); dump_vreg(fp, ir->opr2, ra); fprintf(fp, ", ");} fprintf(fp, "%.*s\n", NAMES(ir->jmp.bb->label)); break;
   case IR_TJMP:
-    dump_vreg(fp, ir->opr1);
+    dump_vreg(fp, ir->opr1, ra);
     for (size_t i = 0; i < ir->tjmp.len; ++i)
       fprintf(fp, "%s%.*s", i == 0 ? ", [" : ", ", NAMES(((BB*)ir->tjmp.bbs[i])->label));
     fprintf(fp, "]");
-    if (ir->opr2 != NULL) {fprintf(fp, " (tmp="); dump_vreg(fp, ir->opr2); fprintf(fp, ")");}
+    if (ir->opr2 != NULL) {fprintf(fp, " (tmp="); dump_vreg(fp, ir->opr2, ra); fprintf(fp, ")");}
     fprintf(fp, "\n");
     break;
-  case IR_PUSHARG: fprintf(fp, "%d, ", ir->pusharg.index); dump_vreg(fp, ir->opr1); fprintf(fp, "\n"); break;
+  case IR_PUSHARG: fprintf(fp, "%d, ", ir->pusharg.index); dump_vreg(fp, ir->opr1, ra); fprintf(fp, "\n"); break;
   case IR_CALL:
-    if (ir->dst != NULL) { dump_vreg(fp, ir->dst); fprintf(fp, " = "); }
+    if (ir->dst != NULL) { dump_vreg(fp, ir->dst, ra); fprintf(fp, " = "); }
     if (ir->call->label != NULL) {
       fprintf(fp, "%.*s(args=#%d)\n", NAMES(ir->call->label), ir->call->reg_arg_count);
     } else {
-      fprintf(fp, "*"); dump_vreg(fp, ir->opr1); fprintf(fp, "(args=#%d)\n", ir->call->reg_arg_count);
+      fprintf(fp, "*"); dump_vreg(fp, ir->opr1, ra); fprintf(fp, "(args=#%d)\n", ir->call->reg_arg_count);
     }
     break;
-  case IR_SUBSP:  dump_vreg(fp, ir->opr1); fprintf(fp, "\n"); break;
+  case IR_SUBSP:  dump_vreg(fp, ir->opr1, ra); fprintf(fp, "\n"); break;
   case IR_KEEP:
-    if (ir->dst != NULL) { fprintf(fp, "dst:"); dump_vreg(fp, ir->dst); fprintf(fp, ", "); }
+    if (ir->dst != NULL) { fprintf(fp, "dst:"); dump_vreg(fp, ir->dst, ra); fprintf(fp, ", "); }
     if (ir->opr1 != NULL) {
-      dump_vreg(fp, ir->opr1);
+      dump_vreg(fp, ir->opr1, ra);
       if (ir->opr2 != NULL) {
         fprintf(fp, ", ");
-        dump_vreg(fp, ir->opr2);
+        dump_vreg(fp, ir->opr2, ra);
       }
     }
     fprintf(fp, "\n");
@@ -219,9 +226,9 @@ static void dump_func_ir(Function *func) {
         {
           fprintf(fp, "  V%3d (flag=%x): live %3d - %3d", li->virt, vreg->flag, li->start, li->end);
           if (keep_virtual_register) {
-            if (vreg->version > 0) {
+            if (vreg->original != vreg) {
               fprintf(fp, ", ");
-              dump_vvreg(fp, vreg);
+              dump_vvreg(fp, vreg, ra);
             }
           } else {
             char regtype = vreg->flag & VRF_FLONUM ? 'F' : 'R';
@@ -249,7 +256,7 @@ static void dump_func_ir(Function *func) {
         VReg *vreg = vregs->data[j];
         if (j > 0)
           fprintf(fp, ", ");
-        dump_vreg(fp, vreg);
+        dump_vreg(fp, vreg, ra);
         fprintf(fp, "(%d)", vreg->virt);
       }
       fprintf(fp, "]\n");
@@ -280,13 +287,13 @@ static void dump_func_ir(Function *func) {
       for (int j = 0; j < bb->phis->len; ++j) {
         Phi *phi = bb->phis->data[j];
         fprintf(fp, "       \tPHI ");
-        dump_vreg2(fp, phi->dst);
+        dump_vreg2(fp, phi->dst, ra);
         fprintf(fp, " = {");
         for (int i = 0; i < phi->params->len; ++i) {
           VReg *vreg = phi->params->data[i];
           if (i > 0)
             fprintf(fp, ", ");
-          dump_vreg2(fp, vreg);
+          dump_vreg2(fp, vreg, ra);
         }
         fprintf(fp, "}\n");
       }
@@ -295,7 +302,7 @@ static void dump_func_ir(Function *func) {
     for (int j = 0; j < bb->irs->len; ++j, ++nip) {
       fprintf(fp, "%6d|\t", nip);
       IR *ir = bb->irs->data[j];
-      dump_ir(fp, ir);
+      dump_ir(fp, ir, ra);
     }
   }
   fprintf(fp, "\n");
diff --git a/src/cc/backend/ir.h b/src/cc/backend/ir.h
index 3b5742090..572152eba 100644
--- a/src/cc/backend/ir.h
+++ b/src/cc/backend/ir.h
@@ -42,10 +42,9 @@ typedef struct VReg {
   union {
     // Non-const:
     struct {
+      struct VReg *original;  // If this member is same as itself, it is the original.
       int virt;             // Virtual reg no.
       int phys;             // Physical reg no.
-      int version;          // Version number for SSA.
-      int orig_virt;
       int reg_param_index;  // Index of function parameter through register: -1=non reg param.
       FrameInfo frame;      // FrameInfo for spilled register.
     };
diff --git a/src/cc/backend/optimize.c b/src/cc/backend/optimize.c
index 81eb4e7d6..8a946a4de 100644
--- a/src/cc/backend/optimize.c
+++ b/src/cc/backend/optimize.c
@@ -193,6 +193,17 @@ static void remove_unused_vregs(RegAlloc *ra, BBContainer *bbcon) {
       VReg *vreg;
       if (!vreg_read[i] && (vreg = ra->vregs->data[i]) != NULL) {
         ra->vregs->data[i] = NULL;
+        if (vreg->original != vreg) {
+          assert(vreg->original->virt < ra->original_vreg_count);
+          Vector *vt = ra->vreg_table[vreg->original->virt];
+          int j;
+          for (j = 0; j < vt->len; ++j) {
+            if (vt->data[j] == vreg)
+              break;
+          }
+          assert(j < vt->len);
+          vec_remove_at(vt, j);
+        }
         vreg->flag |= VRF_UNUSED;
         again = true;
       }
diff --git a/src/cc/backend/regalloc.c b/src/cc/backend/regalloc.c
index b46876389..488762c85 100644
--- a/src/cc/backend/regalloc.c
+++ b/src/cc/backend/regalloc.c
@@ -38,9 +38,9 @@ VReg *reg_alloc_spawn(RegAlloc *ra, enum VRegSize vsize, int vflag) {
   assert(ra != NULL);
   VReg *vreg = reg_alloc_spawn_raw(vsize, vflag);
   if (!(vflag & VRF_CONST)) {
-    vreg->virt = vreg->orig_virt = ra->vregs->len;
+    vreg->original = vreg;  // I am the original.
+    vreg->virt = ra->vregs->len;
     vreg->phys = -1;
-    vreg->version = 0;
     vreg->reg_param_index = -1;
     vreg->frame.offset = 0;
     vec_push(ra->vregs, vreg);
@@ -50,10 +50,10 @@ VReg *reg_alloc_spawn(RegAlloc *ra, enum VRegSize vsize, int vflag) {
   return vreg;
 }
 
-VReg *reg_alloc_with_version(RegAlloc *ra, VReg *parent, int version) {
-  VReg *vreg = reg_alloc_spawn(ra, parent->vsize, parent->flag & VRF_MASK);
-  vreg->orig_virt = parent->virt;
-  vreg->version = version;
+VReg *reg_alloc_with_original(RegAlloc *ra, VReg *original) {
+  assert(original->original == original);
+  VReg *vreg = reg_alloc_spawn(ra, original->vsize, original->flag & VRF_MASK);
+  vreg->original = original;
   return vreg;
 }
 
diff --git a/src/cc/backend/regalloc.h b/src/cc/backend/regalloc.h
index a00d8aba8..37a79e1e2 100644
--- a/src/cc/backend/regalloc.h
+++ b/src/cc/backend/regalloc.h
@@ -55,7 +55,7 @@ typedef struct RegAlloc {
 RegAlloc *new_reg_alloc(const RegAllocSettings *settings);
 VReg *reg_alloc_spawn_raw(enum VRegSize vsize, int vflag);
 VReg *reg_alloc_spawn(RegAlloc *ra, enum VRegSize vsize, int vflag);
-VReg *reg_alloc_with_version(RegAlloc *ra, VReg *parent, int version);
+VReg *reg_alloc_with_original(RegAlloc *ra, VReg *original);
 VReg *reg_alloc_spawn_const(RegAlloc *ra, int64_t value, enum VRegSize vsize);
 #ifndef __NO_FLONUM
 VReg *reg_alloc_spawn_fconst(RegAlloc *ra, double value, enum VRegSize vsize);
diff --git a/src/cc/backend/ssa.c b/src/cc/backend/ssa.c
index 47ea80ed2..cdfefc16a 100644
--- a/src/cc/backend/ssa.c
+++ b/src/cc/backend/ssa.c
@@ -12,7 +12,9 @@
 
 #include "table.h"
 
-#define ORIG_VIRT(vreg)  ((vreg)->orig_virt >= 0 ? (vreg)->orig_virt : (vreg)->virt)
+static inline int ORIG_VIRT(VReg *vreg) {
+  return vreg->original->virt;
+}
 
 static inline void assign_new_vregs(RegAlloc *ra, Vector **vreg_table, BB *bb, VReg **vregs) {
   for (int iir = 0; iir < bb->irs->len; ++iir) {
@@ -28,7 +30,7 @@ static inline void assign_new_vregs(RegAlloc *ra, Vector **vreg_table, BB *bb, V
       Vector *vt = vreg_table[virt];
       VReg *dst = ra->vregs->data[virt];
       if (vt->len > 0)
-        ir->dst = dst = reg_alloc_with_version(ra, dst, vt->len);
+        ir->dst = dst = reg_alloc_with_original(ra, dst);
       vec_push(vt, dst);
       vregs[virt] = dst;
     }
@@ -123,7 +125,7 @@ static Vector **ssa_transform(RegAlloc *ra, BBContainer *bbcon) {
           continue;
         int virt = ORIG_VIRT(vreg);  // `vreg` must be original, though.
         Vector *vt = vreg_table[virt];
-        VReg *newver = reg_alloc_with_version(ra, ra->vregs->data[virt], vt->len);
+        VReg *newver = reg_alloc_with_original(ra, ra->vregs->data[virt]);
         bb->in_regs->data[i] = vregs[virt] = newver;
         vec_push(vt, newver);
       }
@@ -168,14 +170,14 @@ static void insert_phis(BBContainer *bbcon, int original_vreg_count) {
       BB *from = bb->from_bbs->data[ifrom];
       for (int j = 0; j < from->out_regs->len; ++j) {
         VReg *oreg = from->out_regs->data[j];
-        assert(0 <= oreg->orig_virt && oreg->orig_virt < original_vreg_count);
-        from_vregs[oreg->orig_virt] = oreg;
+        assert(0 <= ORIG_VIRT(oreg) && ORIG_VIRT(oreg) < original_vreg_count);
+        from_vregs[ORIG_VIRT(oreg)] = oreg;
       }
 
       for (int j = 0; j < reg_count; ++j) {
         VReg *vreg = bb->in_regs->data[j];
-        assert(0 <= vreg->orig_virt && vreg->orig_virt < original_vreg_count);
-        VReg *fv = from_vregs[vreg->orig_virt];
+        assert(vreg->original != vreg && 0 <= ORIG_VIRT(vreg) && ORIG_VIRT(vreg) < original_vreg_count);
+        VReg *fv = from_vregs[ORIG_VIRT(vreg)];
         vec_push(phi_params->data[j], fv);
       }
     }
@@ -389,11 +391,10 @@ static void replace_phis(RegAlloc *ra, BB *bb, int ifb, Vector *phis) {
       Phi *first = cyclic->data[0];
 
       // Allocate temporary vreg.
-      VReg *parent = first->dst;
-      assert(parent->orig_virt >= 0);  // phi's destination must not be an original virtual register.
-      Vector *vt = vreg_table[parent->orig_virt];
-      VReg *tmp = reg_alloc_with_version(ra, parent, vt->len);
-      tmp->orig_virt = parent->orig_virt;
+      assert(first->dst->original != first->dst);  // phi's destination must not be an original virtual register.
+      VReg *original = first->dst->original;
+      Vector *vt = vreg_table[original->virt];
+      VReg *tmp = reg_alloc_with_original(ra, original);
       vec_push(vt, tmp);
 
       for (int j = 0; j < cyclic->len; ++j) {