cvsuser     03/11/03 16:34:23

  Added:       imcc     reg_alloc.c
  Log:
  Bulk move from imc.c for reorg.
  
  Revision  Changes    Path
  1.1                  parrot/imcc/reg_alloc.c
  
  Index: reg_alloc.c
  ===================================================================
  /*
   * reg_alloc.c
   *
   * Register allocator:
   *
   * This is a brute force register allocator. It uses a graph-coloring
   * algorithm, but the implementation is very kludgy.
   *
   * It is a partial implementation of a Briggs-style register allocator
   * The following parts are just missing:
   *
   * - Renumbering
   * - Coaelesceing
   *
   */
  
  #include <string.h>
  #include <assert.h>
  #include "imc.h"
  #include "optimizer.h"
  
  /* recalculate CFG and register life for all syms, with this
   * set to 1, the old and slow routines are used
   * with 0, this info is recalced on the fly during spilling
   */
  #define DOIT_AGAIN_SAM 0
  
  #define SPILL_STRESS 0
  
  #if SPILL_STRESS
  # undef MAX_COLOR
  # define MAX_COLOR 4
  #endif
  
  static void make_stat(int *sets, int *cols);
  static void imc_stat_init(void);
  static void print_stat(Parrot_Interp);
  static void allocate_wanted_regs(void);
  static void build_reglist(Parrot_Interp, Instruction * unit);
  static void build_interference_graph(Parrot_Interp);
  static void compute_du_chain(Instruction * unit);
  static int interferes(Parrot_Interp, SymReg * r0, SymReg * r1);
  static int map_colors(int x, SymReg ** graph, int colors[], int typ);
  #ifdef DO_SIMPLIFY
  static int simplify (void);
  #endif
  static void compute_spilling_costs (Parrot_Interp);
  static void order_spilling (void);
  static void spill (struct Parrot_Interp *, Instruction * unit, int);
  static int try_allocate(Parrot_Interp);
  static void restore_interference_graph(void);
  static int neighbours(int node);
  
  extern int pasm_file;
  /* Globals: */
  
  static IMCStack nodeStack;
  static SymReg** interference_graph;
  static SymReg** reglist;
  static int n_symbols;
  
  /* imc_reg_alloc is the main loop of the allocation algorithm. It operates
   * on a single compilation unit at a time.
   */
  void
  imc_reg_alloc(struct Parrot_Interp *interpreter, Instruction * unit)
  {
      int to_spill;
      int todo, first;
  
      if (!unit)
          return;
      if (!optimizer_level && pasm_file)
          return;
  
      init_tables(interpreter);
      allocated = 0;
  
      debug(interpreter, DEBUG_IMC, "\n------------------------\n");
      debug(interpreter, DEBUG_IMC, "processing sub %s\n", function);
      debug(interpreter, DEBUG_IMC, "------------------------\n\n");
      if (IMCC_INFO(interpreter)->verbose ||
              (IMCC_INFO(interpreter)->debug & DEBUG_IMC))
          imc_stat_init();
  
      /* consecutive labels, if_branch, unused_labels ... */
      pre_optimize(interpreter);
      if (optimizer_level == OPT_PRE && pasm_file)
          return;
  
      nodeStack = imcstack_new();
      IMCC_INFO(interpreter)->n_spilled = 0;
  
      todo = first = 1;
      while (todo) {
          find_basic_blocks(interpreter, first);
          build_cfg(interpreter);
  
          if (first && (IMCC_INFO(interpreter)->debug & DEBUG_CFG))
              dump_cfg(interpreter);
          first = 0;
          todo = cfg_optimize(interpreter);
      }
  
      todo = first = 1;
      while (todo) {
          if (!first) {
              find_basic_blocks(interpreter, 0);
              build_cfg(interpreter);
          }
          first = 0;
  
          compute_dominators(interpreter);
          find_loops(interpreter);
  
          build_reglist(interpreter, unit);
          life_analysis(interpreter);
          /* optimize, as long as there is something to do */
          if (dont_optimize)
              todo = 0;
          else {
              todo = optimize(interpreter);
              if (todo)
                  pre_optimize(interpreter);
          }
      }
      todo = 1;
  #if !DOIT_AGAIN_SAM
      build_interference_graph(interpreter);
  #endif
      while (todo) {
  #if DOIT_AGAIN_SAM
          build_interference_graph(interpreter);
  #endif
          if (optimizer_level & OPT_SUB)
              allocate_wanted_regs();
          compute_spilling_costs(interpreter);
  #ifdef DO_SIMPLIFY
          /* simplify until no changes can be made */
          while (simplify()) {}
  #endif
          order_spilling();          /* put the remaining items on stack */
  
          to_spill = try_allocate(interpreter);
          allocated = 1;
  
          if ( to_spill >= 0 ) {
              allocated = 0;
              spill(interpreter, unit, to_spill);
              /*
               * build the new cfg/reglist on the fly in spill() and
               * do life analysis there for only the involved regs
               */
  #if DOIT_AGAIN_SAM
              find_basic_blocks(interpreter, 0);
              build_cfg(interpreter);
              build_reglist(interpreter);
              life_analysis(interpreter);
  #endif
          }
          else {
              /* the process is finished */
              todo = 0;
          }
      }
      if (optimizer_level & OPT_SUB)
          pcc_optimize(interpreter);
      if (IMCC_INFO(interpreter)->debug & DEBUG_IMC)
          dump_instructions(interpreter);
      if (IMCC_INFO(interpreter)->verbose  ||
              (IMCC_INFO(interpreter)->debug & DEBUG_IMC))
          print_stat(interpreter);
      imcstack_free(nodeStack);
  }
  
  void
  free_reglist(Parrot_Interp interpreter)
  {
      if (interference_graph) {
          free(interference_graph);
          IMCC_INFO(interpreter)->interference_graph = interference_graph = 0;
      }
      if (reglist) {
          int i;
          for (i = 0; i < n_symbols; i++)
              free_life_info(interpreter, reglist[i]);
          free(reglist);
          IMCC_INFO(interpreter)->reglist = reglist = NULL;
          IMCC_INFO(interpreter)->n_symbols = n_symbols = 0;
      }
  }
  
  /* some statistics about register usage
   * printed with --verbose --verbose
   */
  static void
  make_stat(int *sets, int *cols)
  {
      /* register usage summary */
      char type[] = "INSP";
      int i, j;
      for(i = 0; i < HASH_SIZE; i++) {
          SymReg * r = hash[i];
        for(; r; r = r->next)
              for (j = 0; j < 4; j++)
                  if (r->set == type[j] && (r->type & VTREGISTER)) {
                      sets[j]++;
                      if (cols)
                          if (r->color > cols[j])
                              cols[j] = r->color;
                  }
      }
  }
  static int imcsets[4];
  /* registes usage of .imc */
  static void imc_stat_init() {
      imcsets[0] = imcsets[1] = imcsets[2] = imcsets[3] = 0;
      make_stat(imcsets, 0);
      memset(&ostat, 0, sizeof(ostat));
  }
  
  /* and final */
  static void print_stat(Parrot_Interp interpreter)
  {
      int sets[4] = {0,0,0,0};
      int cols[4] = {-1,-1,-1,-1};
  
      make_stat(sets, cols);
      info(interpreter, 1, "sub %s:\n\tregisters in .imc:\t I%d, N%d, S%d, P%d\n",
              function, imcsets[0], imcsets[1], imcsets[2], imcsets[3]);
      info(interpreter, 1, "\t%d labels, %d lines deleted, %d if_branch, %d 
branch_branch\n",
              ostat.deleted_labels, ostat.deleted_ins, ostat.if_branch,
              ostat.branch_branch);
      info(interpreter, 1, "\t%d used once deleted\n",
              ostat.used_once);
      info(interpreter, 1, "\t%d invariants_moved\n", ostat.invariants_moved);
      info(interpreter, 1, "\tregisters needed:\t I%d, N%d, S%d, P%d\n",
              sets[0], sets[1], sets[2], sets[3]);
      info(interpreter, 1, "\tregisters in .pasm:\t I%d, N%d, S%d, P%d - %d spilled\n",
              cols[0]+1, cols[1]+1, cols[2]+1, cols[3]+1,
              IMCC_INFO(interpreter)->n_spilled);
      info(interpreter, 1, "\t%d basic_blocks, %d edges\n",
              IMCC_INFO(interpreter)->n_basic_blocks, edge_count(interpreter));
  
  }
  
  /* sort list by line  nr */
  static int
  reg_sort_f(const void *a, const void *b) {
      SymReg *ra = *(SymReg**) a;
      SymReg *rb = *(SymReg**) b;
      if (ra->first_ins->index < rb->first_ins->index) {
        return -1;
      }
      else if (ra->first_ins->index == rb->first_ins->index) {
          return 0;
      }
      else {
        return 1;
      }
  }
  
  static void
  sort_reglist(void)
  {
      qsort(reglist, n_symbols, sizeof(SymReg*), reg_sort_f);
  }
  /* make a linear list of IDENTs and VARs, set n_symbols */
  
  static void
  build_reglist(Parrot_Interp interpreter, Instruction * unit)
  {
      int i, count, unused;
  
      info(interpreter, 2, "build_reglist\n");
      /* count symbols */
      if (reglist)
          free_reglist(interpreter);
      for(i = count = 0; i < HASH_SIZE; i++) {
          SymReg * r = hash[i];
          for(; r; r = r->next)
              if(r->type & VTREGISTER)
                  count++;
      }
      n_symbols = count;
      if (count == 0)
          return;
      if (n_symbols >= HASH_SIZE)
          warning(interpreter, "build_reglist", "probably too small HASH_SIZE"
                  " (%d symbols)\n");
      reglist = calloc(n_symbols, sizeof(SymReg*));
      if (reglist == NULL) {
          fatal(1, "build_reglist","Out of mem\n");
      }
      IMCC_INFO(interpreter)->reglist = reglist;
  
      for(i = count = 0; i < HASH_SIZE; i++) {
          SymReg * r = hash[i];
          /* Add each symbol to reglist  */
          for(; r; r = r->next) {
              if(r->type & VTREGISTER) {
                  if (r->type & VT_REGP)
                      reglist[count++] = r->reg;
                  else
                      reglist[count++] = r;
                  /* rearange I/N registers
                   * XXX not here, do it, when reading the source
                   * .nciarg, ... !!!1 */
                  if ((optimizer_level & OPT_PASM) && pasm_file &&
                          (reglist[count-1]->set == 'I' ||
                          reglist[count-1]->set == 'N'))
                      reglist[count-1]->color = -1;
              }
          }
      }
      compute_du_chain(unit);
      /* we might have unused symbols here, from spilling */
      for (i = count = unused = 0; i < n_symbols; i++) {
          if (!reglist[i]->first_ins)
              unused++;
          else if (i == count)
              count++;
          else
              reglist[count++] = reglist[i];
      }
      n_symbols -= unused;
      IMCC_INFO(interpreter)->n_symbols = n_symbols;
      sort_reglist();
  }
  
  /* creates the interference graph between the variables.
   *
   * two variables interfere when they are alive at the
   * same time
   */
  
  static void
  build_interference_graph(Parrot_Interp interpreter)
  {
      int x, y;
  
      if (!n_symbols)
          return;
  
      /* Construct a graph N x N where N = number of symbolics.
       * This piece can be rewritten without the N x N array
       */
      interference_graph = calloc(n_symbols * n_symbols, sizeof(SymReg*));
      if (interference_graph == NULL)
          fatal(1, "build_interference_graph","Out of mem\n");
      IMCC_INFO(interpreter)->interference_graph = interference_graph;
  
      /* Calculate interferences between each chain and populate the the Y-axis */
      for (x = 0; x < n_symbols; x++) {
          /* If symbol was never used in a statment, it can't interfere */
          if (!reglist[x]->first_ins)
              continue;
          for (y = x + 1; y < n_symbols; y++) {
              if (!reglist[y]->first_ins)
                  continue;
              if (interferes(interpreter, reglist[x], reglist[y])) {
                  interference_graph[x*n_symbols+y] = reglist[y];
                  interference_graph[y*n_symbols+x] = reglist[x];
              }
          }
      }
  
      if (IMCC_INFO(interpreter)->debug & DEBUG_IMC)
          dump_interference_graph(interpreter);
  }
  
  
  static void compute_one_du_chain(SymReg * r, Instruction * unit);
  /* Compute a DU-chain for each symbolic in a compilation unit
   */
  static void
  compute_du_chain(Instruction * unit) {
      Instruction * ins, *lastbranch;
      int i;
  
      lastbranch = 0;
  
      /* Compute last branch in this procedure, update instruction index */
      for(i = 0, ins = unit; ins; ins = ins->next) {
          ins->index = i++;
          if(ins->type == ITBRANCH)
              lastbranch = ins;
      }
  
      /* Compute du-chains for all symbolics */
      for(i = 0; i < n_symbols; i++) {
          SymReg * r = reglist[i];
          compute_one_du_chain(r, unit);
          /* what is this used for? -lt */
          if(r->type == VTIDENTIFIER
                  && lastbranch
                  && r->last_ins
                  && r->last_ins->index < lastbranch->index)
              r->last_ins = lastbranch;
      }
  }
  
  static void
  compute_one_du_chain(SymReg * r, Instruction * unit)
  {
      Instruction * ins;
  
      /* We cannot rely on computing the value of r->first when parsing,
       * since the situation can be changed at any time by the register
       * allocation algorithm
       */
  
      r->first_ins = 0;
      r->use_count = r->lhs_use_count = 0;
      for(ins = unit; ins; ins = ins->next) {
          int ro, rw;
          ro = instruction_reads(ins, r);
          rw = instruction_writes(ins, r);
          if(ro || rw) {
              if (!r->first_ins) {
                  r->first_ins = ins;
              }
              r->last_ins = ins;
              if (rw)
                  r->lhs_use_count++;
              r->use_count++;
              /* if this symbol is used in a different scope
               * assume usage
               */
              if (r->reg) {
                  r->lhs_use_count++;
                  r->use_count++;
              }
          }
      }
  }
  
  /* Computes the cost of spilling each symbol. This is estimated by the number
   * of times the symbol appears, weighted by X*loop_depth */
  
  static void
  compute_spilling_costs (Parrot_Interp interpreter) {
      int depth, i, j, k, max_depth;
      SymReg *r;
      Instruction * ins;
  
      for(i = 0; i < n_symbols; i++) {
          r = reglist[i];
          r->score = r->use_count + (r->lhs_use_count << 2);
          /* TODO score high if -Oj and register is used in
           * JITtable instruction
           */
          /* r->score *= (r->jit_usage - r->use_count + 1) */
          /* TODO rewrite this O(n_ins * n_reg) alg:
           * - store max_depth in reg
           * - store a flag, when this reg was already spilled
           */
          for (j = max_depth = 0; j < IMCC_INFO(interpreter)->n_basic_blocks;
                  j++) {
              Life_range *l;
              int used = 0;
  
              l = r->life_info[j];
              if (!l->first_ins)
                  continue;
              for (ins = l->first_ins ; ins; ins = ins->next) {
                  for (k = 0; k < IMCC_MAX_REGS && ins->r[k]; k++)
                      if (ins->r[k] == r) {
                          used = 1;
                          break;
                      }
                  if (used && r->usage & U_SPILL) {
                      r->score = 100000;
                      goto done;
                  }
                  if (ins == l->last_ins)
                      break;
              }
  
              if (used) {
                  depth = IMCC_INFO(interpreter)->bb_list[j]->loop_depth;
                  if (depth > max_depth)
                      max_depth = depth;
              }
          }
          r->score += 1000 * max_depth;
  done:
          ; /* for MSVC 5 */
      }
  
      if (IMCC_INFO(interpreter)->debug & DEBUG_IMC)
          dump_symreg(interpreter);
  
  }
  /* See if r0's chain interferes with r1. */
  /* We currently decide that two vars interfere if they are both alive
   * at any point. This could be improved, requiring that one is alive
   * at the point of _definition_ of the other.
   */
  
  static int
  interferes(Parrot_Interp interpreter, SymReg * r0, SymReg * r1) {
  
      int i;
  
      /* Register doesn't interfere with itself
      */
      if (r0 == r1)
          return 0;
  
      /* different register sets don't interfer */
      if (r0->set != r1->set)
          return 0;
      /* if the first time r0 appears is after (or in the same instruction)
       * than the last appearance of r1, then they can't interfere.
       *
       * even if r0 and r1 are called in the same instruction, and even
       * if this instrucion does modify r0, if it's value is never used
       * later, then they can share the same register
       */
  
      /* Now: */
  
      if (r0->life_info == NULL || r1->life_info == NULL) {
          fatal(1, "interferes", "INTERNAL ERROR: Life range is NULL\n");
      }
  
      for (i=0; i < IMCC_INFO(interpreter)->n_basic_blocks; i++) {
          Life_range *l0, *l1;
  
          l0 = r0->life_info[i];
          l1 = r1->life_info[i];
  
          /* one or both are not alive in this block, so we have
           * no conflict
           */
          if (!l0->first_ins || !l1->first_ins)
              continue;
  
          /* if the registers don't overlap, i.e first_x > last_y
           * then no interference
           */
          if (l0->first_ins->index > l1->last_ins->index)
              continue;
          if (l1->first_ins->index > l0->last_ins->index)
              continue;
  
  #if 1
          /* if they only overlap one instruction and one is used RHS only
           * and the other LHS, then that's ok
           * same if both are LHS
           */
          if (l0->first_ins->index == l1->last_ins->index &&
                  instruction_writes(l0->first_ins, r0) &&
                  instruction_reads(l1->last_ins, r1) &&
                  !instruction_reads(l0->first_ins, r0))
              continue;
          if (l1->first_ins->index == l0->last_ins->index &&
                  instruction_writes(l1->first_ins, r1) &&
                  instruction_reads(l0->last_ins, r0) &&
                  !instruction_reads(l1->first_ins, r1))
              continue;
  #endif
  
          return 1;
      }
  
      return 0;
  }
  
  /*
   * Simplify deletes all the nodes from the interference graph
   * that have arity lower than MAX_COLOR
   *
   * Returns if it has been able to delete at least one node
   * and 0 otherwise.
   *
   * XXX: this doesn't look at score, so I think this is bogus
   *      - currently disabled
   *
   */
  #ifdef DO_SIMPLIFY
  static int
  simplify () {
      int changes = 0;
      int x;
      SymReg **g;
  
      g = reglist;
  
      for(x = 0; x < n_symbols; x++) {
          if (g[x]->color >= 0)   /* VTPASM */
              g[x]->simplified = 1;
      }
      for(x = 0; x < n_symbols; x++) {
        if (g[x]->simplified) {
              break;
        }
  
        if ( neighbours(x) < MAX_COLOR) {
              debug(interpreter, DEBUG_IMC, "#simplifying [%s]\n", g[x]->name);
  
            imcstack_push(nodeStack, x);
  
            g[x]->simplified = 1;
            changes = 1;
            break;
        }
      }
  
      return changes;
  }
  #endif
  
  /* Puts the remaining nodes on the stack, on the correct order.
   *
   * We count how many times does a symbol appear (weighted by the probability
   * that this particular point of code will be reached) and choose the symbol
   * with the lower score until all are in the stack.
   *
   */
  
  static void
  order_spilling () {
      int min_score = 0, total_score;
      int min_node;
      int x;
  
      while (1) {
  
        min_node = -1;
  
          for(x = 0; x < n_symbols; x++) {
  
              /* for now, our score function only
               takes in account how many times a symbols
               has appeared + the loop_depth */
  
             /* we have to combine somehow the rank of the node
              * with the cost of spilling it
              *
              * our current function to maximize is:
              *      rank - spill_cost
              *
              * I have no clue of how good it is
             */
            if (!(reglist[x]->simplified)) {
                total_score = reglist[x]->score - neighbours(x);
  
                  if ( (min_node == -1) || (min_score > total_score) )  {
                   min_node  = x;
                   min_score = total_score;
                }
            }
          }
  
        if (min_node == -1) return; /* We are finished */
  
        imcstack_push(nodeStack, min_node);
        reglist[min_node]->simplified = 1;
      }
  }
  
  
  static void
  restore_interference_graph() {
      int i;
      for (i=0; i < n_symbols; i++) {
          if ((reglist[i]->type & VTPASM) && !(optimizer_level & OPT_PASM))
              continue;
        reglist[i]->color = -1;
        reglist[i]->simplified = 0;
      }
  }
  
  /*
   * try to allocate as much as possible
   */
  static void
  allocate_wanted_regs(void)
  {
      int i, y, interf;
      SymReg *r, *s;
      SymReg ** graph = interference_graph;
  
      for (i = 0; i < n_symbols; i++) {
          r = reglist[i];
          if (r->color >= 0 || r->want_regno == -1)
              continue;
          interf = 0;
          for (y = 0; y < n_symbols; y++) {
              if ((s = graph[i*n_symbols+y])
                      && s->color == r->want_regno
                      && s->set == r->set) {
                  interf = 1;
              }
          }
          if (!interf)
              r->color = r->want_regno;
      }
  }
  /*
   * Color the graph assigning registers to each symbol:
   *
   * We just proceed poping items from the stack, and assigning
   * a free color to the them.
   *
   * If we run out of colors, then we need to spill the top node.
   */
  
  static int
  try_allocate(Parrot_Interp interpreter) {
      int x = 0;
      int color, colors[MAX_COLOR];
      int free_colors, t;
      SymReg ** graph = interference_graph;
  
      while ((imcstack_depth(nodeStack) > 0) ) {
        x=imcstack_pop(nodeStack);
  
        for (t = 0; t < 4; t++) {
            int typ = "INSP"[t];
            memset(colors, 0, sizeof(colors));
            if (reglist[x]->set == typ && reglist[x]->color == -1) {
                free_colors = map_colors(x, graph, colors, typ);
                if (free_colors > 0) {
                    for (color = 0; color < MAX_COLOR; color++) {
                          int c = (color + 16) % 32;
                        if (!colors[c]) {
                            reglist[x]->color = c;
  
                              debug(interpreter, DEBUG_IMC,
                                      "#[%s] provisionally gets color [%d]"
                                       "(%d free colors, score %d)\n",
                                        reglist[x]->name, c,
                                          free_colors, reglist[x]->score);
                            break;
                        }
                    }
                }
  
                if (reglist[x]->color == -1) {
                      debug(interpreter, DEBUG_IMC, "# no more colors free = %d\n", 
free_colors);
  
                    /* It has been impossible to assign a color
                       * to this node, return it so it gets spilled
                       */
  
                    restore_interference_graph();
                    /* clean stack */
                    while ((imcstack_depth(nodeStack) > 0) )
                        imcstack_pop(nodeStack);
                    return x;
                }
            }
        }
      }
  
      return -1; /* we are totally finished */
  }
  
  /*
   * map_colors: calculates what colors can be assigned to the x-th symbol.
   */
  static int
  map_colors(int x, SymReg ** graph, int colors[], int typ) {
      int y = 0;
      SymReg * r;
      int color, free_colors;
      memset(colors, 0, sizeof(colors[0]) * MAX_COLOR);
      /* reserved for spilling */
      if (typ == 'P')
          colors[31] = 1;
      for(y = 0; y < n_symbols; y++) {
          if((r = graph[x*n_symbols+y])
            && r->color != -1
            && r->set == typ) {
            colors[r->color] = 1;
        }
      }
      for(color = free_colors = 0; color < MAX_COLOR; color++)
        if(!colors[color])
            free_colors++;
      return free_colors;
  }
  
  #if ! DOIT_AGAIN_SAM
  /* update bb and life_info after spilling
   * this saves 4 costy routines
   * NOTE {lhs_,}use_count are not set again, this is save, when no
   *      further optimization pass follows
   *
   */
  static void
  update_life(Parrot_Interp interpreter, Instruction * unit, Instruction *ins,
          SymReg *r, int needs_fetch, int needs_store, int add)
  {
      Life_range *l;
      int i;
      Instruction *ins2;
      Basic_block **bb_list = IMCC_INFO(interpreter)->bb_list;
  
      for(i = 0, ins2 = unit; ins2; ins2 = ins2->next) {
          ins2->index = i++;
      }
      /* add this sym to reglist, if not there */
      if (add) {
          reglist = realloc(reglist, (n_symbols + 1) * sizeof(SymReg *));
          reglist[n_symbols++] = r;
      }
  
      r->first_ins = r->last_ins = ins;
      if (needs_fetch) {
          /* prev instructions is a fetch then and the first usage of this reg */
          r->first_ins = ins->prev;
          /* if this ins was the first of a BB, then the fetch is the
           * first ins then
           */
          if (ins == bb_list[ins->bbindex]->start)
              bb_list[ins->bbindex]->start = ins->prev;
      }
      if (needs_store) {
          /* next ins is a store then, and ends life of this reg */
          r->last_ins = ins->next;
          if (ins == bb_list[ins->bbindex]->end)
              bb_list[ins->bbindex]->end = ins->next;
      }
      /* now set life_info */
      free_life_info(interpreter, r);
      r->life_info = calloc(IMCC_INFO(interpreter)->n_basic_blocks,
              sizeof(Life_range*));
      for (i=0; i < IMCC_INFO(interpreter)->n_basic_blocks; i++)
          make_life_range(r, i);
      l = r->life_info[ins->bbindex];
      l->first_ins = r->first_ins;
      l->last_ins = r->last_ins;
      if (IMCC_INFO(interpreter)->debug & DEBUG_IMC) {
          dump_instructions(interpreter);
          dump_symreg(interpreter);
      }
  }
  /*
   * update the interference_graph too and don't call
   *      build_interference_graph again
   */
  
  static void
  update_interference(Parrot_Interp interpreter, SymReg *old, SymReg *new)
  {
      int x, y;
      if (old != new) {
          /* n_symbols is already increased */
          SymReg ** new_graph = calloc(n_symbols * n_symbols, sizeof(SymReg*));
          /* old symbols count of previous graph */
          int o = n_symbols - 1;
          for (x = 0; x < o; x++) {
              for (y = x + 1; y < o; y++) {
                  new_graph[x*n_symbols+y] = interference_graph[x*o+y];
                  new_graph[y*n_symbols+x] = interference_graph[y*o+x];
              }
          }
          free(interference_graph);
          interference_graph = new_graph;
      }
      for (x = 0; x < n_symbols; x++) {
          for (y = x + 1; y < n_symbols; y++) {
              if (reglist[x] == old || reglist[x] == new ||
                      reglist[y] == old || reglist[y] == new) {
                  if (interferes(interpreter, reglist[x], reglist[y])) {
                      interference_graph[x*n_symbols+y] = reglist[y];
                      interference_graph[y*n_symbols+x] = reglist[x];
                  }
                  else {
                      interference_graph[x*n_symbols+y] = NULL;
                      interference_graph[y*n_symbols+x] = NULL;
                  }
              }
          }
      }
      if (IMCC_INFO(interpreter)->debug & DEBUG_IMC) {
          dump_interference_graph(interpreter);
      }
  }
  #endif
  
  /* Rewrites the unit instructions, inserting spill code in every ocurrence
   * of the symbol.
   */
  
  static void
  spill(struct Parrot_Interp *interpreter, Instruction * unit, int spilled)
  {
      Instruction * tmp, *ins;
      int i, n, dl;
      int needs_fetch, needs_store;
      SymReg * old_sym, *p31, *new_sym;
      char * buf;
      SymReg *regs[IMCC_MAX_REGS];
  
      buf = malloc(256 * sizeof(char));
      if (buf == NULL) {
        fatal(1, "spill","Out of mem\n");
      }
  
      debug(interpreter, DEBUG_IMC, "#Spilling [%s]:\n", reglist[spilled]->name);
  
      new_sym = old_sym = reglist[spilled];
      if (old_sym->usage & U_SPILL)
          fatal(1, "spill", "double spill - program too complex\n");
      new_sym->usage |= U_SPILL;
  
      IMCC_INFO(interpreter)->n_spilled++;
      n = 0;
      dl = 0;     /* line corr */
      tmp = NULL;
  
  
      /* first instruction should be ".sub" -- make sure we allocate P31
       * _after_ subroutine entry.  And after the "saveall", or any
       * other assortment of pushes. */
  
      if (!IMCC_INFO(interpreter)->p31) {
          Instruction *spill_ins;
  
          p31 = IMCC_INFO(interpreter)->p31 = mk_pasm_reg(str_dup("P31"));
          ins = unit;
          while (ins
                  && (strncmp(ins->fmt, "push", 4) == 0
                      || strcmp(ins->fmt, "saveall") == 0)) {
              ins = ins->next;
          }
          spill_ins = iNEW(interpreter, p31, str_dup("PerlArray"), NULL, 0);
          insert_ins(ins, spill_ins);
      }
      else
          p31 = IMCC_INFO(interpreter)->p31;
  
      for(ins = unit; ins; ins = ins->next) {
        needs_store = 0;
        needs_fetch = 0;
  
        if (instruction_reads (ins, old_sym) && !(ins->flags & ITSPILL))
            needs_fetch = 1;
  
        if (instruction_writes (ins, old_sym) && !(ins->flags & ITSPILL))
            needs_store = 1;
          if (dl)
              ins->index += dl;
  
        if (needs_fetch) {
            regs[0] = new_sym;
              regs[1] = p31;
              sprintf(buf, "%d", IMCC_INFO(interpreter)->n_spilled);
              regs[2] = mk_const(str_dup(buf), 'I');
            sprintf(buf, "%%s, %%s[%%s] #FETCH %s", old_sym->name);
            tmp = INS(interpreter, "set", buf, regs, 3, 4, 0);
            tmp->bbindex = ins->bbindex;
              tmp->flags |= ITSPILL;
              tmp->index = ins->index;
              ins->index++;
              /* insert tmp before actual ins */
              insert_ins(ins->prev, tmp);
              dl++;
        }
          /* change all occurance of old_sym to new */
          for (i = 0; old_sym != new_sym && ins->r[i] &&
                  i < IMCC_MAX_REGS; i++)
              if (ins->r[i] == old_sym)
                  ins->r[i] = new_sym;
        if (needs_store) {
              regs[0] = p31;
              sprintf(buf, "%d", IMCC_INFO(interpreter)->n_spilled);
              regs[1] = mk_const(str_dup(buf), 'I');
            regs[2] = new_sym;
            sprintf(buf, "%%s[%%s], %%s #STORE %s", old_sym->name);
            tmp = INS(interpreter, "set", buf, regs, 3, 2, 0);
            tmp->bbindex = ins->bbindex;
              tmp->flags |= ITSPILL;
              /* insert tmp after ins */
              insert_ins(ins, tmp);
              tmp->index = ins->index + 1;
              dl++;
        }
          if (needs_fetch || needs_store) {
  #if ! DOIT_AGAIN_SAM
              /* update life info of prev sym */
              update_life(interpreter, unit, ins, new_sym, needs_fetch, needs_store,
                      old_sym != new_sym);
              /* and interference of both */
              update_interference(interpreter, old_sym, new_sym);
  #endif
              /* if all symbols are in one basic_block, we need a new
               * symbol, so that the life_ranges are minimal
               * It would be nice, to detect, when changing the symbol
               * is necessary.
               */
              sprintf(buf, "%s_%d", old_sym->name, n++);
              new_sym = mk_symreg(str_dup(buf), old_sym->set);
              new_sym->usage |= U_SPILL;
              if (needs_store)    /* advance past store */
                  ins = tmp;
          }
      }
  
      free(buf);
  
  #if DOIT_AGAIN_SAM
      /* update index */
      for(i = 0, ins = unit; ins; ins = ins->next) {
        ins->index = i++;
      }
  #endif
      if (IMCC_INFO(interpreter)->debug & DEBUG_IMC)
          dump_instructions(interpreter);
  
  }
  
  static int
  neighbours(int node) {
      int y, cnt;
      SymReg *r;
  
      cnt = 0;
      for (y = 0; y < n_symbols; y++) {
  
        if ( (r = interference_graph[node*n_symbols + y] ) &&
                        (!r->simplified) ) {
             cnt++;
        }
      }
  
      return cnt;
  }
  
  
  /*
   * Local variables:
   * c-indentation-style: bsd
   * c-basic-offset: 4
   * indent-tabs-mode: nil
   * End:
   *
   * vim: expandtab shiftwidth=4:
  */
  
  
  

Reply via email to