3698 lines
93 KiB
C
3698 lines
93 KiB
C
/* Dataflow support routines.
|
||
Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004
|
||
Free Software Foundation, Inc.
|
||
Contributed by Michael P. Hayes (m.hayes@elec.canterbury.ac.nz,
|
||
mhayes@redhat.com)
|
||
|
||
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.
|
||
|
||
|
||
OVERVIEW:
|
||
|
||
This file provides some dataflow routines for computing reaching defs,
|
||
upward exposed uses, live variables, def-use chains, and use-def
|
||
chains. The global dataflow is performed using simple iterative
|
||
methods with a worklist and could be sped up by ordering the blocks
|
||
with a depth first search order.
|
||
|
||
A `struct ref' data structure (ref) is allocated for every register
|
||
reference (def or use) and this records the insn and bb the ref is
|
||
found within. The refs are linked together in chains of uses and defs
|
||
for each insn and for each register. Each ref also has a chain field
|
||
that links all the use refs for a def or all the def refs for a use.
|
||
This is used to create use-def or def-use chains.
|
||
|
||
|
||
USAGE:
|
||
|
||
Here's an example of using the dataflow routines.
|
||
|
||
struct df *df;
|
||
|
||
df = df_init ();
|
||
|
||
df_analyse (df, 0, DF_ALL);
|
||
|
||
df_dump (df, DF_ALL, stderr);
|
||
|
||
df_finish (df);
|
||
|
||
|
||
df_init simply creates a poor man's object (df) that needs to be
|
||
passed to all the dataflow routines. df_finish destroys this
|
||
object and frees up any allocated memory. DF_ALL says to analyse
|
||
everything.
|
||
|
||
df_analyse performs the following:
|
||
|
||
1. Records defs and uses by scanning the insns in each basic block
|
||
or by scanning the insns queued by df_insn_modify.
|
||
2. Links defs and uses into insn-def and insn-use chains.
|
||
3. Links defs and uses into reg-def and reg-use chains.
|
||
4. Assigns LUIDs to each insn (for modified blocks).
|
||
5. Calculates local reaching definitions.
|
||
6. Calculates global reaching definitions.
|
||
7. Creates use-def chains.
|
||
8. Calculates local reaching uses (upwards exposed uses).
|
||
9. Calculates global reaching uses.
|
||
10. Creates def-use chains.
|
||
11. Calculates local live registers.
|
||
12. Calculates global live registers.
|
||
13. Calculates register lifetimes and determines local registers.
|
||
|
||
|
||
PHILOSOPHY:
|
||
|
||
Note that the dataflow information is not updated for every newly
|
||
deleted or created insn. If the dataflow information requires
|
||
updating then all the changed, new, or deleted insns needs to be
|
||
marked with df_insn_modify (or df_insns_modify) either directly or
|
||
indirectly (say through calling df_insn_delete). df_insn_modify
|
||
marks all the modified insns to get processed the next time df_analyse
|
||
is called.
|
||
|
||
Beware that tinkering with insns may invalidate the dataflow information.
|
||
The philosophy behind these routines is that once the dataflow
|
||
information has been gathered, the user should store what they require
|
||
before they tinker with any insn. Once a reg is replaced, for example,
|
||
then the reg-def/reg-use chains will point to the wrong place. Once a
|
||
whole lot of changes have been made, df_analyse can be called again
|
||
to update the dataflow information. Currently, this is not very smart
|
||
with regard to propagating changes to the dataflow so it should not
|
||
be called very often.
|
||
|
||
|
||
DATA STRUCTURES:
|
||
|
||
The basic object is a REF (reference) and this may either be a DEF
|
||
(definition) or a USE of a register.
|
||
|
||
These are linked into a variety of lists; namely reg-def, reg-use,
|
||
insn-def, insn-use, def-use, and use-def lists. For example,
|
||
the reg-def lists contain all the refs that define a given register
|
||
while the insn-use lists contain all the refs used by an insn.
|
||
|
||
Note that the reg-def and reg-use chains are generally short (except for the
|
||
hard registers) and thus it is much faster to search these chains
|
||
rather than searching the def or use bitmaps.
|
||
|
||
If the insns are in SSA form then the reg-def and use-def lists
|
||
should only contain the single defining ref.
|
||
|
||
|
||
TODO:
|
||
|
||
1) Incremental dataflow analysis.
|
||
|
||
Note that if a loop invariant insn is hoisted (or sunk), we do not
|
||
need to change the def-use or use-def chains. All we have to do is to
|
||
change the bb field for all the associated defs and uses and to
|
||
renumber the LUIDs for the original and new basic blocks of the insn.
|
||
|
||
When shadowing loop mems we create new uses and defs for new pseudos
|
||
so we do not affect the existing dataflow information.
|
||
|
||
My current strategy is to queue up all modified, created, or deleted
|
||
insns so when df_analyse is called we can easily determine all the new
|
||
or deleted refs. Currently the global dataflow information is
|
||
recomputed from scratch but this could be propagated more efficiently.
|
||
|
||
2) Reduced memory requirements.
|
||
|
||
We could operate a pool of ref structures. When a ref is deleted it
|
||
gets returned to the pool (say by linking on to a chain of free refs).
|
||
This will require a pair of bitmaps for defs and uses so that we can
|
||
tell which ones have been changed. Alternatively, we could
|
||
periodically squeeze the def and use tables and associated bitmaps and
|
||
renumber the def and use ids.
|
||
|
||
3) Ordering of reg-def and reg-use lists.
|
||
|
||
Should the first entry in the def list be the first def (within a BB)?
|
||
Similarly, should the first entry in the use list be the last use
|
||
(within a BB)?
|
||
|
||
4) Working with a sub-CFG.
|
||
|
||
Often the whole CFG does not need to be analyzed, for example,
|
||
when optimizing a loop, only certain registers are of interest.
|
||
Perhaps there should be a bitmap argument to df_analyse to specify
|
||
which registers should be analyzed?
|
||
|
||
|
||
NOTES:
|
||
|
||
Embedded addressing side-effects, such as POST_INC or PRE_INC, generate
|
||
both a use and a def. These are both marked read/write to show that they
|
||
are dependent. For example, (set (reg 40) (mem (post_inc (reg 42))))
|
||
will generate a use of reg 42 followed by a def of reg 42 (both marked
|
||
read/write). Similarly, (set (reg 40) (mem (pre_dec (reg 41))))
|
||
generates a use of reg 41 then a def of reg 41 (both marked read/write),
|
||
even though reg 41 is decremented before it is used for the memory
|
||
address in this second example.
|
||
|
||
A set to a REG inside a ZERO_EXTRACT, SIGN_EXTRACT, or SUBREG invokes
|
||
a read-modify write operation. We generate both a use and a def
|
||
and again mark them read/write.
|
||
*/
|
||
|
||
#include "config.h"
|
||
#include "system.h"
|
||
#include "coretypes.h"
|
||
#include "tm.h"
|
||
#include "rtl.h"
|
||
#include "tm_p.h"
|
||
#include "insn-config.h"
|
||
#include "recog.h"
|
||
#include "function.h"
|
||
#include "regs.h"
|
||
#include "alloc-pool.h"
|
||
#include "hard-reg-set.h"
|
||
#include "basic-block.h"
|
||
#include "sbitmap.h"
|
||
#include "bitmap.h"
|
||
#include "df.h"
|
||
#include "fibheap.h"
|
||
|
||
#define FOR_EACH_BB_IN_BITMAP(BITMAP, MIN, BB, CODE) \
|
||
do \
|
||
{ \
|
||
unsigned int node_; \
|
||
EXECUTE_IF_SET_IN_BITMAP (BITMAP, MIN, node_, \
|
||
{(BB) = BASIC_BLOCK (node_); CODE;}); \
|
||
} \
|
||
while (0)
|
||
|
||
static alloc_pool df_ref_pool;
|
||
static alloc_pool df_link_pool;
|
||
static struct df *ddf;
|
||
|
||
static void df_reg_table_realloc (struct df *, int);
|
||
static void df_insn_table_realloc (struct df *, unsigned int);
|
||
static void df_bitmaps_alloc (struct df *, int);
|
||
static void df_bitmaps_free (struct df *, int);
|
||
static void df_free (struct df *);
|
||
static void df_alloc (struct df *, int);
|
||
|
||
static rtx df_reg_clobber_gen (unsigned int);
|
||
static rtx df_reg_use_gen (unsigned int);
|
||
|
||
static inline struct df_link *df_link_create (struct ref *, struct df_link *);
|
||
static struct df_link *df_ref_unlink (struct df_link **, struct ref *);
|
||
static void df_def_unlink (struct df *, struct ref *);
|
||
static void df_use_unlink (struct df *, struct ref *);
|
||
static void df_insn_refs_unlink (struct df *, basic_block, rtx);
|
||
#if 0
|
||
static void df_bb_refs_unlink (struct df *, basic_block);
|
||
static void df_refs_unlink (struct df *, bitmap);
|
||
#endif
|
||
|
||
static struct ref *df_ref_create (struct df *, rtx, rtx *, rtx,
|
||
enum df_ref_type, enum df_ref_flags);
|
||
static void df_ref_record_1 (struct df *, rtx, rtx *, rtx, enum df_ref_type,
|
||
enum df_ref_flags);
|
||
static void df_ref_record (struct df *, rtx, rtx *, rtx, enum df_ref_type,
|
||
enum df_ref_flags);
|
||
static void df_def_record_1 (struct df *, rtx, basic_block, rtx);
|
||
static void df_defs_record (struct df *, rtx, basic_block, rtx);
|
||
static void df_uses_record (struct df *, rtx *, enum df_ref_type,
|
||
basic_block, rtx, enum df_ref_flags);
|
||
static void df_insn_refs_record (struct df *, basic_block, rtx);
|
||
static void df_bb_refs_record (struct df *, basic_block);
|
||
static void df_refs_record (struct df *, bitmap);
|
||
|
||
static void df_bb_reg_def_chain_create (struct df *, basic_block);
|
||
static void df_reg_def_chain_create (struct df *, bitmap);
|
||
static void df_bb_reg_use_chain_create (struct df *, basic_block);
|
||
static void df_reg_use_chain_create (struct df *, bitmap);
|
||
static void df_bb_du_chain_create (struct df *, basic_block, bitmap);
|
||
static void df_du_chain_create (struct df *, bitmap);
|
||
static void df_bb_ud_chain_create (struct df *, basic_block);
|
||
static void df_ud_chain_create (struct df *, bitmap);
|
||
static void df_bb_rd_local_compute (struct df *, basic_block);
|
||
static void df_rd_local_compute (struct df *, bitmap);
|
||
static void df_bb_ru_local_compute (struct df *, basic_block);
|
||
static void df_ru_local_compute (struct df *, bitmap);
|
||
static void df_bb_lr_local_compute (struct df *, basic_block);
|
||
static void df_lr_local_compute (struct df *, bitmap);
|
||
static void df_bb_reg_info_compute (struct df *, basic_block, bitmap);
|
||
static void df_reg_info_compute (struct df *, bitmap);
|
||
|
||
static int df_bb_luids_set (struct df *df, basic_block);
|
||
static int df_luids_set (struct df *df, bitmap);
|
||
|
||
static int df_modified_p (struct df *, bitmap);
|
||
static int df_refs_queue (struct df *);
|
||
static int df_refs_process (struct df *);
|
||
static int df_bb_refs_update (struct df *, basic_block);
|
||
static int df_refs_update (struct df *);
|
||
static void df_analyse_1 (struct df *, bitmap, int, int);
|
||
|
||
static void df_insns_modify (struct df *, basic_block, rtx, rtx);
|
||
static int df_rtx_mem_replace (rtx *, void *);
|
||
static int df_rtx_reg_replace (rtx *, void *);
|
||
void df_refs_reg_replace (struct df *, bitmap, struct df_link *, rtx, rtx);
|
||
|
||
static int df_def_dominates_all_uses_p (struct df *, struct ref *def);
|
||
static int df_def_dominates_uses_p (struct df *, struct ref *def, bitmap);
|
||
static struct ref *df_bb_regno_last_use_find (struct df *, basic_block,
|
||
unsigned int);
|
||
static struct ref *df_bb_regno_first_def_find (struct df *, basic_block,
|
||
unsigned int);
|
||
static struct ref *df_bb_insn_regno_last_use_find (struct df *, basic_block,
|
||
rtx, unsigned int);
|
||
static struct ref *df_bb_insn_regno_first_def_find (struct df *, basic_block,
|
||
rtx, unsigned int);
|
||
|
||
static void df_chain_dump (struct df_link *, FILE *file);
|
||
static void df_chain_dump_regno (struct df_link *, FILE *file);
|
||
static void df_regno_debug (struct df *, unsigned int, FILE *);
|
||
static void df_ref_debug (struct df *, struct ref *, FILE *);
|
||
static void df_rd_transfer_function (int, int *, bitmap, bitmap, bitmap,
|
||
bitmap, void *);
|
||
static void df_ru_transfer_function (int, int *, bitmap, bitmap, bitmap,
|
||
bitmap, void *);
|
||
static void df_lr_transfer_function (int, int *, bitmap, bitmap, bitmap,
|
||
bitmap, void *);
|
||
static void hybrid_search_bitmap (basic_block, bitmap *, bitmap *,
|
||
bitmap *, bitmap *, enum df_flow_dir,
|
||
enum df_confluence_op,
|
||
transfer_function_bitmap,
|
||
sbitmap, sbitmap, void *);
|
||
static void hybrid_search_sbitmap (basic_block, sbitmap *, sbitmap *,
|
||
sbitmap *, sbitmap *, enum df_flow_dir,
|
||
enum df_confluence_op,
|
||
transfer_function_sbitmap,
|
||
sbitmap, sbitmap, void *);
|
||
|
||
|
||
/* Local memory allocation/deallocation routines. */
|
||
|
||
|
||
/* Increase the insn info table to have space for at least SIZE + 1
|
||
elements. */
|
||
static void
|
||
df_insn_table_realloc (struct df *df, unsigned int size)
|
||
{
|
||
size++;
|
||
if (size <= df->insn_size)
|
||
return;
|
||
|
||
/* Make the table a little larger than requested, so we do not need
|
||
to enlarge it so often. */
|
||
size += df->insn_size / 4;
|
||
|
||
df->insns = xrealloc (df->insns, size * sizeof (struct insn_info));
|
||
|
||
memset (df->insns + df->insn_size, 0,
|
||
(size - df->insn_size) * sizeof (struct insn_info));
|
||
|
||
df->insn_size = size;
|
||
|
||
if (! df->insns_modified)
|
||
{
|
||
df->insns_modified = BITMAP_XMALLOC ();
|
||
bitmap_zero (df->insns_modified);
|
||
}
|
||
}
|
||
|
||
|
||
/* Increase the reg info table by SIZE more elements. */
|
||
static void
|
||
df_reg_table_realloc (struct df *df, int size)
|
||
{
|
||
/* Make table 25 percent larger by default. */
|
||
if (! size)
|
||
size = df->reg_size / 4;
|
||
|
||
size += df->reg_size;
|
||
if (size < max_reg_num ())
|
||
size = max_reg_num ();
|
||
|
||
df->regs = xrealloc (df->regs, size * sizeof (struct reg_info));
|
||
|
||
/* Zero the new entries. */
|
||
memset (df->regs + df->reg_size, 0,
|
||
(size - df->reg_size) * sizeof (struct reg_info));
|
||
|
||
df->reg_size = size;
|
||
}
|
||
|
||
|
||
/* Allocate bitmaps for each basic block. */
|
||
static void
|
||
df_bitmaps_alloc (struct df *df, int flags)
|
||
{
|
||
int dflags = 0;
|
||
basic_block bb;
|
||
|
||
/* Free the bitmaps if they need resizing. */
|
||
if ((flags & DF_LR) && df->n_regs < (unsigned int) max_reg_num ())
|
||
dflags |= DF_LR | DF_RU;
|
||
if ((flags & DF_RU) && df->n_uses < df->use_id)
|
||
dflags |= DF_RU;
|
||
if ((flags & DF_RD) && df->n_defs < df->def_id)
|
||
dflags |= DF_RD;
|
||
|
||
if (dflags)
|
||
df_bitmaps_free (df, dflags);
|
||
|
||
df->n_defs = df->def_id;
|
||
df->n_uses = df->use_id;
|
||
|
||
FOR_EACH_BB (bb)
|
||
{
|
||
struct bb_info *bb_info = DF_BB_INFO (df, bb);
|
||
|
||
if (flags & DF_RD && ! bb_info->rd_in)
|
||
{
|
||
/* Allocate bitmaps for reaching definitions. */
|
||
bb_info->rd_kill = BITMAP_XMALLOC ();
|
||
bitmap_zero (bb_info->rd_kill);
|
||
bb_info->rd_gen = BITMAP_XMALLOC ();
|
||
bitmap_zero (bb_info->rd_gen);
|
||
bb_info->rd_in = BITMAP_XMALLOC ();
|
||
bb_info->rd_out = BITMAP_XMALLOC ();
|
||
bb_info->rd_valid = 0;
|
||
}
|
||
|
||
if (flags & DF_RU && ! bb_info->ru_in)
|
||
{
|
||
/* Allocate bitmaps for upward exposed uses. */
|
||
bb_info->ru_kill = BITMAP_XMALLOC ();
|
||
bitmap_zero (bb_info->ru_kill);
|
||
/* Note the lack of symmetry. */
|
||
bb_info->ru_gen = BITMAP_XMALLOC ();
|
||
bitmap_zero (bb_info->ru_gen);
|
||
bb_info->ru_in = BITMAP_XMALLOC ();
|
||
bb_info->ru_out = BITMAP_XMALLOC ();
|
||
bb_info->ru_valid = 0;
|
||
}
|
||
|
||
if (flags & DF_LR && ! bb_info->lr_in)
|
||
{
|
||
/* Allocate bitmaps for live variables. */
|
||
bb_info->lr_def = BITMAP_XMALLOC ();
|
||
bitmap_zero (bb_info->lr_def);
|
||
bb_info->lr_use = BITMAP_XMALLOC ();
|
||
bitmap_zero (bb_info->lr_use);
|
||
bb_info->lr_in = BITMAP_XMALLOC ();
|
||
bb_info->lr_out = BITMAP_XMALLOC ();
|
||
bb_info->lr_valid = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* Free bitmaps for each basic block. */
|
||
static void
|
||
df_bitmaps_free (struct df *df, int flags)
|
||
{
|
||
basic_block bb;
|
||
|
||
FOR_EACH_BB (bb)
|
||
{
|
||
struct bb_info *bb_info = DF_BB_INFO (df, bb);
|
||
|
||
if (!bb_info)
|
||
continue;
|
||
|
||
if ((flags & DF_RD) && bb_info->rd_in)
|
||
{
|
||
/* Free bitmaps for reaching definitions. */
|
||
BITMAP_XFREE (bb_info->rd_kill);
|
||
bb_info->rd_kill = NULL;
|
||
BITMAP_XFREE (bb_info->rd_gen);
|
||
bb_info->rd_gen = NULL;
|
||
BITMAP_XFREE (bb_info->rd_in);
|
||
bb_info->rd_in = NULL;
|
||
BITMAP_XFREE (bb_info->rd_out);
|
||
bb_info->rd_out = NULL;
|
||
}
|
||
|
||
if ((flags & DF_RU) && bb_info->ru_in)
|
||
{
|
||
/* Free bitmaps for upward exposed uses. */
|
||
BITMAP_XFREE (bb_info->ru_kill);
|
||
bb_info->ru_kill = NULL;
|
||
BITMAP_XFREE (bb_info->ru_gen);
|
||
bb_info->ru_gen = NULL;
|
||
BITMAP_XFREE (bb_info->ru_in);
|
||
bb_info->ru_in = NULL;
|
||
BITMAP_XFREE (bb_info->ru_out);
|
||
bb_info->ru_out = NULL;
|
||
}
|
||
|
||
if ((flags & DF_LR) && bb_info->lr_in)
|
||
{
|
||
/* Free bitmaps for live variables. */
|
||
BITMAP_XFREE (bb_info->lr_def);
|
||
bb_info->lr_def = NULL;
|
||
BITMAP_XFREE (bb_info->lr_use);
|
||
bb_info->lr_use = NULL;
|
||
BITMAP_XFREE (bb_info->lr_in);
|
||
bb_info->lr_in = NULL;
|
||
BITMAP_XFREE (bb_info->lr_out);
|
||
bb_info->lr_out = NULL;
|
||
}
|
||
}
|
||
df->flags &= ~(flags & (DF_RD | DF_RU | DF_LR));
|
||
}
|
||
|
||
|
||
/* Allocate and initialize dataflow memory. */
|
||
static void
|
||
df_alloc (struct df *df, int n_regs)
|
||
{
|
||
int n_insns;
|
||
basic_block bb;
|
||
|
||
df_link_pool = create_alloc_pool ("df_link pool", sizeof (struct df_link),
|
||
100);
|
||
df_ref_pool = create_alloc_pool ("df_ref pool", sizeof (struct ref), 100);
|
||
|
||
/* Perhaps we should use LUIDs to save memory for the insn_refs
|
||
table. This is only a small saving; a few pointers. */
|
||
n_insns = get_max_uid () + 1;
|
||
|
||
df->def_id = 0;
|
||
df->n_defs = 0;
|
||
/* Approximate number of defs by number of insns. */
|
||
df->def_size = n_insns;
|
||
df->defs = xmalloc (df->def_size * sizeof (*df->defs));
|
||
|
||
df->use_id = 0;
|
||
df->n_uses = 0;
|
||
/* Approximate number of uses by twice number of insns. */
|
||
df->use_size = n_insns * 2;
|
||
df->uses = xmalloc (df->use_size * sizeof (*df->uses));
|
||
|
||
df->n_regs = n_regs;
|
||
df->n_bbs = last_basic_block;
|
||
|
||
/* Allocate temporary working array used during local dataflow analysis. */
|
||
df->reg_def_last = xmalloc (df->n_regs * sizeof (struct ref *));
|
||
|
||
df_insn_table_realloc (df, n_insns);
|
||
|
||
df_reg_table_realloc (df, df->n_regs);
|
||
|
||
df->bbs_modified = BITMAP_XMALLOC ();
|
||
bitmap_zero (df->bbs_modified);
|
||
|
||
df->flags = 0;
|
||
|
||
df->bbs = xcalloc (last_basic_block, sizeof (struct bb_info));
|
||
|
||
df->all_blocks = BITMAP_XMALLOC ();
|
||
FOR_EACH_BB (bb)
|
||
bitmap_set_bit (df->all_blocks, bb->index);
|
||
}
|
||
|
||
|
||
/* Free all the dataflow info. */
|
||
static void
|
||
df_free (struct df *df)
|
||
{
|
||
df_bitmaps_free (df, DF_ALL);
|
||
|
||
if (df->bbs)
|
||
free (df->bbs);
|
||
df->bbs = 0;
|
||
|
||
if (df->insns)
|
||
free (df->insns);
|
||
df->insns = 0;
|
||
df->insn_size = 0;
|
||
|
||
if (df->defs)
|
||
free (df->defs);
|
||
df->defs = 0;
|
||
df->def_size = 0;
|
||
df->def_id = 0;
|
||
|
||
if (df->uses)
|
||
free (df->uses);
|
||
df->uses = 0;
|
||
df->use_size = 0;
|
||
df->use_id = 0;
|
||
|
||
if (df->regs)
|
||
free (df->regs);
|
||
df->regs = 0;
|
||
df->reg_size = 0;
|
||
|
||
if (df->bbs_modified)
|
||
BITMAP_XFREE (df->bbs_modified);
|
||
df->bbs_modified = 0;
|
||
|
||
if (df->insns_modified)
|
||
BITMAP_XFREE (df->insns_modified);
|
||
df->insns_modified = 0;
|
||
|
||
BITMAP_XFREE (df->all_blocks);
|
||
df->all_blocks = 0;
|
||
|
||
free_alloc_pool (df_ref_pool);
|
||
free_alloc_pool (df_link_pool);
|
||
|
||
}
|
||
|
||
/* Local miscellaneous routines. */
|
||
|
||
/* Return a USE for register REGNO. */
|
||
static rtx df_reg_use_gen (unsigned int regno)
|
||
{
|
||
rtx reg;
|
||
rtx use;
|
||
|
||
reg = regno_reg_rtx[regno];
|
||
|
||
use = gen_rtx_USE (GET_MODE (reg), reg);
|
||
return use;
|
||
}
|
||
|
||
|
||
/* Return a CLOBBER for register REGNO. */
|
||
static rtx df_reg_clobber_gen (unsigned int regno)
|
||
{
|
||
rtx reg;
|
||
rtx use;
|
||
|
||
reg = regno_reg_rtx[regno];
|
||
|
||
use = gen_rtx_CLOBBER (GET_MODE (reg), reg);
|
||
return use;
|
||
}
|
||
|
||
/* Local chain manipulation routines. */
|
||
|
||
/* Create a link in a def-use or use-def chain. */
|
||
static inline struct df_link *
|
||
df_link_create (struct ref *ref, struct df_link *next)
|
||
{
|
||
struct df_link *link;
|
||
|
||
link = pool_alloc (df_link_pool);
|
||
link->next = next;
|
||
link->ref = ref;
|
||
return link;
|
||
}
|
||
|
||
|
||
/* Add REF to chain head pointed to by PHEAD. */
|
||
static struct df_link *
|
||
df_ref_unlink (struct df_link **phead, struct ref *ref)
|
||
{
|
||
struct df_link *link = *phead;
|
||
|
||
if (link)
|
||
{
|
||
if (! link->next)
|
||
{
|
||
/* Only a single ref. It must be the one we want.
|
||
If not, the def-use and use-def chains are likely to
|
||
be inconsistent. */
|
||
if (link->ref != ref)
|
||
abort ();
|
||
/* Now have an empty chain. */
|
||
*phead = NULL;
|
||
}
|
||
else
|
||
{
|
||
/* Multiple refs. One of them must be us. */
|
||
if (link->ref == ref)
|
||
*phead = link->next;
|
||
else
|
||
{
|
||
/* Follow chain. */
|
||
for (; link->next; link = link->next)
|
||
{
|
||
if (link->next->ref == ref)
|
||
{
|
||
/* Unlink from list. */
|
||
link->next = link->next->next;
|
||
return link->next;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return link;
|
||
}
|
||
|
||
|
||
/* Unlink REF from all def-use/use-def chains, etc. */
|
||
int
|
||
df_ref_remove (struct df *df, struct ref *ref)
|
||
{
|
||
if (DF_REF_REG_DEF_P (ref))
|
||
{
|
||
df_def_unlink (df, ref);
|
||
df_ref_unlink (&df->insns[DF_REF_INSN_UID (ref)].defs, ref);
|
||
}
|
||
else
|
||
{
|
||
df_use_unlink (df, ref);
|
||
df_ref_unlink (&df->insns[DF_REF_INSN_UID (ref)].uses, ref);
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
|
||
/* Unlink DEF from use-def and reg-def chains. */
|
||
static void
|
||
df_def_unlink (struct df *df ATTRIBUTE_UNUSED, struct ref *def)
|
||
{
|
||
struct df_link *du_link;
|
||
unsigned int dregno = DF_REF_REGNO (def);
|
||
|
||
/* Follow def-use chain to find all the uses of this def. */
|
||
for (du_link = DF_REF_CHAIN (def); du_link; du_link = du_link->next)
|
||
{
|
||
struct ref *use = du_link->ref;
|
||
|
||
/* Unlink this def from the use-def chain. */
|
||
df_ref_unlink (&DF_REF_CHAIN (use), def);
|
||
}
|
||
DF_REF_CHAIN (def) = 0;
|
||
|
||
/* Unlink def from reg-def chain. */
|
||
df_ref_unlink (&df->regs[dregno].defs, def);
|
||
|
||
df->defs[DF_REF_ID (def)] = 0;
|
||
}
|
||
|
||
|
||
/* Unlink use from def-use and reg-use chains. */
|
||
static void
|
||
df_use_unlink (struct df *df ATTRIBUTE_UNUSED, struct ref *use)
|
||
{
|
||
struct df_link *ud_link;
|
||
unsigned int uregno = DF_REF_REGNO (use);
|
||
|
||
/* Follow use-def chain to find all the defs of this use. */
|
||
for (ud_link = DF_REF_CHAIN (use); ud_link; ud_link = ud_link->next)
|
||
{
|
||
struct ref *def = ud_link->ref;
|
||
|
||
/* Unlink this use from the def-use chain. */
|
||
df_ref_unlink (&DF_REF_CHAIN (def), use);
|
||
}
|
||
DF_REF_CHAIN (use) = 0;
|
||
|
||
/* Unlink use from reg-use chain. */
|
||
df_ref_unlink (&df->regs[uregno].uses, use);
|
||
|
||
df->uses[DF_REF_ID (use)] = 0;
|
||
}
|
||
|
||
/* Local routines for recording refs. */
|
||
|
||
|
||
/* Create a new ref of type DF_REF_TYPE for register REG at address
|
||
LOC within INSN of BB. */
|
||
static struct ref *
|
||
df_ref_create (struct df *df, rtx reg, rtx *loc, rtx insn,
|
||
enum df_ref_type ref_type, enum df_ref_flags ref_flags)
|
||
{
|
||
struct ref *this_ref;
|
||
|
||
this_ref = pool_alloc (df_ref_pool);
|
||
DF_REF_REG (this_ref) = reg;
|
||
DF_REF_LOC (this_ref) = loc;
|
||
DF_REF_INSN (this_ref) = insn;
|
||
DF_REF_CHAIN (this_ref) = 0;
|
||
DF_REF_TYPE (this_ref) = ref_type;
|
||
DF_REF_FLAGS (this_ref) = ref_flags;
|
||
|
||
if (ref_type == DF_REF_REG_DEF)
|
||
{
|
||
if (df->def_id >= df->def_size)
|
||
{
|
||
/* Make table 25 percent larger. */
|
||
df->def_size += (df->def_size / 4);
|
||
df->defs = xrealloc (df->defs,
|
||
df->def_size * sizeof (*df->defs));
|
||
}
|
||
DF_REF_ID (this_ref) = df->def_id;
|
||
df->defs[df->def_id++] = this_ref;
|
||
}
|
||
else
|
||
{
|
||
if (df->use_id >= df->use_size)
|
||
{
|
||
/* Make table 25 percent larger. */
|
||
df->use_size += (df->use_size / 4);
|
||
df->uses = xrealloc (df->uses,
|
||
df->use_size * sizeof (*df->uses));
|
||
}
|
||
DF_REF_ID (this_ref) = df->use_id;
|
||
df->uses[df->use_id++] = this_ref;
|
||
}
|
||
return this_ref;
|
||
}
|
||
|
||
|
||
/* Create a new reference of type DF_REF_TYPE for a single register REG,
|
||
used inside the LOC rtx of INSN. */
|
||
static void
|
||
df_ref_record_1 (struct df *df, rtx reg, rtx *loc, rtx insn,
|
||
enum df_ref_type ref_type, enum df_ref_flags ref_flags)
|
||
{
|
||
df_ref_create (df, reg, loc, insn, ref_type, ref_flags);
|
||
}
|
||
|
||
|
||
/* Create new references of type DF_REF_TYPE for each part of register REG
|
||
at address LOC within INSN of BB. */
|
||
static void
|
||
df_ref_record (struct df *df, rtx reg, rtx *loc, rtx insn,
|
||
enum df_ref_type ref_type, enum df_ref_flags ref_flags)
|
||
{
|
||
unsigned int regno;
|
||
|
||
if (GET_CODE (reg) != REG && GET_CODE (reg) != SUBREG)
|
||
abort ();
|
||
|
||
/* For the reg allocator we are interested in some SUBREG rtx's, but not
|
||
all. Notably only those representing a word extraction from a multi-word
|
||
reg. As written in the docu those should have the form
|
||
(subreg:SI (reg:M A) N), with size(SImode) > size(Mmode).
|
||
XXX Is that true? We could also use the global word_mode variable. */
|
||
if (GET_CODE (reg) == SUBREG
|
||
&& (GET_MODE_SIZE (GET_MODE (reg)) < GET_MODE_SIZE (word_mode)
|
||
|| GET_MODE_SIZE (GET_MODE (reg))
|
||
>= GET_MODE_SIZE (GET_MODE (SUBREG_REG (reg)))))
|
||
{
|
||
loc = &SUBREG_REG (reg);
|
||
reg = *loc;
|
||
ref_flags |= DF_REF_STRIPPED;
|
||
}
|
||
|
||
regno = REGNO (GET_CODE (reg) == SUBREG ? SUBREG_REG (reg) : reg);
|
||
if (regno < FIRST_PSEUDO_REGISTER)
|
||
{
|
||
int i;
|
||
int endregno;
|
||
|
||
if (! (df->flags & DF_HARD_REGS))
|
||
return;
|
||
|
||
/* GET_MODE (reg) is correct here. We do not want to go into a SUBREG
|
||
for the mode, because we only want to add references to regs, which
|
||
are really referenced. E.g., a (subreg:SI (reg:DI 0) 0) does _not_
|
||
reference the whole reg 0 in DI mode (which would also include
|
||
reg 1, at least, if 0 and 1 are SImode registers). */
|
||
endregno = HARD_REGNO_NREGS (regno, GET_MODE (reg));
|
||
if (GET_CODE (reg) == SUBREG)
|
||
regno += subreg_regno_offset (regno, GET_MODE (SUBREG_REG (reg)),
|
||
SUBREG_BYTE (reg), GET_MODE (reg));
|
||
endregno += regno;
|
||
|
||
for (i = regno; i < endregno; i++)
|
||
df_ref_record_1 (df, regno_reg_rtx[i],
|
||
loc, insn, ref_type, ref_flags);
|
||
}
|
||
else
|
||
{
|
||
df_ref_record_1 (df, reg, loc, insn, ref_type, ref_flags);
|
||
}
|
||
}
|
||
|
||
|
||
/* Return nonzero if writes to paradoxical SUBREGs, or SUBREGs which
|
||
are too narrow, are read-modify-write. */
|
||
bool
|
||
read_modify_subreg_p (rtx x)
|
||
{
|
||
unsigned int isize, osize;
|
||
if (GET_CODE (x) != SUBREG)
|
||
return false;
|
||
isize = GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)));
|
||
osize = GET_MODE_SIZE (GET_MODE (x));
|
||
/* Paradoxical subreg writes don't leave a trace of the old content. */
|
||
return (isize > osize && isize > UNITS_PER_WORD);
|
||
}
|
||
|
||
|
||
/* Process all the registers defined in the rtx, X. */
|
||
static void
|
||
df_def_record_1 (struct df *df, rtx x, basic_block bb, rtx insn)
|
||
{
|
||
rtx *loc;
|
||
rtx dst;
|
||
enum df_ref_flags flags = 0;
|
||
|
||
/* We may recursively call ourselves on EXPR_LIST when dealing with PARALLEL
|
||
construct. */
|
||
if (GET_CODE (x) == EXPR_LIST || GET_CODE (x) == CLOBBER)
|
||
loc = &XEXP (x, 0);
|
||
else
|
||
loc = &SET_DEST (x);
|
||
dst = *loc;
|
||
|
||
/* Some targets place small structures in registers for
|
||
return values of functions. */
|
||
if (GET_CODE (dst) == PARALLEL && GET_MODE (dst) == BLKmode)
|
||
{
|
||
int i;
|
||
|
||
for (i = XVECLEN (dst, 0) - 1; i >= 0; i--)
|
||
{
|
||
rtx temp = XVECEXP (dst, 0, i);
|
||
if (GET_CODE (temp) == EXPR_LIST || GET_CODE (temp) == CLOBBER
|
||
|| GET_CODE (temp) == SET)
|
||
df_def_record_1 (df, temp, bb, insn);
|
||
}
|
||
return;
|
||
}
|
||
|
||
/* Maybe, we should flag the use of STRICT_LOW_PART somehow. It might
|
||
be handy for the reg allocator. */
|
||
while (GET_CODE (dst) == STRICT_LOW_PART
|
||
|| GET_CODE (dst) == ZERO_EXTRACT
|
||
|| GET_CODE (dst) == SIGN_EXTRACT
|
||
|| ((df->flags & DF_FOR_REGALLOC) == 0
|
||
&& read_modify_subreg_p (dst)))
|
||
{
|
||
/* Strict low part always contains SUBREG, but we do not want to make
|
||
it appear outside, as whole register is always considered. */
|
||
if (GET_CODE (dst) == STRICT_LOW_PART)
|
||
{
|
||
loc = &XEXP (dst, 0);
|
||
dst = *loc;
|
||
}
|
||
loc = &XEXP (dst, 0);
|
||
dst = *loc;
|
||
flags |= DF_REF_READ_WRITE;
|
||
}
|
||
|
||
if (GET_CODE (dst) == REG
|
||
|| (GET_CODE (dst) == SUBREG && GET_CODE (SUBREG_REG (dst)) == REG))
|
||
df_ref_record (df, dst, loc, insn, DF_REF_REG_DEF, flags);
|
||
}
|
||
|
||
|
||
/* Process all the registers defined in the pattern rtx, X. */
|
||
static void
|
||
df_defs_record (struct df *df, rtx x, basic_block bb, rtx insn)
|
||
{
|
||
RTX_CODE code = GET_CODE (x);
|
||
|
||
if (code == SET || code == CLOBBER)
|
||
{
|
||
/* Mark the single def within the pattern. */
|
||
df_def_record_1 (df, x, bb, insn);
|
||
}
|
||
else if (code == PARALLEL)
|
||
{
|
||
int i;
|
||
|
||
/* Mark the multiple defs within the pattern. */
|
||
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
|
||
{
|
||
code = GET_CODE (XVECEXP (x, 0, i));
|
||
if (code == SET || code == CLOBBER)
|
||
df_def_record_1 (df, XVECEXP (x, 0, i), bb, insn);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* Process all the registers used in the rtx at address LOC. */
|
||
static void
|
||
df_uses_record (struct df *df, rtx *loc, enum df_ref_type ref_type,
|
||
basic_block bb, rtx insn, enum df_ref_flags flags)
|
||
{
|
||
RTX_CODE code;
|
||
rtx x;
|
||
retry:
|
||
x = *loc;
|
||
if (!x)
|
||
return;
|
||
code = GET_CODE (x);
|
||
switch (code)
|
||
{
|
||
case LABEL_REF:
|
||
case SYMBOL_REF:
|
||
case CONST_INT:
|
||
case CONST:
|
||
case CONST_DOUBLE:
|
||
case CONST_VECTOR:
|
||
case PC:
|
||
case CC0:
|
||
case ADDR_VEC:
|
||
case ADDR_DIFF_VEC:
|
||
return;
|
||
|
||
case CLOBBER:
|
||
/* If we are clobbering a MEM, mark any registers inside the address
|
||
as being used. */
|
||
if (GET_CODE (XEXP (x, 0)) == MEM)
|
||
df_uses_record (df, &XEXP (XEXP (x, 0), 0),
|
||
DF_REF_REG_MEM_STORE, bb, insn, flags);
|
||
|
||
/* If we're clobbering a REG then we have a def so ignore. */
|
||
return;
|
||
|
||
case MEM:
|
||
df_uses_record (df, &XEXP (x, 0), DF_REF_REG_MEM_LOAD, bb, insn, 0);
|
||
return;
|
||
|
||
case SUBREG:
|
||
/* While we're here, optimize this case. */
|
||
|
||
/* In case the SUBREG is not of a REG, do not optimize. */
|
||
if (GET_CODE (SUBREG_REG (x)) != REG)
|
||
{
|
||
loc = &SUBREG_REG (x);
|
||
df_uses_record (df, loc, ref_type, bb, insn, flags);
|
||
return;
|
||
}
|
||
/* ... Fall through ... */
|
||
|
||
case REG:
|
||
df_ref_record (df, x, loc, insn, ref_type, flags);
|
||
return;
|
||
|
||
case SET:
|
||
{
|
||
rtx dst = SET_DEST (x);
|
||
|
||
df_uses_record (df, &SET_SRC (x), DF_REF_REG_USE, bb, insn, 0);
|
||
|
||
switch (GET_CODE (dst))
|
||
{
|
||
case SUBREG:
|
||
if ((df->flags & DF_FOR_REGALLOC) == 0
|
||
&& read_modify_subreg_p (dst))
|
||
{
|
||
df_uses_record (df, &SUBREG_REG (dst), DF_REF_REG_USE, bb,
|
||
insn, DF_REF_READ_WRITE);
|
||
break;
|
||
}
|
||
/* Fall through. */
|
||
case REG:
|
||
case PARALLEL:
|
||
case PC:
|
||
case CC0:
|
||
break;
|
||
case MEM:
|
||
df_uses_record (df, &XEXP (dst, 0),
|
||
DF_REF_REG_MEM_STORE,
|
||
bb, insn, 0);
|
||
break;
|
||
case STRICT_LOW_PART:
|
||
/* A strict_low_part uses the whole REG and not just the SUBREG. */
|
||
dst = XEXP (dst, 0);
|
||
if (GET_CODE (dst) != SUBREG)
|
||
abort ();
|
||
df_uses_record (df, &SUBREG_REG (dst), DF_REF_REG_USE, bb,
|
||
insn, DF_REF_READ_WRITE);
|
||
break;
|
||
case ZERO_EXTRACT:
|
||
case SIGN_EXTRACT:
|
||
df_uses_record (df, &XEXP (dst, 0), DF_REF_REG_USE, bb, insn,
|
||
DF_REF_READ_WRITE);
|
||
df_uses_record (df, &XEXP (dst, 1), DF_REF_REG_USE, bb, insn, 0);
|
||
df_uses_record (df, &XEXP (dst, 2), DF_REF_REG_USE, bb, insn, 0);
|
||
dst = XEXP (dst, 0);
|
||
break;
|
||
default:
|
||
abort ();
|
||
}
|
||
return;
|
||
}
|
||
|
||
case RETURN:
|
||
break;
|
||
|
||
case ASM_OPERANDS:
|
||
case UNSPEC_VOLATILE:
|
||
case TRAP_IF:
|
||
case ASM_INPUT:
|
||
{
|
||
/* Traditional and volatile asm instructions must be considered to use
|
||
and clobber all hard registers, all pseudo-registers and all of
|
||
memory. So must TRAP_IF and UNSPEC_VOLATILE operations.
|
||
|
||
Consider for instance a volatile asm that changes the fpu rounding
|
||
mode. An insn should not be moved across this even if it only uses
|
||
pseudo-regs because it might give an incorrectly rounded result.
|
||
|
||
For now, just mark any regs we can find in ASM_OPERANDS as
|
||
used. */
|
||
|
||
/* For all ASM_OPERANDS, we must traverse the vector of input operands.
|
||
We can not just fall through here since then we would be confused
|
||
by the ASM_INPUT rtx inside ASM_OPERANDS, which do not indicate
|
||
traditional asms unlike their normal usage. */
|
||
if (code == ASM_OPERANDS)
|
||
{
|
||
int j;
|
||
|
||
for (j = 0; j < ASM_OPERANDS_INPUT_LENGTH (x); j++)
|
||
df_uses_record (df, &ASM_OPERANDS_INPUT (x, j),
|
||
DF_REF_REG_USE, bb, insn, 0);
|
||
return;
|
||
}
|
||
break;
|
||
}
|
||
|
||
case PRE_DEC:
|
||
case POST_DEC:
|
||
case PRE_INC:
|
||
case POST_INC:
|
||
case PRE_MODIFY:
|
||
case POST_MODIFY:
|
||
/* Catch the def of the register being modified. */
|
||
df_ref_record (df, XEXP (x, 0), &XEXP (x, 0), insn, DF_REF_REG_DEF, DF_REF_READ_WRITE);
|
||
|
||
/* ... Fall through to handle uses ... */
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
/* Recursively scan the operands of this expression. */
|
||
{
|
||
const char *fmt = GET_RTX_FORMAT (code);
|
||
int i;
|
||
|
||
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
|
||
{
|
||
if (fmt[i] == 'e')
|
||
{
|
||
/* Tail recursive case: save a function call level. */
|
||
if (i == 0)
|
||
{
|
||
loc = &XEXP (x, 0);
|
||
goto retry;
|
||
}
|
||
df_uses_record (df, &XEXP (x, i), ref_type, bb, insn, flags);
|
||
}
|
||
else if (fmt[i] == 'E')
|
||
{
|
||
int j;
|
||
for (j = 0; j < XVECLEN (x, i); j++)
|
||
df_uses_record (df, &XVECEXP (x, i, j), ref_type,
|
||
bb, insn, flags);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* Record all the df within INSN of basic block BB. */
|
||
static void
|
||
df_insn_refs_record (struct df *df, basic_block bb, rtx insn)
|
||
{
|
||
int i;
|
||
|
||
if (INSN_P (insn))
|
||
{
|
||
rtx note;
|
||
|
||
/* Record register defs. */
|
||
df_defs_record (df, PATTERN (insn), bb, insn);
|
||
|
||
if (df->flags & DF_EQUIV_NOTES)
|
||
for (note = REG_NOTES (insn); note;
|
||
note = XEXP (note, 1))
|
||
{
|
||
switch (REG_NOTE_KIND (note))
|
||
{
|
||
case REG_EQUIV:
|
||
case REG_EQUAL:
|
||
df_uses_record (df, &XEXP (note, 0), DF_REF_REG_USE,
|
||
bb, insn, 0);
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (GET_CODE (insn) == CALL_INSN)
|
||
{
|
||
rtx note;
|
||
rtx x;
|
||
|
||
/* Record the registers used to pass arguments. */
|
||
for (note = CALL_INSN_FUNCTION_USAGE (insn); note;
|
||
note = XEXP (note, 1))
|
||
{
|
||
if (GET_CODE (XEXP (note, 0)) == USE)
|
||
df_uses_record (df, &XEXP (XEXP (note, 0), 0), DF_REF_REG_USE,
|
||
bb, insn, 0);
|
||
}
|
||
|
||
/* The stack ptr is used (honorarily) by a CALL insn. */
|
||
x = df_reg_use_gen (STACK_POINTER_REGNUM);
|
||
df_uses_record (df, &XEXP (x, 0), DF_REF_REG_USE, bb, insn, 0);
|
||
|
||
if (df->flags & DF_HARD_REGS)
|
||
{
|
||
/* Calls may also reference any of the global registers,
|
||
so they are recorded as used. */
|
||
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
||
if (global_regs[i])
|
||
{
|
||
x = df_reg_use_gen (i);
|
||
df_uses_record (df, &SET_DEST (x),
|
||
DF_REF_REG_USE, bb, insn, 0);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Record the register uses. */
|
||
df_uses_record (df, &PATTERN (insn),
|
||
DF_REF_REG_USE, bb, insn, 0);
|
||
|
||
if (GET_CODE (insn) == CALL_INSN)
|
||
{
|
||
rtx note;
|
||
|
||
if (df->flags & DF_HARD_REGS)
|
||
{
|
||
/* Kill all registers invalidated by a call. */
|
||
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
||
if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i))
|
||
{
|
||
rtx reg_clob = df_reg_clobber_gen (i);
|
||
df_defs_record (df, reg_clob, bb, insn);
|
||
}
|
||
}
|
||
|
||
/* There may be extra registers to be clobbered. */
|
||
for (note = CALL_INSN_FUNCTION_USAGE (insn);
|
||
note;
|
||
note = XEXP (note, 1))
|
||
if (GET_CODE (XEXP (note, 0)) == CLOBBER)
|
||
df_defs_record (df, XEXP (note, 0), bb, insn);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* Record all the refs within the basic block BB. */
|
||
static void
|
||
df_bb_refs_record (struct df *df, basic_block bb)
|
||
{
|
||
rtx insn;
|
||
|
||
/* Scan the block an insn at a time from beginning to end. */
|
||
for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn))
|
||
{
|
||
if (INSN_P (insn))
|
||
{
|
||
/* Record defs within INSN. */
|
||
df_insn_refs_record (df, bb, insn);
|
||
}
|
||
if (insn == BB_END (bb))
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
/* Record all the refs in the basic blocks specified by BLOCKS. */
|
||
static void
|
||
df_refs_record (struct df *df, bitmap blocks)
|
||
{
|
||
basic_block bb;
|
||
|
||
FOR_EACH_BB_IN_BITMAP (blocks, 0, bb,
|
||
{
|
||
df_bb_refs_record (df, bb);
|
||
});
|
||
}
|
||
|
||
/* Dataflow analysis routines. */
|
||
|
||
|
||
/* Create reg-def chains for basic block BB. These are a list of
|
||
definitions for each register. */
|
||
static void
|
||
df_bb_reg_def_chain_create (struct df *df, basic_block bb)
|
||
{
|
||
rtx insn;
|
||
|
||
/* Perhaps the defs should be sorted using a depth first search
|
||
of the CFG (or possibly a breadth first search). We currently
|
||
scan the basic blocks in reverse order so that the first defs
|
||
appear at the start of the chain. */
|
||
|
||
for (insn = BB_END (bb); insn && insn != PREV_INSN (BB_HEAD (bb));
|
||
insn = PREV_INSN (insn))
|
||
{
|
||
struct df_link *link;
|
||
unsigned int uid = INSN_UID (insn);
|
||
|
||
if (! INSN_P (insn))
|
||
continue;
|
||
|
||
for (link = df->insns[uid].defs; link; link = link->next)
|
||
{
|
||
struct ref *def = link->ref;
|
||
unsigned int dregno = DF_REF_REGNO (def);
|
||
|
||
/* Do not add ref's to the chain twice, i.e., only add new
|
||
refs. XXX the same could be done by testing if the
|
||
current insn is a modified (or a new) one. This would be
|
||
faster. */
|
||
if (DF_REF_ID (def) < df->def_id_save)
|
||
continue;
|
||
|
||
df->regs[dregno].defs
|
||
= df_link_create (def, df->regs[dregno].defs);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* Create reg-def chains for each basic block within BLOCKS. These
|
||
are a list of definitions for each register. */
|
||
static void
|
||
df_reg_def_chain_create (struct df *df, bitmap blocks)
|
||
{
|
||
basic_block bb;
|
||
|
||
FOR_EACH_BB_IN_BITMAP/*_REV*/ (blocks, 0, bb,
|
||
{
|
||
df_bb_reg_def_chain_create (df, bb);
|
||
});
|
||
}
|
||
|
||
|
||
/* Create reg-use chains for basic block BB. These are a list of uses
|
||
for each register. */
|
||
static void
|
||
df_bb_reg_use_chain_create (struct df *df, basic_block bb)
|
||
{
|
||
rtx insn;
|
||
|
||
/* Scan in forward order so that the last uses appear at the start
|
||
of the chain. */
|
||
|
||
for (insn = BB_HEAD (bb); insn && insn != NEXT_INSN (BB_END (bb));
|
||
insn = NEXT_INSN (insn))
|
||
{
|
||
struct df_link *link;
|
||
unsigned int uid = INSN_UID (insn);
|
||
|
||
if (! INSN_P (insn))
|
||
continue;
|
||
|
||
for (link = df->insns[uid].uses; link; link = link->next)
|
||
{
|
||
struct ref *use = link->ref;
|
||
unsigned int uregno = DF_REF_REGNO (use);
|
||
|
||
/* Do not add ref's to the chain twice, i.e., only add new
|
||
refs. XXX the same could be done by testing if the
|
||
current insn is a modified (or a new) one. This would be
|
||
faster. */
|
||
if (DF_REF_ID (use) < df->use_id_save)
|
||
continue;
|
||
|
||
df->regs[uregno].uses
|
||
= df_link_create (use, df->regs[uregno].uses);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* Create reg-use chains for each basic block within BLOCKS. These
|
||
are a list of uses for each register. */
|
||
static void
|
||
df_reg_use_chain_create (struct df *df, bitmap blocks)
|
||
{
|
||
basic_block bb;
|
||
|
||
FOR_EACH_BB_IN_BITMAP (blocks, 0, bb,
|
||
{
|
||
df_bb_reg_use_chain_create (df, bb);
|
||
});
|
||
}
|
||
|
||
|
||
/* Create def-use chains from reaching use bitmaps for basic block BB. */
|
||
static void
|
||
df_bb_du_chain_create (struct df *df, basic_block bb, bitmap ru)
|
||
{
|
||
struct bb_info *bb_info = DF_BB_INFO (df, bb);
|
||
rtx insn;
|
||
|
||
bitmap_copy (ru, bb_info->ru_out);
|
||
|
||
/* For each def in BB create a linked list (chain) of uses
|
||
reached from the def. */
|
||
for (insn = BB_END (bb); insn && insn != PREV_INSN (BB_HEAD (bb));
|
||
insn = PREV_INSN (insn))
|
||
{
|
||
struct df_link *def_link;
|
||
struct df_link *use_link;
|
||
unsigned int uid = INSN_UID (insn);
|
||
|
||
if (! INSN_P (insn))
|
||
continue;
|
||
|
||
/* For each def in insn... */
|
||
for (def_link = df->insns[uid].defs; def_link; def_link = def_link->next)
|
||
{
|
||
struct ref *def = def_link->ref;
|
||
unsigned int dregno = DF_REF_REGNO (def);
|
||
|
||
DF_REF_CHAIN (def) = 0;
|
||
|
||
/* While the reg-use chains are not essential, it
|
||
is _much_ faster to search these short lists rather
|
||
than all the reaching uses, especially for large functions. */
|
||
for (use_link = df->regs[dregno].uses; use_link;
|
||
use_link = use_link->next)
|
||
{
|
||
struct ref *use = use_link->ref;
|
||
|
||
if (bitmap_bit_p (ru, DF_REF_ID (use)))
|
||
{
|
||
DF_REF_CHAIN (def)
|
||
= df_link_create (use, DF_REF_CHAIN (def));
|
||
|
||
bitmap_clear_bit (ru, DF_REF_ID (use));
|
||
}
|
||
}
|
||
}
|
||
|
||
/* For each use in insn... */
|
||
for (use_link = df->insns[uid].uses; use_link; use_link = use_link->next)
|
||
{
|
||
struct ref *use = use_link->ref;
|
||
bitmap_set_bit (ru, DF_REF_ID (use));
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* Create def-use chains from reaching use bitmaps for basic blocks
|
||
in BLOCKS. */
|
||
static void
|
||
df_du_chain_create (struct df *df, bitmap blocks)
|
||
{
|
||
bitmap ru;
|
||
basic_block bb;
|
||
|
||
ru = BITMAP_XMALLOC ();
|
||
|
||
FOR_EACH_BB_IN_BITMAP (blocks, 0, bb,
|
||
{
|
||
df_bb_du_chain_create (df, bb, ru);
|
||
});
|
||
|
||
BITMAP_XFREE (ru);
|
||
}
|
||
|
||
|
||
/* Create use-def chains from reaching def bitmaps for basic block BB. */
|
||
static void
|
||
df_bb_ud_chain_create (struct df *df, basic_block bb)
|
||
{
|
||
struct bb_info *bb_info = DF_BB_INFO (df, bb);
|
||
struct ref **reg_def_last = df->reg_def_last;
|
||
rtx insn;
|
||
|
||
memset (reg_def_last, 0, df->n_regs * sizeof (struct ref *));
|
||
|
||
/* For each use in BB create a linked list (chain) of defs
|
||
that reach the use. */
|
||
for (insn = BB_HEAD (bb); insn && insn != NEXT_INSN (BB_END (bb));
|
||
insn = NEXT_INSN (insn))
|
||
{
|
||
unsigned int uid = INSN_UID (insn);
|
||
struct df_link *use_link;
|
||
struct df_link *def_link;
|
||
|
||
if (! INSN_P (insn))
|
||
continue;
|
||
|
||
/* For each use in insn... */
|
||
for (use_link = df->insns[uid].uses; use_link; use_link = use_link->next)
|
||
{
|
||
struct ref *use = use_link->ref;
|
||
unsigned int regno = DF_REF_REGNO (use);
|
||
|
||
DF_REF_CHAIN (use) = 0;
|
||
|
||
/* Has regno been defined in this BB yet? If so, use
|
||
the last def as the single entry for the use-def
|
||
chain for this use. Otherwise, we need to add all
|
||
the defs using this regno that reach the start of
|
||
this BB. */
|
||
if (reg_def_last[regno])
|
||
{
|
||
DF_REF_CHAIN (use)
|
||
= df_link_create (reg_def_last[regno], 0);
|
||
}
|
||
else
|
||
{
|
||
/* While the reg-def chains are not essential, it is
|
||
_much_ faster to search these short lists rather than
|
||
all the reaching defs, especially for large
|
||
functions. */
|
||
for (def_link = df->regs[regno].defs; def_link;
|
||
def_link = def_link->next)
|
||
{
|
||
struct ref *def = def_link->ref;
|
||
|
||
if (bitmap_bit_p (bb_info->rd_in, DF_REF_ID (def)))
|
||
{
|
||
DF_REF_CHAIN (use)
|
||
= df_link_create (def, DF_REF_CHAIN (use));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* For each def in insn... record the last def of each reg. */
|
||
for (def_link = df->insns[uid].defs; def_link; def_link = def_link->next)
|
||
{
|
||
struct ref *def = def_link->ref;
|
||
int dregno = DF_REF_REGNO (def);
|
||
|
||
reg_def_last[dregno] = def;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* Create use-def chains from reaching def bitmaps for basic blocks
|
||
within BLOCKS. */
|
||
static void
|
||
df_ud_chain_create (struct df *df, bitmap blocks)
|
||
{
|
||
basic_block bb;
|
||
|
||
FOR_EACH_BB_IN_BITMAP (blocks, 0, bb,
|
||
{
|
||
df_bb_ud_chain_create (df, bb);
|
||
});
|
||
}
|
||
|
||
|
||
|
||
static void
|
||
df_rd_transfer_function (int bb ATTRIBUTE_UNUSED, int *changed, bitmap in,
|
||
bitmap out, bitmap gen, bitmap kill,
|
||
void *data ATTRIBUTE_UNUSED)
|
||
{
|
||
*changed = bitmap_union_of_diff (out, gen, in, kill);
|
||
}
|
||
|
||
|
||
static void
|
||
df_ru_transfer_function (int bb ATTRIBUTE_UNUSED, int *changed, bitmap in,
|
||
bitmap out, bitmap gen, bitmap kill,
|
||
void *data ATTRIBUTE_UNUSED)
|
||
{
|
||
*changed = bitmap_union_of_diff (in, gen, out, kill);
|
||
}
|
||
|
||
|
||
static void
|
||
df_lr_transfer_function (int bb ATTRIBUTE_UNUSED, int *changed, bitmap in,
|
||
bitmap out, bitmap use, bitmap def,
|
||
void *data ATTRIBUTE_UNUSED)
|
||
{
|
||
*changed = bitmap_union_of_diff (in, use, out, def);
|
||
}
|
||
|
||
|
||
/* Compute local reaching def info for basic block BB. */
|
||
static void
|
||
df_bb_rd_local_compute (struct df *df, basic_block bb)
|
||
{
|
||
struct bb_info *bb_info = DF_BB_INFO (df, bb);
|
||
rtx insn;
|
||
|
||
for (insn = BB_HEAD (bb); insn && insn != NEXT_INSN (BB_END (bb));
|
||
insn = NEXT_INSN (insn))
|
||
{
|
||
unsigned int uid = INSN_UID (insn);
|
||
struct df_link *def_link;
|
||
|
||
if (! INSN_P (insn))
|
||
continue;
|
||
|
||
for (def_link = df->insns[uid].defs; def_link; def_link = def_link->next)
|
||
{
|
||
struct ref *def = def_link->ref;
|
||
unsigned int regno = DF_REF_REGNO (def);
|
||
struct df_link *def2_link;
|
||
|
||
for (def2_link = df->regs[regno].defs; def2_link;
|
||
def2_link = def2_link->next)
|
||
{
|
||
struct ref *def2 = def2_link->ref;
|
||
|
||
/* Add all defs of this reg to the set of kills. This
|
||
is greedy since many of these defs will not actually
|
||
be killed by this BB but it keeps things a lot
|
||
simpler. */
|
||
bitmap_set_bit (bb_info->rd_kill, DF_REF_ID (def2));
|
||
|
||
/* Zap from the set of gens for this BB. */
|
||
bitmap_clear_bit (bb_info->rd_gen, DF_REF_ID (def2));
|
||
}
|
||
|
||
bitmap_set_bit (bb_info->rd_gen, DF_REF_ID (def));
|
||
}
|
||
}
|
||
|
||
bb_info->rd_valid = 1;
|
||
}
|
||
|
||
|
||
/* Compute local reaching def info for each basic block within BLOCKS. */
|
||
static void
|
||
df_rd_local_compute (struct df *df, bitmap blocks)
|
||
{
|
||
basic_block bb;
|
||
|
||
FOR_EACH_BB_IN_BITMAP (blocks, 0, bb,
|
||
{
|
||
df_bb_rd_local_compute (df, bb);
|
||
});
|
||
}
|
||
|
||
|
||
/* Compute local reaching use (upward exposed use) info for basic
|
||
block BB. */
|
||
static void
|
||
df_bb_ru_local_compute (struct df *df, basic_block bb)
|
||
{
|
||
/* This is much more tricky than computing reaching defs. With
|
||
reaching defs, defs get killed by other defs. With upwards
|
||
exposed uses, these get killed by defs with the same regno. */
|
||
|
||
struct bb_info *bb_info = DF_BB_INFO (df, bb);
|
||
rtx insn;
|
||
|
||
|
||
for (insn = BB_END (bb); insn && insn != PREV_INSN (BB_HEAD (bb));
|
||
insn = PREV_INSN (insn))
|
||
{
|
||
unsigned int uid = INSN_UID (insn);
|
||
struct df_link *def_link;
|
||
struct df_link *use_link;
|
||
|
||
if (! INSN_P (insn))
|
||
continue;
|
||
|
||
for (def_link = df->insns[uid].defs; def_link; def_link = def_link->next)
|
||
{
|
||
struct ref *def = def_link->ref;
|
||
unsigned int dregno = DF_REF_REGNO (def);
|
||
|
||
for (use_link = df->regs[dregno].uses; use_link;
|
||
use_link = use_link->next)
|
||
{
|
||
struct ref *use = use_link->ref;
|
||
|
||
/* Add all uses of this reg to the set of kills. This
|
||
is greedy since many of these uses will not actually
|
||
be killed by this BB but it keeps things a lot
|
||
simpler. */
|
||
bitmap_set_bit (bb_info->ru_kill, DF_REF_ID (use));
|
||
|
||
/* Zap from the set of gens for this BB. */
|
||
bitmap_clear_bit (bb_info->ru_gen, DF_REF_ID (use));
|
||
}
|
||
}
|
||
|
||
for (use_link = df->insns[uid].uses; use_link; use_link = use_link->next)
|
||
{
|
||
struct ref *use = use_link->ref;
|
||
/* Add use to set of gens in this BB. */
|
||
bitmap_set_bit (bb_info->ru_gen, DF_REF_ID (use));
|
||
}
|
||
}
|
||
bb_info->ru_valid = 1;
|
||
}
|
||
|
||
|
||
/* Compute local reaching use (upward exposed use) info for each basic
|
||
block within BLOCKS. */
|
||
static void
|
||
df_ru_local_compute (struct df *df, bitmap blocks)
|
||
{
|
||
basic_block bb;
|
||
|
||
FOR_EACH_BB_IN_BITMAP (blocks, 0, bb,
|
||
{
|
||
df_bb_ru_local_compute (df, bb);
|
||
});
|
||
}
|
||
|
||
|
||
/* Compute local live variable info for basic block BB. */
|
||
static void
|
||
df_bb_lr_local_compute (struct df *df, basic_block bb)
|
||
{
|
||
struct bb_info *bb_info = DF_BB_INFO (df, bb);
|
||
rtx insn;
|
||
|
||
for (insn = BB_END (bb); insn && insn != PREV_INSN (BB_HEAD (bb));
|
||
insn = PREV_INSN (insn))
|
||
{
|
||
unsigned int uid = INSN_UID (insn);
|
||
struct df_link *link;
|
||
|
||
if (! INSN_P (insn))
|
||
continue;
|
||
|
||
for (link = df->insns[uid].defs; link; link = link->next)
|
||
{
|
||
struct ref *def = link->ref;
|
||
unsigned int dregno = DF_REF_REGNO (def);
|
||
|
||
/* Add def to set of defs in this BB. */
|
||
bitmap_set_bit (bb_info->lr_def, dregno);
|
||
|
||
bitmap_clear_bit (bb_info->lr_use, dregno);
|
||
}
|
||
|
||
for (link = df->insns[uid].uses; link; link = link->next)
|
||
{
|
||
struct ref *use = link->ref;
|
||
/* Add use to set of uses in this BB. */
|
||
bitmap_set_bit (bb_info->lr_use, DF_REF_REGNO (use));
|
||
}
|
||
}
|
||
bb_info->lr_valid = 1;
|
||
}
|
||
|
||
|
||
/* Compute local live variable info for each basic block within BLOCKS. */
|
||
static void
|
||
df_lr_local_compute (struct df *df, bitmap blocks)
|
||
{
|
||
basic_block bb;
|
||
|
||
FOR_EACH_BB_IN_BITMAP (blocks, 0, bb,
|
||
{
|
||
df_bb_lr_local_compute (df, bb);
|
||
});
|
||
}
|
||
|
||
|
||
/* Compute register info: lifetime, bb, and number of defs and uses
|
||
for basic block BB. */
|
||
static void
|
||
df_bb_reg_info_compute (struct df *df, basic_block bb, bitmap live)
|
||
{
|
||
struct reg_info *reg_info = df->regs;
|
||
struct bb_info *bb_info = DF_BB_INFO (df, bb);
|
||
rtx insn;
|
||
|
||
bitmap_copy (live, bb_info->lr_out);
|
||
|
||
for (insn = BB_END (bb); insn && insn != PREV_INSN (BB_HEAD (bb));
|
||
insn = PREV_INSN (insn))
|
||
{
|
||
unsigned int uid = INSN_UID (insn);
|
||
unsigned int regno;
|
||
struct df_link *link;
|
||
|
||
if (! INSN_P (insn))
|
||
continue;
|
||
|
||
for (link = df->insns[uid].defs; link; link = link->next)
|
||
{
|
||
struct ref *def = link->ref;
|
||
unsigned int dregno = DF_REF_REGNO (def);
|
||
|
||
/* Kill this register. */
|
||
bitmap_clear_bit (live, dregno);
|
||
reg_info[dregno].n_defs++;
|
||
}
|
||
|
||
for (link = df->insns[uid].uses; link; link = link->next)
|
||
{
|
||
struct ref *use = link->ref;
|
||
unsigned int uregno = DF_REF_REGNO (use);
|
||
|
||
/* This register is now live. */
|
||
bitmap_set_bit (live, uregno);
|
||
reg_info[uregno].n_uses++;
|
||
}
|
||
|
||
/* Increment lifetimes of all live registers. */
|
||
EXECUTE_IF_SET_IN_BITMAP (live, 0, regno,
|
||
{
|
||
reg_info[regno].lifetime++;
|
||
});
|
||
}
|
||
}
|
||
|
||
|
||
/* Compute register info: lifetime, bb, and number of defs and uses. */
|
||
static void
|
||
df_reg_info_compute (struct df *df, bitmap blocks)
|
||
{
|
||
basic_block bb;
|
||
bitmap live;
|
||
|
||
live = BITMAP_XMALLOC ();
|
||
|
||
FOR_EACH_BB_IN_BITMAP (blocks, 0, bb,
|
||
{
|
||
df_bb_reg_info_compute (df, bb, live);
|
||
});
|
||
|
||
BITMAP_XFREE (live);
|
||
}
|
||
|
||
|
||
/* Assign LUIDs for BB. */
|
||
static int
|
||
df_bb_luids_set (struct df *df, basic_block bb)
|
||
{
|
||
rtx insn;
|
||
int luid = 0;
|
||
|
||
/* The LUIDs are monotonically increasing for each basic block. */
|
||
|
||
for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn))
|
||
{
|
||
if (INSN_P (insn))
|
||
DF_INSN_LUID (df, insn) = luid++;
|
||
DF_INSN_LUID (df, insn) = luid;
|
||
|
||
if (insn == BB_END (bb))
|
||
break;
|
||
}
|
||
return luid;
|
||
}
|
||
|
||
|
||
/* Assign LUIDs for each basic block within BLOCKS. */
|
||
static int
|
||
df_luids_set (struct df *df, bitmap blocks)
|
||
{
|
||
basic_block bb;
|
||
int total = 0;
|
||
|
||
FOR_EACH_BB_IN_BITMAP (blocks, 0, bb,
|
||
{
|
||
total += df_bb_luids_set (df, bb);
|
||
});
|
||
return total;
|
||
}
|
||
|
||
|
||
/* Perform dataflow analysis using existing DF structure for blocks
|
||
within BLOCKS. If BLOCKS is zero, use all basic blocks in the CFG. */
|
||
static void
|
||
df_analyse_1 (struct df *df, bitmap blocks, int flags, int update)
|
||
{
|
||
int aflags;
|
||
int dflags;
|
||
int i;
|
||
basic_block bb;
|
||
|
||
dflags = 0;
|
||
aflags = flags;
|
||
if (flags & DF_UD_CHAIN)
|
||
aflags |= DF_RD | DF_RD_CHAIN;
|
||
|
||
if (flags & DF_DU_CHAIN)
|
||
aflags |= DF_RU;
|
||
|
||
if (flags & DF_RU)
|
||
aflags |= DF_RU_CHAIN;
|
||
|
||
if (flags & DF_REG_INFO)
|
||
aflags |= DF_LR;
|
||
|
||
if (! blocks)
|
||
blocks = df->all_blocks;
|
||
|
||
df->flags = flags;
|
||
if (update)
|
||
{
|
||
df_refs_update (df);
|
||
/* More fine grained incremental dataflow analysis would be
|
||
nice. For now recompute the whole shebang for the
|
||
modified blocks. */
|
||
#if 0
|
||
df_refs_unlink (df, blocks);
|
||
#endif
|
||
/* All the def-use, use-def chains can be potentially
|
||
modified by changes in one block. The size of the
|
||
bitmaps can also change. */
|
||
}
|
||
else
|
||
{
|
||
/* Scan the function for all register defs and uses. */
|
||
df_refs_queue (df);
|
||
df_refs_record (df, blocks);
|
||
|
||
/* Link all the new defs and uses to the insns. */
|
||
df_refs_process (df);
|
||
}
|
||
|
||
/* Allocate the bitmaps now the total number of defs and uses are
|
||
known. If the number of defs or uses have changed, then
|
||
these bitmaps need to be reallocated. */
|
||
df_bitmaps_alloc (df, aflags);
|
||
|
||
/* Set the LUIDs for each specified basic block. */
|
||
df_luids_set (df, blocks);
|
||
|
||
/* Recreate reg-def and reg-use chains from scratch so that first
|
||
def is at the head of the reg-def chain and the last use is at
|
||
the head of the reg-use chain. This is only important for
|
||
regs local to a basic block as it speeds up searching. */
|
||
if (aflags & DF_RD_CHAIN)
|
||
{
|
||
df_reg_def_chain_create (df, blocks);
|
||
}
|
||
|
||
if (aflags & DF_RU_CHAIN)
|
||
{
|
||
df_reg_use_chain_create (df, blocks);
|
||
}
|
||
|
||
df->dfs_order = xmalloc (sizeof (int) * n_basic_blocks);
|
||
df->rc_order = xmalloc (sizeof (int) * n_basic_blocks);
|
||
df->rts_order = xmalloc (sizeof (int) * n_basic_blocks);
|
||
df->inverse_dfs_map = xmalloc (sizeof (int) * last_basic_block);
|
||
df->inverse_rc_map = xmalloc (sizeof (int) * last_basic_block);
|
||
df->inverse_rts_map = xmalloc (sizeof (int) * last_basic_block);
|
||
|
||
flow_depth_first_order_compute (df->dfs_order, df->rc_order);
|
||
flow_reverse_top_sort_order_compute (df->rts_order);
|
||
for (i = 0; i < n_basic_blocks; i++)
|
||
{
|
||
df->inverse_dfs_map[df->dfs_order[i]] = i;
|
||
df->inverse_rc_map[df->rc_order[i]] = i;
|
||
df->inverse_rts_map[df->rts_order[i]] = i;
|
||
}
|
||
if (aflags & DF_RD)
|
||
{
|
||
/* Compute the sets of gens and kills for the defs of each bb. */
|
||
df_rd_local_compute (df, df->flags & DF_RD ? blocks : df->all_blocks);
|
||
{
|
||
bitmap *in = xmalloc (sizeof (bitmap) * last_basic_block);
|
||
bitmap *out = xmalloc (sizeof (bitmap) * last_basic_block);
|
||
bitmap *gen = xmalloc (sizeof (bitmap) * last_basic_block);
|
||
bitmap *kill = xmalloc (sizeof (bitmap) * last_basic_block);
|
||
FOR_EACH_BB (bb)
|
||
{
|
||
in[bb->index] = DF_BB_INFO (df, bb)->rd_in;
|
||
out[bb->index] = DF_BB_INFO (df, bb)->rd_out;
|
||
gen[bb->index] = DF_BB_INFO (df, bb)->rd_gen;
|
||
kill[bb->index] = DF_BB_INFO (df, bb)->rd_kill;
|
||
}
|
||
iterative_dataflow_bitmap (in, out, gen, kill, df->all_blocks,
|
||
DF_FORWARD, DF_UNION, df_rd_transfer_function,
|
||
df->inverse_rc_map, NULL);
|
||
free (in);
|
||
free (out);
|
||
free (gen);
|
||
free (kill);
|
||
}
|
||
}
|
||
|
||
if (aflags & DF_UD_CHAIN)
|
||
{
|
||
/* Create use-def chains. */
|
||
df_ud_chain_create (df, df->all_blocks);
|
||
|
||
if (! (flags & DF_RD))
|
||
dflags |= DF_RD;
|
||
}
|
||
|
||
if (aflags & DF_RU)
|
||
{
|
||
/* Compute the sets of gens and kills for the upwards exposed
|
||
uses in each bb. */
|
||
df_ru_local_compute (df, df->flags & DF_RU ? blocks : df->all_blocks);
|
||
{
|
||
bitmap *in = xmalloc (sizeof (bitmap) * last_basic_block);
|
||
bitmap *out = xmalloc (sizeof (bitmap) * last_basic_block);
|
||
bitmap *gen = xmalloc (sizeof (bitmap) * last_basic_block);
|
||
bitmap *kill = xmalloc (sizeof (bitmap) * last_basic_block);
|
||
FOR_EACH_BB (bb)
|
||
{
|
||
in[bb->index] = DF_BB_INFO (df, bb)->ru_in;
|
||
out[bb->index] = DF_BB_INFO (df, bb)->ru_out;
|
||
gen[bb->index] = DF_BB_INFO (df, bb)->ru_gen;
|
||
kill[bb->index] = DF_BB_INFO (df, bb)->ru_kill;
|
||
}
|
||
iterative_dataflow_bitmap (in, out, gen, kill, df->all_blocks,
|
||
DF_BACKWARD, DF_UNION, df_ru_transfer_function,
|
||
df->inverse_rts_map, NULL);
|
||
free (in);
|
||
free (out);
|
||
free (gen);
|
||
free (kill);
|
||
}
|
||
}
|
||
|
||
if (aflags & DF_DU_CHAIN)
|
||
{
|
||
/* Create def-use chains. */
|
||
df_du_chain_create (df, df->all_blocks);
|
||
|
||
if (! (flags & DF_RU))
|
||
dflags |= DF_RU;
|
||
}
|
||
|
||
/* Free up bitmaps that are no longer required. */
|
||
if (dflags)
|
||
df_bitmaps_free (df, dflags);
|
||
|
||
if (aflags & DF_LR)
|
||
{
|
||
/* Compute the sets of defs and uses of live variables. */
|
||
df_lr_local_compute (df, df->flags & DF_LR ? blocks : df->all_blocks);
|
||
{
|
||
bitmap *in = xmalloc (sizeof (bitmap) * last_basic_block);
|
||
bitmap *out = xmalloc (sizeof (bitmap) * last_basic_block);
|
||
bitmap *use = xmalloc (sizeof (bitmap) * last_basic_block);
|
||
bitmap *def = xmalloc (sizeof (bitmap) * last_basic_block);
|
||
FOR_EACH_BB (bb)
|
||
{
|
||
in[bb->index] = DF_BB_INFO (df, bb)->lr_in;
|
||
out[bb->index] = DF_BB_INFO (df, bb)->lr_out;
|
||
use[bb->index] = DF_BB_INFO (df, bb)->lr_use;
|
||
def[bb->index] = DF_BB_INFO (df, bb)->lr_def;
|
||
}
|
||
iterative_dataflow_bitmap (in, out, use, def, df->all_blocks,
|
||
DF_BACKWARD, DF_UNION, df_lr_transfer_function,
|
||
df->inverse_rts_map, NULL);
|
||
free (in);
|
||
free (out);
|
||
free (use);
|
||
free (def);
|
||
}
|
||
}
|
||
|
||
if (aflags & DF_REG_INFO)
|
||
{
|
||
df_reg_info_compute (df, df->all_blocks);
|
||
}
|
||
|
||
free (df->dfs_order);
|
||
free (df->rc_order);
|
||
free (df->rts_order);
|
||
free (df->inverse_rc_map);
|
||
free (df->inverse_dfs_map);
|
||
free (df->inverse_rts_map);
|
||
}
|
||
|
||
|
||
/* Initialize dataflow analysis. */
|
||
struct df *
|
||
df_init (void)
|
||
{
|
||
struct df *df;
|
||
|
||
df = xcalloc (1, sizeof (struct df));
|
||
|
||
/* Squirrel away a global for debugging. */
|
||
ddf = df;
|
||
|
||
return df;
|
||
}
|
||
|
||
|
||
/* Start queuing refs. */
|
||
static int
|
||
df_refs_queue (struct df *df)
|
||
{
|
||
df->def_id_save = df->def_id;
|
||
df->use_id_save = df->use_id;
|
||
/* ???? Perhaps we should save current obstack state so that we can
|
||
unwind it. */
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* Process queued refs. */
|
||
static int
|
||
df_refs_process (struct df *df)
|
||
{
|
||
unsigned int i;
|
||
|
||
/* Build new insn-def chains. */
|
||
for (i = df->def_id_save; i != df->def_id; i++)
|
||
{
|
||
struct ref *def = df->defs[i];
|
||
unsigned int uid = DF_REF_INSN_UID (def);
|
||
|
||
/* Add def to head of def list for INSN. */
|
||
df->insns[uid].defs
|
||
= df_link_create (def, df->insns[uid].defs);
|
||
}
|
||
|
||
/* Build new insn-use chains. */
|
||
for (i = df->use_id_save; i != df->use_id; i++)
|
||
{
|
||
struct ref *use = df->uses[i];
|
||
unsigned int uid = DF_REF_INSN_UID (use);
|
||
|
||
/* Add use to head of use list for INSN. */
|
||
df->insns[uid].uses
|
||
= df_link_create (use, df->insns[uid].uses);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* Update refs for basic block BB. */
|
||
static int
|
||
df_bb_refs_update (struct df *df, basic_block bb)
|
||
{
|
||
rtx insn;
|
||
int count = 0;
|
||
|
||
/* While we have to scan the chain of insns for this BB, we do not
|
||
need to allocate and queue a long chain of BB/INSN pairs. Using
|
||
a bitmap for insns_modified saves memory and avoids queuing
|
||
duplicates. */
|
||
|
||
for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn))
|
||
{
|
||
unsigned int uid;
|
||
|
||
uid = INSN_UID (insn);
|
||
|
||
if (bitmap_bit_p (df->insns_modified, uid))
|
||
{
|
||
/* Delete any allocated refs of this insn. MPH, FIXME. */
|
||
df_insn_refs_unlink (df, bb, insn);
|
||
|
||
/* Scan the insn for refs. */
|
||
df_insn_refs_record (df, bb, insn);
|
||
|
||
count++;
|
||
}
|
||
if (insn == BB_END (bb))
|
||
break;
|
||
}
|
||
return count;
|
||
}
|
||
|
||
|
||
/* Process all the modified/deleted insns that were queued. */
|
||
static int
|
||
df_refs_update (struct df *df)
|
||
{
|
||
basic_block bb;
|
||
int count = 0;
|
||
|
||
if ((unsigned int) max_reg_num () >= df->reg_size)
|
||
df_reg_table_realloc (df, 0);
|
||
|
||
df_refs_queue (df);
|
||
|
||
FOR_EACH_BB_IN_BITMAP (df->bbs_modified, 0, bb,
|
||
{
|
||
count += df_bb_refs_update (df, bb);
|
||
});
|
||
|
||
df_refs_process (df);
|
||
return count;
|
||
}
|
||
|
||
|
||
/* Return nonzero if any of the requested blocks in the bitmap
|
||
BLOCKS have been modified. */
|
||
static int
|
||
df_modified_p (struct df *df, bitmap blocks)
|
||
{
|
||
int update = 0;
|
||
basic_block bb;
|
||
|
||
if (!df->n_bbs)
|
||
return 0;
|
||
|
||
FOR_EACH_BB (bb)
|
||
if (bitmap_bit_p (df->bbs_modified, bb->index)
|
||
&& (! blocks || (blocks == (bitmap) -1) || bitmap_bit_p (blocks, bb->index)))
|
||
{
|
||
update = 1;
|
||
break;
|
||
}
|
||
|
||
return update;
|
||
}
|
||
|
||
|
||
/* Analyze dataflow info for the basic blocks specified by the bitmap
|
||
BLOCKS, or for the whole CFG if BLOCKS is zero, or just for the
|
||
modified blocks if BLOCKS is -1. */
|
||
int
|
||
df_analyse (struct df *df, bitmap blocks, int flags)
|
||
{
|
||
int update;
|
||
|
||
/* We could deal with additional basic blocks being created by
|
||
rescanning everything again. */
|
||
if (df->n_bbs && df->n_bbs != (unsigned int) last_basic_block)
|
||
abort ();
|
||
|
||
update = df_modified_p (df, blocks);
|
||
if (update || (flags != df->flags))
|
||
{
|
||
if (! blocks)
|
||
{
|
||
if (df->n_bbs)
|
||
{
|
||
/* Recompute everything from scratch. */
|
||
df_free (df);
|
||
}
|
||
/* Allocate and initialize data structures. */
|
||
df_alloc (df, max_reg_num ());
|
||
df_analyse_1 (df, 0, flags, 0);
|
||
update = 1;
|
||
}
|
||
else
|
||
{
|
||
if (blocks == (bitmap) -1)
|
||
blocks = df->bbs_modified;
|
||
|
||
if (! df->n_bbs)
|
||
abort ();
|
||
|
||
df_analyse_1 (df, blocks, flags, 1);
|
||
bitmap_zero (df->bbs_modified);
|
||
bitmap_zero (df->insns_modified);
|
||
}
|
||
}
|
||
return update;
|
||
}
|
||
|
||
|
||
/* Free all the dataflow info and the DF structure. */
|
||
void
|
||
df_finish (struct df *df)
|
||
{
|
||
df_free (df);
|
||
free (df);
|
||
}
|
||
|
||
|
||
/* Unlink INSN from its reference information. */
|
||
static void
|
||
df_insn_refs_unlink (struct df *df, basic_block bb ATTRIBUTE_UNUSED, rtx insn)
|
||
{
|
||
struct df_link *link;
|
||
unsigned int uid;
|
||
|
||
uid = INSN_UID (insn);
|
||
|
||
/* Unlink all refs defined by this insn. */
|
||
for (link = df->insns[uid].defs; link; link = link->next)
|
||
df_def_unlink (df, link->ref);
|
||
|
||
/* Unlink all refs used by this insn. */
|
||
for (link = df->insns[uid].uses; link; link = link->next)
|
||
df_use_unlink (df, link->ref);
|
||
|
||
df->insns[uid].defs = 0;
|
||
df->insns[uid].uses = 0;
|
||
}
|
||
|
||
|
||
#if 0
|
||
/* Unlink all the insns within BB from their reference information. */
|
||
static void
|
||
df_bb_refs_unlink (struct df *df, basic_block bb)
|
||
{
|
||
rtx insn;
|
||
|
||
/* Scan the block an insn at a time from beginning to end. */
|
||
for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn))
|
||
{
|
||
if (INSN_P (insn))
|
||
{
|
||
/* Unlink refs for INSN. */
|
||
df_insn_refs_unlink (df, bb, insn);
|
||
}
|
||
if (insn == BB_END (bb))
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
/* Unlink all the refs in the basic blocks specified by BLOCKS.
|
||
Not currently used. */
|
||
static void
|
||
df_refs_unlink (struct df *df, bitmap blocks)
|
||
{
|
||
basic_block bb;
|
||
|
||
if (blocks)
|
||
{
|
||
FOR_EACH_BB_IN_BITMAP (blocks, 0, bb,
|
||
{
|
||
df_bb_refs_unlink (df, bb);
|
||
});
|
||
}
|
||
else
|
||
{
|
||
FOR_EACH_BB (bb)
|
||
df_bb_refs_unlink (df, bb);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
/* Functions to modify insns. */
|
||
|
||
|
||
/* Delete INSN and all its reference information. */
|
||
rtx
|
||
df_insn_delete (struct df *df, basic_block bb ATTRIBUTE_UNUSED, rtx insn)
|
||
{
|
||
/* If the insn is a jump, we should perhaps call delete_insn to
|
||
handle the JUMP_LABEL? */
|
||
|
||
/* We should not be deleting the NOTE_INSN_BASIC_BLOCK or label. */
|
||
if (insn == BB_HEAD (bb))
|
||
abort ();
|
||
|
||
/* Delete the insn. */
|
||
delete_insn (insn);
|
||
|
||
df_insn_modify (df, bb, insn);
|
||
|
||
return NEXT_INSN (insn);
|
||
}
|
||
|
||
|
||
/* Mark that INSN within BB may have changed (created/modified/deleted).
|
||
This may be called multiple times for the same insn. There is no
|
||
harm calling this function if the insn wasn't changed; it will just
|
||
slow down the rescanning of refs. */
|
||
void
|
||
df_insn_modify (struct df *df, basic_block bb, rtx insn)
|
||
{
|
||
unsigned int uid;
|
||
|
||
uid = INSN_UID (insn);
|
||
if (uid >= df->insn_size)
|
||
df_insn_table_realloc (df, uid);
|
||
|
||
bitmap_set_bit (df->bbs_modified, bb->index);
|
||
bitmap_set_bit (df->insns_modified, uid);
|
||
|
||
/* For incremental updating on the fly, perhaps we could make a copy
|
||
of all the refs of the original insn and turn them into
|
||
anti-refs. When df_refs_update finds these anti-refs, it annihilates
|
||
the original refs. If validate_change fails then these anti-refs
|
||
will just get ignored. */
|
||
}
|
||
|
||
|
||
typedef struct replace_args
|
||
{
|
||
rtx match;
|
||
rtx replacement;
|
||
rtx insn;
|
||
int modified;
|
||
} replace_args;
|
||
|
||
|
||
/* Replace mem pointed to by PX with its associated pseudo register.
|
||
DATA is actually a pointer to a structure describing the
|
||
instruction currently being scanned and the MEM we are currently
|
||
replacing. */
|
||
static int
|
||
df_rtx_mem_replace (rtx *px, void *data)
|
||
{
|
||
replace_args *args = (replace_args *) data;
|
||
rtx mem = *px;
|
||
|
||
if (mem == NULL_RTX)
|
||
return 0;
|
||
|
||
switch (GET_CODE (mem))
|
||
{
|
||
case MEM:
|
||
break;
|
||
|
||
case CONST_DOUBLE:
|
||
/* We're not interested in the MEM associated with a
|
||
CONST_DOUBLE, so there's no need to traverse into one. */
|
||
return -1;
|
||
|
||
default:
|
||
/* This is not a MEM. */
|
||
return 0;
|
||
}
|
||
|
||
if (!rtx_equal_p (args->match, mem))
|
||
/* This is not the MEM we are currently replacing. */
|
||
return 0;
|
||
|
||
/* Actually replace the MEM. */
|
||
validate_change (args->insn, px, args->replacement, 1);
|
||
args->modified++;
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
int
|
||
df_insn_mem_replace (struct df *df, basic_block bb, rtx insn, rtx mem, rtx reg)
|
||
{
|
||
replace_args args;
|
||
|
||
args.insn = insn;
|
||
args.match = mem;
|
||
args.replacement = reg;
|
||
args.modified = 0;
|
||
|
||
/* Search and replace all matching mems within insn. */
|
||
for_each_rtx (&insn, df_rtx_mem_replace, &args);
|
||
|
||
if (args.modified)
|
||
df_insn_modify (df, bb, insn);
|
||
|
||
/* ???? FIXME. We may have a new def or one or more new uses of REG
|
||
in INSN. REG should be a new pseudo so it won't affect the
|
||
dataflow information that we currently have. We should add
|
||
the new uses and defs to INSN and then recreate the chains
|
||
when df_analyse is called. */
|
||
return args.modified;
|
||
}
|
||
|
||
|
||
/* Replace one register with another. Called through for_each_rtx; PX
|
||
points to the rtx being scanned. DATA is actually a pointer to a
|
||
structure of arguments. */
|
||
static int
|
||
df_rtx_reg_replace (rtx *px, void *data)
|
||
{
|
||
rtx x = *px;
|
||
replace_args *args = (replace_args *) data;
|
||
|
||
if (x == NULL_RTX)
|
||
return 0;
|
||
|
||
if (x == args->match)
|
||
{
|
||
validate_change (args->insn, px, args->replacement, 1);
|
||
args->modified++;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* Replace the reg within every ref on CHAIN that is within the set
|
||
BLOCKS of basic blocks with NEWREG. Also update the regs within
|
||
REG_NOTES. */
|
||
void
|
||
df_refs_reg_replace (struct df *df, bitmap blocks, struct df_link *chain, rtx oldreg, rtx newreg)
|
||
{
|
||
struct df_link *link;
|
||
replace_args args;
|
||
|
||
if (! blocks)
|
||
blocks = df->all_blocks;
|
||
|
||
args.match = oldreg;
|
||
args.replacement = newreg;
|
||
args.modified = 0;
|
||
|
||
for (link = chain; link; link = link->next)
|
||
{
|
||
struct ref *ref = link->ref;
|
||
rtx insn = DF_REF_INSN (ref);
|
||
|
||
if (! INSN_P (insn))
|
||
continue;
|
||
|
||
if (bitmap_bit_p (blocks, DF_REF_BBNO (ref)))
|
||
{
|
||
df_ref_reg_replace (df, ref, oldreg, newreg);
|
||
|
||
/* Replace occurrences of the reg within the REG_NOTES. */
|
||
if ((! link->next || DF_REF_INSN (ref)
|
||
!= DF_REF_INSN (link->next->ref))
|
||
&& REG_NOTES (insn))
|
||
{
|
||
args.insn = insn;
|
||
for_each_rtx (®_NOTES (insn), df_rtx_reg_replace, &args);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* Temporary check to ensure that we have a grip on which
|
||
regs should be replaced. */
|
||
abort ();
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* Replace all occurrences of register OLDREG with register NEWREG in
|
||
blocks defined by bitmap BLOCKS. This also replaces occurrences of
|
||
OLDREG in the REG_NOTES but only for insns containing OLDREG. This
|
||
routine expects the reg-use and reg-def chains to be valid. */
|
||
int
|
||
df_reg_replace (struct df *df, bitmap blocks, rtx oldreg, rtx newreg)
|
||
{
|
||
unsigned int oldregno = REGNO (oldreg);
|
||
|
||
df_refs_reg_replace (df, blocks, df->regs[oldregno].defs, oldreg, newreg);
|
||
df_refs_reg_replace (df, blocks, df->regs[oldregno].uses, oldreg, newreg);
|
||
return 1;
|
||
}
|
||
|
||
|
||
/* Try replacing the reg within REF with NEWREG. Do not modify
|
||
def-use/use-def chains. */
|
||
int
|
||
df_ref_reg_replace (struct df *df, struct ref *ref, rtx oldreg, rtx newreg)
|
||
{
|
||
/* Check that insn was deleted by being converted into a NOTE. If
|
||
so ignore this insn. */
|
||
if (! INSN_P (DF_REF_INSN (ref)))
|
||
return 0;
|
||
|
||
if (oldreg && oldreg != DF_REF_REG (ref))
|
||
abort ();
|
||
|
||
if (! validate_change (DF_REF_INSN (ref), DF_REF_LOC (ref), newreg, 1))
|
||
return 0;
|
||
|
||
df_insn_modify (df, DF_REF_BB (ref), DF_REF_INSN (ref));
|
||
return 1;
|
||
}
|
||
|
||
|
||
struct ref*
|
||
df_bb_def_use_swap (struct df *df, basic_block bb, rtx def_insn, rtx use_insn, unsigned int regno)
|
||
{
|
||
struct ref *def;
|
||
struct ref *use;
|
||
int def_uid;
|
||
int use_uid;
|
||
struct df_link *link;
|
||
|
||
def = df_bb_insn_regno_first_def_find (df, bb, def_insn, regno);
|
||
if (! def)
|
||
return 0;
|
||
|
||
use = df_bb_insn_regno_last_use_find (df, bb, use_insn, regno);
|
||
if (! use)
|
||
return 0;
|
||
|
||
/* The USE no longer exists. */
|
||
use_uid = INSN_UID (use_insn);
|
||
df_use_unlink (df, use);
|
||
df_ref_unlink (&df->insns[use_uid].uses, use);
|
||
|
||
/* The DEF requires shifting so remove it from DEF_INSN
|
||
and add it to USE_INSN by reusing LINK. */
|
||
def_uid = INSN_UID (def_insn);
|
||
link = df_ref_unlink (&df->insns[def_uid].defs, def);
|
||
link->ref = def;
|
||
link->next = df->insns[use_uid].defs;
|
||
df->insns[use_uid].defs = link;
|
||
|
||
#if 0
|
||
link = df_ref_unlink (&df->regs[regno].defs, def);
|
||
link->ref = def;
|
||
link->next = df->regs[regno].defs;
|
||
df->insns[regno].defs = link;
|
||
#endif
|
||
|
||
DF_REF_INSN (def) = use_insn;
|
||
return def;
|
||
}
|
||
|
||
|
||
/* Record df between FIRST_INSN and LAST_INSN inclusive. All new
|
||
insns must be processed by this routine. */
|
||
static void
|
||
df_insns_modify (struct df *df, basic_block bb, rtx first_insn, rtx last_insn)
|
||
{
|
||
rtx insn;
|
||
|
||
for (insn = first_insn; ; insn = NEXT_INSN (insn))
|
||
{
|
||
unsigned int uid;
|
||
|
||
/* A non-const call should not have slipped through the net. If
|
||
it does, we need to create a new basic block. Ouch. The
|
||
same applies for a label. */
|
||
if ((GET_CODE (insn) == CALL_INSN
|
||
&& ! CONST_OR_PURE_CALL_P (insn))
|
||
|| GET_CODE (insn) == CODE_LABEL)
|
||
abort ();
|
||
|
||
uid = INSN_UID (insn);
|
||
|
||
if (uid >= df->insn_size)
|
||
df_insn_table_realloc (df, uid);
|
||
|
||
df_insn_modify (df, bb, insn);
|
||
|
||
if (insn == last_insn)
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
/* Emit PATTERN before INSN within BB. */
|
||
rtx
|
||
df_pattern_emit_before (struct df *df, rtx pattern, basic_block bb, rtx insn)
|
||
{
|
||
rtx ret_insn;
|
||
rtx prev_insn = PREV_INSN (insn);
|
||
|
||
/* We should not be inserting before the start of the block. */
|
||
if (insn == BB_HEAD (bb))
|
||
abort ();
|
||
ret_insn = emit_insn_before (pattern, insn);
|
||
if (ret_insn == insn)
|
||
return ret_insn;
|
||
|
||
df_insns_modify (df, bb, NEXT_INSN (prev_insn), ret_insn);
|
||
return ret_insn;
|
||
}
|
||
|
||
|
||
/* Emit PATTERN after INSN within BB. */
|
||
rtx
|
||
df_pattern_emit_after (struct df *df, rtx pattern, basic_block bb, rtx insn)
|
||
{
|
||
rtx ret_insn;
|
||
|
||
ret_insn = emit_insn_after (pattern, insn);
|
||
if (ret_insn == insn)
|
||
return ret_insn;
|
||
|
||
df_insns_modify (df, bb, NEXT_INSN (insn), ret_insn);
|
||
return ret_insn;
|
||
}
|
||
|
||
|
||
/* Emit jump PATTERN after INSN within BB. */
|
||
rtx
|
||
df_jump_pattern_emit_after (struct df *df, rtx pattern, basic_block bb, rtx insn)
|
||
{
|
||
rtx ret_insn;
|
||
|
||
ret_insn = emit_jump_insn_after (pattern, insn);
|
||
if (ret_insn == insn)
|
||
return ret_insn;
|
||
|
||
df_insns_modify (df, bb, NEXT_INSN (insn), ret_insn);
|
||
return ret_insn;
|
||
}
|
||
|
||
|
||
/* Move INSN within BB before BEFORE_INSN within BEFORE_BB.
|
||
|
||
This function should only be used to move loop invariant insns
|
||
out of a loop where it has been proven that the def-use info
|
||
will still be valid. */
|
||
rtx
|
||
df_insn_move_before (struct df *df, basic_block bb, rtx insn, basic_block before_bb, rtx before_insn)
|
||
{
|
||
struct df_link *link;
|
||
unsigned int uid;
|
||
|
||
if (! bb)
|
||
return df_pattern_emit_before (df, insn, before_bb, before_insn);
|
||
|
||
uid = INSN_UID (insn);
|
||
|
||
/* Change bb for all df defined and used by this insn. */
|
||
for (link = df->insns[uid].defs; link; link = link->next)
|
||
DF_REF_BB (link->ref) = before_bb;
|
||
for (link = df->insns[uid].uses; link; link = link->next)
|
||
DF_REF_BB (link->ref) = before_bb;
|
||
|
||
/* The lifetimes of the registers used in this insn will be reduced
|
||
while the lifetimes of the registers defined in this insn
|
||
are likely to be increased. */
|
||
|
||
/* ???? Perhaps all the insns moved should be stored on a list
|
||
which df_analyse removes when it recalculates data flow. */
|
||
|
||
return emit_insn_before (insn, before_insn);
|
||
}
|
||
|
||
/* Functions to query dataflow information. */
|
||
|
||
|
||
int
|
||
df_insn_regno_def_p (struct df *df, basic_block bb ATTRIBUTE_UNUSED,
|
||
rtx insn, unsigned int regno)
|
||
{
|
||
unsigned int uid;
|
||
struct df_link *link;
|
||
|
||
uid = INSN_UID (insn);
|
||
|
||
for (link = df->insns[uid].defs; link; link = link->next)
|
||
{
|
||
struct ref *def = link->ref;
|
||
|
||
if (DF_REF_REGNO (def) == regno)
|
||
return 1;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
static int
|
||
df_def_dominates_all_uses_p (struct df *df ATTRIBUTE_UNUSED, struct ref *def)
|
||
{
|
||
struct df_link *du_link;
|
||
|
||
/* Follow def-use chain to find all the uses of this def. */
|
||
for (du_link = DF_REF_CHAIN (def); du_link; du_link = du_link->next)
|
||
{
|
||
struct ref *use = du_link->ref;
|
||
struct df_link *ud_link;
|
||
|
||
/* Follow use-def chain to check all the defs for this use. */
|
||
for (ud_link = DF_REF_CHAIN (use); ud_link; ud_link = ud_link->next)
|
||
if (ud_link->ref != def)
|
||
return 0;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
|
||
int
|
||
df_insn_dominates_all_uses_p (struct df *df, basic_block bb ATTRIBUTE_UNUSED,
|
||
rtx insn)
|
||
{
|
||
unsigned int uid;
|
||
struct df_link *link;
|
||
|
||
uid = INSN_UID (insn);
|
||
|
||
for (link = df->insns[uid].defs; link; link = link->next)
|
||
{
|
||
struct ref *def = link->ref;
|
||
|
||
if (! df_def_dominates_all_uses_p (df, def))
|
||
return 0;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
|
||
/* Return nonzero if all DF dominates all the uses within the bitmap
|
||
BLOCKS. */
|
||
static int
|
||
df_def_dominates_uses_p (struct df *df ATTRIBUTE_UNUSED, struct ref *def,
|
||
bitmap blocks)
|
||
{
|
||
struct df_link *du_link;
|
||
|
||
/* Follow def-use chain to find all the uses of this def. */
|
||
for (du_link = DF_REF_CHAIN (def); du_link; du_link = du_link->next)
|
||
{
|
||
struct ref *use = du_link->ref;
|
||
struct df_link *ud_link;
|
||
|
||
/* Only worry about the uses within BLOCKS. For example,
|
||
consider a register defined within a loop that is live at the
|
||
loop exits. */
|
||
if (bitmap_bit_p (blocks, DF_REF_BBNO (use)))
|
||
{
|
||
/* Follow use-def chain to check all the defs for this use. */
|
||
for (ud_link = DF_REF_CHAIN (use); ud_link; ud_link = ud_link->next)
|
||
if (ud_link->ref != def)
|
||
return 0;
|
||
}
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
|
||
/* Return nonzero if all the defs of INSN within BB dominates
|
||
all the corresponding uses. */
|
||
int
|
||
df_insn_dominates_uses_p (struct df *df, basic_block bb ATTRIBUTE_UNUSED,
|
||
rtx insn, bitmap blocks)
|
||
{
|
||
unsigned int uid;
|
||
struct df_link *link;
|
||
|
||
uid = INSN_UID (insn);
|
||
|
||
for (link = df->insns[uid].defs; link; link = link->next)
|
||
{
|
||
struct ref *def = link->ref;
|
||
|
||
/* Only consider the defs within BLOCKS. */
|
||
if (bitmap_bit_p (blocks, DF_REF_BBNO (def))
|
||
&& ! df_def_dominates_uses_p (df, def, blocks))
|
||
return 0;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
|
||
/* Return the basic block that REG referenced in or NULL if referenced
|
||
in multiple basic blocks. */
|
||
basic_block
|
||
df_regno_bb (struct df *df, unsigned int regno)
|
||
{
|
||
struct df_link *defs = df->regs[regno].defs;
|
||
struct df_link *uses = df->regs[regno].uses;
|
||
struct ref *def = defs ? defs->ref : 0;
|
||
struct ref *use = uses ? uses->ref : 0;
|
||
basic_block bb_def = def ? DF_REF_BB (def) : 0;
|
||
basic_block bb_use = use ? DF_REF_BB (use) : 0;
|
||
|
||
/* Compare blocks of first def and last use. ???? FIXME. What if
|
||
the reg-def and reg-use lists are not correctly ordered. */
|
||
return bb_def == bb_use ? bb_def : 0;
|
||
}
|
||
|
||
|
||
/* Return nonzero if REG used in multiple basic blocks. */
|
||
int
|
||
df_reg_global_p (struct df *df, rtx reg)
|
||
{
|
||
return df_regno_bb (df, REGNO (reg)) != 0;
|
||
}
|
||
|
||
|
||
/* Return total lifetime (in insns) of REG. */
|
||
int
|
||
df_reg_lifetime (struct df *df, rtx reg)
|
||
{
|
||
return df->regs[REGNO (reg)].lifetime;
|
||
}
|
||
|
||
|
||
/* Return nonzero if REG live at start of BB. */
|
||
int
|
||
df_bb_reg_live_start_p (struct df *df, basic_block bb, rtx reg)
|
||
{
|
||
struct bb_info *bb_info = DF_BB_INFO (df, bb);
|
||
|
||
#ifdef ENABLE_CHECKING
|
||
if (! bb_info->lr_in)
|
||
abort ();
|
||
#endif
|
||
|
||
return bitmap_bit_p (bb_info->lr_in, REGNO (reg));
|
||
}
|
||
|
||
|
||
/* Return nonzero if REG live at end of BB. */
|
||
int
|
||
df_bb_reg_live_end_p (struct df *df, basic_block bb, rtx reg)
|
||
{
|
||
struct bb_info *bb_info = DF_BB_INFO (df, bb);
|
||
|
||
#ifdef ENABLE_CHECKING
|
||
if (! bb_info->lr_in)
|
||
abort ();
|
||
#endif
|
||
|
||
return bitmap_bit_p (bb_info->lr_out, REGNO (reg));
|
||
}
|
||
|
||
|
||
/* Return -1 if life of REG1 before life of REG2, 1 if life of REG1
|
||
after life of REG2, or 0, if the lives overlap. */
|
||
int
|
||
df_bb_regs_lives_compare (struct df *df, basic_block bb, rtx reg1, rtx reg2)
|
||
{
|
||
unsigned int regno1 = REGNO (reg1);
|
||
unsigned int regno2 = REGNO (reg2);
|
||
struct ref *def1;
|
||
struct ref *use1;
|
||
struct ref *def2;
|
||
struct ref *use2;
|
||
|
||
|
||
/* The regs must be local to BB. */
|
||
if (df_regno_bb (df, regno1) != bb
|
||
|| df_regno_bb (df, regno2) != bb)
|
||
abort ();
|
||
|
||
def2 = df_bb_regno_first_def_find (df, bb, regno2);
|
||
use1 = df_bb_regno_last_use_find (df, bb, regno1);
|
||
|
||
if (DF_INSN_LUID (df, DF_REF_INSN (def2))
|
||
> DF_INSN_LUID (df, DF_REF_INSN (use1)))
|
||
return -1;
|
||
|
||
def1 = df_bb_regno_first_def_find (df, bb, regno1);
|
||
use2 = df_bb_regno_last_use_find (df, bb, regno2);
|
||
|
||
if (DF_INSN_LUID (df, DF_REF_INSN (def1))
|
||
> DF_INSN_LUID (df, DF_REF_INSN (use2)))
|
||
return 1;
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* Return last use of REGNO within BB. */
|
||
static struct ref *
|
||
df_bb_regno_last_use_find (struct df *df, basic_block bb, unsigned int regno)
|
||
{
|
||
struct df_link *link;
|
||
|
||
/* This assumes that the reg-use list is ordered such that for any
|
||
BB, the last use is found first. However, since the BBs are not
|
||
ordered, the first use in the chain is not necessarily the last
|
||
use in the function. */
|
||
for (link = df->regs[regno].uses; link; link = link->next)
|
||
{
|
||
struct ref *use = link->ref;
|
||
|
||
if (DF_REF_BB (use) == bb)
|
||
return use;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* Return first def of REGNO within BB. */
|
||
static struct ref *
|
||
df_bb_regno_first_def_find (struct df *df, basic_block bb, unsigned int regno)
|
||
{
|
||
struct df_link *link;
|
||
|
||
/* This assumes that the reg-def list is ordered such that for any
|
||
BB, the first def is found first. However, since the BBs are not
|
||
ordered, the first def in the chain is not necessarily the first
|
||
def in the function. */
|
||
for (link = df->regs[regno].defs; link; link = link->next)
|
||
{
|
||
struct ref *def = link->ref;
|
||
|
||
if (DF_REF_BB (def) == bb)
|
||
return def;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* Return first use of REGNO inside INSN within BB. */
|
||
static struct ref *
|
||
df_bb_insn_regno_last_use_find (struct df *df,
|
||
basic_block bb ATTRIBUTE_UNUSED, rtx insn,
|
||
unsigned int regno)
|
||
{
|
||
unsigned int uid;
|
||
struct df_link *link;
|
||
|
||
uid = INSN_UID (insn);
|
||
|
||
for (link = df->insns[uid].uses; link; link = link->next)
|
||
{
|
||
struct ref *use = link->ref;
|
||
|
||
if (DF_REF_REGNO (use) == regno)
|
||
return use;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* Return first def of REGNO inside INSN within BB. */
|
||
static struct ref *
|
||
df_bb_insn_regno_first_def_find (struct df *df,
|
||
basic_block bb ATTRIBUTE_UNUSED, rtx insn,
|
||
unsigned int regno)
|
||
{
|
||
unsigned int uid;
|
||
struct df_link *link;
|
||
|
||
uid = INSN_UID (insn);
|
||
|
||
for (link = df->insns[uid].defs; link; link = link->next)
|
||
{
|
||
struct ref *def = link->ref;
|
||
|
||
if (DF_REF_REGNO (def) == regno)
|
||
return def;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* Return insn using REG if the BB contains only a single
|
||
use and def of REG. */
|
||
rtx
|
||
df_bb_single_def_use_insn_find (struct df *df, basic_block bb, rtx insn, rtx reg)
|
||
{
|
||
struct ref *def;
|
||
struct ref *use;
|
||
struct df_link *du_link;
|
||
|
||
def = df_bb_insn_regno_first_def_find (df, bb, insn, REGNO (reg));
|
||
|
||
if (! def)
|
||
abort ();
|
||
|
||
du_link = DF_REF_CHAIN (def);
|
||
|
||
if (! du_link)
|
||
return NULL_RTX;
|
||
|
||
use = du_link->ref;
|
||
|
||
/* Check if def is dead. */
|
||
if (! use)
|
||
return NULL_RTX;
|
||
|
||
/* Check for multiple uses. */
|
||
if (du_link->next)
|
||
return NULL_RTX;
|
||
|
||
return DF_REF_INSN (use);
|
||
}
|
||
|
||
/* Functions for debugging/dumping dataflow information. */
|
||
|
||
|
||
/* Dump a def-use or use-def chain for REF to FILE. */
|
||
static void
|
||
df_chain_dump (struct df_link *link, FILE *file)
|
||
{
|
||
fprintf (file, "{ ");
|
||
for (; link; link = link->next)
|
||
{
|
||
fprintf (file, "%c%d ",
|
||
DF_REF_REG_DEF_P (link->ref) ? 'd' : 'u',
|
||
DF_REF_ID (link->ref));
|
||
}
|
||
fprintf (file, "}");
|
||
}
|
||
|
||
|
||
/* Dump a chain of refs with the associated regno. */
|
||
static void
|
||
df_chain_dump_regno (struct df_link *link, FILE *file)
|
||
{
|
||
fprintf (file, "{ ");
|
||
for (; link; link = link->next)
|
||
{
|
||
fprintf (file, "%c%d(%d) ",
|
||
DF_REF_REG_DEF_P (link->ref) ? 'd' : 'u',
|
||
DF_REF_ID (link->ref),
|
||
DF_REF_REGNO (link->ref));
|
||
}
|
||
fprintf (file, "}");
|
||
}
|
||
|
||
|
||
/* Dump dataflow info. */
|
||
void
|
||
df_dump (struct df *df, int flags, FILE *file)
|
||
{
|
||
unsigned int j;
|
||
basic_block bb;
|
||
|
||
if (! df || ! file)
|
||
return;
|
||
|
||
fprintf (file, "\nDataflow summary:\n");
|
||
fprintf (file, "n_regs = %d, n_defs = %d, n_uses = %d, n_bbs = %d\n",
|
||
df->n_regs, df->n_defs, df->n_uses, df->n_bbs);
|
||
|
||
if (flags & DF_RD)
|
||
{
|
||
basic_block bb;
|
||
|
||
fprintf (file, "Reaching defs:\n");
|
||
FOR_EACH_BB (bb)
|
||
{
|
||
struct bb_info *bb_info = DF_BB_INFO (df, bb);
|
||
|
||
if (! bb_info->rd_in)
|
||
continue;
|
||
|
||
fprintf (file, "bb %d in \t", bb->index);
|
||
dump_bitmap (file, bb_info->rd_in);
|
||
fprintf (file, "bb %d gen \t", bb->index);
|
||
dump_bitmap (file, bb_info->rd_gen);
|
||
fprintf (file, "bb %d kill\t", bb->index);
|
||
dump_bitmap (file, bb_info->rd_kill);
|
||
fprintf (file, "bb %d out \t", bb->index);
|
||
dump_bitmap (file, bb_info->rd_out);
|
||
}
|
||
}
|
||
|
||
if (flags & DF_UD_CHAIN)
|
||
{
|
||
fprintf (file, "Use-def chains:\n");
|
||
for (j = 0; j < df->n_defs; j++)
|
||
{
|
||
if (df->defs[j])
|
||
{
|
||
fprintf (file, "d%d bb %d luid %d insn %d reg %d ",
|
||
j, DF_REF_BBNO (df->defs[j]),
|
||
DF_INSN_LUID (df, DF_REF_INSN (df->defs[j])),
|
||
DF_REF_INSN_UID (df->defs[j]),
|
||
DF_REF_REGNO (df->defs[j]));
|
||
if (df->defs[j]->flags & DF_REF_READ_WRITE)
|
||
fprintf (file, "read/write ");
|
||
df_chain_dump (DF_REF_CHAIN (df->defs[j]), file);
|
||
fprintf (file, "\n");
|
||
}
|
||
}
|
||
}
|
||
|
||
if (flags & DF_RU)
|
||
{
|
||
fprintf (file, "Reaching uses:\n");
|
||
FOR_EACH_BB (bb)
|
||
{
|
||
struct bb_info *bb_info = DF_BB_INFO (df, bb);
|
||
|
||
if (! bb_info->ru_in)
|
||
continue;
|
||
|
||
fprintf (file, "bb %d in \t", bb->index);
|
||
dump_bitmap (file, bb_info->ru_in);
|
||
fprintf (file, "bb %d gen \t", bb->index);
|
||
dump_bitmap (file, bb_info->ru_gen);
|
||
fprintf (file, "bb %d kill\t", bb->index);
|
||
dump_bitmap (file, bb_info->ru_kill);
|
||
fprintf (file, "bb %d out \t", bb->index);
|
||
dump_bitmap (file, bb_info->ru_out);
|
||
}
|
||
}
|
||
|
||
if (flags & DF_DU_CHAIN)
|
||
{
|
||
fprintf (file, "Def-use chains:\n");
|
||
for (j = 0; j < df->n_uses; j++)
|
||
{
|
||
if (df->uses[j])
|
||
{
|
||
fprintf (file, "u%d bb %d luid %d insn %d reg %d ",
|
||
j, DF_REF_BBNO (df->uses[j]),
|
||
DF_INSN_LUID (df, DF_REF_INSN (df->uses[j])),
|
||
DF_REF_INSN_UID (df->uses[j]),
|
||
DF_REF_REGNO (df->uses[j]));
|
||
if (df->uses[j]->flags & DF_REF_READ_WRITE)
|
||
fprintf (file, "read/write ");
|
||
df_chain_dump (DF_REF_CHAIN (df->uses[j]), file);
|
||
fprintf (file, "\n");
|
||
}
|
||
}
|
||
}
|
||
|
||
if (flags & DF_LR)
|
||
{
|
||
fprintf (file, "Live regs:\n");
|
||
FOR_EACH_BB (bb)
|
||
{
|
||
struct bb_info *bb_info = DF_BB_INFO (df, bb);
|
||
|
||
if (! bb_info->lr_in)
|
||
continue;
|
||
|
||
fprintf (file, "bb %d in \t", bb->index);
|
||
dump_bitmap (file, bb_info->lr_in);
|
||
fprintf (file, "bb %d use \t", bb->index);
|
||
dump_bitmap (file, bb_info->lr_use);
|
||
fprintf (file, "bb %d def \t", bb->index);
|
||
dump_bitmap (file, bb_info->lr_def);
|
||
fprintf (file, "bb %d out \t", bb->index);
|
||
dump_bitmap (file, bb_info->lr_out);
|
||
}
|
||
}
|
||
|
||
if (flags & (DF_REG_INFO | DF_RD_CHAIN | DF_RU_CHAIN))
|
||
{
|
||
struct reg_info *reg_info = df->regs;
|
||
|
||
fprintf (file, "Register info:\n");
|
||
for (j = 0; j < df->n_regs; j++)
|
||
{
|
||
if (((flags & DF_REG_INFO)
|
||
&& (reg_info[j].n_uses || reg_info[j].n_defs))
|
||
|| ((flags & DF_RD_CHAIN) && reg_info[j].defs)
|
||
|| ((flags & DF_RU_CHAIN) && reg_info[j].uses))
|
||
{
|
||
fprintf (file, "reg %d", j);
|
||
if ((flags & DF_RD_CHAIN) && (flags & DF_RU_CHAIN))
|
||
{
|
||
basic_block bb = df_regno_bb (df, j);
|
||
|
||
if (bb)
|
||
fprintf (file, " bb %d", bb->index);
|
||
else
|
||
fprintf (file, " bb ?");
|
||
}
|
||
if (flags & DF_REG_INFO)
|
||
{
|
||
fprintf (file, " life %d", reg_info[j].lifetime);
|
||
}
|
||
|
||
if ((flags & DF_REG_INFO) || (flags & DF_RD_CHAIN))
|
||
{
|
||
fprintf (file, " defs ");
|
||
if (flags & DF_REG_INFO)
|
||
fprintf (file, "%d ", reg_info[j].n_defs);
|
||
if (flags & DF_RD_CHAIN)
|
||
df_chain_dump (reg_info[j].defs, file);
|
||
}
|
||
|
||
if ((flags & DF_REG_INFO) || (flags & DF_RU_CHAIN))
|
||
{
|
||
fprintf (file, " uses ");
|
||
if (flags & DF_REG_INFO)
|
||
fprintf (file, "%d ", reg_info[j].n_uses);
|
||
if (flags & DF_RU_CHAIN)
|
||
df_chain_dump (reg_info[j].uses, file);
|
||
}
|
||
|
||
fprintf (file, "\n");
|
||
}
|
||
}
|
||
}
|
||
fprintf (file, "\n");
|
||
}
|
||
|
||
|
||
void
|
||
df_insn_debug (struct df *df, rtx insn, FILE *file)
|
||
{
|
||
unsigned int uid;
|
||
int bbi;
|
||
|
||
uid = INSN_UID (insn);
|
||
if (uid >= df->insn_size)
|
||
return;
|
||
|
||
if (df->insns[uid].defs)
|
||
bbi = DF_REF_BBNO (df->insns[uid].defs->ref);
|
||
else if (df->insns[uid].uses)
|
||
bbi = DF_REF_BBNO (df->insns[uid].uses->ref);
|
||
else
|
||
bbi = -1;
|
||
|
||
fprintf (file, "insn %d bb %d luid %d defs ",
|
||
uid, bbi, DF_INSN_LUID (df, insn));
|
||
df_chain_dump (df->insns[uid].defs, file);
|
||
fprintf (file, " uses ");
|
||
df_chain_dump (df->insns[uid].uses, file);
|
||
fprintf (file, "\n");
|
||
}
|
||
|
||
|
||
void
|
||
df_insn_debug_regno (struct df *df, rtx insn, FILE *file)
|
||
{
|
||
unsigned int uid;
|
||
int bbi;
|
||
|
||
uid = INSN_UID (insn);
|
||
if (uid >= df->insn_size)
|
||
return;
|
||
|
||
if (df->insns[uid].defs)
|
||
bbi = DF_REF_BBNO (df->insns[uid].defs->ref);
|
||
else if (df->insns[uid].uses)
|
||
bbi = DF_REF_BBNO (df->insns[uid].uses->ref);
|
||
else
|
||
bbi = -1;
|
||
|
||
fprintf (file, "insn %d bb %d luid %d defs ",
|
||
uid, bbi, DF_INSN_LUID (df, insn));
|
||
df_chain_dump_regno (df->insns[uid].defs, file);
|
||
fprintf (file, " uses ");
|
||
df_chain_dump_regno (df->insns[uid].uses, file);
|
||
fprintf (file, "\n");
|
||
}
|
||
|
||
|
||
static void
|
||
df_regno_debug (struct df *df, unsigned int regno, FILE *file)
|
||
{
|
||
if (regno >= df->reg_size)
|
||
return;
|
||
|
||
fprintf (file, "reg %d life %d defs ",
|
||
regno, df->regs[regno].lifetime);
|
||
df_chain_dump (df->regs[regno].defs, file);
|
||
fprintf (file, " uses ");
|
||
df_chain_dump (df->regs[regno].uses, file);
|
||
fprintf (file, "\n");
|
||
}
|
||
|
||
|
||
static void
|
||
df_ref_debug (struct df *df, struct ref *ref, FILE *file)
|
||
{
|
||
fprintf (file, "%c%d ",
|
||
DF_REF_REG_DEF_P (ref) ? 'd' : 'u',
|
||
DF_REF_ID (ref));
|
||
fprintf (file, "reg %d bb %d luid %d insn %d chain ",
|
||
DF_REF_REGNO (ref),
|
||
DF_REF_BBNO (ref),
|
||
DF_INSN_LUID (df, DF_REF_INSN (ref)),
|
||
INSN_UID (DF_REF_INSN (ref)));
|
||
df_chain_dump (DF_REF_CHAIN (ref), file);
|
||
fprintf (file, "\n");
|
||
}
|
||
|
||
/* Functions for debugging from GDB. */
|
||
|
||
void
|
||
debug_df_insn (rtx insn)
|
||
{
|
||
df_insn_debug (ddf, insn, stderr);
|
||
debug_rtx (insn);
|
||
}
|
||
|
||
|
||
void
|
||
debug_df_reg (rtx reg)
|
||
{
|
||
df_regno_debug (ddf, REGNO (reg), stderr);
|
||
}
|
||
|
||
|
||
void
|
||
debug_df_regno (unsigned int regno)
|
||
{
|
||
df_regno_debug (ddf, regno, stderr);
|
||
}
|
||
|
||
|
||
void
|
||
debug_df_ref (struct ref *ref)
|
||
{
|
||
df_ref_debug (ddf, ref, stderr);
|
||
}
|
||
|
||
|
||
void
|
||
debug_df_defno (unsigned int defno)
|
||
{
|
||
df_ref_debug (ddf, ddf->defs[defno], stderr);
|
||
}
|
||
|
||
|
||
void
|
||
debug_df_useno (unsigned int defno)
|
||
{
|
||
df_ref_debug (ddf, ddf->uses[defno], stderr);
|
||
}
|
||
|
||
|
||
void
|
||
debug_df_chain (struct df_link *link)
|
||
{
|
||
df_chain_dump (link, stderr);
|
||
fputc ('\n', stderr);
|
||
}
|
||
|
||
|
||
/* Hybrid search algorithm from "Implementation Techniques for
|
||
Efficient Data-Flow Analysis of Large Programs". */
|
||
static void
|
||
hybrid_search_bitmap (basic_block block, bitmap *in, bitmap *out, bitmap *gen,
|
||
bitmap *kill, enum df_flow_dir dir,
|
||
enum df_confluence_op conf_op,
|
||
transfer_function_bitmap transfun, sbitmap visited,
|
||
sbitmap pending, void *data)
|
||
{
|
||
int changed;
|
||
int i = block->index;
|
||
edge e;
|
||
basic_block bb = block;
|
||
|
||
SET_BIT (visited, block->index);
|
||
if (TEST_BIT (pending, block->index))
|
||
{
|
||
if (dir == DF_FORWARD)
|
||
{
|
||
/* Calculate <conf_op> of predecessor_outs. */
|
||
bitmap_zero (in[i]);
|
||
for (e = bb->pred; e != 0; e = e->pred_next)
|
||
{
|
||
if (e->src == ENTRY_BLOCK_PTR)
|
||
continue;
|
||
switch (conf_op)
|
||
{
|
||
case DF_UNION:
|
||
bitmap_a_or_b (in[i], in[i], out[e->src->index]);
|
||
break;
|
||
case DF_INTERSECTION:
|
||
bitmap_a_and_b (in[i], in[i], out[e->src->index]);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* Calculate <conf_op> of successor ins. */
|
||
bitmap_zero (out[i]);
|
||
for (e = bb->succ; e != 0; e = e->succ_next)
|
||
{
|
||
if (e->dest == EXIT_BLOCK_PTR)
|
||
continue;
|
||
switch (conf_op)
|
||
{
|
||
case DF_UNION:
|
||
bitmap_a_or_b (out[i], out[i], in[e->dest->index]);
|
||
break;
|
||
case DF_INTERSECTION:
|
||
bitmap_a_and_b (out[i], out[i], in[e->dest->index]);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
/* Common part */
|
||
(*transfun)(i, &changed, in[i], out[i], gen[i], kill[i], data);
|
||
RESET_BIT (pending, i);
|
||
if (changed)
|
||
{
|
||
if (dir == DF_FORWARD)
|
||
{
|
||
for (e = bb->succ; e != 0; e = e->succ_next)
|
||
{
|
||
if (e->dest == EXIT_BLOCK_PTR || e->dest->index == i)
|
||
continue;
|
||
SET_BIT (pending, e->dest->index);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (e = bb->pred; e != 0; e = e->pred_next)
|
||
{
|
||
if (e->src == ENTRY_BLOCK_PTR || e->dest->index == i)
|
||
continue;
|
||
SET_BIT (pending, e->src->index);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (dir == DF_FORWARD)
|
||
{
|
||
for (e = bb->succ; e != 0; e = e->succ_next)
|
||
{
|
||
if (e->dest == EXIT_BLOCK_PTR || e->dest->index == i)
|
||
continue;
|
||
if (!TEST_BIT (visited, e->dest->index))
|
||
hybrid_search_bitmap (e->dest, in, out, gen, kill, dir,
|
||
conf_op, transfun, visited, pending,
|
||
data);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (e = bb->pred; e != 0; e = e->pred_next)
|
||
{
|
||
if (e->src == ENTRY_BLOCK_PTR || e->src->index == i)
|
||
continue;
|
||
if (!TEST_BIT (visited, e->src->index))
|
||
hybrid_search_bitmap (e->src, in, out, gen, kill, dir,
|
||
conf_op, transfun, visited, pending,
|
||
data);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* Hybrid search for sbitmaps, rather than bitmaps. */
|
||
static void
|
||
hybrid_search_sbitmap (basic_block block, sbitmap *in, sbitmap *out,
|
||
sbitmap *gen, sbitmap *kill, enum df_flow_dir dir,
|
||
enum df_confluence_op conf_op,
|
||
transfer_function_sbitmap transfun, sbitmap visited,
|
||
sbitmap pending, void *data)
|
||
{
|
||
int changed;
|
||
int i = block->index;
|
||
edge e;
|
||
basic_block bb = block;
|
||
|
||
SET_BIT (visited, block->index);
|
||
if (TEST_BIT (pending, block->index))
|
||
{
|
||
if (dir == DF_FORWARD)
|
||
{
|
||
/* Calculate <conf_op> of predecessor_outs. */
|
||
sbitmap_zero (in[i]);
|
||
for (e = bb->pred; e != 0; e = e->pred_next)
|
||
{
|
||
if (e->src == ENTRY_BLOCK_PTR)
|
||
continue;
|
||
switch (conf_op)
|
||
{
|
||
case DF_UNION:
|
||
sbitmap_a_or_b (in[i], in[i], out[e->src->index]);
|
||
break;
|
||
case DF_INTERSECTION:
|
||
sbitmap_a_and_b (in[i], in[i], out[e->src->index]);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* Calculate <conf_op> of successor ins. */
|
||
sbitmap_zero (out[i]);
|
||
for (e = bb->succ; e != 0; e = e->succ_next)
|
||
{
|
||
if (e->dest == EXIT_BLOCK_PTR)
|
||
continue;
|
||
switch (conf_op)
|
||
{
|
||
case DF_UNION:
|
||
sbitmap_a_or_b (out[i], out[i], in[e->dest->index]);
|
||
break;
|
||
case DF_INTERSECTION:
|
||
sbitmap_a_and_b (out[i], out[i], in[e->dest->index]);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
/* Common part. */
|
||
(*transfun)(i, &changed, in[i], out[i], gen[i], kill[i], data);
|
||
RESET_BIT (pending, i);
|
||
if (changed)
|
||
{
|
||
if (dir == DF_FORWARD)
|
||
{
|
||
for (e = bb->succ; e != 0; e = e->succ_next)
|
||
{
|
||
if (e->dest == EXIT_BLOCK_PTR || e->dest->index == i)
|
||
continue;
|
||
SET_BIT (pending, e->dest->index);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (e = bb->pred; e != 0; e = e->pred_next)
|
||
{
|
||
if (e->src == ENTRY_BLOCK_PTR || e->dest->index == i)
|
||
continue;
|
||
SET_BIT (pending, e->src->index);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (dir == DF_FORWARD)
|
||
{
|
||
for (e = bb->succ; e != 0; e = e->succ_next)
|
||
{
|
||
if (e->dest == EXIT_BLOCK_PTR || e->dest->index == i)
|
||
continue;
|
||
if (!TEST_BIT (visited, e->dest->index))
|
||
hybrid_search_sbitmap (e->dest, in, out, gen, kill, dir,
|
||
conf_op, transfun, visited, pending,
|
||
data);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (e = bb->pred; e != 0; e = e->pred_next)
|
||
{
|
||
if (e->src == ENTRY_BLOCK_PTR || e->src->index == i)
|
||
continue;
|
||
if (!TEST_BIT (visited, e->src->index))
|
||
hybrid_search_sbitmap (e->src, in, out, gen, kill, dir,
|
||
conf_op, transfun, visited, pending,
|
||
data);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* gen = GEN set.
|
||
kill = KILL set.
|
||
in, out = Filled in by function.
|
||
blocks = Blocks to analyze.
|
||
dir = Dataflow direction.
|
||
conf_op = Confluence operation.
|
||
transfun = Transfer function.
|
||
order = Order to iterate in. (Should map block numbers -> order)
|
||
data = Whatever you want. It's passed to the transfer function.
|
||
|
||
This function will perform iterative bitvector dataflow, producing
|
||
the in and out sets. Even if you only want to perform it for a
|
||
small number of blocks, the vectors for in and out must be large
|
||
enough for *all* blocks, because changing one block might affect
|
||
others. However, it'll only put what you say to analyze on the
|
||
initial worklist.
|
||
|
||
For forward problems, you probably want to pass in a mapping of
|
||
block number to rc_order (like df->inverse_rc_map).
|
||
*/
|
||
void
|
||
iterative_dataflow_sbitmap (sbitmap *in, sbitmap *out, sbitmap *gen,
|
||
sbitmap *kill, bitmap blocks,
|
||
enum df_flow_dir dir,
|
||
enum df_confluence_op conf_op,
|
||
transfer_function_sbitmap transfun, int *order,
|
||
void *data)
|
||
{
|
||
int i;
|
||
fibheap_t worklist;
|
||
basic_block bb;
|
||
sbitmap visited, pending;
|
||
|
||
pending = sbitmap_alloc (last_basic_block);
|
||
visited = sbitmap_alloc (last_basic_block);
|
||
sbitmap_zero (pending);
|
||
sbitmap_zero (visited);
|
||
worklist = fibheap_new ();
|
||
|
||
EXECUTE_IF_SET_IN_BITMAP (blocks, 0, i,
|
||
{
|
||
fibheap_insert (worklist, order[i], (void *) (size_t) i);
|
||
SET_BIT (pending, i);
|
||
if (dir == DF_FORWARD)
|
||
sbitmap_copy (out[i], gen[i]);
|
||
else
|
||
sbitmap_copy (in[i], gen[i]);
|
||
});
|
||
|
||
while (sbitmap_first_set_bit (pending) != -1)
|
||
{
|
||
while (!fibheap_empty (worklist))
|
||
{
|
||
i = (size_t) fibheap_extract_min (worklist);
|
||
bb = BASIC_BLOCK (i);
|
||
if (!TEST_BIT (visited, bb->index))
|
||
hybrid_search_sbitmap (bb, in, out, gen, kill, dir,
|
||
conf_op, transfun, visited, pending, data);
|
||
}
|
||
|
||
if (sbitmap_first_set_bit (pending) != -1)
|
||
{
|
||
EXECUTE_IF_SET_IN_BITMAP (blocks, 0, i,
|
||
{
|
||
fibheap_insert (worklist, order[i], (void *) (size_t) i);
|
||
});
|
||
sbitmap_zero (visited);
|
||
}
|
||
else
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
sbitmap_free (pending);
|
||
sbitmap_free (visited);
|
||
fibheap_delete (worklist);
|
||
}
|
||
|
||
|
||
/* Exactly the same as iterative_dataflow_sbitmap, except it works on
|
||
bitmaps instead. */
|
||
void
|
||
iterative_dataflow_bitmap (bitmap *in, bitmap *out, bitmap *gen, bitmap *kill,
|
||
bitmap blocks, enum df_flow_dir dir,
|
||
enum df_confluence_op conf_op,
|
||
transfer_function_bitmap transfun, int *order,
|
||
void *data)
|
||
{
|
||
int i;
|
||
fibheap_t worklist;
|
||
basic_block bb;
|
||
sbitmap visited, pending;
|
||
|
||
pending = sbitmap_alloc (last_basic_block);
|
||
visited = sbitmap_alloc (last_basic_block);
|
||
sbitmap_zero (pending);
|
||
sbitmap_zero (visited);
|
||
worklist = fibheap_new ();
|
||
|
||
EXECUTE_IF_SET_IN_BITMAP (blocks, 0, i,
|
||
{
|
||
fibheap_insert (worklist, order[i], (void *) (size_t) i);
|
||
SET_BIT (pending, i);
|
||
if (dir == DF_FORWARD)
|
||
bitmap_copy (out[i], gen[i]);
|
||
else
|
||
bitmap_copy (in[i], gen[i]);
|
||
});
|
||
|
||
while (sbitmap_first_set_bit (pending) != -1)
|
||
{
|
||
while (!fibheap_empty (worklist))
|
||
{
|
||
i = (size_t) fibheap_extract_min (worklist);
|
||
bb = BASIC_BLOCK (i);
|
||
if (!TEST_BIT (visited, bb->index))
|
||
hybrid_search_bitmap (bb, in, out, gen, kill, dir,
|
||
conf_op, transfun, visited, pending, data);
|
||
}
|
||
|
||
if (sbitmap_first_set_bit (pending) != -1)
|
||
{
|
||
EXECUTE_IF_SET_IN_BITMAP (blocks, 0, i,
|
||
{
|
||
fibheap_insert (worklist, order[i], (void *) (size_t) i);
|
||
});
|
||
sbitmap_zero (visited);
|
||
}
|
||
else
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
sbitmap_free (pending);
|
||
sbitmap_free (visited);
|
||
fibheap_delete (worklist);
|
||
}
|