cfg-coalesce.c   [plain text]


/* Register coalescing code for GNU compiler.
   Contributed by Jan Hubicka
   Copyright (C) 2002 Free Software Foundation, Inc.

This file is part of GCC.

GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2, or (at your option) any later
version.

GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING.  If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.  */

#include "config.h"
#include "system.h"
#include "input.h"
#include "tree.h"
#include "rtl.h"
#include "toplev.h"
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "flags.h"
#include "basic-block.h"
#include "sbitmap.h"
#include "output.h"

typedef sbitmap *cgraph;
static void mark_conflict	PARAMS ((rtx, rtx, void *));
static cgraph build_cgraph	PARAMS ((void));
static int replace		PARAMS ((rtx *, void *));

struct mark_conflict_data
{
  cgraph graph;
  regset live;
};

/* Mark conflict betwen X and live registers.  */

static void
mark_conflict (x, set, data)
     rtx x;
     rtx set;
     void *data;
{
  struct mark_conflict_data *d = (struct mark_conflict_data *)data;
  regset live = d->live;
  cgraph graph = (cgraph) d->graph;
  int skip = -1;

  /* In the case of reg-reg copy, we may safely omit the conflict
     between source and destination and coalesce both operands into
     single register.  */
  if (GET_CODE (set) == SET
      && REG_P (XEXP (set, 0))
      && REG_P (XEXP (set, 1)))
    skip = REGNO (SET_SRC (set));

  if (GET_CODE (x) == SUBREG)
    x = SUBREG_REG (x);
  if (REG_P (x))
    {
      int regno = REGNO (x);
      int regno2;
      EXECUTE_IF_SET_IN_BITMAP (live, 0, regno2,
				if (regno2 != skip)
				  {
				    SET_BIT (graph[regno], regno2);
				    SET_BIT (graph[regno2], regno);
				  });
    }
}

/* Build conflict graph.  The graph is accurate only for pseudo registers, but
   that is OK for us.  */

static cgraph
build_cgraph ()
{
  cgraph graph = sbitmap_vector_alloc (max_regno + 1, max_regno + 1);
  regset live;
  regset_head live_head;
  basic_block bb;

  live = INITIALIZE_REG_SET (live_head);

  life_analysis (get_insns (), rtl_dump_file,
		 PROP_DEATH_NOTES | PROP_EQUAL_NOTES
		 | PROP_SCAN_DEAD_CODE | PROP_KILL_DEAD_CODE);
  graph = sbitmap_vector_alloc (max_regno + 1, max_regno + 1);
  sbitmap_vector_zero (graph, max_regno + 1);
  FOR_EACH_BB (bb)
    {
      rtx insn;
      struct propagate_block_info *pbi;

      COPY_REG_SET (live, bb->global_live_at_end);
      pbi = init_propagate_block_info (bb, live, NULL, NULL, PROP_EQUAL_NOTES);

      for (insn = bb->end; insn != bb->head; insn = PREV_INSN (insn))
	{
	  if (INSN_P (insn))
	    {
	      struct mark_conflict_data d;

	      d.graph = graph;
	      d.live = live;
	      note_stores (PATTERN (insn), mark_conflict, &d);
	      propagate_one_insn (pbi, insn);
	    }
	}
      free_propagate_block_info (pbi);
    }
  FREE_REG_SET (live);
  count_or_remove_death_notes (NULL, 1);
  return graph;
}

/* Replace registers in X using map passed as DATA.  */

static int
replace (x, data)
     rtx *x;
     void *data;
{
  rtx *map = (rtx *) data;
  if (*x && REG_P (*x) && map[REGNO (*x)])
    *x = map[REGNO (*x)];
  return 0;
}

/* Do stand alone register coalescing pass.  */

void
coalesce ()
{
  cgraph graph = build_cgraph ();
  rtx *replace_reg_by = xcalloc (sizeof (rtx *), max_regno + 1);
  basic_block bb;

#if 0
  if (rtl_dump_file)
    dump_sbitmap_vector (rtl_dump_file, "Conflict graph", "reg", graph,
			 max_regno + 1);
#endif

  FOR_EACH_BB (bb)
    {
      rtx insn;

      for (insn = bb->head; insn != NEXT_INSN (bb->end);
	   insn = NEXT_INSN (insn))
	if (INSN_P (insn))
	  {
	    rtx set = single_set (insn);
	    rtx src, dest;
	    int regno1, regno2, regno3;
	    int i;

	    if (!set || GET_CODE (set) != SET)
	      continue;
	    src = SET_SRC (set);
	    dest = SET_DEST (set);
	    if (GET_CODE (src) == SUBREG
		&& subreg_lowpart_offset (GET_MODE (src),
					  GET_MODE (SUBREG_REG (src))) ==
		SUBREG_BYTE (src))
	      src = SUBREG_REG (src);
	    if (GET_CODE (dest) == SUBREG
		&& subreg_lowpart_offset (GET_MODE (dest),
					  GET_MODE (SUBREG_REG (dest))) ==
		SUBREG_BYTE (dest))
	      dest = SUBREG_REG (dest);
	    if (!REG_P (src) || !REG_P (dest)
		|| GET_MODE (src) != GET_MODE (dest))
	      continue;
	    regno1 = REGNO (src);
	    regno2 = REGNO (dest);
	    if (replace_reg_by[regno1])
	      src = replace_reg_by[regno1], regno1 = REGNO (src);
	    if (replace_reg_by[regno2])
	      dest = replace_reg_by[regno2], regno1 = REGNO (dest);
	    if (regno1 < FIRST_PSEUDO_REGISTER
		|| regno2 < FIRST_PSEUDO_REGISTER
		|| TEST_BIT (graph[regno1], regno2) || regno1 == regno2)
	      continue;

	    EXECUTE_IF_SET_IN_SBITMAP (graph[regno2], 0, regno3,
				       SET_BIT (graph[regno1], regno3);
				       SET_BIT (graph[regno3], regno1);
	      );
	    for (i = 0; i <= max_regno; i++)
	      if (replace_reg_by [i] == dest)
		replace_reg_by[i] = src;
	    replace_reg_by [regno2] = src;
	    if (rtl_dump_file)
	      fprintf (rtl_dump_file, "Coalescing %i and %i for insn %i\n",
		       regno1, regno2, INSN_UID (insn));
	  }
    }
  FOR_EACH_BB (bb)
    {
      rtx insn;

      for (insn = bb->head; insn != NEXT_INSN (bb->end);
	   insn = NEXT_INSN (insn))
	if (INSN_P (insn))
	  for_each_rtx (&insn, replace, replace_reg_by);
    }

  sbitmap_vector_free (graph);
  delete_trivially_dead_insns (get_insns (), max_reg_num ());
  free (replace_reg_by);
}