Add if-chain to switch conversion pass.

gcc/ChangeLog:

	PR tree-optimization/14799
	PR ipa/88702
	* Makefile.in: Add gimple-if-to-switch.o.
	* dbgcnt.def (DEBUG_COUNTER): Add new debug counter.
	* passes.def: Include new pass_if_to_switch pass.
	* timevar.def (TV_TREE_IF_TO_SWITCH): New timevar.
	* tree-pass.h (make_pass_if_to_switch): New.
	* tree-ssa-reassoc.c (struct operand_entry): Move to the header.
	(dump_range_entry): Move to header file.
	(debug_range_entry): Likewise.
	(no_side_effect_bb): Make it global.
	* tree-switch-conversion.h (simple_cluster::simple_cluster):
	Add inline for couple of functions in order to prevent error
	about multiple defined symbols.
	* gimple-if-to-switch.cc: New file.
	* tree-ssa-reassoc.h: New file.

gcc/testsuite/ChangeLog:

	PR tree-optimization/14799
	PR ipa/88702
	* gcc.dg/tree-ssa/pr96480.c: Disable if-to-switch conversion.
	* gcc.dg/tree-ssa/reassoc-32.c: Likewise.
	* g++.dg/tree-ssa/if-to-switch-1.C: New test.
	* gcc.dg/tree-ssa/if-to-switch-1.c: New test.
	* gcc.dg/tree-ssa/if-to-switch-2.c: New test.
	* gcc.dg/tree-ssa/if-to-switch-3.c: New test.
	* gcc.dg/tree-ssa/if-to-switch-4.c: New test.
	* gcc.dg/tree-ssa/if-to-switch-5.c: New test.
	* gcc.dg/tree-ssa/if-to-switch-6.c: New test.
	* gcc.dg/tree-ssa/if-to-switch-7.c: New test.
	* gcc.dg/tree-ssa/if-to-switch-8.c: New test.
This commit is contained in:
Martin Liska 2020-08-28 10:26:13 +02:00
parent 58f71a34c6
commit 03eb09292e
20 changed files with 866 additions and 32 deletions

View File

@ -1364,6 +1364,7 @@ OBJS = \
gimple-array-bounds.o \
gimple-builder.o \
gimple-expr.o \
gimple-if-to-switch.o \
gimple-iterator.o \
gimple-fold.o \
gimple-laddress.o \

View File

@ -170,6 +170,7 @@ DEBUG_COUNTER (if_after_combine)
DEBUG_COUNTER (if_after_reload)
DEBUG_COUNTER (if_conversion)
DEBUG_COUNTER (if_conversion_tree)
DEBUG_COUNTER (if_to_switch)
DEBUG_COUNTER (ipa_cp_bits)
DEBUG_COUNTER (ipa_cp_values)
DEBUG_COUNTER (ipa_cp_vr)

566
gcc/gimple-if-to-switch.cc Normal file
View File

