diff mbox

[PR,52076,P3,regression] Improve m68k xor and bit insertion

Message ID 54C16AD2.6020601@redhat.com
State New
Headers show

Commit Message

Jeff Law Jan. 22, 2015, 9:25 p.m. UTC
Just knocking out another m68k issue since I've got my little aranym 
setup running.

This BZ is a quality of code regression relative to very old versions of 
GCC for two code sequences.

First, setting a bitfield to all ones.  Right now we're doing this via 
moveq/bfins.  It's a 6 byte sequence and uses a register.  We can do 
better using or.b imm,<ea> which is a 4 byte sequence with no scratch 
register needed.  The trick here is to detect that we're setting a field 
to all ones and rewriting the RTL to look like insv_bfset_reg during the 
insv expander.  That in turn looks "nice" to combine which combines it 
with the memory references into the or.b insn.

The second sequence is turning a single bit on within a bitfield.  At 
some point GCC stopped using a single or.[bw] and instead generated a 
movb imm,scr; or.l scr,<ea>, which is two bytes longer than the or.[bw] 
imm,<ea> and uses a scratch register.  At some unknown point since the 
report was filed, we changed movb imm,scr into moveq imm,scr.  That 
brought the code sequence back to parity WRT size.  We still use an 
unnecessary scratch and I'm sure the two sequences run at different 
speeds depending on the cpu variant in use.

Further testing showed that xor of a single bit in a bitfield was 
generating poor code.  Tweaking the constraints fixed it to behave like 
ior & and.

With that small xor issue fixed, the sequences we generate are the same 
size as direct manipulation via [or|eor|and].[bw].  Selecting between 
the same sized sequences based on target processor's characteristics 
probably isn't worth the effort and I'm not considering that part of 
this BZ.

Tested by building m68k-elf as a cross, then running the m68k.exp 
testsuite and by an ongoing bootstrap on m68k linux (into stage2 
libraries right now, not expecting problems).

Installed on the trunk.
commit 2384fd2cc186c1de5b65db282ef8c67674e851df
Author: Jeff Law <law@redhat.com>
Date:   Thu Jan 22 14:21:21 2015 -0700

    	PR target/52076
    	* config/m68k/m68k.md (xorsi3_internal): Twiddle constraints to
    	improve code density for small immediate to memory case.
    	(insv): Better handle bitfield assignments when the field is
    	being set to all ones.
    	* config/m68k/predicates.md (reg_or_pow2_m1_operand): New
    	operand predicate.
    
    	PR target/52076
    	* gcc.target/m68k/pr52076-1.c: New test.
    	* gcc.target/m68k/pr52076-2.c: New test.
diff mbox

Patch

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index c166933..ae5e9a5 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,13 @@ 
+2015-01-22  Jeff Law  <law@redhat.com>
+
+	PR target/52076
+	* config/m68k/m68k.md (xorsi3_internal): Twiddle constraints to
+	improve code density for small immediate to memory case.
+	(insv): Better handle bitfield assignments when the field is
+	being set to all ones.
+	* config/m68k/predicates.md (reg_or_pow2_m1_operand): New
+	operand predicate.
+
 2015-01-22  Rainer Orth  <ro@CeBiTec.Uni-Bielefeld.DE>
 	    Jakub Jelinek  <jakub@redhat.com>
 
diff --git a/gcc/config/m68k/.m68k.md.swp b/gcc/config/m68k/.m68k.md.swp
new file mode 100755
index 0000000..36d7681
Binary files /dev/null and b/gcc/config/m68k/.m68k.md.swp differ
diff --git a/gcc/config/m68k/m68k.md b/gcc/config/m68k/m68k.md
index d34ad1d..6bb296e 100644
--- a/gcc/config/m68k/m68k.md
+++ b/gcc/config/m68k/m68k.md
@@ -3838,9 +3838,9 @@ 
   "")
 
 (define_insn "xorsi3_internal"
-  [(set (match_operand:SI 0 "nonimmediate_operand" "=do,m")
-	(xor:SI (match_operand:SI 1 "general_operand" "%0,0")
-                (match_operand:SI 2 "general_operand" "di,dKT")))]
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=d,o,m")
+	(xor:SI (match_operand:SI 1 "general_operand" "%0, 0,0")
+                (match_operand:SI 2 "general_operand" "di,dK,dKT")))]
 
   "!TARGET_COLDFIRE"
 {
@@ -5583,9 +5583,20 @@ 
   [(set (zero_extract:SI (match_operand:SI 0 "nonimmediate_operand" "")
 			 (match_operand:SI 1 "const_int_operand" "")
 			 (match_operand:SI 2 "const_int_operand" ""))
-	(match_operand:SI 3 "register_operand" ""))]
+	(match_operand:SI 3 "reg_or_pow2_m1_operand" ""))]
   "TARGET_68020 && TARGET_BITFIELD"
