From c49614211ad8f685dba64cdd086c6504f1ea1f63 Mon Sep 17 00:00:00 2001 From: Martin Liska Date: Wed, 25 Sep 2019 15:26:44 +0200 Subject: [PATCH] Add if-chain to switch conversion pass. gcc/ChangeLog: 2019-11-15 Martin Liska PR tree-optimization/14799 PR ipa/88702 * Makefile.in: Add gimple-if-to-switch.o. * common.opt: Add -fconvert-if-to-switch. * dbgcnt.def (DEBUG_COUNTER): Define if_to_switch. * doc/invoke.texi: Document the newly added option. * gimple-if-to-switch.c: New file. * opts.c: Enable OPT_fconvert_if_to_switch with -O2+. * passes.def: Add pass_if_to_switch to pass manager. * timevar.def (TV_TREE_IF_TO_SWITCH): Add TV_TREE_IF_TO_SWITCH time variable. * tree-pass.h (make_pass_if_to_switch): New. gcc/testsuite/ChangeLog: 2019-11-15 Martin Liska PR tree-optimization/14799 PR ipa/88702 * 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. * gcc.dg/tree-ssa/reassoc-32.c: Do not allow if-to-switch convertion. * gcc.dg/tree-ssa/reassoc-33.c: Likewise. --- gcc/Makefile.in | 1 + gcc/common.opt | 4 + gcc/dbgcnt.def | 1 + gcc/doc/invoke.texi | 12 +- gcc/gimple-if-to-switch.c | 661 ++++++++++++++++++ gcc/opts.c | 1 + gcc/passes.def | 1 + .../gcc.dg/tree-ssa/if-to-switch-1.c | 35 + .../gcc.dg/tree-ssa/if-to-switch-2.c | 11 + .../gcc.dg/tree-ssa/if-to-switch-3.c | 11 + .../gcc.dg/tree-ssa/if-to-switch-4.c | 36 + .../gcc.dg/tree-ssa/if-to-switch-5.c | 12 + .../gcc.dg/tree-ssa/if-to-switch-6.c | 42 ++ .../gcc.dg/tree-ssa/if-to-switch-7.c | 25 + .../gcc.dg/tree-ssa/if-to-switch-8.c | 27 + gcc/testsuite/gcc.dg/tree-ssa/reassoc-32.c | 2 +- gcc/testsuite/gcc.dg/tree-ssa/reassoc-33.c | 2 +- gcc/timevar.def | 1 + gcc/tree-pass.h | 1 + 19 files changed, 882 insertions(+), 4 deletions(-) create mode 100644 gcc/gimple-if-to-switch.c create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-1.c create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-2.c create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-3.c create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-4.c create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-5.c create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-6.c create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-7.c create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-8.c diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 7d3c13230e4..0f131c315c8 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1317,6 +1317,7 @@ OBJS = \ gimple.o \ gimple-builder.o \ gimple-expr.o \ + gimple-if-to-switch.o \ gimple-iterator.o \ gimple-fold.o \ gimple-laddress.o \ diff --git a/gcc/common.opt b/gcc/common.opt index e7b87820bc9..7622c5e2e96 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1154,6 +1154,10 @@ fconserve-stack Common Var(flag_conserve_stack) Optimization Do not perform optimizations increasing noticeably stack usage. +fconvert-if-to-switch +Common Report Var(flag_convert_if_to_switch) Optimization +Perform conversions of if-elseif chain into a switch statement. + fcprop-registers Common Report Var(flag_cprop_registers) Optimization Perform a register copy-propagation optimization pass. diff --git a/gcc/dbgcnt.def b/gcc/dbgcnt.def index 5b96a7eaf9d..dfbb15773f9 100644 --- a/gcc/dbgcnt.def +++ b/gcc/dbgcnt.def @@ -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_sra_params) DEBUG_COUNTER (ipa_sra_retvalues) DEBUG_COUNTER (ira_move) diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index fe79ca2247a..42a52fd97d8 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -411,7 +411,7 @@ Objective-C and Objective-C++ Dialects}. -fassociative-math -fauto-profile -fauto-profile[=@var{path}] @gol -fauto-inc-dec -fbranch-probabilities @gol -fcaller-saves @gol --fcombine-stack-adjustments -fconserve-stack @gol +-fcombine-stack-adjustments -fconserve-stack -fconvert-if-to-switch @gol -fcompare-elim -fcprop-registers -fcrossjumping @gol -fcse-follow-jumps -fcse-skip-blocks -fcx-fortran-rules @gol -fcx-limited-range @gol @@ -478,7 +478,7 @@ Objective-C and Objective-C++ Dialects}. -fthread-jumps -ftracer -ftree-bit-ccp @gol -ftree-builtin-call-dce -ftree-ccp -ftree-ch @gol -ftree-coalesce-vars -ftree-copy-prop -ftree-dce -ftree-dominator-opts @gol --ftree-dse -ftree-forwprop -ftree-fre -fcode-hoisting @gol +-ftree-dse -ftree-forwprop -ftree-fre -fcode-hoisting @gol -ftree-loop-if-convert -ftree-loop-im @gol -ftree-phiprop -ftree-loop-distribution -ftree-loop-distribute-patterns @gol -ftree-loop-ivcanon -ftree-loop-linear -ftree-loop-optimize @gol @@ -2271,6 +2271,13 @@ Allow conditional expressions with mismatched types in the second and third arguments. The value of such an expression is void. This option is not supported for C++. +@item -fconvert-if-to-switch +@opindex fconvert-if-to-switch +Perform conversion of an if cascade into a switch statement. +The transformation can help to produce faster code for +the switch statement. This flag is enabled by default +at @option{-O2} and higher. + @item -flax-vector-conversions @opindex flax-vector-conversions Allow implicit conversions between vectors with differing numbers of @@ -8423,6 +8430,7 @@ also turns on the following optimization flags: -falign-labels -falign-loops @gol -fcaller-saves @gol -fcode-hoisting @gol +-fconvert-if-to-switch @gol -fcrossjumping @gol -fcse-follow-jumps -fcse-skip-blocks @gol -fdelete-null-pointer-checks @gol diff --git a/gcc/gimple-if-to-switch.c b/gcc/gimple-if-to-switch.c new file mode 100644 index 00000000000..4fa75598f70 --- /dev/null +++ b/gcc/gimple-if-to-switch.c @@ -0,0 +1,661 @@ +/* If-elseif-else to switch conversion pass + Copyright (C) 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 +. */ + +/* 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" + +/* Tuple that holds minimum and maximum values in a case. */ + +struct case_range +{ + /* Default constructor. */ + case_range (): + m_min (NULL_TREE), m_max (NULL_TREE) + {} + + case_range (tree min, tree max): + m_min (min), m_max (max) + {} + + /* Minimum case value. */ + tree m_min; + /* Maximum case value. */ + tree m_max; +}; + +/* One entry of a if chain. */ + +struct if_chain_entry +{ + /* Constructor. */ + if_chain_entry (basic_block bb, edge true_edge, edge false_edge) + : m_case_values (), m_bb (bb), + m_true_edge (true_edge), m_false_edge (false_edge) + { + m_case_values.create (2); + } + + /* Vector of at maximum 2 case ranges. */ + vec m_case_values; + /* Basic block of the original condition. */ + basic_block m_bb; + /* True edge of the gimple condition. */ + edge m_true_edge; + /* False edge of the gimple condition. */ + edge m_false_edge; +}; + +/* Master structure for one if to switch conversion candidate. */ + +struct if_chain +{ + /* Default constructor. */ + if_chain(): + m_first_condition (NULL), m_index (NULL_TREE), m_entries (), + m_phi_map () + { + m_entries.create (2); + } + + /* Default destructor. */ + ~if_chain () + { + m_entries.release (); + } + + /* Set index and check that it is not a different one. */ + bool set_and_check_index (tree index); + + /* Verify that all case ranges do not overlap. */ + bool check_non_overlapping_cases (); + + /* Record PHI arguments of a given edge E and return true + if GIMPLE switch creation will violate a PHI node. */ + bool record_phi_arguments (edge e); + + /* First condition of the chain. */ + gcond *m_first_condition; + /* Switch index. */ + tree m_index; + /* If chain entries. */ + vec m_entries; + /* PHI map that is later used for reconstruction of PHI nodes. */ + hash_map m_phi_map; +}; + +bool +if_chain::set_and_check_index (tree index) +{ + if (TREE_CODE (index) != SSA_NAME || !INTEGRAL_TYPE_P (TREE_TYPE (index))) + return false; + + if (m_index == NULL) + m_index = index; + + return index == m_index; +} + +/* Compare two case ranges by minimum value. */ + +static int +range_cmp (const void *a, const void *b) +{ + const case_range *cr1 = *(const case_range * const *) a; + const case_range *cr2 = *(const case_range * const *) b; + + return tree_int_cst_compare (cr1->m_min, cr2->m_min); +} + +bool +if_chain::check_non_overlapping_cases () +{ + auto_vec all_ranges; + for (unsigned i = 0; i < m_entries.length (); i++) + for (unsigned j =0; j < m_entries[i].m_case_values.length (); j++) + all_ranges.safe_push (&m_entries[i].m_case_values[j]); + + all_ranges.qsort (range_cmp); + + for (unsigned i = 0; i < all_ranges.length () - 1; i++) + { + case_range *left = all_ranges[i]; + case_range *right = all_ranges[i + 1]; + if (tree_int_cst_le (left->m_min, right->m_min) + && tree_int_cst_le (right->m_min, left->m_max)) + return false; + } + + return true; +} + +bool +if_chain::record_phi_arguments (edge e) +{ + 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); + tree *v = m_phi_map.get (phi); + if (v != NULL) + { + if (arg != *v) + return false; + } + else + m_phi_map.put (phi, arg); + } + } + + return true; +} + +/* Build case label with MIN and MAX values of a given basic block DEST. */ + +static tree +build_case_label (tree min, tree max, basic_block dest) +{ + 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 labels; + if_chain_entry first_cond = chain->m_entries[0]; + + unsigned entries = chain->m_entries.length (); + edge default_edge = chain->m_entries[entries - 1].m_false_edge; + basic_block default_bb = default_edge->dest; + + gimple_stmt_iterator gsi = gsi_for_stmt (chain->m_first_condition); + for (unsigned i = 0; i < chain->m_entries.length (); i++) + { + if_chain_entry entry = chain->m_entries[i]; + + basic_block case_bb = entry.m_true_edge->dest; + + for (unsigned j = 0; j < entry.m_case_values.length (); j++) + labels.safe_push (build_case_label (entry.m_case_values[j].m_min, + entry.m_case_values[j].m_max, + case_bb)); + default_bb = entry.m_false_edge->dest; + + if (i == 0) + { + remove_edge (first_cond.m_true_edge); + remove_edge (first_cond.m_false_edge); + } + else + { + /* Move all statements from the BB to the BB with gswitch. */ + auto_vec stmts; + for (gimple_stmt_iterator gsi = gsi_start_bb (entry.m_bb); + !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + if (gimple_code (stmt) != GIMPLE_COND) + stmts.safe_push (stmt); + } + + for (unsigned i = 0; i < stmts.length (); i++) + { + gimple_stmt_iterator gsi_from = gsi_for_stmt (stmts[i]); + gsi_move_before (&gsi_from, &gsi); + } + + delete_basic_block (entry.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 (chain->m_index, + build_case_label (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 (hash_map::iterator it = chain->m_phi_map.begin (); + it != chain->m_phi_map.end (); ++it) + { + gphi *phi = (*it).first; + if (!virtual_operand_p (gimple_phi_result (phi))) + { + for (unsigned i = 0; i < gimple_phi_num_args (phi); i++) + { + if (gimple_phi_arg_def (phi, i) == NULL_TREE) + { + add_phi_arg (phi, (*it).second, gimple_phi_arg_edge (phi, i), + UNKNOWN_LOCATION); + break; + } + } + } + } +} + +static bool +extract_case_from_stmt (tree rhs1, tree rhs2, tree_code code, + if_chain *chain, if_chain_entry *entry, + unsigned *visited_stmt_count) +{ + tree index = NULL_TREE; + case_range range; + + if (code == EQ_EXPR) + { + /* Handle situation 2a: + _1 = aChar_8(D) == 1; */ + index = rhs1; + + /* We can meet a readonly type used for a constant. */ + rhs2 = fold_convert (TREE_TYPE (index), rhs2); + if (TREE_CODE (rhs2) != INTEGER_CST) + return false; + + range = case_range (rhs2, rhs2); + *visited_stmt_count += 1; + } + else if (code == LE_EXPR) + { + /* Handle situation 2b: + aChar.1_1 = (unsigned int) aChar_10(D); + _2 = aChar.1_1 + 4294967287; + _3 = _2 <= 1; */ + tree ssa = rhs1; + tree range_size = rhs2; + if (TREE_CODE (ssa) != SSA_NAME + || TREE_CODE (range_size) != INTEGER_CST) + return false; + + gassign *subtraction = dyn_cast (SSA_NAME_DEF_STMT (ssa)); + if (subtraction == NULL + || gimple_assign_rhs_code (subtraction) != PLUS_EXPR) + return false; + + tree casted = gimple_assign_rhs1 (subtraction); + tree min = gimple_assign_rhs2 (subtraction); + if (TREE_CODE (casted) != SSA_NAME + || TREE_CODE (min) != INTEGER_CST) + return false; + + if (!SSA_NAME_IS_DEFAULT_DEF (casted)) + { + gassign *to_unsigned + = dyn_cast (SSA_NAME_DEF_STMT (casted)); + if (to_unsigned == NULL + || !gimple_assign_unary_nop_p (to_unsigned) + || !TYPE_UNSIGNED (TREE_TYPE (casted))) + return false; + index = gimple_assign_rhs1 (to_unsigned); + ++(*visited_stmt_count); + } + else + index = casted; + + tree type = TREE_TYPE (index); + tree range_min = fold_convert (type, const_unop (NEGATE_EXPR, type, min)); + range = case_range (range_min, const_binop (PLUS_EXPR, type, range_min, + fold_convert (type, + range_size))); + *visited_stmt_count += 2; + } + else + return false; + + if (!chain->set_and_check_index (index)) + return false; + entry->m_case_values.safe_push (range); + + return true; +} + +static void +find_switch_in_bb (basic_block bb, auto_vec *all_candidates, + auto_bitmap *visited_bbs) +{ + if_chain *chain = new if_chain (); + unsigned total_case_values = 0; + + while (true) + { + bool first = chain->m_entries.is_empty (); + if (bitmap_bit_p (*visited_bbs, bb->index)) + break; + bitmap_set_bit (*visited_bbs, bb->index); + + gimple_stmt_iterator gsi = gsi_last_nondebug_bb (bb); + if (gsi_end_p (gsi)) + break; + + if (!chain->m_entries.is_empty () && EDGE_COUNT (bb->preds) != 1) + break; + + gcond *cond = dyn_cast (gsi_stmt (gsi)); + if (cond == NULL) + break; + + if (first) + chain->m_first_condition = cond; + + edge true_edge, false_edge; + extract_true_false_edges_from_block (bb, &true_edge, &false_edge); + + /* Prevent loosing information for a PHI node where 2 edges will + be folded into one. Note that we must do the same also for false_edge + (for last BB in a if-elseif chain). */ + if (!chain->record_phi_arguments (true_edge) + || !chain->record_phi_arguments (false_edge)) + break; + + if_chain_entry entry (bb, true_edge, false_edge); + + /* Current we support following patterns (situations): + + 1) if condition with equal operation: + + : + if (argc_8(D) == 1) + goto ; [INV] + else + goto ; [INV] + + 2) if condition with a range check: + + : + _4 = c_13(D) + 198; + if (_4 <= 1) + goto ; [INV] + else + goto ; [INV] + + 3) mixture of 1) and 2) with a or condition: + + : + _1 = aChar_8(D) == 1; + _2 = aChar_8(D) == 10; + _3 = _1 | _2; + if (_3 != 0) + goto ; [INV] + else + goto ; [INV] + + or: + + : + aChar.1_1 = (unsigned int) aChar_10(D); + _2 = aChar.1_1 + 4294967287; + _3 = _2 <= 1; + _4 = aChar_10(D) == 12; + _5 = _3 | _4; + if (_5 != 0) + goto ; [INV] + else + goto ; [INV] + */ + + tree lhs = gimple_cond_lhs (cond); + tree rhs = gimple_cond_rhs (cond); + tree_code code = gimple_cond_code (cond); + unsigned visited_stmt_count = 0; + unsigned case_values = 0; + + /* Situation 1. */ + if (code == EQ_EXPR) + { + if (!extract_case_from_stmt (lhs, rhs, code, chain, &entry, + &visited_stmt_count)) + break; + case_values = 1; + } + /* Situation 2. */ + else if (code == LE_EXPR) + { + case_range range; + if (!extract_case_from_stmt (lhs, rhs, code, chain, &entry, + &visited_stmt_count)) + break; + case_values = 1; + } + /* Situation 3. */ + else if (code == NE_EXPR + && integer_zerop (rhs) + && TREE_CODE (lhs) == SSA_NAME + && TREE_CODE (TREE_TYPE (lhs)) == BOOLEAN_TYPE) + { + gassign *def = dyn_cast (SSA_NAME_DEF_STMT (lhs)); + if (def == NULL + || gimple_assign_rhs_code (def) != BIT_IOR_EXPR + || gimple_bb (def) != bb) + break; + + tree rhs1 = gimple_assign_rhs1 (def); + tree rhs2 = gimple_assign_rhs2 (def); + if (TREE_CODE (rhs1) != SSA_NAME || TREE_CODE (rhs2) != SSA_NAME) + break; + + gassign *def1 = dyn_cast (SSA_NAME_DEF_STMT (rhs1)); + gassign *def2 = dyn_cast (SSA_NAME_DEF_STMT (rhs2)); + if (def1 == NULL + || def2 == NULL + || def1 == def2 + || gimple_bb (def1) != bb + || gimple_bb (def2) != bb) + break; + + if (!extract_case_from_stmt (gimple_assign_rhs1 (def1), + gimple_assign_rhs2 (def1), + gimple_assign_rhs_code (def1), + chain, &entry, + &visited_stmt_count)) + break; + + if (!extract_case_from_stmt (gimple_assign_rhs1 (def2), + gimple_assign_rhs2 (def2), + gimple_assign_rhs_code (def2), + chain, &entry, + &visited_stmt_count)) + break; + case_values = 2; + visited_stmt_count += 2; + } + else + break; + + /* If it's not the first condition, then we need a BB without + any statements. */ + if (!first) + { + unsigned stmt_count = 0; + for (gimple_stmt_iterator gsi = gsi_start_nondebug_bb (bb); + !gsi_end_p (gsi); gsi_next_nondebug (&gsi)) + ++stmt_count; + + if (stmt_count - visited_stmt_count != 0) + break; + } + + total_case_values += case_values; + chain->m_entries.safe_push (entry); + + /* Follow if-elseif-elseif chain. */ + bb = false_edge->dest; + } + + if (total_case_values >= 3 + && chain->check_non_overlapping_cases ()) + { + if (dump_file) + { + expanded_location loc + = expand_location (gimple_location (chain->m_first_condition)); + fprintf (dump_file, "Condition chain (at %s:%d) with %d conditions " + "(%d BBs) transformed into a switch statement.\n", + loc.file, loc.line, total_case_values, + chain->m_entries.length ()); + } + + all_candidates->safe_push (chain); + } + else + { + /* Unmark last if chain entry to that it can become a potential beginning + of another chain. */ + if (!chain->m_entries.is_empty ()) + bitmap_clear_bit (*visited_bbs, bb->index); + delete chain; + } +} + +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 flag_convert_if_to_switch != 0; } + virtual unsigned int execute (function *); + +}; // class pass_if_to_switch + +unsigned int +pass_if_to_switch::execute (function *fun) +{ + calculate_dominance_info (CDI_DOMINATORS); + + auto_vec all_candidates; + auto_vec worklist; + auto_bitmap visited_bbs; + + worklist.safe_push (ENTRY_BLOCK_PTR_FOR_FN (fun)); + while (!worklist.is_empty ()) + { + basic_block bb = worklist.pop (); + find_switch_in_bb (bb, &all_candidates, &visited_bbs); + for (basic_block son = first_dom_son (CDI_DOMINATORS, bb); son; + son = next_dom_son (CDI_DOMINATORS, son)) + if (!bitmap_bit_p (visited_bbs, son->index)) + worklist.safe_push (son); + } + + for (unsigned i = 0; i < all_candidates.length (); i++) + { + convert_if_conditions_to_switch (all_candidates[i]); + delete all_candidates[i]; + } + + /* For now, just wipe the dominator information. */ + free_dominance_info (CDI_DOMINATORS); + + if (!all_candidates.is_empty ()) + 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); +} diff --git a/gcc/opts.c b/gcc/opts.c index addebf15365..e415e1a2bd2 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -508,6 +508,7 @@ static const struct default_options default_options_table[] = { OPT_LEVELS_2_PLUS, OPT_ftree_vrp, NULL, 1 }, { OPT_LEVELS_2_PLUS, OPT_fvect_cost_model_, NULL, VECT_COST_MODEL_CHEAP }, { OPT_LEVELS_2_PLUS, OPT_finline_functions, NULL, 1 }, + { OPT_LEVELS_2_PLUS, OPT_fconvert_if_to_switch, NULL, 1 }, /* -O2 and above optimizations, but not -Os or -Og. */ { OPT_LEVELS_2_PLUS_SPEED_ONLY, OPT_falign_functions, NULL, 1 }, diff --git a/gcc/passes.def b/gcc/passes.def index 798a391bd35..cfc00d78ec5 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -90,6 +90,7 @@ along with GCC; see the file COPYING3. If not see NEXT_PASS (pass_cd_dce); NEXT_PASS (pass_phiopt, true /* early_p */); 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); diff --git a/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-1.c b/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-1.c new file mode 100644 index 00000000000..bcb8ef2a160 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-1.c @@ -0,0 +1,35 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-iftoswitch" } */ + +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 \\(at .*if-to-switch-1.c:9\\) with 5 conditions \\(5 BBs\\) transformed into a switch statement." "iftoswitch" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-2.c b/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-2.c new file mode 100644 index 00000000000..316e772ec29 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-2.c @@ -0,0 +1,11 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-iftoswitch" } */ + +int IsHTMLWhitespaceNoRange(int aChar) +{ + return aChar == 0x0001 || aChar == 0x000A || + aChar == 0x000C || aChar == 0x000E || + aChar == 0x0020; +} + +/* { dg-final { scan-tree-dump "Condition chain \\(at .*if-to-switch-2.c:7\\) with 5 conditions \\(3 BBs\\) transformed into a switch statement." "iftoswitch" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-3.c b/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-3.c new file mode 100644 index 00000000000..fd07d909a3c --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-3.c @@ -0,0 +1,11 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-iftoswitch" } */ + +int IsHTMLWhitespace(int aChar) +{ + return aChar == 0x0009 || aChar == 0x000A || + aChar == 0x000C || aChar == 0x000D || + aChar == 0x0020 || aChar == 0x0030; +} + +/* { dg-final { scan-tree-dump "Condition chain \\(at .*if-to-switch-3.c:8\\) with 5 conditions \\(3 BBs\\) transformed into a switch statement." "iftoswitch" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-4.c b/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-4.c new file mode 100644 index 00000000000..930fbd49996 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-4.c @@ -0,0 +1,36 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-iftoswitch" } */ + +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 "Condition chain \\(at .*if-to-switch-4.c:9\\) with 4 conditions \\(4 BBs\\) transformed into a switch statement." "iftoswitch" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-5.c b/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-5.c new file mode 100644 index 00000000000..acb8b4b1211 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-5.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-iftoswitch" } */ + +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 \\(at .*if-to-switch-5.c:9\\) with 8 conditions \\(5 BBs\\) transformed into a switch statement." "iftoswitch" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-6.c b/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-6.c new file mode 100644 index 00000000000..2ac09fc632e --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-6.c @@ -0,0 +1,42 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-iftoswitch" } */ + +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 "Condition chain \\(at .*if-to-switch-6.c:9\\) with 4 conditions \\(4 BBs\\) transformed into a switch statement." "iftoswitch" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-7.c b/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-7.c new file mode 100644 index 00000000000..1a919bf025a --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-7.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-iftoswitch" } */ + +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" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-8.c b/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-8.c new file mode 100644 index 00000000000..2c1c147ba44 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/if-to-switch-8.c @@ -0,0 +1,27 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-iftoswitch" } */ + +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 "Condition chain \\(at .*if-to-switch-8.c:16\\) with 4 conditions \\(4 BBs\\) transformed into a switch statement." "iftoswitch" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/reassoc-32.c b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-32.c index 944362ad076..8e53620a4df 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/reassoc-32.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-32.c @@ -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-convert-if-to-switch" } */ /* { dg-additional-options "-mbranch-cost=2" { target branch_cost } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/reassoc-33.c b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-33.c index db0ce4c8463..5a66114616d 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/reassoc-33.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-33.c @@ -1,6 +1,6 @@ /* { dg-do run { target { ! "m68k*-*-* mmix*-*-* bfin*-*-* v850*-*-* moxie*-*-* cris*-*-* m32c*-*-* fr30*-*-* mcore*-*-* powerpc*-*-* xtensa*-*-* hppa*-*-* nios2*-*-* or1k-*-*-* pru*-*-*"} } } */ -/* { 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-convert-if-to-switch" } */ /* { dg-additional-options "-mbranch-cost=2" { target branch_cost } } */ int test (int a, int b, int c) diff --git a/gcc/timevar.def b/gcc/timevar.def index 357fcfd65c5..20ea20a6178 100644 --- a/gcc/timevar.def +++ b/gcc/timevar.def @@ -290,6 +290,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") diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index a987661530e..84c498cb26e 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -374,6 +374,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); -- 2.24.0