x86 targets mask 32-bit shifts with a 5-bit mask (and 64-bit with 6-bit mask),
so they can use x >> -y instead of x >> 32-y.  This form is very common in
bitstream readers, where it's used to read the top N bits from a word.

The optimization converts:

        movl    $32, %ecx
        subl    %esi, %ecx
        sall    %cl, %eax

to:

        negl    %ecx
        sall    %cl, %eax

    PR target/36503

gcc/ChangeLog:

    * config/i386/i386.md (*ashl<mode>3_negcnt):
    New define_insn_and_split pattern.
    (*ashl<mode>3_negcnt_1): Ditto.
    (*<insn><mode>3_negcnt): Ditto.
    (*<insn><mode>3_negcnt_1): Ditto.

gcc/testsuite/ChangeLog:

    * gcc.target/i386/pr36503-1.c: New test.
    * gcc.target/i386/pr36503-2.c: New test.

Bootstrapped and regression tested on x86_64-linux-gnu {,-m32}.

Uros.
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 1c1bf659fc2..399a6a81f9c 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -15896,6 +15896,62 @@ (define_insn_and_split "*ashl<mode>3_mask_1"
   ""
   [(set_attr "isa" "*,bmi2")])
 
+(define_insn_and_split "*ashl<mode>3_negcnt"
+  [(set (match_operand:SWI48 0 "nonimmediate_operand")
+       (ashift:SWI48
+         (match_operand:SWI48 1 "nonimmediate_operand")
+         (subreg:QI
+           (minus
+             (match_operand 3 "const_int_operand")
+             (match_operand 2 "int248_register_operand" "c,r")) 0)))
+   (clobber (reg:CC FLAGS_REG))]
+  "ix86_binary_operator_ok (ASHIFT, <MODE>mode, operands)
+   && INTVAL (operands[3]) == <MODE_SIZE> * BITS_PER_UNIT
+   && ix86_pre_reload_split ()"
+  "#"
+  "&& 1"
+  [(parallel
+     [(set (match_dup 4)
+          (neg:QI (match_dup 2)))
+      (clobber (reg:CC FLAGS_REG))])
+   (parallel
+     [(set (match_dup 0)
+          (ashift:SWI48 (match_dup 1)
+                        (match_dup 4)))
+      (clobber (reg:CC FLAGS_REG))])]
+{
+  operands[2] = force_reg (GET_MODE (operands[2]), operands[2]);
+  operands[2] = gen_lowpart (QImode, operands[2]);
+
+  operands[4] = gen_reg_rtx (QImode);
+}
+  [(set_attr "isa" "*,bmi2")])
+
+(define_insn_and_split "*ashl<mode>3_negcnt_1"
+  [(set (match_operand:SWI48 0 "nonimmediate_operand")
+       (ashift:SWI48
+         (match_operand:SWI48 1 "nonimmediate_operand")
+         (minus:QI
+           (match_operand:QI 3 "const_int_operand")
+           (match_operand:QI 2 "register_operand" "c,r"))))
+   (clobber (reg:CC FLAGS_REG))]
+  "ix86_binary_operator_ok (ASHIFT, <MODE>mode, operands)
+   && INTVAL (operands[3]) == <MODE_SIZE> * BITS_PER_UNIT
+   && ix86_pre_reload_split ()"
+  "#"
+  "&& 1"
+  [(parallel
+     [(set (match_dup 4)
+          (neg:QI (match_dup 2)))
+      (clobber (reg:CC FLAGS_REG))])
+   (parallel
+     [(set (match_dup 0)
+          (ashift:SWI48 (match_dup 1)
+                        (match_dup 4)))
+      (clobber (reg:CC FLAGS_REG))])]
+  "operands[4] = gen_reg_rtx (QImode);"
+  [(set_attr "isa" "*,bmi2")])
+
 (define_insn "*bmi2_ashl<mode>3_1"
   [(set (match_operand:SWI48 0 "register_operand" "=r")
        (ashift:SWI48 (match_operand:SWI48 1 "nonimmediate_operand" "rm")
@@ -16622,6 +16678,62 @@ (define_insn_and_split "*<insn><mode>3_mask_1"
   ""
   [(set_attr "isa" "*,bmi2")])
 
+(define_insn_and_split "*<insn><mode>3_negcnt"
+  [(set (match_operand:SWI48 0 "nonimmediate_operand")
+       (any_shiftrt:SWI48
+         (match_operand:SWI48 1 "nonimmediate_operand")
+         (subreg:QI
+           (minus
+             (match_operand 3 "const_int_operand")
+             (match_operand 2 "int248_register_operand" "c,r")) 0)))
+   (clobber (reg:CC FLAGS_REG))]
+  "ix86_binary_operator_ok (<CODE>, <MODE>mode, operands)
+   && INTVAL (operands[3]) == <MODE_SIZE> * BITS_PER_UNIT
+   && ix86_pre_reload_split ()"
+  "#"
+  "&& 1"
+  [(parallel
+     [(set (match_dup 4)
+          (neg:QI (match_dup 2)))
+      (clobber (reg:CC FLAGS_REG))])
+   (parallel
+     [(set (match_dup 0)
+          (any_shiftrt:SWI48 (match_dup 1)
+                             (match_dup 4)))
+      (clobber (reg:CC FLAGS_REG))])]
+{
+  operands[2] = force_reg (GET_MODE (operands[2]), operands[2]);
+  operands[2] = gen_lowpart (QImode, operands[2]);
+
+  operands[4] = gen_reg_rtx (QImode);
+}
+  [(set_attr "isa" "*,bmi2")])
+
+(define_insn_and_split "*<insn><mode>3_negcnt_1"
+  [(set (match_operand:SWI48 0 "nonimmediate_operand")
+       (any_shiftrt:SWI48
+         (match_operand:SWI48 1 "nonimmediate_operand")
+         (minus:QI
+           (match_operand:QI 3 "const_int_operand")
+           (match_operand:QI 2 "register_operand" "c,r"))))
+   (clobber (reg:CC FLAGS_REG))]
+  "ix86_binary_operator_ok (<CODE>, <MODE>mode, operands)
+   && INTVAL (operands[3]) == <MODE_SIZE> * BITS_PER_UNIT
+   && ix86_pre_reload_split ()"
+  "#"
+  "&& 1"
+  [(parallel
+     [(set (match_dup 4)
+          (neg:QI (match_dup 2)))
+      (clobber (reg:CC FLAGS_REG))])
+   (parallel
+     [(set (match_dup 0)
+          (any_shiftrt:SWI48 (match_dup 1)
+                             (match_dup 4)))
+      (clobber (reg:CC FLAGS_REG))])]
+  "operands[4] = gen_reg_rtx (QImode);"
+  [(set_attr "isa" "*,bmi2")])
+
 (define_insn_and_split "*<insn><dwi>3_doubleword_mask"
   [(set (match_operand:<DWI> 0 "register_operand")
        (any_shiftrt:<DWI>
diff --git a/gcc/testsuite/gcc.target/i386/pr36503-1.c 
b/gcc/testsuite/gcc.target/i386/pr36503-1.c
new file mode 100644
index 00000000000..d94fabb827e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr36503-1.c
@@ -0,0 +1,20 @@
+/* PR target/36503 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -masm=att" } */
+/* { dg-additional-options "-mregparm=3" { target ia32 } } */
+/* { dg-final { scan-assembler-not "movl\[ \\t\]+\\\$32" } } */
+
+int foo (int i, int n)
+{
+  return i << (32 - n);
+}
+
+int bar (int i, int n)
+{
+  return i >> (32 - n);
+}
+
+unsigned int baz (unsigned int i, int n)
+{
+  return i >> (32 - n);
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr36503-2.c 
b/gcc/testsuite/gcc.target/i386/pr36503-2.c
new file mode 100644
index 00000000000..e1f7a1cf44f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr36503-2.c
@@ -0,0 +1,19 @@
+/* PR target/36503 */
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -masm=att" } */
+/* { dg-final { scan-assembler-not "movl\[ \\t\]+\\\$64" } } */
+
+long long foo (long long i, int n)
+{
+  return i << (64 - n);
+}
+
+long long bar (long long i, int n)
+{
+  return i >> (64 - n);
+}
+
+unsigned long long baz (unsigned long long i, int n)
+{
+  return i >> (64 - n);
+}

Reply via email to