@ -0,0 +1,566 @@
/* If-elseif-else to switch conversion pass
Copyright (C) 2020 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/>. */
/* Algorithm of the pass runs in the following steps:
a) We walk basic blocks in DOMINATOR order so that we first reach
a first condition of a future switch.
b) We follow false edges of a if-else-chain and we record chain
of GIMPLE conditions. These blocks are only used for comparison
of a common SSA_NAME and we do not allow any side effect.
c) We remove all basic blocks (except first) of such chain and
GIMPLE switch replaces the condition in the first basic block.
d) We move all GIMPLE statements in the removed blocks into the
first one. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "backend.h"
#include "rtl.h"
#include "tree.h"
#include "gimple.h"
#include "tree-pass.h"
#include "ssa.h"
#include "gimple-pretty-print.h"
#include "fold-const.h"
#include "gimple-iterator.h"
#include "tree-cfg.h"
#include "tree-dfa.h"
#include "tree-cfgcleanup.h"
#include "alias.h"
#include "tree-ssa-loop.h"
#include "diagnostic.h"
#include "cfghooks.h"
#include "tree-into-ssa.h"
#include "cfganal.h"
#include "dbgcnt.h"
#include "target.h"
#include "alloc-pool.h"
#include "tree-switch-conversion.h"
#include "tree-ssa-reassoc.h"
using namespace tree_switch_conversion;
struct condition_info
{
typedef vec<std::pair<gphi *, tree>> mapping_vec;
condition_info (gcond *cond): m_cond (cond), m_bb (gimple_bb (cond)),
m_forwarder_bb (NULL), m_ranges (), m_true_edge (NULL), m_false_edge (NULL),
m_true_edge_phi_mapping (), m_false_edge_phi_mapping ()
{
m_ranges.create (0);
}
/* Recond PHI mapping for an original edge E and save these into
vector VEC. */
void record_phi_mapping (edge e, mapping_vec *vec);
gcond *m_cond;
basic_block m_bb;
basic_block m_forwarder_bb;
vec<range_entry> m_ranges;
edge m_true_edge;
edge m_false_edge;
mapping_vec m_true_edge_phi_mapping;
mapping_vec m_false_edge_phi_mapping;
};
/* Recond PHI mapping for an original edge E and save these into vector VEC. */
void
condition_info::record_phi_mapping (edge e, mapping_vec *vec)
{
for (gphi_iterator gsi = gsi_start_phis (e->dest); !gsi_end_p (gsi);
gsi_next (&gsi))
{
gphi *phi = gsi.phi ();
if (!virtual_operand_p (gimple_phi_result (phi)))
{
tree arg = PHI_ARG_DEF_FROM_EDGE (phi, e);
vec->safe_push (std::make_pair (phi, arg));
}
}
}
/* Master structure for one if to switch conversion candidate. */
struct if_chain
{
/* Default constructor. */
if_chain (): m_entries ()
{
m_entries.create (2);
}
/* Default destructor. */
~if_chain ()
{
m_entries.release ();
}
/* Verify that all case ranges do not overlap. */
bool check_non_overlapping_cases ();
/* Return true when the switch can be expanded with a jump table or
a bit test (at least partially). */
bool is_beneficial ();
/* If chain entries. */
vec<condition_info *> m_entries;
};
/* Compare two case ranges by minimum value. */
static int
range_cmp (const void *a, const void *b)
{
const range_entry *re1 = *(const range_entry * const *) a;
const range_entry *re2 = *(const range_entry * const *) b;
return tree_int_cst_compare (re1->low, re2->low);
}
/* Verify that all case ranges do not overlap. */
bool
if_chain::check_non_overlapping_cases ()
{
auto_vec<range_entry *> all_ranges;
for (unsigned i = 0; i < m_entries.length (); i++)
for (unsigned j = 0; j < m_entries[i]->m_ranges.length (); j++)
all_ranges.safe_push (&m_entries[i]->m_ranges[j]);
all_ranges.qsort (range_cmp);
for (unsigned i = 0; i < all_ranges.length () - 1; i++)
{
range_entry *left = all_ranges[i];
range_entry *right = all_ranges[i + 1];
if (tree_int_cst_le (left->low, right->low)
&& tree_int_cst_le (right->low, left->high))
return false;
}
return true;
}
/* Compare clusters by minimum value. */
static int
cluster_cmp (const void *a, const void *b)
{
simple_cluster *sc1 = *(simple_cluster * const *) a;
simple_cluster *sc2 = *(simple_cluster * const *) b;
return tree_int_cst_compare (sc1->get_low (), sc2->get_high ());
}
/* Dump constructed CLUSTERS with prefix MESSAGE. */
static void
dump_clusters (vec<cluster *> *clusters, const char *message)
{
if (dump_file)
{
fprintf (dump_file, ";; %s: ", message);
for (unsigned i = 0; i < clusters->length (); i++)
(*clusters)[i]->dump (dump_file, dump_flags & TDF_DETAILS);
fprintf (dump_file, "\n");
}
}
/* Return true when the switch can be expanded with a jump table or
a bit test (at least partially). */
bool
if_chain::is_beneficial ()
{
profile_probability prob = profile_probability::uninitialized ();
auto_vec<cluster *> clusters;
clusters.create (m_entries.length ());
for (unsigned i = 0; i < m_entries.length (); i++)
{
condition_info *info = m_entries[i];
for (unsigned j = 0; j < info->m_ranges.length (); j++)
{
range_entry *range = &info->m_ranges[j];
basic_block bb = info->m_true_edge->dest;
bool has_forwarder = !info->m_true_edge_phi_mapping.is_empty ();
clusters.safe_push (new simple_cluster (range->low, range->high,
NULL_TREE, bb, prob,
has_forwarder));
}
}
/* Sort clusters and merge them. */
auto_vec<cluster *> filtered_clusters;
filtered_clusters.create (16);
clusters.qsort (cluster_cmp);
simple_cluster *left = static_cast<simple_cluster *> (clusters[0]);
filtered_clusters.safe_push (left);
for (unsigned i = 1; i < clusters.length (); i++)
{
simple_cluster *right = static_cast<simple_cluster *> (clusters[i]);
tree type = TREE_TYPE (left->get_low ());
if (!left->m_has_forward_bb
&& !right->m_has_forward_bb
&& left->m_case_bb == right->m_case_bb)
{
if (wi::eq_p (wi::to_wide (right->get_low ()) - wi::to_wide
(left->get_high ()), wi::one (TYPE_PRECISION (type))))
{
left->set_high (right->get_high ());
continue;
}
}
left = static_cast<simple_cluster *> (clusters[i]);
filtered_clusters.safe_push (left);
}
dump_clusters (&filtered_clusters, "Canonical GIMPLE case clusters");
vec<cluster *> output
= jump_table_cluster::find_jump_tables (filtered_clusters);
bool r = output.length () < filtered_clusters.length ();
if (r)
dump_clusters (&output, "JT can be built");
output.release ();
if (r)
return true;
output = bit_test_cluster::find_bit_tests (filtered_clusters);
r = output.length () < filtered_clusters.length ();
if (r)
dump_clusters (&output, "BT can be built");
output.release ();
return r;
}
/* Build case label with MIN and MAX values of a given basic block DEST. */
static tree
build_case_label (tree index_type, tree min, tree max, basic_block dest)
{
if (min != NULL_TREE && index_type != TREE_TYPE (min))
min = fold_convert (index_type, min);
if (max != NULL_TREE && index_type != TREE_TYPE (max))
max = fold_convert (index_type, max);
tree label = gimple_block_label (dest);
return build_case_label (min, min == max ? NULL_TREE : max, label);
}
/* Compare two integer constants. */
static int
label_cmp (const void *a, const void *b)
{
const_tree l1 = *(const const_tree *) a;
const_tree l2 = *(const const_tree *) b;
return tree_int_cst_compare (CASE_LOW (l1), CASE_LOW (l2));
}
/* Convert a given if CHAIN into a switch GIMPLE statement. */
static void
convert_if_conditions_to_switch (if_chain *chain)
{
if (!dbg_cnt (if_to_switch))
return;
auto_vec<tree> labels;
unsigned entries = chain->m_entries.length ();
condition_info *first_cond = chain->m_entries[0];
condition_info *last_cond = chain->m_entries[entries - 1];
edge default_edge = last_cond->m_false_edge;
basic_block default_bb = default_edge->dest;
gimple_stmt_iterator gsi = gsi_for_stmt (first_cond->m_cond);
tree index_type = TREE_TYPE (first_cond->m_ranges[0].exp);
for (unsigned i = 0; i < entries; i++)
{
condition_info *info = chain->m_entries[i];
basic_block case_bb = info->m_true_edge->dest;
/* Create a forwarder block if needed. */
if (!info->m_true_edge_phi_mapping.is_empty ())
{
info->m_forwarder_bb = split_edge (info->m_true_edge);
case_bb = info->m_forwarder_bb;
}
for (unsigned j = 0; j < info->m_ranges.length (); j++)
labels.safe_push (build_case_label (index_type,
info->m_ranges[j].low,
info->m_ranges[j].high,
case_bb));
default_bb = info->m_false_edge->dest;
if (i == 0)
{
remove_edge (first_cond->m_true_edge);
remove_edge (first_cond->m_false_edge);
}
else
delete_basic_block (info->m_bb);
make_edge (first_cond->m_bb, case_bb, 0);
}
labels.qsort (label_cmp);
edge e = find_edge (first_cond->m_bb, default_bb);
if (e == NULL)
e = make_edge (first_cond->m_bb, default_bb, 0);
gswitch *s
= gimple_build_switch (first_cond->m_ranges[0].exp,
build_case_label (index_type, NULL_TREE,
NULL_TREE, default_bb),
labels);
gsi_remove (&gsi, true);
gsi_insert_before (&gsi, s, GSI_NEW_STMT);
if (dump_file)
{
fprintf (dump_file, "Expanded into a new gimple STMT: ");
print_gimple_stmt (dump_file, s, 0, TDF_SLIM);
putc ('\n', dump_file);
}
/* Fill up missing PHI node arguments. */
for (unsigned i = 0; i < chain->m_entries.length (); ++i)
{
condition_info *info = chain->m_entries[i];
for (unsigned j = 0; j < info->m_true_edge_phi_mapping.length (); ++j)
{
std::pair<gphi *, tree> item = info->m_true_edge_phi_mapping[j];
add_phi_arg (item.first, item.second,
single_succ_edge (info->m_forwarder_bb),
UNKNOWN_LOCATION);
}
}
/* Fill up missing PHI nodes for the default BB. */
for (unsigned j = 0; j < last_cond->m_false_edge_phi_mapping.length (); ++j)
{
std::pair<gphi *, tree> item = last_cond->m_false_edge_phi_mapping[j];
add_phi_arg (item.first, item.second, e, UNKNOWN_LOCATION);
}
}
/* Identify an index variable used in BB in a GIMPLE condition.
Save information about the condition into CONDITIONS_IN_BBS. */
static void
find_conditions (basic_block bb,
hash_map<basic_block, condition_info> *conditions_in_bbs)
{
gimple_stmt_iterator gsi = gsi_last_nondebug_bb (bb);
if (gsi_end_p (gsi))
return;
gcond *cond = dyn_cast<gcond *> (gsi_stmt (gsi));
if (cond == NULL)
return;
if (!no_side_effect_bb (bb))
return;
tree lhs = gimple_cond_lhs (cond);
tree rhs = gimple_cond_rhs (cond);
tree_code code = gimple_cond_code (cond);
condition_info info (cond);
gassign *def;
if (code == NE_EXPR
&& TREE_CODE (lhs) == SSA_NAME
&& (def = dyn_cast<gassign *> (SSA_NAME_DEF_STMT (lhs))) != NULL
&& integer_zerop (rhs))
{
enum tree_code rhs_code = gimple_assign_rhs_code (def);
if (rhs_code == BIT_IOR_EXPR)
{
info.m_ranges.safe_grow (2, true);
init_range_entry (&info.m_ranges[0], gimple_assign_rhs1 (def), NULL);
init_range_entry (&info.m_ranges[1], gimple_assign_rhs2 (def), NULL);
}
}
else
{
info.m_ranges.safe_grow (1, true);
init_range_entry (&info.m_ranges[0], NULL_TREE, cond);
}
/* All identified ranges must have equal expression and IN_P flag. */
if (!info.m_ranges.is_empty ())
{
edge true_edge, false_edge;
tree expr = info.m_ranges[0].exp;
bool in_p = info.m_ranges[0].in_p;
extract_true_false_edges_from_block (bb, &true_edge, &false_edge);
info.m_true_edge = in_p ? true_edge : false_edge;
info.m_false_edge = in_p ? false_edge : true_edge;
for (unsigned i = 0; i < info.m_ranges.length (); ++i)
if (info.m_ranges[i].exp == NULL_TREE
|| info.m_ranges[i].low == NULL_TREE
|| info.m_ranges[i].high == NULL_TREE)
return;
for (unsigned i = 1; i < info.m_ranges.length (); ++i)
if (info.m_ranges[i].exp != expr
|| info.m_ranges[i].in_p != in_p)
return;
info.record_phi_mapping (info.m_true_edge,
&info.m_true_edge_phi_mapping);
info.record_phi_mapping (info.m_false_edge,
&info.m_false_edge_phi_mapping);
conditions_in_bbs->put (bb, info);
}
}
namespace {
const pass_data pass_data_if_to_switch =
{
GIMPLE_PASS, /* type */
"iftoswitch", /* name */
OPTGROUP_NONE, /* optinfo_flags */
TV_TREE_IF_TO_SWITCH, /* tv_id */
( PROP_cfg | PROP_ssa ), /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
TODO_cleanup_cfg | TODO_update_ssa /* todo_flags_finish */
};
class pass_if_to_switch : public gimple_opt_pass
{
public:
pass_if_to_switch (gcc::context *ctxt)
: gimple_opt_pass (pass_data_if_to_switch, ctxt)
{}
/* opt_pass methods: */
virtual bool gate (function *)
{
return (jump_table_cluster::is_enabled ()
|| bit_test_cluster::is_enabled ());
}
virtual unsigned int execute (function *);
}; // class pass_if_to_switch
unsigned int
pass_if_to_switch::execute (function *fun)
{
auto_vec<if_chain *> all_candidates;
hash_map<basic_block, condition_info> conditions_in_bbs;
basic_block bb;
FOR_EACH_BB_FN (bb, fun)
find_conditions (bb, &conditions_in_bbs);
if (conditions_in_bbs.is_empty ())
return 0;
int *rpo = XNEWVEC (int, n_basic_blocks_for_fn (fun));
unsigned n = pre_and_rev_post_order_compute_fn (fun, NULL, rpo, false);
auto_bitmap seen_bbs;
for (int i = n - 1; i >= 0; --i)
{
basic_block bb = BASIC_BLOCK_FOR_FN (fun, rpo[i]);
if (bitmap_bit_p (seen_bbs, bb->index))
continue;
bitmap_set_bit (seen_bbs, bb->index);
condition_info *info = conditions_in_bbs.get (bb);
if (info)
{
if_chain *chain = new if_chain ();
chain->m_entries.safe_push (info);
/* Try to find a chain starting in this BB. */
while (true)
{
if (!single_pred_p (gimple_bb (info->m_cond)))
break;
edge e = single_pred_edge (gimple_bb (info->m_cond));
condition_info *info2 = conditions_in_bbs.get (e->src);
if (!info2 || info->m_ranges[0].exp != info2->m_ranges[0].exp)
break;
chain->m_entries.safe_push (info2);
bitmap_set_bit (seen_bbs, e->src->index);
info = info2;
}
chain->m_entries.reverse ();
if (chain->m_entries.length () >= 3
&& chain->check_non_overlapping_cases ()
&& chain->is_beneficial ())
{
gcond *cond = chain->m_entries[0]->m_cond;
if (dump_enabled_p ())
dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, cond,
"Condition chain with %d BBs "
"transformed into a switch statement.\n",
chain->m_entries.length ());
all_candidates.safe_push (chain);
}
}
}
for (unsigned i = 0; i < all_candidates.length (); i++)
{
convert_if_conditions_to_switch (all_candidates[i]);
delete all_candidates[i];
}
free (rpo);
if (!all_candidates.is_empty ())
{
free_dominance_info (CDI_DOMINATORS);
mark_virtual_operands_for_renaming (fun);
}
return 0;
}
} // anon namespace
gimple_opt_pass *
make_pass_if_to_switch (gcc::context *ctxt)
{
return new pass_if_to_switch (ctxt);
}

