/* Global, SSA-based optimizations using mathematical identities.
- Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010
+ Free Software Foundation, Inc.
This file is part of GCC.
#include "flags.h"
#include "tree.h"
#include "tree-flow.h"
-#include "real.h"
#include "timevar.h"
#include "tree-pass.h"
#include "alloc-pool.h"
#include "basic-block.h"
#include "target.h"
#include "diagnostic.h"
-#include "rtl.h"
-#include "expr.h"
+#include "gimple-pretty-print.h"
+
+/* FIXME: RTL headers have to be included here for optabs. */
+#include "rtl.h" /* Because optabs.h wants enum rtx_code. */
+#include "expr.h" /* Because optabs.h wants sepops. */
#include "optabs.h"
/* This structure represents one basic block that either computes a
{
/* 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. */
+ order byte is set to n->size and the lowest order
+ byte to 1. */
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;
+ (unsigned HOST_WIDEST_INT)0x08070605 << 32 | 0x04030201);
+
+ if (n->size < (int)sizeof (HOST_WIDEST_INT))
+ n->n &= ((unsigned HOST_WIDEST_INT)1 <<
+ (n->size * BITS_PER_UNIT)) - 1;
source_expr1 = rhs1;
}
{
/* 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;
}
+ n->size = type_size / BITS_PER_UNIT;
}
break;
default:
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. */
+ have a full byte swap. The number is shifted to the left according
+ to the size of the symbolic number before using it. */
unsigned HOST_WIDEST_INT cmp =
sizeof (HOST_WIDEST_INT) < 8 ? 0 :
- (unsigned HOST_WIDEST_INT)0x08070605 << 32 | 0x04030201;
+ (unsigned HOST_WIDEST_INT)0x01020304 << 32 | 0x05060708;
struct symbolic_number n;
tree source_expr;
((unsigned HOST_WIDEST_INT)1 << (n.size * BITS_PER_UNIT)) - 1;
n.n &= mask;
- cmp &= mask;
+ cmp >>= (sizeof (HOST_WIDEST_INT) - n.size) * BITS_PER_UNIT;
}
/* A complete byte swap should make the symbolic number to start
0 /* todo_flags_finish */
}
};
+
+/* Find integer multiplications where the operands are extended from
+ smaller types, and replace the MULT_EXPR with a WIDEN_MULT_EXPR
+ where appropriate. */
+
+static unsigned int
+execute_optimize_widening_mul (void)
+{
+ bool changed = false;
+ basic_block bb;
+
+ 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);
+ gimple rhs1_stmt = NULL, rhs2_stmt = NULL;
+ tree type, type1 = NULL, type2 = NULL;
+ tree rhs1, rhs2, rhs1_convop = NULL, rhs2_convop = NULL;
+ enum tree_code rhs1_code, rhs2_code;
+
+ if (!is_gimple_assign (stmt)
+ || gimple_assign_rhs_code (stmt) != MULT_EXPR)
+ continue;
+
+ type = TREE_TYPE (gimple_assign_lhs (stmt));
+
+ if (TREE_CODE (type) != INTEGER_TYPE)
+ continue;
+
+ rhs1 = gimple_assign_rhs1 (stmt);
+ rhs2 = gimple_assign_rhs2 (stmt);
+
+ if (TREE_CODE (rhs1) == SSA_NAME)
+ {
+ rhs1_stmt = SSA_NAME_DEF_STMT (rhs1);
+ if (!is_gimple_assign (rhs1_stmt))
+ continue;
+ rhs1_code = gimple_assign_rhs_code (rhs1_stmt);
+ if (!CONVERT_EXPR_CODE_P (rhs1_code))
+ continue;
+ rhs1_convop = gimple_assign_rhs1 (rhs1_stmt);
+ type1 = TREE_TYPE (rhs1_convop);
+ if (TYPE_PRECISION (type1) * 2 != TYPE_PRECISION (type))
+ continue;
+ }
+ else if (TREE_CODE (rhs1) != INTEGER_CST)
+ continue;
+
+ if (TREE_CODE (rhs2) == SSA_NAME)
+ {
+ rhs2_stmt = SSA_NAME_DEF_STMT (rhs2);
+ if (!is_gimple_assign (rhs2_stmt))
+ continue;
+ rhs2_code = gimple_assign_rhs_code (rhs2_stmt);
+ if (!CONVERT_EXPR_CODE_P (rhs2_code))
+ continue;
+ rhs2_convop = gimple_assign_rhs1 (rhs2_stmt);
+ type2 = TREE_TYPE (rhs2_convop);
+ if (TYPE_PRECISION (type2) * 2 != TYPE_PRECISION (type))
+ continue;
+ }
+ else if (TREE_CODE (rhs2) != INTEGER_CST)
+ continue;
+
+ if (rhs1_stmt == NULL && rhs2_stmt == NULL)
+ continue;
+
+ /* Verify that the machine can perform a widening multiply in this
+ mode/signedness combination, otherwise this transformation is
+ likely to pessimize code. */
+ if ((rhs1_stmt == NULL || TYPE_UNSIGNED (type1))
+ && (rhs2_stmt == NULL || TYPE_UNSIGNED (type2))
+ && (optab_handler (umul_widen_optab, TYPE_MODE (type))
+ ->insn_code == CODE_FOR_nothing))
+ continue;
+ else if ((rhs1_stmt == NULL || !TYPE_UNSIGNED (type1))
+ && (rhs2_stmt == NULL || !TYPE_UNSIGNED (type2))
+ && (optab_handler (smul_widen_optab, TYPE_MODE (type))
+ ->insn_code == CODE_FOR_nothing))
+ continue;
+ else if (rhs1_stmt != NULL && rhs2_stmt != 0
+ && (TYPE_UNSIGNED (type1) != TYPE_UNSIGNED (type2))
+ && (optab_handler (usmul_widen_optab, TYPE_MODE (type))
+ ->insn_code == CODE_FOR_nothing))
+ continue;
+
+ if ((rhs1_stmt == NULL && !int_fits_type_p (rhs1, type2))
+ || (rhs2_stmt == NULL && !int_fits_type_p (rhs2, type1)))
+ continue;
+
+ if (rhs1_stmt == NULL)
+ gimple_assign_set_rhs1 (stmt, fold_convert (type2, rhs1));
+ else
+ gimple_assign_set_rhs1 (stmt, rhs1_convop);
+ if (rhs2_stmt == NULL)
+ gimple_assign_set_rhs2 (stmt, fold_convert (type1, rhs2));
+ else
+ gimple_assign_set_rhs2 (stmt, rhs2_convop);
+ gimple_assign_set_rhs_code (stmt, WIDEN_MULT_EXPR);
+ update_stmt (stmt);
+ changed = true;
+ }
+ }
+ return (changed ? TODO_dump_func | TODO_update_ssa | TODO_verify_ssa
+ | TODO_verify_stmts : 0);
+}
+
+static bool
+gate_optimize_widening_mul (void)
+{
+ return flag_expensive_optimizations && optimize;
+}
+
+struct gimple_opt_pass pass_optimize_widening_mul =
+{
+ {
+ GIMPLE_PASS,
+ "widening_mul", /* name */
+ gate_optimize_widening_mul, /* gate */
+ execute_optimize_widening_mul, /* 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 */
+ }
+};