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
This commit is contained in:
Martin Liska 2018-10-24 15:52:21 +02:00 committed by Martin Liska
parent b5d0cdc9c8
commit 767d455188
9 changed files with 301 additions and 19 deletions

View File

@ -1,3 +1,14 @@
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

View File

@ -1,3 +1,12 @@
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.

View File

@ -0,0 +1,36 @@
/* 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" } } */

View File

@ -0,0 +1,67 @@
/* 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" } } */

View File

@ -0,0 +1,24 @@
/* 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" } } */

View File

@ -0,0 +1,38 @@
/* 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" } } */

View File

@ -0,0 +1,38 @@
/* 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" } } */

View File

@ -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 ()
} }
} }
/* 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
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,
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;

View File

@ -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