View File

@ -94,6 +94,7 @@ along with GCC; see the file COPYING3. If not see
NEXT_PASS (pass_phiopt, true /* early_p */);
NEXT_PASS (pass_modref);
NEXT_PASS (pass_tail_recursion);
NEXT_PASS (pass_if_to_switch);
NEXT_PASS (pass_convert_switch);
NEXT_PASS (pass_cleanup_eh);
NEXT_PASS (pass_profile);

View File

@ -0,0 +1,25 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-iftoswitch-optimized" } */
void fancy_abort(const char *, int, const char *);
enum machine_mode
{
MODE_FLOAT,
MODE_DECIMAL_FLOAT,
MODE_COMPLEX_INT,
MODE_COMPLEX_FLOAT,
MODE_VECTOR_BOOL,
MODE_VECTOR_FLOAT
} extern const mode_class;
void tree_node() {
if (mode_class)
mode_class == MODE_FLOAT || mode_class == MODE_DECIMAL_FLOAT ||
mode_class == MODE_COMPLEX_FLOAT || mode_class == MODE_VECTOR_FLOAT
? fancy_abort("aaa", 2, __FUNCTION__),
0 : 0;
int g = 0;
}
/* { dg-final { scan-tree-dump "Condition chain with 3 BBs transformed into a switch statement." "iftoswitch" } } */

