886 lines
27 KiB
C++
886 lines
27 KiB
C++
/* Tree switch conversion for GNU compiler.
|
|
Copyright (C) 2017-2019 Free Software Foundation, Inc.
|
|
|
|
This file is part of GCC.
|
|
|
|
GCC is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free
|
|
Software Foundation; either version 3, 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 COPYING3. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#ifndef TREE_SWITCH_CONVERSION_H
|
|
#define TREE_SWITCH_CONVERSION_H
|
|
|
|
namespace tree_switch_conversion {
|
|
|
|
/* Type of cluster. */
|
|
|
|
enum cluster_type
|
|
{
|
|
SIMPLE_CASE,
|
|
JUMP_TABLE,
|
|
BIT_TEST
|
|
};
|
|
|
|
#define PRINT_CASE(f,c) print_generic_expr (f, c)
|
|
|
|
/* Abstract base class for representing a cluster of cases.
|
|
|
|
Here is the inheritance hierarachy, and the enum_cluster_type
|
|
values for the concrete subclasses:
|
|
|
|
cluster
|
|
|-simple_cluster (SIMPLE_CASE)
|
|
`-group_cluster
|
|
|-jump_table_cluster (JUMP_TABLE)
|
|
`-bit_test_cluster (BIT_TEST). */
|
|
|
|
struct cluster
|
|
{
|
|
/* Constructor. */
|
|
cluster (tree case_label_expr, basic_block case_bb, profile_probability prob,
|
|
profile_probability subtree_prob);
|
|
|
|
/* Destructor. */
|
|
virtual ~cluster ()
|
|
{}
|
|
|
|
/* Return type. */
|
|
virtual cluster_type get_type () = 0;
|
|
|
|
/* Get low value covered by a cluster. */
|
|
virtual tree get_low () = 0;
|
|
|
|
/* Get high value covered by a cluster. */
|
|
virtual tree get_high () = 0;
|
|
|
|
/* Debug content of a cluster. */
|
|
virtual void debug () = 0;
|
|
|
|
/* Dump content of a cluster. */
|
|
virtual void dump (FILE *f, bool details = false) = 0;
|
|
|
|
/* Emit GIMPLE code to handle the cluster. */
|
|
virtual void emit (tree, tree, tree, basic_block) = 0;
|
|
|
|
/* Return true if a cluster handles only a single case value and the
|
|
value is not a range. */
|
|
virtual bool is_single_value_p ()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/* Return range of a cluster. If value would overflow in type of LOW,
|
|
then return 0. */
|
|
static unsigned HOST_WIDE_INT get_range (tree low, tree high)
|
|
{
|
|
tree r = fold_build2 (MINUS_EXPR, TREE_TYPE (low), high, low);
|
|
if (!tree_fits_uhwi_p (r))
|
|
return 0;
|
|
|
|
return tree_to_uhwi (r) + 1;
|
|
}
|
|
|
|
/* Case label. */
|
|
tree m_case_label_expr;
|
|
|
|
/* Basic block of the case. */
|
|
basic_block m_case_bb;
|
|
|
|
/* Probability of taking this cluster. */
|
|
profile_probability m_prob;
|
|
|
|
/* Probability of reaching subtree rooted at this node. */
|
|
profile_probability m_subtree_prob;
|
|
|
|
protected:
|
|
/* Default constructor. */
|
|
cluster () {}
|
|
};
|
|
|
|
cluster::cluster (tree case_label_expr, basic_block case_bb,
|
|
profile_probability prob, profile_probability subtree_prob):
|
|
m_case_label_expr (case_label_expr), m_case_bb (case_bb), m_prob (prob),
|
|
m_subtree_prob (subtree_prob)
|
|
{
|
|
}
|
|
|
|
/* Subclass of cluster representing a simple contiguous range
|
|
from [low..high]. */
|
|
|
|
struct simple_cluster: public cluster
|
|
{
|
|
/* Constructor. */
|
|
simple_cluster (tree low, tree high, tree case_label_expr,
|
|
basic_block case_bb, profile_probability prob);
|
|
|
|
/* Destructor. */
|
|
~simple_cluster ()
|
|
{}
|
|
|
|
cluster_type
|
|
get_type ()
|
|
{
|
|
return SIMPLE_CASE;
|
|
}
|
|
|
|
tree
|
|
get_low ()
|
|
{
|
|
return m_low;
|
|
}
|
|
|
|
tree
|
|
get_high ()
|
|
{
|
|
return m_high;
|
|
}
|
|
|
|
void
|
|
debug ()
|
|
{
|
|
dump (stderr);
|
|
}
|
|
|
|
void
|
|
dump (FILE *f, bool details ATTRIBUTE_UNUSED = false)
|
|
{
|
|
PRINT_CASE (f, get_low ());
|
|
if (get_low () != get_high ())
|
|
{
|
|
fprintf (f, "-");
|
|
PRINT_CASE (f, get_high ());
|
|
}
|
|
fprintf (f, " ");
|
|
}
|
|
|
|
void emit (tree, tree, tree, basic_block)
|
|
{
|
|
gcc_unreachable ();
|
|
}
|
|
|
|
bool is_single_value_p ()
|
|
{
|
|
return tree_int_cst_equal (get_low (), get_high ());
|
|
}
|
|
|
|
/* Low value of the case. */
|
|
tree m_low;
|
|
|
|
/* High value of the case. */
|
|
tree m_high;
|
|
|
|
/* True if case is a range. */
|
|
bool m_range_p;
|
|
};
|
|
|
|
simple_cluster::simple_cluster (tree low, tree high, tree case_label_expr,
|
|
basic_block case_bb, profile_probability prob):
|
|
cluster (case_label_expr, case_bb, prob, prob),
|
|
m_low (low), m_high (high)
|
|
{
|
|
m_range_p = m_high != NULL;
|
|
if (m_high == NULL)
|
|
m_high = m_low;
|
|
}
|
|
|
|
/* Abstract subclass of jump table and bit test cluster,
|
|
handling a collection of simple_cluster instances. */
|
|
|
|
struct group_cluster: public cluster
|
|
{
|
|
/* Constructor. */
|
|
group_cluster (vec<cluster *> &clusters, unsigned start, unsigned end);
|
|
|
|
/* Destructor. */
|
|
~group_cluster ();
|
|
|
|
tree
|
|
get_low ()
|
|
{
|
|
return m_cases[0]->get_low ();
|
|
}
|
|
|
|
tree
|
|
get_high ()
|
|
{
|
|
return m_cases[m_cases.length () - 1]->get_high ();
|
|
}
|
|
|
|
void
|
|
debug ()
|
|
{
|
|
dump (stderr);
|
|
}
|
|
|
|
void dump (FILE *f, bool details = false);
|
|
|
|
/* List of simple clusters handled by the group. */
|
|
vec<simple_cluster *> m_cases;
|
|
};
|
|
|
|
/* Concrete subclass of group_cluster representing a collection
|
|
of cases to be implemented as a jump table.
|
|
The "emit" vfunc gernerates a nested switch statement which
|
|
is later lowered to a jump table. */
|
|
|
|
struct jump_table_cluster: public group_cluster
|
|
{
|
|
/* Constructor. */
|
|
jump_table_cluster (vec<cluster *> &clusters, unsigned start, unsigned end)
|
|
: group_cluster (clusters, start, end)
|
|
{}
|
|
|
|
cluster_type
|
|
get_type ()
|
|
{
|
|
return JUMP_TABLE;
|
|
}
|
|
|
|
void emit (tree index_expr, tree index_type,
|
|
tree default_label_expr, basic_block default_bb);
|
|
|
|
/* Find jump tables of given CLUSTERS, where all members of the vector
|
|
are of type simple_cluster. New clusters are returned. */
|
|
static vec<cluster *> find_jump_tables (vec<cluster *> &clusters);
|
|
|
|
/* Return true when cluster starting at START and ending at END (inclusive)
|
|
can build a jump-table. */
|
|
static bool can_be_handled (const vec<cluster *> &clusters, unsigned start,
|
|
unsigned end);
|
|
|
|
/* Return true if cluster starting at START and ending at END (inclusive)
|
|
is profitable transformation. */
|
|
static bool is_beneficial (const vec<cluster *> &clusters, unsigned start,
|
|
unsigned end);
|
|
|
|
/* Return the smallest number of different values for which it is best
|
|
to use a jump-table instead of a tree of conditional branches. */
|
|
static inline unsigned int case_values_threshold (void);
|
|
|
|
/* Return whether jump table expansion is allowed. */
|
|
static bool is_enabled (void);
|
|
|
|
/* Max growth ratio for code that is optimized for size. */
|
|
static const unsigned HOST_WIDE_INT max_ratio_for_size = 3;
|
|
|
|
/* Max growth ratio for code that is optimized for speed. */
|
|
static const unsigned HOST_WIDE_INT max_ratio_for_speed = 8;
|
|
};
|
|
|
|
/* A GIMPLE switch statement can be expanded to a short sequence of bit-wise
|
|
comparisons. "switch(x)" is converted into "if ((1 << (x-MINVAL)) & CST)"
|
|
where CST and MINVAL are integer constants. This is better than a series
|
|
of compare-and-banch insns in some cases, e.g. we can implement:
|
|
|
|
if ((x==4) || (x==6) || (x==9) || (x==11))
|
|
|
|
as a single bit test:
|
|
|
|
if ((1<<x) & ((1<<4)|(1<<6)|(1<<9)|(1<<11)))
|
|
|
|
This transformation is only applied if the number of case targets is small,
|
|
if CST constains at least 3 bits, and "1 << x" is cheap. The bit tests are
|
|
performed in "word_mode".
|
|
|
|
The following example shows the code the transformation generates:
|
|
|
|
int bar(int x)
|
|
{
|
|
switch (x)
|
|
{
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
case '5': case '6': case '7': case '8': case '9':
|
|
case 'A': case 'B': case 'C': case 'D': case 'E':
|
|
case 'F':
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
==>
|
|
|
|
bar (int x)
|
|
{
|
|
tmp1 = x - 48;
|
|
if (tmp1 > (70 - 48)) goto L2;
|
|
tmp2 = 1 << tmp1;
|
|
tmp3 = 0b11111100000001111111111;
|
|
if ((tmp2 & tmp3) != 0) goto L1 ; else goto L2;
|
|
L1:
|
|
return 1;
|
|
L2:
|
|
return 0;
|
|
}
|
|
|
|
TODO: There are still some improvements to this transformation that could
|
|
be implemented:
|
|
|
|
* A narrower mode than word_mode could be used if that is cheaper, e.g.
|
|
for x86_64 where a narrower-mode shift may result in smaller code.
|
|
|
|
* The compounded constant could be shifted rather than the one. The
|
|
test would be either on the sign bit or on the least significant bit,
|
|
depending on the direction of the shift. On some machines, the test
|
|
for the branch would be free if the bit to test is already set by the
|
|
shift operation.
|
|
|
|
This transformation was contributed by Roger Sayle, see this e-mail:
|
|
http://gcc.gnu.org/ml/gcc-patches/2003-01/msg01950.html
|
|
*/
|
|
|
|
struct bit_test_cluster: public group_cluster
|
|
{
|
|
/* Constructor. */
|
|
bit_test_cluster (vec<cluster *> &clusters, unsigned start, unsigned end,
|
|
bool handles_entire_switch)
|
|
:group_cluster (clusters, start, end),
|
|
m_handles_entire_switch (handles_entire_switch)
|
|
{}
|
|
|
|
cluster_type
|
|
get_type ()
|
|
{
|
|
return BIT_TEST;
|
|
}
|
|
|
|
/* Expand a switch statement by a short sequence of bit-wise
|
|
comparisons. "switch(x)" is effectively converted into
|
|
"if ((1 << (x-MINVAL)) & CST)" where CST and MINVAL are
|
|
integer constants.
|
|
|
|
INDEX_EXPR is the value being switched on.
|
|
|
|
MINVAL is the lowest case value of in the case nodes,
|
|
and RANGE is highest value minus MINVAL. MINVAL and RANGE
|
|
are not guaranteed to be of the same type as INDEX_EXPR
|
|
(the gimplifier doesn't change the type of case label values,
|
|
and MINVAL and RANGE are derived from those values).
|
|
MAXVAL is MINVAL + RANGE.
|
|
|
|
There *MUST* be max_case_bit_tests or less unique case
|
|
node targets. */
|
|
void emit (tree index_expr, tree index_type,
|
|
tree default_label_expr, basic_block default_bb);
|
|
|
|
/* Find bit tests of given CLUSTERS, where all members of the vector
|
|
are of type simple_cluster. New clusters are returned. */
|
|
static vec<cluster *> find_bit_tests (vec<cluster *> &clusters);
|
|
|
|
/* Return true when RANGE of case values with UNIQ labels
|
|
can build a bit test. */
|
|
static bool can_be_handled (unsigned HOST_WIDE_INT range, unsigned uniq);
|
|
|
|
/* Return true when cluster starting at START and ending at END (inclusive)
|
|
can build a bit test. */
|
|
static bool can_be_handled (const vec<cluster *> &clusters, unsigned start,
|
|
unsigned end);
|
|
|
|
/* Return true when COUNT of cases of UNIQ labels is beneficial for bit test
|
|
transformation. */
|
|
static bool is_beneficial (unsigned count, unsigned uniq);
|
|
|
|
/* Return true if cluster starting at START and ending at END (inclusive)
|
|
is profitable transformation. */
|
|
static bool is_beneficial (const vec<cluster *> &clusters, unsigned start,
|
|
unsigned end);
|
|
|
|
/* Split the basic block at the statement pointed to by GSIP, and insert
|
|
a branch to the target basic block of E_TRUE conditional on tree
|
|
expression COND.
|
|
|
|
It is assumed that there is already an edge from the to-be-split
|
|
basic block to E_TRUE->dest block. This edge is removed, and the
|
|
profile information on the edge is re-used for the new conditional
|
|
jump.
|
|
|
|
The CFG is updated. The dominator tree will not be valid after
|
|
this transformation, but the immediate dominators are updated if
|
|
UPDATE_DOMINATORS is true.
|
|
|
|
Returns the newly created basic block. */
|
|
static basic_block hoist_edge_and_branch_if_true (gimple_stmt_iterator *gsip,
|
|
tree cond,
|
|
basic_block case_bb,
|
|
profile_probability prob);
|
|
|
|
/* True when the jump table handles an entire switch statement. */
|
|
bool m_handles_entire_switch;
|
|
|
|
/* Maximum number of different basic blocks that can be handled by
|
|
a bit test. */
|
|
static const int m_max_case_bit_tests = 3;
|
|
};
|
|
|
|
/* Helper struct to find minimal clusters. */
|
|
|
|
struct min_cluster_item
|
|
{
|
|
/* Constructor. */
|
|
min_cluster_item (unsigned count, unsigned start, unsigned non_jt_cases):
|
|
m_count (count), m_start (start), m_non_jt_cases (non_jt_cases)
|
|
{}
|
|
|
|
/* Count of clusters. */
|
|
unsigned m_count;
|
|
|
|
/* Index where is cluster boundary. */
|
|
unsigned m_start;
|
|
|
|
/* Total number of cases that will not be in a jump table. */
|
|
unsigned m_non_jt_cases;
|
|
};
|
|
|
|
/* Helper struct to represent switch decision tree. */
|
|
|
|
struct case_tree_node
|
|
{
|
|
/* Empty Constructor. */
|
|
case_tree_node ();
|
|
|
|
/* Return true when it has a child. */
|
|
bool has_child ()
|
|
{
|
|
return m_left != NULL || m_right != NULL;
|
|
}
|
|
|
|
/* Left son in binary tree. */
|
|
case_tree_node *m_left;
|
|
|
|
/* Right son in binary tree; also node chain. */
|
|
case_tree_node *m_right;
|
|
|
|
/* Parent of node in binary tree. */
|
|
case_tree_node *m_parent;
|
|
|
|
/* Cluster represented by this tree node. */
|
|
cluster *m_c;
|
|
};
|
|
|
|
inline
|
|
case_tree_node::case_tree_node ():
|
|
m_left (NULL), m_right (NULL), m_parent (NULL), m_c (NULL)
|
|
{
|
|
}
|
|
|
|
unsigned int
|
|
jump_table_cluster::case_values_threshold (void)
|
|
{
|
|
unsigned int threshold = PARAM_VALUE (PARAM_CASE_VALUES_THRESHOLD);
|
|
|
|
if (threshold == 0)
|
|
threshold = targetm.case_values_threshold ();
|
|
|
|
return threshold;
|
|
}
|
|
|
|
/* Return whether jump table expansion is allowed. */
|
|
bool jump_table_cluster::is_enabled (void)
|
|
{
|
|
/* If neither casesi or tablejump is available, or flag_jump_tables
|
|
over-ruled us, we really have no choice. */
|
|
if (!targetm.have_casesi () && !targetm.have_tablejump ())
|
|
return false;
|
|
if (!flag_jump_tables)
|
|
return false;
|
|
#ifndef ASM_OUTPUT_ADDR_DIFF_ELT
|
|
if (flag_pic)
|
|
return false;
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
/* A case_bit_test represents a set of case nodes that may be
|
|
selected from using a bit-wise comparison. HI and LO hold
|
|
the integer to be tested against, TARGET_EDGE contains the
|
|
edge to the basic block to jump to upon success and BITS
|
|
counts the number of case nodes handled by this test,
|
|
typically the number of bits set in HI:LO. The LABEL field
|
|
is used to quickly identify all cases in this set without
|
|
looking at label_to_block for every case label. */
|
|
|
|
struct case_bit_test
|
|
{
|
|
wide_int mask;
|
|
basic_block target_bb;
|
|
tree label;
|
|
int bits;
|
|
|
|
/* Comparison function for qsort to order bit tests by decreasing
|
|
probability of execution. */
|
|
static int cmp (const void *p1, const void *p2);
|
|
};
|
|
|
|
struct switch_decision_tree
|
|
{
|
|
/* Constructor. */
|
|
switch_decision_tree (gswitch *swtch): m_switch (swtch), m_phi_mapping (),
|
|
m_case_bbs (), m_case_node_pool ("struct case_node pool"),
|
|
m_case_list (NULL)
|
|
{
|
|
}
|
|
|
|
/* Analyze switch statement and return true when the statement is expanded
|
|
as decision tree. */
|
|
bool analyze_switch_statement ();
|
|
|
|
/* Attempt to expand CLUSTERS as a decision tree. Return true when
|
|
expanded. */
|
|
bool try_switch_expansion (vec<cluster *> &clusters);
|
|
/* Compute the number of case labels that correspond to each outgoing edge of
|
|
switch statement. Record this information in the aux field of the edge.
|
|
*/
|
|
void compute_cases_per_edge ();
|
|
|
|
/* Before switch transformation, record all SSA_NAMEs defined in switch BB
|
|
and used in a label basic block. */
|
|
void record_phi_operand_mapping ();
|
|
|
|
/* Append new operands to PHI statements that were introduced due to
|
|
addition of new edges to case labels. */
|
|
void fix_phi_operands_for_edges ();
|
|
|
|
/* Generate a decision tree, switching on INDEX_EXPR and jumping to
|
|
one of the labels in CASE_LIST or to the DEFAULT_LABEL.
|
|
|
|
We generate a binary decision tree to select the appropriate target
|
|
code. */
|
|
void emit (basic_block bb, tree index_expr,
|
|
profile_probability default_prob, tree index_type);
|
|
|
|
/* Emit step-by-step code to select a case for the value of INDEX.
|
|
The thus generated decision tree follows the form of the
|
|
case-node binary tree NODE, whose nodes represent test conditions.
|
|
DEFAULT_PROB is probability of cases leading to default BB.
|
|
INDEX_TYPE is the type of the index of the switch. */
|
|
basic_block emit_case_nodes (basic_block bb, tree index,
|
|
case_tree_node *node,
|
|
profile_probability default_prob,
|
|
tree index_type, location_t);
|
|
|
|
/* Take an ordered list of case nodes
|
|
and transform them into a near optimal binary tree,
|
|
on the assumption that any target code selection value is as
|
|
likely as any other.
|
|
|
|
The transformation is performed by splitting the ordered
|
|
list into two equal sections plus a pivot. The parts are
|
|
then attached to the pivot as left and right branches. Each
|
|
branch is then transformed recursively. */
|
|
static void balance_case_nodes (case_tree_node **head,
|
|
case_tree_node *parent);
|
|
|
|
/* Dump ROOT, a list or tree of case nodes, to file F. */
|
|
static void dump_case_nodes (FILE *f, case_tree_node *root, int indent_step,
|
|
int indent_level);
|
|
|
|
/* Add an unconditional jump to CASE_BB that happens in basic block BB. */
|
|
static void emit_jump (basic_block bb, basic_block case_bb);
|
|
|
|
/* Generate code to compare OP0 with OP1 so that the condition codes are
|
|
set and to jump to LABEL_BB if the condition is true.
|
|
COMPARISON is the GIMPLE comparison (EQ, NE, GT, etc.).
|
|
PROB is the probability of jumping to LABEL_BB. */
|
|
static basic_block emit_cmp_and_jump_insns (basic_block bb, tree op0,
|
|
tree op1, tree_code comparison,
|
|
basic_block label_bb,
|
|
profile_probability prob,
|
|
location_t);
|
|
|
|
/* Generate code to jump to LABEL if OP0 and OP1 are equal in mode MODE.
|
|
PROB is the probability of jumping to LABEL_BB. */
|
|
static basic_block do_jump_if_equal (basic_block bb, tree op0, tree op1,
|
|
basic_block label_bb,
|
|
profile_probability prob,
|
|
location_t);
|
|
|
|
/* Reset the aux field of all outgoing edges of switch basic block. */
|
|
static inline void reset_out_edges_aux (gswitch *swtch);
|
|
|
|
/* Switch statement. */
|
|
gswitch *m_switch;
|
|
|
|
/* Map of PHI nodes that have to be fixed after expansion. */
|
|
hash_map<tree, tree> m_phi_mapping;
|
|
|
|
/* List of basic blocks that belong to labels of the switch. */
|
|
auto_vec<basic_block> m_case_bbs;
|
|
|
|
/* Basic block with default label. */
|
|
basic_block m_default_bb;
|
|
|
|
/* A pool for case nodes. */
|
|
object_allocator<case_tree_node> m_case_node_pool;
|
|
|
|
/* Balanced tree of case nodes. */
|
|
case_tree_node *m_case_list;
|
|
};
|
|
|
|
/*
|
|
Switch initialization conversion
|
|
|
|
The following pass changes simple initializations of scalars in a switch
|
|
statement into initializations from a static array. Obviously, the values
|
|
must be constant and known at compile time and a default branch must be
|
|
provided. For example, the following code:
|
|
|
|
int a,b;
|
|
|
|
switch (argc)
|
|
{
|
|
case 1:
|
|
case 2:
|
|
a_1 = 8;
|
|
b_1 = 6;
|
|
break;
|
|
case 3:
|
|
a_2 = 9;
|
|
b_2 = 5;
|
|
break;
|
|
case 12:
|
|
a_3 = 10;
|
|
b_3 = 4;
|
|
break;
|
|
default:
|
|
a_4 = 16;
|
|
b_4 = 1;
|
|
break;
|
|
}
|
|
a_5 = PHI <a_1, a_2, a_3, a_4>
|
|
b_5 = PHI <b_1, b_2, b_3, b_4>
|
|
|
|
|
|
is changed into:
|
|
|
|
static const int = CSWTCH01[] = {6, 6, 5, 1, 1, 1, 1, 1, 1, 1, 1, 4};
|
|
static const int = CSWTCH02[] = {8, 8, 9, 16, 16, 16, 16, 16, 16, 16,
|
|
16, 16, 10};
|
|
|
|
if (((unsigned) argc) - 1 < 11)
|
|
{
|
|
a_6 = CSWTCH02[argc - 1];
|
|
b_6 = CSWTCH01[argc - 1];
|
|
}
|
|
else
|
|
{
|
|
a_7 = 16;
|
|
b_7 = 1;
|
|
}
|
|
a_5 = PHI <a_6, a_7>
|
|
b_b = PHI <b_6, b_7>
|
|
|
|
There are further constraints. Specifically, the range of values across all
|
|
case labels must not be bigger than SWITCH_CONVERSION_BRANCH_RATIO (default
|
|
eight) times the number of the actual switch branches.
|
|
|
|
This transformation was contributed by Martin Jambor, see this e-mail:
|
|
http://gcc.gnu.org/ml/gcc-patches/2008-07/msg00011.html */
|
|
|
|
/* The main structure of the pass. */
|
|
struct switch_conversion
|
|
{
|
|
/* Constructor. */
|
|
switch_conversion ();
|
|
|
|
/* Destructor. */
|
|
~switch_conversion ();
|
|
|
|
/* The following function is invoked on every switch statement (the current
|
|
one is given in SWTCH) and runs the individual phases of switch
|
|
conversion on it one after another until one fails or the conversion
|
|
is completed. On success, NULL is in m_reason, otherwise points
|
|
to a string with the reason why the conversion failed. */
|
|
void expand (gswitch *swtch);
|
|
|
|
/* Collection information about SWTCH statement. */
|
|
void collect (gswitch *swtch);
|
|
|
|
/* Checks whether the range given by individual case statements of the switch
|
|
switch statement isn't too big and whether the number of branches actually
|
|
satisfies the size of the new array. */
|
|
bool check_range ();
|
|
|
|
/* Checks whether all but the final BB basic blocks are empty. */
|
|
bool check_all_empty_except_final ();
|
|
|
|
/* This function checks whether all required values in phi nodes in final_bb
|
|
are constants. Required values are those that correspond to a basic block
|
|
which is a part of the examined switch statement. It returns true if the
|
|
phi nodes are OK, otherwise false. */
|
|
bool check_final_bb ();
|
|
|
|
/* The following function allocates default_values, target_{in,out}_names and
|
|
constructors arrays. The last one is also populated with pointers to
|
|
vectors that will become constructors of new arrays. */
|
|
void create_temp_arrays ();
|
|
|
|
/* Populate the array of default values in the order of phi nodes.
|
|
DEFAULT_CASE is the CASE_LABEL_EXPR for the default switch branch
|
|
if the range is non-contiguous or the default case has standard
|
|
structure, otherwise it is the first non-default case instead. */
|
|
void gather_default_values (tree default_case);
|
|
|
|
/* The following function populates the vectors in the constructors array with
|
|
future contents of the static arrays. The vectors are populated in the
|
|
order of phi nodes. */
|
|
void build_constructors ();
|
|
|
|
/* If all values in the constructor vector are products of a linear function
|
|
a * x + b, then return true. When true, COEFF_A and COEFF_B and
|
|
coefficients of the linear function. Note that equal values are special
|
|
case of a linear function with a and b equal to zero. */
|
|
bool contains_linear_function_p (vec<constructor_elt, va_gc> *vec,
|
|
wide_int *coeff_a, wide_int *coeff_b);
|
|
|
|
/* Return type which should be used for array elements, either TYPE's
|
|
main variant or, for integral types, some smaller integral type
|
|
that can still hold all the constants. */
|
|
tree array_value_type (tree type, int num);
|
|
|
|
/* Create an appropriate array type and declaration and assemble a static
|
|
array variable. Also create a load statement that initializes
|
|
the variable in question with a value from the static array. SWTCH is
|
|
the switch statement being converted, NUM is the index to
|
|
arrays of constructors, default values and target SSA names
|
|
for this particular array. ARR_INDEX_TYPE is the type of the index
|
|
of the new array, PHI is the phi node of the final BB that corresponds
|
|
to the value that will be loaded from the created array. TIDX
|
|
is an ssa name of a temporary variable holding the index for loads from the
|
|
new array. */
|
|
void build_one_array (int num, tree arr_index_type,
|
|
gphi *phi, tree tidx);
|
|
|
|
/* Builds and initializes static arrays initialized with values gathered from
|
|
the switch statement. Also creates statements that load values from
|
|
them. */
|
|
void build_arrays ();
|
|
|
|
/* Generates and appropriately inserts loads of default values at the position
|
|
given by GSI. Returns the last inserted statement. */
|
|
gassign *gen_def_assigns (gimple_stmt_iterator *gsi);
|
|
|
|
/* Deletes the unused bbs and edges that now contain the switch statement and
|
|
its empty branch bbs. BBD is the now dead BB containing
|
|
the original switch statement, FINAL is the last BB of the converted
|
|
switch statement (in terms of succession). */
|
|
void prune_bbs (basic_block bbd, basic_block final, basic_block default_bb);
|
|
|
|
/* Add values to phi nodes in final_bb for the two new edges. E1F is the edge
|
|
from the basic block loading values from an array and E2F from the basic
|
|
block loading default values. BBF is the last switch basic block (see the
|
|
bbf description in the comment below). */
|
|
void fix_phi_nodes (edge e1f, edge e2f, basic_block bbf);
|
|
|
|
/* Creates a check whether the switch expression value actually falls into the
|
|
range given by all the cases. If it does not, the temporaries are loaded
|
|
with default values instead. */
|
|
void gen_inbound_check ();
|
|
|
|
/* Switch statement for which switch conversion takes place. */
|
|
gswitch *m_switch;
|
|
|
|
/* The expression used to decide the switch branch. */
|
|
tree m_index_expr;
|
|
|
|
/* The following integer constants store the minimum and maximum value
|
|
covered by the case labels. */
|
|
tree m_range_min;
|
|
tree m_range_max;
|
|
|
|
/* The difference between the above two numbers. Stored here because it
|
|
is used in all the conversion heuristics, as well as for some of the
|
|
transformation, and it is expensive to re-compute it all the time. */
|
|
tree m_range_size;
|
|
|
|
/* Basic block that contains the actual GIMPLE_SWITCH. */
|
|
basic_block m_switch_bb;
|
|
|
|
/* Basic block that is the target of the default case. */
|
|
basic_block m_default_bb;
|
|
|
|
/* The single successor block of all branches out of the GIMPLE_SWITCH,
|
|
if such a block exists. Otherwise NULL. */
|
|
basic_block m_final_bb;
|
|
|
|
/* The probability of the default edge in the replaced switch. */
|
|
profile_probability m_default_prob;
|
|
|
|
/* The count of the default edge in the replaced switch. */
|
|
profile_count m_default_count;
|
|
|
|
/* Combined count of all other (non-default) edges in the replaced switch. */
|
|
profile_count m_other_count;
|
|
|
|
/* Number of phi nodes in the final bb (that we'll be replacing). */
|
|
int m_phi_count;
|
|
|
|
/* Constructors of new static arrays. */
|
|
vec<constructor_elt, va_gc> **m_constructors;
|
|
|
|
/* Array of default values, in the same order as phi nodes. */
|
|
tree *m_default_values;
|
|
|
|
/* Array of ssa names that are initialized with a value from a new static
|
|
array. */
|
|
tree *m_target_inbound_names;
|
|
|
|
/* Array of ssa names that are initialized with the default value if the
|
|
switch expression is out of range. */
|
|
tree *m_target_outbound_names;
|
|
|
|
/* VOP SSA_NAME. */
|
|
tree m_target_vop;
|
|
|
|
/* The first load statement that loads a temporary from a new static array.
|
|
*/
|
|
gimple *m_arr_ref_first;
|
|
|
|
/* The last load statement that loads a temporary from a new static array. */
|
|
gimple *m_arr_ref_last;
|
|
|
|
/* String reason why the case wasn't a good candidate that is written to the
|
|
dump file, if there is one. */
|
|
const char *m_reason;
|
|
|
|
/* True if default case is not used for any value between range_min and
|
|
range_max inclusive. */
|
|
bool m_contiguous_range;
|
|
|
|
/* True if default case does not have the required shape for other case
|
|
labels. */
|
|
bool m_default_case_nonstandard;
|
|
|
|
/* Number of uniq labels for non-default edges. */
|
|
unsigned int m_uniq;
|
|
|
|
/* Count is number of non-default edges. */
|
|
unsigned int m_count;
|
|
|
|
/* True if CFG has been changed. */
|
|
bool m_cfg_altered;
|
|
};
|
|
|
|
void
|
|
switch_decision_tree::reset_out_edges_aux (gswitch *swtch)
|
|
{
|
|
basic_block bb = gimple_bb (swtch);
|
|
edge e;
|
|
edge_iterator ei;
|
|
FOR_EACH_EDGE (e, ei, bb->succs)
|
|
e->aux = (void *) 0;
|
|
}
|
|
|
|
} // tree_switch_conversion namespace
|
|
|
|
#endif // TREE_SWITCH_CONVERSION_H
|