]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
i386: Optimize QImode insn with high input registers
authorUros Bizjak <ubizjak@gmail.com>
Thu, 16 Nov 2023 17:07:36 +0000 (18:07 +0100)
committerUros Bizjak <ubizjak@gmail.com>
Thu, 16 Nov 2023 18:10:21 +0000 (19:10 +0100)
Sometimes the compiler emits the following code with <insn>qi_ext<mode>_0:

        shrl    $8, %eax
        addb    %bh, %al

Patch introduces new low part QImode insn patterns with both of
their input arguments extracted from high register.  This invalid
insn is split after reload to a move from the high register
and <insn>qi_ext<mode>_0 instruction.  The combine pass is able to
convert shift to zero/sign-extract sub-RTX, which we split to the
optimal:

        movzbl  %bh, %edx
        addb    %ah, %dl

PR target/78904

gcc/ChangeLog:

* config/i386/i386.md (*addqi_ext2<mode>_0):
New define_insn_and_split pattern.
(*subqi_ext2<mode>_0): Ditto.
(*<code>qi_ext2<mode>_0): Ditto.

gcc/testsuite/ChangeLog:

* gcc.target/i386/pr78904-10.c: New test.
* gcc.target/i386/pr78904-10a.c: New test.
* gcc.target/i386/pr78904-10b.c: New test.

gcc/config/i386/i386.md
gcc/testsuite/gcc.target/i386/pr78904-10.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/pr78904-10a.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/pr78904-10b.c [new file with mode: 0644]

index f5407ab305491f1eba355f06f6898e2af05902e6..1b5a794b9e54e311c3ae2279773488b21ed58eaf 100644 (file)
    (set_attr "type" "alu")
    (set_attr "mode" "QI")])
 