-  "")
+  "
+{
+  /* Special case initializing a field to all ones. */
+  if (GET_CODE (operands[3]) == CONST_INT)
+    {
+      if (exact_log2 (INTVAL (operands[3]) + 1) != INTVAL (operands[1]))
+	operands[3] = force_reg (SImode, operands[3]);
+      else
+	operands[3] = constm1_rtx;
+
+    }
+}")
 
 (define_insn "*insv_bfins_mem"
   [(set (zero_extract:SI (match_operand:QI 0 "memory_operand" "+o")
diff --git a/gcc/config/m68k/predicates.md b/gcc/config/m68k/predicates.md
index a7b5c42..c652f10 100644
--- a/gcc/config/m68k/predicates.md
+++ b/gcc/config/m68k/predicates.md
@@ -244,3 +244,16 @@ 
 		 || reload_in_progress
 		 || reload_completed));
 })
+
+;; Used to detect when an operand is either a register
+;; or a constant that is all ones in its lower bits.
+;; Used by insv pattern to help detect when we're initializing
+;; a bitfield to all ones.
+
+(define_predicate "reg_or_pow2_m1_operand"
+  (match_code "reg,const_int")
+{
+  return (REG_P (op)
+	  || (GET_CODE (op) == CONST_INT
+	      && exact_log2 (INTVAL (op) + 1) >= 0));
+})
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 8bebdac..1fc0241 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,9 @@ 
+2015-01-22  Jeff Law  <law@redhat.com>
+
+	PR target/52076
+	* gcc.target/m68k/pr52076-1.c: New test.
+	* gcc.target/m68k/pr52076-2.c: New test.
+
 2015-01-22  Richard Biener  <rguenther@suse.de>
 
 	PR middle-end/64728
diff --git a/gcc/testsuite/gcc.target/m68k/pr52076-1.c b/gcc/testsuite/gcc.target/m68k/pr52076-1.c
new file mode 100644
index 0000000..86df0dc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/m68k/pr52076-1.c
@@ -0,0 +1,20 @@ 
+/* { dg-do assemble } /*
+/* { dg-options "-Os -fomit-frame-pointer -m68040" } */
+/* { dg-final { object-size text <= 72 } } */
+
+struct kobject {
+        unsigned int b7:1;
+        unsigned int :6;
+        unsigned int b0:1;
+        unsigned char x;
+        unsigned int f;
+};
+
+void ior(struct kobject *kobj) { kobj->f |= 4; }
+void ior_m(struct kobject *kobj) { kobj->f |= -4; }
+
+void xor(struct kobject *kobj) { kobj->f ^= 4; }
+void xor_m(struct kobject *kobj) { kobj->f ^= -4; }
+
+void and(struct kobject *kobj) { kobj->f &= 4; }
+void and_m(struct kobject *kobj) { kobj->f &= -4; }
diff --git a/gcc/testsuite/gcc.target/m68k/pr52076-2.c b/gcc/testsuite/gcc.target/m68k/pr52076-2.c
new file mode 100644
index 0000000..30c6991
--- /dev/null
+++ b/gcc/testsuite/gcc.target/m68k/pr52076-2.c
@@ -0,0 +1,27 @@ 
+/* { dg-do assemble } /*
+/* { dg-options "-Os -fomit-frame-pointer -m68040" } */
+/* { dg-final { object-size text <= 30 } } */
+
+struct kobject {
+        unsigned int b7:1;
+        unsigned int b56:2;
+        unsigned int b1234:4;
+        unsigned int b0:1;
+        unsigned char x;
+        unsigned int f;
+};
+
+void b7(struct kobject *kobj)
+{
+        kobj->b7 = 1;
+}
+
+void b56(struct kobject *kobj)
+{
+        kobj->b56 = 3;
+}
+
+void b1234(struct kobject *kobj)
+{
+        kobj->b1234 = 15;
+}