View File

@ -0,0 +1,35 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-iftoswitch-optimized" } */
int global;
int foo ();
int main(int argc, char **argv)
{
if (argc == 1)
foo ();
else if (argc == 2)
{
global += 1;
}
else if (argc == 3)
{
foo ();
foo ();
}
else if (argc == 4)
{
foo ();
}
else if (argc == 5)
{
global = 2;
}
else
global -= 123;
global -= 12;
return 0;
}
/* { dg-final { scan-tree-dump "Condition chain with 5 BBs transformed into a switch statement." "iftoswitch" } } */

View File

@ -0,0 +1,11 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-iftoswitch-optimized" } */
int IsHTMLWhitespaceNoRange(int aChar)
{
return aChar == 0x0001 || aChar == 0x000A ||
aChar == 0x000C || aChar == 0x000E ||
aChar == 0x0020;
}
/* { dg-final { scan-tree-dump "Condition chain with 3 BBs transformed into a switch statement." "iftoswitch" } } */

View File

@ -0,0 +1,11 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-iftoswitch-optimized" } */
int IsHTMLWhitespace(int aChar)
{
return aChar == 0x0009 || aChar == 0x000A ||
aChar == 0x000C || aChar == 0x000D ||
aChar == 0x0020 || aChar == 0x0030;
}
/* { dg-final { scan-tree-dump "Condition chain with 3 BBs transformed into a switch statement." "iftoswitch" } } */

View File

@ -0,0 +1,36 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-iftoswitch-optimized" } */
int global;
int foo ();
int main(int argc, char **argv)
{
if (argc == 1)
foo ();
else if (argc == 2)
{
global += 1;
}
else if (argc == 3)
{
foo ();
foo ();
}
else if (argc == 4)
{
foo ();
}
/* This will be removed with EVRP. */
else if (argc == 1)
{
global = 2;
}
else
global -= 123;
global -= 12;
return 0;
}
/* { dg-final { scan-tree-dump-not "Condition chain" "iftoswitch" } } */

View File

@ -0,0 +1,12 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-iftoswitch-optimized" } */
int crud (unsigned char c)
{
return (((((((((((int) c == 46) || (int) c == 44)
|| (int) c == 58) || (int) c == 59) || (int) c == 60)
|| (int) c == 62) || (int) c == 34) || (int) c == 92)
|| (int) c == 39) != 0);
}
/* { dg-final { scan-tree-dump "Condition chain with 5 BBs transformed into a switch statement." "iftoswitch" } } */

View File

@ -0,0 +1,42 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-iftoswitch-optimized" } */
int global;
int foo ();
int main(int argc, char **argv)
{
if (argc >= 1 && argc <= 10)
foo ();
else if (argc == 12)
{
global += 1;
}
else if (argc == 13)
{
foo ();
foo ();
}
else if (argc == 14)
{
foo ();
}
/* This will be removed with EVRP. */
else if (argc == 5)
{
global = 2;
}
/* This will be removed with EVRP. */
else if (argc >= 7 && argc <= 9)
{
global = 2;
}
else
global -= 123;
global -= 12;
return 0;
}
/* { dg-final { scan-tree-dump-not "Condition chain" "iftoswitch" } } */

