Commit 767d4551 by Martin Liska Committed by Martin Liska

Switch conversion: support any ax + b transformation (PR tree-optimization/84436).

2018-10-24  Martin Liska  <mliska@suse.cz>

	PR tree-optimization/84436
	* tree-switch-conversion.c (switch_conversion::contains_same_values_p):
	Remove.
	(switch_conversion::contains_linear_function_p): New.
	(switch_conversion::build_one_array): Support linear
	transformation on input.
	* tree-switch-conversion.h (struct switch_conversion): Add
	contains_linear_function_p declaration.
2018-10-24  Martin Liska  <mliska@suse.cz>

	PR tree-optimization/84436
	* gcc.dg/tree-ssa/pr84436-1.c: New test.
	* gcc.dg/tree-ssa/pr84436-2.c: New test.
	* gcc.dg/tree-ssa/pr84436-3.c: New test.
	* gcc.dg/tree-ssa/pr84436-4.c: New test.
	* gcc.dg/tree-ssa/pr84436-5.c: New test.

From-SVN: r265463
parent b5d0cdc9
2018-10-24 Martin Liska <mliska@suse.cz>
PR tree-optimization/84436
* tree-switch-conversion.c (switch_conversion::contains_same_values_p):
Remove.
(switch_conversion::contains_linear_function_p): New.
(switch_conversion::build_one_array): Support linear
transformation on input.
* tree-switch-conversion.h (struct switch_conversion): Add
contains_linear_function_p declaration.
2018-10-24 Richard Biener <rguenther@suse.de> 2018-10-24 Richard Biener <rguenther@suse.de>
* varasm.c (const_hash_1): Return hash of ADDR_EXPR * varasm.c (const_hash_1): Return hash of ADDR_EXPR
2018-10-24 Martin Liska <mliska@suse.cz>
PR tree-optimization/84436
* gcc.dg/tree-ssa/pr84436-1.c: New test.
* gcc.dg/tree-ssa/pr84436-2.c: New test.
* gcc.dg/tree-ssa/pr84436-3.c: New test.
* gcc.dg/tree-ssa/pr84436-4.c: New test.
* gcc.dg/tree-ssa/pr84436-5.c: New test.
2018-10-24 Ilya Leoshkevich <iii@linux.ibm.com> 2018-10-24 Ilya Leoshkevich <iii@linux.ibm.com>
* gcc.target/s390/20181024-1.c: New test. * gcc.target/s390/20181024-1.c: New test.
......
/* PR tree-optimization/84436 */
/* { dg-options "-O2 -fdump-tree-switchconv -fdump-tree-optimized" } */
/* { dg-do run } */
int
__attribute__ ((noipa))
foo (int how)
{
switch (how) {
case 2: how = 205; break; /* how = 100 * index + 5 */
case 3: how = 305; break;
case 4: how = 405; break;
case 5: how = 505; break;
case 6: how = 605; break;
}
return how;
}
int main()
{
if (foo (2) != 205)
__builtin_abort ();
if (foo (6) != 605)
__builtin_abort ();
if (foo (123) != 123)
__builtin_abort ();
return 0;
}
/* { dg-final { scan-tree-dump-times "100 \\*" 1 "switchconv" } } */
/* { dg-final { scan-tree-dump-times ".* \\+ 5" 1 "switchconv" } } */
/* { dg-final { scan-tree-dump-not "switch" "optimized" } } */
/* PR tree-optimization/84436 */
/* { dg-options "-O2 -fdump-tree-switchconv -fdump-tree-optimized" } */
char
lowerit(char a)
{
switch (a)
{
default:
return a;
case 'A':
return 'a';
case 'B':
return 'b';
case 'C':
return 'c';
case 'D':
return 'd';
case 'E':
return 'e';
case 'F':
return 'f';
case 'G':
return 'g';
case 'H':
return 'h';
case 'I':
return 'i';
case 'J':
return 'j';
case 'K':
return 'k';
case 'L':
return 'l';
case 'M':
return 'm';
case 'N':
return 'n';
case 'O':
return 'o';
case 'P':
return 'p';
case 'Q':
return 'q';
case 'R':
return 'r';
case 'S':
return 's';
case 'T':
return 't';
case 'U':
return 'u';
case 'V':
return 'v';
case 'W':
return 'w';
case 'X':
return 'x';
case 'Y':
return 'y';
case 'Z':
return 'z';
}
}
/* { dg-final { scan-tree-dump-times "a_.*\\+ 32" 1 "switchconv" } } */
/* { dg-final { scan-tree-dump-not "switch" "optimized" } } */
/* PR tree-optimization/84436 */
/* { dg-options "-O2 -fdump-tree-switchconv -fdump-tree-optimized" } */
enum a { b, c, d };
int e;
void h(enum a);
void f() {
enum a g;
switch (e) {
case '1':
g = b;
break;
case '2':
g = c;
break;
case '3':
g = d;
}
h(g);
}
/* { dg-final { scan-tree-dump-times ".* \\+ 4294967247" 1 "switchconv" } } */
/* { dg-final { scan-tree-dump-not "switch" "optimized" } } */
/* PR tree-optimization/84436 */
/* { dg-options "-O2 -fdump-tree-switchconv -fdump-tree-optimized" } */
/* { dg-do run } */
enum E
{
A, B, C,
};
int
__attribute__ ((noipa))
foo(enum E e)
{
switch (e)
{
case A: return 0;
case B: return 1;
case C: return 2;
}
return -1;
}
int main()
{
if (foo (A) != 0)
__builtin_abort ();
if (foo (B) != 1)
__builtin_abort ();
if (foo (C) != 2)
__builtin_abort ();
return 0;
}
/* { dg-final { scan-tree-dump-not "switch" "optimized" } } */
/* PR tree-optimization/84436 */
/* { dg-options "-O2 -fdump-tree-switchconv -fdump-tree-optimized" } */
/* { dg-do run } */
char
__attribute__ ((noipa))
foo (char how)
{
switch (how) {
case -4: how = 96; break;
case -3: how = -120; break;
case -2: how = -80; break;
case -1: how = -40; break;
case 0: how = 0; break;
case 1: how = 40; break;
}
return how;
}
int main()
{
if (foo (-4) != 96)
__builtin_abort ();
if (foo (-3) != -120)
__builtin_abort ();
if (foo (0) != 0)
__builtin_abort ();
if (foo (123) != 123)
__builtin_abort ();
return 0;
}
/* { dg-final { scan-tree-dump-times "40 *\\*" 1 "switchconv" } } */
/* { dg-final { scan-tree-dump-not "switch" "optimized" } } */
...@@ -44,6 +44,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA ...@@ -44,6 +44,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
#include "gimplify.h" #include "gimplify.h"
#include "gimple-iterator.h" #include "gimple-iterator.h"
#include "gimplify-me.h" #include "gimplify-me.h"
#include "gimple-fold.h"
#include "tree-cfg.h" #include "tree-cfg.h"
#include "cfgloop.h" #include "cfgloop.h"
#include "alloc-pool.h" #include "alloc-pool.h"
...@@ -439,25 +440,63 @@ switch_conversion::build_constructors () ...@@ -439,25 +440,63 @@ switch_conversion::build_constructors ()
} }
} }
/* If all values in the constructor vector are the same, return the value. /* If all values in the constructor vector are products of a linear function
Otherwise return NULL_TREE. Not supposed to be called for empty a * x + b, then return true. When true, COEFF_A and COEFF_B and
vectors. */ coefficients of the linear function. Note that equal values are special
case of a linear function with a and b equal to zero. */
tree bool
switch_conversion::contains_same_values_p (vec<constructor_elt, va_gc> *vec) switch_conversion::contains_linear_function_p (vec<constructor_elt, va_gc> *vec,
wide_int *coeff_a,
wide_int *coeff_b)
{ {
unsigned int i; unsigned int i;
tree prev = NULL_TREE;
constructor_elt *elt; constructor_elt *elt;
gcc_assert (vec->length () >= 2);
/* Let's try to find any linear function a * x + y that can apply to
given values. 'a' can be calculated as follows:
a = (y2 - y1) / (x2 - x1) where x2 - x1 = 1 (consecutive case indices)
a = y2 - y1
and
b = y2 - a * x2
*/
tree elt0 = (*vec)[0].value;
tree elt1 = (*vec)[1].value;
if (TREE_CODE (elt0) != INTEGER_CST || TREE_CODE (elt1) != INTEGER_CST)
return false;
wide_int range_min = wi::to_wide (fold_convert (TREE_TYPE (elt0),
m_range_min));
wide_int y1 = wi::to_wide (elt0);
wide_int y2 = wi::to_wide (elt1);
wide_int a = y2 - y1;
wide_int b = y2 - a * (range_min + 1);
/* Verify that all values fulfill the linear function. */
FOR_EACH_VEC_SAFE_ELT (vec, i, elt) FOR_EACH_VEC_SAFE_ELT (vec, i, elt)
{ {
if (!prev) if (TREE_CODE (elt->value) != INTEGER_CST)
prev = elt->value; return false;
else if (!operand_equal_p (elt->value, prev, OEP_ONLY_CONST))
return NULL_TREE; wide_int value = wi::to_wide (elt->value);
if (a * range_min + b != value)
return false;
++range_min;
} }
return prev;
*coeff_a = a;
*coeff_b = b;
return true;
} }
/* Return type which should be used for array elements, either TYPE's /* Return type which should be used for array elements, either TYPE's
...@@ -551,7 +590,7 @@ void ...@@ -551,7 +590,7 @@ void
switch_conversion::build_one_array (int num, tree arr_index_type, switch_conversion::build_one_array (int num, tree arr_index_type,
gphi *phi, tree tidx) gphi *phi, tree tidx)
{ {
tree name, cst; tree name;
gimple *load; gimple *load;
gimple_stmt_iterator gsi = gsi_for_stmt (m_switch); gimple_stmt_iterator gsi = gsi_for_stmt (m_switch);
location_t loc = gimple_location (m_switch); location_t loc = gimple_location (m_switch);
...@@ -561,9 +600,27 @@ switch_conversion::build_one_array (int num, tree arr_index_type, ...@@ -561,9 +600,27 @@ switch_conversion::build_one_array (int num, tree arr_index_type,
name = copy_ssa_name (PHI_RESULT (phi)); name = copy_ssa_name (PHI_RESULT (phi));
m_target_inbound_names[num] = name; m_target_inbound_names[num] = name;
cst = contains_same_values_p (m_constructors[num]); wide_int coeff_a, coeff_b;
if (cst) bool linear_p = contains_linear_function_p (m_constructors[num], &coeff_a,
load = gimple_build_assign (name, cst); &coeff_b);
if (linear_p)
{
if (dump_file && coeff_a.to_uhwi () > 0)
fprintf (dump_file, "Linear transformation with A = %" PRId64
" and B = %" PRId64 "\n", coeff_a.to_shwi (),
coeff_b.to_shwi ());
tree t = unsigned_type_for (TREE_TYPE (m_index_expr));
gimple_seq seq = NULL;
tree tmp = gimple_convert (&seq, t, m_index_expr);
tree tmp2 = gimple_build (&seq, MULT_EXPR, t,
wide_int_to_tree (t, coeff_a), tmp);
tree tmp3 = gimple_build (&seq, PLUS_EXPR, t, tmp2,
wide_int_to_tree (t, coeff_b));
tree tmp4 = gimple_convert (&seq, TREE_TYPE (name), tmp3);
gsi_insert_seq_before (&gsi, seq, GSI_SAME_STMT);
load = gimple_build_assign (name, tmp4);
}
else else
{ {
tree array_type, ctor, decl, value_type, fetch, default_type; tree array_type, ctor, decl, value_type, fetch, default_type;
......
...@@ -733,10 +733,12 @@ struct switch_conversion ...@@ -733,10 +733,12 @@ struct switch_conversion
order of phi nodes. */ order of phi nodes. */
void build_constructors (); void build_constructors ();
/* If all values in the constructor vector are the same, return the value. /* If all values in the constructor vector are products of a linear function
Otherwise return NULL_TREE. Not supposed to be called for empty a * x + b, then return true. When true, COEFF_A and COEFF_B and
vectors. */ coefficients of the linear function. Note that equal values are special
tree contains_same_values_p (vec<constructor_elt, va_gc> *vec); case of a linear function with a and b equal to zero. */
bool contains_linear_function_p (vec<constructor_elt, va_gc> *vec,
wide_int *coeff_a, wide_int *coeff_b);
/* Return type which should be used for array elements, either TYPE's /* Return type which should be used for array elements, either TYPE's
main variant or, for integral types, some smaller integral type main variant or, for integral types, some smaller integral type
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment