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)

Reply via email to