diff mbox

[i386] : Truncate shift count

Message ID AANLkTim7yOLZV8bRvyvxJM3sjk-nZjiBuFmEn++W0kYg@mail.gmail.com
State New
Headers show

Commit Message

Uros Bizjak Oct. 1, 2010, 9:30 p.m. UTC
Hello!

SHIFT_COUNT_TRUNCATED can't be used on i386 (see comment in
gccint.info) and TARGET_SHIFT_TRUNCATION mask is not effective for
some reason on attached testcase. To remove unnecessary and
instructions on shift count, let's implement approach, proposed in the
documentation:

<quote>
     However, on some machines, such as the 80386 and the 680x0,
     truncation only applies to shift operations and not the (real or
     pretended) bit-field operations.  Define `SHIFT_COUNT_TRUNCATED'
     to be zero on such machines.  Instead, add patterns to the `md'
     file that include the implied truncation of the shift instructions.
</quote>

So, we add:

(define_insn_and_split "*ashl<mode>3_mask"
  [(set (match_operand:SWI48 0 "nonimmediate_operand" "=rm")
	(ashift:SWI48
	  (match_operand:SWI48 1 "nonimmediate_operand" "0")
	  (subreg:QI
	    (and:SI
	      (match_operand:SI 2 "register_operand" "c")
	      (match_operand:SI 3 "const_int_operand" "n")) 0)))
   (clobber (reg:CC FLAGS_REG))]
  "ix86_binary_operator_ok (ASHIFT, <MODE>mode, operands)
   && (INTVAL (operands[3]) & (GET_MODE_BITSIZE (<MODE>mode)-1))
      == GET_MODE_BITSIZE (<MODE>mode)-1"
  "#"
  "&& 1"
  [(parallel [(set (match_dup 0)
		   (ashift:SWI48 (match_dup 1) (match_dup 2)))
	      (clobber (reg:CC FLAGS_REG))])]
  "operands[2] = simplify_gen_subreg (QImode, operands[2], SImode, 0);"
  [(set_attr "type" "ishift")
   (set_attr "mode" "<MODE>")])

And similar patterns for other shifts/rotates.

The above pattern is effective for both, SImode and DImode shifts,
since for both modes, combine always generate QImode subregs of SImode
ANDs. I didn't manage to generate a HImode or QImode shift, the shift
has always been promoted to SImode, so I have left them out. Also
note, that ..._cmp version of the shift (that also sets flag register)
operates only with constant shift arguments [see comments in i386.md
about flags and shift-by-zero functionality], so this new pattern
(that operates with variable shift argument exclusively) can't
interfere with ..._cmp versions in any way.

2010-10-01  Uros Bizjak  <ubizjak@gmail.com>

	* config/i386/i386.md (*ashl<mode>3_mask): New insn_and_split pattern.
	(*<shiftrt_insn><mode>3_mask): Ditto.
	(*<rotate_insn><mode>3_mask): Ditto.

testsuite/ChangeLog:

2010-10-01  Uros Bizjak  <ubizjak@gmail.com>

	* gcc.target/i386/shift_mask.c: New test.

Patch was regression tested on x86_64-pc-linux-gnu {,-m32} and
committed to mainline.

Uros.
diff mbox

Patch

Index: config/i386/i386.md
===================================================================
--- config/i386/i386.md	(revision 164890)
+++ config/i386/i386.md	(working copy)
@@ -9151,6 +9151,29 @@ 
   DONE;
 })
 
