On Wed, 17 Jul 2024, Filip Kastl wrote: > Hi, > > This is the second version of my patch introducing "exponential index > transformation" to the switch conversion pass. See the version 1 mail here: > > https://gcc.gnu.org/pipermail/gcc-patches/2024-May/653120.html > > > Changes made > ------------ > > In this version I addressed the following comments: > > - Linaro CI: switch-3.c failing regtest > Exp transform interfered with this test so I added -fdisable-tree-switchconv. > > - richi: gsi_start_bb -> gsi_after_labels > - richi: Use int_const_binop > - richi: Merge two cycles into one > - apinki, richi: Use wide_int instead of HOST_WIDE_INT > - richi: You can use the gimple_build API for nicer code > - richi: Use -mbmi -mpopcount instead -march=znver4 > Made these modifications. > > - richi: Split out code generating GIMPLE for log2 and pow2p operations > Made this modification. The final implementation differs from the one I sent > in a reply to the version 1 patch. I chose to return gimple_seq as Richard > suggested because it seems more elegant to me. > > - richi: "It is good to not leave IL in a broken state" > Removed my FIXME remark suggesting to not fix dominators because the GIMPLE > code gets removed later. > > > Changes not made > ---------------- > > These are the comments that I think were meant as suggestions, not as "this > must be changed" and I'm leaving them for possible future patches: > > - richi: Also handle *minus* powers of 2 and powers of 2 +- a constant > - richi: Also allow using CTZ to compute log2 > - apinski, richi: Smarter handling of type of index variable (current > implementation cannot handle shorts and chars for example) > > These are the comments that I'd like to reply to here but they didn't prompt > any change to the patch: > > - richi: Signed index variable with 0x80000000 as its value may be a problem. > Tested the patch version 2 for this situation. In this situation, switch > conversion evaluates exponential transform as not viable so its fine. > > - richi: > > > + redirect_immediate_dominators (CDI_DOMINATORS, swtch_bb, cond_bb); > > > + > > > + /* FIXME: Since exponential transform is run only if we know that > > > the > > > + switch will be converted, we know these blocks will be removed and we > > > + maybe don't have to bother updating their dominators. */ > > > > It's good to not leave the IL in an intermediate broken state. > > > > > + edge e; > > > + edge_iterator ei; > > > + FOR_EACH_EDGE (e, ei, swtch_bb->succs) > > > + { > > > + basic_block bb = e->dest; > > > + if (bb == m_final_bb || bb == default_bb) > > > + continue; > > > + set_immediate_dominator (CDI_DOMINATORS, bb, swtch_bb); > > > > If there's an alternate edge into the cases, thus the original > > dominator wasn't the swtch_bb the dominator shouldn't change. > > I wonder why it would change at all - you are only creating > > a new edge into the default case so only that block dominance > > relationship changes? > If I read the switch conversion sources right, there cannot be an alternate > edge into the non-default cases. switch_convesion::collect would reject that > kind of switch. So we know that case basic blocks will always have the switch > basic block as their immediate dominator. This code here actually just > partially reverts (only for the case basic blocks) the > redirect_immediate_dominators call that happens a few lines earlier. That > call > is needed because all basic blocks *outside* the switch that had the switch > basic block as their immediate dominator should now have cond_bb as their > immediate dominator. > > - richi: > > > + } > > > + > > > + vec<basic_block> v; > > > + v.create (1); > > > + v.quick_push (m_final_bb); > > > + iterate_fix_dominators (CDI_DOMINATORS, v, true); > > > > The final BB should have the condition block as immediate dominator > > if it's old immediate dominator was the switch block, otherwise > > it's immediate dominator shouldn't change. > I think that this is not true. Consider this CFG where no path through > default > BB intersects final BB: > > switch BB ---+ > / | \ \ > case BBs default BB > \ | / / > final BB / > | / > > Here idom(final BB) == switch BB. > After the index exponential transform the CFG looks like this > > cond BB ---------+ > | | > switch BB ---+ | > / | \ \ | > case BBs default BB > \ | / / > final BB / > | / > > It still holds that idom(final BB) == switch BB.
Sure but you still know the dominator of the default BB as the cond BB then? That said, I think that using iterate_fix_dominators is overkill and you should know the new immediate dominator and can set it directly? That said, even above if there's a merge of the default BB and final BB downstream in the CFG, inserting cond BB requires adjustment of the immediate dominator of that merge block and you are missing that? > > I bootstrapped and regtested the patch on x86_64 linux. > > Can I commit the patch like this? Or are there some things that still need > addressing? Apart from the dominator update question above the patch looks OK to me. Thanks, Richard. > Cheers > Filip Kastl > > > --- 8< --- > > > gimple ssa: Teach switch conversion to optimize powers of 2 switches > > Sometimes a switch has case numbers that are powers of 2. Switch > conversion usually isn't able to optimize switches. This patch adds > "exponential index transformation" to switch conversion. After switch > conversion applies this transformation on the switch the index variable > of the switch becomes the exponent instead of the whole value. For > example: > > switch (i) > { > case (1 << 0): return 0; > case (1 << 1): return 1; > case (1 << 2): return 2; > ... > case (1 << 30): return 30; > default: return 31; > } > > gets transformed roughly into > > switch (log2(i)) > { > case 0: return 0; > case 1: return 1; > case 2: return 2; > ... > case 30: return 30; > default: return 31; > } > > This enables switch conversion to further optimize the switch. > > This patch only enables this transformation if there are optabs for > POPCOUNT and FFS so that the base 2 logarithm can be computed > efficiently at runtime. > > gcc/ChangeLog: > > * tree-switch-conversion.cc (can_log2): > (gen_log2): > (gen_pow2p): > (switch_conversion::switch_conversion): > (switch_conversion::is_exp_index_transform_viable): > (switch_conversion::exp_index_transform): > (switch_conversion::gen_inbound_check): > (switch_conversion::expand): > * tree-switch-conversion.h: > > gcc/testsuite/ChangeLog: > > * gcc.dg/tree-ssa/switch-3.c: > * gcc.target/i386/switch-exp-transform-1.c: New test. > * gcc.target/i386/switch-exp-transform-2.c: New test. > * gcc.target/i386/switch-exp-transform-3.c: New test. > > Signed-off-by: Filip Kastl <fka...@suse.cz> > --- > gcc/testsuite/gcc.dg/tree-ssa/switch-3.c | 2 +- > .../gcc.target/i386/switch-exp-transform-1.c | 32 ++ > .../gcc.target/i386/switch-exp-transform-2.c | 35 ++ > .../gcc.target/i386/switch-exp-transform-3.c | 148 ++++++++ > gcc/tree-switch-conversion.cc | 326 +++++++++++++++++- > gcc/tree-switch-conversion.h | 18 + > 6 files changed, 555 insertions(+), 6 deletions(-) > create mode 100644 gcc/testsuite/gcc.target/i386/switch-exp-transform-1.c > create mode 100644 gcc/testsuite/gcc.target/i386/switch-exp-transform-2.c > create mode 100644 gcc/testsuite/gcc.target/i386/switch-exp-transform-3.c > > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/switch-3.c > b/gcc/testsuite/gcc.dg/tree-ssa/switch-3.c > index 44981e1d186..83aae3843e9 100644 > --- a/gcc/testsuite/gcc.dg/tree-ssa/switch-3.c > +++ b/gcc/testsuite/gcc.dg/tree-ssa/switch-3.c > @@ -1,4 +1,4 @@ > -/* { dg-options "-O2 -fdump-tree-switchlower1" } */ > +/* { dg-options "-O2 -fdump-tree-switchlower1 -fdisable-tree-switchconv" } */ > > int cipher_to_alg(int cipher) > { > diff --git a/gcc/testsuite/gcc.target/i386/switch-exp-transform-1.c > b/gcc/testsuite/gcc.target/i386/switch-exp-transform-1.c > new file mode 100644 > index 00000000000..53d31460ba3 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/switch-exp-transform-1.c > @@ -0,0 +1,32 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fdump-tree-switchconv -mpopcnt -mbmi" } */ > + > +/* Checks that exponential index transform enables switch conversion to > convert > + this switch into an array lookup. Also checks that the "index variable > is a > + power of two" check has been generated. */ > + > +int foo(unsigned bar) > +{ > + switch (bar) > + { > + case (1 << 0): > + return 1; > + case (1 << 1): > + return 2; > + case (1 << 2): > + return 3; > + case (1 << 3): > + return 4; > + case (1 << 4): > + return 8; > + case (1 << 5): > + return 13; > + case (1 << 6): > + return 21; > + default: > + return 0; > + } > +} > + > +/* { dg-final { scan-tree-dump "CSWTCH" "switchconv" } } */ > +/* { dg-final { scan-tree-dump "POPCOUNT" "switchconv" } } */ > diff --git a/gcc/testsuite/gcc.target/i386/switch-exp-transform-2.c > b/gcc/testsuite/gcc.target/i386/switch-exp-transform-2.c > new file mode 100644 > index 00000000000..f1a9a2d1ee9 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/switch-exp-transform-2.c > @@ -0,0 +1,35 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fdump-tree-switchconv -mbmi" } */ > + > +/* Checks that when exponential index transform is viable but switch > conversion > + decides that the switch cannot be converted, the exponential index > transform > + is not done. */ > + > +int a; > + > +int foo(unsigned bar) > +{ > + switch (bar) > + { > + case (1 << 0): > + return 0; > + case (1 << 1): > + return 1; > + case (1 << 2): > + return 2; > + case (1 << 3): > + return 3; > + case (1 << 4): > + return 4; > + case (1 << 5): > + a = 3; > + return 5; > + case (1 << 6): > + return 6; > + default: > + return 0; > + } > +} > + > +/* { dg-final { scan-tree-dump "Exponential index transform viable" > "switchconv" } } */ > +/* { dg-final { scan-tree-dump-not "Applying exponential index transform" > "switchconv" } } */ > diff --git a/gcc/testsuite/gcc.target/i386/switch-exp-transform-3.c > b/gcc/testsuite/gcc.target/i386/switch-exp-transform-3.c > new file mode 100644 > index 00000000000..c8fae70692e > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/switch-exp-transform-3.c > @@ -0,0 +1,148 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fdump-tree-switchconv -mbmi" } */ > + > +/* Checks that the exponential index transformation is done for all these > types > + of the index variable: > + - (unsigned) int > + - (unsigned) long > + - (unsigned) long long */ > + > +int unopt_int(int bit_position) > +{ > + switch (bit_position) > + { > + case (1 << 0): > + return 0; > + case (1 << 1): > + return 1; > + case (1 << 2): > + return 2; > + case (1 << 3): > + return 3; > + case (1 << 4): > + return 4; > + case (1 << 5): > + return 5; > + case (1 << 6): > + return 6; > + default: > + return 0; > + } > +} > + > +int unopt_unsigned_int(unsigned int bit_position) > +{ > + switch (bit_position) > + { > + case (1 << 0): > + return 0; > + case (1 << 1): > + return 1; > + case (1 << 2): > + return 2; > + case (1 << 3): > + return 3; > + case (1 << 4): > + return 4; > + case (1 << 5): > + return 5; > + case (1 << 6): > + return 6; > + default: > + return 0; > + } > +} > + > +int unopt_long(long bit_position) > +{ > + switch (bit_position) > + { > + case (1 << 0): > + return 0; > + case (1 << 1): > + return 1; > + case (1 << 2): > + return 2; > + case (1 << 3): > + return 3; > + case (1 << 4): > + return 4; > + case (1 << 5): > + return 5; > + case (1 << 6): > + return 6; > + default: > + return 0; > + } > +} > + > +int unopt_unsigned_long(unsigned long bit_position) > +{ > + switch (bit_position) > + { > + case (1 << 0): > + return 0; > + case (1 << 1): > + return 1; > + case (1 << 2): > + return 2; > + case (1 << 3): > + return 3; > + case (1 << 4): > + return 4; > + case (1 << 5): > + return 5; > + case (1 << 6): > + return 6; > + default: > + return 0; > + } > +} > + > +int unopt_long_long(long long bit_position) > +{ > + switch (bit_position) > + { > + case (1 << 0): > + return 0; > + case (1 << 1): > + return 1; > + case (1 << 2): > + return 2; > + case (1 << 3): > + return 3; > + case (1 << 4): > + return 4; > + case (1 << 5): > + return 5; > + case (1 << 6): > + return 6; > + default: > + return 0; > + } > +} > + > +int unopt_unsigned_long_long(unsigned long long bit_position) > +{ > + switch (bit_position) > + { > + case (1 << 0): > + return 0; > + case (1 << 1): > + return 1; > + case (1 << 2): > + return 2; > + case (1 << 3): > + return 3; > + case (1 << 4): > + return 4; > + case (1 << 5): > + return 5; > + case (1 << 6): > + return 6; > + default: > + return 0; > + } > +} > + > +/* { dg-final { scan-tree-dump-times "Applying exponential index transform" > 6 "switchconv" } } */ > diff --git a/gcc/tree-switch-conversion.cc b/gcc/tree-switch-conversion.cc > index 64629122ec6..4b11c8d25f4 100644 > --- a/gcc/tree-switch-conversion.cc > +++ b/gcc/tree-switch-conversion.cc > @@ -52,6 +52,8 @@ Software Foundation, 51 Franklin Street, Fifth Floor, > Boston, MA > #include "omp-general.h" > #include "gimple-range.h" > #include "tree-cfgcleanup.h" > +#include "hwint.h" > +#include "internal-fn.h" > > /* ??? For lang_hooks.types.type_for_mode, but is there a word_mode > type in the GIMPLE type system that is language-independent? */ > @@ -61,12 +63,73 @@ Software Foundation, 51 Franklin Street, Fifth Floor, > Boston, MA > > using namespace tree_switch_conversion; > > +/* Does the target have optabs needed to efficiently compute exact base two > + logarithm of a value with type TYPE? > + > + See gen_log2. */ > + > +static bool > +can_log2 (tree type, optimization_type opt_type) > +{ > + /* Check if target supports FFS. */ > + return direct_internal_fn_supported_p (IFN_FFS, type, opt_type); > +} > + > +/* Assume that OP is a power of two. Build a sequence of gimple statements > + efficiently computing the base two logarithm of OP using special optabs. > + Return the ssa name represeting the result of the logarithm through > RESULT. > + > + Should only be used if target supports the needed optabs. See can_log2. > */ > + > +static gimple_seq > +gen_log2 (tree op, location_t loc, tree *result) > +{ > + tree type = TREE_TYPE (op); > + gimple_seq stmts = NULL; > + gimple_stmt_iterator gsi = gsi_last (stmts); > + tree tmp1 = gimple_build (&gsi, false, GSI_NEW_STMT, loc, IFN_FFS, type, > op); > + tree tmp2 = gimple_build (&gsi, false, GSI_NEW_STMT, loc, MINUS_EXPR, type, > + tmp1, build_one_cst (type)); > + *result = tmp2; > + return stmts; > +} > + > +/* Build a sequence of gimple statements checking that OP is a power of 2. > Use > + special optabs if target supports them. Return the result as a > + boolen_type_node ssa name through RESULT. */ > + > +static gimple_seq > +gen_pow2p (tree op, location_t loc, optimization_type opt_type, tree *result) > +{ > + tree type = TREE_TYPE (op); > + gimple_seq stmts = NULL; > + gimple_stmt_iterator gsi = gsi_last (stmts); > + if (direct_internal_fn_supported_p (IFN_POPCOUNT, type, opt_type)) > + { > + tree tmp = gimple_build (&gsi, false, GSI_NEW_STMT, loc, IFN_POPCOUNT, > + type, op); > + *result = gimple_build (&gsi, false, GSI_NEW_STMT, loc, EQ_EXPR, > + boolean_type_node, tmp, build_one_cst (type)); > + } > + else > + { > + tree tmp1 = gimple_build (&gsi, false, GSI_NEW_STMT, loc, NEGATE_EXPR, > + type, op); > + tree tmp2 = gimple_build (&gsi, false, GSI_NEW_STMT, loc, BIT_AND_EXPR, > + type, op, tmp1); > + *result = gimple_build (&gsi, false, GSI_NEW_STMT, loc, EQ_EXPR, > + boolean_type_node, tmp2, op); > + } > + return stmts; > +} > + > /* Constructor. */ > > switch_conversion::switch_conversion (): m_final_bb (NULL), > m_constructors (NULL), m_default_values (NULL), > m_arr_ref_first (NULL), m_arr_ref_last (NULL), > - m_reason (NULL), m_default_case_nonstandard (false), m_cfg_altered (false) > + m_reason (NULL), m_default_case_nonstandard (false), m_cfg_altered (false), > + m_exp_index_transform_applied (false) > { > } > > @@ -202,6 +265,244 @@ switch_conversion::collect (gswitch *swtch) > } > } > > +/* Check that the "exponential index transform" can be applied to this > switch. > + > + See comment of the exp_index_transform function for details about this > + transformation. > + > + We want: > + - This form of the switch is more efficient > + - Cases are powers of 2 > + > + Expects that SWTCH has at least one case. */ > + > +bool > +switch_conversion::is_exp_index_transform_viable (gswitch *swtch) > +{ > + tree index = gimple_switch_index (swtch); > + tree index_type = TREE_TYPE (index); > + basic_block swtch_bb = gimple_bb (swtch); > + unsigned num_labels = gimple_switch_num_labels (swtch); > + > + optimization_type opt_type = bb_optimization_type (swtch_bb); > + if (!can_log2 (index_type, opt_type)) > + return false; > + > + /* Check that each case label corresponds only to one value > + (no case 1..3). */ > + unsigned i; > + for (i = 1; i < num_labels; i++) > + { > + tree label = gimple_switch_label (swtch, i); > + if (CASE_HIGH (label)) > + return false; > + } > + > + /* Check that each label is nonnegative and a power of 2. */ > + for (i = 1; i < num_labels; i++) > + { > + tree label = gimple_switch_label (swtch, i); > + wide_int label_wi = wi::to_wide (CASE_LOW (label)); > + if (!wi::ge_p (label_wi, 0, TYPE_SIGN (index_type))) > + return false; > + if (wi::exact_log2 (label_wi) == -1) > + return false; > + } > + > + if (dump_file) > + fprintf (dump_file, "Exponential index transform viable\n"); > + > + return true; > +} > + > +/* Perform the "exponential index transform". > + > + Assume that cases of SWTCH are powers of 2. The transformation replaces > the > + cases by their exponents (2^k -> k). It also inserts a statement that > + computes the exponent of the original index variable (basically taking the > + logarithm) and then sets the result as the new index variable. > + > + The transformation also inserts a conditional statement checking that the > + incoming original index variable is a power of 2 with the false edge > leading > + to the default case. > + > + The exponential index transform shrinks the range of case numbers which > + helps switch conversion convert switches it otherwise could not. > + > + Consider for example: > + > + switch (i) > + { > + case (1 << 0): return 0; > + case (1 << 1): return 1; > + case (1 << 2): return 2; > + ... > + case (1 << 30): return 30; > + default: return 31; > + } > + > + First, exponential index transform gets applied. Since each case becomes > + case x: return x;, the rest of switch conversion is then able to get rid > of > + the switch statement. > + > + if (i is power of 2) > + return log2 (i); > + else > + return 31; > + > + */ > + > +void > +switch_conversion::exp_index_transform (gswitch *swtch) > +{ > + if (dump_file) > + fprintf (dump_file, "Applying exponential index transform\n"); > + > + tree index = gimple_switch_index (swtch); > + tree index_type = TREE_TYPE (index); > + basic_block swtch_bb = gimple_bb (swtch); > + unsigned num_labels = gimple_switch_num_labels (swtch); > + > + /* Insert a cond stmt that checks if the index variable is a power of 2. > */ > + gimple_stmt_iterator gsi = gsi_for_stmt (swtch); > + gsi_prev (&gsi); > + gimple *foo = gsi_stmt (gsi); > + edge new_edge1 = split_block (swtch_bb, foo); > + > + swtch_bb = new_edge1->dest; > + basic_block cond_bb = new_edge1->src; > + new_edge1->flags |= EDGE_TRUE_VALUE; > + new_edge1->flags &= ~EDGE_FALLTHRU; > + new_edge1->probability = profile_probability::even (); > + > + basic_block default_bb = gimple_switch_default_bb (cfun, swtch); > + edge new_edge2 = make_edge (cond_bb, default_bb, EDGE_FALSE_VALUE); > + new_edge2->probability = profile_probability::even (); > + > + tree tmp; > + optimization_type opt_type = bb_optimization_type (cond_bb); > + gimple_seq stmts = gen_pow2p (index, UNKNOWN_LOCATION, opt_type, &tmp); > + gsi = gsi_last_bb (cond_bb); > + gsi_insert_seq_after (&gsi, stmts, GSI_LAST_NEW_STMT); > + gcond *stmt_cond = gimple_build_cond (NE_EXPR, tmp, boolean_false_node, > + NULL, NULL); > + gsi_insert_after (&gsi, stmt_cond, GSI_NEW_STMT); > + > + /* We just added an edge going to default bb so fix PHI nodes in that bb: > + For each PHI add new PHI arg. It will be the same arg as when comming > to > + the default bb from the switch bb. */ > + edge default_edge = find_edge (swtch_bb, default_bb); > + for (gphi_iterator gsi = gsi_start_phis (default_bb); > + !gsi_end_p (gsi); gsi_next (&gsi)) > + { > + gphi *phi = gsi.phi (); > + tree arg = PHI_ARG_DEF_FROM_EDGE (phi, default_edge); > + location_t loc = gimple_phi_arg_location_from_edge (phi, default_edge); > + add_phi_arg (phi, arg, new_edge2, loc); > + } > + > + /* Insert a sequence of stmts that takes the log of the index variable. */ > + stmts = gen_log2 (index, UNKNOWN_LOCATION, &tmp); > + gsi = gsi_after_labels (swtch_bb); > + gsi_insert_seq_before (&gsi, stmts, GSI_SAME_STMT); > + > + /* Use the result of the logarithm as the new index variable. */ > + gimple_switch_set_index (swtch, tmp); > + update_stmt (swtch); > + > + /* Replace each case number with its logarithm. */ > + unsigned i; > + for (i = 1; i < num_labels; i++) > + { > + tree label = gimple_switch_label (swtch, i); > + CASE_LOW (label) = build_int_cst (index_type, > + tree_log2 (CASE_LOW (label))); > + } > + > + /* Fix the dominator tree, if it is available. */ > + if (dom_info_available_p (CDI_DOMINATORS)) > + { > + /* Analysis of how dominators should look after we add the edge E going > + from the cond block to the default block. > + > + 1 For the blocks between the switch block and the final block > + (excluding the final block itself): They had the switch block as > + their immediate dominator. That shouldn't change. > + > + 2 The final block may now have the switch block or the cond block as > + its immediate dominator. There's no easy way of knowing (consider > + two cases where in both m_default_case_nonstandard = true, in one a > + path through default intersects the final block and in one all paths > + through default avoid the final block but intersect a successor of the > + final block). > + > + 3 Other blocks that had the switch block as their immediate dominator > + should now have the cond block as their immediate dominator. > + > + 4 Immediate dominators of the rest of the blocks shouldn't change. > + > + Reasoning for 3 and 4: > + > + We'll only consider blocks that do not fall into 1 or 2. > + > + Consider a block X whose original imm dom was the switch block. All > + paths to X must also intersect the cond block since it's the only > + pred of the switch block. The final block doesn't dominate X so at > + least one path P must lead through the default block. Let P' be P but > + instead of going through the switch block, take E. The switch block > + doesn't dominate X so its imm dom must now be the cond block. > + > + Consider a block X whose original imm dom was Y != the switch block. > + We only added an edge so all original paths to X are still present. > + So X gained no new dominators. Observe that Y still dominates X. > + There would have to be a path that avoids Y otherwise. But any block > + we can avoid now except for the switch block we were able to avoid > + before adding E. */ > + > + redirect_immediate_dominators (CDI_DOMINATORS, swtch_bb, cond_bb); > + > + edge e; > + edge_iterator ei; > + FOR_EACH_EDGE (e, ei, swtch_bb->succs) > + { > + basic_block bb = e->dest; > + if (bb == m_final_bb || bb == default_bb) > + continue; > + set_immediate_dominator (CDI_DOMINATORS, bb, swtch_bb); > + } > + > + vec<basic_block> v; > + v.create (1); > + v.quick_push (m_final_bb); > + iterate_fix_dominators (CDI_DOMINATORS, v, true); > + } > + > + /* Update information about the switch statement. */ > + tree first_label = gimple_switch_label (swtch, 1); > + tree last_label = gimple_switch_label (swtch, num_labels - 1); > + > + m_range_min = CASE_LOW (first_label); > + m_range_max = CASE_LOW (last_label); > + m_index_expr = gimple_switch_index (swtch); > + m_switch_bb = swtch_bb; > + > + m_range_size = int_const_binop (MINUS_EXPR, m_range_max, m_range_min); > + > + m_cfg_altered = true; > + > + m_contiguous_range = true; > + wide_int last_wi = wi::to_wide (CASE_LOW (first_label)); > + for (i = 2; i < num_labels; i++) > + { > + tree label = gimple_switch_label (swtch, i); > + wide_int label_wi = wi::to_wide (CASE_LOW (label)); > + m_contiguous_range &= wi::eq_p (wi::add (last_wi, 1), label_wi); > + last_wi = label_wi; > + } > + > + m_exp_index_transform_applied = true; > +} > + > /* 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. */ > @@ -973,8 +1274,9 @@ switch_conversion::gen_inbound_check () > bbf->count = e1f->count () + e2f->count (); > > /* Tidy blocks that have become unreachable. */ > - prune_bbs (bbd, m_final_bb, > - m_default_case_nonstandard ? m_default_bb : NULL); > + bool prune_default_bb = !m_default_case_nonstandard > + && !m_exp_index_transform_applied; > + prune_bbs (bbd, m_final_bb, prune_default_bb ? NULL : m_default_bb); > > /* Fixup the PHI nodes in bbF. */ > fix_phi_nodes (e1f, e2f, bbf); > @@ -1053,8 +1355,19 @@ switch_conversion::expand (gswitch *swtch) > return; > } > > - /* Check the case label values are within reasonable range: */ > - if (!check_range ()) > + /* Sometimes it is possible to use the "exponential index transform" to > help > + switch conversion convert switches which it otherwise could not convert. > + However, we want to do this transform only when we know that switch > + conversion will then really be able to convert the switch. So we first > + check if the transformation is applicable and then maybe later do the > + transformation. */ > + bool exp_transform_viable = is_exp_index_transform_viable (swtch); > + > + /* Check the case label values are within reasonable range. > + > + If we will be doing exponential index transform, the range will be > always > + reasonable. */ > + if (!exp_transform_viable && !check_range ()) > { > gcc_assert (m_reason); > return; > @@ -1076,6 +1389,9 @@ switch_conversion::expand (gswitch *swtch) > /* At this point all checks have passed and we can proceed with the > transformation. */ > > + if (exp_transform_viable) > + exp_index_transform (swtch); > + > create_temp_arrays (); > gather_default_values (m_default_case_nonstandard > ? gimple_switch_label (swtch, 1) > diff --git a/gcc/tree-switch-conversion.h b/gcc/tree-switch-conversion.h > index 6939eec6018..1a865f85f3a 100644 > --- a/gcc/tree-switch-conversion.h > +++ b/gcc/tree-switch-conversion.h > @@ -743,6 +743,19 @@ public: > /* Collection information about SWTCH statement. */ > void collect (gswitch *swtch); > > + /* Check that the 'exponential index transform' can be applied. > + > + See the comment at the function definition for more details. */ > + bool is_exp_index_transform_viable (gswitch *swtch); > + > + /* Perform the 'exponential index transform'. > + > + The exponential index transform shrinks the range of case numbers which > + helps switch conversion convert switches it otherwise could not. > + > + See the comment at the function definition for more details. */ > + void exp_index_transform (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. */ > @@ -900,6 +913,11 @@ public: > > /* True if CFG has been changed. */ > bool m_cfg_altered; > + > + /* True if exponential index transform has been applied. See the comment > at > + the definition of exp_index_transform for details about the > + transformation. */ > + bool m_exp_index_transform_applied; > }; > > void > -- Richard Biener <rguent...@suse.de> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg, Germany; GF: Ivo Totev, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)