flow.c (XNMALLOC): New macro.
* flow.c (XNMALLOC): New macro. (flow_int_list_blocks, basic_block_succ, basic_block_pred): New static variables. (add_edge, add_edge_to_label): New static functions. (free_bb_memory): New function. (flow_delete_insn): Delete function. (basic_block_drops_in): Delete variable. (find_basic_blocks): Allocate and initialize basic_block_head, basic_block_succ. Don't allocate basic_block_drops_in. Call free_bb_memory at the beginning. (find_basic_blocks_1): Don't do multiple passes. Delete code to compute basic_block_drops_in. After calling make_edges, mark blocks reached by current block live. Update test for unreachable live blocks. (mark_label_ref): Delete args X, CHECKDUP. Add PRED arg. All callers changed. Simplify to call add_edge_to_label when a LABEL_REF is found. (make_edges): Simplify to call add_edge_to_label instead of mark_label_ref most of the time. Compute here whether control drops into the next block. (delete_unreachable_blocks): Return void. All callers changed. Delete unreachable blocks in reverse order. After deleting all unreachable blocks, renumber the remaining ones and update n_basic_blocks. (delete_block): Speed up deletion a bit. Don't set basic_block_drops_in for deleted blocks. (free_basic_block_vars): Don't free basic_block_drops_in. (life_analysis_1): Update to use new edge representation. (dump_flow_info): Delete code to print basic block info; call dump_bb_data instead. (compute_preds_succs): Delete code to recompute basic_block_drops_in and uid_block_number. Simply copy the previously computed cfg. (dump_bb_data): New arg LIVE_INFO. All callers changed. Print register lifetime information if LIVE_INFO is nonzero. * basic-block.h (dump_bb_data): Adjust prototype. * gcse.c (gcse_main): Update call to dump_bb_data. * rtl.h (free_bb_memory): Declare. * toplev.c (rest_of_compilation): Call free_bb_memory. From-SVN: r23443
This commit is contained in:
parent
1f3b1e1a0b
commit
421382ac8c
@ -1,5 +1,45 @@
|
||||
Thu Oct 29 23:55:43 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
|
||||
|
||||
* flow.c (XNMALLOC): New macro.
|
||||
(flow_int_list_blocks, basic_block_succ, basic_block_pred): New
|
||||
static variables.
|
||||
(add_edge, add_edge_to_label): New static functions.
|
||||
(free_bb_memory): New function.
|
||||
(flow_delete_insn): Delete function.
|
||||
(basic_block_drops_in): Delete variable.
|
||||
(find_basic_blocks): Allocate and initialize basic_block_head,
|
||||
basic_block_succ. Don't allocate basic_block_drops_in.
|
||||
Call free_bb_memory at the beginning.
|
||||
(find_basic_blocks_1): Don't do multiple passes.
|
||||
Delete code to compute basic_block_drops_in.
|
||||
After calling make_edges, mark blocks reached by current block live.
|
||||
Update test for unreachable live blocks.
|
||||
(mark_label_ref): Delete args X, CHECKDUP. Add PRED arg. All callers
|
||||
changed.
|
||||
Simplify to call add_edge_to_label when a LABEL_REF is found.
|
||||
(make_edges): Simplify to call add_edge_to_label instead of
|
||||
mark_label_ref most of the time.
|
||||
Compute here whether control drops into the next block.
|
||||
(delete_unreachable_blocks): Return void. All callers changed.
|
||||
Delete unreachable blocks in reverse order.
|
||||
After deleting all unreachable blocks, renumber the remaining ones
|
||||
and update n_basic_blocks.
|
||||
(delete_block): Speed up deletion a bit.
|
||||
Don't set basic_block_drops_in for deleted blocks.
|
||||
(free_basic_block_vars): Don't free basic_block_drops_in.
|
||||
(life_analysis_1): Update to use new edge representation.
|
||||
(dump_flow_info): Delete code to print basic block info; call
|
||||
dump_bb_data instead.
|
||||
(compute_preds_succs): Delete code to recompute basic_block_drops_in
|
||||
and uid_block_number.
|
||||
Simply copy the previously computed cfg.
|
||||
(dump_bb_data): New arg LIVE_INFO. All callers changed.
|
||||
Print register lifetime information if LIVE_INFO is nonzero.
|
||||
* basic-block.h (dump_bb_data): Adjust prototype.
|
||||
* gcse.c (gcse_main): Update call to dump_bb_data.
|
||||
* rtl.h (free_bb_memory): Declare.
|
||||
* toplev.c (rest_of_compilation): Call free_bb_memory.
|
||||
|
||||
* reload1.c (struct elim_table): Delete MAX_OFFSET member.
|
||||
(update_eliminable_offsets): Don't compute it.
|
||||
(set_initial_elim_offsets): Don't initialize it.
|
||||
|
@ -190,7 +190,8 @@ extern int *uid_block_number;
|
||||
|
||||
extern void compute_preds_succs PROTO ((int_list_ptr *, int_list_ptr *,
|
||||
int *, int *));
|
||||
extern void dump_bb_data PROTO ((FILE *, int_list_ptr *, int_list_ptr *));
|
||||
extern void dump_bb_data PROTO ((FILE *, int_list_ptr *, int_list_ptr *,
|
||||
int));
|
||||
extern void free_bb_mem PROTO ((void));
|
||||
extern void free_basic_block_vars PROTO ((int));
|
||||
|
||||
|
654
gcc/flow.c
654
gcc/flow.c
@ -128,6 +128,8 @@ Boston, MA 02111-1307, USA. */
|
||||
#define obstack_chunk_alloc xmalloc
|
||||
#define obstack_chunk_free free
|
||||
|
||||
#define XNMALLOC(TYPE, COUNT) ((TYPE *) xmalloc ((COUNT) * sizeof (TYPE)))
|
||||
|
||||
/* The contents of the current function definition are allocated
|
||||
in this obstack, and all are freed at the end of the function.
|
||||
For top-level functions, this is temporary_obstack.
|
||||
@ -218,10 +220,14 @@ regset regs_live_at_setjmp;
|
||||
are another pair, etc. */
|
||||
rtx regs_may_share;
|
||||
|
||||
/* Element N is nonzero if control can drop into basic block N
|
||||
from the preceding basic block. Freed after life_analysis. */
|
||||
/* Pointer to head of predecessor/successor block list. */
|
||||
static int_list_block *flow_int_list_blocks;
|
||||
|
||||
static char *basic_block_drops_in;
|
||||
/* Element N is the list of successors of basic block N. */
|
||||
static int_list_ptr *basic_block_succ;
|
||||
|
||||
/* Element N is the list of predecessors of basic block N. */
|
||||
static int_list_ptr *basic_block_pred;
|
||||
|
||||
/* Element N is depth within loops of the last insn in basic block number N.
|
||||
Freed after life_analysis. */
|
||||
@ -249,14 +255,15 @@ static HARD_REG_SET elim_reg_set;
|
||||
|
||||
/* Forward declarations */
|
||||
static void find_basic_blocks_1 PROTO((rtx, rtx));
|
||||
static void add_edge PROTO((int, int));
|
||||
static void add_edge_to_label PROTO((int, rtx));
|
||||
static void make_edges PROTO((int));
|
||||
static void mark_label_ref PROTO((rtx, rtx, int));
|
||||
static int delete_unreachable_blocks PROTO((void));
|
||||
static void mark_label_ref PROTO((int, rtx));
|
||||
static void delete_unreachable_blocks PROTO((void));
|
||||
static int delete_block PROTO((int));
|
||||
static void life_analysis_1 PROTO((rtx, int));
|
||||
static void propagate_block PROTO((regset, rtx, rtx, int,
|
||||
regset, int));
|
||||
static rtx flow_delete_insn PROTO((rtx));
|
||||
static int set_noop_p PROTO((rtx));
|
||||
static int noop_move_p PROTO((rtx));
|
||||
static void record_volatile_insns PROTO((rtx));
|
||||
@ -304,6 +311,10 @@ find_basic_blocks (f, nregs, file)
|
||||
rtx nonlocal_label_list = nonlocal_label_rtx_list ();
|
||||
int in_libcall_block = 0;
|
||||
|
||||
/* Avoid leaking memory if this is called multiple times per compiled
|
||||
function. */
|
||||
free_bb_memory ();
|
||||
|
||||
/* Count the basic blocks. Also find maximum insn uid value used. */
|
||||
|
||||
{
|
||||
@ -382,14 +393,17 @@ find_basic_blocks (f, nregs, file)
|
||||
/* Allocate some tables that last till end of compiling this function
|
||||
and some needed only in find_basic_blocks and life_analysis. */
|
||||
|
||||
basic_block_head = (rtx *) xmalloc (n_basic_blocks * sizeof (rtx));
|
||||
basic_block_end = (rtx *) xmalloc (n_basic_blocks * sizeof (rtx));
|
||||
basic_block_drops_in = (char *) xmalloc (n_basic_blocks);
|
||||
basic_block_head = XNMALLOC (rtx, n_basic_blocks);
|
||||
basic_block_end = XNMALLOC (rtx, n_basic_blocks);
|
||||
basic_block_succ = XNMALLOC (int_list_ptr, n_basic_blocks);
|
||||
basic_block_pred = XNMALLOC (int_list_ptr, n_basic_blocks);
|
||||
bzero ((char *)basic_block_succ, n_basic_blocks * sizeof (int_list_ptr));
|
||||
bzero ((char *)basic_block_pred, n_basic_blocks * sizeof (int_list_ptr));
|
||||
|
||||
basic_block_computed_jump_target = (char *) oballoc (n_basic_blocks);
|
||||
basic_block_loop_depth = (short *) xmalloc (n_basic_blocks * sizeof (short));
|
||||
uid_block_number
|
||||
= (int *) xmalloc ((max_uid_for_flow + 1) * sizeof (int));
|
||||
uid_volatile = (char *) xmalloc (max_uid_for_flow + 1);
|
||||
basic_block_loop_depth = XNMALLOC (short, n_basic_blocks);
|
||||
uid_block_number = XNMALLOC (int, (max_uid_for_flow + 1));
|
||||
uid_volatile = XNMALLOC (char, (max_uid_for_flow + 1));
|
||||
bzero (uid_volatile, max_uid_for_flow + 1);
|
||||
|
||||
find_basic_blocks_1 (f, nonlocal_label_list);
|
||||
@ -435,15 +449,13 @@ find_basic_blocks_1 (f, nonlocal_labels)
|
||||
register char *block_marked = (char *) alloca (n_basic_blocks);
|
||||
rtx note, eh_note;
|
||||
enum rtx_code prev_code, code;
|
||||
int depth, pass;
|
||||
int depth;
|
||||
int in_libcall_block = 0;
|
||||
int call_had_abnormal_edge = 0;
|
||||
|
||||
pass = 1;
|
||||
active_eh_region = (int *) alloca ((max_uid_for_flow + 1) * sizeof (int));
|
||||
nested_eh_region = (int *) alloca ((max_label_num () + 1) * sizeof (int));
|
||||
nonlocal_label_list = nonlocal_labels;
|
||||
restart:
|
||||
|
||||
label_value_list = 0;
|
||||
block_live_static = block_live;
|
||||
@ -564,23 +576,8 @@ find_basic_blocks_1 (f, nonlocal_labels)
|
||||
in_libcall_block = 0;
|
||||
}
|
||||
|
||||
/* During the second pass, `n_basic_blocks' is only an upper bound.
|
||||
Only perform the sanity check for the first pass, and on the second
|
||||
pass ensure `n_basic_blocks' is set to the correct value. */
|
||||
if (pass == 1 && i + 1 != n_basic_blocks)
|
||||
if (i + 1 != n_basic_blocks)
|
||||
abort ();
|
||||
n_basic_blocks = i + 1;
|
||||
|
||||
/* Record which basic blocks control can drop in to. */
|
||||
|
||||
for (i = 0; i < n_basic_blocks; i++)
|
||||
{
|
||||
for (insn = PREV_INSN (basic_block_head[i]);
|
||||
insn && GET_CODE (insn) == NOTE; insn = PREV_INSN (insn))
|
||||
;
|
||||
|
||||
basic_block_drops_in[i] = insn && GET_CODE (insn) != BARRIER;
|
||||
}
|
||||
|
||||
/* Now find which basic blocks can actually be reached
|
||||
and put all jump insns' LABEL_REFS onto the ref-chains
|
||||
@ -589,7 +586,6 @@ find_basic_blocks_1 (f, nonlocal_labels)
|
||||
if (n_basic_blocks > 0)
|
||||
{
|
||||
int something_marked = 1;
|
||||
int deleted = 0;
|
||||
|
||||
/* Pass over all blocks, marking each block that is reachable
|
||||
and has not yet been marked.
|
||||
@ -603,10 +599,15 @@ find_basic_blocks_1 (f, nonlocal_labels)
|
||||
for (i = 0; i < n_basic_blocks; i++)
|
||||
if (block_live[i] && !block_marked[i])
|
||||
{
|
||||
int_list_ptr p;
|
||||
|
||||
block_marked[i] = 1;
|
||||
something_marked = 1;
|
||||
|
||||
make_edges (i);
|
||||
|
||||
for (p = basic_block_succ[i]; p; p = p->next)
|
||||
block_live[INT_LIST_VAL (p)] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -618,40 +619,11 @@ find_basic_blocks_1 (f, nonlocal_labels)
|
||||
later during the compile or at runtime. It's easier to debug the
|
||||
problem here than later! */
|
||||
for (i = 1; i < n_basic_blocks; i++)
|
||||
if (block_live[i] && ! basic_block_drops_in[i]
|
||||
&& GET_CODE (basic_block_head[i]) == CODE_LABEL
|
||||
&& LABEL_REFS (basic_block_head[i]) == basic_block_head[i])
|
||||
if (block_live[i] && basic_block_pred[i] == 0)
|
||||
abort ();
|
||||
|
||||
if (! reload_completed)
|
||||
deleted = delete_unreachable_blocks ();
|
||||
|
||||
/* There are pathological cases where one function calling hundreds of
|
||||
nested inline functions can generate lots and lots of unreachable
|
||||
blocks that jump can't delete. Since we don't use sparse matrices
|
||||
a lot of memory will be needed to compile such functions.
|
||||
Implementing sparse matrices is a fair bit of work and it is not
|
||||
clear that they win more than they lose (we don't want to
|
||||
unnecessarily slow down compilation of normal code). By making
|
||||
another pass for the pathological case, we can greatly speed up
|
||||
their compilation without hurting normal code. This works because
|
||||
all the insns in the unreachable blocks have either been deleted or
|
||||
turned into notes.
|
||||
Note that we're talking about reducing memory usage by 10's of
|
||||
megabytes and reducing compilation time by several minutes. */
|
||||
/* ??? The choice of when to make another pass is a bit arbitrary,
|
||||
and was derived from empirical data. */
|
||||
if (pass == 1
|
||||
&& deleted > 200)
|
||||
{
|
||||
pass++;
|
||||
n_basic_blocks -= deleted;
|
||||
/* `n_basic_blocks' may not be correct at this point: two previously
|
||||
separate blocks may now be merged. That's ok though as we
|
||||
recalculate it during the second pass. It certainly can't be
|
||||
any larger than the current value. */
|
||||
goto restart;
|
||||
}
|
||||
delete_unreachable_blocks ();
|
||||
}
|
||||
}
|
||||
|
||||
@ -671,10 +643,74 @@ set_block_num (insn, bb)
|
||||
}
|
||||
BLOCK_NUM (insn) = bb;
|
||||
}
|
||||
|
||||
|
||||
/* Subroutines of find_basic_blocks. */
|
||||
|
||||
void
|
||||
free_bb_memory ()
|
||||
{
|
||||
free_int_list (&flow_int_list_blocks);
|
||||
}
|
||||
|
||||
/* Make an edge in the cfg from block PRED to block SUCC. */
|
||||
static void
|
||||
add_edge (pred, succ)
|
||||
int pred, succ;
|
||||
{
|
||||
add_int_list_node (&flow_int_list_blocks, basic_block_pred + succ, pred);
|
||||
add_int_list_node (&flow_int_list_blocks, basic_block_succ + pred, succ);
|
||||
}
|
||||
|
||||
/* Make an edge in the cfg from block PRED to the block starting with
|
||||
label LABEL. */
|
||||
static void
|
||||
add_edge_to_label (pred, label)
|
||||
int pred;
|
||||
rtx label;
|
||||
{
|
||||
/* If the label was never emitted, this insn is junk,
|
||||
but avoid a crash trying to refer to BLOCK_NUM (label).
|
||||
This can happen as a result of a syntax error
|
||||
and a diagnostic has already been printed. */
|
||||
if (INSN_UID (label) == 0)
|
||||
return;
|
||||
|
||||
add_edge (pred, BLOCK_NUM (label));
|
||||
}
|
||||
|
||||
/* Check expression X for label references. If one is found, add an edge
|
||||
from basic block PRED to the block beginning with the label. */
|
||||
|
||||
static void
|
||||
mark_label_ref (pred, x)
|
||||
int pred;
|
||||
rtx x;
|
||||
{
|
||||
register RTX_CODE code;
|
||||
register int i;
|
||||
register char *fmt;
|
||||
|
||||
code = GET_CODE (x);
|
||||
if (code == LABEL_REF)
|
||||
{
|
||||
add_edge_to_label (pred, XEXP (x, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
fmt = GET_RTX_FORMAT (code);
|
||||
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
|
||||
{
|
||||
if (fmt[i] == 'e')
|
||||
mark_label_ref (pred, XEXP (x, i));
|
||||
if (fmt[i] == 'E')
|
||||
{
|
||||
register int j;
|
||||
for (j = 0; j < XVECLEN (x, i); j++)
|
||||
mark_label_ref (pred, XVECEXP (x, i, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* For basic block I, make edges and mark live all blocks which are reachable
|
||||
from it. */
|
||||
static void
|
||||
@ -683,18 +719,26 @@ make_edges (i)
|
||||
{
|
||||
rtx insn, x;
|
||||
|
||||
if (i + 1 < n_basic_blocks && basic_block_drops_in[i + 1])
|
||||
block_live_static[i + 1] = 1;
|
||||
/* See if control drops into the next block. */
|
||||
if (i + 1 < n_basic_blocks)
|
||||
{
|
||||
for (insn = PREV_INSN (basic_block_head[i + 1]);
|
||||
insn && GET_CODE (insn) == NOTE; insn = PREV_INSN (insn))
|
||||
;
|
||||
|
||||
if (insn && GET_CODE (insn) != BARRIER)
|
||||
add_edge (i, i + 1);
|
||||
}
|
||||
|
||||
insn = basic_block_end[i];
|
||||
if (GET_CODE (insn) == JUMP_INSN)
|
||||
mark_label_ref (PATTERN (insn), insn, 0);
|
||||
mark_label_ref (i, PATTERN (insn));
|
||||
|
||||
/* If we have any forced labels, mark them as potentially reachable from
|
||||
this block. */
|
||||
for (x = forced_labels; x; x = XEXP (x, 1))
|
||||
if (! LABEL_REF_NONLOCAL_P (x))
|
||||
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, XEXP (x, 0)),
|
||||
insn, 0);
|
||||
add_edge_to_label (i, XEXP (x, 0));
|
||||
|
||||
/* Now scan the insns for this block, we may need to make edges for some of
|
||||
them to various non-obvious locations (exception handlers, nonlocal
|
||||
@ -731,12 +775,7 @@ make_edges (i)
|
||||
{
|
||||
if (REG_NOTE_KIND (note) == REG_LABEL
|
||||
&& XEXP (note, 0) != eh_return_stub_label)
|
||||
{
|
||||
x = XEXP (note, 0);
|
||||
block_live_static[BLOCK_NUM (x)] = 1;
|
||||
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, x),
|
||||
insn, 0);
|
||||
}
|
||||
add_edge_to_label (i, XEXP (note, 0));
|
||||
}
|
||||
|
||||
/* If this is a computed jump, then mark it as reaching everything
|
||||
@ -748,16 +787,14 @@ make_edges (i)
|
||||
{
|
||||
int b = BLOCK_NUM (XEXP (x, 0));
|
||||
basic_block_computed_jump_target[b] = 1;
|
||||
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, XEXP (x, 0)),
|
||||
insn, 0);
|
||||
add_edge (i, b);
|
||||
}
|
||||
|
||||
for (x = forced_labels; x; x = XEXP (x, 1))
|
||||
{
|
||||
int b = BLOCK_NUM (XEXP (x, 0));
|
||||
basic_block_computed_jump_target[b] = 1;
|
||||
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, XEXP (x, 0)),
|
||||
insn, 0);
|
||||
add_edge (i, b);
|
||||
}
|
||||
}
|
||||
|
||||
@ -775,21 +812,17 @@ make_edges (i)
|
||||
int region;
|
||||
handler_info *ptr;
|
||||
region = active_eh_region[INSN_UID (insn)];
|
||||
for ( ; region;
|
||||
region = nested_eh_region[region])
|
||||
for ( ; region; region = nested_eh_region[region])
|
||||
{
|
||||
ptr = get_first_handler (region);
|
||||
for ( ; ptr ; ptr = ptr->next)
|
||||
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode,
|
||||
ptr->handler_label),
|
||||
insn, 0);
|
||||
add_edge_to_label (i, ptr->handler_label);
|
||||
}
|
||||
}
|
||||
if (! asynchronous_exceptions)
|
||||
{
|
||||
for (x = nonlocal_label_list; x; x = XEXP (x, 1))
|
||||
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, XEXP (x, 0)),
|
||||
insn, 0);
|
||||
add_edge_to_label (i, XEXP (x, 0));
|
||||
}
|
||||
/* ??? This could be made smarter: in some cases it's possible
|
||||
to tell that certain calls will not do a nonlocal goto.
|
||||
@ -807,101 +840,79 @@ make_edges (i)
|
||||
the eh_stub labels within it. So we have to make additional edges in
|
||||
the flow graph. */
|
||||
if (i + 1 == n_basic_blocks && eh_return_stub_label != 0)
|
||||
{
|
||||
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, eh_return_stub_label),
|
||||
basic_block_end[i], 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check expression X for label references;
|
||||
if one is found, add INSN to the label's chain of references.
|
||||
|
||||
CHECKDUP means check for and avoid creating duplicate references
|
||||
from the same insn. Such duplicates do no serious harm but
|
||||
can slow life analysis. CHECKDUP is set only when duplicates
|
||||
are likely. */
|
||||
|
||||
static void
|
||||
mark_label_ref (x, insn, checkdup)
|
||||
rtx x, insn;
|
||||
int checkdup;
|
||||
{
|
||||
register RTX_CODE code;
|
||||
register int i;
|
||||
register char *fmt;
|
||||
|
||||
/* We can be called with NULL when scanning label_value_list. */
|
||||
if (x == 0)
|
||||
return;
|
||||
|
||||
code = GET_CODE (x);
|
||||
if (code == LABEL_REF)
|
||||
{
|
||||
register rtx label = XEXP (x, 0);
|
||||
register rtx y;
|
||||
if (GET_CODE (label) != CODE_LABEL)
|
||||
abort ();
|
||||
/* If the label was never emitted, this insn is junk,
|
||||
but avoid a crash trying to refer to BLOCK_NUM (label).
|
||||
This can happen as a result of a syntax error
|
||||
and a diagnostic has already been printed. */
|
||||
if (INSN_UID (label) == 0)
|
||||
return;
|
||||
CONTAINING_INSN (x) = insn;
|
||||
/* if CHECKDUP is set, check for duplicate ref from same insn
|
||||
and don't insert. */
|
||||
if (checkdup)
|
||||
for (y = LABEL_REFS (label); y != label; y = LABEL_NEXTREF (y))
|
||||
if (CONTAINING_INSN (y) == insn)
|
||||
return;
|
||||
LABEL_NEXTREF (x) = LABEL_REFS (label);
|
||||
LABEL_REFS (label) = x;
|
||||
block_live_static[BLOCK_NUM (label)] = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
fmt = GET_RTX_FORMAT (code);
|
||||
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
|
||||
{
|
||||
if (fmt[i] == 'e')
|
||||
mark_label_ref (XEXP (x, i), insn, 0);
|
||||
if (fmt[i] == 'E')
|
||||
{
|
||||
register int j;
|
||||
for (j = 0; j < XVECLEN (x, i); j++)
|
||||
mark_label_ref (XVECEXP (x, i, j), insn, 1);
|
||||
}
|
||||
}
|
||||
add_edge_to_label (i, eh_return_stub_label);
|
||||
}
|
||||
|
||||
/* Now delete the code for any basic blocks that can't be reached.
|
||||
They can occur because jump_optimize does not recognize unreachable loops
|
||||
as unreachable.
|
||||
Return the number of deleted blocks. */
|
||||
static int
|
||||
as unreachable. */
|
||||
static void
|
||||
delete_unreachable_blocks ()
|
||||
{
|
||||
int deleted_handler = 0;
|
||||
int deleted = 0;
|
||||
int i;
|
||||
int i, j;
|
||||
rtx insn;
|
||||
int *block_num_map = XNMALLOC (int, n_basic_blocks);
|
||||
|
||||
for (i = n_basic_blocks - 1; i >= 0; i--)
|
||||
if (! block_live_static[i])
|
||||
deleted_handler |= delete_block (i);
|
||||
|
||||
for (i = 0; i < n_basic_blocks; i++)
|
||||
if (! block_live_static[i])
|
||||
if (block_live_static[i])
|
||||
block_num_map[i] = i - deleted;
|
||||
else
|
||||
{
|
||||
deleted++;
|
||||
|
||||
deleted_handler |= delete_block (i);
|
||||
block_num_map[i] = -1;
|
||||
}
|
||||
|
||||
/* Eliminate all traces of the deleted blocks by renumbering the remaining
|
||||
ones. */
|
||||
for (i = j = 0; i < n_basic_blocks; i++)
|
||||
{
|
||||
int_list_ptr p;
|
||||
|
||||
if (block_num_map[i] == -1)
|
||||
continue;
|
||||
|
||||
for (p = basic_block_pred[i]; p; p = p->next)
|
||||
INT_LIST_VAL (p) = block_num_map[INT_LIST_VAL (p)];
|
||||
for (p = basic_block_succ[i]; p; p = p->next)
|
||||
INT_LIST_VAL (p) = block_num_map[INT_LIST_VAL (p)];
|
||||
|
||||
if (i != j)
|
||||
{
|
||||
rtx tmp = basic_block_head[i];
|
||||
for (;;)
|
||||
{
|
||||
BLOCK_NUM (tmp) = j;
|
||||
if (tmp == basic_block_end[i])
|
||||
break;
|
||||
tmp = NEXT_INSN (tmp);
|
||||
}
|
||||
basic_block_head[j] = basic_block_head[i];
|
||||
basic_block_end[j] = basic_block_end[i];
|
||||
basic_block_pred[j] = basic_block_pred[i];
|
||||
basic_block_succ[j] = basic_block_succ[i];
|
||||
basic_block_loop_depth[j] = basic_block_loop_depth[i];
|
||||
basic_block_computed_jump_target[j]
|
||||
= basic_block_computed_jump_target[i];
|
||||
}
|
||||
j++;
|
||||
}
|
||||
n_basic_blocks -= deleted;
|
||||
free (block_num_map);
|
||||
|
||||
/* If we deleted an exception handler, we may have EH region
|
||||
begin/end blocks to remove as well. */
|
||||
if (deleted_handler)
|
||||
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
||||
if (GET_CODE (insn) == NOTE)
|
||||
{
|
||||
if ((NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG) ||
|
||||
(NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END))
|
||||
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG ||
|
||||
NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END)
|
||||
{
|
||||
int num = CODE_LABEL_NUMBER (insn);
|
||||
/* A NULL handler indicates a region is no longer needed */
|
||||
@ -912,7 +923,6 @@ delete_unreachable_blocks ()
|
||||
}
|
||||
}
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
|
||||
/* Delete the insns in a (non-live) block. We physically delete every
|
||||
@ -932,83 +942,89 @@ delete_block (i)
|
||||
{
|
||||
int deleted_handler = 0;
|
||||
rtx insn;
|
||||
rtx kept_head = 0;
|
||||
rtx kept_tail = 0;
|
||||
|
||||
if (basic_block_head[i] != basic_block_end[i])
|
||||
/* If the head of this block is a CODE_LABEL, then it might
|
||||
be the label for an exception handler which can't be
|
||||
reached.
|
||||
|
||||
We need to remove the label from the exception_handler_label
|
||||
list and remove the associated NOTE_EH_REGION_BEG and
|
||||
NOTE_EH_REGION_END notes. */
|
||||
insn = basic_block_head[i];
|
||||
if (GET_CODE (insn) == CODE_LABEL)
|
||||
{
|
||||
/* It would be quicker to delete all of these with a single
|
||||
unchaining, rather than one at a time, but we need to keep
|
||||
the NOTE's. */
|
||||
insn = NEXT_INSN (basic_block_head[i]);
|
||||
while (insn != basic_block_end[i])
|
||||
rtx x, *prev = &exception_handler_labels;
|
||||
|
||||
for (x = exception_handler_labels; x; x = XEXP (x, 1))
|
||||
{
|
||||
if (GET_CODE (insn) == BARRIER)
|
||||
abort ();
|
||||
else if (GET_CODE (insn) != NOTE)
|
||||
insn = flow_delete_insn (insn);
|
||||
else
|
||||
insn = NEXT_INSN (insn);
|
||||
if (XEXP (x, 0) == insn)
|
||||
{
|
||||
/* Found a match, splice this label out of the
|
||||
EH label list. */
|
||||
*prev = XEXP (x, 1);
|
||||
XEXP (x, 1) = NULL_RTX;
|
||||
XEXP (x, 0) = NULL_RTX;
|
||||
|
||||
/* Remove the handler from all regions */
|
||||
remove_handler (insn);
|
||||
deleted_handler = 1;
|
||||
break;
|
||||
}
|
||||
prev = &XEXP (x, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Walk the insns of the block, building a chain of NOTEs that need to be
|
||||
kept. */
|
||||
insn = basic_block_head[i];
|
||||
if (GET_CODE (insn) != NOTE)
|
||||
for (;;)
|
||||
{
|
||||
/* Turn the head into a deleted insn note. */
|
||||
if (GET_CODE (insn) == BARRIER)
|
||||
abort ();
|
||||
|
||||
/* If the head of this block is a CODE_LABEL, then it might
|
||||
be the label for an exception handler which can't be
|
||||
reached.
|
||||
|
||||
We need to remove the label from the exception_handler_label
|
||||
list and remove the associated NOTE_EH_REGION_BEG and
|
||||
NOTE_EH_REGION_END notes. */
|
||||
if (GET_CODE (insn) == CODE_LABEL)
|
||||
else if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) != NOTE_INSN_DELETED)
|
||||
{
|
||||
rtx x, *prev = &exception_handler_labels;
|
||||
|
||||
for (x = exception_handler_labels; x; x = XEXP (x, 1))
|
||||
if (kept_head == 0)
|
||||
kept_head = kept_tail = insn;
|
||||
else
|
||||
{
|
||||
if (XEXP (x, 0) == insn)
|
||||
{
|
||||
/* Found a match, splice this label out of the
|
||||
EH label list. */
|
||||
*prev = XEXP (x, 1);
|
||||
XEXP (x, 1) = NULL_RTX;
|
||||
XEXP (x, 0) = NULL_RTX;
|
||||
|
||||
/* Remove the handler from all regions */
|
||||
remove_handler (insn);
|
||||
deleted_handler = 1;
|
||||
break;
|
||||
}
|
||||
prev = &XEXP (x, 1);
|
||||
NEXT_INSN (kept_tail) = insn;
|
||||
PREV_INSN (insn) = kept_tail;
|
||||
kept_tail = insn;
|
||||
}
|
||||
}
|
||||
|
||||
PUT_CODE (insn, NOTE);
|
||||
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
|
||||
NOTE_SOURCE_FILE (insn) = 0;
|
||||
}
|
||||
insn = basic_block_end[i];
|
||||
if (GET_CODE (insn) != NOTE)
|
||||
{
|
||||
/* Turn the tail into a deleted insn note. */
|
||||
if (GET_CODE (insn) == BARRIER)
|
||||
abort ();
|
||||
PUT_CODE (insn, NOTE);
|
||||
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
|
||||
NOTE_SOURCE_FILE (insn) = 0;
|
||||
if (insn == basic_block_end[i])
|
||||
break;
|
||||
insn = NEXT_INSN (insn);
|
||||
}
|
||||
insn = NEXT_INSN (insn);
|
||||
|
||||
/* BARRIERs are between basic blocks, not part of one.
|
||||
Delete a BARRIER if the preceding jump is deleted.
|
||||
We cannot alter a BARRIER into a NOTE
|
||||
because it is too short; but we can really delete
|
||||
it because it is not part of a basic block. */
|
||||
if (NEXT_INSN (insn) != 0
|
||||
&& GET_CODE (NEXT_INSN (insn)) == BARRIER)
|
||||
delete_insn (NEXT_INSN (insn));
|
||||
if (insn != 0 && GET_CODE (insn) == BARRIER)
|
||||
insn = NEXT_INSN (insn);
|
||||
|
||||
/* Now unchain all of the block, and put the chain of kept notes in its
|
||||
place. */
|
||||
if (kept_head == 0)
|
||||
{
|
||||
NEXT_INSN (PREV_INSN (basic_block_head[i])) = insn;
|
||||
if (insn != 0)
|
||||
PREV_INSN (insn) = PREV_INSN (basic_block_head[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
NEXT_INSN (PREV_INSN (basic_block_head[i])) = kept_head;
|
||||
if (insn != 0)
|
||||
PREV_INSN (insn) = kept_tail;
|
||||
|
||||
PREV_INSN (kept_head) = PREV_INSN (basic_block_head[i]);
|
||||
NEXT_INSN (kept_tail) = insn;
|
||||
}
|
||||
|
||||
/* Each time we delete some basic blocks,
|
||||
see if there is a jump around them that is
|
||||
@ -1031,14 +1047,6 @@ delete_block (i)
|
||||
&& INSN_UID (label) != 0
|
||||
&& BLOCK_NUM (label) == j)
|
||||
{
|
||||
int k;
|
||||
|
||||
/* The deleted blocks still show up in the cfg,
|
||||
so we must set basic_block_drops_in for blocks
|
||||
I to J inclusive to keep the cfg accurate. */
|
||||
for (k = i; k <= j; k++)
|
||||
basic_block_drops_in[k] = 1;
|
||||
|
||||
PUT_CODE (insn, NOTE);
|
||||
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
|
||||
NOTE_SOURCE_FILE (insn) = 0;
|
||||
@ -1052,20 +1060,6 @@ delete_block (i)
|
||||
|
||||
return deleted_handler;
|
||||
}
|
||||
|
||||
/* Delete INSN by patching it out.
|
||||
Return the next insn. */
|
||||
|
||||
static rtx
|
||||
flow_delete_insn (insn)
|
||||
rtx insn;
|
||||
{
|
||||
/* ??? For the moment we assume we don't have to watch for NULLs here
|
||||
since the start/end of basic blocks aren't deleted like this. */
|
||||
NEXT_INSN (PREV_INSN (insn)) = NEXT_INSN (insn);
|
||||
PREV_INSN (NEXT_INSN (insn)) = PREV_INSN (insn);
|
||||
return NEXT_INSN (insn);
|
||||
}
|
||||
|
||||
/* Perform data flow analysis.
|
||||
F is the first insn of the function and NREGS the number of register numbers
|
||||
@ -1110,12 +1104,6 @@ void
|
||||
free_basic_block_vars (keep_head_end_p)
|
||||
int keep_head_end_p;
|
||||
{
|
||||
if (basic_block_drops_in)
|
||||
{
|
||||
free (basic_block_drops_in);
|
||||
/* Tell dump_flow_info this isn't available anymore. */
|
||||
basic_block_drops_in = 0;
|
||||
}
|
||||
if (basic_block_loop_depth)
|
||||
{
|
||||
free (basic_block_loop_depth);
|
||||
@ -1484,26 +1472,16 @@ life_analysis_1 (f, nregs)
|
||||
}
|
||||
|
||||
{
|
||||
register rtx jump, head;
|
||||
|
||||
/* Update the basic_block_new_live_at_end's of the block
|
||||
that falls through into this one (if any). */
|
||||
head = basic_block_head[i];
|
||||
if (basic_block_drops_in[i])
|
||||
IOR_REG_SET (basic_block_new_live_at_end[i-1],
|
||||
basic_block_live_at_start[i]);
|
||||
int_list_ptr p;
|
||||
|
||||
/* Update the basic_block_new_live_at_end's of
|
||||
all the blocks that jump to this one. */
|
||||
if (GET_CODE (head) == CODE_LABEL)
|
||||
for (jump = LABEL_REFS (head);
|
||||
jump != head;
|
||||
jump = LABEL_NEXTREF (jump))
|
||||
{
|
||||
register int from_block = BLOCK_NUM (CONTAINING_INSN (jump));
|
||||
IOR_REG_SET (basic_block_new_live_at_end[from_block],
|
||||
basic_block_live_at_start[i]);
|
||||
}
|
||||
all the blocks that reach this one. */
|
||||
for (p = basic_block_pred[i]; p; p = p->next)
|
||||
{
|
||||
register int from_block = INT_LIST_VAL (p);
|
||||
IOR_REG_SET (basic_block_new_live_at_end[from_block],
|
||||
basic_block_live_at_start[i]);
|
||||
}
|
||||
}
|
||||
#ifdef USE_C_ALLOCA
|
||||
alloca (0);
|
||||
@ -3191,39 +3169,7 @@ dump_flow_info (file)
|
||||
fprintf (file, ".\n");
|
||||
}
|
||||
fprintf (file, "\n%d basic blocks.\n", n_basic_blocks);
|
||||
for (i = 0; i < n_basic_blocks; i++)
|
||||
{
|
||||
register rtx head, jump;
|
||||
register int regno;
|
||||
fprintf (file, "\nBasic block %d: first insn %d, last %d.\n",
|
||||
i,
|
||||
INSN_UID (basic_block_head[i]),
|
||||
INSN_UID (basic_block_end[i]));
|
||||
/* The control flow graph's storage is freed
|
||||
now when flow_analysis returns.
|
||||
Don't try to print it if it is gone. */
|
||||
if (basic_block_drops_in)
|
||||
{
|
||||
fprintf (file, "Reached from blocks: ");
|
||||
head = basic_block_head[i];
|
||||
if (GET_CODE (head) == CODE_LABEL)
|
||||
for (jump = LABEL_REFS (head);
|
||||
jump != head;
|
||||
jump = LABEL_NEXTREF (jump))
|
||||
{
|
||||
register int from_block = BLOCK_NUM (CONTAINING_INSN (jump));
|
||||
fprintf (file, " %d", from_block);
|
||||
}
|
||||
if (basic_block_drops_in[i])
|
||||
fprintf (file, " previous");
|
||||
}
|
||||
fprintf (file, "\nRegisters live at start:");
|
||||
for (regno = 0; regno < max_regno; regno++)
|
||||
if (REGNO_REG_SET_P (basic_block_live_at_start[i], regno))
|
||||
fprintf (file, " %d", regno);
|
||||
fprintf (file, "\n");
|
||||
}
|
||||
fprintf (file, "\n");
|
||||
dump_bb_data (file, basic_block_pred, basic_block_succ, 1);
|
||||
}
|
||||
|
||||
|
||||
@ -3406,117 +3352,46 @@ compute_preds_succs (s_preds, s_succs, num_preds, num_succs)
|
||||
int *num_preds;
|
||||
int *num_succs;
|
||||
{
|
||||
int bb, clear_local_bb_vars = 0;
|
||||
int bb;
|
||||
|
||||
bzero ((char *) s_preds, n_basic_blocks * sizeof (int_list_ptr));
|
||||
bzero ((char *) s_succs, n_basic_blocks * sizeof (int_list_ptr));
|
||||
bzero ((char *) num_preds, n_basic_blocks * sizeof (int));
|
||||
bzero ((char *) num_succs, n_basic_blocks * sizeof (int));
|
||||
|
||||
/* This routine can be called after life analysis; in that case
|
||||
basic_block_drops_in and uid_block_number will not be available
|
||||
and we must recompute their values. */
|
||||
if (basic_block_drops_in == NULL || uid_block_number == NULL)
|
||||
{
|
||||
clear_local_bb_vars = 1;
|
||||
basic_block_drops_in = (char *) alloca (n_basic_blocks);
|
||||
uid_block_number = (int *) alloca ((get_max_uid () + 1) * sizeof (int));
|
||||
|
||||
bzero ((char *) basic_block_drops_in, n_basic_blocks * sizeof (char));
|
||||
bzero ((char *) uid_block_number, n_basic_blocks * sizeof (int));
|
||||
|
||||
/* Scan each basic block setting basic_block_drops_in and
|
||||
uid_block_number as needed. */
|
||||
for (bb = 0; bb < n_basic_blocks; bb++)
|
||||
{
|
||||
rtx insn, stop_insn;
|
||||
|
||||
if (bb == 0)
|
||||
stop_insn = NULL_RTX;
|
||||
else
|
||||
stop_insn = basic_block_end[bb-1];
|
||||
|
||||
/* Look backwards from the start of this block. Stop if we
|
||||
hit the start of the function or the end of a previous
|
||||
block. Don't walk backwards through blocks that are just
|
||||
deleted insns! */
|
||||
for (insn = PREV_INSN (basic_block_head[bb]);
|
||||
insn && insn != stop_insn && GET_CODE (insn) == NOTE;
|
||||
insn = PREV_INSN (insn))
|
||||
;
|
||||
|
||||
/* Never set basic_block_drops_in for the first block. It is
|
||||
implicit.
|
||||
|
||||
If we stopped on anything other than a BARRIER, then this
|
||||
block drops in. */
|
||||
if (bb != 0)
|
||||
basic_block_drops_in[bb] = (insn ? GET_CODE (insn) != BARRIER : 1);
|
||||
|
||||
insn = basic_block_head[bb];
|
||||
while (insn)
|
||||
{
|
||||
BLOCK_NUM (insn) = bb;
|
||||
if (insn == basic_block_end[bb])
|
||||
break;
|
||||
insn = NEXT_INSN (insn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* It's somewhat stupid to simply copy the information. The passes
|
||||
which use this function ought to be changed to refer directly to
|
||||
basic_block_succ and its relatives. */
|
||||
for (bb = 0; bb < n_basic_blocks; bb++)
|
||||
{
|
||||
rtx head;
|
||||
rtx jump;
|
||||
rtx jump = BLOCK_END (bb);
|
||||
enum rtx_code code = GET_CODE (jump);
|
||||
int_list_ptr p;
|
||||
|
||||
head = BLOCK_HEAD (bb);
|
||||
for (p = basic_block_succ[bb]; p; p = p->next)
|
||||
add_pred_succ (bb, INT_LIST_VAL (p), s_preds, s_succs, num_preds,
|
||||
num_succs);
|
||||
|
||||
if (GET_CODE (head) == CODE_LABEL)
|
||||
for (jump = LABEL_REFS (head);
|
||||
jump != head;
|
||||
jump = LABEL_NEXTREF (jump))
|
||||
{
|
||||
if (! INSN_DELETED_P (CONTAINING_INSN (jump))
|
||||
&& (GET_CODE (CONTAINING_INSN (jump)) != NOTE
|
||||
|| (NOTE_LINE_NUMBER (CONTAINING_INSN (jump))
|
||||
!= NOTE_INSN_DELETED)))
|
||||
add_pred_succ (BLOCK_NUM (CONTAINING_INSN (jump)), bb,
|
||||
s_preds, s_succs, num_preds, num_succs);
|
||||
}
|
||||
|
||||
jump = BLOCK_END (bb);
|
||||
/* If this is a RETURN insn or a conditional jump in the last
|
||||
basic block, or a non-jump insn in the last basic block, then
|
||||
this block reaches the exit block. */
|
||||
if ((GET_CODE (jump) == JUMP_INSN && GET_CODE (PATTERN (jump)) == RETURN)
|
||||
|| (((GET_CODE (jump) == JUMP_INSN
|
||||
if ((code == JUMP_INSN && GET_CODE (PATTERN (jump)) == RETURN)
|
||||
|| (((code == JUMP_INSN
|
||||
&& condjump_p (jump) && !simplejump_p (jump))
|
||||
|| GET_CODE (jump) != JUMP_INSN)
|
||||
&& (bb == n_basic_blocks - 1)))
|
||||
|| code != JUMP_INSN)
|
||||
&& bb == n_basic_blocks - 1))
|
||||
add_pred_succ (bb, EXIT_BLOCK, s_preds, s_succs, num_preds, num_succs);
|
||||
|
||||
if (basic_block_drops_in[bb])
|
||||
add_pred_succ (bb - 1, bb, s_preds, s_succs, num_preds, num_succs);
|
||||
}
|
||||
|
||||
add_pred_succ (ENTRY_BLOCK, 0, s_preds, s_succs, num_preds, num_succs);
|
||||
|
||||
|
||||
/* If we allocated any variables in temporary storage, clear out the
|
||||
pointer to the local storage to avoid dangling pointers. */
|
||||
if (clear_local_bb_vars)
|
||||
{
|
||||
basic_block_drops_in = NULL;
|
||||
uid_block_number = NULL;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dump_bb_data (file, preds, succs)
|
||||
dump_bb_data (file, preds, succs, live_info)
|
||||
FILE *file;
|
||||
int_list_ptr *preds;
|
||||
int_list_ptr *succs;
|
||||
int live_info;
|
||||
{
|
||||
int bb;
|
||||
int_list_ptr p;
|
||||
@ -3545,6 +3420,15 @@ dump_bb_data (file, preds, succs)
|
||||
else
|
||||
fprintf (file, " %d", succ_bb);
|
||||
}
|
||||
if (live_info)
|
||||
{
|
||||
int regno;
|
||||
fprintf (file, "\nRegisters live at start:");
|
||||
for (regno = 0; regno < max_regno; regno++)
|
||||
if (REGNO_REG_SET_P (basic_block_live_at_start[bb], regno))
|
||||
fprintf (file, " %d", regno);
|
||||
fprintf (file, "\n");
|
||||
}
|
||||
fprintf (file, "\n");
|
||||
}
|
||||
fprintf (file, "\n");
|
||||
|
@ -700,9 +700,7 @@ gcse_main (f, file)
|
||||
compute_preds_succs (s_preds, s_succs, num_preds, num_succs);
|
||||
|
||||
if (file)
|
||||
{
|
||||
dump_bb_data (file, s_preds, s_succs);
|
||||
}
|
||||
dump_bb_data (file, s_preds, s_succs, 0);
|
||||
|
||||
/* Record where pseudo-registers are set.
|
||||
This data is kept accurate during each pass.
|
||||
|
@ -1374,6 +1374,7 @@ extern void recompute_reg_usage PROTO ((rtx));
|
||||
#ifdef BUFSIZ
|
||||
extern void dump_flow_info PROTO ((FILE *));
|
||||
#endif
|
||||
extern void free_bb_memory PROTO ((void));
|
||||
|
||||
/* In expmed.c */
|
||||
extern void init_expmed PROTO ((void));
|
||||
|
@ -3951,6 +3951,8 @@ rest_of_compilation (decl)
|
||||
|
||||
exit_rest_of_compilation:
|
||||
|
||||
free_bb_memory ();
|
||||
|
||||
/* In case the function was not output,
|
||||
don't leave any temporary anonymous types
|
||||
queued up for sdb output. */
|
||||
|
Loading…
Reference in New Issue
Block a user