+(define_insn_and_split "*addqi_ext2<mode>_0"
+  [(set (match_operand:QI 0 "register_operand" "=&Q")
+       (plus:QI
+         (subreg:QI
+           (match_operator:SWI248 3 "extract_operator"
+             [(match_operand 1 "int248_register_operand" "Q")
+              (const_int 8)
+              (const_int 8)]) 0)
+         (subreg:QI
+           (match_operator:SWI248 4 "extract_operator"
+             [(match_operand 2 "int248_register_operand" "Q")
+              (const_int 8)
+              (const_int 8)]) 0)))
+   (clobber (reg:CC FLAGS_REG))]
+  ""
+  "#"
+  "&& reload_completed"
+  [(set (match_dup 0)
+       (subreg:QI
+         (match_op_dup 4
+           [(match_dup 2) (const_int 8) (const_int 8)]) 0))
+   (parallel
+     [(set (match_dup 0)
+          (plus:QI
+            (subreg:QI
+              (match_op_dup 3
+                [(match_dup 1) (const_int 8) (const_int 8)]) 0)
+          (match_dup 0)))
+      (clobber (reg:CC FLAGS_REG))])]
+  ""
+  [(set_attr "type" "alu")
+   (set_attr "mode" "QI")])
+
 (define_expand "addqi_ext_1"
   [(parallel
      [(set (zero_extract:HI (match_operand:HI 0 "register_operand")
    (set_attr "type" "alu")
    (set_attr "mode" "QI")])
 
+(define_insn_and_split "*subqi_ext2<mode>_0"
+  [(set (match_operand:QI 0 "register_operand" "=&Q")
+       (minus:QI
+         (subreg:QI
+           (match_operator:SWI248 3 "extract_operator"
+             [(match_operand 1 "int248_register_operand" "Q")
+              (const_int 8)
+              (const_int 8)]) 0)
+         (subreg:QI
+           (match_operator:SWI248 4 "extract_operator"
+             [(match_operand 2 "int248_register_operand" "Q")
+              (const_int 8)
+              (const_int 8)]) 0)))
+   (clobber (reg:CC FLAGS_REG))]
+  ""
+  "#"
+  "&& reload_completed"
+  [(set (match_dup 0)
+       (subreg:QI
+         (match_op_dup 3
+           [(match_dup 1) (const_int 8) (const_int 8)]) 0))
+   (parallel
+     [(set (match_dup 0)
+          (minus:QI
+            (match_dup 0)
+            (subreg:QI
+              (match_op_dup 4
+                [(match_dup 2) (const_int 8) (const_int 8)]) 0)))
+      (clobber (reg:CC FLAGS_REG))])]
+  ""
+  [(set_attr "type" "alu")
+   (set_attr "mode" "QI")])
+
 ;; Alternative 1 is needed to work around LRA limitation, see PR82524.
 (define_insn_and_split "*subqi_ext<mode>_1"
   [(set (zero_extract:SWI248
    (set_attr "type" "alu")
    (set_attr "mode" "QI")])
 
+(define_insn_and_split "*<code>qi_ext2<mode>_0"
+  [(set (match_operand:QI 0 "register_operand" "=&Q")
+       (any_logic:QI
+         (subreg:QI
+           (match_operator:SWI248 3 "extract_operator"
+             [(match_operand 1 "int248_register_operand" "Q")
+              (const_int 8)
+              (const_int 8)]) 0)
+         (subreg:QI
+           (match_operator:SWI248 4 "extract_operator"
+             [(match_operand 2 "int248_register_operand" "Q")
+              (const_int 8)
+              (const_int 8)]) 0)))
+   (clobber (reg:CC FLAGS_REG))]
+  ""
+  "#"
+  "&& reload_completed"
+  [(set (match_dup 0)
+       (subreg:QI
+         (match_op_dup 4
+           [(match_dup 2) (const_int 8) (const_int 8)]) 0))
+   (parallel
+     [(set (match_dup 0)
+          (any_logic:QI
+            (subreg:QI
+              (match_op_dup 3
+                [(match_dup 1) (const_int 8) (const_int 8)]) 0)
+          (match_dup 0)))
+      (clobber (reg:CC FLAGS_REG))])]
+  ""
+  [(set_attr "type" "alu")
+   (set_attr "mode" "QI")])
+
 (define_expand "andqi_ext_1"
   [(parallel
      [(set (zero_extract:HI (match_operand:HI 0 "register_operand")
diff --git a/gcc/testsuite/gcc.target/i386/pr78904-10.c b/gcc/testsuite/gcc.target/i386/pr78904-10.c
new file mode 100644 (file)
index 0000000..0796291
--- /dev/null
@@ -0,0 +1,47 @@
+/* PR target/78904 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -masm=att" } */
+/* { dg-additional-options "-mregparm=3" { target ia32 } } */
+/* { dg-final { scan-assembler-not "shr" } } */
+
+struct S1
+{
+  unsigned char pad1;
+  unsigned char val;
+  unsigned short pad2;
+};
+
+char test_and (struct S1 a, struct S1 b)
+{
+  return a.val & b.val;
+}
+
+/* { dg-final { scan-assembler "\[ \t\]andb" } } */
+
+char test_or (struct S1 a, struct S1 b)
+{
+  return a.val | b.val;
+}
+
+/* { dg-final { scan-assembler "\[ \t\]orb" } } */
+
+char test_xor (struct S1 a, struct S1 b)
+{
+  return a.val ^ b.val;
+}
+
+/* { dg-final { scan-assembler "\[ \t\]xorb" } } */
+
+char test_add (struct S1 a, struct S1 b)
+{
+  return a.val + b.val;
+}
+
+/* { dg-final { scan-assembler "\[ \t\]addb" } } */
+
+char test_sub (struct S1 a, struct S1 b)
+{
+  return a.val - b.val;
+}
+
+/* { dg-final { scan-assembler "\[ \t\]subb" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr78904-10a.c b/gcc/testsuite/gcc.target/i386/pr78904-10a.c
new file mode 100644 (file)
index 0000000..1014028
--- /dev/null
@@ -0,0 +1,46 @@
+/* PR target/78904 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -masm=att" } */
+/* { dg-additional-options "-mregparm=3" { target ia32 } } */
+/* { dg-final { scan-assembler-not "shr" } } */
+
+struct S1
+{
+  unsigned char pad1;
+  unsigned char val;
+};
+
+char test_and (struct S1 a, struct S1 b)
+{
+  return a.val & b.val;
+}
+
+/* { dg-final { scan-assembler "\[ \t\]andb" } } */
+
+char test_or (struct S1 a, struct S1 b)
+{
+  return a.val | b.val;
+}
+
+/* { dg-final { scan-assembler "\[ \t\]orb" } } */
+
+char test_xor (struct S1 a, struct S1 b)
+{
+  return a.val ^ b.val;
+}
+
+/* { dg-final { scan-assembler "\[ \t\]xorb" } } */
+
+char test_add (struct S1 a, struct S1 b)
+{
+  return a.val + b.val;
+}
+
+/* { dg-final { scan-assembler "\[ \t\]addb" } } */
+
+char test_sub (struct S1 a, struct S1 b)
+{
+  return a.val - b.val;
+}
+
+/* { dg-final { scan-assembler "\[ \t\]subb" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr78904-10b.c b/gcc/testsuite/gcc.target/i386/pr78904-10b.c
new file mode 100644 (file)
index 0000000..376acf8
--- /dev/null
@@ -0,0 +1,47 @@
+/* PR target/78904 */
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -masm=att" } */
+/* { dg-final { scan-assembler-not "shr" } } */
+
+struct S1
+{
+  unsigned char pad1;
+  unsigned char val;
+  unsigned short pad2;
+  unsigned int pad3;
+};
+
+char test_and (struct S1 a, struct S1 b)
+{
+  return a.val & b.val;
+}
+
+/* { dg-final { scan-assembler "\[ \t\]andb" } } */
+
+char test_or (struct S1 a, struct S1 b)
+{
+  return a.val | b.val;
+}
+
+/* { dg-final { scan-assembler "\[ \t\]orb" } } */
+
+char test_xor (struct S1 a, struct S1 b)
+{
+  return a.val ^ b.val;
+}
+
+/* { dg-final { scan-assembler "\[ \t\]xorb" } } */
+
+char test_add (struct S1 a, struct S1 b)
+{
+  return a.val + b.val;
+}
+
+/* { dg-final { scan-assembler "\[ \t\]addb" } } */
+
+char test_sub (struct S1 a, struct S1 b)
+{
+  return a.val - b.val;
+}
+
+/* { dg-final { scan-assembler "\[ \t\]subb" } } */