aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog7
-rw-r--r--gcc/optabs.c75
-rw-r--r--gcc/testsuite/ChangeLog5
-rw-r--r--gcc/testsuite/gcc.target/i386/pr68647.c18
4 files changed, 104 insertions, 1 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 845d909..c90a062 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,10 @@
+2015-12-02 Jakub Jelinek <jakub@redhat.com>
+
+ PR target/68647
+ * optabs.c (expand_doubleword_popcount, expand_doubleword_parity):
+ New functions.
+ (expand_unop): Use them.
+
2015-12-02 Marek Polacek <polacek@redhat.com>
PR c++/68653
diff --git a/gcc/optabs.c b/gcc/optabs.c
index 5507642..8cc4802 100644
--- a/gcc/optabs.c
+++ b/gcc/optabs.c
@@ -2223,6 +2223,58 @@ expand_doubleword_clz (machine_mode mode, rtx op0, rtx target)
return 0;
}
+/* Try calculating popcount of a double-word quantity as two popcount's of
+ word-sized quantities and summing up the results. */
+static rtx
+expand_doubleword_popcount (machine_mode mode, rtx op0, rtx target)
+{
+ rtx t0, t1, t;
+ rtx_insn *seq;
+
+ start_sequence ();
+
+ t0 = expand_unop_direct (word_mode, popcount_optab,
+ operand_subword_force (op0, 0, mode), NULL_RTX,
+ true);
+ t1 = expand_unop_direct (word_mode, popcount_optab,
+ operand_subword_force (op0, 1, mode), NULL_RTX,
+ true);
+ if (!t0 || !t1)
+ {
+ end_sequence ();
+ return NULL_RTX;
+ }
+
+ /* If we were not given a target, use a word_mode register, not a
+ 'mode' register. The result will fit, and nobody is expecting
+ anything bigger (the return type of __builtin_popcount* is int). */
+ if (!target)
+ target = gen_reg_rtx (word_mode);
+
+ t = expand_binop (word_mode, add_optab, t0, t1, target, 0, OPTAB_DIRECT);
+
+ seq = get_insns ();
+ end_sequence ();
+
+ add_equal_note (seq, t, POPCOUNT, op0, 0);
+ emit_insn (seq);
+ return t;
+}
+
+/* Try calculating
+ (parity:wide x)
+ as
+ (parity:narrow (low (x) ^ high (x))) */
+static rtx
+expand_doubleword_parity (machine_mode mode, rtx op0, rtx target)
+{
+ rtx t = expand_binop (word_mode, xor_optab,
+ operand_subword_force (op0, 0, mode),
+ operand_subword_force (op0, 1, mode),
+ NULL_RTX, 0, OPTAB_DIRECT);
+ return expand_unop (word_mode, parity_optab, t, target, true);
+}
+
/* Try calculating
(bswap:narrow x)
as
@@ -2582,7 +2634,7 @@ expand_absneg_bit (enum rtx_code code, machine_mode mode,
different mode or with a libcall. */
static rtx
expand_unop_direct (machine_mode mode, optab unoptab, rtx op0, rtx target,
- int unsignedp)
+ int unsignedp)
{
if (optab_handler (unoptab, mode) != CODE_FOR_nothing)
{
@@ -2665,6 +2717,27 @@ expand_unop (machine_mode mode, optab unoptab, rtx op0, rtx target,
goto try_libcall;
}
+ if (unoptab == popcount_optab
+ && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
+ && optab_handler (unoptab, word_mode) != CODE_FOR_nothing
+ && optimize_insn_for_speed_p ())
+ {
+ temp = expand_doubleword_popcount (mode, op0, target);
+ if (temp)
+ return temp;
+ }
+
+ if (unoptab == parity_optab
+ && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
+ && (optab_handler (unoptab, word_mode) != CODE_FOR_nothing
+ || optab_handler (popcount_optab, word_mode) != CODE_FOR_nothing)
+ && optimize_insn_for_speed_p ())
+ {
+ temp = expand_doubleword_parity (mode, op0, target);
+ if (temp)
+ return temp;
+ }
+
/* Widening (or narrowing) bswap needs special treatment. */
if (unoptab == bswap_optab)
{
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index e8291a9..0400e17 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2015-12-02 Jakub Jelinek <jakub@redhat.com>
+
+ PR target/68647
+ * gcc.target/i386/pr68647.c: New test.
+
2015-12-02 Marek Polacek <polacek@redhat.com>
PR c++/68653
diff --git a/gcc/testsuite/gcc.target/i386/pr68647.c b/gcc/testsuite/gcc.target/i386/pr68647.c
new file mode 100644
index 0000000..7054931
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr68647.c
@@ -0,0 +1,18 @@
+/* PR target/68647 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -mpopcnt" } */
+
+int
+f1 (unsigned long long a)
+{
+ return __builtin_popcountll (a);
+}
+
+int
+f2 (unsigned long long a)
+{
+ return __builtin_parityll (a);
+}
+
+/* { dg-final { scan-assembler-not "__popcountdi2" } } */
+/* { dg-final { scan-assembler-not "__paritydi2" } } */