View File

@ -0,0 +1,25 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-iftoswitch-optimized" } */
int global;
int foo(int a)
{
int x = 0;
for (unsigned i = 0; i < a; i++)
{
if (a == 2)
{
global += 123;
x = 1;
}
else if (a == 3)
x = 2;
else if (a == 10)
x = 3;
}
return x;
}
/* { dg-final { scan-tree-dump-not "Condition chain " "iftoswitch" } } */

View File

@ -0,0 +1,27 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-iftoswitch-optimized" } */
int global;
int global1;
int global2;
int global3;
int foo(int a, int b)
{
int x = 0;
for (unsigned i = 0; i < a; i++)
{
if (b == 1)
global += 2;
else if (a == 2)
global = 123;
else if (a == 3)
global1 = 1234;
else if (a == 10)
global2 = 12345;
else if (a == 1)
global2 = 123456;
}
}
/* { dg-final { scan-tree-dump-not "Condition chain" "iftoswitch" } } */

View File

@ -1,6 +1,6 @@
/* PR tree-optimization/96480 */
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-optimized" } */
/* { dg-options "-O2 -fdump-tree-optimized -fno-bit-tests" } */
/* { dg-final { scan-tree-dump " = _\[0-9]* <= 3;" "optimized" } } */
int v[4];

