tree-cfg.c (hashtab.h): Include.
* tree-cfg.c (hashtab.h): Include. (struct edge_to_case_leader_elt): New structure. (edge_to_case_leader): New. (edge_to_case_leader_hash): New hashtable hasing function. (edge_to_case_leader_eq): New hashtable equality function. (record_switch_edge): New function. (get_case_leader_for_edge, get_case_leader_for_edge): New functions. (make_switch_expr_edges): Build the edge-to-case-leader hash table. Tear down the hash table when we're done. (cleanup_dead_labels): Use CASE_LEADER_OR_LABEL instead of CASE_LABEL. (tree_node_can_be_shared): Allow sharing of CASE_LABEL_EXPR nodes. (tree_redirect_edge_and_branch, case SWITCH_EXPR): Update to use new concept of case leaders to reduce overhead of redirecting outgoing edges from switch statements. * tree.c (get_case_label): New function. * tree.h (CASE_LABEL): Define in terms of get_case_label. (CASE_LEADER_OR_LABEL): Define. From-SVN: r90570
This commit is contained in:
parent
2256aa1cff
commit
92b6dff302
4 changed files with 228 additions and 14 deletions
|
@ -1,3 +1,24 @@
|
|||
2004-11-12 Jeff Law <law@redhat.com>
|
||||
|
||||
* tree-cfg.c (hashtab.h): Include.
|
||||
(struct edge_to_case_leader_elt): New structure.
|
||||
(edge_to_case_leader): New.
|
||||
(edge_to_case_leader_hash): New hashtable hasing function.
|
||||
(edge_to_case_leader_eq): New hashtable equality function.
|
||||
(record_switch_edge): New function.
|
||||
(get_case_leader_for_edge, get_case_leader_for_edge): New functions.
|
||||
(make_switch_expr_edges): Build the edge-to-case-leader
|
||||
hash table. Tear down the hash table when we're done.
|
||||
(cleanup_dead_labels): Use CASE_LEADER_OR_LABEL instead of
|
||||
CASE_LABEL.
|
||||
(tree_node_can_be_shared): Allow sharing of CASE_LABEL_EXPR nodes.
|
||||
(tree_redirect_edge_and_branch, case SWITCH_EXPR): Update
|
||||
to use new concept of case leaders to reduce overhead of
|
||||
redirecting outgoing edges from switch statements.
|
||||
* tree.c (get_case_label): New function.
|
||||
* tree.h (CASE_LABEL): Define in terms of get_case_label.
|
||||
(CASE_LEADER_OR_LABEL): Define.
|
||||
|
||||
2004-11-12 Ziemowit Laski <zlaski@apple.com>
|
||||
|
||||
* varasm.c (output_addressed_constants): For CONST_DECLs,
|
||||
|
|
193
gcc/tree-cfg.c
193
gcc/tree-cfg.c
|
@ -44,6 +44,7 @@ Boston, MA 02111-1307, USA. */
|
|||
#include "except.h"
|
||||
#include "cfgloop.h"
|
||||
#include "cfglayout.h"
|
||||
#include "hashtab.h"
|
||||
|
||||
/* This file contains functions for building the Control Flow Graph (CFG)
|
||||
for a function tree. */
|
||||
|
@ -57,6 +58,30 @@ static const int initial_cfg_capacity = 20;
|
|||
building of the CFG in code with lots of gotos. */
|
||||
static GTY(()) varray_type label_to_block_map;
|
||||
|
||||
/* This hash table allows us to efficiently lookup the one and only one
|
||||
CASE_LABEL_EXPR which contains the LABEL_DECL for the target block
|
||||
of one or more case statements. Efficient access to this node
|
||||
allows us to efficiently update the case vector in response to
|
||||
edge redirections and similar operations.
|
||||
|
||||
Right now this is only used to set up case label leaders. In the
|
||||
future we hope to make this table more persistent and use it to
|
||||
more efficiently update case labels. */
|
||||
|
||||
struct edge_to_case_leader_elt
|
||||
{
|
||||
/* The edge itself. Necessary for hashing and equality tests. */
|
||||
edge e;
|
||||
|
||||
/* The "leader" for all the CASE_LABEL_EXPRs which transfer control
|
||||
to E->dest. When we change the destination of E, we will need to
|
||||
update the CASE_LEADER_OR_LABEL of this CASE_LABEL_EXPR (and no
|
||||
others). */
|
||||
tree case_label;
|
||||
};
|
||||
|
||||
static htab_t edge_to_case_leader;
|
||||
|
||||
/* CFG statistics. */
|
||||
struct cfg_stats_d
|
||||
{
|
||||
|
@ -576,6 +601,122 @@ make_cond_expr_edges (basic_block bb)
|
|||
make_edge (bb, else_bb, EDGE_FALSE_VALUE);
|
||||
}
|
||||
|
||||
/* Hashing routine for EDGE_TO_CASE_LEADER. */
|
||||
|
||||
static hashval_t
|
||||
edge_to_case_leader_hash (const void *p)
|
||||
{
|
||||
edge e = ((struct edge_to_case_leader_elt *)p)->e;
|
||||
|
||||
/* Hash on the edge itself (which is a pointer). */
|
||||
return htab_hash_pointer (e);
|
||||
}
|
||||
|
||||
/* Equality routine for EDGE_TO_CASE_LEADER, edges are unique, so testing
|
||||
for equality is just a pointer comparison. */
|
||||
|
||||
static int
|
||||
edge_to_case_leader_eq (const void *p1, const void *p2)
|
||||
{
|
||||
edge e1 = ((struct edge_to_case_leader_elt *)p1)->e;
|
||||
edge e2 = ((struct edge_to_case_leader_elt *)p2)->e;
|
||||
|
||||
return e1 == e2;
|
||||
}
|
||||
|
||||
/* Record that CASE_LABEL (a CASE_LABEL_EXPR) references edge E. */
|
||||
|
||||
static void
|
||||
record_switch_edge (edge e, tree case_label)
|
||||
{
|
||||
struct edge_to_case_leader_elt *elt;
|
||||
void **slot;
|
||||
|
||||
/* Build a hash table element so we can see if E is already
|
||||
in the table. */
|
||||
elt = xmalloc (sizeof (struct edge_to_case_leader_elt));
|
||||
elt->e = e;
|
||||
elt->case_label = case_label;
|
||||
|
||||
slot = htab_find_slot (edge_to_case_leader, elt, INSERT);
|
||||
|
||||
if (*slot == NULL)
|
||||
{
|
||||
/* E was not in the hash table. Install E into the hash table. */
|
||||
*slot = (void *)elt;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* E was already in the hash table. Free ELT as we do not need it
|
||||
anymore. */
|
||||
free (elt);
|
||||
|
||||
/* Get the entry stored in the hash table. */
|
||||
elt = (struct edge_to_case_leader_elt *) *slot;
|
||||
|
||||
/* Make ELT->case_label the leader for CASE_LABEL. */
|
||||
CASE_LEADER_OR_LABEL (case_label) = elt->case_label;
|
||||
}
|
||||
}
|
||||
|
||||
/* Subroutine of get_case_leader_for_edge; returns the case leader for the
|
||||
chain of CASE_LABEL_EXPRs associated with E using a hash table lookup. */
|
||||
|
||||
static tree
|
||||
get_case_leader_for_edge_hash (edge e)
|
||||
{
|
||||
struct edge_to_case_leader_elt elt, *elt_p;
|
||||
void **slot;
|
||||
|
||||
elt.e = e;
|
||||
elt.case_label = NULL;
|
||||
slot = htab_find_slot (edge_to_case_leader, &elt, NO_INSERT);
|
||||
|
||||
if (slot)
|
||||
{
|
||||
tree t;
|
||||
|
||||
elt_p = (struct edge_to_case_leader_elt *)*slot;
|
||||
t = elt_p->case_label;
|
||||
|
||||
while (TREE_CODE (CASE_LEADER_OR_LABEL (t)) == CASE_LABEL_EXPR)
|
||||
t = CASE_LEADER_OR_LABEL (t);
|
||||
return t;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Given an edge E, return the case leader for the chain of CASE_LABEL_EXPRs
|
||||
which use E. */
|
||||
|
||||
static tree
|
||||
get_case_leader_for_edge (edge e)
|
||||
{
|
||||
tree vec, stmt;
|
||||
size_t i, n;
|
||||
|
||||
/* If we have a hash table, then use it as it's significantly faster. */
|
||||
if (edge_to_case_leader)
|
||||
return get_case_leader_for_edge_hash (e);
|
||||
|
||||
/* No hash table. We have to walk the case vector. */
|
||||
stmt = bsi_stmt (bsi_last (e->src));
|
||||
vec = SWITCH_LABELS (stmt);
|
||||
n = TREE_VEC_LENGTH (vec);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
tree elt = TREE_VEC_ELT (vec, i);
|
||||
tree t = CASE_LEADER_OR_LABEL (elt);
|
||||
|
||||
if (TREE_CODE (t) == LABEL_DECL
|
||||
&& label_to_block (t) == e->dest)
|
||||
return elt;
|
||||
}
|
||||
|
||||
abort ();
|
||||
}
|
||||
|
||||
/* Create the edges for a SWITCH_EXPR starting at block BB.
|
||||
At this point, the switch body has been lowered and the
|
||||
|
@ -591,12 +732,22 @@ make_switch_expr_edges (basic_block bb)
|
|||
vec = SWITCH_LABELS (entry);
|
||||
n = TREE_VEC_LENGTH (vec);
|
||||
|
||||
edge_to_case_leader
|
||||
= htab_create (n, edge_to_case_leader_hash, edge_to_case_leader_eq, free);
|
||||
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
tree lab = CASE_LABEL (TREE_VEC_ELT (vec, i));
|
||||
basic_block label_bb = label_to_block (lab);
|
||||
make_edge (bb, label_bb, 0);
|
||||
edge e = make_edge (bb, label_bb, 0);
|
||||
|
||||
if (!e)
|
||||
e = find_edge (bb, label_bb);
|
||||
|
||||
record_switch_edge (e, TREE_VEC_ELT (vec, i));
|
||||
}
|
||||
htab_delete (edge_to_case_leader);
|
||||
edge_to_case_leader = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -865,9 +1016,11 @@ cleanup_dead_labels (void)
|
|||
|
||||
/* Replace all destination labels. */
|
||||
for (i = 0; i < n; ++i)
|
||||
CASE_LABEL (TREE_VEC_ELT (vec, i))
|
||||
= main_block_label (CASE_LABEL (TREE_VEC_ELT (vec, i)));
|
||||
|
||||
{
|
||||
tree elt = TREE_VEC_ELT (vec, i);
|
||||
tree label = main_block_label (CASE_LABEL (elt));
|
||||
CASE_LEADER_OR_LABEL (elt) = label;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -3246,6 +3399,9 @@ tree_node_can_be_shared (tree t)
|
|||
|| TREE_CODE (t) == SSA_NAME)
|
||||
return true;
|
||||
|
||||
if (TREE_CODE (t) == CASE_LABEL_EXPR)
|
||||
return true;
|
||||
|
||||
while (((TREE_CODE (t) == ARRAY_REF || TREE_CODE (t) == ARRAY_RANGE_REF)
|
||||
/* We check for constants explicitly since they are not considered
|
||||
gimple invariants if they overflowed. */
|
||||
|
@ -4134,17 +4290,32 @@ tree_redirect_edge_and_branch (edge e, basic_block dest)
|
|||
|
||||
case SWITCH_EXPR:
|
||||
{
|
||||
tree vec = SWITCH_LABELS (stmt);
|
||||
size_t i, n = TREE_VEC_LENGTH (vec);
|
||||
edge e2;
|
||||
|
||||
for (i = 0; i < n; ++i)
|
||||
/* We need to update the LABEL_DECL in the switch vector to
|
||||
reflect the edge redirection.
|
||||
|
||||
There is precisely one CASE_LABEL_EXPR in the switch vector
|
||||
which needs updating. Either its label needs to be updated
|
||||
or it needs to be directed to a new case leader. */
|
||||
e2 = find_edge (e->src, dest);
|
||||
if (e2)
|
||||
{
|
||||
tree elt = TREE_VEC_ELT (vec, i);
|
||||
if (label_to_block (CASE_LABEL (elt)) == e->dest)
|
||||
CASE_LABEL (elt) = label;
|
||||
/* In this case we need to change the case leader for the
|
||||
current leader of E to be the case leader for E2. */
|
||||
tree e_leader = get_case_leader_for_edge (e);
|
||||
tree e2_leader = get_case_leader_for_edge (e2);
|
||||
CASE_LEADER_OR_LABEL (e_leader) = e2_leader;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No edge exists from E->src to DEST, so we will simply
|
||||
change E->dest. The case leader does not change, but
|
||||
the LABEL_DECL for the leader does change. */
|
||||
CASE_LEADER_OR_LABEL (get_case_leader_for_edge (e)) = label;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case RETURN_EXPR:
|
||||
bsi_remove (&bsi);
|
||||
|
|
13
gcc/tree.c
13
gcc/tree.c
|
@ -6061,4 +6061,17 @@ signed_type_for (tree type)
|
|||
return lang_hooks.types.signed_type (type);
|
||||
}
|
||||
|
||||
/* Return the LABEL_DECL associated with T, which must be a
|
||||
CASE_LABEL_EXPR. This will walk through any CASE_LABEL_EXPRs
|
||||
appearing in operand 2 until it finds a CASE_LABEL_EXPR with
|
||||
a LABEL_DECL in operand 2. */
|
||||
|
||||
tree
|
||||
get_case_label (tree t)
|
||||
{
|
||||
while (TREE_CODE (CASE_LEADER_OR_LABEL (t)) == CASE_LABEL_EXPR)
|
||||
t = CASE_LEADER_OR_LABEL (t);
|
||||
return CASE_LEADER_OR_LABEL (t);
|
||||
}
|
||||
|
||||
#include "gt-tree.h"
|
||||
|
|
15
gcc/tree.h
15
gcc/tree.h
|
@ -1229,9 +1229,17 @@ struct tree_vec GTY(())
|
|||
|
||||
/* CASE_LABEL_EXPR accessors. These give access to the high and low values
|
||||
of a case label, respectively. */
|
||||
#define CASE_LOW(NODE) TREE_OPERAND ((NODE), 0)
|
||||
#define CASE_HIGH(NODE) TREE_OPERAND ((NODE), 1)
|
||||
#define CASE_LABEL(NODE) TREE_OPERAND ((NODE), 2)
|
||||
#define CASE_LOW(NODE) TREE_OPERAND ((NODE), 0)
|
||||
#define CASE_HIGH(NODE) TREE_OPERAND ((NODE), 1)
|
||||
|
||||
/* Operand 2 has two uses, it may either be a LABEL_DECL node or a
|
||||
another CASE_LABEL_EXPR node. This accessor gets direct access
|
||||
to that operand. Use it when you want to assign a value to
|
||||
operand 2 or when you want to conditionalalize actions based on
|
||||
whether operand 2 is a LABEL_DELC or CASE_LABEL_EXPR. */
|
||||
#define CASE_LEADER_OR_LABEL(NODE) TREE_OPERAND ((NODE), 2)
|
||||
|
||||
#define CASE_LABEL(NODE) get_case_label (NODE)
|
||||
|
||||
/* The operands of a BIND_EXPR. */
|
||||
#define BIND_EXPR_VARS(NODE) (TREE_OPERAND (BIND_EXPR_CHECK (NODE), 0))
|
||||
|
@ -3455,6 +3463,7 @@ extern void change_decl_assembler_name (tree, tree);
|
|||
extern int type_num_arguments (tree);
|
||||
extern bool associative_tree_code (enum tree_code);
|
||||
extern bool commutative_tree_code (enum tree_code);
|
||||
extern tree get_case_label (tree);
|
||||
|
||||
|
||||
/* In stmt.c */
|
||||
|
|
Loading…
Add table
Reference in a new issue