Following testcase: --cut here-- struct S { unsigned char pad1; unsigned char val; unsigned short pad2; };
unsigned char test_add (unsigned char a, struct S b) { a += b.val; return a; } --cut here-- should be compiled to something like: addb %dh, %al but is currently compiled to: movzbl %dh, %edx addl %edx, %eax The patch implements insn patterns that model QImode binary ops with high-part QImode input register. These ops can not be encoded with REX prefix, so only Q registers and constant memory output operands are allowed on x86_64 targets. 2023-02-17 Uroš Bizjak <ubiz...@gmail.com> gcc/ChangeLog: PR target/108831 * config/i386/predicates.md (nonimm_x64constmem_operand): New predicate. * config/i386/i386.md (*addqi_ext<mode>_0): New insn pattern. (*subqi_ext<mode>_0): Ditto. (*andqi_ext<mode>_0): Ditto. (*<any_or:code>qi_ext<mode>_0): Ditto. gcc/testsuite/ChangeLog: PR target/108831 * gcc.target/i386/pr108831-1.c: New test. * gcc.target/i386/pr108831-2.c: Ditto. Bootstrapped and regression tested on x86_64-linux-gnu {,-m32}. Pushed to master. Uros.
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index 198f06e0769..55042e7ae15 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -6641,6 +6641,22 @@ (define_insn "*add<mode>_5" (const_string "*"))) (set_attr "mode" "<MODE>")]) +(define_insn "*addqi_ext<mode>_0" + [(set (match_operand:QI 0 "nonimm_x64constmem_operand" "=QBc,m") + (plus:QI + (subreg:QI + (zero_extract:SWI248 + (match_operand 2 "int248_register_operand" "Q,Q") + (const_int 8) + (const_int 8)) 0) + (match_operand:QI 1 "nonimm_x64constmem_operand" "0,0"))) + (clobber (reg:CC FLAGS_REG))] + "" + "add{b}\t{%h2, %0|%0, %h2}" + [(set_attr "isa" "*,nox64") + (set_attr "type" "alu") + (set_attr "mode" "QI")]) + (define_expand "addqi_ext_1" [(parallel [(set (zero_extract:HI (match_operand:HI 0 "register_operand") @@ -7265,6 +7281,22 @@ (define_insn "*subsi_2_zext" [(set_attr "type" "alu") (set_attr "mode" "SI")]) +(define_insn "*subqi_ext<mode>_0" + [(set (match_operand:QI 0 "nonimm_x64constmem_operand" "=QBc,m") + (minus:QI + (match_operand:QI 1 "nonimm_x64constmem_operand" "0,0") + (subreg:QI + (zero_extract:SWI248 + (match_operand 2 "int248_register_operand" "Q,Q") + (const_int 8) + (const_int 8)) 0))) + (clobber (reg:CC FLAGS_REG))] + "" + "sub{b}\t{%h2, %0|%0, %h2}" + [(set_attr "isa" "*,nox64") + (set_attr "type" "alu") + (set_attr "mode" "QI")]) + (define_insn "*subqi_ext<mode>_2" [(set (zero_extract:SWI248 (match_operand 0 "int248_register_operand" "+Q") @@ -10528,6 +10560,22 @@ (define_insn "*and<mode>_2" [(set_attr "type" "alu") (set_attr "mode" "<MODE>")]) +(define_insn "*andqi_ext<mode>_0" + [(set (match_operand:QI 0 "nonimm_x64constmem_operand" "=QBc,m") + (and:QI + (subreg:QI + (zero_extract:SWI248 + (match_operand 2 "int248_register_operand" "Q,Q") + (const_int 8) + (const_int 8)) 0) + (match_operand:QI 1 "nonimm_x64constmem_operand" "0,0"))) + (clobber (reg:CC FLAGS_REG))] + "" + "and{b}\t{%h2, %0|%0, %h2}" + [(set_attr "isa" "*,nox64") + (set_attr "type" "alu") + (set_attr "mode" "QI")]) + (define_expand "andqi_ext_1" [(parallel [(set (zero_extract:HI (match_operand:HI 0 "register_operand") @@ -11269,6 +11317,22 @@ (define_insn "*<code><mode>_3" [(set_attr "type" "alu") (set_attr "mode" "<MODE>")]) +(define_insn "*<code>qi_ext<mode>_0" + [(set (match_operand:QI 0 "nonimm_x64constmem_operand" "=QBc,m") + (any_or:QI + (subreg:QI + (zero_extract:SWI248 + (match_operand 2 "int248_register_operand" "Q,Q") + (const_int 8) + (const_int 8)) 0) + (match_operand:QI 1 "nonimm_x64constmem_operand" "0,0"))) + (clobber (reg:CC FLAGS_REG))] + "" + "<logic>{b}\t{%h2, %0|%0, %h2}" + [(set_attr "isa" "*,nox64") + (set_attr "type" "alu") + (set_attr "mode" "QI")]) + (define_insn "*<code>qi_ext<mode>_1" [(set (zero_extract:SWI248 (match_operand 0 "int248_register_operand" "+Q,Q") diff --git a/gcc/config/i386/predicates.md b/gcc/config/i386/predicates.md index 2f079a6fad8..7b3db0cc851 100644 --- a/gcc/config/i386/predicates.md +++ b/gcc/config/i386/predicates.md @@ -109,6 +109,13 @@ (define_special_predicate "int_nonimmediate_operand" (match_test "GET_MODE (op) == HImode") (match_test "GET_MODE (op) == QImode")))) +;; Match nonimmediate operand, but exclude non-constant addresses for x86_64. +(define_predicate "nonimm_x64constmem_operand" + (ior (match_operand 0 "register_operand") + (and (match_operand 0 "memory_operand") + (ior (not (match_test "TARGET_64BIT")) + (match_test "constant_address_p (XEXP (op, 0))"))))) + ;; Match register operands, but include memory operands for TARGET_SSE_MATH. (define_predicate "register_ssemem_operand" (if_then_else diff --git a/gcc/testsuite/gcc.target/i386/pr108831-1.c b/gcc/testsuite/gcc.target/i386/pr108831-1.c new file mode 100644 index 00000000000..3499b187cbe --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr108831-1.c @@ -0,0 +1,63 @@ +/* PR target/108831 */ +/* { dg-do compile } */ +/* { dg-options "-O2 -masm=att" } */ +/* { dg-additional-options "-mregparm=3" { target ia32 } } */ +/* { dg-final { scan-assembler-not "movzbl" } } */ +/* { dg-final { scan-assembler-not "movb" } } */ + +struct S +{ + unsigned char pad1; + unsigned char val; + unsigned short pad2; +}; + +unsigned char +test_and (unsigned char a, struct S b) +{ + a &= b.val; + + return a; +} + +/* { dg-final { scan-assembler "\[ \t\]andb" } } */ + +unsigned char +test_or (unsigned char a, struct S b) +{ + a |= b.val; + + return a; +} + +/* { dg-final { scan-assembler "\[ \t\]orb" } } */ + +unsigned char +test_xor (unsigned char a, struct S b) +{ + a ^= b.val; + + return a; +} + +/* { dg-final { scan-assembler "\[ \t\]xorb" } } */ + +unsigned char +test_add (unsigned char a, struct S b) +{ + a += b.val; + + return a; +} + +/* { dg-final { scan-assembler "\[ \t\]addb" } } */ + +unsigned char +test_sub (unsigned char a, struct S b) +{ + a -= b.val; + + return a; +} + +/* { dg-final { scan-assembler "\[ \t\]subb" } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr108831-2.c b/gcc/testsuite/gcc.target/i386/pr108831-2.c new file mode 100644 index 00000000000..a415391f69d --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr108831-2.c @@ -0,0 +1,55 @@ +/* PR target/108831 */ +/* { dg-do compile } */ +/* { dg-options "-O2 -masm=att" } */ +/* { dg-additional-options "-mregparm=3" { target ia32 } } */ +/* { dg-final { scan-assembler-not "movzbl" } } */ +/* { dg-final { scan-assembler-not "movb" } } */ + +struct S +{ + unsigned char pad1; + unsigned char val; + unsigned short pad2; +}; + +unsigned char a; + +void +test_and (struct S b) +{ + a &= b.val; +} + +/* { dg-final { scan-assembler "\[ \t\]andb" } } */ + +void +test_or (struct S b) +{ + a |= b.val; +} + +/* { dg-final { scan-assembler "\[ \t\]orb" } } */ + +void +test_xor (struct S b) +{ + a ^= b.val; +} + +/* { dg-final { scan-assembler "\[ \t\]xorb" } } */ + +void +test_add (struct S b) +{ + a += b.val; +} + +/* { dg-final { scan-assembler "\[ \t\]addb" } } */ + +void +test_sub (struct S b) +{ + a -= b.val; +} + +/* { dg-final { scan-assembler "\[ \t\]subb" } } */