aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog15
-rw-r--r--gcc/config/mips/mips.h2
-rw-r--r--gcc/config/rs6000/rs6000.h4
-rw-r--r--gcc/doc/tm.texi19
-rw-r--r--gcc/optabs.c80
5 files changed, 112 insertions, 8 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index d5170d8..826aa53 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,18 @@
+2007-08-09 Sandra Loosemore <sandra@codesourcery.com>
+ Nigel Stephens <nigel@mips.com>
+
+ * doc/tm.texi (CLZ_DEFINED_VALUE_AT_ZERO, CTZ_DEFINED_VALUE_AT_ZERO):
+ Document change in interpretation of value from boolean to
+ tri-state integer.
+ * optabs.c (expand_ffs, expand_ctz): New functions to compute
+ ffs and ctz using clz.
+ (expand_unop): Call them.
+ * config/rs6000/rs6000.h (CLZ_DEFINED_VALUE_AT_ZERO): Fix its
+ result value.
+ (CTZ_DEFINED_VALUE_AT_ZERO): Likewise.
+ * config/mips/mips.h (CLZ_DEFINED_VALUE_AT_ZERO): Likewise, to
+ enable the new ffs expansion on this target.
+
2007-08-09 Jan Hubicka <jh@suse.cz>
* optabs.c (expand_widen_pattern_expr): Use optabs accestors.
diff --git a/gcc/config/mips/mips.h b/gcc/config/mips/mips.h
index 9a55f02..9e90478 100644
--- a/gcc/config/mips/mips.h
+++ b/gcc/config/mips/mips.h
@@ -1295,7 +1295,7 @@ extern enum mips_code_readable_setting mips_code_readable;
/* The [d]clz instructions have the natural values at 0. */
#define CLZ_DEFINED_VALUE_AT_ZERO(MODE, VALUE) \
- ((VALUE) = GET_MODE_BITSIZE (MODE), true)
+ ((VALUE) = GET_MODE_BITSIZE (MODE), 2)
/* Standard register usage. */
diff --git a/gcc/config/rs6000/rs6000.h b/gcc/config/rs6000/rs6000.h
index fae5ea2..5f34c80 100644
--- a/gcc/config/rs6000/rs6000.h
+++ b/gcc/config/rs6000/rs6000.h
@@ -1856,10 +1856,10 @@ do { \
/* The cntlzw and cntlzd instructions return 32 and 64 for input of zero. */
#define CLZ_DEFINED_VALUE_AT_ZERO(MODE, VALUE) \
- ((VALUE) = ((MODE) == SImode ? 32 : 64))
+ ((VALUE) = ((MODE) == SImode ? 32 : 64), 1)
/* The CTZ patterns return -1 for input of zero. */
-#define CTZ_DEFINED_VALUE_AT_ZERO(MODE, VALUE) ((VALUE) = -1)
+#define CTZ_DEFINED_VALUE_AT_ZERO(MODE, VALUE) ((VALUE) = -1, 1)
/* Specify the machine mode that pointers have.
After generation of rtl, the compiler makes no further distinction
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index bf6859d..1b690a1 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -9638,14 +9638,23 @@ given mode.
@defmac CLZ_DEFINED_VALUE_AT_ZERO (@var{mode}, @var{value})
@defmacx CTZ_DEFINED_VALUE_AT_ZERO (@var{mode}, @var{value})
-A C expression that evaluates to true if the architecture defines a value
-for @code{clz} or @code{ctz} with a zero operand. If so, @var{value}
-should be set to this value. If this macro is not defined, the value of
-@code{clz} or @code{ctz} is assumed to be undefined.
+A C expression that indicates whether the architecture defines a value
+for @code{clz} or @code{ctz} with a zero operand.
+A result of @code{0} indicates the value is undefined.
+If the value is defined for only the RTL expression, the macro should
+evaluate to @code{1}; if the value applies also to the corresponding optab
+entry (which is normally the case if it expands directly into
+the corresponding RTL), then the macro should evaluate to @code{2}.
+In the cases where the value is defined, @var{value} should be set to
+this value.
+
+If this macro is not defined, the value of @code{clz} or
+@code{ctz} at zero is assumed to be undefined.
This macro must be defined if the target's expansion for @code{ffs}
relies on a particular value to get correct results. Otherwise it
-is not necessary, though it may be used to optimize some corner cases.
+is not necessary, though it may be used to optimize some corner cases, and
+to provide a default expansion for the @code{ffs} optab.
Note that regardless of this macro the ``definedness'' of @code{clz}
and @code{ctz} at zero do @emph{not} extend to the builtin functions
diff --git a/gcc/optabs.c b/gcc/optabs.c
index 5afd935..bf15d4f 100644
--- a/gcc/optabs.c
+++ b/gcc/optabs.c
@@ -122,6 +122,8 @@ static void prepare_float_lib_cmp (rtx *, rtx *, enum rtx_code *,
enum machine_mode *, int *);
static rtx widen_clz (enum machine_mode, rtx, rtx);
static rtx expand_parity (enum machine_mode, rtx, rtx);
+static rtx expand_ffs (enum machine_mode, rtx, rtx);
+static rtx expand_ctz (enum machine_mode, rtx, rtx);
static enum rtx_code get_rtx_code (enum tree_code, bool);
static rtx vector_compare_rtx (tree, bool, enum insn_code);
@@ -2561,6 +2563,68 @@ expand_parity (enum machine_mode mode, rtx op0, rtx target)
return 0;
}
+/* Try calculating ffs(x) using clz(x). Since the ffs builtin promises
+ to return zero for a zero value and clz may have an undefined value
+ in that case, only do this if we know clz returns the right thing so
+ that we don't have to generate a test and branch. */
+static rtx
+expand_ffs (enum machine_mode mode, rtx op0, rtx target)
+{
+ HOST_WIDE_INT val;
+ if (clz_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing
+ && CLZ_DEFINED_VALUE_AT_ZERO (mode, val) == 2
+ && val == GET_MODE_BITSIZE (mode))
+ {
+ rtx last = get_last_insn ();
+ rtx temp;
+
+ temp = expand_unop (mode, neg_optab, op0, NULL_RTX, true);
+ if (temp)
+ temp = expand_binop (mode, and_optab, op0, temp, NULL_RTX,
+ true, OPTAB_DIRECT);
+ if (temp)
+ temp = expand_unop (mode, clz_optab, temp, NULL_RTX, true);
+ if (temp)
+ temp = expand_binop (mode, sub_optab,
+ GEN_INT (GET_MODE_BITSIZE (mode)),
+ temp,
+ target, true, OPTAB_DIRECT);
+ if (temp == 0)
+ delete_insns_since (last);
+ return temp;
+ }
+ return 0;
+}
+
+/* We can compute ctz(x) using clz(x) with a similar recipe. Here the ctz
+ builtin has an undefined result on zero, just like clz, so we don't have
+ to do that check. */
+static rtx
+expand_ctz (enum machine_mode mode, rtx op0, rtx target)
+{
+ if (clz_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+ {
+ rtx last = get_last_insn ();
+ rtx temp;
+
+ temp = expand_unop (mode, neg_optab, op0, NULL_RTX, true);
+ if (temp)
+ temp = expand_binop (mode, and_optab, op0, temp, NULL_RTX,
+ true, OPTAB_DIRECT);
+ if (temp)
+ temp = expand_unop (mode, clz_optab, temp, NULL_RTX, true);
+ if (temp)
+ temp = expand_binop (mode, xor_optab, temp,
+ GEN_INT (GET_MODE_BITSIZE (mode) - 1),
+ target,
+ true, OPTAB_DIRECT);
+ if (temp == 0)
+ delete_insns_since (last);
+ return temp;
+ }
+ return 0;
+}
+
/* Extract the OMODE lowpart from VAL, which has IMODE. Under certain
conditions, VAL may already be a SUBREG against which we cannot generate
a further SUBREG. In this case, we expect forcing the value into a
@@ -2886,6 +2950,22 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
return temp;
}
+ /* Try implementing ffs (x) in terms of clz (x). */
+ if (unoptab == ffs_optab)
+ {
+ temp = expand_ffs (mode, op0, target);
+ if (temp)
+ return temp;
+ }
+
+ /* Try implementing ctz (x) in terms of clz (x). */
+ if (unoptab == ctz_optab)
+ {
+ temp = expand_ctz (mode, op0, target);
+ if (temp)
+ return temp;
+ }
+
try_libcall:
/* Now try a library call in this mode. */
if (optab_handler (unoptab, mode)->libfunc)