diff mbox series

[committed] i386: Optimize QImode insn with high input registers

Message ID CAFULd4bF5J+gEWE-r=BxaBQLk+GkCLPsXLMEcfOG0KEvtAB6TA@mail.gmail.com
State New
Headers show
Series [committed] i386: Optimize QImode insn with high input registers | expand

Commit Message

Uros Bizjak Nov. 16, 2023, 6:16 p.m. UTC
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.

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

Uros.
diff mbox series

Patch

diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index f5407ab3054..1b5a794b9e5 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -7069,6 +7069,39 @@  (define_insn "*addqi_ext<mode>_0"
    (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")
@@ -7814,6 +7847,39 @@  (define_insn "*subqi_ext<mode>_0"
    (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
@@ -11815,6 +11881,39 @@  (define_insn "*<code>qi_ext<mode>_0"
    (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
index 00000000000..079629150df
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr78904-10.c
@@ -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
index 00000000000..101402867b0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr78904-10a.c
@@ -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
index 00000000000..376acf81962
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr78904-10b.c
@@ -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" } } */