+;; Avoid useless masking of count operand.
+
+(define_insn_and_split "*ashl<mode>3_mask"
+  [(set (match_operand:SWI48 0 "nonimmediate_operand" "=rm")
+	(ashift:SWI48
+	  (match_operand:SWI48 1 "nonimmediate_operand" "0")
+	  (subreg:QI
+	    (and:SI
+	      (match_operand:SI 2 "register_operand" "c")
+	      (match_operand:SI 3 "const_int_operand" "n")) 0)))
+   (clobber (reg:CC FLAGS_REG))]
+  "ix86_binary_operator_ok (ASHIFT, <MODE>mode, operands)
+   && (INTVAL (operands[3]) & (GET_MODE_BITSIZE (<MODE>mode)-1))
+      == GET_MODE_BITSIZE (<MODE>mode)-1"
+  "#"
+  "&& 1"
+  [(parallel [(set (match_dup 0)
+		   (ashift:SWI48 (match_dup 1) (match_dup 2)))
+	      (clobber (reg:CC FLAGS_REG))])]
+  "operands[2] = simplify_gen_subreg (QImode, operands[2], SImode, 0);"
+  [(set_attr "type" "ishift")
+   (set_attr "mode" "<MODE>")])
+
 (define_insn "*ashl<mode>3_1"
   [(set (match_operand:SWI48 0 "nonimmediate_operand" "=rm,r")
 	(ashift:SWI48 (match_operand:SWI48 1 "nonimmediate_operand" "0,l")
@@ -9690,6 +9713,29 @@ 
   ""
   "ix86_expand_binary_operator (<CODE>, <MODE>mode, operands); DONE;")
 
+;; Avoid useless masking of count operand.
+
+(define_insn_and_split "*<shiftrt_insn><mode>3_mask"
+  [(set (match_operand:SWI48 0 "nonimmediate_operand" "=rm")
+	(any_shiftrt:SWI48
+	  (match_operand:SWI48 1 "nonimmediate_operand" "0")
+	  (subreg:QI
+	    (and:SI
+	      (match_operand:SI 2 "register_operand" "c")
+	      (match_operand:SI 3 "const_int_operand" "n")) 0)))
+   (clobber (reg:CC FLAGS_REG))]
+  "ix86_binary_operator_ok (<CODE>, <MODE>mode, operands)
+   && (INTVAL (operands[3]) & (GET_MODE_BITSIZE (<MODE>mode)-1))
+      == GET_MODE_BITSIZE (<MODE>mode)-1"
+  "#"
+  "&& 1"
+  [(parallel [(set (match_dup 0)
+		   (any_shiftrt:SWI48 (match_dup 1) (match_dup 2)))
+	      (clobber (reg:CC FLAGS_REG))])]
+  "operands[2] = simplify_gen_subreg (QImode, operands[2], SImode, 0);"
+  [(set_attr "type" "ishift")
+   (set_attr "mode" "<MODE>")])
+
 (define_insn_and_split "*<shiftrt_insn><mode>3_doubleword"
   [(set (match_operand:DWI 0 "register_operand" "=r")
 	(any_shiftrt:DWI (match_operand:DWI 1 "register_operand" "0")
@@ -10042,6 +10088,29 @@ 
   ""
   "ix86_expand_binary_operator (<CODE>, <MODE>mode, operands); DONE;")
 
+;; Avoid useless masking of count operand.
+
+(define_insn_and_split "*<rotate_insn><mode>3_mask"
+  [(set (match_operand:SWI48 0 "nonimmediate_operand" "=rm")
+	(any_rotate:SWI48
+	  (match_operand:SWI48 1 "nonimmediate_operand" "0")
+	  (subreg:QI
+	    (and:SI
+	      (match_operand:SI 2 "register_operand" "c")
+	      (match_operand:SI 3 "const_int_operand" "n")) 0)))
+   (clobber (reg:CC FLAGS_REG))]
+  "ix86_binary_operator_ok (<CODE>, <MODE>mode, operands)
+   && (INTVAL (operands[3]) & (GET_MODE_BITSIZE (<MODE>mode)-1))
+      == GET_MODE_BITSIZE (<MODE>mode)-1"
+  "#"
+  "&& 1"
+  [(parallel [(set (match_dup 0)
+		   (any_rotate:SWI48 (match_dup 1) (match_dup 2)))
+	      (clobber (reg:CC FLAGS_REG))])]
+  "operands[2] = simplify_gen_subreg (QImode, operands[2], SImode, 0);"
+  [(set_attr "type" "rotate")
+   (set_attr "mode" "<MODE>")])
+
 ;; Implement rotation using two double-precision
 ;; shift instructions and a scratch register.
 
Index: testsuite/gcc.target/i386/shift_mask.c
===================================================================
--- testsuite/gcc.target/i386/shift_mask.c	(revision 0)
+++ testsuite/gcc.target/i386/shift_mask.c	(revision 0)
@@ -0,0 +1,31 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+int test_sal (int a, int c)
+{
+  return a << (c & 0x1f);
+}
+
+int test_sar (int a, int c)
+{
+  return a >> (c & 0x1f);
+}
+
+unsigned int test_shr (unsigned int a, int c)
+{
+  return a >> (c & 0x1f);
+}
+
+unsigned int test_rol (unsigned int a, int c)
+{
+  int z = c & 0x1f;
+  return (a << z) | (a >> (32 - z));
+}
+
+unsigned int test_ror (unsigned int a, int c)
+{
+  int z = c & 0x1f;
+  return (a >> z) | (a << (32 - z));
+}
+
+/* { dg-final { scan-assembler-not "and" } } */