From: Andreas Krebbel Date: Sun, 14 Jun 2009 14:45:32 +0000 (+0000) Subject: passes.c: Add bswap pass. X-Git-Tag: releases/gcc-4.5.0~5219 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=03bd2f1af7c1f2343940f6ca5409048ba16a2e4c;p=thirdparty%2Fgcc.git passes.c: Add bswap pass. 2009-06-14 Andreas Krebbel * passes.c: Add bswap pass. * tree-pass.h: Add pass_optimize_bswap declaration. * tree-ssa-math-opts.c: Include diagnostics.h for print_gimple_stmt. Include rtl.h, expr.h and optabs.h for optab_handler check. (struct symbolic_number, pass_optimize_bswap): New definition. (do_shift_rotate, verify_symbolic_number_p): New functions. (find_bswap_1, find_bswap, execute_optimize_bswap): New functions. (gate_optimize_bswap): New function. * tree.c (widest_int_cst_value): New function. * tree.h (widest_int_cst_value): Prototype added. 2009-06-14 Andreas Krebbel * gcc.dg/optimize-bswap-1.c: New testcase. From-SVN: r148471 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 99a578fd7683..7a924476d114 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,16 @@ +2009-06-14 Andreas Krebbel + + * passes.c: Add bswap pass. + * tree-pass.h: Add pass_optimize_bswap declaration. + * tree-ssa-math-opts.c: Include diagnostics.h for print_gimple_stmt. + Include rtl.h, expr.h and optabs.h for optab_handler check. + (struct symbolic_number, pass_optimize_bswap): New definition. + (do_shift_rotate, verify_symbolic_number_p): New functions. + (find_bswap_1, find_bswap, execute_optimize_bswap): New functions. + (gate_optimize_bswap): New function. + * tree.c (widest_int_cst_value): New function. + * tree.h (widest_int_cst_value): Prototype added. + 2009-06-14 Steven Bosscher * cfgcleanup.c (old_insns_match_p): Remove code to substitute diff --git a/gcc/passes.c b/gcc/passes.c index adf0ed0091fc..6870973229dd 100644 --- a/gcc/passes.c +++ b/gcc/passes.c @@ -638,6 +638,7 @@ init_optimization_passes (void) NEXT_PASS (pass_copy_prop); NEXT_PASS (pass_fold_builtins); NEXT_PASS (pass_cse_sincos); + NEXT_PASS (pass_optimize_bswap); NEXT_PASS (pass_split_crit_edges); NEXT_PASS (pass_pre); NEXT_PASS (pass_sink_code); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 4b7d929e5b0f..25ae32056578 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2009-06-14 Andreas Krebbel + + * gcc.dg/optimize-bswap-1.c: New testcase. + 2009-06-14 Richard Guenther PR middle-end/40389 diff --git a/gcc/testsuite/gcc.dg/optimize-bswap-1.c b/gcc/testsuite/gcc.dg/optimize-bswap-1.c new file mode 100644 index 000000000000..a603f6d9193f --- /dev/null +++ b/gcc/testsuite/gcc.dg/optimize-bswap-1.c @@ -0,0 +1,52 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target stdint_types } */ +/* { dg-options "-O2 -fdump-tree-bswap" } */ + +#include + +#define __const_swab32(x) ((uint32_t)( \ + (((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \ + (((uint32_t)(x) & (uint32_t)0x0000ff00UL) << 8) | \ + (((uint32_t)(x) & (uint32_t)0x00ff0000UL) >> 8) | \ + (((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24))) + +#define __const_swab64(x) ((uint64_t)( \ + (((uint64_t)(x) & (uint64_t)0x00000000000000ffULL) << 56) | \ + (((uint64_t)(x) & (uint64_t)0x000000000000ff00ULL) << 40) | \ + (((uint64_t)(x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \ + (((uint64_t)(x) & (uint64_t)0x00000000ff000000ULL) << 8) | \ + (((uint64_t)(x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \ + (((uint64_t)(x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \ + (((uint64_t)(x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \ + (((uint64_t)(x) & (uint64_t)0xff00000000000000ULL) >> 56))) + +/* This byte swap implementation is used by the Linux kernel and the + GNU C library. */ + +uint32_t +swap32_a (uint32_t in) +{ + return __const_swab32 (in); +} + +uint64_t +swap64 (uint64_t in) +{ + return __const_swab64 (in); +} + +/* The OpenSSH byte swap implementation. */ +uint32_t +swap32_b (uint32_t in) +{ + uint32_t a; + + a = (in << 16) | (in >> 16); + a = ((a & 0x00ff00ff) << 8) | ((a & 0xff00ff00) >> 8); + + return a; +} + +/* { dg-final { scan-tree-dump-times "32 bit bswap implementation found at" 2 "bswap" } } */ +/* { dg-final { scan-tree-dump-times "64 bit bswap implementation found at" 1 "bswap" } } */ +/* { dg-final { cleanup-tree-dump "bswap" } } */ diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index c2bf49456de4..1268e35609ff 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -377,6 +377,7 @@ extern struct gimple_opt_pass pass_late_warn_uninitialized; extern struct gimple_opt_pass pass_cse_reciprocals; extern struct gimple_opt_pass pass_cse_sincos; extern struct gimple_opt_pass pass_convert_to_rsqrt; +extern struct gimple_opt_pass pass_optimize_bswap; extern struct gimple_opt_pass pass_warn_function_return; extern struct gimple_opt_pass pass_warn_function_noreturn; extern struct gimple_opt_pass pass_cselim; diff --git a/gcc/tree-ssa-math-opts.c b/gcc/tree-ssa-math-opts.c index 8d08aa7709fc..d864e201c776 100644 --- a/gcc/tree-ssa-math-opts.c +++ b/gcc/tree-ssa-math-opts.c @@ -97,7 +97,10 @@ along with GCC; see the file COPYING3. If not see #include "alloc-pool.h" #include "basic-block.h" #include "target.h" - +#include "diagnostic.h" +#include "rtl.h" +#include "expr.h" +#include "optabs.h" /* This structure represents one basic block that either computes a division, or is a common dominator for basic block that compute a @@ -879,3 +882,383 @@ struct gimple_opt_pass pass_convert_to_rsqrt = | TODO_verify_stmts /* todo_flags_finish */ } }; + +/* A symbolic number is used to detect byte permutation and selection + patterns. Therefore the field N contains an artificial number + consisting of byte size markers: + + 0 - byte has the value 0 + 1..size - byte contains the content of the byte + number indexed with that value minus one */ + +struct symbolic_number { + unsigned HOST_WIDEST_INT n; + int size; +}; + +/* Perform a SHIFT or ROTATE operation by COUNT bits on symbolic + number N. Return false if the requested operation is not permitted + on a symbolic number. */ + +static inline bool +do_shift_rotate (enum tree_code code, + struct symbolic_number *n, + int count) +{ + if (count % 8 != 0) + return false; + + /* Zero out the extra bits of N in order to avoid them being shifted + into the significant bits. */ + if (n->size < (int)sizeof (HOST_WIDEST_INT)) + n->n &= ((unsigned HOST_WIDEST_INT)1 << (n->size * BITS_PER_UNIT)) - 1; + + switch (code) + { + case LSHIFT_EXPR: + n->n <<= count; + break; + case RSHIFT_EXPR: + n->n >>= count; + break; + case LROTATE_EXPR: + n->n = (n->n << count) | (n->n >> ((n->size * BITS_PER_UNIT) - count)); + break; + case RROTATE_EXPR: + n->n = (n->n >> count) | (n->n << ((n->size * BITS_PER_UNIT) - count)); + break; + default: + return false; + } + return true; +} + +/* Perform sanity checking for the symbolic number N and the gimple + statement STMT. */ + +static inline bool +verify_symbolic_number_p (struct symbolic_number *n, gimple stmt) +{ + tree lhs_type; + + lhs_type = gimple_expr_type (stmt); + + if (TREE_CODE (lhs_type) != INTEGER_TYPE) + return false; + + if (TYPE_PRECISION (lhs_type) != n->size * BITS_PER_UNIT) + return false; + + return true; +} + +/* find_bswap_1 invokes itself recursively with N and tries to perform + the operation given by the rhs of STMT on the result. If the + operation could successfully be executed the function returns the + tree expression of the source operand and NULL otherwise. */ + +static tree +find_bswap_1 (gimple stmt, struct symbolic_number *n, int limit) +{ + enum tree_code code; + tree rhs1, rhs2 = NULL; + gimple rhs1_stmt, rhs2_stmt; + tree source_expr1; + enum gimple_rhs_class rhs_class; + + if (!limit || !is_gimple_assign (stmt)) + return NULL_TREE; + + rhs1 = gimple_assign_rhs1 (stmt); + + if (TREE_CODE (rhs1) != SSA_NAME) + return NULL_TREE; + + code = gimple_assign_rhs_code (stmt); + rhs_class = gimple_assign_rhs_class (stmt); + rhs1_stmt = SSA_NAME_DEF_STMT (rhs1); + + if (rhs_class == GIMPLE_BINARY_RHS) + rhs2 = gimple_assign_rhs2 (stmt); + + /* Handle unary rhs and binary rhs with integer constants as second + operand. */ + + if (rhs_class == GIMPLE_UNARY_RHS + || (rhs_class == GIMPLE_BINARY_RHS + && TREE_CODE (rhs2) == INTEGER_CST)) + { + if (code != BIT_AND_EXPR + && code != LSHIFT_EXPR + && code != RSHIFT_EXPR + && code != LROTATE_EXPR + && code != RROTATE_EXPR + && code != NOP_EXPR + && code != CONVERT_EXPR) + return NULL_TREE; + + source_expr1 = find_bswap_1 (rhs1_stmt, n, limit - 1); + + /* If find_bswap_1 returned NULL STMT is a leaf node and we have + to initialize the symbolic number. */ + if (!source_expr1) + { + /* Set up the symbolic number N by setting each byte to a + value between 1 and the byte size of rhs1. The highest + order byte is set to 1 and the lowest order byte to + n.size. */ + n->size = TYPE_PRECISION (TREE_TYPE (rhs1)); + if (n->size % BITS_PER_UNIT != 0) + return NULL_TREE; + n->size /= BITS_PER_UNIT; + n->n = (sizeof (HOST_WIDEST_INT) < 8 ? 0 : + (unsigned HOST_WIDEST_INT)0x01020304 << 32 | 0x05060708); + n->n >>= (sizeof (HOST_WIDEST_INT) - n->size) * BITS_PER_UNIT; + + source_expr1 = rhs1; + } + + switch (code) + { + case BIT_AND_EXPR: + { + int i; + unsigned HOST_WIDEST_INT val = widest_int_cst_value (rhs2); + unsigned HOST_WIDEST_INT tmp = val; + + /* Only constants masking full bytes are allowed. */ + for (i = 0; i < n->size; i++, tmp >>= BITS_PER_UNIT) + if ((tmp & 0xff) != 0 && (tmp & 0xff) != 0xff) + return NULL_TREE; + + n->n &= val; + } + break; + case LSHIFT_EXPR: + case RSHIFT_EXPR: + case LROTATE_EXPR: + case RROTATE_EXPR: + if (!do_shift_rotate (code, n, (int)TREE_INT_CST_LOW (rhs2))) + return NULL_TREE; + break; + CASE_CONVERT: + { + int type_size; + + type_size = TYPE_PRECISION (gimple_expr_type (stmt)); + if (type_size % BITS_PER_UNIT != 0) + return NULL_TREE; + + type_size /= BITS_PER_UNIT; + + if (type_size / BITS_PER_UNIT < (int)(sizeof (HOST_WIDEST_INT))) + { + /* If STMT casts to a smaller type mask out the bits not + belonging to the target type. */ + n->size = type_size / BITS_PER_UNIT; + n->n &= ((unsigned HOST_WIDEST_INT)1 << type_size) - 1; + } + } + break; + default: + return NULL_TREE; + }; + return verify_symbolic_number_p (n, stmt) ? source_expr1 : NULL; + } + + /* Handle binary rhs. */ + + if (rhs_class == GIMPLE_BINARY_RHS) + { + struct symbolic_number n1, n2; + tree source_expr2; + + if (code != BIT_IOR_EXPR) + return NULL_TREE; + + if (TREE_CODE (rhs2) != SSA_NAME) + return NULL_TREE; + + rhs2_stmt = SSA_NAME_DEF_STMT (rhs2); + + switch (code) + { + case BIT_IOR_EXPR: + source_expr1 = find_bswap_1 (rhs1_stmt, &n1, limit - 1); + + if (!source_expr1) + return NULL_TREE; + + source_expr2 = find_bswap_1 (rhs2_stmt, &n2, limit - 1); + + if (source_expr1 != source_expr2 + || n1.size != n2.size) + return NULL_TREE; + + n->size = n1.size; + n->n = n1.n | n2.n; + + if (!verify_symbolic_number_p (n, stmt)) + return NULL_TREE; + + break; + default: + return NULL_TREE; + } + return source_expr1; + } + return NULL_TREE; +} + +/* Check if STMT completes a bswap implementation consisting of ORs, + SHIFTs and ANDs. Return the source tree expression on which the + byte swap is performed and NULL if no bswap was found. */ + +static tree +find_bswap (gimple stmt) +{ +/* The number which the find_bswap result should match in order to + have a full byte swap. The insignificant bytes are masked out + before using it. */ + unsigned HOST_WIDEST_INT cmp = + sizeof (HOST_WIDEST_INT) < 8 ? 0 : + (unsigned HOST_WIDEST_INT)0x08070605 << 32 | 0x04030201; + + struct symbolic_number n; + tree source_expr; + + source_expr = find_bswap_1 (stmt, &n, + TREE_INT_CST_LOW ( + TYPE_SIZE_UNIT (gimple_expr_type (stmt)))); + + if (!source_expr) + return NULL_TREE; + + /* Zero out the extra bits of N and CMP. */ + if (n.size < (int)sizeof (HOST_WIDEST_INT)) + { + unsigned HOST_WIDEST_INT mask = + ((unsigned HOST_WIDEST_INT)1 << (n.size * BITS_PER_UNIT)) - 1; + + n.n &= mask; + cmp &= mask; + } + + /* A complete byte swap should make the symbolic number to start + with the largest digit in the highest order byte. */ + if (cmp != n.n) + return NULL_TREE; + + return source_expr; +} + +/* Find manual byte swap implementations and turn them into a bswap + builtin invokation. */ + +static unsigned int +execute_optimize_bswap (void) +{ + basic_block bb; + bool bswap32_p, bswap64_p; + bool changed = false; + + if (BITS_PER_UNIT != 8) + return 0; + + if (sizeof (HOST_WIDEST_INT) < 8) + return 0; + + bswap32_p = (built_in_decls[BUILT_IN_BSWAP32] + && optab_handler (bswap_optab, SImode)->insn_code != + CODE_FOR_nothing); + bswap64_p = (built_in_decls[BUILT_IN_BSWAP64] + && optab_handler (bswap_optab, DImode)->insn_code != + CODE_FOR_nothing); + + if (!bswap32_p && !bswap64_p) + return 0; + + FOR_EACH_BB (bb) + { + gimple_stmt_iterator gsi; + + for (gsi = gsi_after_labels (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple stmt = gsi_stmt (gsi); + tree bswap_src; + tree fndecl = NULL_TREE; + int type_size; + gimple call; + + if (!is_gimple_assign (stmt) + || gimple_assign_rhs_code (stmt) != BIT_IOR_EXPR) + continue; + + type_size = TYPE_PRECISION (gimple_expr_type (stmt)); + + switch (type_size) + { + case 32: + if (bswap32_p) + fndecl = built_in_decls[BUILT_IN_BSWAP32]; + break; + case 64: + if (bswap64_p) + fndecl = built_in_decls[BUILT_IN_BSWAP64]; + break; + default: + continue; + } + + if (!fndecl) + continue; + + bswap_src = find_bswap (stmt); + + if (!bswap_src) + continue; + + changed = true; + call = gimple_build_call (fndecl, 1, bswap_src); + gimple_call_set_lhs (call, gimple_assign_lhs (stmt)); + + if (dump_file) + { + fprintf (dump_file, "%d bit bswap implementation found at: ", + (int)type_size); + print_gimple_stmt (dump_file, stmt, 0, 0); + } + + gsi_insert_after (&gsi, call, GSI_SAME_STMT); + gsi_remove (&gsi, true); + } + } + + return (changed ? TODO_dump_func | TODO_update_ssa | TODO_verify_ssa + | TODO_verify_stmts : 0); +} + +static bool +gate_optimize_bswap (void) +{ + return flag_expensive_optimizations && optimize; +} + +struct gimple_opt_pass pass_optimize_bswap = +{ + { + GIMPLE_PASS, + "bswap", /* name */ + gate_optimize_bswap, /* gate */ + execute_optimize_bswap, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_NONE, /* tv_id */ + PROP_ssa, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0 /* todo_flags_finish */ + } +}; diff --git a/gcc/tree.c b/gcc/tree.c index eb1ad157cfa6..6ed29ca42d1b 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -8489,6 +8489,35 @@ int_cst_value (const_tree x) return val; } +/* Return value of a constant X and sign-extend it. */ + +HOST_WIDEST_INT +widest_int_cst_value (const_tree x) +{ + unsigned bits = TYPE_PRECISION (TREE_TYPE (x)); + unsigned HOST_WIDEST_INT val = TREE_INT_CST_LOW (x); + +#if HOST_BITS_PER_WIDEST_INT > HOST_BITS_PER_WIDE_INT + gcc_assert (HOST_BITS_PER_WIDEST_INT >= 2 * HOST_BITS_PER_WIDE_INT); + val |= TREE_INT_CST_HIGH (x) << HOST_BITS_PER_WIDE_INT; +#else + /* Make sure the sign-extended value will fit in a HOST_WIDE_INT. */ + gcc_assert (TREE_INT_CST_HIGH (x) == 0 + || TREE_INT_CST_HIGH (x) == -1); +#endif + + if (bits < HOST_BITS_PER_WIDEST_INT) + { + bool negative = ((val >> (bits - 1)) & 1) != 0; + if (negative) + val |= (~(unsigned HOST_WIDEST_INT) 0) << (bits - 1) << 1; + else + val &= ~((~(unsigned HOST_WIDEST_INT) 0) << (bits - 1) << 1); + } + + return val; +} + /* If TYPE is an integral type, return an equivalent type which is unsigned iff UNSIGNEDP is true. If TYPE is not an integral type, return TYPE itself. */ diff --git a/gcc/tree.h b/gcc/tree.h index f490af402bae..9c1a1aa67dc0 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -4868,6 +4868,7 @@ extern tree build_nonstandard_integer_type (unsigned HOST_WIDE_INT, int); extern tree build_range_type (tree, tree, tree); extern bool subrange_type_for_debug_p (const_tree, tree *, tree *); extern HOST_WIDE_INT int_cst_value (const_tree); +extern HOST_WIDEST_INT widest_int_cst_value (const_tree); extern bool fields_compatible_p (const_tree, const_tree); extern tree find_compatible_field (tree, tree);