View File

@ -1,6 +1,6 @@
/* { dg-do run { target { ! "m68k*-*-* mmix*-*-* bfin*-*-* v850*-*-* moxie*-*-* cris*-*-* m32c*-*-* fr30*-*-* mcore*-*-* powerpc*-*-* xtensa*-*-*"} } } */
/* { dg-options "-O2 -fno-inline -fdump-tree-reassoc1-details --param logical-op-non-short-circuit=1" } */
/* { dg-options "-O2 -fno-inline -fdump-tree-reassoc1-details --param logical-op-non-short-circuit=1 -fno-bit-tests" } */
/* { dg-additional-options "-mbranch-cost=2" { target branch_cost } } */

View File

@ -293,6 +293,7 @@ DEFTIMEVAR (TV_VAR_TRACKING , "variable tracking")
DEFTIMEVAR (TV_VAR_TRACKING_DATAFLOW , "var-tracking dataflow")
DEFTIMEVAR (TV_VAR_TRACKING_EMIT , "var-tracking emit")
DEFTIMEVAR (TV_TREE_IFCOMBINE , "tree if-combine")
DEFTIMEVAR (TV_TREE_IF_TO_SWITCH , "if to switch conversion")
DEFTIMEVAR (TV_TREE_UNINIT , "uninit var analysis")
DEFTIMEVAR (TV_PLUGIN_INIT , "plugin initialization")
DEFTIMEVAR (TV_PLUGIN_RUN , "plugin execution")

View File

@ -379,6 +379,7 @@ extern gimple_opt_pass *make_pass_empty_loop (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_graphite (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_graphite_transforms (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_if_conversion (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_if_to_switch (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_loop_distribution (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_vectorize (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_simduid_cleanup (gcc::context *ctxt);

View File

@ -51,6 +51,7 @@ along with GCC; see the file COPYING3. If not see
#include "builtins.h"
#include "gimplify.h"
#include "case-cfn-macros.h"
#include "tree-ssa-reassoc.h"
/* This is a simple global reassociation pass. It is, in part, based
on the LLVM pass of the same name (They do some things more/less
@ -188,15 +189,6 @@ static struct
int pows_created;
} reassociate_stats;
/* Operator, rank pair. */
struct operand_entry
{
unsigned int rank;
unsigned int id;
tree op;
unsigned int count;
gimple *stmt_to_insert;
};
static object_allocator<operand_entry> operand_entry_pool
("operand entry pool");
@ -226,7 +218,7 @@ static bool reassoc_stmt_dominates_stmt_p (gimple *, gimple *);
/* Wrapper around gsi_remove, which adjusts gimple_uid of debug stmts
possibly added by gsi_remove. */
bool
static bool
reassoc_remove_stmt (gimple_stmt_iterator *gsi)
{
gimple *stmt = gsi_stmt (*gsi);
@ -2408,18 +2400,7 @@ optimize_ops_list (enum tree_code opcode,
For more information see comments above fold_test_range in fold-const.c,
this implementation is for GIMPLE. */
struct range_entry
{
tree exp;
tree low;
tree high;
bool in_p;
bool strict_overflow_p;
unsigned int idx, next;
};
void dump_range_entry (FILE *file, struct range_entry *r);
void debug_range_entry (struct range_entry *r);
/* Dump the range entry R to FILE, skipping its expression if SKIP_EXP. */
@ -2449,7 +2430,7 @@ debug_range_entry (struct range_entry *r)
an SSA_NAME and STMT argument is ignored, otherwise STMT
argument should be a GIMPLE_COND. */
static void
void
init_range_entry (struct range_entry *r, tree exp, gimple *stmt)
{
int in_p;
@ -4286,7 +4267,7 @@ suitable_cond_bb (basic_block bb, basic_block test_bb, basic_block *other_bb,
range test optimization, all SSA_NAMEs set in the bb are consumed
in the bb and there are no PHIs. */
static bool
bool
no_side_effect_bb (basic_block bb)
{
gimple_stmt_iterator gsi;

48
gcc/tree-ssa-reassoc.h Normal file
View File

@ -0,0 +1,48 @@
/* Reassociation for trees.
Copyright (C) 2020 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 GCC_SSA_REASSOC_H
#define GCC_SSA_REASSOC_H
/* Operator, rank pair. */
struct operand_entry
{
unsigned int rank;
unsigned int id;
tree op;
unsigned int count;
gimple *stmt_to_insert;
};
struct range_entry
{
tree exp;
tree low;
tree high;
bool in_p;
bool strict_overflow_p;
unsigned int idx, next;
};
void dump_range_entry (FILE *file, struct range_entry *r);
void debug_range_entry (struct range_entry *r);
void init_range_entry (struct range_entry *r, tree exp, gimple *stmt);
bool no_side_effect_bb (basic_block bb);
#endif /* GCC_SSA_REASSOC_H */

View File

@ -48,8 +48,8 @@ class cluster
{
public:
/* Constructor. */
cluster (tree case_label_expr, basic_block case_bb, profile_probability prob,
profile_probability subtree_prob);
inline cluster (tree case_label_expr, basic_block case_bb,
profile_probability prob, profile_probability subtree_prob);
/* Destructor. */
virtual ~cluster ()
@ -121,8 +121,9 @@ class simple_cluster: public cluster
{
public:
/* Constructor. */
simple_cluster (tree low, tree high, tree case_label_expr,
basic_block case_bb, profile_probability prob);
inline simple_cluster (tree low, tree high, tree case_label_expr,
basic_block case_bb, profile_probability prob,
bool has_forward_bb = false);
/* Destructor. */
~simple_cluster ()
@ -146,6 +147,11 @@ public:
return m_high;
}
void set_high (tree high)
{
m_high = high;
}
void
debug ()
{
@ -182,12 +188,16 @@ public:
/* True if case is a range. */
bool m_range_p;
/* True if the case will use a forwarder BB. */
bool m_has_forward_bb;
};
simple_cluster::simple_cluster (tree low, tree high, tree case_label_expr,
basic_block case_bb, profile_probability prob):
basic_block case_bb, profile_probability prob,
bool has_forward_bb):
cluster (case_label_expr, case_bb, prob, prob),
m_low (low), m_high (high)
m_low (low), m_high (high), m_has_forward_bb (has_forward_bb)
{
m_range_p = m_high != NULL;
if (m_high == NULL)
@ -271,7 +281,7 @@ public:
static inline unsigned int case_values_threshold (void);
/* Return whether jump table expansion is allowed. */
static bool is_enabled (void);
static inline bool is_enabled (void);
};
/* A GIMPLE switch statement can be expanded to a short sequence of bit-wise